OLD | NEW |
1 /* | 1 /* |
2 * $Id: _psutil_mswindows.c 796 2010-11-12 20:31:28Z g.rodola $ | 2 * $Id: _psutil_mswindows.c 1142 2011-10-05 18:45:49Z g.rodola $ |
| 3 * |
| 4 * Copyright (c) 2009, Jay Loden, Giampaolo Rodola'. All rights reserved. |
| 5 * Use of this source code is governed by a BSD-style license that can be |
| 6 * found in the LICENSE file. |
3 * | 7 * |
4 * Windows platform-specific module methods for _psutil_mswindows | 8 * Windows platform-specific module methods for _psutil_mswindows |
5 */ | 9 */ |
6 | 10 |
7 #include <Python.h> | 11 #include <Python.h> |
8 #include <windows.h> | 12 #include <windows.h> |
9 #include <Psapi.h> | 13 #include <Psapi.h> |
10 #include <time.h> | 14 #include <time.h> |
11 #include <lm.h> | 15 #include <lm.h> |
12 #include <tchar.h> | 16 #include <tchar.h> |
13 #include <tlhelp32.h> | 17 #include <tlhelp32.h> |
14 #include <iphlpapi.h> | 18 #include <iphlpapi.h> |
15 | 19 |
16 #include "_psutil_mswindows.h" | 20 #include "_psutil_mswindows.h" |
| 21 #include "_psutil_common.h" |
17 #include "arch/mswindows/security.h" | 22 #include "arch/mswindows/security.h" |
18 #include "arch/mswindows/process_info.h" | 23 #include "arch/mswindows/process_info.h" |
19 #include "arch/mswindows/process_handles.h" | 24 #include "arch/mswindows/process_handles.h" |
| 25 #include "arch/mswindows/ntextapi.h" |
20 | 26 |
21 | 27 |
22 // ------------------------ Python init --------------------------- | |
23 | |
24 static PyMethodDef | |
25 PsutilMethods[] = | |
26 { | |
27 // --- per-process functions | |
28 | |
29 {"get_process_name", get_process_name, METH_VARARGS, | |
30 "Return process name"}, | |
31 {"get_process_cmdline", get_process_cmdline, METH_VARARGS, | |
32 "Return process cmdline as a list of cmdline arguments"}, | |
33 {"get_process_ppid", get_process_ppid, METH_VARARGS, | |
34 "Return process ppid as an integer"}, | |
35 {"kill_process", kill_process, METH_VARARGS, | |
36 "Kill the process identified by the given PID"}, | |
37 {"get_process_cpu_times", get_process_cpu_times, METH_VARARGS, | |
38 "Return tuple of user/kern time for the given PID"}, | |
39 {"get_process_create_time", get_process_create_time, METH_VARARGS, | |
40 "Return a float indicating the process create time expressed in " | |
41 "seconds since the epoch"}, | |
42 {"get_memory_info", get_memory_info, METH_VARARGS, | |
43 "Return a tuple of RSS/VMS memory information"}, | |
44 {"get_process_cwd", get_process_cwd, METH_VARARGS, | |
45 "Return process current working directory"}, | |
46 {"suspend_process", suspend_process, METH_VARARGS, | |
47 "Suspend a process"}, | |
48 {"resume_process", resume_process, METH_VARARGS, | |
49 "Resume a process"}, | |
50 {"get_process_open_files", get_process_open_files, METH_VARARGS, | |
51 "Return files opened by process"}, | |
52 {"_QueryDosDevice", _QueryDosDevice, METH_VARARGS, | |
53 "QueryDosDevice binding"}, | |
54 {"get_process_username", get_process_username, METH_VARARGS, | |
55 "Return the username of a process"}, | |
56 {"get_process_connections", get_process_connections, METH_VARARGS, | |
57 "Return the network connections of a process"}, | |
58 {"get_process_num_threads", get_process_num_threads, METH_VARARGS, | |
59 "Return the network connections of a process"}, | |
60 | |
61 // --- system-related functions | |
62 | |
63 {"get_pid_list", get_pid_list, METH_VARARGS, | |
64 "Returns a list of PIDs currently running on the system"}, | |
65 {"pid_exists", pid_exists, METH_VARARGS, | |
66 "Determine if the process exists in the current process list."}, | |
67 {"get_num_cpus", get_num_cpus, METH_VARARGS, | |
68 "Returns the number of CPUs on the system"}, | |
69 {"get_system_uptime", get_system_uptime, METH_VARARGS, | |
70 "Return system uptime"}, | |
71 {"get_total_phymem", get_total_phymem, METH_VARARGS, | |
72 "Return the total amount of physical memory, in bytes"}, | |
73 {"get_total_virtmem", get_total_virtmem, METH_VARARGS, | |
74 "Return the total amount of virtual memory, in bytes"}, | |
75 {"get_avail_phymem", get_avail_phymem, METH_VARARGS, | |
76 "Return the amount of available physical memory, in bytes"}, | |
77 {"get_avail_virtmem", get_avail_virtmem, METH_VARARGS, | |
78 "Return the amount of available virtual memory, in bytes"}, | |
79 {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, | |
80 "Return system cpu times as a tuple (user, system, idle)"}, | |
81 | |
82 {NULL, NULL, 0, NULL} | |
83 }; | |
84 | |
85 | |
86 struct module_state { | |
87 PyObject *error; | |
88 }; | |
89 | |
90 #if PY_MAJOR_VERSION >= 3 | |
91 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) | |
92 #else | |
93 #define GETSTATE(m) (&_state) | |
94 static struct module_state _state; | |
95 #endif | |
96 | |
97 #if PY_MAJOR_VERSION >= 3 | |
98 | |
99 static int psutil_mswindows_traverse(PyObject *m, visitproc visit, void *arg
) { | |
100 Py_VISIT(GETSTATE(m)->error); | |
101 return 0; | |
102 } | |
103 | |
104 static int psutil_mswindows_clear(PyObject *m) { | |
105 Py_CLEAR(GETSTATE(m)->error); | |
106 return 0; | |
107 } | |
108 | |
109 static struct PyModuleDef moduledef = { | |
110 PyModuleDef_HEAD_INIT, | |
111 "psutil_mswindows", | |
112 NULL, | |
113 sizeof(struct module_state), | |
114 PsutilMethods, | |
115 NULL, | |
116 psutil_mswindows_traverse, | |
117 psutil_mswindows_clear, | |
118 NULL | |
119 }; | |
120 | |
121 #define INITERROR return NULL | |
122 | |
123 PyObject* PyInit__psutil_mswindows(void) | |
124 | |
125 #else | |
126 #define INITERROR return | |
127 void init_psutil_mswindows(void) | |
128 #endif | |
129 { | |
130 struct module_state *st = NULL; | |
131 #if PY_MAJOR_VERSION >= 3 | |
132 PyObject *module = PyModule_Create(&moduledef); | |
133 #else | |
134 PyObject *module = Py_InitModule("_psutil_mswindows", PsutilMethods); | |
135 #endif | |
136 | |
137 if (module == NULL) { | |
138 INITERROR; | |
139 } | |
140 | |
141 st = GETSTATE(module); | |
142 st->error = PyErr_NewException("_psutil_mswindow.Error", NULL, NULL); | |
143 if (st->error == NULL) { | |
144 Py_DECREF(module); | |
145 INITERROR; | |
146 } | |
147 | |
148 SetSeDebug(); | |
149 | |
150 #if PY_MAJOR_VERSION >= 3 | |
151 return module; | |
152 #endif | |
153 } | |
154 | |
155 | |
156 // ------------------------ Public functions --------------------------- | |
157 | |
158 /* | 28 /* |
159 * Return a Python float representing the system uptime expressed in seconds | 29 * Return a Python float representing the system uptime expressed in seconds |
160 * since the epoch. | 30 * since the epoch. |
161 */ | 31 */ |
162 static PyObject* | 32 static PyObject* |
163 get_system_uptime(PyObject* self, PyObject* args) | 33 get_system_uptime(PyObject* self, PyObject* args) |
164 { | 34 { |
165 double uptime; | 35 double uptime; |
166 time_t pt; | 36 time_t pt; |
167 FILETIME fileTime; | 37 FILETIME fileTime; |
(...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 | 116 |
247 /* | 117 /* |
248 * Kill a process given its PID. | 118 * Kill a process given its PID. |
249 */ | 119 */ |
250 static PyObject* | 120 static PyObject* |
251 kill_process(PyObject* self, PyObject* args) | 121 kill_process(PyObject* self, PyObject* args) |
252 { | 122 { |
253 HANDLE hProcess; | 123 HANDLE hProcess; |
254 long pid; | 124 long pid; |
255 | 125 |
256 if (! PyArg_ParseTuple(args, "l", &pid)) | 126 if (! PyArg_ParseTuple(args, "l", &pid)) { |
257 return NULL; | 127 return NULL; |
258 if (pid == 0) | 128 } |
| 129 if (pid == 0) { |
259 return AccessDenied(); | 130 return AccessDenied(); |
| 131 } |
260 | 132 |
261 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); | 133 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, pid); |
262 if (hProcess == NULL) { | 134 if (hProcess == NULL) { |
263 if (GetLastError() == ERROR_INVALID_PARAMETER) { | 135 if (GetLastError() == ERROR_INVALID_PARAMETER) { |
264 // see http://code.google.com/p/psutil/issues/detail?id=24 | 136 // see http://code.google.com/p/psutil/issues/detail?id=24 |
265 NoSuchProcess(); | 137 NoSuchProcess(); |
266 } | 138 } |
267 else { | 139 else { |
268 PyErr_SetFromWindowsErr(0); | 140 PyErr_SetFromWindowsErr(0); |
269 } | 141 } |
270 return NULL; | 142 return NULL; |
271 } | 143 } |
272 | 144 |
273 // kill the process | 145 // kill the process |
274 if (! TerminateProcess(hProcess, 0)) { | 146 if (! TerminateProcess(hProcess, 0)) { |
275 PyErr_SetFromWindowsErr(0); | 147 PyErr_SetFromWindowsErr(0); |
276 CloseHandle(hProcess); | 148 CloseHandle(hProcess); |
277 return NULL; | 149 return NULL; |
278 } | 150 } |
279 | 151 |
280 CloseHandle(hProcess); | 152 CloseHandle(hProcess); |
281 Py_INCREF(Py_None); | 153 Py_INCREF(Py_None); |
282 return Py_None; | 154 return Py_None; |
283 } | 155 } |
284 | 156 |
285 | 157 |
| 158 /* |
| 159 * Wait for process to terminate and return its exit code. |
| 160 */ |
| 161 static PyObject* |
| 162 process_wait(PyObject* self, PyObject* args) |
| 163 { |
| 164 HANDLE hProcess; |
| 165 DWORD ExitCode; |
| 166 DWORD retVal; |
| 167 long pid; |
| 168 long timeout; |
| 169 |
| 170 if (! PyArg_ParseTuple(args, "ll", &pid, &timeout)) { |
| 171 return NULL; |
| 172 } |
| 173 if (pid == 0) { |
| 174 return AccessDenied(); |
| 175 } |
| 176 |
| 177 hProcess = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_INFORMATION, FALSE, pid); |
| 178 if (hProcess == NULL) { |
| 179 if (GetLastError() == ERROR_INVALID_PARAMETER) { |
| 180 // no such process; we do not want to raise NSP but |
| 181 // return None instead. |
| 182 Py_INCREF(Py_None); |
| 183 return Py_None; |
| 184 } |
| 185 else { |
| 186 PyErr_SetFromWindowsErr(0); |
| 187 return NULL; |
| 188 } |
| 189 } |
| 190 |
| 191 // wait until the process has terminated |
| 192 Py_BEGIN_ALLOW_THREADS |
| 193 retVal = WaitForSingleObject(hProcess, timeout); |
| 194 Py_END_ALLOW_THREADS |
| 195 |
| 196 if (retVal == WAIT_FAILED) { |
| 197 CloseHandle(hProcess); |
| 198 return PyErr_SetFromWindowsErr(GetLastError()); |
| 199 } |
| 200 if (retVal == WAIT_TIMEOUT) { |
| 201 CloseHandle(hProcess); |
| 202 return Py_BuildValue("l", WAIT_TIMEOUT); |
| 203 } |
| 204 |
| 205 // get the exit code; note: subprocess module (erroneously?) uses |
| 206 // what returned by WaitForSingleObject |
| 207 if (GetExitCodeProcess(hProcess, &ExitCode) == 0) { |
| 208 CloseHandle(hProcess); |
| 209 return PyErr_SetFromWindowsErr(GetLastError()); |
| 210 } |
| 211 CloseHandle(hProcess); |
| 212 #if PY_MAJOR_VERSION >= 3 |
| 213 return PyLong_FromLong((long) ExitCode); |
| 214 #else |
| 215 return PyInt_FromLong((long) ExitCode); |
| 216 #endif |
| 217 } |
| 218 |
286 | 219 |
287 /* | 220 /* |
288 * Return a Python tuple (user_time, kernel_time) | 221 * Return a Python tuple (user_time, kernel_time) |
289 */ | 222 */ |
290 static PyObject* | 223 static PyObject* |
291 get_process_cpu_times(PyObject* self, PyObject* args) | 224 get_process_cpu_times(PyObject* self, PyObject* args) |
292 { | 225 { |
293 long pid; | 226 long pid; |
294 HANDLE hProcess; | 227 HANDLE hProcess; |
295 FILETIME ftCreate, ftExit, ftKernel, ftUser; | 228 FILETIME ftCreate, ftExit, ftKernel, ftUser; |
296 | 229 |
297 if (! PyArg_ParseTuple(args, "l", &pid)) { | 230 if (! PyArg_ParseTuple(args, "l", &pid)) { |
298 return NULL; | 231 return NULL; |
299 } | 232 } |
300 | 233 |
301 // special case for PID 0 | 234 // special case for PID 0 |
302 if (0 == pid){ | 235 if (0 == pid){ |
303 return Py_BuildValue("(dd)", 0.0, 0.0); | 236 return Py_BuildValue("(dd)", 0.0, 0.0); |
304 } | 237 } |
305 | 238 |
306 hProcess = handle_from_pid(pid); | 239 hProcess = handle_from_pid(pid); |
307 if (hProcess == NULL) { | 240 if (hProcess == NULL) { |
308 return NULL; | 241 return NULL; |
309 } | 242 } |
310 | 243 |
311 if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { | 244 if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { |
| 245 CloseHandle(hProcess); |
312 if (GetLastError() == ERROR_ACCESS_DENIED) { | 246 if (GetLastError() == ERROR_ACCESS_DENIED) { |
313 // usually means the process has died so we throw a NoSuchProcess | 247 // usually means the process has died so we throw a NoSuchProcess |
314 // here | 248 // here |
315 CloseHandle(hProcess); | |
316 return NoSuchProcess(); | 249 return NoSuchProcess(); |
317 } | 250 } |
318 PyErr_SetFromWindowsErr(0); | 251 else { |
319 CloseHandle(hProcess); | 252 PyErr_SetFromWindowsErr(0); |
320 return NULL; | 253 return NULL; |
| 254 } |
321 } | 255 } |
322 | 256 |
323 CloseHandle(hProcess); | 257 CloseHandle(hProcess); |
324 | 258 |
325 /* | 259 /* |
326 user and kernel times are represented as a FILETIME structure wich contains | 260 user and kernel times are represented as a FILETIME structure wich contains |
327 a 64-bit value representing the number of 100-nanosecond intervals since | 261 a 64-bit value representing the number of 100-nanosecond intervals since |
328 January 1, 1601 (UTC). | 262 January 1, 1601 (UTC). |
329 http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx | 263 http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx |
330 | 264 |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
363 if ( (0 == pid) || (4 == pid) ){ | 297 if ( (0 == pid) || (4 == pid) ){ |
364 return Py_BuildValue("d", 0.0); | 298 return Py_BuildValue("d", 0.0); |
365 } | 299 } |
366 | 300 |
367 hProcess = handle_from_pid(pid); | 301 hProcess = handle_from_pid(pid); |
368 if (hProcess == NULL) { | 302 if (hProcess == NULL) { |
369 return NULL; | 303 return NULL; |
370 } | 304 } |
371 | 305 |
372 if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { | 306 if (! GetProcessTimes(hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser)) { |
| 307 CloseHandle(hProcess); |
373 if (GetLastError() == ERROR_ACCESS_DENIED) { | 308 if (GetLastError() == ERROR_ACCESS_DENIED) { |
374 // usually means the process has died so we throw a NoSuchProcess he
re | 309 // usually means the process has died so we throw a NoSuchProcess he
re |
375 return NoSuchProcess(); | 310 return NoSuchProcess(); |
376 } | 311 } |
377 else { | 312 else { |
378 PyErr_SetFromWindowsErr(0); | 313 PyErr_SetFromWindowsErr(0); |
| 314 return NULL; |
379 } | 315 } |
380 CloseHandle(hProcess); | |
381 return NULL; | |
382 } | 316 } |
383 | 317 |
384 CloseHandle(hProcess); | 318 CloseHandle(hProcess); |
385 | 319 |
386 /* | 320 /* |
387 Convert the FILETIME structure to a Unix time. | 321 Convert the FILETIME structure to a Unix time. |
388 It's the best I could find by googling and borrowing code here and there. | 322 It's the best I could find by googling and borrowing code here and there. |
389 The time returned has a precision of 1 second. | 323 The time returned has a precision of 1 second. |
390 */ | 324 */ |
391 unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32; | 325 unix_time = ((LONGLONG)ftCreate.dwHighDateTime) << 32; |
(...skipping 22 matching lines...) Expand all Loading... |
414 | 348 |
415 /* | 349 /* |
416 * Return process name as a Python string. | 350 * Return process name as a Python string. |
417 */ | 351 */ |
418 static PyObject* | 352 static PyObject* |
419 get_process_name(PyObject* self, PyObject* args) { | 353 get_process_name(PyObject* self, PyObject* args) { |
420 long pid; | 354 long pid; |
421 int pid_return; | 355 int pid_return; |
422 PyObject* name; | 356 PyObject* name; |
423 | 357 |
424 if (! PyArg_ParseTuple(args, "l", &pid)) | 358 if (! PyArg_ParseTuple(args, "l", &pid)) { |
425 return NULL; | 359 return NULL; |
| 360 } |
426 | 361 |
427 if (pid == 0) { | 362 if (pid == 0) { |
428 return Py_BuildValue("s", "System Idle Process"); | 363 return Py_BuildValue("s", "System Idle Process"); |
429 } | 364 } |
430 else if (pid == 4) { | 365 else if (pid == 4) { |
431 return Py_BuildValue("s", "System"); | 366 return Py_BuildValue("s", "System"); |
432 } | 367 } |
433 | 368 |
434 pid_return = pid_is_running(pid); | 369 pid_return = pid_is_running(pid); |
435 if (pid_return == 0) { | 370 if (pid_return == 0) { |
436 return NoSuchProcess(); | 371 return NoSuchProcess(); |
437 } | 372 } |
438 if (pid_return == -1) { | 373 if (pid_return == -1) { |
439 return NULL; | 374 return NULL; |
440 } | 375 } |
441 | 376 |
442 name = get_name(pid); | 377 name = get_name(pid); |
443 if (name == NULL) | 378 if (name == NULL) { |
444 return NULL; // exception set in get_name() | 379 return NULL; // exception set in get_name() |
| 380 } |
445 return name; | 381 return name; |
446 } | 382 } |
447 | 383 |
448 | 384 |
449 /* | 385 /* |
450 * Return process parent pid as a Python integer. | 386 * Return process parent pid as a Python integer. |
451 */ | 387 */ |
452 static PyObject* | 388 static PyObject* |
453 get_process_ppid(PyObject* self, PyObject* args) { | 389 get_process_ppid(PyObject* self, PyObject* args) { |
454 long pid; | 390 long pid; |
455 int pid_return; | 391 int pid_return; |
456 PyObject* ppid; | 392 PyObject* ppid; |
457 | 393 |
458 if (! PyArg_ParseTuple(args, "l", &pid)) | 394 if (! PyArg_ParseTuple(args, "l", &pid)) { |
459 return NULL; | 395 return NULL; |
460 if ((pid == 0) || (pid == 4)) | 396 } |
| 397 if ((pid == 0) || (pid == 4)) { |
461 return Py_BuildValue("l", 0); | 398 return Py_BuildValue("l", 0); |
| 399 } |
462 | 400 |
463 pid_return = pid_is_running(pid); | 401 pid_return = pid_is_running(pid); |
464 if (pid_return == 0) { | 402 if (pid_return == 0) { |
465 return NoSuchProcess(); | 403 return NoSuchProcess(); |
466 } | 404 } |
467 if (pid_return == -1) { | 405 if (pid_return == -1) { |
468 return NULL; | 406 return NULL; |
469 } | 407 } |
470 | 408 |
471 ppid = get_ppid(pid); | 409 ppid = get_ppid(pid); |
472 if (ppid == NULL) | 410 if (ppid == NULL) { |
473 return NULL; // exception set in get_ppid() | 411 return NULL; // exception set in get_ppid() |
| 412 } |
474 return ppid; | 413 return ppid; |
475 } | 414 } |
476 | 415 |
477 /* | 416 /* |
478 * Return process cmdline as a Python list of cmdline arguments. | 417 * Return process cmdline as a Python list of cmdline arguments. |
479 */ | 418 */ |
480 static PyObject* | 419 static PyObject* |
481 get_process_cmdline(PyObject* self, PyObject* args) { | 420 get_process_cmdline(PyObject* self, PyObject* args) { |
482 long pid; | 421 long pid; |
483 int pid_return; | 422 int pid_return; |
484 PyObject* arglist; | 423 PyObject* arglist; |
485 | 424 |
486 if (! PyArg_ParseTuple(args, "l", &pid)) | 425 if (! PyArg_ParseTuple(args, "l", &pid)) { |
487 return NULL; | 426 return NULL; |
488 if ((pid == 0) || (pid == 4)) | 427 } |
| 428 if ((pid == 0) || (pid == 4)) { |
489 return Py_BuildValue("[]"); | 429 return Py_BuildValue("[]"); |
| 430 } |
490 | 431 |
491 pid_return = pid_is_running(pid); | 432 pid_return = pid_is_running(pid); |
492 if (pid_return == 0) { | 433 if (pid_return == 0) { |
493 return NoSuchProcess(); | 434 return NoSuchProcess(); |
494 } | 435 } |
495 if (pid_return == -1) { | 436 if (pid_return == -1) { |
496 return NULL; | 437 return NULL; |
497 } | 438 } |
498 | 439 |
499 // May fail any of several ReadProcessMemory calls etc. and not indicate | 440 // May fail any of several ReadProcessMemory calls etc. and not indicate |
(...skipping 22 matching lines...) Expand all Loading... |
522 if (! PyArg_ParseTuple(args, "l", &pid)) { | 463 if (! PyArg_ParseTuple(args, "l", &pid)) { |
523 return NULL; | 464 return NULL; |
524 } | 465 } |
525 | 466 |
526 hProcess = handle_from_pid(pid); | 467 hProcess = handle_from_pid(pid); |
527 if (NULL == hProcess) { | 468 if (NULL == hProcess) { |
528 return NULL; | 469 return NULL; |
529 } | 470 } |
530 | 471 |
531 if (! GetProcessMemoryInfo(hProcess, &counters, sizeof(counters)) ) { | 472 if (! GetProcessMemoryInfo(hProcess, &counters, sizeof(counters)) ) { |
| 473 CloseHandle(hProcess); |
532 return PyErr_SetFromWindowsErr(0); | 474 return PyErr_SetFromWindowsErr(0); |
533 } | 475 } |
534 | 476 |
| 477 CloseHandle(hProcess); |
535 return Py_BuildValue("(nn)", counters.WorkingSetSize, counters.PagefileUsage
); | 478 return Py_BuildValue("(nn)", counters.WorkingSetSize, counters.PagefileUsage
); |
536 } | 479 } |
537 | 480 |
538 | 481 |
539 /* | 482 /* |
540 * Return a Python integer indicating the total amount of physical memory | 483 * Return a Python integer indicating the total amount of physical memory |
541 * in bytes. | 484 * in bytes. |
542 */ | 485 */ |
543 static PyObject* | 486 static PyObject* |
544 get_total_phymem(PyObject* self, PyObject* args) | 487 get_system_phymem(PyObject* self, PyObject* args) |
545 { | 488 { |
546 MEMORYSTATUSEX memInfo; | 489 MEMORYSTATUSEX memInfo; |
547 memInfo.dwLength = sizeof(MEMORYSTATUSEX); | 490 memInfo.dwLength = sizeof(MEMORYSTATUSEX); |
548 | 491 |
549 if (! GlobalMemoryStatusEx(&memInfo) ) { | 492 if (! GlobalMemoryStatusEx(&memInfo) ) { |
550 return PyErr_SetFromWindowsErr(0); | 493 return PyErr_SetFromWindowsErr(0); |
551 } | 494 } |
552 | 495 |
553 return Py_BuildValue("L", memInfo.ullTotalPhys); | 496 return Py_BuildValue("(LLLLLLk)", |
554 } | 497 memInfo.ullTotalPhys, // total |
555 | 498 memInfo.ullAvailPhys, // avail |
556 | 499 memInfo.ullTotalPageFile, // total page file |
557 /* | 500 memInfo.ullAvailPageFile, // avail page file |
558 * Return a Python integer indicating the total amount of virtual memory | 501 memInfo.ullTotalVirtual, // total virtual |
559 * in bytes. | 502 memInfo.ullAvailVirtual, // avail virtual |
560 */ | 503 memInfo.dwMemoryLoad // percent |
561 static PyObject* | 504 ); |
562 get_total_virtmem(PyObject* self, PyObject* args) | |
563 { | |
564 MEMORYSTATUSEX memInfo; | |
565 memInfo.dwLength = sizeof(MEMORYSTATUSEX); | |
566 | |
567 if (! GlobalMemoryStatusEx(&memInfo) ) { | |
568 return PyErr_SetFromWindowsErr(0); | |
569 } | |
570 | |
571 return Py_BuildValue("L", memInfo.ullTotalPageFile); | |
572 } | |
573 | |
574 | |
575 /* | |
576 * Return a Python integer indicating the amount of available physical memory | |
577 * in bytes. | |
578 */ | |
579 static PyObject* | |
580 get_avail_phymem(PyObject* self, PyObject* args) | |
581 { | |
582 MEMORYSTATUSEX memInfo; | |
583 memInfo.dwLength = sizeof(MEMORYSTATUSEX); | |
584 if (! GlobalMemoryStatusEx(&memInfo) ) { | |
585 return PyErr_SetFromWindowsErr(0); | |
586 } | |
587 return Py_BuildValue("L", memInfo.ullAvailPhys); | |
588 } | |
589 | |
590 | |
591 /* | |
592 * Return a Python integer indicating the amount of available virtual memory | |
593 * in bytes. | |
594 */ | |
595 static PyObject* | |
596 get_avail_virtmem(PyObject* self, PyObject* args) | |
597 { | |
598 MEMORYSTATUSEX memInfo; | |
599 memInfo.dwLength = sizeof(MEMORYSTATUSEX); | |
600 | |
601 if (! GlobalMemoryStatusEx(&memInfo) ) { | |
602 return PyErr_SetFromWindowsErr(0); | |
603 } | |
604 return Py_BuildValue("L", memInfo.ullAvailPageFile); | |
605 } | 505 } |
606 | 506 |
607 | 507 |
608 #define LO_T ((float)1e-7) | 508 #define LO_T ((float)1e-7) |
609 #define HI_T (LO_T*4294967296.0) | 509 #define HI_T (LO_T*4294967296.0) |
610 | 510 |
611 // structures and enums from winternl.h (not available under mingw) | |
612 typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { | |
613 LARGE_INTEGER IdleTime; | |
614 LARGE_INTEGER KernelTime; | |
615 LARGE_INTEGER UserTime; | |
616 LARGE_INTEGER Reserved1[2]; | |
617 ULONG Reserved2; | |
618 } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFOR
MATION; | |
619 | |
620 | |
621 typedef enum _SYSTEM_INFORMATION_CLASS { | |
622 SystemBasicInformation = 0, | |
623 SystemPerformanceInformation = 2, | |
624 SystemTimeOfDayInformation = 3, | |
625 SystemProcessInformation = 5, | |
626 SystemProcessorPerformanceInformation = 8, | |
627 SystemInterruptInformation = 23, | |
628 SystemExceptionInformation = 33, | |
629 SystemRegistryQuotaInformation = 37, | |
630 SystemLookasideInformation = 45 | |
631 } SYSTEM_INFORMATION_CLASS; | |
632 | |
633 | 511 |
634 /* | 512 /* |
635 * Return a Python tuple representing user, kernel and idle CPU times | 513 * Return a Python list of tuples representing user, kernel and idle |
| 514 * CPU times for every CPU on the system. |
636 */ | 515 */ |
637 static PyObject* | 516 static PyObject* |
638 get_system_cpu_times(PyObject* self, PyObject* args) | 517 get_system_cpu_times(PyObject* self, PyObject* args) |
639 { | 518 { |
640 typedef BOOL (_stdcall *GST_PROC) (LPFILETIME, LPFILETIME, LPFILETIME); | |
641 static GST_PROC GetSystemTimes; | |
642 static BOOL bFirstCall = TRUE; | |
643 float idle, kernel, user; | 519 float idle, kernel, user; |
| 520 typedef DWORD (_stdcall *NTQSI_PROC) (int, PVOID, ULONG, PULONG); |
| 521 NTQSI_PROC NtQuerySystemInformation; |
| 522 HINSTANCE hNtDll; |
| 523 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; |
| 524 SYSTEM_INFO si; |
| 525 UINT i; |
| 526 PyObject *arg = NULL; |
| 527 PyObject *retlist = PyList_New(0); |
644 | 528 |
645 // Improves performance calling GetProcAddress only the first time | 529 // dynamic linking is mandatory to use NtQuerySystemInformation |
646 if (bFirstCall) { | 530 hNtDll = LoadLibrary(TEXT("ntdll.dll")); |
647 // retrieves GetSystemTimes address in Kernel32 | 531 if (hNtDll != NULL) { |
648 GetSystemTimes=(GST_PROC)GetProcAddress(GetModuleHandle | 532 // gets NtQuerySystemInformation address |
649 (TEXT("Kernel32.dll")), | 533 NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress( |
650 "GetSystemTimes"); | 534 hNtDll, "NtQuerySystemInformation"); |
651 bFirstCall = FALSE; | 535 |
| 536 if (NtQuerySystemInformation != NULL) |
| 537 { |
| 538 // retrives number of processors |
| 539 GetSystemInfo(&si); |
| 540 |
| 541 // allocates an array of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION |
| 542 // structures, one per processor |
| 543 sppi = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ |
| 544 malloc(si.dwNumberOfProcessors * \ |
| 545 sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); |
| 546 if (sppi != NULL) |
| 547 { |
| 548 // gets cpu time informations |
| 549 if (0 == NtQuerySystemInformation( |
| 550 SystemProcessorPerformanceInformation, |
| 551 sppi, |
| 552 si.dwNumberOfProcessors * sizeof |
| 553 (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION), |
| 554 NULL) |
| 555 ) |
| 556 { |
| 557 // computes system global times summing each processor value |
| 558 idle = user = kernel = 0; |
| 559 for (i=0; i<si.dwNumberOfProcessors; i++) { |
| 560 user = (float)((HI_T * sppi[i].UserTime.HighPart) + \ |
| 561 (LO_T * sppi[i].UserTime.LowPart)); |
| 562 idle = (float)((HI_T * sppi[i].IdleTime.HighPart) + \ |
| 563 (LO_T * sppi[i].IdleTime.LowPart)); |
| 564 kernel = (float)((HI_T * sppi[i].KernelTime.HighPart) +
\ |
| 565 (LO_T * sppi[i].KernelTime.LowPart)); |
| 566 // kernel time includes idle time on windows |
| 567 // we return only busy kernel time subtracting |
| 568 // idle time from kernel time |
| 569 arg = Py_BuildValue("(ddd)", user, |
| 570 kernel - idle, |
| 571 idle); |
| 572 PyList_Append(retlist, arg); |
| 573 Py_XDECREF(arg); |
| 574 } |
| 575 free(sppi); |
| 576 FreeLibrary(hNtDll); |
| 577 return retlist; |
| 578 |
| 579 } // END NtQuerySystemInformation |
| 580 } // END malloc SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION |
| 581 } // END GetProcAddress |
| 582 } // END LoadLibrary |
| 583 |
| 584 if (sppi) { |
| 585 free(sppi); |
652 } | 586 } |
653 | 587 if (hNtDll) { |
654 | 588 FreeLibrary(hNtDll); |
655 // Uses GetSystemTimes if supported (winXP sp1+) | |
656 if (NULL!=GetSystemTimes) { | |
657 // GetSystemTimes supported | |
658 | |
659 FILETIME idle_time; | |
660 FILETIME kernel_time; | |
661 FILETIME user_time; | |
662 | |
663 if (!GetSystemTimes(&idle_time, &kernel_time, &user_time)) { | |
664 return PyErr_SetFromWindowsErr(0); | |
665 } | |
666 | |
667 idle = (float)((HI_T * idle_time.dwHighDateTime) + \ | |
668 (LO_T * idle_time.dwLowDateTime)); | |
669 user = (float)((HI_T * user_time.dwHighDateTime) + \ | |
670 (LO_T * user_time.dwLowDateTime)); | |
671 kernel = (float)((HI_T * kernel_time.dwHighDateTime) + \ | |
672 (LO_T * kernel_time.dwLowDateTime)); | |
673 | |
674 // kernel time includes idle time on windows | |
675 // we return only busy kernel time subtracting idle time from kernel tim
e | |
676 return Py_BuildValue("(fff)", user, | |
677 kernel - idle, | |
678 idle); | |
679 | |
680 } | 589 } |
681 | 590 PyErr_SetFromWindowsErr(0); |
682 else { | 591 return NULL; |
683 // GetSystemTimes NOT supported, use NtQuerySystemInformation instead | |
684 | |
685 typedef DWORD (_stdcall *NTQSI_PROC) (int, PVOID, ULONG, PULONG); | |
686 NTQSI_PROC NtQuerySystemInformation; | |
687 HINSTANCE hNtDll; | |
688 SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *sppi = NULL; | |
689 SYSTEM_INFO si; | |
690 UINT i; | |
691 | |
692 // dynamic linking is mandatory to use NtQuerySystemInformation | |
693 hNtDll = LoadLibrary(TEXT("ntdll.dll")); | |
694 if (hNtDll != NULL) { | |
695 // gets NtQuerySystemInformation address | |
696 NtQuerySystemInformation = (NTQSI_PROC)GetProcAddress( | |
697 hNtDll, "NtQuerySystemInformation"); | |
698 | |
699 if (NtQuerySystemInformation != NULL) | |
700 { | |
701 // retrives number of processors | |
702 GetSystemInfo(&si); | |
703 | |
704 // allocates an array of SYSTEM_PROCESSOR_PERFORMANCE_INFORMATIO
N | |
705 // structures, one per processor | |
706 sppi=(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION *) \ | |
707 malloc(si.dwNumberOfProcessors * \ | |
708 sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); | |
709 if (sppi != NULL) | |
710 { | |
711 // gets cpu time informations | |
712 if (0 == NtQuerySystemInformation( | |
713 SystemProcessorPerformanceInformation, | |
714 sppi, | |
715 si.dwNumberOfProcessors * sizeof(SYSTEM_PROCESSO
R_PERFORMANCE_INFORMATION), | |
716 NULL)) | |
717 { | |
718 // computes system global times summing each processor v
alue | |
719 idle = user = kernel = 0; | |
720 for (i=0; i<si.dwNumberOfProcessors; i++) { | |
721 idle += (float)((HI_T * sppi[i].IdleTime.HighPart) +
\ | |
722 (LO_T * sppi[i].IdleTime.LowPart)); | |
723 user += (float)((HI_T * sppi[i].UserTime.HighPart) +
\ | |
724 (LO_T * sppi[i].UserTime.LowPart)); | |
725 kernel += (float)((HI_T * sppi[i].KernelTime.HighPar
t) + \ | |
726 (LO_T * sppi[i].KernelTime.LowPart
)); | |
727 } | |
728 | |
729 // kernel time includes idle time on windows | |
730 // we return only busy kernel time subtracting idle | |
731 // time from kernel time | |
732 return Py_BuildValue("(ddd)", user, | |
733 kernel - idle, | |
734 idle | |
735 ); | |
736 | |
737 } // END NtQuerySystemInformation | |
738 | |
739 } // END malloc SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION | |
740 | |
741 } // END GetProcAddress | |
742 | |
743 } // END LoadLibrary | |
744 | |
745 PyErr_SetFromWindowsErr(0); | |
746 if (sppi) { | |
747 free(sppi); | |
748 } | |
749 if (hNtDll) { | |
750 FreeLibrary(hNtDll); | |
751 } | |
752 return 0; | |
753 | |
754 } // END GetSystemTimes NOT supported | |
755 } | 592 } |
756 | 593 |
757 | 594 |
758 /* | 595 /* |
759 * Sid to User convertion | |
760 */ | |
761 BOOL SidToUser(PSID pSid, LPTSTR szUser, DWORD dwUserLen, LPTSTR szDomain, DWORD | |
762 dwDomainLen, DWORD pid) | |
763 { | |
764 SID_NAME_USE snuSIDNameUse; | |
765 DWORD dwULen; | |
766 DWORD dwDLen; | |
767 | |
768 dwULen = dwUserLen; | |
769 dwDLen = dwDomainLen; | |
770 | |
771 if ( IsValidSid( pSid ) ) { | |
772 // Get user and domain name based on SID | |
773 if ( LookupAccountSid( NULL, pSid, szUser, &dwULen, szDomain, &dwDLen, &
snuSIDNameUse) ) { | |
774 // LocalSystem processes are incorrectly reported as owned | |
775 // by BUILTIN\Administrators We modify that behavior to | |
776 // conform to standard taskmanager only if the process is | |
777 // actually a System process | |
778 if (is_system_proc(pid) == 1) { | |
779 // default to *not* changing the data if we fail to | |
780 // check for local system privileges, so only look for | |
781 // definite confirmed system processes and ignore errors | |
782 if ( lstrcmpi(szDomain, TEXT("builtin")) == 0 && lstrcmpi(szUser
, TEXT("administrators")) == 0) { | |
783 strncpy (szUser, "SYSTEM", dwUserLen); | |
784 strncpy (szDomain, "NT AUTHORITY", dwDomainLen); | |
785 } | |
786 } | |
787 | |
788 return TRUE; | |
789 } | |
790 } | |
791 | |
792 return FALSE; | |
793 } | |
794 | |
795 typedef struct _UNICODE_STRING { | |
796 USHORT Length; | |
797 USHORT MaximumLength; | |
798 PWSTR Buffer; | |
799 } UNICODE_STRING, *PUNICODE_STRING; | |
800 | |
801 /* | |
802 * Return process current working directory as a Python string. | 596 * Return process current working directory as a Python string. |
803 */ | 597 */ |
804 | 598 |
805 static PyObject* | 599 static PyObject* |
806 get_process_cwd(PyObject* self, PyObject* args) | 600 get_process_cwd(PyObject* self, PyObject* args) |
807 { | 601 { |
808 long pid; | 602 long pid; |
809 HANDLE processHandle; | 603 HANDLE processHandle; |
810 PVOID pebAddress; | 604 PVOID pebAddress; |
811 PVOID rtlUserProcParamsAddress; | 605 PVOID rtlUserProcParamsAddress; |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
915 /* | 709 /* |
916 * Resume or suspends a process | 710 * Resume or suspends a process |
917 */ | 711 */ |
918 int | 712 int |
919 suspend_resume_process(DWORD pid, int suspend) | 713 suspend_resume_process(DWORD pid, int suspend) |
920 { | 714 { |
921 // a huge thanks to http://www.codeproject.com/KB/threads/pausep.aspx | 715 // a huge thanks to http://www.codeproject.com/KB/threads/pausep.aspx |
922 HANDLE hThreadSnap = NULL; | 716 HANDLE hThreadSnap = NULL; |
923 THREADENTRY32 te32 = {0}; | 717 THREADENTRY32 te32 = {0}; |
924 | 718 |
| 719 if (pid == 0) { |
| 720 AccessDenied(); |
| 721 return FALSE; |
| 722 } |
| 723 |
925 hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); | 724 hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); |
926 if (hThreadSnap == INVALID_HANDLE_VALUE) { | 725 if (hThreadSnap == INVALID_HANDLE_VALUE) { |
927 PyErr_SetFromWindowsErr(0); | 726 PyErr_SetFromWindowsErr(0); |
928 return FALSE; | 727 return FALSE; |
929 } | 728 } |
930 | 729 |
931 // Fill in the size of the structure before using it | 730 // Fill in the size of the structure before using it |
932 te32.dwSize = sizeof(THREADENTRY32); | 731 te32.dwSize = sizeof(THREADENTRY32); |
933 | 732 |
934 if (! Thread32First(hThreadSnap, &te32)) { | 733 if (! Thread32First(hThreadSnap, &te32)) { |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1008 return NULL; | 807 return NULL; |
1009 } | 808 } |
1010 Py_INCREF(Py_None); | 809 Py_INCREF(Py_None); |
1011 return Py_None; | 810 return Py_None; |
1012 } | 811 } |
1013 | 812 |
1014 | 813 |
1015 static PyObject* | 814 static PyObject* |
1016 get_process_num_threads(PyObject* self, PyObject* args) | 815 get_process_num_threads(PyObject* self, PyObject* args) |
1017 { | 816 { |
| 817 DWORD pid; |
| 818 PSYSTEM_PROCESS_INFORMATION process; |
| 819 PVOID buffer; |
| 820 int num; |
| 821 |
| 822 if (! PyArg_ParseTuple(args, "l", &pid)) { |
| 823 return NULL; |
| 824 } |
| 825 if (get_process_info(pid, &process, &buffer) != 1) { |
| 826 free(buffer); |
| 827 return NULL; |
| 828 } |
| 829 if (pid_is_running(pid) == 0) { |
| 830 free(buffer); |
| 831 return NoSuchProcess(); |
| 832 } |
| 833 |
| 834 num = (int)process->NumberOfThreads; |
| 835 free(buffer); |
| 836 return Py_BuildValue("i", num); |
| 837 } |
| 838 |
| 839 |
| 840 static PyObject* |
| 841 get_process_threads(PyObject* self, PyObject* args) |
| 842 { |
| 843 PyObject* retList = PyList_New(0); |
| 844 PyObject* pyTuple = NULL; |
| 845 HANDLE hThreadSnap = NULL; |
| 846 THREADENTRY32 te32 = {0}; |
1018 long pid; | 847 long pid; |
1019 int pid_return; | 848 int pid_return; |
1020 long nthreads = 0; | 849 int rc; |
1021 HANDLE hThreadSnap = NULL; | 850 FILETIME ftDummy, ftKernel, ftUser; |
1022 THREADENTRY32 te32 = {0}; | |
1023 | 851 |
1024 if (! PyArg_ParseTuple(args, "l", &pid)) | 852 if (! PyArg_ParseTuple(args, "l", &pid)) { |
1025 return NULL; | 853 return NULL; |
| 854 } |
1026 if (pid == 0) { | 855 if (pid == 0) { |
1027 // raise AD instead of returning 0 as procexp is able to | 856 // raise AD instead of returning 0 as procexp is able to |
1028 // retrieve useful information somehow | 857 // retrieve useful information somehow |
1029 return AccessDenied(); | 858 return AccessDenied(); |
1030 } | 859 } |
1031 | 860 |
1032 pid_return = pid_is_running(pid); | 861 pid_return = pid_is_running(pid); |
1033 if (pid_return == 0) { | 862 if (pid_return == 0) { |
1034 return NoSuchProcess(); | 863 return NoSuchProcess(); |
1035 } | 864 } |
(...skipping 18 matching lines...) Expand all Loading... |
1054 | 883 |
1055 // Walk the thread snapshot to find all threads of the process. | 884 // Walk the thread snapshot to find all threads of the process. |
1056 // If the thread belongs to the process, increase the counter. | 885 // If the thread belongs to the process, increase the counter. |
1057 do | 886 do |
1058 { | 887 { |
1059 if (te32.th32OwnerProcessID == pid) | 888 if (te32.th32OwnerProcessID == pid) |
1060 { | 889 { |
1061 HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, | 890 HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, |
1062 FALSE, te32.th32ThreadID); | 891 FALSE, te32.th32ThreadID); |
1063 if (hThread == NULL) { | 892 if (hThread == NULL) { |
1064 if (GetLastError() == ERROR_INVALID_PARAMETER) { | 893 // thread has disappeared on us |
1065 NoSuchProcess(); | 894 continue; |
1066 } | 895 } |
1067 else { | 896 |
1068 PyErr_SetFromWindowsErr(0); | 897 rc = GetThreadTimes(hThread, &ftDummy, &ftDummy, &ftKernel, &ftUser)
; |
1069 } | 898 if (rc == 0) { |
| 899 PyErr_SetFromWindowsErr(0); |
1070 CloseHandle(hThread); | 900 CloseHandle(hThread); |
1071 CloseHandle(hThreadSnap); | 901 CloseHandle(hThreadSnap); |
1072 return NULL; | 902 return NULL; |
1073 } | 903 } |
1074 nthreads += 1; | 904 |
| 905 /* |
| 906 user and kernel times are represented as a FILETIME structure |
| 907 wich contains a 64-bit value representing the number of |
| 908 100-nanosecond intervals since January 1, 1601 (UTC). |
| 909 http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx |
| 910 |
| 911 To convert it into a float representing the seconds that the |
| 912 process has executed in user/kernel mode I borrowed the code |
| 913 below from Python's Modules/posixmodule.c |
| 914 */ |
| 915 pyTuple = Py_BuildValue("kdd", |
| 916 te32.th32ThreadID, |
| 917 (double)(ftUser.dwHighDateTime*429.4967296 + \ |
| 918 ftUser.dwLowDateTime*1e-7), |
| 919 (double)(ftKernel.dwHighDateTime*429.4967296 + \ |
| 920 ftKernel.dwLowDateTime*1e-7) |
| 921 ); |
| 922 PyList_Append(retList, pyTuple); |
| 923 Py_XDECREF(pyTuple); |
| 924 |
1075 CloseHandle(hThread); | 925 CloseHandle(hThread); |
1076 } | 926 } |
1077 } while (Thread32Next(hThreadSnap, &te32)); | 927 } while (Thread32Next(hThreadSnap, &te32)); |
1078 | 928 |
1079 // every process should be supposed to have at least one thread | 929 CloseHandle(hThreadSnap); |
1080 if (nthreads == 0) { | 930 return retList; |
1081 return NoSuchProcess(); | 931 } |
1082 } | |
1083 | 932 |
1084 return Py_BuildValue("l", nthreads); | |
1085 } | |
1086 | 933 |
1087 | 934 |
1088 static PyObject* | 935 static PyObject* |
1089 get_process_open_files(PyObject* self, PyObject* args) | 936 get_process_open_files(PyObject* self, PyObject* args) |
1090 { | 937 { |
1091 long pid; | 938 long pid; |
1092 HANDLE processHandle; | 939 HANDLE processHandle; |
1093 DWORD access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; | 940 DWORD access = PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION; |
1094 PyObject* filesList; | 941 PyObject* filesList; |
1095 | 942 |
1096 if (! PyArg_ParseTuple(args, "l", &pid)) { | 943 if (! PyArg_ParseTuple(args, "l", &pid)) { |
1097 return NULL; | 944 return NULL; |
1098 } | 945 } |
1099 | 946 |
1100 processHandle = handle_from_pid_waccess(pid, access); | 947 processHandle = handle_from_pid_waccess(pid, access); |
1101 if (processHandle == NULL) { | 948 if (processHandle == NULL) { |
1102 return NULL; | 949 return NULL; |
1103 } | 950 } |
1104 | 951 |
1105 filesList = get_open_files(pid, processHandle); | 952 filesList = get_open_files(pid, processHandle); |
| 953 CloseHandle(processHandle); |
1106 if (filesList == NULL) { | 954 if (filesList == NULL) { |
1107 return PyErr_SetFromWindowsErr(0); | 955 return PyErr_SetFromWindowsErr(0); |
1108 } | 956 } |
1109 return filesList; | 957 return filesList; |
1110 } | 958 } |
1111 | 959 |
1112 | 960 |
1113 /* | 961 /* |
1114 Accept a filename's drive in native format like "\Device\HarddiskVolume1\" | 962 Accept a filename's drive in native format like "\Device\HarddiskVolume1\" |
1115 and return the corresponding drive letter (e.g. "C:\\"). | 963 and return the corresponding drive letter (e.g. "C:\\"). |
1116 If no match is found return an empty string. | 964 If no match is found return an empty string. |
1117 */ | 965 */ |
1118 static PyObject* | 966 static PyObject* |
1119 _QueryDosDevice(PyObject* self, PyObject* args) | 967 win32_QueryDosDevice(PyObject* self, PyObject* args) |
1120 { | 968 { |
1121 LPCTSTR lpDevicePath; | 969 LPCTSTR lpDevicePath; |
1122 TCHAR d = TEXT('A'); | 970 TCHAR d = TEXT('A'); |
1123 TCHAR szBuff[5]; | 971 TCHAR szBuff[5]; |
1124 | 972 |
1125 if (!PyArg_ParseTuple(args, "s", &lpDevicePath)) { | 973 if (!PyArg_ParseTuple(args, "s", &lpDevicePath)) { |
1126 return NULL; | 974 return NULL; |
1127 } | 975 } |
1128 | 976 |
1129 while(d <= TEXT('Z')) | 977 while(d <= TEXT('Z')) |
(...skipping 304 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1434 | 1282 |
1435 table = malloc(tableSize); | 1283 table = malloc(tableSize); |
1436 | 1284 |
1437 if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET, | 1285 if (getExtendedTcpTable(table, &tableSize, FALSE, AF_INET, |
1438 TCP_TABLE_OWNER_PID_ALL, 0) == 0) | 1286 TCP_TABLE_OWNER_PID_ALL, 0) == 0) |
1439 { | 1287 { |
1440 tcp4Table = table; | 1288 tcp4Table = table; |
1441 | 1289 |
1442 for (i = 0; i < tcp4Table->dwNumEntries; i++) | 1290 for (i = 0; i < tcp4Table->dwNumEntries; i++) |
1443 { | 1291 { |
1444 if (tcp4Table->table[i].dwOwningPid != pid) | 1292 if (tcp4Table->table[i].dwOwningPid != pid) { |
1445 continue; | 1293 continue; |
| 1294 } |
1446 | 1295 |
1447 if (tcp4Table->table[i].dwLocalAddr != 0 || | 1296 if (tcp4Table->table[i].dwLocalAddr != 0 || |
1448 tcp4Table->table[i].dwLocalPort != 0) | 1297 tcp4Table->table[i].dwLocalPort != 0) |
1449 { | 1298 { |
1450 struct in_addr addr; | 1299 struct in_addr addr; |
1451 | 1300 |
1452 addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr; | 1301 addr.S_un.S_addr = tcp4Table->table[i].dwLocalAddr; |
1453 rtlIpv4AddressToStringA(&addr, addressBufferLocal); | 1302 rtlIpv4AddressToStringA(&addr, addressBufferLocal); |
1454 addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, | 1303 addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, |
1455 BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort)); | 1304 BYTESWAP_USHORT(tcp4Table->table[i].dwLocalPort)); |
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1565 | 1414 |
1566 table = malloc(tableSize); | 1415 table = malloc(tableSize); |
1567 | 1416 |
1568 if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET, | 1417 if (getExtendedUdpTable(table, &tableSize, FALSE, AF_INET, |
1569 UDP_TABLE_OWNER_PID, 0) == 0) | 1418 UDP_TABLE_OWNER_PID, 0) == 0) |
1570 { | 1419 { |
1571 udp4Table = table; | 1420 udp4Table = table; |
1572 | 1421 |
1573 for (i = 0; i < udp4Table->dwNumEntries; i++) | 1422 for (i = 0; i < udp4Table->dwNumEntries; i++) |
1574 { | 1423 { |
1575 if (udp4Table->table[i].dwOwningPid != pid) | 1424 if (udp4Table->table[i].dwOwningPid != pid) { |
1576 continue; | 1425 continue; |
| 1426 } |
1577 | 1427 |
1578 if (udp4Table->table[i].dwLocalAddr != 0 || | 1428 if (udp4Table->table[i].dwLocalAddr != 0 || |
1579 udp4Table->table[i].dwLocalPort != 0) | 1429 udp4Table->table[i].dwLocalPort != 0) |
1580 { | 1430 { |
1581 struct in_addr addr; | 1431 struct in_addr addr; |
1582 | 1432 |
1583 addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr; | 1433 addr.S_un.S_addr = udp4Table->table[i].dwLocalAddr; |
1584 rtlIpv4AddressToStringA(&addr, addressBufferLocal); | 1434 rtlIpv4AddressToStringA(&addr, addressBufferLocal); |
1585 addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, | 1435 addressTupleLocal = Py_BuildValue("(si)", addressBufferLocal, |
1586 BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort)); | 1436 BYTESWAP_USHORT(udp4Table->table[i].dwLocalPort)); |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1646 ); | 1496 ); |
1647 PyList_Append(connectionsList, connectionTuple); | 1497 PyList_Append(connectionsList, connectionTuple); |
1648 } | 1498 } |
1649 } | 1499 } |
1650 | 1500 |
1651 free(table); | 1501 free(table); |
1652 | 1502 |
1653 return connectionsList; | 1503 return connectionsList; |
1654 } | 1504 } |
1655 | 1505 |
| 1506 |
| 1507 /* |
| 1508 * Get process priority as a Python integer. |
| 1509 */ |
| 1510 static PyObject* |
| 1511 get_process_priority(PyObject* self, PyObject* args) |
| 1512 { |
| 1513 long pid; |
| 1514 DWORD priority; |
| 1515 HANDLE hProcess; |
| 1516 if (! PyArg_ParseTuple(args, "l", &pid)) { |
| 1517 return NULL; |
| 1518 } |
| 1519 |
| 1520 hProcess = handle_from_pid(pid); |
| 1521 if (hProcess == NULL) { |
| 1522 return NULL; |
| 1523 } |
| 1524 |
| 1525 priority = GetPriorityClass(hProcess); |
| 1526 CloseHandle(hProcess); |
| 1527 if (priority == 0) { |
| 1528 PyErr_SetFromWindowsErr(0); |
| 1529 return NULL; |
| 1530 } |
| 1531 return Py_BuildValue("i", priority); |
| 1532 } |
| 1533 |
| 1534 |
| 1535 /* |
| 1536 * Set process priority. |
| 1537 */ |
| 1538 static PyObject* |
| 1539 set_process_priority(PyObject* self, PyObject* args) |
| 1540 { |
| 1541 long pid; |
| 1542 int priority; |
| 1543 int retval; |
| 1544 HANDLE hProcess; |
| 1545 DWORD dwDesiredAccess = PROCESS_QUERY_INFORMATION | PROCESS_SET_INFORMATION; |
| 1546 if (! PyArg_ParseTuple(args, "li", &pid, &priority)) { |
| 1547 return NULL; |
| 1548 } |
| 1549 |
| 1550 hProcess = handle_from_pid_waccess(pid, dwDesiredAccess); |
| 1551 if (hProcess == NULL) { |
| 1552 return NULL; |
| 1553 } |
| 1554 |
| 1555 retval = SetPriorityClass(hProcess, priority); |
| 1556 CloseHandle(hProcess); |
| 1557 if (retval == 0) { |
| 1558 PyErr_SetFromWindowsErr(0); |
| 1559 return NULL; |
| 1560 } |
| 1561 Py_INCREF(Py_None); |
| 1562 return Py_None; |
| 1563 } |
| 1564 |
| 1565 |
| 1566 /* |
| 1567 * Return a Python tuple referencing process I/O counters. |
| 1568 */ |
| 1569 static PyObject* |
| 1570 get_process_io_counters(PyObject* self, PyObject* args) |
| 1571 { |
| 1572 DWORD pid; |
| 1573 HANDLE hProcess; |
| 1574 IO_COUNTERS IoCounters; |
| 1575 |
| 1576 if (! PyArg_ParseTuple(args, "l", &pid)) { |
| 1577 return NULL; |
| 1578 } |
| 1579 if (pid == 0) { |
| 1580 return AccessDenied(); |
| 1581 } |
| 1582 hProcess = handle_from_pid(pid); |
| 1583 if (NULL == hProcess) { |
| 1584 return NULL; |
| 1585 } |
| 1586 if (! GetProcessIoCounters(hProcess, &IoCounters)) { |
| 1587 CloseHandle(hProcess); |
| 1588 return PyErr_SetFromWindowsErr(0); |
| 1589 } |
| 1590 CloseHandle(hProcess); |
| 1591 return Py_BuildValue("(KKKK)", IoCounters.ReadOperationCount, |
| 1592 IoCounters.WriteOperationCount, |
| 1593 IoCounters.ReadTransferCount, |
| 1594 IoCounters.WriteTransferCount); |
| 1595 } |
| 1596 |
| 1597 |
| 1598 /* |
| 1599 * Return True if one of the process threads is in a waiting or |
| 1600 * suspended status. |
| 1601 */ |
| 1602 static PyObject* |
| 1603 is_process_suspended(PyObject* self, PyObject* args) |
| 1604 { |
| 1605 DWORD pid; |
| 1606 ULONG i; |
| 1607 PSYSTEM_PROCESS_INFORMATION process; |
| 1608 PVOID buffer; |
| 1609 |
| 1610 if (! PyArg_ParseTuple(args, "l", &pid)) { |
| 1611 return NULL; |
| 1612 } |
| 1613 if (get_process_info(pid, &process, &buffer) != 1) { |
| 1614 free(buffer); |
| 1615 return NULL; |
| 1616 } |
| 1617 if (pid_is_running(pid) == 0) { |
| 1618 free(buffer); |
| 1619 return NoSuchProcess(); |
| 1620 } |
| 1621 |
| 1622 for (i = 0; i < process->NumberOfThreads; i++) { |
| 1623 if (process->Threads[i].ThreadState != Waiting || |
| 1624 process->Threads[i].WaitReason != Suspended) |
| 1625 { |
| 1626 free(buffer); |
| 1627 Py_RETURN_FALSE; |
| 1628 } |
| 1629 } |
| 1630 free(buffer); |
| 1631 Py_RETURN_TRUE; |
| 1632 } |
| 1633 |
| 1634 |
| 1635 /* |
| 1636 * Return path's disk total and free as a Python tuple. |
| 1637 */ |
| 1638 static PyObject* |
| 1639 get_disk_usage(PyObject* self, PyObject* args) |
| 1640 { |
| 1641 BOOL retval; |
| 1642 ULARGE_INTEGER _, total, free; |
| 1643 LPCTSTR path; |
| 1644 |
| 1645 if (! PyArg_ParseTuple(args, "s", &path)) { |
| 1646 return NULL; |
| 1647 } |
| 1648 |
| 1649 Py_BEGIN_ALLOW_THREADS |
| 1650 retval = GetDiskFreeSpaceEx(path, &_, &total, &free); |
| 1651 Py_END_ALLOW_THREADS |
| 1652 if (retval == 0) { |
| 1653 return PyErr_SetFromWindowsErr(0); |
| 1654 } |
| 1655 |
| 1656 return Py_BuildValue("(LL)", total.QuadPart, free.QuadPart); |
| 1657 } |
| 1658 |
| 1659 |
| 1660 /* |
| 1661 * Return disk partitions as a list of namedtuples. |
| 1662 */ |
| 1663 static PyObject* |
| 1664 win32_GetLogicalDriveStrings(PyObject* self, PyObject* args) |
| 1665 { |
| 1666 DWORD num_bytes; |
| 1667 char drive_strings[255]; |
| 1668 char* drive_letter = drive_strings; |
| 1669 PyObject* py_retlist = PyList_New(0); |
| 1670 |
| 1671 Py_BEGIN_ALLOW_THREADS |
| 1672 num_bytes = GetLogicalDriveStrings(254, drive_letter); |
| 1673 Py_END_ALLOW_THREADS |
| 1674 |
| 1675 if (num_bytes == 0) { |
| 1676 return PyErr_SetFromWindowsErr(0); |
| 1677 } |
| 1678 |
| 1679 while (*drive_letter != 0) { |
| 1680 PyList_Append(py_retlist, Py_BuildValue("s", drive_letter)); |
| 1681 drive_letter = strchr(drive_letter, 0) +1; |
| 1682 } |
| 1683 |
| 1684 return py_retlist; |
| 1685 } |
| 1686 |
| 1687 |
| 1688 static PyObject* |
| 1689 win32_GetDriveType(PyObject* self, PyObject* args) |
| 1690 { |
| 1691 LPCTSTR drive_letter; |
| 1692 int type; |
| 1693 char* type_str; |
| 1694 |
| 1695 if (! PyArg_ParseTuple(args, "s", &drive_letter)) { |
| 1696 return NULL; |
| 1697 } |
| 1698 |
| 1699 Py_BEGIN_ALLOW_THREADS |
| 1700 type = GetDriveType(drive_letter); |
| 1701 Py_END_ALLOW_THREADS |
| 1702 |
| 1703 switch (type) { |
| 1704 case DRIVE_UNKNOWN: |
| 1705 type_str = "unknown"; |
| 1706 break; |
| 1707 case DRIVE_NO_ROOT_DIR: |
| 1708 type_str = "unmounted"; |
| 1709 case DRIVE_REMOVABLE: |
| 1710 type_str = "removable"; |
| 1711 break; |
| 1712 case DRIVE_FIXED: |
| 1713 type_str = "fixed"; |
| 1714 break; |
| 1715 case DRIVE_REMOTE: |
| 1716 type_str = "remote"; |
| 1717 break; |
| 1718 case DRIVE_CDROM: |
| 1719 type_str = "cdrom"; |
| 1720 break; |
| 1721 case DRIVE_RAMDISK: |
| 1722 type_str = "ramdisk"; |
| 1723 break; |
| 1724 default: |
| 1725 type_str = "?"; |
| 1726 break; |
| 1727 } |
| 1728 |
| 1729 return Py_BuildValue("s", type_str); |
| 1730 } |
| 1731 |
| 1732 |
| 1733 // ------------------------ Python init --------------------------- |
| 1734 |
| 1735 static PyMethodDef |
| 1736 PsutilMethods[] = |
| 1737 { |
| 1738 // --- per-process functions |
| 1739 |
| 1740 {"get_process_name", get_process_name, METH_VARARGS, |
| 1741 "Return process name"}, |
| 1742 {"get_process_cmdline", get_process_cmdline, METH_VARARGS, |
| 1743 "Return process cmdline as a list of cmdline arguments"}, |
| 1744 {"get_process_ppid", get_process_ppid, METH_VARARGS, |
| 1745 "Return process ppid as an integer"}, |
| 1746 {"kill_process", kill_process, METH_VARARGS, |
| 1747 "Kill the process identified by the given PID"}, |
| 1748 {"get_process_cpu_times", get_process_cpu_times, METH_VARARGS, |
| 1749 "Return tuple of user/kern time for the given PID"}, |
| 1750 {"get_process_create_time", get_process_create_time, METH_VARARGS, |
| 1751 "Return a float indicating the process create time expressed in " |
| 1752 "seconds since the epoch"}, |
| 1753 {"get_memory_info", get_memory_info, METH_VARARGS, |
| 1754 "Return a tuple of RSS/VMS memory information"}, |
| 1755 {"get_process_cwd", get_process_cwd, METH_VARARGS, |
| 1756 "Return process current working directory"}, |
| 1757 {"suspend_process", suspend_process, METH_VARARGS, |
| 1758 "Suspend a process"}, |
| 1759 {"resume_process", resume_process, METH_VARARGS, |
| 1760 "Resume a process"}, |
| 1761 {"get_process_open_files", get_process_open_files, METH_VARARGS, |
| 1762 "Return files opened by process"}, |
| 1763 {"get_process_username", get_process_username, METH_VARARGS, |
| 1764 "Return the username of a process"}, |
| 1765 {"get_process_connections", get_process_connections, METH_VARARGS, |
| 1766 "Return the network connections of a process"}, |
| 1767 {"get_process_num_threads", get_process_num_threads, METH_VARARGS, |
| 1768 "Return the network connections of a process"}, |
| 1769 {"get_process_threads", get_process_threads, METH_VARARGS, |
| 1770 "Return process threads information as a list of tuple"}, |
| 1771 {"process_wait", process_wait, METH_VARARGS, |
| 1772 "Wait for process to terminate and return its exit code."}, |
| 1773 {"get_process_priority", get_process_priority, METH_VARARGS, |
| 1774 "Return process priority."}, |
| 1775 {"set_process_priority", set_process_priority, METH_VARARGS, |
| 1776 "Set process priority."}, |
| 1777 {"get_process_io_counters", get_process_io_counters, METH_VARARGS, |
| 1778 "Get process I/O counters."}, |
| 1779 {"is_process_suspended", is_process_suspended, METH_VARARGS, |
| 1780 "Return True if one of the process threads is in a suspended state"}, |
| 1781 |
| 1782 // --- system-related functions |
| 1783 |
| 1784 {"get_pid_list", get_pid_list, METH_VARARGS, |
| 1785 "Returns a list of PIDs currently running on the system"}, |
| 1786 {"pid_exists", pid_exists, METH_VARARGS, |
| 1787 "Determine if the process exists in the current process list."}, |
| 1788 {"get_num_cpus", get_num_cpus, METH_VARARGS, |
| 1789 "Returns the number of CPUs on the system"}, |
| 1790 {"get_system_uptime", get_system_uptime, METH_VARARGS, |
| 1791 "Return system uptime"}, |
| 1792 {"get_system_phymem", get_system_phymem, METH_VARARGS, |
| 1793 "Return the total amount of physical memory, in bytes"}, |
| 1794 {"get_system_cpu_times", get_system_cpu_times, METH_VARARGS, |
| 1795 "Return system per-cpu times as a list of tuples"}, |
| 1796 {"get_disk_usage", get_disk_usage, METH_VARARGS, |
| 1797 "Return path's disk total and free as a Python tuple."}, |
| 1798 |
| 1799 // --- windows API bindings |
| 1800 {"win32_GetLogicalDriveStrings", win32_GetLogicalDriveStrings, METH_VARARGS
, |
| 1801 "GetLogicalDriveStrings binding"}, |
| 1802 {"win32_GetDriveType", win32_GetDriveType, METH_VARARGS, |
| 1803 "GetDriveType binding"}, |
| 1804 {"win32_QueryDosDevice", win32_QueryDosDevice, METH_VARARGS, |
| 1805 "QueryDosDevice binding"}, |
| 1806 |
| 1807 {NULL, NULL, 0, NULL} |
| 1808 }; |
| 1809 |
| 1810 |
| 1811 struct module_state { |
| 1812 PyObject *error; |
| 1813 }; |
| 1814 |
| 1815 #if PY_MAJOR_VERSION >= 3 |
| 1816 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m)) |
| 1817 #else |
| 1818 #define GETSTATE(m) (&_state) |
| 1819 static struct module_state _state; |
| 1820 #endif |
| 1821 |
| 1822 #if PY_MAJOR_VERSION >= 3 |
| 1823 |
| 1824 static int psutil_mswindows_traverse(PyObject *m, visitproc visit, void *arg
) { |
| 1825 Py_VISIT(GETSTATE(m)->error); |
| 1826 return 0; |
| 1827 } |
| 1828 |
| 1829 static int psutil_mswindows_clear(PyObject *m) { |
| 1830 Py_CLEAR(GETSTATE(m)->error); |
| 1831 return 0; |
| 1832 } |
| 1833 |
| 1834 static struct PyModuleDef moduledef = { |
| 1835 PyModuleDef_HEAD_INIT, |
| 1836 "psutil_mswindows", |
| 1837 NULL, |
| 1838 sizeof(struct module_state), |
| 1839 PsutilMethods, |
| 1840 NULL, |
| 1841 psutil_mswindows_traverse, |
| 1842 psutil_mswindows_clear, |
| 1843 NULL |
| 1844 }; |
| 1845 |
| 1846 #define INITERROR return NULL |
| 1847 |
| 1848 PyObject* PyInit__psutil_mswindows(void) |
| 1849 |
| 1850 #else |
| 1851 #define INITERROR return |
| 1852 void init_psutil_mswindows(void) |
| 1853 #endif |
| 1854 { |
| 1855 struct module_state *st = NULL; |
| 1856 #if PY_MAJOR_VERSION >= 3 |
| 1857 PyObject *module = PyModule_Create(&moduledef); |
| 1858 #else |
| 1859 PyObject *module = Py_InitModule("_psutil_mswindows", PsutilMethods); |
| 1860 #endif |
| 1861 |
| 1862 if (module == NULL) { |
| 1863 INITERROR; |
| 1864 } |
| 1865 |
| 1866 st = GETSTATE(module); |
| 1867 st->error = PyErr_NewException("_psutil_mswindow.Error", NULL, NULL); |
| 1868 if (st->error == NULL) { |
| 1869 Py_DECREF(module); |
| 1870 INITERROR; |
| 1871 } |
| 1872 |
| 1873 // Public constants |
| 1874 // http://msdn.microsoft.com/en-us/library/ms683211(v=vs.85).aspx |
| 1875 PyModule_AddIntConstant(module, "ABOVE_NORMAL_PRIORITY_CLASS", |
| 1876 ABOVE_NORMAL_PRIORITY_CLASS); |
| 1877 PyModule_AddIntConstant(module, "BELOW_NORMAL_PRIORITY_CLASS", |
| 1878 BELOW_NORMAL_PRIORITY_CLASS); |
| 1879 PyModule_AddIntConstant(module, "HIGH_PRIORITY_CLASS", |
| 1880 HIGH_PRIORITY_CLASS); |
| 1881 PyModule_AddIntConstant(module, "IDLE_PRIORITY_CLASS", |
| 1882 IDLE_PRIORITY_CLASS); |
| 1883 PyModule_AddIntConstant(module, "NORMAL_PRIORITY_CLASS", |
| 1884 NORMAL_PRIORITY_CLASS); |
| 1885 PyModule_AddIntConstant(module, "REALTIME_PRIORITY_CLASS", |
| 1886 REALTIME_PRIORITY_CLASS); |
| 1887 // private constants |
| 1888 PyModule_AddIntConstant(module, "INFINITE", INFINITE); |
| 1889 SetSeDebug(); |
| 1890 |
| 1891 #if PY_MAJOR_VERSION >= 3 |
| 1892 return module; |
| 1893 #endif |
| 1894 } |
| 1895 |
| 1896 |
| 1897 |
OLD | NEW |