linux_perf_data/
dso_key.rs

1use linux_perf_event_reader::CpuMode;
2
3/// A canonicalized key which can be used to cross-reference an Mmap record with
4/// an entry in the perf file's build ID list.
5///
6/// This is needed because the entries sometimes don't have matching path strings.
7///
8/// Examples:
9///
10///  - Mmap path "[kernel.kallsyms]_text" + build ID map entry path "[kernel.kallsyms]"
11///  - Mmap path "[kernel.kallsyms]_text" + build ID map entry path "/full/path/to/vmlinux"
12#[derive(Debug, Clone, PartialEq, Eq, Hash)]
13pub enum DsoKey {
14    Kernel,
15    GuestKernel,
16    Vdso32,
17    VdsoX32,
18    Vdso64,
19    Vsyscall,
20    KernelModule {
21        /// The name of the kernel module, without file extension, e.g. "snd-seq-device".
22        ///
23        /// We don't store the full path in the key because the name is enough to
24        /// uniquely identify the kernel module.
25        name: String,
26    },
27    User {
28        /// The file name of the user-space DSO.
29        file_name: String,
30        /// The full path of the user-space DSO. This must be part of the key because
31        /// there could be multiple DSOs with the same file name at different paths.
32        full_path: Vec<u8>,
33    },
34}
35
36impl DsoKey {
37    /// Make a `DsoKey` from a path and a `CpuMode` (which usually comes from a `misc` field).
38    ///
39    /// Returns `None` for things which cannot be detected as a DSO, such as `//anon` mappings.
40    pub fn detect(path: &[u8], cpu_mode: CpuMode) -> Option<Self> {
41        if path == b"//anon" || path == b"[stack]" || path == b"[heap]" || path == b"[vvar]" {
42            return None;
43        }
44
45        if path.starts_with(b"[kernel.kallsyms]") {
46            let dso_key = if cpu_mode == CpuMode::GuestKernel {
47                DsoKey::GuestKernel
48            } else {
49                DsoKey::Kernel
50            };
51            return Some(dso_key);
52        }
53        if path.starts_with(b"[guest.kernel.kallsyms") {
54            return Some(DsoKey::GuestKernel);
55        }
56        if path == b"[vdso32]" {
57            return Some(DsoKey::Vdso32);
58        }
59        if path == b"[vdsox32]" {
60            return Some(DsoKey::VdsoX32);
61        }
62        if path == b"[vdso]" {
63            // TODO: I think this could also be Vdso32 when recording on a 32 bit machine.
64            return Some(DsoKey::Vdso64);
65        }
66        if path == b"[vsyscall]" {
67            return Some(DsoKey::Vsyscall);
68        }
69        if (cpu_mode == CpuMode::Kernel || cpu_mode == CpuMode::GuestKernel)
70            && path.starts_with(b"[")
71        {
72            return Some(DsoKey::KernelModule {
73                name: String::from_utf8_lossy(path).into(),
74            });
75        }
76
77        let filename = if let Some(final_slash_pos) = path.iter().rposition(|b| *b == b'/') {
78            &path[final_slash_pos + 1..]
79        } else {
80            path
81        };
82
83        let dso_key = match (cpu_mode, filename.strip_suffix(b".ko")) {
84            (CpuMode::Kernel | CpuMode::GuestKernel, Some(kmod_name)) => {
85                // "/lib/modules/5.13.0-35-generic/kernel/sound/core/snd-seq-device.ko" -> "[snd-seq-device]"
86                let kmod_name = String::from_utf8_lossy(kmod_name);
87                DsoKey::KernelModule {
88                    name: format!("[{}]", kmod_name),
89                }
90            }
91            (CpuMode::Kernel, _) => DsoKey::Kernel,
92            (CpuMode::GuestKernel, _) => DsoKey::GuestKernel,
93            (CpuMode::User | CpuMode::GuestUser, _) => DsoKey::User {
94                file_name: String::from_utf8_lossy(filename).into(),
95                full_path: path.to_owned(),
96            },
97            _ => return None,
98        };
99        Some(dso_key)
100    }
101
102    /// The name string for this DSO. This is a short string that you'd want
103    /// to see in a profiler UI, for example.
104    pub fn name(&self) -> &str {
105        match self {
106            DsoKey::Kernel => "[kernel.kallsyms]", // or just "[kernel]"?
107            DsoKey::GuestKernel => "[guest.kernel.kallsyms]",
108            DsoKey::Vdso32 => "[vdso32]",
109            DsoKey::VdsoX32 => "[vdsox32]",
110            DsoKey::Vdso64 => "[vdso]",
111            DsoKey::Vsyscall => "[vsyscall]",
112            DsoKey::KernelModule { name } => name,
113            DsoKey::User { file_name, .. } => file_name,
114        }
115    }
116}