I found two APIs: backtrace() (available only with glibc), and
something called "unwind API", which contains an _Unwind_Bactkrace()
function.
The former is not available in gcc/cygwin, the latter is, but I
couldn't find any example anywhere on how to use it.
Can anybody provide some help with this? If this is not the right
group, can you suggest a better one?
Thanks,
Mario
> I found two APIs: backtrace() (available only with glibc), and
> something called "unwind API", which contains an _Unwind_Bactkrace()
> function.
>
> The former is not available in gcc/cygwin, the latter is, but I
> couldn't find any example anywhere on how to use it.
The latter is pretty much internal to g++ -- AFAICT it is used
while throwing exceptions. As such, it is subject to change without
notice, and the only documentation for it that I've seen is the
libgcc source.
Also note, that you are very unlikely to be able to unwind through
any of the M$ libraries, as these are effectively compiled with
-fomit-frame-pointer.
> Can anybody provide some help with this? If this is not the right
> group, can you suggest a better one?
Since you are using cygwin, perhaps you could use the 'run gdb on self' trick:
http://groups.google.com/group/comp.os.linux.development.apps/msg/c38fcc872f978447
Cheers,
--
In order to understand recursion you must first understand recursion.
Remove /-nsp/ for email.
The output I get from the code is:
[Switching to thread 3420.0xab0]
* 4 thread 3420.0xab0 0x77f75a59 in ntdll!DbgUiConnectToDbg () from
/c/WINDOWS/System32/ntdll.dll
3 thread 3420.0xe24 0x7ffe0304 in ?? ()
2 thread 3420.0xfa4 0x7ffe0304 in ?? ()
1 thread 3420.0xffc 0x7ffe0304 in ?? ()
[Switching to thread 1 (thread 3420.0xffc)]#0 0x7ffe0304 in ?? ()
#0 0x7ffe0304 in ?? ()
#1 0x77f5c524 in ntdll!ZwWaitForMultipleObjects () from
/c/WINDOWS/System32/ntdll.dll
#2 0x77e75f0b in WaitForMultipleObjectsEx () from
/c/WINDOWS/system32/kernel32.dll
#3 0x00000002 in ?? ()
#4 0x0022dc1c in ?? ()
#5 0x00000001 in ?? ()
#6 0x00000000 in ?? () from
Now, thread 1 is in fact the one running main() and the called
functions, but it doesn't show the proper stack trace. I can verifty
that thread 1 is the right one by attaching one more gdb started
manually to the very same app. If I do that and type "info threads" I
get:
* 4 thread 3420.0xfc0 0x77f75a59 in ntdll!DbgUiConnectToDbg () from
/c/WINDOWS/System32/ntdll.dll
3 thread 3420.0xe24 0x7ffe0304 in ?? ()
2 thread 3420.0xfa4 0x7ffe0304 in ?? ()
1 thread 3420.0xffc f () at test.c:31
Any Idea what might be wrong?
-----------------------------------8<---------------------------------------------------------------------
#include "stdio.h"
static char program_name[256];
#define CMD_FILE_NAME "./.teja_gdb_cmds"
void backtrace() {
char hugebuf[512];
FILE *cmds;
cmds = fopen(CMD_FILE_NAME, "w");
if (!cmds) {
printf("unable to obtain stack trace (couldn't open cmd file)\n");
return;
}
fprintf(cmds, "attach %d\n", getpid());
fprintf(cmds, "info threads\n");
fprintf(cmds, "thread 1\n");
fprintf(cmds, "bt\n");
fprintf(cmds, "detach\n");
fprintf(cmds, "quit\n");
fclose(cmds);
snprintf( hugebuf, sizeof hugebuf, "gdb -batch -x %s %s",
CMD_FILE_NAME, program_name);
system(hugebuf);
}
void f() {
backtrace();
while(1);
}
void g() {
f();
}
int main(int argc, char **argv) {
strcpy(program_name, argv[0]);
g();
return 0;
}
> thanks for taking the time to answer this. I was in fact trying to
> start gdb and attach back...but it doesn't seem to work as expected
> under cygwin.
No, I guess not. What's happening here is that the debugger
attachment is actually translated into a "create new thread, and
have it call ntdll!DbgUiConnectToDbg() system call".
At that point your process is stopped inside ntdll.dll (compiled
with optimization, which was called from cygwin.dll (also with
optimization). The code in ntdll.dll is compiled effectively with
-fomit-frame-pointer, and neither gdb, nor devstudio (without access
to system symbols) can unwind the stack from there :(
Any code you write will not be able to unwind the stack either.
However, you may be able to call Microsoft debug API from IMAGEHLP.DLL
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/stackwalk64.asp
If the system is setup to download system symbols (e.g. NTDLL.PDB)
off the net, as described here:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/debug/base/using_symsrv.asp
then StackWalk() does a very reasonable job of unwinding.
However, I never tried StackWalk() with cygwin, and I expect it
will not be able to print function names for any non-exported,
gcc-compiled functions, which will probably make it pretty useless
for you :( You might be in a bind here -- GNU tools don't
work because of MSVC-compiled code, and MSVC tools don't work with
gcc-compiled code. Caveat emptor.
Thanks,
Mario
> forgive me if I'm pedantic.. but could you explain me a little more?
> I don't understand... why is there a difference between running gdb
> through system() from within the app (which doesn't work), and running
> it from another cygwin window (which works)?
Because in the former case your app is blocked inside a system call
(system() calls waitpid(), which in turn calls ntdll!ZwWaitForMultipleObjects()),
while in the latter case your app is not blocked at all, but rather
spins on the 'while (1);' statement in your code (which was compiled
with frame pointer).
> it's my understanding that
> system() just executes the passed command in a new sh.....
and blocks in a system call waiting for that command to return.
Now your app is no longer blocked at the time of the debugger
attachment, so gdb works. No mystery here.
The bad news is that when you get a SIGSEGV, gdb will again not be
able to unwind past the signal handling code and into the code that
actually caused SIGSEGV, and you'll get a useless stack trace,
similar to this:
[Switching to thread 1 (thread 3680.0xe6c)]#0 backtrace () at junk.c:31
31 while(1) ;
#0 backtrace () at junk.c:31
#1 0x00401202 in handler () at junk.c:38 ## this is the SIGSEGV handler
#2 0x61027459 in strerror () from /usr/bin/cygwin1.dll ## this and the rest of the stack are bogus :(
#3 0x6108d282 in sigfillset () from /usr/bin/cygwin1.dll
#4 0x61025327 in strerror () from /usr/bin/cygwin1.dll
#5 0x77fbb272 in ntdll!RtlCopyRangeList () from /cygdrive/c/WINNT/system32/NTDLL.DLL
#6 0x0022ed4c in ?? ()
#7 0x0022f098 in ?? ()
#8 0x0022ed68 in ?? ()
#9 0x0022ed24 in ?? ()
#10 0x0022f098 in ?? ()
#11 0x77fbb286 in ntdll!RtlCopyRangeList () from /cygdrive/c/WINNT/system32/NTDLL.DLL
#12 0x0022f098 in ?? ()
#13 0x0022ed34 in ?? ()
#14 0x77facc28 in ntdll!RtlTraceDatabaseUnlock () from /cygdrive/c/WINNT/system32/NTDLL.DLL
#15 0x0022ed68 in ?? ()
#16 0x0022ed24 in ?? ()
#17 0x61025000 in strerror () from /usr/bin/cygwin1.dll
./.teja_gdb_cmds:4: Error in sourced command file:
Previous frame inner to this frame (corrupt stack?)
Here is the source that produced the stack trace above:
#include "stdio.h"
#include "signal.h"
static char program_name[256];
#define CMD_FILE_NAME "./.teja_gdb_cmds"
void backtrace() {
char hugebuf[512];
FILE *cmds;
cmds = fopen(CMD_FILE_NAME, "w");
if (!cmds) {
printf("unable to obtain stack trace (couldn't open cmd file)\n");
return;
}
fprintf(cmds, "attach %d\n", getpid());
fprintf(cmds, "info threads\n");
fprintf(cmds, "thread 1\n");
fprintf(cmds, "bt\n");
fprintf(cmds, "detach\n");
fprintf(cmds, "quit\n");
fclose(cmds);
snprintf( hugebuf, sizeof hugebuf,
"gdb -batch -x %s %s", CMD_FILE_NAME, program_name);
if (0 == fork()) {
system(hugebuf);
exit(0);
} else {
while(1) ;
}
}
void handler()
{
backtrace();
exit(1);
}
void f() {
int *ip = 0;
ip[1] = 123;
}
void g() {
f();
}
int main(int argc, char **argv) {
strcpy(program_name, argv[0]);
signal(SIGSEGV, handler);
g();
return 0;
I tried to look in the StackWalk API you mentioned, but MSDN
documentation is very poor and I'm not familiar with windows API at
all. Also, the only example I could find is a C++ program...
This shouldn't be so hard!!! I can't believe cygwin does not provide
any way to get a stack trace from an error handler....
> This shouldn't be so hard!!! I can't believe cygwin does not provide
> any way to get a stack trace from an error handler....
There is *no* reasonable way to provide a stack trace on x86 through
code compiled with -fomit-frame-pointer (unless the compiler helps
you by creating special unwind descriptor tables).
The fact that you think this shouldn't be hard unfortunately doesn't
make it so :(
FWIW, Win64 on x64 dramatically improves the situation -- on that
platform you can get correct stack traces regardless of any
optimizations (because the compiler does provide unwind tables).
But, AFAICT cygwin doesn't support Win64 yet ...