문제

내가 있는 응용 프로그램 로그인할 수 있습니 stacktrace,할 수 있는 나중에 사용하여 디버깅할 수 있습니다.

윈도우에서,나는 받을 사용하여 우수한 JCLDebug 단위에 의해 제공됩이 프로젝트입니다.

이제 내가 응용 프로그램이 실행되 OSX 에서,나 비치-나가지 않을 얻는 방법을 알고 올바른 stacktrace 경우 예외가 발생합니다.

나는 기초를 가지고 다운

1)나는 얻을 수 있습 stacktrace 를 사용하여'프로그램'(에서 발견 libSystem.dylib)

2)결과:프로그램으로 변환될 수 있는 라인은 숫자를 사용하는.map 파일에 의해 제공됩 델파이의 링커

문제 내가 왼쪽으로는-I don't know where to 화 프로그램에서.내가 알고 있는 델파이 사용하는 마치 예외(별도의 스레드에서),나는 사용할 수 없습니다 posix 신호를 그러나 그 모든 것을 관리합니다.

를 얻을 수 있습 역추적에서 시도한다는'블록,그러나 불행하게도,그 시점에 스택은 이미를 마무리 지었다.

할 수 있는 방법을 설치하는 적절한 예외로거는 것이 바로 실행한 후에 예외가 발생?

업데이트:

따라'Honza R 의 제안,나는 보'GetExceptionStackInfoProc'절차입니다.

이 기능은 나를 얻는'내부'예외 처리 프로세스,그러나 불행하게도 나의 일부와 같은 문제는 되었습니다.

첫째 모든 데스크톱 플랫폼에서,이 기능'GetExceptionStackInfoProc'은 함수 포인터,할 수 있는 지정 당신의 자신의 예외 info handler.그래서 밖의 상자,델파이 제공되지 않 스택 정보 제공자입니다.

는 경우가 지정하는 기능'GetExceptionStackInfoProc'한 후 실행하는'프로그램은'그것의 내부가 나타납 stacktrace,그러나는 추적이 상대적 예외 처리기 스레드가 아닌 발생하는 예외입니다.

'GetExceptionStackInfoProc'이 포함되지 않은 포인터'TExceptionRecord'지만,거기에 아주 제한된 문서를 사용할 수 있습니다.

나 갈 수 있습을 넘어서 깊이 있지만,어떻게 받을 수 있 stacktrace 에서는 올바른 스레드가?이 가능한 것 나를 주입하는 내 자신의'프로그램은'기능으로 예외 처리기와 그 반환 기준에는 예외 처리기에 있는가?

업데이트 2

좀 더 상세정보.한 가지를 명확한 이 질문에는 예외에 대한 의해 처리됩 MACH 메시지지 않은 소프트웨어 처리되는 예외에서 RTL.

엠바카데로가 뻗어있는 일부 의견과 함께 이러한 기능-

    System.Internal.MachExceptions.pas -> catch_exception_raise_state_identity

    {
     Now we set up the thread state for the faulting thread so that when we
     return, control will be passed to the exception dispatcher on that thread,
     and this POSIX thread will continue watching for Mach exception messages.
     See the documentation at <code>DispatchMachException()</code> for more
     detail on the parameters loaded in EAX, EDX, and ECX.
    }

    System.Internal.ExcUtils.pas -> SignalConverter

    {
      Here's the tricky part.  We arrived here directly by virtue of our
      signal handler tweaking the execution context with our address.  That
      means there's no return address on the stack.  The unwinder needs to
      have a return address so that it can unwind past this function when
      we raise the Delphi exception.  We will use the faulting instruction
      pointer as a fake return address.  Because of the fencepost conditions
      in the Delphi unwinder, we need to have an address that is strictly
      greater than the actual faulting instruction, so we increment that
      address by one.  This may be in the middle of an instruction, but we
      don't care, because we will never be returning to that address.
      Finally, the way that we get this address onto the stack is important.
      The compiler will generate unwind information for SignalConverter that
      will attempt to undo any stack modifications that are made by this
      function when unwinding past it.  In this particular case, we don't want
      that to happen, so we use some assembly language tricks to get around
      the compiler noticing the stack modification.
    }

하게 보이는 문제에 대한 책임 있어요.

때 나는 stacktrace 후에 이 예외는 시스템은 손으로 통제하 RTL,그것은 다음과 같습니다-(마음에 베어링,스 unwinder 에 의해 대체되었습 역추적니다.이 프로그램은 것입니다 손으로 통제하 unwinder 이 완료되면)

0: MyExceptionBacktracer
1: initunwinder in System.pas
2: RaiseSignalException in System.Internal.ExcUtils.pas 

RaiseSignalExceptionSignalConverter, 로,나도는 믿 backtrace 기능에 의해 제공되었는지와 호환되는 수정 사항이 적다는 것이다.그래서,그것은 불가능을 읽고 스택을 넘어 그 시점,그러나 스택은 여전히 존재가 아래에 있습니다.

는 사람은 무엇을 해야할지에 대한(또는지 여부를 나 가설은 정확한)?

업데이트 3

나는 마지막으로 관리하는 적절한 stacktraces on OSX.거대한 덕분에 모두 Honza 및 세바스챤.을 결합하여 모두의 자신의 기술을 발견했다.

다른 사람을 위해로부터 혜택을 받을 수 있던 이 여기에서의 기본 소스입니다.베어는 마음에서 나는 아주 확실하지 않다면 그것은 100%정확한할 수 있는 경우,개선을 제안,앞으로 이동합니다.이 기술은 후크에는 예외 전 델파이 풀어 스택에 오류가 있는 쓰레드,그리고 보상에 대한 어떤 스택 구조 손상이 있는 장소를 촬영했습니다.

unit MyExceptionHandler;

interface

implementation

uses
  SysUtils;

var
  PrevRaiseException: function(Exc: Pointer): LongBool; cdecl;

function backtrace2(base : NativeUInt; buffer : PPointer; size : Integer) : Integer;
var SPMin   : NativeUInt;
begin
  SPMin:=base;
  Result:=0;
  while (size > 0) and (base >= SPMin) and (base <> 0) do begin

    buffer^:=PPointer(base + 4)^;
    base:=PNativeInt(base)^;

    //uncomment to test stacktrace
    //WriteLn(inttohex(NativeUInt(buffer^), 8));

    Inc(Result);
    Inc(buffer);
    Dec(size);

  end;
  if (size > 0) then buffer^:=nil;
end;

procedure UnInstallExceptionHandler; forward;

var
  InRaiseException: Boolean;

function RaiseException(Exc: Pointer): LongBool; cdecl;
var b : NativeUInt;
    c : Integer;
    buff : array[0..7] of Pointer;
begin
  InRaiseException := True;

  asm
    mov b, ebp
  end;

  c:=backtrace2(b - $4 {this is the compiler dependent value}, @buff, Length(buff));
  //... do whatever you want to do with the stacktrace

  Result := PrevRaiseException(Exc);
  InRaiseException := False;
end;

procedure InstallExceptionHandler;
var
  U: TUnwinder;
begin
  GetUnwinder(U);
  Assert(Assigned(U.RaiseException));
  PrevRaiseException := U.RaiseException;
  U.RaiseException := RaiseException;
  SetUnwinder(U);
end;

procedure UnInstallExceptionHandler;
var
  U: TUnwinder;
begin
  GetUnwinder(U);
  U.RaiseException := PrevRaiseException;
  SetUnwinder(U);
end;

initialization
  InstallExceptionHandler;
end.
도움이 되었습니까?

해결책

당신이 사용할 수 있는 GetExceptionStackInfoProc, CleanUpStackInfoProcGetStackInfoStringProcException 클래스 저장해야에 스택 추적 GetExceptionStackInfoProc 다이 검색 GetStackInfoStringProc 을 얻을 것이다라는 의 RTL 사용하는 경우 StackTraceException.어쩌면 당신은 수도에서 보 https://bitbucket.org/shadow_cs/delphi-arm-backtrace 는 안드로이드에서 이 방법을 보여 줍니다.

이렇게 하려면 제대로 Mac OS X libc backtrace 기능하기 때문에 사용할 수 없습니다 델파이 손상됩니다 스택 구조를 호출할 때 GetExceptionStackInfoProcException.RaisingException.자신의 구현을 사용해야 할 수있는 걸어 스택에서 다른 기본 주소는 수정할 수 있습니다.

귀하의 GetExceptionStackInfoProc 다음 다음과 같이(내가 사용하는 xe5 를 갖습 이 예제에서 부가가치를 EBP 벨에 따라 달라질 수 있습는 컴파일러를 사용하고 이 예에서 테스트 되었습니다만 맥 OS X,윈도우 구현지 않을 수도 있습 다르다):

var b : NativeUInt;
    c : Integer;
    buff : array[0..7] of Pointer;
begin
  asm
    mov b, ebp
  end;
  c:=backtrace2(b - $14 {this is the compiler dependent value}, @buff, Length(buff));
  //... do whatever you want to do with the stacktrace
end;

backtrace2 기능을 다음과 같이 보일 것입니다(참고는 정지 조건과 다른 유효성 검사에서 누락된 구현하는지 확인 AVs 인해 발생하지 않는 도중 스택을 걷):

function backtrace2(base : NativeUInt; buffer : PPointer; size : Integer) : Integer;
var SPMin   : NativeUInt;
begin
  SPMin:=base;
  Result:=0;
  while (size > 0) and (base >= SPMin) and (base <> 0) do begin
    buffer^:=PPointer(base + 4)^;
    base:=PNativeInt(base)^;
    Inc(Result);

    Inc(buffer);
    Dec(size);
  end;
  if (size > 0) then buffer^:=nil;
end;

다른 팁

할 수 있습 훅으로 자신을 제외 언.다음 호출할 수 있는 프로그램은 예외가 발생합니다.예제는 다음과 같습니다.단위 SBMapFiles 은 무엇인가를 읽기 위해 사용하 mapfiles.그것은 필요하지 않습을 얻을 제외한 호출 스택이 있습니다.

unit MyExceptionHandler;

interface

implementation

uses
  Posix.Base, SysUtils, SBMapFiles;

function backtrace(result: PNativeUInt; size: Integer): Integer; cdecl; external libc name '_backtrace';
function _NSGetExecutablePath(buf: PAnsiChar; BufSize: PCardinal): Integer; cdecl; external libc name '__NSGetExecutablePath';

var
  PrevRaiseException: function(Exc: Pointer): LongBool; cdecl;
  MapFile: TSBMapFile;

const
  MaxDepth = 20;
  SkipFrames = 3;

procedure ShowCurrentStack;
var
  StackLog: PNativeUInt; //array[0..10] of Pointer;
  Cnt: Integer;
  I: Integer;
begin
  {$POINTERMATH ON}
  GetMem(StackLog, SizeOf(Pointer) * MaxDepth);
  try
    Cnt := backtrace(StackLog, MaxDepth);

    for I := SkipFrames to Cnt - 1 do
    begin
      if StackLog[I] = $BE00EF00 then
      begin
        WriteLn('---');
        Break;
      end;
      WriteLn(IntToHex(StackLog[I], 8), ' ', MapFile.GetFunctionName(StackLog[I]));
    end;

   finally
    FreeMem(StackLog);
   end;
  {$POINTERMATH OFF}
end;

procedure InstallExceptionHandler; forward;
procedure UnInstallExceptionHandler; forward;

var
  InRaiseException: Boolean;

function RaiseException(Exc: Pointer): LongBool; cdecl;
begin
  InRaiseException := True;
  ShowCurrentStack;

  Result := PrevRaiseException(Exc);
  InRaiseException := False;
end;

procedure InstallExceptionHandler;
var
  U: TUnwinder;
begin
  GetUnwinder(U);
  Assert(Assigned(U.RaiseException));
  PrevRaiseException := U.RaiseException;
  U.RaiseException := RaiseException;
  SetUnwinder(U);
end;

procedure UnInstallExceptionHandler;
var
  U: TUnwinder;
begin
  GetUnwinder(U);
  U.RaiseException := PrevRaiseException;
  SetUnwinder(U);
end;

procedure LoadMapFile;
var
  FileName: array[0..255] of AnsiChar;
  Len: Integer;
begin
  if MapFile = nil then
  begin
    MapFile := TSBMapFile.Create;
    Len := Length(FileName);
    _NSGetExecutablePath(@FileName[0], @Len);
    if FileExists(ChangeFileExt(FileName, '.map')) then
      MapFile.LoadFromFile(ChangeFileExt(FileName, '.map'));
  end;
end;

initialization
  LoadMapFile;
  InstallExceptionHandler;
end.
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top