profile/
mem.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Memory profiling functions.
6
7use 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    /// The port through which messages are received.
23    pub port: IpcReceiver<ProfilerMsg>,
24
25    /// Registered memory reporters.
26    reporters: HashMap<String, Reporter>,
27}
28
29impl Profiler {
30    pub fn create() -> ProfilerChan {
31        let (chan, port) = ipc::channel().unwrap();
32
33        // Always spawn the memory profiler. If there is no timer thread it won't receive regular
34        // `Print` events, but it will still receive the other events.
35        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        // Register the system memory reporter, which will run on its own thread. It never needs to
46        // be unregistered, because as long as the memory profiler is running the system memory
47        // reporter can make measurements.
48        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                // Panic if it has already been registered.
84                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                // Panic if it hasn't previously been registered.
94                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                // Turn the pid -> reports map into a vector and add the
105                // hint to find the main process.
106                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    /// Returns a map of pid -> reports
123    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}