Unix Programming - how do I print stack

This is Interesting: Free IT Magazines  
Home > Archive > Unix Programming > May 2005 > how do I print stack





You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

Author how do I print stack
Billy Patton

2005-05-04, 6:01 pm

I have this define statement:
static char[4096] thr_buf;
#define THROW(msg) { \
sprintf(thr_buf,"(%s,%s,%d) : %s"\
,__FILE__\
,__FUNCTION__\
,__LINE__\
,msg); \
throw(thr_buf); }


I'm using this in c++
g++ 3.4.1
solaris and Linux.

This works file to give me the some information.
Problem is, where did this function get called from?

ex:

main
try {
switch (token)
case a :
get_long();
break;
case b :
get_long();
get_long()
break;
}
catch {
...
}
}

long get_long()
{
THROW("this failed")
}

How can I get the trace back to show where get_long was called from?

___ _ ____ ___ __ __
/ _ )(_) / /_ __ / _ \___ _/ /_/ /____ ___
/ _ / / / / // / / ___/ _ `/ __/ __/ _ \/ _ \
/____/_/_/_/\_, / /_/ \_,_/\__/\__/\___/_//_/
/___/
Texas Instruments ASIC Circuit Design Methodology Group
Dallas, Texas, 214-480-4455, b-patton@ti.com
Pascal Bourguignon

2005-05-04, 8:48 pm

Billy Patton <bpatton@ti.com> writes:

> I have this define statement:
> static char[4096] thr_buf;
> #define THROW(msg) { \
> sprintf(thr_buf,"(%s,%s,%d) : %s"\
> ,__FILE__\
> ,__FUNCTION__\
> ,__LINE__\
> ,msg); \
> throw(thr_buf); }
>
>
> I'm using this in c++
> g++ 3.4.1
> solaris and Linux.
>
> This works file to give me the some information.
> Problem is, where did this function get called from?
>
> ex:
>
> main
> try {
> switch (token)
> case a :
> get_long();
> break;
> case b :
> get_long();
> get_long()
> break;
> }
> catch {
> ...
> }
> }
>
> long get_long()
> {
> THROW("this failed")
> }
>
> How can I get the trace back to show where get_long was called from?


This is highly implementation and processor dependent.

You could write some assembler to fetch the stack frame pointer,

Or you could guess the stack frame pointer (offset implementation and
processor dependent) with something like:

void* get_stack_frame(void* arg){return((&arg)[-2]);}

For example, with gcc-3.3 on ix86, it gives:
/*
gcc -S a.c -o a.s ; cat a.s
.file "a.c"
.text
..globl get_stack_frame
.type get_stack_frame, @function
get_stack_frame:
pushl %ebp
movl %esp, %ebp
movl 0(%ebp), %eax
popl %ebp
ret
.size get_stack_frame, .-get_stack_frame
.ident "GCC: (GNU) 3.3 20030226 (prerelease) (SuSE Linux)"
*/

then you'd have to write a processor dependant routine to walk the
stack, something like:

#include <stdio.h>
void* get_stack_frame(void* arg){ return((&arg)[-2]);}
typedef struct ix86_gcc33_stack_frame {
struct ix86_gcc33_stack_frame* next;
void* caller;
long arguments[0];
} ix86_gcc33_stack_frame_t;

void backtrace(ix86_gcc33_stack_frame_t* frame){
while(frame){
printf("frame=%10p { next=%10p caller=%10p arg[0]=%10lx }\n",
frame,frame->next,frame->caller,frame->arguments[0]);
frame=frame->next; }}

void r(int a)& #123;backtrace((ix86_gcc33_stack_frame_t
* )get_stack_frame((void*)a));}
void q(int a){r(1+a);}
void p(int a){q(1+a);}
int main(void){p(1);return(0);}
/*
gcc a.c ; ./a.out
frame=0xbfffe588 { next=0xbfffe5a8 caller= 0x80483dd arg[0]= 3 }
frame=0xbfffe5a8 { next=0xbfffe5c8 caller= 0x80483f5 arg[0]= 2 }
frame=0xbfffe5c8 { next=0xbfffe5e8 caller= 0x8048414 arg[0]= 1 }
frame=0xbfffe5e8 { next=0xbfffe608 caller=0x4002b857 arg[0]= 1 }
frame=0xbfffe608 { next= (nil) caller= 0x80482c1 arg[0]= 80483fa }

Compilation finished at Thu May 5 02:03:58
*/


Perhaps you could look at the source of gdb to see how it's done on
different processors, and given the caller addresses, how to find the
name of the functions from the debugging symbols in the executable
file.


But really, either you need this feature to debug your program, then
just use gdb, or if you need it to deliver it, why not deliver gdb
with it and a script that launches the program always with gdb?


--
__Pascal Bourguignon__ http://www.informatimago.com/

This is a signature virus. Add me to your signature and help me to live
David Schwartz

2005-05-04, 8:48 pm


"Pascal Bourguignon" <pjb@informatimago.com> wrote in message
news:87wtqe4juj.fsf@thalassa.informatimago.com...

> But really, either you need this feature to debug your program, then
> just use gdb, or if you need it to deliver it, why not deliver gdb
> with it and a script that launches the program always with gdb?


If you're using gcc, it has all this capability built in. Look up
__builtin_return_address. In conjunction with 'dladdr', this can provide a
full call stack on request from inside the program. As for why you might
want to do this without 'gdb', what if you want to track the stack in
non-fatal cases without disrupting program operation?

DS


David Resnick

2005-05-05, 2:51 am


Billy Patton wrote:
> I have this define statement:
> static char[4096] thr_buf;
> #define THROW(msg) { \
> sprintf(thr_buf,"(%s,%s,%d) : %s"\
> ,__FILE__\
> ,__FUNCTION__\
> ,__LINE__\
> ,msg); \
> throw(thr_buf); }
>
>
> I'm using this in c++
> g++ 3.4.1
> solaris and Linux.
>
> This works file to give me the some information.
> Problem is, where did this function get called from?
>
> ex:
>
> main
> try {
> switch (token)
> case a :
> get_long();
> break;
> case b :
> get_long();
> get_long()
> break;
> }
> catch {
> ...
> }
> }
>
> long get_long()
> {
> THROW("this failed")
> }
>
> How can I get the trace back to show where get_long was called from?
>
> ___ _ ____ ___ __ __
> / _ )(_) / /_ __ / _ \___ _/ /_/ /____ ___
> / _ / / / / // / / ___/ _ `/ __/ __/ _ \/ _ \
> /____/_/_/_/\_, / /_/ \_,_/\__/\__/\___/_//_/
> /___/
> Texas Instruments ASIC Circuit Design Methodology Group
> Dallas, Texas, 214-480-4455, b-patton@ti.com


Have a look at here:

http://www.gnu.org/software/libc/ma...gging%20Support

backtrace together with backtrace_symbols_fd probably do what you
want...

-David

Pascal Bourguignon

2005-05-05, 2:51 am

"David Schwartz" <davids@webmaster.com> writes:

> "Pascal Bourguignon" <pjb@informatimago.com> wrote in message
> news:87wtqe4juj.fsf@thalassa.informatimago.com...
>
>
> If you're using gcc, it has all this capability built in. Look up
> __builtin_return_address. In conjunction with 'dladdr', this can provide a
> full call stack on request from inside the program. As for why you might
> want to do this without 'gdb', what if you want to track the stack in
> non-fatal cases without disrupting program operation?


You could use longjmp to do that.

For example, encapsulating it as:

#define FATAL 1
#define BADARG 2

int f(int simon,int arg){
if(simon){ BcRAISE(FATAL,"Simon said fata!",0); }
if(arg<0){ BcRAISE(BADARG,"arg must not be negative",arg); }
return(2*arg);
}

void demo(void){
BcTRY
BcRETURNVALUE(f(simon,arg));
BcHANDLER
switch(BcEXCEPTIONCODE){
case FATAL: printf("got fatal %s; resuming.\n",BcEXCEPTIONDATA1);
break;
case BADARG: printf("got bad argumennt %s (%d).\n",
BcEXCEPTIONDATA1,(int)BcEXCEPTIONDATA2);

return(demo(simon,1+arg));
default: printf("got an unexpected exception\n");
BcRERAISE;
}
BcENDTRY
}


/ ****************************************
**************************************
FILE: BcExcept.h
LANGUAGE: ANSI-C
SYSTEM: ANSI (setjmp)
USER-INTERFACE: None
DESCRIPTION
In routine or in any procedure (recursively) while inside TRY/HANDLER block
you can call RAISE(resultCode,data1,data2).
AUTHORS
<PJB> Pascal J. Bourguignon
MODIFICATIONS
Revision 1.1 2001/06/05 06:38:18 uid1006
Added common libraries and dosname utility to the repository.

Revision 1.1 2001/04/30 01:58:07 pascal
Initial entry into CVS.

Revision 1.3 95/11/24 17:27:01 pascal
Renamed BcExcept objects.

Revision 1.2 95/11/16 12:07:09 pascal
Some small correction to compile in MSVC.

Revision 1.1 95/11/16 06:37:37 pascal
Initial revision

1992-01-27 <PJB> Creation. Adapted from old Except.
1992-12-21 <PJB> Translated from Modula2.
1993-03-19 <PJB> Renamed BcExcept for "B"ourguignon's "C" library.
(instead of "B"ourguignon's "O"bjective-C library, or
"B"ourguignon's C"P"lusplus library).
This is needed for we can mix Objective-C with CPlusPlus
in the same sources.
LEGAL
Copyright Pascal J. Bourguignon 1992 - 2002

This file is part of the bclib library.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING; if not, write to
the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
****************************************
**************************************/
#ifndef __BcExcept__
#define __BcExcept__ defined
/* standard*/

#ifdef MSWINDOWS
#include <winstjmp.h>
#else
#include <setjmp.h>
#endif


typedef struct BcTRY_EnvironmentT* BcTRY_EnvironmentP;
typedef struct BcTRY_EnvironmentT {
jmp_buf jmpEnv;
BcTRY_EnvironmentP next;
int code;
const void* data1;
const void* data2;
} BcTRY_EnvironmentT;

extern BcTRY_EnvironmentP BcTRY_CurrentEnvironment;



#define BcTRY \
{ BcTRY_EnvironmentT __tryEnvironment; \
__tryEnvironment.code=0; \
__tryEnvironment.next=BcTRY_CurrentEnvironment; \
BcTRY_CurrentEnvironment=&__tryEnvironment; \
if(setjmp(BcTRY_CurrentEnvironment->jmpEnv)==0){

external void BcRAISE(INT32 code,const void* data1,const void* data2);
/*
DO: exits from the last TRY called.
*/

#define BcRERAISE (BcRAISE(__tryEnvironment.code,\
__tryEnvironment.data1,__tryEnvironment.data2))

#if defined(MPW) || defined(AIX)
/* !!! Warning !!!*/
/* !!! Warning !!! With MPW-C, RETURNVALUE can be applied only to simple*/
/* !!! Warning !!! values*/
/* !!! Warning !!!!*/
#define BcRETURNVALUE(val) \
do { \
BcTRY_CurrentEnvironment=BcTRY_CurrentEn
vironment->next; \
return(val); \
} while(0);
#else
#define BcRETURNVALUE(val) \
do { \
typeof(val) temp=(val); \
BcTRY_CurrentEnvironment=BcTRY_CurrentEn
vironment->next; \
return(temp); \
} while(0);
#endif

#define BcRETURNVOID \
do { \
BcTRY_CurrentEnvironment=BcTRY_CurrentEn
vironment->next; \
return; \
} while(0);


#define BcHANDLER \
BcTRY_CurrentEnvironment=BcTRY_CurrentEn
vironment->next; \
}else{ \
BcTRY_CurrentEnvironment=BcTRY_CurrentEn
vironment->next;

#define BcEXCEPTIONCODE (__tryEnvironment.code)

#define BcEXCEPTIONDATA1 (__tryEnvironment.data1)

#define BcEXCEPTIONDATA2 (__tryEnvironment.data2)

#define BcENDTRY }}

#endif /*BcExcept.*/




/ ****************************************
**************************************
FILE: BcExcept.c
LANGUAGE: ANSI-C
SYSTEM: ANSI (setjmp)
USER-INTERFACE: None
DESCRIPTION
In routine or in any procedure (recursively) while inside TRY/HANDLER block
you can call RAISE(resultCode).
AUTHORS
<PJB> Pascal J. Bourguignon
MODIFICATIONS
Revision 1.1 2001/06/05 06:38:19 uid1006
Added common libraries and dosname utility to the repository.

Revision 1.1 2001/04/30 01:58:08 pascal
Initial entry into CVS.

Revision 1.3 95/11/24 17:26:49 pascal
Renamed BcExcept objects.

Revision 1.2 95/11/16 12:15:24 pascal
Small corrections to compile with MSVC.

Revision 1.1 95/11/16 06:28:33 pascal
Initial revision

1992-01-27 <PJB> Creation. Adapted from old Except.
1992-12-21 <PJB> Translated from Modula2.
1993-03-19 <PJB> Renamed BcExcept for "B"ourguignon's "C" library.
(instead of "B"ourguignon's "O"bjective-C library, or
"B"ourguignon's C"P"lusplus library).
This is needed for we can mix Objective-C with CPlusPlus
in the same sources.
LEGAL
Copyright Pascal J. Bourguignon 1992 - 2002

This file is part of the bclib library.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; see the file COPYING; if not, write to
the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
Boston, MA 02111-1307 USA
****************************************
**************************************/
/* standard*/
#include <stdio.h>
#include <stdlib.h> /* exit */
#ifdef UNIX
#include <signal.h>
#include <unistd.h>
#endif
#include <setjmp.h>
/* bclib*/

const char BcExcept_ID[]=
"$Id: BcExcept.c,v 1.2 2004/01/02 00:59:24 pjbpjb Exp $";

BcTRY_EnvironmentP BcTRY_CurrentEnvironment=NIL;

void BcRAISE(INT32 code,const void* data1,const void* data2)
{
if(BcTRY_CurrentEnvironment==NIL){
fprintf(stderr,"BpExcept_BcRAISE: Uncaught exception code=%ld data1=%p data2=%p\n",code,data1,data2);
#ifdef UNIX
/* We use a kill instead of exit to let the debugger catch it here. */
signal(SIGQUIT,SIG_DFL);
kill(getpid(),SIGQUIT);
#else
exit(1);
#endif
}else{
BcTRY_CurrentEnvironment->code=code;
BcTRY_CurrentEnvironment->data1=data1;
BcTRY_CurrentEnvironment->data2=data2;
longjmp(BcTRY_CurrentEnvironment->jmpEnv,1);
}
}/*BcRAISE;*/

/*** BcExcept.c -- 2004-01-01 09:38:50 -- pascal ***/




--
__Pascal Bourguignon__ http://www.informatimago.com/

Nobody can fix the economy. Nobody can be trusted with their finger
on the button. Nobody's perfect. VOTE FOR NOBODY.
Billy Patton

2005-05-05, 6:03 pm

I had some interesting methods of back tracing.
Unfortunately these only provided me with function names.
My problem is that I have, in the main a switch statement than call one
function ,laff_get_long, 47 times. I need to know which line it was called
from. I don't want to surround each call with a try->catch That would create
a lot of clutter in the code.

#include <iostream>
#include <string>
#include <execinfo.h>

char thr_buf[4096];
#define THROW(msg) { \
sprintf(thr_buf,"(file:%s,line# %d) : %s\n",__FILE__,__LINE__,msg);
throw(thr_buf); }

using namespace std;
long laff_get_long()
{
return 10;
}
long laff_get_longx()
{
THROW("bummer");
}

#define GET_LONG(x) \
try { x = laff_get_long(); } \
catch (const char* msg) \
{ THROW(msg); }

#define GET_LONGX(x) \
try { x = laff_get_longx(); } \
catch (const char* msg) \
{ THROW(msg); }


int main(void)
{
long l,k;
try
{
GET_LONG(l);
cout << "l = " << l << "\n";
GET_LONGX(k);
}
catch (const char* msg)
{
cout << "exception caught\n " << msg << "\n";
}
return 0;
}

What I'm expecting this to expand to is
main
try
{
try
{
l = laff_get_long();
cout << "l = " << l << "\n";
}
catch (const char* msg)
{
THROW(msg);
}
try
{
k = laff_get_longx();
cout << "k = " << k << "\n";
}
catch (const char* msg)
{
THROW(msg);
}
}
catch (const char* msg)
{
cout << exception caught\n " << msg << "\n";
}

What I would expect
would be the line number inside laff_get_longx and teh line number where
laff_get_long is called in main. Instead it just shows the same line number.

What is wrong with my logic?

Here's the result of running with the define's
l = 10
exception caught
(file:x.cxx,line# 70) : (file:x.cxx,line# 70) : (file:x





___ _ ____ ___ __ __
/ _ )(_) / /_ __ / _ \___ _/ /_/ /____ ___
/ _ / / / / // / / ___/ _ `/ __/ __/ _ \/ _ \
/____/_/_/_/\_, / /_/ \_,_/\__/\__/\___/_//_/
/___/
Texas Instruments ASIC Circuit Design Methodology Group
Dallas, Texas, 214-480-4455, b-patton@ti.com
David Schwartz

2005-05-05, 6:03 pm


"Billy Patton" <bpatton@ti.com> wrote in message
news:Pine.LNX.4.61.0505050822580.11617@holster07.dal.design.ti.com...

> What I'm expecting this to expand to is
> main
> try
> {
> try
> {
> l = laff_get_long();
> cout << "l = " << l << "\n";
> }
> catch (const char* msg)
> {
> THROW(msg);
> }
> try
> {
> k = laff_get_longx();
> cout << "k = " << k << "\n";
> }
> catch (const char* msg)
> {
> THROW(msg);
> }
> }
> catch (const char* msg)
> {
> cout << exception caught\n " << msg << "\n";
> }
>
> What I would expect
> would be the line number inside laff_get_longx and teh line number where
> laff_get_long is called in main. Instead it just shows the same line
> number.


You pass 'laff_get_long' no parameters, so how can it know what line and
file it was called from?

DS


Jonathan Adams

2005-05-06, 2:52 am

In article
<Pine.LNX.4.61.0505041533410.11617@holster07.dal.design.ti.com>,
Billy Patton <bpatton@ti.com> wrote:

>
> long get_long()
> {
> THROW("this failed")
> }
>
> How can I get the trace back to show where get_long was called from?


It's heavily system and/or ISA dependent. On Solaris, starting with
Solaris 9, printstack(3C) and walkstack(3C) may be of interest.

There isn't really a standard for this sort of thing, unfortunately.

Cheers,
- jonathan
Bjorn Reese

2005-05-06, 6:02 pm

Billy Patton wrote:
> I had some interesting methods of back tracing.
> Unfortunately these only provided me with function names.
> My problem is that I have, in the main a switch statement than call one
> function ,laff_get_long, 47 times. I need to know which line it was
> called from. I don't want to surround each call with a try->catch That
> would create a lot of clutter in the code.


You could create scope tracing class like this:

#include <exception>
#include <stdexcept>
#include <iostream>

class ScopeTracer
{
public:
ScopeTracer(const char *function, const char *file, int line)
: function(function), file(file), line(line)
{
}
~ScopeTracer()
{
if (std::uncaught_exception()) {
std::cout << function << "() " << file << ":" << line << std::endl;
}
}
private:
const char *function;
const char *file;
int line;
};

#define SCOPE ScopeTracer tracer(__FUNCTION__, __FILE__, __LINE__)

void alpha(void)
{
SCOPE;
throw std::runtime_error("Something bad happened");
}

int main(void)
{
int i = 0;
try {
SCOPE;
switch (i) {
case 0:
{
SCOPE;
alpha();
}
break;
default:
{
SCOPE;
alpha();
}
break;
}
} catch (std::exception &e) {
std::cout << "Exception caught: " << e.what() << std::endl;
}
return 0;
}

--
mail1dotstofanetdotdk
Sponsored Links






Free braindumps | Software forum | Database administration forum

Copyright 2003 - 2008 webservertalk.com