Win32 - Backtrace from C code

后端 未结 3 1682
离开以前
离开以前 2020-11-28 21:27

I\'m currently looking for a way to get backtrace information under Windows, from C code (no C++).

I\'m building a cross-platform C library, with reference-counting

3条回答
  •  隐瞒了意图╮
    2020-11-28 22:02

    Here's my super-low-fi alternative, as used for reading stacks from a C++ Builder app. This code is executed within the process itself when it crashes and gets a stack into the cs array.

        int cslev = 0;
        void* cs[300];
        void* it = ;
        void* rm[2];
        while(it && cslev<300)
        {
                /* Could just memcpy instead of ReadProcessMemory, but who knows if 
                   the stack's valid? If  it's invalid, memcpy could cause an AV, which is
                   pretty much exactly what we don't want
                */
                err=ReadProcessMemory(GetCurrentProcess(),it,(LPVOID)rm,sizeof(rm),NULL);
                if(!err)
                        break;
                it=rm[0];
                cs[cslev++]=(void*)rm[1];
        }
    

    UPDATE

    Once I've got the stack, I then go about translating it into names. I do this by cross-referencing with the .map file that C++Builder outputs. The same thing could be done with a map file from another compiler, although the formatting would be somewhat different. The following code works for C++Builder maps. This is again quite low-fi and probably not the canonical MS way of doing things, but it works in my situation. The code below isn't delivered to end users.

    char linbuf[300];
    char *pars;
    unsigned long coff,lngth,csect;
    unsigned long thisa,sect;
    char *fns[300];
    unsigned int maxs[300];
    FILE *map;
    
    map = fopen(mapname, "r");
    if (!map)
    {
        ...Add error handling for missing map...
    }
    
    do
    {
        fgets(linbuf,300,map);
    } while (!strstr(linbuf,"CODE"));
    csect=strtoul(linbuf,&pars,16); /* Find out code segment number */
    pars++; /* Skip colon */
    coff=strtoul(pars,&pars,16); /* Find out code offset */
    lngth=strtoul(pars,NULL,16); /* Find out code length */
    do
    {
        fgets(linbuf,300,map);
    } while (!strstr(linbuf,"Publics by Name"));
    
    for(lop=0;lop!=cslev;lop++)
    {
        fns[lop] = NULL;
        maxs[lop] = 0;
    }
    do
    {
        fgets(linbuf,300,map);
        sect=strtoul(linbuf,&pars,16);
        if(sect!=csect)
            continue;
        pars++;
        thisa=strtoul(pars,&pars,16);
        for(lop=0;lop!=cslev;lop++)
        {
            if(cs[lop]coff+lngth)
                continue;
            if(thisamaxs[lop])
            {
                maxs[lop]=thisa;
                while(*pars==' ')
                    pars++;
                fns[lop] = fnsbuf+(100*lop);
                fnlen = strlen(pars);
                if (fnlen>100)
                    fnlen = 100;
                strncpy(fns[lop], pars, 99);
                fns[lop][fnlen-1]='\0';
            }
        }
    } while (!feof(map));
    fclose(map);
    

    After running this code, the fns array contains the best-matching function from the .map file.

    In my situation, I actually have the call stack as produced by the first piece of code submitting to a PHP script - I do the equivalent of the C code above using a piece of PHP. This first bit parses the map file (Again, this works with C++Builder maps but could be easily adapted to other map file formats):

                $file = fopen($mapdir.$app."-".$appversion.".map","r");
                if (!$file)
                        ... Error handling for missing map ...
                do
                {
                        $mapline = fgets($file);
                } while (!strstr($mapline,"CODE"));
                $tokens = split("[[:space:]\:]", $mapline);
                $codeseg = $tokens[1];
                $codestart = intval($tokens[2],16);
                $codelen = intval($tokens[3],16);
                do
                {
                        $mapline = fgets($file);
                } while (!strstr($mapline,"Publics by Value"));
                fgets($file); // Blank
                $addrnum = 0;
                $lastaddr = 0;
                while (1)
                {
                        if (feof($file))
                                break;
                        $mapline = fgets($file);
                        $tokens = split("[[:space:]\:]", $mapline);
                        $thisseg = $tokens[1];
                        if ($thisseg!=$codeseg)
                                break;
                        $addrs[$addrnum] = intval($tokens[2],16);
                        if ($addrs[$addrnum]==$lastaddr)
                                continue;
                        $lastaddr = $addrs[$addrnum];
                        $funcs[$addrnum] = trim(substr($mapline, 16));
                        $addrnum++;
                }
                fclose($file);
    

    Then this bit translates an address (in $rowaddr) into a given function (as well as the offset after the function):

                        $thisaddr = intval($rowaddr,16);
                        $thisaddr -= $codestart;
                        if ($thisaddr>=0 && $thisaddr<=$codelen)
                        {
                                for ($lop=0; $lop!=$addrnum; $lop++)
                                        if ($thisaddr<$addrs[$lop])
                                                break;
                        }
                        else
                                $lop = $addrnum;
                        if ($lop!=$addrnum)
                        {
                                $lop--;
                                $lines[$ix] = substr($line,0,13).$rowaddr." : ".$funcs[$lop]." (+".sprintf("%04X",$thisaddr-$addrs[$lop]).")";
                                $stack .= $rowaddr;
                        }
                        else
                        {
                                $lines[$ix] = substr($line,0,13).$rowaddr." : external";
                        }
    

提交回复
热议问题