]>
Commit | Line | Data |
---|---|---|
39236c6e A |
1 | from xnu import * |
2 | from utils import * | |
d9a64523 | 3 | from kdp import * |
39236c6e A |
4 | import sys |
5 | ||
6 | ###################################### | |
7 | # Globals | |
8 | ###################################### | |
9 | plane = None | |
10 | ||
3e170ce0 A |
11 | ##################################### |
12 | # Utility functions. | |
13 | ##################################### | |
14 | def CastIOKitClass(obj, target_type): | |
15 | """ Type cast an object to another IOKIT CPP class. | |
16 | params: | |
17 | obj - core.value object representing some C construct in lldb | |
18 | target_type - str : ex 'OSString *' | |
19 | - lldb.SBType : | |
20 | """ | |
21 | v = Cast(obj, target_type) | |
22 | v.GetSBValue().SetPreferDynamicValue(lldb.eNoDynamicValues) | |
23 | return v | |
24 | ||
39236c6e A |
25 | ###################################### |
26 | # Type Summaries | |
27 | ###################################### | |
28 | @lldb_type_summary(['OSObject *']) | |
29 | @header("") | |
30 | def GetObjectSummary(obj): | |
31 | """ Show info about an OSObject - its vtable ptr and retain count, & more info for simple container classes. | |
32 | """ | |
33 | if obj is None: | |
34 | return | |
35 | ||
36 | vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t') | |
37 | vtype = kern.SymbolicateFromAddress(vt) | |
39037602 A |
38 | if len(vtype): |
39 | vtype_str = " <" + vtype[0].GetName() + ">" | |
40 | else: | |
41 | vtype_str = "" | |
39236c6e A |
42 | if hasattr(obj, 'retainCount'): |
43 | retCount = (obj.retainCount & 0xffff) | |
813fb2f6 | 44 | cntnrRetCount = (obj.retainCount >> 16) |
39037602 | 45 | out_string = "`object 0x{0: <16x}, vt 0x{1: <16x}{2:s}, retain count {3:d}, container retain {4:d}` ".format(obj, vt, vtype_str, retCount, cntnrRetCount) |
39236c6e | 46 | else: |
39037602 A |
47 | out_string = "`object 0x{0: <16x}, vt 0x{1: <16x}{2:s}` ".format(obj, vt, vtype_str) |
48 | ||
39236c6e A |
49 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSString') |
50 | if vt == ztvAddr: | |
51 | out_string += GetString(obj) | |
52 | return out_string | |
53 | ||
54 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSSymbol') | |
55 | if vt == ztvAddr: | |
56 | out_string += GetString(obj) | |
57 | return out_string | |
58 | ||
59 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSNumber') | |
60 | if vt == ztvAddr: | |
61 | out_string += GetNumber(obj) | |
62 | return out_string | |
63 | ||
64 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV9OSBoolean') | |
65 | if vt == ztvAddr: | |
66 | out_string += GetBoolean(obj) | |
67 | return out_string | |
68 | ||
69 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV7OSArray') | |
70 | if vt == ztvAddr: | |
3e170ce0 | 71 | out_string += "(" + GetArray(CastIOKitClass(obj, 'OSArray *')) + ")" |
39236c6e A |
72 | return out_string |
73 | ||
74 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV5OSSet') | |
75 | if vt == ztvAddr: | |
3e170ce0 | 76 | out_string += GetSet(CastIOKitClass(obj, 'OSSet *')) |
39236c6e A |
77 | return out_string |
78 | ||
79 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV12OSDictionary') | |
80 | if vt == ztvAddr: | |
3e170ce0 | 81 | out_string += GetDictionary(CastIOKitClass(obj, 'OSDictionary *')) |
39236c6e A |
82 | return out_string |
83 | ||
84 | return out_string | |
85 | ||
39037602 A |
86 | |
87 | def GetObjectTypeStr(obj): | |
88 | """ Return the type of an OSObject's container class | |
89 | """ | |
90 | if obj is None: | |
91 | return None | |
92 | ||
93 | vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t') | |
94 | vtype = kern.SymbolicateFromAddress(vt) | |
95 | if len(vtype): | |
96 | return vtype[0].GetName() | |
97 | ||
98 | # See if the value is in a kext with no symbols | |
99 | for kval in IterateLinkedList(kern.globals.kmod, 'next'): | |
100 | if vt >= unsigned(kval.address) and vt <= (unsigned(kval.address) + unsigned(kval.size)): | |
101 | return "kmod:{:s}+{:#0x}".format(kval.name, vt - unsigned(kval.address)) | |
102 | return None | |
103 | ||
104 | ||
39236c6e A |
105 | @lldb_type_summary(['IORegistryEntry *']) |
106 | @header("") | |
107 | def GetRegistryEntrySummary(entry): | |
108 | """ returns a string containing summary information about an IORegistry | |
109 | object including it's registry id , vtable ptr and retain count | |
110 | """ | |
111 | name = None | |
112 | out_string = "" | |
113 | registryTable = entry.fRegistryTable | |
114 | propertyTable = entry.fPropertyTable | |
115 | ||
116 | name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) | |
117 | if name is None: | |
118 | name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) | |
119 | if name is None: | |
120 | name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) | |
121 | ||
122 | if name is not None: | |
3e170ce0 A |
123 | out_string += "+-o {0:s} ".format(GetString(CastIOKitClass(name, 'OSString *'))) |
124 | elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name: | |
125 | out_string += "+-o {0:s} ".format(CastIOKitClass(entry, 'IOService *').pwrMgt.Name) | |
39236c6e A |
126 | else: |
127 | out_string += "+-o ?? " | |
128 | ||
129 | # I'm using uintptr_t for now to work around <rdar://problem/12749733> FindFirstType & Co. should allow you to make pointer types directly | |
130 | vtableAddr = dereference(Cast(entry, 'uintptr_t *')) - 2 * sizeof('uintptr_t *') | |
131 | vtype = kern.SymbolicateFromAddress(vtableAddr) | |
132 | if vtype is None or len(vtype) < 1: | |
4bd07ac2 | 133 | out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x}".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID, vtableAddr) |
39236c6e | 134 | else: |
4bd07ac2 A |
135 | out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x} <{3:s}>".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID, |
136 | vtableAddr, vtype[0].GetName()) | |
39236c6e A |
137 | |
138 | ztvAddr = kern.GetLoadAddressForSymbol('_ZTV15IORegistryEntry') | |
139 | if vtableAddr != ztvAddr: | |
140 | out_string += ", " | |
3e170ce0 | 141 | state = CastIOKitClass(entry, 'IOService *').__state[0] |
39236c6e A |
142 | # kIOServiceRegisteredState |
143 | if 0 == state & 2: | |
144 | out_string += "!" | |
145 | out_string += "registered, " | |
146 | # kIOServiceMatchedState | |
147 | if 0 == state & 4: | |
148 | out_string += "!" | |
149 | out_string += "matched, " | |
150 | #kIOServiceInactiveState | |
151 | if 0 != state & 1: | |
152 | out_string += "in" | |
3e170ce0 A |
153 | busyCount = (CastIOKitClass(entry, 'IOService *').__state[1] & 0xff) |
154 | retCount = (CastIOKitClass(entry, 'IOService *').retainCount & 0xffff) | |
39236c6e | 155 | out_string += "active, busy {0}, retain count {1}>".format(busyCount, retCount) |
39236c6e A |
156 | return out_string |
157 | ||
158 | ###################################### | |
159 | # Commands | |
160 | ###################################### | |
161 | @lldb_command('showallclasses') | |
162 | def ShowAllClasses(cmd_args=None): | |
163 | """ Show the instance counts and ivar size of all OSObject subclasses. | |
164 | See ioclasscount man page for details | |
165 | """ | |
166 | idx = 0 | |
167 | count = unsigned(kern.globals.sAllClassesDict.count) | |
168 | ||
169 | while idx < count: | |
3e170ce0 | 170 | meta = CastIOKitClass(kern.globals.sAllClassesDict.dictionary[idx].value, 'OSMetaClass *') |
39236c6e A |
171 | idx += 1 |
172 | print GetMetaClass(meta) | |
173 | ||
174 | @lldb_command('showobject') | |
175 | def ShowObject(cmd_args=None): | |
176 | """ Show info about an OSObject - its vtable ptr and retain count, & more info for simple container classes. | |
177 | """ | |
178 | if not cmd_args: | |
179 | print "Please specify the address of the OSObject whose info you want to view. Type help showobject for help" | |
180 | return | |
181 | ||
182 | obj = kern.GetValueFromAddress(cmd_args[0], 'OSObject *') | |
183 | print GetObjectSummary(obj) | |
184 | ||
39037602 A |
185 | #Macro: dumpobject |
186 | @lldb_command('dumpobject') | |
187 | def DumpObject(cmd_args=None): | |
188 | """ Dumps object information if it is a valid object confirmed by showobject | |
189 | Usage: dumpobject <address of object to be dumped> [class/struct type of object] | |
190 | """ | |
191 | if not cmd_args: | |
192 | print "No arguments passed" | |
193 | print DumpObject.__doc__ | |
194 | return False | |
195 | ||
196 | if len(cmd_args) == 1: | |
197 | try: | |
198 | object_info = lldb_run_command("showobject {:s}".format(cmd_args[0])) | |
199 | except: | |
200 | print "Error!! showobject failed due to invalid value" | |
201 | print DumpObject.__doc__ | |
202 | return False | |
203 | ||
204 | srch = re.search(r'<vtable for ([A-Za-z].*)>', object_info) | |
205 | if not srch: | |
206 | print "Error!! Couldn't find object in registry, input type manually as 2nd argument" | |
207 | print DumpObject.__doc__ | |
208 | return False | |
209 | ||
210 | object_type = srch.group(1) | |
211 | else: | |
212 | type_lookup = lldb_run_command("image lookup -t {:s}".format(cmd_args[1])) | |
213 | if type_lookup.find(cmd_args[1])!= -1: | |
214 | object_type = cmd_args[1] | |
215 | else: | |
216 | print "Error!! Input type {:s} isn't available in image lookup".format(cmd_args[1]) | |
217 | return False | |
218 | ||
219 | print "******** Object Dump for value \'{:s}\' with type \"{:s}\" ********".format(cmd_args[0], object_type) | |
220 | print lldb_run_command("p/x *({:s}*){:s}".format(object_type, cmd_args[0])) | |
221 | ||
222 | #EndMacro: dumpobject | |
223 | ||
39236c6e A |
224 | @lldb_command('setregistryplane') |
225 | def SetRegistryPlane(cmd_args=None): | |
226 | """ Set the plane to be used for the IOKit registry macros | |
227 | syntax: (lldb) setregistryplane 0 - will display all known planes | |
228 | syntax: (lldb) setregistryplane 0xaddr - will set the registry plane to 0xaddr | |
229 | syntax: (lldb) setregistryplane gIODTPlane - will set the registry plane to gIODTPlane | |
230 | """ | |
231 | if not cmd_args: | |
232 | print "Please specify the name of the plane you want to use with the IOKit registry macros." | |
233 | print SetRegistryPlane.__doc__ | |
234 | ||
235 | if cmd_args[0] == "0": | |
236 | print GetObjectSummary(kern.globals.gIORegistryPlanes) | |
237 | else: | |
238 | global plane | |
239 | plane = kern.GetValueFromAddress(cmd_args[0], 'IORegistryPlane *') | |
240 | return | |
241 | ||
242 | @lldb_command('showregistryentry') | |
243 | def ShowRegistryEntry(cmd_args=None): | |
244 | """ Show info about a registry entry; its properties and descendants in the current plane | |
245 | syntax: (lldb) showregistryentry 0xaddr | |
246 | syntax: (lldb) showregistryentry gIOPMRootDomain | |
247 | """ | |
248 | if not cmd_args: | |
249 | print "Please specify the address of the registry entry whose info you want to view." | |
250 | print ShowRegistryEntry.__doc__ | |
251 | return | |
252 | ||
253 | entry = kern.GetValueFromAddress(cmd_args[0], 'IORegistryEntry *') | |
254 | ShowRegistryEntryRecurse(entry, "", True) | |
255 | ||
256 | @lldb_command('showregistry') | |
257 | def ShowRegistry(cmd_args=None): | |
258 | """ Show info about all registry entries in the current plane | |
259 | If prior to invoking this command no registry plane is specified | |
260 | using 'setregistryplane', the command defaults to the IOService plane | |
261 | """ | |
262 | ShowRegistryEntryRecurse(kern.globals.gRegistryRoot, "", False) | |
263 | ||
264 | @lldb_command('showregistryprops') | |
265 | def ShowRegistryProps(cmd_args=None): | |
266 | """ Show info about all registry entries in the current plane, and their properties | |
267 | If prior to invoking this command no registry plane is specified | |
268 | using 'setregistryplane', the command defaults to the IOService plane | |
269 | """ | |
270 | ShowRegistryEntryRecurse(kern.globals.gRegistryRoot, "", True) | |
271 | ||
272 | @lldb_command('findregistryentry') | |
273 | def FindRegistryEntry(cmd_args=None): | |
274 | """ Search for registry entry that matches the given string | |
275 | If prior to invoking this command no registry plane is specified | |
276 | using 'setregistryplane', the command defaults to searching entries from the IOService plane | |
277 | syntax: (lldb) findregistryentries AppleACPICPU - will find the first registry entry that matches AppleACPICPU | |
278 | """ | |
279 | if not cmd_args: | |
280 | print "Please specify the name of the registry entry you want to find" | |
281 | print FindRegistryEntry.__doc__ | |
282 | return | |
283 | ||
284 | FindRegistryEntryRecurse(kern.globals.gRegistryRoot, cmd_args[0], True) | |
285 | ||
286 | @lldb_command('findregistryentries') | |
287 | def FindRegistryEntries(cmd_args=None): | |
288 | """ Search for all registry entries that match the given string | |
289 | If prior to invoking this command no registry plane is specified | |
290 | using 'setregistryplane', the command defaults to searching entries from the IOService plane | |
291 | syntax: (lldb) findregistryentries AppleACPICPU - will find all registry entries that match AppleACPICPU | |
292 | """ | |
293 | if not cmd_args: | |
294 | print "Please specify the name of the registry entry/entries you want to find" | |
295 | print FindRegistryEntries.__doc__ | |
296 | return | |
297 | ||
298 | FindRegistryEntryRecurse(kern.globals.gRegistryRoot, cmd_args[0], False) | |
299 | ||
300 | @lldb_command('findregistryprop') | |
301 | def FindRegistryProp(cmd_args=None): | |
302 | """ Given a registry entry, print out the contents for the property that matches | |
303 | a specific string | |
304 | syntax: (lldb) findregistryprop 0xaddr IOSleepSupported | |
305 | syntax: (lldb) findregistryprop gIOPMRootDomain IOSleepSupported | |
306 | syntax: (lldb) findregistryprop gIOPMRootDomain "Supported Features" | |
307 | """ | |
308 | if not cmd_args or len(cmd_args) < 2: | |
309 | print "Please specify the address of a IORegistry entry and the property you're looking for" | |
310 | print FindRegistryProp.__doc__ | |
311 | return | |
312 | ||
313 | entry = kern.GetValueFromAddress(cmd_args[0], 'IOService *') | |
314 | propertyTable = entry.fPropertyTable | |
315 | print GetObjectSummary(LookupKeyInPropTable(propertyTable, cmd_args[1])) | |
316 | ||
317 | @lldb_command('readioport8') | |
318 | def ReadIOPort8(cmd_args=None): | |
319 | """ Read value stored in the specified IO port. The CPU can be optionally | |
320 | specified as well. | |
321 | Prints 0xBAD10AD in case of a bad read | |
322 | Syntax: (lldb) readioport8 <port> [lcpu (kernel's numbering convention)] | |
323 | """ | |
324 | if not cmd_args: | |
325 | print "Please specify a port to read out of" | |
326 | print ReadIOPort8.__doc__ | |
327 | return | |
328 | ||
329 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
330 | if len(cmd_args) >= 2: | |
331 | lcpu = ArgumentStringToInt(cmd_args[1]) | |
332 | else: | |
333 | lcpu = xnudefines.lcpu_self | |
334 | ||
335 | ReadIOPortInt(portAddr, 1, lcpu) | |
336 | ||
337 | @lldb_command('readioport16') | |
4bd07ac2 | 338 | def ReadIOPort16(cmd_args=None): |
39236c6e A |
339 | """ Read value stored in the specified IO port. The CPU can be optionally |
340 | specified as well. | |
341 | Prints 0xBAD10AD in case of a bad read | |
342 | Syntax: (lldb) readioport16 <port> [lcpu (kernel's numbering convention)] | |
343 | """ | |
344 | if not cmd_args: | |
345 | print "Please specify a port to read out of" | |
346 | print ReadIOPort16.__doc__ | |
347 | return | |
348 | ||
349 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
350 | if len(cmd_args) >= 2: | |
351 | lcpu = ArgumentStringToInt(cmd_args[1]) | |
352 | else: | |
353 | lcpu = xnudefines.lcpu_self | |
354 | ||
355 | ReadIOPortInt(portAddr, 2, lcpu) | |
356 | ||
357 | @lldb_command('readioport32') | |
4bd07ac2 | 358 | def ReadIOPort32(cmd_args=None): |
39236c6e A |
359 | """ Read value stored in the specified IO port. The CPU can be optionally |
360 | specified as well. | |
361 | Prints 0xBAD10AD in case of a bad read | |
362 | Syntax: (lldb) readioport32 <port> [lcpu (kernel's numbering convention)] | |
363 | """ | |
364 | if not cmd_args: | |
365 | print "Please specify a port to read out of" | |
366 | print ReadIOPort32.__doc__ | |
367 | return | |
368 | ||
369 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
370 | if len(cmd_args) >= 2: | |
371 | lcpu = ArgumentStringToInt(cmd_args[1]) | |
372 | else: | |
373 | lcpu = xnudefines.lcpu_self | |
374 | ||
375 | ReadIOPortInt(portAddr, 4, lcpu) | |
376 | ||
377 | @lldb_command('writeioport8') | |
378 | def WriteIOPort8(cmd_args=None): | |
379 | """ Write the value to the specified IO port. The size of the value is | |
380 | determined by the name of the command. The CPU used can be optionally | |
381 | specified as well. | |
382 | Syntax: (lldb) writeioport8 <port> <value> [lcpu (kernel's numbering convention)] | |
383 | """ | |
384 | if not cmd_args or len(cmd_args) < 2: | |
385 | print "Please specify a port to write to, followed by the value you want to write" | |
386 | print WriteIOPort8.__doc__ | |
387 | return | |
388 | ||
389 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
390 | value = ArgumentStringToInt(cmd_args[1]) | |
391 | ||
392 | if len(cmd_args) >= 3: | |
393 | lcpu = ArgumentStringToInt(cmd_args[2]) | |
394 | else: | |
395 | lcpu = xnudefines.lcpu_self | |
396 | ||
397 | WriteIOPortInt(portAddr, 1, value, lcpu) | |
398 | ||
399 | @lldb_command('writeioport16') | |
4bd07ac2 | 400 | def WriteIOPort16(cmd_args=None): |
39236c6e A |
401 | """ Write the value to the specified IO port. The size of the value is |
402 | determined by the name of the command. The CPU used can be optionally | |
403 | specified as well. | |
404 | Syntax: (lldb) writeioport16 <port> <value> [lcpu (kernel's numbering convention)] | |
405 | """ | |
406 | if not cmd_args or len(cmd_args) < 2: | |
407 | print "Please specify a port to write to, followed by the value you want to write" | |
408 | print WriteIOPort16.__doc__ | |
409 | return | |
410 | ||
411 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
412 | value = ArgumentStringToInt(cmd_args[1]) | |
413 | ||
414 | if len(cmd_args) >= 3: | |
415 | lcpu = ArgumentStringToInt(cmd_args[2]) | |
416 | else: | |
417 | lcpu = xnudefines.lcpu_self | |
418 | ||
419 | WriteIOPortInt(portAddr, 2, value, lcpu) | |
420 | ||
421 | @lldb_command('writeioport32') | |
4bd07ac2 | 422 | def WriteIOPort32(cmd_args=None): |
39236c6e A |
423 | """ Write the value to the specified IO port. The size of the value is |
424 | determined by the name of the command. The CPU used can be optionally | |
425 | specified as well. | |
426 | Syntax: (lldb) writeioport32 <port> <value> [lcpu (kernel's numbering convention)] | |
427 | """ | |
428 | if not cmd_args or len(cmd_args) < 2: | |
429 | print "Please specify a port to write to, followed by the value you want to write" | |
430 | print WriteIOPort32.__doc__ | |
431 | return | |
432 | ||
433 | portAddr = ArgumentStringToInt(cmd_args[0]) | |
434 | value = ArgumentStringToInt(cmd_args[1]) | |
435 | ||
436 | if len(cmd_args) >= 3: | |
437 | lcpu = ArgumentStringToInt(cmd_args[2]) | |
438 | else: | |
439 | lcpu = xnudefines.lcpu_self | |
440 | ||
441 | WriteIOPortInt(portAddr, 4, value, lcpu) | |
442 | ||
443 | @lldb_command('showioservicepm') | |
444 | def ShowIOServicePM(cmd_args=None): | |
445 | """ Routine to dump the IOServicePM object | |
446 | Syntax: (lldb) showioservicepm <IOServicePM pointer> | |
447 | """ | |
448 | if not cmd_args: | |
449 | print "Please enter the pointer to the IOServicePM object you'd like to introspect" | |
450 | print ShowIOServicePM.__doc__ | |
451 | return | |
452 | ||
453 | iopmpriv = kern.GetValueFromAddress(cmd_args[0], 'IOServicePM *') | |
454 | out_string = "MachineState {0: <6d} (".format(iopmpriv.MachineState) | |
455 | ||
456 | # Power state map | |
457 | pstate_map = { | |
458 | 0: 'kIOPM_Finished', | |
459 | 1: 'kIOPM_OurChangeTellClientsPowerDown', | |
460 | 2: 'kIOPM_OurChangeTellClientsPowerDown', | |
461 | 3: 'kIOPM_OurChangeNotifyInterestedDriversWillChange', | |
462 | 4: 'kIOPM_OurChangeSetPowerState', | |
463 | 5: 'kIOPM_OurChangeWaitForPowerSettle', | |
464 | 6: 'kIOPM_OurChangeNotifyInterestedDriversDidChange', | |
465 | 7: 'kIOPM_OurChangeTellCapabilityDidChange', | |
466 | 8: 'kIOPM_OurChangeFinish', | |
467 | 9: 'Unused_MachineState_9', | |
468 | 10: 'kIOPM_ParentChangeTellPriorityClientsPowerDown', | |
469 | 11: 'kIOPM_ParentChangeNotifyInterestedDriversWillChange', | |
470 | 12: 'kIOPM_ParentChangeSetPowerState', | |
471 | 13: 'kIOPM_ParentChangeWaitForPowerSettle', | |
472 | 14: 'kIOPM_ParentChangeNotifyInterestedDriversDidChange', | |
473 | 15: 'kIOPM_ParentChangeTellCapabilityDidChange', | |
474 | 16: 'kIOPM_ParentChangeAcknowledgePowerChange', | |
475 | 17: 'kIOPM_NotifyChildrenStart', | |
476 | 18: 'kIOPM_NotifyChildrenOrdered', | |
477 | 19: 'kIOPM_NotifyChildrenDelayed', | |
478 | 20: 'kIOPM_SyncTellClientsPowerDown', | |
479 | 21: 'kIOPM_SyncTellPriorityClientsPowerDown', | |
480 | 22: 'kIOPM_SyncNotifyWillChange', | |
481 | 23: 'kIOPM_SyncNotifyDidChange', | |
482 | 24: 'kIOPM_SyncTellCapabilityDidChange', | |
483 | 25: 'kIOPM_SyncFinish', | |
484 | 26: 'kIOPM_TellCapabilityChangeDone', | |
485 | 27: 'kIOPM_DriverThreadCallDone' | |
486 | } | |
487 | powerstate = unsigned(iopmpriv.MachineState) | |
488 | if powerstate in pstate_map: | |
489 | out_string += "{0:s}".format(pstate_map[powerstate]) | |
490 | else: | |
491 | out_string += "Unknown_MachineState" | |
492 | out_string += "), " | |
493 | ||
494 | if iopmpriv.MachineState != 20: | |
495 | out_string += "DriverTimer = {0: <6d}, SettleTime = {1: < 6d}, HeadNoteFlags = {2: #12x}, HeadNotePendingAcks = {3: #012x}, ".format( | |
496 | unsigned(iopmpriv.DriverTimer), | |
497 | unsigned(iopmpriv.SettleTimeUS), | |
498 | unsigned(iopmpriv.HeadNoteChangeFlags), | |
499 | unsigned(iopmpriv.HeadNotePendingAcks)) | |
500 | ||
501 | if iopmpriv.DeviceOverrideEnabled != 0: | |
502 | out_string += "DeviceOverrides, " | |
503 | ||
504 | out_string += "DeviceDesire = {0: <6d}, DesiredPowerState = {1: <6d}, PreviousRequest = {2: <6d}\n".format( | |
505 | unsigned(iopmpriv.DeviceDesire), | |
506 | unsigned(iopmpriv.DesiredPowerState), | |
507 | unsigned(iopmpriv.PreviousRequestPowerFlags)) | |
508 | ||
509 | print out_string | |
510 | ||
511 | ###################################### | |
512 | # Helper routines | |
513 | ###################################### | |
514 | def ShowRegistryEntryRecurse(entry, prefix, printProps): | |
515 | """ prints registry entry summary and recurses through all its children. | |
516 | """ | |
517 | # Setup | |
518 | global plane | |
519 | out_string = "" | |
520 | plen = (len(prefix)//2) | |
521 | registryTable = entry.fRegistryTable | |
522 | propertyTable = entry.fPropertyTable | |
523 | ||
524 | # Print entry details | |
525 | print "{0:s}{1:s}".format(prefix, GetRegistryEntrySummary(entry)) | |
526 | # Printing large property tables make it look like lldb is 'stuck' | |
527 | if printProps: | |
528 | print GetRegDictionary(propertyTable, prefix + " | ") | |
529 | ||
530 | # Recurse | |
531 | if plane is None: | |
532 | childKey = kern.globals.gIOServicePlane.keys[1] | |
533 | else: | |
534 | childKey = plane.keys[1] | |
535 | childArray = LookupKeyInOSDict(registryTable, childKey) | |
536 | if childArray is not None: | |
537 | idx = 0 | |
3e170ce0 | 538 | ca = CastIOKitClass(childArray, 'OSArray *') |
39236c6e A |
539 | count = unsigned(ca.count) |
540 | while idx < count: | |
541 | if plen != 0 and plen != 1 and (plen & (plen - 1)) == 0: | |
3e170ce0 | 542 | ShowRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), prefix + "| ", printProps) |
39236c6e | 543 | else: |
3e170ce0 | 544 | ShowRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), prefix + " ", printProps) |
39236c6e A |
545 | idx += 1 |
546 | ||
547 | def FindRegistryEntryRecurse(entry, search_name, stopAfterFirst): | |
548 | """ Checks if given registry entry's name matches the search_name we're looking for | |
549 | If yes, it prints the entry's summary and then recurses through its children | |
550 | If no, it does nothing and recurses through its children | |
551 | """ | |
552 | # Setup | |
553 | global plane | |
554 | registryTable = entry.fRegistryTable | |
555 | propertyTable = entry.fPropertyTable | |
556 | ||
557 | # Compare | |
558 | name = None | |
559 | name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) | |
560 | if name is None: | |
561 | name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) | |
562 | if name is None: | |
563 | name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) | |
564 | ||
565 | if name is not None: | |
3e170ce0 | 566 | if str(CastIOKitClass(name, 'OSString *').string) == search_name: |
39236c6e A |
567 | print GetRegistryEntrySummary(entry) |
568 | if stopAfterFirst is True: | |
569 | return True | |
3e170ce0 A |
570 | elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name: |
571 | name = CastIOKitClass(entry, 'IOService *').pwrMgt.Name | |
39236c6e A |
572 | if str(name) == search_name: |
573 | print GetRegistryEntrySummary(entry) | |
574 | if stopAfterFirst is True: | |
575 | return True | |
576 | ||
577 | # Recurse | |
578 | if plane is None: | |
579 | childKey = kern.globals.gIOServicePlane.keys[1] | |
580 | else: | |
581 | childKey = plane.keys[1] | |
582 | childArray = LookupKeyInOSDict(registryTable, childKey) | |
583 | if childArray is not None: | |
584 | idx = 0 | |
3e170ce0 | 585 | ca = CastIOKitClass(childArray, 'OSArray *') |
39236c6e A |
586 | count = unsigned(ca.count) |
587 | while idx < count: | |
3e170ce0 | 588 | if FindRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), search_name, stopAfterFirst) is True: |
39236c6e A |
589 | return True |
590 | idx += 1 | |
591 | return False | |
592 | ||
593 | def FindRegistryObjectRecurse(entry, search_name): | |
594 | """ Checks if given registry entry's name matches the search_name we're looking for | |
595 | If yes, return the entry | |
596 | If no, it does nothing and recurses through its children | |
597 | Implicitly stops after finding the first entry | |
598 | """ | |
599 | # Setup | |
600 | global plane | |
601 | registryTable = entry.fRegistryTable | |
602 | propertyTable = entry.fPropertyTable | |
603 | ||
604 | # Compare | |
605 | name = None | |
606 | name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) | |
607 | if name is None: | |
608 | name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) | |
609 | if name is None: | |
610 | name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) | |
611 | ||
612 | if name is not None: | |
3e170ce0 | 613 | if str(CastIOKitClass(name, 'OSString *').string) == search_name: |
39236c6e | 614 | return entry |
3e170ce0 A |
615 | elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name: |
616 | name = CastIOKitClass(entry, 'IOService *').pwrMgt.Name | |
39236c6e A |
617 | if str(name) == search_name: |
618 | return entry | |
619 | ||
620 | # Recurse | |
621 | if plane is None: | |
622 | childKey = kern.globals.gIOServicePlane.keys[1] | |
623 | else: | |
624 | childKey = plane.keys[1] | |
625 | childArray = LookupKeyInOSDict(registryTable, childKey) | |
626 | if childArray is not None: | |
3e170ce0 | 627 | ca = CastIOKitClass(childArray, 'OSArray *') |
39236c6e | 628 | for idx in range(ca.count): |
3e170ce0 | 629 | registry_object = FindRegistryObjectRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), search_name) |
39236c6e A |
630 | if not registry_object or int(registry_object) == int(0): |
631 | continue | |
632 | else: | |
633 | return registry_object | |
634 | return None | |
635 | ||
636 | def LookupKeyInOSDict(osdict, key): | |
637 | """ Returns the value corresponding to a given key in a OSDictionary | |
638 | Returns None if the key was not found | |
639 | """ | |
640 | if not osdict: | |
641 | return | |
642 | count = unsigned(osdict.count) | |
643 | result = None | |
644 | idx = 0 | |
645 | while idx < count and result is None: | |
646 | if key == osdict.dictionary[idx].key: | |
647 | result = osdict.dictionary[idx].value | |
648 | idx += 1 | |
649 | return result | |
650 | ||
651 | def LookupKeyInPropTable(propertyTable, key_str): | |
652 | """ Returns the value corresponding to a given key from a registry entry's property table | |
653 | Returns None if the key was not found | |
654 | The property that is being searched for is specified as a string in key_str | |
655 | """ | |
656 | if not propertyTable: | |
657 | return | |
658 | count = unsigned(propertyTable.count) | |
659 | result = None | |
660 | idx = 0 | |
661 | while idx < count and result is None: | |
662 | if key_str == str(propertyTable.dictionary[idx].key.string): | |
663 | result = propertyTable.dictionary[idx].value | |
664 | idx += 1 | |
665 | return result | |
666 | ||
667 | def GetRegDictionary(osdict, prefix): | |
668 | """ Returns a specially formatted string summary of the given OSDictionary | |
669 | This is done in order to pretty-print registry property tables in showregistry | |
670 | and other macros | |
671 | """ | |
672 | out_string = prefix + "{\n" | |
673 | idx = 0 | |
674 | count = unsigned(osdict.count) | |
675 | ||
676 | while idx < count: | |
677 | out_string += prefix + " " + GetObjectSummary(osdict.dictionary[idx].key) + " = " + GetObjectSummary(osdict.dictionary[idx].value) + "\n" | |
678 | idx += 1 | |
679 | out_string += prefix + "}\n" | |
680 | return out_string | |
681 | ||
682 | def GetString(string): | |
683 | """ Returns the python string representation of a given OSString | |
684 | """ | |
3e170ce0 | 685 | out_string = "\"{0:s}\"".format(CastIOKitClass(string, 'OSString *').string) |
39236c6e A |
686 | return out_string |
687 | ||
688 | def GetNumber(num): | |
3e170ce0 | 689 | out_string = "{0:d}".format(CastIOKitClass(num, 'OSNumber *').value) |
39236c6e A |
690 | return out_string |
691 | ||
692 | def GetBoolean(b): | |
693 | """ Shows info about a given OSBoolean | |
694 | """ | |
695 | out_string = "" | |
696 | if b == kern.globals.gOSBooleanFalse: | |
697 | out_string += "No" | |
698 | else: | |
699 | out_string += "Yes" | |
700 | return out_string | |
701 | ||
702 | def GetMetaClass(mc): | |
703 | """ Shows info about a given OSSymbol | |
704 | """ | |
705 | out_string = "{0: <5d}x {1: >5d} bytes {2:s}\n".format(mc.instanceCount, mc.classSize, mc.className.string) | |
706 | return out_string | |
707 | ||
708 | def GetArray(arr): | |
709 | """ Returns a string containing info about a given OSArray | |
710 | """ | |
711 | out_string = "" | |
712 | idx = 0 | |
713 | count = unsigned(arr.count) | |
714 | ||
715 | while idx < count: | |
716 | obj = arr.array[idx] | |
717 | idx += 1 | |
718 | out_string += GetObjectSummary(obj) | |
719 | if idx < unsigned(arr.count): | |
720 | out_string += "," | |
721 | return out_string | |
722 | ||
723 | def GetDictionary(d): | |
724 | """ Returns a string containing info about a given OSDictionary | |
725 | """ | |
726 | out_string = "{" | |
727 | idx = 0 | |
728 | count = unsigned(d.count) | |
729 | ||
730 | while idx < count: | |
731 | obj = d.dictionary[idx].key | |
732 | out_string += GetObjectSummary(obj) + "=" | |
733 | obj = d.dictionary[idx].value | |
734 | idx += 1 | |
735 | out_string += GetObjectSummary(obj) | |
736 | if idx < count: | |
737 | out_string += "," | |
738 | out_string += "}" | |
739 | return out_string | |
740 | ||
741 | def GetSet(se): | |
742 | """ Returns a string containing info about a given OSSet | |
743 | """ | |
744 | out_string += "[" + GetArray(se.members) + "]" | |
745 | return out_string | |
746 | ||
747 | def ReadIOPortInt(addr, numbytes, lcpu): | |
748 | """ Prints results after reading a given ioport | |
749 | """ | |
750 | result = 0xBAD10AD | |
751 | ||
752 | if "kdp" != GetConnectionProtocol(): | |
753 | print "Target is not connected over kdp. Nothing to do here." | |
754 | return | |
755 | ||
756 | # Set up the manual KDP packet | |
757 | input_address = unsigned(addressof(kern.globals.manual_pkt.input)) | |
758 | len_address = unsigned(addressof(kern.globals.manual_pkt.len)) | |
759 | data_address = unsigned(addressof(kern.globals.manual_pkt.data)) | |
760 | if not WriteInt32ToMemoryAddress(0, input_address): | |
761 | print "0x{0: <4x}: 0x{1: <1x}".format(addr, result) | |
762 | return | |
763 | ||
764 | kdp_pkt_size = GetType('kdp_readioport_req_t').GetByteSize() | |
765 | if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): | |
766 | print "0x{0: <4x}: 0x{1: <1x}".format(addr, result) | |
767 | return | |
768 | ||
769 | kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_readioport_req_t *') | |
770 | ||
771 | header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_READIOPORT'), length = kdp_pkt_size) | |
772 | ||
773 | if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and | |
774 | WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and | |
775 | WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and | |
776 | WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))) and | |
777 | WriteInt32ToMemoryAddress(1, input_address) | |
778 | ): | |
779 | ||
780 | result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_readioport_reply_t *') | |
781 | ||
782 | if(result_pkt.error == 0): | |
4bd07ac2 A |
783 | if numbytes == 1: |
784 | result = dereference(Cast(addressof(result_pkt.data), 'uint8_t *')) | |
785 | elif numbytes == 2: | |
786 | result = dereference(Cast(addressof(result_pkt.data), 'uint16_t *')) | |
787 | elif numbytes == 4: | |
788 | result = dereference(Cast(addressof(result_pkt.data), 'uint32_t *')) | |
789 | ||
790 | print "{0: <#6x}: {1:#0{2}x}".format(addr, result, (numbytes*2)+2) | |
39236c6e A |
791 | |
792 | def WriteIOPortInt(addr, numbytes, value, lcpu): | |
793 | """ Writes 'value' into ioport specified by 'addr'. Prints errors if it encounters any | |
794 | """ | |
795 | if "kdp" != GetConnectionProtocol(): | |
796 | print "Target is not connected over kdp. Nothing to do here." | |
797 | return | |
798 | ||
799 | # Set up the manual KDP packet | |
800 | input_address = unsigned(addressof(kern.globals.manual_pkt.input)) | |
801 | len_address = unsigned(addressof(kern.globals.manual_pkt.len)) | |
802 | data_address = unsigned(addressof(kern.globals.manual_pkt.data)) | |
803 | if not WriteInt32ToMemoryAddress(0, input_address): | |
4bd07ac2 | 804 | print "error writing {0: #x} to port {1: <#6x}: failed to write 0 to input_address".format(value, addr) |
39236c6e A |
805 | return |
806 | ||
807 | kdp_pkt_size = GetType('kdp_writeioport_req_t').GetByteSize() | |
808 | if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): | |
4bd07ac2 | 809 | print "error writing {0: #x} to port {1: <#6x}: failed to write kdp_pkt_size".format(value, addr) |
39236c6e A |
810 | return |
811 | ||
812 | kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_writeioport_req_t *') | |
813 | ||
814 | header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_WRITEIOPORT'), length = kdp_pkt_size) | |
815 | ||
816 | if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and | |
817 | WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and | |
818 | WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and | |
819 | WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))) | |
820 | ): | |
4bd07ac2 A |
821 | if numbytes == 1: |
822 | if not WriteInt8ToMemoryAddress(value, int(addressof(kgm_pkt.data))): | |
823 | print "error writing {0: #x} to port {1: <#6x}: failed to write 8 bit data".format(value, addr) | |
824 | return | |
825 | elif numbytes == 2: | |
826 | if not WriteInt16ToMemoryAddress(value, int(addressof(kgm_pkt.data))): | |
827 | print "error writing {0: #x} to port {1: <#6x}: failed to write 16 bit data".format(value, addr) | |
828 | return | |
829 | elif numbytes == 4: | |
830 | if not WriteInt32ToMemoryAddress(value, int(addressof(kgm_pkt.data))): | |
831 | print "error writing {0: #x} to port {1: <#6x}: failed to write 32 bit data".format(value, addr) | |
832 | return | |
39236c6e | 833 | if not WriteInt32ToMemoryAddress(1, input_address): |
4bd07ac2 | 834 | print "error writing {0: #x} to port {1: <#6x}: failed to write to input_address".format(value, addr) |
39236c6e A |
835 | return |
836 | ||
837 | result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_writeioport_reply_t *') | |
838 | ||
839 | # Done with the write | |
840 | if(result_pkt.error == 0): | |
4bd07ac2 | 841 | print "Writing {0: #x} to port {1: <#6x} was successful".format(value, addr) |
39236c6e | 842 | else: |
4bd07ac2 | 843 | print "error writing {0: #x} to port {1: <#6x}".format(value, addr) |
39236c6e | 844 | |
fe8ab488 A |
845 | @lldb_command('showinterruptcounts') |
846 | def showinterruptcounts(cmd_args=None): | |
847 | """ Shows event source based interrupt counts by nub name and interrupt index. | |
848 | Does not cover interrupts that are not event source based. Will report 0 | |
849 | if interrupt accounting is disabled. | |
850 | """ | |
851 | ||
852 | header_format = "{0: <20s} {1: >5s} {2: >20s}" | |
853 | content_format = "{0: <20s} {1: >5d} {2: >20d}" | |
854 | ||
855 | print header_format.format("Name", "Index", "Count") | |
856 | ||
857 | for i in kern.interrupt_stats: | |
3e170ce0 A |
858 | owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *') |
859 | nub = CastIOKitClass(owner.provider, 'IORegistryEntry *') | |
fe8ab488 A |
860 | name = None |
861 | ||
862 | # To uniquely identify an interrupt, we need the nub name and the index. The index | |
863 | # is stored with the stats object, but we need to retrieve the name. | |
864 | ||
865 | registryTable = nub.fRegistryTable | |
866 | propertyTable = nub.fPropertyTable | |
867 | ||
868 | name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) | |
869 | if name is None: | |
870 | name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) | |
871 | if name is None: | |
872 | name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) | |
873 | ||
874 | if name is None: | |
875 | nub_name = "Unknown" | |
876 | else: | |
3e170ce0 | 877 | nub_name = GetString(CastIOKitClass(name, 'OSString *')) |
fe8ab488 A |
878 | |
879 | # We now have everything we need; spew the requested data. | |
880 | ||
881 | interrupt_index = i.interruptIndex | |
882 | first_level_count = i.interruptStatistics[0] | |
883 | ||
884 | print content_format.format(nub_name, interrupt_index, first_level_count) | |
885 | ||
886 | return True | |
887 | ||
888 | @lldb_command('showinterruptstats') | |
889 | def showinterruptstats(cmd_args=None): | |
890 | """ Shows event source based interrupt statistics by nub name and interrupt index. | |
891 | Does not cover interrupts that are not event source based. Will report 0 | |
892 | if interrupt accounting is disabled, or if specific statistics are disabled. | |
893 | Time is reported in ticks of mach_absolute_time. Statistics are: | |
894 | ||
895 | Interrupt Count: Number of times the interrupt context handler was run | |
896 | Interrupt Time: Total time spent in the interrupt context handler (if any) | |
897 | Workloop Count: Number of times the kernel context handler was run | |
898 | Workloop CPU Time: Total CPU time spent running the kernel context handler | |
899 | Workloop Time: Total time spent running the kernel context handler | |
900 | """ | |
901 | ||
5ba3f43e A |
902 | header_format = "{0: <20s} {1: >5s} {2: >20s} {3: >20s} {4: >20s} {5: >20s} {6: >20s} {7: >20s} {8: >20s} {9: >20s}" |
903 | content_format = "{0: <20s} {1: >5d} {2: >20d} {3: >20d} {4: >20d} {5: >20d} {6: >20d} {7: >20d} {8: >20d} {9: >#20x}" | |
fe8ab488 | 904 | |
5ba3f43e | 905 | print header_format.format("Name", "Index", "Interrupt Count", "Interrupt Time", "Avg Interrupt Time", "Workloop Count", "Workloop CPU Time", "Workloop Time", "Avg Workloop Time", "Owner") |
fe8ab488 A |
906 | |
907 | for i in kern.interrupt_stats: | |
3e170ce0 A |
908 | owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *') |
909 | nub = CastIOKitClass(owner.provider, 'IORegistryEntry *') | |
fe8ab488 A |
910 | name = None |
911 | ||
912 | # To uniquely identify an interrupt, we need the nub name and the index. The index | |
913 | # is stored with the stats object, but we need to retrieve the name. | |
914 | ||
915 | registryTable = nub.fRegistryTable | |
916 | propertyTable = nub.fPropertyTable | |
917 | ||
918 | name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey) | |
919 | if name is None: | |
920 | name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey) | |
921 | if name is None: | |
922 | name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) | |
923 | ||
924 | if name is None: | |
925 | nub_name = "Unknown" | |
926 | else: | |
3e170ce0 | 927 | nub_name = GetString(CastIOKitClass(name, 'OSString *')) |
fe8ab488 A |
928 | |
929 | # We now have everything we need; spew the requested data. | |
930 | ||
931 | interrupt_index = i.interruptIndex | |
932 | first_level_count = i.interruptStatistics[0] | |
933 | second_level_count = i.interruptStatistics[1] | |
934 | first_level_time = i.interruptStatistics[2] | |
935 | second_level_cpu_time = i.interruptStatistics[3] | |
936 | second_level_system_time = i.interruptStatistics[4] | |
937 | ||
5ba3f43e A |
938 | avg_first_level_time = 0 |
939 | if first_level_count != 0: | |
940 | avg_first_level_time = first_level_time / first_level_count | |
941 | ||
942 | avg_second_level_time = 0 | |
943 | if second_level_count != 0: | |
944 | avg_second_level_time = second_level_system_time / second_level_count | |
945 | ||
946 | print content_format.format(nub_name, interrupt_index, first_level_count, first_level_time, avg_first_level_time, | |
947 | second_level_count, second_level_cpu_time, second_level_system_time, avg_second_level_time, owner) | |
fe8ab488 A |
948 | |
949 | return True | |
950 |