Currently just deserializes into a typed representation of the relevant arguments, without actually using them. The next step will be to change this by attaching the self-profile data to crates in the crate graph.
L6QJNN62E4LA65OOTWGFK25QQNIJGLK3SJEEKAHOB5B7RXDUEZUQC BRXHJFU7ANVWOFXBNR5EYUGDURCFCZIF66VDV6L3JBYC45CAFNJAC 7CVIL7UJBYEZ4KHKPJ7ZYSVQ7BLQWWUSJLJR5FOXBICQTD5ETK4QC UXJFRBBL7IZ2PR7ZYNFGOJ6A7EH5ZVLYVFLA3HNFCNRVL6KWJUDAC LOR3KOXGQ2VYGDHXQ6MG22ZME5TMPFTUW7A5OG36IAVQANOCXBRAC ZPFD3275NTWST7F5YCWYOOUS3QB5HE23LUOZXZIND4HBFSX62NCQC UQJO24KBYI77E4J6LXWX2IUN7VABQKG6PGKBKWEPDCH5CKYBTC4AC JVYWRCPTXQUCJ2BYOWAU36BM5ZKJ5FLKHIKMLSJA7XWOVIY2DMDQC Q3Z6XMP5FFCC3PWC5FSV4C6ICNDPMKMELOV7MYQGC5A42LVHGVPAC use std::cell::OnceCell;use std::collections::HashMap;use std::convert::Infallible;use std::str::FromStr;use analyzeme::ProfilingData;use camino::Utf8PathBuf;use cargo_metadata::Edition;use serde::de::value::StrDeserializer;use serde::Deserialize;/// When a crate name is set to 3 underscores (`___`), we assume this is cargo/// probing rustc without actually compiling anything. Clarification:/// https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/New.20visualizations.20for.20cargo-timing.2Ehtml/near/436821099const CARGO_PROBE_CRATE_NAME: &str = "___";const PROFDATA_EXTENSION: &str = "mm_profdata";const PROFILES_DIR: &str = "target/profiles";#[derive(Clone, Copy, Debug, Deserialize)]#[serde(rename_all = "kebab-case")]pub enum Emit {#[serde(rename = "asm")]Assembly,DepInfo,Link,#[serde(rename = "llvm-bc")]LlvmBitcode,LlvmIr,Metadata,Mir,#[serde(rename = "obj")]Object,}#[derive(Clone, Debug)]pub enum RustcFilename {StandardInput,File(Utf8PathBuf),}impl FromStr for RustcFilename {type Err = Infallible;fn from_str(s: &str) -> Result<Self, Self::Err> {Ok(match s {// Cargo sets the filename to be a single hyphen (`-`: U+002D) when probing rustc// This instructs rustc to read the file from standard input"-" => Self::StandardInput,_ => {let path = Utf8PathBuf::from_str(s).unwrap();// TODO: some build-scripts seem to generate relative paths, so the following code would fail:// assert!(path_buf.is_file(), "Path does not exist: {path_buf:#?}");Self::File(path)}})}}#[derive(Clone, Debug)]// TODO: investigate using `rustc --print` to validate this information// TODO: reference data directlypub struct RustcArgs {pub filename: RustcFilename,pub crate_name: String,pub edition: Edition,pub crate_types: Vec<super::timings::TargetKind>,pub emit: Vec<Emit>,pub externs: Vec<String>,pub codegen_options: Vec<String>,pub unstable_options: Vec<String>,pub extra_args: HashMap<String, Vec<String>>,}impl RustcArgs {fn single_value(map: &mut HashMap<String, Vec<String>>, key: &str) -> Option<String> {let mut values = map.remove(key)?;assert_eq!(values.len(), 1);Some(values.remove(0))}pub fn parse_from(source: &str) -> Self {let mut args_map: HashMap<String, Vec<String>> = HashMap::new();let mut arg_chunks = source.split_ascii_whitespace();let command_name = arg_chunks.next().expect("Arguments cannot be empty");assert_eq!(command_name, "rustc");let filename = OnceCell::new();while let Some(chunk) = arg_chunks.next() {let (prefix, arg) = if let Some(long_arg) = chunk.strip_prefix("--") {("--", long_arg)} else if let Some(short_arg) = chunk.strip_prefix('-')&& chunk != "-"{("-", short_arg)} else {let insertion_result = filename.set(chunk.to_string());if let Err(previous_filename) = insertion_result {panic!("Found multiple unexpected arguments, unable to parse filename. First: `{previous_filename}` Second: `{chunk}`")}continue;};let (arg_name, value) = if let Some((name, value)) = chunk.split_once('=') {(name.strip_prefix(prefix).unwrap(), value)} else {let value = arg_chunks.next().expect(&format!("No arguments left, but expected value for argument: `{chunk}`"));(arg, value)};args_map.entry(arg_name.to_string()).or_default().push(value.to_string());}Self {filename: RustcFilename::from_str(filename.get().expect("Missing filename in arguments"),).unwrap(),crate_name: Self::single_value(&mut args_map, "crate-name").unwrap(),edition: Self::single_value(&mut args_map, "edition").map(|edition| deserialize_string(&edition)).unwrap_or(Edition::E2015),crate_types: args_map.remove("crate-type").map(deserialize_vec).unwrap_or_default(),emit: args_map.remove("emit").map(deserialize_vec).unwrap_or_default(),externs: args_map.remove("extern").unwrap_or_default(),codegen_options: args_map.remove("C").unwrap_or_default(),unstable_options: args_map.remove("Z").unwrap_or_default(),extra_args: args_map,}}}#[derive(Debug)]pub struct SelfProfile {pub args: RustcArgs,pub data: ProfilingData,}#[derive(Debug)]pub struct ProfileCollection {pub crates: Vec<SelfProfile>,pub probes: Vec<SelfProfile>,}impl ProfileCollection {pub fn new() -> Self {let mut crate_profiles = Vec::new();let mut probe_profiles = Vec::new();let valid_profiles = std::fs::read_dir(PROFILES_DIR).unwrap().into_iter().filter_map(|potential_entry| potential_entry.ok()).filter_map(|entry| Utf8PathBuf::from_path_buf(entry.path()).ok()).filter(|path| path.is_file()).filter(|path| path.extension() == Some(&PROFDATA_EXTENSION));for profile_path in valid_profiles {let relative_filename = profile_path.file_name().unwrap();let (file_name, _extension) = relative_filename.rsplit_once('.').unwrap();// Self-profiles have paths like:// CRATE_NAME-RUSTC_PID.mm_profdata// We need to special-case the crate name cargo uses when probing rustc;// these profiles are not associated to any specific crate in the graphlet (crate_name, process_id) = file_name.rsplit_once('-').unwrap();// Make sure PID is only numbersassert!(process_id.chars().all(|character| character.is_numeric()),"{}",process_id);let data = ProfilingData::new(profile_path.as_std_path()).unwrap();let cmd = &data.metadata().cmd;let args = RustcArgs::parse_from(cmd);match args.filename {RustcFilename::StandardInput => {if crate_name != CARGO_PROBE_CRATE_NAME {if let Some(suffix) = crate_name.strip_prefix("probe") {assert!(suffix.chars().all(|c| c.is_numeric()),"Unexpected suffix for probe crate name: {suffix}");} else {panic!("Unexpected probe with name: {crate_name}")}}probe_profiles.push(SelfProfile { args, data });}RustcFilename::File(_) => {crate_profiles.push(SelfProfile { args, data });}}}Self {crates: crate_profiles,probes: probe_profiles,}}}fn deserialize_string<'a, T: Deserialize<'a>>(source: &str) -> T {let deserializer = StrDeserializer::<serde::de::value::Error>::new(source);T::deserialize(deserializer).unwrap()}fn deserialize_vec<'a, T: Deserialize<'a>>(source: Vec<String>) -> Vec<T> {source.iter().map(|arg| arg.split_terminator(',')).flatten().map(deserialize_string::<T>).collect()}
name = "analyzeme"version = "11.0.1"source = "git+https://github.com/rust-lang/measureme#7bbe7a985a1ed5cc1043e1c8582b4c7e17efc5b7"dependencies = ["analyzeme 9.2.0","decodeme 10.1.2","decodeme 11.0.1","measureme 10.1.2","measureme 11.0.1","memchr","rustc-hash","serde",][[package]]
[[package]]name = "decodeme"version = "10.1.2"source = "git+https://github.com/rust-lang/measureme?tag=10.1.2#f9f84d1a79c46e9927926c177c33eb3ea3c72979"dependencies = ["measureme 10.1.2","memchr","rustc-hash","serde","serde_json",][[package]]name = "decodeme"version = "11.0.1"source = "git+https://github.com/rust-lang/measureme#7bbe7a985a1ed5cc1043e1c8582b4c7e17efc5b7"dependencies = ["measureme 11.0.1","memchr","rustc-hash","serde","serde_json",]
name = "measureme"version = "9.2.0"source = "git+https://github.com/rust-lang/measureme?tag=9.2.0#9f51cde2e5dd3ef0392f0f6a7201f4946502ef41"dependencies = ["log","memmap2","parking_lot 0.11.2","perf-event-open-sys 1.0.1","rustc-hash","smallvec",][[package]]name = "measureme"version = "10.1.2"source = "git+https://github.com/rust-lang/measureme?tag=10.1.2#f9f84d1a79c46e9927926c177c33eb3ea3c72979"dependencies = ["log","memmap2","parking_lot 0.12.1","perf-event-open-sys 3.0.0","rustc-hash","smallvec",][[package]]name = "measureme"version = "11.0.1"source = "git+https://github.com/rust-lang/measureme#7bbe7a985a1ed5cc1043e1c8582b4c7e17efc5b7"dependencies = ["log","memmap2","parking_lot 0.12.1","perf-event-open-sys 3.0.0","rustc-hash","smallvec",][[package]]
"parking_lot_core",
"parking_lot_core 0.9.9",][[package]]name = "parking_lot_core"version = "0.8.6"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc"dependencies = ["cfg-if","instant","libc","redox_syscall 0.2.16","smallvec","winapi",
name = "perf-event-open-sys"version = "1.0.1"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ce9bedf5da2c234fdf2391ede2b90fabf585355f33100689bc364a3ea558561a"dependencies = ["libc",][[package]]name = "perf-event-open-sys"version = "3.0.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "b29be2ba35c12c6939f6bc73187f728bba82c3c062ecdc5fa90ea739282a1f58"dependencies = ["libc",][[package]]
name = "winapi"version = "0.3.9"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"dependencies = ["winapi-i686-pc-windows-gnu","winapi-x86_64-pc-windows-gnu",][[package]]name = "winapi-i686-pc-windows-gnu"version = "0.4.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"[[package]]name = "winapi-x86_64-pc-windows-gnu"version = "0.4.0"source = "registry+https://github.com/rust-lang/crates.io-index"checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"[[package]]