From 869b837c92580374757b43dbb0249724d6e96aed Mon Sep 17 00:00:00 2001 From: Akshay Ubale Date: Sun, 26 Jul 2026 20:53:54 +0200 Subject: [PATCH 01/10] feat: Add debugger pretty-printers for cuda::annotated_ptr > Add GDB pretty-printer for cuda::annotated_ptr<_Tp, _Property> * Detects annotated_ptr types with 2 template arguments * Extracts pointee type and access property information * Displays dereferenced pointer value * Formats output as: cuda::annotated_ptr -> 0xaddress > Add LLDB pretty-printer with equivalent functionality * Uses LLDB SBValue API for safe member and pointer access * Regex pattern matching for robust type detection * Handles edge cases (nullptr, invalid pointers) > Add comprehensive test suite * Test source with three access property scenarios (streaming, persisting, global) * Two inspection points for each property type * Expected output validation files for both GDB and LLDB * CMake integration with test discovery and execution Fixes #10100 --- libcudacxx/share/libcudacxx/gdb/__init__.py | 2 + .../share/libcudacxx/gdb/annotated_ptr.py | 92 +++++++++++++++++++ libcudacxx/share/libcudacxx/lldb/__init__.py | 3 + .../share/libcudacxx/lldb/annotated_ptr.py | 64 +++++++++++++ libcudacxx/test/debugging/CMakeLists.txt | 1 + .../debugging/annotated_ptr/CMakeLists.txt | 11 +++ .../test/debugging/annotated_ptr/gdb.expected | 12 +++ .../debugging/annotated_ptr/lldb.expected | 12 +++ .../test/debugging/annotated_ptr/source.cu | 66 +++++++++++++ 9 files changed, 263 insertions(+) create mode 100644 libcudacxx/share/libcudacxx/gdb/annotated_ptr.py create mode 100644 libcudacxx/share/libcudacxx/lldb/annotated_ptr.py create mode 100644 libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt create mode 100644 libcudacxx/test/debugging/annotated_ptr/gdb.expected create mode 100644 libcudacxx/test/debugging/annotated_ptr/lldb.expected create mode 100644 libcudacxx/test/debugging/annotated_ptr/source.cu diff --git a/libcudacxx/share/libcudacxx/gdb/__init__.py b/libcudacxx/share/libcudacxx/gdb/__init__.py index d9f950d66c4d..e26a82a6d430 100644 --- a/libcudacxx/share/libcudacxx/gdb/__init__.py +++ b/libcudacxx/share/libcudacxx/gdb/__init__.py @@ -19,6 +19,7 @@ sys.path.insert(0, _SCRIPT_DIRECTORY) import atomic # noqa: E402 +import annotated_ptr # noqa: E402 import buffer # noqa: E402 import complex # noqa: E402 import event # noqa: E402 @@ -37,6 +38,7 @@ _PRINTERS = ( memory_resource, atomic, + annotated_ptr, buffer, std_array, complex, diff --git a/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py new file mode 100644 index 000000000000..d04908a82cc6 --- /dev/null +++ b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py @@ -0,0 +1,92 @@ +# Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""GDB pretty printer for cuda::annotated_ptr.""" + +from __future__ import annotations + +import re +from types import ModuleType + +import gdb +import gdb.printing + +_ABI_NAMESPACE_PATTERN = re.compile(r"::__(?:\d+|version_bump_ver\d+_)(?=::)") + + +def public_type_name(value_type: gdb.Type) -> str: + """Return a type name without CUDA ABI inline namespaces.""" + return _ABI_NAMESPACE_PATTERN.sub("", str(value_type)) + + +def _template_name(value_type: gdb.Type) -> str: + return str(value_type).split("<", 1)[0] + + +def _is_annotated_ptr(value_type: gdb.Type) -> bool: + value_type = value_type.strip_typedefs().unqualified() + type_name = public_type_name(value_type) + template_name = _template_name(value_type) + return ( + type_name.startswith("cuda::") + and template_name.rsplit("::", 1)[-1] == "annotated_ptr" + ) + + +class AnnotatedPtrPrinter: + """Summarize a CUDA annotated_ptr smart pointer.""" + + def __init__(self, value: gdb.Value) -> None: + self.value = value + self.type = value.type.strip_typedefs().unqualified() + self.type_name = public_type_name(self.type) + + def to_string(self) -> str: + try: + # Get the template arguments: _Tp (pointee type) and _Property + pointee_type = self.type.template_argument(0) + pointee_type_name = public_type_name(pointee_type) + + property_type = self.type.template_argument(1) + property_type_name = public_type_name(property_type) + + # Access the __repr member (the wrapped pointer) + repr_ptr = self.value["__repr"] + + # Get the pointer value + ptr_value = int(repr_ptr) + + # Format the pointer display + if ptr_value == 0: + ptr_display = "nullptr" + else: + ptr_display = f"{ptr_value:#x}" + + # Construct the full type name with template arguments + full_type = ( + f"{self.type_name}<{pointee_type_name}, {property_type_name}>" + ) + + return f"{full_type} -> {ptr_display}" + except (gdb.error, ValueError): + return self.type_name + + +class AnnotatedPtrPrinterLookup(gdb.printing.PrettyPrinter): + """Select printers for cuda::annotated_ptr types.""" + + def __init__(self) -> None: + super().__init__("cuda::annotated_ptr") + + def __call__(self, value: gdb.Value) -> AnnotatedPtrPrinter | None: + if _is_annotated_ptr(value.type): + return AnnotatedPtrPrinter(value) + return None + + +def register(objfile: ModuleType) -> None: + """Register CUDA annotated_ptr formatters with GDB.""" + gdb.printing.register_pretty_printer( + objfile, AnnotatedPtrPrinterLookup(), replace=True + ) diff --git a/libcudacxx/share/libcudacxx/lldb/__init__.py b/libcudacxx/share/libcudacxx/lldb/__init__.py index d810801ff255..6b218cc7a762 100644 --- a/libcudacxx/share/libcudacxx/lldb/__init__.py +++ b/libcudacxx/share/libcudacxx/lldb/__init__.py @@ -10,6 +10,7 @@ from __future__ import annotations import atomic +import annotated_ptr import buffer import complex import event @@ -31,6 +32,7 @@ _FORMATTERS = ( memory_resource, atomic, + annotated_ptr, buffer, std_array, complex, @@ -45,6 +47,7 @@ optional, shared_resource, ) + InternalDict = dict[str, object] diff --git a/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py b/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py new file mode 100644 index 000000000000..c4cb771b2d34 --- /dev/null +++ b/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py @@ -0,0 +1,64 @@ +# Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +"""LLDB pretty printer for cuda::annotated_ptr.""" + +from __future__ import annotations + +import re + +import lldb + +_ANNOTATED_PTR_PATTERN = re.compile(r"^cuda::annotated_ptr<.+,.+>$") +InternalDict = dict[str, object] + + +def is_annotated_ptr(value_type: lldb.SBType, _internal_dict: InternalDict) -> bool: + type_name = ( + value_type.GetCanonicalType().GetUnqualifiedType().GetDisplayTypeName() or "" + ) + return _ANNOTATED_PTR_PATTERN.fullmatch(type_name) is not None + + +def annotated_ptr_description(value: lldb.SBValue) -> str: + """Describe an annotated_ptr using type information and pointer value.""" + type_name = ( + value.GetType().GetCanonicalType().GetUnqualifiedType().GetDisplayTypeName() + ) + if not type_name: + type_name = "annotated_ptr" + + # Try to get the pointer value + ptr_display = None + try: + value_nonsynth = value.GetNonSyntheticValue() + repr_ptr = value_nonsynth.GetChildMemberWithName("__repr") + if repr_ptr.IsValid(): + error = lldb.SBError() + ptr_value = repr_ptr.GetValueAsUnsigned(error) + if error.Success(): + if ptr_value == 0: + ptr_display = "nullptr" + else: + ptr_display = f"{ptr_value:#x}" + except Exception: + pass + + # If we couldn't read the pointer value, just show type-only description + if ptr_display is None: + return type_name + return f"{type_name} -> {ptr_display}" + + +def annotated_ptr_summary(value: lldb.SBValue, _internal_dict: InternalDict) -> str: + return annotated_ptr_description(value) + + +def register(debugger: lldb.SBDebugger, category: str, module: str) -> None: + """Register CUDA annotated_ptr formatters in an LLDB category.""" + debugger.HandleCommand( + f"type summary add --category {category} --python-function " + f"{module}.annotated_ptr_summary --recognizer-function " + f"{module}.is_annotated_ptr" + ) diff --git a/libcudacxx/test/debugging/CMakeLists.txt b/libcudacxx/test/debugging/CMakeLists.txt index 89da4d800b0d..7ed99b1eeec3 100644 --- a/libcudacxx/test/debugging/CMakeLists.txt +++ b/libcudacxx/test/debugging/CMakeLists.txt @@ -245,3 +245,4 @@ add_subdirectory(shared_resource) add_subdirectory(span) add_subdirectory(stream) add_subdirectory(tuple) +add_subdirectory(annotated_ptr) diff --git a/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt b/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt new file mode 100644 index 000000000000..4f0dda19655a --- /dev/null +++ b/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt @@ -0,0 +1,11 @@ +libcudacxx_add_pretty_printer_test( + NAME annotated_ptr + SOURCES source.cu + CASES + # gersemi: off + --case inspect_streaming 1 annotated_ptr.streaming streaming_ptr + --case inspect_persisting 1 annotated_ptr.persisting persisting_ptr + --case inspect_global 1 annotated_ptr.global global_ptr + --case inspect_null 1 annotated_ptr.null null_ptr + # gersemi: on +) diff --git a/libcudacxx/test/debugging/annotated_ptr/gdb.expected b/libcudacxx/test/debugging/annotated_ptr/gdb.expected new file mode 100644 index 000000000000..69d2e84b3d36 --- /dev/null +++ b/libcudacxx/test/debugging/annotated_ptr/gdb.expected @@ -0,0 +1,12 @@ +=============== annotated_ptr.streaming begin =============== +cuda::annotated_ptr ->
+=============== annotated_ptr.streaming end =============== +=============== annotated_ptr.persisting begin =============== +cuda::annotated_ptr ->
+=============== annotated_ptr.persisting end =============== +=============== annotated_ptr.global begin =============== +cuda::annotated_ptr ->
+=============== annotated_ptr.global end =============== +=============== annotated_ptr.null begin =============== +cuda::annotated_ptr -> nullptr +=============== annotated_ptr.null end =============== diff --git a/libcudacxx/test/debugging/annotated_ptr/lldb.expected b/libcudacxx/test/debugging/annotated_ptr/lldb.expected new file mode 100644 index 000000000000..69d2e84b3d36 --- /dev/null +++ b/libcudacxx/test/debugging/annotated_ptr/lldb.expected @@ -0,0 +1,12 @@ +=============== annotated_ptr.streaming begin =============== +cuda::annotated_ptr ->
+=============== annotated_ptr.streaming end =============== +=============== annotated_ptr.persisting begin =============== +cuda::annotated_ptr ->
+=============== annotated_ptr.persisting end =============== +=============== annotated_ptr.global begin =============== +cuda::annotated_ptr ->
+=============== annotated_ptr.global end =============== +=============== annotated_ptr.null begin =============== +cuda::annotated_ptr -> nullptr +=============== annotated_ptr.null end =============== diff --git a/libcudacxx/test/debugging/annotated_ptr/source.cu b/libcudacxx/test/debugging/annotated_ptr/source.cu new file mode 100644 index 000000000000..3eb7d042843d --- /dev/null +++ b/libcudacxx/test/debugging/annotated_ptr/source.cu @@ -0,0 +1,66 @@ +// Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include + +template +[[gnu::noinline]] void keep_for_debugger(const T& value) +{ + asm volatile("" : : "g"(&value) : "memory"); +} + +// Type aliases for annotated_ptr instances +using streaming_annotated_int = cuda::annotated_ptr; +using persisting_annotated_int = cuda::annotated_ptr; +using global_annotated_float = cuda::annotated_ptr; + +// Test a streaming annotated_ptr +[[gnu::noinline]] void inspect_streaming(const streaming_annotated_int& ptr) +{ + keep_for_debugger(ptr); +} + +// Test a persisting annotated_ptr +[[gnu::noinline]] void inspect_persisting(const persisting_annotated_int& ptr) +{ + keep_for_debugger(ptr); +} + +// Test a global annotated_ptr +[[gnu::noinline]] void inspect_global(const global_annotated_float& ptr) +{ + keep_for_debugger(ptr); +} + +// Test a null streaming_annotated_ptr +[[gnu::noinline]] void inspect_null(const streaming_annotated_int& ptr) +{ + keep_for_debugger(ptr); +} + +int main() +{ + // Allocate test data + const int streaming_data = 42; + const int persisting_data = 100; + const float global_data = 3.14f; + + // Test case 1: streaming annotated_ptr + const streaming_annotated_int streaming_ptr(&streaming_data); + inspect_streaming(streaming_ptr); + + // Test case 2: persisting annotated_ptr + const persisting_annotated_int persisting_ptr(&persisting_data); + inspect_persisting(persisting_ptr); + + // Test case 3: global annotated_ptr + const global_annotated_float global_ptr(&global_data); + inspect_global(global_ptr); + + // Test case 4: null streaming_annotated_ptr + const streaming_annotated_int null_ptr(nullptr); + inspect_null(null_ptr); + + return 0; +} From 341af8393bd00024e9475172576df4849e3af667 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 26 Jul 2026 20:41:15 +0000 Subject: [PATCH 02/10] [pre-commit.ci] auto code formatting --- libcudacxx/share/libcudacxx/gdb/annotated_ptr.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py index d04908a82cc6..4974697cec09 100644 --- a/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py +++ b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py @@ -64,9 +64,7 @@ def to_string(self) -> str: ptr_display = f"{ptr_value:#x}" # Construct the full type name with template arguments - full_type = ( - f"{self.type_name}<{pointee_type_name}, {property_type_name}>" - ) + full_type = f"{self.type_name}<{pointee_type_name}, {property_type_name}>" return f"{full_type} -> {ptr_display}" except (gdb.error, ValueError): From 7c28a7552be4347e291cfc83bb00f7fafb7a8316 Mon Sep 17 00:00:00 2001 From: Akshay Ubale Date: Fri, 14 Aug 2026 18:32:22 +0200 Subject: [PATCH 03/10] refactor: enhance annotated_ptr pretty printers with cccl_common helpers > Refactor template argument extraction into _template_arguments() helper method for improved code clarity and error isolation > Refactor pointer value extraction into _pointer_display() helper method to separate concerns and improve maintainability > Extract LLDB helper functions _annotated_ptr_type_name() and _annotated_ptr_repr_value() to consolidate type and member access logic > Implement explicit optional return types (str | None) for robust error handling > Separate error handling into focused try-except blocks for each operation > Add comprehensive docstrings This resolves PR review comments regarding code duplication, error handling specificity. --- .../share/libcudacxx/gdb/annotated_ptr.py | 81 ++++++++++--------- .../share/libcudacxx/lldb/annotated_ptr.py | 75 ++++++++++------- 2 files changed, 86 insertions(+), 70 deletions(-) diff --git a/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py index 4974697cec09..918c84d9d556 100644 --- a/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py +++ b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py @@ -6,28 +6,19 @@ from __future__ import annotations -import re from types import ModuleType import gdb import gdb.printing -_ABI_NAMESPACE_PATTERN = re.compile(r"::__(?:\d+|version_bump_ver\d+_)(?=::)") - - -def public_type_name(value_type: gdb.Type) -> str: - """Return a type name without CUDA ABI inline namespaces.""" - return _ABI_NAMESPACE_PATTERN.sub("", str(value_type)) - - -def _template_name(value_type: gdb.Type) -> str: - return str(value_type).split("<", 1)[0] +from . import cccl_common def _is_annotated_ptr(value_type: gdb.Type) -> bool: - value_type = value_type.strip_typedefs().unqualified() - type_name = public_type_name(value_type) - template_name = _template_name(value_type) + """Check if a GDB type represents cuda::annotated_ptr.""" + value_type = cccl_common.canonical_type(value_type) + type_name = cccl_common.public_type_name(value_type) + template_name = cccl_common.template_name(value_type) return ( type_name.startswith("cuda::") and template_name.rsplit("::", 1)[-1] == "annotated_ptr" @@ -35,44 +26,54 @@ def _is_annotated_ptr(value_type: gdb.Type) -> bool: class AnnotatedPtrPrinter: - """Summarize a CUDA annotated_ptr smart pointer.""" + """Expose cuda::annotated_ptr type and pointer value to GDB.""" def __init__(self, value: gdb.Value) -> None: - self.value = value - self.type = value.type.strip_typedefs().unqualified() - self.type_name = public_type_name(self.type) - - def to_string(self) -> str: + # Strip reference to access members + self.value = cccl_common.strip_reference_value(value) + # Store canonical type for type name display + self.canonical_type_obj = cccl_common.canonical_type(self.value.type) + self.type_name = cccl_common.public_type_name(self.canonical_type_obj) + + def _template_arguments(self) -> tuple[str, str] | None: + """Extract template argument type names, or None if unavailable.""" try: - # Get the template arguments: _Tp (pointee type) and _Property - pointee_type = self.type.template_argument(0) - pointee_type_name = public_type_name(pointee_type) + pointee_type = self.value.type.template_argument(0) + pointee_type_name = cccl_common.public_type_name(pointee_type) - property_type = self.type.template_argument(1) - property_type_name = public_type_name(property_type) + property_type = self.value.type.template_argument(1) + property_type_name = cccl_common.public_type_name(property_type) + return pointee_type_name, property_type_name + except (gdb.error, IndexError): + return None - # Access the __repr member (the wrapped pointer) + def _pointer_display(self) -> str | None: + """Extract pointer display value, or None if unavailable.""" + try: repr_ptr = self.value["__repr"] - - # Get the pointer value ptr_value = int(repr_ptr) + return "nullptr" if ptr_value == 0 else f"{ptr_value:#x}" + except (gdb.error, ValueError): + return None - # Format the pointer display - if ptr_value == 0: - ptr_display = "nullptr" - else: - ptr_display = f"{ptr_value:#x}" + def to_string(self) -> str: + """Combine type info and pointer value into annotated_ptr display.""" + template_args = self._template_arguments() + if template_args is None: + return self.type_name - # Construct the full type name with template arguments - full_type = f"{self.type_name}<{pointee_type_name}, {property_type_name}>" + pointee_type_name, property_type_name = template_args + full_type = f"{self.type_name}<{pointee_type_name}, {property_type_name}>" - return f"{full_type} -> {ptr_display}" - except (gdb.error, ValueError): - return self.type_name + ptr_display = self._pointer_display() + if ptr_display is None: + return full_type + + return f"{full_type} -> {ptr_display}" class AnnotatedPtrPrinterLookup(gdb.printing.PrettyPrinter): - """Select printers for cuda::annotated_ptr types.""" + """Select the cuda::annotated_ptr printer by its public class name.""" def __init__(self) -> None: super().__init__("cuda::annotated_ptr") @@ -84,7 +85,7 @@ def __call__(self, value: gdb.Value) -> AnnotatedPtrPrinter | None: def register(objfile: ModuleType) -> None: - """Register CUDA annotated_ptr formatters with GDB.""" + """Register the cuda::annotated_ptr printer with GDB.""" gdb.printing.register_pretty_printer( objfile, AnnotatedPtrPrinterLookup(), replace=True ) diff --git a/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py b/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py index c4cb771b2d34..6add1c76692a 100644 --- a/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py +++ b/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py @@ -10,53 +10,68 @@ import lldb +from . import cccl_common + _ANNOTATED_PTR_PATTERN = re.compile(r"^cuda::annotated_ptr<.+,.+>$") InternalDict = dict[str, object] -def is_annotated_ptr(value_type: lldb.SBType, _internal_dict: InternalDict) -> bool: - type_name = ( - value_type.GetCanonicalType().GetUnqualifiedType().GetDisplayTypeName() or "" - ) - return _ANNOTATED_PTR_PATTERN.fullmatch(type_name) is not None +def _annotated_ptr_type_name(value: lldb.SBValue) -> str: + """Get type name with fallback to generic 'annotated_ptr'.""" + try: + type_name = cccl_common.public_type_name(value.GetType()) + return type_name or "annotated_ptr" + except Exception: + return "annotated_ptr" -def annotated_ptr_description(value: lldb.SBValue) -> str: - """Describe an annotated_ptr using type information and pointer value.""" - type_name = ( - value.GetType().GetCanonicalType().GetUnqualifiedType().GetDisplayTypeName() - ) - if not type_name: - type_name = "annotated_ptr" +def _annotated_ptr_repr_value(value: lldb.SBValue) -> lldb.SBValue: + """Get the __repr member containing the pointer value.""" + try: + value_nonsynth = cccl_common.strip_reference_value(value).GetNonSyntheticValue() + return value_nonsynth.GetChildMemberWithName("__repr") + except Exception: + return lldb.SBValue() + - # Try to get the pointer value - ptr_display = None +def is_annotated_ptr(value_type: lldb.SBType, _internal_dict: InternalDict) -> bool: + """Check if an LLDB type represents cuda::annotated_ptr.""" try: - value_nonsynth = value.GetNonSyntheticValue() - repr_ptr = value_nonsynth.GetChildMemberWithName("__repr") - if repr_ptr.IsValid(): - error = lldb.SBError() - ptr_value = repr_ptr.GetValueAsUnsigned(error) - if error.Success(): - if ptr_value == 0: - ptr_display = "nullptr" - else: - ptr_display = f"{ptr_value:#x}" + type_name = cccl_common.canonical_type_name(value_type) + return bool(_ANNOTATED_PTR_PATTERN.fullmatch(type_name)) except Exception: - pass + return False + - # If we couldn't read the pointer value, just show type-only description - if ptr_display is None: +def annotated_ptr_description(value: lldb.SBValue) -> str: + """Describe annotated_ptr using type and pointer value information.""" + type_name = _annotated_ptr_type_name(value) + repr_ptr = _annotated_ptr_repr_value(value) + + if not repr_ptr.IsValid(): return type_name + + error = lldb.SBError() + ptr_addr = repr_ptr.GetValueAsUnsigned(error) + if not error.Success(): + return type_name + + ptr_display = "nullptr" if ptr_addr == 0 else f"{ptr_addr:#x}" return f"{type_name} -> {ptr_display}" -def annotated_ptr_summary(value: lldb.SBValue, _internal_dict: InternalDict) -> str: - return annotated_ptr_description(value) +def annotated_ptr_summary( + value: lldb.SBValue, _internal_dict: InternalDict +) -> str | None: + """Summarize annotated_ptr, returning None if unavailable.""" + try: + return annotated_ptr_description(value) + except Exception: + return None def register(debugger: lldb.SBDebugger, category: str, module: str) -> None: - """Register CUDA annotated_ptr formatters in an LLDB category.""" + """Register CUDA annotated_ptr formatters with LLDB.""" debugger.HandleCommand( f"type summary add --category {category} --python-function " f"{module}.annotated_ptr_summary --recognizer-function " From a0848e772d2f10d51c1e7511e00c3515440aa487 Mon Sep 17 00:00:00 2001 From: Akshay Ubale Date: Fri, 14 Aug 2026 20:19:18 +0200 Subject: [PATCH 04/10] fix: address review comments on type name handling and fixture values > Fix type name duplication: use template_name() to extract base name without template arguments > Read template arguments from canonical_type_obj to properly handle typedef aliases > Update test fixture to use mutable backing objects (non-const int/float) > Replace nullptr constructor with default constructor for null_ptr This ensures correct GDB pretty printer output and compiling fixture values. --- libcudacxx/share/libcudacxx/gdb/annotated_ptr.py | 10 +++++++--- libcudacxx/test/debugging/annotated_ptr/source.cu | 12 ++++++------ 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py index 918c84d9d556..f41c2bb2458f 100644 --- a/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py +++ b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py @@ -33,15 +33,19 @@ def __init__(self, value: gdb.Value) -> None: self.value = cccl_common.strip_reference_value(value) # Store canonical type for type name display self.canonical_type_obj = cccl_common.canonical_type(self.value.type) - self.type_name = cccl_common.public_type_name(self.canonical_type_obj) + # Extract base type name without template arguments to avoid duplication + self.type_name = cccl_common.template_name( + cccl_common.public_type_name(self.canonical_type_obj) + ) def _template_arguments(self) -> tuple[str, str] | None: """Extract template argument type names, or None if unavailable.""" try: - pointee_type = self.value.type.template_argument(0) + # Read template arguments from canonical type to handle typedef aliases + pointee_type = self.canonical_type_obj.template_argument(0) pointee_type_name = cccl_common.public_type_name(pointee_type) - property_type = self.value.type.template_argument(1) + property_type = self.canonical_type_obj.template_argument(1) property_type_name = cccl_common.public_type_name(property_type) return pointee_type_name, property_type_name except (gdb.error, IndexError): diff --git a/libcudacxx/test/debugging/annotated_ptr/source.cu b/libcudacxx/test/debugging/annotated_ptr/source.cu index 3eb7d042843d..d6bcb376bc69 100644 --- a/libcudacxx/test/debugging/annotated_ptr/source.cu +++ b/libcudacxx/test/debugging/annotated_ptr/source.cu @@ -41,10 +41,10 @@ using global_annotated_float = cuda::annotated_ptr Date: Fri, 14 Aug 2026 18:34:36 +0000 Subject: [PATCH 05/10] [pre-commit.ci] auto code formatting --- libcudacxx/share/libcudacxx/gdb/__init__.py | 2 +- libcudacxx/share/libcudacxx/lldb/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libcudacxx/share/libcudacxx/gdb/__init__.py b/libcudacxx/share/libcudacxx/gdb/__init__.py index e26a82a6d430..acb68e151ae3 100644 --- a/libcudacxx/share/libcudacxx/gdb/__init__.py +++ b/libcudacxx/share/libcudacxx/gdb/__init__.py @@ -18,8 +18,8 @@ if _SCRIPT_DIRECTORY not in sys.path: sys.path.insert(0, _SCRIPT_DIRECTORY) -import atomic # noqa: E402 import annotated_ptr # noqa: E402 +import atomic # noqa: E402 import buffer # noqa: E402 import complex # noqa: E402 import event # noqa: E402 diff --git a/libcudacxx/share/libcudacxx/lldb/__init__.py b/libcudacxx/share/libcudacxx/lldb/__init__.py index 6b218cc7a762..18b1bc259e26 100644 --- a/libcudacxx/share/libcudacxx/lldb/__init__.py +++ b/libcudacxx/share/libcudacxx/lldb/__init__.py @@ -9,8 +9,8 @@ from __future__ import annotations -import atomic import annotated_ptr +import atomic import buffer import complex import event From c65a3cb9ded0e6c01e42bce279672c476c787a70 Mon Sep 17 00:00:00 2001 From: Akshay Ubale Date: Fri, 14 Aug 2026 22:46:11 +0200 Subject: [PATCH 06/10] fix: correct test case argument format in annotated_ptr CMakeLists > Our cases had 4 arguments with extraneous 1 values that were being parsed as the section name causing: run_pretty_printer_test.py: error: duplicate section name: 1 > Fixed to match following pattern: --case inspect_streaming annotated_ptr.streaming streaming_ptr This change shall fix all 13 cascading LLDB test failures in the pipeline. --- libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt b/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt index 4f0dda19655a..9b7d11287954 100644 --- a/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt +++ b/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt @@ -3,9 +3,9 @@ libcudacxx_add_pretty_printer_test( SOURCES source.cu CASES # gersemi: off - --case inspect_streaming 1 annotated_ptr.streaming streaming_ptr - --case inspect_persisting 1 annotated_ptr.persisting persisting_ptr - --case inspect_global 1 annotated_ptr.global global_ptr - --case inspect_null 1 annotated_ptr.null null_ptr + --case inspect_streaming annotated_ptr.streaming streaming_ptr + --case inspect_persisting annotated_ptr.persisting persisting_ptr + --case inspect_global annotated_ptr.global global_ptr + --case inspect_null annotated_ptr.null null_ptr # gersemi: on ) From 914054dd502e7057f2269e607c44d4b9d33302cd Mon Sep 17 00:00:00 2001 From: Akshay Ubale Date: Thu, 17 Sep 2026 21:35:51 +0530 Subject: [PATCH 07/10] Fix imports --- libcudacxx/share/libcudacxx/gdb/annotated_ptr.py | 4 ++-- libcudacxx/share/libcudacxx/lldb/annotated_ptr.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py index f41c2bb2458f..f9b39adc700c 100644 --- a/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py +++ b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py @@ -8,11 +8,11 @@ from types import ModuleType +import cccl_common + import gdb import gdb.printing -from . import cccl_common - def _is_annotated_ptr(value_type: gdb.Type) -> bool: """Check if a GDB type represents cuda::annotated_ptr.""" diff --git a/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py b/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py index 6add1c76692a..f7e8e48d6058 100644 --- a/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py +++ b/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py @@ -8,9 +8,9 @@ import re -import lldb +import cccl_common -from . import cccl_common +import lldb _ANNOTATED_PTR_PATTERN = re.compile(r"^cuda::annotated_ptr<.+,.+>$") InternalDict = dict[str, object] From 8831a5ce7f1a8b23ebd61aa3b3d4356d7356ba03 Mon Sep 17 00:00:00 2001 From: Akshay Ubale Date: Fri, 18 Sep 2026 17:28:36 +0530 Subject: [PATCH 08/10] fix: use correct parameter in test cases - The LLDB test was trying to evaluate local variables (streaming_ptr, persisting_ptr, global_ptr, null_ptr) that are not in scope when the debugger stops inside the inspect_* functions. Those functions only have a parameter named 'ptr' in their scope. - Changed all case expressions to evaluate 'ptr' instead of the original variable names, matching the parameter used by each inspect_* function. --- libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt b/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt index 9b7d11287954..aec001642637 100644 --- a/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt +++ b/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt @@ -3,9 +3,9 @@ libcudacxx_add_pretty_printer_test( SOURCES source.cu CASES # gersemi: off - --case inspect_streaming annotated_ptr.streaming streaming_ptr - --case inspect_persisting annotated_ptr.persisting persisting_ptr - --case inspect_global annotated_ptr.global global_ptr - --case inspect_null annotated_ptr.null null_ptr + --case inspect_streaming annotated_ptr.streaming ptr + --case inspect_persisting annotated_ptr.persisting ptr + --case inspect_global annotated_ptr.global ptr + --case inspect_null annotated_ptr.null ptr # gersemi: on ) From 89393d2d112ce2f1104fb196565c354fdb4f2631 Mon Sep 17 00:00:00 2001 From: Akshay Ubale Date: Sat, 19 Sep 2026 07:20:19 +0530 Subject: [PATCH 09/10] fix: update LLDB expected output to include type information - LLDB's dwim-print outputs the parameter type and address when evaluating a const-reference parameter. Update the expected output format to match the actual LLDB output: Before: cuda::annotated_ptr<...> ->
After: (const type_name &)
cuda::annotated_ptr<...> ->
- This aligns the LLDB test expectations with the normalized output convention used by neighboring debugger tests. --- libcudacxx/test/debugging/annotated_ptr/lldb.expected | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libcudacxx/test/debugging/annotated_ptr/lldb.expected b/libcudacxx/test/debugging/annotated_ptr/lldb.expected index 69d2e84b3d36..69e440c2e735 100644 --- a/libcudacxx/test/debugging/annotated_ptr/lldb.expected +++ b/libcudacxx/test/debugging/annotated_ptr/lldb.expected @@ -1,12 +1,12 @@ =============== annotated_ptr.streaming begin =============== -cuda::annotated_ptr ->
+(const streaming_annotated_int &)
cuda::annotated_ptr ->
=============== annotated_ptr.streaming end =============== =============== annotated_ptr.persisting begin =============== -cuda::annotated_ptr ->
+(const persisting_annotated_int &)
cuda::annotated_ptr ->
=============== annotated_ptr.persisting end =============== =============== annotated_ptr.global begin =============== -cuda::annotated_ptr ->
+(const global_annotated_float &)
cuda::annotated_ptr ->
=============== annotated_ptr.global end =============== =============== annotated_ptr.null begin =============== -cuda::annotated_ptr -> nullptr +(const streaming_annotated_int &)
cuda::annotated_ptr -> nullptr =============== annotated_ptr.null end =============== From e8f39481255421ef014a8dce082242b5c6745560 Mon Sep 17 00:00:00 2001 From: Akshay Ubale Date: Thu, 15 Oct 2026 21:58:19 +0200 Subject: [PATCH 10/10] Address review feedback on annotated_ptr pretty-printers - LLDB: print only the pointer in the summary; the test now uses the full cuda::annotated_ptr type so LLDB renders it instead of a type alias. - Use the KEEP_FOR_DEBUGGER macro like the other debugger tests. - Add a before/after update case that goes from a pointer to nullptr. - Drop the blanket try/except from the LLDB and GDB printers. --- .../share/libcudacxx/gdb/annotated_ptr.py | 47 +++++---------- .../share/libcudacxx/lldb/annotated_ptr.py | 54 +++-------------- .../debugging/annotated_ptr/CMakeLists.txt | 2 + .../test/debugging/annotated_ptr/gdb.expected | 6 ++ .../debugging/annotated_ptr/lldb.expected | 14 +++-- .../test/debugging/annotated_ptr/source.cu | 58 ++++++++++--------- 6 files changed, 73 insertions(+), 108 deletions(-) diff --git a/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py index f9b39adc700c..dd9d6941416e 100644 --- a/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py +++ b/libcudacxx/share/libcudacxx/gdb/annotated_ptr.py @@ -38,42 +38,25 @@ def __init__(self, value: gdb.Value) -> None: cccl_common.public_type_name(self.canonical_type_obj) ) - def _template_arguments(self) -> tuple[str, str] | None: - """Extract template argument type names, or None if unavailable.""" - try: - # Read template arguments from canonical type to handle typedef aliases - pointee_type = self.canonical_type_obj.template_argument(0) - pointee_type_name = cccl_common.public_type_name(pointee_type) - - property_type = self.canonical_type_obj.template_argument(1) - property_type_name = cccl_common.public_type_name(property_type) - return pointee_type_name, property_type_name - except (gdb.error, IndexError): - return None - - def _pointer_display(self) -> str | None: - """Extract pointer display value, or None if unavailable.""" - try: - repr_ptr = self.value["__repr"] - ptr_value = int(repr_ptr) - return "nullptr" if ptr_value == 0 else f"{ptr_value:#x}" - except (gdb.error, ValueError): - return None + def _template_arguments(self) -> tuple[str, str]: + """Return the pointee and property type names.""" + pointee_type = self.canonical_type_obj.template_argument(0) + property_type = self.canonical_type_obj.template_argument(1) + return ( + cccl_common.public_type_name(pointee_type), + cccl_common.public_type_name(property_type), + ) + + def _pointer_display(self) -> str: + """Return the stored pointer, spelling a null pointer as nullptr.""" + ptr_value = int(self.value["__repr"]) + return "nullptr" if ptr_value == 0 else f"{ptr_value:#x}" def to_string(self) -> str: """Combine type info and pointer value into annotated_ptr display.""" - template_args = self._template_arguments() - if template_args is None: - return self.type_name - - pointee_type_name, property_type_name = template_args + pointee_type_name, property_type_name = self._template_arguments() full_type = f"{self.type_name}<{pointee_type_name}, {property_type_name}>" - - ptr_display = self._pointer_display() - if ptr_display is None: - return full_type - - return f"{full_type} -> {ptr_display}" + return f"{full_type} -> {self._pointer_display()}" class AnnotatedPtrPrinterLookup(gdb.printing.PrettyPrinter): diff --git a/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py b/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py index f7e8e48d6058..dbf89f483ba2 100644 --- a/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py +++ b/libcudacxx/share/libcudacxx/lldb/annotated_ptr.py @@ -16,58 +16,22 @@ InternalDict = dict[str, object] -def _annotated_ptr_type_name(value: lldb.SBValue) -> str: - """Get type name with fallback to generic 'annotated_ptr'.""" - try: - type_name = cccl_common.public_type_name(value.GetType()) - return type_name or "annotated_ptr" - except Exception: - return "annotated_ptr" - - -def _annotated_ptr_repr_value(value: lldb.SBValue) -> lldb.SBValue: - """Get the __repr member containing the pointer value.""" - try: - value_nonsynth = cccl_common.strip_reference_value(value).GetNonSyntheticValue() - return value_nonsynth.GetChildMemberWithName("__repr") - except Exception: - return lldb.SBValue() - - def is_annotated_ptr(value_type: lldb.SBType, _internal_dict: InternalDict) -> bool: - """Check if an LLDB type represents cuda::annotated_ptr.""" - try: - type_name = cccl_common.canonical_type_name(value_type) - return bool(_ANNOTATED_PTR_PATTERN.fullmatch(type_name)) - except Exception: - return False + type_name = cccl_common.canonical_type_name(value_type) + return _ANNOTATED_PTR_PATTERN.fullmatch(type_name) is not None -def annotated_ptr_description(value: lldb.SBValue) -> str: - """Describe annotated_ptr using type and pointer value information.""" - type_name = _annotated_ptr_type_name(value) - repr_ptr = _annotated_ptr_repr_value(value) - +def annotated_ptr_summary(value: lldb.SBValue, _internal_dict: InternalDict) -> str: + # LLDB already renders the declared type, so the summary only adds the pointer. + repr_ptr = cccl_common.strip_reference_value(value).GetChildMemberWithName("__repr") if not repr_ptr.IsValid(): - return type_name + return "-> unavailable" error = lldb.SBError() ptr_addr = repr_ptr.GetValueAsUnsigned(error) - if not error.Success(): - return type_name - - ptr_display = "nullptr" if ptr_addr == 0 else f"{ptr_addr:#x}" - return f"{type_name} -> {ptr_display}" - - -def annotated_ptr_summary( - value: lldb.SBValue, _internal_dict: InternalDict -) -> str | None: - """Summarize annotated_ptr, returning None if unavailable.""" - try: - return annotated_ptr_description(value) - except Exception: - return None + if error.Fail(): + return "-> unavailable" + return "-> nullptr" if ptr_addr == 0 else f"-> {ptr_addr:#x}" def register(debugger: lldb.SBDebugger, category: str, module: str) -> None: diff --git a/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt b/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt index aec001642637..1d2a3778d186 100644 --- a/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt +++ b/libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt @@ -7,5 +7,7 @@ libcudacxx_add_pretty_printer_test( --case inspect_persisting annotated_ptr.persisting ptr --case inspect_global annotated_ptr.global ptr --case inspect_null annotated_ptr.null ptr + --case inspect_before_update annotated_ptr.update.before ptr + --case inspect_after_update annotated_ptr.update.after ptr # gersemi: on ) diff --git a/libcudacxx/test/debugging/annotated_ptr/gdb.expected b/libcudacxx/test/debugging/annotated_ptr/gdb.expected index 69d2e84b3d36..5f184bf80045 100644 --- a/libcudacxx/test/debugging/annotated_ptr/gdb.expected +++ b/libcudacxx/test/debugging/annotated_ptr/gdb.expected @@ -10,3 +10,9 @@ cuda::annotated_ptr ->
=============== annotated_ptr.null begin =============== cuda::annotated_ptr -> nullptr =============== annotated_ptr.null end =============== +=============== annotated_ptr.update.before begin =============== +cuda::annotated_ptr ->
+=============== annotated_ptr.update.before end =============== +=============== annotated_ptr.update.after begin =============== +cuda::annotated_ptr -> nullptr +=============== annotated_ptr.update.after end =============== diff --git a/libcudacxx/test/debugging/annotated_ptr/lldb.expected b/libcudacxx/test/debugging/annotated_ptr/lldb.expected index 69e440c2e735..92d837e07e3e 100644 --- a/libcudacxx/test/debugging/annotated_ptr/lldb.expected +++ b/libcudacxx/test/debugging/annotated_ptr/lldb.expected @@ -1,12 +1,18 @@ =============== annotated_ptr.streaming begin =============== -(const streaming_annotated_int &)
cuda::annotated_ptr ->
+(const cuda::annotated_ptr &)
->
=============== annotated_ptr.streaming end =============== =============== annotated_ptr.persisting begin =============== -(const persisting_annotated_int &)
cuda::annotated_ptr ->
+(const cuda::annotated_ptr &)
->
=============== annotated_ptr.persisting end =============== =============== annotated_ptr.global begin =============== -(const global_annotated_float &)
cuda::annotated_ptr ->
+(const cuda::annotated_ptr &)
->
=============== annotated_ptr.global end =============== =============== annotated_ptr.null begin =============== -(const streaming_annotated_int &)
cuda::annotated_ptr -> nullptr +(const cuda::annotated_ptr &)
-> nullptr =============== annotated_ptr.null end =============== +=============== annotated_ptr.update.before begin =============== +(const cuda::annotated_ptr &)
->
+=============== annotated_ptr.update.before end =============== +=============== annotated_ptr.update.after begin =============== +(const cuda::annotated_ptr &)
-> nullptr +=============== annotated_ptr.update.after end =============== diff --git a/libcudacxx/test/debugging/annotated_ptr/source.cu b/libcudacxx/test/debugging/annotated_ptr/source.cu index d6bcb376bc69..e51f0b06817e 100644 --- a/libcudacxx/test/debugging/annotated_ptr/source.cu +++ b/libcudacxx/test/debugging/annotated_ptr/source.cu @@ -4,39 +4,39 @@ #include -template -[[gnu::noinline]] void keep_for_debugger(const T& value) +// Give the inspected parameter a stack location that survives optimization, so the +// debugger can read it in this frame. Without this the parameter stays in a +// caller-clobbered register and reads as unavailable at -O3. +#define KEEP_FOR_DEBUGGER(values) asm volatile("" : : "g"(&(values)) : "memory") + +[[gnu::noinline]] void inspect_streaming(const cuda::annotated_ptr& ptr) { - asm volatile("" : : "g"(&value) : "memory"); + KEEP_FOR_DEBUGGER(ptr); } -// Type aliases for annotated_ptr instances -using streaming_annotated_int = cuda::annotated_ptr; -using persisting_annotated_int = cuda::annotated_ptr; -using global_annotated_float = cuda::annotated_ptr; +[[gnu::noinline]] void inspect_persisting(const cuda::annotated_ptr& ptr) +{ + KEEP_FOR_DEBUGGER(ptr); +} -// Test a streaming annotated_ptr -[[gnu::noinline]] void inspect_streaming(const streaming_annotated_int& ptr) +[[gnu::noinline]] void inspect_global(const cuda::annotated_ptr& ptr) { - keep_for_debugger(ptr); + KEEP_FOR_DEBUGGER(ptr); } -// Test a persisting annotated_ptr -[[gnu::noinline]] void inspect_persisting(const persisting_annotated_int& ptr) +[[gnu::noinline]] void inspect_null(const cuda::annotated_ptr& ptr) { - keep_for_debugger(ptr); + KEEP_FOR_DEBUGGER(ptr); } -// Test a global annotated_ptr -[[gnu::noinline]] void inspect_global(const global_annotated_float& ptr) +[[gnu::noinline]] void inspect_before_update(const cuda::annotated_ptr& ptr) { - keep_for_debugger(ptr); + KEEP_FOR_DEBUGGER(ptr); } -// Test a null streaming_annotated_ptr -[[gnu::noinline]] void inspect_null(const streaming_annotated_int& ptr) +[[gnu::noinline]] void inspect_after_update(const cuda::annotated_ptr& ptr) { - keep_for_debugger(ptr); + KEEP_FOR_DEBUGGER(ptr); } int main() @@ -45,22 +45,26 @@ int main() int streaming_data = 42; int persisting_data = 100; float global_data = 3.14f; + int updated_data = 7; - // Test case 1: streaming annotated_ptr - const streaming_annotated_int streaming_ptr(&streaming_data); + const cuda::annotated_ptr streaming_ptr(&streaming_data); inspect_streaming(streaming_ptr); - // Test case 2: persisting annotated_ptr - const persisting_annotated_int persisting_ptr(&persisting_data); + const cuda::annotated_ptr persisting_ptr(&persisting_data); inspect_persisting(persisting_ptr); - // Test case 3: global annotated_ptr - const global_annotated_float global_ptr(&global_data); + const cuda::annotated_ptr global_ptr(&global_data); inspect_global(global_ptr); - // Test case 4: null streaming_annotated_ptr (use default constructor) - const streaming_annotated_int null_ptr{}; + const cuda::annotated_ptr null_ptr{}; inspect_null(null_ptr); + // The pointer is non-null before the update and null after it, so the two stops print + // different values even though the runner masks non-zero addresses. + cuda::annotated_ptr updated_ptr(&updated_data); + inspect_before_update(updated_ptr); + updated_ptr = cuda::annotated_ptr{}; + inspect_after_update(updated_ptr); + return 0; }