Skip to content

Commit 4314b7f

Browse files
authored
Increase code sharing among the platform implementations (#546)
2 parents 110cc81 + 8c277e5 commit 4314b7f

22 files changed

+1027
-1265
lines changed

samply/src/cli.rs

Lines changed: 628 additions & 0 deletions
Large diffs are not rendered by default.

samply/src/cli_utils.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
use std::ffi::OsStr;
2+
3+
#[allow(unused)]
4+
pub fn parse_time_range(
5+
arg: &str,
6+
) -> Result<(std::time::Duration, std::time::Duration), humantime::DurationError> {
7+
let (is_duration, splitchar) = if arg.contains('+') {
8+
(true, '+')
9+
} else {
10+
(false, '-')
11+
};
12+
13+
let parts: Vec<&str> = arg.splitn(2, splitchar).collect();
14+
15+
let start = if parts[0].is_empty() {
16+
std::time::Duration::ZERO
17+
} else {
18+
humantime::parse_duration(parts[0])?
19+
};
20+
21+
let end = if parts.len() == 1 || parts[1].is_empty() {
22+
std::time::Duration::MAX
23+
} else {
24+
humantime::parse_duration(parts[1])?
25+
};
26+
27+
Ok((start, if is_duration { start + end } else { end }))
28+
}
29+
30+
pub fn split_at_first_equals(s: &OsStr) -> Option<(&OsStr, &OsStr)> {
31+
let bytes = s.as_encoded_bytes();
32+
let pos = bytes.iter().position(|b| *b == b'=')?;
33+
let name = &bytes[..pos];
34+
let val = &bytes[(pos + 1)..];
35+
// SAFETY:
36+
// - `name` and `val` only contain content that originated from `OsStr::as_encoded_bytes`
37+
// - Only split with ASCII '=' which is a non-empty UTF-8 substring
38+
let (name, val) = unsafe {
39+
(
40+
OsStr::from_encoded_bytes_unchecked(name),
41+
OsStr::from_encoded_bytes_unchecked(val),
42+
)
43+
};
44+
Some((name, val))
45+
}

samply/src/import/perf.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::linux_shared::{
1313
ConvertRegs, ConvertRegsAarch64, ConvertRegsX86_64, Converter, EventInterpretation, KnownEvent,
1414
MmapRangeOrVec,
1515
};
16-
use crate::shared::recording_props::ProfileCreationProps;
16+
use crate::shared::prop_types::ProfileCreationProps;
1717

1818
#[derive(thiserror::Error, Debug)]
1919
pub enum Error {

samply/src/linux/profiler.rs

Lines changed: 14 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use std::collections::HashMap;
2-
use std::fs::File;
32
use std::ops::Deref;
43
use std::os::unix::process::ExitStatusExt;
54
use std::path::Path;
@@ -8,7 +7,7 @@ use std::thread;
87
use std::time::{Duration, SystemTime};
98

109
use crossbeam_channel::{Receiver, Sender};
11-
use fxprof_processed_profile::ReferenceTimestamp;
10+
use fxprof_processed_profile::{Profile, ReferenceTimestamp};
1211
use linux_perf_data::linux_perf_event_reader::{
1312
CpuMode, Endianness, EventRecord, Mmap2FileId, Mmap2InodeAndVersion, Mmap2Record, RawData,
1413
};
@@ -23,27 +22,22 @@ use crate::linux_shared::vdso::VdsoObject;
2322
use crate::linux_shared::{
2423
ConvertRegs, Converter, EventInterpretation, MmapRangeOrVec, OffCpuIndicator,
2524
};
26-
use crate::server::{start_server_main, ServerProps};
2725
use crate::shared::ctrl_c::CtrlC;
28-
use crate::shared::recording_props::{
26+
use crate::shared::prop_types::{
2927
ProcessLaunchProps, ProfileCreationProps, RecordingMode, RecordingProps,
3028
};
31-
use crate::shared::save_profile::save_profile_to_file;
32-
use crate::shared::symbol_props::SymbolProps;
3329

3430
#[cfg(target_arch = "x86_64")]
3531
pub type ConvertRegsNative = crate::linux_shared::ConvertRegsX86_64;
3632

3733
#[cfg(target_arch = "aarch64")]
3834
pub type ConvertRegsNative = crate::linux_shared::ConvertRegsAarch64;
3935

40-
pub fn start_recording(
36+
pub fn run(
4137
recording_mode: RecordingMode,
4238
recording_props: RecordingProps,
4339
profile_creation_props: ProfileCreationProps,
44-
symbol_props: SymbolProps,
45-
server_props: Option<ServerProps>,
46-
) -> Result<ExitStatus, ()> {
40+
) -> Result<(Profile, ExitStatus), ()> {
4741
let process_launch_props = match recording_mode {
4842
RecordingMode::All => {
4943
// TODO: Implement, by sudo launching a helper process which opens cpu-wide perf events
@@ -52,14 +46,8 @@ pub fn start_recording(
5246
std::process::exit(1)
5347
}
5448
RecordingMode::Pid(pid) => {
55-
start_profiling_pid(
56-
pid,
57-
recording_props,
58-
profile_creation_props,
59-
symbol_props,
60-
server_props,
61-
);
62-
return Ok(ExitStatus::from_raw(0));
49+
let profile = start_profiling_pid(pid, recording_props, profile_creation_props);
50+
return Ok((profile, ExitStatus::from_raw(0)));
6351
}
6452
RecordingMode::Launch(process_launch_props) => process_launch_props,
6553
};
@@ -102,7 +90,6 @@ pub fn start_recording(
10290
crossbeam_channel::bounded(2);
10391

10492
// Launch the observer thread. This thread will manage the perf events.
105-
let output_file_copy = recording_props.output_file.clone();
10693
let interval = recording_props.interval;
10794
let time_limit = recording_props.time_limit;
10895
let initial_exec_name = command_name.to_string_lossy().to_string();
@@ -115,7 +102,6 @@ pub fn start_recording(
115102
};
116103
let initial_exec_name_and_cmdline = (initial_exec_name, initial_cmdline);
117104
let observer_thread = thread::spawn(move || {
118-
let unstable_presymbolicate = profile_creation_props.unstable_presymbolicate;
119105
let mut converter = make_converter(interval, profile_creation_props);
120106

121107
// Wait for the initial pid to profile.
@@ -142,14 +128,12 @@ pub fn start_recording(
142128
run_profiler(
143129
perf_group,
144130
converter,
145-
&output_file_copy,
146131
time_limit,
147132
profile_another_pid_request_receiver,
148133
profile_another_pid_reply_sender,
149134
stop_receiver,
150-
unstable_presymbolicate,
151135
Some(initial_exec_name_and_cmdline),
152-
);
136+
)
153137
});
154138

155139
// We're on the main thread here and the observer thread has just been launched.
@@ -243,35 +227,23 @@ pub fn start_recording(
243227
// Now wait for the observer thread to quit. It will keep running until all
244228
// perf events are closed, which happens if all processes which the events
245229
// are attached to have quit.
246-
observer_thread
230+
let profile = observer_thread
247231
.join()
248232
.expect("couldn't join observer thread");
249233

250-
if let Some(server_props) = server_props {
251-
let profile_filename = &recording_props.output_file;
252-
let libinfo_map = crate::profile_json_preparse::parse_libinfo_map_from_profile_file(
253-
File::open(profile_filename).expect("Couldn't open file we just wrote"),
254-
profile_filename,
255-
)
256-
.expect("Couldn't parse libinfo map from profile file");
257-
258-
start_server_main(profile_filename, server_props, symbol_props, libinfo_map);
259-
}
260-
261234
let exit_status = match wait_status {
262235
WaitStatus::Exited(_pid, exit_code) => ExitStatus::from_raw(exit_code),
263236
_ => ExitStatus::default(),
264237
};
265-
Ok(exit_status)
238+
239+
Ok((profile, exit_status))
266240
}
267241

268242
fn start_profiling_pid(
269243
pid: u32,
270244
recording_props: RecordingProps,
271245
profile_creation_props: ProfileCreationProps,
272-
symbol_props: SymbolProps,
273-
server_props: Option<ServerProps>,
274-
) {
246+
) -> Profile {
275247
// When the first Ctrl+C is received, stop recording.
276248
let ctrl_c_receiver = CtrlC::observe_oneshot();
277249

@@ -282,12 +254,10 @@ fn start_profiling_pid(
282254
let (profile_another_pid_reply_sender, profile_another_pid_reply_receiver) =
283255
crossbeam_channel::bounded(2);
284256

285-
let output_file = recording_props.output_file.clone();
286257
let observer_thread = thread::spawn({
287258
move || {
288259
let interval = recording_props.interval;
289260
let time_limit = recording_props.time_limit;
290-
let unstable_presymbolicate = profile_creation_props.unstable_presymbolicate;
291261
let mut converter = make_converter(interval, profile_creation_props);
292262
let SamplerRequest::StartProfilingAnotherProcess(pid, attach_mode) =
293263
profile_another_pid_request_receiver.recv().unwrap()
@@ -299,16 +269,13 @@ fn start_profiling_pid(
299269
// Tell the main thread that we are now executing.
300270
profile_another_pid_reply_sender.send(true).unwrap();
301271

302-
let output_file = recording_props.output_file;
303272
run_profiler(
304273
perf_group,
305274
converter,
306-
&output_file,
307275
time_limit,
308276
profile_another_pid_request_receiver,
309277
profile_another_pid_reply_sender,
310278
ctrl_c_receiver,
311-
unstable_presymbolicate,
312279
None,
313280
)
314281
}
@@ -337,20 +304,10 @@ fn start_profiling_pid(
337304
// which happens if all processes which the events are attached to have quit.
338305
observer_thread
339306
.join()
340-
.expect("couldn't join observer thread");
307+
.expect("couldn't join observer thread")
341308

342309
// From now on, pressing Ctrl+C will kill our process, because the observer will have
343310
// dropped its CtrlC receiver by now.
344-
345-
if let Some(server_props) = server_props {
346-
let libinfo_map = crate::profile_json_preparse::parse_libinfo_map_from_profile_file(
347-
File::open(&output_file).expect("Couldn't open file we just wrote"),
348-
&output_file,
349-
)
350-
.expect("Couldn't parse libinfo map from profile file");
351-
352-
start_server_main(&output_file, server_props, symbol_props, libinfo_map);
353-
}
354311
}
355312

356313
fn paranoia_level() -> Option<u32> {
@@ -579,14 +536,12 @@ fn run_profiler(
579536
mut converter: Converter<
580537
framehop::UnwinderNative<MmapRangeOrVec, framehop::MayAllocateDuringUnwind>,
581538
>,
582-
output_filename: &Path,
583539
_time_limit: Option<Duration>,
584540
more_processes_request_receiver: Receiver<SamplerRequest>,
585541
more_processes_reply_sender: Sender<bool>,
586542
mut stop_receiver: oneshot::Receiver<()>,
587-
unstable_presymbolicate: bool,
588543
mut initial_exec_name_and_cmdline: Option<(String, Vec<String>)>,
589-
) {
544+
) -> Profile {
590545
// eprintln!("Running...");
591546

592547
let mut should_stop_profiling_once_perf_events_exhausted = false;
@@ -729,16 +684,7 @@ fn run_profiler(
729684
eprintln!("Lost {total_lost_events} events.");
730685
}
731686

732-
let profile = converter.finish();
733-
734-
save_profile_to_file(&profile, output_filename).expect("Couldn't write JSON");
735-
736-
if unstable_presymbolicate {
737-
crate::shared::symbol_precog::presymbolicate(
738-
&profile,
739-
&output_filename.with_extension("syms.json"),
740-
);
741-
}
687+
converter.finish()
742688
}
743689

744690
pub fn read_string_lossy<P: AsRef<Path>>(path: P) -> std::io::Result<String> {

samply/src/linux_shared/converter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ use crate::shared::process_sample_data::{
4949
OtherEventMarker, RssStatMarker, RssStatMember, SchedSwitchMarkerOnCpuTrack,
5050
SchedSwitchMarkerOnThreadTrack,
5151
};
52-
use crate::shared::recording_props::ProfileCreationProps;
52+
use crate::shared::prop_types::ProfileCreationProps;
5353
use crate::shared::synthetic_jit_library::SyntheticJitLibrary;
5454
use crate::shared::timestamp_converter::TimestampConverter;
5555
use crate::shared::types::{StackFrame, StackMode};

samply/src/mac/profiler.rs

Lines changed: 5 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,27 @@
11
use std::collections::hash_map::Entry;
22
use std::collections::HashMap;
3-
use std::fs::File;
43
use std::process::ExitStatus;
54
use std::thread;
65
use std::time::Duration;
76

87
use crossbeam_channel::unbounded;
8+
use fxprof_processed_profile::Profile;
99

1010
use super::error::SamplingError;
1111
use super::process_launcher::{
1212
ExistingProcessRunner, MachError, ReceivedStuff, RootTaskRunner, TaskAccepter, TaskLauncher,
1313
};
1414
use super::sampler::{ProcessSpecificPath, Sampler, TaskInit, TaskInitOrShutdown};
1515
use super::time::get_monotonic_timestamp;
16-
use crate::server::{start_server_main, ServerProps};
17-
use crate::shared::recording_props::{
16+
use crate::shared::prop_types::{
1817
ProcessLaunchProps, ProfileCreationProps, RecordingMode, RecordingProps,
1918
};
20-
use crate::shared::save_profile::save_profile_to_file;
21-
use crate::shared::symbol_props::SymbolProps;
2219

23-
pub fn start_recording(
20+
pub fn run(
2421
recording_mode: RecordingMode,
2522
recording_props: RecordingProps,
2623
mut profile_creation_props: ProfileCreationProps,
27-
symbol_props: SymbolProps,
28-
server_props: Option<ServerProps>,
29-
) -> Result<ExitStatus, MachError> {
30-
let output_file = recording_props.output_file.clone();
31-
24+
) -> Result<(Profile, ExitStatus), MachError> {
3225
let mut task_accepter = TaskAccepter::new()?;
3326

3427
let mut root_task_runner: Box<dyn RootTaskRunner> = match recording_mode {
@@ -82,8 +75,6 @@ pub fn start_recording(
8275
}
8376
};
8477

85-
let unstable_presymbolicate = profile_creation_props.unstable_presymbolicate;
86-
8778
let (task_sender, task_receiver) = unbounded();
8879

8980
let sampler_thread = thread::spawn(move || {
@@ -213,24 +204,5 @@ pub fn start_recording(
213204
}
214205
};
215206

216-
save_profile_to_file(&profile, &output_file).expect("Couldn't write JSON");
217-
218-
if unstable_presymbolicate {
219-
crate::shared::symbol_precog::presymbolicate(
220-
&profile,
221-
&output_file.with_extension("syms.json"),
222-
);
223-
}
224-
225-
if let Some(server_props) = server_props {
226-
let libinfo_map = crate::profile_json_preparse::parse_libinfo_map_from_profile_file(
227-
File::open(&output_file).expect("Couldn't open file we just wrote"),
228-
&output_file,
229-
)
230-
.expect("Couldn't parse libinfo map from profile file");
231-
232-
start_server_main(&output_file, server_props, symbol_props, libinfo_map);
233-
}
234-
235-
Ok(exit_status)
207+
Ok((profile, exit_status))
236208
}

samply/src/mac/sampler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use mach2::port::mach_port_t;
1212
use super::error::SamplingError;
1313
use super::task_profiler::TaskProfiler;
1414
use super::time::get_monotonic_timestamp;
15-
use crate::shared::recording_props::{ProfileCreationProps, RecordingProps};
15+
use crate::shared::prop_types::{ProfileCreationProps, RecordingProps};
1616
use crate::shared::recycling::ProcessRecycler;
1717
use crate::shared::timestamp_converter::TimestampConverter;
1818
use crate::shared::unresolved_samples::UnresolvedStacks;

samply/src/mac/task_profiler.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ use crate::shared::marker_file::get_markers;
4444
use crate::shared::perf_map::try_load_perf_map;
4545
use crate::shared::process_name::make_process_name;
4646
use crate::shared::process_sample_data::{MarkerSpanOnThread, ProcessSampleData};
47-
use crate::shared::recording_props::ProfileCreationProps;
47+
use crate::shared::prop_types::ProfileCreationProps;
4848
use crate::shared::recycling::{ProcessRecycler, ProcessRecyclingData, ThreadRecycler};
4949
use crate::shared::timestamp_converter::TimestampConverter;
5050
use crate::shared::unresolved_samples::{UnresolvedSamples, UnresolvedStacks};

0 commit comments

Comments
 (0)