You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by ab...@apache.org on 2020/04/17 14:02:48 UTC
[nifi-minifi-cpp] branch master updated: MINIFICPP-1192 - Add macOS
support and in-function offsets to backtrace
This is an automated email from the ASF dual-hosted git repository.
aboda pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git
The following commit(s) were added to refs/heads/master by this push:
new 427fd5d MINIFICPP-1192 - Add macOS support and in-function offsets to backtrace
427fd5d is described below
commit 427fd5d96b1682b124ea4db29e1dfaf5a05597b1
Author: Daniel Bakai <ba...@apache.org>
AuthorDate: Fri Feb 28 18:19:48 2020 +0100
MINIFICPP-1192 - Add macOS support and in-function offsets to backtrace
Signed-off-by: Arpad Boda <ab...@apache.org>
This closes #758
---
libminifi/include/utils/BackTrace.h | 16 ++++
libminifi/src/utils/BackTrace.cpp | 132 +++++++++++++++++++++++----------
libminifi/test/unit/BackTraceTests.cpp | 4 +
3 files changed, 112 insertions(+), 40 deletions(-)
diff --git a/libminifi/include/utils/BackTrace.h b/libminifi/include/utils/BackTrace.h
index 8614500..5bd95cb 100644
--- a/libminifi/include/utils/BackTrace.h
+++ b/libminifi/include/utils/BackTrace.h
@@ -130,6 +130,22 @@ class TraceResolver {
}
/**
+ * Adds a trace line in the format "<file_name> @ <symbol_name> + <symbol_offset>", if symbol_name is not nullptr,
+ * "<file_name>" otherwise.
+ * @param file_name the filename of the shared object
+ * @param symbol_name the name of the symbol (or of the shared object, if the offset is supplied from that)
+ * @param symbol_offset offset from the base address of the symbol (or the shared object)
+ */
+ void addTraceLine(const char* file_name, const char* symbol_name, uintptr_t symbol_offset) {
+ std::stringstream line;
+ line << file_name;
+ if (symbol_name != nullptr) {
+ line << " @ " << symbol_name << " + " << symbol_offset;
+ }
+ trace_.addLine(line.str());
+ }
+
+ /**
* Returns the thread handle reference in the native format.
*/
std::thread::native_handle_type getThreadHandle() {
diff --git a/libminifi/src/utils/BackTrace.cpp b/libminifi/src/utils/BackTrace.cpp
index 0e6c856..282c69f 100644
--- a/libminifi/src/utils/BackTrace.cpp
+++ b/libminifi/src/utils/BackTrace.cpp
@@ -16,63 +16,115 @@
#include "utils/BackTrace.h"
#ifdef HAS_EXECINFO
#include <execinfo.h>
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <dlfcn.h>
+#ifdef __linux__
+#include <link.h>
+#endif
#include <iostream>
#include <utility>
+#include <cstring>
#include <cxxabi.h>
#endif
-void pull_trace(const uint8_t frames_to_skip) {
#ifdef HAS_EXECINFO
- void *stackBuffer[TRACE_BUFFER_SIZE + 1];
-
- // retrieve current stack addresses
- int trace_size = backtrace(stackBuffer, TRACE_BUFFER_SIZE);
-
- char **symboltable = backtrace_symbols(stackBuffer, trace_size);
+namespace {
/**
- * we can skip the signal handler, call to pull_trace, and the first entry for backtrace_symbols
+ * Demangles a symbol name using the cxx abi.
+ * @param symbol_name the mangled name of the symbol
+ * @return the demangled name on success, empty string on failure
*/
+ std::string demangle_symbol(const char* symbol_name) {
+ int status;
+ char* demangled = abi::__cxa_demangle(symbol_name, nullptr, nullptr, &status);
+ if (status == 0) {
+ std::string demangled_name = demangled;
+ free(demangled);
+ return demangled_name;
+ } else {
+ return "";
+ }
+ }
+} // namespace
+#endif
+
+void pull_trace(uint8_t frames_to_skip /* = 1 */) {
+#ifdef HAS_EXECINFO
+ void* stack_buffer[TRACE_BUFFER_SIZE + 1];
+
+ /* Get the backtrace of the current thread */
+ int trace_size = backtrace(stack_buffer, TRACE_BUFFER_SIZE);
+
+ /* We can skip the signal handler, call to pull_trace, and the first entry for backtrace_symbols */
for (int i = frames_to_skip; i < trace_size; i++) {
- char *start_parenthetical = nullptr;
- char *functor = nullptr;
- char *stop_parenthetical = nullptr;
-
- for (char *p = symboltable[i]; *p; ++p) {
- if (*p == '(') {
- start_parenthetical = p;
- } else if (*p == '+') {
- functor = p;
- } else if (*p == ')' && functor) {
- stop_parenthetical = p;
- break;
+ const char* file_name = "???";
+ const char* symbol_name = nullptr;
+ uintptr_t symbol_offset = 0;
+
+ /* Translate the address to symbolic information */
+ Dl_info dl_info{};
+#ifdef __linux__
+ struct link_map* l_map = nullptr;
+ int res = dladdr1(stack_buffer[i], &dl_info, reinterpret_cast<void**>(&l_map), RTLD_DL_LINKMAP);
+#else
+ int res = dladdr(stack_buffer[i], &dl_info);
+#endif
+ if (res == 0 || dl_info.dli_fname == nullptr || dl_info.dli_fname[0] == '\0') {
+ /* We could not determine symbolic information for this address*/
+ TraceResolver::getResolver().addTraceLine(file_name, symbol_name, symbol_offset);
+ continue;
+ }
+
+ /* Determine the filename of the shared object */
+ if (dl_info.dli_fname != nullptr) {
+ const char* last_slash = nullptr;
+ /* If the shared object name is a full path, we still only want the filename component */
+ if ((last_slash = strrchr(dl_info.dli_fname, '/')) != nullptr) {
+ file_name = last_slash + 1;
+ } else {
+ file_name = dl_info.dli_fname;
}
}
- bool hasFunc = start_parenthetical && functor && stop_parenthetical;
- if (hasFunc && start_parenthetical < functor) {
- *start_parenthetical++ = '\0';
- *functor++ = '\0';
- *stop_parenthetical = '\0';
-
- /**
- * Demangle the names -- this requires calling cxx api to demangle the function name.
- * not sending an allocated buffer, so we'll deallocate if status is zero.
- */
- int status;
+ /* Determine the symbol name */
+ std::string demangled_symbol_name;
+ if (dl_info.dli_sname != nullptr) {
+ symbol_name = dl_info.dli_sname;
- auto demangled = abi::__cxa_demangle(start_parenthetical, nullptr, nullptr, &status);
- if (status == 0) {
- TraceResolver::getResolver().addTraceLine(symboltable[i], demangled);
- free(demangled);
- } else {
- TraceResolver::getResolver().addTraceLine(symboltable[i], start_parenthetical);
+ /* Try to demangle the symbol name */
+ demangled_symbol_name = demangle_symbol(symbol_name);
+ if (!demangled_symbol_name.empty()) {
+ symbol_name = demangled_symbol_name.c_str();
}
} else {
- TraceResolver::getResolver().addTraceLine(symboltable[i], "");
+ /* If we could not determine the symbol name, we will use the filename instead */
+ symbol_name = file_name;
}
- }
- free(symboltable);
+ /* Determine our offset */
+ uintptr_t base_address = 0;
+ if (dl_info.dli_sname != nullptr) {
+ /* If we could determine our symbol we will display our offset from the address of the symbol */
+ base_address = reinterpret_cast<uintptr_t>(dl_info.dli_saddr);
+ } else {
+ /* Otherwise we will display our offset from base address of the shared object */
+#ifdef __linux__
+ /*
+ * glibc uses l_addr from the link map instead of dli_fbase in backtrace_symbols for calculating this offset.
+ * I could not find a difference between the two in my limited measurements, but we will use it too, just to be sure.
+ */
+ if (l_map != nullptr) {
+ dl_info.dli_fbase = reinterpret_cast<void*>(l_map->l_addr);
+ }
+#endif
+ base_address = reinterpret_cast<uintptr_t>(dl_info.dli_fbase);
+ }
+ symbol_offset = reinterpret_cast<uintptr_t>(stack_buffer[i]) - base_address;
+
+ TraceResolver::getResolver().addTraceLine(file_name, symbol_name, symbol_offset);
+ }
#endif
}
diff --git a/libminifi/test/unit/BackTraceTests.cpp b/libminifi/test/unit/BackTraceTests.cpp
index 806f95c..73a9be8 100644
--- a/libminifi/test/unit/BackTraceTests.cpp
+++ b/libminifi/test/unit/BackTraceTests.cpp
@@ -103,9 +103,13 @@ TEST_CASE("BT2", "[TPT2]") {
std::vector<BackTrace> traces = pool.getTraces();
for (const auto &trace : traces) {
+ std::cerr << "Thread name: " << trace.getName() << std::endl;
const auto &trace_strings = trace.getTraces();
#ifdef HAS_EXECINFO
REQUIRE(trace_strings.size() > 2);
+ for (const auto& trace_string : trace_strings) {
+ std::cerr << " - " << trace_string << std::endl;
+ }
if (trace_strings.at(0).find("sleep_for") != std::string::npos) {
REQUIRE(trace_strings.at(1).find("counterFunction") != std::string::npos);
}