blob: fa7c65054042e7b62c6dc988dc2ff466f78259b8 [file] [log] [blame]
// Copyright 2016 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 "server.h"
#include <array>
#include <cinttypes>
#include <cstdio>
#include <cstdlib>
#include <limits>
#include <string>
#include <vector>
#include <zircon/syscalls.h>
#include "src/lib/fxl/arraysize.h"
#include "src/lib/fxl/logging.h"
#include "src/lib/fxl/strings/string_printf.h"
#include "garnet/lib/debugger_utils/jobs.h"
#include "garnet/lib/debugger_utils/sysinfo.h"
#include "garnet/lib/debugger_utils/util.h"
#include "control.h"
namespace insntrace {
constexpr char IptConfig::kDefaultOutputPathPrefix[];
IptConfig::IptConfig()
: mode(kDefaultMode),
num_cpus(zx_system_get_num_cpus()),
max_threads(kDefaultMaxThreads),
num_chunks(kDefaultNumChunks),
chunk_order(kDefaultChunkOrder),
is_circular(kDefaultIsCircular),
branch(true),
cr3_match(0),
cr3_match_set(false),
cyc(false),
cyc_thresh(0),
mtc(false),
mtc_freq(0),
psb_freq(0),
os(true),
user(true),
retc(true),
tsc(true),
output_path_prefix(kDefaultOutputPathPrefix) {
addr[0] = AddrFilter::kOff;
addr[1] = AddrFilter::kOff;
}
uint64_t IptConfig::CtlMsr() const {
uint64_t msr = 0;
// For documentation of the fields see the description of the IA32_RTIT_CTL
// MSR in chapter 36 "Intel Processor Trace" of Intel Volume 3.
if (cyc)
msr |= 1 << 1;
if (os)
msr |= 1 << 2;
if (user)
msr |= 1 << 3;
if (cr3_match)
msr |= 1 << 7;
if (mtc)
msr |= 1 << 9;
if (tsc)
msr |= 1 << 10;
if (!retc)
msr |= 1 << 11;
if (branch)
msr |= 1 << 13;
msr |= (mtc_freq & 15) << 14;
msr |= (cyc_thresh & 15) << 19;
msr |= (psb_freq & 15) << 24;
msr |= (uint64_t)addr[0] << 32;
msr |= (uint64_t)addr[1] << 36;
return msr;
}
uint64_t IptConfig::AddrBegin(unsigned i) const {
FXL_DCHECK(i < arraysize(addr_range));
return addr_range[i].begin;
}
uint64_t IptConfig::AddrEnd(unsigned i) const {
FXL_DCHECK(i < arraysize(addr_range));
return addr_range[i].end;
}
IptServer::IptServer(const IptConfig& config,
const debugger_utils::Argv& argv)
: Server(debugger_utils::GetRootJob(), debugger_utils::GetDefaultJob(),
sys::ServiceDirectory::CreateFromNamespace()),
config_(config), inferior_argv_(argv) {}
bool IptServer::StartInferior() {
inferior_control::Process* inferior = current_process();
std::unique_ptr<process::ProcessBuilder> builder;
FXL_DCHECK(!inferior_argv_.empty());
FXL_LOG(INFO) << "Starting program: " << inferior_argv_[0];
if (!AllocTrace(config_))
return false;
if (config_.mode == IPT_MODE_CPUS) {
if (!InitTrace(config_))
goto Fail;
}
if (!InitProcessTrace(config_))
goto Fail;
// N.B. It's important that the PT device be closed at this point as we
// don't want the inferior to inherit the open descriptor: the device can
// only be opened once at a time.
if (!CreateProcessViaBuilder(inferior_argv_[0], inferior_argv_, &builder)) {
FXL_LOG(ERROR) << "Unable to initialize process builder";
goto Fail;
}
builder->CloneAll();
if (!inferior->InitializeFromBuilder(std::move(builder))) {
FXL_LOG(ERROR) << "Unable to initialize inferior process";
goto Fail;
}
if (!config_.cr3_match_set) {
// TODO(dje): fetch cr3 for inferior and apply it to cr3_match
}
// If tracing cpus, defer turning on tracing as long as possible so that we
// don't include all the initialization. For threads it doesn't matter.
// TODO(dje): Could even defer until the first thread is started.
if (config_.mode == IPT_MODE_CPUS) {
if (!StartTrace(config_))
goto Fail;
}
FXL_DCHECK(!inferior->IsLive());
if (!inferior->Start()) {
FXL_LOG(ERROR) << "Unable to start process";
if (config_.mode == IPT_MODE_CPUS)
StopTrace(config_);
goto Fail;
}
FXL_DCHECK(inferior->IsLive());
return true;
Fail:
FreeTrace(config_);
return false;
}
bool IptServer::DumpResults() {
if (config_.mode == IPT_MODE_CPUS)
StopTrace(config_);
StopSidebandDataCollection(config_);
if (config_.mode == IPT_MODE_CPUS)
DumpTrace(config_);
DumpSidebandData(config_);
if (config_.mode == IPT_MODE_CPUS)
ResetTrace(config_);
FreeTrace(config_);
return true;
}
bool IptServer::Run() {
if (!exception_port_.Run()) {
FXL_LOG(ERROR) << "Failed to initialize exception port!";
return false;
}
if (!StartInferior()) {
FXL_LOG(ERROR) << "Failed to start inferior";
return false;
}
// Start the main loop.
message_loop_.Run();
FXL_LOG(INFO) << "Main loop exited";
// Tell the exception port to quit and wait for it to finish.
exception_port_.Quit();
if (!DumpResults()) {
FXL_LOG(ERROR) << "Error dumping results";
return false;
}
return run_status_;
}
void IptServer::OnThreadStarting(inferior_control::Process* process,
inferior_control::Thread* thread,
zx_handle_t eport,
const zx_exception_context_t& context) {
FXL_DCHECK(process);
FXL_DCHECK(thread);
switch (process->state()) {
case inferior_control::Process::State::kStarting:
case inferior_control::Process::State::kRunning:
break;
default:
FXL_DCHECK(false);
}
if (config_.mode == IPT_MODE_THREADS) {
if (!InitThreadTrace(thread, config_))
goto Fail;
if (!StartThreadTrace(thread, config_)) {
ResetThreadTrace(thread, config_);
goto Fail;
}
}
Fail:
thread->ResumeFromException(eport);
}
void IptServer::OnThreadExiting(inferior_control::Process* process,
inferior_control::Thread* thread,
zx_handle_t eport,
const zx_exception_context_t& context) {
FXL_DCHECK(process);
FXL_DCHECK(thread);
// Dump any collected trace.
if (config_.mode == IPT_MODE_THREADS) {
if (thread->ipt_buffer() >= 0) {
StopThreadTrace(thread, config_);
DumpThreadTrace(thread, config_);
ResetThreadTrace(thread, config_);
}
}
// We still have to "resume" the thread so that the o/s will complete the
// termination of the thread.
thread->ResumeForExit(eport);
}
void IptServer::OnProcessTermination(inferior_control::Process* process) {
FXL_DCHECK(process);
printf("Process %s is gone, rc %d\n", process->GetName().c_str(),
process->return_code());
// If the process is gone, unset current thread, and exit main loop.
SetCurrentThread(nullptr);
QuitMessageLoop(true);
}
void IptServer::OnArchitecturalException(
inferior_control::Process* process, inferior_control::Thread* thread,
zx_handle_t eport, const zx_excp_type_t type,
const zx_exception_context_t& context) {
FXL_DCHECK(process);
FXL_DCHECK(thread);
// TODO(armansito): Fine-tune this check if we ever support multi-processing.
FXL_DCHECK(process == current_process());
// This is generally a segv or some such. Not much we can do.
QuitMessageLoop(true);
}
void IptServer::OnSyntheticException(inferior_control::Process* process,
inferior_control::Thread* thread,
zx_handle_t eport, zx_excp_type_t type,
const zx_exception_context_t& context) {
FXL_DCHECK(process);
FXL_DCHECK(thread);
// Program is crashing.
QuitMessageLoop(true);
}
} // namespace insntrace