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
3 changes: 2 additions & 1 deletion libcudacxx/share/libcudacxx/gdb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
sys.path.insert(0, _SCRIPT_DIRECTORY)

import buffer # noqa: E402
import complex # noqa: E402
import memory_resource # noqa: E402
import std_array # noqa: E402

_PRINTERS = (memory_resource, buffer, std_array)
_PRINTERS = (memory_resource, buffer, std_array, complex)


def register() -> None:
Expand Down
60 changes: 60 additions & 0 deletions libcudacxx/share/libcudacxx/gdb/complex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

"""GDB pretty printer for cuda::std::complex and cuda::complex."""

from __future__ import annotations

from collections.abc import Iterator
from types import ModuleType

import memory_resource

import gdb
import gdb.printing

_COMPLEX_NAMES = frozenset({"cuda::complex", "cuda::std::complex"})


def _template_name(type_name: str) -> str:
return type_name.split("<", 1)[0]


def _is_cuda_complex(value_type: gdb.Type) -> bool:
value_type = value_type.strip_typedefs().unqualified()
template_name = _template_name(memory_resource.public_type_name(value_type))
return template_name in _COMPLEX_NAMES


class ComplexPrinter:
"""Expose cuda::std::complex and cuda::complex parts to GDB."""

def __init__(self, value: gdb.Value) -> None:
self.value = value
self.type = value.type.strip_typedefs().unqualified()
self.type_name = memory_resource.public_type_name(self.type)

def children(self) -> Iterator[tuple[str, gdb.Value]]:
yield "real", self.value["__re_"]
yield "imag", self.value["__im_"]

def to_string(self) -> str:
return self.type_name


class ComplexPrinterLookup(gdb.printing.PrettyPrinter):
"""Select the complex printer by its public class name."""

def __init__(self) -> None:
super().__init__("cuda::complex")

def __call__(self, value: gdb.Value) -> ComplexPrinter | None:
if _is_cuda_complex(value.type):
return ComplexPrinter(value)
return None


def register(objfile: ModuleType) -> None:
"""Register the cuda complex printer with GDB."""
gdb.printing.register_pretty_printer(objfile, ComplexPrinterLookup(), replace=True)
3 changes: 2 additions & 1 deletion libcudacxx/share/libcudacxx/lldb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@
from __future__ import annotations

import buffer
import complex
import memory_resource
import std_array

import lldb

_CATEGORY = "cccl"
_FORMATTERS = (memory_resource, buffer, std_array)
_FORMATTERS = (memory_resource, buffer, std_array, complex)
InternalDict = dict[str, object]


Expand Down
79 changes: 79 additions & 0 deletions libcudacxx/share/libcudacxx/lldb/complex.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

"""LLDB pretty printer for cuda::std::complex and cuda::complex."""

from __future__ import annotations

import re

import lldb

_COMPLEX_PATTERN = re.compile(r"^cuda::(?:std::)?complex<.+>$")
_CHILD_NAMES = ("real", "imag")
InternalDict = dict[str, object]


def is_cuda_complex(value_type: lldb.SBType, _internal_dict: InternalDict) -> bool:
type_name = (
value_type.GetCanonicalType().GetUnqualifiedType().GetDisplayTypeName() or ""
)
return _COMPLEX_PATTERN.fullmatch(type_name) is not None


class ComplexSyntheticProvider:
"""Expose complex real and imaginary parts as LLDB synthetic children."""

def __init__(self, value: lldb.SBValue, _internal_dict: InternalDict) -> None:
self.value = value.GetNonSyntheticValue()
self.update()

def update(self) -> bool:
self.type_name = (
self.value.GetType()
.GetCanonicalType()
.GetUnqualifiedType()
.GetDisplayTypeName()
or ""
)
self.parts: list[lldb.SBValue] = []
real = self.value.GetChildMemberWithName("__re_")
imag = self.value.GetChildMemberWithName("__im_")
if not real.IsValid() or not imag.IsValid():
return False
scalar_type = real.GetType()
self.parts = [
self.value.CreateChildAtOffset("real", 0, scalar_type),
self.value.CreateChildAtOffset(
"imag", scalar_type.GetByteSize(), scalar_type
),
]
return True

def num_children(self) -> int:
return len(self.parts)

def has_children(self) -> bool:
return bool(self.parts)

def get_type_name(self) -> str:
return self.type_name

def get_child_index(self, name: str) -> int:
if name in _CHILD_NAMES:
return _CHILD_NAMES.index(name)
return -1

def get_child_at_index(self, index: int) -> lldb.SBValue | None:
if 0 <= index < len(self.parts):
return self.parts[index]
return None


def register(debugger: lldb.SBDebugger, category: str, module: str) -> None:
"""Register the cuda complex formatter in an LLDB category."""
debugger.HandleCommand(
f"type synthetic add --category {category} --python-class {module}.ComplexSyntheticProvider "
f"--recognizer-function {module}.is_cuda_complex"
)
1 change: 1 addition & 0 deletions libcudacxx/test/debugging/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -199,4 +199,5 @@ endfunction()

add_subdirectory(array)
add_subdirectory(buffer)
add_subdirectory(complex)
add_subdirectory(memory_resource)
14 changes: 14 additions & 0 deletions libcudacxx/test/debugging/complex/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
libcudacxx_add_pretty_printer_test(
NAME complex
SOURCES source.cu
CASES
# gersemi: off
--case inspect_std_default 1 complex.std.default std_default
--case inspect_std_double 1 complex.std.double std_double
--case inspect_cuda_float 1 complex.cuda.float cuda_float
--case inspect_alias 1 complex.alias alias
--case inspect_nested 1 complex.nested nested
--case inspect_before_update 1 complex.update.before updated
--case inspect_after_update 1 complex.update.after updated
# gersemi: on
)
48 changes: 48 additions & 0 deletions libcudacxx/test/debugging/complex/gdb.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
=============== complex.std.default begin ===============
cuda::std::complex<float> = {
real = 0,
imag = 0
}
=============== complex.std.default end ===============
=============== complex.std.double begin ===============
cuda::std::complex<double> = {
real = 1.5,
imag = -2.25
}
=============== complex.std.double end ===============
=============== complex.cuda.float begin ===============
cuda::complex<float> = {
real = 0.5,
imag = 42.5
}
=============== complex.cuda.float end ===============
=============== complex.alias begin ===============
cuda::std::complex<float> = {
real = -3.5,
imag = 0.25
}
=============== complex.alias end ===============
=============== complex.nested begin ===============
cuda::std::array<cuda::std::complex<float>, 2> = {
[0] = cuda::std::complex<float> = {
real = 13.5,
imag = -5.5
},
[1] = cuda::std::complex<float> = {
real = 0.25,
imag = 88.5
}
}
=============== complex.nested end ===============
=============== complex.update.before begin ===============
cuda::std::complex<double> = {
real = 6.5,
imag = -91.5
}
=============== complex.update.before end ===============
=============== complex.update.after begin ===============
cuda::std::complex<double> = {
real = 3.25,
imag = 85.5
}
=============== complex.update.after end ===============
24 changes: 24 additions & 0 deletions libcudacxx/test/debugging/complex/lldb.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
=============== complex.std.default begin ===============
(cuda::std::complex<float>) (real = 0, imag = 0)
=============== complex.std.default end ===============
=============== complex.std.double begin ===============
(cuda::std::complex<double>) (real = 1.5, imag = -2.25)
=============== complex.std.double end ===============
=============== complex.cuda.float begin ===============
(cuda::complex<float>) (real = 0.5, imag = 42.5)
=============== complex.cuda.float end ===============
=============== complex.alias begin ===============
(cuda::std::complex<float>) (real = -3.5, imag = 0.25)
=============== complex.alias end ===============
=============== complex.nested begin ===============
(cuda::std::array<cuda::std::complex<float>, 2>) {
[0] = (real = 13.5, imag = -5.5)
[1] = (real = 0.25, imag = 88.5)
}
=============== complex.nested end ===============
=============== complex.update.before begin ===============
(cuda::std::complex<double>) (real = 6.5, imag = -91.5)
=============== complex.update.before end ===============
=============== complex.update.after begin ===============
(cuda::std::complex<double>) (real = 3.25, imag = 85.5)
=============== complex.update.after end ===============
69 changes: 69 additions & 0 deletions libcudacxx/test/debugging/complex/source.cu
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
//
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include <cuda/__complex_>
#include <cuda/std/array>
#include <cuda/std/complex>

template <class T>
[[gnu::noinline]] void keep_for_debugger(const T& value)
{
asm volatile("" : : "g"(&value) : "memory");
}

using complex_alias = cuda::std::complex<float>;

[[gnu::noinline]] void inspect_std_default(const cuda::std::complex<float>& value)
{
keep_for_debugger(value);
}

[[gnu::noinline]] void inspect_std_double(const cuda::std::complex<double>& value)
{
keep_for_debugger(value);
}

[[gnu::noinline]] void inspect_cuda_float(const cuda::complex<float>& value)
{
keep_for_debugger(value);
}

[[gnu::noinline]] void inspect_alias(const complex_alias& value)
{
keep_for_debugger(value);
}

[[gnu::noinline]] void inspect_nested(const cuda::std::array<cuda::std::complex<float>, 2>& values)
{
keep_for_debugger(values);
}

[[gnu::noinline]] void inspect_before_update(const cuda::std::complex<double>& value)
{
keep_for_debugger(value);
}

[[gnu::noinline]] void inspect_after_update(const cuda::std::complex<double>& value)
{
keep_for_debugger(value);
}

int main()
{
const cuda::std::complex<float> std_default{};
const cuda::std::complex<double> std_double{1.5, -2.25};
const cuda::complex<float> cuda_float{0.5f, 42.5f};
const complex_alias alias{-3.5f, 0.25f};
const cuda::std::array<cuda::std::complex<float>, 2> nested = {{{13.5f, -5.5f}, {0.25f, 88.5f}}};
cuda::std::complex<double> updated{6.5, -91.5};

inspect_std_default(std_default);
inspect_std_double(std_double);
inspect_cuda_float(cuda_float);
inspect_alias(alias);
inspect_nested(nested);
inspect_before_update(updated);
updated = {3.25, 85.5};
inspect_after_update(updated);
}
Loading