Sign in Join
Wiki copytemporary

Logging exceptions: what changed

Revision 1 to 2. Removed lines have a minus sign; added lines have a plus sign.

OldNewLine
11## Introduction
22
3-A **stacktrace** is sometimes called **backtrace** or **call stack**. This is a list of stack frames placed on the stack containing return address and local variables. A stacktrace is therefore useful to trace back the path of execution of your program (following the calls to procedures).
3+A **stacktrace** is sometimes called **backtrace** or **call stack**. This is a list of stack frames placed on the stack containing return address and local variables. A stacktrace is therefore useful to trace back the path of execution of your program (following the calls to procedures).
44
5-In order to obtain a stacktrace, the compiler first has to be directed to generate debug information using switches:
5+In order to obtain a stacktrace, the compiler first has to be directed to generate debug information using switches:
66
7-- -g - generate debug information (in the default output format for the platform concerned, which is dwarf2 on a lot of platforms)
8-- -gl - generate line numbers for debug information
9-- -gs - generate older stabs debug information; don't use both -gs and -gw
10-- -gw - generate dwarf2 debug information; don't use both -gs and -gw
11-- -Xg - use external debug symbol file
7+ * -g - generate debug information (in the default output format for the platform concerned, which is dwarf2 on a lot of platforms)
8+ * -gl - generate line numbers for debug information
9+ * -gs - generate older stabs debug information; don't use both -gs and -gw
10+ * -gw - generate dwarf2 debug information; don't use both -gs and -gw
11+ * -Xg - use external debug symbol file
1212
13-#### SysUtils unit
13+
1414
15-[SysUtils](https://www.freepascal.org/docs-html/rtl/sysutils/exception.html) contains some routines useful for debugging exceptions.
15+#### SysUtils unit
1616
17-``` pascal
17+[SysUtils](<https://www.freepascal.org/docs-html/rtl/sysutils/exception.html>) contains some routines useful for debugging exceptions.
18+```
1819{ Exception handling routines }
19-function ExceptObject: TObject;
20-function ExceptAddr: Pointer;
21-function ExceptFrameCount: Longint;
22-function ExceptFrames: PPointer;
20+ function ExceptObject: TObject;
21+ function ExceptAddr: Pointer;
22+ function ExceptFrameCount: Longint;
23+ function ExceptFrames: PPointer;
2324```
2425
2526#### System unit
2627
27-The system unit contains some stack related routines:
28-
29-``` pascal
28+The system unit contains some stack related routines:
29+```
3030function SysBackTraceStr(Addr:Pointer): ShortString; // Default address to string converter assigned to BackTraceStrFunc
31-procedure Dump_Stack(var f : text;bp:pointer); // Dump stack to text file
32-procedure DumpExceptionBackTrace(var f:text); // Dump backtrace to text file
31+ procedure Dump_Stack(var f : text;bp:pointer); // Dump stack to text file
32+ procedure DumpExceptionBackTrace(var f:text); // Dump backtrace to text file
3333```
3434
35-Procedural variable **BackTraceStrFunc** responsible to translate memory address to string debug information. Default behavior is implemented by **SysBackTraceStr**.
35+Procedural variable **BackTraceStrFunc** responsible to translate memory address to string debug information. Default behavior is implemented by **SysBackTraceStr**.
3636
3737#### Line information
3838
39-If the line info debug output is selected (compiler switch -gl), the unit **lineinfo** is automatically included in the program. This unit makes sure that debuggers/exception handlers can find the line numbers of running code. It can be useful if you do not want to deploy a production executable with full debug info, but you do want useful information when errors occur.
39+If the line info debug output is selected (compiler switch -gl), the unit **lineinfo** is automatically included in the program. This unit makes sure that debuggers/exception handlers can find the line numbers of running code. It can be useful if you do not want to deploy a production executable with full debug info, but you do want useful information when errors occur.
4040
4141#### Stabs
4242
43-If the old stabs format (-gs) is used, the **BackTraceStrFunc** function is remapped to **StabBackTraceStr**.
43+If the old stabs format (-gs) is used, the **BackTraceStrFunc** function is remapped to **StabBackTraceStr**.
4444
4545#### DWARF
4646
47-If the dwarf debugging format is selected (compiler switch -gw), the unit **lnfodwrf** is automatically included in the program and **BackTraceStrFunc** function is remapped to **DwarfBacktraceStr**.
47+If the dwarf debugging format is selected (compiler switch -gw), the unit **lnfodwrf** is automatically included in the program and **BackTraceStrFunc** function is remapped to **DwarfBacktraceStr**.
4848
4949### Unit LCLProc
5050
51-This Lazarus unit has some debug-related functions:
52-
53-``` pascal
51+This Lazarus unit has some debug-related functions:
52+```
5453// Debugging
55-procedure RaiseGDBException(const Msg: string);
56-procedure RaiseAndCatchException;
57-procedure DumpExceptionBackTrace;
58-procedure DumpStack;
59-function GetStackTrace(UseCache: boolean): string;
60-procedure GetStackTracePointers(var AStack: TStackTracePointers);
61-function StackTraceAsString(const AStack: TStackTracePointers; UseCache: boolean): string;
62-function GetLineInfo(Addr: Pointer; UseCache: boolean): string;
54+ procedure RaiseGDBException(const Msg: string);
55+ procedure RaiseAndCatchException;
56+ procedure DumpExceptionBackTrace;
57+ procedure DumpStack;
58+ function GetStackTrace(UseCache: boolean): string;
59+ procedure GetStackTracePointers(var AStack: TStackTracePointers);
60+ function StackTraceAsString(const AStack: TStackTracePointers; UseCache: boolean): string;
61+ function GetLineInfo(Addr: Pointer; UseCache: boolean): string;
6362```
6463
6564## Dump current call stack
6665
67-See also FPC help on dumping stack/exception details:
66+See also FPC help on dumping stack/exception details:
6867
69-- [DumpExceptionBackTrace](http://www.freepascal.org/docs-html/rtl/system/dumpexceptionbacktrace.html)
70-- [dump_stack](http://www.freepascal.org/docs-html/rtl/system/dump_stack.html)
68+ * [DumpExceptionBackTrace](<http://www.freepascal.org/docs-html/rtl/system/dumpexceptionbacktrace.html>)
69+ * [dump_stack](<http://www.freepascal.org/docs-html/rtl/system/dump_stack.html>)
7170
72-``` pascal
71+```
7372procedure DumpCallStack;
74-var
75- I: Longint;
76- prevbp: Pointer;
77- CallerFrame,
78- CallerAddress,
79- bp: Pointer;
80- Report: string;
81-const
82- MaxDepth = 20;
83-begin
84- Report := '';
85- bp := get_frame;
86- // This trick skip SendCallstack item
87- // bp:= get_caller_frame(get_frame);
88- try
89- prevbp := bp - 1;
90- I := 0;
91- while bp > prevbp do begin
92- CallerAddress := get_caller_addr(bp);
93- CallerFrame := get_caller_frame(bp);
94- if (CallerAddress = nil) then
95- Break;
96- Report := Report + BackTraceStrFunc(CallerAddress) + LineEnding;
97- Inc(I);
98- if (I >= MaxDepth) or (CallerFrame = nil) then
99- Break;
100- prevbp := bp;
101- bp := CallerFrame;
102- end;
103- except
104- { prevent endless dump if an exception occured }
105- end;
106- ShowMessage(Report);
107-end;
73+ var
74+ I: Longint;
75+ prevbp: Pointer;
76+ CallerFrame,
77+ CallerAddress,
78+ bp: Pointer;
79+ Report: string;
80+ const
81+ MaxDepth = 20;
82+ begin
83+ Report := '';
84+ bp := get_frame;
85+ // This trick skip SendCallstack item
86+ // bp:= get_caller_frame(get_frame);
87+ try
88+ prevbp := bp - 1;
89+ I := 0;
90+ while bp > prevbp do begin
91+ CallerAddress := get_caller_addr(bp);
92+ CallerFrame := get_caller_frame(bp);
93+ if (CallerAddress = nil) then
94+ Break;
95+ Report := Report + BackTraceStrFunc(CallerAddress) + LineEnding;
96+ Inc(I);
97+ if (I >= MaxDepth) or (CallerFrame = nil) then
98+ Break;
99+ prevbp := bp;
100+ bp := CallerFrame;
101+ end;
102+ except
103+ { prevent endless dump if an exception occured }
104+ end;
105+ ShowMessage(Report);
106+ end;
108107```
109108
110109## Dump exception call stack
111110
112-The call stack of an exception can be obtained through SysUtils functions **ExceptAddr**, **ExceptFrames** and **ExceptFrameCount**.
113-
114-``` pascal
111+The call stack of an exception can be obtained through SysUtils functions **ExceptAddr** , **ExceptFrames** and **ExceptFrameCount**.
112+```
115113uses SysUtils;
116-
117-procedure DumpExceptionCallStack(E: Exception);
118-var
119- I: Integer;
120- Frames: PPointer;
121- Report: string;
122-begin
123- Report := 'Program exception! ' + LineEnding +
124- 'Stacktrace:' + LineEnding + LineEnding;
125- if E <> nil then begin
126- Report := Report + 'Exception class: ' + E.ClassName + LineEnding +
127- 'Message: ' + E.Message + LineEnding;
128- end;
129- Report := Report + BackTraceStrFunc(ExceptAddr);
130- Frames := ExceptFrames;
131- for I := 0 to ExceptFrameCount - 1 do
132- Report := Report + LineEnding + BackTraceStrFunc(Frames[I]);
133- ShowMessage(Report);
134- Halt; // End of program execution
135-end;
114+
115+ procedure DumpExceptionCallStack(E: Exception);
116+ var
117+ I: Integer;
118+ Frames: PPointer;
119+ Report: string;
120+ begin
121+ Report := 'Program exception! ' + LineEnding +
122+ 'Stacktrace:' + LineEnding + LineEnding;
123+ if E <> nil then begin
124+ Report := Report + 'Exception class: ' + E.ClassName + LineEnding +
125+ 'Message: ' + E.Message + LineEnding;
126+ end;
127+ Report := Report + BackTraceStrFunc(ExceptAddr);
128+ Frames := ExceptFrames;
129+ for I := 0 to ExceptFrameCount - 1 do
130+ Report := Report + LineEnding + BackTraceStrFunc(Frames[I]);
131+ ShowMessage(Report);
132+ Halt; // End of program execution
133+ end;
136134```
137135
138136## Handling exceptions
139137
140138### Manual exception handling
141139
142-Manual executions of exception handler can be inserted in many places in code.
143-
144-``` pascal
140+Manual executions of exception handler can be inserted in many places in code.
141+```
145142try
146- // ... some operation which should raise an exception...
147- raise Exception.Create('Test error');
148-except
149- on E: Exception do
150- DumpExceptionCallStack(E);
151-end;
143+ // ... some operation which should raise an exception...
144+ raise Exception.Create('Test error');
145+ except
146+ on E: Exception do
147+ DumpExceptionCallStack(E);
148+ end;
152149```
153150
154151### System ExceptProc
155152
156-If an unhandled exception occurs, the ExceptProc procedure variable is executed. Default behaviour is initialized by procedure **InitExceptions** in unit **System**. If you want to, this procedure can be reassigned to a custom handler.
157-
158-An example:
153+If an unhandled exception occurs, the ExceptProc procedure variable is executed. Default behaviour is initialized by procedure **InitExceptions** in unit **System**. If you want to, this procedure can be reassigned to a custom handler.
159154
160-``` pascal
155+An example:
156+```
161157procedure CatchUnhandledException(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer);
162-var
163- Message: string;
164- i: LongInt;
165- hstdout: ^Text;
166-begin
167- hstdout := @stdout;
168- Writeln(hstdout^, 'An unhandled exception occurred at $', sysBackTraceStr(addr), ' :');
169- if Obj is exception then
170- begin
171- Message := Exception(Obj).ClassName + ' : ' + Exception(Obj).Message;
172- Writeln(hstdout^, Message);
173- end
174- else
175- Writeln(hstdout^, 'Exception object ', Obj.ClassName, ' is not of class Exception.');
176- Writeln(hstdout^, BackTraceStrFunc(Addr));
177- if (FrameCount > 0) then
158+ var
159+ Message: string;
160+ i: LongInt;
161+ hstdout: ^Text;
178162 begin
179- for i := 0 to FrameCount - 1 do
180- Writeln(hstdout^, BackTraceStrFunc(Frames[i]));
163+ hstdout := @stdout;
164+ Writeln(hstdout^, 'An unhandled exception occurred at $', sysBackTraceStr(addr), ' :');
165+ if Obj is exception then
166+ begin
167+ Message := Exception(Obj).ClassName + ' : ' + Exception(Obj).Message;
168+ Writeln(hstdout^, Message);
169+ end
170+ else
171+ Writeln(hstdout^, 'Exception object ', Obj.ClassName, ' is not of class Exception.');
172+ Writeln(hstdout^, BackTraceStrFunc(Addr));
173+ if (FrameCount > 0) then
174+ begin
175+ for i := 0 to FrameCount - 1 do
176+ Writeln(hstdout^, BackTraceStrFunc(Frames[i]));
177+ end;
178+ Writeln(hstdout^,'');
181179 end;
182- Writeln(hstdout^,'');
183-end;
184180```
185181
186182### TApplication.OnException
187183
188-This event can be used to override default application wide exceptions handling. A custom logging mechanism could provide show custom dialog, log to file, console, sending report to mail, logging to HTTP server, e.g.
189-
190-``` pascal
184+This event can be used to override default application wide exceptions handling. A custom logging mechanism could provide show custom dialog, log to file, console, sending report to mail, logging to HTTP server, e.g.
185+```
191186procedure TMainForm.CustomExceptionHandler(Sender: TObject; E: Exception);
192-begin
193- DumpExceptionCallStack;
194- Halt; // End of program execution
195-end;
196-
197-procedure TMainForm.FormCreate(Sender: TObject);
198-begin
199- Application.OnException := @CustomExceptionHandler;
200-end;
201-
202-procedure TMainForm.ButtonClick(Sender: TObject);
203-begin
204- raise Exception.Create('Test');
205-end;
187+ begin
188+ DumpExceptionCallStack;
189+ Halt; // End of program execution
190+ end;
191+
192+ procedure TMainForm.FormCreate(Sender: TObject);
193+ begin
194+ Application.OnException := @CustomExceptionHandler;
195+ end;
196+
197+ procedure TMainForm.ButtonClick(Sender: TObject);
198+ begin
199+ raise Exception.Create('Test');
200+ end;
206201```
207202
208203### Handling thread exceptions
209204
210-Handling exceptions which are raised in threads has to be done manually. The main thread TThread method **Execute** is called from the function **ThreadProc** located in unit **Classes**.
211-
212-``` pascal
205+Handling exceptions which are raised in threads has to be done manually. The main thread TThread method **Execute** is called from the function **ThreadProc** located in unit **Classes**.
206+```
213207function ThreadProc(ThreadObjPtr: Pointer): PtrInt;
214-begin
215- ...
216- try
217- Thread.Execute;
218- except
219- Thread.FFatalException := TObject(AcquireExceptionObject);
220- end;
221- ...
222-end;
208+ begin
209+ ...
210+ try
211+ Thread.Execute;
212+ except
213+ Thread.FFatalException := TObject(AcquireExceptionObject);
214+ end;
215+ ...
216+ end;
223217```
224218
225-In this function, the Execute method is enclosed in a try-except block and all exceptions are handled by assigning the exception object to the **FatalException** property of TThread object as last occurred exception object. So exceptions are not displayed to the user at all.
226-
227-In every thread in the application, a separate try-except block should be inserted to catch all unhandled exceptions by a custom exception handler.
219+In this function, the Execute method is enclosed in a try-except block and all exceptions are handled by assigning the exception object to the **FatalException** property of TThread object as last occurred exception object. So exceptions are not displayed to the user at all.
228220
229-``` pascal
221+In every thread in the application, a separate try-except block should be inserted to catch all unhandled exceptions by a custom exception handler.
222+```
230223procedure TMyThread.Execute;
231-begin
232- try
233- // some erroneous code
234- except
235- on E: Exception do
236- CustomExceptionThreadHandler(Self, E);
237- end;
238-end;
224+ begin
225+ try
226+ // some erroneous code
227+ except
228+ on E: Exception do
229+ CustomExceptionThreadHandler(Self, E);
230+ end;
231+ end;
239232```
240233
241-Then **CustomExceptionThreadHandler** can get the exception call stack and manage showing error messages or logging log reports to file. As the handler is executed from a thread, showing message dialog has to be done thread safe using the **Synchronize** method.
242-
243-``` pascal
234+Then **CustomExceptionThreadHandler** can get the exception call stack and manage showing error messages or logging log reports to file. As the handler is executed from a thread, showing message dialog has to be done thread safe using the **Synchronize** method.
235+```
244236procedure TMainForm.CustomExceptionHandler(Thread: TThread; E: Exception);
245-begin
246- Thread.Synchronize(DumpExceptionCallStack);
247-end;
237+ begin
238+ Thread.Synchronize(DumpExceptionCallStack);
239+ end;
248240```
249241
250242### Using map file
251243
252-Use compiler switch -Xm to generate map file.
244+Use compiler switch -Xm to generate map file.
253245
254246### Exceptions in DLL
255247
256-todo
248+todo
257249
258250## See also
259251
260-- [[Profiling]]
261-- [[Creating a Backtrace with GDB]]
262-- [[MultiLog]] - A logging package
263-- [[log4delphi]] - A logging package
252+ * [[Profiling]]
253+ * [[Creating a Backtrace with GDB]]
254+ * [[MultiLog]] \- A logging package
255+ * [[log4delphi]] \- A logging package
264256
265257## External links
266258
267-- [esprinter](http://www.ciuly.com/tools/programming/esprinter/index.html) - tool to get stacktrace from running FreePascal program specified by process id and thread id (for Win32)
268-- [PascalBugReports](https://github.com/r3code/pascalbugreports) - project intended to mimic EurekaLog/MadExcept but has not been worked on for a while
259+ * [esprinter](<http://www.ciuly.com/tools/programming/esprinter/index.html>) \- tool to get stacktrace from running FreePascal program specified by process id and thread id (for Win32)
260+ * [PascalBugReports](<https://github.com/r3code/pascalbugreports>) \- project intended to mimic EurekaLog/MadExcept but has not been worked on for a while
261+ * [Lazarus Exception Logger](<https://github.com/r3code/lazarus-exception-logger>) \- Extended version of ExceptionLogger
269262
270263---
271-*From the Free Pascal wiki, page "Logging exceptions", as it stood on 2020-11-14 after a change by Kirinn, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
272-Category: Debugging
273-Category: FPC
274-Category: Lazarus
264+*From the Free Pascal wiki, page "Logging exceptions", as the Internet Archive kept it on 2025-04-30, last changed on 2025-01-25. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*

Back to history