1use std::borrow::ToOwned;
8use std::collections::HashMap;
9use std::fs::File;
10use std::thread;
11
12use log::debug;
13use profile_traits::mem::{
14 MemoryReport, MemoryReportResult, ProfilerChan, ProfilerMsg, Report, Reporter, ReporterRequest,
15 ReportsChan,
16};
17use servo_base::generic_channel::{self, GenericCallback, GenericReceiver};
18
19use crate::system_reporter;
20
21const LOG_FILE_VAR: &str = "UNTRACKED_LOG_FILE";
22
23pub struct Profiler {
24 pub port: GenericReceiver<ProfilerMsg>,
26
27 reporters: HashMap<String, Reporter>,
29}
30
31impl Profiler {
32 pub fn create() -> ProfilerChan {
33 let (chan, port) = generic_channel::channel().unwrap();
34
35 if servo_allocator::is_tracking_unmeasured() && std::env::var(LOG_FILE_VAR).is_err() {
36 eprintln!("Allocation tracking is enabled but {LOG_FILE_VAR} is unset.");
37 servo_allocator::disable_unmeasured_tracking();
38 }
39
40 thread::Builder::new()
43 .name("MemoryProfiler".to_owned())
44 .spawn(move || {
45 let mut mem_profiler = Profiler::new(port);
46 mem_profiler.start();
47 })
48 .expect("Thread spawning failed");
49
50 let mem_profiler_chan = ProfilerChan(chan);
51
52 let callback = GenericCallback::new(|message| {
56 let request: ReporterRequest = message.unwrap();
57 system_reporter::collect_reports(request)
58 })
59 .expect("Could not create system reporter callback");
60 mem_profiler_chan.send(ProfilerMsg::RegisterReporter(
61 "system-main".to_owned(),
62 Reporter(callback),
63 ));
64
65 mem_profiler_chan
66 }
67
68 pub fn new(port: GenericReceiver<ProfilerMsg>) -> Profiler {
69 Profiler {
70 port,
71 reporters: HashMap::new(),
72 }
73 }
74
75 pub fn start(&mut self) {
76 while let Ok(msg) = self.port.recv() {
77 if !self.handle_msg(msg) {
78 break;
79 }
80 }
81 }
82
83 fn handle_msg(&mut self, msg: ProfilerMsg) -> bool {
84 match msg {
85 ProfilerMsg::RegisterReporter(name, reporter) => {
86 debug!("Registering memory reporter: {}", name);
87 let name_clone = name.clone();
89 match self.reporters.insert(name, reporter) {
90 None => true,
91 Some(_) => panic!("RegisterReporter: '{}' name is already in use", name_clone),
92 }
93 },
94
95 ProfilerMsg::UnregisterReporter(name) => {
96 debug!("Unregistering memory reporter: {}", name);
97 match self.reporters.remove(&name) {
99 Some(_) => true,
100 None => panic!("UnregisterReporter: '{}' name is unknown", name),
101 }
102 },
103
104 ProfilerMsg::Report(sender) => {
105 let main_pid = std::process::id();
106
107 let reports = self.collect_reports();
108 let results: Vec<MemoryReport> = reports
111 .into_iter()
112 .map(|(pid, reports)| MemoryReport {
113 pid,
114 reports,
115 is_main_process: pid == main_pid,
116 })
117 .collect();
118 let _ = sender.send(MemoryReportResult { results });
119
120 if let Ok(value) = std::env::var(LOG_FILE_VAR) {
121 match File::create(&value) {
122 Ok(file) => {
123 servo_allocator::dump_unmeasured(file);
124 },
125 Err(error) => {
126 log::error!("Error creating log file: {error:?}");
127 },
128 }
129 }
130 true
131 },
132 ProfilerMsg::Exit => false,
133 }
134 }
135
136 fn collect_reports(&self) -> HashMap<u32, Vec<Report>> {
138 let mut result = HashMap::new();
139
140 for reporter in self.reporters.values() {
141 let (chan, port) = generic_channel::channel().unwrap();
142 reporter.collect_reports(ReportsChan(chan));
143 if let Ok(mut reports) = port.recv() {
144 result
145 .entry(reports.pid)
146 .or_insert(vec![])
147 .append(&mut reports.reports);
148 }
149 }
150 result
151 }
152}