blob: fee53b6a14f589ad4a7f58cefb08bcfd89657c1a [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; c-basic-offset: 2; indent-tabs-mode: nil; -*- */
#include "DumpCommand.h"
#include <inttypes.h>
#include <limits>
#include <unordered_map>
#include "preload/preload_interface.h"
#include "AddressSpace.h"
#include "Command.h"
#include "Flags.h"
#include "RecordSession.h"
#include "ReplaySession.h"
#include "ReplayTask.h"
#include "TraceStream.h"
#include "core.h"
#include "kernel_metadata.h"
#include "log.h"
#include "main.h"
#include "util.h"
using namespace std;
namespace rr {
class TraceInfoCommand : public Command {
public:
virtual int run(vector<string>& args) override;
protected:
TraceInfoCommand(const char* name, const char* help) : Command(name, help) {}
static TraceInfoCommand singleton;
};
TraceInfoCommand TraceInfoCommand::singleton(
"traceinfo",
" rr traceinfo [<trace_dir>]\n"
" Dump trace header in JSON format.\n");
static int dump_trace_info(const string& trace_dir, FILE* out) {
int ret = 0;
TraceReader trace(trace_dir);
fputs("{\n", out);
const uint8_t* bytes = trace.uuid().bytes;
fputs(" \"uuid\":[", out);
for (size_t i = 0; i < sizeof(trace.uuid().bytes); ++i) {
if (i > 0) {
fputc(',', out);
}
fprintf(out, "%d", bytes[i]);
}
fputs("],\n", out);
fprintf(out, " \"xcr0\":%llu,\n", (unsigned long long)trace.xcr0());
fprintf(out, " \"bindToCpu\":%d,\n", trace.bound_to_cpu());
fprintf(out, " \"cpuidFaulting\":%s,\n", trace.uses_cpuid_faulting() ? "true" : "false");
fprintf(out, " \"requiredForwardCompatibilityVersion\":%d,\n", trace.required_forward_compatibility_version());
const char* semantics;
switch (trace.ticks_semantics()) {
case TICKS_RETIRED_CONDITIONAL_BRANCHES: semantics = "rcb"; break;
case TICKS_TAKEN_BRANCHES: semantics = "branches"; break;
default: semantics = "?"; break;
}
fprintf(out, " \"ticksSemantics\":\"%s\",\n", semantics);
fputs(" \"cpuidRecords\":[", out);
auto& records = trace.cpuid_records();
for (size_t i = 0; i < records.size(); ++i) {
if (i > 0) {
fputc(',', out);
}
auto& r = records[i];
fprintf(out, "\n [%u,%u,%u,%u,%u,%u]", r.eax_in, r.ecx_in,
r.out.eax, r.out.ebx, r.out.ecx, r.out.edx);
}
fputs("\n ],\n", out);
bool chaos_mode_known;
bool chaos_mode = trace.chaos_mode(&chaos_mode_known);
if (chaos_mode_known) {
fprintf(out, " \"chaosMode\":%s,\n", chaos_mode ? "true" : "false");
if (chaos_mode) {
MemoryRange exclusion_range = trace.exclusion_range();
fprintf(out, " \"exclusionRange\": { \"start\": %llu, \"end\": %llu },\n",
(unsigned long long)exclusion_range.start().as_int(),
(unsigned long long)exclusion_range.end().as_int());
}
}
ReplaySession::Flags flags;
flags.redirect_stdio = false;
flags.share_private_mappings = false;
flags.replay_stops_at_first_execve = true;
flags.cpu_unbound = true;
ReplaySession::shr_ptr replay_session = ReplaySession::create(trace_dir, flags);
while (true) {
auto result = replay_session->replay_step(RUN_CONTINUE);
if (replay_session->done_initial_exec()) {
fputs(" \"environ\":[", out);
auto environ = read_env(replay_session->current_task());
for (size_t i = 0; i < environ.size(); ++i) {
if (i > 0) {
fputc(',', out);
}
fprintf(out, "\n \"%s\"", json_escape(environ[i]).c_str());
}
fputs("\n ],\n", out);
fprintf(out, " \"program\": \"%s\"\n", json_escape(replay_session->vms()[0]->exe_image()).c_str());
break;
}
if (result.status == REPLAY_EXITED) {
fputs("Replay finished before initial exec!\n", stderr);
ret = 1;
break;
}
}
fputs("}\n", out);
return ret;
}
int TraceInfoCommand::run(vector<string>& args) {
// Various "cannot replay safely..." warnings cannot affect us since
// we only replay to the first execve.
Flags::get_for_init().suppress_environment_warnings = true;
while (parse_global_option(args)) {
}
string trace_dir;
if (!parse_optional_trace_dir(args, &trace_dir)) {
print_help(stderr);
return 1;
}
return dump_trace_info(trace_dir, stdout);
}
} // namespace rr