|
14 | 14 | use env; |
15 | 15 | use io::prelude::*; |
16 | 16 | use io; |
| 17 | +use path::{self, Path}; |
| 18 | +use ptr; |
| 19 | +use rustc_demangle::demangle; |
17 | 20 | use str; |
18 | 21 | use sync::atomic::{self, Ordering}; |
19 | | -use path::{self, Path}; |
20 | 22 | use sys::mutex::Mutex; |
21 | | -use ptr; |
22 | 23 |
|
23 | 24 | pub use sys::backtrace::{ |
24 | 25 | unwind_backtrace, |
@@ -191,7 +192,14 @@ fn output(w: &mut dyn Write, idx: usize, frame: Frame, |
191 | 192 | PrintFormat::Short => write!(w, " {:2}: ", idx)?, |
192 | 193 | } |
193 | 194 | match s { |
194 | | - Some(string) => demangle(w, string, format)?, |
| 195 | + Some(string) => { |
| 196 | + let symbol = demangle(string); |
| 197 | + match format { |
| 198 | + PrintFormat::Full => write!(w, "{}", symbol)?, |
| 199 | + // strip the trailing hash if short mode |
| 200 | + PrintFormat::Short => write!(w, "{:#}", symbol)?, |
| 201 | + } |
| 202 | + } |
195 | 203 | None => w.write_all(b"<unknown>")?, |
196 | 204 | } |
197 | 205 | w.write_all(b"\n") |
@@ -235,228 +243,3 @@ fn output_fileline(w: &mut dyn Write, |
235 | 243 | w.write_all(b"\n") |
236 | 244 | } |
237 | 245 |
|
238 | | - |
239 | | -// All rust symbols are in theory lists of "::"-separated identifiers. Some |
240 | | -// assemblers, however, can't handle these characters in symbol names. To get |
241 | | -// around this, we use C++-style mangling. The mangling method is: |
242 | | -// |
243 | | -// 1. Prefix the symbol with "_ZN" |
244 | | -// 2. For each element of the path, emit the length plus the element |
245 | | -// 3. End the path with "E" |
246 | | -// |
247 | | -// For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar". |
248 | | -// |
249 | | -// We're the ones printing our backtraces, so we can't rely on anything else to |
250 | | -// demangle our symbols. It's *much* nicer to look at demangled symbols, so |
251 | | -// this function is implemented to give us nice pretty output. |
252 | | -// |
253 | | -// Note that this demangler isn't quite as fancy as it could be. We have lots |
254 | | -// of other information in our symbols like hashes, version, type information, |
255 | | -// etc. Additionally, this doesn't handle glue symbols at all. |
256 | | -pub fn demangle(writer: &mut dyn Write, mut s: &str, format: PrintFormat) -> io::Result<()> { |
257 | | - // During ThinLTO LLVM may import and rename internal symbols, so strip out |
258 | | - // those endings first as they're one of the last manglings applied to |
259 | | - // symbol names. |
260 | | - let llvm = ".llvm."; |
261 | | - if let Some(i) = s.find(llvm) { |
262 | | - let candidate = &s[i + llvm.len()..]; |
263 | | - let all_hex = candidate.chars().all(|c| { |
264 | | - match c { |
265 | | - 'A' ..= 'F' | '0' ..= '9' => true, |
266 | | - _ => false, |
267 | | - } |
268 | | - }); |
269 | | - |
270 | | - if all_hex { |
271 | | - s = &s[..i]; |
272 | | - } |
273 | | - } |
274 | | - |
275 | | - // Validate the symbol. If it doesn't look like anything we're |
276 | | - // expecting, we just print it literally. Note that we must handle non-rust |
277 | | - // symbols because we could have any function in the backtrace. |
278 | | - let mut valid = true; |
279 | | - let mut inner = s; |
280 | | - if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") { |
281 | | - inner = &s[3 .. s.len() - 1]; |
282 | | - // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too. |
283 | | - } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") { |
284 | | - inner = &s[2 .. s.len() - 1]; |
285 | | - } else { |
286 | | - valid = false; |
287 | | - } |
288 | | - |
289 | | - if valid { |
290 | | - let mut chars = inner.chars(); |
291 | | - while valid { |
292 | | - let mut i = 0; |
293 | | - for c in chars.by_ref() { |
294 | | - if c.is_numeric() { |
295 | | - i = i * 10 + c as usize - '0' as usize; |
296 | | - } else { |
297 | | - break |
298 | | - } |
299 | | - } |
300 | | - if i == 0 { |
301 | | - valid = chars.next().is_none(); |
302 | | - break |
303 | | - } else if chars.by_ref().take(i - 1).count() != i - 1 { |
304 | | - valid = false; |
305 | | - } |
306 | | - } |
307 | | - } |
308 | | - |
309 | | - // Alright, let's do this. |
310 | | - if !valid { |
311 | | - writer.write_all(s.as_bytes())?; |
312 | | - } else { |
313 | | - // remove the `::hfc2edb670e5eda97` part at the end of the symbol. |
314 | | - if format == PrintFormat::Short { |
315 | | - // The symbol in still mangled. |
316 | | - let mut split = inner.rsplitn(2, "17h"); |
317 | | - match (split.next(), split.next()) { |
318 | | - (Some(addr), rest) => { |
319 | | - if addr.len() == 16 && |
320 | | - addr.chars().all(|c| c.is_digit(16)) |
321 | | - { |
322 | | - inner = rest.unwrap_or(""); |
323 | | - } |
324 | | - } |
325 | | - _ => (), |
326 | | - } |
327 | | - } |
328 | | - |
329 | | - let mut first = true; |
330 | | - while !inner.is_empty() { |
331 | | - if !first { |
332 | | - writer.write_all(b"::")?; |
333 | | - } else { |
334 | | - first = false; |
335 | | - } |
336 | | - let mut rest = inner; |
337 | | - while rest.chars().next().unwrap().is_numeric() { |
338 | | - rest = &rest[1..]; |
339 | | - } |
340 | | - let i: usize = inner[.. (inner.len() - rest.len())].parse().unwrap(); |
341 | | - inner = &rest[i..]; |
342 | | - rest = &rest[..i]; |
343 | | - if rest.starts_with("_$") { |
344 | | - rest = &rest[1..]; |
345 | | - } |
346 | | - while !rest.is_empty() { |
347 | | - if rest.starts_with(".") { |
348 | | - if let Some('.') = rest[1..].chars().next() { |
349 | | - writer.write_all(b"::")?; |
350 | | - rest = &rest[2..]; |
351 | | - } else { |
352 | | - writer.write_all(b".")?; |
353 | | - rest = &rest[1..]; |
354 | | - } |
355 | | - } else if rest.starts_with("$") { |
356 | | - macro_rules! demangle { |
357 | | - ($($pat:expr => $demangled:expr),*) => ({ |
358 | | - $(if rest.starts_with($pat) { |
359 | | - writer.write_all($demangled)?; |
360 | | - rest = &rest[$pat.len()..]; |
361 | | - } else)* |
362 | | - { |
363 | | - writer.write_all(rest.as_bytes())?; |
364 | | - break; |
365 | | - } |
366 | | - |
367 | | - }) |
368 | | - } |
369 | | - |
370 | | - // see src/librustc/back/link.rs for these mappings |
371 | | - demangle! ( |
372 | | - "$SP$" => b"@", |
373 | | - "$BP$" => b"*", |
374 | | - "$RF$" => b"&", |
375 | | - "$LT$" => b"<", |
376 | | - "$GT$" => b">", |
377 | | - "$LP$" => b"(", |
378 | | - "$RP$" => b")", |
379 | | - "$C$" => b",", |
380 | | - |
381 | | - // in theory we can demangle any Unicode code point, but |
382 | | - // for simplicity we just catch the common ones. |
383 | | - "$u7e$" => b"~", |
384 | | - "$u20$" => b" ", |
385 | | - "$u27$" => b"'", |
386 | | - "$u5b$" => b"[", |
387 | | - "$u5d$" => b"]", |
388 | | - "$u7b$" => b"{", |
389 | | - "$u7d$" => b"}", |
390 | | - "$u3b$" => b";", |
391 | | - "$u2b$" => b"+", |
392 | | - "$u22$" => b"\"" |
393 | | - ) |
394 | | - } else { |
395 | | - let idx = match rest.char_indices().find(|&(_, c)| c == '$' || c == '.') { |
396 | | - None => rest.len(), |
397 | | - Some((i, _)) => i, |
398 | | - }; |
399 | | - writer.write_all(rest[..idx].as_bytes())?; |
400 | | - rest = &rest[idx..]; |
401 | | - } |
402 | | - } |
403 | | - } |
404 | | - } |
405 | | - |
406 | | - Ok(()) |
407 | | -} |
408 | | - |
409 | | -#[cfg(test)] |
410 | | -mod tests { |
411 | | - use sys_common; |
412 | | - macro_rules! t { ($a:expr, $b:expr) => ({ |
413 | | - let mut m = Vec::new(); |
414 | | - sys_common::backtrace::demangle(&mut m, |
415 | | - $a, |
416 | | - super::PrintFormat::Full).unwrap(); |
417 | | - assert_eq!(String::from_utf8(m).unwrap(), $b); |
418 | | - }) } |
419 | | - |
420 | | - #[test] |
421 | | - fn demangle() { |
422 | | - t!("test", "test"); |
423 | | - t!("_ZN4testE", "test"); |
424 | | - t!("_ZN4test", "_ZN4test"); |
425 | | - t!("_ZN4test1a2bcE", "test::a::bc"); |
426 | | - } |
427 | | - |
428 | | - #[test] |
429 | | - fn demangle_dollars() { |
430 | | - t!("_ZN4$RP$E", ")"); |
431 | | - t!("_ZN8$RF$testE", "&test"); |
432 | | - t!("_ZN8$BP$test4foobE", "*test::foob"); |
433 | | - t!("_ZN9$u20$test4foobE", " test::foob"); |
434 | | - t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>"); |
435 | | - } |
436 | | - |
437 | | - #[test] |
438 | | - fn demangle_many_dollars() { |
439 | | - t!("_ZN13test$u20$test4foobE", "test test::foob"); |
440 | | - t!("_ZN12test$BP$test4foobE", "test*test::foob"); |
441 | | - } |
442 | | - |
443 | | - #[test] |
444 | | - fn demangle_windows() { |
445 | | - t!("ZN4testE", "test"); |
446 | | - t!("ZN13test$u20$test4foobE", "test test::foob"); |
447 | | - t!("ZN12test$RF$test4foobE", "test&test::foob"); |
448 | | - } |
449 | | - |
450 | | - #[test] |
451 | | - fn demangle_elements_beginning_with_underscore() { |
452 | | - t!("_ZN13_$LT$test$GT$E", "<test>"); |
453 | | - t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}"); |
454 | | - t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR"); |
455 | | - } |
456 | | - |
457 | | - #[test] |
458 | | - fn demangle_trait_impls() { |
459 | | - t!("_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE", |
460 | | - "<Test + 'static as foo::Bar<Test>>::bar"); |
461 | | - } |
462 | | -} |
0 commit comments