]> git.saurik.com Git - apple/xnu.git/blobdiff - tools/lldbmacros/ioreg.py
xnu-4903.241.1.tar.gz
[apple/xnu.git] / tools / lldbmacros / ioreg.py
old mode 100644 (file)
new mode 100755 (executable)
index 44f3aaf..e2bdaf2
@@ -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
 ######################################
@@ -20,16 +35,17 @@ def GetObjectSummary(obj):
 
     vt = dereference(Cast(obj, 'uintptr_t *')) - 2 * sizeof('uintptr_t')
     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 +68,40 @@ 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')
+    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,9 +120,9 @@ 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 ??  "
     
@@ -95,14 +130,15 @@ def GetRegistryEntrySummary(entry):
     vtableAddr = dereference(Cast(entry, 'uintptr_t *')) - 2 * sizeof('uintptr_t *')
     vtype = kern.SymbolicateFromAddress(vtableAddr)
     if vtype is None or len(vtype) < 1:
-        out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x}".format(entry, entry.reserved.fRegistryEntryID, vtableAddr)
+        out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x}".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID, vtableAddr)
     else:
-        out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x} <{3:s}>".format(entry, entry.reserved.fRegistryEntryID, vtableAddr, vtype[0].GetName())
+        out_string += "<object 0x{0: <16x}, id 0x{1:x}, vtable 0x{2: <16x} <{3:s}>".format(entry, CastIOKitClass(entry, 'IORegistryEntry *').reserved.fRegistryEntryID,
+                                                                                           vtableAddr, vtype[0].GetName())
     
     ztvAddr = kern.GetLoadAddressForSymbol('_ZTV15IORegistryEntry')
     if vtableAddr != ztvAddr:
         out_string += ", "
-        state = Cast(entry, 'IOService *').__state[0]
+        state = CastIOKitClass(entry, 'IOService *').__state[0]
         # kIOServiceRegisteredState
         if 0 == state & 2:
             out_string += "!"
@@ -114,11 +150,9 @@ def GetRegistryEntrySummary(entry):
         #kIOServiceInactiveState
         if 0 != state & 1:
             out_string += "in"
-        busyCount = (Cast(entry, 'IOService *').__state[1] & 0xff)
-        retCount = (Cast(entry, 'IOService *').retainCount & 0xffff)
+        busyCount = (CastIOKitClass(entry, 'IOService *').__state[1] & 0xff)
+        retCount = (CastIOKitClass(entry, 'IOService *').retainCount & 0xffff)
         out_string += "active, busy {0}, retain count {1}>".format(busyCount, retCount)
-    #else:
-    #    out_string += "\n"
     return out_string
 
 ######################################
@@ -133,7 +167,7 @@ def ShowAllClasses(cmd_args=None):
     count = unsigned(kern.globals.sAllClassesDict.count)
     
     while idx < count:
-        meta = Cast(kern.globals.sAllClassesDict.dictionary[idx].value, 'OSMetaClass *')
+        meta = CastIOKitClass(kern.globals.sAllClassesDict.dictionary[idx].value, 'OSMetaClass *')
         idx += 1
         print GetMetaClass(meta)
 
@@ -148,6 +182,45 @@ def ShowObject(cmd_args=None):
     obj = kern.GetValueFromAddress(cmd_args[0], 'OSObject *')
     print GetObjectSummary(obj)
 
+#Macro: dumpobject
+@lldb_command('dumpobject')
+def DumpObject(cmd_args=None):
+    """ Dumps object information if it is a valid object confirmed by showobject
+        Usage: dumpobject <address of object to be dumped> [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'<vtable for ([A-Za-z].*)>', 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 +335,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 +355,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 +397,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 +419,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 +535,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 +563,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 +582,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 +610,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 +624,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 +682,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 +780,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 <rdar://problem/12868059> is fixed"
-            # FIXME: Uncomment me when <rdar://problem/12868059> 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 +801,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 +818,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 <rdar://problem/12868059> is fixed"
-        # FIXME: Uncomment me when <rdar://problem/12868059> 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 +855,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 +874,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 +899,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 +924,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 +935,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