blob: 915c57c6bad4e51c4850d930c78fb76920314846 [file] [log] [blame]
// Copyright 2023 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.
library fuchsia.cpu.profiler;
using zx;
using fuchsia.component;
using fuchsia.url;
/// Curated set of platform independent IDs
type CounterId = flexible enum : uint8 {
/// A counter increments each nanosecond
NANOSECONDS = 0;
/// A counter increments each cpu cycle
CYCLES = 1;
/// A counter increments on each instruction retired
INSTRUCTIONS_RETIRED = 2;
/// A counter increments on each branch correctly predicted
BRANCHES_PREDICTED = 3;
/// A counter increments on each branch mispredicted
BRANCHES_MISPREDIDCTED = 4;
};
type Counter = flexible union {
/// Curated set of platform independent ids that get converted into the
/// appropriate hardware specific counter id.
1: platform_independent CounterId;
/// In the case a counter is not widely supported across
/// platform or not included in the curated set, one can
/// specify a platform dependent id that will not get
/// converted.
2: platform_dependent uint32;
};
/// Various approaches to obtaining
type CallgraphStrategy = flexible enum : uint8 {
/// Don't try to get a callgraph
NONE = 0;
/// Copy out stack memory and attempt to parse the DWARF info.
/// This is the slowest and most memory intensive of the approaches, but
/// gives the most accurate callgraphs. Parsing dwarf allows the callgraph
/// to trace inlined and leaf functions which other approaches do not
/// support.
DWARF = 1;
/// Intel CPUs support a hardware feature called Last Branch Records (LBR)s
/// which record up to the last 32 calls made depending on hardware version.
/// On supported CPUs, this is a low overhead approach to finding the
/// callgraph with the caveat that stacks that exceed 32 calls in depth will
/// overflow the hardware buffer.
LAST_BRANCH_RECORD = 2;
/// arm and riscv support shadow call stacks which are an efficient way for us
/// to find and copy out the call stack. However, leaf functions and code
/// compiled without shadow callstack support, such as the vdso will not
/// appear in the returned callgraph.
SHADOW_CALL_STACK = 3;
/// A general approach of parsing the frame pointers of the callstack. Frame
/// pointers are supported across architectures and are slightly more
/// expensive than the lbrs or shadow call stacks, but much cheaper than
/// parsing dwarf. Code compiled with omitted frame pointers and inlined
/// functions will not appear in the resulting stacks.
FRAME_POINTER = 4;
};
type CallgraphConfig = table {
1: strategy CallgraphStrategy;
};
type Sample = table {
/// Whether the profiler should include callgraph information and if so,
/// what strategy it should use.
1: callgraph CallgraphConfig;
/// The value of a counter since the last sample
2: counters vector<Counter>:64;
};
/// An adhoc set of running tasks to attach to.
type Task = flexible union {
/// Profile a process and all threads associated with it
1: process zx.Koid;
/// Profile a single thread
2: thread zx.Koid;
/// Profile a job and all processes and threads associated with it
3: job zx.Koid;
};
/// A test to launch and attach to
type TestConfig = table {
/// The url of the test to launch
1: url fuchsia.url.Url;
/// Test case filters to limit the test run to
2: filters vector<string:MAX>:MAX;
/// Optional realm in which the test will be run
3: realm string:fuchsia.component.MAX_MONIKER_LENGTH;
};
/// A component to optionally launch and attach to
type ComponentConfig = table {
/// The url of the component to launch
1: url fuchsia.url.Url;
/// If a component exists with the given moniker, attach to it, else launch
/// the component specified by the given url at the moniker
2: moniker string:fuchsia.component.MAX_MONIKER_LENGTH;
};
/// A target may be an adhoc set of tasks, or one defined by a component/test
type TargetConfig = flexible union {
/// An adhoc set of threads/processes/jobs
1: tasks vector<Task>:64;
/// Profile a component and all tasks it launches
2: component ComponentConfig;
/// Profile a test and all tasks it launches
3: test TestConfig;
};
/// Profiling information about the profile itself
type SamplingConfig = table {
/// How many ticks of the counter should occur between each sample
1: period uint64;
/// The counter to the rate on
2: timebase Counter;
/// What information the profiler will write when it takes
/// a sample.
3: sample Sample;
};
/// When a config has multiple SamplingConfigs, the session applies each
/// SamplingConfig to the TargetConfig.
type Config = table {
1: configs vector<SamplingConfig>:64;
2: target TargetConfig;
};
@discoverable
closed protocol Session {
/// Set up and prepare to profile
strict Configure(resource table {
1: output zx.Handle:SOCKET;
2: config Config;
}) -> () error flexible enum : uint32 {
BAD_SOCKET = 1;
BAD_STATE = 2;
INVALID_CONFIGURATION = 3;
};
/// Begin profiling and optionally begin writing data to the configured
/// socket
strict Start(table {
/// If true, delays writing data to the output socket until the profiling
/// session ends. This reduces overhead, but also caps the maximum output
/// size to the requested buffer size.
1: buffer_size_mb uint64;
2: buffer_results bool;
}) -> () error flexible enum : uint32 {
BAD_STATE = 1;
};
/// End the profiling session and write out data to the earlier
/// configured socket if not already writing.
strict Stop() -> (table {
1: samples_collected uint64;
2: mean_sample_time uint64;
3: median_sample_time uint64;
4: min_sample_time uint64;
5: max_sample_time uint64;
});
/// Clean up and unconfigure the profiler. Flushes the remaining data and
/// closes the provided socket. Doesn't return until all data has been
/// successfully written.
strict Reset() -> ();
};