X-Git-Url: https://git.saurik.com/apple/xnu.git/blobdiff_plain/fe8ab488e9161c46dd9885d58fc52996dc0249ff..cb3231590a3c94ab4375e2228bd5e86b0cf1ad7e:/tools/lldbmacros/ioreg.py?ds=sidebyside diff --git a/tools/lldbmacros/ioreg.py b/tools/lldbmacros/ioreg.py old mode 100644 new mode 100755 index 44f3aaf9c..1e55dc219 --- a/tools/lldbmacros/ioreg.py +++ b/tools/lldbmacros/ioreg.py @@ -1,5 +1,6 @@ from xnu import * from utils import * +from kdp import * import sys ###################################### @@ -7,6 +8,20 @@ import sys ###################################### plane = None +##################################### +# Utility functions. +##################################### +def CastIOKitClass(obj, target_type): + """ Type cast an object to another IOKIT CPP class. + params: + obj - core.value object representing some C construct in lldb + target_type - str : ex 'OSString *' + - lldb.SBType : + """ + v = Cast(obj, target_type) + v.GetSBValue().SetPreferDynamicValue(lldb.eNoDynamicValues) + return v + ###################################### # Type Summaries ###################################### @@ -19,17 +34,19 @@ def GetObjectSummary(obj): return vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t') + vt = kern.StripKernelPAC(vt) vtype = kern.SymbolicateFromAddress(vt) + if len(vtype): + vtype_str = " <" + vtype[0].GetName() + ">" + else: + vtype_str = "" if hasattr(obj, 'retainCount'): retCount = (obj.retainCount & 0xffff) - cntnrRetCount = (retCount >> 16) - out_string = "`object 0x{0: <16x}, vt 0x{1: <16x} <{2:s}>, retain count {3:d}, container retain {4:d}` ".format(obj, vt, vtype[0].GetName(), retCount, cntnrRetCount) + cntnrRetCount = (obj.retainCount >> 16) + 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) else: - if len(vtype): - out_string = "`object 0x{0: <16x}, vt 0x{1: <16x} <{2:s}>` ".format(obj, vt, vtype[0].GetName()) - else: - out_string = "`object 0x{0: <16x}, vt 0x{1: <16x}` ".format(obj, vt) - + out_string = "`object 0x{0: <16x}, vt 0x{1: <16x}{2:s}` ".format(obj, vt, vtype_str) + ztvAddr = kern.GetLoadAddressForSymbol('_ZTV8OSString') if vt == ztvAddr: out_string += GetString(obj) @@ -52,21 +69,41 @@ def GetObjectSummary(obj): ztvAddr = kern.GetLoadAddressForSymbol('_ZTV7OSArray') if vt == ztvAddr: - out_string += "(" + GetArray(Cast(obj, 'OSArray *')) + ")" + out_string += "(" + GetArray(CastIOKitClass(obj, 'OSArray *')) + ")" return out_string ztvAddr = kern.GetLoadAddressForSymbol('_ZTV5OSSet') if vt == ztvAddr: - out_string += GetSet(Cast(obj, 'OSSet *')) + out_string += GetSet(CastIOKitClass(obj, 'OSSet *')) return out_string ztvAddr = kern.GetLoadAddressForSymbol('_ZTV12OSDictionary') if vt == ztvAddr: - out_string += GetDictionary(Cast(obj, 'OSDictionary *')) + out_string += GetDictionary(CastIOKitClass(obj, 'OSDictionary *')) return out_string return out_string + +def GetObjectTypeStr(obj): + """ Return the type of an OSObject's container class + """ + if obj is None: + return None + + vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t') + vt = kern.StripKernelPAC(vt) + vtype = kern.SymbolicateFromAddress(vt) + if len(vtype): + return vtype[0].GetName() + + # See if the value is in a kext with no symbols + for kval in IterateLinkedList(kern.globals.kmod, 'next'): + if vt >= unsigned(kval.address) and vt <= (unsigned(kval.address) + unsigned(kval.size)): + return "kmod:{:s}+{:#0x}".format(kval.name, vt - unsigned(kval.address)) + return None + + @lldb_type_summary(['IORegistryEntry *']) @header("") def GetRegistryEntrySummary(entry): @@ -85,24 +122,26 @@ def GetRegistryEntrySummary(entry): name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) if name is not None: - out_string += "+-o {0:s} ".format(GetString(Cast(name, 'OSString *'))) - elif Cast(entry, 'IOService *').pwrMgt and Cast(entry, 'IOService *').pwrMgt.Name: - out_string += "+-o {0:s} ".format(Cast(entry, 'IOService *').pwrMgt.Name) + out_string += "+-o {0:s} ".format(GetString(CastIOKitClass(name, 'OSString *'))) + elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name: + out_string += "+-o {0:s} ".format(CastIOKitClass(entry, 'IOService *').pwrMgt.Name) else: out_string += "+-o ?? " # I'm using uintptr_t for now to work around FindFirstType & Co. should allow you to make pointer types directly vtableAddr = dereference(Cast(entry, 'uintptr_t *')) - 2 * sizeof('uintptr_t *') + vtableAddr = kern.StripKernelPAC(vtableAddr) vtype = kern.SymbolicateFromAddress(vtableAddr) if vtype is None or len(vtype) < 1: - out_string += " [class/struct type of object] + """ + if not cmd_args: + print "No arguments passed" + print DumpObject.__doc__ + return False + + if len(cmd_args) == 1: + try: + object_info = lldb_run_command("showobject {:s}".format(cmd_args[0])) + except: + print "Error!! showobject failed due to invalid value" + print DumpObject.__doc__ + return False + + srch = re.search(r'', object_info) + if not srch: + print "Error!! Couldn't find object in registry, input type manually as 2nd argument" + print DumpObject.__doc__ + return False + + object_type = srch.group(1) + else: + type_lookup = lldb_run_command("image lookup -t {:s}".format(cmd_args[1])) + if type_lookup.find(cmd_args[1])!= -1: + object_type = cmd_args[1] + else: + print "Error!! Input type {:s} isn't available in image lookup".format(cmd_args[1]) + return False + + print "******** Object Dump for value \'{:s}\' with type \"{:s}\" ********".format(cmd_args[0], object_type) + print lldb_run_command("p/x *({:s}*){:s}".format(object_type, cmd_args[0])) + +#EndMacro: dumpobject + @lldb_command('setregistryplane') def SetRegistryPlane(cmd_args=None): """ Set the plane to be used for the IOKit registry macros @@ -262,7 +338,7 @@ def ReadIOPort8(cmd_args=None): ReadIOPortInt(portAddr, 1, lcpu) @lldb_command('readioport16') -def ReadIOPort8(cmd_args=None): +def ReadIOPort16(cmd_args=None): """ Read value stored in the specified IO port. The CPU can be optionally specified as well. Prints 0xBAD10AD in case of a bad read @@ -282,7 +358,7 @@ def ReadIOPort8(cmd_args=None): ReadIOPortInt(portAddr, 2, lcpu) @lldb_command('readioport32') -def ReadIOPort8(cmd_args=None): +def ReadIOPort32(cmd_args=None): """ Read value stored in the specified IO port. The CPU can be optionally specified as well. Prints 0xBAD10AD in case of a bad read @@ -324,7 +400,7 @@ def WriteIOPort8(cmd_args=None): WriteIOPortInt(portAddr, 1, value, lcpu) @lldb_command('writeioport16') -def WriteIOPort8(cmd_args=None): +def WriteIOPort16(cmd_args=None): """ Write the value to the specified IO port. The size of the value is determined by the name of the command. The CPU used can be optionally specified as well. @@ -346,7 +422,7 @@ def WriteIOPort8(cmd_args=None): WriteIOPortInt(portAddr, 2, value, lcpu) @lldb_command('writeioport32') -def WriteIOPort8(cmd_args=None): +def WriteIOPort32(cmd_args=None): """ Write the value to the specified IO port. The size of the value is determined by the name of the command. The CPU used can be optionally specified as well. @@ -462,13 +538,13 @@ def ShowRegistryEntryRecurse(entry, prefix, printProps): childArray = LookupKeyInOSDict(registryTable, childKey) if childArray is not None: idx = 0 - ca = Cast(childArray, 'OSArray *') + ca = CastIOKitClass(childArray, 'OSArray *') count = unsigned(ca.count) while idx < count: if plen != 0 and plen != 1 and (plen & (plen - 1)) == 0: - ShowRegistryEntryRecurse(Cast(ca.array[idx], 'IORegistryEntry *'), prefix + "| ", printProps) + ShowRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), prefix + "| ", printProps) else: - ShowRegistryEntryRecurse(Cast(ca.array[idx], 'IORegistryEntry *'), prefix + " ", printProps) + ShowRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), prefix + " ", printProps) idx += 1 def FindRegistryEntryRecurse(entry, search_name, stopAfterFirst): @@ -490,12 +566,12 @@ def FindRegistryEntryRecurse(entry, search_name, stopAfterFirst): name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) if name is not None: - if str(Cast(name, 'OSString *').string) == search_name: + if str(CastIOKitClass(name, 'OSString *').string) == search_name: print GetRegistryEntrySummary(entry) if stopAfterFirst is True: return True - elif Cast(entry, 'IOService *').pwrMgt and Cast(entry, 'IOService *').pwrMgt.Name: - name = Cast(entry, 'IOService *').pwrMgt.Name + elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name: + name = CastIOKitClass(entry, 'IOService *').pwrMgt.Name if str(name) == search_name: print GetRegistryEntrySummary(entry) if stopAfterFirst is True: @@ -509,10 +585,10 @@ def FindRegistryEntryRecurse(entry, search_name, stopAfterFirst): childArray = LookupKeyInOSDict(registryTable, childKey) if childArray is not None: idx = 0 - ca = Cast(childArray, 'OSArray *') + ca = CastIOKitClass(childArray, 'OSArray *') count = unsigned(ca.count) while idx < count: - if FindRegistryEntryRecurse(Cast(ca.array[idx], 'IORegistryEntry *'), search_name, stopAfterFirst) is True: + if FindRegistryEntryRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), search_name, stopAfterFirst) is True: return True idx += 1 return False @@ -537,10 +613,10 @@ def FindRegistryObjectRecurse(entry, search_name): name = LookupKeyInOSDict(propertyTable, kern.globals.gIOClassKey) if name is not None: - if str(Cast(name, 'OSString *').string) == search_name: + if str(CastIOKitClass(name, 'OSString *').string) == search_name: return entry - elif Cast(entry, 'IOService *').pwrMgt and Cast(entry, 'IOService *').pwrMgt.Name: - name = Cast(entry, 'IOService *').pwrMgt.Name + elif CastIOKitClass(entry, 'IOService *').pwrMgt and CastIOKitClass(entry, 'IOService *').pwrMgt.Name: + name = CastIOKitClass(entry, 'IOService *').pwrMgt.Name if str(name) == search_name: return entry @@ -551,9 +627,9 @@ def FindRegistryObjectRecurse(entry, search_name): childKey = plane.keys[1] childArray = LookupKeyInOSDict(registryTable, childKey) if childArray is not None: - ca = Cast(childArray, 'OSArray *') + ca = CastIOKitClass(childArray, 'OSArray *') for idx in range(ca.count): - registry_object = FindRegistryObjectRecurse(Cast(ca.array[idx], 'IORegistryEntry *'), search_name) + registry_object = FindRegistryObjectRecurse(CastIOKitClass(ca.array[idx], 'IORegistryEntry *'), search_name) if not registry_object or int(registry_object) == int(0): continue else: @@ -609,11 +685,11 @@ def GetRegDictionary(osdict, prefix): def GetString(string): """ Returns the python string representation of a given OSString """ - out_string = "\"{0:s}\"".format(Cast(string, 'OSString *').string) + out_string = "\"{0:s}\"".format(CastIOKitClass(string, 'OSString *').string) return out_string def GetNumber(num): - out_string = "{0:d}".format(Cast(num, 'OSNumber *').value) + out_string = "{0:d}".format(CastIOKitClass(num, 'OSNumber *').value) return out_string def GetBoolean(b): @@ -707,16 +783,14 @@ def ReadIOPortInt(addr, numbytes, lcpu): result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_readioport_reply_t *') if(result_pkt.error == 0): - print "This macro is incomplete till is fixed" - # FIXME: Uncomment me when is fixed - #if numbytes == 1: - # result = dereference(Cast(result_pkt.data, 'uint8_t *')) - #elif numbytes == 2: - # result = dereference(Cast(result_pkt.data, 'uint16_t *')) - #elif numbytes == 4: - # result = dereference(cast(result_pkt.data, 'uint32_t *')) - - print "0x{0: <4x}: 0x{1: <1x}".format(addr, result) + if numbytes == 1: + result = dereference(Cast(addressof(result_pkt.data), 'uint8_t *')) + elif numbytes == 2: + result = dereference(Cast(addressof(result_pkt.data), 'uint16_t *')) + elif numbytes == 4: + result = dereference(Cast(addressof(result_pkt.data), 'uint32_t *')) + + print "{0: <#6x}: {1:#0{2}x}".format(addr, result, (numbytes*2)+2) def WriteIOPortInt(addr, numbytes, value, lcpu): """ Writes 'value' into ioport specified by 'addr'. Prints errors if it encounters any @@ -730,12 +804,12 @@ def WriteIOPortInt(addr, numbytes, value, lcpu): len_address = unsigned(addressof(kern.globals.manual_pkt.len)) data_address = unsigned(addressof(kern.globals.manual_pkt.data)) if not WriteInt32ToMemoryAddress(0, input_address): - print "error writing 0x{0: x} to port 0x{1: <4x}".format(value, addr) + print "error writing {0: #x} to port {1: <#6x}: failed to write 0 to input_address".format(value, addr) return kdp_pkt_size = GetType('kdp_writeioport_req_t').GetByteSize() if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address): - print "error writing 0x{0: x} to port 0x{1: <4x}".format(value, addr) + print "error writing {0: #x} to port {1: <#6x}: failed to write kdp_pkt_size".format(value, addr) return kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_writeioport_req_t *') @@ -747,29 +821,29 @@ def WriteIOPortInt(addr, numbytes, value, lcpu): WriteInt32ToMemoryAddress(numbytes, int(addressof(kgm_pkt.nbytes))) and WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))) ): - print "This macro is incomplete till is fixed" - # FIXME: Uncomment me when is fixed - #if numbytes == 1: - # if not WriteInt8ToMemoryAddress(value, int(addressof(kgm_pkt.data))): - # print "error writing 0x{0: x} to port 0x{1: <4x}".format(value, addr) - #elif numbytes == 2: - # if not WriteInt16ToMemoryAddress(value, int(addressof(kgm_pkt.data))): - # print "error writing 0x{0: x} to port 0x{1: <4x}".format(value, addr) - #elif numbytes == 4: - # if not WriteInt32ToMemoryAddress(value, int(addressof(kgm_pkt.data))): - # print "error writing 0x{0: x} to port 0x{1: <4x}".format(value, addr) - + if numbytes == 1: + if not WriteInt8ToMemoryAddress(value, int(addressof(kgm_pkt.data))): + print "error writing {0: #x} to port {1: <#6x}: failed to write 8 bit data".format(value, addr) + return + elif numbytes == 2: + if not WriteInt16ToMemoryAddress(value, int(addressof(kgm_pkt.data))): + print "error writing {0: #x} to port {1: <#6x}: failed to write 16 bit data".format(value, addr) + return + elif numbytes == 4: + if not WriteInt32ToMemoryAddress(value, int(addressof(kgm_pkt.data))): + print "error writing {0: #x} to port {1: <#6x}: failed to write 32 bit data".format(value, addr) + return if not WriteInt32ToMemoryAddress(1, input_address): - print "error writing 0x{0: x} to port 0x{1: <4x}".format(value, addr) + print "error writing {0: #x} to port {1: <#6x}: failed to write to input_address".format(value, addr) return result_pkt = Cast(addressof(kern.globals.manual_pkt.data), 'kdp_writeioport_reply_t *') # Done with the write if(result_pkt.error == 0): - print "Writing 0x {0: x} to port {1: <4x} was successful".format(value, addr) + print "Writing {0: #x} to port {1: <#6x} was successful".format(value, addr) else: - print "error writing 0x{0: x} to port 0x{1: <4x}".format(value, addr) + print "error writing {0: #x} to port {1: <#6x}".format(value, addr) @lldb_command('showinterruptcounts') def showinterruptcounts(cmd_args=None): @@ -784,8 +858,8 @@ def showinterruptcounts(cmd_args=None): print header_format.format("Name", "Index", "Count") for i in kern.interrupt_stats: - owner = Cast(i.owner, 'IOInterruptEventSource *') - nub = Cast(owner.provider, 'IORegistryEntry *') + owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *') + nub = CastIOKitClass(owner.provider, 'IORegistryEntry *') name = None # To uniquely identify an interrupt, we need the nub name and the index. The index @@ -803,7 +877,7 @@ def showinterruptcounts(cmd_args=None): if name is None: nub_name = "Unknown" else: - nub_name = GetString(Cast(name, 'OSString *')) + nub_name = GetString(CastIOKitClass(name, 'OSString *')) # We now have everything we need; spew the requested data. @@ -828,14 +902,14 @@ def showinterruptstats(cmd_args=None): Workloop Time: Total time spent running the kernel context handler """ - header_format = "{0: <20s} {1: >5s} {2: >20s} {3: >20s} {4: >20s} {5: >20s} {6: >20s}" - content_format = "{0: <20s} {1: >5d} {2: >20d} {3: >20d} {4: >20d} {5: >20d} {6: >20d}" + header_format = "{0: <20s} {1: >5s} {2: >20s} {3: >20s} {4: >20s} {5: >20s} {6: >20s} {7: >20s} {8: >20s} {9: >20s}" + content_format = "{0: <20s} {1: >5d} {2: >20d} {3: >20d} {4: >20d} {5: >20d} {6: >20d} {7: >20d} {8: >20d} {9: >#20x}" - print header_format.format("Name", "Index", "Interrupt Count", "Interrupt Time", "Workloop Count", "Workloop CPU Time", "Workloop Time") + print header_format.format("Name", "Index", "Interrupt Count", "Interrupt Time", "Avg Interrupt Time", "Workloop Count", "Workloop CPU Time", "Workloop Time", "Avg Workloop Time", "Owner") for i in kern.interrupt_stats: - owner = Cast(i.owner, 'IOInterruptEventSource *') - nub = Cast(owner.provider, 'IORegistryEntry *') + owner = CastIOKitClass(i.owner, 'IOInterruptEventSource *') + nub = CastIOKitClass(owner.provider, 'IORegistryEntry *') name = None # To uniquely identify an interrupt, we need the nub name and the index. The index @@ -853,7 +927,7 @@ def showinterruptstats(cmd_args=None): if name is None: nub_name = "Unknown" else: - nub_name = GetString(Cast(name, 'OSString *')) + nub_name = GetString(CastIOKitClass(name, 'OSString *')) # We now have everything we need; spew the requested data. @@ -864,7 +938,16 @@ def showinterruptstats(cmd_args=None): second_level_cpu_time = i.interruptStatistics[3] second_level_system_time = i.interruptStatistics[4] - print content_format.format(nub_name, interrupt_index, first_level_count, first_level_time, second_level_count, second_level_cpu_time, second_level_system_time) + avg_first_level_time = 0 + if first_level_count != 0: + avg_first_level_time = first_level_time / first_level_count + + avg_second_level_time = 0 + if second_level_count != 0: + avg_second_level_time = second_level_system_time / second_level_count + + print content_format.format(nub_name, interrupt_index, first_level_count, first_level_time, avg_first_level_time, + second_level_count, second_level_cpu_time, second_level_system_time, avg_second_level_time, owner) return True