Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/install-rust/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ runs:
steps:
- name: Install rust-toolchain.toml
shell: bash
run: rustup toolchain install
run: rustup show
- name: Install additional rustup components
if: inputs.components != ''
shell: bash
Expand Down
99 changes: 98 additions & 1 deletion src/executor/wall_time/profiler/perf/parse_perf_file.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::loaded_module::LoadedModule;
use super::loaded_module::{LoadedModule, ProcessLoadedModule};
use super::module_symbols::ModuleSymbols;
use super::unwind_data::unwind_data_from_elf;
use crate::prelude::*;
Expand Down Expand Up @@ -57,12 +57,23 @@ pub fn parse_for_memmap2<P: AsRef<Path>>(
continue;
};

// Thread creation also emits FORK, which share parent's address space, so nothing to do
if fork_record.ppid == fork_record.pid {
continue;
}

if pid_filter.add_child_if_parent_tracked(fork_record.ppid, fork_record.pid) {
trace!(
"Fork: Tracking child PID {} from parent PID {}",
fork_record.pid, fork_record.ppid
);
}

inherit_parent_mappings(
&mut loaded_modules_by_path,
fork_record.ppid,
fork_record.pid,
);
}
RecordType::MMAP2 => {
let Ok(parsed_record) = record.parse() else {
Expand Down Expand Up @@ -134,6 +145,37 @@ impl PidFilter {
}
}

/// Copy every module the parent pid has mounted onto the child pid.
///
/// Forked processes inherit their parent's memory mappings, but there will not be any MMAP2 record
/// in the perf data since the mapping has already happened.
fn inherit_parent_mappings(
loaded_modules_by_path: &mut HashMap<PathBuf, LoadedModule>,
ppid: pid_t,
pid: pid_t,
) {
use std::collections::hash_map::Entry;

for loaded_module in loaded_modules_by_path.values_mut() {
let inherited =
loaded_module
.process_loaded_modules
.get(&ppid)
.map(|p| ProcessLoadedModule {
symbols_load_bias: p.symbols_load_bias,
process_unwind_data: p.process_unwind_data.clone(),
});
let Some(inherited) = inherited else {
continue;
};
// Only insert if the child has no entry yet; an existing entry came from
// the child's own MMAP2 and is authoritative.
if let Entry::Vacant(slot) = loaded_module.process_loaded_modules.entry(pid) {
slot.insert(inherited);
}
}
}

/// Process a single MMAP2 record and add it to the symbols and unwind data maps
fn process_mmap2_record(
record: linux_perf_data::linux_perf_event_reader::Mmap2Record,
Expand Down Expand Up @@ -223,3 +265,58 @@ fn process_mmap2_record(
}
};
}

#[cfg(test)]
mod tests {
use super::*;

fn make_module_with_parent(ppid: pid_t, load_bias: u64) -> LoadedModule {
let mut m = LoadedModule::default();
m.process_loaded_modules.insert(
ppid,
ProcessLoadedModule {
symbols_load_bias: Some(load_bias),
process_unwind_data: None,
},
);
m
}

#[test]
fn inherit_parent_mappings_copies_parent_entry_to_child() {
let mut modules: HashMap<PathBuf, LoadedModule> = HashMap::new();
modules.insert(
PathBuf::from("/lib/libpython.so"),
make_module_with_parent(100, 0xdead),
);

inherit_parent_mappings(&mut modules, 100, 200);

let m = &modules[&PathBuf::from("/lib/libpython.so")];
let child = m.process_loaded_modules.get(&200).unwrap();
assert_eq!(child.symbols_load_bias, Some(0xdead));
}

#[test]
fn inherit_parent_mappings_does_not_overwrite_existing_child_entry() {
let mut modules: HashMap<PathBuf, LoadedModule> = HashMap::new();
let mut m = make_module_with_parent(100, 0xdead);
// Child already has its own (post-exec) mapping at a different bias.
m.process_loaded_modules.insert(
200,
ProcessLoadedModule {
symbols_load_bias: Some(0xcafe),
process_unwind_data: None,
},
);
modules.insert(PathBuf::from("/lib/libpython.so"), m);

inherit_parent_mappings(&mut modules, 100, 200);

let child = modules[&PathBuf::from("/lib/libpython.so")]
.process_loaded_modules
.get(&200)
.unwrap();
assert_eq!(child.symbols_load_bias, Some(0xcafe));
}
}
Loading