0

I'm attempting to pass a uint array into the NVML function nvmlDeviceGetAccountingPids(Doc here) from C#, here's a minimum working sample of what I have so far:

    {
        public const string NVMLDLL = "nvml.dll";
        public static class Constants
        {
            public const int NVML_DEVICE_INFOROM_VERSION_BUFFER_SIZE = 16;
            public const int NVML_DEVICE_NAME_BUFFER_SIZE = 64;
            public const int NVML_DEVICE_NAME_V2_BUFFER_SIZE = 96;
            public const int NVML_DEVICE_PART_NUMBER_BUFFER_SIZE = 80;
            public const int NVML_DEVICE_SERIAL_BUFFER_SIZE = 30;
            public const int NVML_DEVICE_UUID_BUFFER_SIZE = 80;
            public const int NVML_DEVICE_UUID_V2_BUFFER_SIZE = 96;
            public const int NVML_DEVICE_VBIOS_VERSION_BUFFER_SIZE = 32;
            public const int NVML_SYSTEM_DRIVER_VERSION_BUFFER_SIZE = 80;
            public const int NVML_SYSTEM_NVML_VERSION_BUFFER_SIZE = 80;
            public const int NVML_INIT_FLAG_NO_ATTACH = 2;
            public const int NVML_INIT_FLAG_NO_GPUS = 1;

        }
        public enum nvmlReturn_t
        {
            NVML_SUCCESS = 0,                           // The operation was successful. 
            NVML_ERROR_UNINITIALIZED = 1,               // NVML was not first initialized with nvmlInit(). 
            NVML_ERROR_INVALID_ARGUMENT = 2,            // A supplied argument is invalid. 
            NVML_ERROR_NOT_SUPPORTED = 3,               // The requested operation is not available on target device. 
            NVML_ERROR_NO_PERMISSION = 4,               // The current user does not have permission for operation. 
            NVML_ERROR_ALREADY_INITIALIZED = 5,         // Deprecated: Multiple initializations are now allowed through ref counting. 
            NVML_ERROR_NOT_FOUND = 6,                   // A query to find an object was unsuccessful. 
            NVML_ERROR_INSUFFICIENT_SIZE = 7,           // An input argument is not large enough. 
            NVML_ERROR_INSUFFICIENT_POWER = 8,          // A device's external power cables are not properly attached. 
            NVML_ERROR_DRIVER_NOT_LOADED = 9,           // NVIDIA driver is not loaded. 
            NVML_ERROR_TIMEOUT = 10,                    // User provided timeout passed. 
            NVML_ERROR_IRQ_ISSUE = 11,                  // NVIDIA Kernel detected an interrupt issue with a GPU. 
            NVML_ERROR_LIBRARY_NOT_FOUND = 12,          // NVML Shared Library couldn't be found or loaded. 
            NVML_ERROR_FUNCTION_NOT_FOUND = 13,         // Local version of NVML doesn't implement this function. 
            NVML_ERROR_CORRUPTED_INFOROM = 14,          // infoROM is corrupted 
            NVML_ERROR_GPU_IS_LOST = 15,                // The GPU has fallen off the bus or has otherwise become inaccessible. 
            NVML_ERROR_RESET_REQUIRED = 16,             // The GPU requires a reset before it can be used again. 
            NVML_ERROR_OPERATING_SYSTEM = 17,           // The GPU control device has been blocked by the operating system/cgroups. 
            NVML_ERROR_LIB_RM_VERSION_MISMATCH = 18,    // RM detects a driver/library version mismatch. 
            NVML_ERROR_IN_USE = 19,                     // An operation cannot be performed because the GPU is currently in use. 
            NVML_ERROR_MEMORY = 20,                     // Insufficient memory. 
            NVML_ERROR_NO_DATA = 21,                    // No data. 
            NVML_ERROR_VGPU_ECC_NOT_SUPPORTED = 22,     // The requested vgpu operation is not available on target device, becasue ECC is enabled. 
            NVML_ERROR_INSUFFICIENT_RESOURCES = 23,     // Ran out of critical resources, other than memory. 
            NVML_ERROR_FREQ_NOT_SUPPORTED = 24,         // Ran out of critical resources, other than memory. 
            NVML_ERROR_UNKNOWN = 999                    // An internal driver error occurred. 
        }
        [DllImport(NVMLDLL)]
        private static extern nvmlReturn_t nvmlInit_v2();
        [DllImport(NVMLDLL)]
        private static extern nvmlReturn_t nvmlShutdown();
        [DllImport(NVMLDLL)]
        private static extern nvmlReturn_t nvmlDeviceGetCount_v2(out uint deviceCount);
        [DllImport(NVMLDLL)]
        private static extern nvmlReturn_t nvmlDeviceGetHandleByIndex_v2(int index, out IntPtr handle);
        [DllImport(NVMLDLL)]
        private static extern nvmlReturn_t nvmlDeviceGetName(IntPtr device, [MarshalAs(UnmanagedType.LPStr)] StringBuilder deviceName, uint length);
        [DllImport(NVMLDLL)]
        private static extern nvmlReturn_t nvmlDeviceGetAccountingPids(IntPtr device, ref uint count, [In, Out] uint[] pids);

        static void Main(string[] args)
        {
            if (nvmlInit_v2() == nvmlReturn_t.NVML_SUCCESS)
            {
                nvmlDeviceGetCount_v2(out uint deviceCount);
                for (int i = 0; i < deviceCount; i++)
                {
                    StringBuilder name = new();
                    nvmlDeviceGetHandleByIndex_v2(i, out IntPtr _device);
                    nvmlDeviceGetName(_device, name, Constants.NVML_DEVICE_NAME_V2_BUFFER_SIZE);

                    // arbitrary number for now
                    uint count = 5;
                    uint[] pids = new uint[count];
                    nvmlDeviceGetAccountingPids(_device, ref count, pids);
                    foreach(uint ui in pids) Console.WriteLine(ui);

                }
                nvmlShutdown();
            }
        }
    }

and so far it's returning the correct # of processes that are utilizing the gpu, but the PID array keeps returning all 0's. I'm not particularly experienced with C# so if someone could show me the proper syntax for this(or lemme know if I have to resort to IntPtr's), the original function has this signature:

nvmlReturn_t nvmlDeviceGetAccountingPids ( nvmlDevice_t device, unsigned int* count, unsigned int* pids )
    Parameters

    device
        The identifier of the target device 
    count
        Reference in which to provide the pids array size, and to return the number of elements ready to be queried 
    pids
        Reference in which to return list of process ids

If anyone knows that magic combo I'd sure appreciate it, thank you for your time =)

codingNewb
  • 470
  • 1
  • 8
  • 23

1 Answers1

0

Sometimes I think I only type things to work them out ^_^

        static void Main(string[] args)
        {
            if (nvmlInit_v2() == nvmlReturn_t.NVML_SUCCESS)
            {
                nvmlDeviceGetCount_v2(out uint deviceCount);
                for (int i = 0; i < deviceCount; i++)
                {
                    StringBuilder name = new();
                    nvmlDeviceGetHandleByIndex_v2(i, out IntPtr _device);
                    nvmlDeviceGetName(_device, name, Constants.NVML_DEVICE_NAME_V2_BUFFER_SIZE);

                    // arbitrary number for now
                    uint count = 0;
                    nvmlDeviceGetAccountingPids(_device, ref count, null);
                    uint[] pids = new uint[count];
                    if (nvmlDeviceGetAccountingPids(_device, ref count, pids) == nvmlReturn_t.NVML_SUCCESS)
                    {
                        foreach (uint ui in pids) Console.WriteLine(ui);
                    }

                }
                nvmlShutdown();
            }

so it turns out that when passing the count to the function it wants the same number as the size of the array in order to populate the pids list(which makes sense I suppose). This is retrieved by just setting count to "0" and passing a null array to the function and it will set count to the size of the array. Create a new uint array with that size, pass the array/count and BAM, it returns the pids as desired. Thanks for reading SO! If anyone has some constructive criticism in terms of syntax/practices please do comment so I can improve this code as I go. Have a wonderful day

codingNewb
  • 470
  • 1
  • 8
  • 23