Skip to content

Commit 6d607e9

Browse files
committed
When computing the product name, don't just pick the first item in the command array, because it might be an environment variable.
This fixes what's displayed in the top left corner in the profiler UI when doing 'samply record MYENV=val my-command' .
1 parent fa07849 commit 6d607e9

File tree

4 files changed

+79
-56
lines changed

4 files changed

+79
-56
lines changed

samply/src/linux/profiler.rs

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use crate::linux_shared::{
2727
ConvertRegs, Converter, EventInterpretation, MmapRangeOrVec, OffCpuIndicator,
2828
};
2929
use crate::server::{start_server_main, ServerProps};
30-
use crate::shared::recording_props::{ProfileCreationProps, RecordingProps};
30+
use crate::shared::recording_props::{ProcessLaunchProps, ProfileCreationProps, RecordingProps};
3131

3232
#[cfg(target_arch = "x86_64")]
3333
pub type ConvertRegsNative = crate::linux_shared::ConvertRegsX86_64;
@@ -37,16 +37,20 @@ pub type ConvertRegsNative = crate::linux_shared::ConvertRegsAarch64;
3737

3838
#[allow(clippy::too_many_arguments)]
3939
pub fn start_recording(
40-
command_name: OsString,
41-
command_args: &[OsString],
42-
env_vars: &[(OsString, OsString)],
43-
iteration_count: u32,
40+
process_launch_props: ProcessLaunchProps,
4441
recording_props: RecordingProps,
4542
profile_creation_props: ProfileCreationProps,
4643
server_props: Option<ServerProps>,
4744
) -> Result<ExitStatus, ()> {
4845
// We want to profile a child process which we are about to launch.
4946

47+
let ProcessLaunchProps {
48+
env_vars,
49+
command_name,
50+
args,
51+
iteration_count,
52+
} = process_launch_props;
53+
5054
// Ignore SIGINT in our process while the child process is running. The
5155
// signal will still reach the child process, because Ctrl+C sends the
5256
// SIGINT signal to all processes in the foreground process group.
@@ -61,7 +65,7 @@ pub fn start_recording(
6165
// Start a new process for the launched command and get its pid.
6266
// The command will not start running until we tell it to.
6367
let process =
64-
SuspendedLaunchedProcess::launch_in_suspended_state(&command_name, command_args, env_vars)
68+
SuspendedLaunchedProcess::launch_in_suspended_state(&command_name, &args, &env_vars)
6569
.unwrap();
6670
let pid = process.pid();
6771

@@ -158,12 +162,9 @@ pub fn start_recording(
158162
break;
159163
}
160164
eprintln!("Running iteration {i} of {iteration_count}...");
161-
let process = SuspendedLaunchedProcess::launch_in_suspended_state(
162-
&command_name,
163-
command_args,
164-
env_vars,
165-
)
166-
.unwrap();
165+
let process =
166+
SuspendedLaunchedProcess::launch_in_suspended_state(&command_name, &args, &env_vars)
167+
.unwrap();
167168
let pid = process.pid();
168169

169170
// Tell the sampler to start profiling another pid, and wait for it to signal us to go ahead.

samply/src/mac/profiler.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ use serde_json::to_writer;
33

44
use std::collections::hash_map::Entry;
55
use std::collections::HashMap;
6-
use std::ffi::OsString;
76
use std::fs::File;
87
use std::io::BufWriter;
98
use std::process::ExitStatus;
@@ -17,7 +16,7 @@ use super::process_launcher::{MachError, ReceivedStuff, TaskAccepter};
1716
use super::sampler::{JitdumpOrMarkerPath, Sampler, TaskInit};
1817
use super::time::get_monotonic_timestamp;
1918
use crate::server::{start_server_main, ServerProps};
20-
use crate::shared::recording_props::{ProfileCreationProps, RecordingProps};
19+
use crate::shared::recording_props::{ProcessLaunchProps, ProfileCreationProps, RecordingProps};
2120

2221
pub fn start_profiling_pid(
2322
_pid: u32,
@@ -31,17 +30,22 @@ pub fn start_profiling_pid(
3130
}
3231

3332
pub fn start_recording(
34-
command_name: OsString,
35-
command_args: &[OsString],
36-
env_vars: &[(OsString, OsString)],
37-
iteration_count: u32,
33+
process_launch_props: ProcessLaunchProps,
3834
recording_props: RecordingProps,
3935
profile_creation_props: ProfileCreationProps,
4036
server_props: Option<ServerProps>,
4137
) -> Result<ExitStatus, MachError> {
42-
let (task_sender, task_receiver) = unbounded();
38+
let ProcessLaunchProps {
39+
env_vars,
40+
command_name,
41+
args,
42+
iteration_count,
43+
} = process_launch_props;
4344
let command_name_copy = command_name.to_string_lossy().to_string();
4445
let output_file = recording_props.output_file.clone();
46+
47+
let (task_sender, task_receiver) = unbounded();
48+
4549
let sampler_thread = thread::spawn(move || {
4650
let sampler = Sampler::new(
4751
command_name_copy,
@@ -63,8 +67,7 @@ pub fn start_recording(
6367
)
6468
.expect("cannot register signal handler");
6569

66-
let (mut task_accepter, task_launcher) =
67-
TaskAccepter::new(&command_name, command_args, env_vars)?;
70+
let (mut task_accepter, task_launcher) = TaskAccepter::new(&command_name, &args, &env_vars)?;
6871

6972
let (accepter_sender, accepter_receiver) = unbounded();
7073
let accepter_thread = thread::spawn(move || {

samply/src/main.rs

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ mod shared;
1212

1313
use clap::{Args, Parser, Subcommand};
1414
use profile_json_preparse::parse_libinfo_map_from_profile_file;
15-
use shared::recording_props::{ProfileCreationProps, RecordingProps};
15+
use shared::recording_props::{ProcessLaunchProps, ProfileCreationProps, RecordingProps};
1616

17-
use std::ffi::{OsStr, OsString};
17+
use std::ffi::OsStr;
1818
use std::fs::File;
1919
use std::io::{BufReader, BufWriter};
2020
use std::path::{Path, PathBuf};
@@ -237,6 +237,7 @@ fn main() {
237237

238238
#[cfg(any(target_os = "android", target_os = "macos", target_os = "linux"))]
239239
Action::Record(record_args) => {
240+
let process_launch_props = record_args.process_launch_props();
240241
let recording_props = record_args.recording_props();
241242
let profile_creation_props = record_args.profile_creation_props();
242243
let server_props = record_args.server_props();
@@ -249,12 +250,8 @@ fn main() {
249250
server_props,
250251
);
251252
} else {
252-
let (env_vars, command_name, args) = parse_command(&record_args.command);
253253
let exit_status = match profiler::start_recording(
254-
command_name,
255-
args,
256-
&env_vars,
257-
record_args.iteration_count,
254+
process_launch_props,
258255
recording_props,
259256
profile_creation_props,
260257
server_props,
@@ -330,13 +327,45 @@ impl RecordArgs {
330327
}
331328
}
332329

330+
pub fn process_launch_props(&self) -> ProcessLaunchProps {
331+
let command = &self.command;
332+
let iteration_count = self.iteration_count;
333+
assert!(
334+
!command.is_empty(),
335+
"CLI parsing should have ensured that we have at least one command name"
336+
);
337+
338+
let mut env_vars = Vec::new();
339+
let mut i = 0;
340+
while let Some((var_name, var_val)) = command.get(i).and_then(|s| split_at_first_equals(s))
341+
{
342+
env_vars.push((var_name.to_owned(), var_val.to_owned()));
343+
i += 1;
344+
}
345+
if i == command.len() {
346+
eprintln!("Error: No command name found. Every item looks like an environment variable (contains '='): {command:?}");
347+
std::process::exit(1);
348+
}
349+
let command_name = command[i].clone();
350+
let args = command[(i + 1)..].to_owned();
351+
ProcessLaunchProps {
352+
env_vars,
353+
command_name,
354+
args,
355+
iteration_count,
356+
}
357+
}
358+
333359
#[allow(unused)]
334360
pub fn profile_creation_props(&self) -> ProfileCreationProps {
335-
let profile_name = match (self.profile_creation_args.profile_name.clone(), self.pid, self.command.first()) {
336-
(Some(profile_name), _, _) => profile_name,
337-
(None, Some(pid), _) => format!("PID {pid}"),
338-
(None, None, Some(command)) => command.to_string_lossy().to_string(),
339-
(None, None, None) => panic!("Either pid or command is guaranteed to be present (clap should have done the validation)"),
361+
let profile_name = match (self.profile_creation_args.profile_name.clone(), self.pid) {
362+
(Some(profile_name), _) => profile_name,
363+
(None, Some(pid)) => format!("PID {pid}"),
364+
_ => self
365+
.process_launch_props()
366+
.command_name
367+
.to_string_lossy()
368+
.to_string(),
340369
};
341370
ProfileCreationProps {
342371
profile_name,
@@ -384,28 +413,6 @@ fn split_at_first_equals(s: &OsStr) -> Option<(&OsStr, &OsStr)> {
384413
Some((name, val))
385414
}
386415

387-
#[allow(unused)]
388-
fn parse_command(command: &[OsString]) -> (Vec<(OsString, OsString)>, OsString, &[OsString]) {
389-
assert!(
390-
!command.is_empty(),
391-
"CLI parsing should have ensured that we have at least one command name"
392-
);
393-
394-
let mut env_vars = Vec::new();
395-
let mut i = 0;
396-
while let Some((var_name, var_val)) = command.get(i).and_then(|s| split_at_first_equals(s)) {
397-
env_vars.push((var_name.to_owned(), var_val.to_owned()));
398-
i += 1;
399-
}
400-
if i == command.len() {
401-
eprintln!("Error: No command name found. Every item looks like an environment variable (contains '='): {command:?}");
402-
std::process::exit(1);
403-
}
404-
let command_name = command[i].clone();
405-
let args = &command[(i + 1)..];
406-
(env_vars, command_name, args)
407-
}
408-
409416
fn convert_file_to_profile(
410417
filename: &Path,
411418
input_file: &File,

samply/src/shared/recording_props.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,28 @@
1-
use std::{path::PathBuf, time::Duration};
1+
use std::{ffi::OsString, path::PathBuf, time::Duration};
22

3+
/// Properties which are meaningful both for recording a fresh process
4+
/// as well as for recording an existing process.
35
pub struct RecordingProps {
46
pub output_file: PathBuf,
57
pub time_limit: Option<Duration>,
68
pub interval: Duration,
79
pub main_thread_only: bool,
810
}
911

12+
/// Properties which are meaningful both for recording a profile and
13+
/// for converting a perf.data file to a profile.
1014
pub struct ProfileCreationProps {
1115
pub profile_name: String,
1216
/// Merge non-overlapping threads of the same name.
1317
pub reuse_threads: bool,
1418
/// Fold repeated frames at the base of the stack.
1519
pub fold_recursive_prefix: bool,
1620
}
21+
22+
/// Properties which are meaningful for launching and recording a fresh process.
23+
pub struct ProcessLaunchProps {
24+
pub env_vars: Vec<(OsString, OsString)>,
25+
pub command_name: OsString,
26+
pub args: Vec<OsString>,
27+
pub iteration_count: u32,
28+
}

0 commit comments

Comments
 (0)