xref: /aosp_15_r20/system/extras/profcollectd/libprofcollectd/trace_provider/simpleperf_lbr.rs (revision 288bf5226967eb3dac5cce6c939ccc2a7f2b4fe5)
1 //
2 // Copyright (C) 2023 The Android Open Source Project
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 //! Trace provider backed by Intel LBR, using simpleperf tool.
18 use anyhow::{anyhow, Result};
19 use std::fs::{read_dir, remove_file};
20 use std::path::{Path, PathBuf};
21 use std::time::Duration;
22 use trace_provider::TraceProvider;
23 
24 use crate::trace_provider;
25 
26 static LBR_TRACEFILE_EXTENSION: &str = "lbrtrace";
27 static LBR_PROFILE_EXTENSION: &str = "data";
28 // Use a prime value to make sure that there are no weird interactions with e.g. short loops.
29 static LBR_SAMPLE_PERIOD: &str = "500009";
30 
31 pub struct SimpleperfLbrTraceProvider {}
32 
33 impl TraceProvider for SimpleperfLbrTraceProvider {
get_name(&self) -> &'static str34     fn get_name(&self) -> &'static str {
35         "simpleperf_lbr"
36     }
37 
is_ready(&self) -> bool38     fn is_ready(&self) -> bool {
39         true
40     }
41 
trace_system( &self, trace_dir: &Path, tag: &str, sampling_period: &Duration, binary_filter: &str, )42     fn trace_system(
43         &self,
44         trace_dir: &Path,
45         tag: &str,
46         sampling_period: &Duration,
47         binary_filter: &str,
48     ) {
49         let trace_file = trace_provider::get_path(trace_dir, tag, LBR_TRACEFILE_EXTENSION);
50         // Record ETM data for kernel space only when it's not filtered out by binary_filter. So we
51         // can get more ETM data for user space when ETM data for kernel space isn't needed.
52         let event_name = if binary_filter.contains("kernel") {
53             "BR_INST_RETIRED.NEAR_TAKEN"
54         } else {
55             "BR_INST_RETIRED.NEAR_TAKEN:u"
56         };
57         let duration: String = sampling_period.as_secs_f64().to_string();
58         let args: Vec<&str> = vec![
59             "-a",
60             "-e",
61             event_name,
62             "-c",
63             LBR_SAMPLE_PERIOD,
64             "--duration",
65             &duration,
66             "-b",
67             "--exclude-perf",
68             "--binary",
69             binary_filter,
70             "--no-dump-symbols",
71             "--no-dump-kernel-symbols",
72             "-o",
73             trace_file.to_str().unwrap(),
74         ];
75         simpleperf_profcollect::run_record_cmd(&args);
76     }
77 
trace_process( &self, trace_dir: &Path, tag: &str, sampling_period: &Duration, processes: &str, )78     fn trace_process(
79         &self,
80         trace_dir: &Path,
81         tag: &str,
82         sampling_period: &Duration,
83         processes: &str,
84     ) {
85         let trace_file = trace_provider::get_path(trace_dir, tag, LBR_TRACEFILE_EXTENSION);
86         let event_name = "BR_INST_RETIRED.NEAR_TAKEN:u";
87         let duration: String = sampling_period.as_secs_f64().to_string();
88         let args: Vec<&str> = vec![
89             "-p",
90             processes,
91             "-e",
92             event_name,
93             "-c",
94             LBR_SAMPLE_PERIOD,
95             "--duration",
96             &duration,
97             "-b",
98             "--no-dump-symbols",
99             "-o",
100             trace_file.to_str().unwrap(),
101         ];
102         simpleperf_profcollect::run_record_cmd(&args);
103     }
104 
process(&self, trace_dir: &Path, profile_dir: &Path, binary_filter: &str) -> Result<()>105     fn process(&self, trace_dir: &Path, profile_dir: &Path, binary_filter: &str) -> Result<()> {
106         let is_lbr_extension = |file: &PathBuf| {
107             file.extension()
108                 .and_then(|f| f.to_str())
109                 .filter(|ext| ext == &LBR_TRACEFILE_EXTENSION)
110                 .is_some()
111         };
112 
113         let process_trace_file = |trace_file: PathBuf| {
114             let mut profile_file = PathBuf::from(profile_dir);
115             profile_file.push(
116                 trace_file
117                     .file_name()
118                     .ok_or_else(|| anyhow!("Malformed trace path: {}", trace_file.display()))?,
119             );
120             profile_file.set_extension(LBR_PROFILE_EXTENSION);
121 
122             let args: Vec<&str> = vec![
123                 "-i",
124                 trace_file.to_str().unwrap(),
125                 "-o",
126                 profile_file.to_str().unwrap(),
127                 "--output",
128                 "branch-list",
129                 "--binary",
130                 binary_filter,
131             ];
132             simpleperf_profcollect::run_inject_cmd(&args);
133             remove_file(&trace_file)?;
134             Ok(())
135         };
136 
137         read_dir(trace_dir)?
138             .filter_map(|e| e.ok())
139             .map(|e| e.path())
140             .filter(|e| e.is_file())
141             .filter(is_lbr_extension)
142             .try_for_each(process_trace_file)
143     }
144 
set_log_file(&self, filename: &Path)145     fn set_log_file(&self, filename: &Path) {
146         simpleperf_profcollect::set_log_file(filename);
147     }
148 
reset_log_file(&self)149     fn reset_log_file(&self) {
150         simpleperf_profcollect::reset_log_file();
151     }
152 }
153 
154 impl SimpleperfLbrTraceProvider {
supported() -> bool155     pub fn supported() -> bool {
156         simpleperf_profcollect::is_lbr_available()
157     }
158 }
159