blob: 9141221ed42c7d9c36a1c6a1d1cf2d05ecfc21b7 [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_LIB_CPUPERF_READER_H_
#define GARNET_LIB_CPUPERF_READER_H_
#include <lib/zx/vmar.h>
#include <lib/zx/vmo.h>
#include <zircon/device/cpu-trace/cpu-perf.h>
#include <zircon/types.h>
#include "lib/fxl/macros.h"
namespace cpuperf {
class Reader {
public:
// When reading sample data, the record we read is one of these.
// To avoid unnecessary copying of the larger records we just return a
// pointer to the record, which will remain valid until the next record
// is read.
union SampleRecord {
const cpuperf_record_header_t* header;
const cpuperf_time_record_t* time;
const cpuperf_tick_record_t* tick;
const cpuperf_count_record_t* count;
const cpuperf_value_record_t* value;
const cpuperf_pc_record_t* pc;
// Ideally this would return the enum type, but we don't make any
// assumptions about the validity of the trace data.
uint8_t type() const { return header->type; }
cpuperf_event_id_t event() const { return header->event; }
};
// |fd| is borrowed.
Reader(int fd, uint32_t buffer_size);
bool is_valid() { return vmar_.is_valid(); }
uint32_t num_cpus() const { return num_cpus_; }
// The returned value is zero until the first call to ReadNextRecord(),
// after which it contains the value used by the trace.
// Note: The returned value could be bogus, including zero.
// We just pass on what the trace told us.
uint64_t ticks_per_second() const { return ticks_per_second_; }
// Return the current time, in ticks, based on the last time record read.
zx_time_t time() const { return time_; }
bool GetProperties(cpuperf_properties_t* props);
bool GetConfig(cpuperf_config_t* config);
// Read the next record.
// Note: To avoid unnecessary copying of larger records, the result contains
// a pointer to the record. Such pointers remain valid until the next call.
bool ReadNextRecord(uint32_t* cpu, SampleRecord* record);
// Returns IPM_RECORD_RESERVED for an invalid record type.
static cpuperf_record_type_t RecordType(const cpuperf_record_header_t* hdr);
// Returns 0 for an invalid record type or invalid record.
static size_t RecordSize(const cpuperf_record_header_t* hdr);
private:
bool MapBufferVmo(zx_handle_t vmo);
int fd_; // borrowed
const uint32_t buffer_size_;
const uint32_t num_cpus_;
uint32_t current_cpu_ = 0;
zx::vmar vmar_;
zx::vmo current_vmo_;
const uint8_t* buffer_start_ = nullptr;
const uint8_t* next_record_ = nullptr;
const uint8_t* capture_end_ = nullptr;
// Reading of one trace can span multiple cpus, and the ticks-per-second
// value comes from each cpu's trace. Generally it's all the same value,
// but there is no uber record to specify that. zx_ticks_per_second() will
// return a constant value (though not necessarily the same value on each
// boot), and it's this value we expect in the trace. OTOH, we use what
// the trace buffer gives us. We don't want each record to encode its own
// value, so keep track of the value here.
uint64_t ticks_per_second_ = 0;
// The time from the last CPUPERF_RECORD_TIME record read.
zx_time_t time_ = 0;
FXL_DISALLOW_COPY_AND_ASSIGN(Reader);
};
} // namespace cpuperf
#endif // GARNET_LIB_CPUPERF_READER_H_