-1

I'm trying to find a random bug (I dont have a specific senario) which crash my C++ linux program. I know that If I'm using gdb and the app crash, I can use "bt" command and get the call stack of the crash. I cant use gdb (logisitic problems) and the crash is random.

  1. Is there a way in Linux to get the call stack of a crashing C++ app , without useing gdb ?
  2. Is there a way to know if there are dead locks (with and without gdb) ?

Thanks

user3668129
  • 4,318
  • 6
  • 45
  • 87
  • 1
    Can you add code to your program? You could catch the offending signal or exception and call one of the [backtrace](http://www.gnu.org/software/libc/manual/html_node/Backtraces.html) functions in the GNU C library. – Mark Plotnick Apr 01 '15 at 18:39
  • 2
    Are you able to get a core file and run gdb on that, or is gdb just not available at all? – Mark B Apr 01 '15 at 18:41

2 Answers2

1

you can use __cxa_demangle() to get the stack message,like this:

string OException::strGetSourceCode(const string& p_strFileName,const string& p_strAddressInfo) throw()
{
    char l_acBuf[CBUFFLEN] = {0};
    char l_acAddress[CBUFFLEN] = {0};
    memcpy(l_acBuf, p_strAddressInfo.c_str(), p_strAddressInfo.length());
    if(sscanf(l_acBuf,"%*[^[][0x%[^]]", l_acAddress)==1)
    {   
        FILE *l_pFp;
        stringstream l_ss;
        if(p_strFileName.find(".so")!=string::npos)
        {   
            unsigned int l_lOffset=strtol(l_acAddress,NULL,16)-strtol(strGetBaseAddress(p_strFileName).c_str(),NULL,16);
            snprintf(l_acAddress,sizeof(l_acAddress),"%x",l_lOffset);
        }   

        l_ss << "addr2line 0x" << l_acAddress<< " -i -e " << p_strFileName;
        if((l_pFp = popen(l_ss.str().c_str(), "r"))) {
            if(fgets(l_acAddress, CBUFFLEN, l_pFp) != NULL) {
                l_acAddress[strlen(l_acAddress) - 1] = '\0';
            }   
        }   
        return l_acAddress;
    }   
    return  l_acBuf;
}
string OException::strGetBaseAddress(const string& p_soFileName) throw()
{
    int l_iPid=getpid();
    char l_acFileName[CBUFFLEN]={0};
    char l_acCmd[CBUFFLEN]={0};
#ifdef __linux__ 
    snprintf(l_acFileName,sizeof(l_acFileName),"/proc/%d/maps",l_iPid);
    snprintf(l_acCmd,sizeof(l_acCmd),"grep %s %s | head -1 | awk -F- \'{print $1}\'",p_soFileName.c_str(),l_acFileName);
#endif
    FILE* l_pFile=popen(l_acCmd,"r");
    char l_acBaseAddress[CBUFFLEN]={0};
    if(fgets(l_acBaseAddress,CBUFFLEN,l_pFile)!=NULL)
        return l_acBaseAddress;
    return "";
}
void OException::vSaveStackTrace() throw()
{
    void* l_acBuffer[CBUFFLEN];
    int l_iStackCount = ::backtrace(l_acBuffer, CBUFFLEN); //get the trace list    //cout<<"l_iStackCount="<<l_iStackCount<<endl;
    //translate trace message 
    char** l_pacStackResult = ::backtrace_symbols(l_acBuffer, l_iStackCount);
    if (l_pacStackResult)
    {
        string l_strFormat="";
        string l_binFile="";
        m_sStack="==============BackTrace===========\n";
        for (int i = 0;i<l_iStackCount-1; i++)//
        {

            l_binFile=l_pacStackResult[i];//get the exe filename
            l_binFile=l_binFile.substr(0,l_binFile.find('(',0));
            m_sStack.append(l_strFormat+"["+l_binFile+"]:"+strDemangle(l_pacStackResult[i])+"-->");
            m_sStack.append(strGetSourceCode(l_binFile,l_pacStackResult[i]));
            m_sStack.push_back('\n');
            l_strFormat+="  ";
        }
        free(l_pacStackResult);
    }

}
string  OException::strDemangle(const char* p_acSymbol) throw()
{
    size_t size;
    int status;
    char l_acTmp[CBUFFLEN]={0};
    char* l_pDemangled;
    //first, try to demangle a c++ name
    if (1 == sscanf(p_acSymbol, "%*[^(](%256[^)+]", l_acTmp)) {
        if (NULL != (l_pDemangled = abi::__cxa_demangle(l_acTmp, NULL, &size, &status))) {
            string l_strResult(l_pDemangled);
            free(l_pDemangled);
            return l_strResult;
        }
    }
    //if that didn't work, try to get a regular c p_acSymbol
    if (1 == sscanf(p_acSymbol, "%256s", l_acTmp)) {
        return l_acTmp;
    }
    return p_acSymbol;
}
Jay Zhou
  • 51
  • 6
0

There is also another solution to get backtrace of crashing program, but of course it is not as pretty as backtrace from gdb, the solution is described here: How to automatically generate a stacktrace when my program crashes

In short words you need to compile your program with options:

-g -rdynamic

then you need to register signal handler (SIGSEGV probably is the signal you need):

signal(SIGSEGV, callbackWhenSignalOccures);

in function callbackWhenSignalOccures you can display backtrace with functions: http://man7.org/linux/man-pages/man3/backtrace.3.html

About gdb -I don't know what You mean by "I cant use gdb (logisitic problems)", but You don't have to run gdb in interactive mode, You can just display backtrace from core then gdb would automatically quit:

ulimit -c unlimited
./program # it crashes
gdb -silent ./program core --eval-command=backtrace --batch

About deadlocks I found solution: How to detect and find out a program is in deadlock?

baziorek
  • 2,502
  • 2
  • 29
  • 43
  • The GNU `backtrace` function is not *async-signal-safe* (see [signal-safety(7)](http://man7.org/linux/man-pages/man7/signal-safety.7.html)) so you should not use it in a signal handler – Basile Starynkevitch Jan 02 '19 at 08:17