include/sal/log.hxx | 95 ++++++++--- sal/inc/backtraceasstring.hxx | 14 + sal/osl/all/log.cxx | 358 +++++++++++++++++++++++++++++------------- sal/osl/all/logformat.hxx | 29 --- sal/osl/unx/backtraceapi.cxx | 54 +++++- sal/osl/w32/backtrace.cxx | 29 ++- sal/util/sal.map | 5 7 files changed, 399 insertions(+), 185 deletions(-)
New commits: commit 0360f89f686c430de5fc5f1582692e580f0fd0f9 Author: Thorsten Behrens <thorsten.behr...@cib.de> Date: Thu Aug 18 14:07:00 2016 +0300 Backport SAL_LOG handling & logging.ini feature This includes: Add handling of a +TIMESTAMP flag in the SAL_LOG environment variable No need for nested unnamed namespaces Add handling of a +RELATIVETIMER flag in the SAL_LOG environment variable If no WARN in SAL_LOG, use the INFO selection for WARNs, too sal::detail::logFormat is no longer needed add an option to pipe log output to file tdf#91872: Make SAL_INFO and friends more efficient Clean up C-style interface nonsense Fold sal_detail_log_backtrace into sal_detail_log Some clean up WNT: allow to set log level/path from file sal: use snprintf for sal log coverity#1427647 acknowledge Resource leak is deliberate loplugin:nullptr (clang-cl) Make SAL_LOG_FILE work on Windows also without logging.ini sal: log windows trace output to debugger console Change-Id: I47dd00144273306ec1acaa9c88a1bf4a89ef8079 sal: log windows traces to debugger console, take two diff --git a/include/sal/log.hxx b/include/sal/log.hxx index e01d70870042..c3f5fb05a071 100644 --- a/include/sal/log.hxx +++ b/include/sal/log.hxx @@ -27,14 +27,17 @@ /// @cond INTERNAL extern "C" SAL_DLLPUBLIC void SAL_CALL sal_detail_log( - enum sal_detail_LogLevel level, char const * area, char const * where, - char const * message); + sal_detail_LogLevel level, char const * area, char const * where, + char const * message, sal_uInt32 backtraceDepth); + +extern "C" SAL_DLLPUBLIC sal_Bool SAL_CALL sal_detail_log_report( + sal_detail_LogLevel level, char const * area); namespace sal { namespace detail { -inline void SAL_CALL log( +inline void log( sal_detail_LogLevel level, char const * area, char const * where, - std::ostringstream const & stream) + std::ostringstream const & stream, sal_uInt32 backtraceDepth) { // An alternative would be to have sal_detail_log take a std::ostringstream // pointer (via a C void pointer); the advantage would be smaller client @@ -44,7 +47,7 @@ inline void SAL_CALL log( // on the C++ ABI; as a compromise, the ".str().c_str()" part has been moved // to this inline function so that it is potentially only emitted once per // dynamic library: - sal_detail_log(level, area, where, stream.str().c_str()); + sal_detail_log(level, area, where, stream.str().c_str(), backtraceDepth); } // Special handling of the common case where the message consists of just a @@ -112,19 +115,20 @@ inline char const * unwrapStream(SAL_UNUSED_PARAMETER StreamIgnore const &) { #define SAL_DETAIL_LOG_STREAM(condition, level, area, where, stream) \ do { \ - if (condition) { \ + if ((condition) && sal_detail_log_report(level, area)) { \ if (sizeof ::sal::detail::getResult( \ ::sal::detail::StreamStart() << stream) == 1) \ { \ ::sal_detail_log( \ (level), (area), (where), \ ::sal::detail::unwrapStream( \ - ::sal::detail::StreamStart() << stream)); \ + ::sal::detail::StreamStart() << stream), \ + 0); \ } else { \ ::std::ostringstream sal_detail_stream; \ sal_detail_stream << stream; \ ::sal::detail::log( \ - (level), (area), (where), sal_detail_stream); \ + (level), (area), (where), sal_detail_stream, 0); \ } \ } \ } while (false) @@ -228,15 +232,31 @@ inline char const * unwrapStream(SAL_UNUSED_PARAMETER StreamIgnore const &) { with @verbatim - <switch> ::= <sense><level>("."<area>)? + <switch> ::= <sense><item> <sense> ::= "+"|"-" + <item> ::= <flag>|<level>("."<area>)? + <flag> ::= "TIMESTAMP"|"RELATIVETIMER" <level> ::= "INFO"|"WARN" @endverbatim - If the environment variable is unset, "+WARN" is used instead (which results - in all warnings being output but no infos). If the given value does not - match the regular expression, "+INFO+WARN" is used instead (which in turn - results in everything being output). + If the environment variable is unset, the setting "+WARN" is + assumed instead (which results in all warnings being output but no + infos). If the given value does not match the regular expression, + "+INFO+WARN" is used instead (which in turn results in everything + being output). + + The "+TIMESTAMP" flag causes each output line (as selected by the level + switch(es)) to be prefixed by a timestamp like 2016-08-18:14:04:43. + + The "+RELATIVETIMER" flag causes each output line (as selected by + the level switch(es)) to be prefixed by a relative timestamp in + seconds since the first output line like 1.312. + + If both +TIMESTAMP and +RELATIVETIMER are specified, they are + output in that order. + + Specifying a flag with a negative sense has no effect. Specifying + the same flag multiple times has no extra effect. A given macro call's level (INFO or WARN) and area is matched against the given switches as follows: Only those switches for which the level matches @@ -246,17 +266,20 @@ inline char const * unwrapStream(SAL_UNUSED_PARAMETER StreamIgnore const &) { that has a sense of "+". (That is, if both +INFO.foo and -INFO.foo are present, +INFO.foo wins.) + If no WARN selection is specified, but an INFO selection is, the + INFO selection is used for WARN messages, too. + For example, if SAL_LOG is "+INFO-INFO.foo+INFO.foo.bar", then calls like SAL_INFO("foo.bar", ...), SAL_INFO("foo.bar.baz", ...), or SAL_INFO("other", ...) generate output, while calls like SAL_INFO("foo", ...) or SAL_INFO("foo.barzzz", ...) do not. - The generated log output consists of the given level ("info" or "warn"), the - given area, the process ID, the thread ID, the source file, and the source - line number, each followed by a colon, followed by a space, the given - message, and a newline. The precise format of the log output is subject to - change. The log output is printed to stderr without further text encoding - conversion. + The generated log output consists of the optinal timestamp, the given level + ("info" or "warn"), the given area, the process ID, the thread ID, the + source file, and the source line number, each followed by a colon, followed + by a space, the given message, and a newline. The precise format of the log + output is subject to change. The log output is printed to stderr without + further text encoding conversion. @see @ref sal_log_areas @@ -317,16 +340,36 @@ inline char const * unwrapStream(SAL_UNUSED_PARAMETER StreamIgnore const &) { SAL_LOG_TRUE, ::SAL_DETAIL_LOG_LEVEL_DEBUG, NULL, NULL, stream) /** - Produce temporary debugging output from stream along with a - stack trace of the calling location. This macro is meant to - be used only while working on code and should never exist - in production code. + Produce temporary debugging output from stream along with a backtrace of the + calling location. + + This macro is meant to be used only while working on code and should never + exist in production code. + + @param backtraceDepth a sal_uInt32 value indicating the maximum backtrace + depth; zero means no backtrace See @ref sal_log "basic logging functionality" for details. */ -#define SAL_DEBUG_TRACE(stream) \ - SAL_DETAIL_LOG_STREAM( \ - SAL_LOG_TRUE, ::SAL_DETAIL_LOG_LEVEL_DEBUG_TRACE, NULL, NULL, stream) +#define SAL_DEBUG_BACKTRACE(stream, backtraceDepth) \ + do { \ + if (sizeof ::sal::detail::getResult( \ + ::sal::detail::StreamStart() << stream) == 1) \ + { \ + ::sal_detail_log( \ + ::SAL_DETAIL_LOG_LEVEL_DEBUG, NULL, NULL, \ + ::sal::detail::unwrapStream( \ + ::sal::detail::StreamStart() << stream), \ + backtraceDepth); \ + } else { \ + ::std::ostringstream sal_detail_stream; \ + sal_detail_stream << stream; \ + ::sal::detail::log( \ + ::SAL_DETAIL_LOG_LEVEL_DEBUG, NULL, NULL, sal_detail_stream, \ + backtraceDepth); \ + } \ + } while (false) + #endif diff --git a/sal/inc/misc.hxx b/sal/inc/backtraceasstring.hxx similarity index 66% rename from sal/inc/misc.hxx rename to sal/inc/backtraceasstring.hxx index b93868e1baab..cd9ce494f789 100644 --- a/sal/inc/misc.hxx +++ b/sal/inc/backtraceasstring.hxx @@ -6,13 +6,21 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef INCLUDED_SAL_INC_INTERNAL_MISC_H -#define INCLUDED_SAL_INC_INTERNAL_MISC_H + +#ifndef INCLUDED_SAL_INC_BACKTRACEASSTRING_HXX +#define INCLUDED_SAL_INC_BACKTRACEASSTRING_HXX + +#include <sal/config.h> #include <rtl/ustring.hxx> +#include <sal/types.h> + +namespace osl { namespace detail { /// Build a debugging backtrace from current PC location. -rtl_uString *osl_backtraceAsString(void); +OUString backtraceAsString(sal_uInt32 maxDepth); + +} } #endif // INCLUDED_SAL_INC_INTERNAL_MISC_H diff --git a/sal/osl/all/log.cxx b/sal/osl/all/log.cxx index 9c75393bd327..4d60536f71dd 100644 --- a/sal/osl/all/log.cxx +++ b/sal/osl/all/log.cxx @@ -19,20 +19,21 @@ #include <stdio.h> #include <string.h> +#include <fstream> +#include <config_global.h> #include "osl/thread.hxx" #include "rtl/string.h" #include "sal/detail/log.h" #include "sal/log.hxx" #include "sal/types.h" -#include "misc.hxx" - -#include "logformat.hxx" +#include "backtraceasstring.hxx" #if defined ANDROID #include <android/log.h> #elif defined WNT #include <process.h> +#include <windows.h> #define OSL_DETAIL_GETPID _getpid() #else #include <unistd.h> @@ -82,14 +83,14 @@ char const * toString(sal_detail_LogLevel level) { // the process is running": #if defined ANDROID -char const * getEnvironmentVariable() { +char const * getLogLevel() { return std::getenv("SAL_LOG"); } #else -char const * getEnvironmentVariable_() { - char const * p1 = std::getenv("SAL_LOG"); +char const * getEnvironmentVariable(const char* env) { + char const * p1 = std::getenv(env); if (p1 == nullptr) { return nullptr; } @@ -100,107 +101,174 @@ char const * getEnvironmentVariable_() { return p2; } -char const * getEnvironmentVariable() { - static char const * env = getEnvironmentVariable_(); - return env; +#ifdef WNT +# define INI_STRINGBUF_SIZE 1024 + +bool getValueFromLoggingIniFile(const char* key, char* value) { + char buffer[MAX_PATH]; + GetModuleFileName(nullptr, buffer, MAX_PATH); + std::string sProgramDirectory = std::string(buffer); + std::string::size_type pos = sProgramDirectory.find_last_of( "\\/" ); + sProgramDirectory = sProgramDirectory.substr(0, pos+1); + sProgramDirectory += "logging.ini"; + + std::ifstream logFileStream(sProgramDirectory); + if (!logFileStream.good()) + return false; + + std::size_t n; + std::string aKey; + std::string aValue; + std::string sWantedKey(key); + std::string sLine; + while (std::getline(logFileStream, sLine)) { + if (sLine.find('#') == 0) + continue; + if ( ( n = sLine.find('=') ) != std::string::npos) { + aKey = sLine.substr(0, n); + if (aKey != sWantedKey) + continue; + aValue = sLine.substr(n+1, sLine.length()); + snprintf(value, INI_STRINGBUF_SIZE, "%s", aValue.c_str()); + return true; + } + } + return false; } +#endif + +char const * getLogLevel() { + // First check the environment variable, then the setting in logging.ini + static char const * env = getEnvironmentVariable("SAL_LOG"); + if (env != nullptr) + return env; + +#ifdef WNT + static char logLevel[INI_STRINGBUF_SIZE]; + if (getValueFromLoggingIniFile("LogLevel", logLevel)) + return logLevel; #endif -namespace { - inline bool isDebug(sal_detail_LogLevel level) { - return level == SAL_DETAIL_LOG_LEVEL_DEBUG || - level == SAL_DETAIL_LOG_LEVEL_DEBUG_TRACE; - } + return nullptr; } -bool report(sal_detail_LogLevel level, char const * area) { - if (isDebug(level)) - return true; - assert(area != nullptr); - char const * env = getEnvironmentVariable(); - if (env == nullptr) { - env = "+WARN"; - } - std::size_t areaLen = std::strlen(area); - enum Sense { POSITIVE = 0, NEGATIVE = 1 }; - std::size_t senseLen[2] = { 0, 1 }; - // initial senseLen[POSITIVE] < senseLen[NEGATIVE], so that if there are - // no matching switches at all, the result will be negative (and - // initializing with 1 is safe as the length of a valid switch, even - // without the "+"/"-" prefix, will always be > 1) +std::ofstream * getLogFile() { + // First check the environment variable, then the setting in logging.ini + static char const * logFile = getEnvironmentVariable("SAL_LOG_FILE"); + if (!logFile) + return nullptr; + +#ifdef WNT + static char logFilePath[INI_STRINGBUF_SIZE]; + if (getValueFromLoggingIniFile("LogFilePath", logFilePath)) + logFile = logFilePath; +#endif + + // stays until process exits + static std::ofstream file(logFile, std::ios::app | std::ios::out); + + return &file; +} + +void maybeOutputTimestamp(std::ostringstream &s) { + static char const * env = getLogLevel(); + if (env == nullptr) + return; + bool outputTimestamp = false; + bool outputRelativeTimer = false; for (char const * p = env;;) { - Sense sense; switch (*p++) { case '\0': - return senseLen[POSITIVE] >= senseLen[NEGATIVE]; - // if a specific item is both positive and negative - // (senseLen[POSITIVE] == senseLen[NEGATIVE]), default to - // positive + if (outputTimestamp) { + char ts[100]; + TimeValue systemTime; + osl_getSystemTime(&systemTime); + TimeValue localTime; + osl_getLocalTimeFromSystemTime(&systemTime, &localTime); + oslDateTime dateTime; + osl_getDateTimeFromTimeValue(&localTime, &dateTime); + struct tm tm; + tm.tm_sec = dateTime.Seconds; + tm.tm_min = dateTime.Minutes; + tm.tm_hour = dateTime.Hours; + tm.tm_mday = dateTime.Day; + tm.tm_mon = dateTime.Month - 1; + tm.tm_year = dateTime.Year - 1900; + strftime(ts, sizeof(ts), "%Y-%m-%d:%H:%M:%S", &tm); + char milliSecs[11]; + snprintf(milliSecs, sizeof(milliSecs), "%03u", static_cast<unsigned>(dateTime.NanoSeconds/1000000)); + s << ts << '.' << milliSecs << ':'; + } + if (outputRelativeTimer) { + static bool beenHere = false; + static TimeValue first; + if (!beenHere) { + osl_getSystemTime(&first); + beenHere = true; + } + TimeValue now; + osl_getSystemTime(&now); + int seconds = now.Seconds - first.Seconds; + int milliSeconds; + if (now.Nanosec < first.Nanosec) { + seconds--; + milliSeconds = 1000-(first.Nanosec-now.Nanosec)/1000000; + } + else + milliSeconds = (now.Nanosec-first.Nanosec)/1000000; + char relativeTimestamp[100]; + snprintf(relativeTimestamp, sizeof(relativeTimestamp), "%d.%03d", seconds, milliSeconds); + s << relativeTimestamp << ':'; + } + return; case '+': - sense = POSITIVE; - break; - case '-': - sense = NEGATIVE; - break; - default: - return true; // upon an illegal SAL_LOG value, enable everything - } - char const * p1 = p; - while (*p1 != '.' && *p1 != '+' && *p1 != '-' && *p1 != '\0') { - ++p1; - } - bool match; - if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("INFO"))) { - match = level == SAL_DETAIL_LOG_LEVEL_INFO; - } else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("WARN"))) - { - match = level == SAL_DETAIL_LOG_LEVEL_WARN; - } else { - return true; - // upon an illegal SAL_LOG value, everything is considered - // positive - } - char const * p2 = p1; - while (*p2 != '+' && *p2 != '-' && *p2 != '\0') { - ++p2; - } - if (match) { - if (*p1 == '.') { - ++p1; - std::size_t n = p2 - p1; - if ((n == areaLen && equalStrings(p1, n, area, areaLen)) - || (n < areaLen && area[n] == '.' - && equalStrings(p1, n, area, n))) - { - senseLen[sense] = p2 - p; + { + char const * p1 = p; + while (*p1 != '.' && *p1 != '+' && *p1 != '-' && *p1 != '\0') { + ++p1; } - } else { - senseLen[sense] = p1 - p; + if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("TIMESTAMP"))) + outputTimestamp = true; + else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("RELATIVETIMER"))) + outputRelativeTimer = true; + char const * p2 = p1; + while (*p2 != '+' && *p2 != '-' && *p2 != '\0') { + ++p2; + } + p = p2; } + break; + default: + ; // nothing } - p = p2; } + return; } -void log( +#endif + +} + +void sal_detail_log( sal_detail_LogLevel level, char const * area, char const * where, - char const * message) + char const * message, sal_uInt32 backtraceDepth) { std::ostringstream s; #if !defined ANDROID // On Android, the area will be used as the "tag," and log info already - // contains the PID + // contains timestamp and PID. if (!sal_use_syslog) { + maybeOutputTimestamp(s); s << toString(level) << ':'; } - if (!isDebug(level)) { + if (level != SAL_DETAIL_LOG_LEVEL_DEBUG) { s << area << ':'; } s << OSL_DETAIL_GETPID << ':'; #endif s << osl::Thread::getCurrentIdentifier() << ':'; - if (isDebug(level)) { + if (level == SAL_DETAIL_LOG_LEVEL_DEBUG) { s << ' '; } else { const size_t nStrLen(std::strlen(SRCDIR "/")); @@ -208,13 +276,10 @@ void log( + (std::strncmp(where, SRCDIR "/", nStrLen) == 0 ? nStrLen : 0)); } - s << message; - if (level == SAL_DETAIL_LOG_LEVEL_DEBUG_TRACE) { - s << " at:\n"; - s << OUString(osl_backtraceAsString(), SAL_NO_ACQUIRE); + if (backtraceDepth != 0) { + s << " at:\n" << osl::detail::backtraceAsString(backtraceDepth); } - s << '\n'; #if defined ANDROID int android_log_level; @@ -258,48 +323,121 @@ void log( syslog(prio, "%s", s.str().c_str()); #endif } else { - std::fputs(s.str().c_str(), stderr); - std::fflush(stderr); - } + // avoid calling getLogFile() more than once + static std::ofstream * logFile = getLogFile(); + if (logFile) { + *logFile << s.str() << std::endl; + } + else { +#ifdef WNT + // write to Windows debugger console, too + OutputDebugStringA(s.str().c_str()); #endif -} - -} - -void sal_detail_log( - sal_detail_LogLevel level, char const * area, char const * where, - char const * message) -{ - if (report(level, area)) { - log(level, area, where, message); + s << '\n'; + std::fputs(s.str().c_str(), stderr); + std::fflush(stderr); + } } +#endif } void sal_detail_logFormat( sal_detail_LogLevel level, char const * area, char const * where, char const * format, ...) { - if (report(level, area)) { + if (sal_detail_log_report(level, area)) { std::va_list args; va_start(args, format); - osl::detail::logFormat(level, area, where, format, args); + char buf[1024]; + int const len = sizeof buf - RTL_CONSTASCII_LENGTH("..."); + int n = vsnprintf(buf, len, format, args); + if (n < 0) { + std::strcpy(buf, "???"); + } else if (n >= len) { + std::strcpy(buf + len - 1, "..."); + } + sal_detail_log(level, area, where, buf, 0); va_end(args); } } -void osl::detail::logFormat( - sal_detail_LogLevel level, char const * area, char const * where, - char const * format, std::va_list arguments) -{ - char buf[1024]; - int const len = sizeof buf - RTL_CONSTASCII_LENGTH("..."); - int n = vsnprintf(buf, len, format, arguments); - if (n < 0) { - std::strcpy(buf, "???"); - } else if (n >= len) { - std::strcpy(buf + len - 1, "..."); +sal_Bool sal_detail_log_report(sal_detail_LogLevel level, char const * area) { + if (level == SAL_DETAIL_LOG_LEVEL_DEBUG) { + return true; + } + assert(area != nullptr); + static char const * env = getLogLevel(); + if (env == nullptr) { + env = "+WARN"; + } + std::size_t areaLen = std::strlen(area); + enum Sense { POSITIVE = 0, NEGATIVE = 1 }; + std::size_t senseLen[2] = { 0, 1 }; + // initial senseLen[POSITIVE] < senseLen[NEGATIVE], so that if there are + // no matching switches at all, the result will be negative (and + // initializing with 1 is safe as the length of a valid switch, even + // without the "+"/"-" prefix, will always be > 1) + bool seenWarn = false; + for (char const * p = env;;) { + Sense sense; + switch (*p++) { + case '\0': + if (level == SAL_DETAIL_LOG_LEVEL_WARN && !seenWarn) + return sal_detail_log_report(SAL_DETAIL_LOG_LEVEL_INFO, area); + return senseLen[POSITIVE] >= senseLen[NEGATIVE]; + // if a specific item is both positive and negative + // (senseLen[POSITIVE] == senseLen[NEGATIVE]), default to + // positive + case '+': + sense = POSITIVE; + break; + case '-': + sense = NEGATIVE; + break; + default: + return true; // upon an illegal SAL_LOG value, enable everything + } + char const * p1 = p; + while (*p1 != '.' && *p1 != '+' && *p1 != '-' && *p1 != '\0') { + ++p1; + } + bool match; + if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("INFO"))) { + match = level == SAL_DETAIL_LOG_LEVEL_INFO; + } else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("WARN"))) + { + match = level == SAL_DETAIL_LOG_LEVEL_WARN; + seenWarn = true; + } else if (equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("TIMESTAMP")) || + equalStrings(p, p1 - p, RTL_CONSTASCII_STRINGPARAM("RELATIVETIMER"))) + { + // handled later + match = false; + } else { + return true; + // upon an illegal SAL_LOG value, everything is considered + // positive + } + char const * p2 = p1; + while (*p2 != '+' && *p2 != '-' && *p2 != '\0') { + ++p2; + } + if (match) { + if (*p1 == '.') { + ++p1; + std::size_t n = p2 - p1; + if ((n == areaLen && equalStrings(p1, n, area, areaLen)) + || (n < areaLen && area[n] == '.' + && equalStrings(p1, n, area, n))) + { + senseLen[sense] = p2 - p; + } + } else { + senseLen[sense] = p1 - p; + } + } + p = p2; } - log(level, area, where, buf); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/all/logformat.hxx b/sal/osl/all/logformat.hxx deleted file mode 100644 index 0e579a9bfc54..000000000000 --- a/sal/osl/all/logformat.hxx +++ /dev/null @@ -1,29 +0,0 @@ -/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* - * This file is part of the LibreOffice project. - * - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -#ifndef INCLUDED_SAL_OSL_ALL_LOGFORMAT_HXX -#define INCLUDED_SAL_OSL_ALL_LOGFORMAT_HXX - -#include "sal/config.h" - -#include <cstdarg> - -#include "sal/detail/log.h" - -namespace osl { namespace detail { - -void logFormat( - sal_detail_LogLevel level, char const * area, char const * where, - char const * format, std::va_list arguments); - -} } - -#endif - -/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/unx/backtraceapi.cxx b/sal/osl/unx/backtraceapi.cxx index 1895c4360837..a838471f1116 100644 --- a/sal/osl/unx/backtraceapi.cxx +++ b/sal/osl/unx/backtraceapi.cxx @@ -9,16 +9,52 @@ #include <sal/config.h> +#include <cassert> +#include <cstdlib> +#include <limits> +#include <memory> + #include <rtl/ustrbuf.hxx> -#include "misc.hxx" - -// FIXME: no-op for now; it needs implementing, cf. above. -rtl_uString *osl_backtraceAsString() -{ - OUStringBuffer aBuf; - OUString aStr = aBuf.makeStringAndClear(); - rtl_uString_acquire( aStr.pData ); - return aStr.pData; +#include <rtl/ustring.hxx> +#include <sal/types.h> + +#include "backtrace.h" +#include "backtraceasstring.hxx" + +namespace { + +struct FreeGuard { + FreeGuard(char ** theBuffer): buffer(theBuffer) {} + + ~FreeGuard() { std::free(buffer); } + + char ** buffer; +}; + +} + +OUString osl::detail::backtraceAsString(sal_uInt32 maxDepth) { + assert(maxDepth != 0); + auto const maxInt = static_cast<unsigned int>( + std::numeric_limits<int>::max()); + if (maxDepth > maxInt) { + maxDepth = static_cast<sal_uInt32>(maxInt); + } + auto b1 = std::unique_ptr<void *[]>(new void *[maxDepth]); + int n = backtrace(b1.get(), static_cast<int>(maxDepth)); + FreeGuard b2(backtrace_symbols(b1.get(), n)); + b1.reset(); + if (b2.buffer == nullptr) { + return OUString(); + } + OUStringBuffer b3; + for (int i = 0; i != n; ++i) { + if (i != 0) { + b3.append("\n"); + } + // TODO b3.append(o3tl::runtimeToOUString(b2.buffer[i])); + } + return b3.makeStringAndClear(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/osl/w32/backtrace.cxx b/sal/osl/w32/backtrace.cxx index 5095d2053051..5bcf230da8d4 100644 --- a/sal/osl/w32/backtrace.cxx +++ b/sal/osl/w32/backtrace.cxx @@ -7,7 +7,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "misc.hxx" +#include <sal/config.h> + +#include <limits> +#include <memory> #include <windows.h> #include <process.h> @@ -17,16 +20,28 @@ #include <rtl/ustrbuf.hxx> -// No-op for now; it needs implementing. -rtl_uString *osl_backtraceAsString() +#include "backtraceasstring.hxx" + +OUString osl::detail::backtraceAsString(sal_uInt32 maxDepth) { + assert(maxDepth != 0); + auto const maxUlong = std::numeric_limits<ULONG>::max(); + if (maxDepth > maxUlong) { + maxDepth = static_cast<sal_uInt32>(maxUlong); + } + OUStringBuffer aBuf; HANDLE hProcess = GetCurrentProcess(); SymInitialize( hProcess, NULL, true ); - void * aStack[ 512 ]; - sal_uInt32 nFrames = CaptureStackBackTrace( 0, 512, aStack, NULL ); + std::unique_ptr<void*[]> aStack(new void*[ maxDepth ]); + // <https://msdn.microsoft.com/en-us/library/windows/desktop/ + // bb204633(v=vs.85).aspx> "CaptureStackBackTrace function" claims that you + // "can capture up to MAXUSHORT frames", and on Windows Server 2003 and + // Windows XP it even "must be less than 63", but assume that a too large + // input value is clamped internally, instead of resulting in an error: + sal_uInt32 nFrames = CaptureStackBackTrace( 0, static_cast<ULONG>(maxDepth), aStack.get(), nullptr ); SYMBOL_INFO * pSymbol; pSymbol = ( SYMBOL_INFO * )calloc( sizeof( SYMBOL_INFO ) + 1024 * sizeof( char ), 1 ); @@ -46,9 +61,7 @@ rtl_uString *osl_backtraceAsString() free( pSymbol ); - OUString aStr = aBuf.makeStringAndClear(); - rtl_uString_acquire( aStr.pData ); - return aStr.pData; + return aBuf.makeStringAndClear(); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/sal/util/sal.map b/sal/util/sal.map index 1ccac615b67f..249473bf62e8 100644 --- a/sal/util/sal.map +++ b/sal/util/sal.map @@ -716,6 +716,11 @@ PRIVATE_1.2 { # LibreOffice 3.5 sal_detail_logFormat; } PRIVATE_1.1; +PRIVATE_1.3 { # LibreOffice 5.4 + global: + sal_detail_log_report; +} PRIVATE_1.2; + PRIVATE_textenc.1 { # LibreOffice 3.6 global: _ZN3sal6detail7textenc20convertCharToUnicode*; _______________________________________________ Libreoffice-commits mailing list libreoffice-comm...@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/libreoffice-commits