1 | //===- NVPTXArch.cpp - list installed NVPTX devies ------*- C++ -*---------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file implements a tool for detecting name of CUDA gpus installed in the |
10 | // system. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "clang/Basic/Version.h" |
15 | #include "llvm/Support/CommandLine.h" |
16 | #include "llvm/Support/DynamicLibrary.h" |
17 | #include "llvm/Support/Error.h" |
18 | #include <cstdint> |
19 | #include <cstdio> |
20 | #include <memory> |
21 | |
22 | using namespace llvm; |
23 | |
24 | static cl::opt<bool> Help("h" , cl::desc("Alias for -help" ), cl::Hidden); |
25 | |
26 | static void PrintVersion(raw_ostream &OS) { |
27 | OS << clang::getClangToolFullVersion(ToolName: "nvptx-arch" ) << '\n'; |
28 | } |
29 | // Mark all our options with this category, everything else (except for -version |
30 | // and -help) will be hidden. |
31 | static cl::OptionCategory NVPTXArchCategory("nvptx-arch options" ); |
32 | |
33 | typedef enum cudaError_enum { |
34 | CUDA_SUCCESS = 0, |
35 | CUDA_ERROR_NO_DEVICE = 100, |
36 | } CUresult; |
37 | |
38 | typedef enum CUdevice_attribute_enum { |
39 | CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR = 75, |
40 | CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR = 76, |
41 | } CUdevice_attribute; |
42 | |
43 | typedef uint32_t CUdevice; |
44 | |
45 | CUresult (*cuInit)(unsigned int); |
46 | CUresult (*cuDeviceGetCount)(int *); |
47 | CUresult (*cuGetErrorString)(CUresult, const char **); |
48 | CUresult (*cuDeviceGet)(CUdevice *, int); |
49 | CUresult (*cuDeviceGetAttribute)(int *, CUdevice_attribute, CUdevice); |
50 | |
51 | constexpr const char *DynamicCudaPath = "libcuda.so.1" ; |
52 | |
53 | llvm::Error loadCUDA() { |
54 | std::string ErrMsg; |
55 | auto DynlibHandle = std::make_unique<llvm::sys::DynamicLibrary>( |
56 | args: llvm::sys::DynamicLibrary::getPermanentLibrary(filename: DynamicCudaPath, errMsg: &ErrMsg)); |
57 | if (!DynlibHandle->isValid()) { |
58 | return llvm::createStringError(EC: llvm::inconvertibleErrorCode(), |
59 | Fmt: "Failed to 'dlopen' %s" , Vals: DynamicCudaPath); |
60 | } |
61 | #define DYNAMIC_INIT(SYMBOL) \ |
62 | { \ |
63 | void *SymbolPtr = DynlibHandle->getAddressOfSymbol(#SYMBOL); \ |
64 | if (!SymbolPtr) \ |
65 | return llvm::createStringError(llvm::inconvertibleErrorCode(), \ |
66 | "Failed to 'dlsym' " #SYMBOL); \ |
67 | SYMBOL = reinterpret_cast<decltype(SYMBOL)>(SymbolPtr); \ |
68 | } |
69 | DYNAMIC_INIT(cuInit); |
70 | DYNAMIC_INIT(cuDeviceGetCount); |
71 | DYNAMIC_INIT(cuGetErrorString); |
72 | DYNAMIC_INIT(cuDeviceGet); |
73 | DYNAMIC_INIT(cuDeviceGetAttribute); |
74 | #undef DYNAMIC_INIT |
75 | return llvm::Error::success(); |
76 | } |
77 | |
78 | static int handleError(CUresult Err) { |
79 | const char *ErrStr = nullptr; |
80 | CUresult Result = cuGetErrorString(Err, &ErrStr); |
81 | if (Result != CUDA_SUCCESS) |
82 | return 1; |
83 | fprintf(stderr, format: "CUDA error: %s\n" , ErrStr); |
84 | return 1; |
85 | } |
86 | |
87 | int main(int argc, char *argv[]) { |
88 | cl::HideUnrelatedOptions(Category&: NVPTXArchCategory); |
89 | |
90 | cl::SetVersionPrinter(PrintVersion); |
91 | cl::ParseCommandLineOptions( |
92 | argc, argv, |
93 | Overview: "A tool to detect the presence of NVIDIA devices on the system. \n\n" |
94 | "The tool will output each detected GPU architecture separated by a\n" |
95 | "newline character. If multiple GPUs of the same architecture are found\n" |
96 | "a string will be printed for each\n" ); |
97 | |
98 | if (Help) { |
99 | cl::PrintHelpMessage(); |
100 | return 0; |
101 | } |
102 | |
103 | // Attempt to load the NVPTX driver runtime. |
104 | if (llvm::Error Err = loadCUDA()) { |
105 | logAllUnhandledErrors(E: std::move(Err), OS&: llvm::errs()); |
106 | return 1; |
107 | } |
108 | |
109 | if (CUresult Err = cuInit(0)) { |
110 | if (Err == CUDA_ERROR_NO_DEVICE) |
111 | return 0; |
112 | else |
113 | return handleError(Err); |
114 | } |
115 | |
116 | int Count = 0; |
117 | if (CUresult Err = cuDeviceGetCount(&Count)) |
118 | return handleError(Err); |
119 | if (Count == 0) |
120 | return 0; |
121 | for (int DeviceId = 0; DeviceId < Count; ++DeviceId) { |
122 | CUdevice Device; |
123 | if (CUresult Err = cuDeviceGet(&Device, DeviceId)) |
124 | return handleError(Err); |
125 | |
126 | int32_t Major, Minor; |
127 | if (CUresult Err = cuDeviceGetAttribute( |
128 | &Major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, Device)) |
129 | return handleError(Err); |
130 | if (CUresult Err = cuDeviceGetAttribute( |
131 | &Minor, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MINOR, Device)) |
132 | return handleError(Err); |
133 | |
134 | printf(format: "sm_%d%d\n" , Major, Minor); |
135 | } |
136 | return 0; |
137 | } |
138 | |