www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to debug FinalizeError?

reply unDEFER <undefer gmail.com> writes:
Hello! After long-long time of debugging, I just have decided 
InvalidMemoryOperationError in my program. But now my program 
after few hours of testing again crashes with "Finalization 
error".

What this error means exactly? I again did something wrong in 
destructor?
And how to debug it? I tried "break onFinalizeError" in gdb like 
"break onInvalidMemoryOperationError", but it says:

Function "onFinalizeError" not defined.

There are too few information about this error in documentation 
and forum, and I didn't find anything about it in wiki.

Thank you.
Nov 28 2018
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Wednesday, 28 November 2018 at 22:40:17 UTC, unDEFER wrote:
 Hello! After long-long time of debugging, I just have decided 
 InvalidMemoryOperationError in my program. But now my program 
 after few hours of testing again crashes with "Finalization 
 error".
So InvalidMemoryOperationError means a GC function got called while the GC was locked. This is usually because you did an allocation inside a destructor. Finalization error happens when a destructor throws an exception. Now, many times, trying to throw an exception from inside a destructor will first trigger the invalid operation - allocating the exception will be an error before the exception actually exists. But there are a few ways around this, like preallocating or calling a function that uses a statically allocated exception. Language features that use statically allocated exceptions include: * InvalidMemoryOperationError * OutOfMemoryError * SwitchError There might be more, but this is all I see right now (my process btw: search the druntime source code for "staticError", the function that makes those. But this is subject to change, so first maybe look up the definition of onOutOfMemoryError and see the pattern there.) Of these... well, they are all kinda difficult to cause in a destructor. Are you preallocating something in your code and throwing it in a destructor? Also, what *exactly* is the message you get when the exception happens? It should include a line number from druntime as well as message from the inner exception that caused it in the first place.
Nov 28 2018
parent reply unDEFER <undefer gmail.com> writes:
No I'm not preallocating any exceptions. It was idea, but I 
removed all calls which can make throw.
I'm using very old dmd 2.074.1, so as I have patched it for my 
text editor with IDE functions. I had a year break in 
development, so now I need to rewrite all my patches.
But exactly the output of my program looks like this:

core.exception.FinalizeError src/rt/lifetime.d(1407): 
Finalization error
----------------
=== Bypassed ===

|||||||||||| BerkeleyDB exceptions mixed with output of 
destructors ||||||||||||||||||

core.exception.InvalidMemoryOperationError src/core/exception.d(696): Invalid
memory operation
----------------

It means that "Invalid memory operation" occurred earlier than 
"Finalization error"?
The line on which shows the pointer src/rt/lifetime.d(1407) is 
exactly:
         onFinalizeError(*pc, e);
Why gdb doesn't see this function?

My program (the text editor) had run test all night under gdb 
with break on InvalidMemoryOperationError and didn't fall. So it 
is very-very-very hard to reproduce.
And I haven't ideas where maybe this throw. At least I don't see 
any throw in explicit form.
Nov 28 2018
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 11/29/18 2:07 AM, unDEFER wrote:
 No I'm not preallocating any exceptions. It was idea, but I removed all 
 calls which can make throw.
 I'm using very old dmd 2.074.1, so as I have patched it for my text 
 editor with IDE functions. I had a year break in development, so now I 
 need to rewrite all my patches.
 But exactly the output of my program looks like this:
 
 core.exception.FinalizeError src/rt/lifetime.d(1407): Finalization error
 ----------------
 === Bypassed ===
 
 |||||||||||| BerkeleyDB exceptions mixed with output of destructors 
 ||||||||||||||||||
 
 core.exception.InvalidMemoryOperationError src/core/exception.d(696): 
 Invalid memory operation
 ----------------
 
 It means that "Invalid memory operation" occurred earlier than 
 "Finalization error"?
 The line on which shows the pointer src/rt/lifetime.d(1407) is exactly:
          onFinalizeError(*pc, e);
 Why gdb doesn't see this function?
You need to compile druntime in debug mode. One thing you can do is implement the function locally, and then break on it (it's a C linkage, so I think the linker will grab your copy instead of the one in druntime) i.e. in your code, do: extern(C) void onFinalizeError(TypeInfo info, Throwable e, string file = __FILE__, size_t line = __LINE__) { import core.stdc.stdio; printf("break here\n"); }
 
 My program (the text editor) had run test all night under gdb with break 
 on InvalidMemoryOperationError and didn't fall. So it is very-very-very 
 hard to reproduce.
 And I haven't ideas where maybe this throw. At least I don't see any 
 throw in explicit form.
These are unfortunately really tough to debug. You get very little info, and the stack trace is generally useless. -Steve
Nov 29 2018
parent unDEFER <undefer gmail.com> writes:
On Thursday, 29 November 2018 at 14:51:40 UTC, Steven 
Schveighoffer wrote:
 You need to compile druntime in debug mode. One thing you can 
 do is implement the function locally, and then break on it 
 (it's a C linkage, so I think the linker will grab your copy 
 instead of the one in druntime)

 i.e. in your code, do:

 extern(C) void onFinalizeError(TypeInfo info, Throwable e, 
 string file = __FILE__, size_t line = __LINE__)
 {
    import core.stdc.stdio;
    printf("break here\n");
 }
Big thanks, Steve. I will try it. Every night will do debugging and maybe at one night it will happen again :-)
Nov 29 2018