/* * Copyright (c) 2009, Giampaolo Rodola'. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include "../../_psutil_common.h" /* * Return the number of logical, active CPUs. Return 0 if undetermined. * See discussion at: https://bugs.python.org/issue33166#msg314631 */ static unsigned int psutil_get_num_cpus(int fail_on_err) { unsigned int ncpus = 0; // Minimum requirement: Windows 7 if (GetActiveProcessorCount != NULL) { ncpus = GetActiveProcessorCount(ALL_PROCESSOR_GROUPS); if ((ncpus == 0) && (fail_on_err == 1)) { PyErr_SetFromWindowsErr(0); } } else { psutil_debug("GetActiveProcessorCount() not available; " "using GetSystemInfo()"); ncpus = (unsigned int)PSUTIL_SYSTEM_INFO.dwNumberOfProcessors; if ((ncpus <= 0) && (fail_on_err == 1)) { PyErr_SetString( PyExc_RuntimeError, "GetSystemInfo() failed to retrieve CPU count"); } } return ncpus; } /* * Retrieves system CPU timing information as a (user, system, idle) * tuple. On a multiprocessor system, the values returned are the * sum of the designated times across all processors. */ PyObject * psutil_cpu_times(PyObject *self, PyObject *args) { double idle, kernel, user, system; FILETIME idle_time, kernel_time, user_time; if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) { PyErr_SetFromWindowsErr(0); return NULL; } idle = (double)((HI_T * idle_time.dwHighDateTime) + \ (LO_T * idle_time.dwLowDateTime)); user = (double)((HI_T * user_time.dwHighDateTime) + \ (LO_T * user_time.dwLowDateTime)); kernel = (double)((HI_T * kernel_time.dwHighDateTime) + \ (LO_T * kernel_time.dwLowDateTime)); // Kernel time includes idle time. // We return only busy kernel time subtracting idle time from // kernel time. system = (kernel - idle); return Py_BuildValue("(ddd)", user, system, idle); } /* * Same as above but for all system CPUs. */ PyObject * psutil_per_cpu_times(PyObject *self, PyObject *args) { double idle, kernel, systemt, user, interrupt, dpc; NTSTATUS status; _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; UINT i; unsigned int ncpus; PyObject *py_tuple = NULL; PyObject *py_retlist = PyList_New(0); if (py_retlist == NULL) return NULL; // retrieves number of processors ncpus = psutil_get_num_cpus(1); if (ncpus == 0) goto error; // allocates an array of _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION // structures, one per processor sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); if (sppi == NULL) { PyErr_NoMemory(); goto error; } // gets cpu time informations status = NtQuerySystemInformation( SystemProcessorPerformanceInformation, sppi, ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), NULL); if (! NT_SUCCESS(status)) { psutil_SetFromNTStatusErr( status, "NtQuerySystemInformation(SystemProcessorPerformanceInformation)" ); goto error; } // computes system global times summing each // processor value idle = user = kernel = interrupt = dpc = 0; for (i = 0; i < ncpus; i++) { py_tuple = NULL; user = (double)((HI_T * sppi[i].UserTime.HighPart) + (LO_T * sppi[i].UserTime.LowPart)); idle = (double)((HI_T * sppi[i].IdleTime.HighPart) + (LO_T * sppi[i].IdleTime.LowPart)); kernel = (double)((HI_T * sppi[i].KernelTime.HighPart) + (LO_T * sppi[i].KernelTime.LowPart)); interrupt = (double)((HI_T * sppi[i].InterruptTime.HighPart) + (LO_T * sppi[i].InterruptTime.LowPart)); dpc = (double)((HI_T * sppi[i].DpcTime.HighPart) + (LO_T * sppi[i].DpcTime.LowPart)); // kernel time includes idle time on windows // we return only busy kernel time subtracting // idle time from kernel time systemt = kernel - idle; py_tuple = Py_BuildValue( "(ddddd)", user, systemt, idle, interrupt, dpc ); if (!py_tuple) goto error; if (PyList_Append(py_retlist, py_tuple)) goto error; Py_CLEAR(py_tuple); } free(sppi); return py_retlist; error: Py_XDECREF(py_tuple); Py_DECREF(py_retlist); if (sppi) free(sppi); return NULL; } /* * Return the number of active, logical CPUs. */ PyObject * psutil_cpu_count_logical(PyObject *self, PyObject *args) { unsigned int ncpus; ncpus = psutil_get_num_cpus(0); if (ncpus != 0) return Py_BuildValue("I", ncpus); else Py_RETURN_NONE; // mimick os.cpu_count() } /* * Return the number of physical CPU cores (hyper-thread CPUs count * is excluded). */ PyObject * psutil_cpu_count_phys(PyObject *self, PyObject *args) { DWORD rc; PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX buffer = NULL; PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX ptr = NULL; DWORD length = 0; DWORD offset = 0; DWORD ncpus = 0; DWORD prev_processor_info_size = 0; // GetLogicalProcessorInformationEx() is available from Windows 7 // onward. Differently from GetLogicalProcessorInformation() // it supports process groups, meaning this is able to report more // than 64 CPUs. See: // https://bugs.python.org/issue33166 if (GetLogicalProcessorInformationEx == NULL) { psutil_debug("Win < 7; cpu_count_phys() forced to None"); Py_RETURN_NONE; } while (1) { rc = GetLogicalProcessorInformationEx( RelationAll, buffer, &length); if (rc == FALSE) { if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { if (buffer) { free(buffer); } buffer = \ (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX)malloc(length); if (NULL == buffer) { PyErr_NoMemory(); return NULL; } } else { psutil_debug("GetLogicalProcessorInformationEx() returned ", GetLastError()); goto return_none; } } else { break; } } ptr = buffer; while (offset < length) { // Advance ptr by the size of the previous // SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX struct. ptr = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX*) \ (((char*)ptr) + prev_processor_info_size); if (ptr->Relationship == RelationProcessorCore) { ncpus += 1; } // When offset == length, we've reached the last processor // info struct in the buffer. offset += ptr->Size; prev_processor_info_size = ptr->Size; } free(buffer); if (ncpus != 0) { return Py_BuildValue("I", ncpus); } else { psutil_debug("GetLogicalProcessorInformationEx() count was 0"); Py_RETURN_NONE; // mimick os.cpu_count() } return_none: if (buffer != NULL) free(buffer); Py_RETURN_NONE; } /* * Return CPU statistics. */ PyObject * psutil_cpu_stats(PyObject *self, PyObject *args) { NTSTATUS status; _SYSTEM_PERFORMANCE_INFORMATION *spi = NULL; _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; _SYSTEM_INTERRUPT_INFORMATION *InterruptInformation = NULL; unsigned int ncpus; UINT i; ULONG64 dpcs = 0; ULONG interrupts = 0; // retrieves number of processors ncpus = psutil_get_num_cpus(1); if (ncpus == 0) goto error; // get syscalls / ctx switches spi = (_SYSTEM_PERFORMANCE_INFORMATION *) \ malloc(ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION)); if (spi == NULL) { PyErr_NoMemory(); goto error; } status = NtQuerySystemInformation( SystemPerformanceInformation, spi, ncpus * sizeof(_SYSTEM_PERFORMANCE_INFORMATION), NULL); if (! NT_SUCCESS(status)) { psutil_SetFromNTStatusErr( status, "NtQuerySystemInformation(SystemPerformanceInformation)"); goto error; } // get DPCs InterruptInformation = \ malloc(sizeof(_SYSTEM_INTERRUPT_INFORMATION) * ncpus); if (InterruptInformation == NULL) { PyErr_NoMemory(); goto error; } status = NtQuerySystemInformation( SystemInterruptInformation, InterruptInformation, ncpus * sizeof(SYSTEM_INTERRUPT_INFORMATION), NULL); if (! NT_SUCCESS(status)) { psutil_SetFromNTStatusErr( status, "NtQuerySystemInformation(SystemInterruptInformation)"); goto error; } for (i = 0; i < ncpus; i++) { dpcs += InterruptInformation[i].DpcCount; } // get interrupts sppi = (_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ malloc(ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); if (sppi == NULL) { PyErr_NoMemory(); goto error; } status = NtQuerySystemInformation( SystemProcessorPerformanceInformation, sppi, ncpus * sizeof(_SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), NULL); if (! NT_SUCCESS(status)) { psutil_SetFromNTStatusErr( status, "NtQuerySystemInformation(SystemProcessorPerformanceInformation)"); goto error; } for (i = 0; i < ncpus; i++) { interrupts += sppi[i].InterruptCount; } // done free(spi); free(InterruptInformation); free(sppi); return Py_BuildValue( "kkkk", spi->ContextSwitches, interrupts, (unsigned long)dpcs, spi->SystemCalls ); error: if (spi) free(spi); if (InterruptInformation) free(InterruptInformation); if (sppi) free(sppi); return NULL; } /* * Return CPU frequency. */ PyObject * psutil_cpu_freq(PyObject *self, PyObject *args) { PROCESSOR_POWER_INFORMATION *ppi; NTSTATUS ret; ULONG size; LPBYTE pBuffer = NULL; ULONG current; ULONG max; unsigned int ncpus; // Get the number of CPUs. ncpus = psutil_get_num_cpus(1); if (ncpus == 0) return NULL; // Allocate size. size = ncpus * sizeof(PROCESSOR_POWER_INFORMATION); pBuffer = (BYTE*)LocalAlloc(LPTR, size); if (! pBuffer) { PyErr_SetFromWindowsErr(0); return NULL; } // Syscall. ret = CallNtPowerInformation( ProcessorInformation, NULL, 0, pBuffer, size); if (ret != 0) { PyErr_SetString(PyExc_RuntimeError, "CallNtPowerInformation syscall failed"); goto error; } // Results. ppi = (PROCESSOR_POWER_INFORMATION *)pBuffer; max = ppi->MaxMhz; current = ppi->CurrentMhz; LocalFree(pBuffer); return Py_BuildValue("kk", current, max); error: if (pBuffer != NULL) LocalFree(pBuffer); return NULL; }