|
19 | 19 |
|
20 | 20 | use crate::profiling::mallctl; |
21 | 21 | use http::{header, Method, Request, Response, StatusCode}; |
22 | | -use std::{env, fs::File, io, num::ParseIntError, process::Command}; |
| 22 | +use std::env; |
23 | 23 |
|
24 | 24 | #[inline] |
25 | | -pub fn router(sym: &SymbolTable, req: Request<Vec<u8>>) -> http::Result<Response<Vec<u8>>> { |
| 25 | +pub fn router(req: Request<Vec<u8>>) -> http::Result<Response<Vec<u8>>> { |
26 | 26 | match (req.method(), req.uri().path()) { |
27 | 27 | (&Method::GET, "/pprof/conf") => get_pprof_conf_handler(req), |
28 | 28 | (&Method::POST, "/pprof/conf") => post_pprof_conf_handler(req), |
29 | 29 | (&Method::GET, "/pprof/heap") => get_pprof_heap_handler(req), |
30 | 30 | (&Method::GET, "/pprof/cmdline") => get_pprof_cmdline_handler(req), |
31 | | - (&Method::GET, "/pprof/symbol") => get_pprof_symbol_handler(sym, req), |
32 | | - (&Method::POST, "/pprof/symbol") => post_pprof_symbol_handler(sym, req), |
| 31 | + (&Method::GET, "/pprof/symbol") => get_pprof_symbol_handler(req), |
| 32 | + (&Method::POST, "/pprof/symbol") => post_pprof_symbol_handler(req), |
33 | 33 | (&Method::GET, "/pprof/stats") => get_pprof_stats_handler(req), |
34 | 34 | _ => { |
35 | 35 | let body = b"Bad Request\r\n"; |
@@ -128,27 +128,29 @@ pub fn get_pprof_cmdline_handler(_req: Request<Vec<u8>>) -> http::Result<Respons |
128 | 128 |
|
129 | 129 | /// HTTP handler for GET /pprof/symbol. |
130 | 130 | #[inline] |
131 | | -pub fn get_pprof_symbol_handler( |
132 | | - sym: &SymbolTable, |
133 | | - _req: Request<Vec<u8>>, |
134 | | -) -> http::Result<Response<Vec<u8>>> { |
135 | | - let num_symbols = sym.len(); |
136 | | - let body = format!("num_symbols: {num_symbols}\r\n"); |
137 | | - response_ok(body.into_bytes()) |
| 131 | +pub fn get_pprof_symbol_handler(_req: Request<Vec<u8>>) -> http::Result<Response<Vec<u8>>> { |
| 132 | + // TODO: any quick way to check if binary is stripped? |
| 133 | + let body = b"num_symbols: 1\r\n"; |
| 134 | + response_ok(body.to_vec()) |
138 | 135 | } |
139 | 136 |
|
140 | 137 | /// HTTP handler for POST /pprof/symbol. |
141 | 138 | #[inline] |
142 | | -pub fn post_pprof_symbol_handler( |
143 | | - sym: &SymbolTable, |
144 | | - req: Request<Vec<u8>>, |
145 | | -) -> http::Result<Response<Vec<u8>>> { |
| 139 | +pub fn post_pprof_symbol_handler(req: Request<Vec<u8>>) -> http::Result<Response<Vec<u8>>> { |
| 140 | + fn lookup_symbol(addr: u64) -> Option<String> { |
| 141 | + let mut s: Option<String> = None; |
| 142 | + backtrace::resolve(addr as *mut _, |symbol| { |
| 143 | + s = symbol.name().map(|n| n.to_string()); |
| 144 | + }); |
| 145 | + s |
| 146 | + } |
| 147 | + |
146 | 148 | let body = String::from_utf8_lossy(req.body()); |
147 | 149 | let addrs = body |
148 | 150 | .split('+') |
149 | 151 | .filter_map(|addr| u64::from_str_radix(addr.trim_start_matches("0x"), 16).ok()) |
150 | | - .map(|addr| (addr, sym.lookup_symbol(addr))) |
151 | | - .filter_map(|(addr, sym)| sym.map(|(_, sym)| (addr, sym))); |
| 152 | + .map(|addr| (addr, lookup_symbol(addr))) |
| 153 | + .filter_map(|(addr, sym)| sym.map(|sym| (addr, sym))); |
152 | 154 |
|
153 | 155 | let mut body = String::new(); |
154 | 156 | for (addr, sym) in addrs { |
@@ -207,145 +209,3 @@ fn response_err(msg: &str) -> http::Result<Response<Vec<u8>>> { |
207 | 209 | .header(header::CONTENT_LENGTH, msg.len()) |
208 | 210 | .body(msg.as_bytes().to_owned()) |
209 | 211 | } |
210 | | - |
211 | | -#[derive(Default, Debug)] |
212 | | -pub struct SymbolTable { |
213 | | - sym: Vec<(u64, String)>, |
214 | | - vstart: u64, |
215 | | - vend: u64, |
216 | | - fstart: u64, |
217 | | -} |
218 | | - |
219 | | -impl SymbolTable { |
220 | | - #[inline] |
221 | | - pub fn load() -> io::Result<Self> { |
222 | | - let nm_output = run_nm()?; |
223 | | - let (vstart, vend, fstart) = Self::load_mapping()?; |
224 | | - let mut sym = SymbolTable { sym: Vec::default(), vstart, vend, fstart }; |
225 | | - sym.read_nm(nm_output.as_ref()) |
226 | | - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; |
227 | | - Ok(sym) |
228 | | - } |
229 | | - |
230 | | - fn load_mapping() -> io::Result<(u64, u64, u64)> { |
231 | | - #[cfg(target_os = "linux")] |
232 | | - { |
233 | | - use std::io::Read; |
234 | | - |
235 | | - // TODO: clean up maps parsing, store all exec mappings |
236 | | - let exepath = env::current_exe()?; |
237 | | - let mut f = File::open("/proc/self/maps")?; |
238 | | - let mut buf = String::with_capacity(4096); |
239 | | - f.read_to_string(&mut buf)?; |
240 | | - for line in buf.lines() { |
241 | | - let parts: Vec<_> = line.splitn(6, ' ').map(str::trim).collect(); |
242 | | - if parts.len() < 6 { |
243 | | - continue; |
244 | | - } |
245 | | - if parts[5] == exepath.to_string_lossy() && parts[1] == "r-xp" { |
246 | | - let addr_range: Vec<_> = parts[0] |
247 | | - .splitn(2, '-') |
248 | | - .filter_map(|n| u64::from_str_radix(n, 16).ok()) |
249 | | - .collect(); |
250 | | - if addr_range.len() != 2 { |
251 | | - continue; |
252 | | - } |
253 | | - let file_offset = u64::from_str_radix(parts[2], 16) |
254 | | - .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; |
255 | | - |
256 | | - return Ok((addr_range[0], addr_range[1], file_offset)); |
257 | | - } |
258 | | - } |
259 | | - } |
260 | | - |
261 | | - Ok((u64::MAX, u64::MAX, 0)) |
262 | | - } |
263 | | - |
264 | | - fn read_nm(&mut self, output: &[u8]) -> Result<(), ParseIntError> { |
265 | | - use std::io::prelude::*; |
266 | | - |
267 | | - let b = io::Cursor::new(output); |
268 | | - for line in b.lines() { |
269 | | - let line = line.expect("no I/O, no panic"); |
270 | | - let parts: Vec<_> = line.split_ascii_whitespace().collect(); |
271 | | - if parts.len() < 3 || parts[0] == "U" { |
272 | | - continue; |
273 | | - } |
274 | | - if parts[1] != "t" && parts[1] != "T" { |
275 | | - continue; |
276 | | - } |
277 | | - |
278 | | - let address = u64::from_str_radix(parts[0].trim_start_matches("0x"), 16)?; |
279 | | - let symbol: String = parts[2..].join(" "); |
280 | | - let symbol = rustc_demangle::demangle(symbol.as_str()); |
281 | | - |
282 | | - self.sym.push((address, symbol.to_string())); |
283 | | - } |
284 | | - |
285 | | - self.sym.sort(); |
286 | | - |
287 | | - Ok(()) |
288 | | - } |
289 | | - |
290 | | - #[must_use] |
291 | | - #[inline] |
292 | | - pub fn len(&self) -> usize { |
293 | | - self.sym.len() |
294 | | - } |
295 | | - |
296 | | - #[must_use] |
297 | | - #[inline] |
298 | | - pub fn is_empty(&self) -> bool { |
299 | | - self.sym.is_empty() |
300 | | - } |
301 | | - |
302 | | - #[must_use] |
303 | | - #[inline] |
304 | | - pub fn lookup_symbol(&self, addr: u64) -> Option<&(u64, String)> { |
305 | | - let lookup_addr = if addr >= self.vstart && addr < self.vend { |
306 | | - addr - self.vstart + self.fstart |
307 | | - } else { |
308 | | - addr |
309 | | - }; |
310 | | - |
311 | | - match self.sym.binary_search_by_key(&lookup_addr, |(saddr, _)| *saddr) { |
312 | | - Ok(index) => self.sym.get(index), |
313 | | - Err(index) => { |
314 | | - if index == 0 { |
315 | | - None |
316 | | - } else { |
317 | | - self.sym.get(index - 1) |
318 | | - } |
319 | | - } |
320 | | - } |
321 | | - } |
322 | | -} |
323 | | - |
324 | | -fn run_nm() -> io::Result<Vec<u8>> { |
325 | | - let exepath = env::current_exe()?; |
326 | | - let output = |
327 | | - Command::new("nm").args(["--numeric-sort", "--no-demangle"]).arg(exepath).output()?; |
328 | | - Ok(output.stdout) |
329 | | -} |
330 | | - |
331 | | -#[cfg(test)] |
332 | | -mod tests { |
333 | | - use super::*; |
334 | | - |
335 | | - #[test] |
336 | | - fn test_symtab_lookup_symbol() { |
337 | | - let symtab = SymbolTable { |
338 | | - sym: vec![(123, "Abc".to_string()), (456, "Def".to_string()), (789, "Xyz".to_string())], |
339 | | - vstart: 0, |
340 | | - vend: u64::MAX, |
341 | | - fstart: 0, |
342 | | - }; |
343 | | - |
344 | | - assert_eq!(None, symtab.lookup_symbol(100)); |
345 | | - assert_eq!(Some(&(123, "Abc".to_string())), symtab.lookup_symbol(123)); |
346 | | - assert_eq!(Some(&(123, "Abc".to_string())), symtab.lookup_symbol(200)); |
347 | | - assert_eq!(Some(&(123, "Abc".to_string())), symtab.lookup_symbol(455)); |
348 | | - assert_eq!(Some(&(456, "Def".to_string())), symtab.lookup_symbol(456)); |
349 | | - assert_eq!(Some(&(789, "Xyz".to_string())), symtab.lookup_symbol(800)); |
350 | | - } |
351 | | -} |
0 commit comments