Skip to content

failing to debug thrust vector type content in vscode #49

@amit-ruf

Description

@amit-ruf

I'm trying to use latest vscode (v1.97.1) with latest Nsight Visual Code Studio (v 2024.1.34572442) to debug a simple .cu script using thrust::device_vectors and thrust::host_vectors. My system includes Quadro 3000 GPU.
cuda-gdb shows the contents of the variables, but vscode just fails to show them (showing empty values). When I try to access these variable from cuda-gpu commandline vscode clears its RUN AND DEBUG completely, not showing even the names of the local variables.

The simple script I'm trying to debug is:

#include <random>
#include <stdexcept>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <algorithm>
#include <cassert>
#include <cstdlib>
int main(int argc, char *argv[])
{
    cudaDeviceReset();
    thrust::host_vector<int> visible_vec(30);
    thrust::device_vector<float> test(30);    
}

I'm using the following .cuda-gdbinit to configure pretty-printing for thrust classes ( I tried many variations , including without .cuda-gdbinit and pretty-printing):

python
import gdb
import sys

# Ensure pretty printing is enabled and auto-load Python scripts are on.
gdb.execute("set print pretty on")
gdb.execute("set auto-load python-scripts on")

# Python2 compatibility for iterators.
if sys.version_info[0] > 2:
    Iterator = object
else:
    class Iterator:
        def next(self):
            return self.__next__()

#---------------------------------------------------------------------
# Pretty Printer for thrust::*_vector
# Attempts a legacy layout first; if that fails, falls back to an
# alternative layout (adjust field names as needed).
#---------------------------------------------------------------------
class ThrustVectorPrinter(object):
    "Pretty printer for thrust::*_vector"

    def __init__(self, val):
        self.val = val
        # Try legacy layout first.
        try:
            self.pointer = val["m_storage"]["m_begin"]["m_iterator"]
            self.size = int(val["m_size"])
            self.capacity = int(val["m_storage"]["m_size"])
        except Exception:
            # Fallback layout.
            try:
                self.pointer = val["data"]
                self.size = int(val["size"])
                self.capacity = self.size  # Update if you have a proper capacity field
            except Exception:
                # Neither layout worked; mark as empty.
                self.pointer = None
                self.size = 0
                self.capacity = 0
        # Check for device pointer.
        self.is_device = False
        if self.pointer is not None and str(self.pointer.type).startswith("thrust::device_ptr"):
            self.pointer = self.pointer["m_iterator"]
            self.is_device = True
        self._children_cache = None

    class _host_accessible_iterator(Iterator):
        def __init__(self, start, size):
            self.item = start
            self.size = size
            self.count = 0

        def __iter__(self):
            return self

        def __next__(self):
            if self.count >= self.size:
                raise StopIteration
            elt = self.item.dereference()
            result = ("[%d]" % self.count, elt)
            self.item = self.item + 1
            self.count += 1
            return result
        next = __next__  # For Python2

    class _device_iterator(Iterator):
        def __init__(self, start, size):
            self.item = start
            self.size = size
            self.count = 0
            self.sizeof = self.item.dereference().type.sizeof
            # Limit buffer to at most 1MB (or at least one element)
            self.buffer_size = min(size, max(1, 2**20 // self.sizeof))
            self.buffer = gdb.parse_and_eval("(void*)malloc(%d)" % (self.buffer_size * self.sizeof))
            self.buffer.fetch_lazy()
            self.buffer_count = self.buffer_size
            self.update_buffer()

        def update_buffer(self):
            if self.buffer_count >= self.buffer_size:
                self.buffer_item = gdb.parse_and_eval(hex(self.buffer)).cast(self.item.type)
                self.buffer_count = 0
                buffer_addr = hex(self.buffer)
                device_addr = hex(self.item.dereference().address)
                num_bytes = min(self.buffer_size, self.size - self.count) * self.sizeof
                status = gdb.parse_and_eval(
                    "(cudaError)cudaMemcpy(%s, %s, %d, cudaMemcpyDeviceToHost)" % (buffer_addr, device_addr, num_bytes)
                )
                if status != 0:
                    raise gdb.MemoryError("memcpy from device failed: %s" % status)

        def __iter__(self):
            return self

        def __next__(self):
            if self.count >= self.size:
                raise StopIteration
            self.update_buffer()
            elt = self.buffer_item.dereference()
            self.buffer_item = self.buffer_item + 1
            self.buffer_count += 1
            result = ("[%d]" % self.count, elt)
            self.item = self.item + 1
            self.count += 1
            return result
        next = __next__

        def __del__(self):
            try:
                gdb.parse_and_eval("(void)free(%s)" % hex(self.buffer)).fetch_lazy()
            except Exception:
                pass

    def _get_children(self):
        if self.pointer is None:
            return []
        if self.is_device:
            return list(ThrustVectorPrinter._device_iterator(self.pointer, self.size))
        else:
            return list(ThrustVectorPrinter._host_accessible_iterator(self.pointer, self.size))

    def children(self):
        if self._children_cache is None:
            self._children_cache = self._get_children()
        return self._children_cache

    def num_children(self):
        return len(self.children())

    def child(self, index):
        return self.children()[index]

    def to_string(self):
        return "thrust vector of length %d, capacity %d" % (self.size, self.capacity)

    def display_hint(self):
        return "array"

#---------------------------------------------------------------------
# Pretty Printer for thrust::device_reference
#---------------------------------------------------------------------
class ThrustReferencePrinter(object):
    "Pretty printer for thrust::device_reference"

    def __init__(self, val):
        self.val = val
        self.pointer = val["ptr"]["m_iterator"]
        self.type = self.pointer.dereference().type
        self.sizeof = self.type.sizeof
        self.buffer = gdb.parse_and_eval("(void*)malloc(%d)" % self.sizeof)
        device_addr = hex(self.pointer)
        buffer_addr = hex(self.buffer)
        status = gdb.parse_and_eval(
            "(cudaError)cudaMemcpy(%s, %s, %d, cudaMemcpyDeviceToHost)" % (buffer_addr, device_addr, self.sizeof)
        )
        if status != 0:
            raise gdb.MemoryError("memcpy from device failed: %s" % status)
        self.buffer_val = gdb.parse_and_eval(hex(self.buffer)).cast(self.pointer.type).dereference()

    def __del__(self):
        try:
            gdb.parse_and_eval("(void)free(%s)" % hex(self.buffer)).fetch_lazy()
        except Exception:
            pass

    def children(self):
        return []

    def to_string(self):
        return "(%s) @%s: %s" % (self.val.type, self.pointer, self.buffer_val)

    def display_hint(self):
        return None

#---------------------------------------------------------------------
# Pretty Printer for a user-defined type: SingleFibleType.
# Adjust the member names as needed.
#---------------------------------------------------------------------
class SingleFibleTypePrinter(object):
    "Pretty printer for SingleFibleType"

    def __init__(self, val):
        self.val = val

    def to_string(self):
        return ("x: %s, y: %s, z: %s, r: %s, pressure: %s, n_collisions: %s, visible: %s, "
                "deflated: %s, splitable: %s" %
                (self.val["x"], self.val["y"], self.val["z"],
                 self.val["r"], self.val["pressure"], self.val["n_collisions"],
                 self.val["visible"], self.val["deflated"], self.val["splitable"]))

#---------------------------------------------------------------------
# Pretty Printer for std::unordered_map<size_t, SingleFibleType>
#---------------------------------------------------------------------
class UnorderedMapPrinter(object):
    "Pretty printer for std::unordered_map<size_t, SingleFibleType>"

    def __init__(self, val):
        self.val = val

    def to_string(self):
        size = self.val["_M_h"]["_M_element_count"]
        return "std::unordered_map with %d elements" % size

    def children(self):
        elements = []
        buckets = self.val["_M_h"]["_M_buckets"]
        for bucket in buckets:
            node = bucket
            while node != 0:
                node = node.dereference()
                pair = node["_M_v"]
                key = pair["first"]
                value = pair["second"]
                elements.append(("Key %s" % key, key))
                elements.append(("Value %s" % key, SingleFibleTypePrinter(value).to_string()))
                node = node["_M_next"]
        return elements

#---------------------------------------------------------------------
# Build a RegexpCollection of pretty printers for our custom types.
#---------------------------------------------------------------------
def build_pretty_printers():
    pp = gdb.printing.RegexpCollectionPrettyPrinter("custom")
    pp.add_printer("thrust_vector", "^thrust::(host_vector|device_vector)<", ThrustVectorPrinter)
    pp.add_printer("thrust_reference", "^thrust::device_reference<", ThrustReferencePrinter)
    pp.add_printer("unordered_map", "^std::unordered_map<.*SingleFibleType", UnorderedMapPrinter)
    return pp

#---------------------------------------------------------------------
# Register our pretty printers globally.
#---------------------------------------------------------------------
gdb.printing.register_pretty_printer(None, build_pretty_printers())

#---------------------------------------------------------------------
# Also register libstdc++ printers if available.
#---------------------------------------------------------------------
sys.path.insert(0, "/usr/share/gcc/python")
try:
    from libstdcxx.v6.printers import register_libstdcxx_printers
    register_libstdcxx_printers(None)
except ImportError:
    pass

end

Below is a detailed log of vscode DEBUG CONSOLE trying to show visible_vec:

-exec p visible_vec
From client: evaluate({"expression":"-exec p visible_vec","frameId":1000,"context":"repl"})
GDB command: 43 p visible_vec
To client: {"seq":0,"type":"event","event":"output","body":{"category":"log","output":"p visible_vec\n"}}
p visible_vec
To client: {"seq":0,"type":"event","event":"output","body":{"category":"stdout","output":"$2 = thrust vector of length 30, capacity 30 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}\n"}}
$2 = thrust vector of length 30, capacity 30 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
GDB result: 43 done
To client: {"seq":0,"type":"response","request_seq":20,"command":"evaluate","success":true}
To client: {"seq":0,"type":"event","event":"stopped","body":{"reason":"Refreshing State","allThreadsStopped":false,"threadId":99999}}
To client: {"seq":0,"type":"event","event":"changedCudaFocus","body":{}}
From client: scopes({"frameId":1000})
To client: {"seq":0,"type":"response","request_seq":21,"command":"scopes","success":false,"message":"TypeError: Cannot read properties of undefined (reading 'threadId')\n\tat _.scopesRequest (/home/myuser/.vscode-server/extensions/nvidia.nsight-vscode-edition-2024.1.34572442/dist/extension.js:2:170520)\n\tat _.dispatchRequest (/home/myuser/.vscode-server/extensions/nvidia.nsight-vscode-edition-2024.1.34572442/dist/extension.js:2:6469)\n\tat _.dispatchRequest (/home/myuser/.vscode-server/extensions/nvidia.nsight-vscode-edition-2024.1.34572442/dist/extension.js:2:17965)\n\tat _.dispatchRequest (/home/myuser/.vscode-server/extensions/nvidia.nsight-vscode-edition-2024.1.34572442/dist/extension.js:2:159643)\n\tat _.handleMessage (/home/myuser/.vscode-server/extensions/nvidia.nsight-vscode-edition-2024.1.34572442/dist/extension.js:2:22600)\n\tat ej.sendMessage (file:///vscode/vscode-server/bin/linux-x64/e249dada235c2083c83813bd65b7f4707fb97b76/out/vs/workbench/api/node/extensionHostProcess.js:120:697)\n\tat Eb.$sendDAMessage (file:///vscode/vscode-server/bin/linux-x64/e249dada235c2083c83813bd65b7f4707fb97b76/out/vs/workbench/api/node/extensionHostProcess.js:119:97915)\n\tat Uy.S (file:///vscode/vscode-server/bin/linux-x64/e249dada235c2083c83813bd65b7f4707fb97b76/out/vs/workbench/api/node/extensionHostProcess.js:29:158274)\n\tat Uy.Q (file:///vscode/vscode-server/bin/linux-x64/e249dada235c2083c83813bd65b7f4707fb97b76/out/vs/workbench/api/node/extensionHostProcess.js:29:158054)\n\tat[...]
From client: threads(undefined)
GDB command: 44 -thread-info
GDB result: 44 done,threads=[{id="1",target-id="Thread 0x7ffff7a09000 (LWP 34557)",name="test_fist",frame={level="0",addr="0x00005555555618ab",func="test_FiblesSystem__random_generator",args=[{name="n",value="30"}],file="/fiSt/source/core/cuda/test_fist.cu",fullname="/fiSt/source/core/cuda/test_fist.cu",line="96",arch="i386:x86-64"},state="stopped",core="10"},{id="2",target-id="Thread 0x7ffff3200000 (LWP 34561)",name="cuda00001400006",frame={level="0",addr="0x00007ffff7b26bcf",func="poll",args=[],from="/usr/lib/x86_64-linux-gnu/libc.so.6",arch="i386:x86-64"},state="stopped",core="3"},{id="3",target-id="Thread 0x7ffff1c00000 (LWP 34562)",name="test_fist",frame={level="0",addr="0x00007ffff7b33e2e",func="epoll_wait",args=[],from="/usr/lib/x86_64-linux-gnu/libc.so.6",arch="i386:x86-64"},state="stopped",core="6"},{id="4",target-id="Thread 0x7ffff0e00000 (LWP 34572)",name="test_fist",frame={level="0",addr="0x00007ffff7a9f117",func="??",args=[],from="/usr/lib/x86_64-linux-gnu/libc.so.6",arch="i386:x86-64"},
GDB -cont-: 44 state="stopped",core="9"},{id="5",target-id="Thread 0x7fffeaa00000 (LWP 34573)",name="cuda-EvtHandlr",frame={level="0",addr="0x00007ffff7b26bcf",func="poll",args=[],from="/usr/lib/x86_64-linux-gnu/libc.so.6",arch="i386:x86-64"},state="stopped",core="2"}],current-thread-id="1"
To client: {"seq":0,"type":"response","request_seq":22,"command":"threads","success":true,"body":{"threads":[{"id":1,"name":"test_fist","running":false},{"id":2,"name":"cuda00001400006","running":false},{"id":3,"name":"test_fist","running":false},{"id":4,"name":"test_fist","running":false},{"id":5,"name":"cuda-EvtHandlr","running":false},{"id":99999,"name":"(CUDA)"}]}}
From client: stackTrace({"startFrame":0,"levels":20})
GDB command: 45 -stack-info-depth 100
GDB result: 45 done,depth="2"
GDB command: 46 -stack-list-frames 0 1
GDB result: 46 done,stack=[frame={level="0",addr="0x00005555555618ab",func="test_FiblesSystem__random_generator",file="/fiSt/source/core/cuda/test_fist.cu",fullname="/fiSt/source/core/cuda/test_fist.cu",line="96",arch="i386:x86-64"},frame={level="1",addr="0x000055555556308a",func="main",file="/fiSt/source/core/cuda/test_fist.cu",fullname="/fiSt/source/core/cuda/test_fist.cu",line="279",arch="i386:x86-64"}]
To client: {"seq":0,"type":"response","request_seq":23,"command":"stackTrace","success":true,"body":{"stackFrames":[{"id":1000,"source":{"name":"test_fist.cu","path":"/fiSt/source/core/cuda/test_fist.cu","sourceReference":0},"line":96,"column":0,"name":"test_FiblesSystem__random_generator","instructionPointerReference":"0x00005555555618ab"},{"id":1001,"source":{"name":"test_fist.cu","path":"/fiSt/source/core/cuda/test_fist.cu","sourceReference":0},"line":279,"column":0,"name":"main","instructionPointerReference":"0x000055555556308a"}],"totalFrames":2}}
From client: scopes({"frameId":1000})
To client: {"seq":0,"type":"response","request_seq":24,"command":"scopes","success":true,"body":{"scopes":[{"name":"Local","variablesReference":1000,"expensive":false},{"name":"Registers","variablesReference":1001,"expensive":false,"presentationHint":"registers"}]}}
From client: variables({"variablesReference":1000})
To client: {"seq":0,"type":"response","request_seq":25,"command":"variables","success":true,"body":{"variables":[]}}

I assume its either a vscode bug or some setup issue on my end.
Your help is highly appreciated

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions