Skip to content

Support searching for and demangling symbols #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Mar 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ jobs:
run: rustup update ${{ matrix.rust }} && rustup default ${{ matrix.rust }}
- run: cargo build --all
- run: cargo test --all
- run: cargo build --features std

fuzz_targets:
name: Fuzz Targets
Expand All @@ -23,7 +24,7 @@ jobs:
# Note that building with fuzzers requires nightly since it uses unstable
# flags to rustc.
- run: rustup update nightly && rustup default nightly
- run: cargo install cargo-fuzz --vers "^0.10"
- run: cargo install cargo-fuzz --vers "^0.11"
- run: cargo fuzz build --dev

rustfmt:
Expand Down
7 changes: 6 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rustc-demangle"
version = "0.1.21"
version = "0.1.22"
authors = ["Alex Crichton <[email protected]>"]
license = "MIT/Apache-2.0"
readme = "README.md"
Expand All @@ -20,6 +20,11 @@ compiler_builtins = { version = '0.1.2', optional = true }

[features]
rustc-dep-of-std = ['core', 'compiler_builtins']
std = []

[profile.release]
lto = true

[package.metadata.docs.rs]
features = ["std"]
rustdoc-args = ["--cfg", "docsrs"]
2 changes: 1 addition & 1 deletion fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ cargo-fuzz = true

[dependencies]
libfuzzer-sys = "0.4"
rustc-demangle = { path = '..' }
rustc-demangle = { path = '..', features = ["std"] }

[[bin]]
name = "demangle"
Expand Down
13 changes: 13 additions & 0 deletions fuzz/fuzz_targets/demangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,17 @@ fuzz_target!(|data: &str| {
if let Ok(sym) = rustc_demangle::try_demangle(data) {
drop(write!(s, "{}", sym));
}

let mut output = Vec::new();
drop(rustc_demangle::demangle_stream(
&mut s.as_bytes(),
&mut output,
true,
));
output.clear();
drop(rustc_demangle::demangle_stream(
&mut s.as_bytes(),
&mut output,
false,
));
});
89 changes: 88 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@

#![no_std]
#![deny(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]

#[cfg(test)]
#[cfg(any(test, feature = "std"))]
#[macro_use]
extern crate std;

Expand Down Expand Up @@ -144,6 +145,74 @@ pub fn demangle(mut s: &str) -> Demangle {
}
}

#[cfg(feature = "std")]
fn demangle_line(line: &str, include_hash: bool) -> std::borrow::Cow<str> {
let mut line = std::borrow::Cow::Borrowed(line);
let mut head = 0;
loop {
// Move to the next potential match
head = match (line[head..].find("_ZN"), line[head..].find("_R")) {
(Some(idx), None) | (None, Some(idx)) => head + idx,
(Some(idx1), Some(idx2)) => head + idx1.min(idx2),
(None, None) => {
// No more matches, we can return our line.
return line;
}
};
// Find the non-matching character.
//
// If we do not find a character, then until the end of the line is the
// thing to demangle.
let match_end = line[head..]
.find(|ch: char| !(ch == '$' || ch == '.' || ch == '_' || ch.is_ascii_alphanumeric()))
.map(|idx| head + idx)
.unwrap_or(line.len());

let mangled = &line[head..match_end];
if let Ok(demangled) = try_demangle(mangled) {
let demangled = if include_hash {
format!("{}", demangled)
} else {
format!("{:#}", demangled)
};
line.to_mut().replace_range(head..match_end, &demangled);
// Start again after the replacement.
head = head + demangled.len();
} else {
// Skip over the full symbol. We don't try to find a partial Rust symbol in the wider
// matched text today.
head = head + mangled.len();
}
}
}

/// Process a stream of data from `input` into the provided `output`, demangling any symbols found
/// within.
///
/// This currently is implemented by buffering each line of input in memory, but that may be
/// changed in the future. Symbols never cross line boundaries so this is just an implementation
/// detail.
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn demangle_stream<R: std::io::BufRead, W: std::io::Write>(
input: &mut R,
output: &mut W,
include_hash: bool,
) -> std::io::Result<()> {
let mut buf = std::string::String::new();
// We read in lines to reduce the memory usage at any time.
//
// demangle_line is also more efficient with relatively small buffers as it will copy around
// trailing data during demangling. In the future we might directly stream to the output but at
// least right now that seems to be less efficient.
while input.read_line(&mut buf)? > 0 {
let demangled_line = demangle_line(&buf, include_hash);
output.write_all(demangled_line.as_bytes())?;
buf.clear();
}
Ok(())
}

/// Error returned from the `try_demangle` function below when demangling fails.
#[derive(Debug, Clone)]
pub struct TryDemangleError {
Expand Down Expand Up @@ -490,4 +559,22 @@ mod tests {
"{size limit reached}"
);
}

#[test]
#[cfg(feature = "std")]
fn find_multiple() {
assert_eq!(
super::demangle_line("_ZN3fooE.llvm moocow _ZN3fooE.llvm", false),
"foo.llvm moocow foo.llvm"
);
}

#[test]
#[cfg(feature = "std")]
fn interleaved_new_legacy() {
assert_eq!(
super::demangle_line("_ZN3fooE.llvm moocow _RNvMNtNtNtNtCs8a2262Dv4r_3mio3sys4unix8selector5epollNtB2_8Selector6select _ZN3fooE.llvm", false),
"foo.llvm moocow <mio::sys::unix::selector::epoll::Selector>::select foo.llvm"
);
}
}