summaryrefslogtreecommitdiff
path: root/sal
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2021-09-02 23:18:54 +0200
committerLuboš Luňák <l.lunak@collabora.com>2021-09-06 14:40:21 +0200
commit665bd649088e6dccf4d53251e10b182950259b46 (patch)
treee202b9749788aeee73b8f54f0119fec96ea22a88 /sal
parent328975ad14a8dbf5a86aacd0b0552b49b77746f4 (diff)
improve sal::backtrace_get() by using addr2line in debug builds
The backtrace_symbols() function provides backtraces that often miss many function names, try harder to resolve them, using addr2line is the best (only?) working solution I've found. Change-Id: Ieda06fc52735596e499fb7f9443cae13d134da5c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/121539 Tested-by: Jenkins Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
Diffstat (limited to 'sal')
-rw-r--r--sal/osl/unx/backtraceapi.cxx145
1 files changed, 130 insertions, 15 deletions
diff --git a/sal/osl/unx/backtraceapi.cxx b/sal/osl/unx/backtraceapi.cxx
index d11128353d47..aee29991b632 100644
--- a/sal/osl/unx/backtraceapi.cxx
+++ b/sal/osl/unx/backtraceapi.cxx
@@ -23,18 +23,6 @@
#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) {
std::unique_ptr<sal::BacktraceState> backtrace = sal::backtrace_get( maxDepth );
return sal::backtrace_to_string( backtrace.get());
@@ -53,10 +41,135 @@ std::unique_ptr<sal::BacktraceState> sal::backtrace_get(sal_uInt32 maxDepth)
return std::unique_ptr<BacktraceState>(new BacktraceState{ b1, n });
}
+#if OSL_DEBUG_LEVEL > 0 && (defined LINUX || defined MACOSX || defined FREEBSD || defined NETBSD || defined OPENBSD || defined(DRAGONFLY))
+// The backtrace_symbols() function is unreliable, it requires -rdynamic and even then it cannot resolve names
+// of many functions, such as those with hidden ELF visibility. Libunwind doesn't resolve names for me either,
+// boost::stacktrace doesn't work properly, the best result I've found is addr2line. Using addr2line is relatively
+// slow, but I don't find that to be a big problem for printing of backtraces. Feel free to improve if needed
+// (e.g. the calls could be grouped by the binary).
+#include <dlfcn.h>
+#include <unistd.h>
+#include <osl/process.h>
+#include <rtl/strbuf.hxx>
+#include "file_url.hxx"
+
+static OString symbol_addr2line_info(const char* file, ptrdiff_t offset)
+{
+ OUString binary("addr2line");
+ OUString dummy;
+ if(!osl::detail::find_in_PATH(binary, dummy) || access( file, R_OK ) != 0)
+ return OString(); // Will not work, avoid warnings from osl process code.
+ OUString arg1("-Cfe");
+ OUString arg2 = OUString::fromUtf8(file);
+ OUString arg3 = "0x" + OUString::number(offset, 16);
+ rtl_uString* args[] = { arg1.pData, arg2.pData, arg3.pData };
+ sal_Int32 nArgs = 3;
+
+ oslProcess aProcess;
+ oslFileHandle pOut = nullptr;
+ oslFileHandle pErr = nullptr;
+ oslSecurity pSecurity = osl_getCurrentSecurity();
+ oslProcessError eErr = osl_executeProcess_WithRedirectedIO(
+ binary.pData, args, nArgs, osl_Process_SEARCHPATH | osl_Process_HIDDEN, pSecurity, nullptr,
+ nullptr, 0, &aProcess, nullptr, &pOut, &pErr);
+ osl_freeSecurityHandle(pSecurity);
+
+ if (eErr != osl_Process_E_None)
+ return OString();
+
+ OStringBuffer result;
+ if (pOut)
+ {
+ const sal_uInt64 BUF_SIZE = 1024;
+ char buffer[BUF_SIZE];
+ while (true)
+ {
+ sal_uInt64 bytesRead = 0;
+ while(osl_readFile(pErr, buffer, BUF_SIZE, &bytesRead) == osl_File_E_None
+ && bytesRead != 0)
+ ; // discard possible stderr output
+ oslFileError err = osl_readFile(pOut, buffer, BUF_SIZE, &bytesRead);
+ if(bytesRead == 0 && err == osl_File_E_None)
+ break;
+ result.append(buffer, bytesRead);
+ if (err != osl_File_E_None && err != osl_File_E_AGAIN)
+ break;
+ }
+ osl_closeFile(pOut);
+ }
+ if(pErr)
+ osl_closeFile(pErr);
+ eErr = osl_joinProcess(aProcess);
+ osl_freeProcessHandle(aProcess);
+ return result.makeStringAndClear();
+}
+
+static OString symbol_addr2line(void* addr)
+{
+ Dl_info dli;
+ ptrdiff_t offset;
+ if (dladdr(addr, &dli) != 0)
+ {
+ if (dli.dli_fname && dli.dli_fbase)
+ {
+ offset = reinterpret_cast<ptrdiff_t>(addr) - reinterpret_cast<ptrdiff_t>(dli.dli_fbase);
+ OString info = symbol_addr2line_info(dli.dli_fname, offset);
+ // There should be two lines, first function name and second source file information.
+ // If each of them starts with ??, it is invalid/unknown.
+ if(!info.isEmpty() && !info.startsWith("??"))
+ {
+ sal_Int32 end1 = info.indexOf('\n');
+ if(end1 > 0)
+ {
+ OString function = info.copy( 0, end1 );
+ sal_Int32 end2 = info.indexOf('\n', end1 + 1);
+ OString source = info.copy( end1 + 1, end2 > 0 ? end2 - end1 - 1 : info.getLength() - end1 );
+ if( source.startsWith("??"))
+ return function + " in " + dli.dli_fname;
+ else
+ return function + " at " + source;
+ }
+ }
+ }
+ }
+ return OString();
+}
+
OUString sal::backtrace_to_string(BacktraceState* backtraceState)
{
- FreeGuard b2(backtrace_symbols(backtraceState->buffer, backtraceState->nDepth));
- if (b2.buffer == nullptr) {
+ OUStringBuffer b3;
+ std::unique_ptr<char*, decltype(free)*> b2{ nullptr, free };
+ bool fallbackInitDone = false;
+ for (int i = 0; i != backtraceState->nDepth; ++i)
+ {
+ if (i != 0)
+ b3.append("\n");
+ OString info = symbol_addr2line(backtraceState->buffer[i]);
+ if(!info.isEmpty())
+ b3.append(o3tl::runtimeToOUString(info.getStr()));
+ else
+ {
+ if(!fallbackInitDone)
+ {
+ b2 = std::unique_ptr<char*, decltype(free)*>
+ {backtrace_symbols(backtraceState->buffer, backtraceState->nDepth), free};
+ fallbackInitDone = true;
+ }
+ if(b2)
+ b3.append(o3tl::runtimeToOUString(b2.get()[i]));
+ else
+ b3.append("??");
+ }
+ }
+ return b3.makeStringAndClear();
+}
+
+#else
+
+OUString sal::backtrace_to_string(BacktraceState* backtraceState)
+{
+ std::unique_ptr<char*, decltype(free)*> b2{backtrace_symbols(backtraceState->buffer, backtraceState->nDepth), free};
+ if (b2.get() == nullptr) {
return OUString();
}
OUStringBuffer b3;
@@ -64,9 +177,11 @@ OUString sal::backtrace_to_string(BacktraceState* backtraceState)
if (i != 0) {
b3.append("\n");
}
- b3.append(o3tl::runtimeToOUString(b2.buffer[i]));
+ b3.append(o3tl::runtimeToOUString(b2.get()[i]));
}
return b3.makeStringAndClear();
}
+#endif
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */