xref: /xnu-10002.61.3/tools/lldbmacros/ioreg.py (revision 0f4c859e951fba394238ab619495c4e1d54d0f34)
1from __future__ import absolute_import, division, print_function
2
3from builtins import chr
4from builtins import zip
5from builtins import hex
6from builtins import range
7from builtins import object
8
9from xnu import *
10from utils import *
11from kdp import *
12from core import caching
13from core.pointer import NativePointer
14import sys
15import lldb
16import os
17import sys
18from collections import deque
19
20######################################
21# Globals
22######################################
23plane = None
24
25#####################################
26# Utility functions.
27#####################################
28def CastIOKitClass(obj, target_type):
29    """ Type cast an object to another IOKIT CPP class.
30        params:
31            obj - core.value  object representing some C construct in lldb
32            target_type - str : ex 'OSString *'
33                        - lldb.SBType :
34    """
35    v = obj.GetSBValue()
36    # We need to do that so that LLDB doesn't try to "helpfully"
37    # Guess which instance type it is...
38    v.SetPreferDynamicValue(lldb.eNoDynamicValues)
39    if isinstance(target_type, six.string_types):
40        target_type = gettype(target_type)
41    return value(v.Cast(target_type))
42
43#####################################
44# Classes.
45#####################################
46class PreoslogHeader(object):
47    """
48    Represents preoslog buffer header. There's no symbol in the kernel for it.
49    """
50    valid_magic = "POSL"
51    def __init__(self):
52        self.magic = ""
53        self.offset = 0
54        self.size = 0
55        self.source = 0
56        self.wrapped = 0
57        self.data = None
58
59
60class IOKitSmartPointer(NativePointer):
61    """ IOKit's smart pointer
62
63        Every smart pointer inherits from libkern::intrusive_shared_ptr.
64        The real pointer is wrapped behind ptr_ member.
65    """
66
67    @classmethod
68    def match(cls, sbvalue):
69
70        # Smart pointers in IOKit are OSSharedPtr and OSTaggedSharedPtr
71        name = sbvalue.GetType().GetCanonicalType().GetName()
72        if name.startswith(("OSSharedPtr", "OSTaggedSharedPtr")):
73            return cls()
74
75        return None
76
77    def GetPointerSBValue(self, sbvalue):
78        sbv = sbvalue.GetChildMemberWithName('ptr_')
79        return super(IOKitSmartPointer, self).GetPointerSBValue(sbv)
80
81
82######################################
83# Type Summaries
84######################################
85@lldb_type_summary(['OSObject *'])
86@header("")
87def GetObjectSummary(obj):
88    """ Show info about an OSObject - its vtable ptr and retain count, & more info for simple container classes.
89    """
90    if obj is None:
91        return
92
93    vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t')
94    vt = kern.StripKernelPAC(vt)
95    vtype = kern.SymbolicateFromAddress(vt)
96    if len(vtype):
97        vtype_str = " <" + vtype[0].GetName() + ">"
98    else:
99        vtype_str = ""
100    if hasattr(obj, 'retainCount'):
101        retCount = (obj.retainCount & 0xffff)
102        cntnrRetCount = (obj.retainCount >> 16)
103        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)
104    else:
105        out_string = "`object 0x{0: <16x}, vt 0x{1: <16x}{2:s}` ".format(obj, vt, vtype_str)
106
107    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSString')
108    if vt == ztvAddr:
109        out_string += GetString(obj)
110        return out_string
111
112    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSSymbol')
113    if vt == ztvAddr:
114        out_string += GetString(obj)
115        return out_string
116
117    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSNumber')
118    if vt == ztvAddr:
119        out_string += GetNumber(obj)
120        return out_string
121
122    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV9OSBoolean')
123    if vt == ztvAddr:
124        out_string += GetBoolean(obj)
125        return out_string
126
127    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV7OSArray')
128    if vt == ztvAddr:
129        out_string += "(" + GetArray(CastIOKitClass(obj, 'OSArray *')) + ")"
130        return out_string
131
132    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV5OSSet')
133    if vt == ztvAddr:
134        out_string += GetSet(CastIOKitClass(obj, 'OSSet *'))
135        return out_string
136
137    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV12OSDictionary')
138    if vt == ztvAddr:
139        out_string += GetDictionary(CastIOKitClass(obj, 'OSDictionary *'))
140        return out_string
141
142    return out_string
143
144
145def GetObjectTypeStr(obj):
146    """ Return the type of an OSObject's container class
147    """
148    if obj is None:
149        return None
150
151    vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t')
152    vt = kern.StripKernelPAC(vt)
153    vtype = kern.SymbolicateFromAddress(vt)
154    if len(vtype):
155        return vtype[0].GetName()
156
157    # See if the value is in a kext with no symbols
158    for kval in IterateLinkedList(kern.globals.kmod, 'next'):
159        if vt >= unsigned(kval.address) and vt <= (unsigned(kval.address) + unsigned(kval.size)):
160            return "kmod:{:s}+{:#0x}".format(kval.name, vt - unsigned(kval.address))
161    return None
162
163
164@lldb_type_summary(['IORegistryEntry *'])
165@header("")
166def GetRegistryEntrySummary(entry):
167    """ returns a string containing summary information about an IORegistry
168        object including it's registry id , vtable ptr and retain count
169    """
170    name = None
171    out_string = ""
172    registryTable = entry.fRegistryTable
173    propertyTable = entry.fPropertyTable
174
175    name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
176    if name is None:
177        name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
178    if name is None:
179        name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
180
181    if name is not None:
182        out_string += "+-o {0:s}  ".format(GetString(CastIOKitClass(name, 'OSString *')))
183    elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name:
184        out_string += "+-o {0:s}  ".format(CastIOKitClass(entry, 'IOService *').pwrMgt.Name)
185    else:
186        out_string += "+-o ??  "
187
188    # I'm using uintptr_t for now to work around <rdar://problem/12749733> FindFirstType & Co. should allow you to make pointer types directly
189    vtableAddr = dereference(Cast(entry, 'uintptr_t *')) - 2 * sizeof('uintptr_t *')
190    vtableAddr = kern.StripKernelPAC(vtableAddr)
191    vtype = kern.SymbolicateFromAddress(vtableAddr)
192    if vtype is None or len(vtype) < 1:
193        out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x}".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID, vtableAddr)
194    else:
195        out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x} <{3:s}>".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID,
196                                                                                           vtableAddr, vtype[0].GetName())
197
198    ztvAddr = kern.GetLoadAddressForSymbol('_ZTV15IORegistryEntry')
199    if vtableAddr != ztvAddr:
200        out_string += ", "
201        state = CastIOKitClass(entry, 'IOService *').__state[0]
202        # kIOServiceRegisteredState
203        if 0 == state & 2:
204            out_string += "!"
205        out_string += "registered, "
206        # kIOServiceMatchedState
207        if 0 == state & 4:
208            out_string += "!"
209        out_string += "matched, "
210        #kIOServiceInactiveState
211        if 0 != state & 1:
212            out_string += "in"
213        busyCount = (CastIOKitClass(entry, 'IOService *').__state[1] & 0xff)
214        retCount = (CastIOKitClass(entry, 'IOService *').retainCount & 0xffff)
215        out_string += "active, busy {0}, retain count {1}>".format(busyCount, retCount)
216    return out_string
217
218######################################
219# Commands
220######################################
221@lldb_command('showallclasses')
222def ShowAllClasses(cmd_args=None):
223    """ Show the instance counts and ivar size of all OSObject subclasses.
224        See ioclasscount man page for details
225    """
226    idx = 0
227    count = unsigned(kern.globals.sAllClassesDict.count)
228
229    while idx < count:
230        meta = CastIOKitClass(kern.globals.sAllClassesDict.dictionary[idx].value, 'OSMetaClass *')
231        idx += 1
232        print(GetMetaClass(meta))
233
234@lldb_command('showobject')
235def ShowObject(cmd_args=None):
236    """ Show info about an OSObject - its vtable ptr and retain count, & more info for simple container classes.
237    """
238    if not cmd_args:
239        print("Please specify the address of the OSObject whose info you want to view. Type help showobject for help")
240        return
241
242    obj = kern.GetValueFromAddress(cmd_args[0], 'OSObject *')
243    print(GetObjectSummary(obj))
244
245#Macro: dumpobject
246@lldb_command('dumpobject')
247def DumpObject(cmd_args=None):
248    """ Dumps object information if it is a valid object confirmed by showobject
249        Usage: dumpobject <address of object to be dumped> [class/struct type of object]
250    """
251    if not cmd_args:
252        print("No arguments passed")
253        print(DumpObject.__doc__)
254        return False
255
256    if len(cmd_args) == 1:
257        try:
258            object_info = lldb_run_command("showobject {:s}".format(cmd_args[0]))
259        except:
260            print("Error!! showobject failed due to invalid value")
261            print(DumpObject.__doc__)
262            return False
263
264        srch = re.search(r'<vtable for ([A-Za-z].*)>', object_info)
265        if not srch:
266            print("Error!! Couldn't find object in registry, input type manually as 2nd argument")
267            print(DumpObject.__doc__)
268            return False
269
270        object_type = srch.group(1)
271    else:
272        type_lookup = lldb_run_command("image lookup -t {:s}".format(cmd_args[1]))
273        if type_lookup.find(cmd_args[1])!= -1:
274            object_type = cmd_args[1]
275        else:
276            print("Error!! Input type {:s} isn't available in image lookup".format(cmd_args[1]))
277            return False
278
279    print("******** Object Dump for value \'{:s}\' with type \"{:s}\" ********".format(cmd_args[0], object_type))
280    print(lldb_run_command("p/x *({:s}*){:s}".format(object_type, cmd_args[0])))
281
282#EndMacro: dumpobject
283
284@lldb_command('setregistryplane')
285def SetRegistryPlane(cmd_args=None):
286    """ Set the plane to be used for the IOKit registry macros
287        syntax: (lldb) setregistryplane 0  - will display all known planes
288        syntax: (lldb) setregistryplane 0xaddr      - will set the registry plane to 0xaddr
289        syntax: (lldb) setregistryplane gIODTPlane  - will set the registry plane to gIODTPlane
290    """
291    if not cmd_args:
292        print("Please specify the name of the plane you want to use with the IOKit registry macros.")
293        print(SetRegistryPlane.__doc__)
294
295    if cmd_args[0] == "0":
296        print(GetObjectSummary(kern.globals.gIORegistryPlanes))
297    else:
298        global plane
299        plane = kern.GetValueFromAddress(cmd_args[0], 'IORegistryPlane *')
300    return
301
302@lldb_command('showregistryentry')
303def ShowRegistryEntry(cmd_args=None):
304    """ Show info about a registry entry; its properties and descendants in the current plane
305        syntax: (lldb) showregistryentry 0xaddr
306        syntax: (lldb) showregistryentry gIOPMRootDomain
307    """
308    if not cmd_args:
309        print("Please specify the address of the registry entry whose info you want to view.")
310        print(ShowRegistryEntry.__doc__)
311        return
312
313    entry = kern.GetValueFromAddress(cmd_args[0], 'IORegistryEntry *')
314    ShowRegistryEntryRecurse(entry, "", True)
315
316@lldb_command('showregistry')
317def ShowRegistry(cmd_args=None):
318    """ Show info about all registry entries in the current plane
319        If prior to invoking this command no registry plane is specified
320        using 'setregistryplane', the command defaults to the IOService plane
321    """
322    ShowRegistryEntryRecurse(kern.globals.gRegistryRoot, "", False)
323
324@lldb_command('showregistryprops')
325def ShowRegistryProps(cmd_args=None):
326    """ Show info about all registry entries in the current plane, and their properties
327        If prior to invoking this command no registry plane is specified
328        using 'setregistryplane', the command defaults to the IOService plane
329    """
330    ShowRegistryEntryRecurse(kern.globals.gRegistryRoot, "", True)
331
332@lldb_command('findregistryentry')
333def FindRegistryEntry(cmd_args=None):
334    """ Search for registry entry that matches the given string
335        If prior to invoking this command no registry plane is specified
336        using 'setregistryplane', the command defaults to searching entries from the IOService plane
337        syntax: (lldb) findregistryentries AppleACPICPU - will find the first registry entry that matches AppleACPICPU
338    """
339    if not cmd_args:
340        print("Please specify the name of the registry entry you want to find")
341        print(FindRegistryEntry.__doc__)
342        return
343
344    FindRegistryEntryRecurse(kern.globals.gRegistryRoot, cmd_args[0], True)
345
346@lldb_command('findregistryentries')
347def FindRegistryEntries(cmd_args=None):
348    """ Search for all registry entries that match the given string
349        If prior to invoking this command no registry plane is specified
350        using 'setregistryplane', the command defaults to searching entries from the IOService plane
351        syntax: (lldb) findregistryentries AppleACPICPU - will find all registry entries that match AppleACPICPU
352    """
353    if not cmd_args:
354        print("Please specify the name of the registry entry/entries you want to find")
355        print(FindRegistryEntries.__doc__)
356        return
357
358    FindRegistryEntryRecurse(kern.globals.gRegistryRoot, cmd_args[0], False)
359
360@lldb_command('findregistryprop')
361def FindRegistryProp(cmd_args=None):
362    """ Given a registry entry, print out the contents for the property that matches
363        a specific string
364        syntax: (lldb) findregistryprop 0xaddr IOSleepSupported
365        syntax: (lldb) findregistryprop gIOPMRootDomain IOSleepSupported
366        syntax: (lldb) findregistryprop gIOPMRootDomain "Supported Features"
367    """
368    if not cmd_args or len(cmd_args) < 2:
369        print("Please specify the address of a IORegistry entry and the property you're looking for")
370        print(FindRegistryProp.__doc__)
371        return
372
373    entry = kern.GetValueFromAddress(cmd_args[0], 'IOService *')
374    propertyTable = entry.fPropertyTable
375    print(GetObjectSummary(LookupKeyInPropTable(propertyTable, cmd_args[1])))
376
377@lldb_command('readioport8')
378def ReadIOPort8(cmd_args=None):
379    """ Read value stored in the specified IO port. The CPU can be optionally
380        specified as well.
381        Prints 0xBAD10AD in case of a bad read
382        Syntax: (lldb) readioport8 <port> [lcpu (kernel's numbering convention)]
383    """
384    if not cmd_args:
385        print("Please specify a port to read out of")
386        print(ReadIOPort8.__doc__)
387        return
388
389    portAddr = ArgumentStringToInt(cmd_args[0])
390    if len(cmd_args) >= 2:
391        lcpu = ArgumentStringToInt(cmd_args[1])
392    else:
393        lcpu = xnudefines.lcpu_self
394
395    ReadIOPortInt(portAddr, 1, lcpu)
396
397@lldb_command('readioport16')
398def ReadIOPort16(cmd_args=None):
399    """ Read value stored in the specified IO port. The CPU can be optionally
400        specified as well.
401        Prints 0xBAD10AD in case of a bad read
402        Syntax: (lldb) readioport16 <port> [lcpu (kernel's numbering convention)]
403    """
404    if not cmd_args:
405        print("Please specify a port to read out of")
406        print(ReadIOPort16.__doc__)
407        return
408
409    portAddr = ArgumentStringToInt(cmd_args[0])
410    if len(cmd_args) >= 2:
411        lcpu = ArgumentStringToInt(cmd_args[1])
412    else:
413        lcpu = xnudefines.lcpu_self
414
415    ReadIOPortInt(portAddr, 2, lcpu)
416
417@lldb_command('readioport32')
418def ReadIOPort32(cmd_args=None):
419    """ Read value stored in the specified IO port. The CPU can be optionally
420        specified as well.
421        Prints 0xBAD10AD in case of a bad read
422        Syntax: (lldb) readioport32 <port> [lcpu (kernel's numbering convention)]
423    """
424    if not cmd_args:
425        print("Please specify a port to read out of")
426        print(ReadIOPort32.__doc__)
427        return
428
429    portAddr = ArgumentStringToInt(cmd_args[0])
430    if len(cmd_args) >= 2:
431        lcpu = ArgumentStringToInt(cmd_args[1])
432    else:
433        lcpu = xnudefines.lcpu_self
434
435    ReadIOPortInt(portAddr, 4, lcpu)
436
437@lldb_command('writeioport8')
438def WriteIOPort8(cmd_args=None):
439    """ Write the value to the specified IO port. The size of the value is
440        determined by the name of the command. The CPU used can be optionally
441        specified as well.
442        Syntax: (lldb) writeioport8 <port> <value> [lcpu (kernel's numbering convention)]
443    """
444    if not cmd_args or len(cmd_args) < 2:
445        print("Please specify a port to write to, followed by the value you want to write")
446        print(WriteIOPort8.__doc__)
447        return
448
449    portAddr = ArgumentStringToInt(cmd_args[0])
450    value = ArgumentStringToInt(cmd_args[1])
451
452    if len(cmd_args) >= 3:
453        lcpu = ArgumentStringToInt(cmd_args[2])
454    else:
455        lcpu = xnudefines.lcpu_self
456
457    WriteIOPortInt(portAddr, 1, value, lcpu)
458
459@lldb_command('writeioport16')
460def WriteIOPort16(cmd_args=None):
461    """ Write the value to the specified IO port. The size of the value is
462        determined by the name of the command. The CPU used can be optionally
463        specified as well.
464        Syntax: (lldb) writeioport16 <port> <value> [lcpu (kernel's numbering convention)]
465    """
466    if not cmd_args or len(cmd_args) < 2:
467        print("Please specify a port to write to, followed by the value you want to write")
468        print(WriteIOPort16.__doc__)
469        return
470
471    portAddr = ArgumentStringToInt(cmd_args[0])
472    value = ArgumentStringToInt(cmd_args[1])
473
474    if len(cmd_args) >= 3:
475        lcpu = ArgumentStringToInt(cmd_args[2])
476    else:
477        lcpu = xnudefines.lcpu_self
478
479    WriteIOPortInt(portAddr, 2, value, lcpu)
480
481@lldb_command('writeioport32')
482def WriteIOPort32(cmd_args=None):
483    """ Write the value to the specified IO port. The size of the value is
484        determined by the name of the command. The CPU used can be optionally
485        specified as well.
486        Syntax: (lldb) writeioport32 <port> <value> [lcpu (kernel's numbering convention)]
487    """
488    if not cmd_args or len(cmd_args) < 2:
489        print("Please specify a port to write to, followed by the value you want to write")
490        print(WriteIOPort32.__doc__)
491        return
492
493    portAddr = ArgumentStringToInt(cmd_args[0])
494    value = ArgumentStringToInt(cmd_args[1])
495
496    if len(cmd_args) >= 3:
497        lcpu = ArgumentStringToInt(cmd_args[2])
498    else:
499        lcpu = xnudefines.lcpu_self
500
501    WriteIOPortInt(portAddr, 4, value, lcpu)
502
503@lldb_command('showioservicepm')
504def ShowIOServicePM(cmd_args=None):
505    """ Routine to dump the IOServicePM object
506        Syntax: (lldb) showioservicepm <IOServicePM pointer>
507    """
508    if not cmd_args:
509        print("Please enter the pointer to the IOServicePM object you'd like to introspect")
510        print(ShowIOServicePM.__doc__)
511        return
512
513    iopmpriv = kern.GetValueFromAddress(cmd_args[0], 'IOServicePM *')
514    out_string = "MachineState {0: <6d} (".format(iopmpriv.MachineState)
515
516    # Power state map
517    pstate_map = {
518            0:  'kIOPM_Finished',
519            1:  'kIOPM_OurChangeTellClientsPowerDown',
520            2:  'kIOPM_OurChangeTellClientsPowerDown',
521            3:  'kIOPM_OurChangeNotifyInterestedDriversWillChange',
522            4:  'kIOPM_OurChangeSetPowerState',
523            5:  'kIOPM_OurChangeWaitForPowerSettle',
524            6:  'kIOPM_OurChangeNotifyInterestedDriversDidChange',
525            7:  'kIOPM_OurChangeTellCapabilityDidChange',
526            8:  'kIOPM_OurChangeFinish',
527            9:  'Unused_MachineState_9',
528            10: 'kIOPM_ParentChangeTellPriorityClientsPowerDown',
529            11: 'kIOPM_ParentChangeNotifyInterestedDriversWillChange',
530            12: 'kIOPM_ParentChangeSetPowerState',
531            13: 'kIOPM_ParentChangeWaitForPowerSettle',
532            14: 'kIOPM_ParentChangeNotifyInterestedDriversDidChange',
533            15: 'kIOPM_ParentChangeTellCapabilityDidChange',
534            16: 'kIOPM_ParentChangeAcknowledgePowerChange',
535            17: 'kIOPM_NotifyChildrenStart',
536            18: 'kIOPM_NotifyChildrenOrdered',
537            19: 'kIOPM_NotifyChildrenDelayed',
538            20: 'kIOPM_SyncTellClientsPowerDown',
539            21: 'kIOPM_SyncTellPriorityClientsPowerDown',
540            22: 'kIOPM_SyncNotifyWillChange',
541            23: 'kIOPM_SyncNotifyDidChange',
542            24: 'kIOPM_SyncTellCapabilityDidChange',
543            25: 'kIOPM_SyncFinish',
544            26: 'kIOPM_TellCapabilityChangeDone',
545            27: 'kIOPM_DriverThreadCallDone'
546        }
547    powerstate = unsigned(iopmpriv.MachineState)
548    if powerstate in pstate_map:
549        out_string += "{0:s}".format(pstate_map[powerstate])
550    else:
551        out_string += "Unknown_MachineState"
552    out_string += "), "
553
554    if iopmpriv.MachineState != 20:
555        if hasattr(iopmpriv, "SettleTimeUS"):
556            out_string += "DriverTimer = {0: <6d}, SettleTime = {1: < 6d}, HeadNoteFlags = {2: #12x}, HeadNotePendingAcks = {3: #012x}, ".format(
557                    unsigned(iopmpriv.DriverTimer),
558                    unsigned(iopmpriv.SettleTimeUS),
559                    unsigned(iopmpriv.HeadNoteChangeFlags),
560                    unsigned(iopmpriv.HeadNotePendingAcks))
561        else:
562            out_string += "DriverTimer = {0: <6d}, HeadNoteFlags = {1: #12x}, HeadNotePendingAcks = {2: #012x}, ".format(
563                    unsigned(iopmpriv.DriverTimer),
564                    unsigned(iopmpriv.HeadNoteChangeFlags),
565                    unsigned(iopmpriv.HeadNotePendingAcks))
566
567    if iopmpriv.DeviceOverrideEnabled != 0:
568        out_string += "DeviceOverrides, "
569
570    out_string += "DeviceDesire = {0: <6d}, DesiredPowerState = {1: <6d}, PreviousRequest = {2: <6d}\n".format(
571            unsigned(iopmpriv.DeviceDesire),
572            unsigned(iopmpriv.DesiredPowerState),
573            unsigned(iopmpriv.PreviousRequestPowerFlags))
574
575    print(out_string)
576
577@lldb_type_summary(['IOPMWorkQueue *'])
578@header("")
579def GetIOPMWorkQueueSummary(wq):
580    out_str = ""
581    ioservicepm_header = "{:<20s}{:<4s}{:<4s}{:<4s}{:<4s}\n"
582    iopmrequest_indent = "    "
583    iopmrequest_header = iopmrequest_indent + "{:<20s}{:<6s}{:<20s}{:<20s}{:<12s}{:<12s}{:<20s}{:<20s}{:<20s}\n"
584
585    for next in IterateQueue(wq.fWorkQueue, 'IOServicePM *', 'WorkChain'):
586        out_str += ioservicepm_header.format("IOService", "ps", "ms", "wr", "name")
587        out_str += "0x{:<16x}  {:<2d}  {:<2d}  {:<2d}  {:<s}\n".format(
588            next.Owner, next.CurrentPowerState, next.MachineState, next.WaitReason, next.Name)
589        out_str += iopmrequest_header.format("IOPMRequest", "type", "next_req", "root_req", "work_wait", "free_wait", "arg0", "arg1", "arg2")
590        for request in IterateQueue(next.RequestHead, 'IOPMRequest *', 'fCommandChain'):
591            out_str += iopmrequest_indent
592            out_str += "0x{:<16x}  0x{:<2x}  0x{:<16x}  0x{:<16x}".format(
593                request, request.fRequestType, request.fRequestNext, request.fRequestRoot)
594            out_str += "  0x{:<8x}  0x{:<8x}".format(
595                request.fWorkWaitCount, request.fFreeWaitCount)
596            out_str += "  0x{:<16x}  0x{:<16x}  0x{:<16x}\n".format(
597                request.fArg0, request.fArg1, request.fArg2)
598    return out_str
599
600@lldb_command('showiopmqueues')
601def ShowIOPMQueues(cmd_args=None):
602    """ Show IOKit power management queues and IOPMRequest objects.
603    """
604    print("IOPMWorkQueue 0x{:<16x} ({:<d} IOServicePM)\n".format(
605        kern.globals.gIOPMWorkQueue, kern.globals.gIOPMWorkQueue.fQueueLength))
606    print(GetIOPMWorkQueueSummary(kern.globals.gIOPMWorkQueue))
607
608@lldb_type_summary(['IOService *'])
609@header("")
610def GetIOPMInterest(service):
611    iopm = CastIOKitClass(service.pwrMgt, 'IOServicePM *')
612    if unsigned(iopm) == 0:
613        print("error: no IOServicePM")
614        return
615
616    list = CastIOKitClass(iopm.InterestedDrivers, 'IOPMinformeeList *')
617    out_str = "IOServicePM 0x{:<16x} ({:<d} interest, {:<d} pending ack)\n".format(
618        iopm, list.length, iopm.HeadNotePendingAcks)
619    if list.length == 0:
620        return
621
622    out_str += "    {:<20s}{:<8s}{:<10s}{:<20s}{:<20s}{:<20s}{:<s}\n".format(
623        "informee", "active", "ticks", "notifyTime", "service", "regId", "name")
624    next = CastIOKitClass(list.firstItem, 'IOPMinformee *')
625    while unsigned(next) != 0:
626        driver = CastIOKitClass(next.whatObject, 'IOService *')
627        name = GetRegistryEntryName(driver)
628        reg_id = CastIOKitClass(driver, 'IORegistryEntry *').reserved.fRegistryEntryID;
629        out_str += "    0x{:<16x}  {:<6s}  {:<8d}  0x{:<16x}  0x{:<16x}  0x{:<16x}  {:<s}\n".format(
630            next, "Yes" if next.active != 0 else "No" , next.timer, next.startTime, next.whatObject, reg_id, name)
631        next = CastIOKitClass(next.nextInList, 'IOPMinformee *')
632    return out_str
633
634@lldb_command('showiopminterest')
635def ShowIOPMInterest(cmd_args=None):
636    """ Show the interested drivers for an IOService.
637        syntax: (lldb) showiopminterest <IOService>
638    """
639    if not cmd_args:
640        print("Please specify the address of the IOService")
641        print(ShowIOPMInterest.__doc__)
642        return
643
644    obj = kern.GetValueFromAddress(cmd_args[0], 'IOService *')
645    print(GetIOPMInterest(obj))
646
647@lldb_command("showinterruptvectors")
648def ShowInterruptVectorInfo(cmd_args=None):
649    """
650    Shows interrupt vectors.
651    """
652
653    # Constants
654    kInterruptTriggerModeMask  = 0x01
655    kInterruptTriggerModeEdge  = 0x00
656    kInterruptTriggerModeLevel = kInterruptTriggerModeMask
657    kInterruptPolarityMask     = 0x02
658    kInterruptPolarityHigh     = 0x00
659    kInterruptPolarityLow      = kInterruptPolarityMask
660    kInterruptShareableMask    = 0x04
661    kInterruptNotShareable     = 0x00
662    kInterruptIsShareable      = kInterruptShareableMask
663    kIOInterruptTypePCIMessaged = 0x00010000
664
665    # Get all interrupt controllers
666    interrupt_controllers = list(SearchInterruptControllerDrivers())
667
668    print("Interrupt controllers: ")
669    for ic in interrupt_controllers:
670        print("  {}".format(ic))
671    print("")
672
673    # Iterate over all entries in the registry
674    for entry in GetMatchingEntries(lambda _: True):
675        # Get the name of the entry
676        entry_name = GetRegistryEntryName(entry)
677
678        # Get the location of the entry
679        entry_location = GetRegistryEntryLocationInPlane(entry, kern.globals.gIOServicePlane)
680        if entry_location is None:
681            entry_location = ""
682        else:
683            entry_location = "@" + entry_location
684
685        # Get the interrupt properties
686        (msi_mode, vectorDataList, vectorContList) = GetRegistryEntryInterruptProperties(entry)
687        should_print = False
688        out_str = ""
689        for (vector_data, vector_cont) in zip(vectorDataList, vectorContList):
690            # vector_cont is the name of the interrupt controller. Find the matching controller from
691            # the list of controllers obtained earlier
692            matching_ics = [ic for ic in interrupt_controllers if ic.name == vector_cont]
693
694            if len(matching_ics) > 0:
695                should_print = True
696                # Take the first match
697                matchingIC = matching_ics[0]
698
699                # Use the vector_data to determine the vector and any flags
700                data_ptr = vector_data.data
701                data_length = vector_data.length
702
703                # Dereference vector_data as a uint32_t * and add the base vector number
704                gsi = unsigned(dereference(Cast(data_ptr, 'uint32_t *')))
705                gsi += matchingIC.base_vector_number
706
707                # If data_length is >= 8 then vector_data contains interrupt flags
708                if data_length >= 8:
709                    # Add sizeof(uint32_t) to data_ptr to get the flags pointer
710                    flags_ptr = kern.GetValueFromAddress(unsigned(data_ptr) + sizeof("uint32_t"))
711                    flags = unsigned(dereference(Cast(flags_ptr, 'uint32_t *')))
712                    out_str += "  +----- [Interrupt Controller {ic}] vector {gsi}, {trigger_level}, {active}, {shareable}{messaged}\n" \
713                            .format(ic=matchingIC.name, gsi=hex(gsi),
714                                    trigger_level="level trigger" if flags & kInterruptTriggerModeLevel else "edge trigger",
715                                    active="active low" if flags & kInterruptPolarityLow else "active high",
716                                    shareable="shareable" if flags & kInterruptIsShareable else "exclusive",
717                                    messaged=", messaged" if flags & kIOInterruptTypePCIMessaged else "")
718                else:
719                    out_str += "  +----- [Interrupt Controller {ic}] vector {gsi}\n".format(ic=matchingIC.name, gsi=hex(gsi))
720        if should_print:
721            print("[ {entry_name}{entry_location} ]{msi_mode}\n{out_str}" \
722                .format(entry_name=entry_name,
723                        entry_location=entry_location,
724                        msi_mode=" - MSIs enabled" if msi_mode else "",
725                        out_str=out_str))
726
727@lldb_command("showiokitclasshierarchy")
728def ShowIOKitClassHierarchy(cmd_args=None):
729    """
730    Show class hierarchy for a IOKit class
731    """
732    if not cmd_args:
733        print("Usage: showiokitclasshierarchy <IOKit class name>")
734        return
735
736    class_name = cmd_args[0]
737    metaclasses = GetMetaClasses()
738    if class_name not in metaclasses:
739        print("Class {} does not exist".format(class_name))
740        return
741    metaclass = metaclasses[class_name]
742
743    # loop over superclasses
744    hierarchy = []
745    current_metaclass = metaclass
746    while current_metaclass is not None:
747        hierarchy.insert(0, current_metaclass)
748        current_metaclass = current_metaclass.superclass()
749
750    for (index, mc) in enumerate(hierarchy):
751        indent = ("    " * index) + "+---"
752        print("{}[ {} ] {}".format(indent, str(mc.className()), str(mc.data())))
753
754
755######################################
756#  Helper routines
757######################################
758def ShowRegistryEntryRecurse(entry, prefix, printProps):
759    """ prints registry entry summary and recurses through all its children.
760    """
761    # Setup
762    global plane
763    out_string = ""
764    plen = (len(prefix)//2)
765    registryTable = entry.fRegistryTable
766    propertyTable = entry.fPropertyTable
767
768    # Print entry details
769    print("{0:s}{1:s}".format(prefix, GetRegistryEntrySummary(entry)))
770    # Printing large property tables make it look like lldb is 'stuck'
771    if printProps:
772        print(GetRegDictionary(propertyTable, prefix + "  | "))
773
774    # Recurse
775    if plane is None:
776        childKey = kern.globals.gIOServicePlane.keys[1]
777    else:
778        childKey = plane.keys[1]
779    childArray = LookupKeyInOSDict(registryTable, childKey)
780    if childArray is not None:
781        idx = 0
782        ca = CastIOKitClass(childArray, 'OSArray *')
783        count = unsigned(ca.count)
784        while idx < count:
785            if plen != 0 and plen != 1 and (plen & (plen - 1)) == 0:
786                ShowRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), prefix + "| ", printProps)
787            else:
788                ShowRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), prefix + "  ", printProps)
789            idx += 1
790
791def FindRegistryEntryRecurse(entry, search_name, stopAfterFirst):
792    """ Checks if given registry entry's name matches the search_name we're looking for
793        If yes, it prints the entry's summary and then recurses through its children
794        If no, it does nothing and recurses through its children
795    """
796    # Setup
797    global plane
798    registryTable = entry.fRegistryTable
799    propertyTable = entry.fPropertyTable
800
801    # Compare
802    name = None
803    name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
804    if name is None:
805        name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
806    if name is None:
807        name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
808
809    if name is not None:
810        if str(CastIOKitClass(name, 'OSString *').string) == search_name:
811            print(GetRegistryEntrySummary(entry))
812            if stopAfterFirst is True:
813                return True
814    elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name:
815        name = CastIOKitClass(entry, 'IOService *').pwrMgt.Name
816        if str(name) == search_name:
817            print(GetRegistryEntrySummary(entry))
818            if stopAfterFirst is True:
819                return True
820
821    # Recurse
822    if plane is None:
823        childKey = kern.globals.gIOServicePlane.keys[1]
824    else:
825        childKey = plane.keys[1]
826    childArray = LookupKeyInOSDict(registryTable, childKey)
827    if childArray is not None:
828        idx = 0
829        ca = CastIOKitClass(childArray, 'OSArray *')
830        count = unsigned(ca.count)
831        while idx < count:
832            if FindRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), search_name, stopAfterFirst) is True:
833                return True
834            idx += 1
835    return False
836
837def FindRegistryObjectRecurse(entry, search_name):
838    """ Checks if given registry entry's name matches the search_name we're looking for
839        If yes, return the entry
840        If no, it does nothing and recurses through its children
841        Implicitly stops after finding the first entry
842    """
843    # Setup
844    global plane
845    registryTable = entry.fRegistryTable
846    propertyTable = entry.fPropertyTable
847
848    # Compare
849    name = None
850    name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
851    if name is None:
852        name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
853    if name is None:
854        name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
855
856    if name is not None:
857        if str(CastIOKitClass(name, 'OSString *').string) == search_name:
858            return entry
859    elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name:
860        name = CastIOKitClass(entry, 'IOService *').pwrMgt.Name
861        if str(name) == search_name:
862            return entry
863
864    # Recurse
865    if plane is None:
866        childKey = kern.globals.gIOServicePlane.keys[1]
867    else:
868        childKey = plane.keys[1]
869    childArray = LookupKeyInOSDict(registryTable, childKey)
870    if childArray is not None:
871        ca = CastIOKitClass(childArray, 'OSArray *')
872        for idx in range(ca.count):
873            registry_object = FindRegistryObjectRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), search_name)
874            if not registry_object or int(registry_object) == int(0):
875                continue
876            else:
877                return registry_object
878    return None
879
880def CompareStringToOSSymbol(string, os_sym):
881    """
882    Lexicographically compare python string to OSSymbol
883    Params:
884    string - python string
885    os_sym - OSSymbol
886
887    Returns:
888    0 if string == os_sym
889    1 if string > os_sym
890    -1 if string < os_sym
891    """
892    os_sym_str = GetString(os_sym)
893    if string > os_sym_str:
894        return 1
895    elif string < os_sym_str:
896        return -1
897    else:
898        return 0
899
900class IOKitMetaClass(object):
901    """
902    A class that represents a IOKit metaclass. This is used to represent the
903    IOKit inheritance hierarchy.
904    """
905
906    def __init__(self, meta):
907        """
908        Initialize a IOKitMetaClass object.
909
910        Args:
911            meta (core.cvalue.value): A LLDB value representing a
912                OSMetaClass *.
913        """
914        self._meta = meta
915        self._superclass = None
916
917    def data(self):
918        return self._meta
919
920    def setSuperclass(self, superclass):
921        """
922        Set the superclass for this metaclass.
923
924        Args:
925            superclass (core.cvalue.value): A LLDB value representing a
926                OSMetaClass *.
927        """
928        self._superclass = superclass
929
930    def superclass(self):
931        """
932        Get the superclass for this metaclass (set by the setSuperclass method).
933
934        Returns:
935            core.cvalue.value: A LLDB value representing a OSMetaClass *.
936        """
937        return self._superclass
938
939    def className(self):
940        """
941        Get the name of the class this metaclass represents.
942
943        Returns:
944            str: The class name
945        """
946        return self._meta.className.string
947
948    def inheritsFrom(self, other):
949        """
950        Check if the class represented by this metaclass inherits from a class
951        represented by another metaclass.
952
953        Args:
954            other (IOKitMetaClass): The other metaclass
955
956        Returns:
957            bool: Returns True if this class inherits from the other class and
958                False otherwise.
959        """
960        current = self
961        while current is not None:
962            if current == other:
963                return True
964            else:
965                current = current.superclass()
966
967
968def GetRegistryEntryClassName(entry):
969    """
970    Get the class name of a registry entry.
971
972    Args:
973        entry (core.cvalue.value): A LLDB value representing a
974            IORegistryEntry *.
975
976    Returns:
977        str: The class name of the entry or None if a class name could not be
978            found.
979    """
980    # Check using IOClass key
981    result = LookupKeyInOSDict(entry.fPropertyTable, kern.globals.gIOClassKey)
982    if result is not None:
983        return GetString(result).replace("\"", "")
984    else:
985        # Use the vtable of the entry to determine the concrete type
986        vt = dereference(Cast(entry, 'uintptr_t *')) - 2 * sizeof('uintptr_t')
987        vt = kern.StripKernelPAC(vt)
988        vtype = kern.SymbolicateFromAddress(vt)
989        if len(vtype) > 0:
990            vtableName = vtype[0].GetName()
991            return vtableName[11:] # strip off "vtable for "
992        else:
993            return None
994
995
996def GetRegistryEntryName(entry):
997    """
998    Get the name of a registry entry.
999
1000    Args:
1001        entry (core.cvalue.value): A LLDB value representing a
1002            IORegistryEntry *.
1003
1004    Returns:
1005        str: The name of the entry or None if a name could not be found.
1006    """
1007    name = None
1008
1009    # First check the IOService plane nameKey
1010    result = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIOServicePlane.nameKey)
1011    if result is not None:
1012        name = GetString(result)
1013
1014    # Check the global IOName key
1015    if name is None:
1016        result = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIONameKey)
1017        if result is not None:
1018            name = GetString(result)
1019
1020    # Check the IOClass key
1021    if name is None:
1022        result = LookupKeyInOSDict(entry.fPropertyTable, kern.globals.gIOClassKey)
1023        if result is not None:
1024            name = GetString(result)
1025
1026    # Remove extra quotes
1027    if name is not None:
1028        return name.replace("\"", "")
1029    else:
1030        return GetRegistryEntryClassName(entry)
1031
1032
1033def GetRegistryEntryLocationInPlane(entry, plane):
1034    """
1035    Get the registry entry location in a IOKit plane.
1036
1037    Args:
1038        entry (core.cvalue.value): A LLDB value representing a
1039            IORegistryEntry *.
1040        plane: An IOKit plane such as kern.globals.gIOServicePlane.
1041
1042    Returns:
1043        str: The location of the entry or None if a location could not be
1044            found.
1045    """
1046    # Check the plane's pathLocationKey
1047    sym = LookupKeyInOSDict(entry.fRegistryTable, plane.pathLocationKey)
1048
1049    # Check the global IOLocation key
1050    if sym is None:
1051        sym = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIOLocationKey)
1052    if sym is not None:
1053        return GetString(sym).replace("\"", "")
1054    else:
1055        return None
1056
1057
1058@caching.cache_dynamically
1059def GetMetaClasses(target=None):
1060    """
1061    Enumerate all IOKit metaclasses. Uses dynamic caching.
1062
1063    Returns:
1064        Dict[str, IOKitMetaClass]: A dictionary mapping each metaclass name to
1065            a IOKitMetaClass object representing the metaclass.
1066    """
1067
1068    # This method takes a while, so it prints a progress indicator
1069    print("Enumerating IOKit metaclasses: ")
1070
1071    do_progress = os.isatty(sys.__stderr__.fileno())
1072
1073    # Iterate over all classes present in sAllClassesDict
1074    count = unsigned(kern.globals.sAllClassesDict.count)
1075    metaclasses_by_address = {}
1076    for idx in range(count):
1077        if do_progress and idx % 10 == 0:
1078            sys.stderr.write("\033[K  {} metaclass found...\r".format(idx))
1079
1080        # Address of metaclass
1081        address = kern.globals.sAllClassesDict.dictionary[idx].value
1082
1083        # Create IOKitMetaClass and store in dict
1084        metaclasses_by_address[int(address)] = IOKitMetaClass(CastIOKitClass(kern.globals.sAllClassesDict.dictionary[idx].value, 'OSMetaClass *'))
1085
1086    # At this point, each metaclass is independent of each other. We don't have superclass links set up yet.
1087
1088    for address, metaclass in metaclasses_by_address.items():
1089        # Get the address of the superclass using the superClassLink in IOMetaClass
1090        superclass_address = int(metaclass.data().superClassLink)
1091
1092        # Skip null superclass
1093        if superclass_address == 0:
1094            continue
1095
1096        # Find the superclass object in the dict
1097        if superclass_address in metaclasses_by_address:
1098            metaclass.setSuperclass(metaclasses_by_address[superclass_address])
1099        else:
1100            print("warning: could not find superclass for {}".format(str(metaclass.data())))
1101
1102    # This method returns a dictionary mapping each class name to the associated metaclass object
1103    metaclasses_by_name = {}
1104    for idx, (_, metaclass) in enumerate(metaclasses_by_address.items()):
1105        if do_progress and idx % 10 == 0:
1106            sys.stderr.write("\033[K  {} metaclass indexed...\r".format(idx))
1107
1108        metaclasses_by_name[str(metaclass.className())] = metaclass
1109
1110    print("  Indexed {} IOKit metaclasses.".format(count))
1111    return metaclasses_by_name
1112
1113
1114def GetMatchingEntries(matcher):
1115    """
1116    Iterate over the IOKit registry and find entries that match specific
1117        criteria.
1118
1119    Args:
1120        matcher (function): A matching function that returns True for a match
1121            and False otherwise.
1122
1123    Yields:
1124        core.cvalue.value: LLDB values that represent IORegistryEntry * for
1125            each registry entry found.
1126    """
1127
1128    # Perform a BFS over the IOKit registry tree
1129    bfs_queue = deque()
1130    bfs_queue.append(kern.globals.gRegistryRoot)
1131    while len(bfs_queue) > 0:
1132        # Dequeue an entry
1133        entry = bfs_queue.popleft()
1134
1135        # Check if entry matches
1136        if matcher(entry):
1137            yield entry
1138
1139        # Find children of this entry and enqueue them
1140        child_array = LookupKeyInOSDict(entry.fRegistryTable, kern.globals.gIOServicePlane.keys[1])
1141        if child_array is not None:
1142            idx = 0
1143            ca = CastIOKitClass(child_array, 'OSArray *')
1144            count = unsigned(ca.count)
1145            while idx < count:
1146                bfs_queue.append(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'))
1147                idx += 1
1148
1149
1150def FindMatchingServices(matching_name):
1151    """
1152    Finds registry entries that match the given string. Works similarly to:
1153
1154    io_iterator_t iter;
1155    IOServiceGetMatchingServices(..., IOServiceMatching(matching_name), &iter);
1156    while (( io_object_t next = IOIteratorNext(iter))) { ... }
1157
1158    Args:
1159        matching_name (str): The class name to search for.
1160
1161    Yields:
1162        core.cvalue.value: LLDB values that represent IORegistryEntry * for
1163            each registry entry found.
1164    """
1165
1166    # Check if the argument is valid
1167    metaclasses = GetMetaClasses()
1168    if matching_name not in metaclasses:
1169        return
1170    matching_metaclass = metaclasses[matching_name]
1171
1172    # An entry matches if it inherits from matching_metaclass
1173    def matcher(entry):
1174        # Get the class name of the entry and the associated metaclass
1175        entry_name = GetRegistryEntryClassName(entry)
1176        if entry_name in metaclasses:
1177            entry_metaclass = metaclasses[entry_name]
1178            return entry_metaclass.inheritsFrom(matching_metaclass)
1179        else:
1180            return False
1181
1182    # Search for entries
1183    for entry in GetMatchingEntries(matcher):
1184        yield entry
1185
1186
1187def GetRegistryEntryParent(entry, iokit_plane=None):
1188    """
1189    Gets the parent entry of a registry entry.
1190
1191    Args:
1192        entry (core.cvalue.value): A LLDB value representing a
1193            IORegistryEntry *.
1194        iokit_plane (core.cvalue.value, optional): A LLDB value representing a
1195            IORegistryPlane *. By default, this method uses the IOService
1196            plane.
1197
1198    Returns:
1199        core.cvalue.value: A LLDB value representing a IORegistryEntry* that
1200            is the parent entry of the entry argument in the specified plane.
1201            Returns None if no entry could be found.
1202    """
1203    kParentSetIndex = 0
1204    parent_key = None
1205    if iokit_plane is None:
1206        parent_key = kern.globals.gIOServicePlane.keys[kParentSetIndex]
1207    else:
1208        parent_key = plane.keys[kParentSetIndex]
1209    parent_array = LookupKeyInOSDict(entry.fRegistryTable, parent_key)
1210    parent_entry = None
1211    if parent_array is not None:
1212        idx = 0
1213        ca = CastIOKitClass(parent_array, 'OSArray *')
1214        count = unsigned(ca.count)
1215        if count > 0:
1216            parent_entry = CastIOKitClass(ca.array[0], 'IORegistryEntry *')
1217    return parent_entry
1218
1219
1220def GetRegistryEntryInterruptProperties(entry):
1221    """
1222    Get the interrupt properties of a registry entry.
1223
1224    Args:
1225        entry (core.cvalue.value): A LLDB value representing a IORegistryEntry *.
1226
1227    Returns:
1228        (bool, List[core.cvalue.value], List[str]): A tuple with the following
1229            fields:
1230                - First field (bool): Whether this entry has a non-null
1231                    IOPCIMSIMode.
1232                - Second field (List[core.cvalue.value]): A list of LLDB values
1233                    representing OSData *. The OSData* pointer points to
1234                    interrupt vector data.
1235                - Third field (List[str]): A list of strings representing the
1236                    interrupt controller names from the
1237                    IOInterruptControllers property.
1238    """
1239    INTERRUPT_SPECIFIERS_PROPERTY = "IOInterruptSpecifiers"
1240    INTERRUPT_CONTROLLERS_PROPERTY = "IOInterruptControllers"
1241    MSI_MODE_PROPERTY = "IOPCIMSIMode"
1242
1243    # Check IOInterruptSpecifiers
1244    interrupt_specifiers = LookupKeyInPropTable(entry.fPropertyTable, INTERRUPT_SPECIFIERS_PROPERTY)
1245    if interrupt_specifiers is not None:
1246        interrupt_specifiers = CastIOKitClass(interrupt_specifiers, 'OSArray *')
1247
1248    # Check IOInterruptControllers
1249    interrupt_controllers = LookupKeyInPropTable(entry.fPropertyTable, INTERRUPT_CONTROLLERS_PROPERTY)
1250    if interrupt_controllers is not None:
1251        interrupt_controllers = CastIOKitClass(interrupt_controllers, 'OSArray *')
1252
1253    # Check MSI mode
1254    msi_mode = LookupKeyInPropTable(entry.fPropertyTable, MSI_MODE_PROPERTY)
1255
1256    result_vector_data = []
1257    result_vector_cont = []
1258    if interrupt_specifiers is not None and interrupt_controllers is not None:
1259        interrupt_specifiers_array_count = unsigned(interrupt_specifiers.count)
1260        interrupt_controllers_array_count = unsigned(interrupt_controllers.count)
1261        # The array lengths should be the same
1262        if interrupt_specifiers_array_count == interrupt_controllers_array_count and interrupt_specifiers_array_count > 0:
1263            idx = 0
1264            while idx < interrupt_specifiers_array_count:
1265                # IOInterruptSpecifiers is an array of OSData *
1266                vector_data = CastIOKitClass(interrupt_specifiers.array[idx], "OSData *")
1267
1268                # IOInterruptControllers is an array of OSString *
1269                vector_cont = GetString(interrupt_controllers.array[idx])
1270
1271                result_vector_data.append(vector_data)
1272                result_vector_cont.append(vector_cont)
1273                idx += 1
1274
1275    return (msi_mode is not None, result_vector_data, result_vector_cont)
1276
1277
1278class InterruptControllerDevice(object):
1279    """Represents a IOInterruptController"""
1280
1281    def __init__(self, device, driver, base_vector_number, name):
1282        """
1283        Initialize a InterruptControllerDevice.
1284
1285        Args:
1286            device (core.cvalue.value): The device object.
1287            driver (core.cvalue.value): The driver object.
1288            base_vector_number (int): The base interrupt vector.
1289            name (str): The name of this interrupt controller.
1290
1291        Note:
1292            Use the factory method makeInterruptControllerDevice to validate
1293            properties.
1294        """
1295        self.device = device
1296        self.driver = driver
1297        self.name = name
1298        self.base_vector_number = base_vector_number
1299
1300
1301    def __str__(self):
1302        """
1303        String representation of this InterruptControllerDevice.
1304        """
1305        return " Name {}, base vector = {}, device = {}, driver = {}".format(
1306            self.name, hex(self.base_vector_number), str(self.device), str(self.driver))
1307
1308    @staticmethod
1309    def makeInterruptControllerDevice(device, driver):
1310        """
1311        Factory method to create a InterruptControllerDevice.
1312
1313        Args:
1314            device (core.cvalue.value): The device object.
1315            driver (core.cvalue.value): The driver object.
1316
1317        Returns:
1318            InterruptControllerDevice: Returns an instance of
1319                InterruptControllerDevice or None if the arguments do not have
1320                the required properties.
1321        """
1322        BASE_VECTOR_PROPERTY = "Base Vector Number"
1323        INTERRUPT_CONTROLLER_NAME_PROPERTY = "InterruptControllerName"
1324        base_vector = LookupKeyInPropTable(device.fPropertyTable, BASE_VECTOR_PROPERTY)
1325        if base_vector is None:
1326            base_vector = LookupKeyInPropTable(driver.fPropertyTable, BASE_VECTOR_PROPERTY)
1327        device_name = LookupKeyInPropTable(device.fPropertyTable, INTERRUPT_CONTROLLER_NAME_PROPERTY)
1328        if device_name is None:
1329            device_name = LookupKeyInPropTable(driver.fPropertyTable, INTERRUPT_CONTROLLER_NAME_PROPERTY)
1330
1331        if device_name is not None:
1332            # Some interrupt controllers do not have a base vector number. Assume it is 0.
1333            base_vector_number = 0
1334            if base_vector is not None:
1335                base_vector_number = unsigned(GetNumber(base_vector))
1336            device_name = GetString(device_name)
1337            # Construct object and return
1338            return InterruptControllerDevice(device, driver, base_vector_number, device_name)
1339        else:
1340            # error case
1341            return None
1342
1343
1344def SearchInterruptControllerDrivers():
1345    """
1346    Search the IOKit registry for entries that match IOInterruptController.
1347
1348    Yields:
1349        core.cvalue.value: A LLDB value representing a IORegistryEntry * that
1350        inherits from IOInterruptController.
1351    """
1352    for entry in FindMatchingServices("IOInterruptController"):
1353        # Get parent
1354        parent = GetRegistryEntryParent(entry)
1355
1356        # Make the interrupt controller object
1357        ic = InterruptControllerDevice.makeInterruptControllerDevice(parent, entry)
1358
1359        # Yield object
1360        if ic is not None:
1361            yield ic
1362
1363
1364def LookupKeyInOSDict(osdict, key, comparer = None):
1365    """ Returns the value corresponding to a given key in a OSDictionary
1366        Returns None if the key was not found
1367    """
1368    if not osdict:
1369        return
1370    count = unsigned(osdict.count)
1371    result = None
1372    idx = 0
1373
1374    while idx < count and result is None:
1375        if comparer is not None:
1376            if comparer(key, osdict.dictionary[idx].key) == 0:
1377                result = osdict.dictionary[idx].value
1378        elif key == osdict.dictionary[idx].key:
1379            result = osdict.dictionary[idx].value
1380        idx += 1
1381    return result
1382
1383def LookupKeyInPropTable(propertyTable, key_str):
1384    """ Returns the value corresponding to a given key from a registry entry's property table
1385        Returns None if the key was not found
1386        The property that is being searched for is specified as a string in key_str
1387    """
1388    if not propertyTable:
1389        return
1390    count = unsigned(propertyTable.count)
1391    result = None
1392    idx = 0
1393    while idx < count and result is None:
1394        if key_str == str(propertyTable.dictionary[idx].key.string):
1395            result = propertyTable.dictionary[idx].value
1396        idx += 1
1397    return result
1398
1399def GetRegDictionary(osdict, prefix):
1400    """ Returns a specially formatted string summary of the given OSDictionary
1401        This is done in order to pretty-print registry property tables in showregistry
1402        and other macros
1403    """
1404    out_string = prefix + "{\n"
1405    idx = 0
1406    count = unsigned(osdict.count)
1407
1408    while idx < count:
1409        out_string += prefix + "  " + GetObjectSummary(osdict.dictionary[idx].key) + " = " + GetObjectSummary(osdict.dictionary[idx].value) + "\n"
1410        idx += 1
1411    out_string += prefix + "}\n"
1412    return out_string
1413
1414def GetString(string):
1415    """ Returns the python string representation of a given OSString
1416    """
1417    out_string = "{0:s}".format(CastIOKitClass(string, 'OSString *').string)
1418    return out_string
1419
1420def GetNumber(num):
1421    out_string = "{0:d}".format(CastIOKitClass(num, 'OSNumber *').value)
1422    return out_string
1423
1424def GetBoolean(b):
1425    """ Shows info about a given OSBoolean
1426    """
1427    out_string = ""
1428    if b == kern.globals.gOSBooleanFalse:
1429        out_string += "No"
1430    else:
1431        out_string += "Yes"
1432    return out_string
1433
1434def GetMetaClass(mc):
1435    """ Shows info about a given OSSymbol
1436    """
1437    out_string = "{0: <5d}x {1: >5d} bytes {2:s}\n".format(mc.instanceCount, mc.classSize, mc.className.string)
1438    return out_string
1439
1440def GetArray(arr):
1441    """ Returns a string containing info about a given OSArray
1442    """
1443    out_string = ""
1444    idx = 0
1445    count = unsigned(arr.count)
1446
1447    while idx < count:
1448        obj = arr.array[idx]
1449        idx += 1
1450        out_string += GetObjectSummary(obj)
1451        if idx < unsigned(arr.count):
1452            out_string += ","
1453    return out_string
1454
1455def GetDictionary(d):
1456    """ Returns a string containing info about a given OSDictionary
1457    """
1458    if d is None:
1459        return ""
1460    out_string = "{\n"
1461    idx = 0
1462    count = unsigned(d.count)
1463
1464    while idx < count:
1465        key = d.dictionary[idx].key
1466        value = d.dictionary[idx].value
1467        out_string += "    \"{}\" = {}\n".format(GetString(key), GetObjectSummary(value))
1468        idx += 1
1469    out_string += "}"
1470    return out_string
1471
1472def GetSet(se):
1473    """ Returns a string containing info about a given OSSet
1474    """
1475    out_string = "[" + GetArray(se.members) + "]"
1476    return out_string
1477
1478def ReadIOPortInt(addr, numbytes, lcpu):
1479    """ Prints results after reading a given ioport
1480    """
1481    result = 0xBAD10AD
1482
1483    if "kdp" != GetConnectionProtocol():
1484        print("Target is not connected over kdp. Nothing to do here.")
1485        return
1486
1487    # Set up the manual KDP packet
1488    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
1489    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
1490    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
1491    if not WriteInt32ToMemoryAddress(0, input_address):
1492        print("0x{0: <4x}: 0x{1: <1x}".format(addr, result))
1493        return
1494
1495    kdp_pkt_size = GetType('kdp_readioport_req_t').GetByteSize()
1496    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
1497        print("0x{0: <4x}: 0x{1: <1x}".format(addr, result))
1498        return
1499
1500    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_readioport_req_t *')
1501
1502    header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_READIOPORT'), length = kdp_pkt_size)
1503
1504    if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and
1505        WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and
1506        WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and
1507        WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))) and
1508        WriteInt32ToMemoryAddress(1, input_address)
1509        ):
1510
1511        result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_readioport_reply_t *')
1512
1513        if(result_pkt.error == 0):
1514            if numbytes == 1:
1515                result = dereference(Cast(addressof(result_pkt.data), 'uint8_t *'))
1516            elif numbytes == 2:
1517                result = dereference(Cast(addressof(result_pkt.data), 'uint16_t *'))
1518            elif numbytes == 4:
1519                result = dereference(Cast(addressof(result_pkt.data), 'uint32_t *'))
1520
1521    print("{0: <#6x}: {1:#0{2}x}".format(addr, result, (numbytes*2)+2))
1522
1523def WriteIOPortInt(addr, numbytes, value, lcpu):
1524    """ Writes 'value' into ioport specified by 'addr'. Prints errors if it encounters any
1525    """
1526    if "kdp" != GetConnectionProtocol():
1527        print("Target is not connected over kdp. Nothing to do here.")
1528        return
1529
1530    # Set up the manual KDP packet
1531    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
1532    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
1533    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
1534    if not WriteInt32ToMemoryAddress(0, input_address):
1535        print("error writing {0: #x} to port {1: <#6x}: failed to write 0 to input_address".format(value, addr))
1536        return
1537
1538    kdp_pkt_size = GetType('kdp_writeioport_req_t').GetByteSize()
1539    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
1540        print("error writing {0: #x} to port {1: <#6x}: failed to write kdp_pkt_size".format(value, addr))
1541        return
1542
1543    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_writeioport_req_t *')
1544
1545    header_value = GetKDPPacketHeaderInt(request=GetEnumValue('kdp_req_t::KDP_WRITEIOPORT'), length = kdp_pkt_size)
1546
1547    if( WriteInt64ToMemoryAddress((header_value), int(addressof(kgm_pkt.hdr))) and
1548        WriteInt16ToMemoryAddress(addr, int(addressof(kgm_pkt.address))) and
1549        WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and
1550        WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu)))
1551        ):
1552        if numbytes == 1:
1553            if not WriteInt8ToMemoryAddress(value, int(addressof(kgm_pkt.data))):
1554                print("error writing {0: #x} to port {1: <#6x}: failed to write 8 bit data".format(value, addr))
1555                return
1556        elif numbytes == 2:
1557            if not WriteInt16ToMemoryAddress(value, int(addressof(kgm_pkt.data))):
1558                print("error writing {0: #x} to port {1: <#6x}: failed to write 16 bit data".format(value, addr))
1559                return
1560        elif numbytes == 4:
1561            if not WriteInt32ToMemoryAddress(value, int(addressof(kgm_pkt.data))):
1562                print("error writing {0: #x} to port {1: <#6x}: failed to write 32 bit data".format(value, addr))
1563                return
1564        if not WriteInt32ToMemoryAddress(1, input_address):
1565            print("error writing {0: #x} to port {1: <#6x}: failed to write to input_address".format(value, addr))
1566            return
1567
1568        result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_writeioport_reply_t *')
1569
1570        # Done with the write
1571        if(result_pkt.error == 0):
1572            print("Writing {0: #x} to port {1: <#6x} was successful".format(value, addr))
1573    else:
1574        print("error writing {0: #x} to port {1: <#6x}".format(value, addr))
1575
1576@lldb_command('showinterruptcounts')
1577def showinterruptcounts(cmd_args=None):
1578    """ Shows event source based interrupt counts by nub name and interrupt index.
1579        Does not cover interrupts that are not event source based.  Will report 0
1580        if interrupt accounting is disabled.
1581    """
1582
1583    header_format = "{0: <20s} {1: >5s} {2: >20s}"
1584    content_format = "{0: <20s} {1: >5d} {2: >20d}"
1585
1586    print(header_format.format("Name", "Index", "Count"))
1587
1588    for i in kern.interrupt_stats:
1589        owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *')
1590        nub = CastIOKitClass(owner.provider, 'IORegistryEntry *')
1591        name = None
1592
1593        # To uniquely identify an interrupt, we need the nub name and the index.  The index
1594        # is stored with the stats object, but we need to retrieve the name.
1595
1596        registryTable = nub.fRegistryTable
1597        propertyTable = nub.fPropertyTable
1598
1599        name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
1600        if name is None:
1601            name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
1602        if name is None:
1603            name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
1604
1605        if name is None:
1606            nub_name = "Unknown"
1607        else:
1608            nub_name = GetString(CastIOKitClass(name, 'OSString *'))
1609
1610        # We now have everything we need; spew the requested data.
1611
1612        interrupt_index = i.interruptIndex
1613        first_level_count = i.interruptStatistics[0]
1614
1615        print(content_format.format(nub_name, interrupt_index, first_level_count))
1616
1617    return True
1618
1619@lldb_command('showinterruptstats')
1620def showinterruptstats(cmd_args=None):
1621    """ Shows event source based interrupt statistics by nub name and interrupt index.
1622        Does not cover interrupts that are not event source based.  Will report 0
1623        if interrupt accounting is disabled, or if specific statistics are disabled.
1624        Time is reported in ticks of mach_absolute_time.  Statistics are:
1625
1626        Interrupt Count: Number of times the interrupt context handler was run
1627        Interrupt Time: Total time spent in the interrupt context handler (if any)
1628        Workloop Count: Number of times the kernel context handler was run
1629        Workloop CPU Time: Total CPU time spent running the kernel context handler
1630        Workloop Time: Total time spent running the kernel context handler
1631    """
1632
1633    header_format = "{0: <20s} {1: >5s} {2: >20s} {3: >20s} {4: >20s} {5: >20s} {6: >20s} {7: >20s} {8: >20s} {9: >20s}"
1634    content_format = "{0: <20s} {1: >5d} {2: >20d} {3: >20d} {4: >20d} {5: >20d} {6: >20d} {7: >20d} {8: >20d} {9: >#20x}"
1635
1636    print(header_format.format("Name", "Index", "Interrupt Count", "Interrupt Time", "Avg Interrupt Time", "Workloop Count", "Workloop CPU Time", "Workloop Time", "Avg Workloop Time", "Owner"))
1637
1638    for i in kern.interrupt_stats:
1639        owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *')
1640        nub = CastIOKitClass(owner.provider, 'IORegistryEntry *')
1641        name = None
1642
1643        # To uniquely identify an interrupt, we need the nub name and the index.  The index
1644        # is stored with the stats object, but we need to retrieve the name.
1645
1646        registryTable = nub.fRegistryTable
1647        propertyTable = nub.fPropertyTable
1648
1649        name = LookupKeyInOSDict(registryTable, kern.globals.gIOServicePlane.nameKey)
1650        if name is None:
1651            name = LookupKeyInOSDict(registryTable, kern.globals.gIONameKey)
1652        if name is None:
1653            name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey)
1654
1655        if name is None:
1656            nub_name = "Unknown"
1657        else:
1658            nub_name = GetString(CastIOKitClass(name, 'OSString *'))
1659
1660        # We now have everything we need; spew the requested data.
1661
1662        interrupt_index = i.interruptIndex
1663        first_level_count = i.interruptStatistics[0]
1664        second_level_count = i.interruptStatistics[1]
1665        first_level_time = i.interruptStatistics[2]
1666        second_level_cpu_time = i.interruptStatistics[3]
1667        second_level_system_time = i.interruptStatistics[4]
1668
1669        avg_first_level_time = 0
1670        if first_level_count != 0:
1671            avg_first_level_time = first_level_time // first_level_count
1672
1673        avg_second_level_time = 0
1674        if second_level_count != 0:
1675            avg_second_level_time = second_level_system_time // second_level_count
1676
1677        print(content_format.format(nub_name, interrupt_index, first_level_count, first_level_time, avg_first_level_time,
1678            second_level_count, second_level_cpu_time, second_level_system_time, avg_second_level_time, owner))
1679
1680    return True
1681
1682def GetRegistryPlane(plane_name):
1683    """
1684    Given plane_name, returns IORegistryPlane * object or None if there's no such registry plane
1685    """
1686    return LookupKeyInOSDict(kern.globals.gIORegistryPlanes, plane_name, CompareStringToOSSymbol)
1687
1688def DecodePreoslogSource(source):
1689    """
1690    Given preoslog source, return a matching string representation
1691    """
1692    source_to_str = {0 : "iboot"}
1693    if source in source_to_str:
1694        return source_to_str[source]
1695    return "UNKNOWN"
1696
1697def GetPreoslogHeader():
1698    """
1699    Scan IODeviceTree for preoslog and return a python representation of it
1700    """
1701    edt_plane = GetRegistryPlane("IODeviceTree")
1702    if edt_plane is None:
1703        print("Couldn't obtain a pointer to IODeviceTree")
1704        return None
1705
1706    # Registry API functions operate on "plane" global variable
1707    global plane
1708    prev_plane = plane
1709    plane = edt_plane
1710    chosen = FindRegistryObjectRecurse(kern.globals.gRegistryRoot, "chosen")
1711    if chosen is None:
1712        print("Couldn't obtain /chosen IORegistryEntry")
1713        return None
1714
1715    memory_map = FindRegistryObjectRecurse(chosen, "memory-map")
1716    if memory_map is None:
1717        print("Couldn't obtain memory-map from /chosen")
1718        return None
1719
1720    plane = prev_plane
1721
1722    mm_preoslog = LookupKeyInOSDict(memory_map.fPropertyTable, "preoslog", CompareStringToOSSymbol)
1723    if mm_preoslog is None:
1724        print("Couldn't find preoslog entry in memory-map")
1725        return None
1726
1727    if mm_preoslog.length != 16:
1728        print("preoslog entry in memory-map is malformed, expected len is 16, given len is {:d}".format(mm_preoslog.length))
1729        return None
1730
1731    data = cast(mm_preoslog.data, "dtptr_t *")
1732    preoslog_paddr = unsigned(data[0])
1733    preoslog_vaddr = kern.PhysToKernelVirt(preoslog_paddr)
1734    preoslog_size = unsigned(data[1])
1735
1736    preoslog_header = PreoslogHeader()
1737
1738    # This structure defnition doesn't exist in xnu
1739    """
1740    typedef struct  __attribute__((packed)) {
1741        char magic[4];
1742        uint32_t size;
1743        uint32_t offset;
1744        uint8_t source;
1745        uint8_t wrapped;
1746        char data[];
1747    } preoslog_header_t;
1748    """
1749    preoslog_header_ptr = kern.GetValueFromAddress(preoslog_vaddr, "uint8_t *")
1750    preoslog_header.magic = preoslog_header_ptr[0:4]
1751    preoslog_header.source = DecodePreoslogSource(unsigned(preoslog_header_ptr[12]))
1752    preoslog_header.wrapped = unsigned(preoslog_header_ptr[13])
1753    preoslog_header_ptr = kern.GetValueFromAddress(preoslog_vaddr, "uint32_t *")
1754    preoslog_header.size = unsigned(preoslog_header_ptr[1])
1755    preoslog_header.offset = unsigned(preoslog_header_ptr[2])
1756
1757    for i in range(len(preoslog_header.valid_magic)):
1758        c = chr(unsigned(preoslog_header.magic[i]))
1759        if c != preoslog_header.valid_magic[i]:
1760            string = "Error: magic doesn't match, expected {:.4s}, given {:.4s}"
1761            print(string.format(preoslog_header.valid_magic, preoslog_header.magic))
1762            return None
1763
1764    if preoslog_header.size != preoslog_size:
1765        string = "Error: size mismatch preoslog_header.size ({}) != preoslog_size ({})"
1766        print(string.format(preoslog_header.size, preoslog_size))
1767        return None
1768
1769    preoslog_data_ptr = kern.GetValueFromAddress(preoslog_vaddr + 14, "char *")
1770    preoslog_header.data = preoslog_data_ptr.GetSBValue().GetPointeeData(0, preoslog_size)
1771    return preoslog_header
1772
1773@lldb_command("showpreoslog")
1774def showpreoslog(cmd_args=None):
1775    """ Display preoslog buffer """
1776
1777    preoslog = GetPreoslogHeader()
1778    if preoslog is None:
1779        print("Error: couldn't obtain preoslog header")
1780        return False
1781
1782    header = "".join([
1783        "----preoslog log header-----\n",
1784        "size - {} bytes\n",
1785        "write offset - {:#x}\n",
1786        "wrapped - {}\n",
1787        "source - {}\n",
1788        "----preoslog log start------"
1789        ])
1790
1791    print(header.format(preoslog.size, preoslog.offset, preoslog.wrapped, preoslog.source))
1792
1793    err = lldb.SBError()
1794    if preoslog.wrapped > 0:
1795        print(six.ensure_str(preoslog.data.GetString(err, preoslog.offset + 1)))
1796    print(six.ensure_str(preoslog.data.GetString(err, 0)))
1797    print("-----preoslog log end-------")
1798    return True
1799
1800@lldb_command('showeventsources')
1801def ShowEventSources(cmd_args=None):
1802    """ Show all event sources for a IOWorkLoop
1803        syntax: (lldb) showeventsources <IOWorkLoop *>
1804    """
1805    if not cmd_args:
1806        print("Please specify the address of the IOWorkLoop")
1807        print(ShowEventSources.__doc__)
1808        return
1809
1810    obj = kern.GetValueFromAddress(cmd_args[0], 'IOWorkLoop *')
1811    idx = 0
1812    event = obj.eventChain
1813    while event != 0:
1814        enabled = event.enabled
1815        print("{}: {} [{}]".format(idx, GetObjectSummary(event), "enabled" if enabled else "disabled"))
1816        event = event.eventChainNext
1817        idx += 1
1818