Difference between revisions of "Logging exceptions/ru"

From Lazarus wiki
Jump to navigationJump to search
m (Fixed template loop and categories)
 
(6 intermediate revisions by one other user not shown)
Line 1: Line 1:
{{MenuTranslate| page=Logging exceptions}}
+
{{LanguageBar}}
  
  
Line 56: Line 56:
 
function GetLineInfo(Addr: Pointer; UseCache: boolean): string;</syntaxhighlight>
 
function GetLineInfo(Addr: Pointer; UseCache: boolean): string;</syntaxhighlight>
  
==Дамп текущего стека вызовов==
+
==Сброс текущего стека вызовов==
See also FPC help on dumping stack/exception details:
 
 
См. также справку FPC [для получения] подробностей по сбросу стека/исключений:
 
См. также справку FPC [для получения] подробностей по сбросу стека/исключений:
 
* [http://www.freepascal.org/docs-html/rtl/system/dumpexceptionbacktrace.html DumpExceptionBackTrace]
 
* [http://www.freepascal.org/docs-html/rtl/system/dumpexceptionbacktrace.html DumpExceptionBackTrace]
Line 75: Line 74:
 
   Report := '';
 
   Report := '';
 
   bp := get_frame;
 
   bp := get_frame;
   // Этот трюк пропустить элемент SendCallstack
+
   // Этот трюк пропустит элемент SendCallstack
 
   // bp:= get_caller_frame(get_frame);
 
   // bp:= get_caller_frame(get_frame);
 
   try
 
   try
Line 98: Line 97:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
==Dump exception call stack==
+
==Сброс исключений стека вызовов==
  
The call stack of an exception can be obtained through SysUtils functions '''ExceptAddr''', '''ExceptFrames''' and '''ExceptFrameCount'''.
+
Стек вызовов исключения может быть получен через функции модуля SysUtils '''ExceptAddr''', '''ExceptFrames''' и '''ExceptFrameCount'''.
  
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
uses SysUtils;
 
uses SysUtils;
  
Line 122: Line 121:
 
     Report := Report + LineEnding + BackTraceStrFunc(Frames[I]);
 
     Report := Report + LineEnding + BackTraceStrFunc(Frames[I]);
 
   ShowMessage(Report);
 
   ShowMessage(Report);
   Halt; // End of program execution
+
   Halt; // Конец выполнения программы
 
end;
 
end;
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Handling exceptions==
+
==Обработка исключений==
  
===Manual exception handling===
+
===Ручная обработка исключений===
  
Manual executions of exception handler can be inserted in many places in code.
+
Ручное выполнение обработчика исключений может быть вставлено во многие места в коде.
  
<syntaxhighlight>
+
<syntaxhighlight lang="pascal">
 
try
 
try
   // ... some operation which should raise an exception...
+
   // ... какая-то операция, которая должна вызвать исключение...
 
   raise Exception.Create('Test error');
 
   raise Exception.Create('Test error');
 
except
 
except
Line 142: Line 141:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
===System ExceptProc===
+
===ExceptProc из модуля System===
  
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.
+
Если происходит необработанное исключение, выполняется переменная процедуры ExceptProc. Поведение по умолчанию инициализируется процедурой '''InitExceptions''' из модуля '''System'''. Если вы хотите, эту процедуру можно переназначить на собственный обработчик.
  
An example:
+
Пример:
<syntaxhighlight>procedure CatchUnhandledException(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer);
+
<syntaxhighlight lang="pascal">procedure CatchUnhandledException(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer);
 
var
 
var
 
   Message: string;
 
   Message: string;
Line 171: Line 170:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
===TApplication.OnException===
+
===Событие TApplication.OnException===
  
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.
+
Это событие можно использовать для перезаписи глобального обработчика исключений приложения по умолчанию. Пользовательский механизм ведения журнала может обеспечивать показ настраиваемого диалога, запись в файл, консоль, отправку отчета на почту, ведение журнала на HTTP-сервер, например,
  
<syntaxhighlight>procedure TMainForm.CustomExceptionHandler(Sender: TObject; E: Exception);
+
<syntaxhighlight lang="pascal">procedure TMainForm.CustomExceptionHandler(Sender: TObject; E: Exception);
 
begin
 
begin
 
   DumpExceptionCallStack;
 
   DumpExceptionCallStack;
   Halt; // End of program execution
+
   Halt; // Конец выполнения программы
 
end;   
 
end;   
  
Line 191: Line 190:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
===Handling thread exceptions===
+
===Обработка исключений доп.потока===
  
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'''.  
+
Обработка исключений, возникающих в потоках, должна выполняться вручную. Метод основного потока TThread '''Execute''' вызывается из функции '''ThreadProc''', расположенной в модуле '''Classes'''.
<syntaxhighlight>function ThreadProc(ThreadObjPtr: Pointer): PtrInt;
+
<syntaxhighlight lang="pascal">function ThreadProc(ThreadObjPtr: Pointer): PtrInt;
 
begin
 
begin
 
   ...
 
   ...
Line 205: Line 204:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
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.
+
В этой функции метод Execute заключен в блок try-except, и все исключения обрабатываются присвоением объекта исключения свойству '''FatalException''' объекта TThread как объекта последнего возникшего исключения. Таким образом, исключения не отображаются для пользователя вообще.
  
In every thread in the application, a separate try-except block should be inserted to catch all unhandled exceptions by a custom exception handler.
+
В каждом потоке приложения должен быть вставлен отдельный блок try-except, чтобы перехватить все необработанные исключения с помощью специального обработчика исключений.
  
<syntaxhighlight>procedure TMyThread.Execute;
+
<syntaxhighlight lang="pascal">procedure TMyThread.Execute;
 
begin
 
begin
 
   try
 
   try
     // some erroneous code
+
     // какой-нибудь ошибочный код
 
   except
 
   except
 
     on E: Exception do  
 
     on E: Exception do  
Line 219: Line 218:
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
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.
+
Затем '''CustomExceptionThreadHandler''' может получить стек вызовов исключений и управлять отображением сообщений об ошибках или протоколированием отчетов журнала в файл. Поскольку обработчик выполняется из потока, показ диалога сообщений должен быть безопасным для потока с использованием метода '''Synchronize'''.
  
<syntaxhighlight>procedure TMainForm.CustomExceptionHandler(Thread: TThread; E: Exception);
+
<syntaxhighlight lang="pascal">procedure TMainForm.CustomExceptionHandler(Thread: TThread; E: Exception);
 
begin
 
begin
 
   Thread.Synchronize(DumpExceptionCallStack);
 
   Thread.Synchronize(DumpExceptionCallStack);
 
end;</syntaxhighlight>
 
end;</syntaxhighlight>
  
===Using map file===
+
===Использование map-файла===
  
Use compiler switch -Xm to generate map file.
+
Используйте ключ компилятора -Xm для генерации map-файла.
  
===Exceptions in DLL===
+
===Исключения в DLL===
  
 
todo
 
todo
  
==See also==
+
==См.также==
  
 
* [[Profiling]]
 
* [[Profiling]]
 
* [[Creating a Backtrace with GDB]]
 
* [[Creating a Backtrace with GDB]]
* [[MultiLog]] - A logging package
+
* [[MultiLog]] - протоколирующий пакет
* [[log4delphi]] - A logging package
+
* [[log4delphi]] - протоколирующий пакет
  
==External links==
+
==Внешние ссылки==
  
* [http://www.ciuly.com/tools/programming/esprinter/index.html esprinter] - tool to get stacktrace from running FreePascal program specified by process id and thread id (for Win32)
+
* [http://www.ciuly.com/tools/programming/esprinter/index.html esprinter] - инструмент для получения трассировки стека из запущенной программы FreePascal, заданной идентификатором процесса и идентификатором потока (для Win32)
* [https://github.com/r3code/pascalbugreports PascalBugReports] - project intended to mimic EurekaLog/MadExcept but has not been worked on for a while
+
* [https://github.com/r3code/pascalbugreports PascalBugReports] - проект, имитирующий EurekaLog / MadExcept, но долгое время не разрабатывался
  
  
[[Category:Debugging]]
+
[[Category:Debugging/ru]]
[[Category:FPC]]
+
[[Category:FPC/ru]]
[[Category:Lazarus]]
+
[[Category:Lazarus/ru]]
 +
{{AutoCategory}}

Latest revision as of 08:19, 20 January 2020

English (en) русский (ru)


Вступление

Stacktrace иногда называют backtrace или call stack. Это список снимков стека, помещенных в стек, содержащих адрес возврата и локальные переменные. Поэтому трассировка стека полезна для отслеживания пути выполнения вашей программы (после вызова процедур).

Чтобы получить трассировку стека, компилятор сначала должен обратиться к сгенерированной отладочной информации с использованием ключей:

  • -g - генерировать отладочную информацию (в выходном формате по умолчанию для соответствующей платформы, который является dwarf2 на многих платформах)
  • -gl - генерировать номера строк для отладочной информации
  • -gs - генерировать отладочную информацию для устаревшего режима stabs; не используйте одновременно оба ключа -gs и -gw
  • -gw - генерировать отладочную информацию dwarf2; не используйте одновременно оба ключа -gs и -gw
  • -Xg - использовать внешний файл символов отладки


Модуль SysUtils

Этот модуль содержит некоторые процедуры, полезные для отладки исключений.

{ Процедуры обработки исключений }
function ExceptObject: TObject;
function ExceptAddr: Pointer;
function ExceptFrameCount: Longint;
function ExceptFrames: PPointer;

Модуль System

Этот модуль содержит некоторые связанные со стеком процедуры:

function SysBackTraceStr(Addr:Pointer): ShortString; // Адрес по умолчанию для преобразования строки, назначенный BackTraceStrFunc
procedure Dump_Stack(var f : text;bp:pointer); // Дамп стека в текстовый файл
procedure DumpExceptionBackTrace(var f:text); // Дамп backtrace в текстовый файл

Процедурная переменная BackTraceStrFunc отвечает за преобразование адреса памяти в строковую отладочную информацию. Поведение по умолчанию реализовано с помощью SysBackTraceStr.

Выводимая информация

Если выбран вывод отладочной информации в виде строк (переключатель компилятора -gl), в программу автоматически включается модуль lineinfo. Этот модуль гарантирует, что отладчики/обработчики исключений смогут найти номера строк выполняющегося кода. Это может быть полезно, если вы не хотите развертывать получающийся исполняемый файл с полной отладочной информацией, но вам нужна полезная информация при возникновении ошибок.

Stabs

Если используется старый формат stabs (-gs), функция BackTraceStrFunc переназначается на StabBackTraceStr.

DWARF

Если выбран формат отладки dwarf (ключ компилятора -gw), в программу автоматически включается модуль lnfodwrf, а функция BackTraceStrFunc преобразуется в DwarfBacktraceStr.

Модуль LCLProc

Этот модуль Lazarus имеет некоторые функции, связанные с отладкой:

//Отладка
procedure RaiseGDBException(const Msg: string);
procedure RaiseAndCatchException;
procedure DumpExceptionBackTrace;
procedure DumpStack;
function GetStackTrace(UseCache: boolean): string;
procedure GetStackTracePointers(var AStack: TStackTracePointers);
function StackTraceAsString(const AStack: TStackTracePointers;
                            UseCache: boolean): string;
function GetLineInfo(Addr: Pointer; UseCache: boolean): string;

Сброс текущего стека вызовов

См. также справку FPC [для получения] подробностей по сбросу стека/исключений:

procedure DumpCallStack;
var
  I: Longint;
  prevbp: Pointer;
  CallerFrame,
  CallerAddress,
  bp: Pointer;
  Report: string;
const
  MaxDepth = 20;
begin
  Report := '';
  bp := get_frame;
  // Этот трюк пропустит элемент SendCallstack
  // bp:= get_caller_frame(get_frame);
  try
    prevbp := bp - 1;
    I := 0;
    while bp > prevbp do begin
       CallerAddress := get_caller_addr(bp);
       CallerFrame := get_caller_frame(bp);
       if (CallerAddress = nil) then
         Break;
       Report := Report + BackTraceStrFunc(CallerAddress) + LineEnding;
       Inc(I);
       if (I >= MaxDepth) or (CallerFrame = nil) then
         Break;
       prevbp := bp;
       bp := CallerFrame;
     end;
   except
     { предотвратить бесконечный сброс, если произошло исключение}
   end;
  ShowMessage(Report);
end;

Сброс исключений стека вызовов

Стек вызовов исключения может быть получен через функции модуля SysUtils ExceptAddr, ExceptFrames и ExceptFrameCount.

uses SysUtils;

procedure DumpExceptionCallStack(E: Exception);
var
  I: Integer;
  Frames: PPointer;
  Report: string;
begin
  Report := 'Program exception! ' + LineEnding +
    'Stacktrace:' + LineEnding + LineEnding;
  if E <> nil then begin
    Report := Report + 'Exception class: ' + E.ClassName + LineEnding +
    'Message: ' + E.Message + LineEnding;
  end;
  Report := Report + BackTraceStrFunc(ExceptAddr);
  Frames := ExceptFrames;
  for I := 0 to ExceptFrameCount - 1 do
    Report := Report + LineEnding + BackTraceStrFunc(Frames[I]);
  ShowMessage(Report);
  Halt; // Конец выполнения программы
end;

Обработка исключений

Ручная обработка исключений

Ручное выполнение обработчика исключений может быть вставлено во многие места в коде.

try
  // ... какая-то операция, которая должна вызвать исключение...
  raise Exception.Create('Test error');
except
  on E: Exception do 
    DumpExceptionCallStack(E);
end;

ExceptProc из модуля System

Если происходит необработанное исключение, выполняется переменная процедуры ExceptProc. Поведение по умолчанию инициализируется процедурой InitExceptions из модуля System. Если вы хотите, эту процедуру можно переназначить на собственный обработчик.

Пример:

procedure CatchUnhandledException(Obj: TObject; Addr: Pointer; FrameCount: Longint; Frames: PPointer);
var
  Message: string;
  i: LongInt;
  hstdout: ^Text;
begin
  hstdout := @stdout;
  Writeln(hstdout^, 'An unhandled exception occurred at $', HexStr(PtrUInt(Addr), SizeOf(PtrUInt) * 2), ' :');
  if Obj is exception then
   begin
     Message := Exception(Obj).ClassName + ' : ' + Exception(Obj).Message;
     Writeln(hstdout^, Message);
   end
  else
    Writeln(hstdout^, 'Exception object ', Obj.ClassName, ' is not of class Exception.');
  Writeln(hstdout^, BackTraceStrFunc(Addr));
  if (FrameCount > 0) then
    begin
      for i := 0 to FrameCount - 1 do
        Writeln(hstdout^, BackTraceStrFunc(Frames[i]));
    end;
  Writeln(hstdout^,'');
end;

Событие TApplication.OnException

Это событие можно использовать для перезаписи глобального обработчика исключений приложения по умолчанию. Пользовательский механизм ведения журнала может обеспечивать показ настраиваемого диалога, запись в файл, консоль, отправку отчета на почту, ведение журнала на HTTP-сервер, например,

procedure TMainForm.CustomExceptionHandler(Sender: TObject; E: Exception);
begin
  DumpExceptionCallStack;
  Halt; // Конец выполнения программы
end;   

procedure TMainForm.FormCreate(Sender: TObject);
begin
  Application.OnException := @CustomExceptionHandler;
end;

procedure TMainForm.ButtonClick(Sender: TObject);
begin
  raise Exception.Create('Test');
end;

Обработка исключений доп.потока

Обработка исключений, возникающих в потоках, должна выполняться вручную. Метод основного потока TThread Execute вызывается из функции ThreadProc, расположенной в модуле Classes.

function ThreadProc(ThreadObjPtr: Pointer): PtrInt;
begin
  ...
  try
    Thread.Execute;
  except
    Thread.FFatalException := TObject(AcquireExceptionObject);
  end; 
  ...
end;

В этой функции метод Execute заключен в блок try-except, и все исключения обрабатываются присвоением объекта исключения свойству FatalException объекта TThread как объекта последнего возникшего исключения. Таким образом, исключения не отображаются для пользователя вообще.

В каждом потоке приложения должен быть вставлен отдельный блок try-except, чтобы перехватить все необработанные исключения с помощью специального обработчика исключений.

procedure TMyThread.Execute;
begin
  try
    // какой-нибудь ошибочный код
  except
    on E: Exception do 
      CustomExceptionThreadHandler(Self, E);
  end;
end;

Затем CustomExceptionThreadHandler может получить стек вызовов исключений и управлять отображением сообщений об ошибках или протоколированием отчетов журнала в файл. Поскольку обработчик выполняется из потока, показ диалога сообщений должен быть безопасным для потока с использованием метода Synchronize.

procedure TMainForm.CustomExceptionHandler(Thread: TThread; E: Exception);
begin
  Thread.Synchronize(DumpExceptionCallStack);
end;

Использование map-файла

Используйте ключ компилятора -Xm для генерации map-файла.

Исключения в DLL

todo

См.также

Внешние ссылки

  • esprinter - инструмент для получения трассировки стека из запущенной программы FreePascal, заданной идентификатором процесса и идентификатором потока (для Win32)
  • PascalBugReports - проект, имитирующий EurekaLog / MadExcept, но долгое время не разрабатывался