blob: 0a05baa79968d1def7ed01ec76c8639e2abd0c2c [file] [log] [blame]
// Copyright 2018 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 <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
#include <functional>
#include <limits>
#include <locale>
#include <sstream>
#include <unordered_map>
#include <vector>
#include <src/lib/fxl/command_line.h>
#include "src/lib/files/file.h"
#include <src/lib/fxl/log_settings.h>
#include <src/lib/fxl/log_settings_command_line.h>
#include <src/lib/fxl/logging.h>
#include <lib/zircon-internal/device/cpu-trace/perf-mon.h>
#include <zircon/syscalls.h>
#include "garnet/lib/debugger_utils/util.h"
#include "garnet/lib/perfmon/controller.h"
#include "garnet/lib/perfmon/events.h"
#include "session_spec.h"
#include "session_result_spec.h"
// Allow space for 999,999,999.
// This is |int| as it is used as the width arg to fprintf.
constexpr int kMinColumnWidth = 11;
// Width of the first column, which is trace names.
// This is |int| as it is used as the width arg to fprintf.
constexpr int kTraceNameColumnWidth = sizeof("Trace NNN:") - 1;
struct EventColumn {
const char* name;
int width;
};
using SessionColumns = std::unordered_map<perfmon::EventId, EventColumn>;
struct EventResult {
uint64_t value_or_count;
};
using TraceResults = std::unordered_map<perfmon::EventId, EventResult>;
// Indexed by trace number.
using SessionResults = std::vector<TraceResults>;
using IterateFunc = std::function<void(perfmon::EventId event,
const perfmon::EventDetails* details)>;
void IterateOverEventIds(const cpuperf::SessionSpec& spec,
const perfmon::ModelEventManager* model_event_manager,
IterateFunc func) {
perfmon::Config::IterateFunc iterate_helper =
[&model_event_manager, &func](const perfmon::Config::EventConfig& event) {
const perfmon::EventDetails* details = nullptr;
if (!model_event_manager->EventIdToEventDetails(event.event, &details)) {
// This shouldn't happen, but let |func| decide what to do.
}
func(event.event, details);
};
spec.perfmon_config.IterateOverEvents(iterate_helper);
}
static SessionColumns BuildSessionColumns(
const cpuperf::SessionSpec& spec,
const perfmon::ModelEventManager* model_event_manager) {
SessionColumns columns;
IterateOverEventIds(spec, model_event_manager,
[&columns] (perfmon::EventId id,
const perfmon::EventDetails* details) {
const char* name;
if (details) {
name = details->name;
} else {
// This shouldn't happen, but better to print what we have.
name = "Unknown";
}
size_t len = strlen(name);
FXL_DCHECK(len <= std::numeric_limits<int>::max());
int int_len = static_cast<int>(len);
int width = std::max(int_len, kMinColumnWidth);
columns[id] = EventColumn{ .name = name, .width = width };
});
return columns;
}
// Data is printed in the order it appears in |spec|.
static void PrintColumnTitles(FILE* f, const cpuperf::SessionSpec& spec,
const perfmon::ModelEventManager* model_event_manager,
const SessionColumns& columns) {
fprintf(f, "%*s", kTraceNameColumnWidth, "");
IterateOverEventIds(spec, model_event_manager,
[&] (perfmon::EventId id,
const perfmon::EventDetails* details) {
auto iter = columns.find(id);
FXL_DCHECK(iter != columns.end());
const EventColumn& column = iter->second;
fprintf(f, "|%*s", column.width, column.name);
});
fprintf(f, "\n");
}
static void PrintTrace(FILE* f, const cpuperf::SessionSpec& spec,
const cpuperf::SessionResultSpec& result_spec,
const perfmon::ModelEventManager* model_event_manager,
const SessionColumns& columns, uint32_t trace_num,
const TraceResults& results) {
char label[kTraceNameColumnWidth + 1];
snprintf(&label[0], sizeof(label), "Trace %u:", trace_num);
fprintf(f, "%-*s", kTraceNameColumnWidth, label);
IterateOverEventIds(spec, model_event_manager,
[&] (perfmon::EventId id,
const perfmon::EventDetails* details) {
auto column_iter = columns.find(id);
FXL_DCHECK(column_iter != columns.end());
const EventColumn& column = column_iter->second;
auto result_iter = results.find(id);
if (result_iter != results.end()) {
const EventResult& result = result_iter->second;
std::stringstream ss;
// Print 123456 as 123,456 (locale appropriate).
struct threes : std::numpunct<char> {
std::string do_grouping() const { return "\3"; }
};
ss.imbue(std::locale(ss.getloc(), new threes));
ss << result.value_or_count;
fprintf(f, "|%*s", column.width, ss.str().c_str());
} else {
// Misc events might not be present in all traces.
// Just print blanks.
// TODO(dje): Distinguish such properties in EventDetails?
fprintf(f, "|%*s", column.width, "");
}
});
fprintf(f, "\n");
}
void PrintTallyResults(FILE* f, const cpuperf::SessionSpec& spec,
const cpuperf::SessionResultSpec& result_spec,
const perfmon::ModelEventManager* model_event_manager,
perfmon::Controller* controller) {
std::unique_ptr<perfmon::Reader> reader = controller->GetReader();
if (!reader) {
return;
}
SessionColumns columns = BuildSessionColumns(spec, model_event_manager);
SessionResults results;
for (size_t i = 0; i < result_spec.num_traces; ++i) {
results.emplace_back(TraceResults{});
}
constexpr uint32_t kCurrentTraceUnset = ~0;
uint32_t current_trace = kCurrentTraceUnset;
uint32_t trace;
perfmon::SampleRecord record;
perfmon::ReaderStatus status;
while ((status = reader->ReadNextRecord(&trace, &record)) ==
perfmon::ReaderStatus::kOk) {
if (trace != current_trace) {
current_trace = trace;
}
if (record.header->event == 0)
continue;
perfmon::EventId id = record.header->event;
const perfmon::EventDetails* details;
if (!model_event_manager->EventIdToEventDetails(id, &details)) {
FXL_LOG(WARNING) << "Unknown event: 0x" << std::hex
<< record.header->event;
continue;
}
switch (record.type()) {
case perfmon::kRecordTypeCount:
results[current_trace][id] = EventResult{record.count->count};
break;
case perfmon::kRecordTypeValue:
results[current_trace][id] = EventResult{record.value->value};
break;
default:
break;
}
}
PrintColumnTitles(f, spec, model_event_manager, columns);
for (uint32_t i = 0; i < result_spec.num_traces; ++i) {
PrintTrace(f, spec, result_spec, model_event_manager, columns, i,
results[i]);
}
}