diff options
Diffstat (limited to 'cpp/src/IceUtil/Exception.cpp')
-rw-r--r-- | cpp/src/IceUtil/Exception.cpp | 646 |
1 files changed, 388 insertions, 258 deletions
diff --git a/cpp/src/IceUtil/Exception.cpp b/cpp/src/IceUtil/Exception.cpp index 3a57e1ba797..27af47fdba7 100644 --- a/cpp/src/IceUtil/Exception.cpp +++ b/cpp/src/IceUtil/Exception.cpp @@ -28,25 +28,44 @@ #include <iomanip> #include <cstdlib> - -#if defined(__GNUC__) && !defined(__sun) && !defined(__FreeBSD__) && !defined(__MINGW32__) && \ - !defined(ICE_STATIC_LIBS) -# include <execinfo.h> -# include <cxxabi.h> -# define ICE_GCC_STACK_TRACES +#ifdef __GNUC__ +# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__) + +# if defined(__linux) && ((GCC_VERSION >= 5003) || ((GCC_VERSION >= 4008) && (!defined(NDEBUG)))) + // On Linux with GCC, we use libbacktrace: + // - all the time with GCC >= 5.3 + // - only for release builds with GCC >= 4.8 and < 5.3 + // in practice, for GCC 4.8, libbacktrace does not seem to work when the final + // executable is built without debug information + +# include <backtrace.h> +# include <backtrace-supported.h> +# if BACKTRACE_SUPPORTED && BACKTRACE_SUPPORTS_THREADS +# include <algorithm> +# include <cxxabi.h> +# define ICE_LIBBACKTRACE +# endif +# endif + +# if !defined(__sun) && !defined(__FreeBSD__) && !defined(__MINGW32__) && !defined(ICE_STATIC_LIBS) +# include <execinfo.h> +# include <cxxabi.h> +# include <stdint.h> +# define ICE_BACKTRACE +# endif #endif #if defined(_WIN32) && !defined(ICE_OS_WINRT) && !defined(__MINGW32__) -# define ICE_WIN32_STACK_TRACES -# if defined(_MSC_VER) && _MSC_VER >= 1700 -# define DBGHELP_TRANSLATE_TCHAR -# include <IceUtil/StringConverter.h> -# if _MSC_VER >= 1900 -# pragma warning(disable:4091) // VS 2015 RC issues this warning for code in DbgHelp.h -# endif -# endif -# include <DbgHelp.h> -# include <tchar.h> +# define ICE_DBGHELP +# if defined(_MSC_VER) && (_MSC_VER >= 1700) +# define DBGHELP_TRANSLATE_TCHAR +# include <IceUtil/StringConverter.h> +# if _MSC_VER >= 1900 +# pragma warning(disable:4091) // VS 2015 RC issues this warning for code in DbgHelp.h +# endif +# endif +# include <DbgHelp.h> +# include <tchar.h> #endif using namespace std; @@ -57,6 +76,14 @@ namespace IceUtilInternal bool ICE_UTIL_API printStackTraces = false; bool ICE_UTIL_API nullHandleAbort = false; +bool canCaptureStackTrace() +{ +#if defined(ICE_DBGHELP) || defined(ICE_LIBBACKTRACE) || defined(ICE_BACKTRACE) + return true; +#else + return false; +#endif +} } namespace @@ -64,35 +91,190 @@ namespace IceUtil::Mutex* globalMutex = 0; -#ifdef ICE_WIN32_STACK_TRACES +#ifdef ICE_DBGHELP HANDLE process = 0; #endif +#ifdef ICE_LIBBACKTRACE +backtrace_state* bstate = 0; +#endif + class Init { public: Init() { - globalMutex = new IceUtil::Mutex; + globalMutex = new IceUtil::Mutex; +#ifdef ICE_LIBBACKTRACE + // Leaked, as libbacktrace does not provide an API to free + // this state + bstate = backtrace_create_state(0, 1, 0, 0); +#endif } ~Init() { - delete globalMutex; - globalMutex = 0; -#ifdef ICE_WIN32_STACK_TRACES - if(process != 0) - { - SymCleanup(process); - process = 0; - } + delete globalMutex; + globalMutex = 0; +#ifdef ICE_DBGHELP + if(process != 0) + { + SymCleanup(process); + process = 0; + } #endif } }; Init init; +#if defined(ICE_LIBBACKTRACE) || defined (ICE_BACKTRACE) + +struct FrameInfo +{ + explicit FrameInfo(int i) : + index(i), + fallback(0) + { + } + + int index; + const char* fallback; + string output; +}; + +void +decode(const string& line, string& function, string& filename) +{ + string::size_type openParen = line.find_first_of('('); + if(openParen != string::npos) + { + // + // Format: "/opt/Ice/lib/libIceUtil.so.33(_ZN7IceUtil9ExceptionC2EPKci+0x51) [0x73b267]" + // + string::size_type closeParen = line.find_first_of(')', openParen); + if(closeParen != string::npos) + { + string tmp = line.substr(openParen + 1, closeParen - openParen - 1); + string::size_type plus = tmp.find_last_of('+'); + if(plus != string::npos) + { + function = tmp.substr(0 , plus); + filename = line.substr(0, openParen); + } + } + } + else + { + // + // Format: "1 libIce.3.3.1.dylib 0x000933a1 _ZN7IceUtil9ExceptionC2EPKci + 71" + // + string::size_type plus = line.find_last_of('+'); + if(plus != string::npos) + { + string tmp = line.substr(0, plus - 1); + string::size_type space = tmp.find_last_of(" \t"); + if(space != string::npos) + { + tmp = tmp.substr(space + 1, tmp.size() - space); + + string::size_type start = line.find_first_not_of(" \t", 3); + if(start != string::npos) + { + string::size_type finish = line.find_first_of(" \t", start); + if(finish != string::npos) + { + function = tmp; + filename = line.substr(start, finish - start); + } + } + } + } + } +} + +int +printFrame(void* data, uintptr_t pc, const char* filename, int lineno, const char* function) +{ + FrameInfo& frameInfo = *reinterpret_cast<FrameInfo*>(data); + + ostringstream os; + os << setw(3) << frameInfo.index << " "; + + string functionHolder, filenameHolder; + + if(!function && frameInfo.fallback) + { + // Extract function and filename from fallback + decode(frameInfo.fallback, functionHolder, filenameHolder); + if(!functionHolder.empty()) + { + function = functionHolder.c_str(); + } + if(!filename && !filenameHolder.empty()) + { + filename = filenameHolder.c_str(); + } + } + + int ret = 0; + + if(function) + { + char* demangledFunction = abi::__cxa_demangle(function, 0, 0, 0); + if(demangledFunction) + { + os << demangledFunction; + free(demangledFunction); + } + else + { + os << function; + } + + if(filename && lineno > 0) + { + os << " at " << filename << ":" << lineno; + } + else if(filename) + { + os << " in " << filename; + } + } + else if(frameInfo.fallback) + { + // decode was not able to parse this string + os << frameInfo.fallback; + ret = 1; + } + else + { + os << hex << setw(sizeof(uintptr_t) * 2) << setfill('0') << pc; + ret = 2; + } + os << "\n"; + frameInfo.output = os.str(); + return ret; +} +#endif + +#ifdef ICE_LIBBACKTRACE +int +addFrame(void* sf, uintptr_t pc) +{ + if(pc != UINTPTR_MAX) + { + vector<void*>* stackFrames = reinterpret_cast<vector<void*>*>(sf); + stackFrames->push_back(reinterpret_cast<void*>(pc)); + return 0; + } + else + { + return 1; + } +} +#endif vector<void*> getStackFrames() @@ -101,10 +283,10 @@ getStackFrames() if(!IceUtilInternal::printStackTraces) { - return stackFrames; + return stackFrames; } -# if defined(ICE_WIN32_STACK_TRACES) +#if defined(ICE_DBGHELP) stackFrames.resize(61); // @@ -114,14 +296,20 @@ getStackFrames() USHORT frameCount = CaptureStackBackTrace(1, static_cast<DWORD>(stackFrames.size()), &stackFrames.front(), 0); stackFrames.resize(frameCount); +#elif defined(ICE_LIBBACKTRACE) + + backtrace_simple(bstate, 1, addFrame, 0, &stackFrames); + +#elif defined(ICE_BACKTRACE) -# elif defined(ICE_GCC_STACK_TRACES) - stackFrames.resize(100); size_t stackDepth = backtrace(&stackFrames.front(), stackFrames.size()); stackFrames.resize(stackDepth); - -# endif + if(!stackFrames.empty()) + { + stackFrames.erase(stackFrames.begin()); // drop the first frame + } +#endif return stackFrames; } @@ -132,12 +320,12 @@ getStackTrace(const vector<void*>& stackFrames) { if(stackFrames.empty()) { - return ""; + return ""; } string stackTrace; -# if defined(ICE_WIN32_STACK_TRACES) +#if defined(ICE_DBGHELP) // // Note: the Sym functions are not thread-safe @@ -145,68 +333,68 @@ getStackTrace(const vector<void*>& stackFrames) IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(globalMutex); if(process == 0) { - // - // Compute Search path (best effort) - // consists of the current working directory, this DLL (or exe) directory and %_NT_SYMBOL_PATH% - // - basic_string<TCHAR> searchPath; - const TCHAR pathSeparator = _T('\\'); - const TCHAR searchPathSeparator = _T(';'); - - TCHAR cwd[MAX_PATH]; - if(GetCurrentDirectory(MAX_PATH, cwd) != 0) - { - searchPath = cwd; - } - - HMODULE myModule = 0; - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, - "startHook", - &myModule); - // - // If GetModuleHandleEx fails, myModule is NULL, i.e. we'll locate the current exe's directory. - // - - TCHAR myFilename[MAX_PATH]; - DWORD len = GetModuleFileName(myModule, myFilename, MAX_PATH); - if(len != 0 && len < MAX_PATH) - { - assert(myFilename[len] == 0); - - basic_string<TCHAR> myPath = myFilename; - size_t pos = myPath.find_last_of(pathSeparator); - if(pos != basic_string<TCHAR>::npos) - { - myPath = myPath.substr(0, pos); - - if(!searchPath.empty()) - { - searchPath += searchPathSeparator; - } - searchPath += myPath; - } - } - - const DWORD size = 1024; - TCHAR symbolPath[size]; - len = GetEnvironmentVariable(_T("_NT_SYMBOL_PATH"), symbolPath, size); - if(len > 0 && len < size) - { - if(!searchPath.empty()) - { - searchPath += searchPathSeparator; - } - searchPath += symbolPath; - } - - process = GetCurrentProcess(); - - SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS | SYMOPT_EXACT_SYMBOLS | SYMOPT_UNDNAME); - if(SymInitialize(process, searchPath.c_str(), TRUE) == 0) - { - process = 0; - return "No stack trace: SymInitialize failed with " + IceUtilInternal::errorToString(GetLastError()); - } + // + // Compute Search path (best effort) + // consists of the current working directory, this DLL (or exe) directory and %_NT_SYMBOL_PATH% + // + basic_string<TCHAR> searchPath; + const TCHAR pathSeparator = _T('\\'); + const TCHAR searchPathSeparator = _T(';'); + + TCHAR cwd[MAX_PATH]; + if(GetCurrentDirectory(MAX_PATH, cwd) != 0) + { + searchPath = cwd; + } + + HMODULE myModule = 0; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + "startHook", + &myModule); + // + // If GetModuleHandleEx fails, myModule is NULL, i.e. we'll locate the current exe's directory. + // + + TCHAR myFilename[MAX_PATH]; + DWORD len = GetModuleFileName(myModule, myFilename, MAX_PATH); + if(len != 0 && len < MAX_PATH) + { + assert(myFilename[len] == 0); + + basic_string<TCHAR> myPath = myFilename; + size_t pos = myPath.find_last_of(pathSeparator); + if(pos != basic_string<TCHAR>::npos) + { + myPath = myPath.substr(0, pos); + + if(!searchPath.empty()) + { + searchPath += searchPathSeparator; + } + searchPath += myPath; + } + } + + const DWORD size = 1024; + TCHAR symbolPath[size]; + len = GetEnvironmentVariable(_T("_NT_SYMBOL_PATH"), symbolPath, size); + if(len > 0 && len < size) + { + if(!searchPath.empty()) + { + searchPath += searchPathSeparator; + } + searchPath += symbolPath; + } + + process = GetCurrentProcess(); + + SymSetOptions(SYMOPT_LOAD_LINES | SYMOPT_DEFERRED_LOADS | SYMOPT_EXACT_SYMBOLS | SYMOPT_UNDNAME); + if(SymInitialize(process, searchPath.c_str(), TRUE) == 0) + { + process = 0; + return "No stack trace: SymInitialize failed with " + IceUtilInternal::errorToString(GetLastError()); + } } lock.release(); @@ -215,193 +403,135 @@ getStackTrace(const vector<void*>& stackFrames) static_assert(sizeof(TCHAR) == sizeof(wchar_t), "Bad TCHAR - should be wchar_t"); # else static_assert(sizeof(TCHAR) == sizeof(char), "Bad TCHAR - should be char"); -# endif +# endif #endif char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(TCHAR)]; - + SYMBOL_INFO* symbol = reinterpret_cast<SYMBOL_INFO*>(buffer); symbol->SizeOfStruct = sizeof(SYMBOL_INFO); symbol->MaxNameLen = MAX_SYM_NAME; - + IMAGEHLP_LINE64 line = {}; line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); DWORD displacement = 0; - + lock.acquire(); - + // TODO: call SymRefreshModuleList here? (not available on XP) - + #ifdef DBGHELP_TRANSLATE_TCHAR const IceUtil::StringConverterPtr converter = IceUtil::getProcessStringConverter(); #endif for(size_t i = 0; i < stackFrames.size(); i++) { - if(!stackTrace.empty()) - { - stackTrace += "\n"; - } - - stringstream s; - s << setw(3) << i << " "; - - DWORD64 address = reinterpret_cast<DWORD64>(stackFrames[i]); - - BOOL ok = SymFromAddr(process, address, 0, symbol); - if(ok) - { + ostringstream s; + s << setw(3) << i << " "; + + DWORD64 address = reinterpret_cast<DWORD64>(stackFrames[i]); + + BOOL ok = SymFromAddr(process, address, 0, symbol); + if(ok) + { #ifdef DBGHELP_TRANSLATE_TCHAR - s << IceUtil::wstringToString(symbol->Name, converter); + s << IceUtil::wstringToString(symbol->Name, converter); #else - s << symbol->Name; + s << symbol->Name; #endif - ok = SymGetLineFromAddr64(process, address, &displacement, &line); - if(ok) - { - s << " at line " << line.LineNumber << " in " + ok = SymGetLineFromAddr64(process, address, &displacement, &line); + if(ok) + { + s << " at " #ifdef DBGHELP_TRANSLATE_TCHAR - << IceUtil::wstringToString(line.FileName, converter); + << IceUtil::wstringToString(line.FileName, converter) #else - << line.FileName; + << line.FileName #endif - } - } - else - { - s << hex << "0x" << address; - } - stackTrace += s.str(); + << ":" << line.LineNumber; + } + } + else + { + s << hex << setw(sizeof(DWORD64) * 2) << setfill('0') << address; + } + s << "\n"; + stackTrace += s.str(); } lock.release(); -# elif defined(ICE_GCC_STACK_TRACES) +#elif defined(ICE_LIBBACKTRACE) || defined (ICE_BACKTRACE) + + vector<void*>::const_iterator p = stackFrames.begin(); + int frameIndex = 0; + int offset = 0; + char** backtraceStrings = 0; - // With some compilers/toolchains this can fail so we must check that - // stackStrings is not null. - - char** stackStrings = backtrace_symbols(&stackFrames.front(), stackFrames.size()); - if(stackStrings != 0) +# if defined(ICE_LIBBACKTRACE) && defined(ICE_BACKTRACE) + bool backtraceStringsInitialized = false; +# endif +# if !defined(ICE_LIBBACKTRACE) + // Initialize backtraceStrings immediately + if(p != stackFrames.end()) { - // - // Start at 1 to skip the top frame (== call to this function) - // - for(size_t i = 1; i < stackFrames.size(); i++) - { - string line(stackStrings[i]); - - if(i > 1) - { - stackTrace += "\n"; - } - - stringstream s; - s << setw(3) << i - 1 << " "; - - // - // For each line attempt to parse the mangled function name as well - // as the source library/executable. - // - string mangled; - string source; - string::size_type openParen = line.find_first_of('('); - if(openParen != string::npos) - { - // - // Format: "/opt/Ice/lib/libIceUtil.so.33(_ZN7IceUtil9ExceptionC2EPKci+0x51) [0x73b267]" - // - string::size_type closeParen = line.find_first_of(')', openParen); - if(closeParen != string::npos) - { - string tmp = line.substr(openParen + 1, closeParen - openParen - 1); - string::size_type plus = tmp.find_last_of('+'); - if(plus != string::npos) - { - mangled = tmp.substr(0 , plus); - - source = line.substr(0, openParen); - } - } - } - else - { - // - // Format: "1 libIce.3.3.1.dylib 0x000933a1 _ZN7IceUtil9ExceptionC2EPKci + 71" - // - string::size_type plus = line.find_last_of('+'); - if(plus != string::npos) - { - string tmp = line.substr(0, plus - 1); - string::size_type space = tmp.find_last_of(" \t"); - if(space != string::npos) - { - tmp = tmp.substr(space + 1, tmp.size() - space); - - string::size_type start = line.find_first_not_of(" \t", 3); - if(start != string::npos) - { - string::size_type finish = line.find_first_of(" \t", start); - if(finish != string::npos) - { - mangled = tmp; - - source = line.substr(start, finish - start); - } - } - } - } - } - if(mangled.size() != 0) - { - // - // Unmangle the function name - // - int status; - char* unmangled = abi::__cxa_demangle(mangled.c_str(), 0, 0, &status); - if(unmangled) - { - s << unmangled; - free(unmangled); - } - else - { - s << mangled << "()"; - } - - if(!source.empty()) - { - s << " in " << source; - } - } - else - { - s << line; - } - - stackTrace += s.str(); - } - free(stackStrings); + backtraceStrings = backtrace_symbols(&*p, stackFrames.size()); } - else +# endif + + do { - stackTrace = "<stack trace unavailable>"; + FrameInfo frameInfo(frameIndex); + bool retry = false; + + if(backtraceStrings) + { + frameInfo.fallback = backtraceStrings[frameIndex - offset]; + } + +# if defined(ICE_LIBBACKTRACE) + if(backtrace_pcinfo(bstate, reinterpret_cast<uintptr_t>(*p), printFrame, 0, &frameInfo) != 0) + { +# if defined(ICE_BACKTRACE) + if(!backtraceStringsInitialized) + { + offset = frameIndex; + // Initialize backtraceStrings as fallback + backtraceStrings = backtrace_symbols(&*p, stackFrames.size() - offset); + backtraceStringsInitialized = true; + retry = true; + } +# endif + } +# else // not using libbacktrace: + printFrame(&frameInfo, reinterpret_cast<uintptr_t>(*p), 0, 0, 0); +# endif + if(!retry) + { + stackTrace += frameInfo.output; + ++p; + ++frameIndex; + } + } while(p != stackFrames.end()); + + if(backtraceStrings) + { + free(backtraceStrings); } -# endif - +#endif return stackTrace; } } IceUtil::Exception::Exception() : _file(0), - _line(0), + _line(0), _stackFrames(getStackFrames()) { } IceUtil::Exception::Exception(const char* file, int line) : _file(file), - _line(line), + _line(line), _stackFrames(getStackFrames()) { } @@ -415,7 +545,7 @@ IceUtil::Exception::ice_print(ostream& out) const { if(_file && _line > 0) { - out << _file << ':' << _line << ": "; + out << _file << ':' << _line << ": "; } out << ice_id(); } @@ -425,16 +555,16 @@ IceUtil::Exception::what() const ICE_NOEXCEPT { try { - IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(globalMutex); - { - if(_str.empty()) - { - stringstream s; - ice_print(s); - _str = s.str(); // Lazy initialization. - } - } - return _str.c_str(); + IceUtilInternal::MutexPtrLock<IceUtil::Mutex> lock(globalMutex); + { + if(_str.empty()) + { + stringstream s; + ice_print(s); + _str = s.str(); // Lazy initialization. + } + } + return _str.c_str(); } catch(...) { @@ -454,11 +584,11 @@ IceUtil::Exception::ice_clone() const { try { - ice_throw(); + ice_throw(); } catch(...) { - return current_exception(); + return current_exception(); } assert(false); return nullptr; // Make compilers happy @@ -514,7 +644,7 @@ IceUtil::NullHandleException::NullHandleException(const char* file, int line) : { if(IceUtilInternal::nullHandleAbort) { - abort(); + abort(); } } @@ -598,7 +728,7 @@ IceUtil::IllegalConversionException::IllegalConversionException(const char* file {} IceUtil::IllegalConversionException::IllegalConversionException(const char* file, int line, - const string& reason): + const string& reason): Exception(file, line), _reason(reason) {} @@ -654,7 +784,7 @@ IceUtil::SyscallException::ice_print(ostream& os) const Exception::ice_print(os); if(_error != 0) { - os << ":\nsyscall exception: " << IceUtilInternal::errorToString(_error); + os << ":\nsyscall exception: " << IceUtilInternal::errorToString(_error); } } @@ -703,7 +833,7 @@ IceUtil::FileLockException::ice_print(ostream& os) const os << ":\ncould not lock file: `" << _path << "'"; if(_error != 0) { - os << "\nsyscall exception: " << IceUtilInternal::errorToString(_error); + os << "\nsyscall exception: " << IceUtilInternal::errorToString(_error); } } @@ -738,7 +868,7 @@ IceUtil::OptionalNotSetException::OptionalNotSetException(const char* file, int { if(IceUtilInternal::nullHandleAbort) { - abort(); + abort(); } } |