Unix编程/应用问答中文版 ---2.堆栈相关问题
2. 堆栈相关问题
2.1 如何理解pstack的输出信息
2.2
2.3 Solaris中如何获取一个C程序的调用栈回溯
2.4 如何编程获取栈底地址
2.5 如何得到一个运行中进程的内存映像
2.6 调试器如何工作的
2.7 x86/Linux上如何处理SIGFPE信号
--------------------------------------------------------------------------
2. 堆栈相关问题
2.1 如何理解pstack的输出信息
Q: 080603a7 main (1, 80479b8, 80479c0) + d53
结尾的d53是什么
A: Roger A. Faulkner <raf@sunraf.Sun.COM>
在代码段绝对地址0x080603a7处,main()调用了一个函数,0x080603a7正是
main + 0xd53,换句话说,从main()函数开始的0xd53偏移处。
2.3 Solaris中如何获取一个C程序的调用栈回溯
Q: 我想在Solaris 2.6极其后续版本上获取一个C程序的调用栈回溯,类似如下输出
(10) 0x00045e08 integ + 0x408 [./two_brn.e]
(11) 0x0006468c trajcem + 0x128 [./two_brn.e]
(12) 0x00055490 fly_traj + 0xf58 [./two_brn.e]
(13) 0x0004052c top_level + 0x14 [./two_brn.e]
(14) 0x000567e4 _start + 0x34 [./two_brn.e]
这样我就可以知道当程序崩溃、死锁的时候代码执行到了何处。在HP-UX和IRIX上
可以利用U_STACK_TRACE()和trace_back_stack_and_print(),Solaris上呢?
Q: 有没有办法显示当前堆栈中的数据(GNU/Linux系统)?我希望自己的异常处理程序
在进程结束前dump整个栈区(stack),以便观察到栈顶是什么函数。对于调试意想
不到的运行时错误而言,这很重要。
A: Bjorn Reese <breese@mail1.stofanet.dk>
用/usr/proc/bin/pstack [-F] <pid ...>
参看这个例子代码,http://home1.stofanet.dk/breese/debug/debug.tar.gz
Q: is there a way to access call stack information at run time from within
a program? i've been maintaining my own crude stack using __FUNCTION__
and linked lists but can't help but think there's gotta be a better
way...
A: Nate Eldredge <neldredge@hmc.edu>
这依赖于你的系统,如果使用glibc 2.1或更新版本,可以使用backtrace()函数,
参看<execinfo.h>,其他系统可能有不同的技术支持。
注意,你所使用的办法可能是唯一能够保证跨平台使用的
A: Andrew Gabriel <andrew@cucumber.demon.co.uk> Consultant Software Engineer
下面是一个backtrace()的应用举例,如果你使用Solaris 2.4及其后续版本,那
么这个例子可以很好的工作。很可能无法工作在64-bit模式下,我没有尝试过,
好像Solaris 7已经提供了一个类似的演示程序。还可以增加某些功能,我没有时
间了。
/*
* Produce a stack trace for Solaris systems.
*
* Copyright (C) 1995-1998 Andrew Gabriel <andrew@cucumber.demon.co.uk>
* Parts derived from Usenet postings of Bart Smaalders and Casper Dik.
*
*/
/* ......................................................................... */
#include <setjmp.h>
#include <sys/types.h>
#include <sys/reg.h>
#include <sys/frame.h>
#include <dlfcn.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#if defined(sparc) || defined(__sparc)
#define FLUSHWIN() asm("ta 3");
#define FRAME_PTR_INDEX 1
#define SKIP_FRAMES 0
#endif
#if defined(i386) || defined(__i386)
#define FLUSHWIN()
#define FRAME_PTR_INDEX 3
#define SKIP_FRAMES 1
#endif
#if defined(ppc) || defined(__ppc)
#define FLUSHWIN()
#define FRAME_PTR_INDEX 0
#define SKIP_FRAMES 2
#endif
/* ......................................................................... */
static void print_address ( void * pc )
{
Dl_info info;
if ( dladdr( pc, &info ) == 0 )
{
/* not found */
fprintf( stderr, "*** %s:0x%x\n", "??", ( unsigned int )pc );
}
else
{
/* found */
fprintf( stderr, "*** %s:%s+0x%x\n", info.dli_fname, info.dli_sname,
( unsigned int )pc - ( unsigned int )info.dli_saddr );
}
return;
} /* end of print_address */
/* ......................................................................... */
static int validaddr ( void * addr )
{
static long pagemask = -1;
char c;
if ( pagemask == -1 )
{
pagemask = ~( sysconf( _SC_PAGESIZE ) - 1 );
}
addr = ( void * )( ( long )addr & pagemask );
if ( mincore( ( char * )addr, 1, &c ) == -1 && errno == ENOMEM )
{
return 0; /* invalid */
}
else
{
return 1; /* valid */
}
} /* end of validaddr */
/* ......................................................................... */
/*
* this function walks up call stack, calling print_addess
* once for each stack frame, passing the pc as the argument.
*/
static void print_stack ( void )
{
struct frame * sp;
jmp_buf env;
int i;
int * iptr;
FLUSHWIN();
setjmp( env );
iptr = ( int * )env;
sp = ( struct frame * )iptr[ FRAME_PTR_INDEX ];
for ( i = 0; i < SKIP_FRAMES && sp; i++ )
{
if ( !validaddr( sp ) || !validaddr( &sp->fr_savpc ) )
{
fprintf( stderr, "***[stack pointer corrupt]\n" );
return;
}
sp = ( struct frame * )sp->fr_savfp;
}
i = 100; /* looping check */
while ( validaddr( sp ) && validaddr( &sp->fr_savpc ) && sp->fr_savpc && --i
)
{
print_address( ( void * )sp->fr_savpc );
sp = ( struct frame * )sp->fr_savfp;
}
} /* end of print_stack */
/* ......................................................................... */
void backtrace( void )
{
fprintf( stderr, "***backtrace...\n" );
print_stack();
fprintf( stderr, "***backtrace ends\n" );
}
/* ......................................................................... */
2.4 如何编程获取栈底地址
Q: 虽然很多操作系统的用户进程栈底地址固定,但是我需要写一个可广泛移植C程序
获取这个栈底地址。
A: tt <warning3@nsfocus.com> 2001-06-02 19:40
假设堆栈(stack)向低地址方向增长,则所谓栈底指堆栈(stack)最高地址
x86/Linux 栈底是0xc0000000( 栈底往低地址的4个字节总是零 )
SPARC/Solaris 7/8 栈底是0xffbf0000( 栈底往低地址的4个字节总是零 )
SPARC/Solaris 2.6 栈底是0xf0000000( 栈底往低地址的4个字节总是零 )
x86/FreeBSD 栈底是0xbfc00000( 栈底往低地址的4个字节总是零 )
x86/NetBSD 1.5 栈底是0xbfbfe000
x86/OpenBSD 2.8 栈底是0xdfbfe000
D: jonah
对于NetBSD 1.5,栈底是0xbfc00000。根据源码,最高用户地址是0xbfbfe000,因为
最后4MB(2^22)的最后
2.1 如何理解pstack的输出信息
2.2
2.3 Solaris中如何获取一个C程序的调用栈回溯
2.4 如何编程获取栈底地址
2.5 如何得到一个运行中进程的内存映像
2.6 调试器如何工作的
2.7 x86/Linux上如何处理SIGFPE信号
--------------------------------------------------------------------------
2. 堆栈相关问题
2.1 如何理解pstack的输出信息
Q: 080603a7 main (1, 80479b8, 80479c0) + d53
结尾的d53是什么
A: Roger A. Faulkner <raf@sunraf.Sun.COM>
在代码段绝对地址0x080603a7处,main()调用了一个函数,0x080603a7正是
main + 0xd53,换句话说,从main()函数开始的0xd53偏移处。
2.3 Solaris中如何获取一个C程序的调用栈回溯
Q: 我想在Solaris 2.6极其后续版本上获取一个C程序的调用栈回溯,类似如下输出
(10) 0x00045e08 integ + 0x408 [./two_brn.e]
(11) 0x0006468c trajcem + 0x128 [./two_brn.e]
(12) 0x00055490 fly_traj + 0xf58 [./two_brn.e]
(13) 0x0004052c top_level + 0x14 [./two_brn.e]
(14) 0x000567e4 _start + 0x34 [./two_brn.e]
这样我就可以知道当程序崩溃、死锁的时候代码执行到了何处。在HP-UX和IRIX上
可以利用U_STACK_TRACE()和trace_back_stack_and_print(),Solaris上呢?
Q: 有没有办法显示当前堆栈中的数据(GNU/Linux系统)?我希望自己的异常处理程序
在进程结束前dump整个栈区(stack),以便观察到栈顶是什么函数。对于调试意想
不到的运行时错误而言,这很重要。
A: Bjorn Reese <breese@mail1.stofanet.dk>
用/usr/proc/bin/pstack [-F] <pid ...>
参看这个例子代码,http://home1.stofanet.dk/breese/debug/debug.tar.gz
Q: is there a way to access call stack information at run time from within
a program? i've been maintaining my own crude stack using __FUNCTION__
and linked lists but can't help but think there's gotta be a better
way...
A: Nate Eldredge <neldredge@hmc.edu>
这依赖于你的系统,如果使用glibc 2.1或更新版本,可以使用backtrace()函数,
参看<execinfo.h>,其他系统可能有不同的技术支持。
注意,你所使用的办法可能是唯一能够保证跨平台使用的
A: Andrew Gabriel <andrew@cucumber.demon.co.uk> Consultant Software Engineer
下面是一个backtrace()的应用举例,如果你使用Solaris 2.4及其后续版本,那
么这个例子可以很好的工作。很可能无法工作在64-bit模式下,我没有尝试过,
好像Solaris 7已经提供了一个类似的演示程序。还可以增加某些功能,我没有时
间了。
/*
* Produce a stack trace for Solaris systems.
*
* Copyright (C) 1995-1998 Andrew Gabriel <andrew@cucumber.demon.co.uk>
* Parts derived from Usenet postings of Bart Smaalders and Casper Dik.
*
*/
/* ......................................................................... */
#include <setjmp.h>
#include <sys/types.h>
#include <sys/reg.h>
#include <sys/frame.h>
#include <dlfcn.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#if defined(sparc) || defined(__sparc)
#define FLUSHWIN() asm("ta 3");
#define FRAME_PTR_INDEX 1
#define SKIP_FRAMES 0
#endif
#if defined(i386) || defined(__i386)
#define FLUSHWIN()
#define FRAME_PTR_INDEX 3
#define SKIP_FRAMES 1
#endif
#if defined(ppc) || defined(__ppc)
#define FLUSHWIN()
#define FRAME_PTR_INDEX 0
#define SKIP_FRAMES 2
#endif
/* ......................................................................... */
static void print_address ( void * pc )
{
Dl_info info;
if ( dladdr( pc, &info ) == 0 )
{
/* not found */
fprintf( stderr, "*** %s:0x%x\n", "??", ( unsigned int )pc );
}
else
{
/* found */
fprintf( stderr, "*** %s:%s+0x%x\n", info.dli_fname, info.dli_sname,
( unsigned int )pc - ( unsigned int )info.dli_saddr );
}
return;
} /* end of print_address */
/* ......................................................................... */
static int validaddr ( void * addr )
{
static long pagemask = -1;
char c;
if ( pagemask == -1 )
{
pagemask = ~( sysconf( _SC_PAGESIZE ) - 1 );
}
addr = ( void * )( ( long )addr & pagemask );
if ( mincore( ( char * )addr, 1, &c ) == -1 && errno == ENOMEM )
{
return 0; /* invalid */
}
else
{
return 1; /* valid */
}
} /* end of validaddr */
/* ......................................................................... */
/*
* this function walks up call stack, calling print_addess
* once for each stack frame, passing the pc as the argument.
*/
static void print_stack ( void )
{
struct frame * sp;
jmp_buf env;
int i;
int * iptr;
FLUSHWIN();
setjmp( env );
iptr = ( int * )env;
sp = ( struct frame * )iptr[ FRAME_PTR_INDEX ];
for ( i = 0; i < SKIP_FRAMES && sp; i++ )
{
if ( !validaddr( sp ) || !validaddr( &sp->fr_savpc ) )
{
fprintf( stderr, "***[stack pointer corrupt]\n" );
return;
}
sp = ( struct frame * )sp->fr_savfp;
}
i = 100; /* looping check */
while ( validaddr( sp ) && validaddr( &sp->fr_savpc ) && sp->fr_savpc && --i
)
{
print_address( ( void * )sp->fr_savpc );
sp = ( struct frame * )sp->fr_savfp;
}
} /* end of print_stack */
/* ......................................................................... */
void backtrace( void )
{
fprintf( stderr, "***backtrace...\n" );
print_stack();
fprintf( stderr, "***backtrace ends\n" );
}
/* ......................................................................... */
2.4 如何编程获取栈底地址
Q: 虽然很多操作系统的用户进程栈底地址固定,但是我需要写一个可广泛移植C程序
获取这个栈底地址。
A: tt <warning3@nsfocus.com> 2001-06-02 19:40
假设堆栈(stack)向低地址方向增长,则所谓栈底指堆栈(stack)最高地址
x86/Linux 栈底是0xc0000000( 栈底往低地址的4个字节总是零 )
SPARC/Solaris 7/8 栈底是0xffbf0000( 栈底往低地址的4个字节总是零 )
SPARC/Solaris 2.6 栈底是0xf0000000( 栈底往低地址的4个字节总是零 )
x86/FreeBSD 栈底是0xbfc00000( 栈底往低地址的4个字节总是零 )
x86/NetBSD 1.5 栈底是0xbfbfe000
x86/OpenBSD 2.8 栈底是0xdfbfe000
D: jonah
对于NetBSD 1.5,栈底是0xbfc00000。根据源码,最高用户地址是0xbfbfe000,因为
最后4MB(2^22)的最后
Tags:Unix,编程,应用,问答,中文版,堆栈,相关,问题

