NVIDIA CUDA Toolkit Symbol Serverを使う

## 1. はじめに

[NVIDIA CUDA Toolkit Symbol Server | NVIDIA Technical Blog](https://developer.nvidia.com/blog/cuda-toolkit-symbol-server/)にてCUDA Runtime(cudart)にシンボルを付与する方法が紹介されています。このブログ記事に

> When you have to profile and debug applications using CUDA and want to share a call stack with NVIDIA for analysis, use the CUDA symbol server. Profiling and debugging will be faster and easier.

とあるようにシンボル名があることでCUDAプログラムのコールスタックを把握しやすくなり、デバッグやプロファイリングで有用です。ただし、

> We are only hosting symbol files, so debug data will not be distributed. The symbol files contain obfuscated symbol names.

とあるように配布されているファイルにはデバッグ情報が含まれていない点に注意が必要です。

ここではシンボル付きCUDA Runtime(cudart)作成について公式ブログ記事の内容を参考にして試してみました（ブログ記事で書かれていない情報も少し補足しています）。また、

> Now, NVIDIA is making available a repository of CUDA Toolkit symbols for Linux.

とあり、この記事ではUbuntu 22.04(x86_64)の環境上で確認しています。

## 2. 動作確認環境

- Ubuntu 22.04(x86_64)
- CUDA 12.2 Update 2

## 3. 前準備

### 3.1 パッケージインストール

必要なパッケージをインストールします。

```shell
$ sudo apt install wget elfutils
```

### 3.2 CUDA Runtime(cudart)のバージョン確認

まず、CUDA Runtime(cudart)のバージョンを確認します。

```shell
$ ls -l /usr/local/cuda-12.2/targets/x86_64-linux/lib/libcudart.so.*
lrwxrwxrwx 1 root root     21  8月 16 14:28 /usr/local/cuda-12.2/targets/x86_64-linux/lib/libcudart.so.12 -> libcudart.so.12.2.140
-rw-r--r-- 1 root root 683360  8月 16 14:28 /usr/local/cuda-12.2/targets/x86_64-linux/lib/libcudart.so.12.2.140
```

この結果の

> libcudart.so.12.2.140

という箇所から、CUDA Runtime(cudart)のバージョンが`12.2.140`であることがわかります。

### 3.3 CUDA Runtime(cudart)のビルドID確認

readelfコマンドでCUDA Runtime(cudart)のビルドIDを確認します。

```shell
$ readelf -n /usr/local/cuda/lib64/libcudart.so

Displaying notes found in: .note.gnu.build-id
  所有者            データサイズ	Description
  GNU                  0x00000014	NT_GNU_BUILD_ID (一意なビルドID ビット列)
    ビルドID: 8606246166aa801e7f467deffc45d9eb4089bb22
```

この結果の

> ビルドID: 8606246166aa801e7f467deffc45d9eb4089bb22

という箇所から、CUDA Runtime(cudart)のビルドIDが`8606246166aa801e7f467deffc45d9eb4089bb22`であることがわかります。

## 4. シンボル付きCUDA Runtime(cudart)作成

### 4.1 シンボルファイルダウンロード

シンボルファイルのURLは以下の通りです。`<CUDART_VERSION>`は3.2で確認したバージョン、`<CUDART_BUILD_ID>`は3.3で確認したビルドIDです。

```shell
https://cudatoolkit-symbols.nvidia.com/libcudart.so/<CUDART_BUILD_ID>/libcudart.so.<CUDART_VERSION>.sym
```

今回用いたcudartライブラリは

- `CUDART_VERSION`：12.2.140
- `CUDART_BUILD_ID`：8606246166aa801e7f467deffc45d9eb4089bb22

であるため、wgetコマンドを用いてシンボルファイルをダウンロードします。

```shell
$ wget https://cudatoolkit-symbols.nvidia.com/libcudart.so/8606246166aa801e7f467deffc45d9eb4089bb22/libcudart.so.12.2.140.sym
```

どのバージョンのシンボルが配布されているかを確認するときは<https://cudatoolkit-symbols.nvidia.com/>を確認するとよいでしょう。

### 4.2 シンボル付きバイナリ作成

eu-unstripコマンドでシンボル付きバイナリを作成します。必要に応じて元のライブラリを退避しておきます。

```shell
$ eu-unstrip /usr/local/cuda-12.2/targets/x86_64-linux/lib/libcudart.so.12.2.140 libcudart.so.12.2.140.sym -o /usr/local/cuda-12.2/targets/x86_64-linux/lib/libcudart.so.12.2.140
```

それでは、シンボル情報が付与されているか確認します。以下の結果からCUDA APIのシンボルが付与されていることがわかります。

```shell
$ nm /usr/local/cuda-12.2/targets/x86_64-linux/lib/libcudart.so.12.2.140 | grep cuda
0000000000022e10 T __cudaGetKernel
0000000000022bf0 T __cudaInitModule
0000000000022e80 T __cudaLaunchKernel
0000000000022e90 T __cudaLaunchKernel_ptsz
0000000000022d50 T __cudaPopCallConfiguration
0000000000022cc0 T __cudaPushCallConfiguration
00000000000229a0 T __cudaRegisterFatBinary
00000000000229d0 T __cudaRegisterFatBinaryEnd
0000000000022b80 T __cudaRegisterFunction
0000000000022b40 T __cudaRegisterHostVar
0000000000022ac0 T __cudaRegisterManagedVar
0000000000022ea0 T __cudaRegisterUnifiedTable
0000000000022a40 T __cudaRegisterVar
0000000000022a00 T __cudaUnregisterFatBinary
0000000000056510 T cudaArrayGetInfo
0000000000056940 T cudaArrayGetMemoryRequirements
0000000000056750 T cudaArrayGetPlane
0000000000056d20 T cudaArrayGetSparseProperties
```

### 4.3 動作確認

ここでは適当なサンプルコードを用いて動作確認します。

```shell
$ git clone https://github.com/NVIDIA/cuda-samples.git
$ cd cuda-samples/Samples/0_Introduction/vectorAdd
```

ここではデバッグ情報を付与してビルドします。

```shell
$ dbg=1 make
```

GDBでデバッグします。ここではcuDeviceGet関数にブレークポイントを設定して実行します。

```shell
$ gdb ./vectorAdd
$ b cuDeviceGet
$ r
```

以下の結果からバックトレースできていることがわかります。

```shell
(gdb) bt
#0  0x00007fc55a8c0550 in cuDeviceGet () from /lib/x86_64-linux-gnu/libcuda.so.1
#1  0x000055d4a5f06ffd in libcudart_static_de551314bb7dd67042b66b9ee43327299f210cb3 ()
#2  0x000055d4a5f0a9b9 in libcudart_static_d83112b48ad6c0c45820fdbef0d64eff96bd4715 ()
#3  0x000055d4a5f163e0 in libcudart_static_0bf7336e71b5df655f7fe4ef2dea52179e6fcf82 ()
#4  0x00007fc55c448f68 in __pthread_once_slow (once_control=0x55d4a5fa5d38 <libcudart_static_08cd9c81021de025f90d77a527f44bb6a85f7117>, init_routine=0x55d4a5f16370 <libcudart_static_0bf7336e71b5df655f7fe4ef2dea52179e6fcf82>) at ./nptl/pthread_once.c:116
#5  0x000055d4a5f663d9 in libcudart_static_5887a27cefafb4cd438bdc166b0a6f874b079d4b ()
#6  0x000055d4a5f0a48f in libcudart_static_418eebf4e9b7463362b8385a31d08da131d0ea88 ()
#7  0x000055d4a5f3bf6d in cudaMalloc ()
#8  0x000055d4a5ef2d7b in main () at /cuda-samples/Samples/0_Introduction/vectorAdd/vectorAdd.cu:92
```

## その他

### cudartバージョンによってはシンボルファイルが提供されていない

CUDA Runtime(cudart)のバージョン、ビルドIDを確認します。

```shell
$ ls -l /usr/local/cuda-12.2/targets/x86_64-linux/lib/libcudart.so.*
lrwxrwxrwx 1 root root     20  5月  1 02:31 /usr/local/cuda-12.2/targets/x86_64-linux/lib/libcudart.so.12 -> libcudart.so.12.2.53
-rw-r--r-- 1 root root 687456  5月  1 02:31 /usr/local/cuda-12.2/targets/x86_64-linux/lib/libcudart.so.12.2.53
$ readelf -n /usr/local/cuda/lib64/libcudart.so

Displaying notes found in: .note.gnu.build-id
  所有者            データサイズ	Description
  GNU                  0x00000014	NT_GNU_BUILD_ID (一意なビルドID ビット列)
    ビルドID: c1ed6973325dbf8b37e751544850cc5aab201bf0
```

確認した結果を用いてシンボルファイルのダウンロードを行いましたが403エラーが出てしまいました。CUDA Runtime(cudart)バージョンによってはシンボルファイルが提供されていないようです。

```shell
$ wget https://cudatoolkit-symbols.nvidia.com/libcudart.so/c1ed6973325dbf8b37e751544850cc5aab201bf0/libcudart.so.12.2.53.sym
--2023-09-09 06:44:53--  https://cudatoolkit-symbols.nvidia.com/libcudart.so/c1ed6973325dbf8b37e751544850cc5aab201bf0/libcudart.so.12.2.53.sym
cudatoolkit-symbols.nvidia.com (cudatoolkit-symbols.nvidia.com) をDNSに問いあわせています... 99.84.54.97, 99.84.54.35, 99.84.54.2, ...
cudatoolkit-symbols.nvidia.com (cudatoolkit-symbols.nvidia.com)|99.84.54.97|:443 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 403 Forbidden
2023-09-09 06:44:54 エラー 403: Forbidden。
```