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
+```