1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/addon-sdk/source/python-lib/mozrunner/qijo.py Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,166 @@ 1.4 +# This Source Code Form is subject to the terms of the Mozilla Public 1.5 +# License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. 1.7 + 1.8 +from ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE, addressof, c_size_t, c_ulong 1.9 +from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LARGE_INTEGER 1.10 + 1.11 +LPVOID = c_void_p 1.12 +LPDWORD = POINTER(DWORD) 1.13 +SIZE_T = c_size_t 1.14 +ULONG_PTR = POINTER(c_ulong) 1.15 + 1.16 +# A ULONGLONG is a 64-bit unsigned integer. 1.17 +# Thus there are 8 bytes in a ULONGLONG. 1.18 +# XXX why not import c_ulonglong ? 1.19 +ULONGLONG = BYTE * 8 1.20 + 1.21 +class IO_COUNTERS(Structure): 1.22 + # The IO_COUNTERS struct is 6 ULONGLONGs. 1.23 + # TODO: Replace with non-dummy fields. 1.24 + _fields_ = [('dummy', ULONGLONG * 6)] 1.25 + 1.26 +class JOBOBJECT_BASIC_ACCOUNTING_INFORMATION(Structure): 1.27 + _fields_ = [('TotalUserTime', LARGE_INTEGER), 1.28 + ('TotalKernelTime', LARGE_INTEGER), 1.29 + ('ThisPeriodTotalUserTime', LARGE_INTEGER), 1.30 + ('ThisPeriodTotalKernelTime', LARGE_INTEGER), 1.31 + ('TotalPageFaultCount', DWORD), 1.32 + ('TotalProcesses', DWORD), 1.33 + ('ActiveProcesses', DWORD), 1.34 + ('TotalTerminatedProcesses', DWORD)] 1.35 + 1.36 +class JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION(Structure): 1.37 + _fields_ = [('BasicInfo', JOBOBJECT_BASIC_ACCOUNTING_INFORMATION), 1.38 + ('IoInfo', IO_COUNTERS)] 1.39 + 1.40 +# see http://msdn.microsoft.com/en-us/library/ms684147%28VS.85%29.aspx 1.41 +class JOBOBJECT_BASIC_LIMIT_INFORMATION(Structure): 1.42 + _fields_ = [('PerProcessUserTimeLimit', LARGE_INTEGER), 1.43 + ('PerJobUserTimeLimit', LARGE_INTEGER), 1.44 + ('LimitFlags', DWORD), 1.45 + ('MinimumWorkingSetSize', SIZE_T), 1.46 + ('MaximumWorkingSetSize', SIZE_T), 1.47 + ('ActiveProcessLimit', DWORD), 1.48 + ('Affinity', ULONG_PTR), 1.49 + ('PriorityClass', DWORD), 1.50 + ('SchedulingClass', DWORD) 1.51 + ] 1.52 + 1.53 +# see http://msdn.microsoft.com/en-us/library/ms684156%28VS.85%29.aspx 1.54 +class JOBOBJECT_EXTENDED_LIMIT_INFORMATION(Structure): 1.55 + _fields_ = [('BasicLimitInformation', JOBOBJECT_BASIC_LIMIT_INFORMATION), 1.56 + ('IoInfo', IO_COUNTERS), 1.57 + ('ProcessMemoryLimit', SIZE_T), 1.58 + ('JobMemoryLimit', SIZE_T), 1.59 + ('PeakProcessMemoryUsed', SIZE_T), 1.60 + ('PeakJobMemoryUsed', SIZE_T)] 1.61 + 1.62 +# XXX Magical numbers like 8 should be documented 1.63 +JobObjectBasicAndIoAccountingInformation = 8 1.64 + 1.65 +# ...like magical number 9 comes from 1.66 +# http://community.flexerasoftware.com/archive/index.php?t-181670.html 1.67 +# I wish I had a more canonical source 1.68 +JobObjectExtendedLimitInformation = 9 1.69 + 1.70 +class JobObjectInfo(object): 1.71 + mapping = { 'JobObjectBasicAndIoAccountingInformation': 8, 1.72 + 'JobObjectExtendedLimitInformation': 9 1.73 + } 1.74 + structures = { 8: JOBOBJECT_BASIC_AND_IO_ACCOUNTING_INFORMATION, 1.75 + 9: JOBOBJECT_EXTENDED_LIMIT_INFORMATION 1.76 + } 1.77 + def __init__(self, _class): 1.78 + if isinstance(_class, basestring): 1.79 + assert _class in self.mapping, 'Class should be one of %s; you gave %s' % (self.mapping, _class) 1.80 + _class = self.mapping[_class] 1.81 + assert _class in self.structures, 'Class should be one of %s; you gave %s' % (self.structures, _class) 1.82 + self.code = _class 1.83 + self.info = self.structures[_class]() 1.84 + 1.85 + 1.86 +QueryInformationJobObjectProto = WINFUNCTYPE( 1.87 + BOOL, # Return type 1.88 + HANDLE, # hJob 1.89 + DWORD, # JobObjectInfoClass 1.90 + LPVOID, # lpJobObjectInfo 1.91 + DWORD, # cbJobObjectInfoLength 1.92 + LPDWORD # lpReturnLength 1.93 + ) 1.94 + 1.95 +QueryInformationJobObjectFlags = ( 1.96 + (1, 'hJob'), 1.97 + (1, 'JobObjectInfoClass'), 1.98 + (1, 'lpJobObjectInfo'), 1.99 + (1, 'cbJobObjectInfoLength'), 1.100 + (1, 'lpReturnLength', None) 1.101 + ) 1.102 + 1.103 +_QueryInformationJobObject = QueryInformationJobObjectProto( 1.104 + ('QueryInformationJobObject', windll.kernel32), 1.105 + QueryInformationJobObjectFlags 1.106 + ) 1.107 + 1.108 +class SubscriptableReadOnlyStruct(object): 1.109 + def __init__(self, struct): 1.110 + self._struct = struct 1.111 + 1.112 + def _delegate(self, name): 1.113 + result = getattr(self._struct, name) 1.114 + if isinstance(result, Structure): 1.115 + return SubscriptableReadOnlyStruct(result) 1.116 + return result 1.117 + 1.118 + def __getitem__(self, name): 1.119 + match = [fname for fname, ftype in self._struct._fields_ 1.120 + if fname == name] 1.121 + if match: 1.122 + return self._delegate(name) 1.123 + raise KeyError(name) 1.124 + 1.125 + def __getattr__(self, name): 1.126 + return self._delegate(name) 1.127 + 1.128 +def QueryInformationJobObject(hJob, JobObjectInfoClass): 1.129 + jobinfo = JobObjectInfo(JobObjectInfoClass) 1.130 + result = _QueryInformationJobObject( 1.131 + hJob=hJob, 1.132 + JobObjectInfoClass=jobinfo.code, 1.133 + lpJobObjectInfo=addressof(jobinfo.info), 1.134 + cbJobObjectInfoLength=sizeof(jobinfo.info) 1.135 + ) 1.136 + if not result: 1.137 + raise WinError() 1.138 + return SubscriptableReadOnlyStruct(jobinfo.info) 1.139 + 1.140 +def test_qijo(): 1.141 + from killableprocess import Popen 1.142 + 1.143 + popen = Popen('c:\\windows\\notepad.exe') 1.144 + 1.145 + try: 1.146 + result = QueryInformationJobObject(0, 8) 1.147 + raise AssertionError('throw should occur') 1.148 + except WindowsError, e: 1.149 + pass 1.150 + 1.151 + try: 1.152 + result = QueryInformationJobObject(0, 1) 1.153 + raise AssertionError('throw should occur') 1.154 + except NotImplementedError, e: 1.155 + pass 1.156 + 1.157 + result = QueryInformationJobObject(popen._job, 8) 1.158 + if result['BasicInfo']['ActiveProcesses'] != 1: 1.159 + raise AssertionError('expected ActiveProcesses to be 1') 1.160 + popen.kill() 1.161 + 1.162 + result = QueryInformationJobObject(popen._job, 8) 1.163 + if result.BasicInfo.ActiveProcesses != 0: 1.164 + raise AssertionError('expected ActiveProcesses to be 0') 1.165 + 1.166 +if __name__ == '__main__': 1.167 + print "testing." 1.168 + test_qijo() 1.169 + print "success!"