Skip to content

Commit

Permalink
Merge pull request #62 from Mark-Simulacrum/stream-api
Browse files Browse the repository at this point in the history
Support searching for and demangling symbols
  • Loading branch information
Mark-Simulacrum authored Mar 23, 2023
2 parents 2811a1a + 9cb4703 commit 69fb82b
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 4 deletions.
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 <alex@alexcrichton.com>"]
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"
);
}
}

0 comments on commit 69fb82b

Please sign in to comment.