backtrace 挂死栈

挂死调用栈实现

dump.c

  • backtrace 在 libgcc 中实现
#include <execinfo.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <string.h>
#define STACK_BUFFER 16

int get_data_from_awk(char *cmd, char *buf, int len)
{
    FILE* output;
    int ret = -1;
    output = popen(cmd, "r");
    if (output != NULL) {
        fgets(buf, len, output);
        ret = 0;
    }

    pclose(output);
    return ret;
}

void is_so(char * str)
{
    char file[1024];
    char offset_base[1024];
    char offset_now[1024];
    char offset[1024];
    char cmd[1024];
    char buf[1024];
    char so[1024];

    /* 1. 获取文件名 */
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "echo '%s' | awk -F'[( ]' '{printf $1}'", str);
    if (get_data_from_awk(cmd, file, sizeof(file)) != 0) {
        goto print_n;
    }

    /* 2. 判断文件是否是 so */
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "echo '%s' | awk '/\\.so/ {printf $1}'", file);
    if (get_data_from_awk(cmd, so, sizeof(so)) != 0) {
        goto print_n;
    }

    if (strcmp(so, file) != 0) {
        goto print_n;
    }


    /* 3. 拿到 offset_now */
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "echo '%s' | awk -F'[][]' '{printf $2}'", str);
    if (get_data_from_awk(cmd, offset_now, sizeof(offset_now)) != 0) {
        goto print_n;
    }

    /* 4. 拿到动态库加载的首地址 offset_base */
    // 获取 so,不包含 路径名
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "echo '%s' | awk -F'/' '{printf $NF}'", file);
    if (get_data_from_awk(cmd, so, sizeof(so)) != 0) {
        goto print_n;
    }

    // 获取 offset_base
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "cat /proc/%d/maps | grep -m 1 %s | awk -F'-' '{printf $1}'", getpid(), so);
    if (get_data_from_awk(cmd, offset_base, sizeof(offset_base)) != 0) {
        goto print_n;
    }

    // 获取 offset
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "echo '%s %s' | awk '{ printf(\"0x%%X\", strtonum($1) - strtonum(\"0x\" $2)) }'", offset_now, offset_base);
    if (get_data_from_awk(cmd, offset, sizeof(offset)) != 0) {
        goto print_n;
    }

    /* 5. 执行 addr2line */
    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "addr2line -e %s %s", file, offset);
    if (get_data_from_awk(cmd, buf, sizeof(buf)) != 0) {
        goto print_n;
    }

    // 输出结果自动带 换行符
    printf ("%s", buf);
    return;

print_n:
    printf("\n");
}

void dump(void)
{
    char buf[1024];
    void *buffer[STACK_BUFFER];
    char **string;
    int nptrs;

    nptrs = backtrace(buffer, STACK_BUFFER);
    string = backtrace_symbols(buffer, nptrs);
    if (string == NULL) {
        printf ("dump: string is NULL\n");
        return;
    }

    for (int i = 0; i < nptrs; i++) {
        printf ("%s ", string[i]);
        // 判断是否是动态库
        is_so(string[i]);
    }

    // 打印 当前进程的 maps,通过 实际地址 - 动态库加载首地址,即可获取实际偏移
    // add2line -e lib*.so 0x...
//    sprintf(buf, "cat /proc/%d/maps", getpid());
//    system((const char *)buf);
}


void sig_handler(int signo)
{
    printf ("=============>>> start dump <<<=================\n");
    dump();
    printf ("=============>>> start dump <<<=================\n");

    /* 设为默认处理 */
    signal(signo, SIG_DFL);
    /* 再次触发该信号 */
    raise(signo);
}

main.c

#include <stdio.h>
#include <signal.h>

extern int add(int a, int b);
extern void sig_handler(int signo);

int main(int argc, char *argv[])
{
    signal(SIGSEGV, sig_handler);
    add(1, 2);
    return 0;
}
posted @ 2023-08-16 02:35  chumoath  阅读(25)  评论(0)    收藏  举报