summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGiampaolo Rodola <g.rodola@gmail.com>2020-02-13 22:29:14 +0100
committerGitHub <noreply@github.com>2020-02-13 22:29:14 +0100
commitb0cff82c6b91f99d2962f10d8d61d64853296e36 (patch)
treebbb4debfa8e914936f7368a43860a3fc573c3d41
parent573886fa81db2c4b0ae9d296fa0fe1ac9055f3f8 (diff)
downloadpsutil-b0cff82c6b91f99d2962f10d8d61d64853296e36.tar.gz
[Windows] increase precision of boot_time() and proc create_time() (#1693)
-rw-r--r--HISTORY.rst2
-rw-r--r--psutil/_psutil_common.c38
-rw-r--r--psutil/_psutil_common.h2
-rw-r--r--psutil/_psutil_windows.c71
-rw-r--r--psutil/arch/windows/process_info.c11
5 files changed, 60 insertions, 64 deletions
diff --git a/HISTORY.rst b/HISTORY.rst
index 44d887e3..539c6fd4 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -19,6 +19,8 @@ XXXX-XX-XX
- 1679_: [Windows] net_connections() and Process.connections() are 10% faster.
- 1681_: [Linux] disk_partitions() now also shows swap partitions.
- 1686_: [Windows] added support for PyPy on Windows.
+- 1693_: [Windows] boot_time() and Process.create_time() now have the precision
+ of a micro second (before the precision was of a second).
**Bug fixes**
diff --git a/psutil/_psutil_common.c b/psutil/_psutil_common.c
index 028e48e0..07578eda 100644
--- a/psutil/_psutil_common.c
+++ b/psutil/_psutil_common.c
@@ -352,6 +352,7 @@ psutil_set_winver() {
return 0;
}
+
int
psutil_load_globals() {
if (psutil_loadlibs() != 0)
@@ -362,4 +363,41 @@ psutil_load_globals() {
InitializeCriticalSection(&PSUTIL_CRITICAL_SECTION);
return 0;
}
+
+
+/*
+ * Convert the hi and lo parts of a FILETIME structure or a LARGE_INTEGER
+ * to a UNIX time.
+ * A FILETIME contains a 64-bit value representing the number of
+ * 100-nanosecond intervals since January 1, 1601 (UTC).
+ * A UNIX time is the number of seconds that have elapsed since the
+ * UNIX epoch, that is the time 00:00:00 UTC on 1 January 1970.
+ */
+static double
+_to_unix_time(ULONGLONG hiPart, ULONGLONG loPart) {
+ ULONGLONG ret;
+
+ // 100 nanosecond intervals since January 1, 1601.
+ ret = hiPart << 32;
+ ret += loPart;
+ // Change starting time to the Epoch (00:00:00 UTC, January 1, 1970).
+ ret -= 116444736000000000ull;
+ // Convert nano secs to secs.
+ return (double) ret / 10000000ull;
+}
+
+
+double
+psutil_FiletimeToUnixTime(FILETIME ft) {
+ return _to_unix_time((ULONGLONG)ft.dwHighDateTime,
+ (ULONGLONG)ft.dwLowDateTime);
+
+}
+
+
+double
+psutil_LargeIntegerToUnixTime(LARGE_INTEGER li) {
+ return _to_unix_time((ULONGLONG)li.HighPart,
+ (ULONGLONG)li.LowPart);
+}
#endif // PSUTIL_WINDOWS
diff --git a/psutil/_psutil_common.h b/psutil/_psutil_common.h
index 97543206..34c428c0 100644
--- a/psutil/_psutil_common.h
+++ b/psutil/_psutil_common.h
@@ -134,4 +134,6 @@ int psutil_setup(void);
PVOID psutil_GetProcAddress(LPCSTR libname, LPCSTR procname);
PVOID psutil_GetProcAddressFromLib(LPCSTR libname, LPCSTR procname);
PVOID psutil_SetFromNTStatusErr(NTSTATUS Status, const char *syscall);
+ double psutil_FiletimeToUnixTime(FILETIME ft);
+ double psutil_LargeIntegerToUnixTime(LARGE_INTEGER li);
#endif
diff --git a/psutil/_psutil_windows.c b/psutil/_psutil_windows.c
index f9405707..64592103 100644
--- a/psutil/_psutil_windows.c
+++ b/psutil/_psutil_windows.c
@@ -79,30 +79,13 @@ psutil_get_num_cpus(int fail_on_err) {
*/
static PyObject *
psutil_boot_time(PyObject *self, PyObject *args) {
- ULONGLONG uptime;
- time_t pt;
+ ULONGLONG upTime;
FILETIME fileTime;
- ULONGLONG ll;
GetSystemTimeAsFileTime(&fileTime);
- /*
- HUGE thanks to:
- http://johnstewien.spaces.live.com/blog/cns!E6885DB5CEBABBC8!831.entry
-
- This function converts the FILETIME structure to the 32 bit
- Unix time structure.
- The time_t is a 32-bit value for the number of seconds since
- January 1, 1970. A FILETIME is a 64-bit for the number of
- 100-nanosecond periods since January 1, 1601. Convert by
- subtracting the number of 100-nanosecond period between 01-01-1970
- and 01-01-1601, from time_t the divide by 1e+7 to get to the same
- base granularity.
- */
- ll = (((ULONGLONG)
- (fileTime.dwHighDateTime)) << 32) + fileTime.dwLowDateTime;
- pt = (time_t)((ll - 116444736000000000ull) / 10000000ull);
- uptime = GetTickCount64() / 1000ull;
- return Py_BuildValue("K", pt - uptime);
+ // Number of milliseconds that have elapsed since the system was started.
+ upTime = GetTickCount64() / 1000ull;
+ return Py_BuildValue("d", psutil_FiletimeToUnixTime(fileTime) - upTime);
}
@@ -320,10 +303,10 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) {
*/
return Py_BuildValue(
"(dd)",
- (double)(ftUser.dwHighDateTime * 429.4967296 + \
- ftUser.dwLowDateTime * 1e-7),
- (double)(ftKernel.dwHighDateTime * 429.4967296 + \
- ftKernel.dwLowDateTime * 1e-7)
+ (double)(ftUser.dwHighDateTime * HI_T + \
+ ftUser.dwLowDateTime * LO_T),
+ (double)(ftKernel.dwHighDateTime * HI_T + \
+ ftKernel.dwLowDateTime * LO_T)
);
}
@@ -335,7 +318,6 @@ psutil_proc_cpu_times(PyObject *self, PyObject *args) {
static PyObject *
psutil_proc_create_time(PyObject *self, PyObject *args) {
DWORD pid;
- long long unix_time;
HANDLE hProcess;
FILETIME ftCreate, ftExit, ftKernel, ftUser;
@@ -363,34 +345,7 @@ psutil_proc_create_time(PyObject *self, PyObject *args) {
}
CloseHandle(hProcess);
-
- /*
- // Make sure the process is not gone as OpenProcess alone seems to be
- // unreliable in doing so (it seems a previous call to p.wait() makes
- // it unreliable).
- // This check is important as creation time is used to make sure the
- // process is still running.
- ret = GetExitCodeProcess(hProcess, &exitCode);
- CloseHandle(hProcess);
- if (ret != 0) {
- if (exitCode != STILL_ACTIVE)
- return NoSuchProcess("GetExitCodeProcess");
- }
- else {
- // Ignore access denied as it means the process is still alive.
- // For all other errors, we want an exception.
- if (GetLastError() != ERROR_ACCESS_DENIED)
- return PyErr_SetFromWindowsErr(0);
- }
- */
-
- // Convert the FILETIME structure to a Unix time.
- // It's the best I could find by googling and borrowing code here
- // and there. The time returned has a precision of 1 second.
- unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32;
- unix_time += ftCreate.dwLowDateTime - 116444736000000000LL;
- unix_time /= 10000000;
- return Py_BuildValue("d", (double)unix_time);
+ return Py_BuildValue("d", psutil_FiletimeToUnixTime(ftCreate));
}
@@ -844,10 +799,10 @@ psutil_proc_threads(PyObject *self, PyObject *args) {
py_tuple = Py_BuildValue(
"kdd",
te32.th32ThreadID,
- (double)(ftUser.dwHighDateTime * 429.4967296 + \
- ftUser.dwLowDateTime * 1e-7),
- (double)(ftKernel.dwHighDateTime * 429.4967296 + \
- ftKernel.dwLowDateTime * 1e-7));
+ (double)(ftUser.dwHighDateTime * HI_T + \
+ ftUser.dwLowDateTime * LO_T),
+ (double)(ftKernel.dwHighDateTime * HI_T + \
+ ftKernel.dwLowDateTime * LO_T));
if (!py_tuple)
goto error;
if (PyList_Append(py_retlist, py_tuple))
diff --git a/psutil/arch/windows/process_info.c b/psutil/arch/windows/process_info.c
index d8104c81..73a69912 100644
--- a/psutil/arch/windows/process_info.c
+++ b/psutil/arch/windows/process_info.c
@@ -578,7 +578,8 @@ out:
* fills the structure with various process information in one shot
* by using NtQuerySystemInformation.
* We use this as a fallback when faster functions fail with access
- * denied. This is slower because it iterates over all processes.
+ * denied. This is slower because it iterates over all processes
+ * but it doesn't require any privilege (also work for PID 0).
* On success return 1, else 0 with Python exception already set.
*/
int
@@ -669,7 +670,7 @@ psutil_proc_info(PyObject *self, PyObject *args) {
ULONG ctx_switches = 0;
double user_time;
double kernel_time;
- long long create_time;
+ double create_time;
PyObject *py_retlist;
if (! PyArg_ParseTuple(args, _Py_PARSE_PID, &pid))
@@ -692,9 +693,7 @@ psutil_proc_info(PyObject *self, PyObject *args) {
create_time = 0;
}
else {
- create_time = ((LONGLONG)process->CreateTime.HighPart) << 32;
- create_time += process->CreateTime.LowPart - 116444736000000000LL;
- create_time /= 10000000;
+ create_time = psutil_LargeIntegerToUnixTime(process->CreateTime);
}
py_retlist = Py_BuildValue(
@@ -707,7 +706,7 @@ psutil_proc_info(PyObject *self, PyObject *args) {
ctx_switches, // num ctx switches
user_time, // cpu user time
kernel_time, // cpu kernel time
- (double)create_time, // create time
+ create_time, // create time
(int)process->NumberOfThreads, // num threads
// IO counters
process->ReadOperationCount.QuadPart, // io rcount