/* Copyright 2013 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the GNU Affero General Public License in all respects * for all of the code used other than as permitted herein. If you modify * file(s) with this exception, you may extend this exception to your * version of the file(s), but you are not obligated to do so. If you do not * wish to do so, delete this exception statement from your version. If you * delete this exception statement from all source files in the program, * then also delete it in the license file. */ #if !defined(_WIN32) #if defined(__sunos__) || !defined(MONGO_HAVE_EXECINFO_BACKTRACE) #include "mongo/platform/backtrace.h" #include #include #include #include #include #include #include "mongo/base/init.h" #include "mongo/base/status.h" using std::string; using std::vector; namespace mongo { namespace pal { namespace { class WalkcontextCallback { public: WalkcontextCallback(uintptr_t* array, int size) : _position(0), _count(size), _addresses(array) {} // This callback function is called from C code, and so must not throw exceptions // static int callbackFunction(uintptr_t address, int signalNumber, WalkcontextCallback* thisContext) { if (thisContext->_position < thisContext->_count) { thisContext->_addresses[thisContext->_position++] = address; return 0; } return 1; } int getCount() const { return static_cast(_position); } private: size_t _position; size_t _count; uintptr_t* _addresses; }; // This function emulates a Solaris function that was added to Solaris 11 at the same time // that the backtrace* functions were added. It is not important to match the function name // or interface for this code, but this is a fine interface for our purposes and following // an existing model seems potentially helpful ... hence the all-lowercase name with no // underscores. The formatting of the output matches what Solaris 11 does; this is similar // to Linux's display, but slightly different. // int addrtosymstr(void* address, char* outputBuffer, int outputBufferSize) { Dl_info_t symbolInfo; if (dladdr(address, &symbolInfo) == 0) { // no info: "[address]" return snprintf(outputBuffer, outputBufferSize, "[0x%p]", address); } if (symbolInfo.dli_sname == NULL) { return snprintf(outputBuffer, // no symbol: "filename'offset [address]" outputBufferSize, "%s'0x%p [0x%p]", symbolInfo.dli_fname, reinterpret_cast(reinterpret_cast(address) - reinterpret_cast(symbolInfo.dli_fbase)), address); } return snprintf(outputBuffer, // symbol: "filename'symbol+offset [address]" outputBufferSize, "%s'%s+0x%p [0x%p]", symbolInfo.dli_fname, symbolInfo.dli_sname, reinterpret_cast(reinterpret_cast(address) - reinterpret_cast(symbolInfo.dli_saddr)), address); } } // namespace typedef int (*WalkcontextCallbackFunc)(uintptr_t address, int signalNumber, void* thisContext); int backtrace_emulation(void** array, int size) { WalkcontextCallback walkcontextCallback(reinterpret_cast(array), size); ucontext_t context; if (getcontext(&context) != 0) { return 0; } int wcReturn = walkcontext( &context, reinterpret_cast(WalkcontextCallback::callbackFunction), static_cast(&walkcontextCallback)); if (wcReturn == 0) { return walkcontextCallback.getCount(); } return 0; } // The API for backtrace_symbols() specifies that the caller must call free() on the char** // returned by this function, and must *not* call free on the individual strings. // In order to support this API, we allocate a single block of memory containing both the // array of pointers to the strings and the strings themselves. Constructing this block // requires two passes: one to collect the strings and calculate the required size of the // combined single memory block; and a second pass to copy the strings into the block and // set their pointers. // The result is that we return a single memory block (allocated with malloc()) which begins // with an array of pointers to strings (of count 'size'). This array is immediately // followed by the strings themselves, each NUL terminated. // char** backtrace_symbols_emulation(void* const* array, int size) { vector stringVector; vector stringLengths; size_t blockSize = size * sizeof(char*); size_t blockPtr = blockSize; const size_t kBufferSize = 8 * 1024; boost::scoped_array stringBuffer(new char[kBufferSize]); for (int i = 0; i < size; ++i) { size_t thisLength = 1 + addrtosymstr(array[i], stringBuffer.get(), kBufferSize); stringVector.push_back(string(stringBuffer.get())); stringLengths.push_back(thisLength); blockSize += thisLength; } char** singleBlock = static_cast(malloc(blockSize)); if (singleBlock == NULL) { return NULL; } for (int i = 0; i < size; ++i) { singleBlock[i] = reinterpret_cast(singleBlock) + blockPtr; strncpy(singleBlock[i], stringVector[i].c_str(), stringLengths[i]); blockPtr += stringLengths[i]; } return singleBlock; } void backtrace_symbols_fd_emulation(void* const* array, int size, int fd) { const int kBufferSize = 4 * 1024; char stringBuffer[kBufferSize]; for (int i = 0; i < size; ++i) { int len = addrtosymstr(array[i], stringBuffer, kBufferSize); if (len > kBufferSize - 1) { len = kBufferSize - 1; } stringBuffer[len] = '\n'; write(fd, stringBuffer, len + 1); } } typedef int (*BacktraceFunc)(void** array, int size); static BacktraceFunc backtrace_switcher = pal::backtrace_emulation; typedef char** (*BacktraceSymbolsFunc)(void* const* array, int size); static BacktraceSymbolsFunc backtrace_symbols_switcher = pal::backtrace_symbols_emulation; typedef void (*BacktraceSymbolsFdFunc)(void* const* array, int size, int fd); static BacktraceSymbolsFdFunc backtrace_symbols_fd_switcher = pal::backtrace_symbols_fd_emulation; int backtrace(void** array, int size) { return backtrace_switcher(array, size); } char** backtrace_symbols(void* const* array, int size) { return backtrace_symbols_switcher(array, size); } void backtrace_symbols_fd(void* const* array, int size, int fd) { backtrace_symbols_fd_switcher(array, size, fd); } } // namespace pal // 'backtrace()', 'backtrace_symbols()' and 'backtrace_symbols_fd()' on Solaris will call // emulation functions if the symbols are not found // MONGO_INITIALIZER_GENERAL(SolarisBacktrace, MONGO_NO_PREREQUISITES, ("default"))(InitializerContext* context) { void* functionAddress = dlsym(RTLD_DEFAULT, "backtrace"); if (functionAddress != NULL) { pal::backtrace_switcher = reinterpret_cast(functionAddress); } functionAddress = dlsym(RTLD_DEFAULT, "backtrace_symbols"); if (functionAddress != NULL) { pal::backtrace_symbols_switcher = reinterpret_cast(functionAddress); } functionAddress = dlsym(RTLD_DEFAULT, "backtrace_symbols_fd"); if (functionAddress != NULL) { pal::backtrace_symbols_fd_switcher = reinterpret_cast(functionAddress); } return Status::OK(); } } // namespace mongo #endif // #if defined(__sunos__) #endif // #if !defined(_WIN32)