cppcheckexecutor.cpp: extracted platform-dependent code into separate files (#4359)
This commit is contained in:
parent
72c764b034
commit
31d704e4bc
16
Makefile
16
Makefile
|
@ -209,10 +209,13 @@ EXTOBJ = externals/simplecpp/simplecpp.o \
|
|||
|
||||
CLIOBJ = cli/cmdlineparser.o \
|
||||
cli/cppcheckexecutor.o \
|
||||
cli/cppcheckexecutorseh.o \
|
||||
cli/cppcheckexecutorsig.o \
|
||||
cli/executor.o \
|
||||
cli/filelister.o \
|
||||
cli/main.o \
|
||||
cli/processexecutor.o \
|
||||
cli/stacktrace.o \
|
||||
cli/threadexecutor.o
|
||||
|
||||
TESTOBJ = test/options.o \
|
||||
|
@ -291,7 +294,7 @@ cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ)
|
|||
|
||||
all: cppcheck testrunner
|
||||
|
||||
testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o
|
||||
testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/cppcheckexecutorseh.o cli/cppcheckexecutorsig.o cli/stacktrace.o cli/filelister.o
|
||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)
|
||||
|
||||
test: all
|
||||
|
@ -574,9 +577,15 @@ $(libcppdir)/valueflow.o: lib/valueflow.cpp lib/analyzer.h lib/astutils.h lib/ca
|
|||
cli/cmdlineparser.o: cli/cmdlineparser.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/filelister.h externals/tinyxml2/tinyxml2.h lib/check.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
|
||||
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cmdlineparser.o cli/cmdlineparser.cpp
|
||||
|
||||
cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/threadexecutor.h externals/simplecpp/simplecpp.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
|
||||
cli/cppcheckexecutor.o: cli/cppcheckexecutor.cpp cli/cmdlineparser.h cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h cli/cppcheckexecutorsig.h cli/executor.h cli/filelister.h cli/processexecutor.h cli/threadexecutor.h externals/simplecpp/simplecpp.h lib/analyzerinfo.h lib/check.h lib/checkunusedfunctions.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/path.h lib/pathmatch.h lib/platform.h lib/preprocessor.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
|
||||
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutor.o cli/cppcheckexecutor.cpp
|
||||
|
||||
cli/cppcheckexecutorseh.o: cli/cppcheckexecutorseh.cpp cli/cppcheckexecutor.h cli/cppcheckexecutorseh.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h lib/utils.h
|
||||
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutorseh.o cli/cppcheckexecutorseh.cpp
|
||||
|
||||
cli/cppcheckexecutorsig.o: cli/cppcheckexecutorsig.cpp cli/cppcheckexecutor.h cli/cppcheckexecutorsig.h cli/stacktrace.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h
|
||||
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/cppcheckexecutorsig.o cli/cppcheckexecutorsig.cpp
|
||||
|
||||
cli/executor.o: cli/executor.cpp cli/executor.h
|
||||
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/executor.o cli/executor.cpp
|
||||
|
||||
|
@ -589,6 +598,9 @@ cli/main.o: cli/main.cpp cli/cppcheckexecutor.h lib/color.h lib/config.h lib/err
|
|||
cli/processexecutor.o: cli/processexecutor.cpp cli/cppcheckexecutor.h cli/executor.h cli/processexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
|
||||
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/processexecutor.o cli/processexecutor.cpp
|
||||
|
||||
cli/stacktrace.o: cli/stacktrace.cpp cli/stacktrace.h lib/config.h lib/utils.h
|
||||
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/stacktrace.o cli/stacktrace.cpp
|
||||
|
||||
cli/threadexecutor.o: cli/threadexecutor.cpp cli/cppcheckexecutor.h cli/executor.h cli/threadexecutor.h lib/analyzerinfo.h lib/check.h lib/color.h lib/config.h lib/cppcheck.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
|
||||
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CPPFILESDIR) $(CXXFLAGS) $(UNDEF_STRICT_ANSI) -c -o cli/threadexecutor.o cli/threadexecutor.cpp
|
||||
|
||||
|
|
|
@ -407,10 +407,13 @@
|
|||
<ClInclude Include="..\lib\config.h" />
|
||||
<ClInclude Include="cmdlineparser.h" />
|
||||
<ClInclude Include="cppcheckexecutor.h" />
|
||||
<ClCompile Include="cppcheckexecutorseh.h" />
|
||||
<ClCompile Include="cppcheckexecutorsig.h" />
|
||||
<ClInclude Include="executor.h" />
|
||||
<ClInclude Include="filelister.h" />
|
||||
<ClInclude Include="processexecutor.h" />
|
||||
<ClInclude Include="threadexecutor.h" />
|
||||
<ClInclude Include="stacktrace.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\lib\cppcheck.vcxproj">
|
||||
|
@ -420,11 +423,14 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="cmdlineparser.cpp" />
|
||||
<ClCompile Include="cppcheckexecutor.cpp" />
|
||||
<ClCompile Include="cppcheckexecutorseh.cpp" />
|
||||
<ClCompile Include="cppcheckexecutorsig.cpp" />
|
||||
<ClCompile Include="executor.cpp" />
|
||||
<ClCompile Include="filelister.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="processexecutor.cpp" />
|
||||
<ClCompile Include="threadexecutor.cpp" />
|
||||
<ClCompile Include="stacktrace.cpp" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets" />
|
||||
|
|
|
@ -26,9 +26,18 @@
|
|||
<ClInclude Include="cppcheckexecutor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cppcheckexecutorseh.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="cppcheckexecutorsig.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="filelister.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="stacktrace.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
|
@ -40,12 +49,21 @@
|
|||
<ClCompile Include="cppcheckexecutor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cppcheckexecutorseh.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cppcheckexecutorsig.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="filelister.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="cmdlineparser.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="stacktrace.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="version.rc">
|
||||
|
|
|
@ -341,6 +341,7 @@ bool CmdLineParser::parseFromArgs(int argc, const char* const argv[])
|
|||
else if (std::strcmp(argv[i], "--exception-handling") == 0)
|
||||
mSettings->exceptionHandling = true;
|
||||
|
||||
// TODO: only applied with Signal handling i.e. Linux
|
||||
else if (std::strncmp(argv[i], "--exception-handling=", 21) == 0) {
|
||||
mSettings->exceptionHandling = true;
|
||||
const std::string exceptionOutfilename = &(argv[i][21]);
|
||||
|
|
|
@ -41,56 +41,26 @@
|
|||
#include "processexecutor.h"
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cstdlib> // EXIT_SUCCESS and EXIT_FAILURE
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#if !defined(NO_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && !defined(__MINGW32__) && !defined(__OS2__)
|
||||
#define USE_UNIX_SIGNAL_HANDLING
|
||||
#include <unistd.h>
|
||||
#if defined(__APPLE__)
|
||||
# define _XOPEN_SOURCE // ucontext.h APIs can only be used on Mac OSX >= 10.7 if _XOPEN_SOURCE is defined
|
||||
# include <ucontext.h>
|
||||
|
||||
# undef _XOPEN_SOURCE
|
||||
#elif !defined(__OpenBSD__) && !defined(__HAIKU__)
|
||||
# include <ucontext.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <syscall.h>
|
||||
#endif
|
||||
#ifdef USE_UNIX_SIGNAL_HANDLING
|
||||
#include "cppcheckexecutorsig.h"
|
||||
#endif
|
||||
|
||||
#if !defined(NO_UNIX_BACKTRACE_SUPPORT) && defined(USE_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && defined(__GLIBC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__NetBSD__) && !defined(__SVR4) && !defined(__QNX__)
|
||||
#define USE_UNIX_BACKTRACE_SUPPORT
|
||||
#include <cxxabi.h>
|
||||
#include <execinfo.h>
|
||||
#ifdef USE_WINDOWS_SEH
|
||||
#include "cppcheckexecutorseh.h"
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if defined(_MSC_VER)
|
||||
#define USE_WINDOWS_SEH
|
||||
#endif
|
||||
#if defined (__MINGW32__)
|
||||
# include <windows.h>
|
||||
# include <dbghelp.h>
|
||||
# include <tchar.h>
|
||||
#else
|
||||
# include <Windows.h>
|
||||
# include <DbgHelp.h>
|
||||
# include <TCHAR.H>
|
||||
#endif
|
||||
#include <excpt.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -264,605 +234,12 @@ void CppCheckExecutor::setSettings(const Settings &settings)
|
|||
mSettings = &settings;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple helper function:
|
||||
* \return size of array
|
||||
* */
|
||||
template<typename T, int size>
|
||||
std::size_t getArrayLength(const T (&)[size])
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
#if defined(USE_UNIX_SIGNAL_HANDLING)
|
||||
/*
|
||||
* 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!
|
||||
* One reason is named first: it's using functions not whitelisted for usage in a signal handler function.
|
||||
*/
|
||||
static void print_stacktrace(FILE* output, bool demangling, int maxdepth, bool lowMem)
|
||||
{
|
||||
#if defined(USE_UNIX_BACKTRACE_SUPPORT)
|
||||
// 32 vs. 64bit
|
||||
#define ADDRESSDISPLAYLENGTH ((sizeof(long)==8)?12:8)
|
||||
const int fd = fileno(output);
|
||||
void *callstackArray[32]= {nullptr}; // the less resources the better...
|
||||
const int currentdepth = backtrace(callstackArray, (int)getArrayLength(callstackArray));
|
||||
const int offset=2; // some entries on top are within our own exception handling code or libc
|
||||
if (maxdepth<0)
|
||||
maxdepth=currentdepth-offset;
|
||||
else
|
||||
maxdepth = std::min(maxdepth, currentdepth);
|
||||
if (lowMem) {
|
||||
fputs("Callstack (symbols only):\n", output);
|
||||
backtrace_symbols_fd(callstackArray+offset, maxdepth, fd);
|
||||
} else {
|
||||
char **symbolStringList = backtrace_symbols(callstackArray, currentdepth);
|
||||
if (symbolStringList) {
|
||||
fputs("Callstack:\n", output);
|
||||
char demangle_buffer[2048]= {0};
|
||||
for (int i = offset; i < maxdepth; ++i) {
|
||||
const char * const symbolString = symbolStringList[i];
|
||||
char * realnameString = nullptr;
|
||||
const char * const firstBracketName = strchr(symbolString, '(');
|
||||
const char * const firstBracketAddress = strchr(symbolString, '[');
|
||||
const char * const secondBracketAddress = strchr(firstBracketAddress, ']');
|
||||
const char * const beginAddress = firstBracketAddress+3;
|
||||
const int addressLen = int(secondBracketAddress-beginAddress);
|
||||
const int padLen = int(ADDRESSDISPLAYLENGTH-addressLen);
|
||||
if (demangling && firstBracketName) {
|
||||
const char * const plus = strchr(firstBracketName, '+');
|
||||
if (plus && (plus>(firstBracketName+1))) {
|
||||
char input_buffer[1024]= {0};
|
||||
strncpy(input_buffer, firstBracketName+1, plus-firstBracketName-1);
|
||||
size_t length = getArrayLength(demangle_buffer);
|
||||
int status=0;
|
||||
// We're violating the specification - passing stack address instead of malloc'ed heap.
|
||||
// Benefit is that no further heap is required, while there is sufficient stack...
|
||||
realnameString = abi::__cxa_demangle(input_buffer, demangle_buffer, &length, &status); // non-NULL on success
|
||||
}
|
||||
}
|
||||
const int ordinal=i-offset;
|
||||
fprintf(output, "#%-2d 0x",
|
||||
ordinal);
|
||||
if (padLen>0)
|
||||
fprintf(output, "%0*d",
|
||||
padLen, 0);
|
||||
if (realnameString) {
|
||||
fprintf(output, "%.*s in %s\n",
|
||||
(int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3,
|
||||
realnameString);
|
||||
} else {
|
||||
fprintf(output, "%.*s in %.*s\n",
|
||||
(int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3,
|
||||
(int)(firstBracketAddress-symbolString), symbolString);
|
||||
}
|
||||
}
|
||||
free(symbolStringList);
|
||||
} else {
|
||||
fputs("Callstack could not be obtained\n", output);
|
||||
}
|
||||
}
|
||||
#undef ADDRESSDISPLAYLENGTH
|
||||
#else
|
||||
(void)output;
|
||||
(void)demangling;
|
||||
(void)maxdepth;
|
||||
(void)lowMem;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef __USE_DYNAMIC_STACK_SIZE
|
||||
static const size_t MYSTACKSIZE = 16*1024+32768; // wild guess about a reasonable buffer
|
||||
#else
|
||||
static const size_t MYSTACKSIZE = 16*1024+SIGSTKSZ; // wild guess about a reasonable buffer
|
||||
#endif
|
||||
static char mytstack[MYSTACKSIZE]= {0}; // alternative stack for signal handler
|
||||
static bool bStackBelowHeap=false; // lame attempt to locate heap vs. stack address space. See CppCheckExecutor::check_wrapper()
|
||||
|
||||
/**
|
||||
* \param[in] ptr address to be examined.
|
||||
* \return true if address is supposed to be on stack (contrary to heap). If ptr is 0 false will be returned.
|
||||
* If unknown better return false.
|
||||
*/
|
||||
static bool IsAddressOnStack(const void* ptr)
|
||||
{
|
||||
if (nullptr==ptr)
|
||||
return false;
|
||||
char a;
|
||||
if (bStackBelowHeap)
|
||||
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)
|
||||
using Signalmap_t = std::map<int, std::string>;
|
||||
static const Signalmap_t listofsignals = {
|
||||
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) no usage currently
|
||||
};
|
||||
#undef DECLARE_SIGNAL
|
||||
/*
|
||||
* Entry pointer for signal handlers
|
||||
* It uses functions which are not safe to be called from a signal handler,
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04 has a whitelist)
|
||||
* 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)
|
||||
{
|
||||
int type = -1;
|
||||
pid_t killid;
|
||||
#if defined(__linux__) && defined(REG_ERR)
|
||||
const ucontext_t* const uc = reinterpret_cast<const ucontext_t*>(context);
|
||||
killid = (pid_t) syscall(SYS_gettid);
|
||||
if (uc) {
|
||||
type = (int)uc->uc_mcontext.gregs[REG_ERR] & 2;
|
||||
}
|
||||
#else
|
||||
(void)context;
|
||||
killid = getpid();
|
||||
#endif
|
||||
|
||||
const Signalmap_t::const_iterator it=listofsignals.find(signo);
|
||||
const char * const signame = (it==listofsignals.end()) ? "unknown" : it->second.c_str();
|
||||
bool printCallstack=true; // try to print a callstack?
|
||||
bool lowMem=false; // was low-memory condition detected? Be careful then! Avoid allocating much more memory then.
|
||||
bool unexpectedSignal=true; // unexpected indicates program failure
|
||||
bool terminate=true; // exit process/thread
|
||||
const bool isAddressOnStack = IsAddressOnStack(info->si_addr);
|
||||
FILE* output = CppCheckExecutor::getExceptionOutput();
|
||||
switch (signo) {
|
||||
case SIGABRT:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
fputs(
|
||||
#ifdef NDEBUG
|
||||
" - out of memory?\n",
|
||||
#else
|
||||
" - out of memory or assertion?\n",
|
||||
#endif
|
||||
output);
|
||||
lowMem=true; // educated guess
|
||||
break;
|
||||
case SIGBUS:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
switch (info->si_code) {
|
||||
case BUS_ADRALN: // invalid address alignment
|
||||
fputs(" - BUS_ADRALN", output);
|
||||
break;
|
||||
case BUS_ADRERR: // nonexistent physical address
|
||||
fputs(" - BUS_ADRERR", output);
|
||||
break;
|
||||
case BUS_OBJERR: // object-specific hardware error
|
||||
fputs(" - BUS_OBJERR", output);
|
||||
break;
|
||||
#ifdef BUS_MCEERR_AR
|
||||
case BUS_MCEERR_AR: // Hardware memory error consumed on a machine check;
|
||||
fputs(" - BUS_MCEERR_AR", output);
|
||||
break;
|
||||
#endif
|
||||
#ifdef BUS_MCEERR_AO
|
||||
case BUS_MCEERR_AO: // Hardware memory error detected in process but not consumed
|
||||
fputs(" - BUS_MCEERR_AO", output);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(output, " (at 0x%lx).\n",
|
||||
(unsigned long)info->si_addr);
|
||||
break;
|
||||
case SIGFPE:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
switch (info->si_code) {
|
||||
case FPE_INTDIV: // integer divide by zero
|
||||
fputs(" - FPE_INTDIV", output);
|
||||
break;
|
||||
case FPE_INTOVF: // integer overflow
|
||||
fputs(" - FPE_INTOVF", output);
|
||||
break;
|
||||
case FPE_FLTDIV: // floating-point divide by zero
|
||||
fputs(" - FPE_FLTDIV", output);
|
||||
break;
|
||||
case FPE_FLTOVF: // floating-point overflow
|
||||
fputs(" - FPE_FLTOVF", output);
|
||||
break;
|
||||
case FPE_FLTUND: // floating-point underflow
|
||||
fputs(" - FPE_FLTUND", output);
|
||||
break;
|
||||
case FPE_FLTRES: // floating-point inexact result
|
||||
fputs(" - FPE_FLTRES", output);
|
||||
break;
|
||||
case FPE_FLTINV: // floating-point invalid operation
|
||||
fputs(" - FPE_FLTINV", output);
|
||||
break;
|
||||
case FPE_FLTSUB: // subscript out of range
|
||||
fputs(" - FPE_FLTSUB", output);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(output, " (at 0x%lx).\n",
|
||||
(unsigned long)info->si_addr);
|
||||
break;
|
||||
case SIGILL:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
switch (info->si_code) {
|
||||
case ILL_ILLOPC: // illegal opcode
|
||||
fputs(" - ILL_ILLOPC", output);
|
||||
break;
|
||||
case ILL_ILLOPN: // illegal operand
|
||||
fputs(" - ILL_ILLOPN", output);
|
||||
break;
|
||||
case ILL_ILLADR: // illegal addressing mode
|
||||
fputs(" - ILL_ILLADR", output);
|
||||
break;
|
||||
case ILL_ILLTRP: // illegal trap
|
||||
fputs(" - ILL_ILLTRP", output);
|
||||
break;
|
||||
case ILL_PRVOPC: // privileged opcode
|
||||
fputs(" - ILL_PRVOPC", output);
|
||||
break;
|
||||
case ILL_PRVREG: // privileged register
|
||||
fputs(" - ILL_PRVREG", output);
|
||||
break;
|
||||
case ILL_COPROC: // coprocessor error
|
||||
fputs(" - ILL_COPROC", output);
|
||||
break;
|
||||
case ILL_BADSTK: // internal stack error
|
||||
fputs(" - ILL_BADSTK", output);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(output, " (at 0x%lx).%s\n",
|
||||
(unsigned long)info->si_addr,
|
||||
(isAddressOnStack)?" Stackoverflow?":"");
|
||||
break;
|
||||
case SIGINT:
|
||||
unexpectedSignal=false; // legal usage: interrupt application via CTRL-C
|
||||
fputs("cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
printCallstack=true;
|
||||
fputs(".\n", output);
|
||||
break;
|
||||
case SIGSEGV:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
switch (info->si_code) {
|
||||
case SEGV_MAPERR: // address not mapped to object
|
||||
fputs(" - SEGV_MAPERR", output);
|
||||
break;
|
||||
case SEGV_ACCERR: // invalid permissions for mapped object
|
||||
fputs(" - SEGV_ACCERR", output);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(output, " (%sat 0x%lx).%s\n",
|
||||
// cppcheck-suppress knownConditionTrueFalse ; FP
|
||||
(type==-1)? "" :
|
||||
(type==0) ? "reading " : "writing ",
|
||||
(unsigned long)info->si_addr,
|
||||
(isAddressOnStack)?" Stackoverflow?":""
|
||||
);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
fputs("cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
fputs(".\n", output);
|
||||
terminate=false;
|
||||
break;
|
||||
default:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
fputs(".\n", output);
|
||||
break;
|
||||
}
|
||||
if (printCallstack) {
|
||||
print_stacktrace(output, true, -1, lowMem);
|
||||
}
|
||||
if (unexpectedSignal) {
|
||||
fputs("\nPlease report this to the cppcheck developers!\n", output);
|
||||
}
|
||||
fflush(output);
|
||||
|
||||
if (terminate) {
|
||||
// now let things proceed, shutdown and hopefully dump core for post-mortem analysis
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler=SIG_DFL;
|
||||
sigaction(signo, &act, nullptr);
|
||||
kill(killid, signo);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_WINDOWS_SEH
|
||||
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);
|
||||
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;
|
||||
|
||||
HMODULE hLibDbgHelp;
|
||||
// avoid explicit dependency on Dbghelp.dll
|
||||
bool loadDbgHelp()
|
||||
{
|
||||
hLibDbgHelp = ::LoadLibraryW(L"Dbghelp.dll");
|
||||
if (!hLibDbgHelp)
|
||||
return false;
|
||||
pStackWalk64 = (fpStackWalk64) ::GetProcAddress(hLibDbgHelp, "StackWalk64");
|
||||
pSymGetModuleBase64 = (fpSymGetModuleBase64) ::GetProcAddress(hLibDbgHelp, "SymGetModuleBase64");
|
||||
pSymGetSymFromAddr64 = (fpSymGetSymFromAddr64) ::GetProcAddress(hLibDbgHelp, "SymGetSymFromAddr64");
|
||||
pSymGetLineFromAddr64 = (fpSymGetLineFromAddr64)::GetProcAddress(hLibDbgHelp, "SymGetLineFromAddr64");
|
||||
pSymFunctionTableAccess64 = (fpSymFunctionTableAccess64)::GetProcAddress(hLibDbgHelp, "SymFunctionTableAccess64");
|
||||
pSymInitialize = (fpSymInitialize) ::GetProcAddress(hLibDbgHelp, "SymInitialize");
|
||||
pUnDecorateSymbolName = (fpUnDecorateSymbolName)::GetProcAddress(hLibDbgHelp, "UnDecorateSymbolName");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void printCallstack(FILE* outputFile, PEXCEPTION_POINTERS ex)
|
||||
{
|
||||
if (!loadDbgHelp())
|
||||
return;
|
||||
const HANDLE hProcess = GetCurrentProcess();
|
||||
const HANDLE hThread = GetCurrentThread();
|
||||
pSymInitialize(
|
||||
hProcess,
|
||||
nullptr,
|
||||
TRUE
|
||||
);
|
||||
CONTEXT context = *(ex->ContextRecord);
|
||||
STACKFRAME64 stack= {0};
|
||||
#ifdef _M_IX86
|
||||
stack.AddrPC.Offset = context.Eip;
|
||||
stack.AddrPC.Mode = AddrModeFlat;
|
||||
stack.AddrStack.Offset = context.Esp;
|
||||
stack.AddrStack.Mode = AddrModeFlat;
|
||||
stack.AddrFrame.Offset = context.Ebp;
|
||||
stack.AddrFrame.Mode = AddrModeFlat;
|
||||
#else
|
||||
stack.AddrPC.Offset = context.Rip;
|
||||
stack.AddrPC.Mode = AddrModeFlat;
|
||||
stack.AddrStack.Offset = context.Rsp;
|
||||
stack.AddrStack.Mode = AddrModeFlat;
|
||||
stack.AddrFrame.Offset = context.Rsp;
|
||||
stack.AddrFrame.Mode = AddrModeFlat;
|
||||
#endif
|
||||
IMAGEHLP_SYMBOL64_EXT symbol;
|
||||
symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||
symbol.MaxNameLength = maxnamelength;
|
||||
DWORD64 displacement = 0;
|
||||
int beyond_main=-1; // emergency exit, see below
|
||||
for (ULONG frame = 0; ; frame++) {
|
||||
BOOL result = pStackWalk64
|
||||
(
|
||||
#ifdef _M_IX86
|
||||
IMAGE_FILE_MACHINE_I386,
|
||||
#else
|
||||
IMAGE_FILE_MACHINE_AMD64,
|
||||
#endif
|
||||
hProcess,
|
||||
hThread,
|
||||
&stack,
|
||||
&context,
|
||||
nullptr,
|
||||
pSymFunctionTableAccess64,
|
||||
pSymGetModuleBase64,
|
||||
nullptr
|
||||
);
|
||||
if (!result) // official end...
|
||||
break;
|
||||
pSymGetSymFromAddr64(hProcess, (ULONG64)stack.AddrPC.Offset, &displacement, &symbol);
|
||||
TCHAR undname[maxnamelength]= {0};
|
||||
pUnDecorateSymbolName((const TCHAR*)symbol.Name, (PTSTR)undname, (DWORD)getArrayLength(undname), UNDNAME_COMPLETE);
|
||||
if (beyond_main>=0)
|
||||
++beyond_main;
|
||||
if (_tcscmp(undname, _T("main"))==0)
|
||||
beyond_main=0;
|
||||
fprintf(outputFile,
|
||||
"%lu. 0x%08I64X in ",
|
||||
frame, (ULONG64)stack.AddrPC.Offset);
|
||||
fputs((const char *)undname, outputFile);
|
||||
fputc('\n', outputFile);
|
||||
if (0==stack.AddrReturn.Offset || beyond_main>2) // StackWalk64() sometimes doesn't reach any end...
|
||||
break;
|
||||
}
|
||||
|
||||
FreeLibrary(hLibDbgHelp);
|
||||
hLibDbgHelp=nullptr;
|
||||
}
|
||||
|
||||
void writeMemoryErrorDetails(FILE* outputFile, PEXCEPTION_POINTERS ex, const char* description)
|
||||
{
|
||||
fputs(description, outputFile);
|
||||
fprintf(outputFile, " (instruction: 0x%p) ", ex->ExceptionRecord->ExceptionAddress);
|
||||
// Using %p for ULONG_PTR later on, so it must have size identical to size of pointer
|
||||
// This is not the universally portable solution but good enough for Win32/64
|
||||
C_ASSERT(sizeof(void*) == sizeof(ex->ExceptionRecord->ExceptionInformation[1]));
|
||||
switch (ex->ExceptionRecord->ExceptionInformation[0]) {
|
||||
case 0:
|
||||
fprintf(outputFile, "reading from 0x%p",
|
||||
reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
|
||||
break;
|
||||
case 1:
|
||||
fprintf(outputFile, "writing to 0x%p",
|
||||
reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
|
||||
break;
|
||||
case 8:
|
||||
fprintf(outputFile, "data execution prevention at 0x%p",
|
||||
reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Any evaluation of the exception needs to be done here!
|
||||
*/
|
||||
int filterException(int code, PEXCEPTION_POINTERS ex)
|
||||
{
|
||||
FILE *outputFile = stdout;
|
||||
fputs("Internal error: ", outputFile);
|
||||
switch (ex->ExceptionRecord->ExceptionCode) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
writeMemoryErrorDetails(outputFile, ex, "Access violation");
|
||||
break;
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||
fputs("Out of array bounds", outputFile);
|
||||
break;
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
fputs("Breakpoint", outputFile);
|
||||
break;
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
fputs("Misaligned data", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
fputs("Denormalized floating-point value", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
fputs("Floating-point divide-by-zero", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
fputs("Inexact floating-point value", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
fputs("Invalid floating-point operation", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
fputs("Floating-point overflow", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_STACK_CHECK:
|
||||
fputs("Floating-point stack overflow", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
fputs("Floating-point underflow", outputFile);
|
||||
break;
|
||||
case EXCEPTION_GUARD_PAGE:
|
||||
fputs("Page-guard access", outputFile);
|
||||
break;
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
fputs("Illegal instruction", outputFile);
|
||||
break;
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
writeMemoryErrorDetails(outputFile, ex, "Invalid page access");
|
||||
break;
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
fputs("Integer divide-by-zero", outputFile);
|
||||
break;
|
||||
case EXCEPTION_INT_OVERFLOW:
|
||||
fputs("Integer overflow", outputFile);
|
||||
break;
|
||||
case EXCEPTION_INVALID_DISPOSITION:
|
||||
fputs("Invalid exception dispatcher", outputFile);
|
||||
break;
|
||||
case EXCEPTION_INVALID_HANDLE:
|
||||
fputs("Invalid handle", outputFile);
|
||||
break;
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||
fputs("Non-continuable exception", outputFile);
|
||||
break;
|
||||
case EXCEPTION_PRIV_INSTRUCTION:
|
||||
fputs("Invalid instruction", outputFile);
|
||||
break;
|
||||
case EXCEPTION_SINGLE_STEP:
|
||||
fputs("Single instruction step", outputFile);
|
||||
break;
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
fputs("Stack overflow", outputFile);
|
||||
break;
|
||||
default:
|
||||
fprintf(outputFile, "Unknown exception (%d)\n",
|
||||
code);
|
||||
break;
|
||||
}
|
||||
fputc('\n', outputFile);
|
||||
printCallstack(outputFile, ex);
|
||||
fflush(outputFile);
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Signal/SEH handling
|
||||
* Has to be clean for using with SEH on windows, i.e. no construction of C++ object instances is allowed!
|
||||
* TODO Check for multi-threading issues!
|
||||
*
|
||||
*/
|
||||
int CppCheckExecutor::check_wrapper(CppCheck& cppcheck)
|
||||
{
|
||||
#ifdef USE_WINDOWS_SEH
|
||||
FILE *outputFile = stdout;
|
||||
__try {
|
||||
return check_internal(cppcheck);
|
||||
} __except (filterException(GetExceptionCode(), GetExceptionInformation())) {
|
||||
// reporting to stdout may not be helpful within a GUI application...
|
||||
fputs("Please report this to the cppcheck developers!\n", outputFile);
|
||||
return -1;
|
||||
}
|
||||
return check_wrapper_seh(*this, &CppCheckExecutor::check_internal, cppcheck);
|
||||
#elif defined(USE_UNIX_SIGNAL_HANDLING)
|
||||
// determine stack vs. heap
|
||||
char stackVariable;
|
||||
char *heapVariable=(char*)malloc(1);
|
||||
bStackBelowHeap = &stackVariable < heapVariable;
|
||||
free(heapVariable);
|
||||
|
||||
// set up alternative stack for signal handler
|
||||
stack_t segv_stack;
|
||||
segv_stack.ss_sp = mytstack;
|
||||
segv_stack.ss_flags = 0;
|
||||
segv_stack.ss_size = MYSTACKSIZE;
|
||||
sigaltstack(&segv_stack, nullptr);
|
||||
|
||||
// install signal handler
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_flags=SA_SIGINFO|SA_ONSTACK;
|
||||
act.sa_sigaction=CppcheckSignalHandler;
|
||||
for (std::map<int, std::string>::const_iterator sig=listofsignals.begin(); sig!=listofsignals.end(); ++sig) {
|
||||
sigaction(sig->first, &act, nullptr);
|
||||
}
|
||||
return check_internal(cppcheck);
|
||||
return check_wrapper_sig(*this, &CppCheckExecutor::check_internal, cppcheck);
|
||||
#else
|
||||
return check_internal(cppcheck);
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* Cppcheck - A tool for static C/C++ code analysis
|
||||
* Copyright (C) 2007-2022 Cppcheck team.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cppcheckexecutorseh.h"
|
||||
|
||||
#ifdef USE_WINDOWS_SEH
|
||||
|
||||
#include "cppcheckexecutor.h"
|
||||
#include "utils.h"
|
||||
|
||||
#include <windows.h>
|
||||
#include <dbghelp.h>
|
||||
#include <tchar.h>
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
HMODULE hLibDbgHelp;
|
||||
// avoid explicit dependency on Dbghelp.dll
|
||||
bool loadDbgHelp()
|
||||
{
|
||||
hLibDbgHelp = ::LoadLibraryW(L"Dbghelp.dll");
|
||||
if (!hLibDbgHelp)
|
||||
return false;
|
||||
pStackWalk64 = (fpStackWalk64) ::GetProcAddress(hLibDbgHelp, "StackWalk64");
|
||||
pSymGetModuleBase64 = (fpSymGetModuleBase64) ::GetProcAddress(hLibDbgHelp, "SymGetModuleBase64");
|
||||
pSymGetSymFromAddr64 = (fpSymGetSymFromAddr64) ::GetProcAddress(hLibDbgHelp, "SymGetSymFromAddr64");
|
||||
pSymGetLineFromAddr64 = (fpSymGetLineFromAddr64)::GetProcAddress(hLibDbgHelp, "SymGetLineFromAddr64");
|
||||
pSymFunctionTableAccess64 = (fpSymFunctionTableAccess64)::GetProcAddress(hLibDbgHelp, "SymFunctionTableAccess64");
|
||||
pSymInitialize = (fpSymInitialize) ::GetProcAddress(hLibDbgHelp, "SymInitialize");
|
||||
pUnDecorateSymbolName = (fpUnDecorateSymbolName)::GetProcAddress(hLibDbgHelp, "UnDecorateSymbolName");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void printCallstack(FILE* outputFile, PEXCEPTION_POINTERS ex)
|
||||
{
|
||||
if (!loadDbgHelp())
|
||||
return;
|
||||
const HANDLE hProcess = GetCurrentProcess();
|
||||
const HANDLE hThread = GetCurrentThread();
|
||||
pSymInitialize(
|
||||
hProcess,
|
||||
nullptr,
|
||||
TRUE
|
||||
);
|
||||
CONTEXT context = *(ex->ContextRecord);
|
||||
STACKFRAME64 stack= {0};
|
||||
#ifdef _M_IX86
|
||||
stack.AddrPC.Offset = context.Eip;
|
||||
stack.AddrPC.Mode = AddrModeFlat;
|
||||
stack.AddrStack.Offset = context.Esp;
|
||||
stack.AddrStack.Mode = AddrModeFlat;
|
||||
stack.AddrFrame.Offset = context.Ebp;
|
||||
stack.AddrFrame.Mode = AddrModeFlat;
|
||||
#else
|
||||
stack.AddrPC.Offset = context.Rip;
|
||||
stack.AddrPC.Mode = AddrModeFlat;
|
||||
stack.AddrStack.Offset = context.Rsp;
|
||||
stack.AddrStack.Mode = AddrModeFlat;
|
||||
stack.AddrFrame.Offset = context.Rsp;
|
||||
stack.AddrFrame.Mode = AddrModeFlat;
|
||||
#endif
|
||||
IMAGEHLP_SYMBOL64_EXT symbol;
|
||||
symbol.SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||
symbol.MaxNameLength = maxnamelength;
|
||||
DWORD64 displacement = 0;
|
||||
int beyond_main=-1; // emergency exit, see below
|
||||
for (ULONG frame = 0; ; frame++) {
|
||||
BOOL result = pStackWalk64
|
||||
(
|
||||
#ifdef _M_IX86
|
||||
IMAGE_FILE_MACHINE_I386,
|
||||
#else
|
||||
IMAGE_FILE_MACHINE_AMD64,
|
||||
#endif
|
||||
hProcess,
|
||||
hThread,
|
||||
&stack,
|
||||
&context,
|
||||
nullptr,
|
||||
pSymFunctionTableAccess64,
|
||||
pSymGetModuleBase64,
|
||||
nullptr
|
||||
);
|
||||
if (!result) // official end...
|
||||
break;
|
||||
pSymGetSymFromAddr64(hProcess, (ULONG64)stack.AddrPC.Offset, &displacement, &symbol);
|
||||
TCHAR undname[maxnamelength]= {0};
|
||||
pUnDecorateSymbolName((const TCHAR*)symbol.Name, (PTSTR)undname, (DWORD)getArrayLength(undname), UNDNAME_COMPLETE);
|
||||
if (beyond_main>=0)
|
||||
++beyond_main;
|
||||
if (_tcscmp(undname, _T("main"))==0)
|
||||
beyond_main=0;
|
||||
fprintf(outputFile,
|
||||
"%lu. 0x%08I64X in ",
|
||||
frame, (ULONG64)stack.AddrPC.Offset);
|
||||
fputs((const char *)undname, outputFile);
|
||||
fputc('\n', outputFile);
|
||||
if (0==stack.AddrReturn.Offset || beyond_main>2) // StackWalk64() sometimes doesn't reach any end...
|
||||
break;
|
||||
}
|
||||
|
||||
FreeLibrary(hLibDbgHelp);
|
||||
hLibDbgHelp=nullptr;
|
||||
}
|
||||
|
||||
void writeMemoryErrorDetails(FILE* outputFile, PEXCEPTION_POINTERS ex, const char* description)
|
||||
{
|
||||
fputs(description, outputFile);
|
||||
fprintf(outputFile, " (instruction: 0x%p) ", ex->ExceptionRecord->ExceptionAddress);
|
||||
// Using %p for ULONG_PTR later on, so it must have size identical to size of pointer
|
||||
// This is not the universally portable solution but good enough for Win32/64
|
||||
C_ASSERT(sizeof(void*) == sizeof(ex->ExceptionRecord->ExceptionInformation[1]));
|
||||
switch (ex->ExceptionRecord->ExceptionInformation[0]) {
|
||||
case 0:
|
||||
fprintf(outputFile, "reading from 0x%p",
|
||||
reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
|
||||
break;
|
||||
case 1:
|
||||
fprintf(outputFile, "writing to 0x%p",
|
||||
reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
|
||||
break;
|
||||
case 8:
|
||||
fprintf(outputFile, "data execution prevention at 0x%p",
|
||||
reinterpret_cast<void*>(ex->ExceptionRecord->ExceptionInformation[1]));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Any evaluation of the exception needs to be done here!
|
||||
*/
|
||||
int filterException(int code, PEXCEPTION_POINTERS ex)
|
||||
{
|
||||
FILE *outputFile = stdout;
|
||||
fputs("Internal error: ", outputFile);
|
||||
switch (ex->ExceptionRecord->ExceptionCode) {
|
||||
case EXCEPTION_ACCESS_VIOLATION:
|
||||
writeMemoryErrorDetails(outputFile, ex, "Access violation");
|
||||
break;
|
||||
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
|
||||
fputs("Out of array bounds", outputFile);
|
||||
break;
|
||||
case EXCEPTION_BREAKPOINT:
|
||||
fputs("Breakpoint", outputFile);
|
||||
break;
|
||||
case EXCEPTION_DATATYPE_MISALIGNMENT:
|
||||
fputs("Misaligned data", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_DENORMAL_OPERAND:
|
||||
fputs("Denormalized floating-point value", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
|
||||
fputs("Floating-point divide-by-zero", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_INEXACT_RESULT:
|
||||
fputs("Inexact floating-point value", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_INVALID_OPERATION:
|
||||
fputs("Invalid floating-point operation", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_OVERFLOW:
|
||||
fputs("Floating-point overflow", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_STACK_CHECK:
|
||||
fputs("Floating-point stack overflow", outputFile);
|
||||
break;
|
||||
case EXCEPTION_FLT_UNDERFLOW:
|
||||
fputs("Floating-point underflow", outputFile);
|
||||
break;
|
||||
case EXCEPTION_GUARD_PAGE:
|
||||
fputs("Page-guard access", outputFile);
|
||||
break;
|
||||
case EXCEPTION_ILLEGAL_INSTRUCTION:
|
||||
fputs("Illegal instruction", outputFile);
|
||||
break;
|
||||
case EXCEPTION_IN_PAGE_ERROR:
|
||||
writeMemoryErrorDetails(outputFile, ex, "Invalid page access");
|
||||
break;
|
||||
case EXCEPTION_INT_DIVIDE_BY_ZERO:
|
||||
fputs("Integer divide-by-zero", outputFile);
|
||||
break;
|
||||
case EXCEPTION_INT_OVERFLOW:
|
||||
fputs("Integer overflow", outputFile);
|
||||
break;
|
||||
case EXCEPTION_INVALID_DISPOSITION:
|
||||
fputs("Invalid exception dispatcher", outputFile);
|
||||
break;
|
||||
case EXCEPTION_INVALID_HANDLE:
|
||||
fputs("Invalid handle", outputFile);
|
||||
break;
|
||||
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
|
||||
fputs("Non-continuable exception", outputFile);
|
||||
break;
|
||||
case EXCEPTION_PRIV_INSTRUCTION:
|
||||
fputs("Invalid instruction", outputFile);
|
||||
break;
|
||||
case EXCEPTION_SINGLE_STEP:
|
||||
fputs("Single instruction step", outputFile);
|
||||
break;
|
||||
case EXCEPTION_STACK_OVERFLOW:
|
||||
fputs("Stack overflow", outputFile);
|
||||
break;
|
||||
default:
|
||||
fprintf(outputFile, "Unknown exception (%d)\n",
|
||||
code);
|
||||
break;
|
||||
}
|
||||
fputc('\n', outputFile);
|
||||
printCallstack(outputFile, ex);
|
||||
fflush(outputFile);
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Signal/SEH handling
|
||||
* Has to be clean for using with SEH on windows, i.e. no construction of C++ object instances is allowed!
|
||||
* TODO Check for multi-threading issues!
|
||||
*
|
||||
*/
|
||||
int check_wrapper_seh(CppCheckExecutor& executor, int (CppCheckExecutor::*f)(CppCheck&), CppCheck& cppcheck)
|
||||
{
|
||||
FILE *outputFile = stdout;
|
||||
__try {
|
||||
return (&executor->*f)(cppcheck);
|
||||
} __except (filterException(GetExceptionCode(), GetExceptionInformation())) {
|
||||
// reporting to stdout may not be helpful within a GUI application...
|
||||
fputs("Please report this to the cppcheck developers!\n", outputFile);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Cppcheck - A tool for static C/C++ code analysis
|
||||
* Copyright (C) 2007-2022 Cppcheck team.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CPPCHECKEXECUTORSEH_H
|
||||
#define CPPCHECKEXECUTORSEH_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef USE_WINDOWS_SEH
|
||||
|
||||
class CppCheckExecutor;
|
||||
class CppCheck;
|
||||
|
||||
int check_wrapper_seh(CppCheckExecutor& executor, int (CppCheckExecutor::*f)(CppCheck&), CppCheck& cppcheck);
|
||||
|
||||
#endif
|
||||
|
||||
#endif // CPPCHECKEXECUTORSEH_H
|
|
@ -0,0 +1,326 @@
|
|||
/*
|
||||
* Cppcheck - A tool for static C/C++ code analysis
|
||||
* Copyright (C) 2007-2022 Cppcheck team.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "cppcheckexecutorsig.h"
|
||||
|
||||
#if defined(USE_UNIX_SIGNAL_HANDLING)
|
||||
|
||||
#include "cppcheckexecutor.h"
|
||||
|
||||
#ifdef USE_UNIX_BACKTRACE_SUPPORT
|
||||
#include "stacktrace.h"
|
||||
#endif
|
||||
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
#if defined(__linux__) && defined(REG_ERR)
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
# define _XOPEN_SOURCE // ucontext.h APIs can only be used on Mac OSX >= 10.7 if _XOPEN_SOURCE is defined
|
||||
# include <ucontext.h>
|
||||
|
||||
# undef _XOPEN_SOURCE
|
||||
#elif !defined(__OpenBSD__) && !defined(__HAIKU__)
|
||||
# include <ucontext.h>
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __USE_DYNAMIC_STACK_SIZE
|
||||
static const size_t MYSTACKSIZE = 16*1024+32768; // wild guess about a reasonable buffer
|
||||
#else
|
||||
static const size_t MYSTACKSIZE = 16*1024+SIGSTKSZ; // wild guess about a reasonable buffer
|
||||
#endif
|
||||
static char mytstack[MYSTACKSIZE]= {0}; // alternative stack for signal handler
|
||||
static bool bStackBelowHeap=false; // lame attempt to locate heap vs. stack address space. See CppCheckExecutor::check_wrapper()
|
||||
|
||||
/**
|
||||
* \param[in] ptr address to be examined.
|
||||
* \return true if address is supposed to be on stack (contrary to heap). If ptr is 0 false will be returned.
|
||||
* If unknown better return false.
|
||||
*/
|
||||
static bool IsAddressOnStack(const void* ptr)
|
||||
{
|
||||
if (nullptr==ptr)
|
||||
return false;
|
||||
char a;
|
||||
if (bStackBelowHeap)
|
||||
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)
|
||||
using Signalmap_t = std::map<int, std::string>;
|
||||
static const Signalmap_t listofsignals = {
|
||||
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) no usage currently
|
||||
};
|
||||
#undef DECLARE_SIGNAL
|
||||
/*
|
||||
* Entry pointer for signal handlers
|
||||
* It uses functions which are not safe to be called from a signal handler,
|
||||
* (http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04 has a whitelist)
|
||||
* 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)
|
||||
{
|
||||
int type = -1;
|
||||
pid_t killid;
|
||||
// TODO: separate these two defines
|
||||
#if defined(__linux__) && defined(REG_ERR)
|
||||
const ucontext_t* const uc = reinterpret_cast<const ucontext_t*>(context);
|
||||
killid = (pid_t) syscall(SYS_gettid);
|
||||
if (uc) {
|
||||
type = (int)uc->uc_mcontext.gregs[REG_ERR] & 2;
|
||||
}
|
||||
#else
|
||||
(void)context;
|
||||
killid = getpid();
|
||||
#endif
|
||||
|
||||
const Signalmap_t::const_iterator it=listofsignals.find(signo);
|
||||
const char * const signame = (it==listofsignals.end()) ? "unknown" : it->second.c_str();
|
||||
bool printCallstack=true; // try to print a callstack?
|
||||
bool lowMem=false; // was low-memory condition detected? Be careful then! Avoid allocating much more memory then.
|
||||
bool unexpectedSignal=true; // unexpected indicates program failure
|
||||
bool terminate=true; // exit process/thread
|
||||
const bool isAddressOnStack = IsAddressOnStack(info->si_addr);
|
||||
FILE* output = CppCheckExecutor::getExceptionOutput();
|
||||
switch (signo) {
|
||||
case SIGABRT:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
fputs(
|
||||
#ifdef NDEBUG
|
||||
" - out of memory?\n",
|
||||
#else
|
||||
" - out of memory or assertion?\n",
|
||||
#endif
|
||||
output);
|
||||
lowMem=true; // educated guess
|
||||
break;
|
||||
case SIGBUS:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
switch (info->si_code) {
|
||||
case BUS_ADRALN: // invalid address alignment
|
||||
fputs(" - BUS_ADRALN", output);
|
||||
break;
|
||||
case BUS_ADRERR: // nonexistent physical address
|
||||
fputs(" - BUS_ADRERR", output);
|
||||
break;
|
||||
case BUS_OBJERR: // object-specific hardware error
|
||||
fputs(" - BUS_OBJERR", output);
|
||||
break;
|
||||
#ifdef BUS_MCEERR_AR
|
||||
case BUS_MCEERR_AR: // Hardware memory error consumed on a machine check;
|
||||
fputs(" - BUS_MCEERR_AR", output);
|
||||
break;
|
||||
#endif
|
||||
#ifdef BUS_MCEERR_AO
|
||||
case BUS_MCEERR_AO: // Hardware memory error detected in process but not consumed
|
||||
fputs(" - BUS_MCEERR_AO", output);
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(output, " (at 0x%lx).\n",
|
||||
(unsigned long)info->si_addr);
|
||||
break;
|
||||
case SIGFPE:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
switch (info->si_code) {
|
||||
case FPE_INTDIV: // integer divide by zero
|
||||
fputs(" - FPE_INTDIV", output);
|
||||
break;
|
||||
case FPE_INTOVF: // integer overflow
|
||||
fputs(" - FPE_INTOVF", output);
|
||||
break;
|
||||
case FPE_FLTDIV: // floating-point divide by zero
|
||||
fputs(" - FPE_FLTDIV", output);
|
||||
break;
|
||||
case FPE_FLTOVF: // floating-point overflow
|
||||
fputs(" - FPE_FLTOVF", output);
|
||||
break;
|
||||
case FPE_FLTUND: // floating-point underflow
|
||||
fputs(" - FPE_FLTUND", output);
|
||||
break;
|
||||
case FPE_FLTRES: // floating-point inexact result
|
||||
fputs(" - FPE_FLTRES", output);
|
||||
break;
|
||||
case FPE_FLTINV: // floating-point invalid operation
|
||||
fputs(" - FPE_FLTINV", output);
|
||||
break;
|
||||
case FPE_FLTSUB: // subscript out of range
|
||||
fputs(" - FPE_FLTSUB", output);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(output, " (at 0x%lx).\n",
|
||||
(unsigned long)info->si_addr);
|
||||
break;
|
||||
case SIGILL:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
switch (info->si_code) {
|
||||
case ILL_ILLOPC: // illegal opcode
|
||||
fputs(" - ILL_ILLOPC", output);
|
||||
break;
|
||||
case ILL_ILLOPN: // illegal operand
|
||||
fputs(" - ILL_ILLOPN", output);
|
||||
break;
|
||||
case ILL_ILLADR: // illegal addressing mode
|
||||
fputs(" - ILL_ILLADR", output);
|
||||
break;
|
||||
case ILL_ILLTRP: // illegal trap
|
||||
fputs(" - ILL_ILLTRP", output);
|
||||
break;
|
||||
case ILL_PRVOPC: // privileged opcode
|
||||
fputs(" - ILL_PRVOPC", output);
|
||||
break;
|
||||
case ILL_PRVREG: // privileged register
|
||||
fputs(" - ILL_PRVREG", output);
|
||||
break;
|
||||
case ILL_COPROC: // coprocessor error
|
||||
fputs(" - ILL_COPROC", output);
|
||||
break;
|
||||
case ILL_BADSTK: // internal stack error
|
||||
fputs(" - ILL_BADSTK", output);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(output, " (at 0x%lx).%s\n",
|
||||
(unsigned long)info->si_addr,
|
||||
(isAddressOnStack)?" Stackoverflow?":"");
|
||||
break;
|
||||
case SIGINT:
|
||||
unexpectedSignal=false; // legal usage: interrupt application via CTRL-C
|
||||
fputs("cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
printCallstack=true;
|
||||
fputs(".\n", output);
|
||||
break;
|
||||
case SIGSEGV:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
switch (info->si_code) {
|
||||
case SEGV_MAPERR: // address not mapped to object
|
||||
fputs(" - SEGV_MAPERR", output);
|
||||
break;
|
||||
case SEGV_ACCERR: // invalid permissions for mapped object
|
||||
fputs(" - SEGV_ACCERR", output);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
fprintf(output, " (%sat 0x%lx).%s\n",
|
||||
// cppcheck-suppress knownConditionTrueFalse ; FP
|
||||
(type==-1)? "" :
|
||||
(type==0) ? "reading " : "writing ",
|
||||
(unsigned long)info->si_addr,
|
||||
(isAddressOnStack)?" Stackoverflow?":""
|
||||
);
|
||||
break;
|
||||
case SIGUSR1:
|
||||
fputs("cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
fputs(".\n", output);
|
||||
terminate=false;
|
||||
break;
|
||||
default:
|
||||
fputs("Internal error: cppcheck received signal ", output);
|
||||
fputs(signame, output);
|
||||
fputs(".\n", output);
|
||||
break;
|
||||
}
|
||||
if (printCallstack) {
|
||||
#ifdef USE_UNIX_BACKTRACE_SUPPORT
|
||||
print_stacktrace(output, true, -1, lowMem);
|
||||
#endif
|
||||
}
|
||||
if (unexpectedSignal) {
|
||||
fputs("\nPlease report this to the cppcheck developers!\n", output);
|
||||
}
|
||||
fflush(output);
|
||||
|
||||
if (terminate) {
|
||||
// now let things proceed, shutdown and hopefully dump core for post-mortem analysis
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_handler=SIG_DFL;
|
||||
sigaction(signo, &act, nullptr);
|
||||
kill(killid, signo);
|
||||
}
|
||||
}
|
||||
|
||||
int check_wrapper_sig(CppCheckExecutor& executor, int (CppCheckExecutor::*f)(CppCheck&), CppCheck& cppcheck)
|
||||
{
|
||||
// determine stack vs. heap
|
||||
char stackVariable;
|
||||
char *heapVariable=(char*)malloc(1);
|
||||
bStackBelowHeap = &stackVariable < heapVariable;
|
||||
free(heapVariable);
|
||||
|
||||
// set up alternative stack for signal handler
|
||||
stack_t segv_stack;
|
||||
segv_stack.ss_sp = mytstack;
|
||||
segv_stack.ss_flags = 0;
|
||||
segv_stack.ss_size = MYSTACKSIZE;
|
||||
sigaltstack(&segv_stack, nullptr);
|
||||
|
||||
// install signal handler
|
||||
struct sigaction act;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_flags=SA_SIGINFO|SA_ONSTACK;
|
||||
act.sa_sigaction=CppcheckSignalHandler;
|
||||
for (std::map<int, std::string>::const_iterator sig=listofsignals.begin(); sig!=listofsignals.end(); ++sig) {
|
||||
sigaction(sig->first, &act, nullptr);
|
||||
}
|
||||
return (&executor->*f)(cppcheck);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Cppcheck - A tool for static C/C++ code analysis
|
||||
* Copyright (C) 2007-2022 Cppcheck team.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef CPPCHECKEXECUTORSIG_H
|
||||
#define CPPCHECKEXECUTORSIG_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#if defined(USE_UNIX_SIGNAL_HANDLING)
|
||||
|
||||
class CppCheckExecutor;
|
||||
class CppCheck;
|
||||
|
||||
int check_wrapper_sig(CppCheckExecutor& executor, int (CppCheckExecutor::*f)(CppCheck&), CppCheck& cppcheck);
|
||||
|
||||
#endif // CPPCHECKEXECUTORSIG_H
|
||||
|
||||
#endif // CPPCHECKEXECUTORSIG_H
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Cppcheck - A tool for static C/C++ code analysis
|
||||
* Copyright (C) 2007-2022 Cppcheck team.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "stacktrace.h"
|
||||
|
||||
#ifdef USE_UNIX_BACKTRACE_SUPPORT
|
||||
|
||||
#include "utils.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cxxabi.h>
|
||||
#include <execinfo.h>
|
||||
|
||||
void print_stacktrace(FILE* output, bool demangling, int maxdepth, bool lowMem)
|
||||
{
|
||||
// 32 vs. 64bit
|
||||
#define ADDRESSDISPLAYLENGTH ((sizeof(long)==8)?12:8)
|
||||
const int fd = fileno(output);
|
||||
void *callstackArray[32]= {nullptr}; // the less resources the better...
|
||||
const int currentdepth = backtrace(callstackArray, (int)getArrayLength(callstackArray));
|
||||
const int offset=2; // some entries on top are within our own exception handling code or libc
|
||||
if (maxdepth<0)
|
||||
maxdepth=currentdepth-offset;
|
||||
else
|
||||
maxdepth = std::min(maxdepth, currentdepth);
|
||||
if (lowMem) {
|
||||
fputs("Callstack (symbols only):\n", output);
|
||||
backtrace_symbols_fd(callstackArray+offset, maxdepth, fd);
|
||||
} else {
|
||||
char **symbolStringList = backtrace_symbols(callstackArray, currentdepth);
|
||||
if (symbolStringList) {
|
||||
fputs("Callstack:\n", output);
|
||||
char demangle_buffer[2048]= {0};
|
||||
for (int i = offset; i < maxdepth; ++i) {
|
||||
const char * const symbolString = symbolStringList[i];
|
||||
char * realnameString = nullptr;
|
||||
const char * const firstBracketName = strchr(symbolString, '(');
|
||||
const char * const firstBracketAddress = strchr(symbolString, '[');
|
||||
const char * const secondBracketAddress = strchr(firstBracketAddress, ']');
|
||||
const char * const beginAddress = firstBracketAddress+3;
|
||||
const int addressLen = int(secondBracketAddress-beginAddress);
|
||||
const int padLen = int(ADDRESSDISPLAYLENGTH-addressLen);
|
||||
if (demangling && firstBracketName) {
|
||||
const char * const plus = strchr(firstBracketName, '+');
|
||||
if (plus && (plus>(firstBracketName+1))) {
|
||||
char input_buffer[1024]= {0};
|
||||
strncpy(input_buffer, firstBracketName+1, plus-firstBracketName-1);
|
||||
size_t length = getArrayLength(demangle_buffer);
|
||||
int status=0;
|
||||
// We're violating the specification - passing stack address instead of malloc'ed heap.
|
||||
// Benefit is that no further heap is required, while there is sufficient stack...
|
||||
realnameString = abi::__cxa_demangle(input_buffer, demangle_buffer, &length, &status); // non-NULL on success
|
||||
}
|
||||
}
|
||||
const int ordinal=i-offset;
|
||||
fprintf(output, "#%-2d 0x",
|
||||
ordinal);
|
||||
if (padLen>0)
|
||||
fprintf(output, "%0*d",
|
||||
padLen, 0);
|
||||
if (realnameString) {
|
||||
fprintf(output, "%.*s in %s\n",
|
||||
(int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3,
|
||||
realnameString);
|
||||
} else {
|
||||
fprintf(output, "%.*s in %.*s\n",
|
||||
(int)(secondBracketAddress-firstBracketAddress-3), firstBracketAddress+3,
|
||||
(int)(firstBracketAddress-symbolString), symbolString);
|
||||
}
|
||||
}
|
||||
free(symbolStringList);
|
||||
} else {
|
||||
fputs("Callstack could not be obtained\n", output);
|
||||
}
|
||||
}
|
||||
#undef ADDRESSDISPLAYLENGTH
|
||||
}
|
||||
|
||||
#endif // USE_UNIX_BACKTRACE_SUPPORT
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Cppcheck - A tool for static C/C++ code analysis
|
||||
* Copyright (C) 2007-2022 Cppcheck team.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef STACKTRACE_H
|
||||
#define STACKTRACE_H
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#ifdef USE_UNIX_BACKTRACE_SUPPORT
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
/*
|
||||
* 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!
|
||||
* One reason is named first: it's using functions not whitelisted for usage in a signal handler function.
|
||||
*/
|
||||
void print_stacktrace(FILE* output, bool demangling, int maxdepth, bool lowMem);
|
||||
|
||||
#endif
|
||||
|
||||
#endif // STACKTRACE_H
|
11
lib/config.h
11
lib/config.h
|
@ -131,5 +131,16 @@ static const std::string emptyString;
|
|||
#define SUPPRESS_FLOAT_EQUAL_WARNING(...) __VA_ARGS__
|
||||
#endif
|
||||
|
||||
#if defined(_WIN32) && defined(_MSC_VER)
|
||||
#define USE_WINDOWS_SEH
|
||||
#endif
|
||||
|
||||
#if !defined(NO_UNIX_BACKTRACE_SUPPORT) && defined(__GNUC__) && defined(__GLIBC__) && !defined(__CYGWIN__) && !defined(__MINGW32__) && !defined(__NetBSD__) && !defined(__SVR4) && !defined(__QNX__)
|
||||
#define USE_UNIX_BACKTRACE_SUPPORT
|
||||
#endif
|
||||
|
||||
#if !defined(NO_UNIX_SIGNAL_HANDLING) && defined(__GNUC__) && !defined(__MINGW32__) && !defined(__OS2__)
|
||||
#define USE_UNIX_SIGNAL_HANDLING
|
||||
#endif
|
||||
|
||||
#endif // configH
|
||||
|
|
10
lib/utils.h
10
lib/utils.h
|
@ -156,4 +156,14 @@ CPPCHECKLIB bool matchglobs(const std::vector<std::string> &patterns, const std:
|
|||
|
||||
CPPCHECKLIB void strTolower(std::string& str);
|
||||
|
||||
/**
|
||||
* Simple helper function:
|
||||
* \return size of array
|
||||
* */
|
||||
template<typename T, int size>
|
||||
std::size_t getArrayLength(const T (&)[size])
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="..\cli\cmdlineparser.cpp" />
|
||||
<ClCompile Include="..\cli\cppcheckexecutor.cpp" />
|
||||
<ClCompile Include="..\cli\cppcheckexecutorseh.cpp" />
|
||||
<ClCompile Include="..\cli\filelister.cpp" />
|
||||
<ClCompile Include="..\cli\executor.cpp" />
|
||||
<ClCompile Include="..\cli\processexecutor.cpp" />
|
||||
|
|
|
@ -415,7 +415,7 @@ int main(int argc, char **argv)
|
|||
fout << "cppcheck: $(LIBOBJ) $(CLIOBJ) $(EXTOBJ)\n";
|
||||
fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n";
|
||||
fout << "all:\tcppcheck testrunner\n\n";
|
||||
fout << "testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/filelister.o\n";
|
||||
fout << "testrunner: $(TESTOBJ) $(LIBOBJ) $(EXTOBJ) cli/executor.o cli/processexecutor.o cli/threadexecutor.o cli/cmdlineparser.o cli/cppcheckexecutor.o cli/cppcheckexecutorseh.o cli/cppcheckexecutorsig.o cli/stacktrace.o cli/filelister.o\n";
|
||||
fout << "\t$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $^ $(LIBS) $(LDFLAGS) $(RDYNAMIC)\n\n";
|
||||
fout << "test:\tall\n";
|
||||
fout << "\t./testrunner\n\n";
|
||||
|
|
Loading…
Reference in New Issue