2013-03-20 79 views
1

如何在Linux下確定線程堆棧的大小及其大小?有沒有可用的C/C++ API或在gdb中查找的方法? 感謝確定線程的堆棧大小和位置

+0

什麼語言? C/C++?還有別的嗎? – Mysticial 2013-03-20 05:01:28

+0

使用第0幀來獲得最內層的框架,然後上來和上? – StarPinkER 2013-03-20 05:12:02

+2

爲什麼你想通過API找到?做什麼的?你可以在TLS或全局數據中存儲每個線程中一些起始局部變量的地址... – 2013-03-20 06:21:59

回答

1

這裏是這樣,涉及到閱讀/proc/self/maps的不同方法。與其他一些方法不同,它不需要在程序開始時使用特殊的儀器,並且可以爲堆棧的末尾提供精確的位置。

如果您嘗試cat /proc/self/maps,你得到的東西是這樣的:

00400000-0040c000 r-xp 00000000 08:01 6039736    /usr/bin/cat 
0060b000-0060c000 r--p 0000b000 08:01 6039736    /usr/bin/cat 
0060c000-0060d000 rw-p 0000c000 08:01 6039736    /usr/bin/cat 
00908000-00929000 rw-p 00000000 00:00 0     [heap] 
7fcdb1c68000-7fcdb1e01000 r-xp 00000000 08:01 6032628  /usr/lib/libc-2.21.so 
7fcdb1e01000-7fcdb2000000 ---p 00199000 08:01 6032628  /usr/lib/libc-2.21.so 
7fcdb2000000-7fcdb2004000 r--p 00198000 08:01 6032628  /usr/lib/libc-2.21.so 
7fcdb2004000-7fcdb2006000 rw-p 0019c000 08:01 6032628  /usr/lib/libc-2.21.so 
7fcdb2006000-7fcdb200a000 rw-p 00000000 00:00 0 
7fcdb200a000-7fcdb202c000 r-xp 00000000 08:01 6032717  /usr/lib/ld-2.21.so 
7fcdb21f5000-7fcdb21f8000 rw-p 00000000 00:00 0 
7fcdb2209000-7fcdb222b000 rw-p 00000000 00:00 0 
7fcdb222b000-7fcdb222c000 r--p 00021000 08:01 6032717  /usr/lib/ld-2.21.so 
7fcdb222c000-7fcdb222d000 rw-p 00022000 08:01 6032717  /usr/lib/ld-2.21.so 
7fcdb222d000-7fcdb222e000 rw-p 00000000 00:00 0 
7ffe78c41000-7ffe78c62000 rw-p 00000000 00:00 0   [stack] 
7ffe78dba000-7ffe78dbc000 r--p 00000000 00:00 0   [vvar] 
7ffe78dbc000-7ffe78dbe000 r-xp 00000000 00:00 0   [vdso] 
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall] 

正如你可以看到,有一個[stack]條目。這可能是你正在尋找的。

一個例子程序來解析該行指出:

#include <stdio.h> 
#include <unistd.h> 
#include <string.h> 

int main(int argc, char *argv[]) { 
    FILE *file = fopen("/proc/self/maps", "r"); 
    char line[1024]; 
    void *result = NULL; 
    while (!feof(file)) { 
     if (fgets(line, sizeof(line)/sizeof(char), file) == NULL) { 
      break; 
     } 
     unsigned long start, end, offset; 
     unsigned int devma, devmi, ino; 
     char perms[6]; 
     char path[128]; 
     if (sscanf(line, "%lx-%lx %5s %lx %d:%d %d %127s", &start, &end, &perms, &offset, &devma, &devmi, &ino, &path) != 8) { 
      continue; // could not parse. fail gracefully and try again on the next line. 
     } 
     if (strcmp(path, "[stack]") == 0) { // use [stack:TID] for a thread besides the main thread 
      printf("Stack found from %lx to %lx\n", start, end); 
      break; 
     } 
    } 
    fclose(file); 
    return 0; 
} 

這將打印出類似這樣:

Stack found from 7fff91834000 to 7fff91855000 

這可能是相當接近你在找什麼。

+0

這就是我最終做到的。如果您有多個線程並且需要爲特定線程查找堆棧,則可以使用任何堆棧變量的地址並將其與堆棧範圍進行匹配。 – hlitz 2015-06-14 17:40:56

+0

是的 - 你也可以使用'gettid()'和'[stack:TID]'。 – 2015-06-14 18:26:20

1

如果您能夠接受沒有得到確切的堆棧頂部(這可能足夠了我的應用程序),遍歷幀指針可以工作:

// main.c 
#include <stdint.h> 

// -fno-omit-frame-pointer might be required - otherwise you might get a crash 
// might not be exactly at the top; but probably very close 

void *stack_top() { 
    void **top; 
    asm("movq %%rbp, %0" : "=r" (top)); // set top to %rbp - replace with %ebp for 32-bit x86 
    // if top is higher in memory than the variable, then still part of the stack. 
    while ((uintptr_t) *top > (uintptr_t) &top) { 
     top = *top; 
    } 
    return top; 
} 

此操作,因爲在32位的%rbp寄存器(或%ebp寄存器)被用於將指針存儲到父堆棧幀的基(其中保存%rbp%ebp值是) - 所以我們可以迭代遍歷這個鏈表直到我們到達一個無效的地址。

注意stack_top可能在涉及&top比較某些情況下失敗 - 我的系統端接一個指針,指向程序加載代碼的位置的鏈表,所以這是我發現,以檢測它的最好方法 - 但你會想要徹底地測試它。 (如果任何人有一個更好的辦法來檢測鏈的末端,請添加評論。)

實例測試程序:

#include <stdio.h> 
#include <pthread.h> 

void *test_another_layer(int x) { 
    return stack_top(); 
} 

void *subthread(void *ptr) { 
    void *st1 = stack_top(); 
    void *st2 = test_another_layer(0); 
    void *st3 = &ptr; 
    printf("stack tops 2: %x %x %x\n", st1, st2, st3); 
    return NULL; 
} 

int main(int argc, char *argv[]) { 
    void *st1 = stack_top(); 
    void *st2 = test_another_layer(0); 
    void *st3 = &argc; 
    printf("stack tops: %x %x %x\n", st1, st2, st3); 
    pthread_t ot; 
    if (pthread_create(&ot, NULL, subthread, NULL) != 0) { 
     perror("cannot create"); 
     return 1; 
    } 
    if (pthread_join(ot, NULL) != 0) { 
     perror("cannot join"); 
     return 2; 
    } 
    return 0; 
}