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}