Skip to content

Commit

Permalink
[Windows] increase precision of boot_time() and proc create_time() (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
giampaolo authored Feb 13, 2020
1 parent 573886f commit b0cff82
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 64 deletions.
2 changes: 2 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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**

Expand Down
38 changes: 38 additions & 0 deletions psutil/_psutil_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,7 @@ psutil_set_winver() {
return 0;
}


int
psutil_load_globals() {
if (psutil_loadlibs() != 0)
Expand All @@ -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
2 changes: 2 additions & 0 deletions psutil/_psutil_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
71 changes: 13 additions & 58 deletions psutil/_psutil_windows.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}


Expand Down Expand Up @@ -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)
);
}

Expand All @@ -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;

Expand Down Expand Up @@ -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));
}


Expand Down Expand Up @@ -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))
Expand Down
11 changes: 5 additions & 6 deletions psutil/arch/windows/process_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,8 @@ psutil_get_environ(DWORD pid) {
* 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
Expand Down Expand Up @@ -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))
Expand All @@ -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(
Expand All @@ -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
Expand Down

0 comments on commit b0cff82

Please sign in to comment.