How to get line number at runtime

Is it possible to get source line number at runtime in Delphi? I know JCL debugging, but I want to avoid using it. Assert is also not quite what I want. I would like to get something like this where GetLineNumber will get the source line number. Is it possible to do this without MAP files (in any case, a MAP file will be created when using Assert)? Is there any example?

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; 

thanks

+6
source share
5 answers

You can use Assert for this. Write a procedure that matches the signature dictated by the TAssertErrorProc type, and then do whatever you want there. To preserve the expected behavior, you should probably call the original engine after you finish.

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

At the start of your program, assign the System.AssertErrorProc procedure.

 AssertErrorProc := MichaelAssertProc; 
+10
source

For our journals and exception tracking classes , we made a .map parser and reader.

A.map can be parsed into a binary compressed version (native mab format), which is much smaller than the original .map. For example, a file with a file size of 900 KB is compressed into a file with a size of 70 KB. This is much higher compression than zip.

This .mab content can be added to .exe without any difference in execution or for the end user.

Then you can use our logging classes or directly the reading class .map / .mab, TSynMapFile .

You have the following methods:

  /// retrieve a .map file content, to be used eg 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 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 // - ie 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; 

Thanks to this class, in just one block, you will have the entire line of source code anywhere.

Open the source code and work with Delphi 5 to XE (note that the .map format has changed a bit from older to newer versions - our class is trying to deal with this).

+5
source

Note: This answer indicates the following question.

Is it possible to get source line number at runtime in Delphi?

You cannot do this without a map file or something like that. The compilation process leaves the source code.

It is not advisable to put your source code with proactive error checks. Moreover, it will only make very limited information for a very limited number of errors in your code. As a rule, if you can anticipate a mistake, you will not be mistaken. These are errors that you do not expect to make them production code.

In fact, you're best off using madExcept, EurekaLog, or JclDebug.

+3
source
 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; 
+1
source

I created my own solution for finding line numbers using a statement.

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

where 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; 

and you can enable / disable this using the debugger flag

 {$DEFINE TRACELOG} 

I hope this helps. I found this to be a great solution for debugging and tracking firmware, let me know if this is helpful.

0
source

Source: https://habr.com/ru/post/896033/


All Articles