backtrace-0.3.44/.github/workflows/main.yml010066400017500001750000000155761361377145500170420ustar0000000000000000name: CI on: push: branches: - master pull_request: branches: - master jobs: test: name: Test runs-on: ${{ matrix.os }} strategy: matrix: thing: [stable, beta, nightly, macos, windows-msvc64, windows-msvc32, windows-gnu64, windows-gnu32] include: - thing: stable os: ubuntu-latest rust: stable - thing: beta os: ubuntu-latest rust: beta - thing: nightly os: ubuntu-latest rust: nightly - thing: macos os: macos-latest rust: stable # Note that these are on nightly due to rust-lang/rust#63700 not being # on stable yet - thing: windows-msvc64 os: windows-latest rust: nightly-x86_64-msvc - thing: windows-msvc32 os: windows-latest rust: nightly-i686-msvc - thing: windows-gnu64 os: windows-latest rust: nightly-x86_64-gnu - thing: windows-gnu32 os: windows-latest rust: nightly-i686-gnu steps: - uses: actions/checkout@v1 with: submodules: true - name: Install Rust (rustup) run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }} shell: bash # full fidelity of backtraces on 32-bit msvc requires frame pointers, so # enable that for our tests - name: Force frame pointers run: echo "##[set-env name=RUSTFLAGS]-Cforce-frame-pointers" shell: bash if: matrix.thing == 'windows-msvc32' - run: cargo build --manifest-path crates/backtrace-sys/Cargo.toml - run: cargo build - run: cargo test - run: cargo test --features "gimli-symbolize" - run: cargo test --no-default-features - run: cargo test --no-default-features --features "std" - run: cargo test --no-default-features --features "libunwind std" - run: cargo test --no-default-features --features "libunwind dladdr std" - run: cargo test --no-default-features --features "libunwind libbacktrace std" - run: cargo test --no-default-features --features "libunwind libbacktrace dbghelp std" - run: cargo test --no-default-features --features "libunwind coresymbolication dbghelp std" - run: cargo test --no-default-features --features "libunwind coresymbolication dbghelp std libbacktrace" - run: cargo test --no-default-features --features "unix-backtrace std" - run: cargo test --no-default-features --features "unix-backtrace dladdr std" - run: cargo test --no-default-features --features "unix-backtrace libbacktrace std" - run: cargo test --no-default-features --features "unix-backtrace coresymbolication std" - run: cargo test --no-default-features --features "serialize-serde std" - run: cargo test --no-default-features --features "serialize-rustc std" - run: cargo test --no-default-features --features "serialize-rustc serialize-serde std" - run: cargo test --no-default-features --features "cpp_demangle std" - run: cargo test --no-default-features --features "dbghelp std" - run: cargo test --no-default-features --features "dbghelp std verify-winapi" - run: cargo test --manifest-path crates/cpp_smoke_test/Cargo.toml - run: cargo test --features libbacktrace --manifest-path crates/without_debuginfo/Cargo.toml - run: cargo test --features "libbacktrace coresymbolication" --manifest-path crates/without_debuginfo/Cargo.toml - run: cargo test --features "libbacktrace gimli-symbolize" --manifest-path crates/without_debuginfo/Cargo.toml windows_arm64: name: Windows AArch64 runs-on: windows-latest steps: - uses: actions/checkout@v1 with: submodules: true - name: Install Rust run: rustup update stable --no-self-update && rustup default stable shell: bash - run: rustup target add aarch64-pc-windows-msvc - run: cargo test --no-run --target aarch64-pc-windows-msvc - run: cargo test --no-run --target aarch64-pc-windows-msvc --features verify-winapi ios: name: iOS runs-on: macos-latest strategy: matrix: target: - aarch64-apple-ios - armv7-apple-ios - armv7s-apple-ios - i386-apple-ios - x86_64-apple-ios include: - target: aarch64-apple-ios sdk: iphoneos - target: armv7-apple-ios sdk: iphoneos - target: armv7s-apple-ios sdk: iphoneos - target: i386-apple-ios sdk: iphonesimulator - target: x86_64-apple-ios sdk: iphonesimulator steps: - uses: actions/checkout@v1 with: submodules: true - name: Install Rust ( run: | curl https://sh.rustup.rs | sh -s -- -y echo "##[add-path]$HOME/.cargo/bin" - run: rustup target add ${{ matrix.target }} - run: | export SDK_PATH=`xcrun --show-sdk-path --sdk ${{ matrix.sdk }}` export RUSTFLAGS="-C link-arg=-isysroot -C link-arg=$SDK_PATH" cargo test --no-run --target ${{ matrix.target }} name: Build tests docker: name: Docker runs-on: ubuntu-latest strategy: matrix: target: - aarch64-unknown-linux-gnu - arm-unknown-linux-gnueabihf - armv7-unknown-linux-gnueabihf - i586-unknown-linux-gnu - i686-unknown-linux-gnu - powerpc64-unknown-linux-gnu - x86_64-pc-windows-gnu - x86_64-unknown-linux-gnu - x86_64-unknown-linux-musl - arm-linux-androideabi - armv7-linux-androideabi - aarch64-linux-android - i686-linux-android - x86_64-linux-android steps: - uses: actions/checkout@v1 with: submodules: true - name: Install Rust run: rustup update stable && rustup default stable - run: rustup target add ${{ matrix.target }} - run: cargo generate-lockfile - run: ./ci/run-docker.sh ${{ matrix.target }} rustfmt: name: Rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 with: submodules: true - name: Install Rust run: rustup update stable && rustup default stable && rustup component add rustfmt - run: cargo fmt --all -- --check build: name: Build Targets runs-on: ubuntu-latest strategy: matrix: target: [wasm32-unknown-unknown, wasm32-wasi, x86_64-fuchsia, x86_64-fortanix-unknown-sgx] steps: - uses: actions/checkout@v1 with: submodules: true - name: Install Rust run: rustup update nightly && rustup default nightly - run: rustup target add ${{ matrix.target }} - run: cargo build --target ${{ matrix.target }} msrv: name: MSRV runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 with: submodules: true - name: Install Rust run: rustup update 1.32.0 && rustup default 1.32.0 - run: cargo build backtrace-0.3.44/.gitignore010066400017500001750000000000221360710306200137240ustar0000000000000000target Cargo.lock backtrace-0.3.44/.gitmodules010066400017500001750000000002241360710306200141150ustar0000000000000000[submodule "backtrace-sys/src/libbacktrace"] path = crates/backtrace-sys/src/libbacktrace url = https://github.com/rust-lang-nursery/libbacktrace backtrace-0.3.44/Cargo.toml.orig010066400017500001750000000125131362056464300146460ustar0000000000000000[package] name = "backtrace" version = "0.3.44" authors = ["The Rust Project Developers"] license = "MIT/Apache-2.0" readme = "README.md" repository = "https://github.com/rust-lang/backtrace-rs" homepage = "https://github.com/rust-lang/backtrace-rs" documentation = "https://docs.rs/backtrace" description = """ A library to acquire a stack trace (backtrace) at runtime in a Rust program. """ autoexamples = true autotests = true edition = "2018" [workspace] members = ['crates/cpp_smoke_test'] exclude = ['crates/without_debuginfo'] [dependencies] cfg-if = "0.1.10" rustc-demangle = "0.1.4" backtrace-sys = { path = "crates/backtrace-sys", version = "0.1.32", optional = true } libc = { version = "0.2.45", default-features = false } core = { version = "1.0.0", optional = true, package = 'rustc-std-workspace-core' } compiler_builtins = { version = '0.1.2', optional = true } # Optionally enable the ability to serialize a `Backtrace` serde = { version = "1.0", optional = true, features = ['derive'] } rustc-serialize = { version = "0.3", optional = true } # Optionally demangle C++ frames' symbols in backtraces. cpp_demangle = { default-features = false, version = "0.2.3", optional = true } # Optional dependencies enabled through the `gimli-symbolize` feature addr2line = { version = "0.11.0", optional = true, default-features = false, features = ['std'] } findshlibs = { version = "0.5.0", optional = true } memmap = { version = "0.7.0", optional = true } goblin = { version = "0.1.3", optional = true, default-features = false, features = ['elf32', 'elf64', 'mach32', 'mach64', 'pe32', 'pe64', 'std'] } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.3", optional = true } # Each feature controls the two phases of finding a backtrace: getting a # backtrace and then resolving instruction pointers to symbols. The default # feature enables all the necessary features for each platform this library # supports, but it can be disabled to have finer grained control over the # dependencies. # # Note that not all features are available on all platforms, so even though a # feature is enabled some other feature may be used instead. [features] default = ["std", "libunwind", "libbacktrace", "dladdr", "dbghelp"] # Include std support. std = [] #======================================= # Methods of acquiring a backtrace # # - libunwind: when using this the libgcc library is linked against to get # the unwinding support. This is generally the most reliable method to get # a backtrace on unix. # - unix-backtrace: this uses the backtrace(3) function to acquire a # backtrace, but is not as reliable as libunwind. It is, however, # generally found in more locations. # - dbghelp: on windows this enables usage of dbghelp.dll to find a # backtrace at runtime # - kernel32: on windows this enables using RtlCaptureStackBackTrace as the # function to acquire a backtrace libunwind = [] unix-backtrace = [] dbghelp = [] kernel32 = [] #======================================= # Methods of resolving symbols # # - libbacktrace: this feature activates the `backtrace-sys` dependency, # building the libbacktrace library found in gcc repos. This library # parses the DWARF info of ELF executables to find symbol names, and it # can also provide filename/line number information if debuginfo is # compiled in. This library currently only primarily works on unixes that # are not OSX, however. # - dladdr: this feature uses the dladdr(3) function (a glibc extension) to # resolve symbol names. This is fairly unreliable on linux, but works well # enough on OSX. # - coresymbolication: this feature uses the undocumented core symbolication # framework on OS X to symbolize. Note that this is not turned on default due # to various issues, you can see more in #202. # - gimli-symbolize: use the `gimli-rs/addr2line` crate to symbolicate # addresses into file, line, and name using DWARF debug information. At # the moment, this is only possible when targetting Linux, since macOS # splits DWARF out into a separate object file. Enabling this feature # means one less C dependency. libbacktrace = ["backtrace-sys"] dladdr = [] coresymbolication = [] gimli-symbolize = ["addr2line", "findshlibs", "memmap", "goblin"] #======================================= # Methods of serialization # # Various features used for enabling rustc-serialize or syntex codegen. serialize-rustc = ["rustc-serialize"] serialize-serde = ["serde"] #======================================= # Internal features for testing and such. verify-winapi = [ 'winapi/dbghelp', 'winapi/handleapi', 'winapi/libloaderapi', 'winapi/minwindef', 'winapi/processthreadsapi', 'winapi/synchapi', 'winapi/winbase', 'winapi/winnt', ] rustc-dep-of-std = [ 'backtrace-sys/rustc-dep-of-std', 'cfg-if/rustc-dep-of-std', 'core', 'compiler_builtins', 'libc/rustc-dep-of-std', 'rustc-demangle/rustc-dep-of-std', ] [[example]] name = "backtrace" required-features = ["std"] [[example]] name = "raw" required-features = ["std"] [[test]] name = "skip_inner_frames" required-features = ["std"] [[test]] name = "long_fn_name" required-features = ["std"] [[test]] name = "smoke" required-features = ["std"] edition = '2018' [[test]] name = "accuracy" required-features = ["std", "dbghelp", "libbacktrace", "libunwind"] edition = '2018' [[test]] name = "concurrent-panics" required-features = ["std"] harness = false backtrace-0.3.44/Cargo.toml0000644000000062431362056464700111610ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "backtrace" version = "0.3.44" authors = ["The Rust Project Developers"] autoexamples = true autotests = true description = "A library to acquire a stack trace (backtrace) at runtime in a Rust program.\n" homepage = "https://github.com/rust-lang/backtrace-rs" documentation = "https://docs.rs/backtrace" readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/rust-lang/backtrace-rs" [[example]] name = "backtrace" required-features = ["std"] [[example]] name = "raw" required-features = ["std"] [[test]] name = "skip_inner_frames" required-features = ["std"] [[test]] name = "long_fn_name" required-features = ["std"] [[test]] name = "smoke" required-features = ["std"] edition = "2018" [[test]] name = "accuracy" required-features = ["std", "dbghelp", "libbacktrace", "libunwind"] edition = "2018" [[test]] name = "concurrent-panics" harness = false required-features = ["std"] [dependencies.addr2line] version = "0.11.0" features = ["std"] optional = true default-features = false [dependencies.backtrace-sys] version = "0.1.32" optional = true [dependencies.cfg-if] version = "0.1.10" [dependencies.compiler_builtins] version = "0.1.2" optional = true [dependencies.core] version = "1.0.0" optional = true package = "rustc-std-workspace-core" [dependencies.cpp_demangle] version = "0.2.3" optional = true default-features = false [dependencies.findshlibs] version = "0.5.0" optional = true [dependencies.goblin] version = "0.1.3" features = ["elf32", "elf64", "mach32", "mach64", "pe32", "pe64", "std"] optional = true default-features = false [dependencies.libc] version = "0.2.45" default-features = false [dependencies.memmap] version = "0.7.0" optional = true [dependencies.rustc-demangle] version = "0.1.4" [dependencies.rustc-serialize] version = "0.3" optional = true [dependencies.serde] version = "1.0" features = ["derive"] optional = true [features] coresymbolication = [] dbghelp = [] default = ["std", "libunwind", "libbacktrace", "dladdr", "dbghelp"] dladdr = [] gimli-symbolize = ["addr2line", "findshlibs", "memmap", "goblin"] kernel32 = [] libbacktrace = ["backtrace-sys"] libunwind = [] rustc-dep-of-std = ["backtrace-sys/rustc-dep-of-std", "cfg-if/rustc-dep-of-std", "core", "compiler_builtins", "libc/rustc-dep-of-std", "rustc-demangle/rustc-dep-of-std"] serialize-rustc = ["rustc-serialize"] serialize-serde = ["serde"] std = [] unix-backtrace = [] verify-winapi = ["winapi/dbghelp", "winapi/handleapi", "winapi/libloaderapi", "winapi/minwindef", "winapi/processthreadsapi", "winapi/synchapi", "winapi/winbase", "winapi/winnt"] [target."cfg(windows)".dependencies.winapi] version = "0.3.3" optional = true backtrace-0.3.44/Cargo.toml.orig0000644000000125131362056464700121150ustar00[package] name = "backtrace" version = "0.3.44" authors = ["The Rust Project Developers"] license = "MIT/Apache-2.0" readme = "README.md" repository = "https://github.com/rust-lang/backtrace-rs" homepage = "https://github.com/rust-lang/backtrace-rs" documentation = "https://docs.rs/backtrace" description = """ A library to acquire a stack trace (backtrace) at runtime in a Rust program. """ autoexamples = true autotests = true edition = "2018" [workspace] members = ['crates/cpp_smoke_test'] exclude = ['crates/without_debuginfo'] [dependencies] cfg-if = "0.1.10" rustc-demangle = "0.1.4" backtrace-sys = { path = "crates/backtrace-sys", version = "0.1.32", optional = true } libc = { version = "0.2.45", default-features = false } core = { version = "1.0.0", optional = true, package = 'rustc-std-workspace-core' } compiler_builtins = { version = '0.1.2', optional = true } # Optionally enable the ability to serialize a `Backtrace` serde = { version = "1.0", optional = true, features = ['derive'] } rustc-serialize = { version = "0.3", optional = true } # Optionally demangle C++ frames' symbols in backtraces. cpp_demangle = { default-features = false, version = "0.2.3", optional = true } # Optional dependencies enabled through the `gimli-symbolize` feature addr2line = { version = "0.11.0", optional = true, default-features = false, features = ['std'] } findshlibs = { version = "0.5.0", optional = true } memmap = { version = "0.7.0", optional = true } goblin = { version = "0.1.3", optional = true, default-features = false, features = ['elf32', 'elf64', 'mach32', 'mach64', 'pe32', 'pe64', 'std'] } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.3", optional = true } # Each feature controls the two phases of finding a backtrace: getting a # backtrace and then resolving instruction pointers to symbols. The default # feature enables all the necessary features for each platform this library # supports, but it can be disabled to have finer grained control over the # dependencies. # # Note that not all features are available on all platforms, so even though a # feature is enabled some other feature may be used instead. [features] default = ["std", "libunwind", "libbacktrace", "dladdr", "dbghelp"] # Include std support. std = [] #======================================= # Methods of acquiring a backtrace # # - libunwind: when using this the libgcc library is linked against to get # the unwinding support. This is generally the most reliable method to get # a backtrace on unix. # - unix-backtrace: this uses the backtrace(3) function to acquire a # backtrace, but is not as reliable as libunwind. It is, however, # generally found in more locations. # - dbghelp: on windows this enables usage of dbghelp.dll to find a # backtrace at runtime # - kernel32: on windows this enables using RtlCaptureStackBackTrace as the # function to acquire a backtrace libunwind = [] unix-backtrace = [] dbghelp = [] kernel32 = [] #======================================= # Methods of resolving symbols # # - libbacktrace: this feature activates the `backtrace-sys` dependency, # building the libbacktrace library found in gcc repos. This library # parses the DWARF info of ELF executables to find symbol names, and it # can also provide filename/line number information if debuginfo is # compiled in. This library currently only primarily works on unixes that # are not OSX, however. # - dladdr: this feature uses the dladdr(3) function (a glibc extension) to # resolve symbol names. This is fairly unreliable on linux, but works well # enough on OSX. # - coresymbolication: this feature uses the undocumented core symbolication # framework on OS X to symbolize. Note that this is not turned on default due # to various issues, you can see more in #202. # - gimli-symbolize: use the `gimli-rs/addr2line` crate to symbolicate # addresses into file, line, and name using DWARF debug information. At # the moment, this is only possible when targetting Linux, since macOS # splits DWARF out into a separate object file. Enabling this feature # means one less C dependency. libbacktrace = ["backtrace-sys"] dladdr = [] coresymbolication = [] gimli-symbolize = ["addr2line", "findshlibs", "memmap", "goblin"] #======================================= # Methods of serialization # # Various features used for enabling rustc-serialize or syntex codegen. serialize-rustc = ["rustc-serialize"] serialize-serde = ["serde"] #======================================= # Internal features for testing and such. verify-winapi = [ 'winapi/dbghelp', 'winapi/handleapi', 'winapi/libloaderapi', 'winapi/minwindef', 'winapi/processthreadsapi', 'winapi/synchapi', 'winapi/winbase', 'winapi/winnt', ] rustc-dep-of-std = [ 'backtrace-sys/rustc-dep-of-std', 'cfg-if/rustc-dep-of-std', 'core', 'compiler_builtins', 'libc/rustc-dep-of-std', 'rustc-demangle/rustc-dep-of-std', ] [[example]] name = "backtrace" required-features = ["std"] [[example]] name = "raw" required-features = ["std"] [[test]] name = "skip_inner_frames" required-features = ["std"] [[test]] name = "long_fn_name" required-features = ["std"] [[test]] name = "smoke" required-features = ["std"] edition = '2018' [[test]] name = "accuracy" required-features = ["std", "dbghelp", "libbacktrace", "libunwind"] edition = '2018' [[test]] name = "concurrent-panics" required-features = ["std"] harness = false backtrace-0.3.44/LICENSE-APACHE010066400017500001750000000251371360710306200136760ustar0000000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. backtrace-0.3.44/LICENSE-MIT010066400017500001750000000020411360710306200133730ustar0000000000000000Copyright (c) 2014 Alex Crichton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. backtrace-0.3.44/README.md010066400017500001750000000040431360710306200132220ustar0000000000000000# backtrace-rs [Documentation](https://docs.rs/backtrace) A library for acquiring backtraces at runtime for Rust. This library aims to enhance the support of the standard library by providing a programmatic interface to work with, but it also supports simply easily printing the current backtrace like libstd's panics. ## Install ```toml [dependencies] backtrace = "0.3" ``` Note that this crate requires `cc` and `ar` to be present on Unix systems when `libbacktrace` is used (which is the default). For configuring C compilers see the [`cc` crate documentation](https://github.com/alexcrichton/cc-rs). ## Usage To simply capture a backtrace and defer dealing with it until a later time, you can use the top-level `Backtrace` type. ```rust extern crate backtrace; use backtrace::Backtrace; fn main() { let bt = Backtrace::new(); // do_some_work(); println!("{:?}", bt); } ``` If, however, you'd like more raw access to the actual tracing functionality, you can use the `trace` and `resolve` functions directly. ```rust extern crate backtrace; fn main() { backtrace::trace(|frame| { let ip = frame.ip(); let symbol_address = frame.symbol_address(); // Resolve this instruction pointer to a symbol name backtrace::resolve_frame(frame, |symbol| { if let Some(name) = symbol.name() { // ... } if let Some(filename) = symbol.filename() { // ... } }); true // keep going to the next frame }); } ``` # License This project is licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in backtrace-rs by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. backtrace-0.3.44/benches/benchmarks.rs010066400017500001750000000040261360710306200160360ustar0000000000000000#![feature(test)] extern crate test; extern crate backtrace; #[cfg(feature = "std")] use backtrace::Backtrace; #[bench] #[cfg(feature = "std")] fn trace(b: &mut test::Bencher) { #[inline(never)] fn the_function() { backtrace::trace(|frame| { let ip = frame.ip(); test::black_box(ip); true }); } b.iter(the_function); } #[bench] #[cfg(feature = "std")] fn trace_and_resolve_callback(b: &mut test::Bencher) { #[inline(never)] fn the_function() { backtrace::trace(|frame| { backtrace::resolve(frame.ip(), |symbol| { let addr = symbol.addr(); test::black_box(addr); }); true }); } b.iter(the_function); } #[bench] #[cfg(feature = "std")] fn trace_and_resolve_separate(b: &mut test::Bencher) { #[inline(never)] fn the_function(frames: &mut Vec<*mut std::ffi::c_void>) { backtrace::trace(|frame| { frames.push(frame.ip()); true }); frames.iter().for_each(|frame_ip| { backtrace::resolve(*frame_ip, |symbol| { test::black_box(symbol); }); }); } let mut frames = Vec::with_capacity(1024); b.iter(|| { the_function(&mut frames); frames.clear(); }); } #[bench] #[cfg(feature = "std")] fn new_unresolved(b: &mut test::Bencher) { #[inline(never)] fn the_function() { let bt = Backtrace::new_unresolved(); test::black_box(bt); } b.iter(the_function); } #[bench] #[cfg(feature = "std")] fn new(b: &mut test::Bencher) { #[inline(never)] fn the_function() { let bt = Backtrace::new(); test::black_box(bt); } b.iter(the_function); } #[bench] #[cfg(feature = "std")] fn new_unresolved_and_resolve_separate(b: &mut test::Bencher) { #[inline(never)] fn the_function() { let mut bt = Backtrace::new_unresolved(); bt.resolve(); test::black_box(bt); } b.iter(the_function); } backtrace-0.3.44/examples/backtrace.rs010066400017500001750000000001521360710306200160430ustar0000000000000000extern crate backtrace; use backtrace::Backtrace; fn main() { println!("{:?}", Backtrace::new()); } backtrace-0.3.44/examples/raw.rs010066400017500001750000000023101360710306200147130ustar0000000000000000extern crate backtrace; fn main() { foo(); } fn foo() { bar() } fn bar() { baz() } fn baz() { print() } #[cfg(target_pointer_width = "32")] const HEX_WIDTH: usize = 10; #[cfg(target_pointer_width = "64")] const HEX_WIDTH: usize = 20; fn print() { let mut cnt = 0; backtrace::trace(|frame| { let ip = frame.ip(); print!("frame #{:<2} - {:#02$x}", cnt, ip as usize, HEX_WIDTH); cnt += 1; let mut resolved = false; backtrace::resolve(frame.ip(), |symbol| { if !resolved { resolved = true; } else { print!("{}", vec![" "; 7 + 2 + 3 + HEX_WIDTH].join("")); } if let Some(name) = symbol.name() { print!(" - {}", name); } else { print!(" - "); } if let Some(file) = symbol.filename() { if let Some(l) = symbol.lineno() { print!("\n{:13}{:4$}@ {}:{}", "", "", file.display(), l, HEX_WIDTH); } } println!(""); }); if !resolved { println!(" - "); } true // keep going }); } backtrace-0.3.44/src/backtrace/dbghelp.rs010066400017500001750000000171251360766000700164400ustar0000000000000000// Copyright 2014 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Backtrace strategy for MSVC platforms. //! //! This module contains the ability to generate a backtrace on MSVC using one //! of two possible methods. The `StackWalkEx` function is primarily used if //! possible, but not all systems have that. Failing that the `StackWalk64` //! function is used instead. Note that `StackWalkEx` is favored because it //! handles debuginfo internally and returns inline frame information. //! //! Note that all dbghelp support is loaded dynamically, see `src/dbghelp.rs` //! for more information about that. #![allow(bad_style)] use crate::dbghelp; use crate::windows::*; use core::ffi::c_void; use core::mem; #[derive(Clone, Copy)] pub enum Frame { New(STACKFRAME_EX), Old(STACKFRAME64), } // we're just sending around raw pointers and reading them, never interpreting // them so this should be safe to both send and share across threads. unsafe impl Send for Frame {} unsafe impl Sync for Frame {} impl Frame { pub fn ip(&self) -> *mut c_void { self.addr_pc().Offset as *mut _ } pub fn symbol_address(&self) -> *mut c_void { self.ip() } fn addr_pc(&self) -> &ADDRESS64 { match self { Frame::New(new) => &new.AddrPC, Frame::Old(old) => &old.AddrPC, } } fn addr_pc_mut(&mut self) -> &mut ADDRESS64 { match self { Frame::New(new) => &mut new.AddrPC, Frame::Old(old) => &mut old.AddrPC, } } fn addr_frame_mut(&mut self) -> &mut ADDRESS64 { match self { Frame::New(new) => &mut new.AddrFrame, Frame::Old(old) => &mut old.AddrFrame, } } fn addr_stack_mut(&mut self) -> &mut ADDRESS64 { match self { Frame::New(new) => &mut new.AddrStack, Frame::Old(old) => &mut old.AddrStack, } } } #[repr(C, align(16))] // required by `CONTEXT`, is a FIXME in winapi right now struct MyContext(CONTEXT); #[inline(always)] pub unsafe fn trace(cb: &mut FnMut(&super::Frame) -> bool) { // Allocate necessary structures for doing the stack walk let process = GetCurrentProcess(); let thread = GetCurrentThread(); let mut context = mem::zeroed::(); RtlCaptureContext(&mut context.0); // Ensure this process's symbols are initialized let dbghelp = match dbghelp::init() { Ok(dbghelp) => dbghelp, Err(()) => return, // oh well... }; // On x86_64 and ARM64 we opt to not use the default `Sym*` functions from // dbghelp for getting the function table and module base. Instead we use // the `RtlLookupFunctionEntry` function in kernel32 which will account for // JIT compiler frames as well. These should be equivalent, but using // `Rtl*` allows us to backtrace through JIT frames. // // Note that `RtlLookupFunctionEntry` only works for in-process backtraces, // but that's all we support anyway, so it all lines up well. cfg_if::cfg_if! { if #[cfg(target_pointer_width = "64")] { use core::ptr; unsafe extern "system" fn function_table_access(_process: HANDLE, addr: DWORD64) -> PVOID { let mut base = 0; RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut()).cast() } unsafe extern "system" fn get_module_base(_process: HANDLE, addr: DWORD64) -> DWORD64 { let mut base = 0; RtlLookupFunctionEntry(addr, &mut base, ptr::null_mut()); base } } else { let function_table_access = dbghelp.SymFunctionTableAccess64(); let get_module_base = dbghelp.SymGetModuleBase64(); } } // Attempt to use `StackWalkEx` if we can, but fall back to `StackWalk64` // since it's in theory supported on more systems. match (*dbghelp.dbghelp()).StackWalkEx() { Some(StackWalkEx) => { let mut frame = super::Frame { inner: Frame::New(mem::zeroed()), }; let image = init_frame(&mut frame.inner, &context.0); let frame_ptr = match &mut frame.inner { Frame::New(ptr) => ptr as *mut STACKFRAME_EX, _ => unreachable!(), }; while StackWalkEx( image as DWORD, process, thread, frame_ptr, &mut context.0 as *mut CONTEXT as *mut _, None, Some(function_table_access), Some(get_module_base), None, 0, ) == TRUE { if !cb(&frame) { break; } } } None => { let mut frame = super::Frame { inner: Frame::Old(mem::zeroed()), }; let image = init_frame(&mut frame.inner, &context.0); let frame_ptr = match &mut frame.inner { Frame::Old(ptr) => ptr as *mut STACKFRAME64, _ => unreachable!(), }; while dbghelp.StackWalk64()( image as DWORD, process, thread, frame_ptr, &mut context.0 as *mut CONTEXT as *mut _, None, Some(function_table_access), Some(get_module_base), None, ) == TRUE { if !cb(&frame) { break; } } } } } #[cfg(target_arch = "x86_64")] fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD { frame.addr_pc_mut().Offset = ctx.Rip as u64; frame.addr_pc_mut().Mode = AddrModeFlat; frame.addr_stack_mut().Offset = ctx.Rsp as u64; frame.addr_stack_mut().Mode = AddrModeFlat; frame.addr_frame_mut().Offset = ctx.Rbp as u64; frame.addr_frame_mut().Mode = AddrModeFlat; IMAGE_FILE_MACHINE_AMD64 } #[cfg(target_arch = "x86")] fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD { frame.addr_pc_mut().Offset = ctx.Eip as u64; frame.addr_pc_mut().Mode = AddrModeFlat; frame.addr_stack_mut().Offset = ctx.Esp as u64; frame.addr_stack_mut().Mode = AddrModeFlat; frame.addr_frame_mut().Offset = ctx.Ebp as u64; frame.addr_frame_mut().Mode = AddrModeFlat; IMAGE_FILE_MACHINE_I386 } #[cfg(target_arch = "aarch64")] fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD { frame.addr_pc_mut().Offset = ctx.Pc as u64; frame.addr_pc_mut().Mode = AddrModeFlat; frame.addr_stack_mut().Offset = ctx.Sp as u64; frame.addr_stack_mut().Mode = AddrModeFlat; unsafe { frame.addr_frame_mut().Offset = ctx.u.s().Fp as u64; } frame.addr_frame_mut().Mode = AddrModeFlat; IMAGE_FILE_MACHINE_ARM64 } #[cfg(target_arch = "arm")] fn init_frame(frame: &mut Frame, ctx: &CONTEXT) -> WORD { frame.addr_pc_mut().Offset = ctx.Pc as u64; frame.addr_pc_mut().Mode = AddrModeFlat; frame.addr_stack_mut().Offset = ctx.Sp as u64; frame.addr_stack_mut().Mode = AddrModeFlat; unsafe { frame.addr_frame_mut().Offset = ctx.R11 as u64; } frame.addr_frame_mut().Mode = AddrModeFlat; IMAGE_FILE_MACHINE_ARMNT } backtrace-0.3.44/src/backtrace/libunwind.rs010066400017500001750000000172041360710306200170150ustar0000000000000000// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Backtrace support using libunwind/gcc_s/etc APIs. //! //! This module contains the ability to unwind the stack using libunwind-style //! APIs. Note that there's a whole bunch of implementations of the //! libunwind-like API, and this is just trying to be compatible with most of //! them all at once instead of being picky. //! //! The libunwind API is powered by `_Unwind_Backtrace` and is in practice very //! reliable at generating a backtrace. It's not entirely clear how it does it //! (frame pointers? eh_frame info? both?) but it seems to work! //! //! Most of the complexity of this module is handling the various platform //! differences across libunwind implementations. Otherwise this is a pretty //! straightforward Rust binding to the libunwind APIs. //! //! This is the default unwinding API for all non-Windows platforms currently. use core::ffi::c_void; pub enum Frame { Raw(*mut uw::_Unwind_Context), Cloned { ip: *mut c_void, symbol_address: *mut c_void, }, } // With a raw libunwind pointer it should only ever be access in a readonly // threadsafe fashion, so it's `Sync`. When sending to other threads via `Clone` // we always switch to a version which doesn't retain interior pointers, so we // should be `Send` as well. unsafe impl Send for Frame {} unsafe impl Sync for Frame {} impl Frame { pub fn ip(&self) -> *mut c_void { let ctx = match *self { Frame::Raw(ctx) => ctx, Frame::Cloned { ip, .. } => return ip, }; unsafe { uw::_Unwind_GetIP(ctx) as *mut c_void } } pub fn symbol_address(&self) -> *mut c_void { if let Frame::Cloned { symbol_address, .. } = *self { return symbol_address; } // It seems that on OSX `_Unwind_FindEnclosingFunction` returns a // pointer to... something that's unclear. It's definitely not always // the enclosing function for whatever reason. It's not entirely clear // to me what's going on here, so pessimize this for now and just always // return the ip. // // Note the `skip_inner_frames.rs` test is skipped on OSX due to this // clause, and if this is fixed that test in theory can be run on OSX! if cfg!(target_os = "macos") || cfg!(target_os = "ios") { self.ip() } else { unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) } } } } impl Clone for Frame { fn clone(&self) -> Frame { Frame::Cloned { ip: self.ip(), symbol_address: self.symbol_address(), } } } #[inline(always)] pub unsafe fn trace(mut cb: &mut FnMut(&super::Frame) -> bool) { uw::_Unwind_Backtrace(trace_fn, &mut cb as *mut _ as *mut _); extern "C" fn trace_fn( ctx: *mut uw::_Unwind_Context, arg: *mut c_void, ) -> uw::_Unwind_Reason_Code { let cb = unsafe { &mut *(arg as *mut &mut FnMut(&super::Frame) -> bool) }; let cx = super::Frame { inner: Frame::Raw(ctx), }; let mut bomb = crate::Bomb { enabled: true }; let keep_going = cb(&cx); bomb.enabled = false; if keep_going { uw::_URC_NO_REASON } else { uw::_URC_FAILURE } } } /// Unwind library interface used for backtraces /// /// Note that dead code is allowed as here are just bindings /// iOS doesn't use all of them it but adding more /// platform-specific configs pollutes the code too much #[allow(non_camel_case_types)] #[allow(non_snake_case)] #[allow(dead_code)] mod uw { pub use self::_Unwind_Reason_Code::*; use core::ffi::c_void; #[repr(C)] pub enum _Unwind_Reason_Code { _URC_NO_REASON = 0, _URC_FOREIGN_EXCEPTION_CAUGHT = 1, _URC_FATAL_PHASE2_ERROR = 2, _URC_FATAL_PHASE1_ERROR = 3, _URC_NORMAL_STOP = 4, _URC_END_OF_STACK = 5, _URC_HANDLER_FOUND = 6, _URC_INSTALL_CONTEXT = 7, _URC_CONTINUE_UNWIND = 8, _URC_FAILURE = 9, // used only by ARM EABI } pub enum _Unwind_Context {} pub type _Unwind_Trace_Fn = extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code; extern "C" { // No native _Unwind_Backtrace on iOS #[cfg(not(all(target_os = "ios", target_arch = "arm")))] pub fn _Unwind_Backtrace( trace: _Unwind_Trace_Fn, trace_argument: *mut c_void, ) -> _Unwind_Reason_Code; // available since GCC 4.2.0, should be fine for our purpose #[cfg(all( not(all(target_os = "android", target_arch = "arm")), not(all(target_os = "freebsd", target_arch = "arm")), not(all(target_os = "linux", target_arch = "arm")) ))] pub fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t; #[cfg(all( not(target_os = "android"), not(all(target_os = "freebsd", target_arch = "arm")), not(all(target_os = "linux", target_arch = "arm")) ))] pub fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void; } // On android, the function _Unwind_GetIP is a macro, and this is the // expansion of the macro. This is all copy/pasted directly from the // header file with the definition of _Unwind_GetIP. #[cfg(any( all(target_os = "android", target_arch = "arm"), all(target_os = "freebsd", target_arch = "arm"), all(target_os = "linux", target_arch = "arm") ))] pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { #[repr(C)] enum _Unwind_VRS_Result { _UVRSR_OK = 0, _UVRSR_NOT_IMPLEMENTED = 1, _UVRSR_FAILED = 2, } #[repr(C)] enum _Unwind_VRS_RegClass { _UVRSC_CORE = 0, _UVRSC_VFP = 1, _UVRSC_FPA = 2, _UVRSC_WMMXD = 3, _UVRSC_WMMXC = 4, } #[repr(C)] enum _Unwind_VRS_DataRepresentation { _UVRSD_UINT32 = 0, _UVRSD_VFPX = 1, _UVRSD_FPAX = 2, _UVRSD_UINT64 = 3, _UVRSD_FLOAT = 4, _UVRSD_DOUBLE = 5, } type _Unwind_Word = libc::c_uint; extern "C" { fn _Unwind_VRS_Get( ctx: *mut _Unwind_Context, klass: _Unwind_VRS_RegClass, word: _Unwind_Word, repr: _Unwind_VRS_DataRepresentation, data: *mut c_void, ) -> _Unwind_VRS_Result; } let mut val: _Unwind_Word = 0; let ptr = &mut val as *mut _Unwind_Word; let _ = _Unwind_VRS_Get( ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, ptr as *mut c_void, ); (val & !1) as libc::uintptr_t } // This function also doesn't exist on Android or ARM/Linux, so make it // a no-op #[cfg(any( target_os = "android", all(target_os = "freebsd", target_arch = "arm"), all(target_os = "linux", target_arch = "arm") ))] pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void { pc } } backtrace-0.3.44/src/backtrace/mod.rs010066400017500001750000000120001360710306200155660ustar0000000000000000use core::ffi::c_void; use core::fmt; /// Inspects the current call-stack, passing all active frames into the closure /// provided to calculate a stack trace. /// /// This function is the workhorse of this library in calculating the stack /// traces for a program. The given closure `cb` is yielded instances of a /// `Frame` which represent information about that call frame on the stack. The /// closure is yielded frames in a top-down fashion (most recently called /// functions first). /// /// The closure's return value is an indication of whether the backtrace should /// continue. A return value of `false` will terminate the backtrace and return /// immediately. /// /// Once a `Frame` is acquired you will likely want to call `backtrace::resolve` /// to convert the `ip` (instruction pointer) or symbol address to a `Symbol` /// through which the name and/or filename/line number can be learned. /// /// Note that this is a relatively low-level function and if you'd like to, for /// example, capture a backtrace to be inspected later, then the `Backtrace` /// type may be more appropriate. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. /// /// # Panics /// /// This function strives to never panic, but if the `cb` provided panics then /// some platforms will force a double panic to abort the process. Some /// platforms use a C library which internally uses callbacks which cannot be /// unwound through, so panicking from `cb` may trigger a process abort. /// /// # Example /// /// ``` /// extern crate backtrace; /// /// fn main() { /// backtrace::trace(|frame| { /// // ... /// /// true // continue the backtrace /// }); /// } /// ``` #[cfg(feature = "std")] pub fn trace bool>(cb: F) { let _guard = crate::lock::lock(); unsafe { trace_unsynchronized(cb) } } /// Same as `trace`, only unsafe as it's unsynchronized. /// /// This function does not have synchronization guarentees but is available /// when the `std` feature of this crate isn't compiled in. See the `trace` /// function for more documentation and examples. /// /// # Panics /// /// See information on `trace` for caveats on `cb` panicking. pub unsafe fn trace_unsynchronized bool>(mut cb: F) { trace_imp(&mut cb) } /// A trait representing one frame of a backtrace, yielded to the `trace` /// function of this crate. /// /// The tracing function's closure will be yielded frames, and the frame is /// virtually dispatched as the underlying implementation is not always known /// until runtime. #[derive(Clone)] pub struct Frame { pub(crate) inner: FrameImp, } impl Frame { /// Returns the current instruction pointer of this frame. /// /// This is normally the next instruction to execute in the frame, but not /// all implementations list this with 100% accuracy (but it's generally /// pretty close). /// /// It is recommended to pass this value to `backtrace::resolve` to turn it /// into a symbol name. pub fn ip(&self) -> *mut c_void { self.inner.ip() } /// Returns the starting symbol address of the frame of this function. /// /// This will attempt to rewind the instruction pointer returned by `ip` to /// the start of the function, returning that value. In some cases, however, /// backends will just return `ip` from this function. /// /// The returned value can sometimes be used if `backtrace::resolve` failed /// on the `ip` given above. pub fn symbol_address(&self) -> *mut c_void { self.inner.symbol_address() } } impl fmt::Debug for Frame { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("Frame") .field("ip", &self.ip()) .field("symbol_address", &self.symbol_address()) .finish() } } cfg_if::cfg_if! { if #[cfg( any( all( unix, not(target_os = "emscripten"), not(all(target_os = "ios", target_arch = "arm")), feature = "libunwind", ), all( target_env = "sgx", target_vendor = "fortanix", ), ) )] { mod libunwind; use self::libunwind::trace as trace_imp; pub(crate) use self::libunwind::Frame as FrameImp; } else if #[cfg( all( unix, not(target_os = "emscripten"), feature = "unix-backtrace", ) )] { mod unix_backtrace; use self::unix_backtrace::trace as trace_imp; pub(crate) use self::unix_backtrace::Frame as FrameImp; } else if #[cfg(all(windows, feature = "dbghelp", not(target_vendor = "uwp")))] { mod dbghelp; use self::dbghelp::trace as trace_imp; pub(crate) use self::dbghelp::Frame as FrameImp; } else { mod noop; use self::noop::trace as trace_imp; pub(crate) use self::noop::Frame as FrameImp; } } backtrace-0.3.44/src/backtrace/noop.rs010066400017500001750000000006001360710306200157650ustar0000000000000000//! Empty implementation of unwinding used when no other implementation is //! appropriate. use core::ffi::c_void; #[inline(always)] pub fn trace(_cb: &mut FnMut(&super::Frame) -> bool) {} #[derive(Clone)] pub struct Frame; impl Frame { pub fn ip(&self) -> *mut c_void { 0 as *mut _ } pub fn symbol_address(&self) -> *mut c_void { 0 as *mut _ } } backtrace-0.3.44/src/backtrace/unix_backtrace.rs010066400017500001750000000032351360710306200200030ustar0000000000000000// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Unwinding through the `backtrace` function provided in Unix. //! //! This is an alternative unwinding strategy for Unix platforms which don't //! have support for libunwind but do have support for `backtrace`. Currently //! there's not a whole lot of those though. This module is a relatively //! straightforward binding of the `backtrace` API to the `Frame` API that we'd //! like to have. use core::ffi::c_void; use core::mem; use libc::c_int; #[derive(Clone)] pub struct Frame { addr: usize, } impl Frame { pub fn ip(&self) -> *mut c_void { self.addr as *mut c_void } pub fn symbol_address(&self) -> *mut c_void { self.ip() } } extern "C" { fn backtrace(buf: *mut *mut c_void, sz: c_int) -> c_int; } #[inline(always)] pub unsafe fn trace(cb: &mut FnMut(&super::Frame) -> bool) { const SIZE: usize = 100; let mut buf: [*mut c_void; SIZE]; let cnt; buf = mem::zeroed(); cnt = backtrace(buf.as_mut_ptr(), SIZE as c_int); for addr in buf[..cnt as usize].iter() { let cx = super::Frame { inner: Frame { addr: *addr as usize, }, }; if !cb(&cx) { return; } } } backtrace-0.3.44/src/capture.rs010066400017500001750000000362401360710306200145470ustar0000000000000000use crate::PrintFmt; use crate::{resolve, resolve_frame, trace, BacktraceFmt, Symbol, SymbolName}; use std::ffi::c_void; use std::fmt; use std::path::{Path, PathBuf}; use std::prelude::v1::*; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// Representation of an owned and self-contained backtrace. /// /// This structure can be used to capture a backtrace at various points in a /// program and later used to inspect what the backtrace was at that time. /// /// `Backtrace` supports pretty-printing of backtraces through its `Debug` /// implementation. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. #[derive(Clone)] #[cfg_attr(feature = "serialize-rustc", derive(RustcDecodable, RustcEncodable))] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct Backtrace { // Frames here are listed from top-to-bottom of the stack frames: Vec, // The index we believe is the actual start of the backtrace, omitting // frames like `Backtrace::new` and `backtrace::trace`. actual_start_index: usize, } fn _assert_send_sync() { fn _assert() {} _assert::(); } /// Captured version of a frame in a backtrace. /// /// This type is returned as a list from `Backtrace::frames` and represents one /// stack frame in a captured backtrace. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. #[derive(Clone)] pub struct BacktraceFrame { frame: Frame, symbols: Option>, } #[derive(Clone)] enum Frame { Raw(crate::Frame), #[allow(dead_code)] Deserialized { ip: usize, symbol_address: usize, }, } impl Frame { fn ip(&self) -> *mut c_void { match *self { Frame::Raw(ref f) => f.ip(), Frame::Deserialized { ip, .. } => ip as *mut c_void, } } fn symbol_address(&self) -> *mut c_void { match *self { Frame::Raw(ref f) => f.symbol_address(), Frame::Deserialized { symbol_address, .. } => symbol_address as *mut c_void, } } } /// Captured version of a symbol in a backtrace. /// /// This type is returned as a list from `BacktraceFrame::symbols` and /// represents the metadata for a symbol in a backtrace. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. #[derive(Clone)] #[cfg_attr(feature = "serialize-rustc", derive(RustcDecodable, RustcEncodable))] #[cfg_attr(feature = "serde", derive(Deserialize, Serialize))] pub struct BacktraceSymbol { name: Option>, addr: Option, filename: Option, lineno: Option, } impl Backtrace { /// Captures a backtrace at the callsite of this function, returning an /// owned representation. /// /// This function is useful for representing a backtrace as an object in /// Rust. This returned value can be sent across threads and printed /// elsewhere, and the purpose of this value is to be entirely self /// contained. /// /// Note that on some platforms acquiring a full backtrace and resolving it /// can be extremely expensive. If the cost is too much for your application /// it's recommended to instead use `Backtrace::new_unresolved()` which /// avoids the symbol resolution step (which typically takes the longest) /// and allows deferring that to a later date. /// /// # Examples /// /// ``` /// use backtrace::Backtrace; /// /// let current_backtrace = Backtrace::new(); /// ``` /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. #[inline(never)] // want to make sure there's a frame here to remove pub fn new() -> Backtrace { let mut bt = Self::create(Self::new as usize); bt.resolve(); bt } /// Similar to `new` except that this does not resolve any symbols, this /// simply captures the backtrace as a list of addresses. /// /// At a later time the `resolve` function can be called to resolve this /// backtrace's symbols into readable names. This function exists because /// the resolution process can sometimes take a significant amount of time /// whereas any one backtrace may only be rarely printed. /// /// # Examples /// /// ``` /// use backtrace::Backtrace; /// /// let mut current_backtrace = Backtrace::new_unresolved(); /// println!("{:?}", current_backtrace); // no symbol names /// current_backtrace.resolve(); /// println!("{:?}", current_backtrace); // symbol names now present /// ``` /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. #[inline(never)] // want to make sure there's a frame here to remove pub fn new_unresolved() -> Backtrace { Self::create(Self::new_unresolved as usize) } fn create(ip: usize) -> Backtrace { let mut frames = Vec::new(); let mut actual_start_index = None; trace(|frame| { frames.push(BacktraceFrame { frame: Frame::Raw(frame.clone()), symbols: None, }); if frame.symbol_address() as usize == ip && actual_start_index.is_none() { actual_start_index = Some(frames.len()); } true }); Backtrace { frames, actual_start_index: actual_start_index.unwrap_or(0), } } /// Returns the frames from when this backtrace was captured. /// /// The first entry of this slice is likely the function `Backtrace::new`, /// and the last frame is likely something about how this thread or the main /// function started. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. pub fn frames(&self) -> &[BacktraceFrame] { &self.frames[self.actual_start_index..] } /// If this backtrace was created from `new_unresolved` then this function /// will resolve all addresses in the backtrace to their symbolic names. /// /// If this backtrace has been previously resolved or was created through /// `new`, this function does nothing. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. pub fn resolve(&mut self) { for frame in self.frames.iter_mut().filter(|f| f.symbols.is_none()) { let mut symbols = Vec::new(); { let sym = |symbol: &Symbol| { symbols.push(BacktraceSymbol { name: symbol.name().map(|m| m.as_bytes().to_vec()), addr: symbol.addr().map(|a| a as usize), filename: symbol.filename().map(|m| m.to_owned()), lineno: symbol.lineno(), }); }; match frame.frame { Frame::Raw(ref f) => resolve_frame(f, sym), Frame::Deserialized { ip, .. } => { resolve(ip as *mut c_void, sym); } } } frame.symbols = Some(symbols); } } } impl From> for Backtrace { fn from(frames: Vec) -> Self { Backtrace { frames, actual_start_index: 0, } } } impl Into> for Backtrace { fn into(self) -> Vec { self.frames } } impl BacktraceFrame { /// Same as `Frame::ip` /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. pub fn ip(&self) -> *mut c_void { self.frame.ip() as *mut c_void } /// Same as `Frame::symbol_address` /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. pub fn symbol_address(&self) -> *mut c_void { self.frame.symbol_address() as *mut c_void } /// Returns the list of symbols that this frame corresponds to. /// /// Normally there is only one symbol per frame, but sometimes if a number /// of functions are inlined into one frame then multiple symbols will be /// returned. The first symbol listed is the "innermost function", whereas /// the last symbol is the outermost (last caller). /// /// Note that if this frame came from an unresolved backtrace then this will /// return an empty list. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. pub fn symbols(&self) -> &[BacktraceSymbol] { self.symbols.as_ref().map(|s| &s[..]).unwrap_or(&[]) } } impl BacktraceSymbol { /// Same as `Symbol::name` /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. pub fn name(&self) -> Option { self.name.as_ref().map(|s| SymbolName::new(s)) } /// Same as `Symbol::addr` /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. pub fn addr(&self) -> Option<*mut c_void> { self.addr.map(|s| s as *mut c_void) } /// Same as `Symbol::filename` /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. pub fn filename(&self) -> Option<&Path> { self.filename.as_ref().map(|p| &**p) } /// Same as `Symbol::lineno` /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. pub fn lineno(&self) -> Option { self.lineno } } impl fmt::Debug for Backtrace { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let full = fmt.alternate(); let (frames, style) = if full { (&self.frames[..], PrintFmt::Full) } else { (&self.frames[self.actual_start_index..], PrintFmt::Short) }; // When printing paths we try to strip the cwd if it exists, otherwise // we just print the path as-is. Note that we also only do this for the // short format, because if it's full we presumably want to print // everything. let cwd = std::env::current_dir(); let mut print_path = move |fmt: &mut fmt::Formatter, path: crate::BytesOrWideString| { let path = path.into_path_buf(); if !full { if let Ok(cwd) = &cwd { if let Ok(suffix) = path.strip_prefix(cwd) { return fmt::Display::fmt(&suffix.display(), fmt); } } } fmt::Display::fmt(&path.display(), fmt) }; let mut f = BacktraceFmt::new(fmt, style, &mut print_path); f.add_context()?; for frame in frames { f.frame().backtrace_frame(frame)?; } f.finish()?; Ok(()) } } impl Default for Backtrace { fn default() -> Backtrace { Backtrace::new() } } impl fmt::Debug for BacktraceFrame { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("BacktraceFrame") .field("ip", &self.ip()) .field("symbol_address", &self.symbol_address()) .finish() } } impl fmt::Debug for BacktraceSymbol { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_struct("BacktraceSymbol") .field("name", &self.name()) .field("addr", &self.addr()) .field("filename", &self.filename()) .field("lineno", &self.lineno()) .finish() } } #[cfg(feature = "serialize-rustc")] mod rustc_serialize_impls { use super::*; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; #[derive(RustcEncodable, RustcDecodable)] struct SerializedFrame { ip: usize, symbol_address: usize, symbols: Option>, } impl Decodable for BacktraceFrame { fn decode(d: &mut D) -> Result where D: Decoder, { let frame: SerializedFrame = SerializedFrame::decode(d)?; Ok(BacktraceFrame { frame: Frame::Deserialized { ip: frame.ip, symbol_address: frame.symbol_address, }, symbols: frame.symbols, }) } } impl Encodable for BacktraceFrame { fn encode(&self, e: &mut E) -> Result<(), E::Error> where E: Encoder, { let BacktraceFrame { frame, symbols } = self; SerializedFrame { ip: frame.ip() as usize, symbol_address: frame.symbol_address() as usize, symbols: symbols.clone(), } .encode(e) } } } #[cfg(feature = "serde")] mod serde_impls { extern crate serde; use self::serde::de::Deserializer; use self::serde::ser::Serializer; use self::serde::{Deserialize, Serialize}; use super::*; #[derive(Serialize, Deserialize)] struct SerializedFrame { ip: usize, symbol_address: usize, symbols: Option>, } impl Serialize for BacktraceFrame { fn serialize(&self, s: S) -> Result where S: Serializer, { let BacktraceFrame { frame, symbols } = self; SerializedFrame { ip: frame.ip() as usize, symbol_address: frame.symbol_address() as usize, symbols: symbols.clone(), } .serialize(s) } } impl<'a> Deserialize<'a> for BacktraceFrame { fn deserialize(d: D) -> Result where D: Deserializer<'a>, { let frame: SerializedFrame = SerializedFrame::deserialize(d)?; Ok(BacktraceFrame { frame: Frame::Deserialized { ip: frame.ip, symbol_address: frame.symbol_address, }, symbols: frame.symbols, }) } } } backtrace-0.3.44/src/dbghelp.rs010066400017500001750000000343751360710306200145200ustar0000000000000000//! A module to assist in managing dbghelp bindings on Windows //! //! Backtraces on Windows (at least for MSVC) are largely powered through //! `dbghelp.dll` and the various functions that it contains. These functions //! are currently loaded *dynamically* rather than linking to `dbghelp.dll` //! statically. This is currently done by the standard library (and is in theory //! required there), but is an effort to help reduce the static dll dependencies //! of a library since backtraces are typically pretty optional. That being //! said, `dbghelp.dll` almost always successfully loads on Windows. //! //! Note though that since we're loading all this support dynamically we can't //! actually use the raw definitions in `winapi`, but rather we need to define //! the function pointer types ourselves and use that. We don't really want to //! be in the business of duplicating winapi, so we have a Cargo feature //! `verify-winapi` which asserts that all bindings match those in winapi and //! this feature is enabled on CI. //! //! Finally, you'll note here that the dll for `dbghelp.dll` is never unloaded, //! and that's currently intentional. The thinking is that we can globally cache //! it and use it between calls to the API, avoiding expensive loads/unloads. If //! this is a problem for leak detectors or something like that we can cross the //! bridge when we get there. #![allow(non_snake_case)] use crate::windows::*; use core::mem; use core::ptr; // Work around `SymGetOptions` and `SymSetOptions` not being present in winapi // itself. Otherwise this is only used when we're double-checking types against // winapi. #[cfg(feature = "verify-winapi")] mod dbghelp { use crate::windows::*; pub use winapi::um::dbghelp::{ StackWalk64, SymCleanup, SymFromAddrW, SymFunctionTableAccess64, SymGetLineFromAddrW64, SymGetModuleBase64, SymInitializeW, }; extern "system" { // Not defined in winapi yet pub fn SymGetOptions() -> u32; pub fn SymSetOptions(_: u32); // This is defined in winapi, but it's incorrect (FIXME winapi-rs#768) pub fn StackWalkEx( MachineType: DWORD, hProcess: HANDLE, hThread: HANDLE, StackFrame: LPSTACKFRAME_EX, ContextRecord: PVOID, ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64, FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64, GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64, TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64, Flags: DWORD, ) -> BOOL; // Not defined in winapi yet pub fn SymFromInlineContextW( hProcess: HANDLE, Address: DWORD64, InlineContext: ULONG, Displacement: PDWORD64, Symbol: PSYMBOL_INFOW, ) -> BOOL; pub fn SymGetLineFromInlineContextW( hProcess: HANDLE, dwAddr: DWORD64, InlineContext: ULONG, qwModuleBaseAddress: DWORD64, pdwDisplacement: PDWORD, Line: PIMAGEHLP_LINEW64, ) -> BOOL; } pub fn assert_equal_types(a: T, _b: T) -> T { a } } // This macro is used to define a `Dbghelp` structure which internally contains // all the function pointers that we might load. macro_rules! dbghelp { (extern "system" { $(fn $name:ident($($arg:ident: $argty:ty),*) -> $ret: ty;)* }) => ( pub struct Dbghelp { /// The loaded DLL for `dbghelp.dll` dll: HMODULE, // Each function pointer for each function we might use $($name: usize,)* } static mut DBGHELP: Dbghelp = Dbghelp { // Initially we haven't loaded the DLL dll: 0 as *mut _, // Initiall all functions are set to zero to say they need to be // dynamically loaded. $($name: 0,)* }; // Convenience typedef for each function type. $(pub type $name = unsafe extern "system" fn($($argty),*) -> $ret;)* impl Dbghelp { /// Attempts to open `dbghelp.dll`. Returns success if it works or /// error if `LoadLibraryW` fails. /// /// Panics if library is already loaded. fn ensure_open(&mut self) -> Result<(), ()> { if !self.dll.is_null() { return Ok(()) } let lib = b"dbghelp.dll\0"; unsafe { self.dll = LoadLibraryA(lib.as_ptr() as *const i8); if self.dll.is_null() { Err(()) } else { Ok(()) } } } // Function for each method we'd like to use. When called it will // either read the cached function pointer or load it and return the // loaded value. Loads are asserted to succeed. $(pub fn $name(&mut self) -> Option<$name> { unsafe { if self.$name == 0 { let name = concat!(stringify!($name), "\0"); self.$name = self.symbol(name.as_bytes())?; } let ret = mem::transmute::(self.$name); #[cfg(feature = "verify-winapi")] dbghelp::assert_equal_types(ret, dbghelp::$name); Some(ret) } })* fn symbol(&self, symbol: &[u8]) -> Option { unsafe { match GetProcAddress(self.dll, symbol.as_ptr() as *const _) as usize { 0 => None, n => Some(n), } } } } // Convenience proxy to use the cleanup locks to reference dbghelp // functions. #[allow(dead_code)] impl Init { $(pub fn $name(&self) -> $name { unsafe { DBGHELP.$name().unwrap() } })* pub fn dbghelp(&self) -> *mut Dbghelp { unsafe { &mut DBGHELP } } } ) } const SYMOPT_DEFERRED_LOADS: DWORD = 0x00000004; dbghelp! { extern "system" { fn SymGetOptions() -> DWORD; fn SymSetOptions(options: DWORD) -> (); fn SymInitializeW( handle: HANDLE, path: PCWSTR, invade: BOOL ) -> BOOL; fn SymCleanup(handle: HANDLE) -> BOOL; fn StackWalk64( MachineType: DWORD, hProcess: HANDLE, hThread: HANDLE, StackFrame: LPSTACKFRAME64, ContextRecord: PVOID, ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64, FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64, GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64, TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64 ) -> BOOL; fn SymFunctionTableAccess64( hProcess: HANDLE, AddrBase: DWORD64 ) -> PVOID; fn SymGetModuleBase64( hProcess: HANDLE, AddrBase: DWORD64 ) -> DWORD64; fn SymFromAddrW( hProcess: HANDLE, Address: DWORD64, Displacement: PDWORD64, Symbol: PSYMBOL_INFOW ) -> BOOL; fn SymGetLineFromAddrW64( hProcess: HANDLE, dwAddr: DWORD64, pdwDisplacement: PDWORD, Line: PIMAGEHLP_LINEW64 ) -> BOOL; fn StackWalkEx( MachineType: DWORD, hProcess: HANDLE, hThread: HANDLE, StackFrame: LPSTACKFRAME_EX, ContextRecord: PVOID, ReadMemoryRoutine: PREAD_PROCESS_MEMORY_ROUTINE64, FunctionTableAccessRoutine: PFUNCTION_TABLE_ACCESS_ROUTINE64, GetModuleBaseRoutine: PGET_MODULE_BASE_ROUTINE64, TranslateAddress: PTRANSLATE_ADDRESS_ROUTINE64, Flags: DWORD ) -> BOOL; fn SymFromInlineContextW( hProcess: HANDLE, Address: DWORD64, InlineContext: ULONG, Displacement: PDWORD64, Symbol: PSYMBOL_INFOW ) -> BOOL; fn SymGetLineFromInlineContextW( hProcess: HANDLE, dwAddr: DWORD64, InlineContext: ULONG, qwModuleBaseAddress: DWORD64, pdwDisplacement: PDWORD, Line: PIMAGEHLP_LINEW64 ) -> BOOL; } } pub struct Init { lock: HANDLE, } /// Initialize all support necessary to access `dbghelp` API functions from this /// crate. /// /// Note that this function is **safe**, it internally has its own /// synchronization. Also note that it is safe to call this function multiple /// times recursively. #[cfg(all(windows, feature = "dbghelp"))] pub fn init() -> Result { use core::sync::atomic::{AtomicUsize, Ordering::SeqCst}; unsafe { // First thing we need to do is to synchronize this function. This can // be called concurrently from other threads or recursively within one // thread. Note that it's trickier than that though because what we're // using here, `dbghelp`, *also* needs to be synchronized with all other // callers to `dbghelp` in this process. // // Typically there aren't really that many calls to `dbghelp` within the // same process and we can probably safely assume that we're the only // ones accessing it. There is, however, one primary other user we have // to worry about which is ironically ourselves, but in the standard // library. The Rust standard library depends on this crate for // backtrace support, and this crate also exists on crates.io. This // means that if the standard library is printing a panic backtrace it // may race with this crate coming from crates.io, causing segfaults. // // To help solve this synchronization problem we employ a // Windows-specific trick here (it is, after all, a Windows-specific // restriction about synchronization). We create a *session-local* named // mutex to protect this call. The intention here is that the standard // library and this crate don't have to share Rust-level APIs to // synchronize here but can instead work behind the scenes to make sure // they're synchronizing with one another. That way when this function // is called through the standard library or through crates.io we can be // sure that the same mutex is being acquired. // // So all of that is to say that the first thing we do here is we // atomically create a `HANDLE` which is a named mutex on Windows. We // synchronize a bit with other threads sharing this function // specifically and ensure that only one handle is created per instance // of this function. Note that the handle is never closed once it's // stored in the global. // // After we've actually go the lock we simply acquire it, and our `Init` // handle we hand out will be responsible for dropping it eventually. static LOCK: AtomicUsize = AtomicUsize::new(0); let mut lock = LOCK.load(SeqCst); if lock == 0 { lock = CreateMutexA( ptr::null_mut(), 0, "Local\\RustBacktraceMutex\0".as_ptr() as _, ) as usize; if lock == 0 { return Err(()); } if let Err(other) = LOCK.compare_exchange(0, lock, SeqCst, SeqCst) { debug_assert!(other != 0); CloseHandle(lock as HANDLE); lock = other; } } debug_assert!(lock != 0); let lock = lock as HANDLE; let r = WaitForSingleObjectEx(lock, INFINITE, FALSE); debug_assert_eq!(r, 0); let ret = Init { lock }; // Ok, phew! Now that we're all safely synchronized, let's actually // start processing everything. First up we need to ensure that // `dbghelp.dll` is actually loaded in this process. We do this // dynamically to avoid a static dependency. This has historically been // done to work around weird linking issues and is intended at making // binaries a bit more portable since this is largely just a debugging // utility. // // Once we've opened `dbghelp.dll` we need to call some initialization // functions in it, and that's detailed more below. We only do this // once, though, so we've got a global boolean indicating whether we're // done yet or not. DBGHELP.ensure_open()?; static mut INITIALIZED: bool = false; if INITIALIZED { return Ok(ret); } let orig = DBGHELP.SymGetOptions().unwrap()(); // Ensure that the `SYMOPT_DEFERRED_LOADS` flag is set, because // according to MSVC's own docs about this: "This is the fastest, most // efficient way to use the symbol handler.", so let's do that! DBGHELP.SymSetOptions().unwrap()(orig | SYMOPT_DEFERRED_LOADS); // Actually initialize symbols with MSVC. Note that this can fail, but we // ignore it. There's not a ton of prior art for this per se, but LLVM // internally seems to ignore the return value here and one of the // sanitizer libraries in LLVM prints a scary warning if this fails but // basically ignores it in the long run. // // One case this comes up a lot for Rust is that the standard library and // this crate on crates.io both want to compete for `SymInitializeW`. The // standard library historically wanted to initialize then cleanup most of // the time, but now that it's using this crate it means that someone will // get to initialization first and the other will pick up that // initialization. DBGHELP.SymInitializeW().unwrap()(GetCurrentProcess(), ptr::null_mut(), TRUE); INITIALIZED = true; Ok(ret) } } impl Drop for Init { fn drop(&mut self) { unsafe { let r = ReleaseMutex(self.lock); debug_assert!(r != 0); } } } backtrace-0.3.44/src/lib.rs010066400017500001750000000107061360710306200136510ustar0000000000000000//! A library for acquiring a backtrace at runtime //! //! This library is meant to supplement the `RUST_BACKTRACE=1` support of the //! standard library by allowing an acquisition of a backtrace at runtime //! programmatically. The backtraces generated by this library do not need to be //! parsed, for example, and expose the functionality of multiple backend //! implementations. //! //! # Implementation //! //! This library makes use of a number of strategies for actually acquiring a //! backtrace. For example unix uses libgcc's libunwind bindings by default to //! acquire a backtrace, but coresymbolication or dladdr is used on OSX to //! acquire symbol names while linux uses gcc's libbacktrace. //! //! When using the default feature set of this library the "most reasonable" set //! of defaults is chosen for the current platform, but the features activated //! can also be controlled at a finer granularity. //! //! # API Principles //! //! This library attempts to be as flexible as possible to accommodate different //! backend implementations of acquiring a backtrace. Consequently the currently //! exported functions are closure-based as opposed to the likely expected //! iterator-based versions. This is done due to limitations of the underlying //! APIs used from the system. //! //! # Usage //! //! First, add this to your Cargo.toml //! //! ```toml //! [dependencies] //! backtrace = "0.3" //! ``` //! //! Next: //! //! ``` //! extern crate backtrace; //! //! fn main() { //! # // Unsafe here so test passes on no_std. //! # #[cfg(feature = "std")] { //! backtrace::trace(|frame| { //! let ip = frame.ip(); //! let symbol_address = frame.symbol_address(); //! //! // Resolve this instruction pointer to a symbol name //! backtrace::resolve_frame(frame, |symbol| { //! if let Some(name) = symbol.name() { //! // ... //! } //! if let Some(filename) = symbol.filename() { //! // ... //! } //! }); //! //! true // keep going to the next frame //! }); //! } //! # } //! ``` #![doc(html_root_url = "https://docs.rs/backtrace")] #![deny(missing_docs)] #![no_std] #![cfg_attr( all(feature = "std", target_env = "sgx", target_vendor = "fortanix"), feature(sgx_platform) )] #![allow(bare_trait_objects)] // TODO: remove when updating to 2018 edition #![allow(rust_2018_idioms)] // TODO: remove when updating to 2018 edition #[cfg(feature = "std")] #[macro_use] extern crate std; pub use crate::backtrace::{trace_unsynchronized, Frame}; mod backtrace; pub use crate::symbolize::resolve_frame_unsynchronized; pub use crate::symbolize::{resolve_unsynchronized, Symbol, SymbolName}; mod symbolize; pub use crate::types::BytesOrWideString; mod types; #[cfg(feature = "std")] pub use crate::symbolize::clear_symbol_cache; mod print; pub use print::{BacktraceFmt, BacktraceFrameFmt, PrintFmt}; cfg_if::cfg_if! { if #[cfg(feature = "std")] { pub use crate::backtrace::trace; pub use crate::symbolize::{resolve, resolve_frame}; pub use crate::capture::{Backtrace, BacktraceFrame, BacktraceSymbol}; mod capture; } } #[allow(dead_code)] struct Bomb { enabled: bool, } #[allow(dead_code)] impl Drop for Bomb { fn drop(&mut self) { if self.enabled { panic!("cannot panic during the backtrace function"); } } } #[allow(dead_code)] #[cfg(feature = "std")] mod lock { use std::boxed::Box; use std::cell::Cell; use std::sync::{Mutex, MutexGuard, Once}; pub struct LockGuard(Option>); static mut LOCK: *mut Mutex<()> = 0 as *mut _; static INIT: Once = Once::new(); thread_local!(static LOCK_HELD: Cell = Cell::new(false)); impl Drop for LockGuard { fn drop(&mut self) { if self.0.is_some() { LOCK_HELD.with(|slot| { assert!(slot.get()); slot.set(false); }); } } } pub fn lock() -> LockGuard { if LOCK_HELD.with(|l| l.get()) { return LockGuard(None); } LOCK_HELD.with(|s| s.set(true)); unsafe { INIT.call_once(|| { LOCK = Box::into_raw(Box::new(Mutex::new(()))); }); LockGuard(Some((*LOCK).lock().unwrap())) } } } #[cfg(all(windows, feature = "dbghelp", not(target_vendor = "uwp")))] mod dbghelp; #[cfg(windows)] mod windows; backtrace-0.3.44/src/print.rs010066400017500001750000000226761362056464300142630ustar0000000000000000use crate::BytesOrWideString; use core::ffi::c_void; use core::fmt; const HEX_WIDTH: usize = 2 + 2 * core::mem::size_of::(); #[cfg(target_os = "fuchsia")] mod fuchsia; /// A formatter for backtraces. /// /// This type can be used to print a backtrace regardless of where the backtrace /// itself comes from. If you have a `Backtrace` type then its `Debug` /// implementation already uses this printing format. pub struct BacktraceFmt<'a, 'b> { fmt: &'a mut fmt::Formatter<'b>, frame_index: usize, format: PrintFmt, print_path: &'a mut (FnMut(&mut fmt::Formatter, BytesOrWideString) -> fmt::Result + 'b), } /// The styles of printing that we can print #[derive(Copy, Clone, Eq, PartialEq)] pub enum PrintFmt { /// Prints a terser backtrace which ideally only contains relevant information Short, /// Prints a backtrace that contains all possible information Full, #[doc(hidden)] __Nonexhaustive, } impl<'a, 'b> BacktraceFmt<'a, 'b> { /// Create a new `BacktraceFmt` which will write output to the provided /// `fmt`. /// /// The `format` argument will control the style in which the backtrace is /// printed, and the `print_path` argument will be used to print the /// `BytesOrWideString` instances of filenames. This type itself doesn't do /// any printing of filenames, but this callback is required to do so. pub fn new( fmt: &'a mut fmt::Formatter<'b>, format: PrintFmt, print_path: &'a mut (FnMut(&mut fmt::Formatter, BytesOrWideString) -> fmt::Result + 'b), ) -> Self { BacktraceFmt { fmt, frame_index: 0, format, print_path, } } /// Prints a preamble for the backtrace about to be printed. /// /// This is required on some platforms for backtraces to be fully /// sumbolicated later, and otherwise this should just be the first method /// you call after creating a `BacktraceFmt`. pub fn add_context(&mut self) -> fmt::Result { #[cfg(target_os = "fuchsia")] fuchsia::print_dso_context(self.fmt)?; Ok(()) } /// Adds a frame to the backtrace output. /// /// This commit returns an RAII instance of a `BacktraceFrameFmt` which can be used /// to actually print a frame, and on destruction it will increment the /// frame counter. pub fn frame(&mut self) -> BacktraceFrameFmt<'_, 'a, 'b> { BacktraceFrameFmt { fmt: self, symbol_index: 0, } } /// Completes the backtrace output. /// /// This is currently a no-op but is added for future compatibility with /// backtrace formats. pub fn finish(&mut self) -> fmt::Result { // Currently a no-op-- including this hook to allow for future additions. Ok(()) } } /// A formatter for just one frame of a backtrace. /// /// This type is created by the `BacktraceFmt::frame` function. pub struct BacktraceFrameFmt<'fmt, 'a, 'b> { fmt: &'fmt mut BacktraceFmt<'a, 'b>, symbol_index: usize, } impl BacktraceFrameFmt<'_, '_, '_> { /// Prints a `BacktraceFrame` with this frame formatter. /// /// This will recusrively print all `BacktraceSymbol` instances within the /// `BacktraceFrame`. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. #[cfg(feature = "std")] pub fn backtrace_frame(&mut self, frame: &crate::BacktraceFrame) -> fmt::Result { let symbols = frame.symbols(); for symbol in symbols { self.backtrace_symbol(frame, symbol)?; } if symbols.is_empty() { self.print_raw(frame.ip(), None, None, None)?; } Ok(()) } /// Prints a `BacktraceSymbol` within a `BacktraceFrame`. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. #[cfg(feature = "std")] pub fn backtrace_symbol( &mut self, frame: &crate::BacktraceFrame, symbol: &crate::BacktraceSymbol, ) -> fmt::Result { self.print_raw( frame.ip(), symbol.name(), // TODO: this isn't great that we don't end up printing anything // with non-utf8 filenames. Thankfully almost everything is utf8 so // this shouldn't be too too bad. symbol .filename() .and_then(|p| Some(BytesOrWideString::Bytes(p.to_str()?.as_bytes()))), symbol.lineno(), )?; Ok(()) } /// Prints a raw traced `Frame` and `Symbol`, typically from within the raw /// callbacks of this crate. pub fn symbol(&mut self, frame: &crate::Frame, symbol: &crate::Symbol) -> fmt::Result { self.print_raw( frame.ip(), symbol.name(), symbol.filename_raw(), symbol.lineno(), )?; Ok(()) } /// Adds a raw frame to the backtrace output. /// /// This method, unlike the previous, takes the raw arguments in case /// they're being source from different locations. Note that this may be /// called multiple times for one frame. pub fn print_raw( &mut self, frame_ip: *mut c_void, symbol_name: Option, filename: Option, lineno: Option, ) -> fmt::Result { // Fuchsia is unable to symbolize within a process so it has a special // format which can be used to symbolize later. Print that instead of // printing addresses in our own format here. if cfg!(target_os = "fuchsia") { self.print_raw_fuchsia(frame_ip)?; } else { self.print_raw_generic(frame_ip, symbol_name, filename, lineno)?; } self.symbol_index += 1; Ok(()) } #[allow(unused_mut)] fn print_raw_generic( &mut self, mut frame_ip: *mut c_void, symbol_name: Option, filename: Option, lineno: Option, ) -> fmt::Result { // No need to print "null" frames, it basically just means that the // system backtrace was a bit eager to trace back super far. if let PrintFmt::Short = self.fmt.format { if frame_ip.is_null() { return Ok(()); } } // To reduce TCB size in Sgx enclave, we do not want to implement symbol // resolution functionality. Rather, we can print the offset of the // address here, which could be later mapped to correct function. #[cfg(all(feature = "std", target_env = "sgx", target_vendor = "fortanix"))] { let image_base = std::os::fortanix_sgx::mem::image_base(); frame_ip = usize::wrapping_sub(frame_ip as usize, image_base as _) as _; } // Print the index of the frame as well as the optional instruction // pointer of the frame. If we're beyond the first symbol of this frame // though we just print appropriate whitespace. if self.symbol_index == 0 { write!(self.fmt.fmt, "{:4}: ", self.fmt.frame_index)?; if let PrintFmt::Full = self.fmt.format { write!(self.fmt.fmt, "{:1$?} - ", frame_ip, HEX_WIDTH)?; } } else { write!(self.fmt.fmt, " ")?; if let PrintFmt::Full = self.fmt.format { write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH + 3)?; } } // Next up write out the symbol name, using the alternate formatting for // more information if we're a full backtrace. Here we also handle // symbols which don't have a name, match (symbol_name, &self.fmt.format) { (Some(name), PrintFmt::Short) => write!(self.fmt.fmt, "{:#}", name)?, (Some(name), PrintFmt::Full) => write!(self.fmt.fmt, "{}", name)?, (None, _) | (_, PrintFmt::__Nonexhaustive) => write!(self.fmt.fmt, "")?, } self.fmt.fmt.write_str("\n")?; // And last up, print out the filename/line number if they're available. if let (Some(file), Some(line)) = (filename, lineno) { self.print_fileline(file, line)?; } Ok(()) } fn print_fileline(&mut self, file: BytesOrWideString, line: u32) -> fmt::Result { // Filename/line are printed on lines under the symbol name, so print // some appropriate whitespace to sort of right-align ourselves. if let PrintFmt::Full = self.fmt.format { write!(self.fmt.fmt, "{:1$}", "", HEX_WIDTH)?; } write!(self.fmt.fmt, " at ")?; // Delegate to our internal callback to print the filename and then // print out the line number. (self.fmt.print_path)(self.fmt.fmt, file)?; write!(self.fmt.fmt, ":{}\n", line)?; Ok(()) } fn print_raw_fuchsia(&mut self, frame_ip: *mut c_void) -> fmt::Result { // We only care about the first symbol of a frame if self.symbol_index == 0 { self.fmt.fmt.write_str("{{{bt:")?; write!(self.fmt.fmt, "{}:{:?}", self.fmt.frame_index, frame_ip)?; self.fmt.fmt.write_str("}}}\n")?; } Ok(()) } } impl Drop for BacktraceFrameFmt<'_, '_, '_> { fn drop(&mut self) { self.fmt.frame_index += 1; } } backtrace-0.3.44/src/print/fuchsia.rs010066400017500001750000000332151360710306200156610ustar0000000000000000use core::fmt::{self, Write}; use core::mem::{size_of, transmute}; use core::slice::from_raw_parts; use libc::c_char; extern "C" { // dl_iterate_phdr takes a callback that will receive a dl_phdr_info pointer // for every DSO that has been linked into the process. dl_iterate_phdr also // ensures that the dynamic linker is locked from start to finish of the // iteration. If the callback returns a non-zero value the iteration is // terminated early. 'data' will be passed as the third argument to the // callback on each call. 'size' gives the size of the dl_phdr_info. #[allow(improper_ctypes)] fn dl_iterate_phdr( f: extern "C" fn(info: &dl_phdr_info, size: usize, data: &mut DsoPrinter) -> i32, data: &mut DsoPrinter, ) -> i32; } // We need to parse out the build ID and some basic program header data // which means that we need a bit of stuff from the ELF spec as well. const PT_LOAD: u32 = 1; const PT_NOTE: u32 = 4; // Now we have to replicate, bit for bit, the structure of the dl_phdr_info // type used by fuchsia's current dynamic linker. Chromium also has this ABI // boundary as well as crashpad. Eventully we'd like to move these cases to // use elf-search but we'd need to provide that in the SDK and that has not // yet been done. Thus we (and they) are stuck having to use this method // which incurs a tight coupling with the fuchsia libc. #[allow(non_camel_case_types)] #[repr(C)] struct dl_phdr_info { addr: *const u8, name: *const c_char, phdr: *const Elf_Phdr, phnum: u16, adds: u64, subs: u64, tls_modid: usize, tls_data: *const u8, } impl dl_phdr_info { fn program_headers(&self) -> PhdrIter<'_> { PhdrIter { phdrs: self.phdr_slice(), base: self.addr, } } // We have no way of knowing of checking if e_phoff and e_phnum are valid. // libc should ensure this for us however so it's safe to form a slice here. fn phdr_slice(&self) -> &[Elf_Phdr] { unsafe { from_raw_parts(self.phdr, self.phnum as usize) } } } struct PhdrIter<'a> { phdrs: &'a [Elf_Phdr], base: *const u8, } impl<'a> Iterator for PhdrIter<'a> { type Item = Phdr<'a>; fn next(&mut self) -> Option { self.phdrs.split_first().map(|(phdr, new_phdrs)| { self.phdrs = new_phdrs; Phdr { phdr, base: self.base, } }) } } // Elf_Phdr represents a 64-bit ELF program header in the endianness of the target // architecture. #[allow(non_camel_case_types)] #[derive(Clone, Debug)] #[repr(C)] struct Elf_Phdr { p_type: u32, p_flags: u32, p_offset: u64, p_vaddr: u64, p_paddr: u64, p_filesz: u64, p_memsz: u64, p_align: u64, } // Phdr represents a valid ELF program header and its contents. struct Phdr<'a> { phdr: &'a Elf_Phdr, base: *const u8, } impl<'a> Phdr<'a> { // We have no way of checking if p_addr or p_memsz are valid. Fuchsia's libc // parses the notes first however so by virtue of being here these headers // must be valid. NoteIter does not require the underlying data to be valid // but it does require the bounds to be valid. We trust that libc has ensured // that this is the case for us here. fn notes(&self) -> NoteIter<'a> { unsafe { NoteIter::new( self.base.add(self.phdr.p_offset as usize), self.phdr.p_memsz as usize, ) } } } // The note type for build IDs. const NT_GNU_BUILD_ID: u32 = 3; // Elf_Nhdr represents an ELF note header in the endianness of the target. #[allow(non_camel_case_types)] #[repr(C)] struct Elf_Nhdr { n_namesz: u32, n_descsz: u32, n_type: u32, } // Note represents an ELF note (header + contents). The name is left as a u8 // slice because it is not always null terminated and rust makes it easy enough // to check that the bytes match eitherway. struct Note<'a> { name: &'a [u8], desc: &'a [u8], tipe: u32, } // NoteIter lets you safely iterate over a note segment. It terminates as soon // as an error occurs or there are no more notes. If you iterate over invalid // data it will function as though no notes were found. struct NoteIter<'a> { base: &'a [u8], error: bool, } impl<'a> NoteIter<'a> { // It is an invariant of function that the pointer and size given denote a // valid range of bytes that can all be read. The contents of these bytes // can be anything but the range must be valid for this to be safe. unsafe fn new(base: *const u8, size: usize) -> Self { NoteIter { base: from_raw_parts(base, size), error: false, } } } // align_to aligns 'x' to 'to'-byte alignment assuming 'to' is a power of 2. // This follows a standard pattern in C/C++ ELF parsing code where // (x + to - 1) & -to is used. Rust does not let you negate usize so I use // 2's-complement conversion to recreate that. fn align_to(x: usize, to: usize) -> usize { (x + to - 1) & (!to + 1) } // take_bytes_align4 consumes num bytes from the slice (if present) and // additionally ensures that the final slice is properlly aligned. If an // either the number of bytes requested is too large or the slice can't be // realigned afterwards due to not enough remaining bytes existing, None is // returned and the slice is not modified. fn take_bytes_align4<'a>(num: usize, bytes: &mut &'a [u8]) -> Option<&'a [u8]> { if bytes.len() < align_to(num, 4) { return None; } let (out, bytes_new) = bytes.split_at(num); *bytes = &bytes_new[align_to(num, 4) - num..]; Some(out) } // This function has no real invariants the caller must uphold other than // perhaps that 'bytes' should be aligned for performance (and on some // architectures correctness). The values in the Elf_Nhdr fields might // be nonsense but this function ensures no such thing. fn take_nhdr<'a>(bytes: &mut &'a [u8]) -> Option<&'a Elf_Nhdr> { if size_of::() > bytes.len() { return None; } // This is safe as long as there is enough space and we just confirmed that // in the if statement above so this should not be unsafe. let out = unsafe { transmute::<*const u8, &'a Elf_Nhdr>(bytes.as_ptr()) }; // Note that sice_of::() is always 4-byte aligned. *bytes = &bytes[size_of::()..]; Some(out) } impl<'a> Iterator for NoteIter<'a> { type Item = Note<'a>; fn next(&mut self) -> Option { // Check if we've reached the end. if self.base.len() == 0 || self.error { return None; } // We transmute out an nhdr but we carefully consider the resulting // struct. We don't trust the namesz or descsz and we make no unsafe // decisions based on the type. So even if we get out complete garbage // we should still be safe. let nhdr = take_nhdr(&mut self.base)?; let name = take_bytes_align4(nhdr.n_namesz as usize, &mut self.base)?; let desc = take_bytes_align4(nhdr.n_descsz as usize, &mut self.base)?; Some(Note { name: name, desc: desc, tipe: nhdr.n_type, }) } } struct Perm(u32); /// Indicates that a segment is executable. const PERM_X: u32 = 0b00000001; /// Indicates that a segment is writable. const PERM_W: u32 = 0b00000010; /// Indicates that a segment is readable. const PERM_R: u32 = 0b00000100; impl core::fmt::Display for Perm { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let v = self.0; if v & PERM_R != 0 { f.write_char('r')? } if v & PERM_W != 0 { f.write_char('w')? } if v & PERM_X != 0 { f.write_char('x')? } Ok(()) } } /// Represents an ELF segment at runtime. struct Segment { /// Gives the runtime virtual address of this segment's contents. addr: usize, /// Gives the memory size of this segment's contents. size: usize, /// Gives the module virtual address of this segment with the ELF file. mod_rel_addr: usize, /// Gives the permissions found in the ELF file. These permissions are not /// necessarily the permissions present at runtime however. flags: Perm, } /// Lets one iterate over Segments from a DSO. struct SegmentIter<'a> { phdrs: &'a [Elf_Phdr], base: usize, } impl Iterator for SegmentIter<'_> { type Item = Segment; fn next(&mut self) -> Option { self.phdrs.split_first().and_then(|(phdr, new_phdrs)| { self.phdrs = new_phdrs; if phdr.p_type != PT_LOAD { self.next() } else { Some(Segment { addr: phdr.p_vaddr as usize + self.base, size: phdr.p_memsz as usize, mod_rel_addr: phdr.p_vaddr as usize, flags: Perm(phdr.p_flags), }) } }) } } /// Represents an ELF DSO (Dynamic Shared Object). This type references /// the data stored in the actual DSO rather than making its own copy. struct Dso<'a> { /// The dynamic linker always gives us a name, even if the name is empty. /// In the case of the main executable this name will be empty. In the case /// of a shared object it will be the soname (see DT_SONAME). name: &'a str, /// On Fuchsia virtually all binaries have build IDs but this is not a strict /// requierment. There's no way to match up DSO information with a real ELF /// file afterwards if there is no build_id so we require that every DSO /// have one here. DSO's without a build_id are ignored. build_id: &'a [u8], base: usize, phdrs: &'a [Elf_Phdr], } impl Dso<'_> { /// Returns an iterator over Segments in this DSO. fn segments(&self) -> SegmentIter<'_> { SegmentIter { phdrs: self.phdrs.as_ref(), base: self.base, } } } struct HexSlice<'a> { bytes: &'a [u8], } impl fmt::Display for HexSlice<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for byte in self.bytes { write!(f, "{:02x}", byte)?; } Ok(()) } } fn get_build_id<'a>(info: &'a dl_phdr_info) -> Option<&'a [u8]> { for phdr in info.program_headers() { if phdr.phdr.p_type == PT_NOTE { for note in phdr.notes() { if note.tipe == NT_GNU_BUILD_ID && (note.name == b"GNU\0" || note.name == b"GNU") { return Some(note.desc); } } } } None } /// These errors encode issues that arise while parsing information about /// each DSO. enum Error { /// NameError means that an error occurred while converting a C style string /// into a rust string. NameError(core::str::Utf8Error), /// BuildIDError means that we didn't find a build ID. This could either be /// because the DSO had no build ID or because the segment containing the /// build ID was malformed. BuildIDError, } /// Calls either 'dso' or 'error' for each DSO linked into the process by the /// dynamic linker. /// /// # Arguments /// /// * `visitor` - A DsoPrinter that will have one of eats methods called foreach DSO. fn for_each_dso(mut visitor: &mut DsoPrinter) { extern "C" fn callback(info: &dl_phdr_info, _size: usize, visitor: &mut DsoPrinter) -> i32 { // dl_iterate_phdr ensures that info.name will point to a valid // location. let name_len = unsafe { libc::strlen(info.name) }; let name_slice: &[u8] = unsafe { core::slice::from_raw_parts(info.name as *const u8, name_len) }; let name = match core::str::from_utf8(name_slice) { Ok(name) => name, Err(err) => { return visitor.error(Error::NameError(err)) as i32; } }; let build_id = match get_build_id(info) { Some(build_id) => build_id, None => { return visitor.error(Error::BuildIDError) as i32; } }; visitor.dso(Dso { name: name, build_id: build_id, phdrs: info.phdr_slice(), base: info.addr as usize, }) as i32 } unsafe { dl_iterate_phdr(callback, &mut visitor) }; } struct DsoPrinter<'a, 'b> { writer: &'a mut core::fmt::Formatter<'b>, module_count: usize, error: core::fmt::Result, } impl DsoPrinter<'_, '_> { fn dso(&mut self, dso: Dso<'_>) -> bool { let mut write = || { write!( self.writer, "{{{{{{module:{:#x}:{}:elf:{}}}}}}}\n", self.module_count, dso.name, HexSlice { bytes: dso.build_id.as_ref() } )?; for seg in dso.segments() { write!( self.writer, "{{{{{{mmap:{:#x}:{:#x}:load:{:#x}:{}:{:#x}}}}}}}\n", seg.addr, seg.size, self.module_count, seg.flags, seg.mod_rel_addr )?; } self.module_count += 1; Ok(()) }; match write() { Ok(()) => false, Err(err) => { self.error = Err(err); true } } } fn error(&mut self, _error: Error) -> bool { false } } /// This function prints the Fuchsia symbolizer markup for all information contained in a DSO. pub fn print_dso_context(out: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { out.write_str("{{{reset}}}\n")?; let mut visitor = DsoPrinter { writer: out, module_count: 0, error: Ok(()), }; for_each_dso(&mut visitor); visitor.error } backtrace-0.3.44/src/symbolize/coresymbolication.rs010066400017500001750000000213131360710306200206410ustar0000000000000000// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Symbolication strategy that's OSX-specific and uses the `CoreSymbolication` //! framework, if possible. //! //! This strategy uses internal private APIs that are somewhat undocumented but //! seem to be widely used on OSX. This is the default symbolication strategy //! for OSX, but is turned off in release builds for iOS due to reports of apps //! being rejected due to using these APIs. //! //! This would probably be good to get official one day and not using private //! APIs, but for now it should largely suffice. //! //! Note that this module will dynamically load `CoreSymbolication` and its APIs //! through dlopen/dlsym, and if the loading fails this falls back to `dladdr` //! as a symbolication strategy. #![allow(bad_style)] use crate::symbolize::dladdr; use crate::symbolize::ResolveWhat; use crate::types::BytesOrWideString; use crate::SymbolName; use core::ffi::c_void; use core::mem; use core::ptr; use core::slice; use libc::{c_char, c_int}; #[repr(C)] #[derive(Copy, Clone, PartialEq)] pub struct CSTypeRef { cpp_data: *const c_void, cpp_obj: *const c_void, } const CS_NOW: u64 = 0x80000000; const CSREF_NULL: CSTypeRef = CSTypeRef { cpp_data: 0 as *const c_void, cpp_obj: 0 as *const c_void, }; pub enum Symbol<'a> { Core { path: *const c_char, lineno: u32, name: *const c_char, addr: *mut c_void, }, Dladdr(dladdr::Symbol<'a>), } impl Symbol<'_> { pub fn name(&self) -> Option { let name = match *self { Symbol::Core { name, .. } => name, Symbol::Dladdr(ref info) => return info.name(), }; if name.is_null() { None } else { Some(SymbolName::new(unsafe { let len = libc::strlen(name); slice::from_raw_parts(name as *const u8, len) })) } } pub fn addr(&self) -> Option<*mut c_void> { match *self { Symbol::Core { addr, .. } => Some(addr), Symbol::Dladdr(ref info) => info.addr(), } } fn filename_bytes(&self) -> Option<&[u8]> { match *self { Symbol::Core { path, .. } => { if path.is_null() { None } else { Some(unsafe { let len = libc::strlen(path); slice::from_raw_parts(path as *const u8, len) }) } } Symbol::Dladdr(_) => None, } } pub fn filename_raw(&self) -> Option { self.filename_bytes().map(BytesOrWideString::Bytes) } #[cfg(feature = "std")] pub fn filename(&self) -> Option<&::std::path::Path> { use std::ffi::OsStr; use std::os::unix::prelude::*; use std::path::Path; self.filename_bytes().map(OsStr::from_bytes).map(Path::new) } pub fn lineno(&self) -> Option { match *self { Symbol::Core { lineno: 0, .. } => None, Symbol::Core { lineno, .. } => Some(lineno), Symbol::Dladdr(_) => None, } } } macro_rules! coresymbolication { (#[load_path = $path:tt] extern "C" { $(fn $name:ident($($arg:ident: $argty:ty),*) -> $ret: ty;)* }) => ( pub struct CoreSymbolication { // The loaded dynamic library dll: *mut c_void, // Each function pointer for each function we might use $($name: usize,)* } static mut CORESYMBOLICATION: CoreSymbolication = CoreSymbolication { // Initially we haven't loaded the dynamic library dll: 0 as *mut _, // Initiall all functions are set to zero to say they need to be // dynamically loaded. $($name: 0,)* }; // Convenience typedef for each function type. $(pub type $name = unsafe extern "C" fn($($argty),*) -> $ret;)* impl CoreSymbolication { /// Attempts to open `dbghelp.dll`. Returns `true` if it works or /// `false` if `dlopen` fails. fn open(&mut self) -> bool { if !self.dll.is_null() { return true; } let lib = concat!($path, "\0").as_bytes(); unsafe { self.dll = libc::dlopen(lib.as_ptr() as *const _, libc::RTLD_LAZY); !self.dll.is_null() } } // Function for each method we'd like to use. When called it will // either read the cached function pointer or load it and return the // loaded value. Loads are asserted to succeed. $(pub fn $name(&mut self) -> $name { unsafe { if self.$name == 0 { let name = concat!(stringify!($name), "\0"); self.$name = self.symbol(name.as_bytes()) .expect(concat!("symbol ", stringify!($name), " is missing")); } mem::transmute::(self.$name) } })* fn symbol(&self, symbol: &[u8]) -> Option { unsafe { match libc::dlsym(self.dll, symbol.as_ptr() as *const _) as usize { 0 => None, n => Some(n), } } } } ) } coresymbolication! { #[load_path = "/System/Library/PrivateFrameworks/CoreSymbolication.framework\ /Versions/A/CoreSymbolication"] extern "C" { fn CSSymbolicatorCreateWithPid(pid: c_int) -> CSTypeRef; fn CSRelease(rf: CSTypeRef) -> (); fn CSSymbolicatorGetSymbolWithAddressAtTime( cs: CSTypeRef, addr: *const c_void, time: u64) -> CSTypeRef; fn CSSymbolicatorGetSourceInfoWithAddressAtTime( cs: CSTypeRef, addr: *const c_void, time: u64) -> CSTypeRef; fn CSSourceInfoGetLineNumber(info: CSTypeRef) -> c_int; fn CSSourceInfoGetPath(info: CSTypeRef) -> *const c_char; fn CSSourceInfoGetSymbol(info: CSTypeRef) -> CSTypeRef; fn CSSymbolGetMangledName(sym: CSTypeRef) -> *const c_char; fn CSSymbolGetSymbolOwner(sym: CSTypeRef) -> CSTypeRef; fn CSSymbolOwnerGetBaseAddress(symowner: CSTypeRef) -> *mut c_void; } } unsafe fn try_resolve(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) -> bool { // Note that this is externally synchronized so there's no need for // synchronization here, making this `static mut` safer. let lib = &mut CORESYMBOLICATION; if !lib.open() { return false; } let cs = lib.CSSymbolicatorCreateWithPid()(libc::getpid()); if cs == CSREF_NULL { return false; } let _dtor = OwnedCSTypeRef { ptr: cs, CSRelease: lib.CSRelease(), }; let info = lib.CSSymbolicatorGetSourceInfoWithAddressAtTime()(cs, addr, CS_NOW); let sym = if info == CSREF_NULL { lib.CSSymbolicatorGetSymbolWithAddressAtTime()(cs, addr, CS_NOW) } else { lib.CSSourceInfoGetSymbol()(info) }; if sym == CSREF_NULL { return false; } let owner = lib.CSSymbolGetSymbolOwner()(sym); if owner == CSREF_NULL { return false; } cb(&super::Symbol { inner: Symbol::Core { path: if info != CSREF_NULL { lib.CSSourceInfoGetPath()(info) } else { ptr::null() }, lineno: if info != CSREF_NULL { lib.CSSourceInfoGetLineNumber()(info) as u32 } else { 0 }, name: lib.CSSymbolGetMangledName()(sym), addr: lib.CSSymbolOwnerGetBaseAddress()(owner), }, }); true } struct OwnedCSTypeRef { ptr: CSTypeRef, CSRelease: unsafe extern "C" fn(CSTypeRef), } impl Drop for OwnedCSTypeRef { fn drop(&mut self) { unsafe { (self.CSRelease)(self.ptr); } } } pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) { let addr = what.address_or_ip(); if try_resolve(addr, cb) { return; } dladdr::resolve(addr, &mut |sym| { cb(&super::Symbol { inner: Symbol::Dladdr(sym), }) }) } backtrace-0.3.44/src/symbolize/dbghelp.rs010066400017500001750000000161411360710306200165240ustar0000000000000000// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Symbolication strategy using `dbghelp.dll` on Windows, only used for MSVC //! //! This symbolication strategy, like with backtraces, uses dynamically loaded //! information from `dbghelp.dll`. (see `src/dbghelp.rs` for info about why //! it's dynamically loaded). //! //! This API selects its resolution strategy based on the frame provided or the //! information we have at hand. If a frame from `StackWalkEx` is given to us //! then we use similar APIs to generate correct information about inlined //! functions. Otherwise if all we have is an address or an older stack frame //! from `StackWalk64` we use the older APIs for symbolication. //! //! There's a good deal of support in this module, but a good chunk of it is //! converting back and forth between Windows types and Rust types. For example //! symbols come to us as wide strings which we then convert to utf-8 strings if //! we can. #![allow(bad_style)] use crate::backtrace::FrameImp as Frame; use crate::dbghelp; use crate::symbolize::ResolveWhat; use crate::types::BytesOrWideString; use crate::windows::*; use crate::SymbolName; use core::char; use core::ffi::c_void; use core::marker; use core::mem; use core::slice; // Store an OsString on std so we can provide the symbol name and filename. pub struct Symbol<'a> { name: *const [u8], addr: *mut c_void, line: Option, filename: Option<*const [u16]>, #[cfg(feature = "std")] _filename_cache: Option<::std::ffi::OsString>, #[cfg(not(feature = "std"))] _filename_cache: (), _marker: marker::PhantomData<&'a i32>, } impl Symbol<'_> { pub fn name(&self) -> Option { Some(SymbolName::new(unsafe { &*self.name })) } pub fn addr(&self) -> Option<*mut c_void> { Some(self.addr as *mut _) } pub fn filename_raw(&self) -> Option { self.filename .map(|slice| unsafe { BytesOrWideString::Wide(&*slice) }) } pub fn lineno(&self) -> Option { self.line } #[cfg(feature = "std")] pub fn filename(&self) -> Option<&::std::path::Path> { use std::path::Path; self._filename_cache.as_ref().map(Path::new) } } #[repr(C, align(8))] struct Aligned8(T); pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) { // Ensure this process's symbols are initialized let dbghelp = match dbghelp::init() { Ok(dbghelp) => dbghelp, Err(()) => return, // oh well... }; match what { ResolveWhat::Address(_) => resolve_without_inline(&dbghelp, what.address_or_ip(), cb), ResolveWhat::Frame(frame) => match &frame.inner { Frame::New(frame) => resolve_with_inline(&dbghelp, frame, cb), Frame::Old(_) => resolve_without_inline(&dbghelp, frame.ip(), cb), }, } } unsafe fn resolve_with_inline( dbghelp: &dbghelp::Init, frame: &STACKFRAME_EX, cb: &mut FnMut(&super::Symbol), ) { do_resolve( |info| { dbghelp.SymFromInlineContextW()( GetCurrentProcess(), super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64, frame.InlineFrameContext, &mut 0, info, ) }, |line| { dbghelp.SymGetLineFromInlineContextW()( GetCurrentProcess(), super::adjust_ip(frame.AddrPC.Offset as *mut _) as u64, frame.InlineFrameContext, 0, &mut 0, line, ) }, cb, ) } unsafe fn resolve_without_inline( dbghelp: &dbghelp::Init, addr: *mut c_void, cb: &mut FnMut(&super::Symbol), ) { do_resolve( |info| dbghelp.SymFromAddrW()(GetCurrentProcess(), addr as DWORD64, &mut 0, info), |line| dbghelp.SymGetLineFromAddrW64()(GetCurrentProcess(), addr as DWORD64, &mut 0, line), cb, ) } unsafe fn do_resolve( sym_from_addr: impl FnOnce(*mut SYMBOL_INFOW) -> BOOL, get_line_from_addr: impl FnOnce(&mut IMAGEHLP_LINEW64) -> BOOL, cb: &mut FnMut(&super::Symbol), ) { const SIZE: usize = 2 * MAX_SYM_NAME + mem::size_of::(); let mut data = Aligned8([0u8; SIZE]); let data = &mut data.0; let info = &mut *(data.as_mut_ptr() as *mut SYMBOL_INFOW); info.MaxNameLen = MAX_SYM_NAME as ULONG; // the struct size in C. the value is different to // `size_of::() - MAX_SYM_NAME + 1` (== 81) // due to struct alignment. info.SizeOfStruct = 88; if sym_from_addr(info) != TRUE { return; } // If the symbol name is greater than MaxNameLen, SymFromAddrW will // give a buffer of (MaxNameLen - 1) characters and set NameLen to // the real value. let name_len = ::core::cmp::min(info.NameLen as usize, info.MaxNameLen as usize - 1); let name_ptr = info.Name.as_ptr() as *const u16; let name = slice::from_raw_parts(name_ptr, name_len); // Reencode the utf-16 symbol to utf-8 so we can use `SymbolName::new` like // all other platforms let mut name_len = 0; let mut name_buffer = [0; 256]; { let mut remaining = &mut name_buffer[..]; for c in char::decode_utf16(name.iter().cloned()) { let c = c.unwrap_or(char::REPLACEMENT_CHARACTER); let len = c.len_utf8(); if len < remaining.len() { c.encode_utf8(remaining); let tmp = remaining; remaining = &mut tmp[len..]; name_len += len; } else { break; } } } let name = &name_buffer[..name_len] as *const [u8]; let mut line = mem::zeroed::(); line.SizeOfStruct = mem::size_of::() as DWORD; let mut filename = None; let mut lineno = None; if get_line_from_addr(&mut line) == TRUE { lineno = Some(line.LineNumber as u32); let base = line.FileName; let mut len = 0; while *base.offset(len) != 0 { len += 1; } let len = len as usize; filename = Some(slice::from_raw_parts(base, len) as *const [u16]); } cb(&super::Symbol { inner: Symbol { name, addr: info.Address as *mut _, line: lineno, filename, _filename_cache: cache(filename), _marker: marker::PhantomData, }, }) } #[cfg(feature = "std")] unsafe fn cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString> { use std::os::windows::ffi::OsStringExt; filename.map(|f| ::std::ffi::OsString::from_wide(&*f)) } #[cfg(not(feature = "std"))] unsafe fn cache(_filename: Option<*const [u16]>) {} backtrace-0.3.44/src/symbolize/dladdr.rs010066400017500001750000000066511360710306200163560ustar0000000000000000// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Common support for resolving with `dladdr`, often used as a fallback if //! other strategies don't work. #![allow(dead_code)] cfg_if::cfg_if! { if #[cfg(all(unix, not(target_os = "emscripten"), feature = "dladdr"))] { use core::ffi::c_void; use core::marker; use core::{mem, slice}; use crate::SymbolName; use crate::types::BytesOrWideString; use libc::{self, Dl_info}; pub struct Symbol<'a> { inner: Dl_info, _marker: marker::PhantomData<&'a i32>, } impl Symbol<'_> { pub fn name(&self) -> Option { if self.inner.dli_sname.is_null() { None } else { let ptr = self.inner.dli_sname as *const u8; unsafe { let len = libc::strlen(self.inner.dli_sname); Some(SymbolName::new(slice::from_raw_parts(ptr, len))) } } } pub fn addr(&self) -> Option<*mut c_void> { Some(self.inner.dli_saddr as *mut _) } pub fn filename_raw(&self) -> Option { None } #[cfg(feature = "std")] pub fn filename(&self) -> Option<&::std::path::Path> { None } pub fn lineno(&self) -> Option { None } } pub unsafe fn resolve(addr: *mut c_void, cb: &mut FnMut(Symbol<'static>)) { let mut info = Symbol { inner: mem::zeroed(), _marker: marker::PhantomData, }; // Skip null addresses to avoid calling into libc and having it do // things with the dynamic symbol table for no reason. if !addr.is_null() && libc::dladdr(addr as *mut _, &mut info.inner) != 0 { cb(info) } } } else { use core::ffi::c_void; use core::marker; use crate::symbolize::SymbolName; use crate::types::BytesOrWideString; pub struct Symbol<'a> { a: Void, _b: marker::PhantomData<&'a i32>, } enum Void {} impl Symbol<'_> { pub fn name(&self) -> Option { match self.a {} } pub fn addr(&self) -> Option<*mut c_void> { match self.a {} } pub fn filename_raw(&self) -> Option { match self.a {} } #[cfg(feature = "std")] pub fn filename(&self) -> Option<&::std::path::Path> { match self.a {} } pub fn lineno(&self) -> Option { match self.a {} } } pub unsafe fn resolve(addr: *mut c_void, cb: &mut FnMut(Symbol<'static>)) { drop((addr, cb)); } } } backtrace-0.3.44/src/symbolize/dladdr_resolve.rs010066400017500001750000000027771360710306200201220ustar0000000000000000// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Symbolication strategy using `dladdr` //! //! The `dladdr` API is available on most Unix implementations but it's quite //! basic, not handling inline frame information at all. Since it's so prevalent //! though we have an option to use it! use crate::symbolize::{dladdr, ResolveWhat, SymbolName}; use crate::types::BytesOrWideString; use core::ffi::c_void; pub struct Symbol<'a>(dladdr::Symbol<'a>); impl Symbol<'_> { pub fn name(&self) -> Option { self.0.name() } pub fn addr(&self) -> Option<*mut c_void> { self.0.addr() } pub fn filename_raw(&self) -> Option { self.0.filename_raw() } #[cfg(feature = "std")] pub fn filename(&self) -> Option<&::std::path::Path> { self.0.filename() } pub fn lineno(&self) -> Option { self.0.lineno() } } pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) { dladdr::resolve(what.address_or_ip(), &mut |sym| { cb(&super::Symbol { inner: Symbol(sym) }) }); } backtrace-0.3.44/src/symbolize/gimli.rs010066400017500001750000000603631360763075500162430ustar0000000000000000//! Support for symbolication using the `gimli` crate on crates.io //! //! This implementation is largely a work in progress and is off by default for //! all platforms, but it's hoped to be developed over time! Long-term this is //! intended to wholesale replace the `libbacktrace.rs` implementation. use self::gimli::read::EndianSlice; use self::gimli::LittleEndian as Endian; use crate::symbolize::dladdr; use crate::symbolize::ResolveWhat; use crate::types::BytesOrWideString; use crate::SymbolName; use addr2line::gimli; use core::mem; use core::u32; use findshlibs::{self, Segment, SharedLibrary}; use libc::c_void; use memmap::Mmap; use std::env; use std::ffi::OsString; use std::fs::File; use std::path::Path; use std::prelude::v1::*; const MAPPINGS_CACHE_SIZE: usize = 4; struct Context<'a> { dwarf: addr2line::Context>, object: Object<'a>, } struct Mapping { // 'static lifetime is a lie to hack around lack of support for self-referential structs. cx: Context<'static>, _map: Mmap, } fn cx<'data>(object: Object<'data>) -> Option> { fn load_section<'data, S>(obj: &Object<'data>) -> S where S: gimli::Section>, { let data = obj.section(S::section_name()).unwrap_or(&[]); S::from(EndianSlice::new(data, Endian)) } let dwarf = addr2line::Context::from_sections( load_section(&object), load_section(&object), load_section(&object), load_section(&object), load_section(&object), load_section(&object), load_section(&object), load_section(&object), load_section(&object), gimli::EndianSlice::new(&[], Endian), ) .ok()?; Some(Context { dwarf, object }) } fn assert_lifetimes<'a>(_: &'a Mmap, _: &Context<'a>) {} macro_rules! mk { (Mapping { $map:expr, $inner:expr }) => {{ assert_lifetimes(&$map, &$inner); Mapping { // Convert to 'static lifetimes since the symbols should // only borrow `map` and we're preserving `map` below. cx: unsafe { mem::transmute::, Context<'static>>($inner) }, _map: $map, } }}; } fn mmap(path: &Path) -> Option { let file = File::open(path).ok()?; // TODO: not completely safe, see https://github.com/danburkert/memmap-rs/issues/25 unsafe { Mmap::map(&file).ok() } } cfg_if::cfg_if! { if #[cfg(windows)] { use goblin::pe::{self, PE}; use goblin::strtab::Strtab; use std::cmp; use std::convert::TryFrom; struct Object<'a> { pe: PE<'a>, data: &'a [u8], symbols: Vec<(usize, pe::symbol::Symbol)>, strtab: Strtab<'a>, } impl<'a> Object<'a> { fn parse(data: &'a [u8]) -> Option> { let pe = PE::parse(data).ok()?; let syms = pe.header.coff_header.symbols(data).ok()?; let strtab = pe.header.coff_header.strings(data).ok()?; // Collect all the symbols into a local vector which is sorted // by address and contains enough data to learn about the symbol // name. Note that we only look at function symbols and also // note that the sections are 1-indexed because the zero section // is special (apparently). let mut symbols = Vec::new(); for (_, _, sym) in syms.iter() { if sym.derived_type() != pe::symbol::IMAGE_SYM_DTYPE_FUNCTION || sym.section_number == 0 { continue; } let addr = usize::try_from(sym.value).ok()?; let section = pe.sections.get(usize::try_from(sym.section_number).ok()? - 1)?; let va = usize::try_from(section.virtual_address).ok()?; symbols.push((addr + va + pe.image_base, sym)); } symbols.sort_unstable_by_key(|x| x.0); Some(Object { pe, data, symbols, strtab }) } fn section(&self, name: &str) -> Option<&'a [u8]> { let section = self.pe .sections .iter() .find(|section| section.name().ok() == Some(name)); section .and_then(|section| { let offset = section.pointer_to_raw_data as usize; let size = cmp::min(section.virtual_size, section.size_of_raw_data) as usize; self.data.get(offset..).and_then(|data| data.get(..size)) }) } fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> { // Note that unlike other formats COFF doesn't embed the size of // each symbol. As a last ditch effort search for the *closest* // symbol to a particular address and return that one. This gets // really wonky once symbols start getting removed because the // symbols returned here can be totally incorrect, but we have // no idea of knowing how to detect that. let addr = usize::try_from(addr).ok()?; let i = match self.symbols.binary_search_by_key(&addr, |p| p.0) { Ok(i) => i, // typically `addr` isn't in the array, but `i` is where // we'd insert it, so the previous position must be the // greatest less than `addr` Err(i) => i.checked_sub(1)?, }; Some(self.symbols[i].1.name(&self.strtab).ok()?.as_bytes()) } } } else if #[cfg(target_os = "macos")] { use goblin::mach::MachO; struct Object<'a> { macho: MachO<'a>, dwarf: Option, } impl<'a> Object<'a> { fn parse(macho: MachO<'a>) -> Option> { if !macho.little_endian { return None; } let dwarf = macho .segments .iter() .enumerate() .find(|(_, segment)| segment.name().ok() == Some("__DWARF")) .map(|p| p.0); Some(Object { macho, dwarf }) } fn section(&self, name: &str) -> Option<&'a [u8]> { let dwarf = self.dwarf?; let dwarf = &self.macho.segments[dwarf]; dwarf .into_iter() .filter_map(|s| s.ok()) .find(|(section, _data)| { let section_name = match section.name() { Ok(s) => s, Err(_) => return false, }; §ion_name[..] == name || { section_name.starts_with("__") && name.starts_with(".") && §ion_name[2..] == &name[1..] } }) .map(|p| p.1) } fn search_symtab<'b>(&'b self, _addr: u64) -> Option<&'b [u8]> { // So far it seems that we don't need to implement this. Maybe // `dladdr` on OSX has us covered? Maybe there's not much in the // symbol table? In any case our relevant tests are passing // without this being implemented, so let's skip it for now. None } } } else { use goblin::elf::Elf; struct Object<'a> { elf: Elf<'a>, data: &'a [u8], // List of pre-parsed and sorted symbols by base address. The // boolean indicates whether it comes from the dynamic symbol table // or the normal symbol table, affecting where it's symbolicated. syms: Vec<(goblin::elf::Sym, bool)>, } impl<'a> Object<'a> { fn parse(data: &'a [u8]) -> Option> { let elf = Elf::parse(data).ok()?; if !elf.little_endian { return None; } let mut syms = elf .syms .iter() .map(|s| (s, false)) .chain(elf.dynsyms.iter().map(|s| (s, true))) // Only look at function/object symbols. This mirrors what // libbacktrace does and in general we're only symbolicating // function addresses in theory. Object symbols correspond // to data, and maybe someone's crazy enough to have a // function go into static data? .filter(|(s, _)| { s.is_function() || s.st_type() == goblin::elf::sym::STT_OBJECT }) // skip anything that's in an undefined section header, // since it means it's an imported function and we're only // symbolicating with locally defined functions. .filter(|(s, _)| { s.st_shndx != goblin::elf::section_header::SHN_UNDEF as usize }) .collect::>(); syms.sort_unstable_by_key(|s| s.0.st_value); Some(Object { syms, elf, data, }) } fn section(&self, name: &str) -> Option<&'a [u8]> { let section = self.elf.section_headers.iter().find(|section| { match self.elf.shdr_strtab.get(section.sh_name) { Some(Ok(section_name)) => section_name == name, _ => false, } }); section .and_then(|section| { self.data.get(section.sh_offset as usize..) .and_then(|data| data.get(..section.sh_size as usize)) }) } fn search_symtab<'b>(&'b self, addr: u64) -> Option<&'b [u8]> { // Same sort of binary search as Windows above let i = match self.syms.binary_search_by_key(&addr, |s| s.0.st_value) { Ok(i) => i, Err(i) => i.checked_sub(1)?, }; let (sym, dynamic) = self.syms.get(i)?; if sym.st_value <= addr && addr <= sym.st_value + sym.st_size { let strtab = if *dynamic { &self.elf.dynstrtab } else { &self.elf.strtab }; Some(strtab.get(sym.st_name)?.ok()?.as_bytes()) } else { None } } } } } impl Mapping { #[cfg(not(target_os = "macos"))] fn new(path: &Path) -> Option { let map = mmap(path)?; let cx = cx(Object::parse(&map)?)?; Some(mk!(Mapping { map, cx })) } // The loading path for OSX is is so different we just have a completely // different implementation of the function here. On OSX we need to go // probing the filesystem for a bunch of files. #[cfg(target_os = "macos")] fn new(path: &Path) -> Option { // First up we need to load the unique UUID which is stored in the macho // header of the file we're reading, specified at `path`. let map = mmap(path)?; let macho = MachO::parse(&map, 0).ok()?; let uuid = find_uuid(&macho)?; // Next we need to look for a `*.dSYM` file. For now we just probe the // containing directory and look around for something that matches // `*.dSYM`. Once it's found we root through the dwarf resources that it // contains and try to find a macho file which has a matching UUID as // the one of our own file. If we find a match that's the dwarf file we // want to return. let parent = path.parent()?; for entry in parent.read_dir().ok()? { let entry = entry.ok()?; let filename = match entry.file_name().into_string() { Ok(name) => name, Err(_) => continue, }; if !filename.ends_with(".dSYM") { continue; } let candidates = entry.path().join("Contents/Resources/DWARF"); if let Some(mapping) = load_dsym(&candidates, &uuid) { return Some(mapping); } } // Looks like nothing matched our UUID, so let's at least return our own // file. This should have the symbol table for at least some // symbolication purposes. let inner = cx(Object::parse(macho)?)?; return Some(mk!(Mapping { map, inner })); fn load_dsym(dir: &Path, uuid: &[u8; 16]) -> Option { for entry in dir.read_dir().ok()? { let entry = entry.ok()?; let map = mmap(&entry.path())?; let macho = MachO::parse(&map, 0).ok()?; let entry_uuid = find_uuid(&macho)?; if entry_uuid != uuid { continue; } if let Some(cx) = Object::parse(macho).and_then(cx) { return Some(mk!(Mapping { map, cx })); } } None } fn find_uuid<'a>(object: &'a MachO) -> Option<&'a [u8; 16]> { use goblin::mach::load_command::CommandVariant; object .load_commands .iter() .filter_map(|cmd| match &cmd.command { CommandVariant::Uuid(u) => Some(&u.uuid), _ => None, }) .next() } } } #[derive(Default)] struct Cache { /// All known shared libraries that have been loaded. libraries: Vec, /// Mappings cache where we retain parsed dwarf information. /// /// This list has a fixed capacity for its entire liftime which never /// increases. The `usize` element of each pair is an index into `libraries` /// above where `usize::max_value()` represents the current executable. The /// `Mapping` is corresponding parsed dwarf information. /// /// Note that this is basically an LRU cache and we'll be shifting things /// around in here as we symbolize addresses. mappings: Vec<(usize, Mapping)>, } struct Library { name: OsString, segments: Vec, bias: findshlibs::Bias, } struct LibrarySegment { len: usize, stated_virtual_memory_address: findshlibs::Svma, } // unsafe because this is required to be externally synchronized pub unsafe fn clear_symbol_cache() { Cache::with_global(|cache| cache.mappings.clear()); } impl Cache { fn new() -> Cache { let mut libraries = Vec::new(); // Iterate over all loaded shared libraries and cache information we // learn about them. This way we only load information here once instead // of once per symbol. findshlibs::TargetSharedLibrary::each(|so| { use findshlibs::IterationControl::*; libraries.push(Library { name: so.name().to_owned(), segments: so .segments() .map(|s| LibrarySegment { len: s.len(), stated_virtual_memory_address: s.stated_virtual_memory_address(), }) .collect(), bias: so.virtual_memory_bias(), }); Continue }); Cache { mappings: Vec::with_capacity(MAPPINGS_CACHE_SIZE), libraries, } } // unsafe because this is required to be externally synchronized unsafe fn with_global(f: impl FnOnce(&mut Self)) { // A very small, very simple LRU cache for debug info mappings. // // The hit rate should be very high, since the typical stack doesn't cross // between many shared libraries. // // The `addr2line::Context` structures are pretty expensive to create. Its // cost is expected to be amortized by subsequent `locate` queries, which // leverage the structures built when constructing `addr2line::Context`s to // get nice speedups. If we didn't have this cache, that amortization would // never happen, and symbolicating backtraces would be ssssllllooooowwww. static mut MAPPINGS_CACHE: Option = None; f(MAPPINGS_CACHE.get_or_insert_with(|| Cache::new())) } fn avma_to_svma(&self, addr: *const u8) -> Option<(usize, findshlibs::Svma)> { // Note that for now `findshlibs` is unimplemented on Windows, so we // just unhelpfully assume that the address is an SVMA. Surprisingly it // seems to at least somewhat work on Wine on Linux though... // // This probably means ASLR on Windows is busted. if cfg!(windows) { let addr = findshlibs::Svma(addr); return Some((usize::max_value(), addr)); } self.libraries .iter() .enumerate() .filter_map(|(i, lib)| { // First up, test if this `lib` has any segment containing the // `addr` (handling relocation). If this check passes then we // can continue below and actually translate the address. // // Note that this is an inlining of `contains_avma` in the // `findshlibs` crate here. if !lib.segments.iter().any(|s| { let svma = s.stated_virtual_memory_address; let start = unsafe { svma.0.offset(lib.bias.0) as usize }; let end = start + s.len; let address = addr as usize; start <= address && address < end }) { return None; } // Now that we know `lib` contains `addr`, do the equiavlent of // `avma_to_svma` in the `findshlibs` crate. let reverse_bias = -lib.bias.0; let svma = findshlibs::Svma(unsafe { addr.offset(reverse_bias) }); Some((i, svma)) }) .next() } fn mapping_for_lib<'a>(&'a mut self, lib: usize) -> Option<&'a Context<'a>> { let idx = self.mappings.iter().position(|(idx, _)| *idx == lib); // Invariant: after this conditional completes without early returning // from an error, the cache entry for this path is at index 0. if let Some(idx) = idx { // When the mapping is already in the cache, move it to the front. if idx != 0 { let entry = self.mappings.remove(idx); self.mappings.insert(0, entry); } } else { // When the mapping is not in the cache, create a new mapping, // insert it into the front of the cache, and evict the oldest cache // entry if necessary. let storage; let path = match self.libraries.get(lib) { Some(lib) => &lib.name, None => { storage = env::current_exe().ok()?.into(); &storage } }; let mapping = Mapping::new(path.as_ref())?; if self.mappings.len() == MAPPINGS_CACHE_SIZE { self.mappings.pop(); } self.mappings.insert(0, (lib, mapping)); } let cx: &'a Context<'static> = &self.mappings[0].1.cx; // don't leak the `'static` lifetime, make sure it's scoped to just // ourselves Some(unsafe { mem::transmute::<&'a Context<'static>, &'a Context<'a>>(cx) }) } } pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) { let addr = what.address_or_ip(); let mut cb = DladdrFallback { cb, addr, called: false, }; Cache::with_global(|cache| { let (lib, addr) = match cache.avma_to_svma(addr as *const u8) { Some(pair) => pair, None => return, }; // Finally, get a cached mapping or create a new mapping for this file, and // evaluate the DWARF info to find the file/line/name for this address. let cx = match cache.mapping_for_lib(lib) { Some(cx) => cx, None => return, }; if let Ok(mut frames) = cx.dwarf.find_frames(addr.0 as u64) { while let Ok(Some(frame)) = frames.next() { cb.call(Symbol::Frame { addr: addr.0 as *mut c_void, location: frame.location, name: frame.function.map(|f| f.name.slice()), }); } } if !cb.called { if let Some(name) = cx.object.search_symtab(addr.0 as u64) { cb.call(Symbol::Symtab { addr: addr.0 as *mut c_void, name, }); } } }); drop(cb); } struct DladdrFallback<'a, 'b> { addr: *mut c_void, called: bool, cb: &'a mut (FnMut(&super::Symbol) + 'b), } impl DladdrFallback<'_, '_> { fn call(&mut self, sym: Symbol) { self.called = true; // Extend the lifetime of `sym` to `'static` since we are unfortunately // required to here, but it's ony ever going out as a reference so no // reference to it should be persisted beyond this frame anyway. let sym = unsafe { mem::transmute::>(sym) }; (self.cb)(&super::Symbol { inner: sym }); } } impl Drop for DladdrFallback<'_, '_> { fn drop(&mut self) { if self.called { return; } unsafe { dladdr::resolve(self.addr, &mut |sym| { (self.cb)(&super::Symbol { inner: Symbol::Dladdr(sym), }) }); } } } pub enum Symbol<'a> { /// We were able to locate frame information for this symbol, and /// `addr2line`'s frame internally has all the nitty gritty details. Frame { addr: *mut c_void, location: Option>, name: Option<&'a [u8]>, }, /// Couldn't find debug information, but we found it in the symbol table of /// the elf executable. Symtab { addr: *mut c_void, name: &'a [u8] }, /// We weren't able to find anything in the original file, so we had to fall /// back to using `dladdr` which had a hit. Dladdr(dladdr::Symbol<'a>), } impl Symbol<'_> { pub fn name(&self) -> Option { match self { Symbol::Dladdr(s) => s.name(), Symbol::Frame { name, .. } => { let name = name.as_ref()?; Some(SymbolName::new(name)) } Symbol::Symtab { name, .. } => Some(SymbolName::new(name)), } } pub fn addr(&self) -> Option<*mut c_void> { match self { Symbol::Dladdr(s) => s.addr(), Symbol::Frame { addr, .. } => Some(*addr), Symbol::Symtab { .. } => None, } } pub fn filename_raw(&self) -> Option { match self { Symbol::Dladdr(s) => return s.filename_raw(), Symbol::Frame { location, .. } => { let file = location.as_ref()?.file?; Some(BytesOrWideString::Bytes(file.as_bytes())) } Symbol::Symtab { .. } => None, } } pub fn filename(&self) -> Option<&Path> { match self { Symbol::Dladdr(s) => return s.filename(), Symbol::Frame { location, .. } => { let file = location.as_ref()?.file?; Some(Path::new(file)) } Symbol::Symtab { .. } => None, } } pub fn lineno(&self) -> Option { match self { Symbol::Dladdr(s) => return s.lineno(), Symbol::Frame { location, .. } => location.as_ref()?.line, Symbol::Symtab { .. } => None, } } } backtrace-0.3.44/src/symbolize/libbacktrace.rs010066400017500001750000000426471360710306200175370ustar0000000000000000// Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT // file at the top-level directory of this distribution and at // http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! Symbolication strategy using the DWARF-parsing code in libbacktrace. //! //! The libbacktrace C library, typically distributed with gcc, supports not //! only generating a backtrace (which we don't actually use) but also //! symbolicating the backtrace and handling dwarf debug information about //! things like inlined frames and whatnot. //! //! This is relatively complicated due to lots of various concerns here, but the //! basic idea is: //! //! * First we call `backtrace_syminfo`. This gets symbol information from the //! dynamic symbol table if we can. //! * Next we call `backtrace_pcinfo`. This will parse debuginfo tables if //! they're available and allow us to recover information about inline frames, //! filenames, line numbers, etc. //! //! There's lots of trickery about getting the dwarf tables into libbacktrace, //! but hopefully it's not the end of the world and is clear enough when reading //! below. //! //! This is the default symbolication strategy for non-MSVC and non-OSX //! platforms. In libstd though this is the default strategy for OSX. #![allow(bad_style)] extern crate backtrace_sys as bt; use core::{ptr, slice}; use libc::{self, c_char, c_int, c_void, uintptr_t}; use crate::symbolize::dladdr; use crate::symbolize::{ResolveWhat, SymbolName}; use crate::types::BytesOrWideString; pub enum Symbol<'a> { Syminfo { pc: uintptr_t, symname: *const c_char, }, Pcinfo { pc: uintptr_t, filename: *const c_char, lineno: c_int, function: *const c_char, symname: *const c_char, }, Dladdr(dladdr::Symbol<'a>), } impl Symbol<'_> { pub fn name(&self) -> Option { let symbol = |ptr: *const c_char| unsafe { if ptr.is_null() { None } else { let len = libc::strlen(ptr); Some(SymbolName::new(slice::from_raw_parts( ptr as *const u8, len, ))) } }; match *self { Symbol::Syminfo { symname, .. } => symbol(symname), Symbol::Pcinfo { function, symname, .. } => { // If possible prefer the `function` name which comes from // debuginfo and can typically be more accurate for inline // frames for example. If that's not present though fall back to // the symbol table name specified in `symname`. // // Note that sometimes `function` can feel somewhat less // accurate, for example being listed as `try` // isntead of `std::panicking::try::do_call`. It's not really // clear why, but overall the `function` name seems more accurate. if let Some(sym) = symbol(function) { return Some(sym); } symbol(symname) } Symbol::Dladdr(ref s) => s.name(), } } pub fn addr(&self) -> Option<*mut c_void> { let pc = match *self { Symbol::Syminfo { pc, .. } => pc, Symbol::Pcinfo { pc, .. } => pc, Symbol::Dladdr(ref s) => return s.addr(), }; if pc == 0 { None } else { Some(pc as *mut _) } } fn filename_bytes(&self) -> Option<&[u8]> { match *self { Symbol::Syminfo { .. } => None, Symbol::Pcinfo { filename, .. } => { let ptr = filename as *const u8; unsafe { let len = libc::strlen(filename); Some(slice::from_raw_parts(ptr, len)) } } Symbol::Dladdr(_) => None, } } pub fn filename_raw(&self) -> Option { self.filename_bytes().map(BytesOrWideString::Bytes) } #[cfg(feature = "std")] pub fn filename(&self) -> Option<&::std::path::Path> { use std::path::Path; #[cfg(unix)] fn bytes2path(bytes: &[u8]) -> Option<&Path> { use std::ffi::OsStr; use std::os::unix::prelude::*; Some(Path::new(OsStr::from_bytes(bytes))) } #[cfg(windows)] fn bytes2path(bytes: &[u8]) -> Option<&Path> { use std::str; str::from_utf8(bytes).ok().map(Path::new) } self.filename_bytes().and_then(bytes2path) } pub fn lineno(&self) -> Option { match *self { Symbol::Syminfo { .. } => None, Symbol::Pcinfo { lineno, .. } => Some(lineno as u32), Symbol::Dladdr(ref s) => s.lineno(), } } } extern "C" fn error_cb(_data: *mut c_void, _msg: *const c_char, _errnum: c_int) { // do nothing for now } /// Type of the `data` pointer passed into `syminfo_cb` struct SyminfoState<'a> { cb: &'a mut (FnMut(&super::Symbol) + 'a), pc: usize, } extern "C" fn syminfo_cb( data: *mut c_void, pc: uintptr_t, symname: *const c_char, _symval: uintptr_t, _symsize: uintptr_t, ) { let mut bomb = crate::Bomb { enabled: true }; // Once this callback is invoked from `backtrace_syminfo` when we start // resolving we go further to call `backtrace_pcinfo`. The // `backtrace_pcinfo` function will consult debug information and attemp tto // do things like recover file/line information as well as inlined frames. // Note though that `backtrace_pcinfo` can fail or not do much if there's // not debug info, so if that happens we're sure to call the callback with // at least one symbol from the `syminfo_cb`. unsafe { let syminfo_state = &mut *(data as *mut SyminfoState); let mut pcinfo_state = PcinfoState { symname, called: false, cb: syminfo_state.cb, }; bt::backtrace_pcinfo( init_state(), syminfo_state.pc as uintptr_t, pcinfo_cb, error_cb, &mut pcinfo_state as *mut _ as *mut _, ); if !pcinfo_state.called { (pcinfo_state.cb)(&super::Symbol { inner: Symbol::Syminfo { pc: pc, symname: symname, }, }); } } bomb.enabled = false; } /// Type of the `data` pointer passed into `pcinfo_cb` struct PcinfoState<'a> { cb: &'a mut (FnMut(&super::Symbol) + 'a), symname: *const c_char, called: bool, } extern "C" fn pcinfo_cb( data: *mut c_void, pc: uintptr_t, filename: *const c_char, lineno: c_int, function: *const c_char, ) -> c_int { if filename.is_null() || function.is_null() { return -1; } let mut bomb = crate::Bomb { enabled: true }; unsafe { let state = &mut *(data as *mut PcinfoState); state.called = true; (state.cb)(&super::Symbol { inner: Symbol::Pcinfo { pc: pc, filename: filename, lineno: lineno, symname: state.symname, function, }, }); } bomb.enabled = false; return 0; } // The libbacktrace API supports creating a state, but it does not // support destroying a state. I personally take this to mean that a // state is meant to be created and then live forever. // // I would love to register an at_exit() handler which cleans up this // state, but libbacktrace provides no way to do so. // // With these constraints, this function has a statically cached state // that is calculated the first time this is requested. Remember that // backtracing all happens serially (one global lock). // // Note the lack of synchronization here is due to the requirement that // `resolve` is externally synchronized. unsafe fn init_state() -> *mut bt::backtrace_state { static mut STATE: *mut bt::backtrace_state = 0 as *mut _; if !STATE.is_null() { return STATE; } STATE = bt::backtrace_create_state( load_filename(), // Don't exercise threadsafe capabilities of libbacktrace since // we're always calling it in a synchronized fashion. 0, error_cb, ptr::null_mut(), // no extra data ); return STATE; // Note that for libbacktrace to operate at all it needs to find the DWARF // debug info for the current executable. It typically does that via a // number of mechanisms including, but not limited to: // // * /proc/self/exe on supported platforms // * The filename passed in explicitly when creating state // // The libbacktrace library is a big wad of C code. This naturally means // it's got memory safety vulnerabilities, especially when handling // malformed debuginfo. Libstd has run into plenty of these historically. // // If /proc/self/exe is used then we can typically ignore these as we // assume that libbacktrace is "mostly correct" and otherwise doesn't do // weird things with "attempted to be correct" dwarf debug info. // // If we pass in a filename, however, then it's possible on some platforms // (like BSDs) where a malicious actor can cause an arbitrary file to be // placed at that location. This means that if we tell libbacktrace about a // filename it may be using an arbitrary file, possibly causing segfaults. // If we don't tell libbacktrace anything though then it won't do anything // on platforms that don't support paths like /proc/self/exe! // // Given all that we try as hard as possible to *not* pass in a filename, // but we must on platforms that don't support /proc/self/exe at all. cfg_if::cfg_if! { if #[cfg(any(target_os = "macos", target_os = "ios"))] { // Note that ideally we'd use `std::env::current_exe`, but we can't // require `std` here. // // Use `_NSGetExecutablePath` to load the current executable path // into a static area (which if it's too small just give up). // // Note that we're seriously trusting libbacktrace here to not die // on corrupt executables, but it surely does... unsafe fn load_filename() -> *const libc::c_char { const N: usize = 256; static mut BUF: [u8; N] = [0; N]; extern { fn _NSGetExecutablePath( buf: *mut libc::c_char, bufsize: *mut u32, ) -> libc::c_int; } let mut sz: u32 = BUF.len() as u32; let ptr = BUF.as_mut_ptr() as *mut libc::c_char; if _NSGetExecutablePath(ptr, &mut sz) == 0 { ptr } else { ptr::null() } } } else if #[cfg(windows)] { use crate::windows::*; // Windows has a mode of opening files where after it's opened it // can't be deleted. That's in general what we want here because we // want to ensure that our executable isn't changing out from under // us after we hand it off to libbacktrace, hopefully mitigating the // ability to pass in arbitrary data into libbacktrace (which may be // mishandled). // // Given that we do a bit of a dance here to attempt to get a sort // of lock on our own image: // // * Get a handle to the current process, load its filename. // * Open a file to that filename with the right flags. // * Reload the current process's filename, making sure it's the same // // If that all passes we in theory have indeed opened our process's // file and we're guaranteed it won't change. FWIW a bunch of this // is copied from libstd historically, so this is my best // interpretation of what was happening. unsafe fn load_filename() -> *const libc::c_char { load_filename_opt().unwrap_or(ptr::null()) } unsafe fn load_filename_opt() -> Result<*const libc::c_char, ()> { const N: usize = 256; // This lives in static memory so we can return it.. static mut BUF: [i8; N] = [0; N]; // ... and this lives on the stack since it's temporary let mut stack_buf = [0; N]; let name1 = query_full_name(&mut BUF)?; let handle = CreateFileA( name1.as_ptr(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, ptr::null_mut(), OPEN_EXISTING, 0, ptr::null_mut(), ); if handle.is_null() { return Err(()); } let name2 = query_full_name(&mut stack_buf)?; if name1 != name2 { CloseHandle(handle); return Err(()) } // intentionally leak `handle` here because having that open // should preserve our lock on this file name. Ok(name1.as_ptr()) } unsafe fn query_full_name(buf: &mut [i8]) -> Result<&[i8], ()> { let dll = GetModuleHandleA(b"kernel32.dll\0".as_ptr() as *const i8); if dll.is_null() { return Err(()) } let ptrQueryFullProcessImageNameA = GetProcAddress(dll, b"QueryFullProcessImageNameA\0".as_ptr() as *const _) as usize; if ptrQueryFullProcessImageNameA == 0 { return Err(()); } use core::mem; let p1 = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId()); let mut len = buf.len() as u32; let pfnQueryFullProcessImageNameA : extern "system" fn( hProcess: HANDLE, dwFlags: DWORD, lpExeName: LPSTR, lpdwSize: PDWORD, ) -> BOOL = mem::transmute(ptrQueryFullProcessImageNameA); let rc = pfnQueryFullProcessImageNameA(p1, 0, buf.as_mut_ptr(), &mut len); CloseHandle(p1); // We want to return a slice that is nul-terminated, so if // everything was filled in and it equals the total length // then equate that to failure. // // Otherwise when returning success make sure the nul byte is // included in the slice. if rc == 0 || len == buf.len() as u32 { Err(()) } else { assert_eq!(buf[len as usize], 0); Ok(&buf[..(len + 1) as usize]) } } } else if #[cfg(target_os = "vxworks")] { unsafe fn load_filename() -> *const libc::c_char { use libc; use core::mem; const N: usize = libc::VX_RTP_NAME_LENGTH as usize + 1; static mut BUF: [libc::c_char; N] = [0; N]; let mut rtp_desc : libc::RTP_DESC = mem::zeroed(); if (libc::rtpInfoGet(0, &mut rtp_desc as *mut libc::RTP_DESC) == 0) { BUF.copy_from_slice(&rtp_desc.pathName); BUF.as_ptr() } else { ptr::null() } } } else { unsafe fn load_filename() -> *const libc::c_char { ptr::null() } } } } pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) { let symaddr = what.address_or_ip() as usize; // backtrace errors are currently swept under the rug let state = init_state(); if state.is_null() { return dladdr_fallback(what.address_or_ip(), cb); } // Call the `backtrace_syminfo` API first. This is (from reading the code) // guaranteed to call `syminfo_cb` exactly once (or fail with an error // presumably). We then handle more within the `syminfo_cb`. // // Note that we do this since `syminfo` will consult the symbol table, // finding symbol names even if there's no debug information in the binary. let mut called = false; { let mut syminfo_state = SyminfoState { pc: symaddr, cb: &mut |sym| { called = true; cb(sym); }, }; bt::backtrace_syminfo( state, symaddr as uintptr_t, syminfo_cb, error_cb, &mut syminfo_state as *mut _ as *mut _, ); } if !called { dladdr_fallback(what.address_or_ip(), cb); } } unsafe fn dladdr_fallback(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) { dladdr::resolve(addr, &mut |sym| { cb(&super::Symbol { inner: Symbol::Dladdr(sym), }) }); } backtrace-0.3.44/src/symbolize/mod.rs010066400017500001750000000417271362056365200157200ustar0000000000000000use core::{fmt, str}; cfg_if::cfg_if! { if #[cfg(feature = "std")] { use std::path::Path; use std::prelude::v1::*; } } use crate::backtrace::Frame; use crate::types::BytesOrWideString; use core::ffi::c_void; use rustc_demangle::{try_demangle, Demangle}; /// Resolve an address to a symbol, passing the symbol to the specified /// closure. /// /// This function will look up the given address in areas such as the local /// symbol table, dynamic symbol table, or DWARF debug info (depending on the /// activated implementation) to find symbols to yield. /// /// The closure may not be called if resolution could not be performed, and it /// also may be called more than once in the case of inlined functions. /// /// Symbols yielded represent the execution at the specified `addr`, returning /// file/line pairs for that address (if available). /// /// Note that if you have a `Frame` then it's recommended to use the /// `resolve_frame` function instead of this one. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. /// /// # Panics /// /// This function strives to never panic, but if the `cb` provided panics then /// some platforms will force a double panic to abort the process. Some /// platforms use a C library which internally uses callbacks which cannot be /// unwound through, so panicking from `cb` may trigger a process abort. /// /// # Example /// /// ``` /// extern crate backtrace; /// /// fn main() { /// backtrace::trace(|frame| { /// let ip = frame.ip(); /// /// backtrace::resolve(ip, |symbol| { /// // ... /// }); /// /// false // only look at the top frame /// }); /// } /// ``` #[cfg(feature = "std")] pub fn resolve(addr: *mut c_void, cb: F) { let _guard = crate::lock::lock(); unsafe { resolve_unsynchronized(addr, cb) } } /// Resolve a previously capture frame to a symbol, passing the symbol to the /// specified closure. /// /// This functin performs the same function as `resolve` except that it takes a /// `Frame` as an argument instead of an address. This can allow some platform /// implementations of backtracing to provide more accurate symbol information /// or information about inline frames for example. It's recommended to use this /// if you can. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. /// /// # Panics /// /// This function strives to never panic, but if the `cb` provided panics then /// some platforms will force a double panic to abort the process. Some /// platforms use a C library which internally uses callbacks which cannot be /// unwound through, so panicking from `cb` may trigger a process abort. /// /// # Example /// /// ``` /// extern crate backtrace; /// /// fn main() { /// backtrace::trace(|frame| { /// backtrace::resolve_frame(frame, |symbol| { /// // ... /// }); /// /// false // only look at the top frame /// }); /// } /// ``` #[cfg(feature = "std")] pub fn resolve_frame(frame: &Frame, cb: F) { let _guard = crate::lock::lock(); unsafe { resolve_frame_unsynchronized(frame, cb) } } pub enum ResolveWhat<'a> { Address(*mut c_void), Frame(&'a Frame), } impl<'a> ResolveWhat<'a> { #[allow(dead_code)] fn address_or_ip(&self) -> *mut c_void { match self { ResolveWhat::Address(a) => adjust_ip(*a), ResolveWhat::Frame(f) => adjust_ip(f.ip()), } } } // IP values from stack frames are typically (always?) the instruction // *after* the call that's the actual stack trace. Symbolizing this on // causes the filename/line number to be one ahead and perhaps into // the void if it's near the end of the function. // // This appears to basically always be the case on all platforms, so we always // subtract one from a resolved ip to resolve it to the previous call // instruction instead of the instruction being returned to. // // Ideally we would not do this. Ideally we would require callers of the // `resolve` APIs here to manually do the -1 and account that they want location // information for the *previous* instruction, not the current. Ideally we'd // also expose on `Frame` if we are indeed the address of the next instruction // or the current. // // For now though this is a pretty niche concern so we just internally always // subtract one. Consumers should keep working and getting pretty good results, // so we should be good enough. fn adjust_ip(a: *mut c_void) -> *mut c_void { if a.is_null() { a } else { (a as usize - 1) as *mut c_void } } /// Same as `resolve`, only unsafe as it's unsynchronized. /// /// This function does not have synchronization guarentees but is available when /// the `std` feature of this crate isn't compiled in. See the `resolve` /// function for more documentation and examples. /// /// # Panics /// /// See information on `resolve` for caveats on `cb` panicking. pub unsafe fn resolve_unsynchronized(addr: *mut c_void, mut cb: F) where F: FnMut(&Symbol), { resolve_imp(ResolveWhat::Address(addr), &mut cb) } /// Same as `resolve_frame`, only unsafe as it's unsynchronized. /// /// This function does not have synchronization guarentees but is available /// when the `std` feature of this crate isn't compiled in. See the /// `resolve_frame` function for more documentation and examples. /// /// # Panics /// /// See information on `resolve_frame` for caveats on `cb` panicking. pub unsafe fn resolve_frame_unsynchronized(frame: &Frame, mut cb: F) where F: FnMut(&Symbol), { resolve_imp(ResolveWhat::Frame(frame), &mut cb) } /// A trait representing the resolution of a symbol in a file. /// /// This trait is yielded as a trait object to the closure given to the /// `backtrace::resolve` function, and it is virtually dispatched as it's /// unknown which implementation is behind it. /// /// A symbol can give contextual information about a function, for example the /// name, filename, line number, precise address, etc. Not all information is /// always available in a symbol, however, so all methods return an `Option`. pub struct Symbol { // TODO: this lifetime bound needs to be persisted eventually to `Symbol`, // but that's currently a breaking change. For now this is safe since // `Symbol` is only ever handed out by reference and can't be cloned. inner: SymbolImp<'static>, } impl Symbol { /// Returns the name of this function. /// /// The returned structure can be used to query various properties about the /// symbol name: /// /// * The `Display` implementation will print out the demangled symbol. /// * The raw `str` value of the symbol can be accessed (if it's valid /// utf-8). /// * The raw bytes for the symbol name can be accessed. pub fn name(&self) -> Option { self.inner.name() } /// Returns the starting address of this function. pub fn addr(&self) -> Option<*mut c_void> { self.inner.addr().map(|p| p as *mut _) } /// Returns the raw filename as a slice. This is mainly useful for `no_std` /// environments. pub fn filename_raw(&self) -> Option { self.inner.filename_raw() } /// Returns the line number for where this symbol is currently executing. /// /// This return value is typically `Some` if `filename` returns `Some`, and /// is consequently subject to similar caveats. pub fn lineno(&self) -> Option { self.inner.lineno() } /// Returns the file name where this function was defined. /// /// This is currently only available when libbacktrace is being used (e.g. /// unix platforms other than OSX) and when a binary is compiled with /// debuginfo. If neither of these conditions is met then this will likely /// return `None`. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. #[cfg(feature = "std")] #[allow(unreachable_code)] pub fn filename(&self) -> Option<&Path> { self.inner.filename() } } impl fmt::Debug for Symbol { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut d = f.debug_struct("Symbol"); if let Some(name) = self.name() { d.field("name", &name); } if let Some(addr) = self.addr() { d.field("addr", &addr); } #[cfg(feature = "std")] { if let Some(filename) = self.filename() { d.field("filename", &filename); } } if let Some(lineno) = self.lineno() { d.field("lineno", &lineno); } d.finish() } } cfg_if::cfg_if! { if #[cfg(feature = "cpp_demangle")] { // Maybe a parsed C++ symbol, if parsing the mangled symbol as Rust // failed. struct OptionCppSymbol<'a>(Option<::cpp_demangle::BorrowedSymbol<'a>>); impl<'a> OptionCppSymbol<'a> { fn parse(input: &'a [u8]) -> OptionCppSymbol<'a> { OptionCppSymbol(::cpp_demangle::BorrowedSymbol::new(input).ok()) } fn none() -> OptionCppSymbol<'a> { OptionCppSymbol(None) } } } else { use core::marker::PhantomData; // Make sure to keep this zero-sized, so that the `cpp_demangle` feature // has no cost when disabled. struct OptionCppSymbol<'a>(PhantomData<&'a ()>); impl<'a> OptionCppSymbol<'a> { fn parse(_: &'a [u8]) -> OptionCppSymbol<'a> { OptionCppSymbol(PhantomData) } fn none() -> OptionCppSymbol<'a> { OptionCppSymbol(PhantomData) } } } } /// A wrapper around a symbol name to provide ergonomic accessors to the /// demangled name, the raw bytes, the raw string, etc. // Allow dead code for when the `cpp_demangle` feature is not enabled. #[allow(dead_code)] pub struct SymbolName<'a> { bytes: &'a [u8], demangled: Option>, cpp_demangled: OptionCppSymbol<'a>, } impl<'a> SymbolName<'a> { /// Creates a new symbol name from the raw underlying bytes. pub fn new(bytes: &'a [u8]) -> SymbolName<'a> { let str_bytes = str::from_utf8(bytes).ok(); let demangled = str_bytes.and_then(|s| try_demangle(s).ok()); let cpp = if demangled.is_none() { OptionCppSymbol::parse(bytes) } else { OptionCppSymbol::none() }; SymbolName { bytes: bytes, demangled: demangled, cpp_demangled: cpp, } } /// Returns the raw (mangled) symbol name as a `str` if the symbol is valid utf-8. /// /// Use the `Display` implementation if you want the demangled version. pub fn as_str(&self) -> Option<&'a str> { self.demangled .as_ref() .map(|s| s.as_str()) .or_else(|| str::from_utf8(self.bytes).ok()) } /// Returns the raw symbol name as a list of bytes pub fn as_bytes(&self) -> &'a [u8] { self.bytes } } fn format_symbol_name( fmt: fn(&str, &mut fmt::Formatter) -> fmt::Result, mut bytes: &[u8], f: &mut fmt::Formatter, ) -> fmt::Result { while bytes.len() > 0 { match str::from_utf8(bytes) { Ok(name) => { fmt(name, f)?; break; } Err(err) => { fmt("\u{FFFD}", f)?; match err.error_len() { Some(len) => bytes = &bytes[err.valid_up_to() + len..], None => break, } } } } Ok(()) } cfg_if::cfg_if! { if #[cfg(feature = "cpp_demangle")] { impl<'a> fmt::Display for SymbolName<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(ref s) = self.demangled { s.fmt(f) } else if let Some(ref cpp) = self.cpp_demangled.0 { cpp.fmt(f) } else { format_symbol_name(fmt::Display::fmt, self.bytes, f) } } } } else { impl<'a> fmt::Display for SymbolName<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(ref s) = self.demangled { s.fmt(f) } else { format_symbol_name(fmt::Display::fmt, self.bytes, f) } } } } } cfg_if::cfg_if! { if #[cfg(all(feature = "std", feature = "cpp_demangle"))] { impl<'a> fmt::Debug for SymbolName<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { use std::fmt::Write; if let Some(ref s) = self.demangled { return s.fmt(f) } // This may to print if the demangled symbol isn't actually // valid, so handle the error here gracefully by not propagating // it outwards. if let Some(ref cpp) = self.cpp_demangled.0 { let mut s = String::new(); if write!(s, "{}", cpp).is_ok() { return s.fmt(f) } } format_symbol_name(fmt::Debug::fmt, self.bytes, f) } } } else { impl<'a> fmt::Debug for SymbolName<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if let Some(ref s) = self.demangled { s.fmt(f) } else { format_symbol_name(fmt::Debug::fmt, self.bytes, f) } } } } } /// Attempt to reclaim that cached memory used to symbolicate addresses. /// /// This method will attempt to release any global data structures that have /// otherwise been cached globally or in the thread which typically represent /// parsed DWARF information or similar. /// /// # Caveats /// /// While this function is always available it doesn't actually do anything on /// most implementations. Libraries like dbghelp or libbacktrace do not provide /// facilities to deallocate state and manage the allocated memory. For now the /// `gimli-symbolize` feature of this crate is the only feature where this /// function has any effect. #[cfg(feature = "std")] pub fn clear_symbol_cache() { let _guard = crate::lock::lock(); unsafe { clear_symbol_cache_imp(); } } mod dladdr; cfg_if::cfg_if! { if #[cfg(all(windows, target_env = "msvc", feature = "dbghelp", not(target_vendor = "uwp")))] { mod dbghelp; use self::dbghelp::resolve as resolve_imp; use self::dbghelp::Symbol as SymbolImp; unsafe fn clear_symbol_cache_imp() {} } else if #[cfg(all( feature = "std", feature = "gimli-symbolize", any( target_os = "linux", target_os = "macos", windows, ), ))] { mod gimli; use self::gimli::resolve as resolve_imp; use self::gimli::Symbol as SymbolImp; use self::gimli::clear_symbol_cache as clear_symbol_cache_imp; // Note that we only enable coresymbolication on iOS when debug assertions // are enabled because it's helpful in debug mode but it looks like apps get // rejected from the app store if they use this API, see #92 for more info } else if #[cfg(all(feature = "coresymbolication", any(target_os = "macos", all(target_os = "ios", debug_assertions))))] { mod coresymbolication; use self::coresymbolication::resolve as resolve_imp; use self::coresymbolication::Symbol as SymbolImp; unsafe fn clear_symbol_cache_imp() {} } else if #[cfg(all(feature = "libbacktrace", any(unix, all(windows, not(target_vendor = "uwp"), target_env = "gnu")), not(target_os = "fuchsia"), not(target_os = "emscripten"), not(target_env = "uclibc")))] { mod libbacktrace; use self::libbacktrace::resolve as resolve_imp; use self::libbacktrace::Symbol as SymbolImp; unsafe fn clear_symbol_cache_imp() {} } else if #[cfg(all(unix, not(target_os = "emscripten"), not(target_os = "fuchsia"), not(target_env = "uclibc"), feature = "dladdr"))] { mod dladdr_resolve; use self::dladdr_resolve::resolve as resolve_imp; use self::dladdr_resolve::Symbol as SymbolImp; unsafe fn clear_symbol_cache_imp() {} } else { mod noop; use self::noop::resolve as resolve_imp; use self::noop::Symbol as SymbolImp; #[allow(unused)] unsafe fn clear_symbol_cache_imp() {} } } backtrace-0.3.44/src/symbolize/noop.rs010066400017500001750000000014151360710306200160700ustar0000000000000000//! Empty symbolication strategy used to compile for platforms that have no //! support. use crate::symbolize::ResolveWhat; use crate::types::BytesOrWideString; use crate::SymbolName; use core::ffi::c_void; use core::marker; pub unsafe fn resolve(_addr: ResolveWhat, _cb: &mut FnMut(&super::Symbol)) {} pub struct Symbol<'a> { _marker: marker::PhantomData<&'a i32>, } impl Symbol<'_> { pub fn name(&self) -> Option { None } pub fn addr(&self) -> Option<*mut c_void> { None } pub fn filename_raw(&self) -> Option { None } #[cfg(feature = "std")] pub fn filename(&self) -> Option<&::std::path::Path> { None } pub fn lineno(&self) -> Option { None } } backtrace-0.3.44/src/types.rs010066400017500001750000000046071360710306200142520ustar0000000000000000//! Platform dependent types. cfg_if::cfg_if! { if #[cfg(feature = "std")] { use std::borrow::Cow; use std::fmt; use std::path::PathBuf; use std::prelude::v1::*; use std::str; } } /// A platform independent representation of a string. When working with `std` /// enabled it is recommended to the convenience methods for providing /// conversions to `std` types. #[derive(Debug)] pub enum BytesOrWideString<'a> { /// A slice, typically provided on Unix platforms. Bytes(&'a [u8]), /// Wide strings typically from Windows. Wide(&'a [u16]), } #[cfg(feature = "std")] impl<'a> BytesOrWideString<'a> { /// Lossy converts to a `Cow`, will allocate if `Bytes` is not valid /// UTF-8 or if `BytesOrWideString` is `Wide`. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. pub fn to_str_lossy(&self) -> Cow<'a, str> { use self::BytesOrWideString::*; match self { &Bytes(slice) => String::from_utf8_lossy(slice), &Wide(wide) => Cow::Owned(String::from_utf16_lossy(wide)), } } /// Provides a `Path` representation of `BytesOrWideString`. /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. pub fn into_path_buf(self) -> PathBuf { #[cfg(unix)] { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; if let BytesOrWideString::Bytes(slice) = self { return PathBuf::from(OsStr::from_bytes(slice)); } } #[cfg(windows)] { use std::ffi::OsString; use std::os::windows::ffi::OsStringExt; if let BytesOrWideString::Wide(slice) = self { return PathBuf::from(OsString::from_wide(slice)); } } if let BytesOrWideString::Bytes(b) = self { if let Ok(s) = str::from_utf8(b) { return PathBuf::from(s); } } unreachable!() } } #[cfg(feature = "std")] impl<'a> fmt::Display for BytesOrWideString<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.to_str_lossy().fmt(f) } } backtrace-0.3.44/src/windows.rs010066400017500001750000000422451360765776400146270ustar0000000000000000//! A module to define the FFI definitions we use on Windows for `dbghelp.dll` //! //! This module uses a custom macro, `ffi!`, to wrap all definitions to //! automatically generate tests to assert that our definitions here are the //! same as `winapi`. //! //! This module largely exists to integrate into libstd itself where winapi is //! not currently available. #![allow(bad_style, dead_code)] cfg_if::cfg_if! { if #[cfg(feature = "verify-winapi")] { pub use self::winapi::c_void; pub use self::winapi::HINSTANCE; pub use self::winapi::FARPROC; pub use self::winapi::LPSECURITY_ATTRIBUTES; #[cfg(target_pointer_width = "64")] pub use self::winapi::PUNWIND_HISTORY_TABLE; #[cfg(target_pointer_width = "64")] pub use self::winapi::PRUNTIME_FUNCTION; mod winapi { pub use winapi::ctypes::*; pub use winapi::shared::basetsd::*; pub use winapi::shared::minwindef::*; pub use winapi::um::dbghelp::*; pub use winapi::um::handleapi::*; pub use winapi::um::libloaderapi::*; pub use winapi::um::processthreadsapi::*; pub use winapi::um::winbase::*; pub use winapi::um::winnt::*; pub use winapi::um::fileapi::*; pub use winapi::um::minwinbase::*; pub use winapi::um::synchapi::*; } } else { pub use core::ffi::c_void; pub type HINSTANCE = *mut c_void; pub type FARPROC = *mut c_void; pub type LPSECURITY_ATTRIBUTES = *mut c_void; #[cfg(target_pointer_width = "64")] pub type PRUNTIME_FUNCTION = *mut c_void; #[cfg(target_pointer_width = "64")] pub type PUNWIND_HISTORY_TABLE = *mut c_void; } } macro_rules! ffi { () => (); (#[repr($($r:tt)*)] pub struct $name:ident { $(pub $field:ident: $ty:ty,)* } $($rest:tt)*) => ( #[repr($($r)*)] #[cfg(not(feature = "verify-winapi"))] #[derive(Copy, Clone)] pub struct $name { $(pub $field: $ty,)* } #[cfg(feature = "verify-winapi")] pub use self::winapi::$name; #[test] #[cfg(feature = "verify-winapi")] fn $name() { use core::mem; #[repr($($r)*)] pub struct $name { $(pub $field: $ty,)* } assert_eq!( mem::size_of::<$name>(), mem::size_of::(), concat!("size of ", stringify!($name), " is wrong"), ); assert_eq!( mem::align_of::<$name>(), mem::align_of::(), concat!("align of ", stringify!($name), " is wrong"), ); type Winapi = winapi::$name; fn assert_same(_: T, _: T) {} unsafe { let a = &*(mem::align_of::<$name>() as *const $name); let b = &*(mem::align_of::() as *const Winapi); $( ffi!(@test_fields a b $field $ty); )* } } ffi!($($rest)*); ); // Handling verification against unions in winapi requires some special care (@test_fields $a:ident $b:ident FltSave $ty:ty) => ( // Skip this field on x86_64 `CONTEXT` since it's a union and a bit funny ); (@test_fields $a:ident $b:ident D $ty:ty) => ({ let a = &$a.D; let b = $b.D(); assert_same(a, b); assert_eq!(a as *const $ty, b as *const $ty, "misplaced field D"); }); (@test_fields $a:ident $b:ident s $ty:ty) => ({ let a = &$a.s; let b = $b.s(); assert_same(a, b); assert_eq!(a as *const $ty, b as *const $ty, "misplaced field s"); }); // Otherwise test all fields normally. (@test_fields $a:ident $b:ident $field:ident $ty:ty) => ({ let a = &$a.$field; let b = &$b.$field; assert_same(a, b); assert_eq!(a as *const $ty, b as *const $ty, concat!("misplaced field ", stringify!($field))); }); (pub type $name:ident = $ty:ty; $($rest:tt)*) => ( pub type $name = $ty; #[cfg(feature = "verify-winapi")] #[allow(dead_code)] const $name: () = { fn _foo() { trait SameType {} impl SameType for (T, T) {} fn assert_same() {} assert_same::<($name, winapi::$name)>(); } }; ffi!($($rest)*); ); (pub const $name:ident: $ty:ty = $val:expr; $($rest:tt)*) => ( pub const $name: $ty = $val; #[cfg(feature = "verify-winapi")] #[allow(unused_imports)] mod $name { use super::*; #[test] fn assert_valid() { let x: $ty = winapi::$name; assert_eq!(x, $val); } } ffi!($($rest)*); ); (extern "system" { $(pub fn $name:ident($($args:tt)*) -> $ret:ty;)* } $($rest:tt)*) => ( extern "system" { $(pub fn $name($($args)*) -> $ret;)* } $( #[cfg(feature = "verify-winapi")] mod $name { #[test] fn assert_same() { use super::*; assert_eq!($name as usize, winapi::$name as usize); let mut x: unsafe extern "system" fn($($args)*) -> $ret; x = $name; drop(x); x = winapi::$name; drop(x); } } )* ffi!($($rest)*); ); (impl $name:ident { $($i:tt)* } $($rest:tt)*) => ( #[cfg(not(feature = "verify-winapi"))] impl $name { $($i)* } ffi!($($rest)*); ); } ffi! { #[repr(C)] pub struct STACKFRAME64 { pub AddrPC: ADDRESS64, pub AddrReturn: ADDRESS64, pub AddrFrame: ADDRESS64, pub AddrStack: ADDRESS64, pub AddrBStore: ADDRESS64, pub FuncTableEntry: PVOID, pub Params: [DWORD64; 4], pub Far: BOOL, pub Virtual: BOOL, pub Reserved: [DWORD64; 3], pub KdHelp: KDHELP64, } pub type LPSTACKFRAME64 = *mut STACKFRAME64; #[repr(C)] pub struct STACKFRAME_EX { pub AddrPC: ADDRESS64, pub AddrReturn: ADDRESS64, pub AddrFrame: ADDRESS64, pub AddrStack: ADDRESS64, pub AddrBStore: ADDRESS64, pub FuncTableEntry: PVOID, pub Params: [DWORD64; 4], pub Far: BOOL, pub Virtual: BOOL, pub Reserved: [DWORD64; 3], pub KdHelp: KDHELP64, pub StackFrameSize: DWORD, pub InlineFrameContext: DWORD, } pub type LPSTACKFRAME_EX = *mut STACKFRAME_EX; #[repr(C)] pub struct IMAGEHLP_LINEW64 { pub SizeOfStruct: DWORD, pub Key: PVOID, pub LineNumber: DWORD, pub FileName: PWSTR, pub Address: DWORD64, } pub type PIMAGEHLP_LINEW64 = *mut IMAGEHLP_LINEW64; #[repr(C)] pub struct SYMBOL_INFOW { pub SizeOfStruct: ULONG, pub TypeIndex: ULONG, pub Reserved: [ULONG64; 2], pub Index: ULONG, pub Size: ULONG, pub ModBase: ULONG64, pub Flags: ULONG, pub Value: ULONG64, pub Address: ULONG64, pub Register: ULONG, pub Scope: ULONG, pub Tag: ULONG, pub NameLen: ULONG, pub MaxNameLen: ULONG, pub Name: [WCHAR; 1], } pub type PSYMBOL_INFOW = *mut SYMBOL_INFOW; pub type PTRANSLATE_ADDRESS_ROUTINE64 = Option< unsafe extern "system" fn(hProcess: HANDLE, hThread: HANDLE, lpaddr: LPADDRESS64) -> DWORD64, >; pub type PGET_MODULE_BASE_ROUTINE64 = Option DWORD64>; pub type PFUNCTION_TABLE_ACCESS_ROUTINE64 = Option PVOID>; pub type PREAD_PROCESS_MEMORY_ROUTINE64 = Option< unsafe extern "system" fn( hProcess: HANDLE, qwBaseAddress: DWORD64, lpBuffer: PVOID, nSize: DWORD, lpNumberOfBytesRead: LPDWORD, ) -> BOOL, >; #[repr(C)] pub struct ADDRESS64 { pub Offset: DWORD64, pub Segment: WORD, pub Mode: ADDRESS_MODE, } pub type LPADDRESS64 = *mut ADDRESS64; pub type ADDRESS_MODE = u32; #[repr(C)] pub struct KDHELP64 { pub Thread: DWORD64, pub ThCallbackStack: DWORD, pub ThCallbackBStore: DWORD, pub NextCallback: DWORD, pub FramePointer: DWORD, pub KiCallUserMode: DWORD64, pub KeUserCallbackDispatcher: DWORD64, pub SystemRangeStart: DWORD64, pub KiUserExceptionDispatcher: DWORD64, pub StackBase: DWORD64, pub StackLimit: DWORD64, pub BuildVersion: DWORD, pub Reserved0: DWORD, pub Reserved1: [DWORD64; 4], } pub const MAX_SYM_NAME: usize = 2000; pub const AddrModeFlat: ADDRESS_MODE = 3; pub const TRUE: BOOL = 1; pub const FALSE: BOOL = 0; pub const PROCESS_QUERY_INFORMATION: DWORD = 0x400; pub const IMAGE_FILE_MACHINE_ARM64: u16 = 43620; pub const IMAGE_FILE_MACHINE_AMD64: u16 = 34404; pub const IMAGE_FILE_MACHINE_I386: u16 = 332; pub const IMAGE_FILE_MACHINE_ARMNT: u16 = 452; pub const FILE_SHARE_READ: DWORD = 0x1; pub const FILE_SHARE_WRITE: DWORD = 0x2; pub const OPEN_EXISTING: DWORD = 0x3; pub const GENERIC_READ: DWORD = 0x80000000; pub const INFINITE: DWORD = !0; pub type DWORD = u32; pub type PDWORD = *mut u32; pub type BOOL = i32; pub type DWORD64 = u64; pub type PDWORD64 = *mut u64; pub type HANDLE = *mut c_void; pub type PVOID = HANDLE; pub type PCWSTR = *const u16; pub type LPSTR = *mut i8; pub type LPCSTR = *const i8; pub type PWSTR = *mut u16; pub type WORD = u16; pub type ULONG = u32; pub type ULONG64 = u64; pub type WCHAR = u16; pub type PCONTEXT = *mut CONTEXT; pub type LPDWORD = *mut DWORD; pub type DWORDLONG = u64; pub type HMODULE = HINSTANCE; extern "system" { pub fn GetCurrentProcess() -> HANDLE; pub fn GetCurrentThread() -> HANDLE; pub fn RtlCaptureContext(ContextRecord: PCONTEXT) -> (); pub fn LoadLibraryA(a: *const i8) -> HMODULE; pub fn GetProcAddress(h: HMODULE, name: *const i8) -> FARPROC; pub fn GetModuleHandleA(name: *const i8) -> HMODULE; pub fn OpenProcess( dwDesiredAccess: DWORD, bInheitHandle: BOOL, dwProcessId: DWORD, ) -> HANDLE; pub fn GetCurrentProcessId() -> DWORD; pub fn CloseHandle(h: HANDLE) -> BOOL; pub fn CreateFileA( lpFileName: LPCSTR, dwDesiredAccess: DWORD, dwShareMode: DWORD, lpSecurityAttributes: LPSECURITY_ATTRIBUTES, dwCreationDisposition: DWORD, dwFlagsAndAttributes: DWORD, hTemplateFile: HANDLE, ) -> HANDLE; pub fn CreateMutexA( attrs: LPSECURITY_ATTRIBUTES, initial: BOOL, name: LPCSTR, ) -> HANDLE; pub fn ReleaseMutex(hMutex: HANDLE) -> BOOL; pub fn WaitForSingleObjectEx( hHandle: HANDLE, dwMilliseconds: DWORD, bAlertable: BOOL, ) -> DWORD; } } #[cfg(target_pointer_width = "64")] ffi! { extern "system" { pub fn RtlLookupFunctionEntry( ControlPc: DWORD64, ImageBase: PDWORD64, HistoryTable: PUNWIND_HISTORY_TABLE, ) -> PRUNTIME_FUNCTION; } } #[cfg(target_arch = "aarch64")] ffi! { #[repr(C, align(16))] pub struct CONTEXT { pub ContextFlags: DWORD, pub Cpsr: DWORD, pub u: CONTEXT_u, pub Sp: u64, pub Pc: u64, pub V: [ARM64_NT_NEON128; 32], pub Fpcr: DWORD, pub Fpsr: DWORD, pub Bcr: [DWORD; ARM64_MAX_BREAKPOINTS], pub Bvr: [DWORD64; ARM64_MAX_BREAKPOINTS], pub Wcr: [DWORD; ARM64_MAX_WATCHPOINTS], pub Wvr: [DWORD64; ARM64_MAX_WATCHPOINTS], } #[repr(C)] pub struct CONTEXT_u { pub s: CONTEXT_u_s, } impl CONTEXT_u { pub unsafe fn s(&self) -> &CONTEXT_u_s { &self.s } } #[repr(C)] pub struct CONTEXT_u_s { pub X0: u64, pub X1: u64, pub X2: u64, pub X3: u64, pub X4: u64, pub X5: u64, pub X6: u64, pub X7: u64, pub X8: u64, pub X9: u64, pub X10: u64, pub X11: u64, pub X12: u64, pub X13: u64, pub X14: u64, pub X15: u64, pub X16: u64, pub X17: u64, pub X18: u64, pub X19: u64, pub X20: u64, pub X21: u64, pub X22: u64, pub X23: u64, pub X24: u64, pub X25: u64, pub X26: u64, pub X27: u64, pub X28: u64, pub Fp: u64, pub Lr: u64, } pub const ARM64_MAX_BREAKPOINTS: usize = 8; pub const ARM64_MAX_WATCHPOINTS: usize = 2; #[repr(C)] pub struct ARM64_NT_NEON128 { pub D: [f64; 2], } } #[cfg(target_arch = "x86")] ffi! { #[repr(C)] pub struct CONTEXT { pub ContextFlags: DWORD, pub Dr0: DWORD, pub Dr1: DWORD, pub Dr2: DWORD, pub Dr3: DWORD, pub Dr6: DWORD, pub Dr7: DWORD, pub FloatSave: FLOATING_SAVE_AREA, pub SegGs: DWORD, pub SegFs: DWORD, pub SegEs: DWORD, pub SegDs: DWORD, pub Edi: DWORD, pub Esi: DWORD, pub Ebx: DWORD, pub Edx: DWORD, pub Ecx: DWORD, pub Eax: DWORD, pub Ebp: DWORD, pub Eip: DWORD, pub SegCs: DWORD, pub EFlags: DWORD, pub Esp: DWORD, pub SegSs: DWORD, pub ExtendedRegisters: [u8; 512], } #[repr(C)] pub struct FLOATING_SAVE_AREA { pub ControlWord: DWORD, pub StatusWord: DWORD, pub TagWord: DWORD, pub ErrorOffset: DWORD, pub ErrorSelector: DWORD, pub DataOffset: DWORD, pub DataSelector: DWORD, pub RegisterArea: [u8; 80], pub Spare0: DWORD, } } #[cfg(target_arch = "x86_64")] ffi! { #[repr(C, align(8))] pub struct CONTEXT { pub P1Home: DWORDLONG, pub P2Home: DWORDLONG, pub P3Home: DWORDLONG, pub P4Home: DWORDLONG, pub P5Home: DWORDLONG, pub P6Home: DWORDLONG, pub ContextFlags: DWORD, pub MxCsr: DWORD, pub SegCs: WORD, pub SegDs: WORD, pub SegEs: WORD, pub SegFs: WORD, pub SegGs: WORD, pub SegSs: WORD, pub EFlags: DWORD, pub Dr0: DWORDLONG, pub Dr1: DWORDLONG, pub Dr2: DWORDLONG, pub Dr3: DWORDLONG, pub Dr6: DWORDLONG, pub Dr7: DWORDLONG, pub Rax: DWORDLONG, pub Rcx: DWORDLONG, pub Rdx: DWORDLONG, pub Rbx: DWORDLONG, pub Rsp: DWORDLONG, pub Rbp: DWORDLONG, pub Rsi: DWORDLONG, pub Rdi: DWORDLONG, pub R8: DWORDLONG, pub R9: DWORDLONG, pub R10: DWORDLONG, pub R11: DWORDLONG, pub R12: DWORDLONG, pub R13: DWORDLONG, pub R14: DWORDLONG, pub R15: DWORDLONG, pub Rip: DWORDLONG, pub FltSave: FLOATING_SAVE_AREA, pub VectorRegister: [M128A; 26], pub VectorControl: DWORDLONG, pub DebugControl: DWORDLONG, pub LastBranchToRip: DWORDLONG, pub LastBranchFromRip: DWORDLONG, pub LastExceptionToRip: DWORDLONG, pub LastExceptionFromRip: DWORDLONG, } #[repr(C)] pub struct M128A { pub Low: u64, pub High: i64, } } #[repr(C)] #[cfg(target_arch = "x86_64")] #[derive(Copy, Clone)] pub struct FLOATING_SAVE_AREA { _Dummy: [u8; 512], } #[cfg(target_arch = "arm")] ffi! { // #[repr(C)] // pub struct NEON128 { // pub Low: ULONG64, // pub High: LONG64, // } // pub type PNEON128 = *mut NEON128; #[repr(C)] pub struct CONTEXT_u { // pub Q: [NEON128; 16], pub D: [ULONG64; 32], // pub S: [DWORD; 32], } pub const ARM_MAX_BREAKPOINTS: usize = 8; pub const ARM_MAX_WATCHPOINTS: usize = 1; #[repr(C)] pub struct CONTEXT { pub ContextFlags: DWORD, pub R0: DWORD, pub R1: DWORD, pub R2: DWORD, pub R3: DWORD, pub R4: DWORD, pub R5: DWORD, pub R6: DWORD, pub R7: DWORD, pub R8: DWORD, pub R9: DWORD, pub R10: DWORD, pub R11: DWORD, pub R12: DWORD, pub Sp: DWORD, pub Lr: DWORD, pub Pc: DWORD, pub Cpsr: DWORD, pub Fpsrc: DWORD, pub Padding: DWORD, pub u: CONTEXT_u, pub Bvr: [DWORD; ARM_MAX_BREAKPOINTS], pub Bcr: [DWORD; ARM_MAX_BREAKPOINTS], pub Wvr: [DWORD; ARM_MAX_WATCHPOINTS], pub Wcr: [DWORD; ARM_MAX_WATCHPOINTS], pub Padding2: [DWORD; 2], } } // IFDEF(arm) backtrace-0.3.44/tests/accuracy/auxiliary.rs010066400017500001750000000004501360710306200172520ustar0000000000000000#[inline(never)] pub fn callback(f: F) where F: FnOnce((&'static str, u32)), { f((file!(), line!())) } #[inline(always)] #[cfg_attr(feature = "coresymbolication", inline(never))] pub fn callback_inlined(f: F) where F: FnOnce((&'static str, u32)), { f((file!(), line!())) } backtrace-0.3.44/tests/accuracy/main.rs010066400017500001750000000051451360710306200161750ustar0000000000000000mod auxiliary; macro_rules! pos { () => { (file!(), line!()) }; } macro_rules! check { ($($pos:expr),*) => ({ verify(&[$($pos,)* pos!()]); }) } type Pos = (&'static str, u32); #[test] fn doit() { outer(pos!()); } #[inline(never)] fn outer(main_pos: Pos) { inner(main_pos, pos!()); inner_inlined(main_pos, pos!()); } #[inline(never)] #[rustfmt::skip] fn inner(main_pos: Pos, outer_pos: Pos) { check!(main_pos, outer_pos); check!(main_pos, outer_pos); let inner_pos = pos!(); auxiliary::callback(|aux_pos| { check!(main_pos, outer_pos, inner_pos, aux_pos); }); let inner_pos = pos!(); auxiliary::callback_inlined(|aux_pos| { check!(main_pos, outer_pos, inner_pos, aux_pos); }); } #[inline(always)] #[cfg_attr(feature = "coresymbolication", inline(never))] #[rustfmt::skip] fn inner_inlined(main_pos: Pos, outer_pos: Pos) { check!(main_pos, outer_pos); check!(main_pos, outer_pos); #[inline(always)] #[cfg_attr(feature = "coresymbolication", inline(never))] fn inner_further_inlined(main_pos: Pos, outer_pos: Pos, inner_pos: Pos) { check!(main_pos, outer_pos, inner_pos); } inner_further_inlined(main_pos, outer_pos, pos!()); let inner_pos = pos!(); auxiliary::callback(|aux_pos| { check!(main_pos, outer_pos, inner_pos, aux_pos); }); let inner_pos = pos!(); auxiliary::callback_inlined(|aux_pos| { check!(main_pos, outer_pos, inner_pos, aux_pos); }); // this tests a distinction between two independent calls to the inlined function. // (un)fortunately, LLVM somehow merges two consecutive such calls into one node. inner_further_inlined(main_pos, outer_pos, pos!()); } fn verify(filelines: &[Pos]) { let trace = backtrace::Backtrace::new(); println!("-----------------------------------"); println!("looking for:"); for (file, line) in filelines.iter().rev() { println!("\t{}:{}", file, line); } println!("found:\n{:?}", trace); let mut symbols = trace.frames().iter().flat_map(|frame| frame.symbols()); let mut iter = filelines.iter().rev(); while let Some((file, line)) = iter.next() { loop { let sym = match symbols.next() { Some(sym) => sym, None => panic!("failed to find {}:{}", file, line), }; if let Some(filename) = sym.filename() { if let Some(lineno) = sym.lineno() { if filename.ends_with(file) && lineno == *line { break; } } } } } } backtrace-0.3.44/tests/concurrent-panics.rs010066400017500001750000000034661360710306200171200ustar0000000000000000use std::env; use std::panic; use std::process::Command; use std::sync::atomic::{AtomicBool, Ordering::SeqCst}; use std::sync::Arc; use std::thread; const PANICS: usize = 100; const THREADS: usize = 8; const VAR: &str = "__THE_TEST_YOU_ARE_LUKE"; fn main() { // These run in docker containers on CI where they can't re-exec the test, // so just skip these for CI. No other reason this can't run on those // platforms though. if cfg!(unix) && (cfg!(target_arch = "arm") || cfg!(target_arch = "aarch64")) { println!("test result: ok"); return; } if env::var(VAR).is_err() { parent(); } else { child(); } } fn parent() { let me = env::current_exe().unwrap(); let result = Command::new(&me) .env("RUST_BACKTRACE", "1") .env(VAR, "1") .output() .unwrap(); if result.status.success() { println!("test result: ok"); return; } println!("stdout:\n{}", String::from_utf8_lossy(&result.stdout)); println!("stderr:\n{}", String::from_utf8_lossy(&result.stderr)); println!("code: {}", result.status); panic!(); } fn child() { let done = Arc::new(AtomicBool::new(false)); let done2 = done.clone(); let a = thread::spawn(move || { while !done2.load(SeqCst) { format!("{:?}", backtrace::Backtrace::new()); } }); let threads = (0..THREADS) .map(|_| { thread::spawn(|| { for _ in 0..PANICS { assert!(panic::catch_unwind(|| { panic!(); }) .is_err()); } }) }) .collect::>(); for thread in threads { thread.join().unwrap(); } done.store(true, SeqCst); a.join().unwrap(); } backtrace-0.3.44/tests/long_fn_name.rs010066400017500001750000000032711360710306200160770ustar0000000000000000extern crate backtrace; use backtrace::Backtrace; // 50-character module name mod _234567890_234567890_234567890_234567890_234567890 { // 50-character struct name #[allow(non_camel_case_types)] pub struct _234567890_234567890_234567890_234567890_234567890(T); impl _234567890_234567890_234567890_234567890_234567890 { #[allow(dead_code)] pub fn new() -> crate::Backtrace { crate::Backtrace::new() } } } // Long function names must be truncated to (MAX_SYM_NAME - 1) characters. // Only run this test for msvc, since gnu prints "" for all frames. #[test] #[cfg(all(windows, feature = "dbghelp", target_env = "msvc"))] fn test_long_fn_name() { use _234567890_234567890_234567890_234567890_234567890::_234567890_234567890_234567890_234567890_234567890 as S; // 10 repetitions of struct name, so fully qualified function name is // atleast 10 * (50 + 50) * 2 = 2000 characters long. // It's actually longer since it also includes `::`, `<>` and the // name of the current module let bt = S::>>>>>>>>>::new(); println!("{:?}", bt); let mut found_long_name_frame = false; for frame in bt.frames() { let symbols = frame.symbols(); if symbols.is_empty() { continue; } if let Some(function_name) = symbols[0].name() { let function_name = function_name.as_str().unwrap(); if function_name.contains("::_234567890_234567890_234567890_234567890_234567890") { found_long_name_frame = true; assert!(function_name.len() > 200); } } } assert!(found_long_name_frame); } backtrace-0.3.44/tests/skip_inner_frames.rs010066400017500001750000000030371360710306200171530ustar0000000000000000extern crate backtrace; use backtrace::Backtrace; // This test only works on platforms which have a working `symbol_address` // function for frames which reports the starting address of a symbol. As a // result it's only enabled on a few platforms. const ENABLED: bool = cfg!(all( // Windows hasn't really been tested, and OSX doesn't support actually // finding an enclosing frame, so disable this target_os = "linux", // This is the only method currently that supports accurate enough // backtraces for this test to work. feature = "libunwind", // On ARM finding the enclosing function is simply returning the ip itself. not(target_arch = "arm"), )); #[test] fn backtrace_new_unresolved_should_start_with_call_site_trace() { if !ENABLED { return; } let mut b = Backtrace::new_unresolved(); b.resolve(); println!("{:?}", b); assert!(!b.frames().is_empty()); let this_ip = backtrace_new_unresolved_should_start_with_call_site_trace as usize; println!("this_ip: {:?}", this_ip as *const usize); let frame_ip = b.frames().first().unwrap().symbol_address() as usize; assert_eq!(this_ip, frame_ip); } #[test] fn backtrace_new_should_start_with_call_site_trace() { if !ENABLED { return; } let b = Backtrace::new(); println!("{:?}", b); assert!(!b.frames().is_empty()); let this_ip = backtrace_new_should_start_with_call_site_trace as usize; let frame_ip = b.frames().first().unwrap().symbol_address() as usize; assert_eq!(this_ip, frame_ip); } backtrace-0.3.44/tests/smoke.rs010066400017500001750000000171651360710306200146020ustar0000000000000000extern crate backtrace; use backtrace::Frame; use std::thread; static LIBUNWIND: bool = cfg!(all(unix, feature = "libunwind")); static UNIX_BACKTRACE: bool = cfg!(all(unix, feature = "unix-backtrace")); static LIBBACKTRACE: bool = cfg!(feature = "libbacktrace") && !cfg!(target_os = "fuchsia"); static CORESYMBOLICATION: bool = cfg!(all( any(target_os = "macos", target_os = "ios"), feature = "coresymbolication" )); static DLADDR: bool = cfg!(all(unix, feature = "dladdr")) && !cfg!(target_os = "fuchsia"); static DBGHELP: bool = cfg!(all(windows, feature = "dbghelp")); static MSVC: bool = cfg!(target_env = "msvc"); static GIMLI_SYMBOLIZE: bool = cfg!(all(feature = "gimli-symbolize", unix, target_os = "linux")); #[test] // FIXME: shouldn't ignore this test on i686-msvc, unsure why it's failing #[cfg_attr(all(target_arch = "x86", target_env = "msvc"), ignore)] #[rustfmt::skip] // we care about line numbers here fn smoke_test_frames() { frame_1(line!()); #[inline(never)] fn frame_1(start_line: u32) { frame_2(start_line) } #[inline(never)] fn frame_2(start_line: u32) { frame_3(start_line) } #[inline(never)] fn frame_3(start_line: u32) { frame_4(start_line) } #[inline(never)] fn frame_4(start_line: u32) { let mut v = Vec::new(); backtrace::trace(|cx| { v.push(cx.clone()); true }); if v.len() < 5 { assert!(!LIBUNWIND); assert!(!UNIX_BACKTRACE); assert!(!DBGHELP); return; } // Various platforms have various bits of weirdness about their // backtraces. To find a good starting spot let's search through the // frames let target = frame_4 as usize; let offset = v .iter() .map(|frame| frame.symbol_address() as usize) .enumerate() .filter_map(|(i, sym)| { if sym >= target { Some((sym, i)) } else { None } }) .min() .unwrap() .1; let mut frames = v[offset..].iter(); assert_frame( frames.next().unwrap(), frame_4 as usize, "frame_4", "tests/smoke.rs", start_line + 6, ); assert_frame( frames.next().unwrap(), frame_3 as usize, "frame_3", "tests/smoke.rs", start_line + 3, ); assert_frame( frames.next().unwrap(), frame_2 as usize, "frame_2", "tests/smoke.rs", start_line + 2, ); assert_frame( frames.next().unwrap(), frame_1 as usize, "frame_1", "tests/smoke.rs", start_line + 1, ); assert_frame( frames.next().unwrap(), smoke_test_frames as usize, "smoke_test_frames", "", 0, ); } fn assert_frame( frame: &Frame, actual_fn_pointer: usize, expected_name: &str, expected_file: &str, expected_line: u32, ) { backtrace::resolve_frame(frame, |sym| { print!("symbol ip:{:?} address:{:?} ", frame.ip(), frame.symbol_address()); if let Some(name) = sym.name() { print!("name:{} ", name); } if let Some(file) = sym.filename() { print!("file:{} ", file.display()); } if let Some(lineno) = sym.lineno() { print!("lineno:{} ", lineno); } println!(); }); let ip = frame.ip() as usize; let sym = frame.symbol_address() as usize; assert!(ip >= sym); assert!( sym >= actual_fn_pointer, "{:?} < {:?} ({} {}:{})", sym as *const usize, actual_fn_pointer as *const usize, expected_name, expected_file, expected_line, ); // windows dbghelp is *quite* liberal (and wrong) in many of its reports // right now... // // This assertion can also fail for release builds, so skip it there if !DBGHELP && cfg!(debug_assertions) { assert!(sym - actual_fn_pointer < 1024); } let mut resolved = 0; let can_resolve = DLADDR || LIBBACKTRACE || CORESYMBOLICATION || DBGHELP || GIMLI_SYMBOLIZE; let mut name = None; let mut addr = None; let mut line = None; let mut file = None; backtrace::resolve_frame(frame, |sym| { resolved += 1; name = sym.name().map(|v| v.to_string()); addr = sym.addr(); line = sym.lineno(); file = sym.filename().map(|v| v.to_path_buf()); }); // dbghelp doesn't always resolve symbols right now match resolved { 0 => return assert!(!can_resolve || DBGHELP), _ => {} } // * linux dladdr doesn't work (only consults local symbol table) // * windows dbghelp isn't great for GNU if can_resolve && !(cfg!(target_os = "linux") && DLADDR) && !(DBGHELP && !MSVC) { let name = name.expect("didn't find a name"); // in release mode names get weird as functions can get merged // together with `mergefunc`, so only assert this in debug mode if cfg!(debug_assertions) { assert!( name.contains(expected_name), "didn't find `{}` in `{}`", expected_name, name ); } } if can_resolve { addr.expect("didn't find a symbol"); } if (LIBBACKTRACE || CORESYMBOLICATION || (DBGHELP && MSVC)) && cfg!(debug_assertions) { let line = line.expect("didn't find a line number"); let file = file.expect("didn't find a line number"); if !expected_file.is_empty() { assert!( file.ends_with(expected_file), "{:?} didn't end with {:?}", file, expected_file ); } if expected_line != 0 { assert!( line == expected_line, "bad line number on frame for `{}`: {} != {}", expected_name, line, expected_line ); } } } } #[test] fn many_threads() { let threads = (0..16) .map(|_| { thread::spawn(|| { for _ in 0..16 { backtrace::trace(|frame| { backtrace::resolve(frame.ip(), |symbol| { let _s = symbol.name().map(|s| s.to_string()); }); true }); } }) }) .collect::>(); for t in threads { t.join().unwrap() } } #[test] #[cfg(feature = "rustc-serialize")] fn is_rustc_serialize() { extern crate rustc_serialize; fn is_encode() {} fn is_decode() {} is_encode::(); is_decode::(); } #[test] #[cfg(feature = "serde")] fn is_serde() { extern crate serde; fn is_serialize() {} fn is_deserialize() {} is_serialize::(); is_deserialize::(); } backtrace-0.3.44/.cargo_vcs_info.json0000644000000001121362056464700131500ustar00{ "git": { "sha1": "190b2f9bb1df384be686eadd1d14cb95de0d87d4" } } backtrace-0.3.44/Cargo.lock0000644000000342021362056464700111320ustar00# This file is automatically @generated by Cargo. # It is not intended for manual editing. [[package]] name = "addr2line" version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "gimli 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "arrayvec" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "backtrace" version = "0.3.44" dependencies = [ "addr2line 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "compiler_builtins 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "cpp_demangle 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "findshlibs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "goblin 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "backtrace-sys" version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)", "compiler_builtins 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "byteorder" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cc" version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cfg-if" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "compiler_builtins 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "compiler_builtins" version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cpp_demangle" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "fallible-iterator" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "findshlibs" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "gimli" version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "glob" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "goblin" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "lazycell" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" version = "0.2.66" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "log" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "memmap" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "plain" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "quote" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-demangle" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "compiler_builtins 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "rustc-serialize" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "rustc-std-workspace-core" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "scroll" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "scroll_derive" version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "smallvec" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "stable_deref_trait" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "syn" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "unicode-xid" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum addr2line 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1c4e698660ed2d0f625c39bb877332b4269668720e330e2aa3d67bb1187a656a" "checksum arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" "checksum backtrace-sys 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" "checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" "checksum compiler_builtins 0.1.24 (registry+https://github.com/rust-lang/crates.io-index)" = "b9975aefa63997ef75ca9cf013ff1bb81487aaa0b622c21053afd3b92979a7af" "checksum cpp_demangle 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "4115af6f575a7bc82c613e9e0ed7cc36a5e4fc3a8b54920dc0c820823a31a0d6" "checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" "checksum findshlibs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1260d61e4fe2a6ab845ffdc426a0bd68ffb240b91cf0ec5a8d1170cec535bd8" "checksum gimli 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "81dd6190aad0f05ddbbf3245c54ed14ca4aa6dd32f22312b70d8f168c3e3e633" "checksum glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" "checksum goblin 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3081214398d39e4bd7f2c1975f0488ed04614ffdd976c6fc7a0708278552c0da" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum plain 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" "checksum proc-macro2 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "0319972dcae462681daf4da1adeeaa066e3ebd29c69be96c6abb1259d2ee2bcc" "checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe" "checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc-std-workspace-core 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1956f5517128a2b6f23ab2dadf1a976f4f5b27962e7724c2bf3d45e539ec098c" "checksum scroll 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "abb2332cb595d33f7edd5700f4cbf94892e680c7f0ae56adab58a35190b66cb1" "checksum scroll_derive 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f8584eea9b9ff42825b46faf46a8c24d2cff13ec152fa2a50df788b87c07ee28" "checksum serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "414115f25f818d7dfccec8ee535d76949ae78584fc4f79a6f45a904bf8ab4449" "checksum serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)" = "128f9e303a5a29922045a830221b8f78ec74a5f544944f3d5984f8ec3895ef64" "checksum smallvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44e59e0c9fa00817912ae6e4e6e3c4fe04455e75699d06eedc7d85917ed8e8f4" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" "checksum syn 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)" = "1e4ff033220a41d1a57d8125eab57bf5263783dfdcc18688b1dacc6ce9651ef8" "checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"