From b826e7be30ed3ae8bbb374ae0b5b395976318303 Mon Sep 17 00:00:00 2001 From: ridwanabdillahi <91507758+ridwanabdillahi@users.noreply.github.com> Date: Thu, 1 Sep 2022 11:49:37 -0700 Subject: [PATCH 1/2] Add Natvis definitions and tests for ArrayVec and ArrayString types. --- .github/workflows/ci.yml | 11 ++++- Cargo.toml | 17 +++++++ debug_metadata/arrayvec.natvis | 35 +++++++++++++++ src/lib.rs | 5 +++ tests/debugger_visualizer.rs | 82 ++++++++++++++++++++++++++++++++++ 5 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 debug_metadata/arrayvec.natvis create mode 100644 tests/debugger_visualizer.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 65b6c53..2ec7983 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,10 +12,10 @@ env: jobs: tests: - runs-on: ubuntu-latest continue-on-error: ${{ matrix.experimental }} strategy: matrix: + os: [ubuntu-latest] include: - rust: 1.51.0 # MSRV features: serde @@ -29,6 +29,12 @@ jobs: - rust: nightly features: serde experimental: false + - rust: nightly + features: debugger_visualizer + os: windows-latest + experimental: false + + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v2 @@ -43,6 +49,9 @@ jobs: cargo doc --verbose --features "${{ matrix.features }}" --no-deps cargo test --verbose --features "${{ matrix.features }}" cargo test --release --verbose --features "${{ matrix.features }}" + - name: Test debugger visualizer + if: matrix.features == 'debugger_visualizer' + run: cargo test --release --verbose --features "${{ matrix.features }}" --test debugger_visualizer -- --test-threads=1 - name: Test run benchmarks if: matrix.bench != '' run: cargo test -v --benches diff --git a/Cargo.toml b/Cargo.toml index ea04113..c15fbdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,8 @@ version = "1.0" [dev-dependencies] matches = { version = "0.1" } bencher = "0.1.4" +debugger_test = "0.1" +debugger_test_parser = "0.1" [[bench]] name = "extend" @@ -34,10 +36,25 @@ harness = false name = "arraystring" harness = false +[[test]] +name = "debugger_visualizer" +path = "tests/debugger_visualizer.rs" +required-features = ["debugger_visualizer"] +# Do not run these tests by default. These tests need to +# be run with the additional rustc flag `--test-threads=1` +# since each test causes a debugger to attach to the current +# test process. If multiple debuggers try to attach at the same +# time, the test will fail. +test = false + [features] default = ["std"] std = [] +# UNSTABLE FEATURES (requires Rust nightly) +# Enable to use the #[debugger_visualizer] attribute. +debugger_visualizer = [] + [profile.bench] debug = true [profile.release] diff --git a/debug_metadata/arrayvec.natvis b/debug_metadata/arrayvec.natvis new file mode 100644 index 0000000..97d35cd --- /dev/null +++ b/debug_metadata/arrayvec.natvis @@ -0,0 +1,35 @@ + + + + {xs,[len]s8} + {xs,[len]s8} + + len + $T1 + + + len + + (char)xs[i].value.value + i++ + + + + + + + {{ len={len} }} + + len + $T2 + + + len + + ($T1)xs[i].value.value + i++ + + + + + diff --git a/src/lib.rs b/src/lib.rs index 5dc0273..4ad2d36 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,11 @@ //! #![doc(html_root_url="https://docs.rs/arrayvec/0.7/")] #![cfg_attr(not(feature="std"), no_std)] +#![cfg_attr( + feature="debugger_visualizer", + feature(debugger_visualizer), + debugger_visualizer(natvis_file = "../debug_metadata/arrayvec.natvis") +)] #[cfg(feature="serde")] extern crate serde; diff --git a/tests/debugger_visualizer.rs b/tests/debugger_visualizer.rs new file mode 100644 index 0000000..401a86a --- /dev/null +++ b/tests/debugger_visualizer.rs @@ -0,0 +1,82 @@ +use arrayvec::ArrayString; +use arrayvec::ArrayVec; +use debugger_test::debugger_test; + +#[inline(never)] +fn __break() { + println!("Breakpoint hit"); +} + +#[debugger_test( + debugger = "cdb", + commands = r#" +.nvlist +dv + +dx array +dx string + +g + +dx string +"#, + expected_statements = r#" +array : { len=0xa } [Type: arrayvec::arrayvec::ArrayVec] + [] [Type: arrayvec::arrayvec::ArrayVec] + [len] : 0xa [Type: unsigned int] + [capacity] : 10 + [0x0] : 1 [Type: i32] + [0x1] : 2 [Type: i32] + [0x2] : 3 [Type: i32] + [0x3] : 4 [Type: i32] + [0x4] : 5 [Type: i32] + [0x5] : 6 [Type: i32] + [0x6] : 7 [Type: i32] + [0x7] : 8 [Type: i32] + [0x8] : 9 [Type: i32] + [0x9] : 10 [Type: i32] + +string : "foo" [Type: arrayvec::array_string::ArrayString<10>] + [] [Type: arrayvec::array_string::ArrayString<10>] + [len] : 0x3 [Type: unsigned int] + [capacity] : 10 + [0x0] : 102 'f' [Type: char] + [0x1] : 111 'o' [Type: char] + [0x2] : 111 'o' [Type: char] + +string : "foo-bar" [Type: arrayvec::array_string::ArrayString<10>] + [] [Type: arrayvec::array_string::ArrayString<10>] + [len] : 0x7 [Type: unsigned int] + [capacity] : 10 + [0x0] : 102 'f' [Type: char] + [0x1] : 111 'o' [Type: char] + [0x2] : 111 'o' [Type: char] + [0x3] : 45 '-' [Type: char] + [0x4] : 98 'b' [Type: char] + [0x5] : 97 'a' [Type: char] + [0x6] : 114 'r' [Type: char] +"# +)] +#[inline(never)] +fn test_debugger_visualizer() { + let mut array = ArrayVec::::new(); + for i in 0..10 { + array.push(i + 1); + } + assert_eq!(&array[..], &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + assert_eq!(array.capacity(), 10); + + let mut string = ArrayString::<10>::new(); + string.push_str("foo"); + assert_eq!(&string[..], "foo"); + assert_eq!(string.capacity(), 10); + __break(); + + string.push_str("-bar"); + assert_eq!(&string[..], "foo-bar"); + assert_eq!(string.capacity(), 10); + + let result = string.to_string(); + assert_eq!(String::from("foo-bar"), result); + __break(); +} From 12abdf16ffd5a13e7cefb079376ddcc4883a90d4 Mon Sep 17 00:00:00 2001 From: ridwanabdillahi <91507758+ridwanabdillahi@users.noreply.github.com> Date: Wed, 21 Sep 2022 14:01:35 -0700 Subject: [PATCH 2/2] Add documentation for running debugger tests. --- debug_metadata/README.md | 111 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 debug_metadata/README.md diff --git a/debug_metadata/README.md b/debug_metadata/README.md new file mode 100644 index 0000000..cbd4769 --- /dev/null +++ b/debug_metadata/README.md @@ -0,0 +1,111 @@ +## Debugger Visualizers + +Many languages and debuggers enable developers to control how a type is +displayed in a debugger. These are called "debugger visualizations" or "debugger +views". + +The Windows debuggers (WinDbg\CDB) support defining custom debugger visualizations using +the `Natvis` framework. To use Natvis, developers write XML documents using the natvis +schema that describe how debugger types should be displayed with the `.natvis` extension. +(See: https://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2019) +The Natvis files provide patterns which match type names a description of how to display +those types. + +The Natvis schema can be found either online (See: https://code.visualstudio.com/docs/cpp/natvis#_schema) +or locally at `\Xml\Schemas\1033\natvis.xsd`. + +The GNU debugger (GDB) supports defining custom debugger views using Pretty Printers. +Pretty printers are written as python scripts that describe how a type should be displayed +when loaded up in GDB/LLDB. (See: https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html#Pretty-Printing) +The pretty printers provide patterns, which match type names, and for matching +types, descibe how to display those types. (For writing a pretty printer, see: https://sourceware.org/gdb/onlinedocs/gdb/Writing-a-Pretty_002dPrinter.html#Writing-a-Pretty_002dPrinter). + +### Embedding Visualizers + +Through the use of the currently unstable `#[debugger_visualizer]` attribute, the `arrayvec` +crate can embed debugger visualizers into the crate metadata. + +Currently the two types of visualizers supported are Natvis and Pretty printers. + +For Natvis files, when linking an executable with a crate that includes Natvis files, +the MSVC linker will embed the contents of all Natvis files into the generated `PDB`. + +For pretty printers, the compiler will encode the contents of the pretty printer +in the `.debug_gdb_scripts` section of the `ELF` generated. + +### Testing Visualizers + +The `arrayvec` crate supports testing debugger visualizers defined for this crate. The entry point for +these tests are `tests/debugger_visualizer.rs`. These tests are defined using the `debugger_test` and +`debugger_test_parser` crates. The `debugger_test` crate is a proc macro crate which defines a +single proc macro attribute, `#[debugger_test]`. For more detailed information about this crate, +see https://crates.io/crates/debugger_test. The CI pipeline for the `arrayvec` crate has been updated +to run the debugger visualizer tests to ensure debugger visualizers do not become broken/stale. + +The `#[debugger_test]` proc macro attribute may only be used on test functions and will run the +function under the debugger specified by the `debugger` meta item. + +This proc macro attribute has 3 required values: + +1. The first required meta item, `debugger`, takes a string value which specifies the debugger to launch. +2. The second required meta item, `commands`, takes a string of new line (`\n`) separated list of debugger +commands to run. +3. The third required meta item, `expected_statements`, takes a string of new line (`\n`) separated list of +statements that must exist in the debugger output. Pattern matching through regular expressions is also +supported by using the `pattern:` prefix for each expected statement. + +#### Example: + +```rust +#[debugger_test( + debugger = "cdb", + commands = "command1\ncommand2\ncommand3", + expected_statements = "statement1\nstatement2\nstatement3")] +fn test() { + +} +``` + +Using a multiline string is also supported, with a single debugger command/expected statement per line: + +```rust +#[debugger_test( + debugger = "cdb", + commands = " +command1 +command2 +command3", + expected_statements = " +statement1 +pattern:statement[0-9]+ +statement3")] +fn test() { + +} +``` + +In the example above, the second expected statement uses pattern matching through a regular expression +by using the `pattern:` prefix. + +#### Testing Locally + +Currently, only Natvis visualizations have been defined for the `arrayvec` crate via `debug_metadata/arrayvec.natvis`, +which means the `tests/debugger_visualizer.rs` tests need to be run on Windows using the `*-pc-windows-msvc` targets. +To run these tests locally, first ensure the debugging tools for Windows are installed or install them following +the steps listed here, [Debugging Tools for Windows](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/). +Once the debugging tools have been installed, the tests can be run in the same manner as they are in the CI +pipeline. + +#### Note + +When running the debugger visualizer tests, `tests/debugger_visualizer.rs`, they need to be run consecutively +and not in parallel. This can be achieved by passing the flag `--test-threads=1` to rustc. This is due to +how the debugger tests are run. Each test marked with the `#[debugger_test]` attribute launches a debugger +and attaches it to the current test process. If tests are running in parallel, the test will try to attach +a debugger to the current process which may already have a debugger attached causing the test to fail. + +For example: + +``` +cargo test --test debugger_visualizer --features debugger_visualizer -- --test-threads=1 +```