|
1 # This Source Code Form is subject to the terms of the Mozilla Public |
|
2 # License, v. 2.0. If a copy of the MPL was not distributed with this |
|
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
|
4 |
|
5 from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE, addressof, c_size_t, c_ulong |
|
6 from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LARGE_INTEGER |
|
7 |
|
8 LPVOID = c_void_p |
|
9 LPDWORD = POINTER(DWORD) |
|
10 SIZE_T = c_size_t |
|
11 ULONG_PTR = POINTER(c_ulong) |
|
12 |
|
13 # A ULONGLONG is a 64-bit unsigned integer. |
|
14 # Thus there are 8 bytes in a ULONGLONG. |
|
15 # XXX why not import c_ulonglong ? |
|
16 ULONGLONG = BYTE * 8 |
|
17 |
|
18 class IO_COUNTERS(Structure): |
|
19 # The IO_COUNTERS struct is 6 ULONGLONGs. |
|
20 # TODO: Replace with non-dummy fields. |
|
21 _fields_ = [('dummy', ULONGLONG * 6)] |
|
22 |
|
23 class JOBOBJECT_BASIC_ACCOUNTING_INFORMATION(Structure): |
|
24 _fields_ = [('TotalUserTime', LARGE_INTEGER), |
|
25 ('TotalKernelTime', LARGE_INTEGER), |
|
26 ('ThisPeriodTotalUserTime', LARGE_INTEGER), |
|
27 ('ThisPeriodTotalKernelTime', LARGE_INTEGER), |
|
28 ('TotalPageFaultCount', DWORD), |
|
29 ('TotalProcesses', DWORD), |
|
30 ('ActiveProcesses', DWORD), |
|
31 ('TotalTerminatedProcesses', DWORD)] |
|
32 |
|
33 class JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION(Structure): |
|
34 _fields_ = [('BasicInfo', JOBOBJECT_BASIC_ACCOUNTING_INFORMATION), |
|
35 ('IoInfo', IO_COUNTERS)] |
|
36 |
|
37 # see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx |
|
38 class JOBOBJECT_BASIC_LIMIT_INFORMATION(Structure): |
|
39 _fields_ = [('PerProcessUserTimeLimit', LARGE_INTEGER), |
|
40 ('PerJobUserTimeLimit', LARGE_INTEGER), |
|
41 ('LimitFlags', DWORD), |
|
42 ('MinimumWorkingSetSize', SIZE_T), |
|
43 ('MaximumWorkingSetSize', SIZE_T), |
|
44 ('ActiveProcessLimit', DWORD), |
|
45 ('Affinity', ULONG_PTR), |
|
46 ('PriorityClass', DWORD), |
|
47 ('SchedulingClass', DWORD) |
|
48 ] |
|
49 |
|
50 # see http://msdn.microsoft.com/en-us/library/ms684156%28VS.85%29.aspx |
|
51 class JOBOBJECT_EXTENDED_LIMIT_INFORMATION(Structure): |
|
52 _fields_ = [('BasicLimitInformation', JOBOBJECT_BASIC_LIMIT_INFORMATION), |
|
53 ('IoInfo', IO_COUNTERS), |
|
54 ('ProcessMemoryLimit', SIZE_T), |
|
55 ('JobMemoryLimit', SIZE_T), |
|
56 ('PeakProcessMemoryUsed', SIZE_T), |
|
57 ('PeakJobMemoryUsed', SIZE_T)] |
|
58 |
|
59 # XXX Magical numbers like 8 should be documented |
|
60 JobObjectBasicAndIoAccountingInformation = 8 |
|
61 |
|
62 # ...like magical number 9 comes from |
|
63 # http://community.flexerasoftware.com/archive/index.php?t-181670.html |
|
64 # I wish I had a more canonical source |
|
65 JobObjectExtendedLimitInformation = 9 |
|
66 |
|
67 class JobObjectInfo(object): |
|
68 mapping = { 'JobObjectBasicAndIoAccountingInformation': 8, |
|
69 'JobObjectExtendedLimitInformation': 9 |
|
70 } |
|
71 structures = { 8: JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION, |
|
72 9: JOBOBJECT_EXTENDED_LIMIT_INFORMATION |
|
73 } |
|
74 def __init__(self, _class): |
|
75 if isinstance(_class, basestring): |
|
76 assert _class in self.mapping, 'Class should be one of %s; you gave %s' % (self.mapping, _class) |
|
77 _class = self.mapping[_class] |
|
78 assert _class in self.structures, 'Class should be one of %s; you gave %s' % (self.structures, _class) |
|
79 self.code = _class |
|
80 self.info = self.structures[_class]() |
|
81 |
|
82 |
|
83 QueryInformationJobObjectProto = WINFUNCTYPE( |
|
84 BOOL, # Return type |
|
85 HANDLE, # hJob |
|
86 DWORD, # JobObjectInfoClass |
|
87 LPVOID, # lpJobObjectInfo |
|
88 DWORD, # cbJobObjectInfoLength |
|
89 LPDWORD # lpReturnLength |
|
90 ) |
|
91 |
|
92 QueryInformationJobObjectFlags = ( |
|
93 (1, 'hJob'), |
|
94 (1, 'JobObjectInfoClass'), |
|
95 (1, 'lpJobObjectInfo'), |
|
96 (1, 'cbJobObjectInfoLength'), |
|
97 (1, 'lpReturnLength', None) |
|
98 ) |
|
99 |
|
100 _QueryInformationJobObject = QueryInformationJobObjectProto( |
|
101 ('QueryInformationJobObject', windll.kernel32), |
|
102 QueryInformationJobObjectFlags |
|
103 ) |
|
104 |
|
105 class SubscriptableReadOnlyStruct(object): |
|
106 def __init__(self, struct): |
|
107 self._struct = struct |
|
108 |
|
109 def _delegate(self, name): |
|
110 result = getattr(self._struct, name) |
|
111 if isinstance(result, Structure): |
|
112 return SubscriptableReadOnlyStruct(result) |
|
113 return result |
|
114 |
|
115 def __getitem__(self, name): |
|
116 match = [fname for fname, ftype in self._struct._fields_ |
|
117 if fname == name] |
|
118 if match: |
|
119 return self._delegate(name) |
|
120 raise KeyError(name) |
|
121 |
|
122 def __getattr__(self, name): |
|
123 return self._delegate(name) |
|
124 |
|
125 def QueryInformationJobObject(hJob, JobObjectInfoClass): |
|
126 jobinfo = JobObjectInfo(JobObjectInfoClass) |
|
127 result = _QueryInformationJobObject( |
|
128 hJob=hJob, |
|
129 JobObjectInfoClass=jobinfo.code, |
|
130 lpJobObjectInfo=addressof(jobinfo.info), |
|
131 cbJobObjectInfoLength=sizeof(jobinfo.info) |
|
132 ) |
|
133 if not result: |
|
134 raise WinError() |
|
135 return SubscriptableReadOnlyStruct(jobinfo.info) |
|
136 |
|
137 def test_qijo(): |
|
138 from killableprocess import Popen |
|
139 |
|
140 popen = Popen('c:\\windows\\notepad.exe') |
|
141 |
|
142 try: |
|
143 result = QueryInformationJobObject(0, 8) |
|
144 raise AssertionError('throw should occur') |
|
145 except WindowsError, e: |
|
146 pass |
|
147 |
|
148 try: |
|
149 result = QueryInformationJobObject(0, 1) |
|
150 raise AssertionError('throw should occur') |
|
151 except NotImplementedError, e: |
|
152 pass |
|
153 |
|
154 result = QueryInformationJobObject(popen._job, 8) |
|
155 if result['BasicInfo']['ActiveProcesses'] != 1: |
|
156 raise AssertionError('expected ActiveProcesses to be 1') |
|
157 popen.kill() |
|
158 |
|
159 result = QueryInformationJobObject(popen._job, 8) |
|
160 if result.BasicInfo.ActiveProcesses != 0: |
|
161 raise AssertionError('expected ActiveProcesses to be 0') |
|
162 |
|
163 if __name__ == '__main__': |
|
164 print "testing." |
|
165 test_qijo() |
|
166 print "success!" |