blob: 546ff7bfd81b261a5c0fba1f024b7b851eacb41a [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.
#ifndef GARNET_BIN_CPUPERF_PROVIDER_IMPORTER_H_
#define GARNET_BIN_CPUPERF_PROVIDER_IMPORTER_H_
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <unordered_map>
#include <src/lib/fxl/logging.h>
#include <src/lib/fxl/macros.h>
#include <lib/zircon-internal/device/cpu-trace/perf-mon.h>
#include <trace-engine/context.h>
#include "garnet/bin/cpuperf_provider/categories.h"
#include "garnet/lib/perfmon/reader.h"
#include "garnet/lib/perfmon/events.h"
namespace cpuperf_provider {
class Importer {
public:
Importer(trace_context* context, const TraceConfig* trace_config,
trace_ticks_t start_time, trace_ticks_t stop_time);
~Importer();
bool Import(perfmon::Reader& reader, const perfmon::Config& perfmon_config);
private:
static constexpr size_t kMaxNumCpus = 32;
static_assert(kMaxNumCpus <= TRACE_ENCODED_THREAD_REF_MAX_INDEX,
"bad value for kMaxNumCpus");
class EventTracker final {
public:
EventTracker(trace_ticks_t start_time) : start_time_(start_time) {}
bool HaveValue(unsigned cpu, perfmon::EventId id) const {
Key key = GenKey(cpu, id);
EventData::const_iterator iter = data_.find(key);
return iter != data_.end();
}
void UpdateTime(unsigned cpu, perfmon::EventId id, trace_ticks_t time) {
Key key = GenKey(cpu, id);
data_[key].time = time;
}
trace_ticks_t GetTime(unsigned cpu, perfmon::EventId id) const {
Key key = GenKey(cpu, id);
EventData::const_iterator iter = data_.find(key);
if (iter == data_.end())
return start_time_;
return iter->second.time;
}
void UpdateValue(unsigned cpu, perfmon::EventId id, uint64_t value) {
Key key = GenKey(cpu, id);
data_[key].is_value = true;
data_[key].count_or_value = value;
}
void AccumulateCount(unsigned cpu, perfmon::EventId id, uint64_t value) {
Key key = GenKey(cpu, id);
data_[key].is_value = false;
data_[key].count_or_value += value;
}
bool IsValue(unsigned cpu, perfmon::EventId id) const {
Key key = GenKey(cpu, id);
EventData::const_iterator iter = data_.find(key);
FXL_DCHECK(iter != data_.end());
return iter->second.is_value;
}
uint64_t GetCountOrValue(unsigned cpu, perfmon::EventId id) const {
Key key = GenKey(cpu, id);
EventData::const_iterator iter = data_.find(key);
if (iter == data_.end())
return 0;
return iter->second.count_or_value;
}
private:
using Key = uint32_t;
struct Data {
trace_ticks_t time = 0;
// false -> count (perfmon::kRecordTypeCount),
// true -> value (perfmon::kRecordTypeValue).
bool is_value;
// This is either a count or a value.
// Records for any particular event should only be using one
// of |perfmon::kRecordType{Count,Value}|.
uint64_t count_or_value = 0;
};
using EventData = std::unordered_map<Key, Data>;
Key GenKey(unsigned cpu, perfmon::EventId id) const {
FXL_DCHECK(cpu < kMaxNumCpus);
static_assert(sizeof(id) == 2, "");
return (cpu << 16) | id;
}
const trace_ticks_t start_time_;
EventData data_;
};
uint64_t ImportRecords(perfmon::Reader& reader,
const perfmon::Config& perfmon_config);
void ImportSampleRecord(trace_cpu_number_t cpu,
const perfmon::SampleRecord& record,
trace_ticks_t previous_time,
trace_ticks_t current_time, uint64_t ticks_per_second,
uint64_t event_value);
void EmitSampleRecord(trace_cpu_number_t cpu,
const perfmon::EventDetails* details,
const perfmon::SampleRecord& record,
trace_ticks_t start_time, trace_ticks_t end_time,
uint64_t ticks_per_second, uint64_t value);
void EmitLastBranchRecordBlob(trace_cpu_number_t cpu,
const perfmon::SampleRecord& record,
trace_ticks_t time);
void EmitTallyCounts(perfmon::Reader& reader,
const perfmon::Config& perfmon_config,
const EventTracker& event_data);
void EmitTallyRecord(trace_cpu_number_t cpu, perfmon::EventId event_id,
trace_ticks_t time, bool is_value, uint64_t value);
trace_string_ref_t GetCpuNameRef(trace_cpu_number_t cpu);
trace_thread_ref_t GetCpuThreadRef(trace_cpu_number_t cpu,
perfmon::EventId id);
trace_context* const context_;
const TraceConfig* trace_config_;
trace_ticks_t start_time_;
trace_ticks_t stop_time_;
trace_string_ref_t const cpu_string_ref_;
// Our use of the "category" argument to trace_context_write_* functions
// is a bit abnormal. The argument "should" be the name of the category
// the user provided. However, users can select individual events or
// collections of events and the mapping from user-provided category
// name to our output is problematic. So just use a single category to
// encompass all of them ("cpu:perf") and use the name argument to
// identify each event.
trace_string_ref_t const cpuperf_category_ref_;
trace_string_ref_t const count_name_ref_;
trace_string_ref_t const value_name_ref_;
trace_string_ref_t const rate_name_ref_;
trace_string_ref_t const aspace_name_ref_;
trace_string_ref_t const pc_name_ref_;
// Add one for events that are system-wide (e.g., memory controller events).
trace_thread_ref_t cpu_thread_refs_[kMaxNumCpus + 1];
// Add one for events that are system-wide (e.g., memory controller events).
trace_string_ref_t cpu_name_refs_[kMaxNumCpus + 1];
FXL_DISALLOW_COPY_AND_ASSIGN(Importer);
};
} // namespace cpuperf_provider
#endif // GARNET_BIN_CPUPERF_PROVIDER_IMPORTER_H_