├── .github └── workflows │ └── main.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src ├── main.rs └── tests.rs /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: main 2 | 3 | on: [push, pull_request] 4 | 5 | concurrency: 6 | group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} 7 | cancel-in-progress: true 8 | 9 | jobs: 10 | lint: 11 | name: Lint 12 | runs-on: ubuntu-22.04 13 | steps: 14 | - uses: actions/checkout@v3 15 | - uses: dtolnay/rust-toolchain@stable 16 | - uses: Swatinem/rust-cache@v2 17 | 18 | # make sure all code has been formatted with rustfmt 19 | - run: rustup component add rustfmt 20 | - name: check rustfmt 21 | run: cargo fmt -- --check --color always 22 | 23 | # run clippy to verify we have no warnings 24 | - run: rustup component add clippy 25 | - run: cargo fetch 26 | - name: cargo clippy 27 | run: cargo clippy --all-targets -- -D warnings 28 | 29 | test: 30 | name: Test 31 | strategy: 32 | matrix: 33 | os: [ubuntu-22.04, macOS-12, windows-2022] 34 | runs-on: ${{ matrix.os }} 35 | steps: 36 | - uses: actions/checkout@v3 37 | - uses: dtolnay/rust-toolchain@stable 38 | - uses: Swatinem/rust-cache@v2 39 | - run: cargo fetch 40 | - name: cargo test build 41 | run: cargo build --tests --release 42 | - name: cargo test 43 | run: cargo test --release 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | *~ 3 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "bitflags" 7 | version = "1.3.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 10 | 11 | [[package]] 12 | name = "clap" 13 | version = "2.34.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" 16 | dependencies = [ 17 | "bitflags", 18 | "term_size", 19 | "textwrap", 20 | "unicode-width", 21 | ] 22 | 23 | [[package]] 24 | name = "libc" 25 | version = "0.2.144" 26 | source = "registry+https://github.com/rust-lang/crates.io-index" 27 | checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" 28 | 29 | [[package]] 30 | name = "rustc-demangle" 31 | version = "0.1.23" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 34 | 35 | [[package]] 36 | name = "rustfilt" 37 | version = "0.2.2-alpha.0" 38 | dependencies = [ 39 | "clap", 40 | "rustc-demangle", 41 | ] 42 | 43 | [[package]] 44 | name = "term_size" 45 | version = "0.3.2" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "1e4129646ca0ed8f45d09b929036bafad5377103edd06e50bf574b353d2b08d9" 48 | dependencies = [ 49 | "libc", 50 | "winapi", 51 | ] 52 | 53 | [[package]] 54 | name = "textwrap" 55 | version = "0.11.0" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 58 | dependencies = [ 59 | "term_size", 60 | "unicode-width", 61 | ] 62 | 63 | [[package]] 64 | name = "unicode-width" 65 | version = "0.1.10" 66 | source = "registry+https://github.com/rust-lang/crates.io-index" 67 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 68 | 69 | [[package]] 70 | name = "winapi" 71 | version = "0.3.9" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 74 | dependencies = [ 75 | "winapi-i686-pc-windows-gnu", 76 | "winapi-x86_64-pc-windows-gnu", 77 | ] 78 | 79 | [[package]] 80 | name = "winapi-i686-pc-windows-gnu" 81 | version = "0.4.0" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 84 | 85 | [[package]] 86 | name = "winapi-x86_64-pc-windows-gnu" 87 | version = "0.4.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 90 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rustfilt" 3 | description = "Demangle Rust symbol names" 4 | homepage = "https://github.com/luser/rustfilt" 5 | repository = "https://github.com/luser/rustfilt" 6 | version = "0.2.2-alpha.0" 7 | authors = ["Ted Mielczarek ", "Nicholas Schlabach "] 8 | readme = "README.md" 9 | license = "Apache-2.0" 10 | 11 | [dependencies] 12 | rustc-demangle = { version = "0.1.23", features = ["std"] } 13 | 14 | [dependencies.clap] 15 | version = "^2.21.1" 16 | # Exclude all optional features except wrap_help 17 | default-features = false 18 | features = ["wrap_help"] 19 | 20 | [profile.release] 21 | # Reduces binary size by 21% 22 | lto = true 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![main](https://github.com/luser/rustfilt/actions/workflows/main.yml/badge.svg)](https://github.com/luser/rustfilt/actions/workflows/main.yml) 2 | [![Crates.io](https://img.shields.io/crates/v/rustfilt.svg)](https://crates.io/crates/rustfilt) 3 | 4 | Demangle Rust symbol names using [rustc-demangle](https://github.com/alexcrichton/rustc-demangle). `rustfilt` works similarly to `c++filt`, in that it accepts mangled symbol names as command line arguments, and if none are provided it accepts mangled symbols from stdin. Demangled symbols are written to stdout. 5 | 6 | ## Installation 7 | ````bash 8 | cargo install rustfilt 9 | ```` 10 | 11 | ## Usage 12 | To demangle a file, simply run: 13 | ````bash 14 | rustfilt -i mangled.txt -o demangled.txt 15 | ```` 16 | Rustfilt can also accept data from stdin, and pipe to stdout: 17 | ```` 18 | curl http://example.com/mangled-symbols.txt | rustfilt | less 19 | ```` 20 | 21 | By default, rustfilt strips the generated 'hashes' from the mangled names. 22 | If these need to be kept, simply pass the `-h` option to rustfilt. 23 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016 Mozilla 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #[macro_use] 16 | extern crate clap; 17 | extern crate rustc_demangle; 18 | 19 | use rustc_demangle::demangle_stream; 20 | 21 | use std::fs::File; 22 | use std::io::{self, stderr, stdin, stdout, BufReader, BufWriter, Write}; 23 | use std::path::{Path, PathBuf}; 24 | use std::process::exit; 25 | use std::str::FromStr; 26 | 27 | mod tests; 28 | 29 | enum InputType { 30 | Stdin, 31 | File(PathBuf), 32 | } 33 | 34 | impl InputType { 35 | fn demangle(&self, output: OutputType, include_hash: bool) -> io::Result<()> { 36 | // NOTE: This has to be separated into two functions for generics 37 | match *self { 38 | InputType::Stdin => { 39 | let stdin = stdin(); 40 | let mut lock = stdin.lock(); 41 | output.write_demangled(&mut lock, include_hash) 42 | } 43 | InputType::File(ref path) => { 44 | output.write_demangled(&mut BufReader::new(File::open(path)?), include_hash) 45 | } 46 | } 47 | } 48 | fn validate(file: String) -> Result<(), String> { 49 | file.parse::().map(|_| ()) 50 | } 51 | } 52 | impl FromStr for InputType { 53 | type Err = String; 54 | fn from_str(file: &str) -> Result { 55 | if file == "-" { 56 | Ok(InputType::Stdin) 57 | } else { 58 | let path = Path::new(&file); 59 | if !path.is_file() { 60 | if !path.exists() { 61 | Err(format!("{} doesn't exist", file)) 62 | } else { 63 | Err(format!("{} isn't a file", file)) 64 | } 65 | } else { 66 | Ok(InputType::File(PathBuf::from(path))) 67 | } 68 | } 69 | } 70 | } 71 | 72 | enum OutputType { 73 | Stdout, 74 | File(PathBuf), 75 | } 76 | 77 | impl OutputType { 78 | #[inline] // It's only used twice 79 | fn write_demangled(&self, input: &mut I, include_hash: bool) -> io::Result<()> { 80 | match *self { 81 | OutputType::Stdout => { 82 | let stdout = stdout(); 83 | let mut lock = stdout.lock(); 84 | demangle_stream(input, &mut lock, include_hash) 85 | } 86 | OutputType::File(ref path) => { 87 | let file = File::create(path)?; 88 | let mut buf = BufWriter::new(&file); 89 | demangle_stream(input, &mut buf, include_hash) 90 | } 91 | } 92 | } 93 | fn write_demangled_names>( 94 | &self, 95 | names: &[S], 96 | include_hash: bool, 97 | ) -> io::Result<()> { 98 | #[inline] // It's only used twice ;) 99 | fn demangle_names_to, O: io::Write>( 100 | names: &[S], 101 | output: &mut O, 102 | include_hash: bool, 103 | ) -> io::Result<()> { 104 | for name in names { 105 | let demangled = rustc_demangle::demangle(name.as_ref()); 106 | if include_hash { 107 | writeln!(output, "{}", demangled)? 108 | } else { 109 | writeln!(output, "{:#}", demangled)? 110 | }; 111 | } 112 | Ok(()) 113 | } 114 | match *self { 115 | OutputType::Stdout => { 116 | let stdout = stdout(); 117 | let mut lock = stdout.lock(); 118 | demangle_names_to(names, &mut lock, include_hash) 119 | } 120 | OutputType::File(ref path) => { 121 | let file = File::create(path)?; 122 | let mut buf = BufWriter::new(&file); 123 | demangle_names_to(names, &mut buf, include_hash) 124 | } 125 | } 126 | } 127 | fn validate(file: String) -> Result<(), String> { 128 | file.parse::().map(|_| ()) 129 | } 130 | } 131 | impl FromStr for OutputType { 132 | type Err = String; 133 | fn from_str(file: &str) -> Result { 134 | if file == "-" { 135 | Ok(OutputType::Stdout) 136 | } else { 137 | let path = Path::new(&file); 138 | if path.exists() { 139 | Err(format!("{} already exists", file)) 140 | } else { 141 | Ok(OutputType::File(PathBuf::from(path))) 142 | } 143 | } 144 | } 145 | } 146 | 147 | fn main() { 148 | let args: clap::ArgMatches = clap_app!(rust_demangle => 149 | (version: crate_version!()) 150 | (author: crate_authors!()) 151 | (about: "Demangles names generated by the rust compiler") 152 | (@arg INCLUDE_HASH: --include-hash --hash "Include the hashes in the demangled names") 153 | (@arg INPUT: --input -i [FILE] default_value("-") {InputType::validate} "The input file to replace the mangled names in, or '-' for stdin") 154 | (@arg OUTPUT: --output -o [FILE] default_value("-") {OutputType::validate} "The output file to emit the demangled names to, or '-' for stdout") 155 | (@arg NAMES: ... [NAME] conflicts_with[INPUT] "The list of names to demangle") 156 | ).get_matches(); 157 | let include_hash = args.is_present("INCLUDE_HASH"); 158 | let output = value_t!(args, "OUTPUT", OutputType).unwrap(); 159 | if let Some(names) = args.values_of("NAMES") { 160 | output 161 | .write_demangled_names(&names.collect::>(), include_hash) 162 | .unwrap_or_else(|e| { 163 | writeln!(stderr(), "Unable to demangle names: {}", e).unwrap(); 164 | exit(1); 165 | }) 166 | } else { 167 | let input = value_t!(args, "INPUT", InputType).unwrap(); 168 | input.demangle(output, include_hash).unwrap_or_else(|e| { 169 | writeln!(stderr(), "Unable to demangle input: {}", e).unwrap(); 170 | exit(1); 171 | }) 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | use super::demangle_stream; 3 | ///! Tests demangle_stream, based upon the assumption rustc_demangle works properly 4 | use rustc_demangle::demangle; 5 | 6 | static MANGLED_NAMES: &'static [&'static str] = &[ 7 | "_ZN55_$LT$$RF$$u27$a$u20$T$u20$as$u20$core..fmt..Display$GT$3fmt17h510ed05e72307174E", 8 | "_ZN7example4main17h0db00b8b32acffd5E", 9 | "_ZN3std2io5stdio6_print17he48522be5b0a80d9E", 10 | "_ZN3foo17h05af221e174051e9E", 11 | "_ZN3foo20h05af221e174051e9abcE", 12 | "_ZN3foo5h05afE", 13 | "_ZN109_$LT$core..str..pattern..CharSearcher$LT$$u27$a$GT$$u20$as$u20$core..str..pattern..Searcher$LT$$u27$a$GT$$GT$10next_match17h9c8d80a58da7cd74E", 14 | "_ZN84_$LT$core..iter..Map$LT$I$C$$u20$F$GT$$u20$as$u20$core..iter..iterator..Iterator$GT$4next17h98ea4751a6975428E", 15 | "_ZN51_$LT$serde_json..read..IteratorRead$LT$Iter$GT$$GT$15parse_str_bytes17h8199b7867f1a334fE", 16 | "_ZN3std11collections4hash3map11RandomState3new4KEYS7__getit5__KEY17h1bc0dbd302b9f01bE", 17 | 18 | // RFC2603 v0 mangled names 19 | "_RNvNtNtCs1234_7mycrate3foo3bar3baz", 20 | "_RNvNvMCs1234_7mycrateINtCs1234_7mycrate3FoopE3bar4QUUX", 21 | "_RNvNvXCs1234_7mycrateINtCs1234_7mycrate3FoopENtNtC3std5clone5Clone5clone4QUUX", 22 | "_RNvNvCs1234_7mycrate4QUUX3FOO", 23 | ]; 24 | 25 | fn demangle_line(s: &str, include_hash: bool) -> std::borrow::Cow { 26 | let mut out = Vec::new(); 27 | demangle_stream(&mut s.as_bytes(), &mut out, include_hash).unwrap(); 28 | std::borrow::Cow::Owned(String::from_utf8(out).unwrap()) 29 | } 30 | 31 | #[test] 32 | fn ignores_text() { 33 | for text in &[ 34 | "boom de yada\tboom de yada\n", 35 | "bananas are fun for everyone", 36 | ] { 37 | assert_eq!(demangle_line(text, false), *text); 38 | assert_eq!(demangle_line(text, true), *text); 39 | } 40 | } 41 | 42 | #[test] 43 | fn standalone_demangles() { 44 | for name in MANGLED_NAMES { 45 | assert_eq!( 46 | demangle_line(name, true).as_ref(), 47 | &demangle(name).to_string() 48 | ); 49 | } 50 | } 51 | 52 | #[test] 53 | fn not_noop_demangles() { 54 | for name in MANGLED_NAMES { 55 | assert_ne!(demangle_line(name, false).as_ref(), *name); 56 | } 57 | } 58 | 59 | #[test] 60 | fn standalone_demangles_nohash() { 61 | for name in MANGLED_NAMES { 62 | assert_eq!( 63 | demangle_line(name, false).as_ref(), 64 | &format!("{:#}", demangle(name)) 65 | ); 66 | } 67 | } 68 | 69 | fn test_embedded_line_demangle(line_demangler: F1, demangler: F2) 70 | where 71 | F1: Fn(&str) -> String, 72 | F2: Fn(&str) -> String, 73 | { 74 | for name in MANGLED_NAMES { 75 | macro_rules! test_context { 76 | ($context:expr) => { 77 | assert_eq!( 78 | line_demangler(&format!($context, name)), 79 | format!($context, demangler(name)) 80 | ) 81 | }; 82 | } 83 | // x86 ASM 84 | test_context!(" lea rax, [rip + {}]"); 85 | test_context!(" call {}@PLT"); 86 | // perf script --no-demangle 87 | test_context!( 88 | " 1a680e {} (/home/user/git/steven-rust/target/debug/steven)" 89 | ); 90 | test_context!( 91 | " 20039f {} (/home/user/git/steven-rust/target/debug/steven)" 92 | ); 93 | test_context!( 94 | " 1dade8 {} (/home/user/git/steven-rust/target/debug/steven)" 95 | ); 96 | // Random unicode symbols 97 | test_context!("J∆ƒƒ∆Ǥ{}∆ʓ∆ɲI∆ɳ"); 98 | // https://xkcd.com/1638/ 99 | test_context!(r#"cat out.txt | grep -o "\\\[[(].*\\\[{}\])][^)\]]*$""#); 100 | test_context!(r"\\\\\\\\{}\\\\\\\\"); // Backslash to end all other text (twice) 101 | } 102 | } 103 | 104 | #[test] 105 | fn embedded_demangle() { 106 | test_embedded_line_demangle( 107 | |line| demangle_line(line, false).into_owned(), 108 | |name| format!("{:#}", demangle(name)), 109 | ); 110 | } 111 | 112 | #[test] 113 | fn embedded_demangle_nohash() { 114 | test_embedded_line_demangle( 115 | |line| demangle_line(line, true).into_owned(), 116 | |name| demangle(name).to_string(), 117 | ); 118 | } 119 | 120 | #[test] 121 | fn stream_without_newlines() { 122 | let tab_mangled = MANGLED_NAMES.join("\t"); 123 | 124 | let mut stream_demangled: Vec = vec![]; 125 | demangle_stream(&mut tab_mangled.as_bytes(), &mut stream_demangled, true).unwrap(); 126 | 127 | let split_demangled: Vec<_> = stream_demangled.split(|&b| b == b'\t').collect(); 128 | assert_eq!(MANGLED_NAMES.len(), split_demangled.len()); 129 | 130 | for (mangled, demangled) in MANGLED_NAMES.iter().zip(split_demangled) { 131 | assert_eq!(demangled, demangle(mangled).to_string().as_bytes()); 132 | } 133 | } 134 | --------------------------------------------------------------------------------