以下は、「Red Hat Enterprise Linux 5.5(Tikanga)Kernel 2.6.18-194.el5xen x86_64」を実行しているマシンで使用したいソースの例です。 OS。Linux上でのバックトレース64ビットシグナルハンドラからmalloc/free on callstack
一般的な考え方は、あるスレッドのバックトレースが必要なので、そのスレッドのSIGUSR1信号を発生させ、ハンドラはbacktrace()呼び出しを行います。
以下のような私のシナリオでは、FrameTwo関数はmallocを呼び出し、ループでフリーになります。シグナルがこの特定のスレッドに対して呼び出され、freeまたはmallocがコールスタック上にあるときは、シグナルハンドラがbacktrace()を呼び出すとprogreamがクラッシュします。
(gdb) where (stack from gdb)
0 0x0000003e67207638 in ??()
1 0x0000003e672088bb in _Unwind_Backtrace
2 0x00000037ba0e5fa8 in backtrace()
3 0x000000000040071a in handler()
4 <signal handler called>
5 0x00000037ba071fac in _int_free()
6 0x0000000a33605000 in ??()
7 0x000000004123b130 in ??()
8 0x00000000004007d4 in ThreadFunction()
9 0x000000001f039020 in ??()
10 0x000000004123b940 in ??()
11 0x0000000000000001 in ??()
12 0x0000000000000000 in ??()
私は、バックトレースは、シグナルハンドラから呼び出すべきではないことを他のソースから学んだので、私はこのような場合のために私自身の関数grok_and_print_thread_stackを()書かれています。
RBPレジスタを使用してスタックをナビゲートします(RBPには、現在のフレームのベースポインタがあり、前のフレームのベースポインタを指しています)が、このアルゴリズムはこの場合は機能しません。_int_free() callstackでは、_int_freeのRBPが0x20のような有効なフレームのベースポインタではない値であるため、RBPレジスタのナビゲーションアルゴリズムが中断します。
どのようにコールスタックをレジスタからナビゲートできるか知っていますか?または、私の目的にバックトレースを使用するにはどうすればいいですか?一般x86_64のプログラムで
#include "stdio.h"
#include "stdlib.h"
#include "pthread.h"
#include "signal.h"
#include "syscall.h"
#include "string.h"
#include "inttypes.h"
//####################################################################
//gcc BacktraceTestProgram.c -o backtracetest -lpthread
//./backtracetest
//gdb -c core backtracetest
//####################################################################
volatile sig_atomic_t flag = 1;
int thlist[6] = {0};
int cnt = 0;
int *memory = NULL;
//####################################################################
void raiseUserSignal(int tid)
{
union sigval value;
value.sival_int = 1;
sigqueue(tid,SIGUSR1, value);
}
//####################################################################
int grok_and_print_thread_stack()
{
int ret = 0;
register uint64_t* rbp asm("rbp");
/*if buffer was built before, add separator */
uint64_t *previous_bp;
/*save pointers*/
previous_bp = rbp;
/* stack Traversal */
while(previous_bp)
{
uint64_t *next_bp;
next_bp = (uint64_t*)*previous_bp;
printf("Read BP: %lx \n", next_bp);
if (NULL == (void*)next_bp)
{
printf("Reached the top of the stack\n");
fflush(stdout);
break;
}
previous_bp = next_bp;
}
return ret;
}
//####################################################################
void handler(int signum, siginfo_t *info, void *context)
{
int nptrs = 0 ;
void *buffer[100] = {NULL};
char **strings = NULL;
nptrs = backtrace(buffer, 100);
flag = 1;
}
//####################################################################
void FrameTwo(const char A)
{
do{
if(memory == NULL)
memory = (int *)malloc(sizeof(int) *5);
if(memory != NULL) {
free(memory);
memory = NULL;
}
}while(1);
}
//####################################################################
void FrameOne(int no)
{
FrameTwo('A');
}
//####################################################################
void *ThreadFunction(void *ptr)
{
int tid = syscall(SYS_gettid);
thlist[cnt++] = tid;
FrameOne(10);
}
//####################################################################
void RegisterSignalHandler()
{
/* Register a Signal Handler */
struct sigaction usrsig_action;
usrsig_action.sa_flags = SA_SIGINFO;
usrsig_action.sa_sigaction = &handler;
sigaction (SIGUSR1, &usrsig_action, NULL);
}
//####################################################################
int main(int no , char *argc[])
{
int iret1;
pthread_t thread1;
RegisterSignalHandler();
/* Create independent threads each of which will execute function */
iret1 = pthread_create(&thread1, NULL, ThreadFunction, NULL);
while(cnt == 0);
while(1) {
if(flag == 1){
flag = 0;
raiseUserSignal(thlist[0]);
}
}
pthread_join(thread1, NULL);
return 0;
}
IIRCシグナルハンドラには独自のスタックがあるため、プログラムのメインスタックは表示されず、シグナルハンドラのメインスタックは表示されません。 – DarkDust
@DarkDust私はシナリオのほとんどでメインスタックを見ることができます..実際には一般的にはクラッシュ解析の人クラッシュのためのシグナルハンドラを実装し、メインスタックを見るためにそのハンドラからコールバックトレース()を提案する.. – sandeep