blob: 09b25c401dd57724c68321d9b8d3ff8a7c21a735 [file] [log] [blame]
// Copyright 2019 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 SRC_PERFORMANCE_CPU_TRACE_PERF_MON_H_
#define SRC_PERFORMANCE_CPU_TRACE_PERF_MON_H_
#include <fidl/fuchsia.perfmon.cpu/cpp/wire.h>
#include <lib/zx/bti.h>
#include <stddef.h>
#include <stdint.h>
#include <threads.h>
#include <zircon/types.h>
#include <memory>
#if defined(__x86_64__)
#include <lib/zircon-internal/device/cpu-trace/intel-pm.h>
#include "intel-pm-impl.h"
#elif defined(__aarch64__)
#include <lib/zircon-internal/device/cpu-trace/arm64-pm.h>
#include "arm64-pm-impl.h"
#else
// include the arm files to provide type definitions
#include <lib/zircon-internal/device/cpu-trace/arm64-pm.h>
#include "unsupported-arch-impl.h"
#endif
namespace perfmon {
// The zx_mtrace_control syscall.
// A pointer to the syscall is provided during driver construction so that a fake version can
// be provided in the tests.
using mtrace_control_func_t = zx_status_t(zx_handle_t handle, uint32_t kind, uint32_t action,
uint32_t options, void* buf, size_t buf_size);
namespace fidl_perfmon = fuchsia_perfmon_cpu;
// Shorten some long FIDL names.
using FidlPerfmonAllocation = fidl_perfmon::wire::Allocation;
using FidlPerfmonConfig = fidl_perfmon::wire::Config;
using FidlPerfmonProperties = fidl_perfmon::wire::Properties;
using FidlPerfmonEventConfigFlags = fidl_perfmon::wire::EventConfigFlags;
#if defined(__x86_64__)
using PmuHwProperties = X86PmuProperties;
using PmuConfig = X86PmuConfig;
#elif defined(__aarch64__)
using PmuHwProperties = Arm64PmuProperties;
using PmuConfig = Arm64PmuConfig;
#else
// Use the arm versions just to provide type definitions
using PmuHwProperties = Arm64PmuProperties;
using PmuConfig = Arm64PmuConfig;
#endif
struct EventDetails {
// Ids are densely allocated. If ids get larger than this we will need
// a more complex id->event map.
uint16_t id;
uint32_t event;
#ifdef __x86_64__
uint32_t umask;
#endif
uint32_t flags;
};
// The minimum value of |PmuCommonProperties.pm_version| we support.
#if defined(__x86_64__)
constexpr uint16_t kMinPmVersion = 2;
#elif defined(__aarch64__)
// KISS and begin with pmu v3.
// Note: This should agree with the kernel driver's check.
constexpr uint16_t kMinPmVersion = 3;
#endif
// Compare function to qsort, bsearch.
int ComparePerfmonEventId(const void* ap, const void* bp);
// Return the largest event id in |events,count|.
uint16_t GetLargestEventId(const EventDetails* events, size_t count);
// Build a lookup map for |events,count|.
// The lookup map translates event ids, which is used as the index into the
// map and returns an enum value for the particular event kind.
// Event ids aren't necessarily dense, but the enums are.
zx_status_t BuildEventMap(const EventDetails* events, uint16_t count,
const uint16_t** out_event_map, size_t* out_map_size);
// All configuration data is staged here before writing any MSRs, etc.
// Then when ready the "Start" FIDL call will write all the necessary MSRS,
// and do whatever kernel operations are required for collecting data.
struct PmuPerTraceState {
// True if |config| has been set.
bool configured;
// The trace configuration as given to us via FIDL.
FidlPerfmonConfig fidl_config;
// The internalized form of |FidlPerfmonConfig| that we pass to the kernel.
PmuConfig config;
// # of entries in |buffers|.
// TODO(dje): This is generally the number of cpus, but it could be
// something else later.
uint32_t num_buffers;
// The size of each buffer in 4K pages.
// Each buffer is the same size (at least for now, KISS).
// There is one buffer per cpu.
uint32_t buffer_size_in_pages;
std::vector<zx::vmo> buffers;
std::vector<std::pair<zx_vaddr_t, size_t>> mappings;
};
// Devhost interface.
class PerfmonController : public fidl::WireServer<fidl_perfmon::Controller> {
public:
// The page size we use.
static constexpr uint32_t kLog2PageSize = 12;
static constexpr uint32_t kPageSize = 1 << kLog2PageSize;
// maximum space, in pages, for trace buffers (per cpu)
static constexpr uint32_t kMaxPerTraceSpaceInPages = (256 * 1024 * 1024) / kPageSize;
// Fetch the pmu hw properties from the kernel.
static zx_status_t GetHwProperties(mtrace_control_func_t* mtrace_control,
PmuHwProperties* out_props,
const zx::unowned_resource& debug_resource);
// Architecture-provided routine to initialize static state.
// TODO(dje): Move static state to the device object for better testability.
static zx_status_t InitOnce();
explicit PerfmonController(perfmon::PmuHwProperties props, mtrace_control_func_t* mtrace_control,
zx::unowned_resource debug_resource);
~PerfmonController() override = default;
const PmuHwProperties& pmu_hw_properties() const { return pmu_hw_properties_; }
// Handlers for each of the operations.
void PmuGetProperties(FidlPerfmonProperties* props) const;
zx_status_t PmuInitialize(const FidlPerfmonAllocation* allocation);
void PmuTerminate();
zx_status_t PmuGetAllocation(FidlPerfmonAllocation* allocation);
zx_status_t PmuGetBufferHandle(uint32_t descriptor, zx_handle_t* out_handle);
zx_status_t PmuStageConfig(const FidlPerfmonConfig* config);
zx_status_t PmuGetConfig(FidlPerfmonConfig* config);
zx_status_t PmuStart();
void PmuStop();
// FIDL server methods
void GetProperties(GetPropertiesCompleter::Sync& completer) override;
void Initialize(InitializeRequestView request, InitializeCompleter::Sync& completer) override;
void Terminate(TerminateCompleter::Sync& completer) override;
void GetAllocation(GetAllocationCompleter::Sync& completer) override;
void StageConfig(StageConfigRequestView request, StageConfigCompleter::Sync& completer) override;
void GetConfig(GetConfigCompleter::Sync& completer) override;
void GetBufferHandle(GetBufferHandleRequestView request,
GetBufferHandleCompleter::Sync& completer) override;
void Start(StartCompleter::Sync& completer) override;
void Stop(StopCompleter::Sync& completer) override;
private:
static void FreeBuffersForTrace(PmuPerTraceState* per_trace, uint32_t num_allocated);
// Architecture-provided helpers for |PmuStageConfig()|.
// Initialize |ss| in preparation for processing the PMU configuration.
void InitializeStagingState(StagingState* ss) const;
// Stage fixed counter |input_index| in |icfg|.
zx_status_t StageFixedConfig(const FidlPerfmonConfig* icfg, StagingState* ss, size_t input_index,
PmuConfig* ocfg) const;
// Stage fixed counter |input_index| in |icfg|.
zx_status_t StageProgrammableConfig(const FidlPerfmonConfig* icfg, StagingState* ss,
size_t input_index, PmuConfig* ocfg) const;
// Stage fixed counter |input_index| in |icfg|.
static zx_status_t StageMiscConfig(const FidlPerfmonConfig* icfg, StagingState* ss,
size_t input_index, PmuConfig* ocfg);
// Verify the result. This is where the architecture can do any last
// minute verification.
zx_status_t VerifyStaging(StagingState* ss, PmuConfig* ocfg);
// End of architecture-provided helpers.
// Properties of the PMU computed when the device driver is loaded.
const PmuHwProperties pmu_hw_properties_;
// The zx_mtrace_control() syscall to use. In the real device this is the syscall itself.
// In tests it is replaced with something suitable for the test.
mtrace_control_func_t* const mtrace_control_;
// Once tracing has started various things are not allowed until it stops.
bool active_ = false;
// one entry for each trace
// TODO(dje): At the moment we only support one trace at a time.
// "trace" == "data collection run"
std::unique_ptr<PmuPerTraceState> per_trace_state_;
zx::unowned_resource debug_resource_;
};
} // namespace perfmon
#endif // SRC_PERFORMANCE_CPU_TRACE_PERF_MON_H_