Code Comments
Programming Forum and web based access to our favorite programming groups.When one of my daemon workers faults I would like to automatically get a backtrace using gdb in Linux. Unfortunately if that process is not root ptrace returns EPERM because the worker was setuid/setgid to "nobody". So when the code faults and I run my "backtrace" script in the signal handler it chokes. Is there any way for a non-root process to backtrace itself? Thanks, Mike
Post Follow-up to this messageMichael B Allen <mba2000@ioplex.com> writes: > When one of my daemon workers faults I would like to automatically > get a backtrace using gdb in Linux. Unfortunately if that process is > not root ptrace returns EPERM because the worker was setuid/setgid to > "nobody". So when the code faults and I run my "backtrace" script in > the signal handler it chokes. Your description of what you are doing is self-inconsistent: - if the *parent* is trying to invoke gdb on the child, then EPERM is expected, but what does this have to do with the signal handler? - if the child invokes gdb on itself in its signal handler, then there should be no problem with EPERM (gdb will also run as "nobody"), and that is how you probably should solve this problem. Cheers, -- In order to understand recursion you must first understand recursion. Remove /-nsp/ for email.
Post Follow-up to this messageMichael B Allen wrote: > Is there any way for a non-root process to backtrace itself? You could consider using __builtin_return_address instead. See the GCC_DumpStack function in stacktrace.c in http://home1.stofanet.dk/breese/debug/debug.tar.gz -- mail1dotstofanetdotdk
Post Follow-up to this messageOn Wed, 22 Dec 2004 11:25:41 -0500, Paul Pluzhnikov wrote: > > Your description of what you are doing is self-inconsistent: - if the > *parent* is trying to invoke gdb <nope> > - if the child invokes gdb on itself in its signal handler, then > there should be no problem with EPERM (gdb will also run as "nobody"), > and that is how you probably should solve this problem. No. This is what I'm doing but I'm seeing ptrace return EPERM. If you ptrace on a process that started out as "user" then ptraceing as that user will work. But an exe that started out as "root" and then setuid/setgid to "nobody" is inherently different and "nobody" cannot ptrace it even though it is also "nobody". Mike
Post Follow-up to this messageOn Wed, 22 Dec 2004 13:19:31 -0500, Bjorn Reese wrote: > Michael B Allen wrote: > > > You could consider using __builtin_return_address instead. > > See the GCC_DumpStack function in stacktrace.c in > > http://home1.stofanet.dk/breese/debug/debug.tar.gz I just wrote a simple test proggie and it faults: Program received signal SIGSEGV, Segmentation fault. 0x08048a69 in GCC_DumpStack () at stacktrace.c:257 257 p = __builtin_return_address(6); Can I use this fn as is or do I need to actually understand what __builtin_return_address actually is? Also, I was hoping I could get the backtrace inside the sighandler after getting SEGV. But won't this just print the stack of the signal handler? Can I change it to print the stack of the process that faulted? Mike
Post Follow-up to this messageMichael B Allen wrote: > Program received signal SIGSEGV, Segmentation fault. > 0x08048a69 in GCC_DumpStack () at stacktrace.c:257 > 257 p = __builtin_return_address(6); > > Can I use this fn as is or do I need to actually understand what > __builtin_return_address actually is? You will notice from the boiler-plate that the code is quite old. It used to work, but apparently __builtin_return_address has changed semantics in the meantime. I ran the program and got the same crash as you. I can make it work if I change the line p = __builtin_return_address(0); to p = __builtin_frame_address(0); if (p) p = __builtin_return_address(0); and similar for all other __builtin_return_address lines. > Also, I was hoping I could get the backtrace inside the sighandler > after getting SEGV. But won't this just print the stack of the signal > handler? Can I change it to print the stack of the process that faulted? It outputs the entire stack, not just the stack of the signal handler. I get the following output (CrashHandler is the SIGSEGV signal handler) [0] 0x08049443 <StackTrace + 0xb> T [1] 0x0804948d <CrashHandler + 0x1b> T [2] 0x420275c8 ??? [3] 0x080494ac <Crash + 0x15> T [4] 0x0804952d <main + 0x7c> T [5] 0x42015574 ??? [6] 0x08048729 <_start + 0x21> T -- mail1dotstofanetdotdk
Post Follow-up to this messageOn Fri, 24 Dec 2004 07:07:01 -0500, Bjorn Reese wrote: > Michael B Allen wrote: > > > You will notice from the boiler-plate that the code is quite old. It > used to work, but apparently __builtin_return_address has changed > semantics in the meantime. > > I ran the program and got the same crash as you. I can make it work if I > change the line > > p = __builtin_return_address(0); > > to > > p = __builtin_frame_address(0); > if (p) > p = __builtin_return_address(0); > > and similar for all other __builtin_return_address lines. > Still faults for me anyway: Program received signal SIGSEGV, Segmentation fault. 0x08048aaf in GCC_DumpStack () at stacktrace.c:264 264 p = __builtin_return_address(6); (gdb) bt #0 0x08048aaf in GCC_DumpStack () at stacktrace.c:264 #1 0x0804962f in fn0 (i=10) at t.c:7 #2 0x0804964c in fn1 (i=11) at t.c:14 #3 0x0804966c in fn2 (i=11) at t.c:19 #4 0x0804969a in main () at t.c:25 #5 0x42017499 in __libc_start_main () from /lib/i686/libc.so.6 If I cut down the ADDRESSLIST_SIZE to 6 it sort of works: $ ./t [0] 0x0804937b ??? [1] 0x08049398 ??? [2] 0x080493b8 ??? [3] 0x080493e6 ??? [4] 0x42017499 ??? [5] 0x08048741 ??? I removed 'static' from GCC_DumpStack, added a prototype to stacktrace.h, and compiled as follows: $ gcc -Wall -W -g -DUSE_BUILTIN -c -o stacktrace.o stacktrace.c stacktrace.c: In function `GCC_DumpStack': stacktrace.c:225: warning: unused variable `pname' $ gcc -Wall -W -g stacktrace.o -o t t.c I also tried: $ gcc -Wall -W -g -DUSE_BUILTIN stacktrace.c -o t t.c stacktrace.c: In function `GCC_DumpStack': stacktrace.c:225: warning: unused variable `pname' No difference. $ gcc -v Reading specs from /usr/lib/gcc-lib/i386-redhat-linux/2.96/specs gcc version 2.96 20000731 (Red Hat Linux 7.3 2.96-110) Is there any documentation on this __builtin_return_address thing? Thanks, Mike
Post Follow-up to this messageMichael B Allen wrote: > If I cut down the ADDRESSLIST_SIZE to 6 it sort of works: Well, the problem is detecting the top of the call stack, so lowering ADDRESSLIST_SIZE will only avoid the crash if you know that your call stack is more than 6 frames deep. __builtin_return_address ought to return 0 in that case, but instead it simply crashes. > [0] 0x0804937b ??? It does not resolve the symbols. Is nm in your path? > gcc version 2.96 20000731 (Red Hat Linux 7.3 2.96-110) Ah, the old version of gcc could be the problem. > Is there any documentation on this __builtin_return_address thing? Yes, it is a built-in function of gcc, so you can find the description in 'info gcc' (at least in later versions of gcc.) -- mail1dotstofanetdotdk
Post Follow-up to this messageBjorn Reese wrote: > > > Ah, the old version of gcc could be the problem. According to my readings (just some googling over the last 24 hours or so) __buildin_return_address() is not guaranteed to return 0 when reaching stack top due to limitations of certain architectures, and supposedly x86 is one of those. -- Henry Townsend
Post Follow-up to this messageHeny Townsend wrote:
> According to my readings (just some googling over the last 24 hours or
> so) __buildin_return_address() is not guaranteed to return 0 when
> reaching stack top due to limitations of certain architectures, and
> supposedly x86 is one of those.
Yes, this is why I suggested the __builtin_frame_address() workaround.
However, that only works if (according to the documentation) "the
first frame pointer is properly initialized by the startup code";
however one achieves that...
Another workaround could be to iterate through the call stack until
we find an address inside the main() function. The could be done
by adding the following two lines (marked by +) in GCC_DumpStack()
if ((p) && (i < ADDRESSLIST_SIZE))
{
syms[i].realAddress = (unsigned long)p;
syms[i].closestAddress = 0;
syms[i].name[0] = (char)0;
syms[i].type = ' ';
+ if ((p > (void *)main) && (p < (void *)main_end))
+ break; /* for */
This solution requires a pointer to the end of the main function.
I did this by adding an "void main_end(void) {}" immediately after
the main function. Better solutions are most welcome.
--
mail1dotstofanetdotdk
Post Follow-up to this message
Show a Printable Version
Email This Page to Someone!
Receive updates to this thread
Powered by vBulletin
Copyright 2000-2006 Jelsoft Enterprises Limited.