Skip to content

Commit dfce3aa

Browse files
sstricklCommit Queue
authored and
Commit Queue
committed
[vm] Attempt to retrieve build ID or UUID from the loaded snapshot.
For direct-to-ELF snapshots, the story remains the same as before, as we use the information from the Image header if available. If it isn't, then we fall back to dladdr to get the dynamic shared object containing the app snapshot and then walk the ELF or Mach-O headers to find the build ID or UUID information. TEST=vm/dart/use_dwarf_stack_traces_flag Issue: #51941 Change-Id: I3705ed244d1b4a1255e75fffd238a29fc2a60800 Cq-Include-Trybots: luci.dart.try:vm-aot-dwarf-linux-product-x64-try,vm-aot-linux-debug-simarm_x64-try,vm-aot-linux-debug-x64-try,vm-aot-linux-release-x64-try,vm-aot-mac-product-arm64-try,vm-aot-mac-release-arm64-try,vm-aot-mac-release-x64-try,vm-aot-linux-product-x64-try,vm-aot-win-release-x64-try,vm-aot-win-product-x64-try,vm-aot-win-debug-x64c-try,vm-aot-android-release-arm_x64-try,vm-aot-android-release-arm64c-try,vm-fuchsia-release-x64-try Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/306640 Reviewed-by: Slava Egorov <[email protected]> Commit-Queue: Tess Strickland <[email protected]>
1 parent 69172b7 commit dfce3aa

9 files changed

+227
-19
lines changed

runtime/tests/vm/dart/use_dwarf_stack_traces_flag_test.dart

+9
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,15 @@ Future<void> compareTraces(List<String> nonDwarfTrace, DwarfTestOutput output1,
268268
Expect.isFalse(buildId2.isEmpty);
269269
print('Trace 2 build ID: "${buildId2}"');
270270
Expect.equals(dwarfBuildId, buildId2);
271+
} else {
272+
// Just check that the build IDs exist in the traces and are the same.
273+
final buildId1 = buildId(output1.trace);
274+
Expect.isFalse(buildId1.isEmpty, 'Could not find build ID in first trace');
275+
print('Trace 1 build ID: "${buildId1}"');
276+
final buildId2 = buildId(output2.trace);
277+
Expect.isFalse(buildId2.isEmpty, 'Could not find build ID in second trace');
278+
print('Trace 2 build ID: "${buildId2}"');
279+
Expect.equals(buildId1, buildId2);
271280
}
272281

273282
final decoder = DwarfStackTraceDecoder(dwarf!);

runtime/tests/vm/dart_2/use_dwarf_stack_traces_flag_test.dart

+9
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,15 @@ Future<void> compareTraces(List<String> nonDwarfTrace, DwarfTestOutput output1,
270270
Expect.isFalse(buildId2.isEmpty);
271271
print('Trace 2 build ID: "${buildId2}"');
272272
Expect.equals(dwarfBuildId, buildId2);
273+
} else {
274+
// Just check that the build IDs exist in the traces and are the same.
275+
final buildId1 = buildId(output1.trace);
276+
Expect.isFalse(buildId1.isEmpty, 'Could not find build ID in first trace');
277+
print('Trace 1 build ID: "${buildId1}"');
278+
final buildId2 = buildId(output2.trace);
279+
Expect.isFalse(buildId2.isEmpty, 'Could not find build ID in second trace');
280+
print('Trace 2 build ID: "${buildId2}"');
281+
Expect.equals(buildId1, buildId2);
273282
}
274283

275284
final decoder = DwarfStackTraceDecoder(dwarf);

runtime/vm/object.cc

+7-4
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
#include "vm/native_symbol.h"
5353
#include "vm/object_graph.h"
5454
#include "vm/object_store.h"
55+
#include "vm/os.h"
5556
#include "vm/parser.h"
5657
#include "vm/profiler.h"
5758
#include "vm/regexp.h"
@@ -26615,11 +26616,13 @@ const char* StackTrace::ToCString() const {
2661526616
buffer.Printf("os: %s arch: %s comp: %s sim: %s\n",
2661626617
kHostOperatingSystemName, kTargetArchitectureName,
2661726618
kCompressedPointers, kUsingSimulator);
26618-
if (auto const build_id = isolate_instructions_image.build_id()) {
26619-
const intptr_t length = isolate_instructions_image.build_id_length();
26619+
const OS::BuildId& build_id =
26620+
OS::GetAppBuildId(T->isolate_group()->source()->snapshot_instructions);
26621+
if (build_id.data != nullptr) {
26622+
ASSERT(build_id.len > 0);
2662026623
buffer.Printf("build_id: '");
26621-
for (intptr_t i = 0; i < length; i++) {
26622-
buffer.Printf("%2.2x", build_id[i]);
26624+
for (intptr_t i = 0; i < build_id.len; i++) {
26625+
buffer.Printf("%2.2x", build_id.data[i]);
2662326626
}
2662426627
buffer.Printf("'\n");
2662526628
}

runtime/vm/os.h

+10
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,16 @@ class OS {
119119
DART_NORETURN static void Abort();
120120

121121
DART_NORETURN static void Exit(int code);
122+
123+
struct BuildId {
124+
intptr_t len;
125+
const uint8_t* data;
126+
};
127+
128+
// Retrieves the build ID information for the current application isolate.
129+
// If found, returns a BuildId with the length of the build ID and a pointer
130+
// to its contents, otherwise returns a BuildId with contents {0, nullptr}.
131+
static BuildId GetAppBuildId(const uint8_t* snapshot_instructions);
122132
};
123133

124134
} // namespace dart

runtime/vm/os_android.cc

+47
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "vm/os.h"
99

1010
#include <android/log.h> // NOLINT
11+
#include <dlfcn.h> // NOLINT
12+
#include <elf.h> // NOLINT
1113
#include <errno.h> // NOLINT
1214
#include <limits.h> // NOLINT
1315
#include <malloc.h> // NOLINT
@@ -20,6 +22,7 @@
2022
#include "platform/utils.h"
2123
#include "vm/code_observers.h"
2224
#include "vm/dart.h"
25+
#include "vm/image_snapshot.h"
2326
#include "vm/isolate.h"
2427
#include "vm/timeline.h"
2528
#include "vm/zone.h"
@@ -342,6 +345,50 @@ void OS::Exit(int code) {
342345
exit(code);
343346
}
344347

348+
// Used to choose between Elf32/Elf64 types based on host archotecture bitsize.
349+
#if defined(ARCH_IS_64_BIT)
350+
#define ElfW(Type) Elf64_##Type
351+
#else
352+
#define ElfW(Type) Elf32_##Type
353+
#endif
354+
355+
OS::BuildId OS::GetAppBuildId(const uint8_t* snapshot_instructions) {
356+
// First return the build ID information from the instructions image if
357+
// available.
358+
const Image instructions_image(snapshot_instructions);
359+
if (auto* const image_build_id = instructions_image.build_id()) {
360+
return {instructions_image.build_id_length(), image_build_id};
361+
}
362+
Dl_info snapshot_info;
363+
if (dladdr(snapshot_instructions, &snapshot_info) == 0) {
364+
return {0, nullptr};
365+
}
366+
const uint8_t* dso_base =
367+
static_cast<const uint8_t*>(snapshot_info.dli_fbase);
368+
const ElfW(Ehdr)& elf_header = *reinterpret_cast<const ElfW(Ehdr)*>(dso_base);
369+
const ElfW(Phdr)* const phdr_array =
370+
reinterpret_cast<const ElfW(Phdr)*>(dso_base + elf_header.e_phoff);
371+
for (intptr_t i = 0; i < elf_header.e_phnum; i++) {
372+
const ElfW(Phdr)& header = phdr_array[i];
373+
if (header.p_type != PT_NOTE) continue;
374+
if ((header.p_flags & PF_R) != PF_R) continue;
375+
const uint8_t* const note_addr = dso_base + header.p_vaddr;
376+
const Elf32_Nhdr& note_header =
377+
*reinterpret_cast<const Elf32_Nhdr*>(note_addr);
378+
if (note_header.n_type != NT_GNU_BUILD_ID) continue;
379+
const char* const note_contents =
380+
reinterpret_cast<const char*>(note_addr + sizeof(Elf32_Nhdr));
381+
// The note name contains the null terminator as well.
382+
if (note_header.n_namesz != strlen(ELF_NOTE_GNU) + 1) continue;
383+
if (strncmp(ELF_NOTE_GNU, note_contents, note_header.n_namesz) == 0) {
384+
return {static_cast<intptr_t>(note_header.n_descsz),
385+
reinterpret_cast<const uint8_t*>(note_contents +
386+
note_header.n_namesz)};
387+
}
388+
}
389+
return {0, nullptr};
390+
}
391+
345392
} // namespace dart
346393

347394
#endif // defined(DART_HOST_OS_ANDROID)

runtime/vm/os_fuchsia.cc

+47
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include "vm/os.h"
99

10+
#include <dlfcn.h>
11+
#include <elf.h>
1012
#include <errno.h>
1113
#include <fcntl.h>
1214
#include <stdint.h>
@@ -37,6 +39,7 @@
3739
#include "platform/assert.h"
3840
#include "platform/syslog.h"
3941
#include "platform/utils.h"
42+
#include "vm/image_snapshot.h"
4043
#include "vm/lockers.h"
4144
#include "vm/os_thread.h"
4245
#include "vm/zone.h"
@@ -622,6 +625,50 @@ void OS::Exit(int code) {
622625
exit(code);
623626
}
624627

628+
// Used to choose between Elf32/Elf64 types based on host archotecture bitsize.
629+
#if defined(ARCH_IS_64_BIT)
630+
#define ElfW(Type) Elf64_##Type
631+
#else
632+
#define ElfW(Type) Elf32_##Type
633+
#endif
634+
635+
OS::BuildId OS::GetAppBuildId(const uint8_t* snapshot_instructions) {
636+
// First return the build ID information from the instructions image if
637+
// available.
638+
const Image instructions_image(snapshot_instructions);
639+
if (auto* const image_build_id = instructions_image.build_id()) {
640+
return {instructions_image.build_id_length(), image_build_id};
641+
}
642+
Dl_info snapshot_info;
643+
if (dladdr(snapshot_instructions, &snapshot_info) == 0) {
644+
return {0, nullptr};
645+
}
646+
const uint8_t* dso_base =
647+
static_cast<const uint8_t*>(snapshot_info.dli_fbase);
648+
const ElfW(Ehdr)& elf_header = *reinterpret_cast<const ElfW(Ehdr)*>(dso_base);
649+
const ElfW(Phdr)* const phdr_array =
650+
reinterpret_cast<const ElfW(Phdr)*>(dso_base + elf_header.e_phoff);
651+
for (intptr_t i = 0; i < elf_header.e_phnum; i++) {
652+
const ElfW(Phdr)& header = phdr_array[i];
653+
if (header.p_type != PT_NOTE) continue;
654+
if ((header.p_flags & PF_R) != PF_R) continue;
655+
const uint8_t* const note_addr = dso_base + header.p_vaddr;
656+
const Elf32_Nhdr& note_header =
657+
*reinterpret_cast<const Elf32_Nhdr*>(note_addr);
658+
if (note_header.n_type != NT_GNU_BUILD_ID) continue;
659+
const char* const note_contents =
660+
reinterpret_cast<const char*>(note_addr + sizeof(Elf32_Nhdr));
661+
// The note name contains the null terminator as well.
662+
if (note_header.n_namesz != strlen(ELF_NOTE_GNU) + 1) continue;
663+
if (strncmp(ELF_NOTE_GNU, note_contents, note_header.n_namesz) == 0) {
664+
return {static_cast<intptr_t>(note_header.n_descsz),
665+
reinterpret_cast<const uint8_t*>(note_contents +
666+
note_header.n_namesz)};
667+
}
668+
}
669+
return {0, nullptr};
670+
}
671+
625672
} // namespace dart
626673

627674
#endif // defined(DART_HOST_OS_FUCHSIA)

runtime/vm/os_linux.cc

+48-15
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77

88
#include "vm/os.h"
99

10+
#include <dlfcn.h> // NOLINT
11+
#include <elf.h> // NOLINT
1012
#include <errno.h> // NOLINT
1113
#include <fcntl.h> // NOLINT
1214
#include <limits.h> // NOLINT
@@ -26,6 +28,7 @@
2628
#include "vm/code_observers.h"
2729
#include "vm/dart.h"
2830
#include "vm/flags.h"
31+
#include "vm/image_snapshot.h"
2932
#include "vm/isolate.h"
3033
#include "vm/lockers.h"
3134
#include "vm/os_thread.h"
@@ -34,6 +37,13 @@
3437

3538
namespace dart {
3639

40+
// Used to choose between Elf32/Elf64 types based on host archotecture bitsize.
41+
#if defined(ARCH_IS_64_BIT)
42+
#define ElfW(Type) Elf64_##Type
43+
#else
44+
#define ElfW(Type) Elf32_##Type
45+
#endif
46+
3747
#ifndef PRODUCT
3848

3949
DEFINE_FLAG(bool,
@@ -271,14 +281,6 @@ class JitDumpCodeObserver : public CodeObserver {
271281
// Followed by nul-terminated name.
272282
};
273283

274-
// ELF machine architectures
275-
// From linux/include/uapi/linux/elf-em.h
276-
static constexpr uint32_t EM_386 = 3;
277-
static constexpr uint32_t EM_X86_64 = 62;
278-
static constexpr uint32_t EM_ARM = 40;
279-
static constexpr uint32_t EM_AARCH64 = 183;
280-
static constexpr uint32_t EM_RISCV = 243;
281-
282284
static uint32_t GetElfMachineArchitecture() {
283285
#if TARGET_ARCH_IA32
284286
return EM_386;
@@ -296,12 +298,6 @@ class JitDumpCodeObserver : public CodeObserver {
296298
#endif
297299
}
298300

299-
#if ARCH_IS_64_BIT
300-
static constexpr int kElfHeaderSize = 0x40;
301-
#else
302-
static constexpr int kElfHeaderSize = 0x34;
303-
#endif
304-
305301
void WriteDebugInfo(uword base, const CodeComments* comments) {
306302
if (comments == nullptr || comments->Length() == 0) {
307303
return;
@@ -349,7 +345,7 @@ class JitDumpCodeObserver : public CodeObserver {
349345
i++;
350346
}
351347
DebugInfoEntry entry;
352-
entry.address = base + pc_offset + kElfHeaderSize;
348+
entry.address = base + pc_offset + sizeof(ElfW(Ehdr));
353349
entry.line_number = line_number;
354350
entry.column = 0;
355351
WriteFully(&entry, sizeof(entry));
@@ -657,6 +653,43 @@ void OS::Exit(int code) {
657653
exit(code);
658654
}
659655

656+
OS::BuildId OS::GetAppBuildId(const uint8_t* snapshot_instructions) {
657+
// First return the build ID information from the instructions image if
658+
// available.
659+
const Image instructions_image(snapshot_instructions);
660+
if (auto* const image_build_id = instructions_image.build_id()) {
661+
return {instructions_image.build_id_length(), image_build_id};
662+
}
663+
Dl_info snapshot_info;
664+
if (dladdr(snapshot_instructions, &snapshot_info) == 0) {
665+
return {0, nullptr};
666+
}
667+
const uint8_t* dso_base =
668+
static_cast<const uint8_t*>(snapshot_info.dli_fbase);
669+
const ElfW(Ehdr)& elf_header = *reinterpret_cast<const ElfW(Ehdr)*>(dso_base);
670+
const ElfW(Phdr)* const phdr_array =
671+
reinterpret_cast<const ElfW(Phdr)*>(dso_base + elf_header.e_phoff);
672+
for (intptr_t i = 0; i < elf_header.e_phnum; i++) {
673+
const ElfW(Phdr)& header = phdr_array[i];
674+
if (header.p_type != PT_NOTE) continue;
675+
if ((header.p_flags & PF_R) != PF_R) continue;
676+
const uint8_t* const note_addr = dso_base + header.p_vaddr;
677+
const Elf32_Nhdr& note_header =
678+
*reinterpret_cast<const Elf32_Nhdr*>(note_addr);
679+
if (note_header.n_type != NT_GNU_BUILD_ID) continue;
680+
const char* const note_contents =
681+
reinterpret_cast<const char*>(note_addr + sizeof(Elf32_Nhdr));
682+
// The note name contains the null terminator as well.
683+
if (note_header.n_namesz != strlen(ELF_NOTE_GNU) + 1) continue;
684+
if (strncmp(ELF_NOTE_GNU, note_contents, note_header.n_namesz) == 0) {
685+
return {static_cast<intptr_t>(note_header.n_descsz),
686+
reinterpret_cast<const uint8_t*>(note_contents +
687+
note_header.n_namesz)};
688+
}
689+
}
690+
return {0, nullptr};
691+
}
692+
660693
} // namespace dart
661694

662695
#endif // defined(DART_HOST_OS_LINUX)

runtime/vm/os_macos.cc

+40
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
#include "vm/os.h"
99

10+
#include <dlfcn.h> // NOLINT
1011
#include <errno.h> // NOLINT
1112
#include <limits.h> // NOLINT
13+
#include <mach-o/loader.h> // NOLINT
1214
#include <mach/clock.h> // NOLINT
1315
#include <mach/mach.h> // NOLINT
1416
#include <mach/mach_time.h> // NOLINT
@@ -20,6 +22,7 @@
2022
#endif
2123

2224
#include "platform/utils.h"
25+
#include "vm/image_snapshot.h"
2326
#include "vm/isolate.h"
2427
#include "vm/timeline.h"
2528
#include "vm/zone.h"
@@ -307,6 +310,43 @@ void OS::Exit(int code) {
307310
exit(code);
308311
}
309312

313+
OS::BuildId OS::GetAppBuildId(const uint8_t* snapshot_instructions) {
314+
// First return the build ID information from the instructions image if
315+
// available.
316+
const Image instructions_image(snapshot_instructions);
317+
if (auto* const image_build_id = instructions_image.build_id()) {
318+
return {instructions_image.build_id_length(), image_build_id};
319+
}
320+
Dl_info snapshot_info;
321+
if (dladdr(snapshot_instructions, &snapshot_info) == 0) {
322+
return {0, nullptr};
323+
}
324+
const uint8_t* dso_base =
325+
static_cast<const uint8_t*>(snapshot_info.dli_fbase);
326+
const auto& macho_header =
327+
*reinterpret_cast<const struct mach_header*>(dso_base);
328+
// We assume host endianness in the Mach-O file.
329+
if (macho_header.magic != MH_MAGIC && macho_header.magic != MH_MAGIC_64) {
330+
return {0, nullptr};
331+
}
332+
const size_t macho_header_size = macho_header.magic == MH_MAGIC
333+
? sizeof(struct mach_header)
334+
: sizeof(struct mach_header_64);
335+
const uint8_t* it = dso_base + macho_header_size;
336+
const uint8_t* end = it + macho_header.sizeofcmds;
337+
while (it < end) {
338+
const auto& current_cmd = *reinterpret_cast<const struct load_command*>(it);
339+
if ((current_cmd.cmd & ~LC_REQ_DYLD) == LC_UUID) {
340+
const auto& uuid_cmd = *reinterpret_cast<const struct uuid_command*>(it);
341+
return {
342+
static_cast<intptr_t>(uuid_cmd.cmdsize - sizeof(struct load_command)),
343+
uuid_cmd.uuid};
344+
}
345+
it += current_cmd.cmdsize;
346+
}
347+
return {0, nullptr};
348+
}
349+
310350
} // namespace dart
311351

312352
#endif // defined(DART_HOST_OS_MACOS)

0 commit comments

Comments
 (0)