├── .gulp.js ├── nsight-128.png ├── types.d.ts ├── Gulpfile.mjs ├── images ├── nsight-debug.gif └── nsight-debug-config.gif ├── src ├── test │ ├── testPrograms │ │ ├── .clangd │ │ ├── launchEnvVars │ │ │ ├── launchEnvVars.txt │ │ │ ├── .vscode │ │ │ │ ├── tasks.json │ │ │ │ └── launch.json │ │ │ ├── Makefile │ │ │ └── launchEnvVars.cpp │ │ ├── driverApis │ │ │ ├── Makefile │ │ │ ├── .vscode │ │ │ │ ├── tasks.json │ │ │ │ └── launch.json │ │ │ ├── kernel.cu │ │ │ └── driverApis.cpp │ │ ├── frames │ │ │ ├── .vscode │ │ │ │ ├── launch.json │ │ │ │ └── tasks.json │ │ │ ├── Makefile │ │ │ └── frames.cpp │ │ ├── varAssign │ │ │ ├── .vscode │ │ │ │ ├── tasks.json │ │ │ │ └── launch.json │ │ │ ├── Makefile │ │ │ └── varAssign.cpp │ │ ├── concurrent │ │ │ ├── .vscode │ │ │ │ ├── tasks.json │ │ │ │ └── launch.json │ │ │ ├── Makefile │ │ │ └── concurrent.cu │ │ ├── variables │ │ │ ├── .vscode │ │ │ │ ├── tasks.json │ │ │ │ └── launch.json │ │ │ ├── Makefile │ │ │ └── variables.cu │ │ ├── helpers.h │ │ ├── registers │ │ │ ├── Makefile │ │ │ └── registers_test.c │ │ └── Makefile │ ├── tsconfig.json │ ├── setup.ts │ ├── types.d.ts │ ├── basics.test.ts │ ├── advancedBreakpoints.test.ts │ ├── stepping.test.ts │ ├── focus.test.ts │ ├── cudaDebugClient.ts │ ├── breakpoints.test.ts │ ├── registers.test.ts │ ├── testUtils.ts │ └── varAssignments.test.ts ├── tsconfig.json ├── debugger │ ├── cudaGdbAdapter.ts │ ├── deviceRegisterGroups.json │ ├── types.ts │ ├── cudaDebugProtocol.ts │ ├── processList.ts │ ├── cudaQnxGdbServerSession.ts │ ├── cudaGdbServerSession.ts │ └── utils.ts ├── extension.ts ├── autoStartTaskProvider.ts ├── debugController.ts └── telemetryService.ts ├── .gitmodules ├── .cursorindexingignore ├── .eslintignore ├── .cursor └── rules │ ├── cdt-gdb-adapter.mdc │ ├── localized-changes.mdc │ ├── tests.mdc │ └── coding-style.mdc ├── .vscode ├── extensions.json ├── c_cpp_properties.json ├── settings.json ├── tasks.json └── launch.json ├── .prettierrc.yaml ├── .crawler-overrides.json ├── register-ts-node.js ├── jest.config.ts ├── .cursorignore ├── .gitignore ├── tsconfig.json ├── tsconfig.base.json ├── webpack.dev.mts ├── webpack.prod.mts ├── source.fish ├── source.sh ├── webpack.base.mts ├── .eslintrc.js ├── prepare.js ├── README.md ├── CHANGELOG.md ├── updateThirdPartyNotices.ts ├── gulpfile.mts └── LICENSE /.gulp.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preload: ['./register-ts-node.js'] 3 | }; 4 | -------------------------------------------------------------------------------- /nsight-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/nsight-vscode-edition/HEAD/nsight-128.png -------------------------------------------------------------------------------- /types.d.ts: -------------------------------------------------------------------------------- 1 | // Type declarations for dependencies without @types. 2 | 3 | declare module 'await-spawn'; 4 | -------------------------------------------------------------------------------- /Gulpfile.mjs: -------------------------------------------------------------------------------- 1 | // Gulp running in ESM mode only supports .mjs entrypoints. 2 | export * from './gulpfile.mts'; 3 | -------------------------------------------------------------------------------- /images/nsight-debug.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/nsight-vscode-edition/HEAD/images/nsight-debug.gif -------------------------------------------------------------------------------- /images/nsight-debug-config.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA/nsight-vscode-edition/HEAD/images/nsight-debug-config.gif -------------------------------------------------------------------------------- /src/test/testPrograms/.clangd: -------------------------------------------------------------------------------- 1 | CompileFlags: 2 | Add: 3 | - -L/usr/local/cuda/lib64 4 | - -I/usr/local/cuda/include 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "cdt-gdb-adapter"] 2 | path = cdt-gdb-adapter 3 | url = https://github.com/NVIDIA/cdt-gdb-adapter.git 4 | -------------------------------------------------------------------------------- /.cursorindexingignore: -------------------------------------------------------------------------------- 1 | # Don't index SpecStory auto-save files, but allow explicit context inclusion via @ references 2 | .specstory/** 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | cdt-gdb-adapter 3 | out 4 | dist 5 | .eslintrc.js 6 | Gulpfile.mjs 7 | jest.*.js 8 | prepare.js 9 | register-ts-node.js 10 | -------------------------------------------------------------------------------- /src/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "types": ["node", "jest"], 5 | }, 6 | "exclude": [], 7 | "files": ["types.d.ts"] 8 | } 9 | -------------------------------------------------------------------------------- /src/test/testPrograms/launchEnvVars/launchEnvVars.txt: -------------------------------------------------------------------------------- 1 | ENV_VAR1=Value1 2 | ENV_VAR2 = Value2 3 | ENV_VAR3 = 4 | MISFORMED_LINE 5 | # A comment (should be ignored) 6 | =X 7 | = 8 | unset PRE_EXISTING_ENV_VAR 9 | -------------------------------------------------------------------------------- /.cursor/rules/cdt-gdb-adapter.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: true 5 | --- 6 | cdt-gdb-adapter is a vendored dependency that can be edited directly if needed to simplify the solution. However, it may not reference anything from the main package. -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "dbaeumer.vscode-eslint", 6 | "esbenp.prettier-vscode" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /.prettierrc.yaml: -------------------------------------------------------------------------------- 1 | # https://prettier.io/docs/en/options.html 2 | 3 | arrowParens: "always" 4 | bracketSpacing: true 5 | printWidth: 250 6 | proseWrap: never 7 | quoteProps: "as-needed" 8 | semi: true 9 | singleQuote: true 10 | tabWidth: 4 11 | trailingComma: "none" 12 | useTabs: false -------------------------------------------------------------------------------- /.crawler-overrides.json: -------------------------------------------------------------------------------- 1 | { 2 | "assert-plus" : { 3 | "licenseFile": "https://raw.githubusercontent.com/joyent/node-assert-plus/master/LICENSE" 4 | }, 5 | "uri-js": { 6 | "licenseFile": "https://raw.githubusercontent.com/garycourt/uri-js/master/LICENSE" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.cursor/rules/localized-changes.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: true 5 | --- 6 | Only edit things that are directly relevant to the task at hand. Don't add gratuitous comments to existing code or refactor things not relevant to the change being made unless specifically requested. -------------------------------------------------------------------------------- /register-ts-node.js: -------------------------------------------------------------------------------- 1 | // Can be passed to node --import to enable ts-node support for ESM modules. 2 | // Used by .gulp.js to enable gulpfile.ts. 3 | 4 | const { register } = require('node:module'); 5 | const { pathToFileURL } = require('node:url'); 6 | 7 | register('ts-node/esm', pathToFileURL('./')); 8 | -------------------------------------------------------------------------------- /src/test/setup.ts: -------------------------------------------------------------------------------- 1 | import { jest, expect } from '@jest/globals'; 2 | import * as jestExtended from 'jest-extended'; 3 | 4 | if (process.env.VSCODE_INSPECTOR_OPTIONS) { 5 | console.info('Debugger detected; disabling test timeout.'); 6 | jest.setTimeout(1_000_000_000); 7 | } 8 | 9 | expect.extend(jestExtended); 10 | -------------------------------------------------------------------------------- /src/test/testPrograms/driverApis/Makefile: -------------------------------------------------------------------------------- 1 | all: build 2 | 3 | build: driverApis kernel.fatbin 4 | 5 | driverApis: driverApis.cpp 6 | nvcc -arch=sm_75 -g -G -o $@ $+ -lcuda 7 | 8 | kernel.fatbin: kernel.cu 9 | nvcc -arch=sm_75 -g -G -o $@ $+ -fatbin 10 | 11 | clean: 12 | rm -f driverApis 13 | rm -f kernel.fatbin 14 | 15 | clobber: clean 16 | -------------------------------------------------------------------------------- /src/test/testPrograms/launchEnvVars/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build", 6 | "type": "shell", 7 | "command": "make", 8 | "group": { 9 | "kind": "build", 10 | "isDefault": true 11 | } 12 | } 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "ES2022", 5 | "moduleResolution": "Bundler", 6 | "types": ["node", "vscode"], 7 | }, 8 | "include": ["**/*.ts", "**/*.mts", "**/*.json"], 9 | "exclude": ["test"], 10 | "references": [ 11 | {"path": "../cdt-gdb-adapter" }, 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /src/test/testPrograms/frames/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "CUDA: Debug with CUDA-GDB", 6 | "type": "cuda-gdb", 7 | "request": "launch", 8 | "gdb": "cuda-gdb", 9 | "program": "${workspaceFolder}/frames", 10 | "arguments": "" 11 | } 12 | 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/test/testPrograms/frames/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build", 6 | "type": "shell", 7 | "command": "make", 8 | "problemMatcher":["$gcc"], 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/test/testPrograms/varAssign/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build", 6 | "type": "shell", 7 | "command": "make", 8 | "problemMatcher":["$gcc"], 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/test/testPrograms/concurrent/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build", 6 | "type": "shell", 7 | "command": "make", 8 | "problemMatcher":["$nvcc"], 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/test/testPrograms/driverApis/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build", 6 | "type": "shell", 7 | "command": "make", 8 | "problemMatcher":["$nvcc"], 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/test/testPrograms/variables/.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "label": "Build", 6 | "type": "shell", 7 | "command": "make", 8 | "problemMatcher":["$nvcc"], 9 | "group": { 10 | "kind": "build", 11 | "isDefault": true 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/test/types.d.ts: -------------------------------------------------------------------------------- 1 | import 'jest-extended'; 2 | import 'jest-extended-fs'; 3 | 4 | // 'jest-extended' only registers types for implicit `expect` that comes from @types/jest, 5 | // but not for the explicitly imported one from @jest/globals; this fixes that. 6 | declare module 'expect' { 7 | interface AsymmetricMatchers extends jest.Matchers {} 8 | interface Matchers extends jest.Matchers {} 9 | } 10 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import { type JestConfigWithTsJest } from 'ts-jest'; 2 | 3 | export default { 4 | testEnvironment: 'node', 5 | testMatch: ['/src/test/**/*.test.ts'], 6 | transform: { 7 | '^.+.m?[jt]sx?$': ['ts-jest', { tsconfig: '/src/test/tsconfig.json' }] 8 | }, 9 | transformIgnorePatterns: [], 10 | setupFilesAfterEnv: ['/src/test/setup.ts'], 11 | testTimeout: 10_000 12 | } satisfies JestConfigWithTsJest; 13 | -------------------------------------------------------------------------------- /src/test/testPrograms/variables/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "CUDA: Debug with CUDA-GDB", 6 | "type": "cuda-gdb", 7 | "request": "launch", 8 | "program": "${workspaceFolder}/variables", 9 | "logFile": "${workspaceFolder}/.rubicon_log", 10 | "verboseLogging": true, 11 | "testMode": true 12 | } 13 | 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /src/test/testPrograms/varAssign/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "CUDA: Debug with CUDA-GDB", 6 | "type": "cuda-gdb", 7 | "request": "launch", 8 | "gdb": "cuda-gdb", 9 | "program": "${workspaceFolder}/varAssign", 10 | "arguments": "", 11 | "logFile": "${workspaceFolder}/.rubicon_log", 12 | "verbose": true 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /.cursorignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /out 3 | /dist 4 | .rubicon_log 5 | /nsight-vscode-edition-*.vsix 6 | /Rubicon-*.zip 7 | /src/test/testPrograms/concurrent/concurrent 8 | /src/test/testPrograms/driverApis/driverApis 9 | /src/test/testPrograms/driverApis/kernel.fatbin 10 | /src/test/testPrograms/frames/frames 11 | /src/test/testPrograms/launchEnvVars/launchEnvVars 12 | /src/test/testPrograms/registers/registers_test 13 | /src/test/testPrograms/varAssign/varAssign 14 | /src/test/testPrograms/variables/variables 15 | -------------------------------------------------------------------------------- /src/test/testPrograms/concurrent/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "CUDA: Debug with CUDA-GDB", 6 | "type": "cuda-gdb", 7 | "request": "launch", 8 | "gdb": "cuda-gdb", 9 | "program": "${workspaceFolder}/concurrent", 10 | "logFile": "${workspaceFolder}/.rubicon_log", 11 | "verboseLogging": true, 12 | "testMode": true 13 | } 14 | 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.specstory 2 | /node_modules 3 | /out 4 | /dist 5 | .rubicon_log 6 | /nsight-vscode-edition-*.vsix 7 | /Rubicon-*.zip 8 | /src/test/testPrograms/concurrent/concurrent 9 | /src/test/testPrograms/driverApis/driverApis 10 | /src/test/testPrograms/driverApis/kernel.fatbin 11 | /src/test/testPrograms/frames/frames 12 | /src/test/testPrograms/launchEnvVars/launchEnvVars 13 | /src/test/testPrograms/registers/registers_test 14 | /src/test/testPrograms/varAssign/varAssign 15 | /src/test/testPrograms/variables/variables 16 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "/usr/local/cuda/include", 7 | "${workspaceFolder}/**" 8 | ], 9 | "defines": [], 10 | "compilerPath": "/usr/local/cuda/bin/nvcc", 11 | "cStandard": "gnu17", 12 | "cppStandard": "gnu++14", 13 | "intelliSenseMode": "linux-gcc-x64" 14 | } 15 | ], 16 | "version": 4 17 | } -------------------------------------------------------------------------------- /src/test/testPrograms/driverApis/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "CUDA: Debug with CUDA-GDB", 6 | "type": "cuda-gdb", 7 | "request": "launch", 8 | "debuggerPath": "/usr/local/cuda/bin/cuda-gdb", 9 | "program": "${workspaceFolder}/driverApis", 10 | "logFile": "${workspaceFolder}/.rubicon_log", 11 | "cwd": "${workspaceFolder}", 12 | "verboseLogging": true 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "module": "Node16", 5 | "moduleResolution": "Node16", 6 | "noEmit": true, 7 | "allowImportingTsExtensions": true, 8 | }, 9 | "include": ["**/*.ts", "**/*.mts", "**/*.js", "**/*.mjs", "**/*.json"], 10 | "exclude": [ 11 | ".vscode-test", 12 | "cdt-gdb-adapter", 13 | "node_modules", 14 | "out", 15 | "src", 16 | ], 17 | "references": [ 18 | {"path": "src"}, 19 | {"path": "src/test"}, 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /src/test/testPrograms/launchEnvVars/.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "CUDA: Debug with CUDA-GDB", 6 | "type": "cuda-gdb", 7 | "request": "launch", 8 | "gdb": "cuda-gdb", 9 | "program": "${workspaceFolder}/launchEnvVars", 10 | "arguments": "", 11 | "envFile": "${workspaceFolder}/launchEnvVars.txt", 12 | "initCommands": ["set env ENV_VAR1=Value3"], 13 | "cwd": "~", 14 | "stopAtEntry": true 15 | } 16 | 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node20/tsconfig.json", 3 | "compilerOptions": { 4 | "rootDir": "", 5 | "outDir": "out", 6 | "composite": true, 7 | "sourceMap": true, 8 | "declarationMap": true, 9 | "strictNullChecks": true, 10 | "noImplicitReturns": true, 11 | "noFallthroughCasesInSwitch": true, 12 | "noUnusedLocals": true, 13 | "resolveJsonModule": true, 14 | "allowJs": true, 15 | "types": ["node"], 16 | }, 17 | "files": [ 18 | "types.d.ts", 19 | ], 20 | "include": [], 21 | "exclude": [], 22 | } 23 | -------------------------------------------------------------------------------- /.cursor/rules/tests.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: src/test/** 4 | alwaysApply: false 5 | --- 6 | Don't use Chai for tests, use Jest instead. 7 | 8 | # Asserts 9 | 10 | Use `expect.objectContaining` where possible to validate structured outputs in their entirety, instead of drilling down to individual properties to validate them one by one. 11 | 12 | # Test programs 13 | 14 | Don't hardcode line numbers for test programs, it's brittle and requires renumbering if test program is edited. Instead, use comments in debuggee code and `TestUtils.getLineNumbersFromComments` in test code. Use `TestUtils.getTestSource` to generate expected DAP `source` values. -------------------------------------------------------------------------------- /src/test/testPrograms/frames/Makefile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------- 2 | # 3 | # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 4 | # 5 | # The contents of this file are licensed under the Eclipse Public License 2.0. 6 | # The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ 7 | # 8 | # SPDX-License-Identifier: EPL-2.0 9 | # 10 | # ---------------------------------------------------------------------------------- 11 | 12 | 13 | all: build 14 | 15 | build: frames 16 | 17 | frames: frames.cpp 18 | g++ -pthread -g -o $@ $+ 19 | 20 | clean: 21 | rm -f frames 22 | 23 | clobber: clean 24 | -------------------------------------------------------------------------------- /src/test/testPrograms/varAssign/Makefile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------- 2 | # 3 | # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 4 | # 5 | # The contents of this file are licensed under the Eclipse Public License 2.0. 6 | # The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ 7 | # 8 | # SPDX-License-Identifier: EPL-2.0 9 | # 10 | # ---------------------------------------------------------------------------------- 11 | 12 | 13 | all: build 14 | 15 | build: varAssign 16 | 17 | varAssign: varAssign.cpp 18 | g++ -g -o $@ $+ 19 | 20 | clean: 21 | rm -f varAssign 22 | 23 | clobber: clean 24 | -------------------------------------------------------------------------------- /src/test/testPrograms/helpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if defined(assert) 4 | #undef assert 5 | #endif 6 | 7 | #define assert(c) \ 8 | do { \ 9 | if(!(c)) { \ 10 | fprintf(stderr, "Assertion \"%s\" failed. (%s:%d)\n", \ 11 | #c, __FILE__, __LINE__); \ 12 | exit(1); \ 13 | } \ 14 | } while(0) 15 | 16 | #define assertSucceeded(c) \ 17 | do { \ 18 | unsigned __tmp = c; \ 19 | if(__tmp != cudaSuccess) { \ 20 | fprintf(stderr, "Operation \"%s\" failed with error code %x. (%s:%d)\n", \ 21 | #c, (__tmp), __FILE__, __LINE__); \ 22 | exit(__tmp); \ 23 | } \ 24 | } while(0) 25 | -------------------------------------------------------------------------------- /src/test/testPrograms/variables/Makefile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------- 2 | # 3 | # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 4 | # 5 | # The contents of this file are licensed under the Eclipse Public License 2.0. 6 | # The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ 7 | # 8 | # SPDX-License-Identifier: EPL-2.0 9 | # 10 | # ---------------------------------------------------------------------------------- 11 | 12 | 13 | all: build 14 | 15 | build: variables 16 | 17 | variables: variables.cu 18 | nvcc -arch=sm_75 -g -G -o $@ $+ 19 | 20 | clean: 21 | rm -f variables 22 | 23 | clobber: clean 24 | -------------------------------------------------------------------------------- /src/test/testPrograms/concurrent/Makefile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------- 2 | # 3 | # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 4 | # 5 | # The contents of this file are licensed under the Eclipse Public License 2.0. 6 | # The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ 7 | # 8 | # SPDX-License-Identifier: EPL-2.0 9 | # 10 | # ---------------------------------------------------------------------------------- 11 | 12 | 13 | all: build 14 | 15 | build: concurrent 16 | 17 | concurrent: concurrent.cu 18 | nvcc -arch=sm_75 -g -G -o $@ $+ 19 | 20 | clean: 21 | rm -f concurrent 22 | 23 | clobber: clean 24 | -------------------------------------------------------------------------------- /src/test/testPrograms/launchEnvVars/Makefile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------- 2 | # 3 | # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 4 | # 5 | # The contents of this file are licensed under the Eclipse Public License 2.0. 6 | # The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ 7 | # 8 | # SPDX-License-Identifier: EPL-2.0 9 | # 10 | # ---------------------------------------------------------------------------------- 11 | 12 | 13 | all: build 14 | 15 | build: launchEnvVars 16 | 17 | launchEnvVars: launchEnvVars.cpp 18 | g++ -g -o $@ $+ 19 | 20 | clean: 21 | rm -f launchEnvVars 22 | 23 | clobber: clean 24 | -------------------------------------------------------------------------------- /src/test/testPrograms/registers/Makefile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------- 2 | # 3 | # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 4 | # 5 | # The contents of this file are licensed under the Eclipse Public License 2.0. 6 | # The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ 7 | # 8 | # SPDX-License-Identifier: EPL-2.0 9 | # 10 | # ---------------------------------------------------------------------------------- 11 | 12 | 13 | all: build 14 | 15 | build: registers_test 16 | 17 | registers_test: registers_test.c 18 | gcc -g -o $@ $+ 19 | 20 | clean: 21 | rm -f registers_test 22 | 23 | clobber: clean 24 | -------------------------------------------------------------------------------- /src/test/testPrograms/Makefile: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------------------- 2 | # 3 | # Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved. 4 | # 5 | # The contents of this file are licensed under the Eclipse Public License 2.0. 6 | # The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ 7 | # 8 | # SPDX-License-Identifier: EPL-2.0 9 | # 10 | # ---------------------------------------------------------------------------------- 11 | 12 | SUBDIRS := $(patsubst %/Makefile, %, $(wildcard */Makefile)) 13 | 14 | GOALS := $(or $(MAKECMDGOALS), all) 15 | 16 | .PHONY: $(GOALS) $(SUBDIRS) 17 | 18 | $(GOALS): $(SUBDIRS) 19 | 20 | $(SUBDIRS): 21 | $(MAKE) -C $@ $(MAKECMDGOALS) 22 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "explorer.excludeGitIgnore": true, 3 | 4 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 5 | "typescript.tsc.autoDetect": "off", 6 | 7 | // Set the default 8 | "editor.formatOnSave": false, 9 | 10 | // Enable per-language for Prettier 11 | "[javascript]": { 12 | "editor.formatOnSave": true 13 | }, 14 | "[typescript]": { 15 | "editor.formatOnSave": true 16 | }, 17 | "editor.defaultFormatter": "esbenp.prettier-vscode", 18 | "eslint.lintTask.enable": true, 19 | 20 | // https://marketplace.visualstudio.com/items?itemName=Orta.vscode-jest 21 | // Debugger tests are rather heavyweight because they require the extension to be bundled 22 | // and spawn the debug adapter as a separate process for each test, so they shouldn't be 23 | // run automatically. 24 | "jest.runMode": { 25 | "type": "on-demand", 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/testPrograms/launchEnvVars/launchEnvVars.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | int main() 8 | { 9 | std::vector> envVars = { 10 | {"ENV_VAR1", "Value1"}, 11 | {"ENV_VAR2", "Value2"}, 12 | {"ENV_VAR3", ""}, 13 | {"PRE_EXISTING_ENV_VAR", nullptr} 14 | }; 15 | 16 | bool success = true; 17 | for (const auto& var : envVars) 18 | { 19 | const char* actualValue = std::getenv(var.first); 20 | const char* expectedValue = var.second; 21 | 22 | success = success && (!actualValue || !expectedValue ? actualValue == expectedValue : !strcmp(actualValue, expectedValue)); 23 | std::cout << var.first << "=" << (actualValue ? actualValue : "Var not found.") << std::endl; 24 | } 25 | 26 | std::cout << std::getenv("PWD") << std::endl; 27 | 28 | return success ? 0 : 1; 29 | } 30 | -------------------------------------------------------------------------------- /src/debugger/cudaGdbAdapter.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import { CudaGdbSession } from './cudaGdbSession'; 13 | 14 | CudaGdbSession.run(CudaGdbSession); 15 | -------------------------------------------------------------------------------- /src/debugger/deviceRegisterGroups.json: -------------------------------------------------------------------------------- 1 | { 2 | "deviceRegisterGroups": [ 3 | { 4 | "groupName": "SASS", 5 | "groupPattern": "^R\\d+$" 6 | }, 7 | { 8 | "groupName": "Predicate", 9 | "groupPattern": "^P\\d+$", 10 | "isPredicate": true 11 | }, 12 | { 13 | "groupName": "Uniform", 14 | "groupPattern": "^UR\\d+$" 15 | }, 16 | { 17 | "groupName": "Uniform Predicate", 18 | "groupPattern": "^UP\\d+$", 19 | "isPredicate": true 20 | }, 21 | { 22 | "groupName": "Hidden Registers", 23 | "groupPattern": "\\(dummy internal register\\)", 24 | "isHidden": true 25 | }, 26 | { 27 | "groupName": "Unnamed Registers", 28 | "groupPattern": "^$", 29 | "isHidden": true 30 | }, 31 | { 32 | "groupName": "Other", 33 | "groupPattern": ".*" 34 | } 35 | ] 36 | } -------------------------------------------------------------------------------- /webpack.dev.mts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import { merge } from 'webpack-merge'; 13 | import base from './webpack.base.mts'; 14 | 15 | export default merge(base, { 16 | mode: 'development' 17 | }); 18 | -------------------------------------------------------------------------------- /webpack.prod.mts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import { merge } from 'webpack-merge'; 13 | import base from './webpack.base.mts'; 14 | 15 | export default merge(base, { 16 | mode: 'production' 17 | }); 18 | -------------------------------------------------------------------------------- /source.fish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env fish 2 | 3 | if test "$_" != "source" 4 | printf "Usage: source %s\n" (status filename) 5 | exit 1 6 | end 7 | 8 | set -l EXTENSION_DIR (status dirname) 9 | set -l NODE_VERSION (cat $EXTENSION_DIR/../target-node-version.txt) 10 | 11 | echo "Using node $NODE_VERSION" 12 | 13 | set -l NODE_REL_DIR "$EXTENSION_DIR/../../../../Prebuilt/Linux/NodeJS/$NODE_VERSION/bin" 14 | if test ! -d "$NODE_REL_DIR" 15 | printf "Error: '$NODE_REL_DIR' is not a valid directory." 16 | end 17 | 18 | set -l NODE_DIR (realpath "$NODE_REL_DIR") 19 | 20 | if contains $NODE_DIR $PATH 21 | echo "PATH already contains '$NODE_DIR'." 22 | else 23 | if test -d "$NODE_DIR" 24 | echo "Adding '$NODE_DIR' to PATH" 25 | set -g PATH $NODE_DIR $PATH 26 | else 27 | echo "$NODE_DIR does not exist, please sync before continuing." 28 | end 29 | end 30 | 31 | set -l EXTENSION_DIR_ABS (realpath $EXTENSION_DIR) 32 | set -l MODULES_DIR "$EXTENSION_DIR_ABS/node_modules/.bin" 33 | 34 | if contains $MODULES_DIR $PATH 35 | echo "PATH already contains '$MODULES_DIR'." 36 | else 37 | echo "Adding '$MODULES_DIR' to PATH" 38 | set -g PATH $MODULES_DIR $PATH 39 | end 40 | 41 | echo "PATH:" $PATH 42 | -------------------------------------------------------------------------------- /src/test/testPrograms/registers/registers_test.c: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | #include 13 | 14 | __attribute__((naked)) size_t testFunc(size_t a, size_t b, size_t c, size_t d, size_t e, size_t f) 15 | { 16 | __asm__("movq $0x59,%rax"); 17 | __asm__("retq"); 18 | } 19 | 20 | int main() 21 | { 22 | return testFunc(5, 8, 13, 21, 34, 55); 23 | } 24 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "watch", 7 | "problemMatcher": { 8 | "fileLocation":"absolute", 9 | "pattern":[ 10 | { 11 | "regexp": "^\\s*\\[tsl\\] ERROR in ([^\\s]*)\\((\\d+,\\d+)\\)$", 12 | "file": 1, 13 | "location": 2, 14 | }, 15 | { 16 | "regexp": "^\\s*(TS\\d+: .*)$", 17 | "message": 1 18 | } 19 | ], 20 | "background": { 21 | "beginsPattern":"(asset extension.js)|(assets by status)|(webpack.*--watch)", 22 | "endsPattern":"(compiled successfully in)|(compiled with \\d+ errors? in)" 23 | } 24 | }, 25 | "isBackground": true, 26 | "presentation": { 27 | "reveal": "never" 28 | }, 29 | "group": { 30 | "kind": "build", 31 | "isDefault": true 32 | }, 33 | "label": "npm: watch", 34 | "detail": "webpack -c webpack.dev.js --watch" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /src/test/testPrograms/driverApis/kernel.cu: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | */ 25 | 26 | extern "C" __global__ void kernel(unsigned *results) 27 | { 28 | int idx = blockIdx.x * blockDim.x + threadIdx.x; 29 | results[idx] = idx; 30 | } 31 | -------------------------------------------------------------------------------- /src/test/basics.test.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import { TestUtils } from './testUtils'; 13 | import { CudaDebugClient } from './cudaDebugClient'; 14 | 15 | describe('Basic tests', () => { 16 | let dc: CudaDebugClient; 17 | 18 | beforeEach(async () => { 19 | dc = await TestUtils.launchDebugger('variables/variables'); 20 | }); 21 | 22 | afterEach(async () => { 23 | await dc?.stop(); 24 | }); 25 | 26 | it('Can launch the adapter', async () => { 27 | // Only run before-each and after-each 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /source.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | # Source this script via '. init.sh' 4 | function check_sourced() { 5 | if [[ ${FUNCNAME[-1]} != "source" ]]; then 6 | printf "Usage: source %s\n" "$0" 7 | exit 1 8 | fi 9 | } 10 | function add_node_path { 11 | EXTENSION_DIR=$(dirname $BASH_SOURCE[0]) 12 | NODE_VERSION=`cat $EXTENSION_DIR/../target-node-version.txt` 13 | 14 | echo "Using node $NODE_VERSION" 15 | 16 | NODE_REL_DIR="$EXTENSION_DIR/../../../../Prebuilt/Linux/NodeJS/$NODE_VERSION/bin" 17 | if [ ! -d "$NODE_REL_DIR" ]; then 18 | printf "Error: $NODE_REL_DIR is not a valid directory." 19 | fi 20 | 21 | NODE_DIR=`realpath "$NODE_REL_DIR"` 22 | 23 | if [[ "$PATH" =~ (^|:)"$NODE_DIR"(:|$) ]]; then 24 | echo "PATH already contains $NODE_DIR." 25 | else 26 | if [ -d "$NODE_DIR" ]; then 27 | echo "Adding $NODE_DIR to path" 28 | export PATH="$NODE_DIR:$PATH" 29 | else 30 | echo "$NODE_DIR does not exist, please sync before continuing." 31 | fi 32 | fi 33 | 34 | EXTENSION_DIR_ABS=`realpath $EXTENSION_DIR` 35 | MODULES_DIR="$EXTENSION_DIR_ABS/node_modules/.bin" 36 | 37 | if [[ "$PATH" =~ (^|:)"$MODULES_DIR"(:|$) ]]; then 38 | echo "PATH already contains $MODULES_DIR." 39 | else 40 | echo "Adding $MODULES_DIR to path" 41 | export PATH="$MODULES_DIR:$PATH" 42 | fi 43 | 44 | echo "PATH=$PATH" 45 | } 46 | 47 | check_sourced 48 | add_node_path 49 | -------------------------------------------------------------------------------- /src/test/testPrograms/varAssign/varAssign.cpp: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | #include 13 | 14 | struct A 15 | { 16 | int alpha; 17 | int beta; 18 | A(int alpha, int beta) 19 | { 20 | this->alpha = alpha; 21 | this->beta = beta; 22 | } 23 | }; 24 | 25 | int structAssignTest(A* a1, A* a2) 26 | { 27 | A* a = a1; 28 | return (a->alpha + a2->alpha) * (a->beta + a2->beta); 29 | } 30 | 31 | int main() 32 | { 33 | A a1(1,1); 34 | A a2(2,2); 35 | 36 | int x = 2; 37 | int satResult = structAssignTest(&a1, &a2); 38 | int result = x + satResult; 39 | 40 | std::cout << result << '\n'; 41 | 42 | return 0; 43 | } 44 | 45 | -------------------------------------------------------------------------------- /src/debugger/types.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | export interface CudaDim { 13 | x?: number; 14 | y?: number; 15 | z?: number; 16 | } 17 | 18 | export interface CudaSwFocus { 19 | type: 'software'; 20 | blockIdx?: CudaDim; 21 | threadIdx?: CudaDim; 22 | } 23 | 24 | export interface CudaHwFocus { 25 | type: 'hardware'; 26 | sm?: number; 27 | warp?: number; 28 | lane?: number; 29 | } 30 | 31 | export type CudaFocus = CudaSwFocus | CudaHwFocus; 32 | 33 | export interface OsInfo { 34 | platform?: string; 35 | 36 | architecture?: string; 37 | 38 | distribution?: string; 39 | 40 | distributionVersion?: string; 41 | } 42 | 43 | export interface GpuInfo { 44 | // "current" is a string that contains a "*" character for the currently active device. 45 | current?: string; 46 | 47 | name?: string; 48 | 49 | description?: string; 50 | 51 | smType?: string; 52 | } 53 | 54 | export interface SystemInfo { 55 | os?: OsInfo; 56 | 57 | gpus?: GpuInfo[]; 58 | } 59 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Extension", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "runtimeExecutable": "${execPath}", 9 | "args": [ 10 | "--extensionDevelopmentPath=${workspaceFolder}" 11 | ], 12 | "outFiles": [ 13 | "${workspaceFolder}/dist/**/*.js" 14 | ], 15 | "preLaunchTask": "${defaultBuildTask}" 16 | }, 17 | { 18 | "name": "Tests", 19 | "type": "node", 20 | "request": "launch", 21 | "runtimeExecutable": "npm", 22 | "args": [ 23 | "run", 24 | "test", 25 | "--", 26 | "-i", 27 | "-t", 28 | "${input:testsPattern}", 29 | ], 30 | "console": "integratedTerminal" 31 | }, 32 | { 33 | "name": "Gulp Build", 34 | "type": "node", 35 | "request": "launch", 36 | "program": "${workspaceRoot}/node_modules/gulp/bin/gulp.js", 37 | "stopOnEntry": false, 38 | "args": [ 39 | "${input:gulpArgs}" 40 | ], 41 | "cwd": "${workspaceRoot}", 42 | "runtimeArgs": [ 43 | "--nolazy" 44 | ], 45 | "console": "internalConsole", 46 | } 47 | ], 48 | "inputs": [ 49 | { 50 | "id": "gulpArgs", 51 | "type": "promptString", 52 | "description": "Build task and optionally its command-line arguments" 53 | }, 54 | { 55 | "id": "testsPattern", 56 | "type": "promptString", 57 | "description": "Title of the test case or test suite to run. Leave blank to run all tests.", 58 | }, 59 | ] 60 | } -------------------------------------------------------------------------------- /src/test/advancedBreakpoints.test.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import { expect } from 'chai'; 13 | import { TestUtils } from './testUtils'; 14 | import { CudaDebugClient } from './cudaDebugClient'; 15 | 16 | describe('Advanced Breakpoint tests', () => { 17 | let dc: CudaDebugClient; 18 | 19 | afterEach(async () => { 20 | await dc?.stop(); 21 | }); 22 | 23 | it('Breakpoints on kernel loaded using driver APIs work', async () => { 24 | const pathToFatbin = TestUtils.resolveTestPath('driverApis/kernel.fatbin'); 25 | dc = await TestUtils.launchDebugger('driverApis/driverApis', pathToFatbin); 26 | 27 | const bpLine = 28; 28 | 29 | const bpResp = await dc.setBreakpointsRequest({ 30 | source: TestUtils.getTestSource('driverApis/kernel.cu'), 31 | breakpoints: [ 32 | { 33 | line: bpLine 34 | } 35 | ] 36 | }); 37 | 38 | expect(bpResp.body.breakpoints.length).eq(1); 39 | expect(bpResp.body.breakpoints[0].verified).eq(true); 40 | 41 | await dc.configurationDoneRequest(); 42 | await TestUtils.assertStoppedLocation(dc, 'breakpoint', 'kernel.cu', bpLine); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import * as fse from 'fs-extra'; 13 | import path from 'node:path'; 14 | import * as vscode from 'vscode'; 15 | 16 | import { activateDebugController } from './debugController'; 17 | import { OsInfo } from './debugger/types'; 18 | import { readOsInfo } from './debugger/utils'; 19 | import { TelemetryService } from './telemetryService'; 20 | import { AutoStartTaskProvider } from './autoStartTaskProvider'; 21 | 22 | const GA4_API_SECRET = 'VaT67ILSRsGM10lr8Dut7A'; 23 | const GA4_MEASUREMENT_ID = 'G-VELYBBB7X1'; 24 | 25 | export async function activate(context: vscode.ExtensionContext): Promise { 26 | const packagePath: string = path.resolve(context.extensionPath, 'package.json'); 27 | const packageJson = await fse.readJson(packagePath); 28 | 29 | const telemetry: TelemetryService = new TelemetryService(context, GA4_API_SECRET, GA4_MEASUREMENT_ID, packageJson.version); 30 | 31 | if (telemetry.isEnabled) { 32 | const hostOsInfo: OsInfo = await readOsInfo(); 33 | telemetry.trackSystemInfo('host', { os: hostOsInfo }); 34 | } 35 | 36 | context.subscriptions.push(vscode.tasks.registerTaskProvider('Autostart', new AutoStartTaskProvider())); 37 | 38 | activateDebugController(context, telemetry); 39 | } 40 | 41 | export function deactivate(): void {} 42 | -------------------------------------------------------------------------------- /src/test/testPrograms/concurrent/concurrent.cu: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "../helpers.h" 31 | 32 | __global__ void kernelFunc(cuda::barrier* barrier) { 33 | volatile int threadNum = threadIdx.x; 34 | /*@device1*/ barrier->arrive_and_wait(); 35 | /*@device2*/ for (volatile int dummy = 0;; ++dummy); 36 | } 37 | 38 | int main(int argc, char** argv) { 39 | const int numHostThreads = 1, numDeviceThreads = 32; 40 | 41 | int cudaDeviceCount; 42 | assertSucceeded(cudaGetDeviceCount(&cudaDeviceCount)); 43 | assert(cudaDeviceCount > 0); 44 | assertSucceeded(cudaSetDevice(0)); 45 | 46 | typedef cuda::barrier barrier_t; 47 | barrier_t* barrier; 48 | assertSucceeded(cudaMallocHost(&barrier, sizeof *barrier)); 49 | new(barrier) barrier_t(numHostThreads + numDeviceThreads); 50 | 51 | kernelFunc<<<1, numDeviceThreads>>>(barrier); 52 | /*@host1*/ barrier->arrive_and_wait(); 53 | /*@host2*/ for (volatile int dummy = 0;; ++dummy); 54 | } 55 | -------------------------------------------------------------------------------- /src/debugger/cudaDebugProtocol.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | /* eslint-disable max-classes-per-file */ 13 | import { DebugProtocol } from '@vscode/debugprotocol'; 14 | import * as types from './types'; 15 | 16 | // eslint-disable-next-line @typescript-eslint/no-namespace 17 | export namespace CudaDebugProtocol { 18 | export enum Request { 19 | changeCudaFocus = 'changeCudaFocus' 20 | } 21 | 22 | export enum Event { 23 | changedCudaFocus = 'changedCudaFocus', 24 | systemInfo = 'systemInfo' 25 | } 26 | 27 | export interface ChangeCudaFocusRequest extends DebugProtocol.Request { 28 | //command: Request.changeCudaFocus; 29 | arguments: ChangeCudaFocusArguments; 30 | } 31 | 32 | export interface ChangeCudaFocusArguments { 33 | focus?: types.CudaFocus; 34 | } 35 | 36 | export interface ChangeCudaFocusResponse extends DebugProtocol.Response { 37 | body: { 38 | focus?: types.CudaFocus; 39 | }; 40 | } 41 | 42 | export interface ChangedCudaFocusEvent extends DebugProtocol.Event { 43 | //event: Event.changedCudaFocus; 44 | body: { 45 | focus?: types.CudaFocus; 46 | }; 47 | } 48 | 49 | export interface SystemInfoEvent extends DebugProtocol.Event { 50 | //event: Event.systemInfo; 51 | body: { 52 | systemInfo?: types.SystemInfo; 53 | }; 54 | } 55 | } 56 | 57 | /* eslint-enable max-classes-per-file */ 58 | -------------------------------------------------------------------------------- /.cursor/rules/coding-style.mdc: -------------------------------------------------------------------------------- 1 | --- 2 | description: 3 | globs: 4 | alwaysApply: true 5 | --- 6 | # Variable naming 7 | 8 | Use `ok` for local boolean variables that are used solely for error checking. Reuse this variable across multiple calls unless you actually need to run things and capture errors in parallel. 9 | 10 | # Prefer declarative over imperative code 11 | 12 | Avoid constructing objects and collections step by step using explicit loops etc. Use declarative techniques instead. 13 | 14 | # Prefer runtime-checked correctness constructs over compile-time-only 15 | 16 | Use `#` for private members for runtime checking and proper namespacing of private properties in class hierarchies. Avoid TypeScript's compile-time-only `private`. 17 | 18 | # Use array and object destructuring for conciseness 19 | 20 | For example, instead of: 21 | ``` 22 | const a = foo.a; 23 | const b = foo.b; 24 | ``` 25 | consider writing: 26 | ``` 27 | const {a, b} = foo; 28 | ``` 29 | 30 | # Use implicit property names when variable name matches the property 31 | 32 | For example, instead of: 33 | ``` 34 | const bar = {a: a, b: foo.b} 35 | ``` 36 | consider writing: 37 | ``` 38 | const var = {a, foo.b} 39 | ``` 40 | 41 | # Refactor non-trivial computations into local functions 42 | 43 | If some value must be computed in a non-trivial way in the middle of a function - e.g. involving a complex loop with lots of additional local variables that are used only by this loop - and this computation is otherwise isolated from the rest of the function except for captured immutable variables, consider refactoring the computation into a local function and then calling it to obtain the value. For example, instead of: 44 | ``` 45 | let foo = []; 46 | while (something) { 47 | // ... lots of other code ... 48 | foo.push(value); 49 | // ... lots of other code ... 50 | } 51 | // use foo 52 | console.log(foo) 53 | ``` 54 | consider doing: 55 | ``` 56 | const computeFoo = () => { 57 | let foo = []; 58 | while (something) { 59 | // ... lots of other code ... 60 | foo.push(value); 61 | // ... lots of other code ... 62 | } 63 | }; 64 | const foo = computeFoo(); 65 | ``` 66 | Note that this example is only applicable to imperative code that cannot be more succintly and clearly rewritten in a declarative fashion! In general, you should still prefer to build up collections and objects in a purely declarative way, without loops or assignments, where it doesn't hamper readability. -------------------------------------------------------------------------------- /src/test/stepping.test.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import { expect } from 'chai'; 13 | import { TestUtils } from './testUtils'; 14 | import { CudaDebugClient } from './cudaDebugClient'; 15 | 16 | describe('Stepping tests', () => { 17 | let dc: CudaDebugClient; 18 | 19 | const programName = 'variables'; 20 | const programExe = `${programName}/${programName}`; 21 | const programSrc = `${programName}.cu`; 22 | 23 | beforeEach(async () => { 24 | dc = await TestUtils.launchDebugger(programExe); 25 | }); 26 | 27 | afterEach(async () => { 28 | await dc.stop(); 29 | }); 30 | 31 | it('Can step (in & out) of device function', async () => { 32 | const deviceCallSite = 113; 33 | const nextLineAfterCallSite = 114; 34 | const deviceEntry = 87; 35 | 36 | const bpResponse = await dc.setBreakpointsRequest({ 37 | source: TestUtils.getTestSource(`${programName}/${programSrc}`), 38 | breakpoints: [ 39 | { 40 | line: deviceCallSite 41 | } 42 | ] 43 | }); 44 | 45 | expect(bpResponse.body.breakpoints.length).eq(1); 46 | expect(bpResponse.body.breakpoints[0].verified).eq(true); 47 | 48 | await dc.configurationDoneRequest(); 49 | const stopLocation = await TestUtils.assertStoppedLocation(dc, 'breakpoint', programSrc, deviceCallSite); 50 | 51 | await dc.stepInRequest({ threadId: stopLocation.threadId }); 52 | await TestUtils.assertStoppedLocation(dc, 'step', programSrc, deviceEntry); 53 | 54 | await dc.stepOutRequest({ threadId: stopLocation.threadId }); 55 | await TestUtils.assertStoppedLocation(dc, 'step', programSrc, nextLineAfterCallSite); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /src/autoStartTaskProvider.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | 3 | enum AutostartType { 4 | LocalHost = 'autostart (localhost)', 5 | RemoteHost = 'autostart (remote)', 6 | ScpRemoteHost = 'autostart (secure copy executable binary, remote)', 7 | QNXHost = 'autostart (remote QNX)', 8 | ScpQNXHost = 'autostart (secure copy cuda-gdbserver binary, remote QNX)' 9 | } 10 | 11 | export interface RemoteTaskDefinition extends vscode.TaskDefinition { 12 | label: AutostartType; 13 | type: 'shell'; 14 | command: string; 15 | } 16 | 17 | export class AutoStartTaskProvider implements vscode.TaskProvider { 18 | private tasks: vscode.Task[] | undefined; 19 | 20 | static remoteScriptType = 'shell'; 21 | 22 | public async provideTasks(): Promise { 23 | return this.getTasks(); 24 | } 25 | 26 | public resolveTask(task: vscode.Task): vscode.Task | undefined { 27 | const { label } = task.definition; 28 | if (!label) { 29 | // eslint-disable-next-line unicorn/no-useless-undefined 30 | return undefined; 31 | } 32 | const { definition } = task as any; 33 | return this.getTask(definition); 34 | } 35 | 36 | private getTasks(): vscode.Task[] { 37 | if (this.tasks !== undefined) { 38 | return this.tasks; 39 | } 40 | 41 | /* eslint-disable no-template-curly-in-string */ 42 | const taskList: RemoteTaskDefinition[] = [ 43 | { label: AutostartType.LocalHost, type: 'shell', command: 'cuda-gdbserver ${config:host}:${config:port} ${config:executable}' }, 44 | { label: AutostartType.RemoteHost, type: 'shell', command: 'ssh ${config:username}@${config:host} "cuda-gdbserver ${config:host}:${config:port} ${config:remoteExecutable}"' }, 45 | { 46 | label: AutostartType.ScpRemoteHost, 47 | type: 'shell', 48 | command: 'scp ${config:executable} ${config:username}@${config:host}:/tmp && ssh ${config:username}@${config:host} "cuda-gdbserver ${config:host}:${config:port} /tmp/${config:execName}"' 49 | }, 50 | { label: AutostartType.QNXHost, type: 'shell', command: 'ssh ${config:username}@${config:host} ${config:cudaGdbServerPath} ${config:port}' }, 51 | { label: AutostartType.ScpQNXHost, type: 'shell', command: 'scp ${config:cudaGdbServerPath} ${config:username}@${config:host}:/tmp && ssh ${config:username}@${config:host} /tmp/cuda-gdbserver ${config:port}' } 52 | ]; 53 | /* eslint-enable no-template-curly-in-string */ 54 | 55 | this.tasks = taskList.map((taskItem) => this.getTask(taskItem)); 56 | 57 | return this.tasks; 58 | } 59 | 60 | // eslint-disable-next-line class-methods-use-this 61 | private getTask(taskItem: RemoteTaskDefinition): vscode.Task { 62 | const shellExec = new vscode.ShellExecution(taskItem.command); 63 | const task = new vscode.Task(taskItem, vscode.TaskScope.Workspace, taskItem.label, 'Nsight', shellExec); 64 | // Setting this again based on suggestion from https://github.com/microsoft/vscode/issues/95876 65 | task.definition.command = taskItem.command; 66 | 67 | return task; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/test/focus.test.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import { TestUtils } from './testUtils'; 13 | import { CudaDebugClient } from './cudaDebugClient'; 14 | import { CudaDebugProtocol } from '../debugger/cudaDebugProtocol'; 15 | 16 | describe('CUDA focus tests', () => { 17 | let dc: CudaDebugClient; 18 | 19 | const programName = 'variables'; 20 | const programExe = `${programName}/${programName}`; 21 | const programSrc = `${programExe}.cu`; 22 | 23 | beforeEach(async () => { 24 | dc = await TestUtils.launchDebugger(programExe); 25 | }); 26 | 27 | afterEach(async () => { 28 | await dc.stop(); 29 | }); 30 | 31 | it('Notifies about focus change when switching focus via gdb commmands', async () => { 32 | const lineNumbers = TestUtils.getLineNumbersFromComments(programSrc); 33 | 34 | dc = await TestUtils.launchDebugger(programExe); 35 | 36 | const breakpoints = [{ line: lineNumbers.deviceCall }]; 37 | const bpResp = await dc.setBreakpointsRequest({ 38 | breakpoints, 39 | source: TestUtils.getTestSource(programSrc) 40 | }); 41 | expect(bpResp).toEqual( 42 | expect.objectContaining({ 43 | body: expect.objectContaining({ 44 | breakpoints: [ 45 | expect.objectContaining({ 46 | verified: true, 47 | line: lineNumbers.deviceCall 48 | }) 49 | ] 50 | }) 51 | }) 52 | ); 53 | 54 | const stopInfo = await dc.expectToStopAt('breakpoint', programSrc, lineNumbers.deviceCall, undefined, async () => { 55 | await dc.configurationDoneRequest(); 56 | }); 57 | 58 | const changedCudaFocusPromise = dc.waitForEvent('changedCudaFocus'); 59 | await dc.evaluateRequest({ 60 | expression: '`cuda thread 1', 61 | context: 'repl', 62 | frameId: stopInfo.frameId 63 | }); 64 | const changedCudaFocusEvent = (await changedCudaFocusPromise) as CudaDebugProtocol.ChangedCudaFocusEvent; 65 | expect(changedCudaFocusEvent).toEqual( 66 | expect.objectContaining({ 67 | body: expect.objectContaining({ 68 | focus: { 69 | type: 'software', 70 | blockIdx: { x: 0, y: 0, z: 0 }, 71 | threadIdx: { x: 1, y: 0, z: 0 } 72 | } 73 | }) 74 | }) 75 | ); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /src/debugger/processList.ts: -------------------------------------------------------------------------------- 1 | import * as vscode from 'vscode'; 2 | import { promisify } from 'node:util'; 3 | import * as childProcess from 'node:child_process'; 4 | 5 | const exec = promisify(childProcess.exec); 6 | 7 | interface ProcessItem extends vscode.QuickPickItem { 8 | pid: number; 9 | } 10 | 11 | export async function pickProcess(): Promise { 12 | const processToReturn = await chooseProcess(); 13 | const pid = processToReturn?.pid.toString(); 14 | const label = processToReturn?.label; 15 | // returning this as a string because pickProcess must only return a string because of how package.json is set up 16 | const toReturn = `${pid}:${label}`; 17 | return toReturn; 18 | } 19 | 20 | export async function chooseProcess(): Promise { 21 | const items = getAttachItems(); 22 | 23 | const chosenProcess: vscode.QuickPickOptions = { 24 | matchOnDescription: true, 25 | matchOnDetail: true 26 | }; 27 | 28 | const process = await vscode.window.showQuickPick(items, chosenProcess); 29 | 30 | if (process === undefined) { 31 | throw new Error('Process not selected'); 32 | } else { 33 | return process; 34 | } 35 | } 36 | 37 | export async function getAttachItems(): Promise { 38 | // these are the indices of the pid information and command name which we need to populate the process picker 39 | // sample ps -af -o uname,pid,time, cmd output 40 | // USER PID TIME CMD 41 | // uname1 13710 00:00:00 bash 42 | // uname1 18042 00:00:00 \_ ps -af -o uname,pid,time,cmd 43 | // uname1 13361 00:00:00 bash 44 | // uname1 13390 00:00:09 \_ /usr/bin/p4v.bin 45 | // root 1710 00:00:50 /usr/lib/xorg/Xorg -core :0 -seat seat0 -auth /var/run 46 | // root 1713 00:00:00 /sbin/agetty -o -p -- \u --noclear tty1 linux 47 | 48 | let stdout: string, stderr: string; 49 | try { 50 | ({ stdout, stderr } = await exec('ps -af -o uname,pid,time,cmd')); 51 | if (stderr) { 52 | throw new Error(stderr); 53 | } 54 | } catch (error) { 55 | const message = error instanceof Error ? error.message : `${error}`; 56 | throw new Error(`Unable to retrieve process list:\n${message}`); 57 | } 58 | 59 | const output = stdout.split('\n'); 60 | 61 | // figure out the index where PID ends because all PIDs would end at that index 62 | const pidEndIdx = output[0].indexOf('PID') + 3; 63 | 64 | // based on the the format ps returns info, the pids would start after the first set of spaces we encounter 65 | const pidArray = output.map((x: string) => Number.parseInt(x.slice(x.indexOf(' '), pidEndIdx).trimStart())).slice(1); 66 | 67 | const cmdArray = output 68 | .map((x: string) => { 69 | // figuring out the index of the executable based on the last index of ':' 70 | const fullPathCmdSlice = x.slice(x.lastIndexOf(':') + 3, x.length).trimStart(); 71 | const execCmdSlice = fullPathCmdSlice.slice(fullPathCmdSlice.lastIndexOf('/') + 1, fullPathCmdSlice.length); 72 | return execCmdSlice; 73 | }) 74 | .slice(1); 75 | 76 | const username = output.map((x: string) => x.slice(0, x.indexOf(' ')).trimStart()).slice(1); 77 | 78 | const items: ProcessItem[] = pidArray.map((item, index) => ({ pid: pidArray[index], label: `${username[index]} : ${cmdArray[index]}` })); 79 | items.sort((a, b) => 0 - (a.label > b.label ? -1 : 1)); 80 | 81 | const quickPickList: ProcessItem[] = items.filter((item: ProcessItem) => item.label.trim() !== ':'); 82 | 83 | return quickPickList; 84 | } 85 | -------------------------------------------------------------------------------- /webpack.base.mts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import { fileURLToPath } from 'node:url'; 13 | import { type Configuration } from 'webpack'; 14 | 15 | const config: Configuration = { 16 | // VSCode extensions run in Electron with Node.js as the runtime. 17 | target: 'node', 18 | entry: { 19 | // The main entry point of this extension used by VSCode. 20 | main: { 21 | import: './src/extension.ts', 22 | filename: 'extension.js' 23 | }, 24 | // Entry point for the standalone debug adapter, used by test suite. 25 | debugAdapter: { 26 | import: './src/debugger/cudaGdbAdapter.ts', 27 | filename: 'debugAdapter.js' 28 | } 29 | }, 30 | // We want the bundle to include source maps for better diagnostics and debugging even in release builds, but 31 | // we don't want the source maps to include the original source code. Instead, "devtoolModuleFilenameTemplate" 32 | // is used below to point to the original source files in the source tree during development. 33 | devtool: 'nosources-source-map', 34 | output: { 35 | // VSCode extensions must be CommonJS modules. 36 | library: { type: 'commonjs2' }, 37 | // The bundle is stored in dist/, whence "main" in package.json references it. 38 | path: fileURLToPath(new URL('dist', import.meta.url)), 39 | // Source map should resolve filenames relative to the root of the source tree. 40 | devtoolModuleFilenameTemplate: '../[resource-path]', 41 | // Chunking the output is unnecessary for a VSCode extension. 42 | chunkFormat: false 43 | }, 44 | // Packages that shouldn't be bundled, and how the bundler should resolve references to them. 45 | externals: { 46 | // The "vscode" module is provided by VSCode itself and exposed via require(). 47 | vscode: 'commonjs vscode' 48 | }, 49 | resolve: { 50 | // File extensions to consider for extensionless imports, in order. 51 | extensions: ['.ts', '.js'] 52 | }, 53 | module: { 54 | rules: [ 55 | { 56 | test: /\.ts$/, 57 | exclude: /node_modules/, 58 | use: [ 59 | { 60 | loader: 'ts-loader', 61 | options: { 62 | configFile: 'tsconfig.json' 63 | } 64 | } 65 | ] 66 | }, 67 | { 68 | test: /\.node$/, 69 | loader: 'node-loader' 70 | }, 71 | { 72 | // Extract and re-bundle source maps for JS files in node_modules. 73 | // Required to get correct line numbers for cdt-gdb-adapter tracebacks & debugging. 74 | test: /\.m?js$/, 75 | enforce: 'pre', 76 | use: ['source-map-loader'] 77 | } 78 | ] 79 | } 80 | }; 81 | 82 | export default config; 83 | -------------------------------------------------------------------------------- /src/test/cudaDebugClient.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import assert from 'node:assert/strict'; 13 | import { type SpawnOptions } from 'node:child_process'; 14 | import * as fs from 'node:fs'; 15 | import { expect } from '@jest/globals'; 16 | import { DebugClient } from '@vscode/debugadapter-testsupport'; 17 | import { DebugProtocol } from '@vscode/debugprotocol'; 18 | import { CudaDebugProtocol } from '../debugger/cudaDebugProtocol'; 19 | 20 | export interface StopLocationInfo { 21 | threadId: number; 22 | frameId: number; 23 | } 24 | 25 | export class CudaDebugClient extends DebugClient { 26 | public capabilities: DebugProtocol.Capabilities; 27 | 28 | constructor(debugAdapterPath: string, spawnOptions?: SpawnOptions) { 29 | expect(fs.existsSync(debugAdapterPath)).toBeTrue(); 30 | 31 | super('node', debugAdapterPath, 'cuda-gdb', spawnOptions); 32 | 33 | this.capabilities = {}; 34 | } 35 | 36 | public async changeCudaFocusRequest(args: CudaDebugProtocol.ChangeCudaFocusArguments): Promise { 37 | return (await this.send(CudaDebugProtocol.Request.changeCudaFocus, args)) as CudaDebugProtocol.ChangeCudaFocusResponse; 38 | } 39 | 40 | public async expectStopped(reason: string, file: string, line: number, event: DebugProtocol.Event): Promise { 41 | expect(event).toEqual( 42 | expect.objectContaining({ 43 | event: 'stopped', 44 | body: expect.objectContaining({ 45 | reason, 46 | threadId: expect.any(Number) 47 | }) 48 | }) 49 | ); 50 | 51 | const stoppedEvent = event as DebugProtocol.StoppedEvent; 52 | const { threadId } = stoppedEvent.body; 53 | assert(threadId !== undefined); 54 | 55 | const { 56 | body: { stackFrames } 57 | } = await this.stackTraceRequest({ 58 | threadId 59 | }); 60 | 61 | expect(stackFrames).toBeArray(); 62 | expect(stackFrames).toEqual( 63 | expect.objectContaining({ 64 | [0]: expect.objectContaining({ 65 | line, 66 | source: expect.objectContaining({ 67 | path: expect.toEndWith(file) 68 | }) 69 | }) 70 | }) 71 | ); 72 | 73 | expect(stackFrames[0]).toEqual( 74 | expect.objectContaining({ 75 | line, 76 | source: expect.objectContaining({ 77 | path: expect.toEndWith(file) 78 | }) 79 | }) 80 | ); 81 | 82 | return { threadId, frameId: stackFrames[0].id }; 83 | } 84 | 85 | public async expectToStopAt(reason: string, file: string, line: number, timeout: number | undefined, body: () => Promise): Promise { 86 | const stoppedEvent = this.waitForEvent('stopped', timeout); 87 | await body(); 88 | return await this.expectStopped(reason, file, line, await stoppedEvent); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/test/testPrograms/driverApis/driverApis.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include 34 | 35 | #if defined(assert) 36 | #undef assert 37 | #endif 38 | 39 | #define assert(c) \ 40 | do { \ 41 | if(!(c)) { \ 42 | fprintf(stderr, "Assertion \"%s\" failed. (%s:%d)\n", \ 43 | #c, __FILE__, __LINE__); \ 44 | exit(1); \ 45 | } \ 46 | } while(0) 47 | 48 | #define assertSucceeded(c) \ 49 | do { \ 50 | unsigned __tmp = c; \ 51 | if(__tmp != CUDA_SUCCESS) { \ 52 | fprintf(stderr, "Operation \"%s\" failed with error code %x. (%s:%d)\n", \ 53 | #c, (__tmp), __FILE__, __LINE__); \ 54 | exit(__tmp); \ 55 | } \ 56 | } while(0) 57 | 58 | constexpr int logOfThreadsPerBlock = 5; 59 | constexpr int logOfDataLength = 7; 60 | constexpr int threadsPerBlock = 1 << logOfThreadsPerBlock; 61 | constexpr int dataLength = 1 << logOfDataLength; 62 | 63 | constexpr const char* binaryPath = "kernel.fatbin"; 64 | constexpr const char* functionName = "kernel"; 65 | 66 | int main(int argc, char* argv[]) 67 | { 68 | assert(argc == 2); 69 | 70 | assertSucceeded(cuInit(0)); 71 | 72 | CUdevice cuDevice; 73 | assertSucceeded(cuDeviceGet(&cuDevice, 0)); 74 | 75 | CUcontext cuContext; 76 | assertSucceeded(cuCtxCreate(&cuContext, 0, cuDevice)); 77 | 78 | std::ostringstream binaryStream; 79 | 80 | { 81 | const char *binaryPath = argv[1]; 82 | 83 | std::ifstream binaryFile(binaryPath, std::ios::binary); 84 | assert(binaryFile.good()); 85 | 86 | binaryStream << binaryFile.rdbuf(); 87 | 88 | binaryFile.close(); 89 | } 90 | 91 | CUmodule cuModule; 92 | assertSucceeded(cuModuleLoadData(&cuModule, binaryStream.str().c_str())); 93 | 94 | CUfunction cuFunction; 95 | assertSucceeded(cuModuleGetFunction(&cuFunction, cuModule, functionName)); 96 | 97 | CUdeviceptr d_results; 98 | assertSucceeded(cuMemAlloc(&d_results, sizeof(unsigned) << logOfDataLength)); 99 | 100 | constexpr int blocks = 1 << (logOfDataLength - logOfThreadsPerBlock); 101 | void *args[] = {&d_results}; 102 | 103 | assertSucceeded(cuLaunchKernel(cuFunction, blocks, 1, 1, threadsPerBlock, 1, 1, 0, NULL, args, NULL)); 104 | 105 | unsigned* h_results = reinterpret_cast(malloc(sizeof(unsigned) << logOfDataLength)); 106 | assertSucceeded(cuMemcpyDtoH(reinterpret_cast(h_results), d_results, sizeof(unsigned) << logOfDataLength)); 107 | 108 | for (unsigned i = 0; i < dataLength; i++) 109 | { 110 | if (h_results[i] != i){ 111 | fprintf(stderr, "h_results[%u] is %u\n", i, h_results[i]); 112 | exit(1); 113 | } 114 | } 115 | 116 | delete[] h_results; 117 | assertSucceeded(cuMemFree(d_results)); 118 | assertSucceeded(cuCtxDestroy(cuContext)); 119 | 120 | fprintf(stderr, "Success\n"); 121 | 122 | exit(0); 123 | } 124 | -------------------------------------------------------------------------------- /src/test/testPrograms/frames/frames.cpp: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | #include 13 | #include 14 | 15 | int callee1() 16 | { 17 | int a = 3; 18 | int b = 5; 19 | 20 | // Breakpoint on the line with return: 21 | // Variable c in the callee should've 22 | // gone away from the variables panel 23 | // at this point. 24 | return a + b; 25 | } 26 | 27 | int callee2() 28 | { 29 | int a = 8; 30 | int b = 13; 31 | int c = 21; 32 | int d = 34; 33 | 34 | // Breakpoint on the line with return: 35 | // Variable c should come back now but 36 | // have the value 21. Variable "a" and 37 | // "b" should be correctly updated and 38 | // a new variable "d" should appear. 39 | return a + b + c + d; 40 | } 41 | 42 | int callee3() 43 | { 44 | int a = 8; 45 | 46 | // Breakpoint on the line with return: 47 | // Variable "a" still has the same value 48 | // meaning that -var-update should return 49 | // an empty changelist. 50 | return a; 51 | } 52 | 53 | int callee4() 54 | { 55 | struct 56 | { 57 | int x; 58 | int y; 59 | int z; 60 | } a = {55, 89, 144}; 61 | 62 | // Breakpoint on the line with return: 63 | // Only variable "a" should be shown in 64 | // the panel but it should be expandable 65 | // now as it is a struct. 66 | return a.x + a.y; 67 | } 68 | 69 | int callee5() 70 | { 71 | struct 72 | { 73 | int x; 74 | int y; 75 | int z; 76 | } a = {233, 89, 377}; 77 | 78 | // Breakpoint on the line with return: 79 | // a.x and a.z change but a.y remains 80 | // the same. 81 | return a.x + a.y; 82 | } 83 | 84 | int callee6() 85 | { 86 | // Breakpoint on the line with return: 87 | // Only "a" should be in the panel but 88 | // it should be back to a leaf node. 89 | int a = 610; 90 | return a; 91 | } 92 | 93 | int caller() 94 | { 95 | int c = 2; 96 | return c + callee1() + callee2() + callee3() + callee4() + callee5() + callee6(); 97 | } 98 | 99 | int ovFunc() 100 | { 101 | int a = 2; 102 | int b = 3; 103 | int c = 5; 104 | return a + b + c; 105 | } 106 | 107 | int ovFunc(int a) 108 | { 109 | int b = a; 110 | char c = static_cast(a) + '0'; 111 | c++; 112 | a--; 113 | return a + b + c; 114 | } 115 | 116 | int ovFunc(int a, int c) 117 | { 118 | struct { 119 | int x; 120 | int y; 121 | } b = {8, 13}; 122 | 123 | return a + b.x + b.y + c; 124 | } 125 | 126 | double globalArray[2]; 127 | 128 | void threadMain( 129 | unsigned ufThreadId) // User-friendly thread ID 130 | { 131 | int elementIdx = ufThreadId - 1; 132 | double result = ufThreadId; 133 | 134 | for(unsigned long long i = 0; i < 1000; i++) 135 | { 136 | result *= 1.5; 137 | if(result > 10) 138 | { 139 | result /= 10; 140 | } 141 | } 142 | 143 | globalArray[elementIdx] = result; 144 | } 145 | 146 | int main() 147 | { 148 | int callChainResult = caller(); 149 | 150 | int ovResult = 151 | ovFunc() + ovFunc(3) + ovFunc(1, 2) + ovFunc(3) + ovFunc(); 152 | 153 | std::thread thread1(threadMain, 1); 154 | std::thread thread2(threadMain, 2); 155 | 156 | thread1.join(); 157 | thread2.join(); 158 | 159 | std::cout << callChainResult << '\n'; 160 | std::cout << ovResult << '\n'; 161 | std::cout << globalArray[0] << ", " << globalArray[1] << '\n'; 162 | 163 | return 0; 164 | } 165 | 166 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | module.exports = { 13 | parserOptions: { 14 | ecmaVersion: 2022, 15 | project: true, 16 | sourceType: 'module', 17 | tsconfigRootDir: __dirname 18 | }, 19 | settings: { 20 | // Resolve warning (https://github.com/yannickcr/eslint-plugin-react/issues/1955) 21 | react: { 22 | version: 'latest' 23 | } 24 | }, 25 | plugins: ['@typescript-eslint', 'eslint-comments', 'import', 'prettier', 'promise', 'unicorn'], 26 | extends: ['plugin:@typescript-eslint/strict', 'plugin:eslint-comments/recommended', 'plugin:promise/recommended', 'plugin:unicorn/recommended', 'prettier'], 27 | rules: { 28 | // TODO: Temporary during early development, re-enable this once we have logging 29 | 'no-console': 'off', 30 | 31 | // Allow function declaration hoisting 32 | '@typescript-eslint/no-use-before-define': ['error', { functions: false, classes: true, variables: true }], 33 | 34 | // Prefer named exports, regardless of how many exports there are 35 | 'import/prefer-default-export': 'off', 36 | 37 | // Allow referencing devDependencies in tests and development scripts 38 | 'import/no-extraneous-dependencies': [ 39 | 'error', 40 | { 41 | devDependencies: ['gulpfile*.*', 'webpack.*', 'src/test/**/*.ts', 'updateThirdPartyNotices.ts'] 42 | } 43 | ], 44 | 45 | // Allow property names such as `_foo`. 46 | // Rationale: this is the de facto convention for accessor-backing properties. 47 | 'no-underscore-dangle': 'off', 48 | 49 | // Allow `return await`. 50 | // Rationale: explicit `await` makes function part of the callstack, which improves stacktraces. 51 | 'no-return-await': 'off', 52 | 53 | // Allow multiple variables in a single `let` or `var` statement. 54 | // Rationale: it's exactly like function parameters. 55 | 'one-var': 'off', 56 | 57 | // Do not complain about `let ... = undefined`. 58 | // Rationale: while a no-op, it is useful to clarify intent. 59 | 'no-undef-init': 'off', 60 | 'unicorn/no-useless-undefined': 'off', 61 | 62 | // Do not complain about if (!x) and if (x != y). 63 | // Rationale: sometimes negated conditions are more readable. 64 | 'unicorn/no-negated-condition': 'off', 65 | 66 | // Do not complain about (await foo).bar. 67 | // Rationale: simple one-liners can become less readable when broken up. 68 | 'unicorn/no-await-expression-member': 'off', 69 | 70 | radix: 'off', 71 | 72 | // Make Prettier settings lint rules 73 | 'prettier/prettier': ['error'], 74 | 75 | // Prefer specifying return types, but don't require it for inline expressions 76 | '@typescript-eslint/explicit-function-return-type': ['error', { allowExpressions: true }], 77 | 78 | // Similar to 'no-use-before-define' above but disallowing using typedefs before they are declared 79 | '@typescript-eslint/no-use-before-define': ['error', { functions: false, classes: true, variables: true, typedefs: true }], 80 | 81 | '@typescript-eslint/no-explicit-any': 'off', 82 | 83 | // Default is 'kebab-case' but we prefer camel-case 84 | 'unicorn/filename-case': [ 85 | 'error', 86 | { 87 | case: 'camelCase' 88 | } 89 | ], 90 | 91 | 'unicorn/prefer-trim-start-end': 'off', 92 | 93 | // Allow abbreviations (judiciously) 94 | 'unicorn/prevent-abbreviations': 0 95 | } 96 | }; 97 | -------------------------------------------------------------------------------- /src/test/breakpoints.test.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import { expect } from 'chai'; 13 | import { TestUtils } from './testUtils'; 14 | import { CudaDebugClient } from './cudaDebugClient'; 15 | 16 | describe('Breakpoint tests', () => { 17 | let dc: CudaDebugClient; 18 | 19 | beforeEach(async () => { 20 | dc = await TestUtils.launchDebugger('variables/variables'); 21 | }); 22 | 23 | afterEach(async () => { 24 | await dc.stop(); 25 | }); 26 | 27 | it('Breakpoints on kernel source work', async () => { 28 | const bpResp = await dc.setBreakpointsRequest({ 29 | source: TestUtils.getTestSource('variables/variables.cu'), 30 | breakpoints: [ 31 | { 32 | line: 87 33 | } 34 | ] 35 | }); 36 | 37 | expect(bpResp.body.breakpoints.length).eq(1); 38 | expect(bpResp.body.breakpoints[0].verified).eq(true); 39 | 40 | await dc.configurationDoneRequest(); 41 | await TestUtils.assertStoppedLocation(dc, 'breakpoint', 'variables.cu', 87); 42 | }); 43 | 44 | it('Breakpoints on kernel functions work', async () => { 45 | expect(dc.capabilities.supportsFunctionBreakpoints).eq(true); 46 | 47 | const bpResp = await dc.setFunctionBreakpointsRequest({ 48 | breakpoints: [ 49 | { 50 | name: 'cudaComputeHash' 51 | } 52 | ] 53 | }); 54 | 55 | expect(bpResp.body.breakpoints.length).eq(1); 56 | expect(bpResp.body.breakpoints[0].verified).eq(true); 57 | 58 | await dc.configurationDoneRequest(); 59 | await TestUtils.assertStoppedLocation(dc, 'function breakpoint', 'variables.cu', 112); 60 | }); 61 | 62 | it('Can step (over) through source lines', async () => { 63 | const bpResp = await dc.setBreakpointsRequest({ 64 | source: TestUtils.getTestSource('variables/variables.cu'), 65 | breakpoints: [ 66 | { 67 | line: 87 68 | } 69 | ] 70 | }); 71 | 72 | expect(bpResp.body.breakpoints.length).eq(1); 73 | expect(bpResp.body.breakpoints[0].verified).eq(true); 74 | 75 | await dc.configurationDoneRequest(); 76 | 77 | let { threadId } = await TestUtils.assertStoppedLocation(dc, 'breakpoint', 'variables/variables.cu', 87); 78 | 79 | const expectedLineNumbers = [88, 90, 92, 93]; 80 | 81 | // We use a for-loop here because: 82 | // -- forEach will run the iterations in parallel 83 | // -- for-of will require regenerating iterators, which results in a different eslint warning. 84 | // eslint-disable-next-line unicorn/no-for-loop 85 | for (let i = 0; i < expectedLineNumbers.length; i += 1) { 86 | // eslint-disable-next-line no-await-in-loop 87 | await dc.nextRequest({ threadId }); 88 | // eslint-disable-next-line no-await-in-loop 89 | ({ threadId } = await TestUtils.assertStoppedLocation(dc, 'step', 'variables/variables.cu', expectedLineNumbers[i])); 90 | } 91 | }); 92 | 93 | it('Conditional breakpoints work', async () => { 94 | const variablesSource = 'variables/variables.cu'; 95 | const bpLine = 102; 96 | 97 | const bpResp = await dc.setBreakpointsRequest({ 98 | source: TestUtils.getTestSource(variablesSource), 99 | breakpoints: [ 100 | { 101 | line: bpLine, 102 | condition: 'i == 2' 103 | } 104 | ] 105 | }); 106 | 107 | expect(bpResp.body.breakpoints.length).eq(1); 108 | expect(bpResp.body.breakpoints[0].verified).eq(true); 109 | 110 | await dc.configurationDoneRequest(); 111 | await TestUtils.verifyLocalsOnStop(dc, variablesSource, bpLine, 'breakpoint', [{ name: 'i', value: '2' }]); 112 | }); 113 | }); 114 | -------------------------------------------------------------------------------- /src/test/registers.test.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import { expect } from 'chai'; 13 | import { TestUtils } from './testUtils'; 14 | 15 | describe('Register tests', () => { 16 | it('Device registers are shown correctly', async () => { 17 | const dc = await TestUtils.launchDebugger('variables/variables'); 18 | 19 | const bpResp = await dc.setBreakpointsRequest({ 20 | source: TestUtils.getTestSource('variables/variables.cu'), 21 | breakpoints: [ 22 | { 23 | line: 87 24 | } 25 | ] 26 | }); 27 | 28 | expect(bpResp.body.breakpoints.length).eq(1); 29 | expect(bpResp.body.breakpoints[0].verified).eq(true); 30 | 31 | await dc.configurationDoneRequest(); 32 | const { frameId } = await TestUtils.assertStoppedLocation(dc, 'breakpoint', 'variables.cu', 87); 33 | 34 | const scopesResp = await dc.scopesRequest({ frameId }); 35 | const { scopes } = scopesResp.body; 36 | 37 | const registersScope = scopes.find((s) => s.name === 'Registers'); 38 | 39 | // eslint-disable-next-line @typescript-eslint/no-unused-expressions 40 | expect(registersScope).exist; 41 | 42 | if (!registersScope) { 43 | await dc.stop(); 44 | return; 45 | } 46 | 47 | let variablesResp = await dc.variablesRequest({ variablesReference: registersScope?.variablesReference }); 48 | let { variables } = variablesResp.body; 49 | 50 | const sassRegGroup = variables.find((v) => v.name === 'SASS'); 51 | 52 | // eslint-disable-next-line @typescript-eslint/no-unused-expressions 53 | expect(sassRegGroup).exist; 54 | 55 | if (!sassRegGroup) { 56 | await dc.stop(); 57 | return; 58 | } 59 | 60 | variablesResp = await dc.variablesRequest({ variablesReference: sassRegGroup?.variablesReference }); 61 | 62 | variables = variablesResp.body.variables; 63 | 64 | const r0Found = variables.some((v) => v.name === 'R0'); 65 | 66 | // eslint-disable-next-line @typescript-eslint/no-unused-expressions 67 | expect(r0Found).true; 68 | 69 | await dc.stop(); 70 | }); 71 | 72 | it('Machine registers are shown correctly', async () => { 73 | const dc = await TestUtils.launchDebugger('registers/registers_test'); 74 | 75 | const bpResp = await dc.setBreakpointsRequest({ 76 | source: TestUtils.getTestSource('registers/registers_test.c'), 77 | breakpoints: [ 78 | { 79 | line: 16 80 | } 81 | ] 82 | }); 83 | 84 | expect(bpResp.body.breakpoints.length).eq(1); 85 | expect(bpResp.body.breakpoints[0].verified).eq(true); 86 | 87 | await dc.configurationDoneRequest(); 88 | const { frameId } = await TestUtils.assertStoppedLocation(dc, 'breakpoint', 'registers_test.c', 16); 89 | 90 | const scopesResp = await dc.scopesRequest({ frameId }); 91 | const { scopes } = scopesResp.body; 92 | 93 | const registersScope = scopes.find((s) => s.name === 'Registers'); 94 | 95 | // eslint-disable-next-line @typescript-eslint/no-unused-expressions 96 | expect(registersScope).exist; 97 | 98 | if (!registersScope) { 99 | return; 100 | } 101 | 102 | const variablesResp = await dc.variablesRequest({ variablesReference: registersScope?.variablesReference }); 103 | const { variables } = variablesResp.body; 104 | 105 | const registersMap = new Map(variables.map((v) => [v.name, v.value])); 106 | 107 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 108 | expect(Number.parseInt(registersMap.get('rcx')!)).eq(21); 109 | 110 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 111 | expect(Number.parseInt(registersMap.get('rdx')!)).eq(13); 112 | 113 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 114 | expect(Number.parseInt(registersMap.get('dx')!)).eq(13); 115 | 116 | // eslint-disable-next-line @typescript-eslint/no-non-null-assertion 117 | expect(Number.parseInt(registersMap.get('dh')!)).eq(0); 118 | 119 | await dc.stop(); 120 | }); 121 | }); 122 | -------------------------------------------------------------------------------- /prepare.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // The contents of this file are a trimmed down version of the (outstanding) 4 | // relative-deps project, that focus on a reduced set of (dev) depenedencies 5 | // with modifications and additions to tailor it to our specific use case. 6 | // 7 | // The relative-deps lives at the following address on GitHub: 8 | // https://github.com/mweststrate/relative-deps 9 | // 10 | // A link to the LICENSE file for the project is here: 11 | // https://github.com/mweststrate/relative-deps/blob/master/LICENSE 12 | // 13 | // The contents of the license at the time of this writing are listed below: 14 | // 15 | // ------------------------ 16 | // 17 | // MIT License 18 | // 19 | // Copyright (c) 2019 Michel Weststrate 20 | // 21 | // Permission is hereby granted, free of charge, to any person obtaining a copy 22 | // of this software and associated documentation files (the "Software"), to deal 23 | // in the Software without restriction, including without limitation the rights 24 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | // copies of the Software, and to permit persons to whom the Software is 26 | // furnished to do so, subject to the following conditions: 27 | // 28 | // The above copyright notice and this permission notice shall be included in all 29 | // copies or substantial portions of the Software. 30 | // 31 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | // SOFTWARE. 38 | // 39 | // ------------------------ 40 | // 41 | // The contents of this file follow the same (MIT) license described above. 42 | 43 | 44 | const path = require('path'); 45 | const fs = require('fs'); 46 | const spawnSync = require('child_process').spawnSync; 47 | const globby = require('globby'); 48 | const rimraf = require('rimraf'); 49 | const tar = require('tar'); 50 | 51 | const cgaPackageName = 'cdt-gdb-adapter'; 52 | const pathToCGA = path.resolve(process.cwd(), 'cdt-gdb-adapter'); 53 | 54 | if (!fs.existsSync(pathToCGA)) { 55 | console.error('cdt-gdb-adapter not found.'); 56 | process.exit(1); 57 | } 58 | 59 | // Check to see if there has been any changes requiring a rebuild. 60 | 61 | const timestampsFile = path.resolve(process.cwd(), 'node_modules', cgaPackageName, '.prepare_timestamps'); 62 | const oldTimestamps = fs.existsSync(timestampsFile) ? fs.readFileSync(timestampsFile, 'utf8') : ''; 63 | 64 | const files = globby 65 | .sync(['**/*', '!node_modules', '!.git'], { 66 | gitignore: true, 67 | cwd: pathToCGA, 68 | nodir: true 69 | }) 70 | .sort(); 71 | 72 | const mtimes = []; 73 | for (let file of files) mtimes.push(fs.statSync(path.resolve(pathToCGA, file)).mtime.getTime()); 74 | const newTimestamps = files.map((file, index) => mtimes[index] + ' ' + file).join('\n'); 75 | 76 | if (newTimestamps === oldTimestamps) { 77 | console.log('[prepare] No changes.'); 78 | return; 79 | } 80 | 81 | let debugRemoveThisLater = 0; 82 | yarn = (...args) => { 83 | if (debugRemoveThisLater !== 0) { 84 | console.log(`args: ${args.join(' ')}`); 85 | return; 86 | } 87 | const result = spawnSync('yarn', args, { 88 | cwd: pathToCGA, 89 | args: args, 90 | stdio: [0, 1, 2] 91 | }); 92 | 93 | if (result.error) throw new Error(`yarn failed with arguments '${command} ${args.join(' ')}'.`); 94 | }; 95 | 96 | // Run install if never done before 97 | if (!fs.existsSync(path.join(pathToCGA, 'node_modules'))) { 98 | console.log(`[prepare] Running 'install' in ${pathToCGA}`); 99 | yarn('install'); 100 | } 101 | 102 | // Run build script if present 103 | const packageJson = JSON.parse(fs.readFileSync(path.join(pathToCGA, 'package.json'), 'utf8')); 104 | if (packageJson.scripts && packageJson.scripts.build) { 105 | console.log(`[prepare] Building ${cgaPackageName} in ${pathToCGA}`); 106 | yarn('run', 'build'); 107 | } 108 | 109 | // Pack and locally install the package. 110 | const destDir = path.join(process.cwd(), 'node_modules', cgaPackageName); 111 | let pathToTarFile; 112 | try { 113 | console.log('[prepare] Copying to local node_modules'); 114 | 115 | yarn('pack'); 116 | 117 | if (fs.existsSync(destDir)) { 118 | rimraf.sync(destDir); 119 | } 120 | 121 | fs.mkdirSync(destDir, { recursive: true }); 122 | 123 | const packagedName = fs.readdirSync(pathToCGA).find((file) => file.startsWith(cgaPackageName)); 124 | if (!packagedName) { 125 | console.error('Package tar file not found.'); 126 | process.exit(1); 127 | } 128 | 129 | pathToTarFile = path.join(pathToCGA, packagedName); 130 | 131 | console.log(`[prepare] Extracting "${pathToTarFile}" to ${destDir}`); 132 | 133 | tar.extract({ 134 | cwd: path.relative(process.cwd(), destDir), 135 | file: path.relative(process.cwd(), pathToTarFile), 136 | gzip: true, 137 | stripComponents: 1, 138 | sync: true 139 | }); 140 | } finally { 141 | if (pathToTarFile) { 142 | fs.unlinkSync(pathToTarFile); 143 | } 144 | } 145 | 146 | fs.writeFileSync(timestampsFile, newTimestamps); 147 | console.log(`[prepare] Done.`); 148 | -------------------------------------------------------------------------------- /src/test/testPrograms/variables/variables.cu: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | 32 | #if defined(assert) 33 | #undef assert 34 | #endif 35 | 36 | #define assert(c) \ 37 | do { \ 38 | if(!(c)) { \ 39 | fprintf(stderr, "Assertion \"%s\" failed. (%s:%d)\n", \ 40 | #c, __FILE__, __LINE__); \ 41 | exit(1); \ 42 | } \ 43 | } while(0) 44 | 45 | #define assertSucceeded(c) \ 46 | do { \ 47 | unsigned __tmp = c; \ 48 | if(__tmp != cudaSuccess) { \ 49 | fprintf(stderr, "Operation \"%s\" failed with error code %x. (%s:%d)\n", \ 50 | #c, (__tmp), __FILE__, __LINE__); \ 51 | exit(__tmp); \ 52 | } \ 53 | } while(0) 54 | 55 | #define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0])) 56 | 57 | constexpr int dataLength = 1 << 5; 58 | constexpr int threadsPerBlock = 1 << 5; 59 | 60 | typedef unsigned char byte; 61 | 62 | struct TestType 63 | { 64 | union { 65 | struct 66 | { 67 | unsigned lowHalf; 68 | unsigned highHalf; 69 | } halfAndHalf; 70 | 71 | unsigned long long whole; 72 | } takeYourPick; 73 | 74 | int arr[5]; 75 | 76 | struct { 77 | char a; 78 | char b; 79 | } structArr[5]; 80 | 81 | float theFloats[2]; 82 | double theDouble; 83 | }; 84 | 85 | __device__ void cudaComputeHashInner(TestType* input, unsigned *results) 86 | { 87 | int idx = blockIdx.x * threadsPerBlock + threadIdx.x; 88 | TestType* myInput = input + idx; 89 | 90 | unsigned myResult = 0; 91 | 92 | myResult += myInput->takeYourPick.halfAndHalf.lowHalf - idx; 93 | myResult += myInput->takeYourPick.halfAndHalf.highHalf - idx; 94 | 95 | for(size_t i = 0; i < ARRAY_LENGTH(myInput->arr); i++) 96 | { 97 | myResult += myInput->arr[i] - idx; 98 | } 99 | 100 | for(size_t i = 0; i < sizeof(myInput->structArr); i++) 101 | { 102 | myResult += reinterpret_cast(myInput->structArr)[i] - '0'; 103 | } 104 | 105 | __syncthreads(); 106 | 107 | results[idx] = myResult; 108 | } 109 | 110 | __global__ void cudaComputeHash(TestType* input, unsigned *results) 111 | { 112 | int idx = blockIdx.x * threadsPerBlock + threadIdx.x; 113 | /*@deviceCall*/ cudaComputeHashInner(input, results); 114 | results[idx] += 1; 115 | } 116 | 117 | int main() 118 | { 119 | int cudaDeviceCount; 120 | assertSucceeded(cudaGetDeviceCount(&cudaDeviceCount)); 121 | assert(cudaDeviceCount > 0); 122 | 123 | assertSucceeded(cudaSetDevice(0)); 124 | 125 | TestType* input; 126 | unsigned* results; 127 | 128 | assertSucceeded(cudaMallocManaged(&input, sizeof(TestType) * dataLength)); 129 | assert(!!input); 130 | 131 | for (size_t i = 0; i < dataLength; i++) 132 | { 133 | input[i].takeYourPick.halfAndHalf.lowHalf = i + 1; 134 | input[i].takeYourPick.halfAndHalf.highHalf = i + 3; 135 | 136 | for(size_t j = 0; j < ARRAY_LENGTH(input[i].arr); j++) 137 | { 138 | input[i].arr[j] = i + j + 2; 139 | } 140 | 141 | for(size_t j = 0; j < sizeof(input[i].structArr); j++) 142 | { 143 | reinterpret_cast(input[i].structArr)[j] = '0' + static_cast((i + j) % 10); 144 | } 145 | 146 | input[i].theFloats[0] = i + 1; 147 | input[i].theFloats[1] = input[i].theFloats[0] / 2; 148 | 149 | input[i].theDouble = input[i].theFloats[1] + 1; 150 | } 151 | 152 | assertSucceeded(cudaMallocManaged(reinterpret_cast(&results), sizeof(unsigned) * dataLength)); 153 | assert(!!results); 154 | 155 | constexpr int blocks = dataLength / threadsPerBlock; 156 | cudaComputeHash<<>>(input, results); 157 | 158 | assertSucceeded(cudaDeviceSynchronize()); 159 | 160 | const unsigned expectedResult = 161 | 1 + 162 | 3 + 163 | ARRAY_LENGTH(input[0].arr) * (ARRAY_LENGTH(input[0].arr) - 1) / 2 + 164 | ARRAY_LENGTH(input[0].arr) * 2 + 165 | sizeof(input[0].structArr) * (sizeof(input[0].structArr) - 1) / 2 + 166 | 1; // Added by cudaComputeHash (rather than by cudaComputeHashInner) 167 | 168 | for (unsigned i = 0; i < dataLength; i++) 169 | { 170 | if (results[i] != expectedResult){ 171 | fprintf(stderr, "results[%u] (%u) != %u\n", i, results[i], expectedResult); 172 | exit(1); 173 | } 174 | } 175 | 176 | assertSucceeded(cudaFree(input)); 177 | assertSucceeded(cudaFree(results)); 178 | 179 | fprintf(stderr, "Success\n"); 180 | 181 | exit(0); 182 | } 183 | -------------------------------------------------------------------------------- /src/debugger/cudaQnxGdbServerSession.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | /* eslint-disable no-param-reassign */ 12 | /* eslint-disable max-classes-per-file */ 13 | import { DebugProtocol } from '@vscode/debugprotocol'; 14 | import { GDBBackend, GDBTargetDebugSession } from 'cdt-gdb-adapter'; 15 | import { ChildProcess } from 'node:child_process'; 16 | import { EventEmitter } from 'node:events'; 17 | import { logger, OutputEvent, TerminatedEvent } from '@vscode/debugadapter'; 18 | import { CudaGdbSession, CudaGdbBackend } from './cudaGdbSession'; 19 | import { CudaTargetLaunchRequestArguments, CudaTargetAttachRequestArguments } from './cudaGdbServerSession'; 20 | 21 | class CudaQNXGdbServerBackend extends CudaGdbBackend { 22 | async spawn(args: CudaTargetAttachRequestArguments): Promise { 23 | await super.spawn(args); 24 | } 25 | } 26 | 27 | export class CudaQnxGdbServerSession extends CudaGdbSession { 28 | private readonly gdbTargetDebugSession: GDBTargetDebugSession = new GDBTargetDebugSession(); 29 | 30 | protected gdbserver?: ChildProcess; 31 | 32 | protected isInitialized = false; 33 | 34 | protected async setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): Promise { 35 | await (this.gdbTargetDebugSession as any).setBreakPointsRequest.call(this, response, args); 36 | } 37 | 38 | protected createBackend(): GDBBackend { 39 | const backend: CudaGdbBackend = new CudaQNXGdbServerBackend(this); 40 | const emitter: EventEmitter = backend as EventEmitter; 41 | 42 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 43 | emitter.on(CudaGdbBackend.eventCudaGdbExit, (code: number, signal: string) => { 44 | if (code === CudaGdbSession.codeModuleNotFound) { 45 | this.sendEvent(new OutputEvent('Failed to find cuda-gdb or a dependent library.')); 46 | this.sendEvent(new TerminatedEvent()); 47 | } 48 | }); 49 | 50 | return backend; 51 | } 52 | 53 | public async spawn(args: CudaTargetAttachRequestArguments): Promise { 54 | await (this.gdbTargetDebugSession as any).spawn.call(this, args); 55 | } 56 | 57 | protected setupCommonLoggerAndHandlers(args: CudaTargetAttachRequestArguments): void { 58 | (this.gdbTargetDebugSession as any).setupCommonLoggerAndHandlers.call(this, args); 59 | } 60 | 61 | /** 62 | * It is intentional that this function overrides the base class implementation 63 | */ 64 | 65 | protected async launchRequest(response: DebugProtocol.LaunchResponse, args: CudaTargetLaunchRequestArguments): Promise { 66 | logger.verbose('Executing launch request'); 67 | 68 | this.initializeLogger(args); 69 | 70 | let ok = await this.validateLinuxPlatform(response); 71 | if (!ok) { 72 | // Error response sent within validateLinuxPlatform 73 | return; 74 | } 75 | 76 | const cdtLaunchArgs: CudaTargetAttachRequestArguments = { ...args }; 77 | 78 | // Assume true for isQNX in the QNX server session 79 | const isQNX = true; 80 | 81 | ok = await this.runConfigureLaunch(response, args, cdtLaunchArgs, 'LaunchRequest'); 82 | if (!ok) { 83 | // Error response sent within runConfigureLaunch 84 | return; 85 | } 86 | 87 | // This also sets the path if found 88 | ok = await this.validateAndSetCudaGdbPath(response, cdtLaunchArgs, isQNX); 89 | if (!ok) { 90 | // Error response sent within validateAndSetCudaGdbPath 91 | return; 92 | } 93 | 94 | logger.verbose('Calling launch request in super class'); 95 | await (this.gdbTargetDebugSession as any).launchRequest.call(this, response, cdtLaunchArgs); 96 | } 97 | 98 | /* eslint-disable @typescript-eslint/no-unused-vars */ 99 | // eslint-disable-next-line class-methods-use-this 100 | protected async startGDBServer(args: CudaTargetLaunchRequestArguments): Promise { 101 | // This function is defined so that we do not inadvertently call cdt-gdb-adapter's implementation of this function 102 | } 103 | /* eslint-enable @typescript-eslint/no-unused-vars */ 104 | 105 | protected attachOrLaunchRequest(response: DebugProtocol.Response, request: 'launch' | 'attach', args: CudaTargetLaunchRequestArguments): Promise { 106 | return (this.gdbTargetDebugSession as any).attachOrLaunchRequest.call(this, response, request, args, true); 107 | } 108 | 109 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 110 | protected async startGDBAndAttachToTarget(response: DebugProtocol.AttachResponse | DebugProtocol.LaunchResponse, args: CudaTargetAttachRequestArguments, isQNX = true): Promise { 111 | await (this.gdbTargetDebugSession as any).startGDBAndAttachToTarget.call(this, response, args, true); 112 | } 113 | } 114 | 115 | /* eslint-enable max-classes-per-file */ 116 | /* eslint-enable no-param-reassign */ 117 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Nsight Visual Studio Code Edition 2 | 3 | #### [Overview](https://developer.nvidia.com/nsight-visual-studio-code-edition)  |  [Documentation](https://docs.nvidia.com/nsight-visual-studio-code-edition/)  |  [Forum](https://forums.developer.nvidia.com/c/developer-tools/nsight-vscode-edition)  |  [Code Samples](https://github.com/NVIDIA/cuda-samples) 4 | 5 | Nsight Visual Studio Code edition is an extension for 6 | [Visual Studio Code](https://code.visualstudio.com/) that 7 | provides support for [CUDA](https://developer.nvidia.com/cuda-zone) 8 | development, including features such as Intellisense, debugging, debugger views, 9 | and productivity enhancements. 10 | 11 | ## Benefits 12 | 13 | * **Higher Productivity**
14 | Using smart CUDA auto-code completion features improves the overall 15 | development experience and enables users to save time and effort when writing 16 | code. 17 | 18 | * **Interactivity**
19 | Debugging with Nsight Visual Studio Code Edition provides diverse benefits, 20 | including code formatting, easy navigation through source code, displaying and 21 | interacting with different source files, building executables, and testing. 22 | 23 | * **Remote Development Support**
24 | Nsight Visual Studio Code Edition enables developers to implement CUDA code in 25 | various cluster environments such as Virtual Machines or remote Docker 26 | containers. It also supports code development for Linux systems via the Remote 27 | – WSL extension. 28 | 29 | * **Free**
30 | As with other Nsight tools from NVIDIA, Nsight Visual Studio Code Edition is 31 | offered free of charge. We love it when your code works better and is 32 | delivered sooner. Enjoy! 33 | 34 | ## Key Features 35 | 36 | * **CUDA Syntax Highlighting for Code Development and Debugging**
37 | Edit code productively with syntax highlighting and IntelliSense for CUDA 38 | code. Auto-completion, go to definition, find references, rename symbols, and 39 | more all seamlessly work for kernel functions the same as they do for C++ 40 | functions. 41 | 42 | * **CUDA Kernel Breakpoint Support and Kernel Execution Control**
43 | Break into a debugging session in CPU or GPU device code using standard 44 | breakpoints, including support for conditional breakpoints with expression 45 | evaluation. GUI controls allow you to step over, into, or out of statements in 46 | the source code, just like normal CPU debugging. Breakpoints are evaluated for 47 | every kernel thread and will halt execution when any thread encounters them. 48 | 49 | * **GPU and Kernel State Inspection**
50 | Break into the debugger to see all the detailed information at any point in 51 | the application with GPU debugging support for register, variable, and 52 | call-stack. There is watchlist support to add specific variables of interest 53 | for tracking. Additionally, there are focus controls to manually select 54 | block and thread coordinates to switch the debugger 55 | focus. 56 | 57 | ## Want to know more? 58 | 59 | * **See the Nsight VSCode Edition spotlight video**
60 | This [Nsight VSCode Edition 61 | spotlight](https://www.youtube.com/watch?v=gN3XeFwZ4ng) shows you how Nsight 62 | VSCode Edition fits in with the other NVIDIA IDE debuggers and can be set up 63 | in Microsoft's Visual Studio Code. Then, you'll see all the key features in 64 | action. You're going to love it! 65 | 66 | * **See Nsight VSCode Edition demonstrated at GTC'21**
67 | [GTC'21 Video On Demand: Latest Enhancements to CUDA Debugger IDEs](https://gtc21.event.nvidia.com/media/Latest%20Enhancements%20to%20CUDA%20Debugger%20IDEs%20%5BS31884%5D/1_geie6h11) 68 | 69 | * **View the Microsoft announcement for the VSCode extension by Nsight VSCE**
70 | [CUDA Support in Visual Studio Code with Julia Reid](https://www.youtube.com/watch?v=l6PgYhiQr-I&list=PLReL099Y5nRcWPNnKO4cwxN5RJZl9A48P&index=4) 71 | 72 | * **Read the blog posting**
73 | [Announcing NVIDIA Nsight Visual Studio Code Edition: New Addition to the Nsight Developer Tools Suite](https://developer.nvidia.com/blog/announcing-nvidia-nsight-visual-studio-code-edition-new-addition-to-the-nsight-developer-tools-suite/) 74 | 75 | * **Visit the Nsight VSCode Edition overview page**
76 | The [Nsight VSCode Edition overview 77 | page](https://developer.nvidia.com/nsight-visual-studio-code-edition) is your 78 | information hub for general information, availability, videos, and other links 79 | to other NVIDIA tools for GPU code development. 80 | 81 | ## Requirements 82 | 83 | * **[CUDA Toolkit](https://developer.nvidia.com/cuda-toolkit)**: Install the CUDA Toolkit to get important tools for 84 | CUDA application development including the 85 | [NVCC compiler driver](https://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html) and 86 | [cuda-gdb](https://docs.nvidia.com/cuda/cuda-gdb/index.html), the NVIDIA tool for debugging CUDA. 87 | 88 | * **[Microsoft vscode-cpptools](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools)**: 89 | Install Microsoft's C/C++ for Visual Studio Code to get Intellisense support for CUDA C++ code. 90 | Nsight VS Code Edition will automatically install this extension. 91 | 92 | ## Quick Start 93 | 94 | **Open or create a new CUDA application.**
95 | **Configure the debugging connection**
96 | by creating a [launch configuration](https://docs.nvidia.com/nsight-visual-studio-code-edition/cuda-debugger/index.html#walkthrough-create-launch-config) to launch and debug your application, or
97 | an [attach configuration](https://docs.nvidia.com/nsight-visual-studio-code-edition/cuda-debugger/index.html#walkthrough-attach-create-launch-config) if the target application is already running 98 | 99 | ![Create launch configuration](nsight-debug-config.gif) 100 | 101 | **Start debugging!** 102 | 103 | ![Start debugging](nsight-debug.gif) 104 | 105 | ## Support 106 | Reach out to us for feedback and questions via [our developer forum](https://forums.developer.nvidia.com/c/development-tools/nsight-vscode-edition/). 107 | 108 | ## Data and telemetry 109 | 110 | This extension collects usage data and sends it to NVIDIA to help improve our products. This 111 | extension respects the `"telemetry.enableTelemetry"` setting, for more info see 112 | [Visual Studio Code Telemetry](https://code.visualstudio.com/docs/getstarted/telemetry). -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Nsight Visual Studio Code Edition Changelog 2 | 3 | 4 | ## Version 2025.1 5 | 6 | * **General Enhancements** 7 | * Arguments to debuggeed process (`args` in launch.json) can now be an array of strings rather than a single string. 8 | 9 | * CPU threads, stack frames, and variables can be fully inspected while stopped in GPU code, and vice versa. 10 | 11 | * **CUDA Debugger** 12 | * See cuda-gdb release notes in the NVIDIA CUDA Toolkit 12.6 Update 2. 13 | 14 | ## Version 2024.1 15 | 16 | * **General Enhancements** 17 | * Added ability to pass custom arguments in debugger using miDebuggerArgs. 18 | 19 | * Added ability to specify path to debugger using miDebuggerPath in addition to previously available debuggerPath. 20 | 21 | * Line cursor now changes accordingly when switching focus between blocks/threads via the Debug console. 22 | 23 | * **CUDA Debugger** 24 | * See cuda-gdb release notes in the NVIDIA CUDA Toolkit 12.5 Update 1. 25 | 26 | ## Version 2023.2 27 | 28 | * **Key Features** 29 | 30 | * Added support for remote debugging (via cuda-gdbserver) an application running on L4T. 31 | 32 | * Added support for remote debugging (via cuda-gdbserver) an application running on QNX. 33 | 34 | * Added five autostart tasks which help users easily set up and instantiate remote debugging sessions on L4T and QNX platforms. 35 | 36 | 37 | * **General Enhancements** 38 | 39 | * Added the ability to set a SOLibSearchPath for the debugging session. 40 | 41 | * Added the ability to set environment variables before cuda-gdb/cuda-qnx-gdb is launched. 42 | 43 | * **CUDA Debugger** See cuda-gdb release notes in the NVIDIA CUDA Toolkit 12.1. 44 | 45 | ## Version 2023.1 46 | 47 | * **Important Fixes** 48 | 49 | * Fixed an issue where Nsight VSCode Edition was not able to set breakpoints 50 | and debug in delayed module load scenarios (for example, in CUDA Driver 51 | API applications). 52 | 53 | * Fixed a bug where read for ptrace_scope was attempted on systems where 54 | it was not present. 55 | 56 | * **CUDA Debugger** See cuda-gdb release notes in the NVIDIA CUDA Toolkit 12.0 Update 1. 57 | 58 | ## Version 2022.2 59 | 60 | * **General Enhancements** 61 | 62 | * A fix for truncated process names during attach. 63 | 64 | * Support for when PIDs for attach are entered as a string. 65 | 66 | * New warnings for when cuda-gdb is not found in the path. 67 | 68 | * Improvements to the user experience when stepping out of functions during debugging. 69 | 70 | * Various bug fixes and performance improvements. 71 | 72 | * **CUDA Debugger** See cuda-gdb release notes in the NVIDIA CUDA Toolkit 11.7 Update 1. 73 | 74 | ## Version 2022.1 75 | 76 | ### General 77 | 78 | * **Attach to a running CUDA process** It is now possible to attach to a CUDA 79 | application that is already running. It is also possible to detach from the 80 | application before letting it run to completion. When attached, all the usual 81 | features of the debugger are available to the user, as if the application had 82 | been launched from the debugger. This feature is also supported with 83 | applications using Dynamic Parallelism. 84 | 85 | * **Additional Launch Settings** 86 | 87 | * **envFile** Path to a file containing environment variables to set for the 88 | debuggee process. Each line is formatted as either: 89 | 90 | * KEY=VALUE 91 | * unset KEY 92 | 93 | * **initCommands** Provide an array of cuda-gdb commands to run before 94 | debugging is started. 95 | 96 | * **stopAtEntry** If true, the debugger should stop at the entry points of the debuggee. 97 | 98 | * **cwd** Set current working directory for debuggee. 99 | 100 | * **Security Updates** We've updated the vscode npm packages to the latest 101 | versions to address known vulnerabilities. 102 | 103 | * **CUDA Debugger** See cuda-gdb release notes in the NVIDIA CUDA Toolkit 11.6 Update 2. 104 | 105 | ## Version 2021.1 106 | 107 | We would like to introduce our newest developer tool for CUDA kernel debugging, 108 | NVIDIA Nsight™ Visual Studio Code Edition. NVIDIA Nsight™ Visual Studio Code 109 | Edition (VSCE) is an application development environment for heterogeneous 110 | platforms that brings CUDA® development for GPUs into Microsoft Visual Studio 111 | Code. NVIDIA Nsight™ VSCE enables you to build and debug GPU kernels and native 112 | CPU code as well as inspect the state of the GPU and memory. 113 | 114 | ### Benefits 115 | 116 | * **Higher Productivity** Using smart CUDA auto-code completion features 117 | improves the overall development experience and allows users to save time and 118 | effort when writing code. 119 | 120 | * **Interactivity** Debugging with Nsight Visual Studio Code Edition provides 121 | i diverse benefits, including code formatting, easy navigation through source 122 | code, displaying and interacting with different source files, building 123 | executables, and testing. 124 | 125 | * **Remote Development Support** Nsight Visual Studio Code Edition allows 126 | developers to implement CUDA code in various cluster environments such as 127 | Virtual Machines or remote Docker containers. It also supports code 128 | development for Linux systems via the Remote – WSL extension. 129 | 130 | * **Free** As with other Nsight tools from NVIDIA, Nsight Visual Studio Code 131 | Edition is offered free of charge. We love it when your code works better and 132 | is delivered sooner. Enjoy! 133 | 134 | ### Key Features 135 | 136 | * **CUDA Syntax Highlighting for Code Development and Debugging** Edit code 137 | productively with syntax highlighting and IntelliSense for CUDA code. 138 | Auto-completion, go to definition, find references, rename symbols, and more 139 | all seamlessly work for kernel functions the same as they do for C++ 140 | functions. 141 | 142 | * **CUDA Kernel Breakpoint Support and Kernel Execution Control** Break into a 143 | debugging session in CPU or GPU device code using standard breakpoints, 144 | including support for conditional breakpoints with expression evaluation. GUI 145 | controls allow you to step over, into, or out of statements in the source 146 | code, just like normal CPU debugging. Breakpoints are evaluated for every 147 | kernel thread and will halt execution when any thread encounters them. 148 | 149 | * **GPU and Kernel State Inspection** Break into the debugger to see all the 150 | detailed information at any point in the application with GPU debugging 151 | support for register, variable, and call-stack. There is watchlist support to 152 | add specific variables of interest for tracking. Additionally, there are focus 153 | controls to manually select CUDA block and thread numbers to switch the 154 | debugger focus. 155 | -------------------------------------------------------------------------------- /src/debugger/cudaGdbServerSession.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-param-reassign */ 2 | /* ---------------------------------------------------------------------------------- *\ 3 | | | 4 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 5 | | | 6 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 7 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 8 | | | 9 | | SPDX-License-Identifier: EPL-2.0 | 10 | | | 11 | \* ---------------------------------------------------------------------------------- */ 12 | /* eslint-disable max-classes-per-file */ 13 | import { DebugProtocol } from '@vscode/debugprotocol'; 14 | import { GDBBackend, GDBTargetDebugSession } from 'cdt-gdb-adapter'; 15 | import { ChildProcess } from 'node:child_process'; 16 | import { logger, OutputEvent, TerminatedEvent } from '@vscode/debugadapter'; 17 | import { EventEmitter } from 'node:events'; 18 | import { CudaGdbSession, CudaLaunchRequestArguments, CudaGdbBackend } from './cudaGdbSession'; 19 | 20 | export interface ImageAndSymbolArguments { 21 | symbolFileName?: string; 22 | symbolOffset?: string; 23 | imageFileName?: string; 24 | imageOffset?: string; 25 | } 26 | 27 | export interface CudaTargetAttachArguments { 28 | type?: string; 29 | parameters?: string[]; 30 | host?: string; 31 | port?: string; 32 | connectCommands?: string[]; 33 | } 34 | 35 | export interface CudaTargetLaunchArguments extends CudaTargetAttachArguments { 36 | serverParameters?: string[]; 37 | server?: string; 38 | serverPortRegExp?: string; 39 | cwd?: string; 40 | serverStartupDelay?: number; 41 | } 42 | 43 | export interface CudaTargetAttachRequestArguments extends CudaLaunchRequestArguments { 44 | server?: string; 45 | target?: CudaTargetAttachArguments; 46 | imageAndSymbols?: ImageAndSymbolArguments; 47 | preRunCommands?: string[]; 48 | serverParameters?: string[]; 49 | sysroot?: string; 50 | } 51 | 52 | export interface CudaTargetLaunchRequestArguments extends CudaTargetAttachRequestArguments { 53 | server?: string; 54 | target?: CudaTargetLaunchArguments; 55 | imageAndSymbols?: ImageAndSymbolArguments; 56 | preRunCommands?: string[]; 57 | serverParameters?: string[]; 58 | sysroot?: string; 59 | } 60 | 61 | class CudaGdbServerBackend extends CudaGdbBackend { 62 | async spawn(args: CudaTargetAttachRequestArguments): Promise { 63 | await super.spawn(args); 64 | } 65 | } 66 | 67 | export class CudaGdbServerSession extends CudaGdbSession { 68 | private readonly gdbTargetDebugSession: GDBTargetDebugSession = new GDBTargetDebugSession(); 69 | 70 | protected gdbserver?: ChildProcess; 71 | 72 | protected async setBreakPointsRequest(response: DebugProtocol.SetBreakpointsResponse, args: DebugProtocol.SetBreakpointsArguments): Promise { 73 | await (this.gdbTargetDebugSession as any).setBreakPointsRequest.call(this, response, args); 74 | } 75 | 76 | protected createBackend(): GDBBackend { 77 | const backend: CudaGdbBackend = new CudaGdbServerBackend(this); 78 | const emitter: EventEmitter = backend as EventEmitter; 79 | 80 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 81 | emitter.on(CudaGdbBackend.eventCudaGdbExit, (code: number, signal: string) => { 82 | if (code === CudaGdbSession.codeModuleNotFound) { 83 | this.sendEvent(new OutputEvent('Failed to find cuda-gdb or a dependent library.')); 84 | this.sendEvent(new TerminatedEvent()); 85 | } 86 | }); 87 | 88 | return backend; 89 | } 90 | 91 | public async spawn(args: CudaTargetAttachRequestArguments): Promise { 92 | await (this.gdbTargetDebugSession as any).spawn.call(this, args); 93 | } 94 | 95 | protected setupCommonLoggerAndHandlers(args: CudaTargetAttachRequestArguments): void { 96 | return (this.gdbTargetDebugSession as any).setupCommonLoggerAndHandlers.call(this, args); 97 | } 98 | 99 | /** 100 | * It is intentional that this function overrides the base class implementation 101 | */ 102 | 103 | protected async launchRequest(response: DebugProtocol.LaunchResponse, args: CudaTargetLaunchRequestArguments): Promise { 104 | logger.verbose('Executing launch request'); 105 | 106 | this.initializeLogger(args); 107 | 108 | let ok = await this.validateLinuxPlatform(response); 109 | if (!ok) { 110 | // Error response sent within validateLinuxPlatform 111 | return; 112 | } 113 | 114 | const cdtLaunchArgs: CudaTargetAttachRequestArguments = { ...args }; 115 | 116 | // Assume false for isQNX in the generic server session 117 | const isQNX = false; 118 | 119 | ok = await this.runConfigureLaunch(response, args, cdtLaunchArgs, 'LaunchRequest'); 120 | if (!ok) { 121 | // Error response sent within runConfigureLaunch 122 | return; 123 | } 124 | 125 | // This also sets the path if found 126 | ok = await this.validateAndSetCudaGdbPath(response, cdtLaunchArgs, isQNX); 127 | if (!ok) { 128 | // Error response sent within validateAndSetCudaGdbPath 129 | return; 130 | } 131 | 132 | // we want to call cdtLaunchArgs because they have all the information we need from args in a type can be used cdt-gdn-adapter's launchRequest 133 | logger.verbose('Calling launch request in super class'); 134 | await (this.gdbTargetDebugSession as any).launchRequest.call(this, response, cdtLaunchArgs); 135 | } 136 | 137 | /* eslint-disable @typescript-eslint/no-unused-vars */ 138 | // eslint-disable-next-line class-methods-use-this 139 | protected async startGDBServer(args: CudaTargetLaunchRequestArguments): Promise { 140 | // This function will be implemented later when we support autostart 141 | // For now this function is defined so that we do not inadvertently call cdt-gdb-adapter's implementation of this function 142 | } 143 | /* eslint-enable @typescript-eslint/no-unused-vars */ 144 | 145 | protected attachOrLaunchRequest(response: DebugProtocol.Response, request: 'launch' | 'attach', args: CudaTargetLaunchRequestArguments): Promise { 146 | return (this.gdbTargetDebugSession as any).attachOrLaunchRequest.call(this, response, request, args, true); 147 | } 148 | 149 | protected async startGDBAndAttachToTarget(response: DebugProtocol.AttachResponse | DebugProtocol.LaunchResponse, args: CudaTargetAttachRequestArguments): Promise { 150 | await (this.gdbTargetDebugSession as any).startGDBAndAttachToTarget.call(this, response, args); 151 | } 152 | } 153 | 154 | /* eslint-enable max-classes-per-file */ 155 | /* eslint-enable no-param-reassign */ 156 | -------------------------------------------------------------------------------- /updateThirdPartyNotices.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'node:fs'; 2 | import path from 'node:path'; 3 | import * as licenseChecker from 'license-checker'; 4 | 5 | const crawlerOverridesFileName = '.crawler-overrides.json'; 6 | 7 | // This section is intentionally manually maintained. When you update a version number 8 | // please also ensure that you look up any additional files that need to be included 9 | // in our third-party-notices.txt file 10 | const additionalFiles = [ 11 | { name: '@vscode/debugadapter', version: '1.67.0', files: ['thirdpartynotices.txt'] }, 12 | { name: '@vscode/debugprotocol', version: '1.67.0', files: [] }, 13 | { name: 'axios', version: '1.7.6', files: [] }, 14 | { name: 'cdt-gdb-adapter', version: '0.0.19', files: [] }, 15 | { name: 'uuid', version: '10.0.0', files: [] }, 16 | { name: 'which', version: '3.0.1', files: [] }, 17 | { name: 'combined-stream', version: '1.0.8', files: [] }, 18 | { name: 'delayed-stream', version: '1.0.0', files: [] }, 19 | { name: 'asynckit', version: '0.4.0', files: [] }, 20 | { name: 'follow-redirects', version: '1.15.6', files: [] }, 21 | { name: 'form-data', version: '4.0.0', files: [] }, 22 | { name: 'fs-extra', version: '11.2.0', files: [] }, 23 | { name: 'graceful-fs', version: '4.2.11', files: [] }, 24 | { name: 'isexe', version: '2.0.0', files: [] }, 25 | { name: 'jsonfile', version: '6.1.0', files: [] }, 26 | { name: 'mime-db', version: '1.52.0', files: [] }, 27 | { name: 'mime-types', version: '2.1.35', files: [] }, 28 | { name: 'proxy-from-env', version: '1.1.0', files: [] }, 29 | { name: 'universalify', version: '2.0.1', files: [] } 30 | ]; 31 | 32 | function getPackageNameAndVersion(packageName: string): { name: string; version: string } { 33 | const match = packageName.match(/(.*)@([\d.]*)/); 34 | return match ? { name: match[1], version: match[2] } : { name: packageName, version: '' }; 35 | } 36 | 37 | function readJsonFile(filePath: string): any { 38 | try { 39 | return JSON.parse(fs.readFileSync(filePath, 'utf8')); 40 | } catch (error) { 41 | console.error(`Failed to read JSON file at ${filePath}: ${(error as Error).message}`); 42 | throw error; 43 | } 44 | } 45 | 46 | async function fetchLicenseContent(licenseFile: string): Promise { 47 | if (licenseFile.toLowerCase().startsWith('http://') || licenseFile.toLowerCase().startsWith('https://')) { 48 | const fetch = await import('node-fetch'); 49 | const response = await fetch.default(licenseFile); 50 | return await response.text(); 51 | } else { 52 | return fs.readFileSync(licenseFile, 'utf8'); 53 | } 54 | } 55 | 56 | function crawlLicenseInformation(workspaceFolder: string, mainPackage: string): Promise { 57 | return new Promise((resolve, reject) => { 58 | licenseChecker.init( 59 | { 60 | start: workspaceFolder, 61 | production: true, 62 | development: false, 63 | direct: false, 64 | unknown: true, 65 | excludePackages: mainPackage 66 | }, 67 | (error, moduleInfos) => { 68 | if (error) { 69 | reject(error); 70 | } 71 | resolve(moduleInfos); 72 | } 73 | ); 74 | }); 75 | } 76 | 77 | async function updateThirdPartyNotices(workingDirectory: string, outFile: string): Promise { 78 | try { 79 | const packageDefinition = readJsonFile(path.resolve(workingDirectory, 'package.json')); 80 | console.log('Crawling license information...'); 81 | 82 | const moduleInfos = await crawlLicenseInformation(workingDirectory, `${packageDefinition.name}@${packageDefinition.version}`); 83 | let generatedContent = `${packageDefinition.displayName} incorporates third-party components listed below:\n`; 84 | 85 | // Used to separate pacakges 86 | const starSeparator = `\n${'*'.repeat(80)}\n`; 87 | 88 | // Used to separate additional files associated with package information 89 | const dashSeparator = `\n${'-'.repeat(80)}\n\n`; 90 | 91 | const crawlerOverrides = fs.existsSync(crawlerOverridesFileName) ? readJsonFile(crawlerOverridesFileName) : {}; 92 | 93 | const packageNames = Object.keys(moduleInfos); 94 | console.log(`${packageNames.length} packages found.`); 95 | 96 | for (const packageName of packageNames) { 97 | const { name: barePackageName, version } = getPackageNameAndVersion(packageName); 98 | const moduleInfo = moduleInfos[packageName]; 99 | process.stdout.write(`Processing package: ${packageName}...`); 100 | 101 | if (crawlerOverrides[barePackageName]) { 102 | Object.assign(moduleInfo, crawlerOverrides[barePackageName]); 103 | } 104 | 105 | if (moduleInfo['licenses'] === 'UNKNOWN') { 106 | console.error(`License information unknown for package "${packageName}"`); 107 | continue; 108 | } 109 | 110 | const licenseFile = moduleInfo.licenseFile; 111 | if (!licenseFile) { 112 | console.error(`No license file found for package "${packageName}"`); 113 | continue; 114 | } 115 | 116 | const licenseContents = await fetchLicenseContent(licenseFile); 117 | const headerData = [starSeparator, packageName, `Publisher: ${moduleInfo.publisher}`, `Repository: ${moduleInfo.repository}`, `License: ${moduleInfo.licenses}`, 'License text:', '', ''].join('\n'); 118 | generatedContent += headerData; 119 | generatedContent += licenseContents; 120 | 121 | // Check for additional files 122 | const additionalFileConfig = additionalFiles.find((config) => config.name === barePackageName && config.version === version); 123 | 124 | if (additionalFileConfig) { 125 | for (const file of additionalFileConfig.files) { 126 | const filePath = path.resolve(workingDirectory, 'node_modules', barePackageName, file); 127 | if (fs.existsSync(filePath)) { 128 | const fileContents = fs.readFileSync(filePath, 'utf8'); 129 | generatedContent += dashSeparator; 130 | generatedContent += fileContents; 131 | generatedContent += '\n'; 132 | } else { 133 | console.error(`Expected file ${file} not found for package ${barePackageName}@${version}`); 134 | } 135 | } 136 | } else { 137 | console.error(' error!'); 138 | throw new Error(`Version mismatch or missing additional file configuration for package ${barePackageName}@${version}`); 139 | } 140 | 141 | process.stdout.write(' done.\n'); 142 | } 143 | 144 | // Read existing file content if it exists 145 | let currentContent = ''; 146 | if (fs.existsSync(outFile)) { 147 | currentContent = fs.readFileSync(outFile, 'utf8'); 148 | } 149 | 150 | // Write to file only if content has changed 151 | if (currentContent !== generatedContent) { 152 | fs.writeFileSync(outFile, generatedContent, 'utf8'); 153 | console.log(`Third-party notices updated successfully in '${outFile}'.`); 154 | } else { 155 | console.log(`Third-party notices in '${outFile}' are already up-to-date.`); 156 | } 157 | } catch (error) { 158 | console.error('An internal error occurred while crawling license data:'); 159 | console.error((error as Error).stack); 160 | 161 | // This is a command line tool, so this is appropriate error handling. 162 | // eslint-disable-next-line unicorn/no-process-exit 163 | process.exit(1); 164 | } 165 | } 166 | 167 | // Get the path from the command-line arguments 168 | const args = process.argv.slice(2); 169 | if (args.length === 0) { 170 | console.error('Please provide the path to the working directory as an argument.'); 171 | 172 | // This is a command line tool, so this is appropriate error handling. 173 | // eslint-disable-next-line unicorn/no-process-exit 174 | process.exit(1); 175 | } 176 | const workingDirectory = args[0]; 177 | const thirdPartyNoticesPath = path.resolve(workingDirectory, 'third-party-notices.txt'); 178 | 179 | // Unable to compile with top-level await due to CommonJS module resolution 180 | // eslint-disable-next-line unicorn/prefer-top-level-await 181 | (async () => { 182 | await updateThirdPartyNotices(workingDirectory, thirdPartyNoticesPath); 183 | })(); 184 | -------------------------------------------------------------------------------- /src/test/testUtils.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import assert from 'node:assert/strict'; 13 | import { type SpawnOptions } from 'node:child_process'; 14 | import * as fs from 'node:fs'; 15 | import path from 'node:path'; 16 | import { DebugProtocol } from '@vscode/debugprotocol'; 17 | import { DebugClient } from '@vscode/debugadapter-testsupport'; 18 | import { CudaDebugClient, type StopLocationInfo } from './cudaDebugClient'; 19 | import { type CudaLaunchRequestArguments } from '../debugger/cudaGdbSession'; 20 | import { expect } from '@jest/globals'; 21 | 22 | export { StopLocationInfo } from './cudaDebugClient'; 23 | 24 | export interface StoppedContext { 25 | threadId: number; 26 | frameId: number; 27 | actLocals: Map; 28 | } 29 | 30 | export interface LineNumbers { 31 | [marker: string]: number; 32 | } 33 | 34 | // eslint-disable-next-line @typescript-eslint/no-extraneous-class 35 | export class TestUtils { 36 | static readonly localScopeName = 'Local'; 37 | 38 | static getDebugAdapterPath(): string { 39 | // eslint-disable-next-line unicorn/prefer-module 40 | const debugAdapterPath = path.resolve(__dirname, '../../dist/debugAdapter.js'); 41 | expect(fs.existsSync(debugAdapterPath)).toBe(true); 42 | return debugAdapterPath; 43 | } 44 | 45 | public static resolveTestPath(testPath: string): string { 46 | const searchPaths = ['../../src/test/testPrograms', '../src/test/testPrograms']; 47 | 48 | const testProgDirEnvVar = process.env.TEST_PROG_DIR; 49 | if (testProgDirEnvVar) { 50 | searchPaths.push(testProgDirEnvVar); 51 | } 52 | 53 | for (const searchPath of searchPaths) { 54 | // eslint-disable-next-line unicorn/prefer-module 55 | const testProgramsDir = path.resolve(__dirname, searchPath); 56 | const resolvedTestPath = path.resolve(testProgramsDir, testPath); 57 | 58 | if (fs.existsSync(resolvedTestPath)) { 59 | return resolvedTestPath; 60 | } 61 | } 62 | 63 | throw new Error(`Unable to resolve test path "${testPath}"`); 64 | } 65 | 66 | static getTestProgram(programName: string): string { 67 | return this.resolveTestPath(programName); 68 | } 69 | 70 | /** For each lineNumber in programName that has a comment of the form 71 | * 72 | * /*@someMarker* 73 | * 74 | * the returned object will contain {"someMarker": lineNumber}. 75 | */ 76 | static getLineNumbersFromComments(fileName: string): LineNumbers { 77 | const filePath = this.resolveTestPath(fileName); 78 | const lines = fs.readFileSync(filePath, 'utf8').split('\n'); 79 | const lineNumbers: LineNumbers = {}; 80 | for (const [i, line] of lines.entries()) { 81 | const { marker } = /\/\*@(?.+?)\*/.exec(line)?.groups ?? {}; 82 | if (marker !== undefined) { 83 | lineNumbers[marker] = i + 1; 84 | } 85 | } 86 | return lineNumbers; 87 | } 88 | 89 | static getTestSource(fileName: string): DebugProtocol.Source { 90 | return { 91 | name: fileName, 92 | path: TestUtils.getTestProgram(fileName) 93 | }; 94 | } 95 | 96 | static async createDebugClient(spawnOptions?: SpawnOptions): Promise { 97 | const debugAdapterPath = TestUtils.getDebugAdapterPath(); 98 | 99 | const dc = new CudaDebugClient(debugAdapterPath, spawnOptions); 100 | 101 | await dc.start(); 102 | const initResp = await dc.initializeRequest(); 103 | 104 | expect(initResp.success).toBe(true); 105 | assert(initResp.body); 106 | 107 | dc.capabilities = initResp.body; 108 | return dc; 109 | } 110 | 111 | static async getLaunchArguments(testProgram: string, args?: string): Promise { 112 | const testProgramPath = TestUtils.getTestProgram(testProgram); 113 | const logFilePath = path.resolve(path.dirname(testProgramPath), '.rubicon_log'); 114 | 115 | return { 116 | program: testProgramPath, 117 | args, 118 | verboseLogging: true, 119 | logFile: logFilePath, 120 | onAPIError: 'stop' 121 | }; 122 | } 123 | 124 | static async launchDebugger(testProgram: string, args?: string): Promise { 125 | const dc = await this.createDebugClient(); 126 | const launchArguments = await this.getLaunchArguments(testProgram, args); 127 | 128 | await dc.launchRequest(launchArguments); 129 | 130 | return dc; 131 | } 132 | 133 | static async assertStoppedLocation(dc: CudaDebugClient, reason: string, file: string, line: number, timeout?: number): Promise { 134 | return dc.expectToStopAt(reason, file, line, timeout, async () => {}); 135 | } 136 | 137 | static async getLocals(dc: DebugClient, frameId: number): Promise> { 138 | const localsScopeReference = await this.getLocalsScopeReference(dc, frameId); 139 | const locals = await this.getChildren(dc, localsScopeReference); 140 | return locals; 141 | } 142 | 143 | static async getLocalsAsObject(dc: DebugClient, frameId: number): Promise<{ [name: string]: DebugProtocol.Variable }> { 144 | const localsScopeReference = await this.getLocalsScopeReference(dc, frameId); 145 | const locals = await this.getChildrenAsObject(dc, localsScopeReference); 146 | return locals; 147 | } 148 | 149 | static async getLocalsScopeReference(dc: DebugClient, frameId: number): Promise { 150 | const scopesResp = await dc.scopesRequest({ frameId }); 151 | const { scopes } = scopesResp.body; 152 | 153 | const localScope = scopes.find((s) => s.name === TestUtils.localScopeName); 154 | assert(localScope); 155 | 156 | return localScope.variablesReference; 157 | } 158 | 159 | static async getChildren(dc: DebugClient, variablesReference: number): Promise> { 160 | const vars = new Map(); 161 | 162 | const variablesResp = await dc.variablesRequest({ 163 | variablesReference 164 | }); 165 | 166 | for (const v of variablesResp.body.variables) { 167 | vars.set(v.name, v); 168 | } 169 | 170 | return vars; 171 | } 172 | 173 | static async getChildrenAsObject(dc: DebugClient, variablesReference: number): Promise<{ [name: string]: DebugProtocol.Variable }> { 174 | const vars: { [name: string]: DebugProtocol.Variable } = {}; 175 | const variablesResp = await dc.variablesRequest({ variablesReference }); 176 | for (const v of variablesResp.body.variables) { 177 | vars[v.name] = v; 178 | } 179 | return vars; 180 | } 181 | 182 | static readonly defaultVerifyLocalsTimeout = 120_000; 183 | 184 | static async verifyLocalsOnStop( 185 | dc: CudaDebugClient, 186 | source: string, 187 | line: number, 188 | stopReason: string, 189 | expLocals: { 190 | name: string; 191 | value?: string; 192 | }[], 193 | allowOthers?: boolean | undefined, 194 | stopTimeout?: number 195 | ): Promise { 196 | const { threadId, frameId } = await TestUtils.assertStoppedLocation(dc, stopReason, source, line, stopTimeout ?? TestUtils.defaultVerifyLocalsTimeout); 197 | 198 | const actual = await TestUtils.getLocals(dc, frameId); 199 | 200 | if (allowOthers === false) { 201 | expect(actual.size).toEqual(expLocals.length); 202 | } 203 | 204 | for (const v of expLocals) { 205 | const local = actual.get(v.name); 206 | assert(local); 207 | 208 | if (v.value) { 209 | expect(local.value).toEqual(v.value); 210 | } 211 | } 212 | 213 | return { threadId, frameId, actLocals: actual }; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /src/test/varAssignments.test.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | /* eslint-disable @typescript-eslint/no-non-null-assertion */ 13 | 14 | import { expect } from 'chai'; 15 | import { DebugProtocol } from '@vscode/debugprotocol'; 16 | import { TestUtils } from './testUtils'; 17 | import { CudaDebugClient } from './cudaDebugClient'; 18 | 19 | describe('Variable assignment tests', () => { 20 | const varAssignSource = 'varAssign/varAssign.cpp'; 21 | 22 | let dc: CudaDebugClient; 23 | 24 | afterEach(async () => { 25 | if (dc) { 26 | await dc.stop(); 27 | } 28 | }); 29 | 30 | it('Variable assignment works for scalar values', async () => { 31 | dc = await TestUtils.launchDebugger('varAssign/varAssign'); 32 | 33 | const bpResp = await dc.setBreakpointsRequest({ 34 | source: TestUtils.getTestSource(varAssignSource), 35 | breakpoints: [37, 42].map((ln) => { 36 | return { line: ln }; 37 | }) 38 | }); 39 | 40 | for (const bp of bpResp.body.breakpoints) { 41 | expect(bp.verified).eq(true); 42 | } 43 | 44 | await dc.configurationDoneRequest(); 45 | const { threadId, frameId } = await TestUtils.assertStoppedLocation(dc, 'breakpoint', varAssignSource, 37); 46 | 47 | const localsScopeReference = await TestUtils.getLocalsScopeReference(dc, frameId); 48 | const locals = await TestUtils.getChildren(dc, localsScopeReference); 49 | const varX = locals.get('x')!; 50 | expect(varX.value).eq('2'); 51 | 52 | const setVarResp = await dc.setVariableRequest({ 53 | name: 'x', 54 | value: '3', 55 | variablesReference: localsScopeReference 56 | }); 57 | 58 | expect(setVarResp.body.value).eq('3'); 59 | 60 | await dc.continueRequest({ threadId }); 61 | 62 | await TestUtils.assertStoppedLocation(dc, 'breakpoint', varAssignSource, 42); 63 | 64 | const locals2 = await TestUtils.getLocals(dc, frameId); 65 | const varResult = locals2.get('result')!; 66 | expect(varResult.value).eq('12'); 67 | }); 68 | 69 | it('Variable assignment works for structs', async () => { 70 | const verifyStructs = async (locals: Map, values: string[]): Promise => { 71 | const a1VarRef = locals.get('a1')!.variablesReference; 72 | const a1Children = await TestUtils.getChildren(dc, a1VarRef); 73 | expect(a1Children.get('alpha')!.value).eq(values[0]); 74 | expect(a1Children.get('beta')!.value).eq(values[1]); 75 | 76 | const a2VarRef = locals.get('a2')!.variablesReference; 77 | const a2Children = await TestUtils.getChildren(dc, a2VarRef); 78 | expect(a2Children.get('alpha')!.value).eq(values[2]); 79 | expect(a2Children.get('beta')!.value).eq(values[3]); 80 | 81 | const aVarRef = locals.get('a')!.variablesReference; 82 | const aChildren = await TestUtils.getChildren(dc, aVarRef); 83 | expect(aChildren.get('alpha')!.value).eq(values[4]); 84 | expect(aChildren.get('beta')!.value).eq(values[5]); 85 | }; 86 | 87 | dc = await TestUtils.launchDebugger('varAssign/varAssign'); 88 | 89 | const bpResp = await dc.setBreakpointsRequest({ 90 | source: TestUtils.getTestSource(varAssignSource), 91 | breakpoints: [{ line: 28 }] 92 | }); 93 | 94 | for (const bp of bpResp.body.breakpoints) { 95 | expect(bp.verified).eq(true); 96 | } 97 | 98 | await dc.configurationDoneRequest(); 99 | const { frameId } = await TestUtils.assertStoppedLocation(dc, 'breakpoint', varAssignSource, 28); 100 | 101 | let localsScopeReference = await TestUtils.getLocalsScopeReference(dc, frameId); 102 | let locals = await TestUtils.getChildren(dc, localsScopeReference); 103 | 104 | await verifyStructs(locals, ['1', '1', '2', '2', '1', '1']); 105 | 106 | const a2Value = locals.get('a2')!.value; 107 | 108 | const invalidatedPromise = dc.waitForEvent('invalidated'); 109 | const setVarResp0 = await dc.setVariableRequest({ 110 | name: 'a', 111 | value: a2Value, 112 | variablesReference: localsScopeReference 113 | }); 114 | expect(setVarResp0.body.value).eq(a2Value); 115 | const invalidatedEvent = await invalidatedPromise; 116 | expect(invalidatedEvent.body.areas).to.include('variables'); 117 | 118 | // All existing variable references are invalid after InvalidatedEvent. 119 | localsScopeReference = await TestUtils.getLocalsScopeReference(dc, frameId); 120 | locals = await TestUtils.getChildren(dc, localsScopeReference); 121 | 122 | const aVarRef = locals.get('a')!.variablesReference; 123 | const aChildren = await TestUtils.getChildren(dc, aVarRef); 124 | expect(aChildren.get('alpha')!.value).eq('2'); 125 | expect(aChildren.get('beta')!.value).eq('2'); 126 | 127 | const setVariableResponse = await dc.setVariableRequest({ 128 | name: 'alpha', 129 | value: '3', 130 | variablesReference: aVarRef 131 | }); 132 | expect(setVariableResponse.body.value).eq('3'); 133 | }); 134 | 135 | const verifyAndManipulateLocals = async (frameId: number): Promise => { 136 | let localsRef = await TestUtils.getLocalsScopeReference(dc, frameId); 137 | let locals = await TestUtils.getChildren(dc, localsRef); 138 | let myInputChildren = await TestUtils.getChildren(dc, locals.get('myInput')!.variablesReference); 139 | let takeYourPickReference = myInputChildren.get('takeYourPick')!.variablesReference; 140 | let takeYourPickChildren = await TestUtils.getChildren(dc, takeYourPickReference); 141 | const halfAndHalfChildren = await TestUtils.getChildren(dc, takeYourPickChildren.get('halfAndHalf')!.variablesReference); 142 | 143 | expect(halfAndHalfChildren.get('lowHalf')).property('value').eq('1'); 144 | expect(halfAndHalfChildren.get('highHalf')).property('value').eq('3'); 145 | 146 | let invalidatedPromise = dc.waitForEvent('invalidated'); 147 | const setVarResp0 = await dc.setVariableRequest({ 148 | name: 'myResult', 149 | value: '1', 150 | variablesReference: localsRef 151 | }); 152 | expect(setVarResp0).nested.property('body.value').eq('1'); 153 | 154 | let invalidatedEvent = await invalidatedPromise; 155 | expect(invalidatedEvent).nested.property('body.areas').to.include('variables'); 156 | 157 | localsRef = await TestUtils.getLocalsScopeReference(dc, frameId); 158 | locals = await TestUtils.getChildren(dc, localsRef); 159 | myInputChildren = await TestUtils.getChildren(dc, locals.get('myInput')!.variablesReference); 160 | takeYourPickReference = myInputChildren.get('takeYourPick')!.variablesReference; 161 | 162 | // The variable needs to be expanded before its children can be assigned. 163 | takeYourPickChildren = await TestUtils.getChildren(dc, takeYourPickReference); 164 | expect(takeYourPickChildren.get('whole')).property('value').not.undefined.and.not.eq('17179869188'); 165 | 166 | invalidatedPromise = dc.waitForEvent('invalidated'); 167 | await dc.setVariableRequest({ 168 | name: 'whole', 169 | value: '17179869188', 170 | variablesReference: takeYourPickReference 171 | }); 172 | 173 | invalidatedEvent = await invalidatedPromise; 174 | expect(invalidatedEvent).nested.property('body.areas').to.include('variables'); 175 | }; 176 | 177 | it('Variable assignment works when in device code', async () => { 178 | dc = await TestUtils.launchDebugger('variables/variables'); 179 | 180 | const bpResp = await dc.setBreakpointsRequest({ 181 | source: TestUtils.getTestSource('variables/variables.cu'), 182 | breakpoints: [{ line: 92 }, { line: 107 }] 183 | }); 184 | 185 | for (const bp of bpResp.body.breakpoints) { 186 | expect(bp.verified).eq(true); 187 | } 188 | 189 | await dc.configurationDoneRequest(); 190 | const { threadId, frameId } = await TestUtils.assertStoppedLocation(dc, 'breakpoint', 'variables/variables.cu', 92); 191 | 192 | await verifyAndManipulateLocals(frameId); 193 | 194 | await dc.continueRequest({ threadId }); 195 | const stoppedAtRet = await TestUtils.assertStoppedLocation(dc, 'breakpoint', 'variables/variables.cu', 107); 196 | 197 | const localsRef = await TestUtils.getLocals(dc, stoppedAtRet.frameId); 198 | expect(localsRef.get('myResult')!.value).eq('74'); 199 | }); 200 | }); 201 | 202 | /* eslint-enable @typescript-eslint/no-non-null-assertion */ 203 | -------------------------------------------------------------------------------- /gulpfile.mts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import { chmod, copyFile } from 'node:fs/promises'; 13 | import { fileURLToPath } from 'node:url'; 14 | import { promisify } from 'node:util'; 15 | import spawn from 'await-spawn'; 16 | import { deleteAsync } from 'del'; 17 | import { move, readJson, remove, writeJson } from 'fs-extra/esm'; 18 | import gulp, { type TaskFunction, type TaskFunctionCallback } from 'gulp'; 19 | import * as execa from 'gulp-execa'; 20 | import * as semver from 'semver'; 21 | import * as vsce from '@vscode/vsce'; 22 | import webpack from 'webpack'; 23 | import { merge } from 'webpack-merge'; 24 | import makeArgv, * as yargs from 'yargs'; 25 | import webpackProdConfig from './webpack.prod.mts'; 26 | 27 | const argv = makeArgv(process.argv.slice(2)); 28 | 29 | const exportOptions = ['Public', 'Internal', 'NDA'] as const; 30 | const configOptions = ['Release', 'Debug'] as const; 31 | 32 | const testProgramsBase = 'src/test/testPrograms/'; 33 | const licenseFileName = 'LICENSE'; 34 | const thirdPartyNoticesFileName = 'third-party-notices.txt'; 35 | const webpackLicenseFileName = 'extension.js.LICENSE.txt'; 36 | 37 | const webpackOutputUrl = new URL('dist/', import.meta.url); 38 | 39 | type OptionDefinitions = { [key: string]: yargs.Options } | undefined; 40 | 41 | type TaskDefinition = { 42 | displayName: string; 43 | description?: string; 44 | options?: OD; 45 | }; 46 | 47 | type TaskResult = ReturnType; 48 | 49 | /* 50 | * Strongly typed builder for Gulp tasks. 51 | * Given the yargs options definition in def.options, it automatically generates flags for the task, 52 | * and exposes the strongly typed yargs parse result to the task function via `this`. 53 | */ 54 | // eslint-disable-next-line @typescript-eslint/explicit-function-return-type 55 | function task(def: TaskDefinition) { 56 | const { options } = def; 57 | if (options === undefined) { 58 | return task({ ...def, options: {} }); 59 | } 60 | 61 | const flags = Object.fromEntries( 62 | Object.entries(options).map(([name, option]) => { 63 | const arg = option.choices ? option.choices.map((s) => JSON.stringify(s)).join(' | ') : `<${option.type}>`; 64 | 65 | let key = `--${name} ${arg}`; 66 | if (!option.demandOption) { 67 | key = `[${key}]`; 68 | } 69 | 70 | let description = option.description ?? ''; 71 | if (option.default !== undefined) { 72 | description += ` (default: ${JSON.stringify(option.default)})`; 73 | } 74 | 75 | return [key, description]; 76 | }) 77 | ); 78 | 79 | // eslint-disable-next-line @typescript-eslint/explicit-function-return-type 80 | const parseArgv = () => argv.options(options).parseSync(); 81 | 82 | type UnboundTaskFunction = (this: ReturnType, callback: TaskFunctionCallback) => TaskResult; 83 | 84 | return { 85 | does(func: F): TaskFunction { 86 | const taskFunc: TaskFunction = (callback) => { 87 | const boundFunc = func.bind(parseArgv()); 88 | return boundFunc(callback); 89 | }; 90 | taskFunc.displayName = def.displayName; 91 | taskFunc.description = def.description; 92 | if (flags) { 93 | taskFunc.flags = flags; 94 | } 95 | return taskFunc; 96 | } 97 | }; 98 | } 99 | 100 | async function webpackAsync(firstConfiguration: webpack.Configuration | webpack.Configuration[], ...configurations: webpack.Configuration[]): Promise { 101 | const config = merge(firstConfiguration, ...configurations); 102 | const compiler = webpack(config); 103 | const stats = await promisify(compiler.run).call(compiler); 104 | 105 | if (stats) { 106 | console.log( 107 | stats.toString({ 108 | chunks: false, 109 | colors: true 110 | }) 111 | ); 112 | } 113 | 114 | if (!stats || stats.hasErrors() || stats.hasWarnings()) { 115 | throw new Error('Errors/warnings present after compiling for webpack.'); 116 | } 117 | } 118 | 119 | function getDayOfYear(date?: Date): number { 120 | const startDate = date ?? new Date(); 121 | const firstDate = new Date(startDate.getFullYear(), 0, 1); 122 | const msInDay = 1000 * 60 * 60 * 24; 123 | const dayOfYear = Math.floor((startDate.getTime() - firstDate.getTime()) / msInDay); 124 | return dayOfYear; 125 | } 126 | 127 | function updateVersion(packageJson: any, patchNumber: number): void { 128 | const version = semver.parse(packageJson.version); 129 | if (!version) { 130 | throw new Error(`Version '${packageJson.version}' is not in a correct format`); 131 | } 132 | 133 | const newVersion = `${version.major}.${version.minor}.${patchNumber}`; 134 | packageJson.version = newVersion; 135 | } 136 | 137 | export const compileTestsTask = task({ 138 | displayName: 'compile:tests', 139 | description: 'Compile all test programs' 140 | }).does(async function () { 141 | await execa.exec('make', { cwd: testProgramsBase }); 142 | }); 143 | 144 | export const cleanTestsTask = task({ 145 | displayName: 'clean:tests' 146 | }).does(async function () { 147 | await execa.exec('make clean', { cwd: testProgramsBase }); 148 | }); 149 | 150 | export const recompileTestsTask = task({ 151 | displayName: 'recompile:tests', 152 | description: 'Delete all test artifacts and recompile the tests' 153 | }).does(gulp.series(cleanTestsTask, compileTestsTask)); 154 | 155 | export const cleanWebpackTask = task({ 156 | displayName: 'clean:webpack', 157 | description: 'Clean artifacts generated by webpack' 158 | }).does(async function () { 159 | await deleteAsync(['dist']); 160 | }); 161 | 162 | export const packageTask = task({ 163 | displayName: 'package', 164 | description: 'Package extension into a deployable .vsix', 165 | options: { 166 | exportLevel: { 167 | choices: exportOptions, 168 | default: 'Public' 169 | }, 170 | config: { 171 | choices: configOptions, 172 | default: 'Release' 173 | }, 174 | changelist: { 175 | type: 'number', 176 | description: 'The current P4 changelist number to derive version number from; if not specified, version is derived from current date.' 177 | } 178 | } 179 | }).does(async function () { 180 | const patchNumber = this.changelist ?? getDayOfYear(); 181 | 182 | // Update patch number in package.json for .vsix package, then revert 183 | // to original version 184 | 185 | try { 186 | await copyFile('package.json', '__package.json'); 187 | await chmod('package.json', 0o666); 188 | 189 | const packageJson = await readJson('package.json'); 190 | updateVersion(packageJson, patchNumber); 191 | await writeJson('package.json', packageJson, { spaces: 4 }); 192 | 193 | await webpackAsync(webpackProdConfig); 194 | 195 | const original = { 196 | licenseFileUrl: new URL(licenseFileName, import.meta.url), 197 | thirdPartyNoticesFileUrl: new URL(thirdPartyNoticesFileName, import.meta.url) 198 | }; 199 | const webpackOutput = { 200 | licenseFileUrl: new URL(licenseFileName, webpackOutputUrl), 201 | mangledlicenseFileUrl: new URL(webpackLicenseFileName, webpackOutputUrl), 202 | thirdPartyNoticesFileUrl: new URL(thirdPartyNoticesFileName, webpackOutputUrl) 203 | }; 204 | 205 | await remove(fileURLToPath(webpackOutput.mangledlicenseFileUrl)); 206 | await copyFile(original.licenseFileUrl, webpackOutput.licenseFileUrl); 207 | await copyFile(original.thirdPartyNoticesFileUrl, webpackOutput.thirdPartyNoticesFileUrl); 208 | 209 | const vsixFileName = `${packageJson.name}-${packageJson.version}.vsix`; 210 | await vsce.createVSIX({ 211 | packagePath: vsixFileName, 212 | baseImagesUrl: 'https://developer.nvidia.com/sites/default/files/akamai/tools/nsvsce' 213 | }); 214 | 215 | const zipFileName = `Rubicon-${this.exportLevel}-${this.config}.zip`; 216 | await spawn('zip', [zipFileName, vsixFileName]); 217 | } finally { 218 | await move('__package.json', 'package.json', { overwrite: true }); 219 | } 220 | }); 221 | 222 | export const cleanPackageTask = task({ 223 | displayName: 'clean:package' 224 | }).does(async function () { 225 | await deleteAsync(['*.vsix', 'Rubicon*.zip']); 226 | }); 227 | 228 | export const cleanOutputTask = task({ 229 | displayName: 'clean:out', 230 | description: 'Delete all files under out/' 231 | }).does(async function () { 232 | await deleteAsync(['out']); 233 | }); 234 | 235 | export const cleanTask = task({ 236 | displayName: 'clean', 237 | description: 'Delete all build/test/publish artifacts' 238 | }).does( 239 | gulp.parallel( 240 | // 241 | gulp.series(cleanTestsTask, cleanOutputTask), 242 | cleanWebpackTask, 243 | cleanPackageTask 244 | ) 245 | ); 246 | -------------------------------------------------------------------------------- /src/debugController.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | /* eslint-disable max-classes-per-file */ 13 | /* eslint-disable class-methods-use-this */ 14 | /* eslint-disable @typescript-eslint/no-unused-vars */ 15 | import * as vscode from 'vscode'; 16 | import { DebugProtocol } from '@vscode/debugprotocol'; 17 | 18 | import { CudaDebugProtocol } from './debugger/cudaDebugProtocol'; 19 | import { CudaGdbSession } from './debugger/cudaGdbSession'; 20 | import { CudaGdbServerSession } from './debugger/cudaGdbServerSession'; 21 | import { CudaQnxGdbServerSession } from './debugger/cudaQnxGdbServerSession'; 22 | import * as types from './debugger/types'; 23 | import { TelemetryService } from './telemetryService'; 24 | import * as utils from './debugger/utils'; 25 | import { pickProcess } from './debugger/processList'; 26 | 27 | const cudaGdbDebugType = 'cuda-gdb'; 28 | const cudaGdbServerType = 'cuda-gdbserver'; 29 | const cudaQnxGdbServerType = 'cuda-qnx-gdbserver'; 30 | const cudaChangeDebugFocus = 'cuda.changeDebugFocus'; 31 | const cudaPickProcess = 'cuda.pickProcess'; 32 | 33 | class InlineDebugAdapterFactory implements vscode.DebugAdapterDescriptorFactory { 34 | createDebugAdapterDescriptor(session: vscode.DebugSession, executable: vscode.DebugAdapterExecutable | undefined): vscode.ProviderResult { 35 | if (session.type === cudaGdbServerType) { 36 | const debugServerSession = new CudaGdbServerSession(); 37 | return new vscode.DebugAdapterInlineImplementation(debugServerSession); 38 | // eslint-disable-next-line no-else-return 39 | } else if (session.type === cudaQnxGdbServerType) { 40 | const debugQNXServerSession = new CudaQnxGdbServerSession(); 41 | return new vscode.DebugAdapterInlineImplementation(debugQNXServerSession); 42 | } else { 43 | const debugSession = new CudaGdbSession(); 44 | return new vscode.DebugAdapterInlineImplementation(debugSession); 45 | } 46 | } 47 | } 48 | 49 | enum DebuggerMode { 50 | design, 51 | stopped, 52 | running 53 | } 54 | 55 | class CudaDebugAdapterTracker implements vscode.DebugAdapterTracker { 56 | private static readonly SESSION_LABEL = 'debug-session'; 57 | 58 | constructor(private debugController: CudaDebugController) {} 59 | 60 | onError(error: Error): void { 61 | // An error with the debug adapter has occurred. 62 | if (error) { 63 | this.debugController.telemetry.trackError(error.name, error.message); 64 | } 65 | } 66 | 67 | onExit(code: number | undefined, signal: string | undefined): void { 68 | // The debug adapter has exited with the given exit code or signal. 69 | if (code !== undefined || signal) { 70 | this.debugController.telemetry.trackExit(code, signal); 71 | } 72 | } 73 | 74 | onWillStartSession(): void { 75 | this.debugController.telemetry.startSession(CudaDebugAdapterTracker.SESSION_LABEL); 76 | } 77 | 78 | onWillStopSession(): void { 79 | this.debugController.updateDebuggerMode(DebuggerMode.design); 80 | this.debugController.telemetry.endSession(CudaDebugAdapterTracker.SESSION_LABEL); 81 | } 82 | 83 | onWillReceiveMessage(message: any): void { 84 | // The debug adapter is about to receive a Debug Adapter Protocol message from VS Code. 85 | // These are requests that are sent to the DA. 86 | } 87 | 88 | onDidSendMessage(message: any): void { 89 | // The debug adapter has sent a Debug Adapter Protocol message to VS Code. 90 | // These are responses and events that are received from the DA. 91 | 92 | const protocolMessage = message as DebugProtocol.ProtocolMessage; 93 | if (protocolMessage.type === 'event') { 94 | const eventMessage = message as DebugProtocol.Event; 95 | const messageName: string = eventMessage.event; 96 | 97 | switch (messageName) { 98 | case 'initialized': 99 | case 'continue': { 100 | this.debugController.updateDebuggerMode(DebuggerMode.running); 101 | break; 102 | } 103 | 104 | case 'stopped': { 105 | this.debugController.updateDebuggerMode(DebuggerMode.stopped); 106 | break; 107 | } 108 | 109 | case 'exited': 110 | case 'terminated': { 111 | this.debugController.updateDebuggerMode(DebuggerMode.design); 112 | break; 113 | } 114 | 115 | case CudaDebugProtocol.Event.changedCudaFocus: { 116 | const typedEvent = eventMessage as CudaDebugProtocol.ChangedCudaFocusEvent; 117 | this.debugController.setDebugFocus(typedEvent.body?.focus); 118 | break; 119 | } 120 | 121 | case CudaDebugProtocol.Event.systemInfo: { 122 | const typedEvent = eventMessage as CudaDebugProtocol.SystemInfoEvent; 123 | this.debugController.telemetry.trackSystemInfo('debug-adapter', typedEvent?.body?.systemInfo); 124 | break; 125 | } 126 | 127 | default: { 128 | break; 129 | } 130 | } 131 | } 132 | } 133 | } 134 | 135 | class CudaDebugController implements vscode.Disposable, vscode.DebugAdapterTrackerFactory { 136 | private focusStatusBarItem: vscode.StatusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Right); 137 | 138 | private debuggerMode: DebuggerMode = DebuggerMode.design; 139 | 140 | telemetry: TelemetryService; 141 | 142 | constructor(context: vscode.ExtensionContext, telemetry: TelemetryService) { 143 | this.telemetry = telemetry; 144 | 145 | context.subscriptions.push(this.focusStatusBarItem); 146 | this.focusStatusBarItem.command = cudaChangeDebugFocus; 147 | this.focusStatusBarItem.text = utils.formatCudaFocus(); 148 | this.focusStatusBarItem.hide(); 149 | 150 | vscode.debug.onDidChangeActiveDebugSession((session: vscode.DebugSession | undefined) => { 151 | if (session === undefined) { 152 | this.focusStatusBarItem.hide(); 153 | } else if (session.type === cudaGdbDebugType || session.type === cudaGdbServerType || session.type === cudaQnxGdbServerType) { 154 | this.focusStatusBarItem.show(); 155 | } 156 | }); 157 | } 158 | 159 | dispose(): void { 160 | // Deactivation 161 | } 162 | 163 | createDebugAdapterTracker(session: vscode.DebugSession): vscode.ProviderResult { 164 | return new CudaDebugAdapterTracker(this); 165 | } 166 | 167 | setDebugFocus(focus?: types.CudaFocus): void { 168 | const formattedFocus: string = utils.formatCudaFocus(focus); 169 | this.focusStatusBarItem.text = formattedFocus; 170 | } 171 | 172 | async changeDebugFocus(): Promise { 173 | const tracker = this.telemetry.trackCommand(cudaChangeDebugFocus); 174 | try { 175 | if (this.debuggerMode !== DebuggerMode.stopped) { 176 | tracker.cancel('stopped debugger'); 177 | vscode.window.showWarningMessage('The debugger must be stopped in order to set the debug focus.'); 178 | return; 179 | } 180 | 181 | const newDebugFocus: string | undefined = await vscode.window.showInputBox({ 182 | ignoreFocusOut: true, 183 | placeHolder: 'Set debug focus: block (?, ?, ?) thread (?, ?, ?)', 184 | prompt: '', 185 | validateInput(value: string): string | undefined | null | Thenable { 186 | // Validate that focus is set with the expected syntax 187 | return ''; 188 | } 189 | }); 190 | 191 | if (!newDebugFocus) { 192 | tracker.cancel('input dismissed'); 193 | return; 194 | } 195 | 196 | const typedDebugFocus: types.CudaFocus | undefined = utils.parseCudaSwFocus(newDebugFocus); 197 | if (!typedDebugFocus) { 198 | tracker.cancel('input invalid'); 199 | vscode.window.showWarningMessage('No block or thread was specified to switch the CUDA debug focus to.'); 200 | } else { 201 | await vscode.debug.activeDebugSession?.customRequest(CudaDebugProtocol.Request.changeCudaFocus, { focus: typedDebugFocus }); 202 | tracker.complete(); 203 | } 204 | } finally { 205 | tracker.dispose(); 206 | } 207 | } 208 | 209 | updateDebuggerMode(mode: DebuggerMode): void { 210 | if (this.debuggerMode === mode) { 211 | return; 212 | } 213 | 214 | this.debuggerMode = mode; 215 | 216 | if (this.debuggerMode === DebuggerMode.design) { 217 | // Reset the label when the debug session ends. 218 | this.focusStatusBarItem.text = utils.formatCudaFocus(); 219 | } 220 | } 221 | } 222 | 223 | export function activateDebugController(context: vscode.ExtensionContext, telemetry: TelemetryService): void { 224 | const cudaGdbFactory: vscode.DebugAdapterDescriptorFactory = new InlineDebugAdapterFactory(); 225 | const debugController: CudaDebugController = new CudaDebugController(context, telemetry); 226 | 227 | context.subscriptions.push( 228 | vscode.debug.registerDebugAdapterDescriptorFactory(cudaGdbDebugType, cudaGdbFactory), 229 | vscode.debug.registerDebugAdapterDescriptorFactory(cudaGdbServerType, cudaGdbFactory), 230 | vscode.debug.registerDebugAdapterDescriptorFactory(cudaQnxGdbServerType, cudaGdbFactory), 231 | debugController, 232 | vscode.debug.registerDebugAdapterTrackerFactory(cudaGdbDebugType, debugController), 233 | vscode.debug.registerDebugAdapterTrackerFactory(cudaGdbServerType, debugController), 234 | vscode.debug.registerDebugAdapterTrackerFactory(cudaQnxGdbServerType, debugController), 235 | vscode.commands.registerCommand(cudaChangeDebugFocus, async () => await debugController.changeDebugFocus()), 236 | vscode.commands.registerCommand(cudaPickProcess, async () => pickProcess()) 237 | ); 238 | } 239 | 240 | /* eslint-enable @typescript-eslint/no-unused-vars */ 241 | /* eslint-enable class-methods-use-this */ 242 | /* eslint-enable max-classes-per-file */ 243 | -------------------------------------------------------------------------------- /src/telemetryService.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | // eslint-disable-next-line max-classes-per-file 13 | import axios from 'axios'; 14 | import * as uuid from 'uuid'; 15 | import * as vscode from 'vscode'; 16 | 17 | import * as types from './debugger/types'; 18 | 19 | export type TelemetryLocation = 'host' | 'debug-adapter'; 20 | 21 | type ActivityStartEvent = { 22 | name: 'activity_start'; 23 | params: { 24 | name: string; 25 | }; 26 | }; 27 | 28 | type ActivityEndEvent = { 29 | name: 'activity_end'; 30 | params: { 31 | name: string; 32 | }; 33 | }; 34 | 35 | type AdapterExitEvent = { 36 | name: 'adapter_exit'; 37 | params: { 38 | code?: number; 39 | signal?: string; 40 | }; 41 | }; 42 | 43 | type ErrorEvent = { 44 | name: 'error'; 45 | params: { 46 | name: string; 47 | message: string; 48 | }; 49 | }; 50 | 51 | type ExecCommandEvent = { 52 | name: 'exec_command'; 53 | params: { 54 | name: string; 55 | status: string; 56 | reason?: string; 57 | }; 58 | }; 59 | 60 | type GpuInfoEvent = { 61 | name: 'gpu_info'; 62 | params: { 63 | location: TelemetryLocation; 64 | name?: string; 65 | description?: string; 66 | sm_type?: string; 67 | name_by_os?: string; 68 | }; 69 | }; 70 | 71 | type OsInfoEvent = { 72 | name: 'os_info'; 73 | params: { 74 | location: TelemetryLocation; 75 | platform?: string; 76 | architecture?: string; 77 | distribution?: string; 78 | distribution_version?: string; 79 | }; 80 | }; 81 | 82 | type CustomEvent = ActivityStartEvent | ActivityEndEvent | AdapterExitEvent | ErrorEvent | ExecCommandEvent | GpuInfoEvent | OsInfoEvent; 83 | 84 | type Event = CustomEvent & { 85 | params: { 86 | session_id?: number; 87 | engagement_time_msec?: number; 88 | debug_mode?: boolean; 89 | traffic_type?: string; 90 | }; 91 | }; 92 | 93 | type UserProperties = { 94 | [key: string]: { 95 | value: string | number; 96 | }; 97 | }; 98 | 99 | type Payload = { 100 | client_id: string; 101 | events: Event[]; 102 | user_properties: UserProperties; 103 | }; 104 | 105 | class TelemetryClient { 106 | private readonly clientID: string; 107 | 108 | private readonly endpoint: string; 109 | 110 | private readonly userProperties: UserProperties = {}; 111 | 112 | private sessionStartMs: number | undefined = undefined; 113 | 114 | private lastEngagementMs: number | undefined = undefined; 115 | 116 | GA4_ENDPOINT = 'https://www.google-analytics.com/mp/collect'; 117 | 118 | NV_DEVTOOLS_UXT_DEBUG = 'NV_DEVTOOLS_UXT_DEBUG'; 119 | 120 | NV_DEVTOOLS_UXT_ROLE = 'NV_DEVTOOLS_UXT_ROLE'; 121 | 122 | NV_DEVTOOLS_QA_ROLE = 'internal-qa'; 123 | 124 | isEnabled = false; 125 | 126 | isDebugMode = false; 127 | 128 | isInternal = false; 129 | 130 | constructor(clientID: string, apiSecret: string, measurementID: string) { 131 | this.clientID = clientID; 132 | this.endpoint = `${this.GA4_ENDPOINT}?api_secret=${apiSecret}&measurement_id=${measurementID}`; 133 | 134 | if (process.env[this.NV_DEVTOOLS_UXT_DEBUG]) { 135 | this.isDebugMode = true; 136 | } 137 | 138 | if (process.env[this.NV_DEVTOOLS_UXT_ROLE] === this.NV_DEVTOOLS_QA_ROLE) { 139 | this.isInternal = true; 140 | } 141 | } 142 | 143 | addUserProperty(name: string, value: string | number): void { 144 | this.userProperties[name] = { 145 | value 146 | }; 147 | } 148 | 149 | sendEvent(event: T): void { 150 | this.sendEvents([event]); 151 | } 152 | 153 | sendEvents(events: Event[]): void { 154 | if (!this.isEnabled) { 155 | return; 156 | } 157 | 158 | if (!this.sessionStartMs) { 159 | this.sessionStartMs = Date.now(); 160 | } 161 | 162 | const engagementTimeMs: number = (() => { 163 | if (!this.lastEngagementMs) { 164 | this.lastEngagementMs = this.sessionStartMs; 165 | return 0; 166 | } 167 | 168 | const currentTimeMs = Date.now(); 169 | const elapsedTimeMs = currentTimeMs - this.lastEngagementMs; 170 | this.lastEngagementMs = currentTimeMs; 171 | 172 | return elapsedTimeMs; 173 | })(); 174 | 175 | // eslint-disable-next-line no-restricted-syntax 176 | for (const event of events) { 177 | event.params.session_id = this.sessionStartMs; 178 | event.params.engagement_time_msec = engagementTimeMs; 179 | 180 | if (this.isDebugMode) { 181 | event.params.debug_mode = true; 182 | } 183 | 184 | if (this.isInternal) { 185 | event.params.traffic_type = 'internal'; 186 | } 187 | } 188 | 189 | const payload: Payload = { 190 | client_id: this.clientID, 191 | events, 192 | user_properties: this.userProperties 193 | }; 194 | 195 | axios.post(this.endpoint, payload); 196 | } 197 | } 198 | 199 | export class TelemetryService { 200 | private readonly client: TelemetryClient; 201 | 202 | private static readonly CLIENT_ID_KEY = 'nsight.telemetryClientId'; 203 | 204 | private static readonly TELEMETRY_CONFIG_ID = 'telemetry'; 205 | 206 | private static readonly TELEMETRY_CONFIG_LEVEL = 'telemetryLevel'; 207 | 208 | constructor(context: vscode.ExtensionContext, apiSecret: string, measurementID: string, extensionVersion: string) { 209 | let clientID: string | undefined = context.globalState.get(TelemetryService.CLIENT_ID_KEY); 210 | if (!clientID) { 211 | clientID = uuid.v4(); 212 | context.globalState.update(TelemetryService.CLIENT_ID_KEY, clientID); 213 | } 214 | 215 | this.client = new TelemetryClient(clientID, apiSecret, measurementID); 216 | 217 | const version: string = parseMajorMinorVersion(extensionVersion); 218 | this.client.addUserProperty('app_version', version); 219 | 220 | this.updateTelemetryEnabled(); 221 | context.subscriptions.push( 222 | vscode.workspace.onDidChangeConfiguration(() => { 223 | this.updateTelemetryEnabled(); 224 | }) 225 | ); 226 | } 227 | 228 | get isEnabled(): boolean { 229 | return this.client.isEnabled; 230 | } 231 | 232 | set isEnabled(value: boolean) { 233 | this.client.isEnabled = value; 234 | } 235 | 236 | startSession(name: string): void { 237 | this.client.sendEvent({ 238 | name: 'activity_start', 239 | params: { 240 | name 241 | } 242 | }); 243 | } 244 | 245 | endSession(name: string): void { 246 | this.client.sendEvent({ 247 | name: 'activity_end', 248 | params: { 249 | name 250 | } 251 | }); 252 | } 253 | 254 | trackError(name: string, message: string): void { 255 | this.client.sendEvent({ 256 | name: 'error', 257 | params: { 258 | name, 259 | message 260 | } 261 | }); 262 | } 263 | 264 | trackExit(code: number | undefined, signal: string | undefined): void { 265 | this.client.sendEvent({ 266 | name: 'adapter_exit', 267 | params: { 268 | code, 269 | signal 270 | } 271 | }); 272 | } 273 | 274 | trackCommand(name: string): CommandTracker { 275 | type CommandTrackerStatus = 'initiated' | 'canceled' | 'completed'; 276 | 277 | let status: CommandTrackerStatus = 'initiated'; 278 | let reason: string | undefined; 279 | 280 | const tracker = { 281 | service: this, 282 | 283 | cancel(cancelReason: string): void { 284 | status = 'canceled'; 285 | reason = cancelReason; 286 | }, 287 | 288 | complete(): void { 289 | status = 'completed'; 290 | }, 291 | 292 | dispose(): void { 293 | this.service.client.sendEvent({ 294 | name: 'exec_command', 295 | params: { 296 | name, 297 | status, 298 | reason 299 | } 300 | }); 301 | } 302 | }; 303 | 304 | return tracker; 305 | } 306 | 307 | trackSystemInfo(location: TelemetryLocation, systemInfo?: types.SystemInfo): void { 308 | const events: Event[] = []; 309 | 310 | if (systemInfo?.os) { 311 | events.push({ 312 | name: 'os_info', 313 | params: { 314 | location, 315 | platform: systemInfo?.os?.platform, 316 | architecture: systemInfo?.os?.architecture, 317 | distribution: systemInfo?.os?.distribution, 318 | distribution_version: systemInfo?.os?.distributionVersion 319 | } 320 | } as OsInfoEvent); 321 | } 322 | 323 | if (systemInfo?.gpus?.length) { 324 | // eslint-disable-next-line no-restricted-syntax 325 | for (const gpu of systemInfo.gpus) { 326 | events.push({ 327 | name: 'gpu_info', 328 | params: { 329 | location, 330 | name: gpu.name, 331 | description: gpu.description, 332 | sm_type: gpu.smType, 333 | name_by_os: `${gpu.name}: ${systemInfo?.os?.platform}` 334 | } 335 | } as GpuInfoEvent); 336 | } 337 | } 338 | 339 | this.client.sendEvents(events); 340 | } 341 | 342 | private updateTelemetryEnabled(): void { 343 | const telemetryConfig: vscode.WorkspaceConfiguration = vscode.workspace.getConfiguration(TelemetryService.TELEMETRY_CONFIG_ID); 344 | this.isEnabled = !(telemetryConfig[TelemetryService.TELEMETRY_CONFIG_LEVEL] === 'off'); 345 | } 346 | } 347 | 348 | export interface CommandTracker extends vscode.Disposable { 349 | cancel(reason: string): void; 350 | 351 | complete(): void; 352 | 353 | dispose(): void; 354 | } 355 | 356 | function parseMajorMinorVersion(extensionVersion: string): string { 357 | const matches = extensionVersion.match(/^\d+\.\d+/); 358 | if (!matches || matches.length === 0) { 359 | return ''; 360 | } 361 | 362 | return matches[0]; 363 | } 364 | -------------------------------------------------------------------------------- /src/debugger/utils.ts: -------------------------------------------------------------------------------- 1 | /* ---------------------------------------------------------------------------------- *\ 2 | | | 3 | | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. | 4 | | | 5 | | The contents of this file are licensed under the Eclipse Public License 2.0. | 6 | | The full terms of the license are available at https://eclipse.org/legal/epl-2.0/ | 7 | | | 8 | | SPDX-License-Identifier: EPL-2.0 | 9 | | | 10 | \* ---------------------------------------------------------------------------------- */ 11 | 12 | import * as fse from 'fs-extra'; 13 | import { logger } from '@vscode/debugadapter'; 14 | import { createInterface } from 'node:readline'; 15 | 16 | import * as types from './types'; 17 | 18 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 19 | export function assertNever(value: never): never { 20 | throw new Error(`Unexpected value: '${value}'`); 21 | } 22 | 23 | function formatCudaCoord(coord?: number): string { 24 | const formattedCoord = coord?.toString() ?? '?'; 25 | return formattedCoord; 26 | } 27 | 28 | export function formatCudaDim(dim?: types.CudaDim): string { 29 | const x: string = formatCudaCoord(dim?.x); 30 | const y: string = formatCudaCoord(dim?.y); 31 | const z: string = formatCudaCoord(dim?.z); 32 | const formattedDim = `(${x}, ${y}, ${z})`; 33 | 34 | return formattedDim; 35 | } 36 | 37 | // eslint-disable-next-line consistent-return 38 | export function formatCudaFocus(focus?: types.CudaFocus): string { 39 | const focusPrefix = 'CUDA:'; 40 | 41 | if (!focus) { 42 | const undefDim: string = formatCudaDim(); 43 | const formattedFocus = `${focusPrefix} ${undefDim} ${undefDim}`; 44 | 45 | return formattedFocus; 46 | } 47 | 48 | switch (focus?.type) { 49 | case 'software': { 50 | const formattedBlock: string = formatCudaDim(focus.blockIdx); 51 | const formattedThread: string = formatCudaDim(focus.threadIdx); 52 | const formattedFocus = `${focusPrefix} ${formattedBlock} ${formattedThread}`; 53 | 54 | return formattedFocus; 55 | } 56 | 57 | case 'hardware': { 58 | const sm: string = focus.sm?.toString() ?? '?'; 59 | const warp: string = focus.warp?.toString() ?? '?'; 60 | const lane: string = focus.lane?.toString() ?? '?'; 61 | const formattedFocus = `${focusPrefix} sm ${sm} warp ${warp} lane ${lane}`; 62 | 63 | return formattedFocus; 64 | } 65 | 66 | default: { 67 | assertNever(focus); 68 | } 69 | } 70 | } 71 | 72 | export function parseCudaDim(input: string, name?: string): types.CudaDim | undefined { 73 | let dimExpr = String.raw`\(?(\d*)\s*(?:,?\s*(\d*)\s*(?:,?\s*(\d+))?)?\s*\)?`; 74 | if (name) { 75 | dimExpr = String.raw`(?:${name}\s*)` + dimExpr; 76 | } 77 | 78 | const matchGroupCount = 4; 79 | const matcher = new RegExp(dimExpr); 80 | const matches = matcher.exec(input); 81 | 82 | // There will be 4 elements in the matches array: 83 | // 84 | // [0]: Either matched expression or '' 85 | // [1]: x-value or '' 86 | // [2]: y-value or '' 87 | // [3]: z-value or '' 88 | 89 | if (!matches || matches.length !== matchGroupCount || !matches[0]) { 90 | // eslint-disable-next-line unicorn/no-useless-undefined 91 | return undefined; 92 | } 93 | 94 | // eslint-disable-next-line unicorn/consistent-function-scoping 95 | const parseCoord = (value: string): number | undefined => { 96 | const coord: number = Number.parseInt(value); 97 | return Number.isNaN(coord) ? undefined : coord; 98 | }; 99 | 100 | const x: number | undefined = parseCoord(matches[1]); 101 | const y: number | undefined = parseCoord(matches[2]); 102 | const z: number | undefined = parseCoord(matches[3]); 103 | 104 | return { x, y, z }; 105 | } 106 | 107 | function parseHwCoord(input: string, coordName: string): number | undefined { 108 | const expr = `${coordName}\\s+(\\d+)`; 109 | const regex = new RegExp(expr, 'i'); 110 | const coordMatch = regex.exec(input); 111 | 112 | if (!coordMatch) { 113 | // eslint-disable-next-line unicorn/no-useless-undefined 114 | return undefined; 115 | } 116 | 117 | const coordStr: string = coordMatch[1]; 118 | const coord: number = Number.parseInt(coordStr); 119 | 120 | return coord; 121 | } 122 | 123 | export function parseCudaHwFocus(focus: string): types.CudaHwFocus | undefined { 124 | if (!focus) { 125 | // eslint-disable-next-line unicorn/no-useless-undefined 126 | return undefined; 127 | } 128 | 129 | const sm: number | undefined = parseHwCoord(focus, 'sm'); 130 | const warp: number | undefined = parseHwCoord(focus, 'warp'); 131 | const lane: number | undefined = parseHwCoord(focus, 'lane'); 132 | 133 | if (sm === undefined && warp === undefined && lane === undefined) { 134 | // eslint-disable-next-line unicorn/no-useless-undefined 135 | return undefined; 136 | } 137 | 138 | return { type: 'hardware', sm, warp, lane }; 139 | } 140 | 141 | export function parseCudaSwFocus(focus: string): types.CudaSwFocus | undefined { 142 | if (!focus) { 143 | // eslint-disable-next-line unicorn/no-useless-undefined 144 | return undefined; 145 | } 146 | 147 | const blockIdx: types.CudaDim | undefined = parseCudaDim(focus, 'block'); 148 | const threadIdx: types.CudaDim | undefined = parseCudaDim(focus, 'thread'); 149 | 150 | if (blockIdx === undefined && threadIdx === undefined) { 151 | // eslint-disable-next-line unicorn/no-useless-undefined 152 | return undefined; 153 | } 154 | 155 | return { type: 'software', blockIdx, threadIdx }; 156 | } 157 | 158 | export function equalsCudaDim(lhs?: types.CudaDim, rhs?: types.CudaDim): boolean { 159 | if (!lhs || !rhs) { 160 | return false; 161 | } 162 | 163 | // prettier-ignore 164 | const equals: boolean = 165 | lhs.x === rhs.x 166 | && lhs.y === rhs.y 167 | && lhs.z === rhs.z; 168 | 169 | return equals; 170 | } 171 | 172 | export function equalsCudaFocus(lhs?: types.CudaFocus, rhs?: types.CudaFocus): boolean { 173 | if (!lhs || !rhs) { 174 | return false; 175 | } 176 | 177 | if (lhs.type === 'software' && rhs.type === 'software') { 178 | const equalsBlock = equalsCudaDim(lhs.blockIdx, rhs.blockIdx); 179 | const equalsThread = equalsCudaDim(lhs.threadIdx, rhs.threadIdx); 180 | const equals: boolean = equalsBlock && equalsThread; 181 | 182 | return equals; 183 | } 184 | 185 | if (lhs.type === 'hardware' && rhs.type === 'hardware') { 186 | // prettier-ignore 187 | const equals: boolean = 188 | lhs.sm === rhs.sm 189 | && lhs.warp === rhs.warp 190 | && lhs.lane === rhs.lane; 191 | 192 | return equals; 193 | } 194 | 195 | return false; 196 | } 197 | 198 | export function isCudaDimValid(dim?: types.CudaDim): boolean { 199 | if (!dim) { 200 | return false; 201 | } 202 | 203 | // prettier-ignore 204 | const isValid: boolean = 205 | dim.x !== undefined 206 | || dim.y !== undefined 207 | || dim.z !== undefined; 208 | 209 | return isValid; 210 | } 211 | 212 | // eslint-disable-next-line consistent-return 213 | export function isCudaFocusValid(focus?: types.CudaFocus): boolean { 214 | if (!focus) { 215 | return false; 216 | } 217 | 218 | switch (focus.type) { 219 | case 'software': { 220 | const isBlockValid: boolean = isCudaDimValid(focus.blockIdx); 221 | const isThreadValid: boolean = isCudaDimValid(focus.threadIdx); 222 | const isValid: boolean = isBlockValid && isThreadValid; 223 | 224 | return isValid; 225 | } 226 | 227 | case 'hardware': { 228 | // prettier-ignore 229 | const isValid: boolean = 230 | focus.sm !== undefined 231 | || focus.warp !== undefined 232 | || focus.lane !== undefined; 233 | 234 | return isValid; 235 | } 236 | 237 | default: { 238 | assertNever(focus); 239 | } 240 | } 241 | } 242 | 243 | export function formatSetDimCommand(name: string, dim?: types.CudaDim): string { 244 | if (!dim || dim.x === undefined) { 245 | return ''; 246 | } 247 | 248 | let setFocusCommand = `${name} (${dim.x}`; 249 | 250 | if (dim.y !== undefined) { 251 | setFocusCommand += `, ${dim.y}`; 252 | 253 | if (dim.z !== undefined) { 254 | setFocusCommand += `, ${dim.z}`; 255 | } 256 | } 257 | 258 | setFocusCommand += ')'; 259 | return setFocusCommand; 260 | } 261 | 262 | export function formatSetFocusCommand(focus?: types.CudaFocus): string { 263 | if (!focus) { 264 | return ''; 265 | } 266 | 267 | let setFocusCommand = '-cuda-focus-switch'; 268 | 269 | if (focus.type === 'software') { 270 | const setBlockCommand = formatSetDimCommand('block', focus.blockIdx); 271 | const setThreadCommand = formatSetDimCommand('thread', focus.threadIdx); 272 | setFocusCommand += ` ${setBlockCommand} ${setThreadCommand}`; 273 | } else if (focus.type === 'hardware') { 274 | if (focus?.sm !== undefined) { 275 | setFocusCommand += ` sm ${focus.sm}`; 276 | } 277 | 278 | if (focus?.warp !== undefined) { 279 | setFocusCommand += ` warp ${focus.warp}`; 280 | } 281 | 282 | if (focus?.lane !== undefined) { 283 | setFocusCommand += ` lane ${focus.lane}`; 284 | } 285 | } else { 286 | assertNever(focus); 287 | } 288 | 289 | return setFocusCommand; 290 | } 291 | 292 | async function readReleaseFile(releaseFile: string): Promise> { 293 | const fileStream: fse.ReadStream = fse.createReadStream(releaseFile); 294 | const fileInterface = createInterface({ 295 | input: fileStream 296 | }); 297 | 298 | try { 299 | const fileInfo: Record = {}; 300 | 301 | // eslint-disable-next-line no-restricted-syntax 302 | for await (const fileLine of fileInterface) { 303 | const [key, value] = fileLine.split('='); 304 | if (key && value) { 305 | const normalizedValue: string = value.replaceAll(/[\r"']/gi, ''); 306 | fileInfo[key] = normalizedValue; 307 | } 308 | } 309 | 310 | return fileInfo; 311 | } finally { 312 | if (fileInterface) { 313 | fileInterface.close(); 314 | } 315 | 316 | if (fileStream) { 317 | fileStream.destroy(); 318 | } 319 | } 320 | } 321 | 322 | export async function readOsInfo(): Promise { 323 | const releaseFiles: string[] = ['/etc/os-release', '/usr/lib/os-release']; 324 | 325 | const osInfo: types.OsInfo = { 326 | platform: process.platform, 327 | architecture: process.arch 328 | }; 329 | 330 | if (process.platform === 'linux') { 331 | // eslint-disable-next-line no-restricted-syntax 332 | for (const releaseFile of releaseFiles) { 333 | try { 334 | // eslint-disable-next-line no-await-in-loop 335 | const releaseFileInfo = await readReleaseFile(releaseFile); 336 | if (releaseFileInfo) { 337 | osInfo.distribution = releaseFileInfo.ID; 338 | osInfo.distributionVersion = releaseFileInfo.VERSION_ID; 339 | break; 340 | } 341 | } catch (error) { 342 | const message = `Failed to read OS release file: ${(error as Error).message}`; 343 | logger.error(message); 344 | } 345 | } 346 | } 347 | 348 | return osInfo; 349 | } 350 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 2 | 3 | All files in this repository are licensed under terms of the Eclipse Public License 4 | 2.0 (SPDX-License-Identifier: EPL-2.0) with the following exceptions: 5 | 6 | - The test source file "variables.cu" (./src/test/testPrograms/variables/variables.cu) 7 | is licensed under an MIT license (SPDX-License-Identifier: MIT). 8 | 9 | - The contents of the script "prepare.js" (./prepare.js) are licensed under an MIT 10 | license (SPDX-License-Identifier: MIT). 11 | 12 | The terms of the MIT and EPL 2.0 license are listed at the end of this file. 13 | 14 | This project uses cdt-gdb-adapter, which is licensed under an EPL 2.0 license. The 15 | source code for cdt-gdb-adapter is publicly available at the following address: 16 | 17 | https://github.com/eclipse-cdt/cdt-gdb-adapter 18 | 19 | We use a specialized fork of cdt-gdb-adapter, which is available at the following 20 | address: 21 | 22 | https://github.com/NVIDIA/cdt-gdb-adapter 23 | 24 | The copyright notices for our third-party dependencies (including cdt-gdb-adapter) 25 | is listed in third-party-notices.txt located at the root of this repository. 26 | 27 | ******************************************************************************** 28 | 29 | The MIT License (MIT) 30 | 31 | Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 32 | 33 | Permission is hereby granted, free of charge, to any person obtaining a copy of 34 | this software and associated documentation files (the "Software"), to deal in 35 | the Software without restriction, including without limitation the rights to 36 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 37 | the Software, and to permit persons to whom the Software is furnished to do so, 38 | subject to the following conditions: 39 | 40 | The above copyright notice and this permission notice shall be included in all 41 | copies or substantial portions of the Software. 42 | 43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 44 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 45 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 46 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 47 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 48 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 49 | 50 | ******************************************************************************** 51 | 52 | Eclipse Public License - v 2.0 53 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE (“AGREEMENT”). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 54 | 55 | 1. DEFINITIONS 56 | “Contribution” means: 57 | 58 | a) in the case of the initial Contributor, the initial content Distributed under this Agreement, and 59 | b) in the case of each subsequent Contributor: 60 | i) changes to the Program, and 61 | ii) additions to the Program; 62 | where such changes and/or additions to the Program originate from and are Distributed by that particular Contributor. A Contribution “originates” from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include changes or additions to the Program that are not Modified Works. 63 | “Contributor” means any person or entity that Distributes the Program. 64 | 65 | “Licensed Patents” mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. 66 | 67 | “Program” means the Contributions Distributed in accordance with this Agreement. 68 | 69 | “Recipient” means anyone who receives the Program under this Agreement or any Secondary License (as applicable), including Contributors. 70 | 71 | “Derivative Works” shall mean any work, whether in Source Code or other form, that is based on (or derived from) the Program and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. 72 | 73 | “Modified Works” shall mean any work in Source Code or other form that results from an addition to, deletion from, or modification of the contents of the Program, including, for purposes of clarity any new file in Source Code form that contains any contents of the Program. Modified Works shall not include works that contain only declarations, interfaces, types, classes, structures, or files of the Program solely in each case in order to link to, bind by name, or subclass the Program or Modified Works thereof. 74 | 75 | “Distribute” means the acts of a) distributing or b) making available in any manner that enables the transfer of a copy. 76 | 77 | “Source Code” means the form of a Program preferred for making modifications, including but not limited to software source code, documentation source, and configuration files. 78 | 79 | “Secondary License” means either the GNU General Public License, Version 2.0, or any later versions of that license, including any exceptions or additional permissions as identified by the initial Contributor. 80 | 81 | 2. GRANT OF RIGHTS 82 | a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, Distribute and sublicense the Contribution of such Contributor, if any, and such Derivative Works. 83 | b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in Source Code or other form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. 84 | c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to Distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. 85 | d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. 86 | e) Notwithstanding the terms of any Secondary License, no Contributor makes additional grants to any Recipient (other than those set forth in this Agreement) as a result of such Recipient's receipt of the Program under the terms of a Secondary License (if permitted under the terms of Section 3). 87 | 3. REQUIREMENTS 88 | 3.1 If a Contributor Distributes the Program in any form, then: 89 | 90 | a) the Program must also be made available as Source Code, in accordance with section 3.2, and the Contributor must accompany the Program with a statement that the Source Code for the Program is available under this Agreement, and informs Recipients how to obtain it in a reasonable manner on or through a medium customarily used for software exchange; and 91 | b) the Contributor may Distribute the Program under a license different than this Agreement, provided that such license: 92 | i) effectively disclaims on behalf of all other Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; 93 | ii) effectively excludes on behalf of all other Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; 94 | iii) does not attempt to limit or alter the recipients' rights in the Source Code under section 3.2; and 95 | iv) requires any subsequent distribution of the Program by any party to be under a license that satisfies the requirements of this section 3. 96 | 3.2 When the Program is Distributed as Source Code: 97 | 98 | a) it must be made available under this Agreement, or if the Program (i) is combined with other material in a separate file or files made available under a Secondary License, and (ii) the initial Contributor attached to the Source Code the notice described in Exhibit A of this Agreement, then the Program may be made available under the terms of such Secondary Licenses, and 99 | b) a copy of this Agreement must be included with each copy of the Program. 100 | 3.3 Contributors may not remove or alter any copyright, patent, trademark, attribution notices, disclaimers of warranty, or limitations of liability (‘notices’) contained within the Program from any copy of the Program which they Distribute, provided that Contributors may add their own appropriate notices. 101 | 102 | 4. COMMERCIAL DISTRIBUTION 103 | Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor (“Commercial Contributor”) hereby agrees to defend and indemnify every other Contributor (“Indemnified Contributor”) against any losses, damages and costs (collectively “Losses”) arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. 104 | 105 | For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 106 | 107 | 5. NO WARRANTY 108 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 109 | 110 | 6. DISCLAIMER OF LIABILITY 111 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 112 | 113 | 7. GENERAL 114 | If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. 115 | 116 | If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. 117 | 118 | All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. 119 | 120 | Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be Distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to Distribute the Program (including its Contributions) under the new version. 121 | 122 | Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. Nothing in this Agreement is intended to be enforceable by any entity that is not a Contributor or Recipient. No third-party beneficiary rights are created under this Agreement. 123 | 124 | Exhibit A – Form of Secondary Licenses Notice 125 | “This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), version(s), and exceptions or additional permissions here}.” 126 | 127 | Simply including a copy of this Agreement, including this Exhibit A is not sufficient to license the Source Code under Secondary Licenses. 128 | 129 | If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. 130 | 131 | You may add additional accurate notices of copyright ownership. 132 | 133 | ******************************************************************************** --------------------------------------------------------------------------------