Sign in Join
Wiki copytemporary

Logging exceptions/ru: what changed

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

Title: Logging exceptions/ru

OldNewLine
1+## Вступление
2+
3+**Stacktrace** иногда называют **backtrace** или **call stack**. Это список снимков стека, помещенных в стек, содержащих адрес возврата и локальные переменные. Поэтому трассировка стека полезна для отслеживания пути выполнения вашей программы (после вызова процедур).
4+
5+Чтобы получить трассировку стека, компилятор сначала должен обратиться к сгенерированной отладочной информации с использованием ключей:
6+
7+- -g - генерировать отладочную информацию (в выходном формате по умолчанию для соответствующей платформы, который является dwarf2 на многих платформах)
8+- -gl - генерировать номера строк для отладочной информации
9+- -gs - генерировать отладочную информацию для устаревшего режима stabs; не используйте одновременно оба ключа -gs и -gw
10+- -gw - генерировать отладочную информацию dwarf2; не используйте одновременно оба ключа -gs и -gw
11+- -Xg - использовать внешний файл символов отладки
12+
13+### Модуль SysUtils
14+
15+Этот модуль содержит некоторые процедуры, полезные для отладки исключений.
16+
17+``` pascal
18+{ Процедуры обработки исключений }
19+function ExceptObject: TObject;
20+function ExceptAddr: Pointer;
21+function ExceptFrameCount: Longint;
22+function ExceptFrames: PPointer;
23+```
24+
25+### Модуль System
26+
27+Этот модуль содержит некоторые связанные со стеком процедуры:
28+
29+``` pascal
30+function SysBackTraceStr(Addr:Pointer): ShortString; // Адрес по умолчанию для преобразования строки, назначенный BackTraceStrFunc
31+procedure Dump_Stack(var f : text;bp:pointer); // Дамп стека в текстовый файл
32+procedure DumpExceptionBackTrace(var f:text); // Дамп backtrace в текстовый файл
33+```
34+
35+Процедурная переменная **BackTraceStrFunc** отвечает за преобразование адреса памяти в строковую отладочную информацию. Поведение по умолчанию реализовано с помощью **SysBackTraceStr**.
36+
37+#### Выводимая информация
38+
39+Если выбран вывод отладочной информации в виде строк (переключатель компилятора -gl), в программу автоматически включается модуль **lineinfo**. Этот модуль гарантирует, что отладчики/обработчики исключений смогут найти номера строк выполняющегося кода. Это может быть полезно, если вы не хотите развертывать получающийся исполняемый файл с полной отладочной информацией, но вам нужна полезная информация при возникновении ошибок.
40+
41+#### Stabs
42+
43+Если используется старый формат stabs (-gs), функция **BackTraceStrFunc** переназначается на **StabBackTraceStr**.
44+
45+#### DWARF
46+
47+Если выбран формат отладки dwarf (ключ компилятора -gw), в программу автоматически включается модуль **lnfodwrf**, а функция **BackTraceStrFunc** преобразуется в **DwarfBacktraceStr**.
48+
49+### Модуль LCLProc
50+
51+Этот модуль Lazarus имеет некоторые функции, связанные с отладкой:
52+
53+``` pascal
54+//Отладка
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;
62+ UseCache: boolean): string;
63+function GetLineInfo(Addr: Pointer; UseCache: boolean): string;
64+```
65+
66+## Сброс текущего стека вызовов
67+
68+См. также справку FPC \[для получения\] подробностей по сбросу стека/исключений:
69+
70+- [DumpExceptionBackTrace](http://www.freepascal.org/docs-html/rtl/system/dumpexceptionbacktrace.html)
71+- [dump_stack](http://www.freepascal.org/docs-html/rtl/system/dump_stack.html)
72+
73+``` pascal
74+procedure DumpCallStack;
75+var
76+ I: Longint;
77+ prevbp: Pointer;
78+ CallerFrame,
79+ CallerAddress,
80+ bp: Pointer;
81+ Report: string;
82+const
83+ MaxDepth = 20;
84+begin
85+ Report := '';
86+ bp := get_frame;
87+ // Этот трюк пропустит элемент SendCallstack
88+ // bp:= get_caller_frame(get_frame);
89+ try
90+ prevbp := bp - 1;
91+ I := 0;
92+ while bp > prevbp do begin
93+ CallerAddress := get_caller_addr(bp);
94+ CallerFrame := get_caller_frame(bp);
95+ if (CallerAddress = nil) then
96+ Break;
97+ Report := Report + BackTraceStrFunc(CallerAddress) + LineEnding;
98+ Inc(I);
99+ if (I >= MaxDepth) or (CallerFrame = nil) then
100+ Break;
101+ prevbp := bp;
102+ bp := CallerFrame;
103+ end;
104+ except
105+ { предотвратить бесконечный сброс, если произошло исключение}
106+ end;
107+ ShowMessage(Report);
108+end;
109+```
110+
111+## Сброс исключений стека вызовов
112+
113+Стек вызовов исключения может быть получен через функции модуля SysUtils **ExceptAddr**, **ExceptFrames** и **ExceptFrameCount**.
114+
115+``` pascal
116+uses SysUtils;
117+
118+procedure DumpExceptionCallStack(E: Exception);
119+var
120+ I: Integer;
121+ Frames: PPointer;
122+ Report: string;
123+begin
124+ Report := 'Program exception! ' + LineEnding +
125+ 'Stacktrace:' + LineEnding + LineEnding;
126+ if E <> nil then begin
127+ Report := Report + 'Exception class: ' + E.ClassName + LineEnding +
128+ 'Message: ' + E.Message + LineEnding;
129+ end;
130+ Report := Report + BackTraceStrFunc(ExceptAddr);
131+ Frames := ExceptFrames;
132+ for I := 0 to ExceptFrameCount - 1 do
133+ Report := Report + LineEnding + BackTraceStrFunc(Frames[I]);
134+ ShowMessage(Report);
135+ Halt; // Конец выполнения программы
136+end;
137+```
138+
139+## Обработка исключений
140+
141+### Ручная обработка исключений
142+
143+Ручное выполнение обработчика исключений может быть вставлено во многие места в коде.
144+
145+``` pascal
146+try
147+ // ... какая-то операция, которая должна вызвать исключение...
148+ raise Exception.Create('Test error');
149+except
150+ on E: Exception do
151+ DumpExceptionCallStack(E);
152+end;
153+```
154+
155+### ExceptProc из модуля System
156+
157+Если происходит необработанное исключение, выполняется переменная процедуры ExceptProc. Поведение по умолчанию инициализируется процедурой **InitExceptions** из модуля **System**. Если вы хотите, эту процедуру можно переназначить на собственный обработчик.
158+
159+Пример:
160+
161+``` pascal
162+procedure CatchUnhandledException(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer);
163+var
164+ Message: string;
165+ i: LongInt;
166+ hstdout: ^Text;
167+begin
168+ hstdout := @stdout;
169+ Writeln(hstdout^, 'An unhandled exception occurred at $', HexStr(PtrUInt(Addr), SizeOf(PtrUInt) * 2), ' :');
170+ if Obj is exception then
171+ begin
172+ Message := Exception(Obj).ClassName + ' : ' + Exception(Obj).Message;
173+ Writeln(hstdout^, Message);
174+ end
175+ else
176+ Writeln(hstdout^, 'Exception object ', Obj.ClassName, ' is not of class Exception.');
177+ Writeln(hstdout^, BackTraceStrFunc(Addr));
178+ if (FrameCount > 0) then
179+ begin
180+ for i := 0 to FrameCount - 1 do
181+ Writeln(hstdout^, BackTraceStrFunc(Frames[i]));
182+ end;
183+ Writeln(hstdout^,'');
184+end;
185+```
186+
187+### Событие TApplication.OnException
188+
189+Это событие можно использовать для перезаписи глобального обработчика исключений приложения по умолчанию. Пользовательский механизм ведения журнала может обеспечивать показ настраиваемого диалога, запись в файл, консоль, отправку отчета на почту, ведение журнала на HTTP-сервер, например,
190+
191+``` pascal
192+procedure TMainForm.CustomExceptionHandler(Sender: TObject; E: Exception);
193+begin
194+ DumpExceptionCallStack;
195+ Halt; // Конец выполнения программы
196+end;
197+
198+procedure TMainForm.FormCreate(Sender: TObject);
199+begin
200+ Application.OnException := @CustomExceptionHandler;
201+end;
202+
203+procedure TMainForm.ButtonClick(Sender: TObject);
204+begin
205+ raise Exception.Create('Test');
206+end;
207+```
208+
209+### Обработка исключений доп.потока
210+
211+Обработка исключений, возникающих в потоках, должна выполняться вручную. Метод основного потока TThread **Execute** вызывается из функции **ThreadProc**, расположенной в модуле **Classes**.
212+
213+``` pascal
214+function ThreadProc(ThreadObjPtr: Pointer): PtrInt;
215+begin
216+ ...
217+ try
218+ Thread.Execute;
219+ except
220+ Thread.FFatalException := TObject(AcquireExceptionObject);
221+ end;
222+ ...
223+end;
224+```
225+
226+В этой функции метод Execute заключен в блок try-except, и все исключения обрабатываются присвоением объекта исключения свойству **FatalException** объекта TThread как объекта последнего возникшего исключения. Таким образом, исключения не отображаются для пользователя вообще.
227+
228+В каждом потоке приложения должен быть вставлен отдельный блок try-except, чтобы перехватить все необработанные исключения с помощью специального обработчика исключений.
229+
230+``` pascal
231+procedure TMyThread.Execute;
232+begin
233+ try
234+ // какой-нибудь ошибочный код
235+ except
236+ on E: Exception do
237+ CustomExceptionThreadHandler(Self, E);
238+ end;
239+end;
240+```
241+
242+Затем **CustomExceptionThreadHandler** может получить стек вызовов исключений и управлять отображением сообщений об ошибках или протоколированием отчетов журнала в файл. Поскольку обработчик выполняется из потока, показ диалога сообщений должен быть безопасным для потока с использованием метода **Synchronize**.
243+
244+``` pascal
245+procedure TMainForm.CustomExceptionHandler(Thread: TThread; E: Exception);
246+begin
247+ Thread.Synchronize(DumpExceptionCallStack);
248+end;
249+```
250+
251+### Использование map-файла
252+
253+Используйте ключ компилятора -Xm для генерации map-файла.
254+
255+### Исключения в DLL
256+
257+todo
258+
259+## См.также
260+
261+- [[Profiling]]
262+- [[Creating a Backtrace with GDB]]
263+- [[MultiLog]] - протоколирующий пакет
264+- [[log4delphi]] - протоколирующий пакет
265+
266+## Внешние ссылки
267+
268+- [esprinter](http://www.ciuly.com/tools/programming/esprinter/index.html) - инструмент для получения трассировки стека из запущенной программы FreePascal, заданной идентификатором процесса и идентификатором потока (для Win32)
269+- [PascalBugReports](https://github.com/r3code/pascalbugreports) - проект, имитирующий EurekaLog / MadExcept, но долгое время не разрабатывался
270+
271+---
272+*From the Free Pascal wiki, page "Logging exceptions/ru", as it stood on 2020-01-20 after a change by Trev, 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).*
273+Category: Debugging/ru
274+Category: FPC/ru
275+Category: Lazarus/ru
276+Category: Russian

Back to history