├── .gitignore ├── LICENSE ├── include ├── nvidia-query-resource-opengl-ipc.h ├── nvidia-query-resource-opengl-data.h ├── nvidia-query-resource-opengl-ipc-util.h └── nvidia-query-resource-opengl.h ├── CMakeLists.txt ├── common └── nvidia-query-resource-opengl-ipc-util.c ├── README.md ├── tool ├── nvidia-query-resource-opengl-data.c ├── main.c └── nvidia-query-resource-opengl.c └── preload └── nvidia-query-resource-opengl-preload.c /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeCache.txt 2 | CMakeFiles 3 | Makefile 4 | cmake_install.cmake 5 | *.o 6 | *.a 7 | *.so 8 | *.lib 9 | *.exe 10 | nvidia-query-resource-opengl 11 | *.sw[op] 12 | *~ 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /include/nvidia-query-resource-opengl-ipc.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __NVIDIA_QUERY_RESOURCE_OPENGL_IPC_H__ 24 | #define __NVIDIA_QUERY_RESOURCE_OPENGL_IPC_H__ 25 | 26 | #include "nvidia-query-resource-opengl-data.h" 27 | 28 | // Data types used by the IPC between queryResource client and server 29 | 30 | typedef enum { 31 | NVQR_QUERY_CONNECT = 1, 32 | NVQR_QUERY_MEMORY_INFO, 33 | NVQR_QUERY_DISCONNECT 34 | } NVQRqueryOp; 35 | 36 | typedef struct NVQRQueryCmdBufferRec { 37 | NVQRqueryOp op; 38 | int queryType; 39 | int pid; 40 | } NVQRQueryCmdBuffer; 41 | 42 | typedef struct NVQRQueryDataBufferRec { 43 | NVQRqueryOp op; 44 | int cnt; 45 | NVQRQueryData_t data[NVQR_MAX_DATA_BUFFER_LEN]; 46 | } NVQRQueryDataBuffer; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /include/nvidia-query-resource-opengl-data.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __NVIDIA_QUERY_RESOURCE_OPENGL_DATA_H__ 24 | #define __NVIDIA_QUERY_RESOURCE_OPENGL_DATA_H__ 25 | 26 | // Data types for interpreting data returned by the queryResource extension 27 | 28 | #define NVQR_DATA_FORMAT_VERSION 2 29 | #define NVQR_MAX_DATA_BUFFER_LEN 1024 30 | 31 | typedef int NVQRQueryData_t; 32 | 33 | typedef struct NVQRQueryDataHeaderRec { 34 | NVQRQueryData_t headerBlkSize; 35 | NVQRQueryData_t version; 36 | NVQRQueryData_t numDevices; 37 | } NVQRQueryDataHeader; 38 | 39 | typedef struct NVQRQueryDeviceInfoRec { 40 | NVQRQueryData_t deviceBlkSize; 41 | NVQRQueryData_t summaryBlkSize; 42 | NVQRQueryData_t totalAllocs; 43 | NVQRQueryData_t vidMemUsedkiB; 44 | NVQRQueryData_t vidMemFreekiB; 45 | NVQRQueryData_t numDetailBlocks; 46 | } NVQRQueryDeviceInfo; 47 | 48 | typedef struct NVQRQueryDetailInfoRec { 49 | NVQRQueryData_t detailBlkSize; 50 | NVQRQueryData_t memType; 51 | NVQRQueryData_t objectType; 52 | NVQRQueryData_t numAllocs; 53 | NVQRQueryData_t memUsedkiB; 54 | } NVQRQueryDetailInfo; 55 | 56 | typedef struct NVQRTagBlockRec { 57 | NVQRQueryData_t tagBlkSize; 58 | NVQRQueryData_t tagId; 59 | NVQRQueryData_t deviceId; 60 | NVQRQueryData_t numAllocs; 61 | NVQRQueryData_t vidmemUsedkiB; 62 | NVQRQueryData_t tagLength; 63 | char tag[4]; 64 | } NVQRTagBlock; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 2 | # 3 | # Permission is hereby granted, free of charge, to any person obtaining a 4 | # copy of this software and associated documentation files (the "Software"), 5 | # to deal in the Software without restriction, including without limitation 6 | # the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | # and/or sell copies of the Software, and to permit persons to whom the 8 | # Software is furnished to do so, subject to the following conditions: 9 | # 10 | # The above copyright notice and this permission notice shall be included in 11 | # all copies or substantial portions of the Software. 12 | # 13 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 16 | # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | # DEALINGS IN THE SOFTWARE. 20 | 21 | cmake_minimum_required (VERSION 2.6) 22 | 23 | project (nvidia-query-resource-opengl) 24 | 25 | include_directories(include) 26 | 27 | # The query tool executable 28 | 29 | add_executable (nvqrgl-bin 30 | tool/main.c 31 | ) 32 | set_target_properties (nvqrgl-bin PROPERTIES 33 | OUTPUT_NAME nvidia-query-resource-opengl 34 | ) 35 | 36 | # Static library for custom clients 37 | 38 | add_library (nvqrgl-lib STATIC 39 | common/nvidia-query-resource-opengl-ipc-util.c 40 | tool/nvidia-query-resource-opengl.c 41 | tool/nvidia-query-resource-opengl-data.c 42 | ) 43 | set_target_properties (nvqrgl-lib PROPERTIES 44 | OUTPUT_NAME nvidia-query-resource-opengl 45 | ) 46 | 47 | # Socket functionality is in a separate libsocket library on Solaris 48 | 49 | include (CheckLibraryExists) 50 | 51 | CHECK_LIBRARY_EXISTS (socket bind "" SOCKET_LIBRARY) 52 | if (SOCKET_LIBRARY) 53 | set(LINK_SOCKET socket) 54 | endif () 55 | 56 | target_link_libraries (nvqrgl-bin nvqrgl-lib ${LINK_SOCKET}) 57 | 58 | # Build the preload library on Unix 59 | 60 | if (NOT WIN32) 61 | add_library (nvidia-query-resource-opengl-preload SHARED 62 | common/nvidia-query-resource-opengl-ipc-util.c 63 | preload/nvidia-query-resource-opengl-preload.c 64 | ) 65 | 66 | # Find GL and X11 include / link paths 67 | 68 | # XXX find_path() doesn't seem to work on Solaris, but it's okay, 69 | # since the GL and X11 headers tend to be in /usr/include there. 70 | if (NOT "${CMAKE_SYSTEM_NAME}" MATCHES "SunOS") 71 | find_path (GL_INCLUDE_DIR GL/gl.h) 72 | find_path (X11_INCLUDE_DIR X11/Xlib.h) 73 | 74 | include_directories("${GL_INCLUDE_DIR}" "${X11_INCLUDE_DIR}") 75 | endif () 76 | 77 | find_library (LIBGL_PATH GL) 78 | find_library (LIBX11_PATH X11) 79 | 80 | target_link_libraries (nvidia-query-resource-opengl-preload 81 | ${LIBGL_PATH} ${LIBX11_PATH} pthread ${LINK_SOCKET} 82 | ) 83 | endif () 84 | -------------------------------------------------------------------------------- /include/nvidia-query-resource-opengl-ipc-util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __NVIDIA_QUERY_RESOURCE_IPC_UTIL_H__ 24 | #define __NVIDIA_QUERY_RESOURCE_IPC_UTIL_H__ 25 | 26 | // Windows IPC uses a pair of named pipes: one for each of the client and server 27 | #if defined (_WIN32) 28 | #include 29 | 30 | //------------------------------------------------------------------------------ 31 | // Return the pipe name for the client with the given PID in a newly allocated 32 | // buffer. The server should call this function with the PID provided by the 33 | // client, and the client should call this function with its own PID. The caller 34 | // is responsible for freeing the buffer. 35 | 36 | char *nvqr_ipc_client_pipe_name(DWORD pid); 37 | 38 | //------------------------------------------------------------------------------ 39 | // Return the pipe name for the server with the given PID in a newly allocated 40 | // buffer. The server should call this function with its own PID, and the client 41 | // should call this function with the PID of the target server. The caller is 42 | // responsible for freeing the buffer. 43 | 44 | char *nvqr_ipc_server_pipe_name(DWORD pid); 45 | 46 | // Unix IPC uses a domain socket created by the server 47 | #else 48 | #include 49 | 50 | //------------------------------------------------------------------------------ 51 | // Write the socket name for the given pid into the provided buffer. On Linux, 52 | // use the abstract namespace for domain sockets. On other Unixen, create the 53 | // socket file in /tmp. Returns the length of the socket name, which may exceed 54 | // the provided storage length. The socket name will be truncated if its length 55 | // exceeds the available storage, so the caller should check the return value of 56 | // this function to verify that the socket name was not truncated. 57 | 58 | int nvqr_ipc_get_socket_name(char *dest, size_t len, pid_t pid); 59 | 60 | #endif 61 | 62 | //------------------------------------------------------------------------------ 63 | // Both ULONG_MAX and LONG_MIN (including the negative sign '-') are twenty 64 | // characters long in decimal representation. It shouldn't be necessary to 65 | // reserve more than twenty characters to represent any integer as a string. 66 | 67 | #define NVQR_IPC_MAX_DIGIT_LENGTH 20 68 | 69 | #endif 70 | -------------------------------------------------------------------------------- /common/nvidia-query-resource-opengl-ipc-util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include "nvidia-query-resource-opengl-ipc-util.h" 28 | 29 | #if defined (_WIN32) 30 | static char *construct_name(const char *basename, DWORD pid) 31 | { 32 | static const char basedir[] = "\\\\.\\pipe\\"; 33 | size_t len = strlen(basedir) + strlen(basename) + 34 | NVQR_IPC_MAX_DIGIT_LENGTH; 35 | char *buf = calloc(len + 1, 1); 36 | 37 | if (!buf) { 38 | fprintf(stderr, "Failed to allocate memory for connection name!"); 39 | exit(1); 40 | } 41 | 42 | _snprintf_s(buf, len + 1, len, "%s%s%d", basedir, basename, pid); 43 | return buf; 44 | } 45 | 46 | char *nvqr_ipc_client_pipe_name(DWORD pid) 47 | { 48 | return construct_name("clientpipe", pid); 49 | } 50 | 51 | char *nvqr_ipc_server_pipe_name(DWORD pid) 52 | { 53 | return construct_name("serverpipe", pid); 54 | } 55 | 56 | #else 57 | 58 | int nvqr_ipc_get_socket_name(char *dest, size_t len, pid_t pid) 59 | { 60 | int total_len; 61 | static const char *basename = "nvidia-query-resource-opengl-socket."; 62 | 63 | #if __linux 64 | // Socket names in the abstract namespace are not strings; rather, the 65 | // entire contents of the sun_path field are considered. Zero out the 66 | // whole buffer to avoid surprises. 67 | memset(dest, 0, len); 68 | 69 | total_len = snprintf(dest, len, "0%s%d", basename, pid); 70 | dest[0] = '\0'; 71 | #else 72 | const char *basedir = "/tmp"; 73 | 74 | // Uncomment the below code if you want socket files to be created under 75 | // XDG_RUNTIME_DIR on FreeBSD and Solaris, when that variable is set. The 76 | // hardcoded default to "/tmp" is intentional, to facilitate use cases where 77 | // the user performing the query may not be the same as the user running the 78 | // queried process. 79 | 80 | /* 81 | basedir = getenv("XDG_RUNTIME_DIR"); 82 | if (!basedir) { 83 | basedir = "/tmp"; 84 | } 85 | */ 86 | 87 | total_len = snprintf(dest, len, "%s/%s%ld", basedir, basename, (long) pid); 88 | #endif // __linux 89 | 90 | // Terminate the string properly if truncation occurred. 91 | if (total_len >= len) { 92 | dest[len - 1] = '\0'; 93 | } 94 | 95 | return total_len; 96 | } 97 | #endif // _WIN32 98 | -------------------------------------------------------------------------------- /include/nvidia-query-resource-opengl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #ifndef __NVIDIA_QUERY_RESOURCE_OPENGL_H__ 24 | #define __NVIDIA_QUERY_RESOURCE_OPENGL_H__ 25 | 26 | #include 27 | #include 28 | 29 | #include "nvidia-query-resource-opengl-ipc.h" 30 | 31 | #if defined (_WIN32) 32 | #include 33 | typedef DWORD pid_t; 34 | #endif 35 | 36 | typedef enum { 37 | NVQR_SUCCESS = 0, 38 | NVQR_ERROR_INVALID_ARGUMENT = 2, 39 | NVQR_ERROR_NOT_SUPPORTED = 3, 40 | NVQR_ERROR_UNKNOWN = 999, 41 | } nvqrReturn_t; 42 | 43 | typedef struct { 44 | pid_t pid; 45 | char *process_name; 46 | #if defined(_WIN32) 47 | HANDLE server_handle; 48 | HANDLE client_handle; 49 | #else 50 | int server_handle; 51 | #endif 52 | } NVQRConnection; 53 | 54 | //------------------------------------------------------------------------------ 55 | // Open a connection to an OpenGL process. This process must be using an NVIDIA 56 | // driver that supports the GL_query_resource_NV extension. On Unix, this 57 | // process must additionally have libnvidia-query-resource-opengl-preload.so 58 | // preloaded into it with LD_PRELOAD. The connection may be reused for any 59 | // number of query operations as long as it is left open; however, Windows only 60 | // supports one client connection per OpenGL process. 61 | 62 | nvqrReturn_t nvqr_connect(NVQRConnection *connection, pid_t pid); 63 | 64 | //------------------------------------------------------------------------------ 65 | // Close an existing connection to an OpenGL process. 66 | 67 | nvqrReturn_t nvqr_disconnect(NVQRConnection *connection); 68 | 69 | //------------------------------------------------------------------------------ 70 | // Perform a glQueryResourceNV() query in the remote OpenGL process. The 71 | // process must be in the connected state when performing the query. 72 | 73 | nvqrReturn_t nvqr_request_meminfo(NVQRConnection c, GLenum queryType, 74 | NVQRQueryDataBuffer *buf); 75 | 76 | //------------------------------------------------------------------------------ 77 | // Decode and print out the dta buffer returned from glQueryResourceNV() 78 | // 79 | 80 | void nvqr_print_memory_info(GLenum queryType, NVQRQueryData_t *buffer); 81 | 82 | /* GL_NV_query_resource defines - these should be removed once the 83 | * extension has been finalized and these values become part of real 84 | * OpenGL header files. */ 85 | #ifndef GL_QUERY_RESOURCE_TYPE_VIDMEM_ALLOC_NV 86 | 87 | #define GL_QUERY_RESOURCE_TYPE_VIDMEM_ALLOC_NV 0x9540 88 | #define GL_QUERY_RESOURCE_MEMTYPE_VIDMEM_NV 0x9542 89 | #define GL_QUERY_RESOURCE_SYS_RESERVED_NV 0x9544 90 | #define GL_QUERY_RESOURCE_TEXTURE_NV 0x9545 91 | #define GL_QUERY_RESOURCE_RENDERBUFFER_NV 0x9546 92 | #define GL_QUERY_RESOURCE_BUFFEROBJECT_NV 0x9547 93 | 94 | #endif 95 | 96 | #endif 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | nvidia-query-resource-opengl 2 | ============================ 3 | 4 | About 5 | ----- 6 | 7 | nvidia-query-resource-opengl queries the NVIDIA OpenGL driver to determine the 8 | OpenGL resource usage of an application. OpenGL applications may query their 9 | own resource usage using the GL\_NV\_query\_resource extension, but the 10 | nvidia-query-resource-opengl tool allows users to perform resource queries 11 | externally, against unmodified OpenGL applications. 12 | 13 | Requirements 14 | ------------ 15 | 16 | * A Windows, Linux, Solaris, or FreeBSD system with an NVIDIA GPU, running a 17 | version of the NVIDIA OpenGL driver supporting the GL\_NV\_query\_resource 18 | extension. Support for this extension was introduced with the 387.xx driver 19 | release. **Note: this tool operates on the released NV extension and 20 | replaces an earlier tool that operated on the experimental NVX version of 21 | the extension.*** 22 | * CMake 2.6 or later, and a suitable build system (e.g. Windows SDK and/or 23 | Microsoft Visual Studio on Windows; make and cc/gcc on Unix-like systems) 24 | that is supported by the CMake generators on the target platform. (Not needed 25 | when using precompiled executables on Windows.) 26 | 27 | Building 28 | -------- 29 | 30 | nvidia-query-resource-opengl uses [CMake](http://www.cmake.org) to support 31 | building on multiple platforms. In order to build nvidia-query-resource-opengl, 32 | you will need to first use the CMake graphical or command line interface to 33 | generate a build system that will work on your platform, then use the generated 34 | build system to build the project. For example, on a typical Unix-like system, 35 | the following commands run from within the top level directory of this source 36 | code repository will create a "build" directory and build within it: 37 | 38 | mkdir build 39 | cd build 40 | cmake .. 41 | make 42 | 43 | On Windows, run `nmake` instead of `make` from the Visual Studio command line 44 | when using the nmake build system generator with the Windows SDK, or choose a 45 | Visual Studio solution generator to create a solution that can be built within 46 | Microsoft Visual Studio. Windows users may also download precompiled executable 47 | files for convenience. 48 | 49 | A successful build will produce the following items: 50 | 51 | * The resource query tool, 'nvidia-query-resource-opengl' (on Windows, the .exe 52 | file extension is appended to the executable name.) 53 | * A static library, 'libnvidia-query-resource-opengl.a' on Unix-like systems, 54 | or 'nvidia-query-resource-opengl.lib' on Windows. This can be used together 55 | with the API defined in include/nvidia-query-resource-opengl.h to add OpenGL 56 | resource query functionality to your own monitoring tools. 57 | * On Unix-like systems only, the 'libnvidia-query-resource-preload.so' DSO, 58 | which must be preloaded into any OpenGL applications that will be the target 59 | of resource queries. (See "Usage" section below for more details.) 60 | 61 | Usage 62 | ----- 63 | 64 | You can query an application's OpenGL resource usage by executing the command: 65 | 66 | nvidia-query-resource-opengl -p 67 | 68 | * pid: the process ID of the target OpenGL application of the query 69 | 70 | The tool reports a summary, per device, of allocated video memory, the total 71 | amount of memory in use by the the driver, and the total amount of allocated 72 | but unused memory. 73 | 74 | In addition a more detailed per object memory usage is reported. The current 75 | set of reported object types includes: 76 | - SYSTEM RESERVED - driver allocated memory 77 | - TEXTURE - memory in use by 1D/2D/3D textures 78 | - RENDERBUFFER - render buffer memory 79 | - BUFFEROBJ_ARRAY - buffer object memory 80 | 81 | Resource queries are handled asynchronously to the OpenGL applications being 82 | queried. Due to this, and other factors, including object migration between 83 | video and system memory, it is possible for subsequent queries to yield 84 | different results. 85 | 86 | On Windows, nvidia-query-resource-opengl will communicate directly with any 87 | OpenGL application to perform resource queries; however, on Unix-like systems, 88 | the libnvidia-query-resource-preload.so DSO must be preloaded into the target 89 | application before a resource query can be performed. This is achieved by 90 | setting a relative or absolute path to the preload DSO in the LD\_PRELOAD 91 | variable of the target application's environment, e.g.: 92 | 93 | $ LD_PRELOAD=path/to/libnvidia-query-resource-opengl-preload.so app 94 | -------------------------------------------------------------------------------- /tool/nvidia-query-resource-opengl-data.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #if defined (_WIN32) 28 | #include 29 | #endif 30 | #include 31 | 32 | #include "nvidia-query-resource-opengl.h" 33 | #include "nvidia-query-resource-opengl-data.h" 34 | 35 | //------------------------------------------------------------------------------ 36 | // print the tag info 37 | // 38 | static void print_tag_info(int tagCount, NVQRQueryData_t *tagBuffer) 39 | { 40 | int i; 41 | NVQRQueryData_t *ptr = tagBuffer; 42 | 43 | printf("\n number of tags = %d\n", tagCount); 44 | for (i = 0; i < tagCount; i++) { 45 | NVQRTagBlock *tag = (NVQRTagBlock *)ptr; 46 | ptr += (tag->tagBlkSize + tag->tagLength); // skip over to next tag 47 | printf(" device %d: tagId %d - %s\n", tag->deviceId, tag->tagId, tag->tag); 48 | if (tag->numAllocs != 0) { 49 | printf(" number of vidmem allocations = %d, vidmem allocated = %d kb\n", 50 | tag->numAllocs, tag->vidmemUsedkiB); 51 | } 52 | } 53 | } 54 | 55 | //------------------------------------------------------------------------------ 56 | // print the detailed memory info for a single device 57 | // 58 | static void print_detail_info(int detailCount, NVQRQueryData_t *detailBuffer) 59 | { 60 | int i; 61 | NVQRQueryData_t *ptr = detailBuffer; 62 | NVQRQueryDetailInfo *nextDetailBlk = (NVQRQueryDetailInfo *)ptr; 63 | 64 | for (i = 0; i < detailCount; i++) { 65 | NVQRQueryDetailInfo *detailBlk; 66 | 67 | detailBlk = nextDetailBlk; 68 | ptr += detailBlk->detailBlkSize; // step to next detail block 69 | nextDetailBlk = (NVQRQueryDetailInfo *)ptr; 70 | 71 | printf(" %6d kiB ", detailBlk->memUsedkiB); 72 | switch (detailBlk->objectType) { 73 | case GL_QUERY_RESOURCE_SYS_RESERVED_NV: printf("SYSTEM RESERVED"); break; 74 | case GL_QUERY_RESOURCE_TEXTURE_NV: printf("TEXTURE"); break; 75 | case GL_QUERY_RESOURCE_RENDERBUFFER_NV: printf("RENDERBUFFER"); break; 76 | case GL_QUERY_RESOURCE_BUFFEROBJECT_NV: printf("BUFFEROBJ_ARRAY"); break; 77 | default: printf("UNKNOWN ALLOCATION TYPE"); break; 78 | } 79 | printf(", number of allocations = %d\n", detailBlk->numAllocs); 80 | } 81 | } 82 | 83 | 84 | //------------------------------------------------------------------------------ 85 | // print the memory info for a single device 86 | // 87 | static void print_device_info(GLenum queryType, NVQRQueryDeviceInfo *devInfo) 88 | { 89 | printf("number of memory resource allocations = %d\n", devInfo->totalAllocs); 90 | 91 | if (devInfo->totalAllocs > 0) { 92 | printf(" vidmem allocated = %d kiB (in use = %d kiB, free = %d kiB\n", 93 | devInfo->vidMemUsedkiB + devInfo->vidMemFreekiB, 94 | devInfo->vidMemUsedkiB, 95 | devInfo->vidMemFreekiB); 96 | if (devInfo->numDetailBlocks > 0) { 97 | NVQRQueryData_t *detailInfo = ((NVQRQueryData_t *)devInfo + devInfo->summaryBlkSize); 98 | print_detail_info(devInfo->numDetailBlocks, detailInfo); 99 | } 100 | } 101 | } 102 | 103 | void nvqr_print_memory_info(GLenum queryType, NVQRQueryData_t *buffer) 104 | { 105 | NVQRQueryData_t *ptr = buffer; 106 | NVQRQueryDataHeader *header; 107 | NVQRQueryDeviceInfo *nextDeviceBlk; 108 | int num_devices, num_tags; 109 | int i; 110 | 111 | header = (NVQRQueryDataHeader *)ptr; 112 | ptr += header->headerBlkSize; // step over header to first device block 113 | nextDeviceBlk = (NVQRQueryDeviceInfo *)ptr; 114 | 115 | num_devices = header->numDevices; 116 | for (i = 0; i < num_devices; i++) { 117 | NVQRQueryDeviceInfo *deviceBlk; 118 | 119 | deviceBlk = nextDeviceBlk; 120 | ptr += deviceBlk->deviceBlkSize; // step over to next device block 121 | nextDeviceBlk = (NVQRQueryDeviceInfo *)ptr; 122 | 123 | printf(" Device %d: ", i); 124 | print_device_info(queryType, deviceBlk); 125 | } 126 | 127 | num_tags = *ptr++; 128 | if (num_tags > 0) { 129 | print_tag_info(num_tags, ptr); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /tool/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #if defined (_WIN32) 28 | #include 29 | #endif 30 | #include 31 | 32 | #include "nvidia-query-resource-opengl.h" 33 | #include "nvidia-query-resource-opengl-data.h" 34 | 35 | static void print_help(const char *progname) 36 | { 37 | printf("Query OpenGL resource (vidmem and GPU-mapped sysmem) usage\n\n" 38 | "Usage: %s -p pid\n" 39 | " %s -h\n\n" 40 | " -h: print this help message\n" 41 | " -p : select process to query\n", 42 | progname, progname); 43 | } 44 | 45 | 46 | //------------------------------------------------------------------------------ 47 | // Parse the command line and pass the values of any parsed options. 48 | static nvqrReturn_t parse_commandline(int argc, char * const * const argv, 49 | pid_t *pid, GLenum *queryType) 50 | { 51 | int i; 52 | 53 | // default values 54 | *queryType = GL_QUERY_RESOURCE_TYPE_VIDMEM_ALLOC_NV; 55 | 56 | for (i = 1; i < argc; i++) { 57 | if (strcmp(argv[i], "-h") == 0) { 58 | // help 59 | print_help(argv[0]); 60 | return NVQR_SUCCESS; 61 | } else if (strcmp(argv[i], "-p") == 0) { 62 | // specific pid 63 | i++; 64 | 65 | if (i < argc) { 66 | *pid = atoi(argv[i]); 67 | } else { 68 | print_help(argv[0]); 69 | return NVQR_ERROR_INVALID_ARGUMENT; 70 | } 71 | } else { 72 | print_help(argv[0]); 73 | return NVQR_ERROR_INVALID_ARGUMENT; 74 | } 75 | } 76 | 77 | // validation 78 | if (*pid == 0) { 79 | // PID 0 on Unix is the scheduler, and on Windows is the System Idle 80 | // process, neither of which is a valid target for queryResources. 81 | // If the PID is zero, we may assume that the user did not set one, 82 | // and if the user actually did set a PID of zero, we can treat that 83 | // as an invalid request. 84 | print_help(argv[0]); 85 | return NVQR_ERROR_INVALID_ARGUMENT; 86 | } 87 | 88 | return NVQR_SUCCESS; 89 | } 90 | 91 | 92 | int main (int argc, char * const * const argv) 93 | { 94 | NVQRConnection connection; 95 | NVQRQueryDataBuffer buffer; 96 | pid_t pid = 0; 97 | GLenum queryType; 98 | nvqrReturn_t result; 99 | 100 | result = parse_commandline(argc, argv, &pid, &queryType); 101 | if (result != NVQR_SUCCESS) { 102 | fprintf(stderr, "%s: invalid command line\n", argv[0]); 103 | return result; 104 | } 105 | 106 | result = nvqr_connect(&connection, pid); 107 | if (result != NVQR_SUCCESS) { 108 | if (result == NVQR_ERROR_NOT_SUPPORTED && 109 | connection.process_name) { 110 | printf("Resource query not supported for '%s' (pid %ld)\n", 111 | connection.process_name, (long) connection.pid); 112 | } else { 113 | fprintf(stderr, "Error: failed to open connection to pid %ld\n", 114 | (long) connection.pid); 115 | } 116 | return result; 117 | } 118 | 119 | result = nvqr_request_meminfo(connection, queryType, &buffer); 120 | if (result == NVQR_SUCCESS) { 121 | NVQRQueryDataHeader *header = (NVQRQueryDataHeader *)&buffer.data; 122 | if (header->version != NVQR_DATA_FORMAT_VERSION) { 123 | fprintf(stderr, "Error: unrecognized data format version '%d'. " 124 | "(version supported: %d)\n", header->version, 125 | NVQR_DATA_FORMAT_VERSION); 126 | result = nvqr_disconnect(&connection); 127 | return result; 128 | } 129 | 130 | if (connection.process_name) { 131 | printf("%s, pid = %ld, data format version %d\n", 132 | connection.process_name, (long) connection.pid, header->version); 133 | } 134 | 135 | nvqr_print_memory_info(queryType, buffer.data); 136 | } else { 137 | fprintf(stderr, "Error: failed to query resource usage information " 138 | "for pid %ld.\n", (long) connection.pid); 139 | } 140 | 141 | result = nvqr_disconnect(&connection); 142 | return result; 143 | } 144 | -------------------------------------------------------------------------------- /preload/nvidia-query-resource-opengl-preload.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #include "nvidia-query-resource-opengl-ipc.h" 41 | #include "nvidia-query-resource-opengl-ipc-util.h" 42 | 43 | __attribute__((constructor)) void queryResourcePreloadInit(void); 44 | __attribute__((destructor)) void queryResourcePreloadExit(void); 45 | 46 | #define NVQR_QUEUE_MAX 8 47 | 48 | /* XXX GL_NV_query_resource defines - these should be removed once the 49 | * extension has been finalized and these values become part of real 50 | * OpenGL header files. */ 51 | #ifndef GL_NV_query_resource 52 | #define GL_NV_query_resource 1 53 | 54 | #ifdef GL_GLEXT_PROTOTYPES 55 | GLAPI GLint GLAPIENTRY glQueryResourceNV (GLenum queryType, GLuint pname, GLuint bufSize, GLint *buffer); 56 | #endif /* GL_GLEXT_PROTOTYPES */ 57 | typedef GLint (GLAPIENTRYP PFNGLQUERYRESOURCENVPROC) (GLenum queryType, GLuint pname, GLuint bufSize, GLint *buffer); 58 | 59 | #endif 60 | 61 | #define NVQR_EXTENSION ((const GLubyte *)"glQueryResourceNV") 62 | 63 | #define SOCKET_NAME_MAX_LENGTH sizeof(((struct sockaddr_un *)0)->sun_path) 64 | static char socket_name[SOCKET_NAME_MAX_LENGTH]; 65 | static int socket_fd = -1; 66 | 67 | static int clientsConnected = 0; 68 | // Mutex around connect/disconnect requests from clients 69 | static pthread_mutex_t connect_lock; 70 | // Mutex around query requests from clients: the GLXContext needs to be made 71 | // current to the thread that is making the query 72 | static pthread_mutex_t query_lock; 73 | 74 | static PFNGLQUERYRESOURCENVPROC glQueryResourceNV = NULL; 75 | 76 | static Display *dpy = NULL; 77 | static GLXContext ctx = NULL; 78 | 79 | 80 | //------------------------------------------------------------------------------ 81 | // Release any X11/GLX resources that have been created 82 | static void cleanup_glx_resources(void) { 83 | if (ctx) { 84 | glXDestroyContext(dpy, ctx); 85 | ctx = NULL; 86 | } 87 | if (dpy) { 88 | XCloseDisplay(dpy); 89 | dpy = NULL; 90 | } 91 | } 92 | 93 | 94 | //------------------------------------------------------------------------------ 95 | // Wrapper around vfprintf(3) that prepends a header and appends a newline 96 | static void print_msg(FILE *stream, const char *type, const char *fmt, 97 | va_list vargs) 98 | { 99 | fprintf(stream, "NVIDIA QUERY RESOURCE %s: ", type); 100 | vfprintf(stream, fmt, vargs); 101 | fprintf(stream, "\n"); 102 | } 103 | 104 | 105 | //------------------------------------------------------------------------------ 106 | // Print an error message to stderr with the NVQR header 107 | static void error_msg(const char *fmt, ...) 108 | { 109 | va_list vargs; 110 | 111 | va_start(vargs, fmt); 112 | print_msg(stderr, "ERROR", fmt, vargs); 113 | va_end(vargs); 114 | } 115 | 116 | 117 | //------------------------------------------------------------------------------ 118 | // Print a warning message to stderr with the NVQR header 119 | static void warning_msg(const char *fmt, ...) 120 | { 121 | va_list vargs; 122 | 123 | va_start(vargs, fmt); 124 | print_msg(stderr, "WARNING", fmt, vargs); 125 | va_end(vargs); 126 | } 127 | 128 | 129 | //------------------------------------------------------------------------------ 130 | // Lazily create the GLX context that will be used to service query requests, 131 | // if no clients are currently connected, and increment the connected client 132 | // refcount if the context was successfully created. 133 | static bool connectToClient(void) 134 | { 135 | bool success = false; 136 | 137 | pthread_mutex_lock(&connect_lock); 138 | 139 | if (clientsConnected == 0) { 140 | int screen; 141 | XVisualInfo *visual; 142 | static int attribs[] = { GLX_RGBA, None }; 143 | 144 | // connect to X and create a GLX context 145 | // XOpenDisplay(NULL) + DefaultScreen(dpy) may not give same display app 146 | // is using: may need to revisit this if issues come up 147 | dpy = XOpenDisplay(NULL); 148 | if (dpy == NULL) { 149 | error_msg("failed to open X11 display"); 150 | goto done; 151 | } 152 | screen = DefaultScreen(dpy); 153 | 154 | visual = glXChooseVisual(dpy, screen, attribs); 155 | if (visual == NULL) { 156 | error_msg("failed to choose a GLX visual"); 157 | goto done; 158 | } 159 | 160 | ctx = glXCreateContext(dpy, visual, NULL, True); 161 | XFree(visual); 162 | 163 | if (ctx == NULL) { 164 | error_msg("failed to create GLX context"); 165 | goto done; 166 | } 167 | } 168 | 169 | clientsConnected++; 170 | success = true; 171 | 172 | done: 173 | 174 | if (!success) { 175 | cleanup_glx_resources(); 176 | } 177 | 178 | pthread_mutex_unlock(&connect_lock); 179 | 180 | return success; 181 | } 182 | 183 | //------------------------------------------------------------------------------ 184 | // Decrement the connected clients refcount and clean up the GLX context and 185 | // X11 resources if the last client has disconnected. 186 | static void disconnectFromClient(void) 187 | { 188 | pthread_mutex_lock(&connect_lock); 189 | 190 | clientsConnected--; 191 | 192 | if (clientsConnected == 0) { 193 | cleanup_glx_resources(); 194 | } 195 | 196 | pthread_mutex_unlock(&connect_lock); 197 | } 198 | 199 | 200 | //------------------------------------------------------------------------------ 201 | // Make the GLX context current to the current thread and perform the resource 202 | // query. Returns 0 on failure, and passes along the return value of the 203 | // resource query operation on success. 204 | static int do_query(GLenum queryType, size_t len, int *data) 205 | { 206 | int ret = 0; 207 | 208 | pthread_mutex_lock(&query_lock); 209 | 210 | if (glXMakeCurrent(dpy, None, ctx)) { 211 | ret = glQueryResourceNV(queryType, -1, len, data); 212 | if (!glXMakeCurrent(dpy, None, NULL)) { 213 | ret = 0; 214 | } 215 | } 216 | 217 | pthread_mutex_unlock(&query_lock); 218 | 219 | return ret; 220 | } 221 | 222 | //------------------------------------------------------------------------------ 223 | // Handle client requests over an accept(2)ed (accept(3socket) on Solaris) 224 | // domain socket connection. Keep the connection open until a disconnect 225 | // request is received from the client or an error occurs. 226 | static void *process_client_commands(void *fdp) 227 | { 228 | NVQRQueryCmdBuffer readBuffer; 229 | NVQRQueryDataBuffer writeBuffer; 230 | int fd = *(int*)fdp; 231 | bool connected = false, connection_successful = false; 232 | sigset_t block_signals; 233 | 234 | // Suppress SIGPIPE in this thread, in case a client closes its connection 235 | // before the server can respond to a request. 236 | sigemptyset(&block_signals); 237 | sigaddset(&block_signals, SIGPIPE); 238 | pthread_sigmask(SIG_BLOCK, &block_signals, NULL); 239 | 240 | do { 241 | memset(&writeBuffer, 0, sizeof(writeBuffer)); 242 | 243 | // read a command from the query tool 244 | read(fd, &readBuffer, sizeof(readBuffer)); 245 | // by default, echo back the command op to the caller 246 | writeBuffer.op = readBuffer.op; 247 | 248 | // handle query commands appropriately 249 | switch(readBuffer.op) { 250 | // connect the client 251 | case NVQR_QUERY_CONNECT: 252 | connected = connection_successful = connectToClient(); 253 | 254 | if (connected) { 255 | break; // fall through to error if connection fails 256 | } 257 | 258 | // perform the resource query 259 | case NVQR_QUERY_MEMORY_INFO: 260 | if (connected) { 261 | writeBuffer.cnt = do_query(readBuffer.queryType, 262 | sizeof(writeBuffer.data), 263 | writeBuffer.data); 264 | if (writeBuffer.cnt) { 265 | break; // fall through to error if the query fails 266 | } 267 | } // also fall through if the client was not connected 268 | 269 | // Handle connection/query errors and unknown commands 270 | default: 271 | writeBuffer.op = 0; // tell the client there was an error 272 | // fall through to disconnect 273 | 274 | // disconnect the client 275 | case NVQR_QUERY_DISCONNECT: 276 | connected = false; 277 | break; 278 | } 279 | 280 | // write a response to the client: if the client is already disconnected 281 | // the write will fail with EPIPE. handle all write failures by closing 282 | // the connection. 283 | if (write(fd, &writeBuffer, sizeof(writeBuffer)) != 284 | sizeof(writeBuffer)) { 285 | connected = false; 286 | } 287 | } while (connected); 288 | 289 | if (connection_successful) { 290 | disconnectFromClient(); 291 | } 292 | 293 | close(fd); 294 | return NULL; 295 | } 296 | 297 | //------------------------------------------------------------------------------ 298 | // Bind the domain socket and begin accepting client connections, spawning 299 | // a new thread for each connection. 300 | static void *queryResourcePreloadThread(void *ptr) 301 | { 302 | struct sockaddr_un addr; 303 | socklen_t addrlen = sizeof(addr); 304 | int accept_fd; 305 | pid_t my_pid = getpid(); 306 | 307 | memset(&addr, 0, sizeof(addr)); 308 | addr.sun_family = AF_UNIX; 309 | // socket_name may begin with '\0', so use memcpy(3) instead of strncpy(3) 310 | memcpy(addr.sun_path, socket_name, SOCKET_NAME_MAX_LENGTH); 311 | 312 | if (bind(socket_fd, (struct sockaddr*) &addr, sizeof(addr)) != 0) { 313 | error_msg("failed to bind socket for pid %ld.", (long) my_pid); 314 | return NULL; 315 | } 316 | 317 | if (listen(socket_fd, NVQR_QUEUE_MAX) != 0) { 318 | error_msg("failed to listen on pid %ld's socket.", (long) my_pid); 319 | return NULL; 320 | } 321 | 322 | while ((accept_fd = accept(socket_fd, (struct sockaddr*) &addr, &addrlen)) 323 | != -1) { 324 | pthread_t thread; 325 | 326 | pthread_create(&thread, NULL, process_client_commands, &accept_fd); 327 | } 328 | 329 | return NULL; 330 | } 331 | 332 | //------------------------------------------------------------------------------ 333 | // Create a domain socket and spawn a thread to accept connections over it. 334 | __attribute__((constructor)) void queryResourcePreloadInit(void) 335 | { 336 | pthread_t queryThreadId; 337 | pid_t my_pid = getpid(); 338 | 339 | pthread_mutex_init(&connect_lock, NULL); 340 | 341 | glQueryResourceNV = 342 | (PFNGLQUERYRESOURCENVPROC) glXGetProcAddressARB(NVQR_EXTENSION); 343 | 344 | if (glQueryResourceNV == NULL) { 345 | // XXX should check extension string once extension is exported there 346 | error_msg("failed to load %s", NVQR_EXTENSION); 347 | return; 348 | } 349 | 350 | socket_fd = socket(PF_UNIX, SOCK_STREAM, 0); 351 | if (socket_fd == -1) { 352 | error_msg("failed to create socket."); 353 | return; 354 | } 355 | 356 | if (nvqr_ipc_get_socket_name(socket_name, SOCKET_NAME_MAX_LENGTH, my_pid) >= 357 | SOCKET_NAME_MAX_LENGTH) { 358 | warning_msg("socket name for pid %ld truncated - " 359 | "name collision may be possible.", (long) my_pid); 360 | } 361 | 362 | // create the thread 363 | if (!XInitThreads()) { 364 | error_msg("failed to initialize X threads."); 365 | return; 366 | } 367 | pthread_create(&queryThreadId, NULL, &queryResourcePreloadThread, NULL); 368 | } 369 | 370 | //------------------------------------------------------------------------------ 371 | // Clean up resources 372 | __attribute__((destructor)) void queryResourcePreloadExit(void) 373 | { 374 | pthread_mutex_destroy(&connect_lock); 375 | pthread_mutex_destroy(&query_lock); 376 | 377 | if (socket_fd != -1) { 378 | close(socket_fd); 379 | unlink(socket_name); 380 | } 381 | } 382 | -------------------------------------------------------------------------------- /tool/nvidia-query-resource-opengl.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a 5 | * copy of this software and associated documentation files (the "Software"), 6 | * to deal in the Software without restriction, including without limitation 7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, 8 | * and/or sell copies of the Software, and to permit persons to whom the 9 | * Software is furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 17 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 19 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 20 | * DEALINGS IN THE SOFTWARE. 21 | */ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #if defined(_WIN32) 29 | #include 30 | #include 31 | #else 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #endif 41 | 42 | #include 43 | 44 | #if defined (__sun) && defined (__SVR4) 45 | #include 46 | #endif 47 | 48 | #include "nvidia-query-resource-opengl.h" 49 | #include "nvidia-query-resource-opengl-ipc.h" 50 | #include "nvidia-query-resource-opengl-ipc-util.h" 51 | 52 | 53 | #if defined (_WIN32) 54 | // Older versions of MSVC didn't support stdbool.h 55 | typedef int bool; 56 | #ifndef true 57 | #define true 1 58 | #endif // true 59 | #ifndef false 60 | #define false 0 61 | #endif // false 62 | typedef HANDLE nvqr_handle_t; 63 | typedef DWORD iosize_t; 64 | char *(*nvqr_strdup)(const char *src) = _strdup; 65 | #else 66 | typedef int nvqr_handle_t; 67 | typedef size_t iosize_t; 68 | char *(*nvqr_strdup)(const char *src) = strdup; 69 | #endif // _WIN32 70 | 71 | 72 | static pid_t get_my_pid(void) 73 | { 74 | #if defined(_WIN32) 75 | return GetCurrentProcessId(); 76 | #else 77 | return getpid(); 78 | #endif 79 | } 80 | 81 | 82 | //------------------------------------------------------------------------------ 83 | // Functions to abstract away the OS-specific IPC interface. The client_*() 84 | // functions are no-ops on Unix, where bidirectional sockets are used. 85 | 86 | 87 | static bool open_server_connection(nvqr_handle_t *handle, pid_t pid) 88 | { 89 | #if defined(_WIN32) 90 | char *name = nvqr_ipc_server_pipe_name(pid); 91 | int i; 92 | const int max_retries = 3; 93 | 94 | for (i = 0, *handle = INVALID_HANDLE_VALUE; 95 | i < max_retries && *handle == INVALID_HANDLE_VALUE; 96 | i++) { 97 | *handle = CreateFile( 98 | (LPCSTR)(LPCTSTR)name, 99 | GENERIC_WRITE, // write access 100 | 0, // no sharing 101 | NULL, // default security attributes 102 | OPEN_EXISTING, // opens existing pipe 103 | 0, // default attributes 104 | NULL); // no template file 105 | 106 | if (*handle == INVALID_HANDLE_VALUE && 107 | GetLastError() == ERROR_PIPE_BUSY) { 108 | WaitNamedPipe((LPCSTR)(LPCSTR)name, 10000); 109 | } 110 | } 111 | free(name); 112 | 113 | return *handle != INVALID_HANDLE_VALUE; 114 | #else 115 | bool connected = false; 116 | 117 | *handle = socket(PF_UNIX, SOCK_STREAM, 0); 118 | if (*handle != -1) { 119 | struct sockaddr_un addr; 120 | int len; 121 | 122 | memset(&addr, 0, sizeof(addr)); 123 | addr.sun_family = AF_UNIX; 124 | len = nvqr_ipc_get_socket_name(addr.sun_path, 125 | sizeof(addr.sun_path), pid); 126 | if (len >= sizeof(addr.sun_path)) { 127 | fprintf(stderr, "Warning: socket name for pid %ld truncated - " 128 | "name collision may be possible.", (long) pid); 129 | } 130 | connected = connect(*handle, (struct sockaddr *) &addr, 131 | sizeof(addr)) == 0; 132 | } 133 | 134 | return connected; 135 | #endif 136 | } 137 | 138 | 139 | static bool create_client(NVQRConnection *c) 140 | { 141 | #if defined(_WIN32) 142 | char *name = nvqr_ipc_client_pipe_name(get_my_pid()); 143 | c->client_handle = CreateNamedPipe( 144 | (LPCSTR)(LPCTSTR)name, // pipe name 145 | PIPE_ACCESS_INBOUND, // read access 146 | PIPE_TYPE_MESSAGE | // message type pipe 147 | PIPE_READMODE_MESSAGE | // message-read mode 148 | PIPE_WAIT, // blocking mode 149 | PIPE_UNLIMITED_INSTANCES, // max. instances 150 | 512, // output buffer size 151 | 512, // input buffer size 152 | 0, // client time-out 153 | NULL); // default security attribute 154 | free(name); 155 | return c->client_handle != INVALID_HANDLE_VALUE; 156 | #else 157 | return true; 158 | #endif 159 | } 160 | 161 | 162 | static bool open_client_connection(NVQRConnection *c) 163 | { 164 | #if defined(_WIN32) 165 | return ConnectNamedPipe(c->client_handle, NULL) || 166 | GetLastError() == ERROR_PIPE_CONNECTED; 167 | #else 168 | return true; 169 | #endif 170 | } 171 | 172 | 173 | static iosize_t write_file(nvqr_handle_t handle, const void *buf, iosize_t len) 174 | { 175 | iosize_t bytes_written; 176 | #if defined(_WIN32) 177 | 178 | if (!WriteFile(handle, buf, len, &bytes_written, NULL)) { 179 | bytes_written = 0; 180 | } 181 | #else 182 | bytes_written = write(handle, buf, len); 183 | #endif 184 | return bytes_written; 185 | } 186 | 187 | 188 | static void close_file(nvqr_handle_t handle) 189 | { 190 | #if defined (_WIN32) 191 | CloseHandle(handle); 192 | #else 193 | close(handle); 194 | #endif 195 | } 196 | 197 | 198 | static void flush_file(nvqr_handle_t handle) 199 | { 200 | #if defined (_WIN32) 201 | FlushFileBuffers(handle); 202 | #else 203 | fsync(handle); 204 | #endif 205 | } 206 | 207 | 208 | static void close_client_connection(NVQRConnection c) 209 | { 210 | #if defined (_WIN32) 211 | DisconnectNamedPipe(c.client_handle); 212 | #else 213 | #endif 214 | } 215 | 216 | static void destroy_client(NVQRConnection c) 217 | { 218 | #if defined(_WIN32) 219 | CloseHandle(c.client_handle); 220 | #else 221 | #endif 222 | } 223 | 224 | 225 | //----------------------------------------------------------------------------- 226 | // Read a response from the server. This is done through the client handle 227 | // (a named pipe) on Windows, and the server handle (a domain socket) on Unix. 228 | static bool read_server_response(NVQRConnection c, NVQRQueryDataBuffer *buf) { 229 | iosize_t bytesRead; 230 | 231 | memset(buf, 0, sizeof(*buf)); 232 | #if defined (_WIN32) 233 | return ReadFile(c.client_handle, buf, sizeof(*buf), &bytesRead, NULL); 234 | #else 235 | bytesRead = read(c.server_handle, buf, sizeof(*buf)); 236 | return bytesRead == sizeof(*buf); 237 | #endif 238 | } 239 | 240 | 241 | static bool write_server_command(NVQRConnection c, NVQRqueryOp op, 242 | int queryType, pid_t pid) 243 | { 244 | NVQRQueryCmdBuffer buf; 245 | bool ret; 246 | 247 | memset(&buf, 0, sizeof(buf)); 248 | buf.op = op; 249 | buf.queryType = queryType; 250 | buf.pid = pid; 251 | 252 | ret = write_file(c.server_handle, &buf, sizeof(buf)) == sizeof(buf); 253 | 254 | flush_file(c.server_handle); 255 | 256 | return ret; 257 | } 258 | 259 | 260 | //----------------------------------------------------------------------------- 261 | // Send NVQR_QUERY_CONNECT to the server and verify that it ACKs with 262 | // NVQR_QUERY_CONNECT. Returns TRUE on success; FALSE on failure. 263 | static bool connect_to_server(NVQRConnection *conn) 264 | { 265 | NVQRQueryDataBuffer data; 266 | bool ret = write_server_command(*conn, NVQR_QUERY_CONNECT, 0, get_my_pid()); 267 | 268 | if (ret) { 269 | ret = open_client_connection(conn); 270 | 271 | if (ret) { 272 | if (!read_server_response(*conn, &data) || 273 | data.op != NVQR_QUERY_CONNECT) { 274 | close_client_connection(*conn); 275 | } 276 | } 277 | } 278 | 279 | return ret; 280 | } 281 | 282 | 283 | static void close_server_connection(nvqr_handle_t handle) 284 | { 285 | close_file(handle); 286 | } 287 | 288 | 289 | //----------------------------------------------------------------------------- 290 | // Send NVQR_QUERY_MEMINFO to the server and verify that it ACKs with 291 | // NVQR_QUERY_MEMINFO. Pass the meminfo read from the server back to the caller. 292 | nvqrReturn_t nvqr_request_meminfo(NVQRConnection c, GLenum queryType, 293 | NVQRQueryDataBuffer *buf) 294 | { 295 | if (write_server_command(c, NVQR_QUERY_MEMORY_INFO, queryType, 0) && 296 | read_server_response(c, buf) && 297 | buf->op == NVQR_QUERY_MEMORY_INFO) 298 | { 299 | return NVQR_SUCCESS; 300 | } 301 | 302 | return NVQR_ERROR_UNKNOWN; 303 | } 304 | 305 | 306 | //----------------------------------------------------------------------------- 307 | // Send NVQR_QUERY_DISCONNECT to the server and verify that it ACKs with 308 | // NVQR_QUERY_DISCONNECT. Returns TRUE on success; FALSE on failure. 309 | static bool disconnect_from_server(NVQRConnection c) 310 | { 311 | NVQRQueryDataBuffer data; 312 | 313 | return write_server_command(c, NVQR_QUERY_DISCONNECT, 0, 0) && 314 | read_server_response(c, &data) && 315 | data.op == NVQR_QUERY_DISCONNECT; 316 | } 317 | 318 | 319 | //----------------------------------------------------------------------------- 320 | // Use the Toolhelp API on Win32 or the /proc virtual filesystem on Unix to 321 | // determine the name of the process with the given PID. Strip all file path 322 | // components except the last and return the process name to the caller in a 323 | // newly heap-allocated buffer, or return NULL on error. 324 | static char *process_name_from_pid(int pid) 325 | { 326 | char *name = NULL; 327 | #if defined (_WIN32) 328 | static const char directory_separator = '\\'; 329 | HANDLE hTHSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 330 | 331 | if (hTHSnap) { 332 | PROCESSENTRY32 pe; 333 | int ret; 334 | 335 | pe.dwSize=(sizeof(pe)); 336 | for (ret = Process32First(hTHSnap, &pe); 337 | ret; 338 | ret = Process32Next(hTHSnap, &pe)) { 339 | if (pe.th32ProcessID == pid) { 340 | name = nvqr_strdup(pe.szExeFile); 341 | break; 342 | } 343 | } 344 | CloseHandle(hTHSnap); 345 | } 346 | #else 347 | // Format string to determine the name of a procfs file to read 348 | #if defined (__sun) && defined (__SVR4) 349 | // Solaris exposes process info through a psinfo_t structure in a "psinfo" file 350 | #define PROCFILE_FMT_STRING "/proc/%ld/psinfo" 351 | #else 352 | // FreeBSD and Linux expose the command line of a process in a "cmdline" file 353 | #define PROCFILE_FMT_STRING "/proc/%ld/cmdline" 354 | #endif 355 | 356 | static const char directory_separator = '/'; 357 | int fd = -1; 358 | char *procfile; 359 | size_t len = strlen(PROCFILE_FMT_STRING) + NVQR_IPC_MAX_DIGIT_LENGTH; 360 | 361 | procfile = calloc(len + 1, 1); 362 | if (!procfile) { 363 | return NULL; 364 | } 365 | snprintf(procfile, len, PROCFILE_FMT_STRING, (long) pid); 366 | fd = open(procfile, O_RDONLY); 367 | free(procfile); 368 | 369 | if (fd != -1) { 370 | #if defined (__sun) && defined (__SVR4) 371 | psinfo_t psinfo; 372 | 373 | if (read(fd, &psinfo, sizeof(psinfo)) == sizeof(psinfo)) { 374 | name = strdup(psinfo.pr_fname); 375 | } 376 | #else 377 | int namelen; 378 | char c; 379 | 380 | // neither fstat(2) nor lseek(fd, 0, SEEK_END) seem to work for finding 381 | // the length of a /proc/$pid/cmdline file, so just crawl the file to 382 | // find the length of argv[0] for the target process id. 383 | for (namelen = 0; read(fd, &c, 1) == 1 && c; namelen++); 384 | lseek(fd, 0, SEEK_SET); 385 | 386 | name = calloc(namelen + 1, 1); 387 | if (name && read(fd, name, namelen) != namelen) { 388 | free(name); 389 | name = NULL; 390 | } 391 | #endif // __sun && __SVR4 392 | close(fd); 393 | } 394 | #endif // _WIN32 395 | if (name) { 396 | char *shortname, *fullname = name; 397 | 398 | shortname = strrchr(fullname, directory_separator); 399 | if (shortname) { 400 | shortname++; 401 | } else { 402 | shortname = fullname; 403 | } 404 | 405 | name = nvqr_strdup(shortname); 406 | free(fullname); 407 | } 408 | 409 | return name; 410 | } 411 | 412 | 413 | nvqrReturn_t nvqr_connect(NVQRConnection *connection, pid_t pid) 414 | { 415 | memset(connection, 0, sizeof(*connection)); 416 | connection->pid = pid; 417 | connection->process_name = process_name_from_pid(pid); 418 | 419 | if (!create_client(connection)) { 420 | return NVQR_ERROR_UNKNOWN; 421 | } 422 | 423 | if (!open_server_connection(&(connection->server_handle), pid)) { 424 | destroy_client(*connection); 425 | return NVQR_ERROR_NOT_SUPPORTED; 426 | } 427 | 428 | if (!connect_to_server(connection)) { 429 | destroy_client(*connection); 430 | close_server_connection(connection->server_handle); 431 | return NVQR_ERROR_UNKNOWN; 432 | } 433 | 434 | return NVQR_SUCCESS; 435 | } 436 | 437 | 438 | nvqrReturn_t nvqr_disconnect(NVQRConnection *connection) 439 | { 440 | if (disconnect_from_server(*connection)) { 441 | close_client_connection(*connection); 442 | destroy_client(*connection); 443 | close_server_connection(connection->server_handle); 444 | free(connection->process_name); 445 | return NVQR_SUCCESS; 446 | } 447 | 448 | return NVQR_ERROR_UNKNOWN; 449 | } 450 | --------------------------------------------------------------------------------