1use std::borrow::ToOwned;
8use std::collections::HashMap;
9use std::thread;
10
11use ipc_channel::ipc::{self, IpcReceiver};
12use ipc_channel::router::ROUTER;
13use log::debug;
14use profile_traits::mem::{
15 MemoryReport, MemoryReportResult, ProfilerChan, ProfilerMsg, Report, Reporter, ReporterRequest,
16 ReportsChan,
17};
18
19use crate::system_reporter;
20
21pub struct Profiler {
22 pub port: IpcReceiver<ProfilerMsg>,
24
25 reporters: HashMap<String, Reporter>,
27}
28
29impl Profiler {
30 pub fn create() -> ProfilerChan {
31 let (chan, port) = ipc::channel().unwrap();
32
33 thread::Builder::new()
36 .name("MemoryProfiler".to_owned())
37 .spawn(move || {
38 let mut mem_profiler = Profiler::new(port);
39 mem_profiler.start();
40 })
41 .expect("Thread spawning failed");
42
43 let mem_profiler_chan = ProfilerChan(chan);
44
45 let (system_reporter_sender, system_reporter_receiver) = ipc::channel().unwrap();
49 ROUTER.add_typed_route(
50 system_reporter_receiver,
51 Box::new(|message| {
52 let request: ReporterRequest = message.unwrap();
53 system_reporter::collect_reports(request)
54 }),
55 );
56 mem_profiler_chan.send(ProfilerMsg::RegisterReporter(
57 "system-main".to_owned(),
58 Reporter(system_reporter_sender),
59 ));
60
61 mem_profiler_chan
62 }
63
64 pub fn new(port: IpcReceiver<ProfilerMsg>) -> Profiler {
65 Profiler {
66 port,
67 reporters: HashMap::new(),
68 }
69 }
70
71 pub fn start(&mut self) {
72 while let Ok(msg) = self.port.recv() {
73 if !self.handle_msg(msg) {
74 break;
75 }
76 }
77 }
78
79 fn handle_msg(&mut self, msg: ProfilerMsg) -> bool {
80 match msg {
81 ProfilerMsg::RegisterReporter(name, reporter) => {
82 debug!("Registering memory reporter: {}", name);
83 let name_clone = name.clone();
85 match self.reporters.insert(name, reporter) {
86 None => true,
87 Some(_) => panic!("RegisterReporter: '{}' name is already in use", name_clone),
88 }
89 },
90
91 ProfilerMsg::UnregisterReporter(name) => {
92 debug!("Unregistering memory reporter: {}", name);
93 match self.reporters.remove(&name) {
95 Some(_) => true,
96 None => panic!("UnregisterReporter: '{}' name is unknown", &name),
97 }
98 },
99
100 ProfilerMsg::Report(sender) => {
101 let main_pid = std::process::id();
102
103 let reports = self.collect_reports();
104 let results: Vec<MemoryReport> = reports
107 .into_iter()
108 .map(|(pid, reports)| MemoryReport {
109 pid,
110 reports,
111 is_main_process: pid == main_pid,
112 })
113 .collect();
114 let _ = sender.send(MemoryReportResult { results });
115 servo_allocator::dump_unmeasured();
116 true
117 },
118 ProfilerMsg::Exit => false,
119 }
120 }
121
122 fn collect_reports(&self) -> HashMap<u32, Vec<Report>> {
124 let mut result = HashMap::new();
125
126 for reporter in self.reporters.values() {
127 let (chan, port) = ipc::channel().unwrap();
128 reporter.collect_reports(ReportsChan(chan));
129 if let Ok(mut reports) = port.recv() {
130 result
131 .entry(reports.pid)
132 .or_insert(vec![])
133 .append(&mut reports.reports);
134 }
135 }
136 result
137 }
138}