Skip to content

Commit

Permalink
fix(symbolize): simplify resource management
Browse files Browse the repository at this point in the history
  • Loading branch information
sergiud committed Jan 7, 2024
1 parent 42a185c commit 7d2d462
Showing 1 changed file with 53 additions and 46 deletions.
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

0 comments on commit 7d2d462

Please sign in to comment.