--exception-handling now prints callstack on SIGINT, allows to debug program hangs. Also add handler for some more signals.
This commit is contained in:
parent
5cc2b38767
commit
bda9c8c2d5
2
Makefile
2
Makefile
|
@ -430,7 +430,7 @@ $(SRCDIR)/valueflow.o: lib/valueflow.cpp lib/cxx11emu.h lib/valueflow.h lib/conf
|
|||
cli/cmdlineparser.o: cli/cmdlineparser.cpp lib/cxx11emu.h cli/cmdlineparser.h lib/cppcheck.h lib/config.h lib/settings.h lib/library.h lib/mathlib.h lib/token.h lib/valueflow.h lib/suppressions.h lib/standards.h lib/timer.h lib/errorlogger.h lib/check.h lib/tokenize.h lib/tokenlist.h cli/cppcheckexecutor.h cli/filelister.h lib/path.h
|
||||
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cmdlineparser.o cli/cmdlineparser.cpp
|
||||
|
||||
cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp lib/cxx11emu.h cli/cppcheckexecutor.h lib/errorlogger.h lib/config.h lib/suppressions.h cli/cmdlineparser.h lib/cppcheck.h lib/settings.h lib/library.h lib/mathlib.h lib/token.h lib/valueflow.h lib/standards.h lib/timer.h lib/check.h lib/tokenize.h lib/tokenlist.h cli/filelister.h lib/path.h cli/pathmatch.h lib/preprocessor.h cli/threadexecutor.h
|
||||
cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp lib/cxx11emu.h cli/cppcheckexecutor.h lib/errorlogger.h lib/config.h lib/suppressions.h cli/cmdlineparser.h lib/cppcheck.h lib/settings.h lib/library.h lib/mathlib.h lib/token.h lib/valueflow.h lib/standards.h lib/timer.h lib/check.h lib/tokenize.h lib/tokenlist.h cli/filelister.h lib/path.h cli/pathmatch.h lib/preprocessor.h cli/threadexecutor.h lib/utils.h
|
||||
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CFG) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutor.o cli/cppcheckexecutor.cpp
|
||||
|
||||
cli/filelister.o: cli/filelister.cpp lib/cxx11emu.h cli/filelister.h lib/path.h lib/config.h cli/pathmatch.h
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "pathmatch.h"
|
||||
#include "preprocessor.h"
|
||||
#include "threadexecutor.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <climits>
|
||||
#include <cstdlib> // EXIT_SUCCESS and EXIT_FAILURE
|
||||
|
@ -167,7 +168,7 @@ int CppCheckExecutor::check(int argc, const char* const argv[])
|
|||
|
||||
CppCheck cppCheck(*this, true);
|
||||
|
||||
Settings& settings = cppCheck.settings();
|
||||
const Settings& settings = cppCheck.settings();
|
||||
_settings = &settings;
|
||||
|
||||
if (!parseFromArgs(&cppCheck, argc, argv)) {
|
||||
|
@ -189,53 +190,21 @@ int CppCheckExecutor::check(int argc, const char* const argv[])
|
|||
* \return size of array
|
||||
* */
|
||||
template<typename T, int size>
|
||||
size_t GetArrayLength(const T(&)[size])
|
||||
constexpr size_t GetArrayLength(const T(&)[size])
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
#if defined(USE_UNIX_SIGNAL_HANDLING)
|
||||
/* (declare this list here, so it may be used in signal handlers in addition to main())
|
||||
* A list of signals available in ISO C
|
||||
* Check out http://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html
|
||||
* For now we only want to detect abnormal behaviour for a few selected signals:
|
||||
*/
|
||||
struct Signaltype {
|
||||
int signalnumber;
|
||||
const char *signalname;
|
||||
};
|
||||
#define DECLARE_SIGNAL(x) {x, #x}
|
||||
static const Signaltype listofsignals[] = {
|
||||
// don't care: SIGABRT,
|
||||
DECLARE_SIGNAL(SIGBUS),
|
||||
DECLARE_SIGNAL(SIGFPE),
|
||||
DECLARE_SIGNAL(SIGILL),
|
||||
DECLARE_SIGNAL(SIGINT),
|
||||
DECLARE_SIGNAL(SIGSEGV),
|
||||
// don't care: SIGTERM
|
||||
DECLARE_SIGNAL(SIGUSR1)
|
||||
};
|
||||
|
||||
/*
|
||||
* Simple mapping
|
||||
*/
|
||||
static const char *signal_name(int signo)
|
||||
{
|
||||
for (size_t s=0; s<GetArrayLength(listofsignals); ++s) {
|
||||
if (listofsignals[s].signalnumber==signo)
|
||||
return listofsignals[s].signalname;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
namespace {
|
||||
/*
|
||||
* Try to print the callstack.
|
||||
* 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.
|
||||
*/
|
||||
static void print_stacktrace(FILE* f, bool demangling, int maxdepth)
|
||||
{
|
||||
static void print_stacktrace(FILE* f, bool demangling, int maxdepth)
|
||||
{
|
||||
#if defined(USE_UNIX_BACKTRACE_SUPPORT)
|
||||
// 32 vs. 64bit
|
||||
#define ADDRESSDISPLAYLENGTH ((sizeof(long)==8)?12:8)
|
||||
|
@ -291,18 +260,18 @@ static void print_stacktrace(FILE* f, bool demangling, int maxdepth)
|
|||
}
|
||||
#undef ADDRESSDISPLAYLENGTH
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
static const size_t MYSTACKSIZE = 16*1024+SIGSTKSZ; // wild guess about a reasonable buffer
|
||||
static char mytstack[MYSTACKSIZE]; // alternative stack for signal handler
|
||||
static bool bStackBelowHeap=false; // lame attempt to locate heap vs. stack address space
|
||||
const size_t MYSTACKSIZE = 16*1024+SIGSTKSZ; // wild guess about a reasonable buffer
|
||||
char mytstack[MYSTACKSIZE]; // alternative stack for signal handler
|
||||
bool bStackBelowHeap=false; // lame attempt to locate heap vs. stack address space
|
||||
|
||||
/*
|
||||
/*
|
||||
* \return true if address is supposed to be on stack (contrary to heap or elsewhere). If ptr is 0 false will be returned.
|
||||
* If unknown better return false.
|
||||
*/
|
||||
static bool isAddressOnStack(const void* ptr)
|
||||
{
|
||||
bool isAddressOnStack(const void* ptr)
|
||||
{
|
||||
if (nullptr==ptr)
|
||||
return false;
|
||||
char a;
|
||||
|
@ -310,16 +279,37 @@ static bool isAddressOnStack(const void* ptr)
|
|||
return ptr < &a;
|
||||
else
|
||||
return ptr > &a;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/* (declare this list here, so it may be used in signal handlers in addition to main())
|
||||
* A list of signals available in ISO C
|
||||
* Check out http://pubs.opengroup.org/onlinepubs/009695399/basedefs/signal.h.html
|
||||
* For now we only want to detect abnormal behaviour for a few selected signals:
|
||||
*/
|
||||
|
||||
#define DECLARE_SIGNAL(x) << std::make_pair(x, #x)
|
||||
typedef std::map<int, std::string> Signalmap_t;
|
||||
const Signalmap_t listofsignals = make_container< Signalmap_t > ()
|
||||
DECLARE_SIGNAL(SIGABRT)
|
||||
DECLARE_SIGNAL(SIGBUS)
|
||||
DECLARE_SIGNAL(SIGFPE)
|
||||
DECLARE_SIGNAL(SIGILL)
|
||||
DECLARE_SIGNAL(SIGINT)
|
||||
DECLARE_SIGNAL(SIGQUIT)
|
||||
DECLARE_SIGNAL(SIGSEGV)
|
||||
DECLARE_SIGNAL(SIGSYS)
|
||||
// don't care: SIGTERM
|
||||
DECLARE_SIGNAL(SIGUSR1)
|
||||
DECLARE_SIGNAL(SIGUSR2)
|
||||
;
|
||||
/*
|
||||
* Entry pointer for signal handlers
|
||||
* It uses functions which are not safe to be called from a signal handler,
|
||||
* but when ending up here something went terribly wrong anyway.
|
||||
* And all which is left is just printing some information and terminate.
|
||||
*/
|
||||
static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context)
|
||||
{
|
||||
void CppcheckSignalHandler(int signo, siginfo_t * info, void * context)
|
||||
{
|
||||
int type = -1;
|
||||
pid_t killid = getpid();
|
||||
const ucontext_t* const uc = reinterpret_cast<const ucontext_t*>(context);
|
||||
|
@ -329,7 +319,8 @@ static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context)
|
|||
type = (int)uc->uc_mcontext.gregs[REG_ERR] & 2;
|
||||
}
|
||||
#endif
|
||||
const char * const signame = signal_name(signo);
|
||||
const Signalmap_t::const_iterator it=listofsignals.find(signo);
|
||||
const char * const signame = (it==listofsignals.end()) ? "unknown" : it->second.c_str();
|
||||
bool bPrintCallstack=true;
|
||||
bool bUnexpectedSignal=true;
|
||||
const bool isaddressonstack = isAddressOnStack(info->si_addr);
|
||||
|
@ -437,7 +428,7 @@ static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context)
|
|||
bUnexpectedSignal=false;
|
||||
fputs("cppcheck received signal ", f);
|
||||
fputs(signame, f);
|
||||
bPrintCallstack=false;
|
||||
bPrintCallstack=true;
|
||||
fputs(".\n", f);
|
||||
break;
|
||||
case SIGSEGV:
|
||||
|
@ -483,33 +474,35 @@ static void CppcheckSignalHandler(int signo, siginfo_t * info, void * context)
|
|||
// now let things proceed, shutdown and hopefully dump core for post-mortem analysis
|
||||
signal(signo, SIG_DFL);
|
||||
kill(killid, signo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_WINDOWS_SEH
|
||||
static const ULONG maxnamelength = 512;
|
||||
struct IMAGEHLP_SYMBOL64_EXT : public IMAGEHLP_SYMBOL64 {
|
||||
namespace {
|
||||
const ULONG maxnamelength = 512;
|
||||
struct IMAGEHLP_SYMBOL64_EXT : public IMAGEHLP_SYMBOL64 {
|
||||
TCHAR NameExt[maxnamelength]; // actually no need to worry about character encoding here
|
||||
};
|
||||
typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
|
||||
static fpStackWalk64 pStackWalk64;
|
||||
typedef DWORD64(WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64);
|
||||
static fpSymGetModuleBase64 pSymGetModuleBase64;
|
||||
typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
|
||||
static fpSymGetSymFromAddr64 pSymGetSymFromAddr64;
|
||||
typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
|
||||
static fpSymGetLineFromAddr64 pSymGetLineFromAddr64;
|
||||
typedef DWORD (WINAPI *fpUnDecorateSymbolName)(const TCHAR*, PTSTR, DWORD, DWORD) ;
|
||||
static fpUnDecorateSymbolName pUnDecorateSymbolName;
|
||||
typedef PVOID(WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64);
|
||||
static fpSymFunctionTableAccess64 pSymFunctionTableAccess64;
|
||||
typedef BOOL (WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL);
|
||||
static fpSymInitialize pSymInitialize;
|
||||
};
|
||||
typedef BOOL (WINAPI *fpStackWalk64)(DWORD, HANDLE, HANDLE, LPSTACKFRAME64, PVOID, PREAD_PROCESS_MEMORY_ROUTINE64, PFUNCTION_TABLE_ACCESS_ROUTINE64, PGET_MODULE_BASE_ROUTINE64, PTRANSLATE_ADDRESS_ROUTINE64);
|
||||
fpStackWalk64 pStackWalk64;
|
||||
typedef DWORD64(WINAPI *fpSymGetModuleBase64)(HANDLE, DWORD64);
|
||||
fpSymGetModuleBase64 pSymGetModuleBase64;
|
||||
typedef BOOL (WINAPI *fpSymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64);
|
||||
fpSymGetSymFromAddr64 pSymGetSymFromAddr64;
|
||||
typedef BOOL (WINAPI *fpSymGetLineFromAddr64)(HANDLE, DWORD64, PDWORD, PIMAGEHLP_LINE64);
|
||||
fpSymGetLineFromAddr64 pSymGetLineFromAddr64;
|
||||
typedef DWORD (WINAPI *fpUnDecorateSymbolName)(const TCHAR*, PTSTR, DWORD, DWORD) ;
|
||||
fpUnDecorateSymbolName pUnDecorateSymbolName;
|
||||
typedef PVOID(WINAPI *fpSymFunctionTableAccess64)(HANDLE, DWORD64);
|
||||
fpSymFunctionTableAccess64 pSymFunctionTableAccess64;
|
||||
typedef BOOL (WINAPI *fpSymInitialize)(HANDLE, PCSTR, BOOL);
|
||||
fpSymInitialize pSymInitialize;
|
||||
|
||||
static HMODULE hLibDbgHelp;
|
||||
HMODULE hLibDbgHelp;
|
||||
// avoid explicit dependency on Dbghelp.dll
|
||||
static bool loadDbgHelp()
|
||||
{
|
||||
bool loadDbgHelp()
|
||||
{
|
||||
hLibDbgHelp = ::LoadLibraryW(L"Dbghelp.dll");
|
||||
if (!hLibDbgHelp)
|
||||
return false;
|
||||
|
@ -521,11 +514,11 @@ static bool loadDbgHelp()
|
|||
pSymInitialize = (fpSymInitialize) ::GetProcAddress(hLibDbgHelp, "SymInitialize");
|
||||
pUnDecorateSymbolName = (fpUnDecorateSymbolName)::GetProcAddress(hLibDbgHelp, "UnDecorateSymbolName");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void PrintCallstack(FILE* f, PEXCEPTION_POINTERS ex)
|
||||
{
|
||||
void PrintCallstack(FILE* f, PEXCEPTION_POINTERS ex)
|
||||
{
|
||||
if (!loadDbgHelp())
|
||||
return;
|
||||
const HANDLE hProcess = GetCurrentProcess();
|
||||
|
@ -594,10 +587,10 @@ static void PrintCallstack(FILE* f, PEXCEPTION_POINTERS ex)
|
|||
|
||||
FreeLibrary(hLibDbgHelp);
|
||||
hLibDbgHelp=0;
|
||||
}
|
||||
}
|
||||
|
||||
static void writeMemoryErrorDetails(FILE* f, PEXCEPTION_POINTERS ex, const char* description)
|
||||
{
|
||||
void writeMemoryErrorDetails(FILE* f, PEXCEPTION_POINTERS ex, const char* description)
|
||||
{
|
||||
fputs(description, f);
|
||||
fprintf(f, " (instruction: 0x%p) ", ex->ExceptionRecord->ExceptionAddress);
|
||||
// Using %p for ULONG_PTR later on, so it must have size identical to size of pointer
|
||||
|
@ -619,13 +612,13 @@ static void writeMemoryErrorDetails(FILE* f, PEXCEPTION_POINTERS ex, const char*
|
|||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Any evaluation of the exception needs to be done here!
|
||||
*/
|
||||
static int filterException(int code, PEXCEPTION_POINTERS ex)
|
||||
{
|
||||
int filterException(int code, PEXCEPTION_POINTERS ex)
|
||||
{
|
||||
FILE *f = stdout;
|
||||
fputs("Internal error: ", f);
|
||||
switch (ex->ExceptionRecord->ExceptionCode) {
|
||||
|
@ -704,6 +697,7 @@ static int filterException(int code, PEXCEPTION_POINTERS ex)
|
|||
PrintCallstack(f, ex);
|
||||
fflush(f);
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -743,8 +737,8 @@ int CppCheckExecutor::check_wrapper(CppCheck& cppcheck, int argc, const char* co
|
|||
memset(&act, 0, sizeof(act));
|
||||
act.sa_flags=SA_SIGINFO|SA_ONSTACK;
|
||||
act.sa_sigaction=CppcheckSignalHandler;
|
||||
for (std::size_t s=0; s<GetArrayLength(listofsignals); ++s) {
|
||||
sigaction(listofsignals[s].signalnumber, &act, NULL);
|
||||
for (std::map<int, std::string>::const_iterator sig=listofsignals.begin(); sig!=listofsignals.end(); ++sig) {
|
||||
sigaction(sig->first, &act, NULL);
|
||||
}
|
||||
return check_internal(cppcheck, argc, argv);
|
||||
#else
|
||||
|
|
Loading…
Reference in New Issue