printStack: rewritten to utilize dladdr for resolution of the stack addresses

Patch provided by Alexey Matveichev
Resolves bug-report http://www.openfoam.org/mantisbt/view.php?id=1676
This commit is contained in:
Henry 2015-05-19 08:41:59 +01:00
parent 8b9a57af74
commit 218dafd76b

View file

@ -24,12 +24,11 @@ License
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "error.H" #include "error.H"
#include "IStringStream.H"
#include "OStringStream.H" #include "OStringStream.H"
#include "OSspecific.H" #include "OSspecific.H"
#include "IFstream.H" #include "IFstream.H"
#include "ReadHex.H"
#include <inttypes.h>
#include <cxxabi.h> #include <cxxabi.h>
#include <execinfo.h> #include <execinfo.h>
#include <dlfcn.h> #include <dlfcn.h>
@ -43,70 +42,70 @@ namespace Foam
string pOpen(const string &cmd, label line=0) string pOpen(const string &cmd, label line=0)
{ {
const int MAX = 1000; string res = "\n";
FILE *cmdPipe = popen(cmd.c_str(), "r"); FILE *cmdPipe = popen(cmd.c_str(), "r");
if (cmdPipe) if (cmdPipe)
{ {
char *buf = NULL;
// Read line number of lines // Read line number of lines
for (label cnt = 0; cnt <= line; cnt++) for (label cnt = 0; cnt <= line; cnt++)
{ {
char buffer[MAX]; size_t linecap = 0;
char* s = fgets(buffer, MAX-1, cmdPipe); ssize_t linelen;
linelen = getline(&buf, &linecap, cmdPipe);
if (s == NULL) if (linelen < 0)
{ {
return ""; break;
} }
if (cnt == line) if (cnt == line)
{ {
string str(buffer); res = string(buf);
return str.substr(0, str.size()-1); break;
} }
} }
if (buf != NULL)
{
free(buf);
}
pclose(cmdPipe); pclose(cmdPipe);
} }
return ""; return res.substr(0, res.size() - 1);
} }
// use popen to call addr2line (using bfd.h directly would have inline word addressToWord(const uintptr_t addr)
// meant relinking everything) {
OStringStream nStream;
nStream << "0x" << hex << addr;
return nStream.str();
}
void printSourceFileAndLine void printSourceFileAndLine
( (
Ostream& os, Ostream& os,
const HashTable<label, fileName>& addressMap,
const fileName& filename, const fileName& filename,
const word& address Dl_info *info,
void *addr
) )
{ {
word myAddress = address; uintptr_t address = uintptr_t(addr);
word myAddress = addressToWord(address);
if (filename.ext() == "so") if (filename.ext() == "so")
{ {
// Convert offset into .so into offset into executable. // Convert address into offset into dynamic library
uintptr_t offset = uintptr_t(info->dli_fbase);
void *addr; intptr_t relativeAddress = address - offset;
sscanf(myAddress.c_str(), "%p", &addr); myAddress = addressToWord(relativeAddress);
Dl_info info;
dladdr(addr, &info);
uintptr_t offset = uintptr_t(info.dli_fbase);
IStringStream addressStr(address.substr(2));
uintptr_t addressValue = ReadHex<uintptr_t>(addressStr);
intptr_t relativeAddress = addressValue - offset;
// Reconstruct hex word from address
OStringStream nStream;
nStream << "0x" << hex << relativeAddress;
myAddress = nStream.str();
} }
if (filename[0] == '/') if (filename[0] == '/')
@ -139,35 +138,58 @@ void printSourceFileAndLine
} }
void getSymbolForRaw fileName absolutePath(const char* fn)
(
Ostream& os,
const string& raw,
const fileName& filename,
const word& address
)
{ {
if (filename.size() && filename[0] == '/') fileName fname(fn);
{
string fcnt = pOpen
(
"addr2line -f --demangle=auto --exe "
+ filename
+ " "
+ address
);
if (fcnt != "") if (fname[0] != '/' && fname[0] != '~')
{
string tmp = pOpen("which " + fname);
if (tmp[0] == '/' || tmp[0] == '~')
{ {
os << fcnt.c_str(); fname = tmp;
return;
} }
} }
os << "Uninterpreted: " << raw.c_str();
return fname;
} }
void error::safePrintStack(std::ostream& os) word demangleSymbol(const char* sn)
{
word res;
int st;
char* cxx_sname = abi::__cxa_demangle
(
sn,
NULL,
0,
&st
);
if (st == 0 && cxx_sname)
{
res = word(cxx_sname);
free(cxx_sname);
}
else
{
res = word(sn);
}
return res;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
void Foam::error::safePrintStack(std::ostream& os)
{ {
// Get raw stack symbols // Get raw stack symbols
void *array[100]; void *array[100];
@ -187,143 +209,46 @@ void error::safePrintStack(std::ostream& os)
} }
void error::printStack(Ostream& os) void Foam::error::printStack(Ostream& os)
{ {
// Reads the starting addresses for the dynamically linked libraries
// from the /proc/pid/maps-file
// I'm afraid this works only for Linux 2.6-Kernels (may work on 2.4)
// Note2: the filenames in here will have softlinks resolved so will
// go wrong when having e.g. OpenFOAM installed under a softlink.
HashTable<label, fileName> addressMap;
{
IFstream is("/proc/" + name(pid()) + "/maps");
while (is.good())
{
string line;
is.getLine(line);
string::size_type space = line.rfind(' ') + 1;
fileName libPath = line.substr(space, line.size()-space);
if (libPath.size() && libPath[0] == '/')
{
string offsetString(line.substr(0, line.find('-')));
IStringStream offsetStr(offsetString);
addressMap.insert(libPath, ReadHex<label>(offsetStr));
}
}
}
// Get raw stack symbols // Get raw stack symbols
void *array[100]; const size_t CALLSTACK_SIZE = 128;
size_t size = backtrace(array, 100);
char **strings = backtrace_symbols(array, size);
// See if they contain function between () e.g. "(__libc_start_main+0xd0)" void *callstack[CALLSTACK_SIZE];
// and see if cplus_demangle can make sense of part before + size_t size = backtrace(callstack, CALLSTACK_SIZE);
for (size_t i = 0; i < size; i++)
Dl_info *info = new Dl_info;
fileName fname = "???";
word address;
for(size_t i=0; i<size; i++)
{ {
string msg(strings[i]); int st = dladdr(callstack[i], info);
fileName programFile;
word address;
os << '#' << label(i) << " "; os << '#' << label(i) << " ";
//os << "Raw : " << msg << "\n\t"; if (st != 0 && info->dli_fname != NULL && info->dli_fname[0] != '\0')
{ {
string::size_type lPos = msg.find('['); fname = absolutePath(info->dli_fname);
string::size_type rPos = msg.find(']');
if (lPos != string::npos && rPos != string::npos && lPos < rPos) os <<
{ (
address = msg.substr(lPos+1, rPos-lPos-1); (info->dli_sname != NULL)
msg = msg.substr(0, lPos); ? demangleSymbol(info->dli_sname)
} : "?"
);
string::size_type bracketPos = msg.find('(');
string::size_type spacePos = msg.find(' ');
if (bracketPos != string::npos || spacePos != string::npos)
{
programFile = msg.substr(0, min(spacePos, bracketPos));
// not an absolute path
if (programFile[0] != '/')
{
string tmp = pOpen("which " + programFile);
if (tmp[0] == '/' || tmp[0] == '~')
{
programFile = tmp;
}
}
}
}
string::size_type bracketPos = msg.find('(');
if (bracketPos != string::npos)
{
string::size_type start = bracketPos+1;
string::size_type plusPos = msg.find('+', start);
if (plusPos != string::npos)
{
string cName(msg.substr(start, plusPos-start));
int status;
char* cplusNamePtr = abi::__cxa_demangle
(
cName.c_str(),
NULL, // have it malloc itself
0,
&status
);
if (status == 0 && cplusNamePtr)
{
os << cplusNamePtr;
free(cplusNamePtr);
}
else
{
os << cName.c_str();
}
}
else
{
string::size_type endBracketPos = msg.find(')', start);
if (endBracketPos != string::npos)
{
string fullName(msg.substr(start, endBracketPos-start));
os << fullName.c_str() << nl;
}
else
{
// Print raw message
getSymbolForRaw(os, msg, programFile, address);
}
}
} }
else else
{ {
// Print raw message os << "?";
getSymbolForRaw(os, msg, programFile, address);
} }
printSourceFileAndLine(os, addressMap, programFile, address); printSourceFileAndLine(os, fname, info, callstack[i]);
os << nl;
os << nl;
} }
free(strings); delete info;
} }
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// ************************************************************************* // // ************************************************************************* //