├── .gitignore ├── .travis.yml ├── Cargo.lock ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── main ├── Cargo.toml ├── src │ ├── code.rs │ ├── filter.rs │ ├── lib.rs │ ├── main.rs │ └── print │ │ ├── base_type.rs │ │ ├── enumeration.rs │ │ ├── file.rs │ │ ├── frame_location.rs │ │ ├── function.rs │ │ ├── html.rs │ │ ├── inherit.rs │ │ ├── inlined_function.rs │ │ ├── local_variable.rs │ │ ├── member.rs │ │ ├── mod.rs │ │ ├── namespace.rs │ │ ├── parameter.rs │ │ ├── range.rs │ │ ├── register.rs │ │ ├── section.rs │ │ ├── source.rs │ │ ├── struct_type.rs │ │ ├── symbol.rs │ │ ├── text.rs │ │ ├── type_def.rs │ │ ├── type_modifier.rs │ │ ├── types.rs │ │ ├── union_type.rs │ │ ├── unit.rs │ │ └── variable.rs └── tests │ ├── Makefile │ ├── bin │ ├── diff1 │ └── diff2 │ ├── diff.rs │ └── src │ ├── diff.c │ ├── diff.rs │ └── support.c ├── parser ├── Cargo.toml └── src │ ├── cfi.rs │ ├── file │ ├── dwarf.rs │ ├── mod.rs │ └── pdb.rs │ ├── function.rs │ ├── lib.rs │ ├── location.rs │ ├── namespace.rs │ ├── range.rs │ ├── source.rs │ ├── types.rs │ ├── unit.rs │ └── variable.rs └── rustfmt.toml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.swp 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | os: 7 | - linux 8 | matrix: 9 | include: 10 | - os: osx 11 | rust: stable 12 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "main", 4 | ] 5 | 6 | [profile.release] 7 | debug = true 8 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2021 The ddbug Developers 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ddbug - Display debugging information 2 | 3 | `ddbug` is a utility for using DWARF debugging information to explore aspects 4 | code generation, and in particular to see how the code generation changes due 5 | to things such as source code changes or compiler option changes. 6 | 7 | Features: 8 | * Type size and layout 9 | * Function type, size, inlined functions, function calls, and disassembly 10 | * Display the differences between two files 11 | * Plain text or HTTP/HTML output 12 | * Options to filter/sort the plain text output 13 | 14 | **This is alpha software. It is likely to contain many bugs and 15 | incomplete features.** Neverthless, it is in a state where it can still 16 | provide some use. Bug reports and feature requests are welcome. 17 | 18 | Supports: 19 | * ELF files with DWARF 20 | * Mach-O files with DWARF 21 | 22 | ## Installing 23 | After installing [Rust](https://www.rust-lang.org/), run: 24 | ``` 25 | cargo install --git https://github.com/gimli-rs/ddbug 26 | ``` 27 | 28 | ## Running 29 | 30 | Find the file containing the debugging information, then run: 31 | ``` 32 | ddbug path-to-file 33 | ``` 34 | 35 | See `ddbug --help` for details on options to control which information 36 | is displayed. 37 | 38 | Usually you will want to run `ddbug` on binaries that have been 39 | optimized, but which still contain debugging information. For rust, you 40 | can build your code using: 41 | ``` 42 | RUSTFLAGS=-g cargo build --release 43 | ``` 44 | 45 | ### Diff mode 46 | 47 | When given the `--diff` option and two paths to files, `ddbug` will 48 | display the differences between the two binaries. There are some command 49 | line options to specify which differences are considered significant. 50 | Usually you will want to at least ignore addresses (`-i address`). 51 | 52 | ## Example output 53 | 54 | ### struct and union 55 | ``` 56 | struct core::fmt::Formatter 57 | size: 96 58 | members: 59 | 0[16] width: union core::option::Option 60 | 16[16] precision: union core::option::Option 61 | 32[16] buf: struct core::fmt::&mut Write 62 | 48[16] curarg: struct core::slice::Iter 63 | 64[16] args: struct &[core::fmt::ArgumentV1] 64 | 80[4] flags: u32 65 | 84[4] fill: char 66 | 88[1] align: enum core::fmt::rt::v1::Alignment 67 | 89[7] 68 | ``` 69 | 70 | ### enum 71 | Note that this is a C style enumeration. Rust enumerations are encoded 72 | in the debugging information as both a union and an enum. 73 | ``` 74 | enum core::result::Result 75 | size: 1 76 | enumerators: 77 | Ok(0) 78 | Err(1) 79 | ``` 80 | 81 | 82 | ### function 83 | ``` 84 | fn ddbug::main 85 | linkage name: _ZN5ddbug4mainE 86 | address: 0x601f0-0x629d9 87 | size: 10218 88 | inlined functions: 89 | [30] log::__static_max_level 90 | [59] log::max_log_level 91 | calls: 92 | 0x40eee0 env_logger::init 93 | 0x48870 core::result::Result<(), log::SetLoggerError>::ok<(),log::SetLoggerError> 94 | 95 | fn log::__static_max_level 96 | linkage name: _ZN3log18__static_max_levelE 97 | inline: yes 98 | return type: 99 | [8] enum log::LogLevelFilter 100 | ``` 101 | 102 | ## License 103 | 104 | This software is licensed under either of 105 | 106 | * Apache License, Version 2.0 ([`LICENSE-APACHE`](LICENSE-APACHE)) 107 | * MIT license ([`LICENSE-MIT`](LICENSE-MIT)) 108 | 109 | at your option. 110 | 111 | Unless you explicitly state otherwise, any contribution intentionally submitted 112 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 113 | dual licensed as above, without any additional terms or conditions. 114 | -------------------------------------------------------------------------------- /main/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ddbug" 3 | version = "0.4.0" 4 | description = "Display debugging information" 5 | repository = "https://github.com/gimli-rs/ddbug" 6 | readme = "../README.md" 7 | keywords = ["debug", "DWARF"] 8 | categories = ["development-tools::debugging"] 9 | license = "Apache-2.0 OR MIT" 10 | edition = "2018" 11 | 12 | [dependencies] 13 | capstone = "0.12.0" 14 | capstone-sys = "0.16.0" 15 | clap = { version = "4.5.4", features = ["cargo", "wrap_help"] } 16 | env_logger = "0.11.3" 17 | log = "0.4" 18 | marksman_escape = "0.1" 19 | parser = { package = "ddbug_parser", version = "0.4.0", path = "../parser" } 20 | hyper = { version = "1.2.0", features = ["server", "http1"] } 21 | hyper-util = { version = "0.1.3", features = ["tokio"] } 22 | http-body-util = "0.1.0" 23 | tokio = { version = "1.37.0", features = ["net", "rt"] } 24 | 25 | [features] 26 | system_alloc = [] 27 | default = [] 28 | -------------------------------------------------------------------------------- /main/src/filter.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::collections::HashSet; 3 | 4 | use parser::{ 5 | BaseType, EnumerationType, File, FileHash, Function, StructType, Type, TypeDef, TypeKind, 6 | TypeOffset, UnionType, Unit, UnspecifiedType, Variable, 7 | }; 8 | 9 | use crate::Options; 10 | 11 | pub(crate) fn filter_units<'input, 'file>( 12 | file: &'file File<'input>, 13 | options: &Options, 14 | ) -> Vec<&'file Unit<'input>> { 15 | file.units() 16 | .iter() 17 | .filter(|a| filter_unit(a, options)) 18 | .collect() 19 | } 20 | 21 | pub(crate) fn enumerate_and_filter_units<'input, 'file>( 22 | file: &'file File<'input>, 23 | options: &Options, 24 | ) -> Vec<(usize, &'file Unit<'input>)> { 25 | file.units() 26 | .iter() 27 | .enumerate() 28 | .filter(|a| filter_unit(a.1, options)) 29 | .collect() 30 | } 31 | 32 | /// Return true if this unit matches the filter options. 33 | fn filter_unit(unit: &Unit, options: &Options) -> bool { 34 | if let Some(filter) = options.filter_unit.as_ref() { 35 | let (prefix, suffix) = options.prefix_map(unit.name().unwrap_or("")); 36 | let iter = prefix.bytes().chain(suffix.bytes()); 37 | iter.cmp(filter.bytes()) == cmp::Ordering::Equal 38 | } else { 39 | true 40 | } 41 | } 42 | 43 | /// The offsets of types that should be printed inline. 44 | fn inline_types(unit: &Unit, hash: &FileHash) -> HashSet { 45 | let mut inline_types = HashSet::new(); 46 | for ty in unit.types() { 47 | // Assume all anonymous types are inline. We don't actually check 48 | // that they will be inline, but in future we could (eg for TypeDefs). 49 | // TODO: is this a valid assumption? 50 | if ty.is_anon() && ty.offset().is_some() { 51 | inline_types.insert(ty.offset()); 52 | } 53 | 54 | // Find all inline members. 55 | for t in ty.members() { 56 | if t.is_inline(hash) && t.type_offset().is_some() { 57 | inline_types.insert(t.type_offset()); 58 | } 59 | } 60 | } 61 | inline_types 62 | } 63 | 64 | /// Filter and the list of types using the options. 65 | /// Perform additional filtering when diffing. 66 | pub(crate) fn filter_types<'input, 'unit>( 67 | unit: &'unit Unit<'input>, 68 | hash: &FileHash, 69 | options: &Options, 70 | diff: bool, 71 | ) -> Vec<&'unit Type<'input>> { 72 | let inline_types = inline_types(unit, hash); 73 | unit.types() 74 | .iter() 75 | .filter(|a| filter_type(a, options, diff, &inline_types)) 76 | .collect() 77 | } 78 | 79 | pub(crate) fn enumerate_and_filter_types<'input, 'unit>( 80 | unit: &'unit Unit<'input>, 81 | hash: &FileHash, 82 | options: &Options, 83 | diff: bool, 84 | ) -> Vec<(usize, &'unit Type<'input>)> { 85 | let inline_types = inline_types(unit, hash); 86 | unit.types() 87 | .iter() 88 | .enumerate() 89 | .filter(|a| filter_type(a.1, options, diff, &inline_types)) 90 | .collect() 91 | } 92 | 93 | pub(crate) fn filter_functions<'input, 'unit>( 94 | unit: &'unit Unit<'input>, 95 | options: &Options, 96 | ) -> Vec<&'unit Function<'input>> { 97 | unit.functions() 98 | .iter() 99 | .filter(|a| filter_function(a, options)) 100 | .collect() 101 | } 102 | 103 | pub(crate) fn enumerate_and_filter_functions<'input, 'unit>( 104 | unit: &'unit Unit<'input>, 105 | options: &Options, 106 | ) -> Vec<(usize, &'unit Function<'input>)> { 107 | unit.functions() 108 | .iter() 109 | .enumerate() 110 | .filter(|a| filter_function(a.1, options)) 111 | .collect() 112 | } 113 | 114 | pub(crate) fn filter_variables<'input, 'unit>( 115 | unit: &'unit Unit<'input>, 116 | options: &Options, 117 | ) -> Vec<&'unit Variable<'input>> { 118 | unit.variables() 119 | .iter() 120 | .filter(|a| filter_variable(a, options)) 121 | .collect() 122 | } 123 | 124 | pub(crate) fn enumerate_and_filter_variables<'input, 'unit>( 125 | unit: &'unit Unit<'input>, 126 | options: &Options, 127 | ) -> Vec<(usize, &'unit Variable<'input>)> { 128 | unit.variables() 129 | .iter() 130 | .enumerate() 131 | .filter(|a| filter_variable(a.1, options)) 132 | .collect() 133 | } 134 | 135 | fn filter_function(f: &Function, options: &Options) -> bool { 136 | if !f.is_inline() && (f.address().is_none() || f.size().is_none()) { 137 | // This is either a declaration or a dead function that was removed 138 | // from the code, but wasn't removed from the debuginfo. 139 | // TODO: make this configurable? 140 | return false; 141 | } 142 | options.filter_name(f.name()) 143 | && options.filter_namespace(f.namespace()) 144 | && options.filter_function_inline(f.is_inline()) 145 | } 146 | 147 | fn filter_variable(v: &Variable, options: &Options) -> bool { 148 | if !v.is_declaration() && v.address().is_none() { 149 | // TODO: make this configurable? 150 | return false; 151 | } 152 | options.filter_name(v.name()) && options.filter_namespace(v.namespace()) 153 | } 154 | 155 | fn filter_type( 156 | ty: &Type, 157 | options: &Options, 158 | diff: bool, 159 | inline_types: &HashSet, 160 | ) -> bool { 161 | // Filter by user options. 162 | if !match ty.kind() { 163 | TypeKind::Base(val) => filter_base(val, options), 164 | TypeKind::Def(val) => filter_type_def(val, options), 165 | TypeKind::Struct(val) => filter_struct(val, options), 166 | TypeKind::Union(val) => filter_union(val, options), 167 | TypeKind::Enumeration(val) => filter_enumeration(val, options), 168 | TypeKind::Unspecified(val) => filter_unspecified(val, options), 169 | TypeKind::Void 170 | | TypeKind::Array(..) 171 | | TypeKind::Function(..) 172 | | TypeKind::PointerToMember(..) 173 | | TypeKind::Modifier(..) 174 | | TypeKind::Subrange(..) => options.filter_name.is_none(), 175 | } { 176 | return false; 177 | } 178 | match ty.kind() { 179 | TypeKind::Struct(val) => { 180 | // Hack for rust closures 181 | // TODO: is there better way of identifying these, or a 182 | // a way to match pairs for diffing? 183 | if diff && val.name() == Some("closure") { 184 | return false; 185 | } 186 | } 187 | TypeKind::Base(..) 188 | | TypeKind::Def(..) 189 | | TypeKind::Union(..) 190 | | TypeKind::Enumeration(..) => {} 191 | TypeKind::Void 192 | | TypeKind::Array(..) 193 | | TypeKind::Function(..) 194 | | TypeKind::Unspecified(..) 195 | | TypeKind::PointerToMember(..) 196 | | TypeKind::Modifier(..) 197 | | TypeKind::Subrange(..) => return false, 198 | } 199 | // Filter out inline types. 200 | ty.offset().is_some() && !inline_types.contains(&ty.offset()) 201 | } 202 | 203 | fn filter_base(ty: &BaseType, options: &Options) -> bool { 204 | options.filter_name(ty.name()) && options.filter_namespace(None) 205 | } 206 | 207 | fn filter_type_def(ty: &TypeDef, options: &Options) -> bool { 208 | options.filter_name(ty.name()) && options.filter_namespace(ty.namespace()) 209 | } 210 | 211 | fn filter_struct(ty: &StructType, options: &Options) -> bool { 212 | options.filter_name(ty.name()) && options.filter_namespace(ty.namespace()) 213 | } 214 | 215 | fn filter_union(ty: &UnionType, options: &Options) -> bool { 216 | options.filter_name(ty.name()) && options.filter_namespace(ty.namespace()) 217 | } 218 | 219 | fn filter_enumeration(ty: &EnumerationType, options: &Options) -> bool { 220 | options.filter_name(ty.name()) && options.filter_namespace(ty.namespace()) 221 | } 222 | 223 | fn filter_unspecified(ty: &UnspecifiedType, options: &Options) -> bool { 224 | options.filter_name(ty.name()) && options.filter_namespace(ty.namespace()) 225 | } 226 | -------------------------------------------------------------------------------- /main/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Enable some rust 2018 idioms. 2 | #![warn(bare_trait_objects)] 3 | #![warn(unused_extern_crates)] 4 | // Calm down clippy. 5 | #![allow(clippy::comparison_chain)] 6 | #![allow(clippy::too_many_arguments)] 7 | #![allow(clippy::type_complexity)] 8 | // False positive. 9 | #![allow(clippy::explicit_auto_deref)] 10 | // Requires type annotations which isn't better. 11 | #![allow(clippy::or_fun_call)] 12 | 13 | #[macro_use] 14 | extern crate log; 15 | 16 | use parser::Namespace; 17 | 18 | pub use parser::{Error, File, Result}; 19 | 20 | mod code; 21 | mod filter; 22 | 23 | mod print; 24 | pub use self::print::file::{ 25 | bloat, bloat_id, bloat_index, diff, diff_id, diff_index, print, print_id, print_index, 26 | print_parent, BloatIndex, DiffIndex, PrintIndex, 27 | }; 28 | pub use self::print::{DiffPrefix, HtmlPrinter, Id, Printer, TextPrinter}; 29 | 30 | #[derive(Default, Debug, Clone, Copy, PartialEq, Eq)] 31 | pub enum Sort { 32 | #[default] 33 | None, 34 | Name, 35 | Size, 36 | } 37 | 38 | #[derive(Debug, Default, Clone)] 39 | pub struct Options { 40 | pub print_source: bool, 41 | pub print_file_address: bool, 42 | pub print_unit_address: bool, 43 | pub print_function_calls: bool, 44 | pub print_function_instructions: bool, 45 | pub print_function_variables: bool, 46 | pub print_function_stack_frame: bool, 47 | pub print_inlined_function_parameters: bool, 48 | pub print_variable_locations: bool, 49 | pub inline_depth: usize, 50 | pub html: bool, 51 | pub http: bool, 52 | 53 | pub category_file: bool, 54 | pub category_unit: bool, 55 | pub category_type: bool, 56 | pub category_function: bool, 57 | pub category_variable: bool, 58 | 59 | pub filter_function_inline: Option, 60 | pub filter_name: Option, 61 | pub filter_namespace: Vec, 62 | pub filter_unit: Option, 63 | 64 | pub sort: Sort, 65 | 66 | pub ignore_added: bool, 67 | pub ignore_deleted: bool, 68 | pub ignore_function_address: bool, 69 | pub ignore_function_size: bool, 70 | pub ignore_function_inline: bool, 71 | pub ignore_function_linkage_name: bool, 72 | pub ignore_function_symbol_name: bool, 73 | pub ignore_variable_address: bool, 74 | pub ignore_variable_linkage_name: bool, 75 | pub ignore_variable_symbol_name: bool, 76 | pub prefix_map: Vec<(String, String)>, 77 | } 78 | 79 | impl Options { 80 | pub fn unit(&mut self, unit: &str) -> &mut Self { 81 | self.filter_unit = Some(unit.into()); 82 | self 83 | } 84 | 85 | pub fn name(&mut self, name: &str) -> &mut Self { 86 | self.filter_name = Some(name.into()); 87 | self 88 | } 89 | 90 | fn filter_function_inline(&self, inline: bool) -> bool { 91 | self.filter_function_inline.is_none() || self.filter_function_inline == Some(inline) 92 | } 93 | 94 | fn filter_name(&self, name: Option<&str>) -> bool { 95 | self.filter_name.is_none() || self.filter_name.as_ref().map(String::as_ref) == name 96 | } 97 | 98 | fn filter_namespace(&self, namespace: Option<&Namespace>) -> bool { 99 | self.filter_namespace.is_empty() || { 100 | match namespace { 101 | Some(namespace) => namespace.is_within(&self.filter_namespace), 102 | None => false, 103 | } 104 | } 105 | } 106 | 107 | fn prefix_map<'name>(&self, name: &'name str) -> (&str, &'name str) { 108 | for (old, new) in &self.prefix_map { 109 | if name.starts_with(old) { 110 | return (new, &name[old.len()..]); 111 | } 112 | } 113 | ("", name) 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /main/src/print/base_type.rs: -------------------------------------------------------------------------------- 1 | use parser::{BaseType, BaseTypeEncoding, Endianity, FileHash, Unit}; 2 | 3 | use crate::print::{DiffState, PrintHeader, PrintState, ValuePrinter}; 4 | use crate::Result; 5 | 6 | fn print_name(ty: &BaseType, w: &mut dyn ValuePrinter) -> Result<()> { 7 | write!(w, "base {}", ty.name().unwrap_or(""))?; 8 | Ok(()) 9 | } 10 | 11 | pub(crate) fn print_ref(ty: &BaseType, w: &mut dyn ValuePrinter, id: usize) -> Result<()> { 12 | w.link(id, &mut |w| { 13 | write!(w, "{}", ty.name().unwrap_or(""))?; 14 | Ok(()) 15 | }) 16 | } 17 | 18 | impl<'input> PrintHeader for BaseType<'input> { 19 | fn print_header(&self, state: &mut PrintState) -> Result<()> { 20 | state.line(|w, _state| print_name(self, w)) 21 | } 22 | 23 | fn print_body(&self, state: &mut PrintState, _unit: &Unit) -> Result<()> { 24 | state.field("size", |w, state| print_byte_size(self, w, state))?; 25 | state.field("encoding", |w, state| print_encoding(self, w, state))?; 26 | state.field("endianity", |w, state| print_endianity(self, w, state)) 27 | } 28 | 29 | fn diff_header(state: &mut DiffState, a: &Self, b: &Self) -> Result<()> { 30 | state.line(a, b, |w, _state, x| print_name(x, w)) 31 | } 32 | 33 | fn diff_body( 34 | state: &mut DiffState, 35 | _unit_a: &parser::Unit, 36 | a: &Self, 37 | _unit_b: &parser::Unit, 38 | b: &Self, 39 | ) -> Result<()> { 40 | state.field("size", a, b, |w, state, x| print_byte_size(x, w, state))?; 41 | state.field("encoding", a, b, |w, state, x| print_encoding(x, w, state))?; 42 | state.field("endianity", a, b, |w, state, x| { 43 | print_endianity(x, w, state) 44 | }) 45 | } 46 | } 47 | 48 | fn print_byte_size(ty: &BaseType, w: &mut dyn ValuePrinter, _hash: &FileHash) -> Result<()> { 49 | if let Some(size) = ty.byte_size() { 50 | write!(w, "{}", size)?; 51 | } else { 52 | debug!("base type with no size"); 53 | } 54 | Ok(()) 55 | } 56 | 57 | fn print_encoding(ty: &BaseType, w: &mut dyn ValuePrinter, _hash: &FileHash) -> Result<()> { 58 | let name = match ty.encoding() { 59 | BaseTypeEncoding::Other => return Ok(()), 60 | BaseTypeEncoding::Boolean => "boolean", 61 | BaseTypeEncoding::Address => "address", 62 | BaseTypeEncoding::Signed => "signed", 63 | BaseTypeEncoding::SignedChar => "signed char", 64 | BaseTypeEncoding::Unsigned => "unsigned", 65 | BaseTypeEncoding::UnsignedChar => "unsigned char", 66 | BaseTypeEncoding::Float => "floating-point", 67 | }; 68 | write!(w, "{}", name)?; 69 | Ok(()) 70 | } 71 | 72 | fn print_endianity(ty: &BaseType, w: &mut dyn ValuePrinter, _hash: &FileHash) -> Result<()> { 73 | let name = match ty.endianity() { 74 | Endianity::Default => return Ok(()), 75 | Endianity::Big => "big", 76 | Endianity::Little => "little", 77 | }; 78 | write!(w, "{}", name)?; 79 | Ok(()) 80 | } 81 | -------------------------------------------------------------------------------- /main/src/print/enumeration.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::{EnumerationType, Enumerator, FileHash, Unit}; 4 | 5 | use crate::print::{self, DiffList, DiffState, Print, PrintHeader, PrintState, ValuePrinter}; 6 | use crate::Result; 7 | 8 | fn print_name(ty: &EnumerationType, w: &mut dyn ValuePrinter) -> Result<()> { 9 | write!(w, "enum ")?; 10 | if let Some(namespace) = ty.namespace() { 11 | print::namespace::print(namespace, w)?; 12 | } 13 | w.name(ty.name().unwrap_or(""))?; 14 | Ok(()) 15 | } 16 | 17 | pub(crate) fn print_ref(ty: &EnumerationType, w: &mut dyn ValuePrinter, id: usize) -> Result<()> { 18 | w.link(id, &mut |w| print_name(ty, w)) 19 | } 20 | 21 | impl<'input> PrintHeader for EnumerationType<'input> { 22 | fn print_header(&self, state: &mut PrintState) -> Result<()> { 23 | state.line(|w, _state| print_name(self, w)) 24 | } 25 | 26 | fn print_body(&self, state: &mut PrintState, unit: &Unit) -> Result<()> { 27 | if state.options().print_source { 28 | state.field("source", |w, _state| print_source(self, w, unit))?; 29 | } 30 | state.field("declaration", |w, _state| print_declaration(self, w))?; 31 | state.field("size", |w, state| print_byte_size(self, w, state))?; 32 | let enumerators = self.enumerators(state.hash()); 33 | state.field_expanded("enumerators", |state| state.list(unit, &enumerators)) 34 | } 35 | 36 | fn diff_header(state: &mut DiffState, a: &Self, b: &Self) -> Result<()> { 37 | state.line(a, b, |w, _state, x| print_name(x, w)) 38 | } 39 | 40 | fn diff_body( 41 | state: &mut DiffState, 42 | unit_a: &parser::Unit, 43 | a: &Self, 44 | unit_b: &parser::Unit, 45 | b: &Self, 46 | ) -> Result<()> { 47 | if state.options().print_source { 48 | state.field( 49 | "source", 50 | (unit_a, a), 51 | (unit_b, b), 52 | |w, _state, (unit, x)| print_source(x, w, unit), 53 | )?; 54 | } 55 | state.field("declaration", a, b, |w, _state, x| print_declaration(x, w))?; 56 | state.field("size", a, b, |w, state, x| print_byte_size(x, w, state))?; 57 | // TODO: handle reordering better 58 | let enumerators_a = a.enumerators(state.hash_a()); 59 | let enumerators_b = b.enumerators(state.hash_b()); 60 | state.field_expanded("enumerators", |state| { 61 | state.list(unit_a, &enumerators_a, unit_b, &enumerators_b) 62 | }) 63 | } 64 | } 65 | 66 | fn print_source(ty: &EnumerationType, w: &mut dyn ValuePrinter, unit: &Unit) -> Result<()> { 67 | print::source::print(ty.source(), w, unit) 68 | } 69 | 70 | fn print_declaration(ty: &EnumerationType, w: &mut dyn ValuePrinter) -> Result<()> { 71 | if ty.is_declaration() { 72 | write!(w, "yes")?; 73 | } 74 | Ok(()) 75 | } 76 | 77 | fn print_byte_size(ty: &EnumerationType, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 78 | if let Some(size) = ty.byte_size(hash) { 79 | write!(w, "{}", size)?; 80 | } else { 81 | debug!("enum with no size"); 82 | } 83 | Ok(()) 84 | } 85 | 86 | fn print_enumerator(ty: &Enumerator, w: &mut dyn ValuePrinter) -> Result<()> { 87 | write!(w, "{}", ty.name().unwrap_or(""))?; 88 | if let Some(value) = ty.value() { 89 | write!(w, "({})", value)?; 90 | } 91 | Ok(()) 92 | } 93 | 94 | impl<'input> Print for Enumerator<'input> { 95 | type Arg = Unit<'input>; 96 | 97 | fn print(&self, state: &mut PrintState, _unit: &Unit) -> Result<()> { 98 | state.line(|w, _state| print_enumerator(self, w)) 99 | } 100 | 101 | fn diff( 102 | state: &mut DiffState, 103 | _unit_a: &Unit, 104 | a: &Self, 105 | _unit_b: &Unit, 106 | b: &Self, 107 | ) -> Result<()> { 108 | state.line(a, b, |w, _state, x| print_enumerator(x, w)) 109 | } 110 | } 111 | 112 | impl<'input> DiffList for Enumerator<'input> { 113 | fn step_cost(&self, _state: &DiffState, _arg: &Unit) -> usize { 114 | 3 115 | } 116 | 117 | fn diff_cost(_state: &DiffState, _unit_a: &Unit, a: &Self, _unit_b: &Unit, b: &Self) -> usize { 118 | // A difference in name is usually more significant than a difference in value, 119 | // such as for enums where the value is assigned by the compiler. 120 | let mut cost = 0; 121 | if a.name().cmp(&b.name()) != cmp::Ordering::Equal { 122 | cost += 4; 123 | } 124 | if a.value().cmp(&b.value()) != cmp::Ordering::Equal { 125 | cost += 2; 126 | } 127 | cost 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /main/src/print/frame_location.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::FrameLocation; 4 | 5 | use crate::print::{self, DiffList, DiffState, Print, PrintState, ValuePrinter}; 6 | use crate::Result; 7 | 8 | pub(crate) fn print_list( 9 | state: &mut PrintState, 10 | mut frame_locations: Vec, 11 | ) -> Result<()> { 12 | frame_locations.sort_unstable(); 13 | frame_locations.dedup(); 14 | if frame_locations.len() > 1 { 15 | state.field_expanded("stack frame", |state| state.list(&(), &frame_locations))?; 16 | } else if let Some(frame_location) = frame_locations.first() { 17 | state.field("stack frame", |w, _state| { 18 | print::frame_location::print(frame_location, w) 19 | })?; 20 | } 21 | Ok(()) 22 | } 23 | 24 | pub(crate) fn diff_list( 25 | state: &mut DiffState, 26 | mut frame_locations_a: Vec, 27 | mut frame_locations_b: Vec, 28 | ) -> Result<()> { 29 | frame_locations_a.sort_unstable(); 30 | frame_locations_a.dedup(); 31 | frame_locations_b.sort_unstable(); 32 | frame_locations_b.dedup(); 33 | if frame_locations_a.len() > 1 || frame_locations_a.len() > 1 { 34 | state.field_expanded("stack frame", |state| { 35 | state.ord_list(&(), &frame_locations_a, &(), &frame_locations_b) 36 | })?; 37 | } else { 38 | let location_a = frame_locations_a.first(); 39 | let location_b = frame_locations_b.first(); 40 | state.field( 41 | "stack frame", 42 | location_a, 43 | location_b, 44 | |w, _state, location| { 45 | if let Some(location) = location { 46 | print::frame_location::print(location, w)?; 47 | } 48 | Ok(()) 49 | }, 50 | )?; 51 | } 52 | Ok(()) 53 | } 54 | 55 | pub(crate) fn print(location: &FrameLocation, w: &mut dyn ValuePrinter) -> Result<()> { 56 | write!(w, "{}", location.offset)?; 57 | if let Some(bit_size) = location.bit_size.get() { 58 | write!(w, "[{}]", (bit_size + 7) / 8)?; 59 | } 60 | Ok(()) 61 | } 62 | 63 | impl Print for FrameLocation { 64 | type Arg = (); 65 | 66 | fn print(&self, state: &mut PrintState, _arg: &()) -> Result<()> { 67 | state.line(|w, _hash| print(self, w)) 68 | } 69 | 70 | fn diff(state: &mut DiffState, _arg_a: &(), a: &Self, _arg_b: &(), b: &Self) -> Result<()> { 71 | state.line(a, b, |w, _hash, x| print(x, w)) 72 | } 73 | } 74 | 75 | impl DiffList for FrameLocation { 76 | fn step_cost(&self, _state: &DiffState, _arg: &()) -> usize { 77 | 1 78 | } 79 | 80 | fn diff_cost(_state: &DiffState, _unit_a: &(), a: &Self, _unit_b: &(), b: &Self) -> usize { 81 | let mut cost = 0; 82 | if a.cmp(b) != cmp::Ordering::Equal { 83 | cost += 1; 84 | } 85 | cost 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /main/src/print/html.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use marksman_escape::Escape; 4 | 5 | use super::{DiffPrefix, Printer, ValuePrinter}; 6 | use crate::{Options, Result}; 7 | 8 | const HEADER: &str = r##" 9 | 10 | 11 | 12 | 68 | 215 | 216 | 217 |
218 |
219 |
    "##; 220 | 221 | const FOOTER: &str = r#"
222 |
223 |
224 |
225 |
226 |
227 | 228 | "#; 229 | 230 | pub struct HtmlPrinter<'w> { 231 | w: &'w mut dyn Write, 232 | indent: usize, 233 | prefix: DiffPrefix, 234 | inline_depth: usize, 235 | // Hack to allow indented
    to be included within parent
  • . 236 | line_started: bool, 237 | http: bool, 238 | } 239 | 240 | impl<'w> HtmlPrinter<'w> { 241 | pub fn new(w: &'w mut dyn Write, options: &Options) -> Self { 242 | HtmlPrinter { 243 | w, 244 | indent: 0, 245 | prefix: DiffPrefix::None, 246 | inline_depth: options.inline_depth, 247 | line_started: false, 248 | http: options.http, 249 | } 250 | } 251 | 252 | pub fn begin(&mut self) -> Result<()> { 253 | self.w.write_all(HEADER.as_bytes())?; 254 | Ok(()) 255 | } 256 | 257 | pub fn end(&mut self) -> Result<()> { 258 | self.w.write_all(FOOTER.as_bytes())?; 259 | Ok(()) 260 | } 261 | } 262 | 263 | impl<'w> Printer for HtmlPrinter<'w> { 264 | fn value( 265 | &mut self, 266 | buf: &mut Vec, 267 | f: &mut dyn FnMut(&mut dyn ValuePrinter) -> Result<()>, 268 | ) -> Result<()> { 269 | let mut p = HtmlValuePrinter { w: buf }; 270 | f(&mut p) 271 | } 272 | 273 | /// Calls `f` to write to a temporary buffer. 274 | fn buffer( 275 | &mut self, 276 | buf: &mut Vec, 277 | f: &mut dyn FnMut(&mut dyn Printer) -> Result<()>, 278 | ) -> Result<()> { 279 | let mut p = HtmlPrinter { 280 | w: buf, 281 | // TODO: need to ensure these are all unchanged? 282 | indent: self.indent, 283 | prefix: self.prefix, 284 | inline_depth: self.inline_depth, 285 | line_started: self.line_started, 286 | http: self.http, 287 | }; 288 | f(&mut p)?; 289 | Ok(()) 290 | } 291 | 292 | fn write_buf(&mut self, buf: &[u8]) -> Result<()> { 293 | self.w.write_all(buf)?; 294 | Ok(()) 295 | } 296 | 297 | fn line_break(&mut self) -> Result<()> { 298 | Ok(()) 299 | } 300 | 301 | fn line(&mut self, label: &str, buf: &[u8]) -> Result<()> { 302 | if !self.line_started { 303 | write!(self.w, "
  • ")?; 304 | } 305 | if buf.is_empty() { 306 | write!(self.w, "{}:", label)?; 311 | } else { 312 | if !label.is_empty() { 313 | write!(self.w, "{}: ", label)?; 314 | } 315 | write!(self.w, " write!(self.w, " class=\"field\"")?, 318 | DiffPrefix::Modify => { 319 | write!(self.w, " class=\"field mod\"")?; 320 | } 321 | DiffPrefix::Delete => { 322 | write!(self.w, " class=\"field del\"")?; 323 | } 324 | DiffPrefix::Add => { 325 | write!(self.w, " class=\"field add\"")?; 326 | } 327 | } 328 | write!(self.w, ">")?; 329 | self.w.write_all(buf)?; 330 | write!(self.w, "")?; 331 | } 332 | if !self.line_started { 333 | writeln!(self.w, "
  • ")?; 334 | } 335 | Ok(()) 336 | } 337 | 338 | fn line_diff(&mut self, label: &str, a: &[u8], b: &[u8]) -> Result<()> { 339 | if !self.line_started { 340 | write!(self.w, "
  • ")?; 341 | } 342 | if !label.is_empty() { 343 | write!(self.w, "{}: ", label)?; 344 | } 345 | write!(self.w, "")?; 346 | self.w.write_all(a)?; 347 | write!(self.w, "
    \n")?; 348 | self.w.write_all(b)?; 349 | write!(self.w, "
    ")?; 350 | if !self.line_started { 351 | writeln!(self.w, "
  • ")?; 352 | } 353 | Ok(()) 354 | } 355 | 356 | fn indent_body( 357 | &mut self, 358 | buf: &mut Vec, 359 | body: &mut dyn FnMut(&mut dyn Printer) -> Result<()>, 360 | ) -> Result<()> { 361 | let mut printer = HtmlPrinter { 362 | w: buf, 363 | // TODO: need to ensure these are all unchanged? 364 | indent: self.indent + 1, 365 | prefix: self.prefix, 366 | inline_depth: self.inline_depth, 367 | line_started: false, 368 | http: self.http, 369 | }; 370 | body(&mut printer)?; 371 | Ok(()) 372 | } 373 | 374 | fn indent_header( 375 | &mut self, 376 | collapsed: bool, 377 | body: &[u8], 378 | header: &mut dyn FnMut(&mut dyn Printer) -> Result<()>, 379 | ) -> Result<()> { 380 | debug_assert!(!self.line_started); 381 | write!(self.w, "
  • ")?; 382 | self.line_started = true; 383 | header(self)?; 384 | if collapsed { 385 | writeln!(self.w, "
      ")?; 386 | } else { 387 | writeln!(self.w, "
        ")?; 388 | } 389 | self.write_buf(body)?; 390 | writeln!(self.w, "
      ")?; 391 | self.line_started = false; 392 | Ok(()) 393 | } 394 | 395 | fn indent_id( 396 | &mut self, 397 | id: usize, 398 | header: &mut dyn FnMut(&mut dyn Printer) -> Result<()>, 399 | body: &mut dyn FnMut(&mut dyn Printer) -> Result<()>, 400 | ) -> Result<()> { 401 | debug_assert!(!self.line_started); 402 | write!( 403 | self.w, 404 | "
    • ", 405 | if self.http { " lazy" } else { "" }, 406 | id, 407 | )?; 408 | self.line_started = true; 409 | header(self)?; 410 | writeln!(self.w, "
        ")?; 411 | self.line_started = false; 412 | if !self.http { 413 | body(self)?; 414 | } 415 | writeln!(self.w, "
    • ")?; 416 | Ok(()) 417 | } 418 | 419 | fn indent_detail(&mut self, id: &str, label: &str) -> Result<()> { 420 | debug_assert!(self.http); 421 | debug_assert!(!self.line_started); 422 | write!(self.w, "
    • ", id,)?; 423 | self.line_started = true; 424 | self.line(label, &[])?; 425 | writeln!(self.w, "
    • ")?; 426 | self.line_started = false; 427 | Ok(()) 428 | } 429 | 430 | fn prefix(&mut self, prefix: DiffPrefix) { 431 | self.prefix = prefix; 432 | } 433 | 434 | fn get_prefix(&self) -> DiffPrefix { 435 | self.prefix 436 | } 437 | 438 | fn inline_begin(&mut self) -> bool { 439 | if self.inline_depth == 0 { 440 | false 441 | } else { 442 | self.inline_depth -= 1; 443 | true 444 | } 445 | } 446 | 447 | fn inline_end(&mut self) { 448 | self.inline_depth += 1; 449 | } 450 | 451 | fn instruction(&mut self, address: Option, mnemonic: &str, buf: &[u8]) -> Result<()> { 452 | write!(self.w, "")?; 453 | if let Some(address) = address { 454 | write!(self.w, "{:x}", address)?; 455 | } 456 | write!(self.w, "{}", mnemonic)?; 457 | self.w.write_all(buf)?; 458 | write!(self.w, "")?; 459 | Ok(()) 460 | } 461 | } 462 | 463 | fn escaped(bytes: &[u8]) -> Vec { 464 | Escape::new(bytes.iter().cloned()).collect() 465 | } 466 | 467 | fn escaped_str(s: &str) -> String { 468 | String::from_utf8(Escape::new(s.as_bytes().iter().cloned()).collect()).unwrap() 469 | } 470 | 471 | struct HtmlValuePrinter<'w> { 472 | w: &'w mut Vec, 473 | } 474 | 475 | impl<'w> Write for HtmlValuePrinter<'w> { 476 | fn write(&mut self, buf: &[u8]) -> std::result::Result { 477 | self.w.write_all(&escaped(buf))?; 478 | Ok(buf.len()) 479 | } 480 | 481 | fn flush(&mut self) -> std::result::Result<(), std::io::Error> { 482 | self.w.flush() 483 | } 484 | } 485 | 486 | impl<'w> ValuePrinter for HtmlValuePrinter<'w> { 487 | fn link( 488 | &mut self, 489 | id: usize, 490 | f: &mut dyn FnMut(&mut dyn ValuePrinter) -> Result<()>, 491 | ) -> Result<()> { 492 | if id == 0 { 493 | f(self) 494 | } else { 495 | write!( 496 | self.w, 497 | "", 498 | id, id, 499 | )?; 500 | f(self)?; 501 | write!(self.w, "")?; 502 | Ok(()) 503 | } 504 | } 505 | 506 | fn name(&mut self, name: &str) -> Result<()> { 507 | let mut generic = 0; 508 | let mut colon = 0; 509 | let mut brace = 0; 510 | let mut result = String::new(); 511 | let mut s = String::new(); 512 | // Strip leading namespaces from generics in Rust. 513 | for c in name.chars() { 514 | if c == '{' { 515 | // Start of brace. Treat like part of an identifier. 516 | brace += 1; 517 | s.push(c); 518 | } else if c == '}' { 519 | // End of brace. Treat like part of an identifier. 520 | if brace > 0 { 521 | brace -= 1; 522 | } 523 | s.push(c); 524 | } else if brace > 0 { 525 | // Inside brace. Treat like part of an identifier. 526 | s.push(c); 527 | } else if c == '<' { 528 | // Start of a generic. Previous string is identifier, not namespace. 529 | result.push_str(&s); 530 | s.clear(); 531 | generic += 1; 532 | if generic <= 2 { 533 | result.push(c); 534 | } 535 | if generic == 2 { 536 | // Deeply nested generic. Skip everything. 537 | result.push_str("..."); 538 | } 539 | } else if c == '>' { 540 | // End of a generic. Previous string is identifier, not namespace. 541 | if generic <= 2 { 542 | result.push_str(&s); 543 | s.clear(); 544 | result.push(c); 545 | } 546 | if generic > 0 { 547 | generic -= 1; 548 | } 549 | } else if generic == 0 { 550 | // Not in generic. Allow everything. 551 | result.push(c); 552 | } else if generic >= 2 { 553 | // Deeply nested generic. Skip everything. 554 | } else if c == ':' { 555 | if colon == 0 { 556 | // Possible start of namespace separator. 557 | colon = 1; 558 | s.push(c); 559 | } else { 560 | // Namespace separator so discard s. 561 | colon = 0; 562 | s.clear(); 563 | } 564 | } else if c == '_' || c.is_alphanumeric() { 565 | // Part of an identifier. 566 | // TODO: probably missing some allowable characters here. 567 | s.push(c); 568 | } else { 569 | // Some other punctuation. Previous string is identifier, not namespace. 570 | result.push_str(&s); 571 | s.clear(); 572 | result.push(c); 573 | } 574 | } 575 | result.push_str(&s); 576 | if name != result { 577 | write!( 578 | self.w, 579 | "{}", 580 | &escaped_str(name), 581 | &escaped_str(&result), 582 | )?; 583 | } else { 584 | self.w.write_all(&escaped(name.as_bytes()))?; 585 | } 586 | Ok(()) 587 | } 588 | } 589 | -------------------------------------------------------------------------------- /main/src/print/inherit.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::{FileHash, Inherit, Type}; 4 | 5 | use crate::print::{self, DiffList, DiffState, Print, PrintState, ValuePrinter}; 6 | use crate::Result; 7 | 8 | pub(crate) fn print_list(state: &mut PrintState, inherits: &[Inherit]) -> Result<()> { 9 | if inherits.len() > 1 { 10 | state.field_expanded("inherits", |state| state.list(&(), inherits))?; 11 | } else if let Some(inherit) = inherits.first() { 12 | state.field("inherits", |w, hash| print_inherit(inherit, w, hash))?; 13 | } 14 | Ok(()) 15 | } 16 | 17 | pub(crate) fn diff_list( 18 | state: &mut DiffState, 19 | inherits_a: &[Inherit], 20 | inherits_b: &[Inherit], 21 | ) -> Result<()> { 22 | if inherits_a.len() > 1 || inherits_b.len() > 1 { 23 | state.field_expanded("inherits", |state| { 24 | state.list(&(), inherits_a, &(), inherits_b) 25 | })?; 26 | } else { 27 | let inherit_a = inherits_a.first(); 28 | let inherit_b = inherits_b.first(); 29 | state.field("inherits", inherit_a, inherit_b, |w, hash, inherit| { 30 | if let Some(inherit) = inherit { 31 | print_inherit(inherit, w, hash)?; 32 | } 33 | Ok(()) 34 | })?; 35 | } 36 | Ok(()) 37 | } 38 | 39 | fn print_inherit(inherit: &Inherit, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 40 | print::types::print_ref(inherit.ty(hash), w, hash) 41 | } 42 | 43 | impl Print for Inherit { 44 | type Arg = (); 45 | 46 | fn print(&self, state: &mut PrintState, _arg: &()) -> Result<()> { 47 | state.line(|w, hash| print_inherit(self, w, hash)) 48 | } 49 | 50 | fn diff(state: &mut DiffState, _arg_a: &(), a: &Self, _arg_b: &(), b: &Self) -> Result<()> { 51 | state.line(a, b, |w, hash, x| print_inherit(x, w, hash)) 52 | } 53 | } 54 | 55 | impl DiffList for Inherit { 56 | fn step_cost(&self, _state: &DiffState, _arg: &()) -> usize { 57 | 1 58 | } 59 | 60 | fn diff_cost(state: &DiffState, _arg_a: &(), a: &Self, _arg_b: &(), b: &Self) -> usize { 61 | let mut cost = 0; 62 | match (&a.ty(state.hash_a()), &b.ty(state.hash_b())) { 63 | (Some(ty_a), Some(ty_b)) => { 64 | if Type::cmp_id(state.hash_a(), ty_a, state.hash_b(), ty_b) != cmp::Ordering::Equal 65 | { 66 | cost += 2; 67 | } 68 | } 69 | (None, None) => {} 70 | _ => { 71 | cost += 2; 72 | } 73 | } 74 | cost 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /main/src/print/inlined_function.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::{FileHash, Function, InlinedFunction, LocalVariable, Unit}; 4 | 5 | use crate::print::{self, DiffList, DiffState, Print, PrintState, SortList, ValuePrinter}; 6 | use crate::Result; 7 | 8 | fn print_size_and_decl( 9 | f: &InlinedFunction, 10 | w: &mut dyn ValuePrinter, 11 | hash: &FileHash, 12 | ) -> Result<()> { 13 | match f.size() { 14 | Some(size) => write!(w, "[{}]", size)?, 15 | None => write!(w, "[??]")?, 16 | } 17 | write!(w, "\t")?; 18 | match f.abstract_origin(hash) { 19 | Some(function) => print::function::print_ref(function, w)?, 20 | None => write!(w, "")?, 21 | } 22 | Ok(()) 23 | } 24 | 25 | fn print_call_source(f: &InlinedFunction, w: &mut dyn ValuePrinter, unit: &Unit) -> Result<()> { 26 | print::source::print(f.call_source(), w, unit) 27 | } 28 | 29 | impl<'input> Print for InlinedFunction<'input> { 30 | type Arg = Unit<'input>; 31 | 32 | fn print(&self, state: &mut PrintState, unit: &Unit) -> Result<()> { 33 | state.collapsed( 34 | // TODO: for html, print_size_and_decl includes the function ID, 35 | // which requires that there are no duplicate functions 36 | |state| state.line(|w, state| print_size_and_decl(self, w, state)), 37 | |state| { 38 | if state.options().print_source { 39 | state.field("call source", |w, _state| print_call_source(self, w, unit))?; 40 | } 41 | if state.options().print_inlined_function_parameters { 42 | state.field_expanded("parameters", |state| { 43 | state.list(unit, self.parameters()) 44 | })?; 45 | } 46 | if state.options().print_function_variables { 47 | state 48 | .field_collapsed("variables", |state| state.list(unit, self.variables()))?; 49 | } 50 | state.inline(|state| state.list(unit, self.inlined_functions()))?; 51 | Ok(()) 52 | }, 53 | )?; 54 | Ok(()) 55 | } 56 | 57 | fn diff(state: &mut DiffState, unit_a: &Unit, a: &Self, unit_b: &Unit, b: &Self) -> Result<()> { 58 | state.collapsed( 59 | |state| state.line(a, b, |w, state, x| print_size_and_decl(x, w, state)), 60 | |state| { 61 | if state.options().print_source { 62 | state.field( 63 | "call source", 64 | (unit_a, a), 65 | (unit_b, b), 66 | |w, _state, (unit, x)| print_call_source(x, w, unit), 67 | )?; 68 | } 69 | if state.options().print_inlined_function_parameters { 70 | state.field_expanded("parameters", |state| { 71 | state.list(unit_a, a.parameters(), unit_b, b.parameters()) 72 | })?; 73 | } 74 | if state.options().print_function_variables { 75 | let mut variables_a: Vec<_> = a.variables().iter().collect(); 76 | variables_a.sort_by(|x, y| { 77 | LocalVariable::cmp_id(state.hash_a(), x, state.hash_a(), y) 78 | }); 79 | let mut variables_b: Vec<_> = b.variables().iter().collect(); 80 | variables_b.sort_by(|x, y| { 81 | LocalVariable::cmp_id(state.hash_b(), x, state.hash_b(), y) 82 | }); 83 | state.field_collapsed("variables", |state| { 84 | state.list(unit_a, &variables_a, unit_b, &variables_b) 85 | })?; 86 | } 87 | state.inline(|state| { 88 | state.list(unit_a, a.inlined_functions(), unit_b, b.inlined_functions()) 89 | })?; 90 | Ok(()) 91 | }, 92 | )?; 93 | 94 | Ok(()) 95 | } 96 | } 97 | 98 | impl<'input> DiffList for InlinedFunction<'input> { 99 | // Make the cost proportional to the size, so that we give priority to matching large 100 | // functions. Probably not ideal, but seemed to help for one test case. 101 | fn step_cost(&self, _state: &DiffState, _arg: &Unit) -> usize { 102 | // Ensure cost is at least 1. 103 | 2 + 4 * self.size().unwrap_or(0) as usize 104 | } 105 | 106 | // TODO: other options to consider: 107 | // - include diff cost of lower levels of inlined functions 108 | fn diff_cost(state: &DiffState, unit_a: &Unit, a: &Self, unit_b: &Unit, b: &Self) -> usize { 109 | let mut cost = 0; 110 | let function_a = a.abstract_origin(state.hash_a()); 111 | let function_b = b.abstract_origin(state.hash_b()); 112 | match (function_a, function_b) { 113 | (Some(function_a), Some(function_b)) => { 114 | if ::cmp_id( 115 | state.hash_a(), 116 | function_a, 117 | state.hash_b(), 118 | function_b, 119 | state.options(), 120 | ) != cmp::Ordering::Equal 121 | { 122 | cost += 3; 123 | } 124 | } 125 | (None, None) => {} 126 | _ => { 127 | cost += 3; 128 | } 129 | } 130 | 131 | let path_a = a.call_source().path(unit_a); 132 | let path_b = b.call_source().path(unit_b); 133 | if path_a.cmp(&path_b) != cmp::Ordering::Equal 134 | || a.call_source().line().cmp(&b.call_source().line()) != cmp::Ordering::Equal 135 | || a.call_source().column().cmp(&b.call_source().column()) != cmp::Ordering::Equal 136 | { 137 | cost += 1; 138 | } 139 | 140 | // max diff_cost needs be a.step_cost + b.step_cost = 4 + 4 * a.size + 4 * b.size 141 | // max cost so far is 4 142 | cost *= 1 + (a.size().unwrap_or(0) + b.size().unwrap_or(0)) as usize; 143 | cost 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /main/src/print/local_variable.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::{FileHash, LocalVariable, Type, Unit}; 4 | 5 | use crate::print::{self, DiffList, DiffState, Print, PrintState, ValuePrinter}; 6 | use crate::Result; 7 | 8 | pub(crate) fn print_decl( 9 | v: &LocalVariable, 10 | w: &mut dyn ValuePrinter, 11 | hash: &FileHash, 12 | ) -> Result<()> { 13 | write!(w, "{}: ", v.name().unwrap_or(""))?; 14 | print::types::print_ref(v.ty(hash), w, hash)?; 15 | Ok(()) 16 | } 17 | 18 | fn print_size_and_decl(v: &LocalVariable, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 19 | // TODO: print address? 20 | match v.byte_size(hash) { 21 | Some(byte_size) => write!(w, "[{}]", byte_size)?, 22 | None => write!(w, "[??]")?, 23 | } 24 | write!(w, "\t")?; 25 | print_decl(v, w, hash) 26 | } 27 | 28 | fn print_address(v: &LocalVariable, w: &mut dyn ValuePrinter) -> Result<()> { 29 | if let Some(address) = v.address() { 30 | write!(w, "0x{:x}", address)?; 31 | } 32 | Ok(()) 33 | } 34 | 35 | impl<'input> Print for LocalVariable<'input> { 36 | type Arg = Unit<'input>; 37 | 38 | fn print(&self, state: &mut PrintState, _unit: &Unit) -> Result<()> { 39 | state.expanded( 40 | |state| state.line(|w, state| print_size_and_decl(self, w, state)), 41 | |state| { 42 | if state.options().print_variable_locations { 43 | state.field("address", |w, _state| print_address(self, w))?; 44 | print::register::print_list(state, self.registers().map(|x| x.1).collect())?; 45 | print::frame_location::print_list(state, self.frame_locations().collect())?; 46 | } 47 | Ok(()) 48 | }, 49 | ) 50 | } 51 | 52 | fn diff( 53 | state: &mut DiffState, 54 | _unit_a: &Unit, 55 | a: &Self, 56 | _unit_b: &Unit, 57 | b: &Self, 58 | ) -> Result<()> { 59 | state.expanded( 60 | |state| state.line(a, b, |w, state, x| print_size_and_decl(x, w, state)), 61 | |state| { 62 | if state.options().print_variable_locations { 63 | // TODO: should we ignore diff for all locations? 64 | let flag = state.options().ignore_variable_address; 65 | state.ignore_diff(flag, |state| { 66 | state.field("address", a, b, |w, _state, x| print_address(x, w)) 67 | })?; 68 | 69 | print::register::diff_list( 70 | state, 71 | a.registers().map(|x| x.1).collect(), 72 | b.registers().map(|x| x.1).collect(), 73 | )?; 74 | print::frame_location::diff_list( 75 | state, 76 | a.frame_locations().collect(), 77 | b.frame_locations().collect(), 78 | )?; 79 | } 80 | Ok(()) 81 | }, 82 | ) 83 | } 84 | } 85 | 86 | impl<'a, 'input> DiffList for &'a LocalVariable<'input> { 87 | fn step_cost(&self, _state: &DiffState, _arg: &Unit) -> usize { 88 | 1 89 | } 90 | 91 | fn diff_cost(state: &DiffState, _unit_a: &Unit, a: &Self, _unit_b: &Unit, b: &Self) -> usize { 92 | let mut cost = 0; 93 | if a.name().cmp(&b.name()) != cmp::Ordering::Equal { 94 | cost += 1; 95 | } 96 | match (&a.ty(state.hash_a()), &b.ty(state.hash_b())) { 97 | (Some(ty_a), Some(ty_b)) => { 98 | if Type::cmp_id(state.hash_a(), ty_a, state.hash_b(), ty_b) != cmp::Ordering::Equal 99 | { 100 | cost += 1; 101 | } 102 | } 103 | (None, None) => {} 104 | _ => { 105 | cost += 1; 106 | } 107 | } 108 | cost 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /main/src/print/member.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::{FileHash, Inherit, Layout, LayoutItem, Member, Type, Unit, Variant, VariantPart}; 4 | 5 | use crate::print::{self, DiffList, DiffState, Print, PrintState, ValuePrinter}; 6 | use crate::Result; 7 | 8 | fn print_member(member: &Member, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 9 | write!(w, "{}", format_bit(member.bit_offset()))?; 10 | match member.bit_size(hash) { 11 | Some(bit_size) => { 12 | write!(w, "[{}]", format_bit(bit_size))?; 13 | } 14 | None => { 15 | // TODO: show element size for unsized arrays. 16 | debug!("no size for {:?}", member); 17 | write!(w, "[??]")?; 18 | } 19 | } 20 | write!(w, "\t{}: ", member.name().unwrap_or(""))?; 21 | print::types::print_ref(member.ty(hash), w, hash)?; 22 | Ok(()) 23 | } 24 | 25 | fn print_variant_part( 26 | layout: &Layout, 27 | _variant_part: &VariantPart, 28 | w: &mut dyn ValuePrinter, 29 | ) -> Result<()> { 30 | // TODO: indicate which discriminant 31 | write!( 32 | w, 33 | "{}[{}]\t", 34 | format_bit(layout.bit_offset), 35 | format_bit(layout.bit_size.get().unwrap_or(0)), 36 | )?; 37 | Ok(()) 38 | } 39 | 40 | fn print_variant(variant: &Variant, w: &mut dyn ValuePrinter) -> Result<()> { 41 | if let Some(name) = variant.name() { 42 | write!(w, "{}: ", name)?; 43 | } 44 | // TODO: use discriminant type to display value 45 | write!(w, "<{}>", variant.discriminant_value().unwrap_or(0))?; 46 | Ok(()) 47 | } 48 | 49 | fn print_inherit( 50 | layout: &Layout, 51 | inherit: &Inherit, 52 | w: &mut dyn ValuePrinter, 53 | hash: &FileHash, 54 | ) -> Result<()> { 55 | write!( 56 | w, 57 | "{}[{}]\t: ", 58 | format_bit(layout.bit_offset), 59 | format_bit(layout.bit_size.get().unwrap_or(0)) 60 | )?; 61 | print::types::print_ref(inherit.ty(hash), w, hash)?; 62 | Ok(()) 63 | } 64 | 65 | fn print_padding(layout: &Layout, w: &mut dyn ValuePrinter) -> Result<()> { 66 | write!( 67 | w, 68 | "{}[{}]\t", 69 | format_bit(layout.bit_offset), 70 | format_bit(layout.bit_size.get().unwrap_or(0)) 71 | )?; 72 | Ok(()) 73 | } 74 | 75 | impl<'input> Print for Member<'input> { 76 | type Arg = Unit<'input>; 77 | 78 | fn print(&self, state: &mut PrintState, unit: &Unit) -> Result<()> { 79 | let hash = state.hash(); 80 | let ty = if self.is_inline(hash) { 81 | self.ty(hash) 82 | } else { 83 | None 84 | }; 85 | let ty = ty.as_deref(); 86 | state.expanded( 87 | |state| state.line(|w, hash| print_member(self, w, hash)), 88 | |state| print::types::print_members(state, unit, ty), 89 | ) 90 | } 91 | 92 | fn diff(state: &mut DiffState, unit_a: &Unit, a: &Self, unit_b: &Unit, b: &Self) -> Result<()> { 93 | let ty_a = if a.is_inline(state.hash_a()) { 94 | a.ty(state.hash_a()) 95 | } else { 96 | None 97 | }; 98 | let ty_a = ty_a.as_deref(); 99 | let ty_b = if b.is_inline(state.hash_b()) { 100 | b.ty(state.hash_b()) 101 | } else { 102 | None 103 | }; 104 | let ty_b = ty_b.as_deref(); 105 | state.expanded( 106 | |state| state.line(a, b, |w, hash, x| print_member(x, w, hash)), 107 | |state| print::types::diff_members(state, unit_a, ty_a, unit_b, ty_b), 108 | ) 109 | } 110 | } 111 | 112 | impl<'input> DiffList for Member<'input> { 113 | fn step_cost(&self, _state: &DiffState, _arg: &Unit) -> usize { 114 | 1 115 | } 116 | 117 | fn diff_cost(state: &DiffState, _unit_a: &Unit, a: &Self, _unit_b: &Unit, b: &Self) -> usize { 118 | let mut cost = 0; 119 | if a.name().cmp(&b.name()) != cmp::Ordering::Equal { 120 | cost += 1; 121 | } 122 | match (&a.ty(state.hash_a()), &b.ty(state.hash_b())) { 123 | (Some(ty_a), Some(ty_b)) => { 124 | if Type::cmp_id(state.hash_a(), ty_a, state.hash_b(), ty_b) != cmp::Ordering::Equal 125 | { 126 | cost += 1; 127 | } 128 | } 129 | (None, None) => {} 130 | _ => { 131 | cost += 1; 132 | } 133 | } 134 | cost 135 | } 136 | } 137 | 138 | impl<'input> Print for Variant<'input> { 139 | type Arg = (&'input Unit<'input>, u64, Option); 140 | 141 | fn print( 142 | &self, 143 | state: &mut PrintState, 144 | (unit, bit_offset, bit_size): &Self::Arg, 145 | ) -> Result<()> { 146 | state.expanded( 147 | |state| state.line(|w, _hash| print_variant(self, w)), 148 | |state| { 149 | let layout = self.layout(*bit_offset, *bit_size, state.hash()); 150 | state.list(*unit, &layout) 151 | }, 152 | ) 153 | } 154 | 155 | fn diff( 156 | state: &mut DiffState, 157 | (unit_a, bit_offset_a, bit_size_a): &Self::Arg, 158 | a: &Self, 159 | (unit_b, bit_offset_b, bit_size_b): &Self::Arg, 160 | b: &Self, 161 | ) -> Result<()> { 162 | state.expanded( 163 | |state| state.line(a, b, |w, _hash, variant| print_variant(variant, w)), 164 | |state| { 165 | let layout_a = a.layout(*bit_offset_a, *bit_size_a, state.hash_a()); 166 | let layout_b = b.layout(*bit_offset_b, *bit_size_b, state.hash_b()); 167 | state.list(*unit_a, &layout_a, *unit_b, &layout_b) 168 | }, 169 | ) 170 | } 171 | } 172 | 173 | impl<'input> DiffList for Variant<'input> { 174 | fn step_cost(&self, _state: &DiffState, _arg: &Self::Arg) -> usize { 175 | 1 176 | } 177 | 178 | fn diff_cost( 179 | _state: &DiffState, 180 | _arg_a: &Self::Arg, 181 | a: &Self, 182 | _arg_b: &Self::Arg, 183 | b: &Self, 184 | ) -> usize { 185 | let mut cost = 0; 186 | if a.name().cmp(&b.name()) != cmp::Ordering::Equal { 187 | cost += 1; 188 | } 189 | if a.discriminant_value() != b.discriminant_value() { 190 | cost += 1; 191 | } 192 | cost 193 | } 194 | } 195 | 196 | impl<'input, 'member> Print for Layout<'input, 'member> { 197 | type Arg = Unit<'input>; 198 | 199 | fn print(&self, state: &mut PrintState, unit: &Unit) -> Result<()> { 200 | match self.item { 201 | LayoutItem::Padding => state.line(|w, _hash| print_padding(self, w)), 202 | LayoutItem::Member(member) => member.print(state, unit), 203 | LayoutItem::VariantPart(variant_part) => state.expanded( 204 | |state| state.line(|w, _hash| print_variant_part(self, variant_part, w)), 205 | |state| { 206 | state.list( 207 | &(unit, self.bit_offset, self.bit_size.get()), 208 | variant_part.variants(), 209 | ) 210 | }, 211 | ), 212 | LayoutItem::Inherit(inherit) => { 213 | state.line(|w, hash| print_inherit(self, inherit, w, hash)) 214 | } 215 | } 216 | } 217 | 218 | fn diff(state: &mut DiffState, unit_a: &Unit, a: &Self, unit_b: &Unit, b: &Self) -> Result<()> { 219 | match (&a.item, &b.item) { 220 | (&LayoutItem::Padding, &LayoutItem::Padding) => { 221 | state.line(a, b, |w, _hash, x| print_padding(x, w)) 222 | } 223 | (&LayoutItem::Member(member_a), &LayoutItem::Member(member_b)) => { 224 | Member::diff(state, unit_a, member_a, unit_b, member_b) 225 | } 226 | ( 227 | &LayoutItem::VariantPart(variant_part_a), 228 | &LayoutItem::VariantPart(variant_part_b), 229 | ) => state.expanded( 230 | |state| { 231 | state.line( 232 | (a, variant_part_a), 233 | (b, variant_part_b), 234 | |w, _hash, (x, v)| print_variant_part(x, v, w), 235 | ) 236 | }, 237 | |state| { 238 | let variants_a = variant_part_a.variants(); 239 | let variants_b = variant_part_b.variants(); 240 | state.list( 241 | &(unit_a, a.bit_offset, a.bit_size.get()), 242 | variants_a, 243 | &(unit_b, b.bit_offset, b.bit_size.get()), 244 | variants_b, 245 | ) 246 | }, 247 | ), 248 | (&LayoutItem::Inherit(inherit_a), &LayoutItem::Inherit(inherit_b)) => { 249 | state.line((a, inherit_a), (b, inherit_b), |w, hash, (x, inherit)| { 250 | print_inherit(x, inherit, w, hash) 251 | }) 252 | } 253 | _ => state.block((unit_a, a), (unit_b, b), |state, (unit, x)| { 254 | Layout::print(x, state, unit) 255 | }), 256 | } 257 | } 258 | } 259 | 260 | impl<'input, 'member> DiffList for Layout<'input, 'member> { 261 | fn step_cost(&self, _state: &DiffState, _arg: &Unit) -> usize { 262 | // Must be same as diff cost for each layout item 263 | 1 264 | } 265 | 266 | fn diff_cost(state: &DiffState, unit_a: &Unit, a: &Self, unit_b: &Unit, b: &Self) -> usize { 267 | match (&a.item, &b.item) { 268 | (&LayoutItem::Padding, &LayoutItem::Padding) => 0, 269 | (&LayoutItem::Member(a), &LayoutItem::Member(b)) => { 270 | Member::diff_cost(state, unit_a, a, unit_b, b) 271 | } 272 | (&LayoutItem::VariantPart(_a), &LayoutItem::VariantPart(_b)) => { 273 | // TODO: for now we assume there is only one variant part 274 | // Later we should compare `VariantPart::discr` 275 | 0 276 | } 277 | (&LayoutItem::Inherit(a), &LayoutItem::Inherit(b)) => { 278 | Inherit::diff_cost(state, &(), a, &(), b) 279 | } 280 | _ => 2, 281 | } 282 | } 283 | } 284 | 285 | fn format_bit(val: u64) -> String { 286 | let byte = val / 8; 287 | let bit = val % 8; 288 | if bit == 0 { 289 | format!("{}", byte) 290 | } else { 291 | format!("{}.{}", byte, bit) 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /main/src/print/namespace.rs: -------------------------------------------------------------------------------- 1 | use parser::{Namespace, NamespaceKind}; 2 | 3 | use crate::print::ValuePrinter; 4 | use crate::Result; 5 | 6 | pub(crate) fn print(namespace: &Namespace, w: &mut dyn ValuePrinter) -> Result<()> { 7 | if let Some(parent) = namespace.parent() { 8 | print(parent, w)?; 9 | } 10 | w.name(namespace.name().unwrap_or(""))?; 11 | if namespace.kind() == NamespaceKind::Function { 12 | write!(w, "()")?; 13 | } 14 | write!(w, "::")?; 15 | Ok(()) 16 | } 17 | -------------------------------------------------------------------------------- /main/src/print/parameter.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::{FileHash, Parameter, Type, Unit}; 4 | 5 | use crate::print::{self, DiffList, DiffState, Print, PrintState, ValuePrinter}; 6 | use crate::Result; 7 | 8 | pub(crate) fn print_decl(p: &Parameter, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 9 | if let Some(name) = p.name() { 10 | write!(w, "{}: ", name)?; 11 | } 12 | print::types::print_ref(p.ty(hash), w, hash)?; 13 | Ok(()) 14 | } 15 | 16 | fn print_size_and_decl(p: &Parameter, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 17 | match p.byte_size(hash) { 18 | Some(byte_size) => write!(w, "[{}]", byte_size)?, 19 | None => write!(w, "[??]")?, 20 | } 21 | write!(w, "\t")?; 22 | print_decl(p, w, hash) 23 | } 24 | 25 | impl<'input> Print for Parameter<'input> { 26 | type Arg = Unit<'input>; 27 | 28 | fn print(&self, state: &mut PrintState, _unit: &Unit) -> Result<()> { 29 | state.expanded( 30 | |state| state.line(|w, state| print_size_and_decl(self, w, state)), 31 | |state| { 32 | if state.options().print_variable_locations { 33 | print::register::print_list(state, self.registers().map(|x| x.1).collect())?; 34 | print::frame_location::print_list(state, self.frame_locations().collect())?; 35 | } 36 | Ok(()) 37 | }, 38 | ) 39 | } 40 | 41 | fn diff( 42 | state: &mut DiffState, 43 | _unit_a: &Unit, 44 | a: &Self, 45 | _unit_b: &Unit, 46 | b: &Self, 47 | ) -> Result<()> { 48 | state.expanded( 49 | |state| state.line(a, b, |w, state, x| print_size_and_decl(x, w, state)), 50 | |state| { 51 | if state.options().print_variable_locations { 52 | print::register::diff_list( 53 | state, 54 | a.registers().map(|x| x.1).collect(), 55 | b.registers().map(|x| x.1).collect(), 56 | )?; 57 | print::frame_location::diff_list( 58 | state, 59 | a.frame_locations().collect(), 60 | b.frame_locations().collect(), 61 | )?; 62 | } 63 | Ok(()) 64 | }, 65 | ) 66 | } 67 | } 68 | 69 | impl<'input> DiffList for Parameter<'input> { 70 | fn step_cost(&self, _state: &DiffState, _arg: &Unit) -> usize { 71 | 1 72 | } 73 | 74 | fn diff_cost(state: &DiffState, _unit_a: &Unit, a: &Self, _unit_b: &Unit, b: &Self) -> usize { 75 | let mut cost = 0; 76 | if a.name().cmp(&b.name()) != cmp::Ordering::Equal { 77 | cost += 1; 78 | } 79 | match (&a.ty(state.hash_a()), &b.ty(state.hash_b())) { 80 | (Some(ty_a), Some(ty_b)) => { 81 | if Type::cmp_id(state.hash_a(), ty_a, state.hash_b(), ty_b) != cmp::Ordering::Equal 82 | { 83 | cost += 1; 84 | } 85 | } 86 | (None, None) => {} 87 | _ => { 88 | cost += 1; 89 | } 90 | } 91 | cost 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /main/src/print/range.rs: -------------------------------------------------------------------------------- 1 | use parser::Range; 2 | 3 | use crate::print::{DiffState, Print, PrintState, ValuePrinter}; 4 | use crate::Result; 5 | 6 | pub(crate) fn print_address(range: &Range, w: &mut dyn ValuePrinter) -> Result<()> { 7 | if range.end > range.begin { 8 | write!(w, "0x{:x}-0x{:x}", range.begin, range.end - 1)?; 9 | } else { 10 | write!(w, "0x{:x}", range.begin)?; 11 | } 12 | Ok(()) 13 | } 14 | 15 | pub(crate) fn print_address_and_size(range: &Range, w: &mut dyn ValuePrinter) -> Result<()> { 16 | if range.end > range.begin { 17 | write!( 18 | w, 19 | "0x{:x}-0x{:x} ({})", 20 | range.begin, 21 | range.end - 1, 22 | range.end - range.begin 23 | )?; 24 | } else { 25 | write!(w, "0x{:x}", range.begin)?; 26 | } 27 | Ok(()) 28 | } 29 | 30 | impl Print for Range { 31 | type Arg = (); 32 | 33 | fn print(&self, state: &mut PrintState, _arg: &()) -> Result<()> { 34 | state.line(|w, _state| print_address_and_size(self, w)) 35 | } 36 | 37 | fn diff(state: &mut DiffState, _arg_a: &(), a: &Self, _arg_b: &(), b: &Self) -> Result<()> { 38 | state.line(a, b, |w, _state, x| print_address_and_size(x, w)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /main/src/print/register.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::{FileHash, Register}; 4 | 5 | use crate::print::{DiffList, DiffState, Print, PrintState, ValuePrinter}; 6 | use crate::Result; 7 | 8 | pub(crate) fn print_list(state: &mut PrintState, mut registers: Vec) -> Result<()> { 9 | registers.sort_unstable(); 10 | registers.dedup(); 11 | state.field_expanded("registers", |state| state.list(&(), ®isters))?; 12 | Ok(()) 13 | } 14 | 15 | pub(crate) fn diff_list( 16 | state: &mut DiffState, 17 | mut registers_a: Vec, 18 | mut registers_b: Vec, 19 | ) -> Result<()> { 20 | registers_a.sort_unstable(); 21 | registers_a.dedup(); 22 | registers_b.sort_unstable(); 23 | registers_b.dedup(); 24 | state.field_expanded("registers", |state| { 25 | state.list(&(), ®isters_a, &(), ®isters_b) 26 | })?; 27 | Ok(()) 28 | } 29 | 30 | pub(crate) fn print(register: Register, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 31 | match register.name(hash) { 32 | Some(name) => write!(w, "{}", name)?, 33 | None => write!(w, "r{}", register.0)?, 34 | }; 35 | Ok(()) 36 | } 37 | 38 | impl Print for Register { 39 | type Arg = (); 40 | 41 | fn print(&self, state: &mut PrintState, _arg: &()) -> Result<()> { 42 | state.line(|w, hash| print(*self, w, hash)) 43 | } 44 | 45 | fn diff(state: &mut DiffState, _arg_a: &(), a: &Self, _arg_b: &(), b: &Self) -> Result<()> { 46 | state.line(a, b, |w, hash, x| print(*x, w, hash)) 47 | } 48 | } 49 | 50 | impl DiffList for Register { 51 | fn step_cost(&self, _state: &DiffState, _arg: &()) -> usize { 52 | 1 53 | } 54 | 55 | fn diff_cost(_state: &DiffState, _unit_a: &(), a: &Self, _unit_b: &(), b: &Self) -> usize { 56 | let mut cost = 0; 57 | if a.0.cmp(&b.0) != cmp::Ordering::Equal { 58 | cost += 1; 59 | } 60 | cost 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /main/src/print/section.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::Section; 4 | 5 | use crate::print::{self, DiffList, DiffState, Print, PrintState, ValuePrinter}; 6 | use crate::Result; 7 | 8 | fn print_name(section: &Section, w: &mut dyn ValuePrinter) -> Result<()> { 9 | if let Some(segment) = section.segment() { 10 | write!(w, "{},", segment)?; 11 | } 12 | match section.name() { 13 | Some(name) => write!(w, "{}", name)?, 14 | None => write!(w, "")?, 15 | } 16 | Ok(()) 17 | } 18 | 19 | fn print_address(section: &Section, w: &mut dyn ValuePrinter) -> Result<()> { 20 | if let Some(address) = section.address() { 21 | print::range::print_address(&address, w)?; 22 | } 23 | Ok(()) 24 | } 25 | 26 | impl<'input> Print for Section<'input> { 27 | type Arg = (); 28 | 29 | fn print(&self, state: &mut PrintState, _arg: &()) -> Result<()> { 30 | state.collapsed( 31 | |state| state.line(|w, _state| print_name(self, w)), 32 | |state| { 33 | state.field("address", |w, _state| print_address(self, w))?; 34 | state.field_u64("size", self.size()) 35 | }, 36 | ) 37 | } 38 | 39 | fn diff(state: &mut DiffState, _arg_a: &(), a: &Self, _arg_b: &(), b: &Self) -> Result<()> { 40 | state.collapsed( 41 | |state| state.line(a, b, |w, _state, x| print_name(x, w)), 42 | |state| { 43 | state.field("address", a, b, |w, _state, x| print_address(x, w))?; 44 | state.field_u64("size", a.size(), b.size()) 45 | }, 46 | ) 47 | } 48 | } 49 | 50 | impl<'input> DiffList for Section<'input> { 51 | fn step_cost(&self, _state: &DiffState, _arg: &()) -> usize { 52 | 1 53 | } 54 | 55 | fn diff_cost(_state: &DiffState, _arg_a: &(), a: &Self, _arg_b: &(), b: &Self) -> usize { 56 | let mut cost = 0; 57 | if a.name().cmp(&b.name()) != cmp::Ordering::Equal 58 | || a.segment().cmp(&b.segment()) != cmp::Ordering::Equal 59 | { 60 | cost += 2; 61 | } 62 | cost 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /main/src/print/source.rs: -------------------------------------------------------------------------------- 1 | use parser::{Source, Unit}; 2 | 3 | use crate::print::ValuePrinter; 4 | use crate::Result; 5 | 6 | pub(crate) fn print(source: &Source, w: &mut dyn ValuePrinter, unit: &Unit) -> Result<()> { 7 | if let Some(path) = source.path(unit) { 8 | write!(w, "{}", path)?; 9 | if source.line() != 0 { 10 | write!(w, ":{}", source.line())?; 11 | if source.column() != 0 { 12 | write!(w, ":{}", source.column())?; 13 | } 14 | } 15 | } 16 | Ok(()) 17 | } 18 | -------------------------------------------------------------------------------- /main/src/print/struct_type.rs: -------------------------------------------------------------------------------- 1 | use parser::{FileHash, StructType, Unit}; 2 | 3 | use crate::print::{self, DiffState, PrintHeader, PrintState, ValuePrinter}; 4 | use crate::Result; 5 | 6 | fn print_name(ty: &StructType, w: &mut dyn ValuePrinter) -> Result<()> { 7 | write!(w, "struct ")?; 8 | if let Some(namespace) = ty.namespace() { 9 | print::namespace::print(namespace, w)?; 10 | } 11 | w.name(ty.name().unwrap_or(""))?; 12 | Ok(()) 13 | } 14 | 15 | pub(crate) fn print_ref(ty: &StructType, w: &mut dyn ValuePrinter, id: usize) -> Result<()> { 16 | w.link(id, &mut |w| print_name(ty, w)) 17 | } 18 | 19 | impl<'input> PrintHeader for StructType<'input> { 20 | fn print_header(&self, state: &mut PrintState) -> Result<()> { 21 | state.line(|w, _state| print_name(self, w)) 22 | } 23 | 24 | fn print_body(&self, state: &mut PrintState, unit: &Unit) -> Result<()> { 25 | if state.options().print_source { 26 | state.field("source", |w, _state| print_source(self, w, unit))?; 27 | } 28 | state.field("declaration", |w, state| print_declaration(self, w, state))?; 29 | state.field("size", |w, state| print_byte_size(self, w, state))?; 30 | print::inherit::print_list(state, self.inherits())?; 31 | state.field_expanded("members", |state| print_members(self, state, unit)) 32 | } 33 | 34 | fn diff_header(state: &mut DiffState, a: &Self, b: &Self) -> Result<()> { 35 | state.line(a, b, |w, _state, x| print_name(x, w)) 36 | } 37 | 38 | fn diff_body( 39 | state: &mut DiffState, 40 | unit_a: &parser::Unit, 41 | a: &Self, 42 | unit_b: &parser::Unit, 43 | b: &Self, 44 | ) -> Result<()> { 45 | if state.options().print_source { 46 | state.field( 47 | "source", 48 | (unit_a, a), 49 | (unit_b, b), 50 | |w, _state, (unit, x)| print_source(x, w, unit), 51 | )?; 52 | } 53 | state.field("declaration", a, b, |w, state, x| { 54 | print_declaration(x, w, state) 55 | })?; 56 | state.field("size", a, b, |w, state, x| print_byte_size(x, w, state))?; 57 | print::inherit::diff_list(state, a.inherits(), b.inherits())?; 58 | state.field_expanded("members", |state| diff_members(state, unit_a, a, unit_b, b)) 59 | } 60 | } 61 | 62 | fn print_source(ty: &StructType, w: &mut dyn ValuePrinter, unit: &Unit) -> Result<()> { 63 | print::source::print(ty.source(), w, unit) 64 | } 65 | 66 | fn print_byte_size(ty: &StructType, w: &mut dyn ValuePrinter, _hash: &FileHash) -> Result<()> { 67 | if let Some(size) = ty.byte_size() { 68 | write!(w, "{}", size)?; 69 | } else if !ty.is_declaration() { 70 | debug!("struct with no size"); 71 | } 72 | Ok(()) 73 | } 74 | 75 | fn print_declaration(ty: &StructType, w: &mut dyn ValuePrinter, _hash: &FileHash) -> Result<()> { 76 | if ty.is_declaration() { 77 | write!(w, "yes")?; 78 | } 79 | Ok(()) 80 | } 81 | 82 | pub(crate) fn print_members(ty: &StructType, state: &mut PrintState, unit: &Unit) -> Result<()> { 83 | let layout = ty.layout(state.hash()); 84 | state.list(unit, &layout) 85 | } 86 | 87 | pub(crate) fn diff_members( 88 | state: &mut DiffState, 89 | unit_a: &Unit, 90 | a: &StructType, 91 | unit_b: &Unit, 92 | b: &StructType, 93 | ) -> Result<()> { 94 | let layout_a = a.layout(state.hash_a()); 95 | let layout_b = b.layout(state.hash_b()); 96 | state.list(unit_a, &layout_a, unit_b, &layout_b) 97 | } 98 | -------------------------------------------------------------------------------- /main/src/print/symbol.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::{Symbol, SymbolKind}; 4 | 5 | use crate::print::{self, DiffList, DiffState, Print, PrintState, ValuePrinter}; 6 | use crate::Result; 7 | 8 | fn print_name(symbol: &Symbol, w: &mut dyn ValuePrinter) -> Result<()> { 9 | match symbol.kind() { 10 | SymbolKind::Variable => write!(w, "var ")?, 11 | SymbolKind::Function => write!(w, "fn ")?, 12 | } 13 | match symbol.name() { 14 | Some(name) => write!(w, "{}", name)?, 15 | None => write!(w, "")?, 16 | } 17 | Ok(()) 18 | } 19 | 20 | fn print_address(symbol: &Symbol, w: &mut dyn ValuePrinter) -> Result<()> { 21 | print::range::print_address(&symbol.address(), w) 22 | } 23 | 24 | impl<'input> Print for Symbol<'input> { 25 | type Arg = (); 26 | 27 | fn print(&self, state: &mut PrintState, _arg: &()) -> Result<()> { 28 | state.collapsed( 29 | |state| state.line(|w, _state| print_name(self, w)), 30 | |state| { 31 | state.field("address", |w, _state| print_address(self, w))?; 32 | state.field_u64("size", self.size()) 33 | }, 34 | ) 35 | } 36 | 37 | fn diff(state: &mut DiffState, _arg_a: &(), a: &Self, _arg_b: &(), b: &Self) -> Result<()> { 38 | state.collapsed( 39 | |state| state.line(a, b, |w, _state, x| print_name(x, w)), 40 | |state| { 41 | state.field("address", a, b, |w, _state, x| print_address(x, w))?; 42 | state.field_u64("size", a.size(), b.size()) 43 | }, 44 | ) 45 | } 46 | } 47 | 48 | impl<'input> DiffList for Symbol<'input> { 49 | fn step_cost(&self, _state: &DiffState, _arg: &()) -> usize { 50 | 1 51 | } 52 | 53 | fn diff_cost(_state: &DiffState, _arg_a: &(), a: &Self, _arg_b: &(), b: &Self) -> usize { 54 | let mut cost = 0; 55 | if a.name().cmp(&b.name()) != cmp::Ordering::Equal { 56 | cost += 2; 57 | } 58 | cost 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /main/src/print/text.rs: -------------------------------------------------------------------------------- 1 | use std::io::Write; 2 | 3 | use super::{DiffPrefix, Printer, ValuePrinter}; 4 | use crate::{Options, Result}; 5 | 6 | pub struct TextPrinter<'w> { 7 | w: &'w mut dyn Write, 8 | indent: usize, 9 | prefix: DiffPrefix, 10 | inline_depth: usize, 11 | } 12 | 13 | impl<'w> TextPrinter<'w> { 14 | pub fn new(w: &'w mut dyn Write, options: &Options) -> Self { 15 | TextPrinter { 16 | w, 17 | indent: 0, 18 | prefix: DiffPrefix::None, 19 | inline_depth: options.inline_depth, 20 | } 21 | } 22 | 23 | fn write_indent(&mut self) -> Result<()> { 24 | match self.prefix { 25 | DiffPrefix::None => {} 26 | DiffPrefix::Equal | DiffPrefix::Modify => write!(self.w, " ")?, 27 | DiffPrefix::Delete => { 28 | write!(self.w, "- ")?; 29 | } 30 | DiffPrefix::Add => { 31 | write!(self.w, "+ ")?; 32 | } 33 | } 34 | for _ in 0..self.indent { 35 | write!(self.w, "\t")?; 36 | } 37 | Ok(()) 38 | } 39 | } 40 | 41 | impl<'w> Printer for TextPrinter<'w> { 42 | fn value( 43 | &mut self, 44 | buf: &mut Vec, 45 | f: &mut dyn FnMut(&mut dyn ValuePrinter) -> Result<()>, 46 | ) -> Result<()> { 47 | let mut p = TextValuePrinter { w: buf }; 48 | f(&mut p) 49 | } 50 | 51 | /// Calls `f` to write to a temporary buffer. 52 | fn buffer( 53 | &mut self, 54 | buf: &mut Vec, 55 | f: &mut dyn FnMut(&mut dyn Printer) -> Result<()>, 56 | ) -> Result<()> { 57 | let mut p = TextPrinter { 58 | w: buf, 59 | indent: self.indent, 60 | prefix: self.prefix, 61 | inline_depth: self.inline_depth, 62 | }; 63 | f(&mut p) 64 | } 65 | 66 | fn write_buf(&mut self, buf: &[u8]) -> Result<()> { 67 | self.w.write_all(buf)?; 68 | Ok(()) 69 | } 70 | 71 | fn line_break(&mut self) -> Result<()> { 72 | writeln!(self.w).map_err(From::from) 73 | } 74 | 75 | fn line(&mut self, label: &str, buf: &[u8]) -> Result<()> { 76 | self.write_indent()?; 77 | if !label.is_empty() { 78 | write!(self.w, "{}:", label)?; 79 | if !buf.is_empty() { 80 | write!(self.w, " ")?; 81 | } 82 | } 83 | self.w.write_all(buf)?; 84 | writeln!(self.w)?; 85 | Ok(()) 86 | } 87 | 88 | fn line_diff(&mut self, label: &str, a: &[u8], b: &[u8]) -> Result<()> { 89 | self.prefix = DiffPrefix::Delete; 90 | self.line(label, a)?; 91 | self.prefix = DiffPrefix::Add; 92 | self.line(label, b) 93 | } 94 | 95 | fn indent_body( 96 | &mut self, 97 | buf: &mut Vec, 98 | body: &mut dyn FnMut(&mut dyn Printer) -> Result<()>, 99 | ) -> Result<()> { 100 | let mut printer = TextPrinter { 101 | w: buf, 102 | indent: self.indent + 1, 103 | prefix: self.prefix, 104 | inline_depth: self.inline_depth, 105 | }; 106 | body(&mut printer) 107 | } 108 | 109 | fn indent_header( 110 | &mut self, 111 | _collapsed: bool, 112 | body: &[u8], 113 | header: &mut dyn FnMut(&mut dyn Printer) -> Result<()>, 114 | ) -> Result<()> { 115 | header(self)?; 116 | self.write_buf(body)?; 117 | Ok(()) 118 | } 119 | 120 | fn indent_id( 121 | &mut self, 122 | _id: usize, 123 | header: &mut dyn FnMut(&mut dyn Printer) -> Result<()>, 124 | body: &mut dyn FnMut(&mut dyn Printer) -> Result<()>, 125 | ) -> Result<()> { 126 | header(self)?; 127 | let mut printer = TextPrinter { 128 | w: self.w, 129 | indent: self.indent + 1, 130 | prefix: self.prefix, 131 | inline_depth: self.inline_depth, 132 | }; 133 | body(&mut printer) 134 | } 135 | 136 | fn indent_detail(&mut self, _id: &str, _label: &str) -> Result<()> { 137 | unreachable!(); 138 | } 139 | 140 | fn prefix(&mut self, prefix: DiffPrefix) { 141 | self.prefix = prefix; 142 | } 143 | 144 | fn get_prefix(&self) -> DiffPrefix { 145 | self.prefix 146 | } 147 | 148 | fn inline_begin(&mut self) -> bool { 149 | if self.inline_depth == 0 { 150 | false 151 | } else { 152 | self.inline_depth -= 1; 153 | true 154 | } 155 | } 156 | 157 | fn inline_end(&mut self) { 158 | self.inline_depth += 1; 159 | } 160 | 161 | fn instruction(&mut self, address: Option, mnemonic: &str, buf: &[u8]) -> Result<()> { 162 | self.write_indent()?; 163 | if let Some(address) = address { 164 | write!(self.w, "{:3x}: ", address)?; 165 | } else { 166 | write!(self.w, "{:3} ", "")?; 167 | } 168 | if mnemonic.is_empty() { 169 | // When caller doesn't specify a mnemonic, the operands don't a leading space, 170 | // so add one here. 171 | // TODO: fix this in callers instead? 172 | write!(self.w, "{:6} ", "")?; 173 | } else { 174 | write!(self.w, "{:6}", mnemonic)?; 175 | } 176 | if !buf.is_empty() { 177 | write!(self.w, " ")?; 178 | self.w.write_all(buf)?; 179 | } 180 | writeln!(self.w)?; 181 | Ok(()) 182 | } 183 | } 184 | 185 | struct TextValuePrinter<'w> { 186 | w: &'w mut Vec, 187 | } 188 | 189 | impl<'w> Write for TextValuePrinter<'w> { 190 | fn write(&mut self, buf: &[u8]) -> std::result::Result { 191 | self.w.write(buf) 192 | } 193 | 194 | fn flush(&mut self) -> std::result::Result<(), std::io::Error> { 195 | self.w.flush() 196 | } 197 | } 198 | 199 | impl<'w> ValuePrinter for TextValuePrinter<'w> { 200 | fn link( 201 | &mut self, 202 | _id: usize, 203 | f: &mut dyn FnMut(&mut dyn ValuePrinter) -> Result<()>, 204 | ) -> Result<()> { 205 | f(self) 206 | } 207 | 208 | fn name(&mut self, name: &str) -> Result<()> { 209 | self.w.write_all(name.as_bytes())?; 210 | Ok(()) 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /main/src/print/type_def.rs: -------------------------------------------------------------------------------- 1 | use parser::{FileHash, TypeDef, Unit}; 2 | 3 | use crate::print::{self, DiffState, PrintHeader, PrintState, ValuePrinter}; 4 | use crate::Result; 5 | 6 | fn print_name(ty: &TypeDef, w: &mut dyn ValuePrinter) -> Result<()> { 7 | if let Some(namespace) = ty.namespace() { 8 | print::namespace::print(namespace, w)?; 9 | } 10 | w.name(ty.name().unwrap_or(""))?; 11 | Ok(()) 12 | } 13 | 14 | pub(crate) fn print_ref(ty: &TypeDef, w: &mut dyn ValuePrinter, id: usize) -> Result<()> { 15 | w.link(id, &mut |w| print_name(ty, w)) 16 | } 17 | 18 | fn print_def(ty: &TypeDef, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 19 | write!(w, "type ")?; 20 | print_name(ty, w)?; 21 | write!(w, " = ")?; 22 | print::types::print_ref(ty.ty(hash), w, hash)?; 23 | Ok(()) 24 | } 25 | 26 | fn print_source(ty: &TypeDef, w: &mut dyn ValuePrinter, unit: &Unit) -> Result<()> { 27 | print::source::print(ty.source(), w, unit) 28 | } 29 | 30 | fn print_byte_size(ty: &TypeDef, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 31 | if let Some(byte_size) = ty.byte_size(hash) { 32 | write!(w, "{}", byte_size)?; 33 | } 34 | Ok(()) 35 | } 36 | 37 | impl<'input> PrintHeader for TypeDef<'input> { 38 | fn print_header(&self, state: &mut PrintState) -> Result<()> { 39 | state.line(|w, state| print_def(self, w, state)) 40 | } 41 | 42 | fn print_body(&self, state: &mut PrintState, unit: &Unit) -> Result<()> { 43 | let ty = self.ty(state.hash()); 44 | if state.options().print_source { 45 | state.field("source", |w, _state| print_source(self, w, unit))?; 46 | } 47 | state.field("size", |w, state| print_byte_size(self, w, state))?; 48 | if let Some(ty) = &ty { 49 | if ty.is_anon() { 50 | state.field_expanded("members", |state| { 51 | print::types::print_members(state, unit, Some(ty)) 52 | })?; 53 | } 54 | } 55 | Ok(()) 56 | } 57 | 58 | fn diff_header(state: &mut DiffState, a: &Self, b: &Self) -> Result<()> { 59 | state.line(a, b, |w, state, x| print_def(x, w, state)) 60 | } 61 | 62 | fn diff_body( 63 | state: &mut DiffState, 64 | unit_a: &parser::Unit, 65 | a: &Self, 66 | unit_b: &parser::Unit, 67 | b: &Self, 68 | ) -> Result<()> { 69 | if state.options().print_source { 70 | state.field( 71 | "source", 72 | (unit_a, a), 73 | (unit_b, b), 74 | |w, _state, (unit, x)| print_source(x, w, unit), 75 | )?; 76 | } 77 | state.field("size", a, b, |w, state, x| print_byte_size(x, w, state))?; 78 | let ty_a = filter_option(a.ty(state.hash_a()), |ty| ty.is_anon()); 79 | let ty_a = ty_a.as_deref(); 80 | let ty_b = filter_option(b.ty(state.hash_b()), |ty| ty.is_anon()); 81 | let ty_b = ty_b.as_deref(); 82 | state.field_expanded("members", |state| { 83 | print::types::diff_members(state, unit_a, ty_a, unit_b, ty_b) 84 | }) 85 | } 86 | } 87 | 88 | fn filter_option(o: Option, f: F) -> Option 89 | where 90 | F: FnOnce(&T) -> bool, 91 | { 92 | o.and_then(|v| if f(&v) { Some(v) } else { None }) 93 | } 94 | -------------------------------------------------------------------------------- /main/src/print/type_modifier.rs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gimli-rs/ddbug/cd5a14c10db4b75646e70e7ac1b6caef6b1e5442/main/src/print/type_modifier.rs -------------------------------------------------------------------------------- /main/src/print/types.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::cmp; 3 | 4 | use parser::{ 5 | ArrayType, FileHash, FunctionType, PointerToMemberType, SubrangeType, Type, TypeKind, 6 | TypeModifier, TypeModifierKind, Unit, UnspecifiedType, 7 | }; 8 | 9 | use crate::print::{self, DiffState, Print, PrintHeader, PrintState, SortList, ValuePrinter}; 10 | use crate::{Options, Result, Sort}; 11 | 12 | pub(crate) fn kind<'a>(ty: &'a Type) -> Result<&'a dyn PrintHeader> { 13 | Ok(match ty.kind() { 14 | TypeKind::Base(val) => val, 15 | TypeKind::Def(val) => val, 16 | TypeKind::Struct(val) => val, 17 | TypeKind::Union(val) => val, 18 | TypeKind::Enumeration(val) => val, 19 | TypeKind::Void 20 | | TypeKind::Array(..) 21 | | TypeKind::Function(..) 22 | | TypeKind::Unspecified(..) 23 | | TypeKind::PointerToMember(..) 24 | | TypeKind::Modifier(..) 25 | | TypeKind::Subrange(..) => return Err(format!("can't print {:?}", ty).into()), 26 | }) 27 | } 28 | 29 | pub(crate) fn print(ty: &Type, state: &mut PrintState, unit: &Unit) -> Result<()> { 30 | let kind = kind(ty)?; 31 | state.id( 32 | ty.id(), 33 | |state| kind.print_header(state), 34 | |state| kind.print_body(state, unit), 35 | )?; 36 | state.line_break()?; 37 | Ok(()) 38 | } 39 | 40 | pub(crate) fn print_ref( 41 | ty: Option>, 42 | w: &mut dyn ValuePrinter, 43 | hash: &FileHash, 44 | ) -> Result<()> { 45 | match ty { 46 | None => { 47 | write!(w, "")?; 48 | Ok(()) 49 | } 50 | Some(ty) => { 51 | let id = ty.id(); 52 | match ty.kind() { 53 | TypeKind::Void => print_ref_void(w), 54 | TypeKind::Base(val) => print::base_type::print_ref(val, w, id), 55 | TypeKind::Def(val) => print::type_def::print_ref(val, w, id), 56 | TypeKind::Struct(val) => print::struct_type::print_ref(val, w, id), 57 | TypeKind::Union(val) => print::union_type::print_ref(val, w, id), 58 | TypeKind::Enumeration(val) => print::enumeration::print_ref(val, w, id), 59 | TypeKind::Array(val) => print_ref_array(val, w, hash), 60 | TypeKind::Function(val) => print_ref_function(val, w, hash), 61 | TypeKind::Unspecified(val) => print_ref_unspecified(val, w), 62 | TypeKind::PointerToMember(val) => print_ref_pointer_to_member(val, w, hash), 63 | TypeKind::Modifier(val) => print_ref_modifier(val, w, hash), 64 | TypeKind::Subrange(val) => print_ref_subrange(val, w, hash), 65 | } 66 | } 67 | } 68 | } 69 | 70 | fn ref_id(ty: Option>, hash: &FileHash) -> Option { 71 | let ty = ty?; 72 | let id = Some(ty.id()); 73 | match ty.kind() { 74 | TypeKind::Void 75 | | TypeKind::Function(_) 76 | | TypeKind::Unspecified(_) 77 | | TypeKind::PointerToMember(_) => None, 78 | TypeKind::Base(_) 79 | | TypeKind::Def(_) 80 | | TypeKind::Struct(_) 81 | | TypeKind::Union(_) 82 | | TypeKind::Enumeration(_) => id, 83 | TypeKind::Array(val) => ref_id(val.element_type(hash), hash), 84 | TypeKind::Modifier(val) => ref_id(val.ty(hash), hash), 85 | TypeKind::Subrange(val) => ref_id(val.ty(hash), hash), 86 | } 87 | } 88 | 89 | fn print_ref_void(w: &mut dyn ValuePrinter) -> Result<()> { 90 | write!(w, "void")?; 91 | Ok(()) 92 | } 93 | 94 | fn print_ref_array(ty: &ArrayType, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 95 | write!(w, "[")?; 96 | print_ref(ty.element_type(hash), w, hash)?; 97 | let mut counts = ty.counts(); 98 | if let Some(count) = counts.next() { 99 | if let Some(count) = count { 100 | write!(w, "; {}", count)?; 101 | } else { 102 | write!(w, "; ??")?; 103 | } 104 | } 105 | for count in counts { 106 | if let Some(count) = count { 107 | write!(w, ", {}", count)?; 108 | } else { 109 | write!(w, ", ??")?; 110 | } 111 | } 112 | write!(w, "]")?; 113 | Ok(()) 114 | } 115 | 116 | fn print_ref_function(ty: &FunctionType, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 117 | let mut first = true; 118 | write!(w, "(")?; 119 | for parameter in ty.parameters() { 120 | if first { 121 | first = false; 122 | } else { 123 | write!(w, ", ")?; 124 | } 125 | if let Some(name) = parameter.name() { 126 | write!(w, "{}: ", name)?; 127 | } 128 | print_ref(parameter.ty(hash), w, hash)?; 129 | } 130 | write!(w, ")")?; 131 | 132 | if let Some(return_type) = ty.return_type(hash) { 133 | if !return_type.is_void() { 134 | write!(w, " -> ")?; 135 | print_ref(Some(return_type), w, hash)?; 136 | } 137 | } 138 | Ok(()) 139 | } 140 | 141 | fn print_ref_unspecified(ty: &UnspecifiedType, w: &mut dyn ValuePrinter) -> Result<()> { 142 | if let Some(namespace) = ty.namespace() { 143 | print::namespace::print(namespace, w)?; 144 | } 145 | w.name(ty.name().unwrap_or(""))?; 146 | Ok(()) 147 | } 148 | 149 | fn print_ref_pointer_to_member( 150 | ty: &PointerToMemberType, 151 | w: &mut dyn ValuePrinter, 152 | hash: &FileHash, 153 | ) -> Result<()> { 154 | print_ref(ty.containing_type(hash), w, hash)?; 155 | write!(w, "::* ")?; 156 | print_ref(ty.member_type(hash), w, hash)?; 157 | Ok(()) 158 | } 159 | 160 | fn print_ref_modifier(ty: &TypeModifier, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 161 | if let Some(name) = ty.name() { 162 | if let Some(id) = ref_id(ty.ty(hash), hash) { 163 | w.link(id, &mut |w| w.name(name))?; 164 | } else { 165 | w.name(name)?; 166 | } 167 | } else { 168 | match ty.kind() { 169 | TypeModifierKind::Pointer => write!(w, "* ")?, 170 | TypeModifierKind::Reference | TypeModifierKind::RvalueReference => write!(w, "& ")?, 171 | TypeModifierKind::Const => write!(w, "const ")?, 172 | TypeModifierKind::Volatile => write!(w, "volatile ")?, 173 | TypeModifierKind::Restrict => write!(w, "restrict ")?, 174 | TypeModifierKind::Packed 175 | | TypeModifierKind::Shared 176 | | TypeModifierKind::Atomic 177 | | TypeModifierKind::Other => {} 178 | } 179 | print_ref(ty.ty(hash), w, hash)?; 180 | } 181 | Ok(()) 182 | } 183 | 184 | fn print_ref_subrange(ty: &SubrangeType, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 185 | if let Some(name) = ty.name() { 186 | if let Some(id) = ref_id(ty.ty(hash), hash) { 187 | w.link(id, &mut |w| w.name(name))?; 188 | } else { 189 | w.name(name)?; 190 | } 191 | } else { 192 | print_ref(ty.ty(hash), w, hash)?; 193 | } 194 | // TODO: display bounds use underlying type 195 | match (ty.lower(), ty.upper()) { 196 | (Some(lower), Some(upper)) => write!(w, " {}..{}", lower, upper)?, 197 | (Some(lower), None) => write!(w, " {}..", lower)?, 198 | (None, Some(upper)) => write!(w, " ..{}", upper)?, 199 | (None, None) => {} 200 | } 201 | Ok(()) 202 | } 203 | 204 | pub(crate) fn diff_header(state: &mut DiffState, type_a: &Type, type_b: &Type) -> Result<()> { 205 | use self::TypeKind::*; 206 | match (type_a.kind(), type_b.kind()) { 207 | (Base(a), Base(b)) => PrintHeader::diff_header(state, a, b), 208 | (Def(a), Def(b)) => PrintHeader::diff_header(state, a, b), 209 | (Struct(a), Struct(b)) => PrintHeader::diff_header(state, a, b), 210 | (Union(a), Union(b)) => PrintHeader::diff_header(state, a, b), 211 | (Enumeration(a), Enumeration(b)) => PrintHeader::diff_header(state, a, b), 212 | _ => Err(format!("can't diff {:?}, {:?}", type_a, type_b).into()), 213 | } 214 | } 215 | 216 | pub(crate) fn diff_body( 217 | state: &mut DiffState, 218 | unit_a: &Unit, 219 | type_a: &Type, 220 | unit_b: &Unit, 221 | type_b: &Type, 222 | ) -> Result<()> { 223 | use self::TypeKind::*; 224 | match (type_a.kind(), type_b.kind()) { 225 | (Base(a), Base(b)) => PrintHeader::diff_body(state, unit_a, a, unit_b, b), 226 | (Def(a), Def(b)) => PrintHeader::diff_body(state, unit_a, a, unit_b, b), 227 | (Struct(a), Struct(b)) => PrintHeader::diff_body(state, unit_a, a, unit_b, b), 228 | (Union(a), Union(b)) => PrintHeader::diff_body(state, unit_a, a, unit_b, b), 229 | (Enumeration(a), Enumeration(b)) => PrintHeader::diff_body(state, unit_a, a, unit_b, b), 230 | _ => Err(format!("can't diff {:?}, {:?}", type_a, type_b).into()), 231 | } 232 | } 233 | 234 | pub(crate) fn diff( 235 | state: &mut DiffState, 236 | unit_a: &Unit, 237 | a: &Type, 238 | unit_b: &Unit, 239 | b: &Type, 240 | ) -> Result<()> { 241 | state.id( 242 | a.id(), 243 | |state| diff_header(state, a, b), 244 | |state| diff_body(state, unit_a, a, unit_b, b), 245 | )?; 246 | state.line_break()?; 247 | Ok(()) 248 | } 249 | 250 | pub(crate) fn print_members(state: &mut PrintState, unit: &Unit, ty: Option<&Type>) -> Result<()> { 251 | if let Some(ty) = ty { 252 | match ty.kind() { 253 | TypeKind::Struct(t) => return print::struct_type::print_members(t, state, unit), 254 | TypeKind::Union(t) => return print::union_type::print_members(t, state, unit), 255 | _ => {} 256 | } 257 | } 258 | Ok(()) 259 | } 260 | 261 | pub(crate) fn diff_members( 262 | state: &mut DiffState, 263 | unit_a: &Unit, 264 | type_a: Option<&Type>, 265 | unit_b: &Unit, 266 | type_b: Option<&Type>, 267 | ) -> Result<()> { 268 | if let (Some(type_a), Some(type_b)) = (type_a, type_b) { 269 | match (type_a.kind(), type_b.kind()) { 270 | (TypeKind::Struct(a), TypeKind::Struct(b)) => { 271 | return print::struct_type::diff_members(state, unit_a, a, unit_b, b); 272 | } 273 | (TypeKind::Union(a), TypeKind::Union(b)) => { 274 | return print::union_type::diff_members(state, unit_a, a, unit_b, b); 275 | } 276 | _ => {} 277 | } 278 | } 279 | 280 | // Different types, so don't try to diff the members. 281 | state.block((unit_a, type_a), (unit_b, type_b), |state, (unit, x)| { 282 | print_members(state, unit, x) 283 | }) 284 | } 285 | 286 | impl<'input> Print for Type<'input> { 287 | type Arg = Unit<'input>; 288 | 289 | fn print(&self, state: &mut PrintState, unit: &Self::Arg) -> Result<()> { 290 | print(self, state, unit) 291 | } 292 | 293 | fn diff( 294 | state: &mut DiffState, 295 | unit_a: &Self::Arg, 296 | a: &Self, 297 | unit_b: &Self::Arg, 298 | b: &Self, 299 | ) -> Result<()> { 300 | diff(state, unit_a, a, unit_b, b) 301 | } 302 | } 303 | 304 | impl<'input> SortList for Type<'input> { 305 | /// This must only be called for types that have identifiers. 306 | fn cmp_id( 307 | hash_a: &FileHash, 308 | type_a: &Type, 309 | hash_b: &FileHash, 310 | type_b: &Type, 311 | _options: &Options, 312 | ) -> cmp::Ordering { 313 | Type::cmp_id(hash_a, type_a, hash_b, type_b) 314 | } 315 | 316 | fn cmp_by( 317 | hash_a: &FileHash, 318 | a: &Self, 319 | hash_b: &FileHash, 320 | b: &Self, 321 | options: &Options, 322 | ) -> cmp::Ordering { 323 | match options.sort { 324 | Sort::None => a.offset().cmp(&b.offset()), 325 | Sort::Name => Type::cmp_id(hash_a, a, hash_b, b), 326 | Sort::Size => a.byte_size(hash_a).cmp(&b.byte_size(hash_b)), 327 | } 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /main/src/print/union_type.rs: -------------------------------------------------------------------------------- 1 | use parser::{FileHash, UnionType, Unit}; 2 | 3 | use crate::print::{self, DiffState, PrintHeader, PrintState, ValuePrinter}; 4 | use crate::Result; 5 | 6 | fn print_name(ty: &UnionType, w: &mut dyn ValuePrinter) -> Result<()> { 7 | write!(w, "union ")?; 8 | if let Some(namespace) = ty.namespace() { 9 | print::namespace::print(namespace, w)?; 10 | } 11 | w.name(ty.name().unwrap_or(""))?; 12 | Ok(()) 13 | } 14 | 15 | pub(crate) fn print_ref(ty: &UnionType, w: &mut dyn ValuePrinter, id: usize) -> Result<()> { 16 | w.link(id, &mut |w| print_name(ty, w)) 17 | } 18 | 19 | impl<'input> PrintHeader for UnionType<'input> { 20 | fn print_header(&self, state: &mut PrintState) -> Result<()> { 21 | state.line(|w, _state| print_name(self, w)) 22 | } 23 | 24 | fn print_body(&self, state: &mut PrintState, unit: &Unit) -> Result<()> { 25 | if state.options().print_source { 26 | state.field("source", |w, _state| print_source(self, w, unit))?; 27 | } 28 | state.field("declaration", |w, state| print_declaration(self, w, state))?; 29 | state.field("size", |w, state| print_byte_size(self, w, state))?; 30 | state.field_expanded("members", |state| print_members(self, state, unit)) 31 | } 32 | 33 | fn diff_header(state: &mut DiffState, a: &Self, b: &Self) -> Result<()> { 34 | state.line(a, b, |w, _state, x| print_name(x, w)) 35 | } 36 | 37 | fn diff_body( 38 | state: &mut DiffState, 39 | unit_a: &parser::Unit, 40 | a: &Self, 41 | unit_b: &parser::Unit, 42 | b: &Self, 43 | ) -> Result<()> { 44 | if state.options().print_source { 45 | state.field( 46 | "source", 47 | (unit_a, a), 48 | (unit_b, b), 49 | |w, _state, (unit, x)| print_source(x, w, unit), 50 | )?; 51 | } 52 | state.field("declaration", a, b, |w, state, x| { 53 | print_declaration(x, w, state) 54 | })?; 55 | state.field("size", a, b, |w, state, x| print_byte_size(x, w, state))?; 56 | state.field_expanded("members", |state| diff_members(state, unit_a, a, unit_b, b)) 57 | } 58 | } 59 | 60 | fn print_source(ty: &UnionType, w: &mut dyn ValuePrinter, unit: &Unit) -> Result<()> { 61 | print::source::print(ty.source(), w, unit) 62 | } 63 | 64 | fn print_byte_size(ty: &UnionType, w: &mut dyn ValuePrinter, _hash: &FileHash) -> Result<()> { 65 | if let Some(size) = ty.byte_size() { 66 | write!(w, "{}", size)?; 67 | } else if !ty.is_declaration() { 68 | debug!("struct with no size"); 69 | } 70 | Ok(()) 71 | } 72 | 73 | fn print_declaration(ty: &UnionType, w: &mut dyn ValuePrinter, _hash: &FileHash) -> Result<()> { 74 | if ty.is_declaration() { 75 | write!(w, "yes")?; 76 | } 77 | Ok(()) 78 | } 79 | 80 | pub(crate) fn print_members(ty: &UnionType, state: &mut PrintState, unit: &Unit) -> Result<()> { 81 | state.list(unit, ty.members()) 82 | } 83 | 84 | pub(crate) fn diff_members( 85 | state: &mut DiffState, 86 | unit_a: &Unit, 87 | a: &UnionType, 88 | unit_b: &Unit, 89 | b: &UnionType, 90 | ) -> Result<()> { 91 | // TODO: handle reordering better 92 | state.list(unit_a, a.members(), unit_b, b.members()) 93 | } 94 | -------------------------------------------------------------------------------- /main/src/print/unit.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::{FileHash, Function, Range, Type, Unit, Variable}; 4 | 5 | use crate::filter; 6 | use crate::print::{ 7 | self, DiffState, MergeIterator, MergeResult, Print, PrintState, SortList, ValuePrinter, 8 | }; 9 | use crate::{Options, Result, Sort}; 10 | 11 | pub(crate) fn merged_types<'a, 'input>( 12 | hash_a: &FileHash, 13 | unit_a: &'a Unit<'input>, 14 | hash_b: &FileHash, 15 | unit_b: &'a Unit<'input>, 16 | options: &Options, 17 | ) -> Vec, &'a Type<'input>>> { 18 | let mut types_a = filter::filter_types(unit_a, hash_a, options, true); 19 | types_a.sort_by(|x, y| Type::cmp_id_for_sort(hash_a, x, hash_a, y, options)); 20 | let mut types_b = filter::filter_types(unit_b, hash_b, options, true); 21 | types_b.sort_by(|x, y| Type::cmp_id_for_sort(hash_b, x, hash_b, y, options)); 22 | MergeIterator::new(types_a.into_iter(), types_b.into_iter(), |a, b| { 23 | Type::cmp_id(hash_a, a, hash_b, b) 24 | }) 25 | .collect() 26 | } 27 | 28 | pub(crate) fn merged_functions<'a, 'input>( 29 | hash_a: &FileHash, 30 | unit_a: &'a Unit<'input>, 31 | hash_b: &FileHash, 32 | unit_b: &'a Unit<'input>, 33 | options: &Options, 34 | ) -> ( 35 | Vec, &'a Function<'input>>>, 36 | Vec, &'a Function<'input>>>, 37 | ) { 38 | let mut functions_a = filter::filter_functions(unit_a, options); 39 | functions_a.sort_by(|x, y| Function::cmp_id_for_sort(hash_a, x, hash_a, y, options)); 40 | let mut functions_b = filter::filter_functions(unit_b, options); 41 | functions_b.sort_by(|x, y| Function::cmp_id_for_sort(hash_b, x, hash_b, y, options)); 42 | let mut functions = Vec::new(); 43 | let mut inlined_functions = Vec::new(); 44 | for function in MergeIterator::new(functions_a.into_iter(), functions_b.into_iter(), |a, b| { 45 | ::cmp_id(hash_a, a, hash_b, b, options) 46 | }) { 47 | let inline = match function { 48 | MergeResult::Both(a, b) => a.size().is_none() || b.size().is_none(), 49 | MergeResult::Left(a) => a.size().is_none(), 50 | MergeResult::Right(b) => b.size().is_none(), 51 | }; 52 | if inline { 53 | inlined_functions.push(function); 54 | } else { 55 | functions.push(function); 56 | } 57 | } 58 | (functions, inlined_functions) 59 | } 60 | 61 | pub(crate) fn merged_variables<'a, 'input>( 62 | hash_a: &FileHash, 63 | unit_a: &'a Unit<'input>, 64 | hash_b: &FileHash, 65 | unit_b: &'a Unit<'input>, 66 | options: &Options, 67 | ) -> Vec, &'a Variable<'input>>> { 68 | let mut variables_a = filter::filter_variables(unit_a, options); 69 | variables_a.sort_by(|x, y| Variable::cmp_id_for_sort(hash_a, x, hash_a, y, options)); 70 | let mut variables_b = filter::filter_variables(unit_b, options); 71 | variables_b.sort_by(|x, y| Variable::cmp_id_for_sort(hash_b, x, hash_b, y, options)); 72 | MergeIterator::new(variables_a.into_iter(), variables_b.into_iter(), |a, b| { 73 | ::cmp_id(hash_a, a, hash_b, b, options) 74 | }) 75 | .collect() 76 | } 77 | 78 | pub(crate) fn print_ref(unit: &Unit, w: &mut dyn ValuePrinter) -> Result<()> { 79 | let name = unit.name().unwrap_or(""); 80 | // TODO: windows support 81 | if !name.starts_with('/') { 82 | let dir = unit.dir().unwrap_or(""); 83 | if !dir.is_empty() { 84 | write!(w, "{}", dir)?; 85 | if !dir.ends_with('/') { 86 | write!(w, "/")?; 87 | } 88 | } 89 | } 90 | write!(w, "{}", name)?; 91 | Ok(()) 92 | } 93 | 94 | pub(crate) fn print_header(unit: &Unit, state: &mut PrintState) -> Result<()> { 95 | state.line(|w, _state| { 96 | write!(w, "unit ")?; 97 | print_ref(unit, w) 98 | }) 99 | } 100 | 101 | pub(crate) fn print_body(unit: &Unit, state: &mut PrintState) -> Result<()> { 102 | let options = state.options(); 103 | 104 | let print_unit = |state: &mut PrintState| { 105 | let unknown_ranges = unit.unknown_ranges(state.hash()); 106 | 107 | if options.print_unit_address { 108 | let ranges = unit.ranges(state.hash()); 109 | if ranges.list().len() > 1 { 110 | state.field_collapsed("addresses", |state| state.list(&(), ranges.list()))?; 111 | } else { 112 | let range = ranges.list().first().cloned(); 113 | state.field("address", |w, _state| print_address(unit, w, range))?; 114 | } 115 | 116 | state.field_collapsed("unknown addresses", |state| { 117 | state.list(&(), unknown_ranges.list()) 118 | })?; 119 | } 120 | 121 | let fn_size = unit.function_size(); 122 | if fn_size != 0 { 123 | state.field_u64("fn size", fn_size)?; 124 | } 125 | 126 | let var_size = unit.variable_size(state.hash()); 127 | if var_size != 0 { 128 | state.field_u64("var size", var_size)?; 129 | } 130 | 131 | let unknown_size = unknown_ranges.size(); 132 | if unknown_size != 0 { 133 | state.field_u64("unknown size", unknown_size)?; 134 | } 135 | 136 | state.line_break()?; 137 | Ok(()) 138 | }; 139 | 140 | let print_types = |state: &mut PrintState| -> Result<()> { 141 | if options.category_type { 142 | let mut types = filter::filter_types(unit, state.hash(), options, false); 143 | state.sort_list(unit, &mut types)?; 144 | } 145 | Ok(()) 146 | }; 147 | let print_functions = |state: &mut PrintState| -> Result<()> { 148 | if options.category_function { 149 | let mut functions = filter::filter_functions(unit, options); 150 | state.sort_list(unit, &mut functions)?; 151 | } 152 | Ok(()) 153 | }; 154 | let print_variables = |state: &mut PrintState| -> Result<()> { 155 | if options.category_variable { 156 | let mut variables = filter::filter_variables(unit, options); 157 | state.sort_list(unit, &mut variables)?; 158 | } 159 | Ok(()) 160 | }; 161 | 162 | if options.html { 163 | if options.category_unit { 164 | print_unit(state)?; 165 | } 166 | state.field_collapsed("types", &print_types)?; 167 | if options.category_function { 168 | let functions = filter::filter_functions(unit, options); 169 | let (mut functions, mut inlined_functions): (Vec<_>, Vec<_>) = 170 | functions.into_iter().partition(|f| f.size().is_some()); 171 | state.field_collapsed("functions", |state| state.sort_list(unit, &mut functions))?; 172 | state.field_collapsed("inlined functions", |state| { 173 | state.sort_list(unit, &mut inlined_functions) 174 | })?; 175 | } 176 | state.field_collapsed("variables", &print_variables)?; 177 | } else { 178 | if options.category_unit { 179 | state.expanded(|state| print_header(unit, state), print_unit)?; 180 | } 181 | print_types(state)?; 182 | print_functions(state)?; 183 | print_variables(state)?; 184 | } 185 | 186 | Ok(()) 187 | } 188 | 189 | pub(crate) fn print(unit: &Unit, state: &mut PrintState) -> Result<()> { 190 | if state.options().html { 191 | state.id( 192 | unit.id(), 193 | |state| print_header(unit, state), 194 | |state| print_body(unit, state), 195 | )?; 196 | } else { 197 | print_body(unit, state)?; 198 | } 199 | Ok(()) 200 | } 201 | 202 | fn diff_header(state: &mut DiffState, unit_a: &Unit, unit_b: &Unit) -> Result<()> { 203 | state.line(unit_a, unit_b, |w, _state, unit| { 204 | write!(w, "unit ")?; 205 | print_ref(unit, w) 206 | }) 207 | } 208 | 209 | pub(crate) fn diff_body(state: &mut DiffState, unit_a: &Unit, unit_b: &Unit) -> Result<()> { 210 | let options = state.options(); 211 | 212 | let diff_unit = |state: &mut DiffState| -> Result<()> { 213 | let unknown_ranges_a = unit_a.unknown_ranges(state.hash_a()); 214 | let unknown_ranges_b = unit_b.unknown_ranges(state.hash_b()); 215 | 216 | if options.print_unit_address { 217 | let ranges_a = unit_a.ranges(state.hash_a()); 218 | let ranges_b = unit_b.ranges(state.hash_b()); 219 | if ranges_a.list().len() > 1 || ranges_b.list().len() > 1 { 220 | state.field_collapsed("addresses", |state| { 221 | state.ord_list(&(), ranges_a.list(), &(), ranges_b.list()) 222 | })?; 223 | } else { 224 | let range_a = ranges_a.list().first().cloned(); 225 | let range_b = ranges_b.list().first().cloned(); 226 | state.field( 227 | "address", 228 | (unit_a, range_a), 229 | (unit_b, range_b), 230 | |w, _state, (unit, range)| print_address(unit, w, range), 231 | )?; 232 | } 233 | 234 | state.field_collapsed("unknown addresses", |state| { 235 | state.ord_list(&(), unknown_ranges_a.list(), &(), unknown_ranges_b.list()) 236 | })?; 237 | } 238 | 239 | let fn_size_a = unit_a.function_size(); 240 | let fn_size_b = unit_b.function_size(); 241 | if fn_size_a != 0 || fn_size_b != 0 { 242 | state.field_u64("fn size", fn_size_a, fn_size_b)?; 243 | } 244 | 245 | let var_size_a = unit_a.variable_size(state.hash_a()); 246 | let var_size_b = unit_b.variable_size(state.hash_b()); 247 | if var_size_a != 0 || var_size_b != 0 { 248 | state.field_u64("var size", var_size_a, var_size_b)?; 249 | } 250 | 251 | let unknown_size_a = unknown_ranges_a.size(); 252 | let unknown_size_b = unknown_ranges_b.size(); 253 | if unknown_size_a != 0 || unknown_size_b != 0 { 254 | state.field_u64("unknown size", unknown_size_a, unknown_size_b)?; 255 | } 256 | 257 | state.line_break()?; 258 | Ok(()) 259 | }; 260 | 261 | let diff_types = |state: &mut DiffState| -> Result<()> { 262 | if options.category_type { 263 | let mut types = merged_types(state.hash_a(), unit_a, state.hash_b(), unit_b, options); 264 | state.sort_list(unit_a, unit_b, &mut types)?; 265 | } 266 | Ok(()) 267 | }; 268 | let merged_functions = |state: &mut DiffState| { 269 | merged_functions(state.hash_a(), unit_a, state.hash_b(), unit_b, options) 270 | }; 271 | let diff_variables = |state: &mut DiffState| -> Result<()> { 272 | if options.category_variable { 273 | let mut variables = 274 | merged_variables(state.hash_a(), unit_a, state.hash_b(), unit_b, options); 275 | state.sort_list(unit_a, unit_b, &mut variables)?; 276 | } 277 | Ok(()) 278 | }; 279 | 280 | if options.html { 281 | if options.category_unit { 282 | diff_unit(state)?; 283 | } 284 | state.field_collapsed("types", &diff_types)?; 285 | if options.category_function { 286 | let (mut functions, mut inlined_functions) = merged_functions(state); 287 | state.field_collapsed("functions", |state| { 288 | state.sort_list(unit_a, unit_b, &mut functions) 289 | })?; 290 | state.field_collapsed("inlined functions", |state| { 291 | state.sort_list(unit_a, unit_b, &mut inlined_functions) 292 | })?; 293 | } 294 | state.field_collapsed("variables", &diff_variables)?; 295 | } else { 296 | if options.category_unit { 297 | state.collapsed(|state| diff_header(state, unit_a, unit_b), diff_unit)?; 298 | } 299 | diff_types(state)?; 300 | if options.category_function { 301 | let (mut functions, mut inlined_functions) = merged_functions(state); 302 | state.sort_list(unit_a, unit_b, &mut functions)?; 303 | state.sort_list(unit_a, unit_b, &mut inlined_functions)?; 304 | } 305 | diff_variables(state)?; 306 | } 307 | Ok(()) 308 | } 309 | 310 | pub(crate) fn diff(state: &mut DiffState, unit_a: &Unit, unit_b: &Unit) -> Result<()> { 311 | if state.options().html { 312 | state.id( 313 | unit_a.id(), 314 | |state| diff_header(state, unit_a, unit_b), 315 | |state| diff_body(state, unit_a, unit_b), 316 | )?; 317 | } else { 318 | diff_body(state, unit_a, unit_b)?; 319 | } 320 | Ok(()) 321 | } 322 | 323 | fn print_address(unit: &Unit, w: &mut dyn ValuePrinter, range: Option) -> Result<()> { 324 | if let Some(range) = range { 325 | print::range::print_address(&range, w)?; 326 | } else if let Some(low_pc) = unit.address() { 327 | write!(w, "0x{:x}", low_pc)?; 328 | } 329 | Ok(()) 330 | } 331 | 332 | impl<'input> Print for Unit<'input> { 333 | type Arg = (); 334 | 335 | fn print(&self, state: &mut PrintState, _arg: &()) -> Result<()> { 336 | print(self, state) 337 | } 338 | 339 | fn diff(state: &mut DiffState, _arg_a: &(), a: &Self, _arg_b: &(), b: &Self) -> Result<()> { 340 | diff(state, a, b) 341 | } 342 | } 343 | 344 | impl<'input> SortList for Unit<'input> { 345 | fn cmp_id( 346 | _hash_a: &FileHash, 347 | a: &Self, 348 | _hash_b: &FileHash, 349 | b: &Self, 350 | options: &Options, 351 | ) -> cmp::Ordering { 352 | let (prefix_a, suffix_a) = options.prefix_map(a.name().unwrap_or("")); 353 | let (prefix_b, suffix_b) = options.prefix_map(b.name().unwrap_or("")); 354 | let iter_a = prefix_a.bytes().chain(suffix_a.bytes()); 355 | let iter_b = prefix_b.bytes().chain(suffix_b.bytes()); 356 | iter_a.cmp(iter_b) 357 | } 358 | 359 | fn cmp_by( 360 | hash_a: &FileHash, 361 | a: &Self, 362 | hash_b: &FileHash, 363 | b: &Self, 364 | options: &Options, 365 | ) -> cmp::Ordering { 366 | match options.sort { 367 | // TODO: sort by offset? 368 | Sort::None => cmp::Ordering::Equal, 369 | Sort::Name => Unit::cmp_id(hash_a, a, hash_b, b, options), 370 | Sort::Size => a.size(hash_a).cmp(&b.size(hash_b)), 371 | } 372 | } 373 | } 374 | -------------------------------------------------------------------------------- /main/src/print/variable.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | 3 | use parser::{FileHash, Unit, Variable}; 4 | 5 | use crate::print::{self, DiffState, Print, PrintHeader, PrintState, SortList, ValuePrinter}; 6 | use crate::{Options, Result, Sort}; 7 | 8 | pub(crate) fn print_ref(v: &Variable, w: &mut dyn ValuePrinter) -> Result<()> { 9 | w.link(v.id(), &mut |w| { 10 | if let Some(namespace) = v.namespace() { 11 | print::namespace::print(namespace, w)?; 12 | } 13 | w.name(v.name().unwrap_or(""))?; 14 | Ok(()) 15 | }) 16 | } 17 | 18 | impl<'input> PrintHeader for Variable<'input> { 19 | fn print_header(&self, state: &mut PrintState) -> Result<()> { 20 | state.line(|w, state| print_name(self, w, state)) 21 | } 22 | 23 | fn print_body(&self, state: &mut PrintState, unit: &Unit) -> Result<()> { 24 | state.field("linkage name", |w, _state| print_linkage_name(self, w))?; 25 | state.field("symbol name", |w, _state| print_symbol_name(self, w))?; 26 | if state.options().print_source { 27 | state.field("source", |w, _state| print_source(self, w, unit))?; 28 | } 29 | state.field("address", |w, _state| print_address(self, w))?; 30 | state.field("size", |w, state| print_size(self, w, state))?; 31 | state.field("declaration", |w, _state| print_declaration(self, w)) 32 | // TODO: print anon type inline 33 | } 34 | 35 | fn diff_header(state: &mut DiffState, a: &Self, b: &Self) -> Result<()> { 36 | state.line(a, b, |w, state, x| print_name(x, w, state)) 37 | } 38 | 39 | fn diff_body( 40 | state: &mut DiffState, 41 | unit_a: &parser::Unit, 42 | a: &Self, 43 | unit_b: &parser::Unit, 44 | b: &Self, 45 | ) -> Result<()> { 46 | let flag = state.options().ignore_variable_linkage_name; 47 | state.ignore_diff(flag, |state| { 48 | state.field("linkage name", a, b, |w, _state, x| { 49 | print_linkage_name(x, w) 50 | }) 51 | })?; 52 | let flag = state.options().ignore_variable_symbol_name; 53 | state.ignore_diff(flag, |state| { 54 | state.field("symbol name", a, b, |w, _state, x| print_symbol_name(x, w)) 55 | })?; 56 | if state.options().print_source { 57 | state.field( 58 | "source", 59 | (unit_a, a), 60 | (unit_b, b), 61 | |w, _state, (unit, x)| print_source(x, w, unit), 62 | )?; 63 | } 64 | let flag = state.options().ignore_variable_address; 65 | state.ignore_diff(flag, |state| { 66 | state.field("address", a, b, |w, _state, x| print_address(x, w)) 67 | })?; 68 | state.field("size", a, b, |w, state, x| print_size(x, w, state))?; 69 | state.field("declaration", a, b, |w, _state, x| print_declaration(x, w)) 70 | } 71 | } 72 | 73 | pub(crate) fn print(v: &Variable, state: &mut PrintState, unit: &Unit) -> Result<()> { 74 | state.id( 75 | v.id(), 76 | |state| v.print_header(state), 77 | |state| v.print_body(state, unit), 78 | )?; 79 | state.line_break()?; 80 | Ok(()) 81 | } 82 | 83 | pub(crate) fn diff( 84 | state: &mut DiffState, 85 | unit_a: &Unit, 86 | a: &Variable, 87 | unit_b: &Unit, 88 | b: &Variable, 89 | ) -> Result<()> { 90 | state.collapsed( 91 | |state| PrintHeader::diff_header(state, a, b), 92 | |state| PrintHeader::diff_body(state, unit_a, a, unit_b, b), 93 | )?; 94 | state.line_break()?; 95 | Ok(()) 96 | } 97 | 98 | fn print_name(v: &Variable, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 99 | write!(w, "var ")?; 100 | if let Some(namespace) = v.namespace() { 101 | print::namespace::print(namespace, w)?; 102 | } 103 | w.name(v.name().unwrap_or(""))?; 104 | write!(w, ": ")?; 105 | print::types::print_ref(v.ty(hash), w, hash)?; 106 | Ok(()) 107 | } 108 | 109 | fn print_linkage_name(v: &Variable, w: &mut dyn ValuePrinter) -> Result<()> { 110 | if let Some(linkage_name) = v.linkage_name() { 111 | write!(w, "{}", linkage_name)?; 112 | } 113 | Ok(()) 114 | } 115 | 116 | fn print_symbol_name(v: &Variable, w: &mut dyn ValuePrinter) -> Result<()> { 117 | if let Some(symbol_name) = v.symbol_name() { 118 | write!(w, "{}", symbol_name)?; 119 | } 120 | Ok(()) 121 | } 122 | 123 | fn print_source(v: &Variable, w: &mut dyn ValuePrinter, unit: &Unit) -> Result<()> { 124 | print::source::print(v.source(), w, unit) 125 | } 126 | 127 | fn print_address(v: &Variable, w: &mut dyn ValuePrinter) -> Result<()> { 128 | if let Some(address) = v.address() { 129 | write!(w, "0x{:x}", address)?; 130 | } 131 | Ok(()) 132 | } 133 | 134 | fn print_size(v: &Variable, w: &mut dyn ValuePrinter, hash: &FileHash) -> Result<()> { 135 | if let Some(byte_size) = v.byte_size(hash) { 136 | write!(w, "{}", byte_size)?; 137 | } else if !v.is_declaration() { 138 | debug!("variable with no size"); 139 | } 140 | Ok(()) 141 | } 142 | 143 | fn print_declaration(v: &Variable, w: &mut dyn ValuePrinter) -> Result<()> { 144 | if v.is_declaration() { 145 | write!(w, "yes")?; 146 | } 147 | Ok(()) 148 | } 149 | 150 | impl<'input> Print for Variable<'input> { 151 | type Arg = Unit<'input>; 152 | 153 | fn print(&self, state: &mut PrintState, unit: &Unit) -> Result<()> { 154 | print(self, state, unit) 155 | } 156 | 157 | fn diff(state: &mut DiffState, unit_a: &Unit, a: &Self, unit_b: &Unit, b: &Self) -> Result<()> { 158 | diff(state, unit_a, a, unit_b, b) 159 | } 160 | } 161 | 162 | impl<'input> SortList for Variable<'input> { 163 | fn cmp_id( 164 | hash_a: &FileHash, 165 | a: &Self, 166 | hash_b: &FileHash, 167 | b: &Self, 168 | _options: &Options, 169 | ) -> cmp::Ordering { 170 | Variable::cmp_id(hash_a, a, hash_b, b) 171 | } 172 | 173 | fn cmp_by( 174 | hash_a: &FileHash, 175 | a: &Self, 176 | hash_b: &FileHash, 177 | b: &Self, 178 | options: &Options, 179 | ) -> cmp::Ordering { 180 | match options.sort { 181 | // TODO: sort by offset? 182 | Sort::None => a.address().cmp(&b.address()), 183 | Sort::Name => SortList::cmp_id(hash_a, a, hash_b, b, options), 184 | Sort::Size => a.byte_size(hash_a).cmp(&b.byte_size(hash_b)), 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /main/tests/Makefile: -------------------------------------------------------------------------------- 1 | all: src/diff.rs bin/diff1 bin/diff2 2 | 3 | src/diff.rs: src/diff.c 4 | gcc -E -P -trigraphs -D TESTRS $^ -o $@ 5 | 6 | bin/diff1: src/diff.c src/support.c 7 | gcc -fdebug-prefix-map=`pwd`= -g -D TEST1 $^ -o $@ 8 | 9 | bin/diff2: src/diff.c src/support.c 10 | gcc -fdebug-prefix-map=`pwd`= -g -D TEST2 $^ -o $@ 11 | -------------------------------------------------------------------------------- /main/tests/bin/diff1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gimli-rs/ddbug/cd5a14c10db4b75646e70e7ac1b6caef6b1e5442/main/tests/bin/diff1 -------------------------------------------------------------------------------- /main/tests/bin/diff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gimli-rs/ddbug/cd5a14c10db4b75646e70e7ac1b6caef6b1e5442/main/tests/bin/diff2 -------------------------------------------------------------------------------- /main/tests/diff.rs: -------------------------------------------------------------------------------- 1 | fn diff(name: &str, expect: &str) { 2 | let mut options = options(); 3 | options.unit("src/diff.c").name(name); 4 | let mut diff = Vec::new(); 5 | let output_1 = ddbug::File::parse("tests/bin/diff1".into()).unwrap(); 6 | let output_2 = ddbug::File::parse("tests/bin/diff2".into()).unwrap(); 7 | let mut printer = ddbug::TextPrinter::new(&mut diff, &options); 8 | ddbug::diff(&mut printer, output_1.file(), output_2.file(), &options).unwrap(); 9 | let diff = String::from_utf8(diff).unwrap(); 10 | if !equal(&diff, expect) { 11 | println!("\nDiff:"); 12 | println!("{}", diff); 13 | println!("Expected:"); 14 | println!("{}", expect); 15 | assert_eq!(diff, expect); 16 | } 17 | } 18 | 19 | fn options<'a>() -> ddbug::Options { 20 | ddbug::Options { 21 | print_function_variables: true, 22 | inline_depth: 1, 23 | 24 | category_unit: false, 25 | category_type: true, 26 | category_function: true, 27 | category_variable: true, 28 | 29 | filter_name: None, 30 | filter_namespace: Vec::new(), 31 | filter_unit: None, 32 | 33 | sort: ddbug::Sort::None, 34 | 35 | ignore_function_address: true, 36 | ignore_variable_address: true, 37 | ..Default::default() 38 | } 39 | } 40 | 41 | fn equal(mut diff: &str, expect: &str) -> bool { 42 | let mut expects = expect.split("[..]"); 43 | if let Some(e) = expects.next() { 44 | if !diff.starts_with(e) { 45 | return false; 46 | } 47 | diff = &diff[e.len()..]; 48 | } 49 | for e in expects { 50 | loop { 51 | if diff.starts_with(e) { 52 | diff = &diff[e.len()..]; 53 | break; 54 | } 55 | if diff.is_empty() { 56 | return false; 57 | } 58 | diff = &diff[1..]; 59 | } 60 | } 61 | diff.is_empty() 62 | } 63 | 64 | macro_rules! test { 65 | ($name:ident, $($val:expr),*) => { 66 | #[test] 67 | fn $name() { 68 | let expect = concat!($($val),*); 69 | diff(stringify!($name), expect); 70 | } 71 | } 72 | } 73 | 74 | include!("src/diff.rs"); 75 | -------------------------------------------------------------------------------- /main/tests/src/diff.rs: -------------------------------------------------------------------------------- 1 | test!(typedef_diff_base_equal, ""); 2 | test!(typedef_diff_base, "- type ", "typedef_diff_base", " = char\n", "+ type ", "typedef_diff_base", " = int\n", "- \tsize: 1\n", "+ \tsize: 4\n", "\n"); 3 | test!(typedef_diff_anon_equal, ""); 4 | test!(typedef_diff_anon, " type ", "typedef_diff_anon", " = struct \n", "- \tsize: 1\n", "+ \tsize: 4\n", " \tmembers:\n", "- \t\t0[1]\tc: char\n", "+ \t\t0[4]\ti: int\n", "\n"); 5 | test!(typedef_diff_anon_base, "- type ", "typedef_diff_anon_base", " = char\n", "+ type ", "typedef_diff_anon_base", " = struct \n", " \tsize: 1\n", " \tmembers:\n", "+ \t\t0[1]\tc: char\n", "\n"); 6 | test!(typedef_diff_anon_struct_union, "- type ", "typedef_diff_anon_struct_union", " = struct \n", "+ type ", "typedef_diff_anon_struct_union", " = union \n", " \tsize: 1\n", " \tmembers:\n", "- \t\t0[1]\tc: char\n", "+ \t\t0[1]\tc: char\n", "\n"); 7 | test!(typedef_diff_base_anon, "- type ", "typedef_diff_base_anon", " = struct \n", "+ type ", "typedef_diff_base_anon", " = char\n", " \tsize: 1\n", " \tmembers:\n", "- \t\t0[1]\tc: char\n", "\n"); 8 | test!(typedef_diff_struct_name, "- type ", "typedef_diff_struct_name", " = struct ", "typedef_diff_struct_name_s1", "\n", "+ type ", "typedef_diff_struct_name", " = struct ", "typedef_diff_struct_name_s2", "\n", " \tsize: 1\n", "\n"); 9 | test!(typedef_diff_struct_size, " type ", "typedef_diff_struct_size", " = struct s\n", "- \tsize: 1\n", "+ \tsize: 4\n", "\n"); 10 | test!(struct_diff_defn_equal, ""); 11 | test!(struct_diff_decl_equal, ""); 12 | test!(struct_diff_defn_decl, " struct ", "struct_diff_defn_decl", "\n", "+ \tdeclaration: yes\n", "- \tsize: 1\n", " \tmembers:\n", "- \t\t0[1]\tc: char\n", "\n"); 13 | test!(struct_diff_decl_defn, " struct ", "struct_diff_decl_defn", "\n", "- \tdeclaration: yes\n", "+ \tsize: 1\n", " \tmembers:\n", "+ \t\t0[1]\tc: char\n", "\n"); 14 | test!(struct_diff_size_equal, " struct ", "struct_diff_size_equal", "\n", " \tsize: 2\n", " \tmembers:\n", "- \t\t0[2]\tc: [char; 2]\n", "+ \t\t0[1]\tc1: char\n", "+ \t\t1[1]\tc2: char\n", "\n"); 15 | test!(struct_diff_member, " struct ", "struct_diff_member", "\n", "- \tsize: 1\n", "+ \tsize: 4\n", " \tmembers:\n", "- \t\t0[1]\ta: char\n", "+ \t\t0[4]\ta: int\n", "\n"); 16 | test!(struct_diff_member_reorder, " struct ", "struct_diff_member_reorder", "\n", " \tsize: 7\n", " \tmembers:\n", "+ \t\t0[2]\td: [char; 2]\n", "+ \t\t2[1]\tc: char\n", "- \t\t0[1]\ta: char\n", "+ \t\t3[1]\ta: char\n", "- \t\t1[1]\tb: char\n", "+ \t\t4[1]\tb: char\n", "- \t\t2[1]\tc: char\n", "- \t\t3[2]\td: [char; 2]\n", " \t\t5[1]\tx: char\n", "- \t\t6[1]\ty: char\n", "+ \t\t6[1]\tz: char\n", "\n"); 17 | test!(struct_diff_recursive_equal, ""); 18 | test!(union_diff_defn_equal, ""); 19 | test!(union_diff_decl_equal, ""); 20 | test!(union_diff_defn_decl, " union ", "union_diff_defn_decl", "\n", "+ \tdeclaration: yes\n", "- \tsize: 1\n", " \tmembers:\n", "- \t\t0[1]\tc: char\n", "\n"); 21 | test!(union_diff_decl_defn, " union ", "union_diff_decl_defn", "\n", "- \tdeclaration: yes\n", "+ \tsize: 1\n", " \tmembers:\n", "+ \t\t0[1]\tc: char\n", "\n"); 22 | test!(union_diff_size_equal, " union ", "union_diff_size_equal", "\n", " \tsize: 2\n", " \tmembers:\n", " \t\t0[2]\t: struct \n", "- \t\t\t0[2]\tc: [char; 2]\n", "+ \t\t\t0[1]\tc1: char\n", "+ \t\t\t1[1]\tc2: char\n", "\n"); 23 | test!(union_diff_member, " union ", "union_diff_member", "\n", "- \tsize: 1\n", "+ \tsize: 4\n", " \tmembers:\n", "- \t\t0[1]\ta: char\n", "+ \t\t0[4]\ta: int\n", "\n"); 24 | test!(union_diff_member_reorder, " union ", "union_diff_member_reorder", "\n", "- \tsize: 1\n", "+ \tsize: 2\n", " \tmembers:\n", "+ \t\t0[2]\tb: [char; 2]\n", " \t\t0[1]\ta: char\n", "- \t\t0[1]\tb: char\n", " \t\t0[1]\tc: char\n", "\n"); 25 | test!(member_diff_padding_equal, ""); 26 | test!(member_diff_padding, " struct ", "member_diff_padding", "\n", " \tsize: 8\n", " \tmembers:\n", "- \t\t0[1]\ta: [char; 1]\n", "+ \t\t0[2]\ta: [char; 2]\n", "- \t\t1[3]\t\n", "+ \t\t2[2]\t\n", " \t\t4[4]\tb: int\n", "\n"); 27 | test!(member_diff_padding_none, " struct ", "member_diff_padding_none", "\n", " \tsize: 8\n", " \tmembers:\n", "- \t\t0[1]\ta: [char; 1]\n", "+ \t\t0[4]\ta: [char; 4]\n", "- \t\t1[3]\t\n", " \t\t4[4]\tb: int\n", "\n"); 28 | test!(member_diff_bitfield_equal, " struct ", "member_diff_bitfield_equal", "\n", " \tsize: 2\n", " \tmembers:\n", "- \t\t0[1]\ta: char\n", "+ \t\t0[1]\tb: char\n", " \t\t1[0.1]\tc: char\n", " \t\t1.1[0.7]\t\n", "\n"); 29 | test!(member_diff_bitfield, " struct ", "member_diff_bitfield", "\n", " \tsize: 1\n", " \tmembers:\n", "- \t\t0[0.1]\ta: char\n", "+ \t\t0[0.2]\ta: char\n", "- \t\t0.1[0.7]\t\n", "+ \t\t0.2[0.6]\t\n", "\n"); 30 | test!(member_diff_unsized, " struct ", "member_diff_unsized", "\n", "- \tsize: 2\n", "+ \tsize: 1\n", " \tmembers:\n", " \t\t0[1]\ta: char\n", "- \t\t1[1]\tb: [char; 1]\n", "+ \t\t1[??]\tb: [char; ??]\n", "\n"); 31 | test!(member_diff_inline_struct_struct_equal, ""); 32 | test!(member_diff_inline_struct_struct, " struct ", "member_diff_inline_struct_struct", "\n", " \tsize: 1\n", " \tmembers:\n", " \t\t0[1]\ta: struct \n", "- \t\t\t0[1]\tb: char\n", "+ \t\t\t0[1]\tc: char\n", "\n"); 33 | test!(member_diff_inline_union_union_equal, ""); 34 | test!(member_diff_inline_union_union, " struct ", "member_diff_inline_union_union", "\n", " \tsize: 1\n", " \tmembers:\n", " \t\t0[1]\ta: union \n", "- \t\t\t0[1]\tb: char\n", "+ \t\t\t0[1]\tc: char\n", "\n"); 35 | test!(member_diff_inline_union_struct, " struct ", "member_diff_inline_union_struct", "\n", " \tsize: 1\n", " \tmembers:\n", "- \t\t0[1]\ta: struct \n", "+ \t\t0[1]\ta: union \n", "- \t\t\t0[1]\tb: char\n", "+ \t\t\t0[1]\tb: char\n", "\n"); 36 | test!(member_diff_inline_struct_none, " struct ", "member_diff_inline_struct_none", "\n", " \tsize: 1\n", " \tmembers:\n", "- \t\t0[1]\ta: struct \n", "+ \t\t0[1]\ta: char\n", "- \t\t\t0[1]\tb: char\n", "\n"); 37 | test!(member_diff_inline_none_struct, " struct ", "member_diff_inline_none_struct", "\n", " \tsize: 1\n", " \tmembers:\n", "- \t\t0[1]\ta: char\n", "+ \t\t0[1]\ta: struct \n", "+ \t\t\t0[1]\tb: char\n", "\n"); 38 | test!(enum_diff_equal, ""); 39 | test!(enum_diff, " enum ", "enum_diff", "\n", " \tsize: 4\n", " \tenumerators:\n", " \t\tA2(1)\n", "- \t\tB2(2)\n", "+ \t\tC2(2)\n", "- \t\tC2(3)\n", "+ \t\tB2(3)\n", "- \t\tD2(4)\n", " \t\tE2(5)\n", "+ \t\tF2(6)\n", "\n"); 40 | test!(array_diff_equal, ""); 41 | test!(array_diff_type, "- type ", "array_diff_type", " = char\n", "+ type ", "array_diff_type", " = C\n", " \tsize: 1\n", "\n"); 42 | test!(array_diff_size, "- type ", "array_diff_size", " = [char; 1]\n", "+ type ", "array_diff_size", " = [char; 2]\n", "- \tsize: 1\n", "+ \tsize: 2\n", "\n"); 43 | test!(function_equal, ""); 44 | test!(function_diff_return_type, " fn ", "function_diff_return_type", "\n", "[..]\n", " \treturn type:\n", "- \t\t[1]\tchar\n", "+ \t\t[4]\tint\n", "\n"); 45 | test!(function_diff_variables, " fn ", "function_diff_variables", "\n", "[..]\n", " \tvariables:\n", "- \t\t[1]\ta: char\n", "+ \t\t[1]\tb: char\n", " \t\t[1]\tc: char\n", "- \t\t[1]\td: char\n", "+ \t\t[4]\td: int\n", "- \t\t[1]\te: char\n", "+ \t\t[1]\tf: char\n", "- \t\t[4]\textra: int\n", " \t\t[1]\tg: char\n", "\n"); 46 | test!(variable_equal, ""); 47 | test!(variable_diff_size, "- var ", "variable_diff_size", ": [char; 1]\n", "+ var ", "variable_diff_size", ": [char; 2]\n", "[..]", "- \tsize: 1\n", "+ \tsize: 2\n", "\n"); 48 | test!(variable_diff_size_multi, "- var ", "variable_diff_size_multi", ": [char; 1, 3]\n", "+ var ", "variable_diff_size_multi", ": [char; 2, 4]\n", "[..]", "- \tsize: 3\n", "+ \tsize: 8\n", "\n"); 49 | test!(variable_diff_decl, " var ", "variable_diff_decl", ": int\n", "[..]", " \tsize: 4\n", "+ \tdeclaration: yes\n", "\n"); 50 | -------------------------------------------------------------------------------- /main/tests/src/support.c: -------------------------------------------------------------------------------- 1 | #ifdef TEST2 2 | int variable_diff_decl; 3 | #endif 4 | -------------------------------------------------------------------------------- /parser/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ddbug_parser" 3 | version = "0.4.0" 4 | description = "Unified debug information parser" 5 | repository = "https://github.com/gimli-rs/ddbug" 6 | keywords = ["debug", "DWARF"] 7 | categories = ["development-tools::debugging"] 8 | license = "Apache-2.0 OR MIT" 9 | edition = "2018" 10 | 11 | [dependencies] 12 | fnv = "1.0" 13 | gimli = "0.31.0" 14 | log = "0.4" 15 | memmap2 = "0.9.4" 16 | object = { version = "0.36.3", features = ["wasm"] } 17 | 18 | [features] 19 | default = [] 20 | -------------------------------------------------------------------------------- /parser/src/cfi.rs: -------------------------------------------------------------------------------- 1 | use crate::location::Register; 2 | use crate::Address; 3 | 4 | /// A CFI directive and the function offset it applies to. 5 | /// 6 | /// Address::none() is used for directives that apply to the whole function. 7 | pub type Cfi = (Address, CfiDirective); 8 | 9 | /// A CFI directive. 10 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 11 | pub enum CfiDirective { 12 | /// .cfi_startproc 13 | StartProc, 14 | 15 | /// .cfi_endproc 16 | EndProc, 17 | 18 | /// .cfi_personality
      19 | Personality(Address), 20 | 21 | /// .cfi_lsda
      22 | // TODO: encoding? 23 | Lsda(Address), 24 | 25 | /// .cfi_signal_frame 26 | SignalFrame, 27 | 28 | /// .cfi_return_column 29 | ReturnColumn(Register), 30 | 31 | /// .cfi_def_cfa , 32 | DefCfa(Register, i64), 33 | 34 | /// .cfi_def_cfa_register 35 | DefCfaRegister(Register), 36 | 37 | /// .cfi_def_cfa_offset 38 | DefCfaOffset(i64), 39 | 40 | /// .cfi_offset , 41 | Offset(Register, i64), 42 | 43 | /// .cfi_val_offset , 44 | ValOffset(Register, i64), 45 | 46 | /// .cfi_register , 47 | Register(Register, Register), 48 | 49 | /// .cfi_restore 50 | Restore(Register), 51 | 52 | /// .cfi_undefined 53 | Undefined(Register), 54 | 55 | /// .cfi_same_value 56 | SameValue(Register), 57 | 58 | /// .cfi_remember_state 59 | RememberState, 60 | 61 | /// .cfi_restore_state 62 | RestoreState, 63 | 64 | /// An unsupported instruction. 65 | Other, 66 | } 67 | -------------------------------------------------------------------------------- /parser/src/file/pdb.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use std::collections::BTreeMap; 3 | use std::io; 4 | use std::sync::Arc; 5 | 6 | use crate_pdb as pdb; 7 | use crate_pdb::FallibleIterator; 8 | 9 | use Result; 10 | use file::File; 11 | use function::{Function, FunctionOffset, Parameter}; 12 | use namespace::Namespace; 13 | use types::{ArrayType, BaseType, EnumerationType, Enumerator, FunctionType, Member, StructType, 14 | Type, TypeKind, TypeModifier, TypeModifierKind, TypeOffset, UnionType}; 15 | use unit::Unit; 16 | 17 | pub(crate) fn parse( 18 | input: &[u8], 19 | path: &str, 20 | cb: &mut FnMut(&mut File) -> Result<()>, 21 | ) -> Result<()> { 22 | let mut cursor = io::Cursor::new(input); 23 | let mut pdb = pdb::PDB::open(&mut cursor)?; 24 | let type_information = pdb.type_information()?; 25 | let symbol_table = pdb.global_symbols()?; 26 | 27 | let mut member_lists = BTreeMap::new(); 28 | let mut enumerator_lists = BTreeMap::new(); 29 | let mut argument_lists = BTreeMap::new(); 30 | let mut bitfields = BTreeMap::new(); 31 | 32 | let mut unit = Unit::default(); 33 | let namespace = None; 34 | 35 | let mut types = type_information.iter(); 36 | add_primitive_types(&mut unit.types); 37 | while let Some(ty) = types.next()? { 38 | let index = ty.type_index() as usize; 39 | // debug!("Type: {} {:?}", index, ty.parse()); 40 | match ty.parse() { 41 | Ok(pdb::TypeData::Class(ref data)) => { 42 | parse_class(&mut unit, &member_lists, &namespace, index, data)?; 43 | } 44 | Ok(pdb::TypeData::Union(ref data)) => { 45 | parse_union(&mut unit, &member_lists, &namespace, index, data)?; 46 | } 47 | Ok(pdb::TypeData::Enumeration(ref data)) => { 48 | parse_enumeration(&mut unit, &enumerator_lists, &namespace, index, data)?; 49 | } 50 | Ok(pdb::TypeData::Procedure(ref data)) => { 51 | parse_procedure(&mut unit, &argument_lists, index, data)?; 52 | } 53 | Ok(pdb::TypeData::MemberFunction(ref data)) => { 54 | parse_member_function(&mut unit, &argument_lists, index, data)?; 55 | } 56 | Ok(pdb::TypeData::Pointer(ref data)) => { 57 | let underlying_type = parse_type_index(data.underlying_type); 58 | let byte_size = u64::from(data.attributes.size()); 59 | let byte_size = if byte_size == 0 { 60 | None 61 | } else { 62 | Some(byte_size) 63 | }; 64 | unit.types.push(Type { 65 | id: Cell::new(0), 66 | offset: TypeOffset(index), 67 | kind: TypeKind::Modifier(TypeModifier { 68 | kind: TypeModifierKind::Pointer, 69 | ty: underlying_type, 70 | name: None, 71 | byte_size, 72 | address_size: None, 73 | }), 74 | }); 75 | } 76 | Ok(pdb::TypeData::Modifier(ref data)) => { 77 | let underlying_type = parse_type_index(data.underlying_type); 78 | // TODO: volatile, unaligned 79 | let kind = if data.constant { 80 | TypeModifierKind::Const 81 | } else { 82 | TypeModifierKind::Other 83 | }; 84 | unit.types.push(Type { 85 | id: Cell::new(0), 86 | offset: TypeOffset(index), 87 | kind: TypeKind::Modifier(TypeModifier { 88 | kind, 89 | ty: underlying_type, 90 | name: None, 91 | byte_size: None, 92 | address_size: None, 93 | }), 94 | }); 95 | } 96 | Ok(pdb::TypeData::Bitfield(data)) => { 97 | bitfields.insert(index, data); 98 | } 99 | Ok(pdb::TypeData::Array(ref data)) => { 100 | parse_array(&mut unit, index, data)?; 101 | } 102 | Ok(pdb::TypeData::FieldList(ref data)) => { 103 | parse_field_list( 104 | &mut member_lists, 105 | &mut enumerator_lists, 106 | &bitfields, 107 | index, 108 | data, 109 | )?; 110 | } 111 | Ok(pdb::TypeData::ArgumentList(data)) => { 112 | argument_lists.insert(index, data.arguments); 113 | } 114 | Ok(other) => { 115 | debug!("PDB unimplemented type {} {:?}", index, other); 116 | } 117 | Err(pdb::Error::UnimplementedTypeKind(kind)) => { 118 | debug!("PDB unimplemented type {} {}", index, kind); 119 | } 120 | Err(e) => { 121 | return Err(e.into()); 122 | } 123 | } 124 | } 125 | 126 | let mut symbols = symbol_table.iter(); 127 | let mut symbol_index = 0; 128 | while let Some(symbol) = symbols.next()? { 129 | match symbol.parse()? { 130 | pdb::SymbolData::PublicSymbol(data) => if data.function { 131 | unit.functions.push(Function { 132 | id: Cell::new(0), 133 | offset: Some(FunctionOffset(symbol_index)), 134 | namespace: namespace.clone(), 135 | name: Some(symbol.name()?.as_bytes()), 136 | symbol_name: None, 137 | linkage_name: None, 138 | source: Default::default(), 139 | address: Some(u64::from(data.offset)), 140 | size: None, 141 | inline: false, 142 | declaration: false, 143 | parameters: Vec::new(), 144 | return_type: None, 145 | inlined_functions: Vec::new(), 146 | variables: Vec::new(), 147 | }); 148 | symbol_index += 1; 149 | }, 150 | _ => {} 151 | } 152 | } 153 | 154 | let mut units = Vec::new(); 155 | units.push(unit); 156 | 157 | let mut file = File { 158 | path, 159 | // TODO 160 | code: None, 161 | // TODO 162 | sections: Vec::new(), 163 | // TODO 164 | symbols: Vec::new(), 165 | units, 166 | }; 167 | file.normalize(); 168 | cb(&mut file) 169 | } 170 | 171 | fn add_primitive_types<'input>(types: &mut Vec>) { 172 | add_primitive_type(types, 0x00, b"NoType", 4); 173 | add_primitive_type(types, 0x03, b"void", 0); 174 | add_primitive_type(types, 0x10, b"i8", 1); // signed char 175 | add_primitive_type(types, 0x11, b"i16", 2); // short 176 | add_primitive_type(types, 0x12, b"i32", 4); // long 177 | add_primitive_type(types, 0x13, b"i64", 8); 178 | add_primitive_type(types, 0x20, b"u8", 1); // unsigned char 179 | add_primitive_type(types, 0x21, b"u16", 2); // unsigned short 180 | add_primitive_type(types, 0x22, b"u32", 4); // unsigned long 181 | add_primitive_type(types, 0x23, b"u64", 8); 182 | add_primitive_type(types, 0x30, b"bool", 1); 183 | add_primitive_type(types, 0x40, b"f32", 4); // float 184 | add_primitive_type(types, 0x41, b"f64", 8); // double 185 | add_primitive_type(types, 0x68, b"i8", 1); // int8_t 186 | add_primitive_type(types, 0x69, b"u8", 1); // uint8_t 187 | add_primitive_type(types, 0x70, b"i8", 1); // char 188 | add_primitive_type(types, 0x71, b"wchar_t", 2); // wchar_t 189 | add_primitive_type(types, 0x72, b"i16", 4); // int16_t 190 | add_primitive_type(types, 0x73, b"u16", 4); // uint16_t 191 | add_primitive_type(types, 0x74, b"i32", 4); // int32_t 192 | add_primitive_type(types, 0x75, b"u32", 4); // uint32_t 193 | add_primitive_type(types, 0x76, b"i64", 8); // int64_t 194 | add_primitive_type(types, 0x77, b"u64", 8); // uint64_t 195 | } 196 | 197 | fn add_primitive_type<'input>( 198 | types: &mut Vec>, 199 | index: usize, 200 | name: &'static [u8], 201 | size: u64, 202 | ) { 203 | types.push(Type { 204 | id: Cell::new(0), 205 | offset: TypeOffset(index), 206 | kind: TypeKind::Base(BaseType { 207 | name: Some(name), 208 | byte_size: Some(size), 209 | }), 210 | }); 211 | 212 | types.push(Type { 213 | id: Cell::new(0), 214 | offset: TypeOffset(0x400 + index), 215 | kind: TypeKind::Modifier(TypeModifier { 216 | kind: TypeModifierKind::Pointer, 217 | ty: Some(TypeOffset(index)), 218 | name: None, 219 | byte_size: Some(4), 220 | address_size: None, 221 | }), 222 | }); 223 | 224 | types.push(Type { 225 | id: Cell::new(0), 226 | offset: TypeOffset(0x600 + index), 227 | kind: TypeKind::Modifier(TypeModifier { 228 | kind: TypeModifierKind::Pointer, 229 | ty: Some(TypeOffset(index)), 230 | name: None, 231 | byte_size: Some(8), 232 | address_size: None, 233 | }), 234 | }); 235 | } 236 | 237 | fn parse_class<'input>( 238 | unit: &mut Unit<'input>, 239 | member_lists: &BTreeMap>>, 240 | namespace: &Option>>, 241 | index: usize, 242 | data: &pdb::ClassType<'input>, 243 | ) -> Result<()> { 244 | // TODO: derived_from, vtable_shape 245 | let fields = data.fields.and_then(parse_type_index); 246 | let declaration = data.properties.forward_reference(); 247 | let byte_size = if declaration { 248 | None 249 | } else { 250 | Some(u64::from(data.size)) 251 | }; 252 | let mut members = match fields { 253 | Some(ref fields) => match member_lists.get(&fields.0) { 254 | Some(members) => members.clone(), 255 | None => return Err(format!("Missing field list for index {}", fields.0).into()), 256 | }, 257 | None => Vec::new(), 258 | }; 259 | let mut bit_offset = byte_size.map(|v| v * 8); 260 | for member in members.iter_mut().rev() { 261 | member.next_bit_offset = bit_offset; 262 | bit_offset = Some(member.bit_offset); 263 | } 264 | unit.types.push(Type { 265 | id: Cell::new(0), 266 | offset: TypeOffset(index), 267 | kind: TypeKind::Struct(StructType { 268 | namespace: namespace.clone(), 269 | name: Some(data.name.as_bytes()), 270 | source: Default::default(), 271 | byte_size, 272 | declaration, 273 | members, 274 | }), 275 | }); 276 | Ok(()) 277 | } 278 | 279 | fn parse_union<'input>( 280 | unit: &mut Unit<'input>, 281 | member_lists: &BTreeMap>>, 282 | namespace: &Option>>, 283 | index: usize, 284 | data: &pdb::UnionType<'input>, 285 | ) -> Result<()> { 286 | let fields = parse_type_index(data.fields); 287 | let declaration = data.properties.forward_reference(); 288 | let byte_size = if declaration { 289 | None 290 | } else { 291 | Some(u64::from(data.size)) 292 | }; 293 | let mut members = match fields { 294 | Some(fields) => match member_lists.get(&fields.0) { 295 | Some(members) => members.clone(), 296 | None => return Err(format!("Missing field list for index {}", fields.0).into()), 297 | }, 298 | None => Vec::new(), 299 | }; 300 | let mut bit_offset = byte_size.map(|v| v * 8); 301 | for member in members.iter_mut().rev() { 302 | member.next_bit_offset = bit_offset; 303 | bit_offset = Some(member.bit_offset); 304 | } 305 | unit.types.push(Type { 306 | id: Cell::new(0), 307 | offset: TypeOffset(index), 308 | kind: TypeKind::Union(UnionType { 309 | namespace: namespace.clone(), 310 | name: Some(data.name.as_bytes()), 311 | source: Default::default(), 312 | byte_size, 313 | declaration, 314 | members, 315 | }), 316 | }); 317 | Ok(()) 318 | } 319 | 320 | fn parse_enumeration<'input>( 321 | unit: &mut Unit<'input>, 322 | enumerator_lists: &BTreeMap>>, 323 | namespace: &Option>>, 324 | index: usize, 325 | data: &pdb::EnumerationType<'input>, 326 | ) -> Result<()> { 327 | let underlying_type = parse_type_index(data.underlying_type); 328 | let fields = parse_type_index(data.fields); 329 | let declaration = data.properties.forward_reference(); 330 | let enumerators = match fields { 331 | Some(ref fields) => match enumerator_lists.get(&fields.0) { 332 | Some(enumerators) => enumerators.clone(), 333 | None => return Err(format!("Missing field list for index {}", fields.0).into()), 334 | }, 335 | None => Vec::new(), 336 | }; 337 | unit.types.push(Type { 338 | id: Cell::new(0), 339 | offset: TypeOffset(index), 340 | kind: TypeKind::Enumeration(EnumerationType { 341 | namespace: namespace.clone(), 342 | name: Some(data.name.as_bytes()), 343 | source: Default::default(), 344 | declaration, 345 | ty: underlying_type, 346 | byte_size: None, 347 | enumerators, 348 | }), 349 | }); 350 | Ok(()) 351 | } 352 | 353 | fn parse_procedure<'input>( 354 | unit: &mut Unit<'input>, 355 | argument_lists: &BTreeMap>, 356 | index: usize, 357 | data: &pdb::ProcedureType, 358 | ) -> Result<()> { 359 | let return_type = data.return_type.and_then(parse_type_index); 360 | let argument_list = parse_type_index(data.argument_list); 361 | let parameter_count = data.parameter_count as usize; 362 | let parameters = match argument_list { 363 | Some(ref argument_list) => match argument_lists.get(&argument_list.0) { 364 | Some(arguments) => { 365 | if arguments.len() != parameter_count { 366 | debug!( 367 | "PDB parameter count mismatch {}, {}", 368 | arguments.len(), 369 | parameter_count 370 | ); 371 | } 372 | arguments 373 | .iter() 374 | .map(|argument| Parameter { 375 | offset: None, 376 | name: None, 377 | ty: parse_type_index(*argument), 378 | }) 379 | .collect() 380 | } 381 | None => return Err(format!("Missing argument list {}", argument_list.0).into()), 382 | }, 383 | None => Vec::new(), 384 | }; 385 | 386 | unit.types.push( 387 | // TODO: attributes 388 | Type { 389 | id: Cell::new(0), 390 | offset: TypeOffset(index), 391 | kind: TypeKind::Function(FunctionType { 392 | parameters, 393 | return_type, 394 | byte_size: None, 395 | }), 396 | }, 397 | ); 398 | Ok(()) 399 | } 400 | 401 | fn parse_member_function<'input>( 402 | unit: &mut Unit<'input>, 403 | argument_lists: &BTreeMap>, 404 | index: usize, 405 | data: &pdb::MemberFunctionType, 406 | ) -> Result<()> { 407 | let return_type = parse_type_index(data.return_type); 408 | //let class_type = parse_type_index(data.class_type); 409 | let this_pointer_type = data.this_pointer_type.and_then(parse_type_index); 410 | let argument_list = parse_type_index(data.argument_list); 411 | let parameter_count = data.parameter_count as usize; 412 | let mut parameters = Vec::with_capacity(parameter_count + 1); 413 | match this_pointer_type { 414 | None | Some(TypeOffset(3)) => {} 415 | ty => { 416 | parameters.push(Parameter { 417 | offset: None, 418 | name: None, 419 | ty, 420 | }); 421 | } 422 | } 423 | if let Some(ref argument_list) = argument_list { 424 | match argument_lists.get(&argument_list.0) { 425 | Some(arguments) => { 426 | if arguments.len() != parameter_count { 427 | debug!( 428 | "PDB parameter count mismatch {}, {}", 429 | arguments.len(), 430 | parameter_count 431 | ); 432 | } 433 | for argument in arguments { 434 | parameters.push(Parameter { 435 | offset: None, 436 | name: None, 437 | ty: parse_type_index(*argument), 438 | }); 439 | } 440 | } 441 | None => return Err(format!("Missing argument list {}", argument_list.0).into()), 442 | } 443 | }; 444 | 445 | unit.types.push( 446 | // TODO: class_type, attributes, this_adjustment 447 | Type { 448 | id: Cell::new(0), 449 | offset: TypeOffset(index), 450 | kind: TypeKind::Function(FunctionType { 451 | parameters, 452 | return_type, 453 | byte_size: None, 454 | }), 455 | }, 456 | ); 457 | Ok(()) 458 | } 459 | 460 | fn parse_array<'input>(unit: &mut Unit<'input>, index: usize, data: &pdb::ArrayType) -> Result<()> { 461 | if data.dimensions.len() != 1 { 462 | return Err("Unsupported multi-dimensional array".into()); 463 | } 464 | let element_type = parse_type_index(data.element_type); 465 | //let indexing_type = parse_type_index(indexing_type); 466 | let byte_size = Some(u64::from(data.dimensions[0])); 467 | unit.types.push( 468 | // TODO: indexing_type, stride 469 | Type { 470 | id: Cell::new(0), 471 | offset: TypeOffset(index), 472 | kind: TypeKind::Array(ArrayType { 473 | ty: element_type, 474 | byte_size, 475 | ..Default::default() 476 | }), 477 | }, 478 | ); 479 | Ok(()) 480 | } 481 | 482 | fn parse_field_list<'input>( 483 | member_lists: &mut BTreeMap>>, 484 | enumerator_lists: &mut BTreeMap>>, 485 | bitfields: &BTreeMap, 486 | index: usize, 487 | data: &pdb::FieldList<'input>, 488 | ) -> Result<()> { 489 | let continuation = data.continuation.and_then(parse_type_index); 490 | if continuation.is_some() { 491 | return Err("Unsupported PDB field list continuation".into()); 492 | } 493 | let mut members = Vec::new(); 494 | let mut enumerators = Vec::new(); 495 | for field in &data.fields { 496 | match *field { 497 | pdb::TypeData::Member(ref member) => { 498 | let mut ty = parse_type_index(member.field_type); 499 | let mut bit_offset = u64::from(member.offset) * 8; 500 | let mut bit_size = None; 501 | match bitfields.get(&(member.field_type as usize)) { 502 | Some(bitfield) => { 503 | ty = parse_type_index(bitfield.underlying_type); 504 | bit_offset += u64::from(bitfield.position); 505 | bit_size = Some(u64::from(bitfield.length)); 506 | } 507 | None => {} 508 | } 509 | members.push(Member { 510 | name: Some(member.name.as_bytes()), 511 | ty, 512 | bit_offset, 513 | bit_size, 514 | next_bit_offset: None, 515 | }); 516 | } 517 | pdb::TypeData::Enumerate(ref enumerate) => { 518 | let value = match enumerate.value { 519 | pdb::Variant::U8(val) => i64::from(val), 520 | pdb::Variant::U16(val) => i64::from(val), 521 | pdb::Variant::U32(val) => i64::from(val), 522 | pdb::Variant::U64(val) => val as i64, 523 | pdb::Variant::I8(val) => i64::from(val), 524 | pdb::Variant::I16(val) => i64::from(val), 525 | pdb::Variant::I32(val) => i64::from(val), 526 | pdb::Variant::I64(val) => val, 527 | }; 528 | enumerators.push(Enumerator { 529 | name: Some(enumerate.name.as_bytes()), 530 | value: Some(value), 531 | }); 532 | } 533 | _ => { 534 | debug!("PDB unimplemented field type {:?}", field); 535 | } 536 | } 537 | } 538 | member_lists.insert(index, members); 539 | enumerator_lists.insert(index, enumerators); 540 | Ok(()) 541 | } 542 | 543 | fn parse_type_index(index: pdb::TypeIndex) -> Option { 544 | if index == 0 { 545 | None 546 | } else { 547 | Some(TypeOffset(index as usize)) 548 | } 549 | } 550 | -------------------------------------------------------------------------------- /parser/src/function.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::cmp; 3 | use std::sync::Arc; 4 | 5 | use crate::file::FileHash; 6 | use crate::location::{self, FrameLocation, Piece, Register}; 7 | use crate::namespace::Namespace; 8 | use crate::range::Range; 9 | use crate::source::Source; 10 | use crate::types::{ParameterType, Type, TypeOffset}; 11 | use crate::variable::LocalVariable; 12 | use crate::{Address, Id, Size}; 13 | 14 | /// The debuginfo offset of a function. 15 | /// 16 | /// This is unique for all functions in a file. 17 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] 18 | pub struct FunctionOffset(usize); 19 | 20 | impl FunctionOffset { 21 | #[inline] 22 | pub(crate) fn new(offset: usize) -> FunctionOffset { 23 | debug_assert!(FunctionOffset(offset) != FunctionOffset::none()); 24 | FunctionOffset(offset) 25 | } 26 | 27 | #[inline] 28 | pub(crate) fn none() -> FunctionOffset { 29 | FunctionOffset(usize::MAX) 30 | } 31 | 32 | #[inline] 33 | pub(crate) fn is_none(self) -> bool { 34 | self == Self::none() 35 | } 36 | 37 | #[inline] 38 | pub(crate) fn is_some(self) -> bool { 39 | self != Self::none() 40 | } 41 | 42 | #[inline] 43 | pub(crate) fn get(self) -> Option { 44 | if self.is_none() { 45 | None 46 | } else { 47 | Some(self.0) 48 | } 49 | } 50 | } 51 | 52 | impl Default for FunctionOffset { 53 | #[inline] 54 | fn default() -> Self { 55 | FunctionOffset::none() 56 | } 57 | } 58 | 59 | /// A function. 60 | #[derive(Debug, Default)] 61 | pub struct Function<'input> { 62 | pub(crate) id: Id, 63 | pub(crate) offset: FunctionOffset, 64 | pub(crate) namespace: Option>>, 65 | pub(crate) name: Option<&'input str>, 66 | pub(crate) linkage_name: Option<&'input str>, 67 | pub(crate) symbol_name: Option<&'input str>, 68 | pub(crate) source: Source<'input>, 69 | pub(crate) address: Address, 70 | pub(crate) size: Size, 71 | pub(crate) ranges: Vec, 72 | pub(crate) inline: bool, 73 | pub(crate) declaration: bool, 74 | pub(crate) parameters: Vec>, 75 | pub(crate) return_type: TypeOffset, 76 | } 77 | 78 | /// Extra function details. 79 | /// 80 | /// These are kept separate from `Function` so that they can be loaded only when needed. 81 | #[derive(Debug, Default)] 82 | pub struct FunctionDetails<'input> { 83 | pub(crate) parameters: Vec>, 84 | pub(crate) variables: Vec>, 85 | pub(crate) inlined_functions: Vec>, 86 | } 87 | 88 | impl<'input> Function<'input> { 89 | pub(crate) fn from_offset<'a>( 90 | hash: &'a FileHash<'input>, 91 | offset: FunctionOffset, 92 | ) -> Option<&'a Function<'input>> { 93 | if offset.is_none() { 94 | return None; 95 | } 96 | hash.functions_by_offset.get(&offset).cloned() 97 | } 98 | 99 | /// The user defined id for this function. 100 | #[inline] 101 | pub fn id(&self) -> usize { 102 | self.id.get() 103 | } 104 | 105 | /// Set a user defined id for this function. 106 | #[inline] 107 | pub fn set_id(&self, id: usize) { 108 | self.id.set(id) 109 | } 110 | 111 | /// The namespace of the function. 112 | pub fn namespace(&self) -> Option<&Namespace<'input>> { 113 | self.namespace.as_deref() 114 | } 115 | 116 | /// The name of the function. 117 | #[inline] 118 | pub fn name(&self) -> Option<&'input str> { 119 | self.name 120 | } 121 | 122 | /// The linkage name of the variable. 123 | #[inline] 124 | pub fn linkage_name(&self) -> Option<&'input str> { 125 | self.linkage_name 126 | } 127 | 128 | /// The symbol name of the function. 129 | /// 130 | /// This is determined from a symbol table entry with a matching address. 131 | #[inline] 132 | pub fn symbol_name(&self) -> Option<&'input str> { 133 | self.symbol_name 134 | } 135 | 136 | /// The source information for the function. 137 | #[inline] 138 | pub fn source(&self) -> &Source<'input> { 139 | &self.source 140 | } 141 | 142 | /// The address of the function. 143 | #[inline] 144 | pub fn address(&self) -> Option { 145 | self.address.get() 146 | } 147 | 148 | /// The size in bytes of the function. 149 | /// 150 | /// This may exclude padding, and may be non-contiguous. 151 | #[inline] 152 | pub fn size(&self) -> Option { 153 | self.size.get() 154 | } 155 | 156 | /// The address ranges of the function. 157 | pub fn ranges(&self) -> &[Range] { 158 | &self.ranges 159 | } 160 | 161 | /// Return true if this is an inlined function. 162 | #[inline] 163 | pub fn is_inline(&self) -> bool { 164 | self.inline 165 | } 166 | 167 | /// Return true if this is a declaration. 168 | #[inline] 169 | pub fn is_declaration(&self) -> bool { 170 | self.declaration 171 | } 172 | 173 | /// The function parameter types. 174 | #[inline] 175 | pub fn parameters(&self) -> &[ParameterType<'input>] { 176 | &self.parameters 177 | } 178 | 179 | /// The return type. 180 | /// 181 | /// Returns `None` if the return type is invalid. 182 | #[inline] 183 | pub fn return_type<'a>(&self, hash: &'a FileHash<'input>) -> Option>> { 184 | Type::from_offset(hash, self.return_type) 185 | } 186 | 187 | /// Extra function details. 188 | pub fn details(&self, hash: &FileHash<'input>) -> FunctionDetails<'input> { 189 | hash.file.get_function_details(self.offset, hash) 190 | } 191 | 192 | /// Compare the identifying information of two functions. 193 | /// 194 | /// Functions are equal if they have the same namespace and name. 195 | /// 196 | /// This can be used to sort, and to determine if two functions refer to the same definition 197 | /// (even if there are differences in the definitions). 198 | pub fn cmp_id( 199 | _hash_a: &FileHash, 200 | a: &Function, 201 | _hash_b: &FileHash, 202 | b: &Function, 203 | ) -> cmp::Ordering { 204 | Namespace::cmp_ns_and_name(a.namespace(), a.name(), b.namespace(), b.name()) 205 | } 206 | } 207 | 208 | impl<'input> FunctionDetails<'input> { 209 | /// The function parameters. 210 | #[inline] 211 | pub fn parameters(&self) -> &[Parameter<'input>] { 212 | &self.parameters 213 | } 214 | 215 | /// The local variables. 216 | #[inline] 217 | pub fn variables(&self) -> &[LocalVariable<'input>] { 218 | &self.variables 219 | } 220 | 221 | /// The inlined functions. 222 | #[inline] 223 | pub fn inlined_functions(&self) -> &[InlinedFunction<'input>] { 224 | &self.inlined_functions 225 | } 226 | } 227 | 228 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 229 | pub(crate) struct ParameterOffset(usize); 230 | 231 | impl ParameterOffset { 232 | #[inline] 233 | pub(crate) fn new(offset: usize) -> ParameterOffset { 234 | debug_assert!(ParameterOffset(offset) != ParameterOffset::none()); 235 | ParameterOffset(offset) 236 | } 237 | 238 | #[inline] 239 | pub(crate) fn none() -> ParameterOffset { 240 | ParameterOffset(usize::MAX) 241 | } 242 | } 243 | 244 | impl Default for ParameterOffset { 245 | #[inline] 246 | fn default() -> Self { 247 | ParameterOffset::none() 248 | } 249 | } 250 | 251 | /// A function parameter. 252 | #[derive(Debug, Default, Clone)] 253 | pub struct Parameter<'input> { 254 | pub(crate) offset: ParameterOffset, 255 | pub(crate) name: Option<&'input str>, 256 | pub(crate) ty: TypeOffset, 257 | // TODO: move this to ParameterDetails 258 | pub(crate) locations: Vec<(Range, Piece)>, 259 | } 260 | 261 | impl<'input> Parameter<'input> { 262 | /// The name of the parameter. 263 | #[inline] 264 | pub fn name(&self) -> Option<&'input str> { 265 | self.name 266 | } 267 | 268 | /// The type offset of the parameter. 269 | /// 270 | /// A type offset is unique for all types in a file. 271 | #[inline] 272 | pub fn type_offset(&self) -> TypeOffset { 273 | self.ty 274 | } 275 | 276 | /// The type of the parameter. 277 | #[inline] 278 | pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option>> { 279 | Type::from_offset(hash, self.ty) 280 | } 281 | 282 | /// The size in bytes of the parameter. 283 | pub fn byte_size(&self, hash: &FileHash) -> Option { 284 | self.ty(hash).and_then(|v| v.byte_size(hash)) 285 | } 286 | 287 | /// A list of all locations where this parameter is stored. 288 | pub fn locations(&self) -> &[(Range, Piece)] { 289 | &self.locations 290 | } 291 | 292 | /// The registers in which this parameter is stored. 293 | pub fn registers(&self) -> impl Iterator + '_ { 294 | location::registers(&self.locations) 295 | } 296 | 297 | /// The registers pointing to where this variable is stored. 298 | pub fn register_offsets(&self) -> impl Iterator + '_ { 299 | location::register_offsets(&self.locations) 300 | } 301 | 302 | /// The stack frame locations at which this parameter is stored. 303 | pub fn frame_locations(&self) -> impl Iterator + '_ { 304 | location::frame_locations(&self.locations) 305 | } 306 | 307 | /// Compare the identifying information of two parameters. 308 | /// 309 | /// Parameters are considered equal if their name and type are equal. 310 | /// 311 | /// This can be used to sort, and to determine if two types refer to the same definition 312 | /// (even if there are differences in the definitions). 313 | #[allow(dead_code)] 314 | fn cmp_id(hash_a: &FileHash, a: &Parameter, hash_b: &FileHash, b: &Parameter) -> cmp::Ordering { 315 | let ord = Self::cmp_type(hash_a, a, hash_b, b); 316 | if ord != cmp::Ordering::Equal { 317 | return ord; 318 | } 319 | a.name.cmp(&b.name) 320 | } 321 | 322 | /// Compare the types of two parameters. 323 | pub fn cmp_type( 324 | hash_a: &FileHash, 325 | a: &Parameter, 326 | hash_b: &FileHash, 327 | b: &Parameter, 328 | ) -> cmp::Ordering { 329 | match (&a.ty(hash_a), &b.ty(hash_b)) { 330 | (Some(ty_a), Some(ty_b)) => Type::cmp_id(hash_a, ty_a, hash_b, ty_b), 331 | (Some(_), None) => cmp::Ordering::Less, 332 | (None, Some(_)) => cmp::Ordering::Greater, 333 | (None, None) => cmp::Ordering::Equal, 334 | } 335 | } 336 | } 337 | 338 | /// An inlined instance of a function. 339 | #[derive(Debug, Default)] 340 | pub struct InlinedFunction<'input> { 341 | pub(crate) abstract_origin: FunctionOffset, 342 | pub(crate) size: Size, 343 | pub(crate) ranges: Vec, 344 | pub(crate) parameters: Vec>, 345 | pub(crate) variables: Vec>, 346 | pub(crate) inlined_functions: Vec>, 347 | pub(crate) call_source: Source<'input>, 348 | } 349 | 350 | impl<'input> InlinedFunction<'input> { 351 | /// The function that this is an inlined instance of. 352 | #[inline] 353 | pub fn abstract_origin<'a>(&self, hash: &'a FileHash<'input>) -> Option<&'a Function<'input>> { 354 | Function::from_offset(hash, self.abstract_origin) 355 | } 356 | 357 | /// The address ranges of the inlined function instance. 358 | #[inline] 359 | pub fn ranges(&self) -> &[Range] { 360 | &self.ranges 361 | } 362 | 363 | /// The size of the inlined function instance. 364 | #[inline] 365 | pub fn size(&self) -> Option { 366 | self.size.get() 367 | } 368 | 369 | /// The source information for call location. 370 | #[inline] 371 | pub fn call_source(&self) -> &Source<'input> { 372 | &self.call_source 373 | } 374 | 375 | /// The function parameters. 376 | #[inline] 377 | pub fn parameters(&self) -> &[Parameter<'input>] { 378 | &self.parameters 379 | } 380 | 381 | /// The local variables. 382 | #[inline] 383 | pub fn variables(&self) -> &[LocalVariable<'input>] { 384 | &self.variables 385 | } 386 | 387 | /// The inlined functions within this inlined functions. 388 | #[inline] 389 | pub fn inlined_functions(&self) -> &[InlinedFunction<'input>] { 390 | &self.inlined_functions 391 | } 392 | } 393 | -------------------------------------------------------------------------------- /parser/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A library for parsing debuginfo. 2 | //! 3 | //! ## Example usage 4 | //! 5 | //! ```rust,no_run 6 | //! # fn main() -> Result<(), Box> { 7 | //! # let a_file_path = String::new(); 8 | //! let ctx = ddbug_parser::File::parse(a_file_path)?; 9 | //! let file = ctx.file(); 10 | //! for unit in file.units() { 11 | //! for function in unit.functions() { 12 | //! if let Some(name) = function.name() { 13 | //! println!("{}", name); 14 | //! } 15 | //! } 16 | //! } 17 | //! Ok(()) 18 | //! } 19 | //! ``` 20 | #![deny(missing_docs)] 21 | // Enable some rust 2018 idioms. 22 | #![warn(bare_trait_objects)] 23 | #![warn(unused_extern_crates)] 24 | // Calm down clippy. 25 | #![allow(clippy::single_match)] 26 | #![allow(clippy::match_single_binding)] 27 | #![allow(clippy::too_many_arguments)] 28 | 29 | #[macro_use] 30 | extern crate log; 31 | 32 | mod cfi; 33 | mod file; 34 | mod function; 35 | mod location; 36 | mod namespace; 37 | mod range; 38 | mod source; 39 | mod types; 40 | mod unit; 41 | mod variable; 42 | 43 | pub use crate::cfi::*; 44 | pub use crate::file::*; 45 | pub use crate::function::*; 46 | pub use crate::location::*; 47 | pub use crate::namespace::*; 48 | pub use crate::range::*; 49 | pub use crate::source::*; 50 | pub use crate::types::*; 51 | pub use crate::unit::*; 52 | pub use crate::variable::*; 53 | 54 | use std::borrow::{Borrow, Cow}; 55 | use std::error; 56 | use std::fmt; 57 | use std::io; 58 | use std::result; 59 | use std::sync::atomic::{AtomicUsize, Ordering}; 60 | 61 | /// A parsing error. 62 | #[derive(Debug)] 63 | pub struct Error(pub Cow<'static, str>); 64 | 65 | impl error::Error for Error { 66 | fn description(&self) -> &str { 67 | self.0.borrow() 68 | } 69 | } 70 | 71 | impl fmt::Display for Error { 72 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 73 | write!(f, "{}", self.0) 74 | } 75 | } 76 | 77 | impl From<&'static str> for Error { 78 | fn from(s: &'static str) -> Error { 79 | Error(Cow::Borrowed(s)) 80 | } 81 | } 82 | 83 | impl From for Error { 84 | fn from(s: String) -> Error { 85 | Error(Cow::Owned(s)) 86 | } 87 | } 88 | 89 | impl From for Error { 90 | fn from(e: io::Error) -> Error { 91 | Error(Cow::Owned(format!("IO error: {}", e))) 92 | } 93 | } 94 | 95 | impl From for Error { 96 | fn from(e: gimli::Error) -> Error { 97 | Error(Cow::Owned(format!("DWARF error: {}", e))) 98 | } 99 | } 100 | 101 | impl From for Error { 102 | fn from(e: object::Error) -> Error { 103 | Error(Cow::Owned(format!("object error: {}", e))) 104 | } 105 | } 106 | 107 | /* 108 | impl From for Error { 109 | fn from(e: crate_pdb::Error) -> Error { 110 | Error(Cow::Owned(format!("PDB error: {}", e))) 111 | } 112 | } 113 | */ 114 | 115 | /// A parsing result. 116 | pub type Result = result::Result; 117 | 118 | mod address { 119 | /// An optional address. 120 | /// 121 | /// This is similar to `Option`, but uses `!0` to encode the `None` case. 122 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 123 | pub struct Address(u64); 124 | 125 | impl Address { 126 | /// Create a known address value. 127 | #[inline] 128 | pub fn new(address: u64) -> Address { 129 | debug_assert!(Address(address) != Address::none()); 130 | Address(address) 131 | } 132 | 133 | /// Create an unknown or absent address value. 134 | #[inline] 135 | pub fn none() -> Address { 136 | Address(!0) 137 | } 138 | 139 | /// Return true if the address is unknown or absent. 140 | #[inline] 141 | pub fn is_none(self) -> bool { 142 | self == Self::none() 143 | } 144 | 145 | /// Return true if the address is known. 146 | #[inline] 147 | pub fn is_some(self) -> bool { 148 | self != Self::none() 149 | } 150 | 151 | /// Return the address. 152 | #[inline] 153 | pub fn get(self) -> Option { 154 | if self.is_none() { 155 | None 156 | } else { 157 | Some(self.0) 158 | } 159 | } 160 | } 161 | 162 | impl Default for Address { 163 | #[inline] 164 | fn default() -> Self { 165 | Address::none() 166 | } 167 | } 168 | } 169 | 170 | pub use crate::address::Address; 171 | 172 | mod size { 173 | /// An optional size. 174 | /// 175 | /// This is similar to `Option`, but uses `u64::MAX` to encode the `None` case. 176 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 177 | pub struct Size(u64); 178 | 179 | impl Size { 180 | /// Create a known size value. 181 | #[inline] 182 | pub fn new(size: u64) -> Size { 183 | debug_assert!(Size(size) != Size::none()); 184 | Size(size) 185 | } 186 | 187 | /// Create an unknown or absent size value. 188 | #[inline] 189 | pub fn none() -> Size { 190 | Size(u64::MAX) 191 | } 192 | 193 | /// Return true if the size is unknown or absent. 194 | #[inline] 195 | pub fn is_none(self) -> bool { 196 | self == Self::none() 197 | } 198 | 199 | /// Return true if the size is known. 200 | #[inline] 201 | pub fn is_some(self) -> bool { 202 | self != Self::none() 203 | } 204 | 205 | /// Return the size. 206 | #[inline] 207 | pub fn get(self) -> Option { 208 | if self.is_none() { 209 | None 210 | } else { 211 | Some(self.0) 212 | } 213 | } 214 | } 215 | 216 | impl Default for Size { 217 | #[inline] 218 | fn default() -> Self { 219 | Size::none() 220 | } 221 | } 222 | 223 | impl From> for Size { 224 | fn from(size: Option) -> Size { 225 | match size { 226 | Some(size) => Size::new(size), 227 | None => Size::none(), 228 | } 229 | } 230 | } 231 | } 232 | 233 | pub use crate::size::Size; 234 | 235 | #[derive(Debug, Default)] 236 | struct Id(AtomicUsize); 237 | 238 | impl Clone for Id { 239 | fn clone(&self) -> Self { 240 | Id(AtomicUsize::new(self.get())) 241 | } 242 | } 243 | 244 | impl Id { 245 | fn new(id: usize) -> Self { 246 | Id(AtomicUsize::new(id)) 247 | } 248 | 249 | fn get(&self) -> usize { 250 | self.0.load(Ordering::Acquire) 251 | } 252 | 253 | fn set(&self, id: usize) { 254 | self.0.store(id, Ordering::Release) 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /parser/src/location.rs: -------------------------------------------------------------------------------- 1 | use crate::file::FileHash; 2 | use crate::{Address, Range, Size}; 3 | 4 | /// A register number. 5 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 6 | pub struct Register(pub u16); 7 | 8 | impl Register { 9 | /// The name of the register, if known. 10 | pub fn name(self, hash: &FileHash) -> Option<&'static str> { 11 | hash.file.get_register_name(self) 12 | } 13 | } 14 | 15 | /// A location within the stack frame. 16 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 17 | pub struct FrameLocation { 18 | /// The offset from the frame pointer. 19 | pub offset: i64, 20 | /// The size of the value in bits. 21 | pub bit_size: Size, 22 | } 23 | 24 | /// A piece of a value. 25 | /// 26 | /// The value of an object may be split into multiple pieces, each with a different location. 27 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] 28 | pub struct Piece { 29 | /// The offset of the piece within the containing object. 30 | pub bit_offset: u64, 31 | /// The size of the piece. If none, then the piece is the complete value. 32 | pub bit_size: Size, 33 | /// The location of the piece. 34 | pub location: Location, 35 | /// The offset of the piece within the location. 36 | pub location_offset: u64, 37 | /// If `true`, then the piece does not have a location. 38 | /// Instead, `location` is the value of the piece. 39 | pub is_value: bool, 40 | } 41 | 42 | /// A value location. 43 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 44 | pub enum Location { 45 | /// The value has been optimized away. 46 | Empty, 47 | /// A literal address or value. 48 | Literal { 49 | /// The literal address or value. 50 | value: u64, 51 | }, 52 | /// The value is stored in a register. 53 | Register { 54 | /// The register number. 55 | register: Register, 56 | }, 57 | /// The value is stored in memory at an offset from an address stored in a register. 58 | RegisterOffset { 59 | /// The register number. 60 | register: Register, 61 | /// The offset. 62 | offset: i64, 63 | }, 64 | /// The value is stored in memory at an offset from the frame base. 65 | FrameOffset { 66 | /// The offset. 67 | offset: i64, 68 | }, 69 | /// The value is stored in memory at an offset from the CFA. 70 | CfaOffset { 71 | /// The offset. 72 | offset: i64, 73 | }, 74 | /// The value is stored in memory at an address. This address may need relocation. 75 | Address { 76 | /// The offset. 77 | address: Address, 78 | }, 79 | /// The value is stored in memory at an offset within TLS. 80 | TlsOffset { 81 | /// The offset. 82 | offset: u64, 83 | }, 84 | /// The value is more complex than any of the above variants. 85 | Other, 86 | } 87 | 88 | pub(crate) fn registers( 89 | locations: &[(Range, Piece)], 90 | ) -> impl Iterator + '_ { 91 | locations.iter().filter_map(|(range, piece)| { 92 | if piece.is_value { 93 | return None; 94 | } 95 | match piece.location { 96 | Location::Register { register } => Some((*range, register)), 97 | _ => None, 98 | } 99 | }) 100 | } 101 | 102 | pub(crate) fn frame_locations( 103 | locations: &[(Range, Piece)], 104 | ) -> impl Iterator + '_ { 105 | locations.iter().filter_map(|(_, piece)| { 106 | if piece.is_value { 107 | return None; 108 | } 109 | match piece.location { 110 | // TODO: do we need to distinguish between these? 111 | Location::FrameOffset { offset } | Location::CfaOffset { offset } => { 112 | Some(FrameLocation { 113 | offset, 114 | bit_size: piece.bit_size, 115 | }) 116 | } 117 | _ => None, 118 | } 119 | }) 120 | } 121 | 122 | pub(crate) fn register_offsets( 123 | locations: &[(Range, Piece)], 124 | ) -> impl Iterator + '_ { 125 | locations.iter().filter_map(|(range, piece)| { 126 | if piece.is_value { 127 | return None; 128 | } 129 | match piece.location { 130 | Location::RegisterOffset { register, offset } => Some((*range, register, offset)), 131 | _ => None, 132 | } 133 | }) 134 | } 135 | -------------------------------------------------------------------------------- /parser/src/namespace.rs: -------------------------------------------------------------------------------- 1 | use std::cmp; 2 | use std::sync::Arc; 3 | 4 | /// A namespace kind. 5 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] 6 | pub enum NamespaceKind { 7 | /// An explicit namespace. 8 | Namespace, 9 | /// A namespace for items defined within a function. 10 | Function, 11 | /// A namespace for items defined within a type. 12 | Type, 13 | } 14 | 15 | /// A nestable namspace. 16 | #[derive(Debug)] 17 | pub struct Namespace<'input> { 18 | pub(crate) parent: Option>>, 19 | pub(crate) name: Option<&'input str>, 20 | pub(crate) kind: NamespaceKind, 21 | } 22 | 23 | impl<'input> Namespace<'input> { 24 | pub(crate) fn new( 25 | parent: &Option>>, 26 | name: Option<&'input str>, 27 | kind: NamespaceKind, 28 | ) -> Arc> { 29 | Arc::new(Namespace { 30 | parent: parent.clone(), 31 | name, 32 | kind, 33 | }) 34 | } 35 | 36 | /// The parent namespace. 37 | pub fn parent(&self) -> Option<&Namespace<'input>> { 38 | self.parent.as_deref() 39 | } 40 | 41 | /// The namespace name. 42 | #[inline] 43 | pub fn name(&self) -> Option<&'input str> { 44 | self.name 45 | } 46 | 47 | /// The namespace kind. 48 | #[inline] 49 | pub fn kind(&self) -> NamespaceKind { 50 | self.kind 51 | } 52 | 53 | fn len(&self) -> usize { 54 | match &self.parent { 55 | Some(parent) => parent.len() + 1, 56 | None => 1, 57 | } 58 | } 59 | 60 | fn up(&self, len: usize) -> &Namespace<'input> { 61 | if len == 0 { 62 | self 63 | } else { 64 | match &self.parent { 65 | Some(parent) => parent.up(len - 1), 66 | None => self, 67 | } 68 | } 69 | } 70 | 71 | pub(crate) fn is_anon_type(namespace: &Option>) -> bool { 72 | match namespace { 73 | Some(namespace) => { 74 | namespace.kind == NamespaceKind::Type 75 | && (namespace.name.is_none() || Namespace::is_anon_type(&namespace.parent)) 76 | } 77 | None => false, 78 | } 79 | } 80 | 81 | fn _is_within>(&self, namespace: &[T]) -> (bool, usize) { 82 | let (ret, offset) = match &self.parent { 83 | Some(parent) => parent._is_within(namespace), 84 | None => (true, 0), 85 | }; 86 | 87 | if ret { 88 | if offset < namespace.len() { 89 | match self.name() { 90 | Some(name) => (name == namespace[offset].as_ref(), offset + 1), 91 | None => (false, offset + 1), 92 | } 93 | } else { 94 | (true, offset) 95 | } 96 | } else { 97 | (false, 0) 98 | } 99 | } 100 | 101 | /// Return true if this namespace is within the given namespace. 102 | /// 103 | /// `namespace` is a slice of names, starting with the root namespace name. 104 | pub fn is_within>(&self, namespace: &[T]) -> bool { 105 | self._is_within(namespace) == (true, namespace.len()) 106 | } 107 | 108 | fn _cmp(a: &Namespace, b: &Namespace) -> cmp::Ordering { 109 | debug_assert_eq!(a.len(), b.len()); 110 | match (a.parent.as_ref(), b.parent.as_ref()) { 111 | (Some(p1), Some(p2)) => { 112 | let ord = Self::_cmp(p1, p2); 113 | if ord != cmp::Ordering::Equal { 114 | return ord; 115 | } 116 | } 117 | _ => {} 118 | } 119 | a.name.cmp(&b.name) 120 | } 121 | 122 | fn cmp(a: &Namespace, b: &Namespace) -> cmp::Ordering { 123 | let len_a = a.len(); 124 | let len_b = b.len(); 125 | match len_a.cmp(&len_b) { 126 | cmp::Ordering::Equal => Self::_cmp(a, b), 127 | cmp::Ordering::Less => { 128 | let b = b.up(len_b - len_a); 129 | match Self::_cmp(a, b) { 130 | cmp::Ordering::Equal => cmp::Ordering::Less, 131 | other => other, 132 | } 133 | } 134 | cmp::Ordering::Greater => { 135 | let a = a.up(len_a - len_b); 136 | match Self::_cmp(a, b) { 137 | cmp::Ordering::Equal => cmp::Ordering::Greater, 138 | other => other, 139 | } 140 | } 141 | } 142 | } 143 | 144 | pub(crate) fn cmp_ns_and_name( 145 | ns1: Option<&Namespace>, 146 | name1: Option<&str>, 147 | ns2: Option<&Namespace>, 148 | name2: Option<&str>, 149 | ) -> cmp::Ordering { 150 | match (ns1, ns2) { 151 | (Some(ns1), Some(ns2)) => match Namespace::cmp(ns1, ns2) { 152 | cmp::Ordering::Equal => name1.cmp(&name2), 153 | o => o, 154 | }, 155 | (Some(_), None) => cmp::Ordering::Greater, 156 | (None, Some(_)) => cmp::Ordering::Less, 157 | (None, None) => name1.cmp(&name2), 158 | } 159 | } 160 | } 161 | 162 | #[cfg(test)] 163 | mod test { 164 | use super::*; 165 | 166 | #[test] 167 | fn cmp() { 168 | let ns1 = Namespace::new(&None, Some("a".into()), NamespaceKind::Namespace); 169 | let ns2 = Namespace::new(&None, Some("b".into()), NamespaceKind::Namespace); 170 | assert_eq!(Namespace::cmp(&ns1, &ns2), cmp::Ordering::Less); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /parser/src/range.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | 3 | /// An address range. 4 | #[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 5 | pub struct Range { 6 | /// The beginning of the address range (inclusive). 7 | pub begin: u64, 8 | 9 | /// The end of the address range (exclusive). 10 | pub end: u64, 11 | } 12 | 13 | impl Range { 14 | /// A range that covers everything. 15 | pub fn all() -> Self { 16 | Range { begin: 0, end: !0 } 17 | } 18 | 19 | /// The size of the address range. 20 | #[inline] 21 | pub fn size(&self) -> u64 { 22 | self.end - self.begin 23 | } 24 | 25 | /// Return true if the range contains the value. 26 | #[inline] 27 | pub fn contains(&self, addr: u64) -> bool { 28 | self.begin <= addr && addr < self.end 29 | } 30 | } 31 | 32 | /// A list of address ranges. 33 | #[derive(Debug, Default, Clone)] 34 | pub struct RangeList { 35 | ranges: Vec, 36 | } 37 | 38 | impl RangeList { 39 | /// The ranges in the list. 40 | #[inline] 41 | pub fn list(&self) -> &[Range] { 42 | &self.ranges 43 | } 44 | 45 | /// The total size of the ranges in the list. 46 | pub fn size(&self) -> u64 { 47 | let mut size = 0; 48 | for range in &self.ranges { 49 | size += range.size(); 50 | } 51 | size 52 | } 53 | 54 | /// Append a range, combining with previous range if possible. 55 | pub fn push(&mut self, range: Range) { 56 | if range.end <= range.begin { 57 | debug!("invalid range: {:?}", range); 58 | return; 59 | } 60 | if let Some(prev) = self.ranges.last_mut() { 61 | // Assume up to 15 bytes of padding if range.begin is aligned. 62 | // (This may be a wrong assumption, but does it matter and 63 | // how do we do better?) 64 | // TODO: make alignment configurable 65 | let padding = if range.begin == range.begin & !15 { 66 | 15 67 | } else { 68 | 0 69 | }; 70 | // Merge ranges if new range begins in or after previous range. 71 | // We don't care about merging in opposite order (that'll happen 72 | // when sorting). 73 | if range.begin >= prev.begin && range.begin <= prev.end + padding { 74 | if prev.end < range.end { 75 | prev.end = range.end; 76 | } 77 | return; 78 | } 79 | } 80 | self.ranges.push(range); 81 | } 82 | 83 | /// Sort the ranges by beginning address, and combine ranges where possible. 84 | pub fn sort(&mut self) { 85 | self.ranges.sort_by(|a, b| a.begin.cmp(&b.begin)); 86 | // Combine ranges by adding to a new list. 87 | let mut ranges = Vec::new(); 88 | mem::swap(&mut ranges, &mut self.ranges); 89 | for range in ranges { 90 | self.push(range); 91 | } 92 | } 93 | 94 | /// Remove a list of ranges from the list. 95 | /// 96 | /// This handles ranges that only partially overlap with existing ranges. 97 | pub fn subtract(&self, other: &Self) -> Self { 98 | let mut ranges = RangeList::default(); 99 | let mut other_ranges = other.ranges.iter(); 100 | let mut other_range = other_ranges.next(); 101 | for range in &*self.ranges { 102 | let mut range = *range; 103 | loop { 104 | match other_range { 105 | Some(r) => { 106 | // Is r completely before range? 107 | if r.end <= range.begin { 108 | other_range = other_ranges.next(); 109 | continue; 110 | } 111 | // Is r completely after range? 112 | if r.begin >= range.end { 113 | ranges.push(range); 114 | break; 115 | } 116 | // Do we need to keep the head of the range? 117 | if r.begin > range.begin { 118 | ranges.push(Range { 119 | begin: range.begin, 120 | end: r.begin, 121 | }); 122 | } 123 | // Do we need to keep the tail of the range? 124 | if r.end < range.end { 125 | range.begin = r.end; 126 | other_range = other_ranges.next(); 127 | continue; 128 | } 129 | break; 130 | } 131 | None => { 132 | ranges.push(range); 133 | break; 134 | } 135 | } 136 | } 137 | } 138 | ranges.sort(); 139 | ranges 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /parser/src/source.rs: -------------------------------------------------------------------------------- 1 | use crate::unit::Unit; 2 | 3 | /// A source location. 4 | #[derive(Debug, Default, Clone)] 5 | pub struct Source<'input> { 6 | pub(crate) directory: Option<&'input str>, 7 | pub(crate) file: Option<&'input str>, 8 | pub(crate) line: u32, 9 | pub(crate) column: u32, 10 | } 11 | 12 | impl<'input> Source<'input> { 13 | /// The directory. 14 | /// 15 | /// This may be absolute, or relative to the working directory of the unit. 16 | #[inline] 17 | pub fn directory(&self) -> Option<&'input str> { 18 | self.directory 19 | } 20 | 21 | /// The file name. 22 | #[inline] 23 | pub fn file(&self) -> Option<&'input str> { 24 | self.file 25 | } 26 | 27 | /// Return true if there is no file name. 28 | #[inline] 29 | pub fn is_none(&self) -> bool { 30 | self.file.is_none() 31 | } 32 | 33 | /// Return true if there is a file name. 34 | #[inline] 35 | pub fn is_some(&self) -> bool { 36 | self.file.is_some() 37 | } 38 | 39 | /// The complete file path. 40 | pub fn path(&self, unit: &Unit) -> Option { 41 | fn is_absolute(directory: &str) -> bool { 42 | directory.get(0..1) == Some("/") || directory.get(1..2) == Some(":") 43 | } 44 | 45 | self.file().map(|file| { 46 | let mut path = String::new(); 47 | if let Some(directory) = self.directory() { 48 | if let (false, Some(unit_dir)) = (is_absolute(directory), unit.dir()) { 49 | path.push_str(unit_dir); 50 | if !unit_dir.ends_with('/') { 51 | path.push('/'); 52 | } 53 | } 54 | path.push_str(directory); 55 | if !directory.ends_with('/') { 56 | path.push('/'); 57 | } 58 | } 59 | path.push_str(file); 60 | path 61 | }) 62 | } 63 | 64 | /// The source line number. 65 | /// 66 | /// 0 means unknown line number. 67 | #[inline] 68 | pub fn line(&self) -> u32 { 69 | self.line 70 | } 71 | 72 | /// The source column number. 73 | /// 74 | /// 0 means unknown column number. 75 | #[inline] 76 | pub fn column(&self) -> u32 { 77 | self.column 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /parser/src/unit.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | use crate::file::FileHash; 4 | use crate::function::Function; 5 | use crate::range::RangeList; 6 | use crate::types::Type; 7 | use crate::variable::Variable; 8 | use crate::Id; 9 | 10 | /// A compilation unit. 11 | #[derive(Debug, Default)] 12 | pub struct Unit<'input> { 13 | pub(crate) id: Id, 14 | pub(crate) dir: Option>, 15 | pub(crate) name: Option>, 16 | pub(crate) language: Option, 17 | pub(crate) low_pc: Option, 18 | pub(crate) ranges: RangeList, 19 | pub(crate) types: Vec>, 20 | pub(crate) functions: Vec>, 21 | pub(crate) variables: Vec>, 22 | } 23 | 24 | impl<'input> Unit<'input> { 25 | /// The user defined id for this type. 26 | #[inline] 27 | pub fn id(&self) -> usize { 28 | self.id.get() 29 | } 30 | 31 | /// Set a user defined id for this type. 32 | #[inline] 33 | pub fn set_id(&self, id: usize) { 34 | self.id.set(id) 35 | } 36 | 37 | /// The working directory when the unit was compiled. 38 | pub fn dir(&self) -> Option<&str> { 39 | self.dir.as_deref() 40 | } 41 | 42 | /// The path of the primary source file. 43 | pub fn name(&self) -> Option<&str> { 44 | self.name.as_deref() 45 | } 46 | 47 | /// The source language. 48 | // TODO: avoid gimli dependency. 49 | #[inline] 50 | pub fn language(&self) -> Option { 51 | self.language 52 | } 53 | 54 | /// The base address. 55 | #[inline] 56 | pub fn address(&self) -> Option { 57 | self.low_pc 58 | } 59 | 60 | /// The address ranges covered by functions and variables in the unit. 61 | /// 62 | /// Does not include unknown ranges. 63 | pub fn ranges(&self, hash: &FileHash) -> RangeList { 64 | let mut ranges = RangeList::default(); 65 | for function in &self.functions { 66 | for range in function.ranges() { 67 | ranges.push(*range); 68 | } 69 | } 70 | for variable in &self.variables { 71 | if let Some(range) = variable.range(hash) { 72 | ranges.push(range); 73 | } 74 | } 75 | ranges.sort(); 76 | ranges 77 | } 78 | 79 | /// The address ranges covered that are covered by the unit, but which 80 | /// are not known to be associated with any functions or variables. 81 | pub fn unknown_ranges(&self, hash: &FileHash) -> RangeList { 82 | let mut ranges = RangeList::default(); 83 | for range in self.ranges.list() { 84 | ranges.push(*range); 85 | } 86 | ranges.sort(); 87 | ranges.subtract(&self.ranges(hash)) 88 | } 89 | 90 | /// The total size of all functions and variables. 91 | pub fn size(&self, hash: &FileHash) -> u64 { 92 | // TODO: account for padding and overlap between functions and variables? 93 | self.function_size() + self.variable_size(hash) 94 | } 95 | 96 | /// The total size of all functions. 97 | pub fn function_size(&self) -> u64 { 98 | let mut ranges = RangeList::default(); 99 | for function in &self.functions { 100 | for range in function.ranges() { 101 | ranges.push(*range); 102 | } 103 | } 104 | ranges.sort(); 105 | ranges.size() 106 | } 107 | 108 | /// The total size of all variables. 109 | pub fn variable_size(&self, hash: &FileHash) -> u64 { 110 | let mut ranges = RangeList::default(); 111 | for variable in &self.variables { 112 | if let Some(range) = variable.range(hash) { 113 | ranges.push(range); 114 | } 115 | } 116 | ranges.sort(); 117 | ranges.size() 118 | } 119 | 120 | /// The types declared or defined by this unit. 121 | #[inline] 122 | pub fn types(&self) -> &[Type<'input>] { 123 | &self.types 124 | } 125 | 126 | /// The functions declared or defined by this unit. 127 | #[inline] 128 | pub fn functions(&self) -> &[Function<'input>] { 129 | &self.functions 130 | } 131 | 132 | /// The variables declared or defined by this unit. 133 | #[inline] 134 | pub fn variables(&self) -> &[Variable<'input>] { 135 | &self.variables 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /parser/src/variable.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | use std::cmp; 3 | use std::sync::Arc; 4 | 5 | use crate::file::FileHash; 6 | use crate::location::{self, FrameLocation, Location, Piece, Register}; 7 | use crate::namespace::Namespace; 8 | use crate::range::Range; 9 | use crate::source::Source; 10 | use crate::types::{Type, TypeOffset}; 11 | use crate::{Address, Id, Size}; 12 | 13 | /// The debuginfo offset of a variable. 14 | /// 15 | /// This is unique for all variables in a file. 16 | #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 17 | pub struct VariableOffset(usize); 18 | 19 | impl VariableOffset { 20 | #[inline] 21 | pub(crate) fn new(offset: usize) -> VariableOffset { 22 | debug_assert!(VariableOffset(offset) != VariableOffset::none()); 23 | VariableOffset(offset) 24 | } 25 | 26 | #[inline] 27 | pub(crate) fn none() -> VariableOffset { 28 | VariableOffset(usize::MAX) 29 | } 30 | } 31 | 32 | impl Default for VariableOffset { 33 | #[inline] 34 | fn default() -> Self { 35 | VariableOffset::none() 36 | } 37 | } 38 | 39 | /// A global variable. 40 | #[derive(Debug, Default)] 41 | pub struct Variable<'input> { 42 | pub(crate) id: Id, 43 | pub(crate) offset: VariableOffset, 44 | pub(crate) namespace: Option>>, 45 | pub(crate) name: Option<&'input str>, 46 | pub(crate) linkage_name: Option<&'input str>, 47 | pub(crate) symbol_name: Option<&'input str>, 48 | pub(crate) ty: TypeOffset, 49 | pub(crate) source: Source<'input>, 50 | pub(crate) address: Address, 51 | pub(crate) size: Size, 52 | pub(crate) declaration: bool, 53 | } 54 | 55 | impl<'input> Variable<'input> { 56 | /// The user defined id for this variable. 57 | #[inline] 58 | pub fn id(&self) -> usize { 59 | self.id.get() 60 | } 61 | 62 | /// Set a user defined id for this variable. 63 | #[inline] 64 | pub fn set_id(&self, id: usize) { 65 | self.id.set(id) 66 | } 67 | 68 | /// The namespace of the variable. 69 | pub fn namespace(&self) -> Option<&Namespace<'input>> { 70 | self.namespace.as_deref() 71 | } 72 | 73 | /// The name of the variable. 74 | #[inline] 75 | pub fn name(&self) -> Option<&'input str> { 76 | self.name 77 | } 78 | 79 | /// The linkage name of the variable. 80 | #[inline] 81 | pub fn linkage_name(&self) -> Option<&'input str> { 82 | self.linkage_name 83 | } 84 | 85 | /// The symbol name of the variable. 86 | /// 87 | /// This is determined from a symbol table entry with a matching address. 88 | #[inline] 89 | pub fn symbol_name(&self) -> Option<&'input str> { 90 | self.symbol_name 91 | } 92 | 93 | /// The type of the variable. 94 | /// 95 | /// Returns `None` if the type is invalid. 96 | #[inline] 97 | pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option>> { 98 | Type::from_offset(hash, self.ty) 99 | } 100 | 101 | /// The source information for the variable. 102 | #[inline] 103 | pub fn source(&self) -> &Source<'input> { 104 | &self.source 105 | } 106 | 107 | /// The address of the variable. 108 | #[inline] 109 | pub fn address(&self) -> Option { 110 | self.address.get() 111 | } 112 | 113 | /// The size in bytes of the variable. 114 | pub fn byte_size(&self, hash: &FileHash) -> Option { 115 | if self.size.is_some() { 116 | self.size.get() 117 | } else { 118 | self.ty(hash).and_then(|t| t.byte_size(hash)) 119 | } 120 | } 121 | 122 | /// The address range of the variable. 123 | pub fn range(&self, hash: &FileHash) -> Option { 124 | match (self.address(), self.byte_size(hash)) { 125 | (Some(begin), Some(size)) => { 126 | if size != 0 { 127 | Some(Range { 128 | begin, 129 | end: begin + size, 130 | }) 131 | } else { 132 | None 133 | } 134 | } 135 | _ => None, 136 | } 137 | } 138 | 139 | /// Return true if this is a declaration. 140 | #[inline] 141 | pub fn is_declaration(&self) -> bool { 142 | self.declaration 143 | } 144 | 145 | /// Compare the identifying information of two variables. 146 | /// 147 | /// Variables are equal if they have the same namespace and name. 148 | /// 149 | /// This can be used to sort, and to determine if two variables refer to the same definition 150 | /// (even if there are differences in the definitions). 151 | pub fn cmp_id( 152 | _hash_a: &FileHash, 153 | a: &Variable, 154 | _hash_b: &FileHash, 155 | b: &Variable, 156 | ) -> cmp::Ordering { 157 | Namespace::cmp_ns_and_name(a.namespace(), a.name(), b.namespace(), b.name()) 158 | } 159 | } 160 | 161 | /// A local variable. 162 | #[derive(Debug, Default, Clone)] 163 | pub struct LocalVariable<'input> { 164 | pub(crate) offset: VariableOffset, 165 | pub(crate) name: Option<&'input str>, 166 | pub(crate) ty: TypeOffset, 167 | pub(crate) source: Source<'input>, 168 | pub(crate) address: Address, 169 | pub(crate) size: Size, 170 | pub(crate) locations: Vec<(Range, Piece)>, 171 | } 172 | 173 | impl<'input> LocalVariable<'input> { 174 | /// The name of the variable. 175 | #[inline] 176 | pub fn name(&self) -> Option<&'input str> { 177 | self.name 178 | } 179 | 180 | /// The type offset of the variable. 181 | /// 182 | /// A type offset is unique for all types in a file. 183 | #[inline] 184 | pub fn type_offset(&self) -> TypeOffset { 185 | self.ty 186 | } 187 | 188 | /// The type of the variable. 189 | /// 190 | /// Returns `None` if the type is invalid. 191 | #[inline] 192 | pub fn ty<'a>(&self, hash: &'a FileHash<'input>) -> Option>> { 193 | Type::from_offset(hash, self.ty) 194 | } 195 | 196 | /// The source information for the variable. 197 | #[inline] 198 | pub fn source(&self) -> &Source<'input> { 199 | &self.source 200 | } 201 | 202 | /// The address of the variable. 203 | /// 204 | /// This will only be known for static variables. 205 | #[inline] 206 | pub fn address(&self) -> Option { 207 | self.address.get() 208 | } 209 | 210 | /// The size in bytes of the variable. 211 | pub fn byte_size(&self, hash: &FileHash) -> Option { 212 | if self.size.is_some() { 213 | self.size.get() 214 | } else { 215 | self.ty(hash).and_then(|t| t.byte_size(hash)) 216 | } 217 | } 218 | 219 | /// A list of all locations where this variable is stored. 220 | pub fn locations(&self) -> &[(Range, Piece)] { 221 | &self.locations 222 | } 223 | 224 | /// The registers in which this variable is stored. 225 | pub fn registers(&self) -> impl Iterator + '_ { 226 | location::registers(&self.locations) 227 | } 228 | 229 | /// The registers pointing to where this variable is stored. 230 | pub fn register_offsets(&self) -> impl Iterator + '_ { 231 | location::register_offsets(&self.locations) 232 | } 233 | 234 | /// The stack frame locations at which this variable is stored. 235 | pub fn frame_locations(&self) -> impl Iterator + '_ { 236 | self.locations.iter().filter_map(|(_, piece)| { 237 | if piece.is_value { 238 | return None; 239 | } 240 | match piece.location { 241 | // TODO: do we need to distinguish between these? 242 | Location::FrameOffset { offset } | Location::CfaOffset { offset } => { 243 | Some(FrameLocation { 244 | offset, 245 | bit_size: piece.bit_size, 246 | }) 247 | } 248 | _ => None, 249 | } 250 | }) 251 | } 252 | 253 | /// Compare the identifying information of two variables. 254 | /// 255 | /// Variables are considered equal if their names are equal. 256 | /// 257 | /// This can be used to sort, and to determine if two variables refer to the same definition 258 | /// (even if there are differences in the definitions). 259 | pub fn cmp_id(_hash_a: &FileHash, a: &Self, _hash_b: &FileHash, b: &Self) -> cmp::Ordering { 260 | a.name.cmp(&b.name) 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | #use_small_heuristics = false 2 | --------------------------------------------------------------------------------