Struct Profile

Source
pub struct Profile { /* private fields */ }
Expand description

Stores the profile data and can be serialized as JSON, via serde::Serialize.

The profile data is organized into a list of processes with threads. Each thread has its own samples and markers.

use fxprof_processed_profile::{Profile, CategoryHandle, CpuDelta, Frame, FrameInfo, FrameFlags, SamplingInterval, Timestamp};
use std::time::SystemTime;

let mut profile = Profile::new("My app", SystemTime::now().into(), SamplingInterval::from_millis(1));
let process = profile.add_process("App process", 54132, Timestamp::from_millis_since_reference(0.0));
let thread = profile.add_thread(process, 54132000, Timestamp::from_millis_since_reference(0.0), true);
profile.set_thread_name(thread, "Main thread");
let stack_frames = vec![
    FrameInfo { frame: Frame::Label(profile.intern_string("Root node")), category_pair: CategoryHandle::OTHER.into(), flags: FrameFlags::empty() },
    FrameInfo { frame: Frame::Label(profile.intern_string("First callee")), category_pair: CategoryHandle::OTHER.into(), flags: FrameFlags::empty() }
];
let stack = profile.intern_stack_frames(thread, stack_frames.into_iter());
profile.add_sample(thread, Timestamp::from_millis_since_reference(0.0), stack, CpuDelta::ZERO, 1);

let writer = std::io::BufWriter::new(output_file);
serde_json::to_writer(writer, &profile)?;

Implementations§

Source§

impl Profile

Source

pub fn new( product: &str, reference_timestamp: ReferenceTimestamp, interval: SamplingInterval, ) -> Self

Create a new profile.

The product is the name of the main application which was profiled. The reference_timestamp is some arbitrary absolute timestamp which all other timestamps in the profile data are relative to. The interval is the intended time delta between samples.

Source

pub fn set_interval(&mut self, interval: SamplingInterval)

Change the declared sampling interval.

Source

pub fn set_reference_timestamp( &mut self, reference_timestamp: ReferenceTimestamp, )

Change the reference timestamp.

Source

pub fn set_product(&mut self, product: &str)

Change the product name.

Source

pub fn set_os_name(&mut self, os_name: &str)

Set the name of the operating system.

Source

pub fn add_category( &mut self, name: &str, color: CategoryColor, ) -> CategoryHandle

Add a category and return its handle.

Categories are used for stack frames and markers, as part of a “category pair”.

Source

pub fn add_subcategory( &mut self, category: CategoryHandle, name: &str, ) -> CategoryPairHandle

Add a subcategory for a category, and return the “category pair” handle.

Every category has a default subcategory; you can convert a Category into its corresponding CategoryPairHandle for the default category using category.into().

Source

pub fn add_process( &mut self, name: &str, pid: u32, start_time: Timestamp, ) -> ProcessHandle

Add an empty process. The name, pid and start time can be changed afterwards, but they are required here because they have to be present in the profile JSON.

Source

pub fn add_counter( &mut self, process: ProcessHandle, name: &str, category: &str, description: &str, ) -> CounterHandle

Create a counter. Counters let you make graphs with a time axis and a Y axis. One example of a counter is memory usage.

§Example
use fxprof_processed_profile::{Profile, CategoryHandle, CpuDelta, Frame, SamplingInterval, Timestamp};
use std::time::SystemTime;

let mut profile = Profile::new("My app", SystemTime::now().into(), SamplingInterval::from_millis(1));
let process = profile.add_process("App process", 54132, Timestamp::from_millis_since_reference(0.0));
let memory_counter = profile.add_counter(process, "malloc", "Memory", "Amount of allocated memory");
profile.add_counter_sample(memory_counter, Timestamp::from_millis_since_reference(0.0), 0.0, 0);
profile.add_counter_sample(memory_counter, Timestamp::from_millis_since_reference(1.0), 1000.0, 2);
profile.add_counter_sample(memory_counter, Timestamp::from_millis_since_reference(2.0), 800.0, 1);
Source

pub fn set_counter_color(&mut self, counter: CounterHandle, color: GraphColor)

Set the color to use when rendering the counter.

Source

pub fn set_process_start_time( &mut self, process: ProcessHandle, start_time: Timestamp, )

Change the start time of a process.

Source

pub fn set_process_end_time( &mut self, process: ProcessHandle, end_time: Timestamp, )

Set the end time of a process.

Source

pub fn set_process_name(&mut self, process: ProcessHandle, name: &str)

Change the name of a process.

Source

pub fn add_lib(&mut self, library: LibraryInfo) -> LibraryHandle

Get the LibraryHandle for a library. This handle is used in Profile::add_lib_mapping and in the pre-resolved Frame variants.

Knowing the library information allows symbolication of native stacks once the profile is opened in the Firefox Profiler.

Source

pub fn set_lib_symbol_table( &mut self, library: LibraryHandle, symbol_table: Arc<SymbolTable>, )

Set the symbol table for a library.

This symbol table can also be specified in the LibraryInfo which is given to Profile::add_lib. However, sometimes you may want to have the LibraryHandle for a library before you know about all its symbols. In those cases, you can call Profile::add_lib with symbol_table set to None, and then supply the symbol table afterwards.

Symbol tables are optional.

Source

pub fn add_lib_mapping( &mut self, process: ProcessHandle, lib: LibraryHandle, start_avma: u64, end_avma: u64, relative_address_at_start: u32, )

For a given process, define where in the virtual memory of this process the given library is mapped.

Existing mappings which overlap with the range start_avma..end_avma will be removed.

A single library can have multiple mappings in the same process.

The new mapping will be respected by future Profile::add_sample calls, when resolving absolute frame addresses to library-relative addresses.

Source

pub fn remove_lib_mapping(&mut self, process: ProcessHandle, start_avma: u64)

Mark the library mapping at the specified start address in the specified process as unloaded, so that future calls to Profile::add_sample know about the removal.

Source

pub fn clear_process_lib_mappings(&mut self, process: ProcessHandle)

Clear all library mappings in the specified process.

Source

pub fn add_kernel_lib_mapping( &mut self, lib: LibraryHandle, start_avma: u64, end_avma: u64, relative_address_at_start: u32, )

Add a kernel library mapping. This allows symbolication of kernel stacks once the profile is opened in the Firefox Profiler. Kernel libraries are global and not tied to a process.

Each kernel library covers an address range in the kernel address space, which is global across all processes. Future calls to Profile::add_sample with native frames resolve the frame’s code address with respect to the currently loaded kernel and process libraries.

Source

pub fn remove_kernel_lib_mapping(&mut self, start_avma: u64)

Mark the kernel library at the specified start address as unloaded, so that future calls to Profile::add_sample know about the unloading.

Source

pub fn add_thread( &mut self, process: ProcessHandle, tid: u32, start_time: Timestamp, is_main: bool, ) -> ThreadHandle

Add an empty thread to the specified process.

Source

pub fn set_thread_name(&mut self, thread: ThreadHandle, name: &str)

Change the name of a thread.

Source

pub fn set_thread_start_time( &mut self, thread: ThreadHandle, start_time: Timestamp, )

Change the start time of a thread.

Source

pub fn set_thread_end_time(&mut self, thread: ThreadHandle, end_time: Timestamp)

Set the end time of a thread.

Source

pub fn set_thread_tid(&mut self, thread: ThreadHandle, tid: u32)

Set the tid (thread ID) of a thread.

Source

pub fn set_thread_show_markers_in_timeline( &mut self, thread: ThreadHandle, v: bool, )

Set whether to show a timeline which displays MarkerLocations::TIMELINE_OVERVIEW markers for this thread.

Main threads always have such a timeline view and always display such markers, but non-main threads only do so when specified using this method.

Source

pub fn set_thread_samples_weight_type( &mut self, thread: ThreadHandle, t: WeightType, )

Set the weighting type of samples of a thread.

Default is WeightType::Samples.

Source

pub fn add_initial_visible_thread(&mut self, thread: ThreadHandle)

Add a thread as initially visible in the UI.

If not called, the UI uses its own ranking heuristic to choose which threads are visible.

Source

pub fn clear_initial_visible_threads(&mut self)

Clear the list of threads marked as initially visible in the UI.

Source

pub fn add_initial_selected_thread(&mut self, thread: ThreadHandle)

Add a thread as initially selected in the UI.

If not called, the UI uses its own heuristic to choose which threads are initially selected.

Source

pub fn clear_initial_selected_threads(&mut self)

Clear the list of threads marked as initially selected in the UI.

Source

pub fn intern_string(&mut self, s: &str) -> StringHandle

Turn the string into in a StringHandle, for use in Frame::Label.

Source

pub fn get_string(&self, handle: StringHandle) -> &str

Get the string for a string handle. This is sometimes useful when writing tests.

Panics if the handle wasn’t found, which can happen if you pass a handle from a different Profile instance.

Source

pub fn intern_frame( &mut self, thread: ThreadHandle, frame_info: FrameInfo, ) -> FrameHandle

Get the frame handle for a stack frame.

The returned handle can only be used with this thread.

Source

pub fn intern_stack( &mut self, thread: ThreadHandle, parent: Option<StackHandle>, frame: FrameHandle, ) -> StackHandle

Get the stack handle for a stack with the given frame and parent, for the given thread.

The returned stack handle can be used with Profile::add_sample and Profile::set_marker_stack, but only for samples / markers of the same thread.

If parent is None, this creates a root stack node. Otherwise, parent is the caller of the returned stack node.

Source

pub fn intern_stack_frames( &mut self, thread: ThreadHandle, frames: impl Iterator<Item = FrameInfo>, ) -> Option<StackHandle>

Get the stack handle for a stack whose frames are given by an iterator.

The stack frames yielded by the iterator need to be ordered from caller-most to callee-most.

Returns None if the stack has zero frames.

Source

pub fn add_sample( &mut self, thread: ThreadHandle, timestamp: Timestamp, stack: Option<StackHandle>, cpu_delta: CpuDelta, weight: i32, )

Add a sample to the given thread.

The sample has a timestamp, a stack, a CPU delta, and a weight.

To get the stack handle, you can use Profile::intern_stack or Profile::intern_stack_frames.

The CPU delta is the amount of CPU time that the CPU was busy with work for this thread since the previous sample. It should always be less than or equal the time delta between the sample timestamps.

The weight affects the sample’s stack’s score in the call tree. You usually set this to 1. You can use weights greater than one if you want to combine multiple adjacent samples with the same stack into one sample, to save space. However, this discards any CPU deltas between the adjacent samples, so it’s only really useful if no CPU time has occurred between the samples, and for that use case the Profile::add_sample_same_stack_zero_cpu method should be preferred.

You can can also set the weight to something negative, such as -1, to create a “diff profile”. For example, if you have partitioned your samples into “before” and “after” groups, you can use -1 for all “before” samples and 1 for all “after” samples, and the call tree will show you which stacks occur more frequently in the “after” part of the profile, by sorting those stacks to the top.

Source

pub fn add_sample_same_stack_zero_cpu( &mut self, thread: ThreadHandle, timestamp: Timestamp, weight: i32, )

Add a sample with a CPU delta of zero. Internally, multiple consecutive samples with a delta of zero will be combined into one sample with an accumulated weight.

Source

pub fn add_allocation_sample( &mut self, thread: ThreadHandle, timestamp: Timestamp, stack: Option<StackHandle>, allocation_address: u64, allocation_size: i64, )

Add an allocation or deallocation sample to the given thread. This is used to collect stacks showing where allocations and deallocations happened.

When loading profiles with allocation samples in the Firefox Profiler, the UI will display a dropdown above the call tree to switch between regular samples and allocation samples.

An allocation sample has a timestamp, a stack, a memory address, and an allocation size.

The size should be in bytes, with positive values for allocations and negative values for deallocations.

The memory address allows correlating the allocation and deallocation stacks of the same object. This lets the UI display just the stacks for objects which haven’t been deallocated yet (“Retained memory”).

To avoid having to capture stacks for every single allocation, you can sample just a subset of allocations. The sampling should be done based on the allocation size (“probability per byte”). The decision whether to sample should be done at allocation time and remembered for the lifetime of the allocation, so that for each allocated object you either sample both its allocation and deallocation, or neither.

To get the stack handle, you can use Profile::intern_stack or Profile::intern_stack_frames.

Source

pub fn register_marker_type( &mut self, schema: RuntimeSchemaMarkerSchema, ) -> MarkerTypeHandle

Registers a marker type for a RuntimeSchemaMarkerSchema. You only need to call this for marker types whose schema is dynamically created at runtime.

After you register the marker type, you’ll save its MarkerTypeHandle somewhere, and then store it in every marker you create of this type. The marker then needs to return the handle from its implementation of Marker::marker_type.

For marker types whose schema is known at compile time, you’ll want to implement StaticSchemaMarker instead, and you don’t need to call this method.

Source

pub fn static_schema_marker_type<T: StaticSchemaMarker>( &mut self, ) -> MarkerTypeHandle

Returns the marker type handle for a type that implements StaticSchemaMarker.

You usually don’t need to call this, ever. It is called by the blanket impl of Marker::marker_type for all types which implement StaticSchemaMarker.

Source

pub fn add_marker<T: Marker>( &mut self, thread: ThreadHandle, timing: MarkerTiming, marker: T, ) -> MarkerHandle

Add a marker to the given thread.

The marker handle that’s returned by this method can be used in Profile::set_marker_stack.

use fxprof_processed_profile::{
    Profile, CategoryHandle, Marker, MarkerFieldFlags, MarkerFieldFormat, MarkerTiming,
    StaticSchemaMarker, StaticSchemaMarkerField, StringHandle, ThreadHandle, Timestamp,
};

let name = profile.intern_string("Marker name");
let text = profile.intern_string("Marker text");
let my_marker = TextMarker { name, text };
profile.add_marker(thread, MarkerTiming::Interval(start_time, end_time), my_marker);

#[derive(Debug, Clone)]
pub struct TextMarker {
  pub name: StringHandle,
  pub text: StringHandle,
}

impl StaticSchemaMarker for TextMarker {
    const UNIQUE_MARKER_TYPE_NAME: &'static str = "Text";

    const CHART_LABEL: Option<&'static str> = Some("{marker.data.text}");
    const TABLE_LABEL: Option<&'static str> = Some("{marker.name} - {marker.data.text}");

    const FIELDS: &'static [StaticSchemaMarkerField] = &[StaticSchemaMarkerField {
        key: "text",
        label: "Contents",
        format: MarkerFieldFormat::String,
        flags: MarkerFieldFlags::SEARCHABLE,
    }];

    fn name(&self, _profile: &mut Profile) -> StringHandle {
        self.name
    }

    fn category(&self, _profile: &mut Profile) -> CategoryHandle {
        CategoryHandle::OTHER
    }

    fn string_field_value(&self, _field_index: u32) -> StringHandle {
        self.text
    }

    fn number_field_value(&self, _field_index: u32) -> f64 {
        unreachable!()
    }
}
Source

pub fn set_marker_stack( &mut self, thread: ThreadHandle, marker: MarkerHandle, stack: Option<StackHandle>, )

Sets a marker’s stack. Every marker can have an optional stack, regardless of its marker type.

A marker’s stack is shown in its tooltip, and in the sidebar in the marker table panel if a marker with a stack is selected.

To get the stack handle, you can use Profile::intern_stack or Profile::intern_stack_frames.

Source

pub fn add_counter_sample( &mut self, counter: CounterHandle, timestamp: Timestamp, value_delta: f64, number_of_operations_delta: u32, )

Add a data point to a counter. For a memory counter, value_delta is the number of bytes that have been allocated / deallocated since the previous counter sample, and number_of_operations is the number of malloc / free calls since the previous counter sample. Both numbers are deltas.

The graph in the profiler UI will connect subsequent data points with diagonal lines. Counters are intended for values that are measured at a time-based sample rate; for example, you could add a counter sample once every millisecond with the current memory usage.

Alternatively, you can emit a new data point only whenever the value changes. In that case you probably want to emit two values per change: one right before (with the old value) and one right at the timestamp of change (with the new value). This way you’ll get more horizontal lines, and the diagonal line will be very short.

Source

pub fn set_symbolicated(&mut self, v: bool)

Set whether the profile is already symbolicated.

Read: whether symbols are resolved.

If your samples refer to labels instead of addresses, it is safe to set to true.

Setting to true prevents the Firefox Profiler from attempting to resolve symbols.

By default, this is set to false. This causes the Firefox Profiler to look up symbols for any address-based Frame, i.e. any frame which is not a Frame::Label.

If you use address-based frames and supply your own symbols using Profile::add_lib or Profile::set_lib_symbol_table, you can choose to set this to true and avoid another symbol lookup, or you can leave it set to false if there is a way to obtain richer symbol information than the information supplied in those symbol tables.

For example, when samply creates a profile which includes JIT frames, and there is a Jitdump file with symbol information about those JIT frames, samply uses Profile::set_lib_symbol_table to provide the function names for the JIT functions. But it does not call Profile::set_symbolicated with true, because the Jitdump files may include additional information that’s not in the SymbolTable, specifically the Jitdump file may have file name and line number information. This information is only added into the profile by the Firefox Profiler’s resolution of symbols: The Firefox Profiler requests symbol information for the JIT frame addresses from samply’s symbol server, at which point samply obtains the richer information from the Jitdump file and returns it via the symbol server response.

Source

pub fn lib_used_rva_iter(&self) -> UsedLibraryAddressesIterator<'_>

Trait Implementations§

Source§

impl Debug for Profile

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Serialize for Profile

Source§

fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error>

Serialize this value into the given Serde serializer. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.