Skip to content

Commit 09b4988

Browse files
Ridwan Abdilahimbrubeck
Ridwan Abdilahi
authored andcommitted
Add Natvis definitions and tests for SmallVec type with and without the union crate feature enabled.
Unify Natvis definitions into a single Natvis file.
1 parent 7fa951f commit 09b4988

File tree

7 files changed

+257
-5
lines changed

7 files changed

+257
-5
lines changed

.github/workflows/main.yml

+18-5
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ on:
77
workflow_dispatch:
88

99
jobs:
10-
linux-ci:
11-
name: Linux
12-
runs-on: ubuntu-latest
10+
ci:
11+
name: Build/Test
1312
strategy:
1413
matrix:
1514
toolchain: ["stable", "beta", "nightly", "1.36.0"]
@@ -20,10 +19,16 @@ jobs:
2019
- toolchain: beta
2120
env:
2221
DO_FUZZ: 1
22+
- os: windows-latest
23+
toolchain: nightly
24+
25+
runs-on: ${{ matrix.os }}
26+
2327
steps:
2428
- uses: actions/checkout@v2
2529

2630
- name: Install packages
31+
if: matrix.os == 'ubuntu-latest'
2732
run: sudo apt-get install -y binutils-dev libunwind8-dev libcurl4-openssl-dev libelf-dev libdw-dev cmake gcc libiberty-dev
2833

2934
- name: Install toolchain
@@ -50,6 +55,14 @@ jobs:
5055
if: matrix.toolchain == 'beta'
5156
run: cargo test --verbose --features union
5257

58+
- name: Cargo test w/ debugger_visualizer
59+
if: matrix.toolchain == 'nightly'
60+
run: cargo test --test debugger_visualizer --verbose --features debugger_visualizer -- --test-threads=1
61+
62+
- name: Cargo test w/ debugger_visualizer and union
63+
if: matrix.toolchain == 'nightly'
64+
run: cargo test --test debugger_visualizer --verbose --features 'debugger_visualizer,union' -- --test-threads=1
65+
5366
- name: Cargo test all features
5467
if: matrix.toolchain == 'nightly'
5568
run: cargo test --verbose --all-features
@@ -59,7 +72,7 @@ jobs:
5972
run: cargo bench --verbose bench
6073

6174
- name: miri
62-
if: matrix.toolchain == 'nightly'
75+
if: matrix.toolchain == 'nightly' && matrix.os == 'ubuntu-latest'
6376
run: bash ./scripts/run_miri.sh
6477
env:
6578
MIRIFLAGS: '-Zmiri-tag-raw-pointers'
@@ -73,7 +86,7 @@ jobs:
7386
name: homu build finished
7487
runs-on: ubuntu-latest
7588
needs:
76-
- "linux-ci"
89+
- "ci"
7790

7891
steps:
7992
- name: Mark the job as successful

Cargo.toml

+17
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,30 @@ union = []
1919
specialization = []
2020
may_dangle = []
2121

22+
# UNSTABLE FEATURES (requires Rust nightly)
23+
# Enable to use the #[debugger_visualizer] attribute.
24+
debugger_visualizer = []
25+
2226
[dependencies]
2327
serde = { version = "1", optional = true, default-features = false }
2428
arbitrary = { version = "1", optional = true }
2529

2630
[dev_dependencies]
2731
bincode = "1.0.1"
32+
debugger_test = "0.1.0"
33+
debugger_test_parser = "0.1.0"
2834

2935
[package.metadata.docs.rs]
3036
all-features = true
3137
rustdoc-args = ["--cfg", "docsrs"]
38+
39+
[[test]]
40+
name = "debugger_visualizer"
41+
path = "tests/debugger_visualizer.rs"
42+
required-features = ["debugger_visualizer"]
43+
# Do not run these tests by default. These tests need to
44+
# be run with the additional rustc flag `--test-threads=1`
45+
# since each test causes a debugger to attach to the current
46+
# test process. If multiple debuggers try to attach at the same
47+
# time, the test will fail.
48+
test = false

debug_metadata/README.md

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
## Debugger Visualizers
2+
3+
Many languages and debuggers enable developers to control how a type is
4+
displayed in a debugger. These are called "debugger visualizations" or "debugger
5+
views".
6+
7+
The Windows debuggers (WinDbg\CDB) support defining custom debugger visualizations using
8+
the `Natvis` framework. To use Natvis, developers write XML documents using the natvis
9+
schema that describe how debugger types should be displayed with the `.natvis` extension.
10+
(See: https://docs.microsoft.com/en-us/visualstudio/debugger/create-custom-views-of-native-objects?view=vs-2019)
11+
The Natvis files provide patterns which match type names a description of how to display
12+
those types.
13+
14+
The Natvis schema can be found either online (See: https://code.visualstudio.com/docs/cpp/natvis#_schema)
15+
or locally at `<VS Installation Folder>\Xml\Schemas\1033\natvis.xsd`.
16+
17+
The GNU debugger (GDB) supports defining custom debugger views using Pretty Printers.
18+
Pretty printers are written as python scripts that describe how a type should be displayed
19+
when loaded up in GDB/LLDB. (See: https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing.html#Pretty-Printing)
20+
The pretty printers provide patterns, which match type names, and for matching
21+
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).
22+
23+
### Embedding Visualizers
24+
25+
Through the use of the currently unstable `#[debugger_visualizer]` attribute, the `smallvec`
26+
crate can embed debugger visualizers into the crate metadata.
27+
28+
Currently the two types of visualizers supported are Natvis and Pretty printers.
29+
30+
For Natvis files, when linking an executable with a crate that includes Natvis files,
31+
the MSVC linker will embed the contents of all Natvis files into the generated `PDB`.
32+
33+
For pretty printers, the compiler will encode the contents of the pretty printer
34+
in the `.debug_gdb_scripts` section of the `ELF` generated.
35+
36+
### Testing Visualizers
37+
38+
The `smallvec` crate supports testing debugger visualizers defined for this crate. The entry point for
39+
these tests are `tests/debugger_visualizer.rs`. These tests are defined using the `debugger_test` and
40+
`debugger_test_parser` crates. The `debugger_test` crate is a proc macro crate which defines a
41+
single proc macro attribute, `#[debugger_test]`. For more detailed information about this crate,
42+
see https://crates.io/crates/debugger_test. The CI pipeline for the `smallvec` crate has been updated
43+
to run the debugger visualizer tests to ensure debugger visualizers do not become broken/stale.
44+
45+
The `#[debugger_test]` proc macro attribute may only be used on test functions and will run the
46+
function under the debugger specified by the `debugger` meta item.
47+
48+
This proc macro attribute has 3 required values:
49+
50+
1. The first required meta item, `debugger`, takes a string value which specifies the debugger to launch.
51+
2. The second required meta item, `commands`, takes a string of new line (`\n`) separated list of debugger
52+
commands to run.
53+
3. The third required meta item, `expected_statements`, takes a string of new line (`\n`) separated list of
54+
statements that must exist in the debugger output. Pattern matching through regular expressions is also
55+
supported by using the `pattern:` prefix for each expected statement.
56+
57+
#### Example:
58+
59+
```rust
60+
#[debugger_test(
61+
debugger = "cdb",
62+
commands = "command1\ncommand2\ncommand3",
63+
expected_statements = "statement1\nstatement2\nstatement3")]
64+
fn test() {
65+
66+
}
67+
```
68+
69+
Using a multiline string is also supported, with a single debugger command/expected statement per line:
70+
71+
```rust
72+
#[debugger_test(
73+
debugger = "cdb",
74+
commands = "
75+
command1
76+
command2
77+
command3",
78+
expected_statements = "
79+
statement1
80+
pattern:statement[0-9]+
81+
statement3")]
82+
fn test() {
83+
84+
}
85+
```
86+
87+
In the example above, the second expected statement uses pattern matching through a regular expression
88+
by using the `pattern:` prefix.
89+
90+
#### Testing Locally
91+
92+
Currently, only Natvis visualizations have been defined for the `smallvec` crate via `debug_metadata/smallvec.natvis`,
93+
which means the `tests/debugger_visualizer.rs` tests need to be run on Windows using the `*-pc-windows-msvc` targets.
94+
To run these tests locally, first ensure the debugging tools for Windows are installed or install them following
95+
the steps listed here, [Debugging Tools for Windows](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/).
96+
Once the debugging tools have been installed, the tests can be run in the same manner as they are in the CI
97+
pipeline.
98+
99+
#### Note
100+
101+
When running the debugger visualizer tests, `tests/debugger_visualizer.rs`, they need to be run consecutively
102+
and not in parallel. This can be achieved by passing the flag `--test-threads=1` to rustc. This is due to
103+
how the debugger tests are run. Each test marked with the `#[debugger_test]` attribute launches a debugger
104+
and attaches it to the current test process. If tests are running in parallel, the test will try to attach
105+
a debugger to the current process which may already have a debugger attached causing the test to fail.
106+
107+
For example:
108+
109+
```
110+
cargo test --test debugger_visualizer --features debugger_visualizer -- --test-threads=1
111+
```

debug_metadata/smallvec.natvis

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
2+
<Type Name="smallvec::SmallVec&lt;array$&lt;*,*&gt;&gt;" Priority="Medium">
3+
<Intrinsic Name="is_inline" Expression="$T2 &gt; capacity" />
4+
<Intrinsic Name="len" Expression="is_inline() ? capacity : data.variant1.value.__0.__1" />
5+
<Intrinsic Name="data_ptr" Expression="is_inline() ? data.variant0.value.__0.value.value : data.variant1.value.__0.__0" />
6+
7+
<DisplayString>{{ len={len()} }}</DisplayString>
8+
<Expand>
9+
<Item Name="[capacity]">is_inline() ? $T2 : capacity</Item>
10+
<Item Name="[len]">len()</Item>
11+
12+
<ArrayItems>
13+
<Size>len()</Size>
14+
<ValuePointer>data_ptr()</ValuePointer>
15+
</ArrayItems>
16+
</Expand>
17+
</Type>
18+
19+
<Type Name="smallvec::SmallVec&lt;array$&lt;*,*&gt;&gt;" Priority="MediumLow">
20+
<Intrinsic Name="is_inline" Expression="$T2 &gt; capacity" />
21+
<Intrinsic Name="len" Expression="is_inline() ? capacity : data.heap.__1" />
22+
<Intrinsic Name="data_ptr" Expression="is_inline() ? data.inline.value.value.value : data.heap.__0" />
23+
24+
<DisplayString>{{ len={len()} }}</DisplayString>
25+
<Expand>
26+
<Item Name="[capacity]">is_inline() ? $T2 : capacity</Item>
27+
<Item Name="[len]">len()</Item>
28+
29+
<ArrayItems>
30+
<Size>len()</Size>
31+
<ValuePointer>data_ptr()</ValuePointer>
32+
</ArrayItems>
33+
</Expand>
34+
</Type>
35+
</AutoVisualizer>

scripts/run_miri.sh

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ cargo clean
1111

1212
MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri)
1313
echo "Installing latest nightly with Miri: $MIRI_NIGHTLY"
14+
rustup override unset
1415
rustup default "$MIRI_NIGHTLY"
1516

1617
rustup component add miri
@@ -19,3 +20,5 @@ cargo miri setup
1920
cargo miri test --verbose
2021
cargo miri test --verbose --features union
2122
cargo miri test --verbose --all-features
23+
24+
rustup override set nightly

src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@
8181
#![cfg_attr(feature = "specialization", allow(incomplete_features))]
8282
#![cfg_attr(feature = "specialization", feature(specialization))]
8383
#![cfg_attr(feature = "may_dangle", feature(dropck_eyepatch))]
84+
#![cfg_attr(
85+
feature = "debugger_visualizer",
86+
feature(debugger_visualizer),
87+
debugger_visualizer(natvis_file = "../debug_metadata/smallvec.natvis")
88+
)]
8489
#![deny(missing_docs)]
8590

8691
#[doc(hidden)]

tests/debugger_visualizer.rs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use debugger_test::debugger_test;
2+
use smallvec::{smallvec, SmallVec};
3+
4+
#[inline(never)]
5+
fn __break() {}
6+
7+
#[debugger_test(
8+
debugger = "cdb",
9+
commands = r#"
10+
.nvlist
11+
dx sv
12+
13+
g
14+
15+
dx sv
16+
17+
g
18+
19+
dx sv
20+
"#,
21+
expected_statements = r#"
22+
sv : { len=0x2 } [Type: smallvec::SmallVec<array$<i32,4> >]
23+
[<Raw View>] [Type: smallvec::SmallVec<array$<i32,4> >]
24+
[capacity] : 4
25+
[len] : 0x2 [Type: unsigned __int64]
26+
[0] : 1 [Type: int]
27+
[1] : 2 [Type: int]
28+
29+
sv : { len=0x5 } [Type: smallvec::SmallVec<array$<i32,4> >]
30+
[<Raw View>] [Type: smallvec::SmallVec<array$<i32,4> >]
31+
[capacity] : 0x8 [Type: unsigned __int64]
32+
[len] : 0x5 [Type: unsigned __int64]
33+
[0] : 5 [Type: int]
34+
[1] : 2 [Type: int]
35+
[2] : 3 [Type: int]
36+
[3] : 4 [Type: int]
37+
[4] : 5 [Type: int]
38+
39+
sv : { len=0x5 } [Type: smallvec::SmallVec<array$<i32,4> >]
40+
[<Raw View>] [Type: smallvec::SmallVec<array$<i32,4> >]
41+
[capacity] : 0x8 [Type: unsigned __int64]
42+
[len] : 0x5 [Type: unsigned __int64]
43+
[0] : 2 [Type: int]
44+
[1] : 3 [Type: int]
45+
[2] : 4 [Type: int]
46+
[3] : 5 [Type: int]
47+
[4] : 5 [Type: int]
48+
"#
49+
)]
50+
#[inline(never)]
51+
fn test_debugger_visualizer() {
52+
// This SmallVec can hold up to 4 items on the stack:
53+
let mut sv: SmallVec<[i32; 4]> = smallvec![1, 2];
54+
__break();
55+
56+
// Overfill the SmallVec to move its contents to the heap
57+
for i in 3..6 {
58+
sv.push(i);
59+
}
60+
61+
// Update the contents of the first value of the SmallVec.
62+
sv[0] = sv[1] + sv[2];
63+
__break();
64+
65+
// Sort the SmallVec in place.
66+
sv.sort();
67+
__break();
68+
}

0 commit comments

Comments
 (0)