How to get a stack trace for C++ using gcc with line number information?

后端 未结 14 2018
长发绾君心
长发绾君心 2020-11-27 09:54

We use stack traces in proprietary assert like macro to catch developer mistakes - when error is caught, stack trace is printed.

I find gcc\'s pair

14条回答
  •  南方客
    南方客 (楼主)
    2020-11-27 10:37

    AFAICS all of the solutions provided so far won't print functions names and line numbers from shared libraries. That's what I needed, so i altered karlphillip's solution (and some other answer from a similar question) to resolve shared library addresses using /proc/id/maps.

    #include 
    #include 
    #include 
    #include 
    #include 
    #include 
    
    struct Region { // one mapped file, for example a shared library
        uintptr_t start;
        uintptr_t end;
        char* path;
    };
    
    static struct Region* getRegions(int* size) { 
    // parse /proc/self/maps and get list of mapped files 
        FILE* file;
        int allocated = 10;
        *size = 0;
        struct Region* res;
        uintptr_t regionStart = 0x00000000;
        uintptr_t regionEnd = 0x00000000;
        char* regionPath = "";
        uintmax_t matchedStart;
        uintmax_t matchedEnd;
        char* matchedPath;
    
        res = (struct Region*)malloc(sizeof(struct Region) * allocated);
        file = fopen("/proc/self/maps", "r");
        while (!feof(file)) {
            fscanf(file, "%jx-%jx %*s %*s %*s %*s%*[ ]%m[^\n]\n",  &matchedStart, &matchedEnd, &matchedPath);
            bool bothNull = matchedPath == 0x0 && regionPath == 0x0;
            bool similar = matchedPath && regionPath && !strcmp(matchedPath, regionPath);
            if(bothNull || similar) {
                free(matchedPath);
                regionEnd = matchedEnd;
            } else {
                if(*size == allocated) {
                    allocated *= 2;
                    res = (struct Region*)realloc(res, sizeof(struct Region) * allocated);
                }
    
                res[*size].start = regionStart;
                res[*size].end = regionEnd;
                res[*size].path = regionPath;
                (*size)++;
                regionStart = matchedStart;
                regionEnd = matchedEnd;
                regionPath = matchedPath;
            }
        }
        return res;
    }
    
    struct SemiResolvedAddress {
        char* path;
        uintptr_t offset;
    };
    static struct SemiResolvedAddress semiResolve(struct Region* regions, int regionsNum, uintptr_t address) {
    // convert address from our address space to
    // address suitable fo addr2line 
        struct Region* region;
        struct SemiResolvedAddress res = {"", address};
        for(region = regions; region < regions+regionsNum; region++) {
            if(address >= region->start && address < region->end) {
                res.path = region->path;
                res.offset = address - region->start;
            }
        }
        return res;
    }
    
    void printStacktraceWithLines(unsigned int max_frames)
    {
        int regionsNum;
        fprintf(stderr, "stack trace:\n");
    
        // storage array for stack trace address data
        void* addrlist[max_frames+1];
    
        // retrieve current stack addresses
        int addrlen = backtrace(addrlist, sizeof(addrlist) / sizeof(void*));
        if (addrlen == 0) {
            fprintf(stderr, "  \n");
            return;
        }
        struct Region* regions = getRegions(®ionsNum); 
        for (int i = 1; i < addrlen; i++)
        {
            struct SemiResolvedAddress hres =
                    semiResolve(regions, regionsNum, (uintptr_t)(addrlist[i]));
            char syscom[256];
            sprintf(syscom, "addr2line -C -f -p -a -e %s 0x%jx", hres.path, (intmax_t)(hres.offset));
            system(syscom);
        }
        free(regions);
    }
    

提交回复
热议问题