Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libcudacxx/share/libcudacxx/gdb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
if _SCRIPT_DIRECTORY not in sys.path:
sys.path.insert(0, _SCRIPT_DIRECTORY)

import annotated_ptr # noqa: E402
import atomic # noqa: E402
import buffer # noqa: E402
import complex # noqa: E402
Expand All @@ -37,6 +38,7 @@
_PRINTERS = (
memory_resource,
atomic,
annotated_ptr,
buffer,
std_array,
complex,
Expand Down
78 changes: 78 additions & 0 deletions libcudacxx/share/libcudacxx/gdb/annotated_ptr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# 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

from types import ModuleType

import cccl_common

import gdb
import gdb.printing


def _is_annotated_ptr(value_type: gdb.Type) -> bool:
"""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"
)


class AnnotatedPtrPrinter:
"""Expose cuda::annotated_ptr type and pointer value to GDB."""

def __init__(self, value: gdb.Value) -> None:
# 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)
# 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]:
"""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."""
pointee_type_name, property_type_name = self._template_arguments()
full_type = f"{self.type_name}<{pointee_type_name}, {property_type_name}>"
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Resolved
return f"{full_type} -> {self._pointer_display()}"


class AnnotatedPtrPrinterLookup(gdb.printing.PrettyPrinter):
"""Select the cuda::annotated_ptr printer by its public class name."""

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 the cuda::annotated_ptr printer with GDB."""
gdb.printing.register_pretty_printer(
objfile, AnnotatedPtrPrinterLookup(), replace=True
)
3 changes: 3 additions & 0 deletions libcudacxx/share/libcudacxx/lldb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

from __future__ import annotations

import annotated_ptr
import atomic
import buffer
import complex
Expand All @@ -31,6 +32,7 @@
_FORMATTERS = (
memory_resource,
atomic,
annotated_ptr,
buffer,
std_array,
complex,
Expand All @@ -45,6 +47,7 @@
optional,
shared_resource,
)

InternalDict = dict[str, object]


Expand Down
43 changes: 43 additions & 0 deletions libcudacxx/share/libcudacxx/lldb/annotated_ptr.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# 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 cccl_common

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 = cccl_common.canonical_type_name(value_type)
return _ANNOTATED_PTR_PATTERN.fullmatch(type_name) is not None


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 "-> unavailable"

error = lldb.SBError()
ptr_addr = repr_ptr.GetValueAsUnsigned(error)
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:
"""Register CUDA annotated_ptr formatters with LLDB."""
debugger.HandleCommand(
f"type summary add --category {category} --python-function "
f"{module}.annotated_ptr_summary --recognizer-function "
f"{module}.is_annotated_ptr"
)
1 change: 1 addition & 0 deletions libcudacxx/test/debugging/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -245,3 +245,4 @@ add_subdirectory(shared_resource)
add_subdirectory(span)
add_subdirectory(stream)
add_subdirectory(tuple)
add_subdirectory(annotated_ptr)
13 changes: 13 additions & 0 deletions libcudacxx/test/debugging/annotated_ptr/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
libcudacxx_add_pretty_printer_test(
NAME annotated_ptr
SOURCES source.cu
CASES
# gersemi: off
--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
--case inspect_before_update annotated_ptr.update.before ptr
--case inspect_after_update annotated_ptr.update.after ptr
# gersemi: on
)
18 changes: 18 additions & 0 deletions libcudacxx/test/debugging/annotated_ptr/gdb.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
=============== annotated_ptr.streaming begin ===============
cuda::annotated_ptr<int, cuda::access_property::streaming> -> <address>
=============== annotated_ptr.streaming end ===============
=============== annotated_ptr.persisting begin ===============
cuda::annotated_ptr<int, cuda::access_property::persisting> -> <address>
=============== annotated_ptr.persisting end ===============
=============== annotated_ptr.global begin ===============
cuda::annotated_ptr<float, cuda::access_property::global> -> <address>
=============== annotated_ptr.global end ===============
=============== annotated_ptr.null begin ===============
cuda::annotated_ptr<int, cuda::access_property::streaming> -> nullptr
=============== annotated_ptr.null end ===============
=============== annotated_ptr.update.before begin ===============
cuda::annotated_ptr<int, cuda::access_property::streaming> -> <address>
=============== annotated_ptr.update.before end ===============
=============== annotated_ptr.update.after begin ===============
cuda::annotated_ptr<int, cuda::access_property::streaming> -> nullptr
=============== annotated_ptr.update.after end ===============
18 changes: 18 additions & 0 deletions libcudacxx/test/debugging/annotated_ptr/lldb.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
=============== annotated_ptr.streaming begin ===============
(const cuda::annotated_ptr<int, cuda::access_property::streaming> &) <address> -> <address>
=============== annotated_ptr.streaming end ===============
=============== annotated_ptr.persisting begin ===============
(const cuda::annotated_ptr<int, cuda::access_property::persisting> &) <address> -> <address>
=============== annotated_ptr.persisting end ===============
=============== annotated_ptr.global begin ===============
(const cuda::annotated_ptr<float, cuda::access_property::global> &) <address> -> <address>
=============== annotated_ptr.global end ===============
=============== annotated_ptr.null begin ===============
(const cuda::annotated_ptr<int, cuda::access_property::streaming> &) <address> -> nullptr
=============== annotated_ptr.null end ===============
=============== annotated_ptr.update.before begin ===============
(const cuda::annotated_ptr<int, cuda::access_property::streaming> &) <address> -> <address>
=============== annotated_ptr.update.before end ===============
=============== annotated_ptr.update.after begin ===============
(const cuda::annotated_ptr<int, cuda::access_property::streaming> &) <address> -> nullptr
=============== annotated_ptr.update.after end ===============
70 changes: 70 additions & 0 deletions libcudacxx/test/debugging/annotated_ptr/source.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
// Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <cuda/annotated_ptr>

// 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<int, cuda::access_property::streaming>& ptr)
{
KEEP_FOR_DEBUGGER(ptr);
}
Comment thread
Jacobfaib marked this conversation as resolved.
Resolved

[[gnu::noinline]] void inspect_persisting(const cuda::annotated_ptr<int, cuda::access_property::persisting>& ptr)
{
KEEP_FOR_DEBUGGER(ptr);
}

[[gnu::noinline]] void inspect_global(const cuda::annotated_ptr<float, cuda::access_property::global>& ptr)
{
KEEP_FOR_DEBUGGER(ptr);
}

[[gnu::noinline]] void inspect_null(const cuda::annotated_ptr<int, cuda::access_property::streaming>& ptr)
{
KEEP_FOR_DEBUGGER(ptr);
}

[[gnu::noinline]] void inspect_before_update(const cuda::annotated_ptr<int, cuda::access_property::streaming>& ptr)
{
KEEP_FOR_DEBUGGER(ptr);
}

[[gnu::noinline]] void inspect_after_update(const cuda::annotated_ptr<int, cuda::access_property::streaming>& ptr)
{
KEEP_FOR_DEBUGGER(ptr);
}

int main()
{
// Allocate test data with mutable backing (required by annotated_ptr)
int streaming_data = 42;
int persisting_data = 100;
float global_data = 3.14f;
int updated_data = 7;

const cuda::annotated_ptr<int, cuda::access_property::streaming> streaming_ptr(&streaming_data);
inspect_streaming(streaming_ptr);

const cuda::annotated_ptr<int, cuda::access_property::persisting> persisting_ptr(&persisting_data);
inspect_persisting(persisting_ptr);

const cuda::annotated_ptr<float, cuda::access_property::global> global_ptr(&global_data);
inspect_global(global_ptr);
Comment thread
Jacobfaib marked this conversation as resolved.
Resolved

const cuda::annotated_ptr<int, cuda::access_property::streaming> 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<int, cuda::access_property::streaming> updated_ptr(&updated_data);
inspect_before_update(updated_ptr);
updated_ptr = cuda::annotated_ptr<int, cuda::access_property::streaming>{};
inspect_after_update(updated_ptr);

return 0;
}
Loading