如何在运行时获取行号

时间:2011-08-27 11:23:45

标签: delphi debugging

是否可以在Delphi中获取运行时的源代码行? 我知道JCL调试,但我想避免使用它。 Assert也不是我想要的。我想得到这样的东西,GetLineNumber将得到源代码行。是否可以在没有MAP文件的情况下执行此操作(无论如何,当我使用Assert时会生成MAP文件)?有什么例子吗?

function GetLineNumber: integer;
begin
  ???
end;

procedure ThisWouldBeGreat;
begin
  if not SomeCondition then
    LogMemo.Lines.Add('Error on line: ' + IntToStr(GetLineNumber));
end;

procedure ThatsWhatIWont;
begin
  Assert(not SomeCondition, 'Error');
end;

谢谢

5 个答案:

答案 0 :(得分:10)

您确实可以使用Assert。编写一个与TAssertErrorProc类型指定的签名匹配的过程,然后在那里做任何你想做的事情。为了保留预期的行为,您应该在完成后调用原始处理程序。

procedure MichaelAssertProc(const Message, Filename: string;
  LineNumber: Integer; ErrorAddr: Pointer);
begin
  LogMessage(...);
  SysUtils.AssertErrorHandler(Message, Filename, LineNumber, ErrorAddr);
end;

在程序启动时的某个时间将该过程分配给System.AssertErrorProc

AssertErrorProc := MichaelAssertProc;

答案 1 :(得分:6)

对于our logging and exception tracing classes,我们制作了.map解析器和读者。

可以将.map解析为二进制压缩版本(.mab专有格式),该版本比原始.map小得多。例如,一个900 KB的.map文件被压缩成一个70 KB的.mab文件 - 这比压缩文件压缩得多。

此.mab内容可以附加到.exe,执行时没有任何差异,也可以附加到最终用户。

然后您可以使用我们的日志记录类,或直接使用.map / .mab阅读器类TSynMapFile

您手头有以下方法:

  /// retrieve a .map file content, to be used e.g. with TSynLog to provide
  // additional debugging information
  // - original .map content can be saved as .mab file in a more optimized format
  TSynMapFile = class   
  public
    /// get the available debugging information
    // - will first search for a .map file in the .exe directory: if found,
    // will be read to retrieve all necessary debugging information - a .mab
    // file will be also created in the same directory (if MabCreate is TRUE)
    // - if .map is not not available, will search for the .mab file in the
    // .exe directory
    // - if no .mab is available, will search for a .mab appended to the exe 
    // - if nothing is available, will log as hexadecimal pointers, without
    // debugging information
    // - if aExeName is not specified, will use the current process executable
    constructor Create(const aExeName: TFileName=''; MabCreate: boolean=true);
    /// save all debugging information in the .mab custom binary format
    // - if no file name is specified, it will be saved as ExeName.mab
    // - this file content can be appended to the executable via SaveToExe method
    // - this function returns the created file name
    function SaveToFile(const aFileName: TFileName=''): TFileName;
    /// save all debugging informat in our custom binary format
    procedure SaveToStream(aStream: TStream);
    /// append all debugging information to an executable
    // - the executable name must be specified, because it's impossible to
    // write to the executable of a running process
    procedure SaveToExe(const aExeName: TFileName);
    /// add some debugging information according to the specified memory address
    // - will create a global TSynMapFile instance for the current process, if
    // necessary
    // - if no debugging information is available (.map or .mab), will write
    // the address as hexadecimal
    class procedure Log(W: TTextWriter; Addr: PtrUInt);
    /// retrieve a symbol according to an absolute code address
    function FindSymbol(aAddr: cardinal): integer;
    /// retrieve an unit and source line, according to an absolute code address
    function FindUnit(aAddr: cardinal; out LineNumber: integer): integer;
    /// return the symbol location according to the supplied absolute address
    // - i.e. unit name, symbol name and line number (if any), as plain text
    // - returns '' if no match found
    function FindLocation(aAddr: Cardinal): RawUTF8;
    /// all symbols associated to the executable
    property Symbols: TSynMapSymbolDynArray read fSymbol;
    /// all units, including line numbers, associated to the executable
    property Units: TSynMapUnitDynArray read fUnit;
  published
    /// the associated file name
    property FileName: TFileName read fMapFile;
    /// equals true if a .map or .mab debugging information has been loaded
    property HasDebugInfo: boolean read fHasDebugInfo;
  end;

感谢这个课程,只需一个单元,您就可以拥有任何位置的所有源代码行。

开源,使用Delphi 5到XE(请注意.map格式从旧版本到新版本稍有变化 - 我们的课程尝试处理它。)

答案 2 :(得分:3)

注意:此答案解决了以下问题。

  

是否可以在Delphi中获取运行时的源代码行?

如果没有地图文件或等效文件,则无法执行此操作。编译过程留下了源代码。

使用先发制人的错误检查来丢弃源代码是不切实际的。更重要的是,这样做只会为您的代码中的数量非常有限的故障提供非常有限的信息。一般来说,如果你能预料到错误,你就不会错。这是您没有预料到会将其变为生产代码的错误。

你最好使用madExcept,EurekaLog或JclDebug。

答案 3 :(得分:1)

var  LineNumber: Integer; 

procedure MyAssert(const M, F: string; L: Integer; E: Pointer);
begin  
  LineNumber := L;
end; 

procedure TForm1.Button1Click(Sender: TObject);
var  I: Integer;  
S: TAssertErrorProc;
begin  
  I := 0;  
  S := AssertErrorProc;  
  AssertErrorProc := MyAssert;  
  try
    Assert(I <> 0);  
  finally    
    AssertErrorProc := S;  
  end;  
  showmessage(IntToStr(LineNumber));
end;

答案 4 :(得分:0)

我创建了自己的解决方案,用于通过断言捕获行号。

{$IFDEF TRACELOG} 
try 
    assert(0=1,''); 
except on E : Exception do 
    tracelog(E.Message); 
end; 
{$ENDIF}

其中tracelog()是:

procedure TraceLog(LogMessage: WideString);
var
    tfile : TextFile;
    logTime : WideString;
begin
    logTime := formatDateTime('YYYY-MM-DD HH:NN:SS.zzz',Now);
    logMessage := Copy(LogMessage,pos(', line ',LogMessage)+7,pos(')',LogMessage) - pos(', line ',LogMessage) - 7);

    Assign(tfile,SrcDir+'data\TraceLog.txt');
    if FileExists(SrcDir+'data\TraceLog.txt') then Append(tfile) else Rewrite(tfile);
    Writeln(tfile,'{' + logTime + '} [' + LogMessage + ']');
    CloseFile(tfile);
end;

您可以使用调试器标志

启用/禁用此功能
{$DEFINE TRACELOG}

我希望能帮到你。我发现这是一个很好的解决方案,用于调配和追踪亚麻布,让我知道它是否有用。