backtrace-0.3.13/.gitignore010066400017500001750000000000221253571146200137300ustar0000000000000000target Cargo.lock backtrace-0.3.13/.gitmodules010066400017500001750000000002151330115333100141040ustar0000000000000000[submodule "backtrace-sys/src/libbacktrace"] path = backtrace-sys/src/libbacktrace url = https://github.com/rust-lang-nursery/libbacktrace backtrace-0.3.13/.travis.yml010066400017500001750000000111101340452515300140450ustar0000000000000000language: rust sudo: false dist: trusty matrix: fast_finish: true include: # Test everything on stable linux - rust: stable addons: sources: # Provides clang-3.9 - llvm-toolchain-trusty-3.9 apt: packages: # Required for `bindgen`, which is required by `findshlibs`, which is # required by the `gimli` feature. - clang-3.9 script: &test_all - cargo build --manifest-path backtrace-sys/Cargo.toml - cargo build - cargo test - cargo test --no-default-features - cargo test --no-default-features --features 'libunwind' - cargo test --no-default-features --features 'libunwind dladdr' - cargo test --no-default-features --features 'libunwind libbacktrace' - cargo test --no-default-features --features 'unix-backtrace' - cargo test --no-default-features --features 'unix-backtrace dladdr' - cargo test --no-default-features --features 'unix-backtrace libbacktrace' - cargo test --no-default-features --features 'serialize-serde std' - cargo test --no-default-features --features 'serialize-rustc std' - cargo test --no-default-features --features 'serialize-rustc serialize-serde std' - cargo test --no-default-features --features 'cpp_demangle std' - cargo test --no-default-features --features 'gimli-symbolize std' - cd ./cpp_smoke_test && cargo test && cd .. - cargo clean && cargo build # Test everything on OSX as well as beta/nightly - os: osx script: *test_all - rust: beta script: *test_all - rust: nightly script: *test_all # Make sure the default crate builds with 1.25.0 - rust: 1.25.0 script: cargo test # Upload docs on nightly - rust: nightly script: - pip install 'travis-cargo<0.2' --user && export PATH=$HOME/.local/bin:$PATH - cargo doc --no-deps --all-features after_success: - travis-cargo doc-upload # Cross-compile tests, not as comprehensive as above but gets us breadth of # targets - env: TARGET=aarch64-unknown-linux-gnu - env: TARGET=arm-unknown-linux-gnueabihf - env: TARGET=armv7-unknown-linux-gnueabihf - env: TARGET=i586-unknown-linux-gnu - env: TARGET=i686-unknown-linux-gnu - env: TARGET=powerpc64-unknown-linux-gnu - env: TARGET=powerpc-unknown-linux-gnu - env: TARGET=x86_64-pc-windows-gnu - env: TARGET=x86_64-unknown-linux-gnu - env: TARGET=x86_64-unknown-linux-musl # Cross compile Android targets from linux - env: TARGET=arm-linux-androideabi - env: TARGET=armv7-linux-androideabi - env: TARGET=aarch64-linux-android - env: TARGET=i686-linux-android - env: TARGET=x86_64-linux-android # Build iOS targets from OSX - env: TARGET=aarch64-apple-ios SDK=iphoneos os: osx script: &ios-build - rustup target add $TARGET - export SDK_PATH=`xcrun --show-sdk-path --sdk $SDK` - export RUSTFLAGS="-C link-arg=-isysroot -C link-arg=$SDK_PATH" - cargo test --no-run --target $TARGET - env: TARGET=armv7-apple-ios SDK=iphoneos os: osx script: *ios-build - env: TARGET=armv7s-apple-ios SDK=iphoneos os: osx script: *ios-build - env: TARGET=i386-apple-ios SDK=iphonesimulator os: osx script: *ios-build - env: TARGET=x86_64-apple-ios SDK=iphonesimulator os: osx script: *ios-build - env: TARGET=wasm32-unknown-unknown script: cargo build --target $TARGET install: - if [ "$TARGET" != "" ]; then rustup target add $TARGET; fi # docker cross-compilation targets script: - cargo generate-lockfile - ci/run-docker.sh $TARGET notifications: email: on_success: never branches: only: - master env: global: # serde-codegen has historically needed a large stack to expand - RUST_MIN_STACK=16777216 - secure: "Kuf3j6gC3MhR+F7g8/5J4+3tu+FXJP/SqKjsUVVjs/qjniIVX3MwZPhtP/pVtdRvYjW0NzLw5Nufb4o1cyY4uKwR8BHHNuEUE/h3mPShjWHqzLyn5QiBumPozsFCa32H4gconRmp3+s0YrBT7nLoGvUZZS0dkldMkpvvrPL/yUKXLS8HEP4L1GO5iMQQYG6i3sbWTbHikE6ZQogW/iZommyqUkVB/s/SQvdH9SXu89ttNXlm/F+EIsgsgyzpbULp5sD34GRDPJe+H1m+sgA1kTRrzmuBGNmz9mx6GyIKaqACTm1gRcb06nFjTPVTQioJBNnoV7TEqZCvjuSsUjcGmP4Aeissafo93ADzV+bd0uoWIScE9ltSVS+RgCDV+sd0GHz5U6FjhgZp0amaVl3d6hPp8lbTfK/gfj1i9ktQfKZbG7rB4tfIU1KeQRkyE9vb/TaKp8nwBbc4SVQ4EKFOlRbE1S1FooaKZweW8w57d2u+sMMMVJbO28/Ap8tk9xDSOl4shPaT0iM0U9/heF8FmCZB1OKXLKn6TAaNFnaMTvdTHl+Tjrf6Vzd/oPXJ7GuaB6eLxXYjXvZHuKiLkSZriOzhL7PbijNILbSgZt7+Fa0vcnXP8zgD4dmupx/CoIHLN9NP4o9cGXuBcaJ/iFryJ4i5LKGFNEUHtXkavDrcgcA=" backtrace-0.3.13/Cargo.toml.orig010066400017500001750000000110741340476603400146410ustar0000000000000000[package] name = "backtrace" version = "0.3.13" authors = ["Alex Crichton ", "The Rust Project Developers"] license = "MIT/Apache-2.0" readme = "README.md" repository = "https://github.com/alexcrichton/backtrace-rs" homepage = "https://github.com/alexcrichton/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 [dependencies] cfg-if = "0.1.6" rustc-demangle = "0.1.4" # Optionally enable the ability to serialize a `Backtrace` serde = { version = "1.0", optional = true } serde_derive = { version = "1.0", optional = true } 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 } addr2line = { version = "0.7.0", optional = true } findshlibs = { version = "0.4.0", optional = true } gimli = { version = "0.16.0", optional = true } memmap = { version = "0.7.0", optional = true } object = { version = "0.9.0", optional = true } [target.'cfg(unix)'.dependencies] libc = { version = "0.2.45", default-features = false } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.3", features = ["dbghelp", "processthreadsapi", "winnt", "minwindef"] } [target.'cfg(all(unix, not(target_os = "fuchsia"), not(target_os = "emscripten"), not(target_os = "macos"), not(target_os = "ios")))'.dependencies] backtrace-sys = { path = "backtrace-sys", version = "0.1.17", optional = true } [build-dependencies] autocfg = "0.1" # 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", "coresymbolication", "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. # - 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", "std"] dladdr = [] coresymbolication = [] gimli-symbolize = ["addr2line", "findshlibs", "gimli", "memmap", "object" ] #======================================= # Methods of serialization # # Various features used for enabling rustc-serialize or syntex codegen. serialize-rustc = ["rustc-serialize"] serialize-serde = ["serde", "serde_derive"] [[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"] backtrace-0.3.13/Cargo.toml0000644000000053620000000000000111020ustar00# 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] name = "backtrace" version = "0.3.13" authors = ["Alex Crichton ", "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/alexcrichton/backtrace-rs" documentation = "https://docs.rs/backtrace" readme = "README.md" license = "MIT/Apache-2.0" repository = "https://github.com/alexcrichton/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"] [dependencies.addr2line] version = "0.7.0" optional = true [dependencies.cfg-if] version = "0.1.6" [dependencies.cpp_demangle] version = "0.2.3" optional = true default-features = false [dependencies.findshlibs] version = "0.4.0" optional = true [dependencies.gimli] version = "0.16.0" optional = true [dependencies.memmap] version = "0.7.0" optional = true [dependencies.object] version = "0.9.0" optional = true [dependencies.rustc-demangle] version = "0.1.4" [dependencies.rustc-serialize] version = "0.3" optional = true [dependencies.serde] version = "1.0" optional = true [dependencies.serde_derive] version = "1.0" optional = true [build-dependencies.autocfg] version = "0.1" [features] coresymbolication = [] dbghelp = [] default = ["std", "libunwind", "libbacktrace", "coresymbolication", "dladdr", "dbghelp"] dladdr = [] gimli-symbolize = ["addr2line", "findshlibs", "gimli", "memmap", "object"] kernel32 = [] libbacktrace = ["backtrace-sys", "std"] libunwind = [] serialize-rustc = ["rustc-serialize"] serialize-serde = ["serde", "serde_derive"] std = [] unix-backtrace = [] [target."cfg(all(unix, not(target_os = \"fuchsia\"), not(target_os = \"emscripten\"), not(target_os = \"macos\"), not(target_os = \"ios\")))".dependencies.backtrace-sys] version = "0.1.17" optional = true [target."cfg(unix)".dependencies.libc] version = "0.2.45" default-features = false [target."cfg(windows)".dependencies.winapi] version = "0.3.3" features = ["dbghelp", "processthreadsapi", "winnt", "minwindef"] backtrace-0.3.13/LICENSE-APACHE010066400017500001750000000251371253576523100137050ustar0000000000000000 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.13/LICENSE-MIT010066400017500001750000000020411253576523100134020ustar0000000000000000Copyright (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.13/README.md010066400017500001750000000043701340452515200132240ustar0000000000000000# backtrace-rs [![Build Status](https://travis-ci.org/alexcrichton/backtrace-rs.svg?branch=master)](https://travis-ci.org/alexcrichton/backtrace-rs) [![Build status](https://ci.appveyor.com/api/projects/status/v4l9oj4aqbbgyx44?svg=true)](https://ci.appveyor.com/project/alexcrichton/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 more stable and programmatic interface. ## Install ```toml [dependencies] backtrace = "0.3" ``` ```rust extern crate backtrace; ``` Note that this crate requires `make`, `objcopy`, and `ar` to be present on Linux systems. ## 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(ip, |symbol| { if let Some(name) = symbol.name() { // ... } if let Some(filename) = symbol.filename() { // ... } }); true // keep going to the next frame }); } ``` ## Platform Support This library currently supports OSX, Linux, and Windows. Support for other platforms is always welcome! # 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.13/appveyor.yml010066400017500001750000000014021340410606700143260ustar0000000000000000environment: matrix: - TARGET: x86_64-pc-windows-gnu MSYS_BITS: 64 - TARGET: i686-pc-windows-gnu MSYS_BITS: 32 - TARGET: x86_64-pc-windows-msvc - TARGET: i686-pc-windows-msvc install: - ps: Start-FileDownload "https://static.rust-lang.org/dist/rust-nightly-${env:TARGET}.exe" - rust-nightly-%TARGET%.exe /VERYSILENT /NORESTART /DIR="C:\Program Files (x86)\Rust" - set PATH=%PATH%;C:\Program Files (x86)\Rust\bin - if defined MSYS_BITS set PATH=%PATH%;C:\msys64\mingw%MSYS_BITS%\bin - rustc -V - cargo -V build: false test_script: - cargo test --target %TARGET% - cargo build --target %TARGET% --no-default-features - cargo build --target %TARGET% --no-default-features --features dbghelp branches: only: - master backtrace-0.3.13/build.rs010066400017500001750000000005011340452515300134030ustar0000000000000000extern crate autocfg; fn main() { let ac = autocfg::new(); // ffi types moved from `std` to `core` in Rust 1.30, so we need to adjust imports based on // this. // // ac.emit_rustc_version(1, 30); println!("cargo:rerun-if-changed=build.rs"); } backtrace-0.3.13/ci/android-ndk.sh010077500017500001750000000007631336270676200151060ustar0000000000000000set -ex ANDROID_ARCH=$1 ANDROID_SDK_VERSION=4333796 mkdir /tmp/android cd /tmp/android curl -o android-sdk.zip \ "https://dl.google.com/android/repository/sdk-tools-linux-${ANDROID_SDK_VERSION}.zip" unzip -q android-sdk.zip yes | ./tools/bin/sdkmanager --licenses > /dev/null ./tools/bin/sdkmanager ndk-bundle > /dev/null ./ndk-bundle/build/tools/make_standalone_toolchain.py \ --arch $ANDROID_ARCH \ --stl=libc++ \ --api 21 \ --install-dir /android-toolchain cd /tmp rm -rf android backtrace-0.3.13/ci/docker/aarch64-linux-android/Dockerfile010066400017500001750000000006611330425063500216030ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ ca-certificates \ unzip \ openjdk-8-jre \ python \ gcc \ libc6-dev COPY android-ndk.sh / RUN /android-ndk.sh arm64 ENV PATH=$PATH:/android-toolchain/bin # TODO: run tests in an emulator eventually ENV CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER=aarch64-linux-android-gcc \ CARGO_TARGET_AARCH64_LINUX_ANDROID_RUNNER="true" backtrace-0.3.13/ci/docker/aarch64-unknown-linux-gnu/Dockerfile010066400017500001750000000005451330202746000224460ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ ca-certificates \ libc6-dev \ gcc-aarch64-linux-gnu \ libc6-dev-arm64-cross \ qemu-user ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-aarch64 -L /usr/aarch64-linux-gnu" backtrace-0.3.13/ci/docker/arm-linux-androideabi/Dockerfile010066400017500001750000000006571336270676200217730ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ ca-certificates \ unzip \ openjdk-8-jre \ python \ gcc \ libc6-dev COPY android-ndk.sh / RUN /android-ndk.sh arm ENV PATH=$PATH:/android-toolchain/bin # TODO: run tests in an emulator eventually ENV CARGO_TARGET_ARM_LINUX_ANDROIDEABI_LINKER=arm-linux-androideabi-gcc \ CARGO_TARGET_ARM_LINUX_ANDROIDEABI_RUNNER="true" backtrace-0.3.13/ci/docker/arm-unknown-linux-gnueabihf/Dockerfile010066400017500001750000000005521330202746000231320ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ ca-certificates \ libc6-dev \ gcc-arm-linux-gnueabihf \ libc6-dev-armhf-cross \ qemu-user ENV CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \ CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_RUNNER="qemu-arm -L /usr/arm-linux-gnueabihf" backtrace-0.3.13/ci/docker/armv7-linux-androideabi/Dockerfile010066400017500001750000000006631330425063500222320ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ ca-certificates \ unzip \ openjdk-8-jre \ python \ gcc \ libc6-dev COPY android-ndk.sh / RUN /android-ndk.sh arm ENV PATH=$PATH:/android-toolchain/bin # TODO: run tests in an emulator eventually ENV CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_LINKER=arm-linux-androideabi-gcc \ CARGO_TARGET_ARMV7_LINUX_ANDROIDEABI_RUNNER="true" backtrace-0.3.13/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile010066400017500001750000000005561330202746000234130ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ ca-certificates \ libc6-dev \ gcc-arm-linux-gnueabihf \ libc6-dev-armhf-cross \ qemu-user ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \ CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER="qemu-arm -L /usr/arm-linux-gnueabihf" backtrace-0.3.13/ci/docker/i586-unknown-linux-gnu/Dockerfile010066400017500001750000000002061330202746000217030ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc-multilib \ libc6-dev \ ca-certificates backtrace-0.3.13/ci/docker/i686-linux-android/Dockerfile010066400017500001750000000006461330425063500210520ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ ca-certificates \ unzip \ openjdk-8-jre \ python \ gcc \ libc6-dev COPY android-ndk.sh / RUN /android-ndk.sh x86 ENV PATH=$PATH:/android-toolchain/bin # TODO: run tests in an emulator eventually ENV CARGO_TARGET_I686_LINUX_ANDROID_LINKER=i686-linux-android-gcc \ CARGO_TARGET_I686_LINUX_ANDROID_RUNNER="true" backtrace-0.3.13/ci/docker/i686-unknown-linux-gnu/Dockerfile010066400017500001750000000002061330202746000217040ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc-multilib \ libc6-dev \ ca-certificates backtrace-0.3.13/ci/docker/powerpc-unknown-linux-gnu/Dockerfile010066400017500001750000000006041330202746000226710ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc libc6-dev qemu-user ca-certificates \ gcc-powerpc-linux-gnu libc6-dev-powerpc-cross \ qemu-system-ppc ENV CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_LINKER=powerpc-linux-gnu-gcc \ CARGO_TARGET_POWERPC_UNKNOWN_LINUX_GNU_RUNNER="qemu-ppc -cpu Vger -L /usr/powerpc-linux-gnu" backtrace-0.3.13/ci/docker/powerpc64-unknown-linux-gnu/Dockerfile010066400017500001750000000010321330202746000230370ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ ca-certificates \ libc6-dev \ gcc-powerpc64-linux-gnu \ libc6-dev-ppc64-cross \ qemu-user \ qemu-system-ppc ENV CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_LINKER=powerpc64-linux-gnu-gcc \ # TODO: should actually run these tests #CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_RUNNER="qemu-ppc64 -L /usr/powerpc64-linux-gnu" \ CARGO_TARGET_POWERPC64_UNKNOWN_LINUX_GNU_RUNNER=true \ CC=powerpc64-linux-gnu-gcc backtrace-0.3.13/ci/docker/x86_64-linux-android/Dockerfile010066400017500001750000000006571330425063500213160ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ curl \ ca-certificates \ unzip \ openjdk-8-jre \ python \ gcc \ libc6-dev COPY android-ndk.sh / RUN /android-ndk.sh x86_64 ENV PATH=$PATH:/android-toolchain/bin # TODO: run tests in an emulator eventually ENV CARGO_TARGET_X86_64_LINUX_ANDROID_LINKER=x86_64-linux-android-gcc \ CARGO_TARGET_X86_64_LINUX_ANDROID_RUNNER="true" backtrace-0.3.13/ci/docker/x86_64-pc-windows-gnu/Dockerfile010066400017500001750000000005151330202746000214070ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ libc6-dev \ ca-certificates \ gcc-mingw-w64-x86-64 # No need to run tests, we're just testing that it compiles ENV CARGO_TARGET_X86_64_PC_WINDOWS_GNU_RUNNER=true \ CARGO_TARGET_X86_64_PC_WINDOWS_GNU_LINKER=x86_64-w64-mingw32-gcc backtrace-0.3.13/ci/docker/x86_64-unknown-linux-gnu/Dockerfile010066400017500001750000000001751330202746000221530ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ libc6-dev \ ca-certificates backtrace-0.3.13/ci/docker/x86_64-unknown-linux-musl/Dockerfile010066400017500001750000000002141330202746000223340ustar0000000000000000FROM ubuntu:18.04 RUN apt-get update && apt-get install -y --no-install-recommends \ gcc \ libc6-dev \ ca-certificates \ musl-tools backtrace-0.3.13/ci/run-docker.sh010077500017500001750000000013161330425063500147450ustar0000000000000000# Small script to run tests for a target (or all targets) inside all the # respective docker images. set -ex run() { docker build -t backtrace -f ci/docker/$1/Dockerfile ci mkdir -p target docker run \ --user `id -u`:`id -g` \ --rm \ --init \ --volume $HOME/.cargo:/cargo \ --env CARGO_HOME=/cargo \ --volume `rustc --print sysroot`:/rust:ro \ --env TARGET=$1 \ --volume `pwd`:/checkout:ro \ --volume `pwd`/target:/checkout/target \ --workdir /checkout \ --privileged \ backtrace \ bash \ -c 'PATH=$PATH:/rust/bin exec ci/run.sh' } if [ -z "$1" ]; then for d in `ls ci/docker/`; do run $d done else run $1 fi backtrace-0.3.13/ci/run.sh010077500017500001750000000000601330202746000134670ustar0000000000000000#!/bin/sh set -ex cargo test --target $TARGET backtrace-0.3.13/examples/backtrace.rs010066400017500001750000000001521270323272600160450ustar0000000000000000extern crate backtrace; use backtrace::Backtrace; fn main() { println!("{:?}", Backtrace::new()); } backtrace-0.3.13/examples/raw.rs010066400017500001750000000023301270323021500147060ustar0000000000000000extern 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.13/src/backtrace/dbghelp.rs010066400017500001750000000062561340410606700164330ustar0000000000000000// 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. #![allow(bad_style)] use core::mem; use core::prelude::v1::*; use winapi::shared::minwindef::*; use winapi::um::processthreadsapi; use winapi::um::winnt::{self, CONTEXT}; use winapi::um::dbghelp; use winapi::um::dbghelp::*; use types::c_void; pub struct Frame { inner: STACKFRAME64, } impl Frame { pub fn ip(&self) -> *mut c_void { self.inner.AddrPC.Offset as *mut _ } pub fn symbol_address(&self) -> *mut c_void { self.ip() } } #[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 = processthreadsapi::GetCurrentProcess(); let thread = processthreadsapi::GetCurrentThread(); let mut context = mem::zeroed::(); winnt::RtlCaptureContext(&mut context.0); let mut frame = super::Frame { inner: Frame { inner: mem::zeroed() }, }; let image = init_frame(&mut frame.inner.inner, &context.0); // Initialize this process's symbols let _c = ::dbghelp_init(); // And now that we're done with all the setup, do the stack walking! while dbghelp::StackWalk64(image as DWORD, process, thread, &mut frame.inner.inner, &mut context.0 as *mut CONTEXT as *mut _, None, Some(dbghelp::SymFunctionTableAccess64), Some(dbghelp::SymGetModuleBase64), None) == TRUE { if frame.inner.inner.AddrPC.Offset == frame.inner.inner.AddrReturn.Offset || frame.inner.inner.AddrPC.Offset == 0 || frame.inner.inner.AddrReturn.Offset == 0 { break } if !cb(&frame) { break } } } #[cfg(target_arch = "x86_64")] fn init_frame(frame: &mut STACKFRAME64, ctx: &CONTEXT) -> WORD { frame.AddrPC.Offset = ctx.Rip as u64; frame.AddrPC.Mode = AddrModeFlat; frame.AddrStack.Offset = ctx.Rsp as u64; frame.AddrStack.Mode = AddrModeFlat; frame.AddrFrame.Offset = ctx.Rbp as u64; frame.AddrFrame.Mode = AddrModeFlat; winnt::IMAGE_FILE_MACHINE_AMD64 } #[cfg(target_arch = "x86")] fn init_frame(frame: &mut STACKFRAME64, ctx: &CONTEXT) -> WORD { frame.AddrPC.Offset = ctx.Eip as u64; frame.AddrPC.Mode = AddrModeFlat; frame.AddrStack.Offset = ctx.Esp as u64; frame.AddrStack.Mode = AddrModeFlat; frame.AddrFrame.Offset = ctx.Ebp as u64; frame.AddrFrame.Mode = AddrModeFlat; winnt::IMAGE_FILE_MACHINE_I386 } backtrace-0.3.13/src/backtrace/libunwind.rs010066400017500001750000000155071340410606700170200ustar0000000000000000// 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. use types::c_void; pub struct Frame { ctx: *mut uw::_Unwind_Context, } impl Frame { pub fn ip(&self) -> *mut c_void { let mut ip_before_insn = 0; let mut ip = unsafe { uw::_Unwind_GetIPInfo(self.ctx, &mut ip_before_insn) as *mut c_void }; if !ip.is_null() && ip_before_insn == 0 { // this is a non-signaling frame, so `ip` refers to the address // after the calling instruction. account for that. ip = (ip as usize - 1) as *mut _; } return ip } pub fn symbol_address(&self) -> *mut c_void { // dladdr() on osx gets whiny when we use FindEnclosingFunction, and // it appears to work fine without it, so we only use // FindEnclosingFunction on non-osx platforms. In doing so, we get a // slightly more accurate stack trace in the process. // // This is often because panic involves the last instruction of a // function being "call std::rt::begin_unwind", with no ret // instructions after it. This means that the return instruction // pointer points *outside* of the calling function, and by // unwinding it we go back to the original function. if cfg!(target_os = "macos") || cfg!(target_os = "ios") { self.ip() } else { unsafe { uw::_Unwind_FindEnclosingFunction(self.ip()) } } } } #[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 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 { ctx: ctx }, }; let mut bomb = ::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 libc::{self, c_int}; use types::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 fn(ctx: *mut _Unwind_Context, arg: *mut c_void) -> _Unwind_Reason_Code; extern { // 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 = "linux", target_arch = "arm"))))] pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, ip_before_insn: *mut c_int) -> libc::uintptr_t; #[cfg(all(not(target_os = "android"), 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 = "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 { 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 doesn't exist on Android or ARM/Linux, so make it same // to _Unwind_GetIP #[cfg(any(all(target_os = "android", target_arch = "arm"), all(target_os = "linux", target_arch = "arm")))] pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, ip_before_insn: *mut c_int) -> libc::uintptr_t { *ip_before_insn = 0; _Unwind_GetIP(ctx) } // 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 = "linux", target_arch = "arm")))] pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut c_void) -> *mut c_void { pc } } backtrace-0.3.13/src/backtrace/mod.rs010066400017500001750000000105261340410606700156000ustar0000000000000000use core::fmt; use types::c_void; /// 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. /// /// # Example /// /// ``` /// extern crate backtrace; /// /// fn main() { /// backtrace::trace(|frame| { /// // ... /// /// true // continue the backtrace /// }); /// } /// ``` #[inline(always)] #[cfg(feature = "std")] pub fn trace bool>(cb: F) { let _guard = ::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. #[inline(never)] 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. pub struct Frame { 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! { if #[cfg(all(unix, not(target_os = "emscripten"), not(all(target_os = "ios", target_arch = "arm")), feature = "libunwind"))] { mod libunwind; use self::libunwind::trace as trace_imp; 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; use self::unix_backtrace::Frame as FrameImp; } else if #[cfg(all(windows, feature = "dbghelp"))] { mod dbghelp; use self::dbghelp::trace as trace_imp; use self::dbghelp::Frame as FrameImp; } else { mod noop; use self::noop::trace as trace_imp; use self::noop::Frame as FrameImp; } } backtrace-0.3.13/src/backtrace/noop.rs010066400017500001750000000004161340410606700157710ustar0000000000000000use types::c_void; #[inline(always)] pub fn trace(_cb: &mut FnMut(&super::Frame) -> bool) {} 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.13/src/backtrace/unix_backtrace.rs010066400017500001750000000022611340410606700200000ustar0000000000000000// 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. use core::mem; use libc::c_int; use types::c_void; pub struct Frame { addr: *mut c_void, } impl Frame { pub fn ip(&self) -> *mut c_void { self.addr } pub fn symbol_address(&self) -> *mut c_void { self.addr } } extern { 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 }, }; if !cb(&cx) { return } } } backtrace-0.3.13/src/capture.rs010066400017500001750000000212651340452515200145470ustar0000000000000000use std::prelude::v1::*; use std::fmt; use std::path::{Path, PathBuf}; use {trace, resolve, SymbolName}; use types::c_void; /// 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. #[derive(Clone)] #[cfg_attr(feature = "serialize-rustc", derive(RustcDecodable, RustcEncodable))] #[cfg_attr(feature = "serialize-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, } /// 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. #[derive(Clone)] #[cfg_attr(feature = "serialize-rustc", derive(RustcDecodable, RustcEncodable))] #[cfg_attr(feature = "serialize-serde", derive(Deserialize, Serialize))] pub struct BacktraceFrame { ip: usize, symbol_address: usize, symbols: Option>, } /// 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. #[derive(Clone)] #[cfg_attr(feature = "serialize-rustc", derive(RustcDecodable, RustcEncodable))] #[cfg_attr(feature = "serialize-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. /// /// # Examples /// /// ``` /// use backtrace::Backtrace; /// /// let current_backtrace = Backtrace::new(); /// ``` #[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 /// ``` #[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 ip_lo = ip; let ip_hi = ip + 128; let mut frames = Vec::new(); let mut actual_start_index = None; trace(|frame| { let ip = frame.ip() as usize; frames.push(BacktraceFrame { ip, symbol_address: frame.symbol_address() as usize, symbols: None, }); if cfg!(not(all(target_os = "windows", target_arch = "x86"))) && ip >= ip_lo && ip <= ip_hi && 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. 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. pub fn resolve(&mut self) { for frame in self.frames.iter_mut().filter(|f| f.symbols.is_none()) { let mut symbols = Vec::new(); resolve(frame.ip as *mut _, |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(), }); }); 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` pub fn ip(&self) -> *mut c_void { self.ip as *mut c_void } /// Same as `Frame::symbol_address` pub fn symbol_address(&self) -> *mut c_void { self.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. pub fn symbols(&self) -> &[BacktraceSymbol] { self.symbols.as_ref().map(|s| &s[..]).unwrap_or(&[]) } } impl BacktraceSymbol { /// Same as `Symbol::name` pub fn name(&self) -> Option { self.name.as_ref().map(|s| SymbolName::new(s)) } /// Same as `Symbol::addr` pub fn addr(&self) -> Option<*mut c_void> { self.addr.map(|s| s as *mut c_void) } /// Same as `Symbol::filename` pub fn filename(&self) -> Option<&Path> { self.filename.as_ref().map(|p| &**p) } /// Same as `Symbol::lineno` pub fn lineno(&self) -> Option { self.lineno } } impl fmt::Debug for Backtrace { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "stack backtrace:")?; let iter = if fmt.alternate() { self.frames.iter() } else { self.frames[self.actual_start_index..].iter() }; for (idx, frame) in iter.enumerate() { let ip = frame.ip(); write!(fmt, "\n{:4}: ", idx)?; let symbols = match frame.symbols { Some(ref s) => s, None => { write!(fmt, " ({:?})", ip)?; continue } }; if symbols.len() == 0 { write!(fmt, " ({:?})", ip)?; continue; } for (idx, symbol) in symbols.iter().enumerate() { if idx != 0 { write!(fmt, "\n ")?; } if let Some(name) = symbol.name() { write!(fmt, "{}", name)?; } else { write!(fmt, "")?; } if idx == 0 { write!(fmt, " ({:?})", ip)?; } if let (Some(file), Some(line)) = (symbol.filename(), symbol.lineno()) { write!(fmt, "\n at {}:{}", file.display(), line)?; } } } Ok(()) } } impl Default for Backtrace { fn default() -> Backtrace { Backtrace::new() } } backtrace-0.3.13/src/dylib.rs010066400017500001750000000034301340410606700142010ustar0000000000000000use core::marker; use core::mem; use core::sync::atomic::{AtomicUsize, Ordering}; use libc::{self, c_char, c_void}; pub struct Dylib { pub init: AtomicUsize, } pub struct Symbol { pub name: &'static str, pub addr: AtomicUsize, pub _marker: marker::PhantomData, } impl Dylib { pub unsafe fn get<'a, T>(&self, sym: &'a Symbol) -> Option<&'a T> { self.load().and_then(|handle| { sym.get(handle) }) } pub unsafe fn init(&self, path: &str) -> bool { if self.init.load(Ordering::SeqCst) != 0 { return true } assert!(path.as_bytes()[path.len() - 1] == 0); let ptr = libc::dlopen(path.as_ptr() as *const c_char, libc::RTLD_LAZY); if ptr.is_null() { return false } match self.init.compare_and_swap(0, ptr as usize, Ordering::SeqCst) { 0 => {} _ => { libc::dlclose(ptr); } } return true } unsafe fn load(&self) -> Option<*mut c_void> { match self.init.load(Ordering::SeqCst) { 0 => None, n => Some(n as *mut c_void), } } } impl Symbol { unsafe fn get(&self, handle: *mut c_void) -> Option<&T> { assert_eq!(mem::size_of::(), mem::size_of_val(&self.addr)); if self.addr.load(Ordering::SeqCst) == 0 { self.addr.store(fetch(handle, self.name.as_ptr()), Ordering::SeqCst) } if self.addr.load(Ordering::SeqCst) == 1 { None } else { mem::transmute::<&AtomicUsize, Option<&T>>(&self.addr) } } } unsafe fn fetch(handle: *mut c_void, name: *const u8) -> usize { let ptr = libc::dlsym(handle, name as *const _); if ptr.is_null() { 1 } else { ptr as usize } } backtrace-0.3.13/src/lib.rs010066400017500001750000000121371340410606700136500ustar0000000000000000//! 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. //! //! # Platform Support //! //! Currently this library is verified to work on Linux, OSX, and Windows, but //! it may work on other platforms as well. Note that the quality of the //! backtrace may vary across platforms. //! //! # 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.2" //! ``` //! //! 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(ip, |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(feature = "std")] #[macro_use] extern crate std; #[cfg(unix)] extern crate libc; #[cfg(windows)] extern crate winapi; #[cfg(feature = "serde_derive")] #[cfg_attr(feature = "serde_derive", macro_use)] extern crate serde_derive; #[cfg(feature = "rustc-serialize")] extern crate rustc_serialize; #[macro_use] extern crate cfg_if; extern crate rustc_demangle; #[cfg(feature = "cpp_demangle")] extern crate cpp_demangle; cfg_if! { if #[cfg(all(feature = "gimli-symbolize", unix, target_os = "linux"))] { extern crate addr2line; extern crate findshlibs; extern crate gimli; extern crate memmap; extern crate object; } } #[allow(dead_code)] // not used everywhere #[cfg(unix)] #[macro_use] mod dylib; pub use backtrace::{trace_unsynchronized, Frame}; mod backtrace; pub use symbolize::{resolve_unsynchronized, Symbol, SymbolName}; mod symbolize; pub use types::BytesOrWideString; mod types; cfg_if! { if #[cfg(feature = "std")] { pub use backtrace::trace; pub use symbolize::resolve; pub use 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::cell::Cell; use std::boxed::Box; use std::sync::{Once, Mutex, MutexGuard, ONCE_INIT}; pub struct LockGuard(MutexGuard<'static, ()>); static mut LOCK: *mut Mutex<()> = 0 as *mut _; static INIT: Once = ONCE_INIT; thread_local!(static LOCK_HELD: Cell = Cell::new(false)); impl Drop for LockGuard { fn drop(&mut self) { LOCK_HELD.with(|slot| { assert!(slot.get()); slot.set(false); }); } } pub fn lock() -> Option { if LOCK_HELD.with(|l| l.get()) { return None } LOCK_HELD.with(|s| s.set(true)); unsafe { INIT.call_once(|| { LOCK = Box::into_raw(Box::new(Mutex::new(()))); }); Some(LockGuard((*LOCK).lock().unwrap())) } } } // requires external synchronization #[cfg(all(windows, feature = "dbghelp"))] unsafe fn dbghelp_init() { use winapi::shared::minwindef; use winapi::um::{dbghelp, processthreadsapi}; static mut INITIALIZED: bool = false; if !INITIALIZED { dbghelp::SymInitializeW(processthreadsapi::GetCurrentProcess(), 0 as *mut _, minwindef::TRUE); INITIALIZED = true; } } backtrace-0.3.13/src/symbolize/coresymbolication.rs010066400017500001750000000132201340410606700206360ustar0000000000000000// 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. #![allow(bad_style)] use core::mem; use core::ptr; use core::slice; use core::sync::atomic::ATOMIC_USIZE_INIT; use libc::{self, Dl_info, c_char, c_int}; use SymbolName; use dylib::Dylib; use dylib::Symbol as DylibSymbol; use types::{BytesOrWideString, c_void}; #[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 { Core { path: *const c_char, lineno: u32, name: *const c_char, addr: *mut c_void, }, Dladdr(Dl_info), } impl Symbol { pub fn name(&self) -> Option { let name = match *self { Symbol::Core { name, .. } => name, Symbol::Dladdr(ref info) => info.dli_sname, }; 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) => Some(info.dli_saddr as *mut _), } } pub fn filename_raw(&self) -> Option { match *self { Symbol::Core { path, .. } => { if path.is_null() { None } else { Some(BytesOrWideString::Bytes(unsafe { let len = libc::strlen(path); slice::from_raw_parts(path as *const u8, len) })) } } Symbol::Dladdr(_) => None, } } pub fn lineno(&self) -> Option { match *self { Symbol::Core { lineno: 0, .. } => None, Symbol::Core { lineno, .. } => Some(lineno), Symbol::Dladdr(_) => None, } } } static CORESYMBOLICATION: Dylib = Dylib { init: ATOMIC_USIZE_INIT }; macro_rules! dlsym { (extern { $(fn $name:ident($($arg:ident: $t:ty),*) -> $ret:ty;)* }) => ($( static $name: ::dylib::Symbol $ret> = ::dylib::Symbol { name: concat!(stringify!($name), "\0"), addr: ::core::sync::atomic::ATOMIC_USIZE_INIT, _marker: ::core::marker::PhantomData, }; )*) } dlsym! { extern { fn CSSymbolicatorCreateWithPid(pid: c_int) -> CSTypeRef; fn CSRelease(rf: CSTypeRef) -> c_void; 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 get(sym: &DylibSymbol) -> &T { CORESYMBOLICATION.get(sym).unwrap() } unsafe fn try_resolve(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) -> bool { let path = "/System/Library/PrivateFrameworks/CoreSymbolication.framework\ /Versions/A/CoreSymbolication\0"; if !CORESYMBOLICATION.init(path) { return false; } let cs = get(&CSSymbolicatorCreateWithPid)(libc::getpid()); if cs == CSREF_NULL { return false } let info = get(&CSSymbolicatorGetSourceInfoWithAddressAtTime)( cs, addr, CS_NOW); let sym = if info == CSREF_NULL { get(&CSSymbolicatorGetSymbolWithAddressAtTime)(cs, addr, CS_NOW) } else { get(&CSSourceInfoGetSymbol)(info) }; let mut rv = false; if sym != CSREF_NULL { let owner = get(&CSSymbolGetSymbolOwner)(sym); if owner != CSREF_NULL { cb(&super::Symbol { inner: Symbol::Core { path: if info != CSREF_NULL { get(&CSSourceInfoGetPath)(info) } else { ptr::null() }, lineno: if info != CSREF_NULL { get(&CSSourceInfoGetLineNumber)(info) as u32 } else { 0 }, name: get(&CSSymbolGetMangledName)(sym), addr: get(&CSSymbolOwnerGetBaseAddress)(owner), }, }); rv = true; } } get(&CSRelease)(cs); rv } pub unsafe fn resolve(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) { if try_resolve(addr, cb) { return } let mut info: Dl_info = mem::zeroed(); if libc::dladdr(addr as *mut _, &mut info) != 0 { cb(&super::Symbol { inner: Symbol::Dladdr(info), }); } } backtrace-0.3.13/src/symbolize/dbghelp.rs010066400017500001750000000125361340452515300165300ustar0000000000000000// 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. #![allow(bad_style)] // This is a hack for compatibility with rustc 1.25.0. The no_std mode of this // crate is not supported pre-1.30.0, but in std mode the `char` module here // moved in rustc 1.26.0 (ish). As a result, in std mode we use `std::char` to // retain compatibility with rustc 1.25.0, but in `no_std` mode (which is // 1.30.0+ already) we use `core::char`. #[cfg(feature = "std")] use std::char; #[cfg(not(feature = "std"))] use core::char; use core::mem; use core::slice; use winapi::ctypes::*; use winapi::shared::basetsd::*; use winapi::shared::minwindef::*; use winapi::um::processthreadsapi; use winapi::um::dbghelp; use winapi::um::dbghelp::*; use SymbolName; use types::BytesOrWideString; // Store an OsString on std so we can provide the symbol name and filename. pub struct Symbol { 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: (), } 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::ffi::OsString> { self._filename_cache.as_ref() } } #[repr(C, align(8))] struct Aligned8(T); pub unsafe fn resolve(addr: *mut c_void, 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; let _c = ::dbghelp_init(); let mut displacement = 0u64; let ret = dbghelp::SymFromAddrW(processthreadsapi::GetCurrentProcess(), addr as DWORD64, &mut displacement, info); if ret != 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 displacement = 0; let ret = dbghelp::SymGetLineFromAddrW64(processthreadsapi::GetCurrentProcess(), addr as DWORD64, &mut displacement, &mut line); let mut filename = None; let mut lineno = None; if ret == 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), }, }) } #[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.13/src/symbolize/dladdr.rs010066400017500001750000000030111340410606700163400ustar0000000000000000// 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. use core::{mem, slice}; use types::{BytesOrWideString, c_void}; use libc::{self, Dl_info}; use SymbolName; pub struct Symbol { inner: Dl_info, } 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 } pub fn lineno(&self) -> Option { None } } pub unsafe fn resolve(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) { let mut info: super::Symbol = super::Symbol { inner: Symbol { inner: mem::zeroed(), }, }; if libc::dladdr(addr as *mut _, &mut info.inner.inner) != 0 { cb(&info) } } backtrace-0.3.13/src/symbolize/gimli.rs010066400017500001750000000156751340410606700162320ustar0000000000000000use addr2line; use findshlibs::{self, Segment, SharedLibrary}; use gimli; use memmap::Mmap; use object::{self, Object}; use std::cell::RefCell; use std::env; use std::fs::File; use std::mem; use libc::c_void; use std::path::PathBuf; use std::u32; use std::prelude::v1::*; use SymbolName; use types::BytesOrWideString; const MAPPINGS_CACHE_SIZE: usize = 4; type Dwarf = addr2line::Context>; type Symbols<'map> = object::SymbolMap<'map>; struct Mapping { dwarf: Dwarf, // 'static lifetime is a lie to hack around lack of support for self-referential structs. symbols: Symbols<'static>, _map: Mmap, } impl Mapping { fn new(path: &PathBuf) -> Option { let file = File::open(path).ok()?; // TODO: not completely safe, see https://github.com/danburkert/memmap-rs/issues/25 let map = unsafe { Mmap::map(&file).ok()? }; let (dwarf, symbols) = { let object = object::File::parse(&*map).ok()?; let dwarf = addr2line::Context::new(&object).ok()?; let symbols = object.symbol_map(); // Convert to 'static lifetimes. (dwarf, unsafe { mem::transmute(symbols) }) }; Some(Mapping { dwarf, symbols, _map: map, }) } // Ensure the 'static lifetimes don't leak. fn rent(&self, mut f: F) where F: FnMut(&Dwarf, &Symbols), { f(&self.dwarf, &self.symbols) } } thread_local! { // 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 MAPPINGS_CACHE: RefCell> = RefCell::new(Vec::with_capacity(MAPPINGS_CACHE_SIZE)); } fn with_mapping_for_path(path: PathBuf, f: F) where F: FnMut(&Dwarf, &Symbols), { MAPPINGS_CACHE.with(|cache| { let mut cache = cache.borrow_mut(); let idx = cache.iter().position(|&(ref p, _)| p == &path); // 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 = cache.remove(idx); cache.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 mapping = match Mapping::new(&path) { None => return, Some(m) => m, }; if cache.len() == MAPPINGS_CACHE_SIZE { cache.pop(); } cache.insert(0, (path, mapping)); } cache[0].1.rent(f); }); } pub fn resolve(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) { // First, find the file containing the segment that the given AVMA (after // relocation) address falls within. Use the containing segment to compute // the SVMA (before relocation) address. // // Note that the OS APIs that `SharedLibrary::each` is implemented with hold // a lock for the duration of the `each` call, so we want to keep this // section as short as possible to avoid contention with other threads // capturing backtraces. let addr = findshlibs::Avma(addr as *mut u8 as *const u8); let mut so_info = None; findshlibs::TargetSharedLibrary::each(|so| { use findshlibs::IterationControl::*; for segment in so.segments() { if segment.contains_avma(so, addr) { let addr = so.avma_to_svma(addr); let path = so.name().to_string_lossy(); so_info = Some((addr, path.to_string())); return Break; } } Continue }); let (addr, path) = match so_info { None => return, Some((a, p)) => (a, p), }; // Second, fixup the path. Empty path means that this address falls within // the main executable, not a shared library. let path = if path.is_empty() { match env::current_exe() { Err(_) => return, Ok(p) => p, } } else { PathBuf::from(path) }; // 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. with_mapping_for_path(path, |dwarf, symbols| { let mut found_sym = false; if let Ok(mut frames) = dwarf.find_frames(addr.0 as u64) { while let Ok(Some(frame)) = frames.next() { let (file, line) = frame .location .map(|l| (l.file, l.line)) .unwrap_or((None, None)); let name = frame .function .and_then(|f| f.raw_name().ok().map(|f| f.to_string())); let sym = super::Symbol { inner: Symbol::new(addr.0 as usize, file, line, name), }; cb(&sym); found_sym = true; } } // No DWARF info found, so fallback to the symbol table. if !found_sym { if let Some(name) = symbols.get(addr.0 as u64).and_then(|x| x.name()) { let sym = super::Symbol { inner: Symbol::new(addr.0 as usize, None, None, Some(name.to_string())), }; cb(&sym); } } }); } pub struct Symbol { addr: usize, file: Option, line: Option, name: Option, } impl Symbol { fn new(addr: usize, file: Option, line: Option, name: Option) -> Symbol { Symbol { addr, file, line, name, } } pub fn name(&self) -> Option { self.name.as_ref().map(|s| SymbolName::new(s.as_bytes())) } pub fn addr(&self) -> Option<*mut c_void> { Some(self.addr as *mut c_void) } pub fn filename_raw(&self) -> Option { self.file.as_ref().map(|f| BytesOrWideString::Bytes(f.as_bytes())) } pub fn lineno(&self) -> Option { self.line .and_then(|l| if l > (u32::MAX as u64) { None } else { Some(l as u32) }) } } backtrace-0.3.13/src/symbolize/libbacktrace.rs010066400017500001750000000132301340410606700175200ustar0000000000000000// 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. #![allow(bad_style)] extern crate backtrace_sys as bt; use std::ffi::CStr; use std::{ptr, slice}; use std::sync::{ONCE_INIT, Once}; use libc::{self, c_char, c_int, c_void, uintptr_t}; use SymbolName; use types::BytesOrWideString; pub enum Symbol { Syminfo { pc: uintptr_t, symname: *const c_char, }, Pcinfo { pc: uintptr_t, filename: *const c_char, lineno: c_int, function: *const c_char, }, } impl Symbol { pub fn name(&self) -> Option { let ptr = match *self { Symbol::Syminfo { symname, .. } => symname, Symbol::Pcinfo { function, .. } => function, }; if ptr.is_null() { None } else { Some(SymbolName::new(unsafe { CStr::from_ptr(ptr).to_bytes() })) } } pub fn addr(&self) -> Option<*mut c_void> { let pc = match *self { Symbol::Syminfo { pc, .. } => pc, Symbol::Pcinfo { pc, .. } => pc, }; if pc == 0 {None} else {Some(pc as *mut _)} } pub fn filename_raw(&self) -> Option { match *self { Symbol::Syminfo { .. } => None, Symbol::Pcinfo { filename, .. } => { let ptr = filename as *const u8; unsafe { let len = libc::strlen(filename); Some(BytesOrWideString::Bytes(slice::from_raw_parts(ptr, len))) } } } } pub fn lineno(&self) -> Option { match *self { Symbol::Syminfo { .. } => None, Symbol::Pcinfo { lineno, .. } => Some(lineno as u32), } } } extern fn error_cb(_data: *mut c_void, _msg: *const c_char, _errnum: c_int) { // do nothing for now } extern fn syminfo_cb(data: *mut c_void, pc: uintptr_t, symname: *const c_char, _symval: uintptr_t, _symsize: uintptr_t) { unsafe { call(data, &super::Symbol { inner: Symbol::Syminfo { pc: pc, symname: symname, }, }); } } extern fn pcinfo_cb(data: *mut c_void, pc: uintptr_t, filename: *const c_char, lineno: c_int, function: *const c_char) -> c_int { unsafe { if filename.is_null() || function.is_null() { return -1 } call(data, &super::Symbol { inner: Symbol::Pcinfo { pc: pc, filename: filename, lineno: lineno, function: function, }, }); return 0 } } unsafe fn call(data: *mut c_void, sym: &super::Symbol) { let cb = data as *mut &mut FnMut(&super::Symbol); let mut bomb = ::Bomb { enabled: true }; (*cb)(sym); bomb.enabled = false; } // 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). // // Things don't work so well on not-Linux since libbacktrace can't track down // that executable this is. We at one point used env::current_exe but it turns // out that there are some serious security issues with that approach. // // Specifically, on certain platforms like BSDs, a malicious actor can cause an // arbitrary file to be placed at the path returned by current_exe. libbacktrace // does not behave defensively in the presence of ill-formed DWARF information, // and has been demonstrated to segfault in at least one case. There is no // evidence at the moment to suggest that a more carefully constructed file // can't cause arbitrary code execution. As a result of all of this, we don't // hint libbacktrace with the path to the current process. unsafe fn init_state() -> *mut bt::backtrace_state { static mut STATE: *mut bt::backtrace_state = 0 as *mut _; static INIT: Once = ONCE_INIT; INIT.call_once(|| { // Our libbacktrace may not have multithreading support, so // set `threaded = 0` and synchronize ourselves. STATE = bt::backtrace_create_state(ptr::null(), 0, error_cb, ptr::null_mut()); }); STATE } pub unsafe fn resolve(symaddr: *mut c_void, mut cb: &mut FnMut(&super::Symbol)) { // backtrace errors are currently swept under the rug let state = init_state(); if state.is_null() { return } let ret = bt::backtrace_pcinfo(state, symaddr as uintptr_t, pcinfo_cb, error_cb, &mut cb as *mut _ as *mut _); if ret != 0 { bt::backtrace_syminfo(state, symaddr as uintptr_t, syminfo_cb, error_cb, &mut cb as *mut _ as *mut _); } } backtrace-0.3.13/src/symbolize/mod.rs010066400017500001750000000275161340452515200157050ustar0000000000000000use core::{fmt, str}; cfg_if! { if #[cfg(feature = "std")] { use std::path::Path; use std::prelude::v1::*; } } use types::{BytesOrWideString, 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). /// /// # Required features /// /// This function requires the `std` feature of the `backtrace` crate to be /// enabled, and the `std` feature is enabled by default. /// /// # 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 = ::lock::lock(); unsafe { resolve_unsynchronized(addr, cb) } } /// 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. pub unsafe fn resolve_unsynchronized(addr: *mut c_void, mut cb: F) where F: FnMut(&Symbol) { resolve_imp(addr as *mut _, &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 { inner: SymbolImp, } 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`. /// /// This function requires the `std` feature to be enabled for this crate. #[cfg(feature = "std")] pub fn filename(&self) -> Option<&Path> { #[cfg(unix)] { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; match self.filename_raw() { Some(BytesOrWideString::Bytes(slice)) => { Some(Path::new(OsStr::from_bytes(slice))) } None => None, _ => unreachable!(), } } #[cfg(windows)] { self.inner.filename().map(Path::new) } #[cfg(all(not(windows), not(unix)))] { None } } } 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! { 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 symbol name as a `str` if the symbols is valid utf-8. 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! { 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! { 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) } } } } } cfg_if! { if #[cfg(all(windows, feature = "dbghelp"))] { mod dbghelp; use self::dbghelp::resolve as resolve_imp; use self::dbghelp::Symbol as SymbolImp; } else if #[cfg(all(feature = "std", feature = "gimli-symbolize", unix, target_os = "linux"))] { mod gimli; use self::gimli::resolve as resolve_imp; use self::gimli::Symbol as SymbolImp; } else if #[cfg(all(feature = "libbacktrace", unix, not(target_os = "fuchsia"), not(target_os = "emscripten"), not(target_os = "macos"), not(target_os = "ios")))] { mod libbacktrace; use self::libbacktrace::resolve as resolve_imp; use self::libbacktrace::Symbol as SymbolImp; // 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; } else if #[cfg(all(unix, not(target_os = "emscripten"), feature = "dladdr"))] { mod dladdr; use self::dladdr::resolve as resolve_imp; use self::dladdr::Symbol as SymbolImp; } else { mod noop; use self::noop::resolve as resolve_imp; use self::noop::Symbol as SymbolImp; } } backtrace-0.3.13/src/symbolize/noop.rs010066400017500001750000000007031340410606700160660ustar0000000000000000use types::{BytesOrWideString, c_void}; use SymbolName; pub unsafe fn resolve(_addr: *mut c_void, _cb: &mut FnMut(&super::Symbol)) { } pub struct Symbol; impl Symbol { pub fn name(&self) -> Option { None } pub fn addr(&self) -> Option<*mut c_void> { None } pub fn filename_raw(&self) -> Option { None } pub fn lineno(&self) -> Option { None } } backtrace-0.3.13/src/types.rs010066400017500001750000000043201340452515300142420ustar0000000000000000//! Platform dependent types. cfg_if! { if #[cfg(feature = "std")] { pub use std::os::raw::c_void; use std::borrow::Cow; use std::fmt; use std::path::PathBuf; use std::prelude::v1::*; } else if #[cfg(rustc_1_30)] { pub use core::ffi::c_void; } else { compile_error!("`backtrace` requires Rust >=1.30.0 to support `no_std`."); } } /// 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`. 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`. pub fn into_path_buf(self) -> PathBuf { #[cfg(unix)] { use self::BytesOrWideString::*; use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; match self { Bytes(slice) => PathBuf::from(OsStr::from_bytes(slice)), _ => unreachable!(), } } #[cfg(windows)] { use self::BytesOrWideString::*; use std::ffi::OsString; use std::os::windows::ffi::OsStringExt; match self { Wide(slice) => PathBuf::from(OsString::from_wide(slice)), _ => unreachable!(), } } #[cfg(all(not(windows), not(unix)))] { 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.13/tests/long_fn_name.rs010066400017500001750000000034621340410606700161000ustar0000000000000000extern crate backtrace; #[cfg(all(windows, feature = "dbghelp"))] extern crate winapi; 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() -> ::Backtrace { ::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 winapi::um::dbghelp; 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.13/tests/skip_inner_frames.rs010066400017500001750000000024061340410606700171510ustar0000000000000000extern crate backtrace; use backtrace::Backtrace; const FRAME_RANGE: usize = 128; // should be close enough not to give false positives #[test] #[cfg_attr(any(not(any(feature = "libunwind", feature = "unix-backtrace", feature = "dbghelp")), all(target_os = "windows", target_arch = "x86")), ignore)] fn backtrace_new_unresolved_should_start_with_call_site_trace() { let mut b = Backtrace::new_unresolved(); b.resolve(); println!("{:?}", b); println!("{:#?}", b); assert!(!b.frames().is_empty()); let this_ip = backtrace_new_unresolved_should_start_with_call_site_trace as usize; let frame_ip = b.frames().first().unwrap().ip() as usize; assert!(frame_ip >= this_ip); assert!(frame_ip <= this_ip + FRAME_RANGE); } #[test] #[cfg_attr(any(not(any(feature = "libunwind", feature = "unix-backtrace", feature = "dbghelp")), all(target_os = "windows", target_arch = "x86")), ignore)] fn backtrace_new_should_start_with_call_site_trace() { 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().ip() as usize; assert!(frame_ip >= this_ip); assert!(frame_ip <= this_ip + FRAME_RANGE); } backtrace-0.3.13/tests/smoke.rs010066400017500001750000000150611340410606700145720ustar0000000000000000extern crate backtrace; use std::os::raw::c_void; 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!(all(unix, feature = "libbacktrace")) && !cfg!(target_os = "fuchsia") && !cfg!(target_os = "macos") && !cfg!(target_os = "ios"); 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)] 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.ip(), cx.symbol_address())); true }); if v.len() < 5 { assert!(!LIBUNWIND); assert!(!UNIX_BACKTRACE); assert!(!DBGHELP); return } // On 32-bit windows apparently the first frame isn't our backtrace // frame but it's actually this frame. I'm not entirely sure why, but at // least it seems consistent? let o = if cfg!(all(windows, target_pointer_width = "32")) {1} else {0}; // frame offset 0 is the `backtrace::trace` function, but that's generic assert_frame(&v, o, 1, frame_4 as usize, "frame_4", "tests/smoke.rs", start_line + 6); assert_frame(&v, o, 2, frame_3 as usize, "frame_3", "tests/smoke.rs", start_line + 3); assert_frame(&v, o, 3, frame_2 as usize, "frame_2", "tests/smoke.rs", start_line + 2); assert_frame(&v, o, 4, frame_1 as usize, "frame_1", "tests/smoke.rs", start_line + 1); assert_frame(&v, o, 5, smoke_test_frames as usize, "smoke_test_frames", "", 0); } fn assert_frame(syms: &[(*mut c_void, *mut c_void)], offset: usize, idx: usize, actual_fn_pointer: usize, expected_name: &str, expected_file: &str, expected_line: u32) { if offset > idx { return } println!("frame: {}", idx); let (ip, sym) = syms[idx - offset]; let ip = ip as usize; let sym = sym as usize; assert!(ip >= sym); assert!(sym >= actual_fn_pointer); // 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(ip as *mut c_void, |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()); println!(" sym: {:?}", name); }); // 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.13/.cargo_vcs_info.json0000644000000001120000000000000130700ustar00{ "git": { "sha1": "444a13d12f99b8634f5bf5de0d17330ea34da689" } }