blob: 6b06de4c4e074a8a9eff2cc63e86a16b5f5629c2 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "load_maps.h"
#include <lib/fit/defer.h>
#include <lib/syslog/cpp/macros.h>
#include <cinttypes>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <map>
#include "src/lib/files/directory.h"
#include "src/lib/files/path.h"
#include "src/lib/fxl/strings/string_printf.h"
#include "util.h"
namespace debugger_utils {
bool LoadMapTable::ReadLogListenerOutput(const std::string& file) {
FX_LOGS(INFO) << "Loading load maps from " << file;
FILE* f = fopen(file.c_str(), "r");
if (!f) {
FX_LOGS(ERROR) << "error opening file, " << ErrnoString(errno);
return false;
}
auto close_file = fit::defer([&]() { fclose(f); });
constexpr size_t kMaxLineLen = 1024;
char* line = nullptr;
size_t line_capacity = 0;
int lineno = 1;
std::map<uint64_t, LoadMap> map_data;
ssize_t line_len;
// These buffers are needed to break out elements of the line. Rather than
// allocate space for them during each iteration we allocate them here.
// And rather than use up a fair bit of stack, we use the heap.
char* prefix = reinterpret_cast<char*>(malloc(kMaxLineLen));
char* build_id = reinterpret_cast<char*>(malloc(kMaxLineLen));
char* name = reinterpret_cast<char*>(malloc(kMaxLineLen));
char* so_name = reinterpret_cast<char*>(malloc(kMaxLineLen));
auto free_mem = fit::defer([&]() {
free(line);
free(prefix);
free(build_id);
free(name);
free(so_name);
});
if (!prefix || !build_id || !name || !so_name) {
FX_LOGS(ERROR) << "Out of memory";
return false;
}
for (; (line_len = getline(&line, &line_capacity, f)) > 0; ++lineno) {
// For paranoia's sake, watch for embedded NULs.
size_t n = strlen(line);
if (static_cast<ssize_t>(n) != line_len) {
FX_LOGS(WARNING) << "Line contains embedded NULs: " << std::string(line, line_len);
continue;
}
if (n > 0 && line[n - 1] == '\n')
line[n - 1] = '\0';
FX_VLOGS(2) << fxl::StringPrintf("%d: %s", lineno, line);
if (n > kMaxLineLen) {
FX_VLOGS(2) << fxl::StringPrintf("%d: too long, ignoring", lineno);
}
if (!strcmp(line, "\n"))
continue;
if (line[0] == '#')
continue;
// If this is a new boot, start over.
if (strstr(line, "welcome to Zircon")) {
FX_VLOGS(1) << "Restarting reading of load maps, machine rebooted";
map_data.clear();
Clear();
continue;
}
// The sequence number is used for grouping records, done beforehand, but
// is no longer needed after that.
unsigned seqno;
uint64_t pid, base_addr, load_addr, end_addr;
// ld.so dumps the data in three separate records to avoid line-wrapping:
// a: base load end
// b: build_id
// c: name so_name
// TODO(dje): See MG-519. This is a temp hack until ld.so logs this data
// via something better.
#define GET_ENTRY_ID(pid, seqno) (((pid) << 8) + (seqno))
if (sscanf(line,
"%[^@]@trace_load: %" PRIu64 ":%ua"
" 0x%" PRIx64 " 0x%" PRIx64 " 0x%" PRIx64,
prefix, &pid, &seqno, &base_addr, &load_addr, &end_addr) == 6) {
uint64_t id = GET_ENTRY_ID(pid, seqno);
if (map_data.find(id) != map_data.end()) {
FX_LOGS(ERROR) << "Already have map entry for: " << line;
continue;
}
struct LoadMap entry;
entry.pid = pid;
entry.base_addr = base_addr;
entry.load_addr = load_addr;
entry.end_addr = end_addr;
map_data[id] = entry;
} else if (sscanf(line,
"%[^@]@trace_load: %" PRIu64 ":%ub"
" %s",
prefix, &pid, &seqno, build_id) == 4) {
uint64_t id = GET_ENTRY_ID(pid, seqno);
auto entry_iter = map_data.find(id);
if (entry_iter == map_data.end()) {
FX_LOGS(ERROR) << "Missing entry (A record) for: " << line;
continue;
}
(*entry_iter).second.build_id = build_id;
} else if (sscanf(line,
"%[^@]@trace_load: %" PRIu64 ":%uc"
" %s %s",
prefix, &pid, &seqno, name, so_name) == 5) {
uint64_t id = GET_ENTRY_ID(pid, seqno);
auto entry_iter = map_data.find(id);
if (entry_iter == map_data.end()) {
FX_LOGS(ERROR) << "Missing entry (A record) for: " << line;
continue;
}
LoadMap& entry = (*entry_iter).second;
entry.name = name;
entry.so_name = so_name;
// We should now have the full record.
AddLoadMap(entry);
} else {
FX_VLOGS(2) << fxl::StringPrintf("%d: ignoring", lineno);
}
}
if (!feof(f)) {
FX_LOGS(ERROR) << "Error reading file";
return false;
}
return true;
}
void LoadMapTable::AddLoadMap(const LoadMap& map) {
FX_VLOGS(2) << fxl::StringPrintf("Adding map entry, pid %" PRIu64 " %s 0x%" PRIx64 "-0x%" PRIx64,
map.pid, map.name.c_str(), map.load_addr, map.end_addr);
maps_.push_back(map);
}
void LoadMapTable::Clear() { maps_.clear(); }
const LoadMap* LoadMapTable::LookupLoadMap(zx_koid_t pid, uint64_t addr) {
for (auto& m : maps_) {
if (pid == m.pid && addr >= m.load_addr && addr < m.end_addr)
return &m;
}
return nullptr;
}
} // namespace debugger_utils