blob: 8d3ea5375281912e8ee8b4044e33388307dd6dd0 [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 "decoder.h"
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <lib/syslog/cpp/macros.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <string>
#include "garnet/lib/debugger_utils/util.h"
#include "src/lib/files/directory.h"
#include "src/lib/files/path.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace intel_processor_trace {
static void* MmapFile(const char* file, size_t* size) {
int fd = open(file, O_RDONLY);
if (fd < 0)
return nullptr;
struct stat st;
void* map = MAP_FAILED;
if (fstat(fd, &st) >= 0) {
*size = st.st_size;
map = mmap(nullptr, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
}
close(fd);
return map != MAP_FAILED ? map : nullptr;
}
static void UnmapFile(void* map, size_t size) { munmap(map, size); }
std::unique_ptr<DecoderState> DecoderState::Create(const DecoderConfig& config) {
auto decoder = std::unique_ptr<DecoderState>(new DecoderState());
FX_DCHECK(config.pt_file_name != "" || config.pt_list_file_name != "");
FX_DCHECK(config.ktrace_file_name != "");
if (!decoder->AllocImage("ipt-dump"))
return nullptr;
// Read sideband data before we read anything else.
if (!decoder->ReadKtraceFile(config.ktrace_file_name))
return nullptr;
for (auto& f : config.map_file_names) {
if (!decoder->ReadMapFile(f))
return nullptr;
}
for (auto& f : config.ids_file_names) {
if (!decoder->ReadIdsFile(f))
return nullptr;
}
if (config.pt_file_name != "") {
decoder->AddPtFile(files::GetCurrentDirectory(), PtFile::kIdUnset, config.pt_file_name);
} else {
if (!decoder->ReadPtListFile(config.pt_list_file_name))
return nullptr;
}
for (auto& f : config.elf_file_names) {
// TODO(dje): This isn't useful without base addr, cr3, etc.
if (!decoder->ReadElf(f, 0, 0, 0, 0))
return nullptr;
}
if (config.kernel_file_name != "") {
decoder->SetKernelCr3(config.kernel_cr3);
if (!decoder->ReadKernelElf(config.kernel_file_name, config.kernel_cr3))
return nullptr;
}
return decoder;
}
DecoderState::DecoderState() : image_(nullptr), decoder_(nullptr), kernel_cr3_(pt_asid_no_cr3) {
pt_config_init(&config_);
}
DecoderState::~DecoderState() {
if (config_.begin)
UnmapFile(config_.begin, config_.end - config_.begin);
if (decoder_)
pt_insn_free_decoder(decoder_);
if (image_)
pt_image_free(image_);
}
Process::Process(zx_koid_t p, uint64_t c, uint64_t start, uint64_t end)
: pid(p), cr3(c), start_time(start), end_time(end) {
FX_VLOGS(2) << fxl::StringPrintf("pid %" PRIu64 " cr3 0x%" PRIx64 " start %" PRIu64, pid, cr3,
start_time);
}
PtFile::PtFile(uint64_t i, const std::string& f) : id(i), file(f) {
FX_VLOGS(2) << fxl::StringPrintf("pt_file %" PRIu64 ", file %s", id, file.c_str());
}
const Process* DecoderState::LookupProcessByPid(zx_koid_t pid) {
// TODO(dje): Add O(1) lookup when there's a need.
for (const auto& p : processes_) {
if (p.pid == pid)
return &p;
}
return nullptr;
}
const Process* DecoderState::LookupProcessByCr3(uint64_t cr3) {
// TODO(dje): Add O(1) lookup when there's a need.
for (const auto& p : processes_) {
if (p.cr3 == cr3)
return &p;
// If tracing just threads, cr3 values in the trace may be this.
// If there's only one process, we're ok.
// TODO(dje): Tracing threads with multiple processes.
if (cr3 == pt_asid_no_cr3 && processes_.size() == 1)
return &p;
}
return nullptr;
}
const LoadMap* DecoderState::LookupMapEntry(zx_koid_t pid, uint64_t addr) {
return load_maps_.LookupLoadMap(pid, addr);
}
const BuildId* DecoderState::LookupBuildId(const std::string& bid) {
return build_ids_.LookupBuildId(bid);
}
std::string DecoderState::LookupFile(const std::string& file) {
// TODO(dje): This function is here in case we need to do fancier lookup
// later.
return file;
}
// static
int DecoderState::ReadMemCallback(uint8_t* buffer, size_t size, const struct pt_asid* asid,
uint64_t addr, void* context) {
auto decoder = reinterpret_cast<DecoderState*>(context);
uint64_t cr3 = asid->cr3;
auto proc = decoder->LookupProcessByCr3(cr3);
if (!proc) {
FX_VLOGS(1) << fxl::StringPrintf(
"process lookup failed for cr3:"
" 0x%" PRIx64,
cr3);
decoder->unknown_cr3s_.emplace(cr3);
return -pte_nomap;
}
auto map = decoder->LookupMapEntry(proc->pid, addr);
if (!map) {
FX_VLOGS(1) << fxl::StringPrintf(
"map lookup failed for cr3/addr:"
" 0x%" PRIx64 "/0x%" PRIx64,
cr3, addr);
return -pte_nomap;
}
auto bid = decoder->LookupBuildId(map->build_id);
if (!bid) {
FX_VLOGS(1) << fxl::StringPrintf(
"build_id not found: %s, for cr3/addr:"
" 0x%" PRIx64 "/0x%" PRIx64,
map->build_id.c_str(), cr3, addr);
return -pte_nomap;
}
auto file = decoder->LookupFile(bid->file);
if (!file.size()) {
FX_VLOGS(1) << fxl::StringPrintf(
"file not found: %s, for build_id %s, cr3/addr:"
" 0x%" PRIx64 "/0x%" PRIx64,
bid->file.c_str(), map->build_id.c_str(), cr3, addr);
return -pte_nomap;
}
if (!decoder->ReadElf(file.c_str(), map->base_addr, cr3, 0, map->end_addr - map->load_addr)) {
FX_VLOGS(1) << "Reading ELF file failed: " << file;
return -pte_nomap;
}
return pt_image_read_for_callback(decoder->image_, buffer, size, asid, addr);
}
bool DecoderState::AllocImage(const std::string& name) {
FX_DCHECK(!image_);
struct pt_image* image = pt_image_alloc(name.c_str());
FX_DCHECK(image);
pt_image_set_callback(image, ReadMemCallback, this);
image_ = image;
return true;
}
bool DecoderState::AddProcess(zx_koid_t pid, uint64_t cr3, uint64_t start_time) {
FX_VLOGS(2) << fxl::StringPrintf("New process: %" PRIu64 ", cr3 0x%" PRIx64 " @%" PRIu64, pid,
cr3, start_time);
processes_.push_back(Process(pid, cr3, start_time, 0));
return true;
}
bool DecoderState::MarkProcessExited(zx_koid_t pid, uint64_t end_time) {
FX_VLOGS(2) << fxl::StringPrintf("Marking process exit: %" PRIu64 " @%" PRIu64, pid, end_time);
// We don't remove the process as process start/exit records are read in
// one pass over the ktrace file. Instead just mark when it exited.
// We assume process ids won't wrap, which is pretty safe for now.
for (auto i = processes_.begin(); i != processes_.end(); ++i) {
if (i->pid == pid) {
i->end_time = end_time;
break;
}
}
// If we didn't find an entry that's ok. We might have gotten a process-exit
// notification for a process that we didn't get a start notification for.
return true;
}
void DecoderState::AddPtFile(const std::string& file_dir, uint64_t id, const std::string& path) {
std::string abs_path;
// Convert relative paths to absolute ones.
if (path[0] != '/') {
std::string abs_file_dir = files::AbsolutePath(file_dir);
abs_path = abs_file_dir + "/" + path;
} else {
abs_path = path;
}
pt_files_.push_back(PtFile(id, abs_path));
}
bool DecoderState::AllocDecoder(const std::string& pt_file_name) {
unsigned zero = 0;
FX_DCHECK(decoder_ == nullptr);
pt_cpu_errata(&config_.errata, &config_.cpu);
// When no bit is set, set all, as libipt does not keep up with newer
// CPUs otherwise.
if (!memcmp(&config_.errata, &zero, 4))
memset(&config_.errata, 0xff, sizeof(config_.errata));
size_t len;
unsigned char* map = reinterpret_cast<unsigned char*>(MmapFile(pt_file_name.c_str(), &len));
if (!map) {
fprintf(stderr, "Cannot open PT file %s: %s\n", pt_file_name.c_str(), strerror(errno));
return false;
}
config_.begin = map;
config_.end = map + len;
decoder_ = pt_insn_alloc_decoder(&config_);
if (!decoder_) {
fprintf(stderr, "Cannot create PT decoder\n");
UnmapFile(map, len);
return false;
}
pt_insn_set_image(decoder_, image_);
return true;
}
void DecoderState::FreeDecoder() {
FX_DCHECK(decoder_);
pt_insn_free_decoder(decoder_);
decoder_ = nullptr;
}
const SymbolTable* DecoderState::FindSymbolTable(uint64_t cr3, uint64_t pc) {
return simple_pt::FindSymbolTable(symtabs_, cr3, pc);
}
const Symbol* DecoderState::FindSymbol(uint64_t cr3, uint64_t pc, const SymbolTable** out_symtab) {
return simple_pt::FindSymbol(symtabs_, cr3, pc, out_symtab);
}
const char* DecoderState::FindPcFileName(uint64_t cr3, uint64_t pc) {
return simple_pt::FindPcFileName(symtabs_, cr3, pc);
}
bool DecoderState::SeenCr3(uint64_t cr3) { return simple_pt::SeenCr3(symtabs_, cr3); }
} // namespace intel_processor_trace