Page 1 of 1
Log exceptions
Posted: Fri May 03, 2019 3:50 pm
by Berocoder
I have been using JCL as exceptionhandler for years. But I think that later versions have not good quality of callstack.
So I download and evaluate MadExcept instead.
I use a global exceptionhandler ApplicationEvents.OnException
This determine if the exception would be shown for enduser, save to to log-file etc.
So now I try to use MadExcept to save exception to log-file as before.
I have registred an event to be called.
procedure TServerData.HandleApplicationException(const exceptIntf: IMEException; var handled: Boolean);
So I have working code that can log the callstack to file. I am using GetCrashStackTrace for that.
I have two questions
1. I want to modify the callstack and log that to file.
It is like this now:
02bb0591 AttracsXE.exe FrmClientMainCommon 1211 TClientMainCommonForm.Logon
02bb22c1 AttracsXE.exe FrmClientMainCommon 1585 TClientMainCommonForm.FormShow
006e7a69 AttracsXE.exe Vcl.Forms 4104 TCustomForm.DoShow
And I want it like this
[00] TClientMainForm.Logon (frmClientMainForm.pas:1211)
[01] TClientMainCommonForm.FormShow (FrmClientMainCommon.pas:1585)
[02] TCustomForm.DoShow (Vcl.Forms.pas:4104)
So basically
[callstack number] classname.methodname (unitname:linenumber)
2. How can I get the interface of IMEException in try except
I want to do this
try
// some code with a bug
except
LogLastException(GetIMEException, 'Some custom description');
end
Now I send a standard Delphi E: Exception to LogLastException.
I want to change that to send IMEException instead.
Thanks in advance
Regards Roland
Re: Log exceptions
Posted: Sat May 04, 2019 10:10 am
by madshi
The IMEException interface has a property called "Callstacks", see here:
http://help.madshi.net/madExceptUnit.ht ... Callstacks
The type TStackTrace is defined like this:
Code: Select all
type
TStackItem = record
Addr : pointer; // code address
relAddr : dword; // relative address to function entry point
ModuleName : UnicodeString;
UnitName : UnicodeString;
Line : integer; // line number (0 = unknown)
relLine : integer; // relative line number to function entry point
FunctionName : UnicodeString; // function/method name
end;
TStackTrace = array of TStackItem;
TPStackTrace = ^TStackTrace;
This should give you the "power" to format the callstacks whichever way you prefer.
Do you *have* to do all this in a try..except block? It would be easier to simply do all this in your registered exception handler, because then you already get an "exceptIntf" interface delivered to you. If you insist on needing to do this in a try..except block, you can call "NewException(etNormal)" to get an IMEException interface for the "current" crash.
Re: Log exceptions
Posted: Tue May 07, 2019 8:25 am
by Berocoder
madshi wrote:The IMEException interface has a property called "Callstacks", see here:
http://help.madshi.net/madExceptUnit.ht ... Callstacks
The type TStackTrace is defined like this:
Code: Select all
type
TStackItem = record
Addr : pointer; // code address
relAddr : dword; // relative address to function entry point
ModuleName : UnicodeString;
UnitName : UnicodeString;
Line : integer; // line number (0 = unknown)
relLine : integer; // relative line number to function entry point
FunctionName : UnicodeString; // function/method name
end;
TStackTrace = array of TStackItem;
TPStackTrace = ^TStackTrace;
This should give you the "power" to format the callstacks whichever way you prefer.
Do you *have* to do all this in a try..except block? It would be easier to simply do all this in your registered exception handler, because then you already get an "exceptIntf" interface delivered to you. If you insist on needing to do this in a try..except block, you can call "NewException(etNormal)" to get an IMEException interface for the "current" crash.
Ok thanks for the answer. And how do I know how many Stackitems I have ?
I tried
but I got compiler error
[dcc32 Error] AttracsErrorMgr.pas(209): E2029 '[' expected but ')' found
And yes a global exceptionhandler is the prefered way of handling exceptions.
Unfortunately there are many calls to LogLastExceptions with E: Exception as parameter and additional information for just that case as a string.
But I think it could be replaced with
Code: Select all
try
// Exception may be raised
except
on E: Exception do
begin
E.Message := Format('%s Error in ID: %s', [E.Message, ObjectID]);
raise;
end;
end;
Then the global exceptionhandler take care of the rest.
Regards
Roland
Re: Log exceptions
Posted: Tue May 07, 2019 8:40 am
by madshi
There is no callstack "count" information available. You need to simply loop through "exceptIntf.ThreadIDs" until you get 0. And for each such ThreadID there's a matching callstack.
Re: Log exceptions
Posted: Tue May 07, 2019 9:30 am
by Berocoder
Ok have you some kind of demo how to access the callstack of the main thread ?
I still don't manage to get it...
Re: Log exceptions
Posted: Tue May 07, 2019 9:42 am
by madshi
Nope, no demo available. Something like this (not tested):
Code: Select all
i := 0;
while exceptIntf.ThreadIds[i] > 0 do
begin
doSomething(exceptIntf.Callstacks[i]);
inc(i);
end;
Re: Log exceptions
Posted: Tue May 07, 2019 11:40 am
by Berocoder
Actually everything works now as I want, except one thing.
I still don't understand how I can read amount of Stackitems in callstack...
This is my code so far
Code: Select all
// MadExcept exception handling
class procedure TATErrorManager.LogLastException(const exceptIntf: IMEException);
var
s, vActiveForm: string;
vCallStack: TStringBuilder;
vST: TStackTrace;
i, j: Integer;
vThreadID: Cardinal;
begin
if AfterInitAndBeforeFinalization then
begin
if Assigned(gGetActiveFormEvent) then
vActiveForm := gGetActiveFormEvent
else
vActiveForm := '';
vCallStack := TStringBuilder.Create;
try
if vActiveForm = '' then
vCallStack.AppendFormat('[EXCEPTION] %s %s%s', [exceptIntf.ExceptClass, exceptIntf.ExceptMessage, sLineBreak])
else
vCallStack.AppendFormat('[EXCEPTION] %s %s. Active form %s', [exceptIntf.ExceptClass, exceptIntf.ExceptMessage, vActiveForm, sLineBreak]);
vCallStack.AppendLine('');
vCallStack.AppendLine('Call Stack:');
i := 0;
while True do
begin
vThreadID := exceptIntf.ThreadIds[i];
if vThreadID = 0 then
break;
if vThreadID = GetCurrentThreadId then
begin
vST := exceptIntf.Callstacks[i];
for j := 0 to 10 do // Avoid hardcode count. Where can I stop this loop ?
begin
vCallStack.AppendFormat('%s[%d] %s (%s:%d)%s', [#9, j, vST[j].FunctionName,
vST[j].UnitName,
vST[j].Line,
sLineBreak]);
end;
break;
end;
inc(i);
end;
TraceLog.Trace(vCallStack.ToString, -100000);
finally
vCallSTack.Free;
end;
end;
end;
So the line I want to change
for j := 0 to 10 do
Btw it is not I that have the wallet at company but I will push for a change to MadExcept as exceptionhandler.
Thanks
Roland Bengtsson
Re: Log exceptions
Posted: Tue May 07, 2019 12:22 pm
by madshi
Length(exceptIntf.Callstacks) or High(). I know you think you already tried that, but you tried it on "exceptIntf.Callstacks" not on "exceptIntf.Callstacks".
Re: Log exceptions
Posted: Tue May 07, 2019 12:24 pm
by madshi
P.S: Also, if you want the callstack of the crashing thread, don't check for GetCurrentThreadId, instead look for exceptIntf.CrashedThreadId, because your global exception handler might not run in the context of the crashing thread, so GetCurrentThreadId is not guaranteed to be the thread ID you're looking for.
Or, to make it easier, just use index 0, which is always the crashing thread...
Re: Log exceptions
Posted: Tue May 07, 2019 12:50 pm
by Berocoder
Of course
Thanks for the help!
Re: Log exceptions
Posted: Thu May 09, 2019 6:07 am
by Berocoder
One more question, how can I get the callstack of main thread without raise exception and let generic exceptionhandler be called ?
And with my own custom formatting of callstack mentioned earlier.
GetCrashStackTrace is preformatted as a string.
Reason is that sometimes I have this situation:
Code: Select all
for i := 0 to list.count - 1 do
begin
try
vObj := list[i];
// Do something with vObj that might raise exception
except
on E: Exception do
begin
LogLastException(E, 'Something bad happened with ' + vObj.ID); // This log callstack with JCL and objects ID.
end;
end;
end;
So idea is that even when an exception happen the loop is not aborted
/Roland
Re: Log exceptions
Posted: Thu May 09, 2019 7:09 am
by madshi
NewException(etNormal).
Re: Log exceptions
Posted: Thu May 09, 2019 8:28 am
by Berocoder
Thanks for the answer. The problem is that High(vException.Callstacks[0]) is -1.
The call is TATErrorManager.LogLastException(nil, 'Testing');
Code: Select all
class procedure TATErrorManager.LogLastException(const exceptIntf: IMEException; const MsgText: String);
var
vException: IMEException;
vMsg, vActiveForm: string;
vCallStack: TStringBuilder;
vST: TStackTrace;
i: Integer;
begin
if AfterInitAndBeforeFinalization then
begin
if Assigned(gGetActiveFormEvent) then
vActiveForm := gGetActiveFormEvent
else
vActiveForm := '';
if Assigned(exceptIntf) then
vException := exceptIntf
else
vException := NewException(etNormal);
vCallStack := TStringBuilder.Create;
try
if vMsg <> '' then
vMsg := Format('%s %s', [vException.ExceptMessage, Msgtext])
else
vMsg := vException.ExceptMessage;
if vActiveForm = '' then
vCallStack.AppendFormat('[EXCEPTION] %s %s%s', [vException.ExceptClass, vMsg, sLineBreak])
else
vCallStack.AppendFormat('[EXCEPTION] %s %s. Active form %s', [vException.ExceptClass, vMsg, vActiveForm, sLineBreak]);
vCallStack.AppendLine('');
vCallStack.AppendLine(cnCallStack);
vST := vException.Callstacks[0]; // Exception thread´always at 0
for i := 0 to High(vException.Callstacks[0]) do
vCallStack.AppendFormat('%s[%2d] %s (%s:%d)%s', [#9, i, vST[i].FunctionName,
vST[i].UnitName,
vST[i].Line,
sLineBreak]);
TraceLog.Trace(vCallStack.ToString, -100000);
finally
vCallSTack.Free;
end;
end;
end;
Re: Log exceptions
Posted: Thu May 09, 2019 8:45 am
by madshi
Hmmmm... Maybe it does the stack tracing in the background and doesn't wait for that to complete, I'm not sure right now. Try calling "exceptIntf.BugReport". It returns a string, you can ignore that. Calling that should force the exceptIntf to block until all stack tracing is complete.
Re: Log exceptions
Posted: Thu May 09, 2019 8:57 am
by Berocoder
Yes, you was correct. Thanks!