| // 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 <stddef.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include <string> |
| #include <vector> |
| |
| #include <lib/fxl/command_line.h> |
| #include <lib/fxl/log_settings.h> |
| #include <lib/fxl/logging.h> |
| #include <lib/fxl/strings/string_number_conversions.h> |
| #include <lib/fxl/strings/string_printf.h> |
| |
| #include "garnet/lib/cpuperf/file_reader.h" |
| |
| #include "raw_printer.h" |
| |
| namespace cpuperf { |
| |
| bool RawPrinter::Create( |
| const SessionResultSpec* session_result_spec, const Config& config, |
| std::unique_ptr<RawPrinter>* out_printer) { |
| const std::string& output_file_name = config.output_file_name; |
| FILE* out_file = stdout; |
| if (output_file_name != "") { |
| out_file = fopen(output_file_name.c_str(), "w"); |
| if (!out_file) { |
| FXL_LOG(ERROR) << "Unable to open file for writing: " |
| << output_file_name; |
| return false; |
| } |
| } |
| |
| out_printer->reset(new RawPrinter(out_file, session_result_spec, config)); |
| return true; |
| } |
| |
| RawPrinter::RawPrinter(FILE* out_file, |
| const SessionResultSpec* session_result_spec, |
| const Config& config) |
| : out_file_(out_file), |
| session_result_spec_(session_result_spec), |
| config_(config) {} |
| |
| RawPrinter::~RawPrinter() { |
| if (config_.output_file_name != "") |
| fclose(out_file_); |
| } |
| |
| // TODO(dje): attribute format |
| void RawPrinter::Printf(const char* format, ...) { |
| va_list args; |
| va_start(args, format); |
| vfprintf(out_file_, format, args); |
| va_end(args); |
| } |
| |
| void RawPrinter::PrintHeader(const SampleRecord& record) { |
| // There's no need to print the type here, caller does that. |
| Printf("Event 0x%x", record.header->event); |
| } |
| |
| void RawPrinter::PrintTimeRecord(const SampleRecord& record) { |
| Printf("Time: %" PRIu64 "\n", record.time->time); |
| } |
| |
| void RawPrinter::PrintTickRecord(const SampleRecord& record) { |
| Printf("Tick: "); |
| PrintHeader(record); |
| Printf("\n"); |
| } |
| |
| void RawPrinter::PrintCountRecord(const SampleRecord& record) { |
| Printf("Count: "); |
| PrintHeader(record); |
| Printf(", %" PRIu64 "\n", record.count->count); |
| } |
| |
| void RawPrinter::PrintValueRecord(const SampleRecord& record) { |
| Printf("Value: "); |
| PrintHeader(record); |
| Printf(", %" PRIu64 "\n", record.value->value); |
| } |
| |
| void RawPrinter::PrintPcRecord(const SampleRecord& record) { |
| Printf("PC: "); |
| PrintHeader(record); |
| Printf(", aspace 0x%" PRIx64 ", pc 0x%" PRIx64 "\n", |
| record.pc->aspace, record.pc->pc); |
| } |
| |
| void RawPrinter::PrintLastBranchRecord(const SampleRecord& record) { |
| Printf("LastBranch: "); |
| PrintHeader(record); |
| Printf(", aspace 0x%" PRIx64 ", %u branches\n", |
| record.last_branch->aspace, record.last_branch->num_branches); |
| // TODO(dje): Print each branch, but it's a lot so maybe only if verbose? |
| } |
| |
| uint64_t RawPrinter::PrintOneTrace(uint32_t iter_num) { |
| uint64_t total_records = 0; |
| |
| auto get_file_name = [this, &iter_num] (uint32_t trace_num) -> std::string { |
| return session_result_spec_->GetTraceFilePath(iter_num, trace_num); |
| }; |
| |
| std::unique_ptr<FileReader> reader; |
| if (!FileReader::Create(get_file_name, |
| session_result_spec_->num_traces, |
| &reader)) { |
| return 0; |
| } |
| |
| uint32_t current_trace = ~0; |
| |
| uint32_t trace; |
| SampleRecord record; |
| while (reader->ReadNextRecord(&trace, &record) == ReaderStatus::kOk) { |
| ++total_records; |
| |
| if (trace != current_trace) { |
| current_trace = trace; |
| Printf("\nTrace %u\n", current_trace); |
| // No, the number of -s doesn't line up, it's close enough. |
| Printf("--------\n"); |
| } |
| |
| Printf("%04zx: ", reader->GetLastRecordOffset()); |
| |
| switch (record.type()) { |
| case CPUPERF_RECORD_TIME: |
| PrintTimeRecord(record); |
| break; |
| case CPUPERF_RECORD_TICK: |
| PrintTickRecord(record); |
| break; |
| case CPUPERF_RECORD_COUNT: |
| PrintCountRecord(record); |
| break; |
| case CPUPERF_RECORD_VALUE: |
| PrintValueRecord(record); |
| break; |
| case CPUPERF_RECORD_PC: |
| PrintPcRecord(record); |
| break; |
| case CPUPERF_RECORD_LAST_BRANCH: |
| PrintLastBranchRecord(record); |
| break; |
| default: |
| // The reader shouldn't be returning unknown records. |
| FXL_NOTREACHED(); |
| break; |
| } |
| } |
| |
| return total_records; |
| } |
| |
| uint64_t RawPrinter::PrintFiles() { |
| uint64_t total_records = 0; |
| |
| for (uint32_t iter = 0; |
| iter < session_result_spec_->num_iterations; |
| ++iter) { |
| Printf("\nIteration %u\n", iter); |
| // No, the number of =s doesn't line up, it's close enough. |
| Printf("==============\n"); |
| total_records += PrintOneTrace(iter); |
| } |
| |
| Printf("\n"); |
| |
| return total_records; |
| } |
| |
| } // namespace cpuperf |