Wednesday, June 26, 2013

Automatically Obtaining Source and Line Number from Symbol Name and Offset

Once upon a time, unmanaged code developers had to work very hard to correlate code offsets back to source file names and line numbers. One approach involved generating .cod files (assembly listings) for each module, and then painstakingly comparing the instruction offsets with the contents of the .cod file.

For example, if you received a faulting stack trace from a client machine that had the frame BatteryMeter!TemperatureAndBatteryUpdaterThread+0xd0, you could go back to the .cod file for BatteryMeter.exe, look up the code listing for TemperatureAndBatteryUpdaterThread, and then look for the source line located at (or near) offset 0xd0.

This process can be automated. I was asked a few days ago if it's still necessary to use .cod files for this, and the answer is no. If you fire up WinDbg, you can simply File > Open Dump File, pass in your .exe or .dll as the dump file name, and then issue the ln command, as follows:

0:000> ln BatteryMeter!TemperatureAndBatteryUpdaterThread+0xd0 
d:\dev\batterymeter\batterymeterdlg.cpp(58) 

If you don't have WinDbg handy and/or want to perform this automatically (maybe from a script), you can use the DbgHelp API to load the symbols for the appropriate module and then look up the symbol name and source information. The functions involved are SymLoadModule64, SymFromName, and SymGetLineFromAddr64, and the resulting program is not much more than 100 lines of code. Here's a highlight:

DWORD displacement; 
IMAGEHLP_LINE64 line; 
RtlZeroMemory(&line, sizeof(line)); 
line.SizeOfStruct = sizeof(line); 
if (!SymGetLineFromAddr64(hProcess, symbolAddress, &displacement, &line)) 
    printf("*** Error retrieving source line for %s: 0x%x\n", 
        argv[1], GetLastError()); 
    return 1; 
printf("%s [0x%I64x] = %s line %d (+0x%x)\n", argv[1], symbolAddress, 
    line.FileName, line.LineNumber, displacement);

Read more: GitHub
QR: Inline image 1