Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2013.07.28;
Скачать: CL | DM;

Вниз

Раскрутка стека в Eureka log   Найти похожие ветки 

 
Pit   (2013-03-06 16:04) [0]

Надеюсь на Розыча, но верю во всех :)

Нужна раскрутка стека в произвольном месте программы. Я в свое время писал это, но где-то потерялось. Может, есть уже отлаженный код.

Как я это себе вижу - вызываем что-то типа:

function getCallStack: string

она возвращает список указателей. Например:

$00AAB3CC
$00BBCE9D
$00B2CCFF
...


Вторая функция:

getCallStackWithName: string

Возвращает вместе с именами уже методов:

TForm.blabla1
TForm.blabla2
TForm.blabla3
...


Розыч даже обещал вместе с указанием номеров строк кода.

Чем все упрощается - есть подключенная Еврика версии 6.x - я никогда не использовал её API (оно же наверняка есть), куда смотреть?


 
Pit   (2013-03-06 16:08) [1]

Серия вопросов еще:

1) когда происходит исключение - можно выцепить адрес кода, где произошло исключение. То есть, в секции except..end можно определить адрес инструкции, где произошло исключение. Как его определить? я подзабыл...

2) нужно адрес перевести в название метода, которому соответствует этот адрес :-) Тоже, наверное, с помощью еврики легко сделать (перекликается со стартовым постом). А еще лучше - в номер строки.


 
Игорь Шевченко ©   (2013-03-06 18:14) [2]


> Может, есть уже отлаженный код


Если без еврики, то jcldebug


 
брат Птибурдукова   (2013-03-06 18:19) [3]


> Если без еврики, то jcldebug
Крайне не любит вложенные процедуры. Если таковые имеются, пользоваться осторожно и снисходительно.


 
Pit   (2013-03-06 18:23) [4]

коллеги, как в секции except..end вытащить адрес инструкции, где возникло исключение?


 
Игорь Шевченко ©   (2013-03-06 18:28) [5]


> как в секции except..end вытащить адрес инструкции, где
> возникло исключение?


function ExceptAddr: Pointer;


 
Pit   (2013-03-06 18:44) [6]


> function ExceptAddr: Pointer;

это где такая функция?


 
брат Птибурдукова   (2013-03-06 18:54) [7]

Ты не поверишь... В модуле System.


 
Pit   (2013-03-06 19:02) [8]

тьфу ты, я подумал что это метод у Exception, обыскал как только мог и не нашел)


 
Rouse_ ©   (2013-03-06 19:03) [9]

Стек какой? Стек вызовов или стек SEH фреймов?
Еврика на работе, но на память могу сказать что в главном модуле есть и та и та функция (вроде даже как самостоятельные они) которые показывают трассу обоих видов.

Вообще на исходниках есть один из авторов эврики: http://forum.sources.ru/index.php?showuser=80667

Я обычно при непонятках всегда с ним напрямую связываюсь, отвечает достаточно оперативно (по крайней мере мне), а так вообще у них неплохой саппорт есть.


 
Pit   (2013-03-06 19:31) [10]

А не подскажите... Если преобразовывать из внутреннего формата в строчки, то как пример вывод еврики:

-----------------------------------------------------------------------------
|Address |Module      |Unit        |Class |Procedure/Method           |Line |
-----------------------------------------------------------------------------
|004AB772|Project1.exe|Unit1.pas   |      |bbb                        |32[2]|
|004AB76C|Project1.exe|Unit1.pas   |      |bbb                        |30[0]|
|004AB77C|Project1.exe|Unit1.pas   |      |aaa                        |37[1]|


Что это за значения в квадратных скобках у номера линии в исходниках?

Да, строчки 32, 30, 37 - все верно. Но что за [2], [0], [1]


 
Rouse_ ©   (2013-03-06 20:16) [11]

Оффсет от самой первой строки с начала входа в процедуру/функцию (ноль - база).


 
Германн ©   (2013-03-07 02:56) [12]


> Rouse_ ©   (06.03.13 19:03) [9]
>
> Стек какой? Стек вызовов или стек SEH фреймов?
> Еврика на работе, но на память могу сказать что в главном
> модуле есть и та и та функция (вроде даже как самостоятельные
> они) которые показывают трассу обоих видов.
>
> Вообще на исходниках есть один из авторов эврики: http://forum.
> sources.ru/index.php?showuser=80667
>
> Я обычно при непонятках всегда с ним напрямую связываюсь,
>  отвечает достаточно оперативно (по крайней мере мне), а
> так вообще у них неплохой саппорт есть.
>

Как мне когда-то ответил Александр - этот суппорт и есть он сам :)


 
Германн ©   (2013-03-07 03:06) [13]


>
> Что это за значения в квадратных скобках у номера линии
> в исходниках?

Кстати в стандартную поставку Эврики входит их вьювер, который определяет себя как программу для"обработки" файлов *.elf Как ты не увидел её, я не понимаю.


 
Pit   (2013-03-07 10:52) [14]


> Как ты не увидел её, я не понимаю.

как ты делаешь такие выводы - не понимаю.


 
Pit   (2013-03-07 11:17) [15]

А может кто видел утилиту (писал?), которая получив в текстовом виде какого-нибудь формата callstack (адреса) и с другой стороны имея MAP-файл - выдавала Callstack в текстовом виде, как делает еврика?

То есть, задумка в том, что приложение БЕЗ еврики раскручивает стек и в лог пишет раскрученные адреса. А потом уже "на базе" из этих адресов и MAP-файла от соответствующей версии строится стек.
Лень писать анализатор MAP-файла... как-то так)


 
Игорь Шевченко ©   (2013-03-07 11:23) [16]


> Лень писать анализатор MAP-файла... как-то так)


unit DelphiMapParser;

interface

procedure ParseDelphiMap(const AMapFileName: string);

implementation
uses
 SysUtils, Classes,
 image, symtab;

const
 UsableMapSectionID = "  Address             Publics by Value";
 LineNumbersID = "Line numbers for ";
 BoundResourcesID = "Bound resource files";

procedure ParseDelphiMap(const AMapFileName: string);

 procedure ProcessSymbolAddressFromMap(const AMapLine: string);
 var
   Section: Integer;
   Address: Cardinal;
   Symbol: string;
 begin
   if Length(AMapLine) > 22 then
   begin
     Section := StrToIntDef("$" + Copy(AMapLine, 2, 4), 9999);
     Address := StrToIntDef("$" + Copy(AMapLine, 7, 8), 0);
     Symbol := Copy(AMapLine, 22, MaxInt);
     if (Section <= g_SectionCount) and g_Sections[Section].ContainsCode then
     begin
       Inc(Address, g_Sections[Section].VirtualAddress);
       if not Assigned(SymbolTable.FindSymbol(Address)) then
         SymbolTable.AddSymbol(Address, Symbol);
     end;
   end;
 end;

 procedure ProcessLineNumbersFromMap(const AMapLine: string; var F: TextFile);

   procedure ProcessLineNumbersLine(const AMapLine, ASegment: string);

     procedure ProcessLineNumber(const ALineNumber: PChar; const ASegment:
       string);
     var
       Section: Integer;
       Address: Cardinal;
       Symbol: string;
       LineNo: Integer;
       Worker: string;
     begin
       SetString(Worker, ALineNumber, 6);
       LineNo := StrToIntDef(Trim(Worker), 0);
       SetString(Worker, ALineNumber + 7, 4);
       Section := StrToIntDef("$" + Worker, 9999);
       SetString(Worker, ALineNumber + 12, 8);
       Address := StrToIntDef("$" + Worker, 0);
       if (Section <= g_SectionCount) and g_Sections[Section].ContainsCode then
       begin
         Symbol := Format("%s_line#_%d", [ASegment, LineNo]);
         Inc(Address, g_Sections[Section].VirtualAddress);
         if not Assigned(SymbolTable.FindSymbol(Address)) then
           SymbolTable.AddSymbol(Address, Symbol);
       end;
     end;

     //   152 0001:00004128
   const
     LineNumberLength = 20;
   var
     I: Integer;
   begin
     //each line numbers line consist of up to 4 sections, 20 characters each.
     I := 1;
     while I < Length(AMapLine) do
     begin
       ProcessLineNumber(@AMapLine[I], ASegment);
       Inc(I, LineNumberLength);
     end;

   end;

 var
   Segment: string;
   S: string;
 begin
   Segment := Copy(AMapLine, Length(LineNumbersID) + 1, Pos("(", AMapLine) -
     Length(LineNumbersID) - 1); //+1-1 fencepost error :)
   readln(F, S); //skip empty line after the line numbers section header
   while not Eof(F) do
   begin
     readln(F, S);
     if Length(S) = 0 then
       Break; //end of line numbers section
     ProcessLineNumbersLine(S, Segment);
   end;
 end;

var
 MapF: TextFile;
 S: string;
begin
 AssignFile(MapF, AMapFileName);
 Reset(MapF);
 try
   while not Eof(MapF) do
   begin
     readln(MapF, S);
     if S = UsableMapSectionID then
       Break;
   end;
   if Eof(MapF) then
     Exit; //Illegal map
   readln(MapF, S); //Skip empty line
   //Next lines till empty line have the following format:
   // 0001:00000000       System.AllocMem
   // for code segments index in section table is used to determine section RVA
   // to add to address specified in map to compute symbol RVA in image.
   while not Eof(MapF) do
   begin
     readln(MapF, S);
     if Length(S) = 0 then
       Break;
     ProcessSymbolAddressFromMap(S);
   end;
   //Next lines are sections of line numbers
   while not Eof(MapF) do
   begin
     readln(MapF, S);
     if Copy(S, 1, Length(LineNumbersID)) = LineNumbersID then
       ProcessLineNumbersFromMap(S, MapF);
   end;
 finally
   CloseFile(MapF);
 end;
end;

end.


как канва ?


 
Rouse_ ©   (2013-03-07 11:35) [17]


> То есть, задумка в том, что приложение БЕЗ еврики раскручивает
> стек и в лог пишет раскрученные адреса.


Без еврики раньше пользовался решением от Jedy используя вот такой юнит:

unit SimpleExceptionLog;

interface

{$IFDEF DEBUG}
uses
 Windows,
 Classes,
 JclDebug,
 JclHookExcept,
 TypInfo,
 SysUtils,
 Forms;

 procedure ShowCallStack(ShowResultMessage: Boolean;
   const Prefix: string = "");
 function GetCallStack(LogStart: Integer = 3): string;
 procedure SetLogPath(const Value: string);

{$ENDIF}

implementation

{$IFDEF DEBUG}

var
 _LogPath: string = "";

 procedure SetLogPath(const Value: string);
 begin
   if Value <> "" then
     _LogPath := IncludeTrailingPathDelimiter(Value);
 end;

 function GetVersionInt(const Value: string): Int64Rec;
 var
 VerInfoSize, Dummy: DWORD;
 PFixed : Pointer;
 M: TMemoryStream;
 FlName : PChar;
 FixLength : UINT;
 FVersionMS : Cardinal;
 FVersionLS : Cardinal;
 begin
 Result.Lo := 0;
 Result.Hi := 0;
 FlName := PChar(Value);
 VerInfoSize := GetFileVersionInfoSize(FlName, Dummy);
 if VerInfoSize = 0 then Exit;
 M := TMemoryStream.Create;
 try
   M.Size := VerInfoSize;
   if GetFileVersionInfo(FlName, 0, VerInfoSize, M.Memory) then
   begin
     if VerQueryValue(M.Memory, "\", PFixed, FixLength) then
     begin
       FVersionMS := PVSFixedFileInfo(PFixed)^.dwFileVersionMS;
       FVersionLS := PVSFixedFileInfo(PFixed)^.dwFileVersionLS;
       Result.Words[0] := LongRec(FVersionLS).Lo;
       Result.Words[1] := LongRec(FVersionLS).Hi;
       Result.Words[2] := LongRec(FVersionMS).Lo;
       Result.Words[3] := LongRec(FVersionMS).Hi;
     end;
   end;
 finally
   M.Free;
 end;
 end;

 procedure LogException(ExceptObj: TObject;
   ExceptAddr: Pointer; IsOS: Boolean);
 var
   mmLog: TStringList;
   ExcceptionLog, AppName, LogPath, VerStr: string;
   ModInfo: TJclLocationInfo;
   I: Integer;
   ExceptionHandled: Boolean;
   HandlerLocation: Pointer;
   ExceptFrame: TJclExceptFrame;
   FS: TFormatSettings;
   Ver: Int64Rec;
 begin
   LogPath := GetModuleName(HInstance);
   Ver := GetVersionInt(LogPath);
   VerStr := Format("%d.%d.%d.%d", [
     Ver.Words[3], Ver.Words[2], Ver.Words[1], Ver.Words[0]]);
   AppName := ExtractFileName(LogPath);

   if _LogPath <> "" then
     LogPath := _LogPath + "ErrorLogs\"
   else
     LogPath := IncludeTrailingPathDelimiter(ExtractFilePath(LogPath)) + "ErrorLogs\";

   ForceDirectories(LogPath);
   if ExceptObj.ClassName = "ELicenceError" then Exit;
   if ExceptObj.ClassName = "EGuardantStealthException" then Exit;
   mmLog := TStringList.Create;
   try
     mmLog.Add(AppName + " (ver " + VerStr +
       ") exception data: " + DateTimeToStr(Now));
     ExcceptionLog := "Exception " + ExceptObj.ClassName;
     if ExceptObj is Exception then
       ExcceptionLog := ExcceptionLog + ": " + Exception(ExceptObj).Message;
     if IsOS then
       ExcceptionLog := ExcceptionLog + " (OS Exception)";
     mmLog.Add(ExcceptionLog);
     ModInfo := GetLocationInfo(ExceptAddr);
     mmLog.Add(Format(
       "  Exception occured at $%p (Module "%s", Procedure "%s", Unit "%s", Line %d)",
       [ModInfo.Address,
        ModInfo.UnitName,
        ModInfo.ProcedureName,
        ModInfo.SourceName,
        ModInfo.LineNumber]));
     if stExceptFrame in JclStackTrackingOptions then
     begin
       mmLog.Add("  Except frame-dump:");
       I := 0;
       ExceptionHandled := False;
       if JclLastExceptFrameList <> nil then
       begin
         while (not ExceptionHandled) and
           (I < JclLastExceptFrameList.Count) do
         begin
           ExceptFrame := JclLastExceptFrameList.Items[I];
           ExceptionHandled := ExceptFrame.HandlerInfo(ExceptObj, HandlerLocation);
           if (ExceptFrame.FrameKind = efkFinally) or
               (ExceptFrame.FrameKind = efkUnknown) or
               not ExceptionHandled then
             HandlerLocation := ExceptFrame.CodeLocation;
           ModInfo := GetLocationInfo(HandlerLocation);

           ExcceptionLog := Format(
             "    Frame at $%p (type: %s",
             [ExceptFrame.FrameLocation,
              GetEnumName(TypeInfo(TExceptFrameKind), Ord(ExceptFrame.FrameKind))]);

           if ExceptionHandled then
             ExcceptionLog := ExcceptionLog + ", handles exception)"
           else
             ExcceptionLog := ExcceptionLog + ")";
           mmLog.Add(ExcceptionLog);
           if ExceptionHandled then
             mmLog.Add(Format(
               "      Handler at $%p",
               [HandlerLocation]))
           else
             mmLog.Add(Format(
               "      Code at $%p",
               [HandlerLocation]));
           mmLog.Add(Format(
             "      Module "%s", Procedure "%s", Unit "%s", Line %d",
             [ModInfo.UnitName,
              ModInfo.ProcedureName,
              ModInfo.SourceName,
              ModInfo.LineNumber]));
           mmLog.Add("");
           Inc(I);
         end;
       end;
     end;
     GetLocaleFormatSettings(GetThreadLocale, FS);
     FS.DateSeparator := ".";
     FS.TimeSeparator := "-";
     FS.ShortDateFormat := "dd.MM.yyyy";
     AppName := ChangeFileExt(AppName, "");
     LogPath := LogPath + AppName + "(" +
       StringReplace(DateTimeToStr(Now, FS), ":", "-", [rfReplaceAll]) + ")_error.log";
     mmLog.SaveToFile(LogPath);
     ExcceptionLog := mmLog.Text;
   finally
     mmLog.Free;
   end;
 end;


 
Rouse_ ©   (2013-03-07 11:35) [18]

Не влезло:

procedure ShowCallStack(ShowResultMessage: Boolean;
   const Prefix: string = "");
 var
   StackInfoList: TJclStackInfoList;
   S, SS: TStringList;
   P, LogPath, AppName, ShortAppName, VerStr: string;
   ST: TSystemTime;
   Ver: Int64Rec;
 begin
   LogPath := GetModuleName(HInstance);
   Ver := GetVersionInt(LogPath);
   VerStr := Format("%d.%d.%d.%d", [
     Ver.Words[3], Ver.Words[2], Ver.Words[1], Ver.Words[0]]);
   AppName := ExtractFileName(LogPath);
   LogPath := IncludeTrailingPathDelimiter(
     ExtractFilePath(LogPath)) + "ErrorLogs\";
   ForceDirectories(LogPath);
   DateTimeToSystemTime(Now, ST);
   ShortAppName := ChangeFileExt(AppName, "");
   P := LogPath + Format("callstack_%s_%d.log", [ShortAppName, ST.wDay]);
   SS := TStringList.Create;
   try
     SS.Add("======================================================================== ========================================");
     SS.Add(AppName + " (ver " + VerStr +
       ") callstack data: " + DateTimeToStr(Now));
     if Prefix <> "" then
       SS.Add(Prefix);
     SS.Add("======================================================================== ========================================");
     StackInfoList := JclCreateStackList(true, 3, nil);
     try
       StackInfoList.AddToStrings(SS, true, true, true);
     finally
       StackInfoList.Free;
     end;

     S := TStringList.Create;
     try
       if FileExists(P) then
         S.LoadFromFile(P);
       S.Add(SS.Text);
       S.SaveToFile(P);
     finally
       S.Free;
     end;
     if ShowResultMessage then
       MessageBox(0, PChar(SS.Text),
         "CallStack dumped", MB_ICONINFORMATION);
   finally
     SS.Free;
   end;

 end;

 function GetCallStack(LogStart: Integer): string;
 var
   StackInfoList: TJclStackInfoList;
   SS: TStringList;
 begin
   SS := TStringList.Create;
   try
     SS.Add("======================================================================== ========================================");
     SS.Add("callstack data: " + DateTimeToStr(Now));
     SS.Add("======================================================================== ========================================");
     StackInfoList := JclCreateStackList(true, LogStart, nil);
     try
       StackInfoList.AddToStrings(SS, true, true, true);
     finally
       StackInfoList.Free;
     end;
     Result := SS.Text;
   finally
     SS.Free;
   end;
 end;

{$ENDIF}

initialization

 {$IFDEF DEBUG}
   JclStackTrackingOptions := JclStackTrackingOptions + [stExceptFrame];
   JclStartExceptionTracking;
   JclAddExceptNotifier(LogException);
 {$ENDIF}

end.


 
Pit   (2013-03-07 12:37) [19]


> как канва ?

Разбираться надо)

А можно пример вызова, как этим пользоваться?
Я понял функцию, куда передать MAP-файл можно)


 
Eraser ©   (2013-03-07 12:38) [20]

кстати, анализатор map файлов есть в утилите ExceptionReportConverter.exe из JEDI.


 
Игорь Шевченко ©   (2013-03-07 12:53) [21]

Pit   (07.03.13 12:37) [19]

Готовой у меня нету. Принцип - анализируется MAP и строится таблица символов и номеров строк в юнитах.

Возможно, другие исходники будут более полезны



Страницы: 1 вся ветка

Текущий архив: 2013.07.28;
Скачать: CL | DM;

Наверх




Память: 0.55 MB
Время: 0.01 c
2-1354043911
adept
2012-11-27 23:18
2013.07.28
Операции с данными в ячейках StringGrid а


15-1362601803
Юрий
2013-03-07 00:30
2013.07.28
С днем рождения ! 7 марта 2013 четверг


15-1362261924
Германн
2013-03-03 02:05
2013.07.28
Нужен алгоритм.


15-1362408571
Хыхы
2013-03-04 18:49
2013.07.28
Запущен ли скрин-сейвер или трабла с константами?


15-1362377389
O'ShinW
2013-03-04 10:09
2013.07.28
Почем нынче и в какие сроки раскрутят сайт? Опыт у кого есть?