The cause turned out to be the symbol search path I was passing to
::SymInitialize( ). I was always specifying the current directory of
the process running my StackWalk code instead of the current directory
of the process whose call stack was being walked. ( the hProcess
passed to SymInitialize( ))
Why was SymGetSymFromAddr so easily confused? If it does not return
an error code in such an situation how can I ever be sure the
information is correct?
thanks,
-Steve
You can analyze what kind of symbols is loaded for the modules in question,
and base your decision on the presence of "good" symbols ("good" means
"good enough for the task you are trying to accomplish").
Or you can show the information about loaded symbols to the user and let him
decide whether he trusts the call stack with the available quality of symbols.
See SymGetModuleInfo64 function and IMAGEHLP_MODULE64 structure.
Also, SYMOPT_DEBUG option can provide additional insight into the process
of loading symbols (both for you and for your users).
Regards,
Oleg
The SymGetSymFromAddr functions works fine on Win 2000 but returns FALSE
on Win Xp. Why?
pls mail me at shilpig at momentum-tech dot com
Thanx in advance
1) What error code is returned by GetLastError ?
2) Check which version of DbgHelp.dll is used on both systems.
DbgHelp.dll is redistributable, so you can always have the latest version
with your application. The latest version is included with Debugging Tools
for Windows package:
http://www.microsoft.com/whdc/ddk/debugging/default.mspx
3) Check what kind of symbols is loaded for the module. Check if loaded symbols
differ in Win2000 and XP.
Enable SYMOPT_DEBUG option and see what DbgHelp thinks about symbol
load process (by default, it will use debug output; you can also provide a callback
function using SymRegisterCallback64).
You can also use SymGetModuleInfo64 to obtain information about symbols
loaded for the module.
4) If you write a new code (but do not maintain something old), consider using
newer function SymFromAddr instead of SymGetSymFromAddr.
Regards,
Oleg
It returns - #126 ( The specified module could not be found.
ERROR_MOD_NOT_FOUND)
> 2) Check which version of DbgHelp.dll is used on both systems.
>
> DbgHelp.dll is redistributable, so you can always have the latest version
> with your application. The latest version is included with Debugging Tools
> for Windows package:
> http://www.microsoft.com/whdc/ddk/debugging/default.mspx
I have downloaded the latest version of dbghelp.dll (6.3.17.0) and
copied this file in the system directory on both the OS. Still the
result is - My program is working on Win2K but it fails (126) on
Winxp.
> 3) Check what kind of symbols is loaded for the module. Check if loaded symbols
> differ in Win2000 and XP.
>
> Enable SYMOPT_DEBUG option and see what DbgHelp thinks about symbol
> load process (by default, it will use debug output; you can also provide a callback
> function using SymRegisterCallback64).
>
> You can also use SymGetModuleInfo64 to obtain information about symbols
> loaded for the module.
How will I do this?
> 4) If you write a new code (but do not maintain something old), consider using
> newer function SymFromAddr instead of SymGetSymFromAddr.
SymFromAddr doesnt work.
> Regards,
> Oleg
My Code is :
#include <stdio.h>
#include <stdlib.h>
#include "windows.h"
#include <process.h>
#include <imagehlp.h>
union SYMBUFFER
{
IMAGEHLP_SYMBOL sym;
BYTE buffer [ sizeof (IMAGEHLP_SYMBOL) + 512 ];
};
unsigned int level = 0;
CONTEXT context;
STACKFRAME frame;
union SYMBUFFER symbol;
IMAGEHLP_MODULE module;
char modulename [512];
DWORD section;
DWORD offset;
HANDLE handle;
#pragma comment( lib, "imagehlp.lib" )
int main()
{
if (! SymInitialize (GetCurrentProcess (), NULL, TRUE))
{
printf("failed to dump stack trace: cannot get symbolic
information\n");
return -1;
}
// FIXME: XP 64-bit adds: RtlCaptureContext (&context);
// This is documented to *not* work, but apparently it does.
context.ContextFlags = CONTEXT_FULL;
if (! GetThreadContext (GetCurrentThread (), &context))
return -1;
memset (&module, 0, sizeof (module));
memset (&frame, 0, sizeof (frame));
module.SizeOfStruct = sizeof (module);
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
if (! StackWalk (IMAGE_FILE_MACHINE_I386,
GetCurrentProcess (),
GetCurrentThread (),
&frame,
&context,
NULL,
SymFunctionTableAccess,
SymGetModuleBase,
NULL)
|| frame.AddrFrame.Offset == 0)
{
printf("StackWalk fail\n");
return -1;
}
// Print stack frame too? If we know how many arguments there
// are (from demangling function name -- see below, could count
// commas), args are: *((ULONG *)frame.AddrFrame.Offset+2+ARG).
printf ("(%2u) 0x%08lx 0x%08lx\n", level, frame.AddrPC.Offset,
frame.AddrFrame.Offset);
memset (&symbol, 0, sizeof (symbol));
symbol.sym.SizeOfStruct = sizeof (symbol);
symbol.sym.MaxNameLength = sizeof (symbol) - sizeof (symbol.sym);
offset = 0;
handle = GetCurrentProcess ();
if (! SymGetSymFromAddr (handle, frame.AddrPC.Offset, &offset,
&symbol.sym))
{
printf("SymGetSymFromAddr fail\n");
return -1;
}
printf("%s", symbol.sym.Name);
printf (" + %lx", offset);
return 0;
}
Dbghelp.dll is protected by Windows File Protection,
so you cannot replace it in the system directory - it will be
replaced by an older version almost immediately (see Event Log
to verify it).
You should store Dbghelp.dll in the same directory with your application -
then your application will be able to use the latest version.
(That's why SymFromAddr does not work, too).
I will take a look at the code later today.
Regards,
Oleg
Now it seems to be clear. The application calls GetThreadContext to obtain
the context for StackWalk, and since it is a system call, the first (and only) frame
returned by StackWalk will have its EIP point to the system call stub
which does not belong to any module. Therefore SymGetSymFromAddr
responds with the given error.
This behaviour is different in Windows 2000 and XP, because of the new
way to call system services in XP. (In Win2000, the first frame returned
by StackWalk will usually belong to NTDLL.DLL, and SymGetSymFromAddr
is usually able to find the symbol)
If this application would continue calling StackWalk after the first call,
it would probably encounter good symbols for subsequent frames.
> > You can also use SymGetModuleInfo64 to obtain information about symbols
> > loaded for the module.
>
> How will I do this?
>
Call the function to obtain module information into IMAGEHLP_MODULE64 structure.
The latest documentation is also with Debugging Tools for Windows
(choose custom setup, install SDK and find the help file in sdk\help folder).
Regards,
Oleg
I tried doing this. But thst too didnt gave me correct output.
My sample code is:
module.SizeOfStruct = sizeof (module);
while( TRUE )
{
if (! StackWalk (IMAGE_FILE_MACHINE_I386,
GetCurrentProcess (),
GetCurrentThread (),
&frame,
&context,
NULL,
SymFunctionTableAccess,
SymGetModuleBase,
NULL)
|| frame.AddrFrame.Offset == 0)
{
printf("StackWalk fail\n");
break;
}
// Print stack frame too? If we know how many arguments there
// are (from demangling function name -- see below, could count
// commas), args are: *((ULONG *)frame.AddrFrame.Offset+2+ARG).
printf ("(%2u) 0x%08lx 0x%08lx\n", level, frame.AddrPC.Offset,
frame.AddrFrame.Offset);
memset (&symbol, 0, sizeof (symbol));
symbol.sym.SizeOfStruct = sizeof (symbol);
symbol.sym.MaxNameLength = sizeof (symbol) - sizeof (symbol.sym);
offset = 0;
handle = GetCurrentProcess ();
if (! SymGetSymFromAddr (handle, frame.AddrPC.Offset, &offset,
&symbol.sym))
{
printf("SymGetSymFromAddr fail\n");
}
else
{
printf("%s", symbol.sym.Name);
printf (" + %lx", offset);
}
}
getch();
return 0;
}
The output on Win 2k:
( 0) 0x77f9be6 0x0012ff80
ZwGetContextThread + b( 0) 0x00401549 0x0012ffc0
mainCRTStartup + e9( 0) 0x7c5987e7 0x0012fff0
ProcessIdToSessionId + 17dStackWalk fail
The output on Win XP:
( 0) 0x7ffe0304 0x0012ff80
SymGetSymFromAddr fail
( 0) 0x00401549 0x0012ffc0
SymGetSymFromAddr fail
( 0) 0x77e814c7 0x0012fff0
SymGetSymFromAddr fail
GetCurrentDirectoryW + 44StackWalk fail
Pls note that I have copied the latest dbghlp.dll (918k of
version-6.3.17.0) in the debug directory (path which contains my
executables).
Pls guide me. This is becoming critical.
Thanx in Advance.
Shilpi
> #pragma comment( lib, "imagehlp.lib" )
>
Your application does not use Dbghelp.dll.
Change the pragma shown above to use dbghelp.lib,
and configure include and lib paths so that they find
the Debugging Tools SDK \inc and \lib\i386 directories
before any other directories.
> The output on Win 2k:
> ( 0) 0x77f9be6 0x0012ff80
> ZwGetContextThread + b( 0) 0x00401549 0x0012ffc0
> mainCRTStartup + e9( 0) 0x7c5987e7 0x0012fff0
> ProcessIdToSessionId + 17dStackWalk fail
>
> The output on Win XP:
> ( 0) 0x7ffe0304 0x0012ff80
> SymGetSymFromAddr fail
> ( 0) 0x00401549 0x0012ffc0
> SymGetSymFromAddr fail
> ( 0) 0x77e814c7 0x0012fff0
> SymGetSymFromAddr fail
> GetCurrentDirectoryW + 44StackWalk fail
>
I suspect that on WinXP it simply cannot find symbols for the executable.
Make sure that you copy the application's symbols (.PDB file, if you use it)
to that system along with the executable.
DbgHelp can provide an additional insight into the process
of loading symbols:
After you have linked the application to Dbghelp.dll,
include the following code _before_ the call to SymInitialize():
DWORD dwOptions = SymGetOptions();
dwOptions |= SYMOPT_DEBUG;
SymSetOptions( dwOptions );
Then run the application on Windows XP under debugger and check
messages with "DBGHELP:" prefix in Debug Output window.
If SymGetSymFromAddr still continues to fail, please post the complete
contents of Debug Output window on Windows XP (with the mentioned
"DBGHELP: xxx" messages).
Regards,
Oleg
First thing I would do is change Imagehlp to DbgHelp. Then use the
StackWalk64 api instead of StackWalk. The documentation says that
Stackwalk64 supercedes Stackwalk.
http://msdn.microsoft.com/library/en-us/debug/base/stackwalk64.asp?frame=true
-Steve
Thanx everybody, esp Oleg