3

I recently started working on an existing project written in C++ Builder. The application consists of a MainModule that loads lots of modules (DLLs). The MainModule itself is a DLL (there is a small loader (.exe) that starts the MainModule).

The MainModule should not crash if a problem appears in any of the loaded modules (DLLs). So, it is imperative that the exceptions will not leave the module(s). Therefore, in each DLL there is a global try/catch that traps all exceptions inside.

To make the code more compact, exceptions are used everywhere for

  • flow control
  • signaling things
  • error logging (when something wrong happens, the exception handler will output a message to the console)

The debugging is done based on the messages shown in the console and also printf lines are inserted in code to see if the program reaches a certain point in a function or not.

I wonder, how could I debug those DLLs?
The problem is that when an access violation happens in one of those plugins/DLLs, the global try/catch will catch the exception. It will never reach the main application/debugger.

Note: The code is reviewed. Therefore, it is also imperative to do as little changes as possible to the existing code!

  • This MS article says that structured exception handling should work ok with debugging. The thing that crates problems is the IMPERATIVE condition that says that the main module should not crash if a sub-module crashes. msdn.microsoft.com/en-us/library/windows/desktop/… – Rigel Jul 5 '18 at 13:00
  • 1
    Normally the way you prevent crashes in your main program is by having a top level try/catch. Do you have one of those? – Robert Harvey Jul 5 '18 at 15:24
  • @RobertHarvey - yes. – Rigel Jul 5 '18 at 15:27
  • 1
    Perhaps extend your logging to include a full stack trace? I don't know how well C++ supports this, but Java and C# will actually tell you which line of code threw the exception. – Robert Harvey Jul 5 '18 at 15:44
  • 4
    If it's that crucial that your host application doesn't crash, I really think a more robust tool for the job is going to be to launch a separate process and use IPC like file mappings for shared memory to share data between those processes. If the sub-process crashes, then it doesn't crash your main process. – Dragon Energy Dec 5 '18 at 8:27
2

I see two approaches to your issue:

  • Configure your debugger to break when an exception is thrown and not just on unhandled exceptions. This is the approach I usually choose, but it only works if exceptions are only used for exceptional events and not all the time. So it doesn't appear to fit your application very well.

    Some debuggers allow configuring this settings based on the exception type. So you can use this approach for exceptions that should never happen (e.g. access violations) while resorting to the second approach for exceptions that happen frequently in your code-base.

  • You could put a break-point into the global exception handler in the dll.

    Something along the lines of: if(IsDebuggerPresent())__debugbreak();

    Unfortunately part of the stack will already be unrolled by that point, removing a lot of information that'd be useful for debugging.

Though it's unlikely that you can do anything about that, an access violation is generally considered an unrecoverable exception (because it might result from/in memory corruption) and you should use a separate process that gets terminated to contain the fallout instead of letting a process continue in which such an error happened.

  • Let me see if I understand correctly the last phrase: any sub-module is a DLL. These DLLs share the same memory space with the main module. So, a sub-module can corrupt the memory of the main process. In this case does not matter if the exception was catch into the DLL or not. The memory of the main module was corrupted anyway! Right? – Rigel Jul 6 '18 at 8:49
  • 1
    @ComfortablyNumb Yes. of course it depends on luck which parts of memory actually get corrupted, if at all. – CodesInChaos Jul 6 '18 at 9:24
  • 3
    @ComfortablyNumb C++ exceptions DO NOT indicate memory corruption. Those are completely unrelated failure modes. However, if the modules are not exception-safe (= if they don't use RAII properly) then throwing an exception could leave data structures in a corrupted state (e.g. referencing freed memory, leaking memory, or violating invariants). Use-after-free bugs can then later lead to memory corruption. Using separate processes as suggested in this answer makes it possible to mostly ignore these problems as you can simply restart a failed sub-process. – amon Jul 6 '18 at 11:37
  • He's talking about structured exceptions, not c++exceptions – Bwmat Jul 6 '18 at 14:04
3

'swallowing' a structured exception in general, is very dangerous. Are you only doing so with exceptions you have triggered yourself intentionally? I. E. If you swallow a segfault, that may subtly corrupt your process and make the problem nigh impossible to debug when it REALLY messes up later.

I would suggest isolating your sub modules in separate processes if possible

  • the exceptions are "swallowed" ONLY in the sub-modules. – Rigel Jul 6 '18 at 7:35
  • My point stands, assuming they're running in the same process – Bwmat Jul 6 '18 at 7:35
  • Also, this answer was meant to be a comment, whoops – Bwmat Jul 6 '18 at 7:36
  • ok. How to re-implement this (if possible without a lot of changes) so that we meet the condition "main module should NEVER crash if a module crashes"? – Rigel Jul 6 '18 at 8:06
  • 3
    That's very lucky. Replace direct function calls with messages passed with sockets or pipes, and run each DLL in its own process. In your top-level exception handler, you can send an error message to the main nodule/process and then crash (maybe after capturing a dump or w/e) – Bwmat Jul 6 '18 at 8:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.