diff --git a/debuggerd/libdebuggerd/backtrace.cpp b/debuggerd/libdebuggerd/backtrace.cpp index 753ebcb7c..94fcfb28c 100644 --- a/debuggerd/libdebuggerd/backtrace.cpp +++ b/debuggerd/libdebuggerd/backtrace.cpp @@ -74,6 +74,7 @@ void dump_backtrace_thread(int output_fd, unwindstack::Unwinder* unwinder, return; } + unwinder->SetDisplayBuildID(true); for (size_t i = 0; i < unwinder->NumFrames(); i++) { _LOG(&log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(i).c_str()); } diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp index c08afdafe..47a7a8ff8 100644 --- a/debuggerd/libdebuggerd/tombstone.cpp +++ b/debuggerd/libdebuggerd/tombstone.cpp @@ -371,6 +371,7 @@ static void dump_all_maps(log_t* log, unwindstack::Unwinder* unwinder, uint64_t } void dump_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* prefix) { + unwinder->SetDisplayBuildID(true); for (size_t i = 0; i < unwinder->NumFrames(); i++) { _LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str()); } diff --git a/libunwindstack/Unwinder.cpp b/libunwindstack/Unwinder.cpp index a1c58ddb3..3f2e1c1b8 100644 --- a/libunwindstack/Unwinder.cpp +++ b/libunwindstack/Unwinder.cpp @@ -284,17 +284,9 @@ void Unwinder::Unwind(const std::vector* initial_map_names_to_skip, } } -std::string Unwinder::FormatFrame(size_t frame_num) { - if (frame_num >= frames_.size()) { - return ""; - } - return FormatFrame(frames_[frame_num], regs_->Is32Bit()); -} - -std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) { +std::string Unwinder::FormatFrame(const FrameData& frame) { std::string data; - - if (is32bit) { + if (regs_->Is32Bit()) { data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc); } else { data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc); @@ -320,9 +312,24 @@ std::string Unwinder::FormatFrame(const FrameData& frame, bool is32bit) { } data += ')'; } + + MapInfo* map_info = maps_->Find(frame.map_start); + if (map_info != nullptr && display_build_id_) { + std::string build_id = map_info->GetPrintableBuildID(); + if (!build_id.empty()) { + data += " (BuildId: " + build_id + ')'; + } + } return data; } +std::string Unwinder::FormatFrame(size_t frame_num) { + if (frame_num >= frames_.size()) { + return ""; + } + return FormatFrame(frames_[frame_num]); +} + void Unwinder::SetJitDebug(JitDebug* jit_debug, ArchEnum arch) { jit_debug->SetArch(arch); jit_debug_ = jit_debug; diff --git a/libunwindstack/include/unwindstack/Unwinder.h b/libunwindstack/include/unwindstack/Unwinder.h index a0554e2f1..8b01654a9 100644 --- a/libunwindstack/include/unwindstack/Unwinder.h +++ b/libunwindstack/include/unwindstack/Unwinder.h @@ -88,7 +88,7 @@ class Unwinder { } std::string FormatFrame(size_t frame_num); - static std::string FormatFrame(const FrameData& frame, bool is32bit); + std::string FormatFrame(const FrameData& frame); void SetJitDebug(JitDebug* jit_debug, ArchEnum arch); @@ -105,6 +105,8 @@ class Unwinder { // NOTE: This does nothing unless resolving names is enabled. void SetEmbeddedSoname(bool embedded_soname) { embedded_soname_ = embedded_soname; } + void SetDisplayBuildID(bool display_build_id) { display_build_id_ = display_build_id; } + #if !defined(NO_LIBDEXFILE_SUPPORT) void SetDexFiles(DexFiles* dex_files, ArchEnum arch); #endif @@ -130,6 +132,7 @@ class Unwinder { #endif bool resolve_names_ = true; bool embedded_soname_ = true; + bool display_build_id_ = false; ErrorData last_error_; }; diff --git a/libunwindstack/tests/RegsFake.h b/libunwindstack/tests/RegsFake.h index d6ca9b7b5..207d46efe 100644 --- a/libunwindstack/tests/RegsFake.h +++ b/libunwindstack/tests/RegsFake.h @@ -23,6 +23,8 @@ #include #include +#include "Check.h" + namespace unwindstack { class RegsFake : public Regs { @@ -47,7 +49,10 @@ class RegsFake : public Regs { void IterateRegisters(std::function) override {} - bool Is32Bit() { return false; } + bool Is32Bit() { + CHECK(fake_arch_ != ARCH_UNKNOWN); + return fake_arch_ == ARCH_ARM || fake_arch_ == ARCH_X86 || fake_arch_ == ARCH_MIPS; + } uint64_t GetPcAdjustment(uint64_t, Elf*) override { return 2; } diff --git a/libunwindstack/tests/UnwinderTest.cpp b/libunwindstack/tests/UnwinderTest.cpp index 504b57a4e..48e038e9a 100644 --- a/libunwindstack/tests/UnwinderTest.cpp +++ b/libunwindstack/tests/UnwinderTest.cpp @@ -58,7 +58,9 @@ class UnwinderTest : public ::testing::Test { maps_.reset(new Maps); ElfFake* elf = new ElfFake(new MemoryFake); - elf->FakeSetInterface(new ElfInterfaceFake(nullptr)); + ElfInterfaceFake* interface_fake = new ElfInterfaceFake(nullptr); + interface_fake->FakeSetBuildID("FAKE"); + elf->FakeSetInterface(interface_fake); AddMapInfo(0x1000, 0x8000, 0, PROT_READ | PROT_WRITE, "/system/fake/libc.so", elf); AddMapInfo(0x10000, 0x12000, 0, PROT_READ | PROT_WRITE, "[stack]"); @@ -1102,7 +1104,15 @@ TEST_F(UnwinderTest, dex_pc_max_frames) { } // Verify format frame code. -TEST_F(UnwinderTest, format_frame_static) { +TEST_F(UnwinderTest, format_frame) { + RegsFake regs_arm(10); + regs_arm.FakeSetArch(ARCH_ARM); + Unwinder unwinder32(10, maps_.get(), ®s_arm, process_memory_); + + RegsFake regs_arm64(10); + regs_arm64.FakeSetArch(ARCH_ARM64); + Unwinder unwinder64(10, maps_.get(), ®s_arm64, process_memory_); + FrameData frame; frame.num = 1; frame.rel_pc = 0x1000; @@ -1117,39 +1127,61 @@ TEST_F(UnwinderTest, format_frame_static) { frame.map_flags = PROT_READ; EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (offset 0x2000) (function+100)", - Unwinder::FormatFrame(frame, false)); + unwinder64.FormatFrame(frame)); EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (offset 0x2000) (function+100)", - Unwinder::FormatFrame(frame, true)); + unwinder32.FormatFrame(frame)); frame.map_elf_start_offset = 0; EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function+100)", - Unwinder::FormatFrame(frame, false)); - EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", - Unwinder::FormatFrame(frame, true)); + unwinder64.FormatFrame(frame)); + EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", unwinder32.FormatFrame(frame)); frame.function_offset = 0; EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (function)", - Unwinder::FormatFrame(frame, false)); - EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function)", Unwinder::FormatFrame(frame, true)); + unwinder64.FormatFrame(frame)); + EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function)", unwinder32.FormatFrame(frame)); // Verify the function name is demangled. frame.function_name = "_ZN4funcEv"; - EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (func())", - Unwinder::FormatFrame(frame, false)); - EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (func())", Unwinder::FormatFrame(frame, true)); + EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so (func())", unwinder64.FormatFrame(frame)); + EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (func())", unwinder32.FormatFrame(frame)); frame.function_name = ""; - EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so", Unwinder::FormatFrame(frame, false)); - EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so", Unwinder::FormatFrame(frame, true)); + EXPECT_EQ(" #01 pc 0000000000001000 /fake/libfake.so", unwinder64.FormatFrame(frame)); + EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so", unwinder32.FormatFrame(frame)); frame.map_name = ""; - EXPECT_EQ(" #01 pc 0000000000001000 ", Unwinder::FormatFrame(frame, false)); - EXPECT_EQ(" #01 pc 00001000 ", Unwinder::FormatFrame(frame, true)); + EXPECT_EQ(" #01 pc 0000000000001000 ", unwinder64.FormatFrame(frame)); + EXPECT_EQ(" #01 pc 00001000 ", unwinder32.FormatFrame(frame)); frame.map_start = 0; frame.map_end = 0; - EXPECT_EQ(" #01 pc 0000000000001000 ", Unwinder::FormatFrame(frame, false)); - EXPECT_EQ(" #01 pc 00001000 ", Unwinder::FormatFrame(frame, true)); + EXPECT_EQ(" #01 pc 0000000000001000 ", unwinder64.FormatFrame(frame)); + EXPECT_EQ(" #01 pc 00001000 ", unwinder32.FormatFrame(frame)); +} + +TEST_F(UnwinderTest, format_frame_build_id) { + RegsFake regs(10); + regs.FakeSetArch(ARCH_ARM); + Unwinder unwinder(10, maps_.get(), ®s, process_memory_); + + FrameData frame; + frame.num = 1; + frame.rel_pc = 0x1000; + frame.pc = 0x4000; + frame.sp = 0x1000; + frame.function_name = "function"; + frame.function_offset = 100; + frame.map_name = "/fake/libfake.so"; + frame.map_elf_start_offset = 0; + frame.map_start = 0x3000; + frame.map_end = 0x6000; + frame.map_flags = PROT_READ; + + EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100)", unwinder.FormatFrame(frame)); + unwinder.SetDisplayBuildID(true); + EXPECT_EQ(" #01 pc 00001000 /fake/libfake.so (function+100) (BuildId: 46414b45)", + unwinder.FormatFrame(frame)); } static std::string ArchToString(ArchEnum arch) { @@ -1167,7 +1199,7 @@ static std::string ArchToString(ArchEnum arch) { } // Verify format frame code. -TEST_F(UnwinderTest, format_frame) { +TEST_F(UnwinderTest, format_frame_by_arch) { std::vector reg_list; RegsArm* arm = new RegsArm; arm->set_pc(0x2300);