diff --git a/libbacktrace/BacktraceCurrent.cpp b/libbacktrace/BacktraceCurrent.cpp index b7190e256..fd1f4da7b 100644 --- a/libbacktrace/BacktraceCurrent.cpp +++ b/libbacktrace/BacktraceCurrent.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define _GNU_SOURCE 1 #include #include #include @@ -73,6 +74,16 @@ bool BacktraceCurrent::Unwind(size_t num_ignore_frames, ucontext_t* ucontext) { return UnwindFromContext(num_ignore_frames, nullptr); } +bool BacktraceCurrent::DiscardFrame(const backtrace_frame_data_t& frame) { + if (BacktraceMap::IsValid(frame.map)) { + const std::string library = basename(frame.map.name.c_str()); + if (library == "libunwind.so" || library == "libbacktrace.so") { + return true; + } + } + return false; +} + static pthread_mutex_t g_sigaction_mutex = PTHREAD_MUTEX_INITIALIZER; static void SignalHandler(int, siginfo_t*, void* sigcontext) { diff --git a/libbacktrace/BacktraceCurrent.h b/libbacktrace/BacktraceCurrent.h index 81ea81d5a..8aad36d00 100644 --- a/libbacktrace/BacktraceCurrent.h +++ b/libbacktrace/BacktraceCurrent.h @@ -46,6 +46,9 @@ public: bool Unwind(size_t num_ignore_frames, ucontext_t* ucontext) override; +protected: + bool DiscardFrame(const backtrace_frame_data_t& frame); + private: bool UnwindThread(size_t num_ignore_frames); diff --git a/libbacktrace/UnwindCurrent.cpp b/libbacktrace/UnwindCurrent.cpp index 12e289095..67e583f08 100644 --- a/libbacktrace/UnwindCurrent.cpp +++ b/libbacktrace/UnwindCurrent.cpp @@ -99,25 +99,30 @@ bool UnwindCurrent::UnwindFromContext(size_t num_ignore_frames, ucontext_t* ucon break; } - if (num_ignore_frames == 0) { - frames_.resize(num_frames+1); - backtrace_frame_data_t* frame = &frames_.at(num_frames); - frame->num = num_frames; - frame->pc = static_cast(pc); - frame->sp = static_cast(sp); - frame->stack_size = 0; + frames_.resize(num_frames+1); + backtrace_frame_data_t* frame = &frames_.at(num_frames); + frame->num = num_frames; + frame->pc = static_cast(pc); + frame->sp = static_cast(sp); + frame->stack_size = 0; - if (num_frames > 0) { - // Set the stack size for the previous frame. - backtrace_frame_data_t* prev = &frames_.at(num_frames-1); - prev->stack_size = frame->sp - prev->sp; + FillInMap(frame->pc, &frame->map); + // Check to see if we should skip this frame because it's coming + // from within the library, and we are doing a local unwind. + if (ucontext != nullptr || num_frames != 0 || !DiscardFrame(*frame)) { + if (num_ignore_frames == 0) { + // GetFunctionName is an expensive call, only do it if we are + // keeping the frame. + frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); + if (num_frames > 0) { + // Set the stack size for the previous frame. + backtrace_frame_data_t* prev = &frames_.at(num_frames-1); + prev->stack_size = frame->sp - prev->sp; + } + num_frames++; + } else { + num_ignore_frames--; } - - frame->func_name = GetFunctionName(frame->pc, &frame->func_offset); - FillInMap(frame->pc, &frame->map); - num_frames++; - } else { - num_ignore_frames--; } ret = unw_step (cursor.get()); } while (ret > 0 && num_frames < MAX_BACKTRACE_FRAMES); diff --git a/libbacktrace/backtrace_test.cpp b/libbacktrace/backtrace_test.cpp index d40885647..5de80b1ac 100644 --- a/libbacktrace/backtrace_test.cpp +++ b/libbacktrace/backtrace_test.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define _GNU_SOURCE 1 #include #include #include @@ -200,6 +201,23 @@ bool WaitForNonZero(int32_t* value, uint64_t seconds) { return false; } +TEST(libbacktrace, local_no_unwind_frames) { + // Verify that a local unwind does not include any frames within + // libunwind or libbacktrace. + std::unique_ptr backtrace(Backtrace::Create(getpid(), getpid())); + ASSERT_TRUE(backtrace->Unwind(0)); + + ASSERT_TRUE(backtrace->NumFrames() != 0); + for (const auto& frame : *backtrace ) { + if (BacktraceMap::IsValid(frame.map)) { + const std::string name = basename(frame.map.name.c_str()); + ASSERT_TRUE(name != "libunwind.so" && name != "libbacktrace.so") + << DumpFrames(backtrace.get()); + } + break; + } +} + TEST(libbacktrace, local_trace) { ASSERT_NE(test_level_one(1, 2, 3, 4, VerifyLevelBacktrace, nullptr), 0); }