1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use crate::json_writer::JSONWriter;
6 use crate::marker::schema::MarkerSchema;
7 use crate::marker::{transmute_and_stream, ProfilerMarker};
8 use std::collections::HashMap;
9 use std::sync::{RwLock, RwLockReadGuard};
10 
11 lazy_static! {
12     static ref DESERIALIZER_TAGS_STATE: RwLock<DeserializerTagsState> =
13         RwLock::new(DeserializerTagsState::new());
14 }
15 
16 /// A state that keeps track of each marker types and their deserializer tags.
17 /// They are added during the marker insertion and read during the marker serialization.
18 pub struct DeserializerTagsState {
19     /// C++ side accepts only u8 values, but we only know usize values as the
20     /// unique marker type values. So, we need to keep track of each
21     /// "marker tag -> deserializer tag" conversions to directly get the
22     /// deserializer tags of the already added marker types.
23     pub marker_tag_to_deserializer_tag: HashMap<usize, u8>,
24     /// Vector of marker type functions.
25     /// 1-based, i.e.: [0] -> tag 1. Elements are pushed to the end of the vector
26     /// whenever a new marker type is used in a Firefox session; the content is
27     /// kept between profiler runs in that session. On the C++ side, we have the
28     /// same algorithm (althought it's a sized array). See `sMarkerTypeFunctions1Based`.
29     pub marker_type_functions_1_based: Vec<MarkerTypeFunctions>,
30 }
31 
32 /// Functions that will be stored per marker type, so we can serialize the marker
33 /// schema and stream the marker payload for a specific type.
34 pub struct MarkerTypeFunctions {
35     /// A function that returns the name of the marker type.
36     pub marker_type_name_fn: fn() -> &'static str,
37     /// A function that returns a `MarkerSchema`, which contains all the
38     /// information needed to stream the display schema associated with a
39     /// marker type.
40     pub marker_type_display_fn: fn() -> MarkerSchema,
41     /// A function that can read a serialized payload from bytes and streams it
42     /// as JSON object properties.
43     pub transmute_and_stream_fn:
44         unsafe fn(payload: *const u8, payload_size: usize, json_writer: &mut JSONWriter),
45 }
46 
47 impl DeserializerTagsState {
new() -> Self48     fn new() -> Self {
49         DeserializerTagsState {
50             marker_tag_to_deserializer_tag: HashMap::new(),
51             marker_type_functions_1_based: vec![],
52         }
53     }
54 }
55 
56 /// Get or insert the deserializer tag for each marker type. The tag storage
57 /// is limited to 255 marker types. This is the same with the C++ side. It's
58 /// unlikely to reach to this limit, but if that's the case, C++ side needs
59 /// to change the uint8_t type for the deserializer tag as well.
get_or_insert_deserializer_tag<T>() -> u8 where T: ProfilerMarker,60 pub fn get_or_insert_deserializer_tag<T>() -> u8
61 where
62     T: ProfilerMarker,
63 {
64     let unique_marker_tag = &T::marker_type_name as *const _ as usize;
65     let mut state = DESERIALIZER_TAGS_STATE.write().unwrap();
66 
67     match state.marker_tag_to_deserializer_tag.get(&unique_marker_tag) {
68         None => {
69             // It's impossible to have length more than u8.
70             let deserializer_tag = state.marker_type_functions_1_based.len() as u8 + 1;
71             debug_assert!(
72                 deserializer_tag < 250,
73                 "Too many rust marker payload types! Please consider increasing the profiler \
74                  buffer tag size."
75             );
76 
77             state
78                 .marker_tag_to_deserializer_tag
79                 .insert(unique_marker_tag, deserializer_tag);
80             state
81                 .marker_type_functions_1_based
82                 .push(MarkerTypeFunctions {
83                     marker_type_name_fn: T::marker_type_name,
84                     marker_type_display_fn: T::marker_type_display,
85                     transmute_and_stream_fn: transmute_and_stream::<T>,
86                 });
87             deserializer_tag
88         }
89         Some(deserializer_tag) => *deserializer_tag,
90     }
91 }
92 
93 /// A guard that will be used by the marker FFI functions for getting marker type functions.
94 pub struct MarkerTypeFunctionsReadGuard {
95     guard: RwLockReadGuard<'static, DeserializerTagsState>,
96 }
97 
98 impl MarkerTypeFunctionsReadGuard {
iter<'a>(&'a self) -> impl Iterator<Item = &'a MarkerTypeFunctions>99     pub fn iter<'a>(&'a self) -> impl Iterator<Item = &'a MarkerTypeFunctions> {
100         self.guard.marker_type_functions_1_based.iter()
101     }
102 
get<'a>(&'a self, deserializer_tag: u8) -> &'a MarkerTypeFunctions103     pub fn get<'a>(&'a self, deserializer_tag: u8) -> &'a MarkerTypeFunctions {
104         self.guard
105             .marker_type_functions_1_based
106             .get(deserializer_tag as usize - 1)
107             .expect("Failed to find the marker type functions for given deserializer tag")
108     }
109 }
110 
111 /// Locks the DESERIALIZER_TAGS_STATE and returns the marker type functions read guard.
get_marker_type_functions_read_guard() -> MarkerTypeFunctionsReadGuard112 pub fn get_marker_type_functions_read_guard() -> MarkerTypeFunctionsReadGuard {
113     MarkerTypeFunctionsReadGuard {
114         guard: DESERIALIZER_TAGS_STATE.read().unwrap(),
115     }
116 }
117