blob: 027fb8a2ff08f2c8dd31eb7a3020660b3705817f [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 "raw_printer.h"
#include <inttypes.h>
#include <lib/syslog/cpp/log_settings.h>
#include <lib/syslog/cpp/macros.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <string>
#include <vector>
#include "garnet/lib/intel_pt_decode/decoder.h"
#include "src/lib/fxl/command_line.h"
#include "src/lib/fxl/strings/string_number_conversions.h"
#include "src/lib/fxl/strings/string_printf.h"
#include "third_party/processor-trace/libipt/include/intel-pt.h"
#include "third_party/simple-pt/printer-util.h"
namespace intel_processor_trace {
std::unique_ptr<RawPrinter> RawPrinter::Create(DecoderState* state, const Config& config) {
FILE* out_file = stdout;
if (config.output_file_name != "") {
out_file = fopen(config.output_file_name.c_str(), "w");
if (!out_file) {
FX_LOGS(ERROR) << "Unable to open file for writing: " << config.output_file_name;
return nullptr;
}
}
auto printer = std::unique_ptr<RawPrinter>(new RawPrinter(out_file, state, config));
return printer;
}
RawPrinter::RawPrinter(FILE* out_file, DecoderState* state, const Config& config)
: out_file_(out_file), state_(state), config_(config) {}
RawPrinter::~RawPrinter() {
if (config_.output_file_name != "")
fclose(out_file_);
}
void RawPrinter::Printf(const char* format, ...) {
va_list args;
va_start(args, format);
vfprintf(out_file_, format, args);
va_end(args);
}
RawPrinter::Space RawPrinter::GetSpace(uint64_t cr3, const SymbolTable* symtab) {
if (symtab) {
if (symtab->is_kernel())
return Space::kKernel;
else
return Space::kUser;
} else if (cr3 != pt_asid_no_cr3) {
// If we're in kernel space on behalf of userspace that is intended to
// be caught by the preceding case (symtab != nullptr).
if (cr3 == state_->kernel_cr3())
return Space::kKernel;
else
return Space::kUser;
} else {
return Space::kUnknown;
}
}
void RawPrinter::PrintInsn(const pt_insn* insn, PrintState* ps) {
Printf("%" PRIu64 ": %" PRIx64 ": %s", ps->current_ts, ps->current_pc,
simple_pt::InsnClassName(insn->iclass));
// TODO(dje): Add option to include disassembly.
Printf("\n");
}
int RawPrinter::ProcessNextInsn(struct pt_insn_decoder* pt_decoder, PrintState* ps) {
// This is the data we obtain from libipt.
struct pt_insn insn;
// Do the increment before checking the result of pt_insn_next so that
// error lines have reference numbers as well.
++ps->total_insncnt;
pt_insn_get_offset(pt_decoder, &ps->current_pos);
// TODO(dje): Verify this always stores values in the arguments even
// if there's an error (which according to intel-pt.h can only be
// -pte_no_time).
uint64_t ts;
uint32_t lost_mtc, lost_cyc;
pt_insn_time(pt_decoder, &ts, &lost_mtc, &lost_cyc);
if (ts)
ps->current_ts = ts;
insn.ip = 0;
int err = pt_insn_next(pt_decoder, &insn, sizeof(insn));
uint64_t cr3;
pt_insn_get_cr3(pt_decoder, &cr3);
ps->current_pc = insn.ip;
if (err < 0) {
ps->current_cr3 = cr3;
return err;
}
// Watch for changes to the core bus ratio recorded in the trace.
uint32_t ratio;
pt_insn_core_bus_ratio(pt_decoder, &ratio);
if (ratio != ps->current_core_bus_ratio) {
Printf("Core bus ratio is now %u\n", ratio);
ps->current_core_bus_ratio = ratio;
}
// Watch for changes to CR3.
if (cr3 != ps->current_cr3) {
Printf("CR3 is now 0x%" PRIx64 "\n", cr3);
ps->current_cr3 = cr3;
}
const SymbolTable* symtab = state_->FindSymbolTable(ps->current_cr3, ps->current_pc);
const Symbol* sym = symtab ? symtab->FindSymbol(ps->current_pc) : nullptr;
Space space = GetSpace(ps->current_cr3, symtab);
if (space != ps->current_space) {
Printf("Space is now ");
switch (space) {
case Space::kKernel:
Printf("kernel");
break;
case Space::kUser:
Printf("user");
break;
default:
Printf("unknown");
break;
}
Printf("\n");
ps->current_space = space;
}
// Watch for changes to the current function.
if (sym != ps->current_function) {
if (sym) {
Printf("Current function is now %s:%s\n", symtab->file_name().c_str(),
sym->name ? sym->name : "unknown");
} else {
Printf("Entering unknown function\n");
}
ps->current_symtab = symtab;
ps->current_function = sym;
}
PrintInsn(&insn, ps);
return 0;
}
uint64_t RawPrinter::PrintOneFile(const PtFile& pt_file) {
if (!state_->AllocDecoder(pt_file.file)) {
FX_LOGS(ERROR) << "Unable to open pt file: " << pt_file.file;
return 0;
}
Printf("Dump of PT file %s, id 0x%" PRIx64 "\n", pt_file.file.c_str(), pt_file.id);
PrintState ps;
struct pt_insn_decoder* pt_decoder = state_->decoder();
for (;;) {
// Every time we get an error while reading the trace we start over
// at the top of this loop.
int err = pt_insn_sync_forward(pt_decoder);
pt_insn_get_offset(pt_decoder, &ps.current_pos);
if (err < 0) {
std::string message = fxl::StringPrintf("0x%" PRIx64 ": sync forward: %s\n", ps.current_pos,
pt_errstr(pt_errcode(err)));
if (err == -pte_eos) {
FX_LOGS(INFO) << message;
} else {
FX_LOGS(ERROR) << message;
}
break;
}
for (;;) {
err = ProcessNextInsn(pt_decoder, &ps);
if (err < 0)
break;
}
if (err == -pte_eos) {
// Let the top of the loop test catch and report this.
continue;
}
FX_LOGS(ERROR) << fxl::StringPrintf(
"[%8" PRIu64 "] @0x%" PRIx64 ": %" PRIx64 ":%" PRIx64 ": error %s", ps.total_insncnt,
ps.current_pos, ps.current_cr3, ps.current_pc, pt_errstr(pt_errcode(err)));
}
state_->FreeDecoder();
return ps.total_insncnt;
}
uint64_t RawPrinter::PrintFiles() {
uint64_t total_insns = 0;
for (const auto& file : state_->pt_files()) {
total_insns += PrintOneFile(file);
}
return total_insns;
}
} // namespace intel_processor_trace