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}
;
6 use std
::path
::{Path, PathBuf}
;
8 use std
::time
::Instant
;
10 pub struct ProfilerFiles
{
11 pub events_file
: PathBuf
,
12 pub string_data_file
: PathBuf
,
13 pub string_index_file
: PathBuf
,
17 pub fn new(path_stem
: &Path
) -> 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"),
26 pub struct Profiler
<S
: SerializationSink
> {
28 string_table
: StringTableBuilder
<S
>,
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
)?
);
37 // The first thing in every file we generate must be the file header.
38 write_file_header(&*event_sink
, FILE_MAGIC_EVENT_STREAM
);
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
)?
),
45 let profiler
= Profiler
{
48 start_time
: Instant
::now(),
51 let mut args
= String
::new();
52 for arg
in std
::env
::args() {
53 args
.push_str(&arg
.escape_default().to_string());
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
)
71 pub fn alloc_string_with_reserved_id
<STR
: SerializableString
+ ?Sized
>(
76 self.string_table
.alloc_with_reserved_id(id
, s
)
80 pub fn alloc_string
<STR
: SerializableString
+ ?Sized
>(&self, s
: &STR
) -> StringId
{
81 self.string_table
.alloc(s
)
84 /// Records an event with the given parameters. The event time is computed
86 pub fn record_instant_event(&self, event_kind
: StringId
, event_id
: StringId
, thread_id
: u32) {
88 RawEvent
::new_instant(event_kind
, event_id
, thread_id
, self.nanos_since_start());
90 self.record_raw_event(&raw_event
);
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
>(
100 ) -> TimingGuard
<'a
, S
> {
106 start_ns
: self.nanos_since_start(),
110 fn record_raw_event(&self, raw_event
: &RawEvent
) {
112 .write_atomic(std
::mem
::size_of
::<RawEvent
>(), |bytes
| {
113 raw_event
.serialize(bytes
);
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
123 /// When dropped, this `TimingGuard` will record an "end" event in the
124 /// `Profiler` it was created by.
126 pub struct TimingGuard
<'a
, S
: SerializationSink
> {
127 profiler
: &'a Profiler
<S
>,
129 event_kind
: StringId
,
134 impl<'a
, S
: SerializationSink
> Drop
for TimingGuard
<'a
, S
> {
137 let raw_event
= RawEvent
::new_interval(
142 self.profiler
.nanos_since_start(),
145 self.profiler
.record_raw_event(&raw_event
);