SymGetLineFromAddr not working properly
Asked Answered
P

2

3

I have the following code:

#include "stdafx.h"
#include <process.h>
#include <iostream>
#include <Windows.h>
#include "dbghelp.h"

using namespace std;

int LogStackTrace()
{
    void *stack[1024];
    HANDLE process = GetCurrentProcess();
    SymInitialize(process, NULL, TRUE);
    WORD numberOfFrames = CaptureStackBackTrace(0, 1000, stack, NULL);
    SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO));
    symbol->MaxNameLen = 1024;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    IMAGEHLP_LINE *line = (IMAGEHLP_LINE *)malloc(sizeof(IMAGEHLP_LINE));
    line->SizeOfStruct = sizeof(IMAGEHLP_LINE);
    printf("Caught exception ");
    for (int i = 0; i < numberOfFrames; i++)
    {
        SymFromAddr(process, (DWORD64)(stack[i]), NULL, symbol);
        SymGetLineFromAddr(process, (DWORD)(stack[i]), NULL, line);
        printf("at %s in %s, address 0x%0X\n", symbol->Name, line->FileName, symbol->Address);
    }
    return 0;
}

void function2()
{
    int a = 0;
    int b = 0;
    throw new exception("Expected exception.");
}

void function1()
{
    int a = 0;
    function2();
}

void function0()
{
    function1();
}

static void threadFunction(void *param)
{
    try
    {
        function0();
    }
    catch (...)
    {
        LogStackTrace();
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    try
    {
        _beginthread(threadFunction, 0, NULL);
    }
    catch (...)
    {
        LogStackTrace();
    }
    printf("Press any key to exit.\n");
    cin.get();
    return 0;
}

The problem is that it always errors out at this line: printf("at %s in %s, address 0x%0X\n", symbol->Name, line->FileName, symbol->Address);

The reason is because line's FileName seems to be NULL. Actually, the entire line structure is messed up. I am trying to write an application to show a stack trace on an error. But why is that? Shouldn't it be working using the above code? PS I compiled it against Win32, as a simple MSVC++ Console application.

Phosphorylase answered 17/3, 2014 at 20:59 Comment(3)
Update your question with version information. Windows XP, or Windows Seven? 32 bits or 64 bits? MBCS or Unicode?Raleighraley
Windows 8.1, Using Unicode Character Set, Win32 App.Phosphorylase
@Raleighraley Hey, I wrote a blog post on all of this and made a nice wrapper class that people can use to trace the stack, and went into things like Windows Error Reporting (which is honestly a life-saver): dima.to/blog/?p=13Phosphorylase
R
4

Had the same problem with your code (Windows Seven 64b, Unicode 32 bits build, VS2012 Express)

Fixed it with :

DWORD dwDisplacement;
SymGetLineFromAddr(process, (DWORD)(stack[i]), &dwDisplacement, line);
Raleighraley answered 17/3, 2014 at 21:44 Comment(8)
so much for optional values eh? this might explain the The error code 3221225477 is 0xC0000005 in hex, which on Windows is: #define STATUS_ACCESS_VIOLATION ((NTSTATUS)0xC0000005L) Access violation is Windows' version of "segmentation fault", which simply said means that the program tried to access a memory which is not allocated. This can happen for a lot of different reasons, but mostly (if not always) is a bug in the program.Phosphorylase
Well, it's not really documented as optional :-) the 'or zero' is to be interpreted for the output value...Raleighraley
I made the mistake of thinking the displacement was optional like in SymFromAddrPhosphorylase
Thanks to you, I learned something today :-). Didn't know about that kind of APIs. Happy hacking!Raleighraley
Do you know how uh...surprised I am that I haven't found such an example of this elsewhere? I keep trying to pioneer these crazy APIs. You might find some of my other Stack questions very interesting, but in particular: read this #20061959 which leads to #20104029Phosphorylase
Shit, I think there's still something wrong because the line number doesn't always come through. First 2 loops it works, then SymGetLineFromAddr64 returns false. The reason why you still see the file name come up in the trace is because its piggybacking off the last struct for line that properly came through...I'm trying to see where its failing, but for sure calling GetLastError() shows us that error code is 487 (access violation?)Phosphorylase
Made a spin-off of this (PS still not entirely sure why the line numbers weren't working for me before but it seems to work with the code I supplied in this thread): #22468104Phosphorylase
Hey, I wrote a blog post on all of this and made a nice wrapper class that people can use to trace the stack, and went into things like Windows Error Reporting (which is honestly a life-saver): dima.to/blog/?p=13Phosphorylase
G
3
SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO));
symbol->MaxNameLen = 1024;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);

The documentation for SizeOfStruct states:

The size of the structure, in bytes. This member must be set to sizeof(SYMBOL_INFO). Note that the total size of the data is the SizeOfStruct + (MaxNameLen - 1) * sizeof(TCHAR). The reason to subtract one is that the first character in the name is accounted for in the size of the structure.

Emphasis mine. You must allocate storage of at least sizeof(SYMBOL_INFO) + MaxNameLen + 1 bytes. You are only allocating sizeof(SYMBOL_INFO) bytes.

Guaiacol answered 17/3, 2014 at 21:18 Comment(13)
That's not the issue. The struct returns addresses correctly. No problem with the SYMBOL_INFO, although I could increase the size there of that struct, sure. The problem is with SymGetLineFromAddr and the IMAGEHLP_LINE structure. Even by increasing the size of SYMBOL_INFO, the problem still exists with the IMAGEHLP_LINE structure not being correctly set.Phosphorylase
It isn't enough, core problem is that he's ignoring the return value and not calling GetLastError() to find out what went wrong.Quadragesima
@HansPassant True. As soon as I see that code may be smashing the heap I tend to stop looking for other problems since the code is already broken...Guaiacol
@HansPassant That makes no sense. Why would calling GetLastError() in this case prevent stack smashing?Phosphorylase
Its unreliable anyways, according to docs: "some functions set the last-error code to 0 on success and others do not."Phosphorylase
It's not unreliable. The documentation for SymGetLineFromAddr expressly states "If the function fails, the return value is FALSE. To retrieve extended error information, call GetLastError."Guaiacol
Oh, you're talking about the return type of SymGetLineFromAddr. I thought you guys meant to check the last error on the start of the method lolPhosphorylase
error is The error code 3221225477 is 0xC0000005 in hex, which on Windows is: #define STATUS_ACCESS_VIOLATION ((NTSTATUS)0xC0000005L) Access violation is Windows' version of "segmentation fault", which simply said means that the program tried to access a memory which is not allocated. This can happen for a lot of different reasons, but mostly (if not always) is a bug in the program.Phosphorylase
Yes, well, as I already pointed out, your program is smashing the heap.Guaiacol
No its not see the answer abovePhosphorylase
sorry it is. i forgot to actually include the out argument when it should have been there.Phosphorylase
When I saw it as an optional value in the result of SymFromAddr i must have assumed it was also optional in the SymGetLineFromAddrPhosphorylase
If you're using C++ and would rather use the new operator instead, you can allocate the block as a char array and use reinterpret_cast<SYMBOL_INFO*>(block)Eldredge

© 2022 - 2024 — McMap. All rights reserved.