]> git.proxmox.com Git - rustc.git/blob - vendor/measureme/src/profiler.rs
New upstream version 1.41.1+dfsg1
[rustc.git] / vendor / measureme / src / profiler.rs
1 use crate::file_header::{write_file_header, FILE_MAGIC_EVENT_STREAM};
2 use crate::raw_event::RawEvent;
3 use crate::serialization::SerializationSink;
4 use crate::stringtable::{SerializableString, StringId, StringTableBuilder};
5 use std::error::Error;
6 use std::path::{Path, PathBuf};
7 use std::sync::Arc;
8 use std::time::Instant;
9
10 pub struct ProfilerFiles {
11 pub events_file: PathBuf,
12 pub string_data_file: PathBuf,
13 pub string_index_file: PathBuf,
14 }
15
16 impl ProfilerFiles {
17 pub fn new(path_stem: &Path) -> ProfilerFiles {
18 ProfilerFiles {
19 events_file: path_stem.with_extension("events"),
20 string_data_file: path_stem.with_extension("string_data"),
21 string_index_file: path_stem.with_extension("string_index"),
22 }
23 }
24 }
25
26 pub struct Profiler<S: SerializationSink> {
27 event_sink: Arc<S>,
28 string_table: StringTableBuilder<S>,
29 start_time: Instant,
30 }
31
32 impl<S: SerializationSink> Profiler<S> {
33 pub fn new(path_stem: &Path) -> Result<Profiler<S>, Box<dyn Error>> {
34 let paths = ProfilerFiles::new(path_stem);
35 let event_sink = Arc::new(S::from_path(&paths.events_file)?);
36
37 // The first thing in every file we generate must be the file header.
38 write_file_header(&*event_sink, FILE_MAGIC_EVENT_STREAM);
39
40 let string_table = StringTableBuilder::new(
41 Arc::new(S::from_path(&paths.string_data_file)?),
42 Arc::new(S::from_path(&paths.string_index_file)?),
43 );
44
45 let profiler = Profiler {
46 event_sink,
47 string_table,
48 start_time: Instant::now(),
49 };
50
51 let mut args = String::new();
52 for arg in std::env::args() {
53 args.push_str(&arg.escape_default().to_string());
54 args.push(' ');
55 }
56
57 profiler.string_table.alloc_metadata(&*format!(
58 r#"{{ "start_time": {}, "process_id": {}, "cmd": "{}" }}"#,
59 std::time::SystemTime::now()
60 .duration_since(std::time::UNIX_EPOCH)
61 .unwrap()
62 .as_nanos(),
63 std::process::id(),
64 args,
65 ));
66
67 Ok(profiler)
68 }
69
70 #[inline(always)]
71 pub fn alloc_string_with_reserved_id<STR: SerializableString + ?Sized>(
72 &self,
73 id: StringId,
74 s: &STR,
75 ) -> StringId {
76 self.string_table.alloc_with_reserved_id(id, s)
77 }
78
79 #[inline(always)]
80 pub fn alloc_string<STR: SerializableString + ?Sized>(&self, s: &STR) -> StringId {
81 self.string_table.alloc(s)
82 }
83
84 /// Records an event with the given parameters. The event time is computed
85 /// automatically.
86 pub fn record_instant_event(&self, event_kind: StringId, event_id: StringId, thread_id: u32) {
87 let raw_event =
88 RawEvent::new_instant(event_kind, event_id, thread_id, self.nanos_since_start());
89
90 self.record_raw_event(&raw_event);
91 }
92
93 /// Creates a "start" event and returns a `TimingGuard` that will create
94 /// the corresponding "end" event when it is dropped.
95 pub fn start_recording_interval_event<'a>(
96 &'a self,
97 event_kind: StringId,
98 event_id: StringId,
99 thread_id: u32,
100 ) -> TimingGuard<'a, S> {
101 TimingGuard {
102 profiler: self,
103 event_id,
104 event_kind,
105 thread_id,
106 start_ns: self.nanos_since_start(),
107 }
108 }
109
110 fn record_raw_event(&self, raw_event: &RawEvent) {
111 self.event_sink
112 .write_atomic(std::mem::size_of::<RawEvent>(), |bytes| {
113 raw_event.serialize(bytes);
114 });
115 }
116
117 fn nanos_since_start(&self) -> u64 {
118 let duration_since_start = self.start_time.elapsed();
119 duration_since_start.as_secs() * 1_000_000_000 + duration_since_start.subsec_nanos() as u64
120 }
121 }
122
123 /// When dropped, this `TimingGuard` will record an "end" event in the
124 /// `Profiler` it was created by.
125 #[must_use]
126 pub struct TimingGuard<'a, S: SerializationSink> {
127 profiler: &'a Profiler<S>,
128 event_id: StringId,
129 event_kind: StringId,
130 thread_id: u32,
131 start_ns: u64,
132 }
133
134 impl<'a, S: SerializationSink> Drop for TimingGuard<'a, S> {
135 #[inline]
136 fn drop(&mut self) {
137 let raw_event = RawEvent::new_interval(
138 self.event_kind,
139 self.event_id,
140 self.thread_id,
141 self.start_ns,
142 self.profiler.nanos_since_start(),
143 );
144
145 self.profiler.record_raw_event(&raw_event);
146 }
147 }