Skip to content

Commit 884da76

Browse files
authored
Some fxprof-processed-profile API changes (#524)
- Rename `intern_string` to `handle_for_string`. Do the same for `intern_frame`, `intern_stack` and so on. - Change `add_category` and `add_subcategory` to `handle_for_category` and `handle_for_subcategory` . - For markers, make the category part of the schema. Add a static `Category` type to make this possible for `StaticSchemaMarker`.
2 parents 0940fbb + 1f9fdd3 commit 884da76

33 files changed

+521
-429
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

fxprof-processed-profile/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "fxprof-processed-profile"
33
version = "0.8.1"
44
edition = "2021"
5-
rust-version = "1.60" # needed by bytesize
5+
rust-version = "1.63" # needed by indexmap
66
authors = ["Markus Stange <mstange.moz@gmail.com>"]
77
license = "MIT OR Apache-2.0"
88
description = "Create profiles in the Firefox Profiler's processed profile JSON format."
@@ -16,6 +16,7 @@ serde = "1.0.204"
1616
serde_derive = "1.0.188"
1717
debugid = "0.8.0"
1818
rustc-hash = "2"
19+
indexmap = { version = "2.7", features = ["serde"] }
1920

2021
[dev-dependencies]
2122
assert-json-diff = "2.0.1"

fxprof-processed-profile/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ let process = profile.add_process("App process", 54132, Timestamp::from_millis_s
2020
let thread = profile.add_thread(process, 54132000, Timestamp::from_millis_since_reference(0.0), true);
2121
profile.set_thread_name(thread, "Main thread");
2222
let stack_frames = vec![
23-
FrameInfo { frame: Frame::Label(profile.intern_string("Root node")), category_pair: CategoryHandle::OTHER.into(), flags: FrameFlags::empty() },
24-
FrameInfo { frame: Frame::Label(profile.intern_string("First callee")), category_pair: CategoryHandle::OTHER.into(), flags: FrameFlags::empty() }
23+
FrameInfo { frame: Frame::Label(profile.handle_for_string("Root node")), subcategory: CategoryHandle::OTHER.into(), flags: FrameFlags::empty() },
24+
FrameInfo { frame: Frame::Label(profile.handle_for_string("First callee")), subcategory: CategoryHandle::OTHER.into(), flags: FrameFlags::empty() }
2525
];
26-
let stack = profile.intern_stack_frames(thread, stack_frames.into_iter());
26+
let stack = profile.handle_for_stack_frames(thread, stack_frames.into_iter());
2727
profile.add_sample(thread, Timestamp::from_millis_since_reference(0.0), stack, CpuDelta::ZERO, 1);
2828

2929
let writer = std::io::BufWriter::new(output_file);

fxprof-processed-profile/src/category.rs

Lines changed: 116 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,38 @@
1+
use std::hash::Hash;
2+
3+
use indexmap::Equivalent;
14
use serde::ser::{Serialize, SerializeMap, Serializer};
25

6+
use crate::Profile;
7+
38
use super::category_color::CategoryColor;
9+
use super::fast_hash_map::FastIndexSet;
10+
11+
/// Implemented by [`Category`], [`Subcategory`], [`CategoryHandle`] and [`SubcategoryHandle`].
12+
pub trait IntoSubcategoryHandle {
13+
/// Returns the corresponding [`SubcategoryHandle`].
14+
fn into_subcategory_handle(self, profile: &mut Profile) -> SubcategoryHandle;
15+
}
16+
17+
/// A profiling category. Has a name and a color.
18+
///
19+
/// Used to categorize stack frames and markers in the front-end. The category's
20+
/// color is used in the activity graph and in the call tree, and in a few other places.
21+
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
22+
pub struct Category<'a>(pub &'a str, pub CategoryColor);
23+
24+
impl IntoSubcategoryHandle for Category<'_> {
25+
fn into_subcategory_handle(self, profile: &mut Profile) -> SubcategoryHandle {
26+
let category_handle = profile.handle_for_category(self);
27+
category_handle.into()
28+
}
29+
}
430

5-
/// A profiling category, can be set on stack frames and markers as part of a [`CategoryPairHandle`].
31+
/// The handle for a [`Category`], obtained from [`Profile::handle_for_category`](crate::Profile::handle_for_category).
632
///
7-
/// Categories can be created with [`Profile::add_category`](crate::Profile::add_category).
33+
/// Storing and reusing the handle avoids repeated lookups and can improve performance.
34+
///
35+
/// The handle is specific to a [`Profile`] instance and cannot be reused across profiles.
836
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
937
pub struct CategoryHandle(pub(crate) u16);
1038

@@ -13,15 +41,33 @@ impl CategoryHandle {
1341
pub const OTHER: Self = CategoryHandle(0);
1442
}
1543

44+
impl IntoSubcategoryHandle for CategoryHandle {
45+
fn into_subcategory_handle(self, _profile: &mut Profile) -> SubcategoryHandle {
46+
self.into()
47+
}
48+
}
49+
1650
impl Serialize for CategoryHandle {
1751
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1852
self.0.serialize(serializer)
1953
}
2054
}
2155

22-
/// A profiling subcategory, can be set on stack frames and markers as part of a [`CategoryPairHandle`].
56+
/// A named subcategory of a [`Category`], for fine-grained annotation of stack frames.
2357
///
24-
/// Subategories can be created with [`Profile::add_subcategory`](crate::Profile::add_subcategory).
58+
/// If you don't need named subcategories, you can just pass a [`Category`] or a
59+
/// [`CategoryHandle`] in any place where an [`IntoSubcategoryHandle`] is expected;
60+
/// this will give you the category's default subcategory.
61+
pub struct Subcategory<'a>(pub Category<'a>, pub &'a str);
62+
63+
impl IntoSubcategoryHandle for Subcategory<'_> {
64+
fn into_subcategory_handle(self, profile: &mut Profile) -> SubcategoryHandle {
65+
let Subcategory(category, subcategory_name) = self;
66+
let category_handle = profile.handle_for_category(category);
67+
profile.handle_for_subcategory(category_handle, subcategory_name)
68+
}
69+
}
70+
2571
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
2672
pub struct SubcategoryIndex(pub u16);
2773

@@ -30,16 +76,30 @@ impl SubcategoryIndex {
3076
pub const OTHER: Self = SubcategoryIndex(0);
3177
}
3278

33-
/// A profiling category pair, consisting of a category and an optional subcategory. Can be set on stack frames and markers.
79+
/// A handle for a [`Subcategory`], or for the default subcategory of a [`CategoryHandle`].
80+
///
81+
/// Used to annotate stack frames.
82+
///
83+
/// Every [`CategoryHandle`] can be turned into a [`SubcategoryHandle`] by calling `.into()` -
84+
/// this will give you the default subcategory of that category.
3485
///
35-
/// Category pairs can be created with [`Profile::add_subcategory`](crate::Profile::add_subcategory)
36-
/// and from a [`CategoryHandle`].
86+
/// Subcategory handles for named subcategories can be obtained from
87+
/// [`Profile::handle_for_subcategory`](crate::Profile::handle_for_subcategory).
88+
/// Storing and reusing the handle avoids repeated lookups and can improve performance.
89+
///
90+
/// The handle is specific to a Profile instance and cannot be reused across profiles.
3791
#[derive(Debug, Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
38-
pub struct CategoryPairHandle(pub(crate) CategoryHandle, pub(crate) SubcategoryIndex);
92+
pub struct SubcategoryHandle(pub(crate) CategoryHandle, pub(crate) SubcategoryIndex);
3993

40-
impl From<CategoryHandle> for CategoryPairHandle {
94+
impl IntoSubcategoryHandle for SubcategoryHandle {
95+
fn into_subcategory_handle(self, _profile: &mut Profile) -> SubcategoryHandle {
96+
self
97+
}
98+
}
99+
100+
impl From<CategoryHandle> for SubcategoryHandle {
41101
fn from(category: CategoryHandle) -> Self {
42-
CategoryPairHandle(category, SubcategoryIndex::OTHER)
102+
SubcategoryHandle(category, SubcategoryIndex::OTHER)
43103
}
44104
}
45105

@@ -48,24 +108,61 @@ impl From<CategoryHandle> for CategoryPairHandle {
48108
pub struct InternalCategory {
49109
name: String,
50110
color: CategoryColor,
51-
subcategories: Vec<String>,
111+
subcategories: FastIndexSet<String>,
112+
}
113+
114+
impl Hash for InternalCategory {
115+
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
116+
self.as_category().hash(state)
117+
}
118+
}
119+
120+
impl Equivalent<Category<'_>> for InternalCategory {
121+
fn equivalent(&self, key: &Category<'_>) -> bool {
122+
&self.as_category() == key
123+
}
52124
}
53125

126+
impl Equivalent<InternalCategory> for Category<'_> {
127+
fn equivalent(&self, key: &InternalCategory) -> bool {
128+
self == &key.as_category()
129+
}
130+
}
131+
132+
impl PartialEq for InternalCategory {
133+
fn eq(&self, other: &Self) -> bool {
134+
self.as_category() == other.as_category()
135+
}
136+
}
137+
138+
impl Eq for InternalCategory {}
139+
54140
impl InternalCategory {
55-
pub fn new(name: String, color: CategoryColor) -> Self {
56-
let subcategories = vec!["Other".to_string()];
141+
pub fn new(name: &str, color: CategoryColor) -> Self {
142+
let mut subcategories = FastIndexSet::default();
143+
subcategories.insert("Other".to_string());
57144
Self {
58-
name,
145+
name: name.to_string(),
59146
color,
60147
subcategories,
61148
}
62149
}
63150

64-
/// Add a subcategory to this category.
65-
pub fn add_subcategory(&mut self, subcategory_name: String) -> SubcategoryIndex {
66-
let subcategory_index = SubcategoryIndex(u16::try_from(self.subcategories.len()).unwrap());
67-
self.subcategories.push(subcategory_name);
68-
subcategory_index
151+
/// Get or create a subcategory to this category.
152+
pub fn index_for_subcategory(&mut self, subcategory_name: &str) -> SubcategoryIndex {
153+
let index = self
154+
.subcategories
155+
.get_index_of(subcategory_name)
156+
.unwrap_or_else(|| {
157+
self.subcategories
158+
.insert_full(subcategory_name.to_owned())
159+
.0
160+
});
161+
SubcategoryIndex(u16::try_from(index).unwrap())
162+
}
163+
164+
pub fn as_category(&self) -> Category<'_> {
165+
Category(&self.name, self.color)
69166
}
70167
}
71168

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use indexmap::IndexSet;
12
use rustc_hash::FxHashMap;
23

34
pub type FastHashMap<K, V> = FxHashMap<K, V>;
5+
pub type FastIndexSet<V> = IndexSet<V, rustc_hash::FxBuildHasher>;

fxprof-processed-profile/src/frame.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use bitflags::bitflags;
22

3-
use crate::category::CategoryPairHandle;
3+
use crate::category::SubcategoryHandle;
44
use crate::global_lib_table::LibraryHandle;
55
use crate::profile::StringHandle;
66

@@ -46,7 +46,7 @@ pub enum Frame {
4646
/// has already been resolved to a `LibraryHandle`.
4747
RelativeAddressFromAdjustedReturnAddress(LibraryHandle, u32),
4848
/// A string, containing an index returned by
49-
/// [`Profile::intern_string`](crate::Profile::intern_string).
49+
/// [`Profile::handle_for_string`](crate::Profile::handle_for_string).
5050
Label(StringHandle),
5151
}
5252

@@ -55,8 +55,8 @@ pub enum Frame {
5555
pub struct FrameInfo {
5656
/// The absolute address or label of this frame.
5757
pub frame: Frame,
58-
/// The category pair of this frame.
59-
pub category_pair: CategoryPairHandle,
58+
/// The subcategory of this frame.
59+
pub subcategory: SubcategoryHandle,
6060
/// The flags of this frame. Use `FrameFlags::empty()` if unsure.
6161
pub flags: FrameFlags,
6262
}

fxprof-processed-profile/src/frame_table.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer};
22

3-
use crate::category::{CategoryHandle, CategoryPairHandle, SubcategoryIndex};
3+
use crate::category::{CategoryHandle, SubcategoryHandle, SubcategoryIndex};
44
use crate::fast_hash_map::FastHashMap;
55
use crate::frame::FrameFlags;
66
use crate::func_table::{FuncIndex, FuncTable};
@@ -84,7 +84,7 @@ impl FrameTable {
8484
};
8585
let func_index =
8686
func_table.index_for_func(location_string_index, resource, frame.flags);
87-
let CategoryPairHandle(category, subcategory) = frame.category_pair;
87+
let SubcategoryHandle(category, subcategory) = frame.subcategory;
8888
addresses.push(address);
8989
categories.push(category);
9090
subcategories.push(subcategory);
@@ -134,7 +134,7 @@ impl Serialize for SerializableFrameTableAddressColumn<'_> {
134134
#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq, Hash)]
135135
pub struct InternalFrame {
136136
pub location: InternalFrameLocation,
137-
pub category_pair: CategoryPairHandle,
137+
pub subcategory: SubcategoryHandle,
138138
pub flags: FrameFlags,
139139
}
140140

fxprof-processed-profile/src/lib.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@
1919
//! let thread = profile.add_thread(process, 54132000, Timestamp::from_millis_since_reference(0.0), true);
2020
//! profile.set_thread_name(thread, "Main thread");
2121
//! let stack_frames = vec![
22-
//! FrameInfo { frame: Frame::Label(profile.intern_string("Root node")), category_pair: CategoryHandle::OTHER.into(), flags: FrameFlags::empty() },
23-
//! FrameInfo { frame: Frame::Label(profile.intern_string("First callee")), category_pair: CategoryHandle::OTHER.into(), flags: FrameFlags::empty() }
22+
//! FrameInfo { frame: Frame::Label(profile.handle_for_string("Root node")), subcategory: CategoryHandle::OTHER.into(), flags: FrameFlags::empty() },
23+
//! FrameInfo { frame: Frame::Label(profile.handle_for_string("First callee")), subcategory: CategoryHandle::OTHER.into(), flags: FrameFlags::empty() }
2424
//! ];
25-
//! let stack = profile.intern_stack_frames(thread, stack_frames.into_iter());
25+
//! let stack = profile.handle_for_stack_frames(thread, stack_frames.into_iter());
2626
//! profile.add_sample(thread, Timestamp::from_millis_since_reference(0.0), stack, CpuDelta::ZERO, 1);
2727
//!
2828
//! let writer = std::io::BufWriter::new(output_file);
@@ -59,7 +59,9 @@ mod thread;
5959
mod thread_string_table;
6060
mod timestamp;
6161

62-
pub use category::{CategoryHandle, CategoryPairHandle};
62+
pub use category::{
63+
Category, CategoryHandle, IntoSubcategoryHandle, Subcategory, SubcategoryHandle,
64+
};
6365
pub use category_color::CategoryColor;
6466
pub use counters::CounterHandle;
6567
pub use cpu_delta::CpuDelta;

fxprof-processed-profile/src/marker_table.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,6 @@ impl MarkerTable {
5858
schema: &InternalMarkerSchema,
5959
marker: T,
6060
timing: MarkerTiming,
61-
category: CategoryHandle,
6261
thread_string_table: &mut ThreadStringTable,
6362
global_string_table: &mut GlobalStringTable,
6463
) -> MarkerHandle {
@@ -68,7 +67,7 @@ impl MarkerTable {
6867
MarkerTiming::IntervalStart(s) => (Some(s), None, Phase::IntervalStart),
6968
MarkerTiming::IntervalEnd(e) => (None, Some(e), Phase::IntervalEnd),
7069
};
71-
self.marker_categories.push(category);
70+
self.marker_categories.push(schema.category());
7271
self.marker_name_string_indexes.push(name_string_index);
7372
self.marker_starts.push(s);
7473
self.marker_ends.push(e);

0 commit comments

Comments
 (0)