|
The function StackWalk() called from within GetFuncName() fails with error code: error_partial_copy (299). I searched for the Internet for this error code in this context but couldn't find much help.
|
|
|
|
|
Note that GetFuncInfo doesn't use the frame offset parameter. Is it suppose to?
|
|
|
|
|
The call to SymGetSymFromAddr() in GetFuncInfo() fails due to GetCurrentProcess() returning 0xffffffff. GetLastError() at this point gives 6, which is ERROR_INVALID_HANDLE.
I'm running this in an ATL coclass. I'm calling calling InitSymInfo() during FinalConstruct() and __FUNCTION__ is being called in a COM interface method on the coclass.
|
|
|
|
|
You are probably calling it from a thread otside of the process context; if you could somehow get a process id, try getting the process handle by calling the OpenProcess function. GetCurrentProcessId function returns the process identifier of the calling process, so that might do it.
|
|
|
|
|
I got the current process id by GetCurrentProcessId , then I call OpenProcess , it returned 0x00000000.
Thanks!
|
|
|
|
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* xtrace.cpp by Hector J. Rivas, torjo2k@hotmail.com from "ExtendedTrace"
* by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com
*
* A Win32 VC++ 6.0 implementation of the __FUNCTION__ macro that works for me.
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
#if defined(_DEBUG) && defined(WIN32)
#include <stdio.h>
#include <windows.h>
#include <tchar.h>
#include <ImageHlp.h>
#include "xtrace.h"
// Unicode safe char* -> TCHAR* conversion
void PCSTR2LPTSTR(PCSTR lpszIn, LPTSTR lpszOut)
{
#if defined(UNICODE) || defined(_UNICODE)
ULONG index = 0;
PCSTR lpAct = lpszIn;
for(;; lpAct++)
{
lpszOut[index++] = (TCHAR)(*lpAct);
if (*lpAct == 0) break;
}
#else
strcpy(lpszOut, lpszIn);
#endif
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* InitSymPath(): figure out the path for the symbol files; the search
path is:
*
* . +
* __FILE__ (path) + Debug +
* %_NT_SYMBOL_PATH% +
* %_NT_ALTERNATE_SYMBOL_PATH% +
* %SYSTEMROOT% +
* %SYSTEMROOT%\System32 +
* lpszIniPath
*
* NOTES: There is no size check for lpszSymbolPath. If you want to limit the
macro to
* symbols in your debug executable, you can omit the environment variables (
default).
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
void InitSymPath(PSTR lpszSymbolPath, PCSTR lpszIniPath, BOOL bSysPath)
{
CHAR lpszPath[BUFFERSIZE] = "";
CHAR lpszTemp[BUFFERSIZE] = "";
// create the default path
strcpy(lpszSymbolPath, ".;");
// get the current path
sprintf(lpszTemp, __FILE__);
strcpy(lpszPath, strrev(strchr(strrev(lpszTemp), '\\')));
strcat(lpszPath, "Debug");
strcat(lpszSymbolPath, lpszPath);
if (bSysPath)
{
// environment variable _NT_SYMBOL_PATH
if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE))
{
strcat(lpszSymbolPath, ";");
strcat(lpszSymbolPath, lpszPath);
}
// environment variable _NT_ALTERNATE_SYMBOL_PATH
if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", lpszPath,
BUFFERSIZE))
{
strcat(lpszSymbolPath, ";");
strcat(lpszSymbolPath, lpszPath);
}
// environment variable SYSTEMROOT
if (GetEnvironmentVariableA("SYSTEMROOT", lpszPath, BUFFERSIZE))
{
strcat(lpszSymbolPath, ";");
strcat(lpszSymbolPath, lpszPath);
// SYSTEMROOT\System32
strcat(lpszSymbolPath, ";");
strcat(lpszSymbolPath, lpszPath);
strcat(lpszSymbolPath, "\\System32");
}
}
// Add any user defined path
if (lpszIniPath != NULL)
{
if (lpszIniPath[0] != '\0')
{
strcat(lpszSymbolPath, ";");
strcat(lpszSymbolPath, lpszIniPath);
}
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* InitSymInfo(): initializes the symbol files
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
BOOL InitSymInfo(PCSTR lpszInitialSymbolPath, BOOL bSysPath)
{
CHAR lpszSymbolPath[BUFFERSIZE];
DWORD symOptions = SymGetOptions();
// set current image help API options; according to the SDK docs, with
// SYMOPT_DEFERRED_LOADS: "Symbols are not loaded until a reference is made
// requiring the symbols be loaded. This is the fastest, most efficient way
to use
// the symbol handler.". SYMOPT_UNDNAME is excluded to do the undecoration
// ourselves.
symOptions |= SYMOPT_DEFERRED_LOADS;
symOptions &= ~SYMOPT_UNDNAME;
SymSetOptions(symOptions);
// get the search path for the symbol files
InitSymPath(lpszSymbolPath, lpszInitialSymbolPath, bSysPath);
return SymInitialize(GetCurrentProcess(), lpszSymbolPath, TRUE);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* GetFuncInfo(): Get function prototype from address and stack address
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
BOOL GetFuncInfo(ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol)
{
BOOL ret = FALSE;
DWORD dwDisp = 0, dwSymSize = 10000;
TCHAR lpszUDSymbol[BUFFERSIZE] = _T("?");
CHAR lpszANSIUDSymbol[BUFFERSIZE] = "?";
PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc(GMEM_FIXED, dwSymSize);
ZeroMemory(pSym, dwSymSize);
pSym->SizeOfStruct = dwSymSize;
pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL);
// Set the default to unknown
_tcscpy(lpszSymbol, _T("?"));
// Get symbol info
if (SymGetSymFromAddr(GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym))
{
// Make the symbol readable for humans
UnDecorateSymbolName(pSym->Name,
lpszANSIUDSymbol,
BUFFERSIZE,
UNDNAME_COMPLETE |
UNDNAME_NO_THISTYPE |
UNDNAME_NO_SPECIAL_SYMS |
UNDNAME_NO_MEMBER_TYPE |
UNDNAME_NO_MS_KEYWORDS |
UNDNAME_NO_ACCESS_SPECIFIERS |
UNDNAME_NO_ARGUMENTS);
// Symbol information is ANSI string
PCSTR2LPTSTR(lpszANSIUDSymbol, lpszUDSymbol);
lpszSymbol[0] = _T('\0');
_tcscat(lpszSymbol, lpszUDSymbol);
ret = TRUE;
}
GlobalFree(pSym);
return ret;
}
#ifndef _MANUAL_INIT
class InitXTrace {
public:
InitXTrace()
{
InitSymInfo(NULL);
}
} initXTrace;
#endif // #ifndef _MANUAL_INIT
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* GetFuncName(): return the undecorated function name
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
LPCTSTR GetFuncName()
{
BOOL bResult = FALSE;
STACKFRAME callStack;
TCHAR lpszFnInfo[BUFFERSIZE];
HANDLE hProcess = GetCurrentProcess();
HANDLE hThread = GetCurrentThread();
// initialize a stack frame struct in preparation to walk the stack
ZeroMemory(&callStack, sizeof(callStack));
__asm {
call getEIP;
getEIP:
pop callStack.AddrPC.Offset;
mov callStack.AddrStack.Offset, Esp;
mov callStack.AddrFrame.Offset, Ebp;
}
callStack.AddrPC.Mode = AddrModeFlat;
callStack.AddrStack.Mode = AddrModeFlat;
callStack.AddrFrame.Mode = AddrModeFlat;
// obtain a stack trace of the calling function (i.e., omit this one)
for (ULONG n = 0; n < 2; n++)
{
bResult = StackWalk(IMAGE_FILE_MACHINE_I386,
hProcess,
hThread,
&callStack,
NULL,
NULL,
SymFunctionTableAccess,
SymGetModuleBase,
NULL);
}
if (bResult && callStack.AddrFrame.Offset != 0)
{
GetFuncInfo(callStack.AddrPC.Offset, callStack.AddrFrame.Offset, lpszFnInfo);
// from now on its all personal display preferences with string manipulation
// tokenize the undecorated returned symbol to omit the class name
CHAR* lpszToken = strtok(lpszFnInfo, "::");
CHAR lpszLast[BUFFERSIZE] = "";
while (lpszToken != NULL)
{
strcpy(lpszLast, lpszToken);
lpszToken = strtok(NULL, "::");
}
// append a delimiter, so that our display in printf instructions is
// 'functionname: message' for debug builds and 'message' for release builds,
// using the format string "%smessage" and __FUNCTION__ as an argument
strcat(lpszLast, ": ");
return lpszLast;
}
return "";
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * *
* KillSymInfo(): uninitialize the loaded symbol files
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * */
BOOL KillSymInfo() { return SymCleanup(GetCurrentProcess()); }
#endif //_DEBUG && WIN32
|
|
|
|
|
What does these changes fix?
Chris
|
|
|
|
|
from MSDN:
You cannot get a valid context for a running thread. Use the SuspendThread function to suspend the thread before calling GetThreadContext.
so, that result of "GetThreadContext" is invalid, I removed the GetThreadContext calling. change to inline assembler.
|
|
|
|
|
|
I am trying to use your function in a console application in debug mode and it is not working? Any suggestions?
Chris
|
|
|
|
|
It works for me in debug console mode after removing the compile option "/Zp1". Hope this will help you.
|
|
|
|
|
ir does not work to me even after removing the option from options.
I have done changes in project-->settings-->compile options.
is this correct?
|
|
|
|
|
Not working for me either. I know I should not be using VC6 anymore, but I am! Any clues from the gurus out there?
Conrad -
Always do badly to start off, that way when you get the hang of it suddenly, everyone is surprised.
|
|
|
|
|
Is there any way to make __FUNCTION__ work in release mode. Right now it just returns an empty string. I would like to have __FUNCTION__ return the function name in release mode as well. The only way I think it may be possible is to replace NODEBUG preprocessor definition with _DEBUG for the release version. Is there any other way to make __FUNCTION__ work besides doing that?
Thanks Chris
|
|
|
|
|
To use it in release mode I guess the easiest thing to do would be to remove the #if define/#else/#endif lines pertaining to the DEBUG.
|
|
|
|
|
I tried this and there is a function called "StackWalk" used in the "LPCTSTR GetFuncName()" function that returns false and gives an invalid "callStack.AddrFrame.Offset"?
Chris
|
|
|
|
|
Sorry I took so long to reply; somehow I wasn't getting the emails.
Anyway, I tried making it work in the release build with no success, but since there's no way of debugging it I cannot see where it fails! Well at least I got away with finding out that it is the SymGetSymFromAddr function that fails, for the fnAddress argument is always invalid.
I guess there is no symbol table for release builds. I'll keep looking into it, and let you know.
Sorry about it: StackWalk and UnDecorateSymbolName are debugging service functions of the ImageHlp API DLL, so I guess they will only be available for debug builds; Still, there is a way to create a symbol file for an executable (a .DBG) file; that may be the way to go.
-- modified at 21:34 Wednesday 12th July, 2006
|
|
|
|
|
To me it is not working even after disabling #if DEBUG.
Can you suggest how do I go about this...
|
|
|
|
|
I am getting SysGetModuleBase instead of actual function in release mode.
did I miss out something?
|
|
|
|
|
|
I guess some of them are self explanatory, but here it goes.
Bear in mind that functions in C and C++ programs are known internally by their decorated names. A decorated name is a string created by the compiler during compilation of the function definition or prototype.
Here's an example of a decorated function name:
?func1@a@@AAEXH@Z
and it's undecorated version:
private: void __thiscall a::func1(int)
so the flags I used are there for the purpose of retrieving "func1" exclusively.
UNDNAME_COMPLETE
This flag actually has a value of 0x0000, so I guess complete undecoration
is always performed.
UNDNAME_NO_THISTYPE
Disable all modifiers on the this type. I'm guessing, but it probably gets rid of "private: void __thiscall" in the example above.
UNDNAME_NO_SPECIAL_SYMS
Do not undecorate special names, such as vtable, vcall, vector, metatype, and so on. In other words, get rid of them; These names are inherent to the procedure stack in C++, and I want nothing to do with them.
UNDNAME_NO_MEMBER_TYPE
Disable expansion of the static or virtual attribute of members. I guess it gets rid of those keywords.
UNDNAME_NO_MS_KEYWORDS
Disable expansion of Microsoft keywords. What Microsoft considers special is up to them.
UNDNAME_NO_ACCESS_SPECIFIERS
Disable expansion of access specifiers for members. Dispenses of the "::" and hopefully the class names.
UNDNAME_NO_ARGUMENTS
Do not undecorate function arguments. Don't even show them!
Well I hope this helps.
|
|
|
|
|
callStack.AddrFrame.Offset is 0 so I get an empty string.
If I comment out the if just to get into the code to pull the string:
// if (bResult && callStack.AddrFrame.Offset != 0)
I get {"DbgHelpCreateUserDump: "} as the function.
My OS is XP.
-- modified at 11:41 Thursday 2nd March, 2006
I'm not building the exe to the default directory - could that be the problem?
What's the name of the symbol file?
|
|
|
|
|
Try adding the exact path to your executable in the InitSymPath function, say C:\\My Systems\\My App\\Debug; if that does not work, you will have to check the documentation for any differences in the image api between W2K and XP, specially the SymInitialize function.
I hope that helps.
|
|
|
|
|
I'm having the same problems.
I too output to a different directory than the defaults, but I tried using the default debug directory but to no avail.
Stepping through the code I didn't notice anything a miss but I'm not too hot with the Win32 API internals (hence using this code!).
|
|
|
|
|
make sure you are doing the correct number of iterations when walking the stack.
The callStack.AddrFrame.Offset is set from the EBP register
directly, which is part of the CONTEXT section that is returned if the
ContextFlags word contains the flag CONTEXT_CONTROL, so try adding the flag
before retrieving the thread's context:
context.ContextFlags = CONTEXT_FULL | CONTEXT_CONTROL;<br />
<br />
if (!GetThreadContext(hThread, &context))<br />
return "";
See WINNT.H for more details!
|
|
|
|
|