Improve exception handling on un*x: try to print a callstack in out-of-memory situations as well

This commit is contained in:
Alexander Mai 2015-11-21 16:50:57 +01:00
parent c62b23c4fc
commit 9757f5b5f4
2 changed files with 64 additions and 47 deletions

View File

@ -66,6 +66,9 @@
#include <TCHAR.H> #include <TCHAR.H>
#endif #endif
/*static*/ std::string CppCheckExecutor::exceptionOutput = "stdout";
CppCheckExecutor::CppCheckExecutor() CppCheckExecutor::CppCheckExecutor()
: _settings(0), time1(0), errorlist(false) : _settings(0), time1(0), errorlist(false)
{ {
@ -203,11 +206,13 @@ namespace {
* That is very sensitive to the operating system, hardware, compiler and runtime! * That is very sensitive to the operating system, hardware, compiler and runtime!
* The code is not meant for production environment, it's using functions not whitelisted for usage in a signal handler function. * The code is not meant for production environment, it's using functions not whitelisted for usage in a signal handler function.
*/ */
static void print_stacktrace(FILE* f, bool demangling, int maxdepth) void print_stacktrace(bool useStdout, bool demangling, int maxdepth, bool bLowMem)
{ {
#if defined(USE_UNIX_BACKTRACE_SUPPORT) #if defined(USE_UNIX_BACKTRACE_SUPPORT)
// 32 vs. 64bit // 32 vs. 64bit
#define ADDRESSDISPLAYLENGTH ((sizeof(long)==8)?12:8) #define ADDRESSDISPLAYLENGTH ((sizeof(long)==8)?12:8)
FILE* f = (useStdout) ? stdout : stderr;
const int fd = (useStdout) ? 1 : 2;
void *array[32]= {0}; // the less resources the better... void *array[32]= {0}; // the less resources the better...
const int currentdepth = backtrace(array, (int)GetArrayLength(array)); const int currentdepth = backtrace(array, (int)GetArrayLength(array));
const int offset=2; // some entries on top are within our own exception handling code or libc const int offset=2; // some entries on top are within our own exception handling code or libc
@ -215,6 +220,10 @@ namespace {
maxdepth=currentdepth-offset; maxdepth=currentdepth-offset;
else else
maxdepth = std::min(maxdepth, currentdepth); maxdepth = std::min(maxdepth, currentdepth);
if (bLowMem) {
fputs("Callstack:\n", f);
backtrace_symbols_fd(array+offset, maxdepth, fd);
} else {
char **symbolstrings = backtrace_symbols(array, currentdepth); char **symbolstrings = backtrace_symbols(array, currentdepth);
if (symbolstrings) { if (symbolstrings) {
fputs("Callstack:\n", f); fputs("Callstack:\n", f);
@ -258,13 +267,14 @@ namespace {
} else { } else {
fputs("Callstack could not be obtained\n", f); fputs("Callstack could not be obtained\n", f);
} }
}
#undef ADDRESSDISPLAYLENGTH #undef ADDRESSDISPLAYLENGTH
#endif #endif
} }
const size_t MYSTACKSIZE = 16*1024+SIGSTKSZ; // wild guess about a reasonable buffer const size_t MYSTACKSIZE = 16*1024+SIGSTKSZ; // wild guess about a reasonable buffer
char mytstack[MYSTACKSIZE]; // alternative stack for signal handler char mytstack[MYSTACKSIZE]= {0}; // alternative stack for signal handler
bool bStackBelowHeap=false; // lame attempt to locate heap vs. stack address space bool bStackBelowHeap=false; // lame attempt to locate heap vs. stack address space. See CppCheckExecutor::check_wrapper()
/* /*
* \return true if address is supposed to be on stack (contrary to heap or elsewhere). If ptr is 0 false will be returned. * \return true if address is supposed to be on stack (contrary to heap or elsewhere). If ptr is 0 false will be returned.
@ -322,10 +332,18 @@ namespace {
const Signalmap_t::const_iterator it=listofsignals.find(signo); const Signalmap_t::const_iterator it=listofsignals.find(signo);
const char * const signame = (it==listofsignals.end()) ? "unknown" : it->second.c_str(); const char * const signame = (it==listofsignals.end()) ? "unknown" : it->second.c_str();
bool bPrintCallstack=true; bool bPrintCallstack=true;
bool bLowMem=false;
bool bUnexpectedSignal=true; bool bUnexpectedSignal=true;
const bool isaddressonstack = isAddressOnStack(info->si_addr); const bool isaddressonstack = isAddressOnStack(info->si_addr);
FILE* f = (CppCheckExecutor::getExceptionOutput()=="stderr") ? stderr : stdout; const bool useStdout = CppCheckExecutor::getExceptionOutput()=="stdout";
FILE* f = (useStdout) ? stdout : stderr;
switch (signo) { switch (signo) {
case SIGABRT:
fputs("Internal error: cppcheck received signal ", f);
fputs(signame, f);
fputs(" - out of memory?\n", f);
bLowMem=true;
break;
case SIGBUS: case SIGBUS:
fputs("Internal error: cppcheck received signal ", f); fputs("Internal error: cppcheck received signal ", f);
fputs(signame, f); fputs(signame, f);
@ -464,7 +482,7 @@ namespace {
break; break;
} }
if (bPrintCallstack) { if (bPrintCallstack) {
print_stacktrace(f, true, -1); print_stacktrace(f, true, -1, bLowMem);
} }
if (bUnexpectedSignal) { if (bUnexpectedSignal) {
fputs("\nPlease report this to the cppcheck developers!\n", f); fputs("\nPlease report this to the cppcheck developers!\n", f);
@ -990,5 +1008,3 @@ bool CppCheckExecutor::tryLoadLibrary(Library& destination, const char* basepath
} }
return true; return true;
} }
std::string CppCheckExecutor::exceptionOutput;

View File

@ -89,11 +89,12 @@ public:
static void reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal); static void reportStatus(std::size_t fileindex, std::size_t filecount, std::size_t sizedone, std::size_t sizetotal);
/** /**
* @param fn file name to be used from exception handler * @param fn file name to be used from exception handler: Has to be either "stdout" or "stderr".
* Invalid arguments will be silently ignored. Default is "stdout".
*/ */
static void setExceptionOutput(const std::string& fn); static void setExceptionOutput(const std::string& fn);
/** /**
* @return file name to be used for output from exception handler * @return file name to be used for output from exception handler. Has to be either "stdout" or "stderr".
*/ */
static const std::string& getExceptionOutput(); static const std::string& getExceptionOutput();