Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(symbolize): simplify resource management #1047

Merged
merged 1 commit into from
Jan 7, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 53 additions & 46 deletions src/symbolize.cc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2023, Google Inc.
// Copyright (c) 2024, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -134,10 +134,17 @@ static ATTRIBUTE_NOINLINE void DemangleInplace(char* out, size_t out_size) {
# include "glog/raw_logging.h"
# include "symbolize.h"

// Re-runs fn until it doesn't cause EINTR.
# define NO_INTR(fn) \
do { \
} while ((fn) < 0 && errno == EINTR)
// Re-runs run until it doesn't cause EINTR.
// Similar to the TEMP_FAILURE_RETRY macro from GNU C.
template <class Functor>
auto FailureRetry(Functor run, int error = EINTR) noexcept(noexcept(run())) {
decltype(run()) result;

do {
} while ((result = run()) == -1 && errno == error);

return result;
}

namespace google {

Expand All @@ -153,9 +160,10 @@ static ssize_t ReadFromOffset(const int fd, void* buf, const size_t count,
char* buf0 = reinterpret_cast<char*>(buf);
size_t num_bytes = 0;
while (num_bytes < count) {
ssize_t len;
NO_INTR(len = pread(fd, buf0 + num_bytes, count - num_bytes,
static_cast<off_t>(offset + num_bytes)));
ssize_t len = FailureRetry([fd, p = buf0 + num_bytes, n = count - num_bytes,
m = static_cast<off_t>(offset + num_bytes)] {
return pread(fd, p, n, m);
});
if (len < 0) { // There was an error other than EINTR.
return -1;
}
Expand Down Expand Up @@ -496,34 +504,33 @@ static char* GetHex(const char* start, const char* end, uint64_t* hex) {
// file is opened successfully, returns the file descriptor. Otherwise,
// returns -1. |out_file_name_size| is the size of the file name buffer
// (including the null-terminator).
static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(
uint64_t pc, uint64_t& start_address, uint64_t& base_address,
char* out_file_name, size_t out_file_name_size) {
int object_fd;

int maps_fd;
NO_INTR(maps_fd = open("/proc/self/maps", O_RDONLY));
FileDescriptor wrapped_maps_fd(maps_fd);
if (!wrapped_maps_fd) {
return -1;
static ATTRIBUTE_NOINLINE FileDescriptor
OpenObjectFileContainingPcAndGetStartAddress(uint64_t pc,
uint64_t& start_address,
uint64_t& base_address,
char* out_file_name,
size_t out_file_name_size) {
FileDescriptor maps_fd{
FailureRetry([] { return open("/proc/self/maps", O_RDONLY); })};
if (!maps_fd) {
return nullptr;
}

int mem_fd;
NO_INTR(mem_fd = open("/proc/self/mem", O_RDONLY));
FileDescriptor wrapped_mem_fd(mem_fd);
if (!wrapped_mem_fd) {
return -1;
FileDescriptor mem_fd{
FailureRetry([] { return open("/proc/self/mem", O_RDONLY); })};
if (!mem_fd) {
return nullptr;
}

// Iterate over maps and look for the map containing the pc. Then
// look into the symbol tables inside.
char buf[1024]; // Big enough for line of sane /proc/self/maps
LineReader reader(wrapped_maps_fd.get(), buf, sizeof(buf), 0);
LineReader reader(maps_fd.get(), buf, sizeof(buf), 0);
while (true) {
const char* cursor;
const char* eol;
if (!reader.ReadLine(&cursor, &eol)) { // EOF or malformed line.
return -1;
return nullptr;
}

// Start parsing line in /proc/self/maps. Here is an example:
Expand All @@ -536,15 +543,15 @@ static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(
// Read start address.
cursor = GetHex(cursor, eol, &start_address);
if (cursor == eol || *cursor != '-') {
return -1; // Malformed line.
return nullptr; // Malformed line.
}
++cursor; // Skip '-'.

// Read end address.
uint64_t end_address;
cursor = GetHex(cursor, eol, &end_address);
if (cursor == eol || *cursor != ' ') {
return -1; // Malformed line.
return nullptr; // Malformed line.
}
++cursor; // Skip ' '.

Expand All @@ -555,14 +562,15 @@ static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(
}
// We expect at least four letters for flags (ex. "r-xp").
if (cursor == eol || cursor < flags_start + 4) {
return -1; // Malformed line.
return nullptr; // Malformed line.
}

// Determine the base address by reading ELF headers in process memory.
ElfW(Ehdr) ehdr;
// Skip non-readable maps.
if (flags_start[0] == 'r' &&
ReadFromOffsetExact(mem_fd, &ehdr, sizeof(ElfW(Ehdr)), start_address) &&
ReadFromOffsetExact(mem_fd.get(), &ehdr, sizeof(ElfW(Ehdr)),
start_address) &&
memcmp(ehdr.e_ident, ELFMAG, SELFMAG) == 0) {
switch (ehdr.e_type) {
case ET_EXEC:
Expand All @@ -581,7 +589,7 @@ static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(
for (unsigned i = 0; i != ehdr.e_phnum; ++i) {
ElfW(Phdr) phdr;
if (ReadFromOffsetExact(
mem_fd, &phdr, sizeof(phdr),
mem_fd.get(), &phdr, sizeof(phdr),
start_address + ehdr.e_phoff + i * sizeof(phdr)) &&
phdr.p_type == PT_LOAD && phdr.p_offset == 0) {
base_address = start_address - phdr.p_vaddr;
Expand All @@ -597,7 +605,7 @@ static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(
}

// Check start and end addresses.
if (!(start_address <= pc && pc < end_address)) {
if (start_address > pc || pc >= end_address) {
continue; // We skip this map. PC isn't in this map.
}

Expand All @@ -611,7 +619,7 @@ static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(
uint64_t file_offset;
cursor = GetHex(cursor, eol, &file_offset);
if (cursor == eol || *cursor != ' ') {
return -1; // Malformed line.
return nullptr; // Malformed line.
}
++cursor; // Skip ' '.

Expand All @@ -629,18 +637,19 @@ static ATTRIBUTE_NOINLINE int OpenObjectFileContainingPcAndGetStartAddress(
++cursor;
}
if (cursor == eol) {
return -1; // Malformed line.
return nullptr; // Malformed line.
}

// Finally, "cursor" now points to file name of our interest.
NO_INTR(object_fd = open(cursor, O_RDONLY));
if (object_fd < 0) {
FileDescriptor object_fd{
FailureRetry([cursor] { return open(cursor, O_RDONLY); })};
if (!object_fd) {
// Failed to open object file. Copy the object file name to
// |out_file_name|.
strncpy(out_file_name, cursor, out_file_name_size);
// Making sure |out_file_name| is always null-terminated.
out_file_name[out_file_name_size - 1] = '\0';
return -1;
return nullptr;
}
return object_fd;
}
Expand Down Expand Up @@ -736,7 +745,7 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
auto pc0 = reinterpret_cast<uintptr_t>(pc);
uint64_t start_address = 0;
uint64_t base_address = 0;
int object_fd = -1;
FileDescriptor object_fd;

if (out_size < 1) {
return false;
Expand All @@ -745,20 +754,18 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
SafeAppendString("(", out, out_size);

if (g_symbolize_open_object_file_callback) {
object_fd = g_symbolize_open_object_file_callback(
pc0, start_address, base_address, out + 1, out_size - 1);
object_fd.reset(g_symbolize_open_object_file_callback(
pc0, start_address, base_address, out + 1, out_size - 1));
} else {
object_fd = OpenObjectFileContainingPcAndGetStartAddress(
pc0, start_address, base_address, out + 1, out_size - 1);
}

FileDescriptor wrapped_object_fd(object_fd);

# if defined(PRINT_UNSYMBOLIZED_STACK_TRACES)
{
# else
// Check whether a file name was returned.
if (object_fd < 0) {
if (!object_fd) {
# endif
if (out[1]) {
// The object file containing PC was determined successfully however the
Expand All @@ -774,7 +781,7 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
// Failed to determine the object file containing PC. Bail out.
return false;
}
int elf_type = FileGetElfType(wrapped_object_fd.get());
int elf_type = FileGetElfType(object_fd.get());
if (elf_type == -1) {
return false;
}
Expand All @@ -783,14 +790,14 @@ static ATTRIBUTE_NOINLINE bool SymbolizeAndDemangle(void* pc, char* out,
// Note: relocation (and much of the rest of this code) will be
// wrong for prelinked shared libraries and PIE executables.
uint64_t relocation = (elf_type == ET_DYN) ? start_address : 0;
int num_bytes_written = g_symbolize_callback(wrapped_object_fd.get(), pc,
out, out_size, relocation);
int num_bytes_written =
g_symbolize_callback(object_fd.get(), pc, out, out_size, relocation);
if (num_bytes_written > 0) {
out += static_cast<size_t>(num_bytes_written);
out_size -= static_cast<size_t>(num_bytes_written);
}
}
if (!GetSymbolFromObjectFile(wrapped_object_fd.get(), pc0, out, out_size,
if (!GetSymbolFromObjectFile(object_fd.get(), pc0, out, out_size,
base_address)) {
if (out[1] && !g_symbolize_callback) {
// The object file containing PC was opened successfully however the
Expand Down