Страница 1 из 1

Трассировка стека под Linux

СообщениеДобавлено: 12.03.2010 12:20:27
*vmr
Всем доброго времени суток!

Есть готовый трассировщик, отлично работающий под Delphi.
Работать он может двумя методами — проходом по фреймам, и raw-трассировкой.
Используется на обработчике exception-ов, для выяснения где именно эти исключения происходят.

Проблема в том, что ни один из двух методов не работает в FPC+Linux.

1. При трассировке методом обхода стек-фреймов падает с общесистемной ошибкой (защита памяти?)
Код: Выделить всё
function FillCallStack(var ST: TCallStack; LevelsToExclude: integer = 5): integer;
var
  Count: integer;
  SkipCount: integer;

  StackMax: Pointer;
  StackStart: Pointer;

  CurrentFrame: Pointer;
begin
  FillChar(ST, SizeOf(ST), 0);

  asm
    mov EAX, FS:[4]  <<<<---- Тут sigsegv
    mov StackMax, EAX
    mov StackStart, EBP
  end;


Тут совсем не шарю. Каким образом FPC генерит стековые фреймы?

2. Трассировку raw-методом нельзя реализовать, поскольку для этого надо узнать диапазон
адресов в котором размещается программный код.
Если просто для Линукса что-то гуглится, то под FPC инфу не найти.
А если учесть, что я не линуксоид, то сия задача для меня становится крайне затруднительной.
Подскажите, в какую сторону гуглить?

Добавлено спустя 12 минут 10 секунд:
Нагуглил некую backtracestrfunc.
Я так понимаю, они использует первый метод.
И возвращает строку..... Есть ли способ ее отформатировать? Неужели парсить надо?

Забыл сказать, что нужны только адреса. Резолв этих адресов делается уже после — отдельной утилитой.
И получается, чтоб резолвить эти адреса под линуксом, нужно все-равно знать базовый адрес куда загрузчик размещает код

Re: Трассировка стека под Linux

СообщениеДобавлено: 12.03.2010 12:53:53
Sergei I. Gorelkin
Вообще говоря, при возникновении исключения его адрес можно получить ф-цией ExceptAddr...

Селектор fs как таковой специфичен для Windows, в Linux не используется. Для FPC справедливо многое из того, что относится к gcc. Обработка исключений основана на механизме setjmp/longjmp. См. исходники rtl по словам FPC_PUSHEXCEPTADDR, FPC_POPADDRSTACK, а чтобы понять, как оно вызывается, достаточно любой пример с try..except скомпилировать в ассемблерный файл. Как таковых exception-фреймов нет, а для работы с обычными фреймами есть ф-ции get_frame, get_caller_addr, get_caller_frame.

Добавлено спустя 12 минут:
backtracestrfunc - это адрес ф-ции, используемой модулями lineinfo и lnfodwrf как раз для резолва. Она вызывается с нужными адресами, так что парсить ее результат не нужно.

Re: Трассировка стека под Linux

СообщениеДобавлено: 12.03.2010 15:11:06
*vmr
Sergei I. Gorelkin писал(а):Вообще говоря, при возникновении исключения его адрес можно получить ф-цией ExceptAddr...

Да не, узнать адрес не проблема
И обработать исключение тоже

Вопрос был в том, как узнать цепочку вызовов приведших к исключению

Sergei I. Gorelkin писал(а):а для работы с обычными фреймами есть ф-ции get_frame, get_caller_addr, get_caller_frame.

Спасибо за наводку! Буду гуглить.

Re: Трассировка стека под Linux

СообщениеДобавлено: 13.03.2010 00:59:29
Sergei I. Gorelkin
Вот функция (бессовестно содранная из LCL), выдающая backtrace в той точке, откуда вызвана.

Код: Выделить всё
function GetStackTrace: string;
var
  bp: Pointer;
  addr: Pointer;
  oldbp: Pointer;
  CurAddress: shortstring;
begin
  Result:='';
  { retrieve backtrace info }
  bp := get_caller_frame(get_frame);
  while bp<>nil do
  begin
    addr := get_caller_addr(bp);
    CurAddress := BackTraceStrFunc(addr);
    Result := Result+CurAddress+LineEnding;
    oldbp := bp;
    bp := get_caller_frame(bp);
    if (bp <= oldbp) or (bp > (StackBottom + StackLength)) then
      bp := nil;
  end;
end;