]> git.saurik.com Git - apple/xnu.git/blobdiff - kgmacros
xnu-1228.15.4.tar.gz
[apple/xnu.git] / kgmacros
index f0f7e3df4b260f92914557b18fdd333e90469f31..7623092e2b8ac6dd14c19a926a184c2d09b58685 100644 (file)
--- a/kgmacros
+++ b/kgmacros
@@ -159,6 +159,11 @@ document kgm
 |     kdp-reenter      Schedule reentry into the debugger and continue.
 |     kdp-reboot       Restart remote target
 |
+|     zstack           Print zalloc caller stack (zone leak debugging)
+|     findoldest       Find oldest zone leak debugging record
+|     countpcs         Print how often a pc occurs in the zone leak log
+|
+|
 | Type "help <macro>" for more specific help on a particular macro.
 | Type "show user <macro>" to see what the macro is really doing.
 end
@@ -454,6 +459,7 @@ end
 define showcurrentthreads
 set $kgm_prp = (struct processor *)processor_list
     while $kgm_prp != 0
+       printf "Processor 0x%08x State %d (cpu_id %x)\n", $kgm_prp, ($kgm_prp)->state, ($kgm_prp)->cpu_num
        if ($kgm_prp)->active_thread != 0
            set $kgm_actp = ($kgm_prp)->active_thread
            showtaskheader
@@ -504,6 +510,7 @@ end
 define showcurrentstacks
 set $kgm_prp = processor_list
     while $kgm_prp != 0
+       printf "Processor 0x%08x State %d (cpu_id %x)\n", $kgm_prp, ($kgm_prp)->state, ($kgm_prp)->cpu_num
        if ($kgm_prp)->active_thread != 0
            set $kgm_actp = ($kgm_prp)->active_thread
            showtaskheader
@@ -1960,6 +1967,7 @@ define showuserstack
                     _kgm_update_loop
                   end
                else
+               if ($kgm_mtype == 7)
                        set $newact = (struct thread *) $arg0
 #This needs to identify 64-bit processes as well
                        set $newiss = (x86_saved_state32_t) ($newact->machine.pcb->iss.uss.ss_32)
@@ -1975,6 +1983,9 @@ define showuserstack
                        _kgm_flush_loop
                        _kgm_update_loop
                        end                     
+               else
+                       echo showuserstack not supported on this architecture\n
+               end
                end
 end
 document showuserstack
@@ -2053,11 +2064,15 @@ define switchtocorethread
           flushstack
           set $pc = $newact->machine->pcb.save_srr0
        else
+       if ($kgm_mtype == 7)
                set $kgm_cstatep = (struct x86_kernel_state32 *) \
                                        ($newact->kernel_stack + 0x4000 \
                                         - sizeof(struct x86_kernel_state32))
                loadcontext $kgm_cstatep
                flushstack
+       else
+               echo switchtocorethread not supported on this architecture\n
+       end
        end
        showcontext_int
        end
@@ -2116,6 +2131,7 @@ define loadcontext
        set $cr = $kgm_contextp.save_cr
        set $ctr = $kgm_contextp.save_ctr
        else
+       if ($kgm_mtype == 7)
                set $kgm_contextp = (struct x86_kernel_state32 *) $arg0
                set $ebx = $kgm_contextp->k_ebx 
                set $ebp = $kgm_contextp->k_ebp 
@@ -2123,6 +2139,9 @@ define loadcontext
                set $esi = $kgm_contextp->k_esi 
                set $eip = $kgm_contextp->k_eip 
                set $pc =  $kgm_contextp->k_eip
+       else
+               echo loadcontext not supported on this architecture\n
+       end
        end
 end
 
@@ -2146,6 +2165,8 @@ define resetcorectx
                flushstack
                set $pc = $kdpstatep->eip
                update
+       else
+               echo resetcorectx not supported on this architecture\n
        end
        end
        showcontext_int
@@ -2525,6 +2546,10 @@ define showobjectint
     set $kgm_obj = (OSObject *) $arg1
     set $kgm_vt = *((void **) $arg1)
 
+    if ($kgm_mtype == 12)
+        set $kgm_vt = $kgm_vt - 2 * sizeof(void *)
+    end
+
     if ($kgm_show_object_addrs)
        printf "`object %p, vt ", $arg1
        output /a (unsigned) $kgm_vt
@@ -2654,6 +2679,9 @@ define showregistryentryrecurse
     printf "  <object %p, ", $kgm_re
     printf "vtable "
     set $kgm_vt = (unsigned) *(void**) $kgm_re
+    if ($kgm_mtype == 12)
+        set $kgm_vt = $kgm_vt - 2 * sizeof(void *)
+    end
     output /a $kgm_vt
 
     if ($kgm_vt != _ZTV15IORegistryEntry)
@@ -5350,7 +5378,7 @@ define showMCAstate
         _if_present mca_threshold_status_present
         printf "\n%d error banks, ", mca_error_bank_count
         printf "family code 0x%x, ", mca_family
-        printf "machine-check exception taken: %d\n", mca_exception_taken
+        printf "machine-check dump state: %d\n", mca_dump_state
         set $kgm_cpu = 0
         while cpu_data_ptr[$kgm_cpu] != 0
             set $kgm_mcp = cpu_data_ptr[$kgm_cpu]->cpu_mca_state
@@ -5383,3 +5411,339 @@ document showMCAstate
 Syntax: showMCAstate
 | Print machine-check register state after MC exception.
 end
+
+define _pt_step
+    #
+    # Step to lower-level page table and print attributes
+    #   $kgm_pt_paddr: current page table entry physical address
+    #   $kgm_pt_index: current page table entry index (0..511)
+    # returns
+    #   $kgm_pt_paddr: next level page table entry physical address
+    #                  or null if invalid
+    # For $kgm_pt_verbose = 0: print nothing
+    #                       1: print basic information
+    #                       2: print basic information and hex table dump
+    # The trickery with kdp_src_high32 is required for accesses above 4GB.
+    #
+    set $kgm_entryp = $kgm_pt_paddr + 8*$kgm_pt_index
+    set kdp_src_high32 = $kgm_pt_paddr >> 32
+    set kdp_trans_off = 1
+    set $entry =  *(pt_entry_t *)($kgm_entryp & 0x0ffffffffULL)
+    if $kgm_pt_verbose == 2
+        x/512g ($kgm_pt_paddr & 0x0ffffffffULL)
+    end
+    set kdp_trans_off = 0
+    set kdp_src_high32 = 0
+    set $kgm_paddr_mask = ~((0xffffULL<<48) | 0xfffULL)
+    if $kgm_pt_verbose == 0
+        if $entry & (0x1 << 0)
+            set $kgm_pt_paddr = $entry & $kgm_paddr_mask
+        else
+            set $kgm_pt_paddr = 0
+        end
+    else
+        printf "0x%016llx:\n\t0x%016llx\n\t", $kgm_entryp, $entry
+        if $entry & (0x1 << 0)
+            printf "valid"     
+            set $kgm_pt_paddr = $entry & $kgm_paddr_mask
+        else
+            printf "invalid"
+            set $kgm_pt_paddr = 0
+        end
+        if $entry & (0x1 << 1)
+            printf " writeable" 
+        else
+            printf " read-only" 
+        end
+        if $entry & (0x1 << 2)
+            printf " user" 
+        else
+            printf " supervisor" 
+        end
+        if $entry & (0x1 << 3)
+            printf " PWT" 
+        end
+        if $entry & (0x1 << 4)
+            printf " PCD" 
+        end
+        if $entry & (0x1 << 5)
+            printf " accessed" 
+        end
+        if $entry & (0x1 << 6)
+            printf " dirty" 
+        end
+        if $entry & (0x1 << 7)
+            printf " PAT" 
+        end
+        if $entry & (0x1 << 8)
+            printf " global" 
+        end
+        if $entry & (0x3 << 9)
+            printf " avail:0x%x", ($entry >> 9) & 0x3
+        end
+        if $entry & (0x1 << 63)
+            printf " noexec" 
+        end
+        printf "\n"
+    end
+end
+
+define _pmap_walk
+    set $kgm_pmap = (pmap_t) $arg0
+    set $kgm_vaddr = $arg1
+    set $kgm_pt_paddr = $kgm_pmap->pm_cr3
+    if $kgm_pt_paddr && cpu_64bit
+        set $kgm_pt_index = ($kgm_vaddr >> 39) & 0x1ffULL
+        if $kgm_pt_verbose
+            printf "pml4 (index %d):\n", $kgm_pt_index
+        end
+        _pt_step
+    end
+    if $kgm_pt_paddr
+        set $kgm_pt_index = ($kgm_vaddr >> 30) & 0x1ffULL
+        if $kgm_pt_verbose
+            printf "pdpt (index %d):\n", $kgm_pt_index
+        end
+        _pt_step
+    end
+    if $kgm_pt_paddr
+        set $kgm_pt_index = ($kgm_vaddr >> 21) & 0x1ffULL
+        if $kgm_pt_verbose
+            printf "pdt (index %d):\n", $kgm_pt_index
+        end
+        _pt_step
+    end
+    if $kgm_pt_paddr
+        set $kgm_pt_index = ($kgm_vaddr >> 12) & 0x1ffULL
+        if $kgm_pt_verbose
+            printf "pt (index %d):\n", $kgm_pt_index
+        end
+        _pt_step
+    end
+    if $kgm_pt_paddr
+        set $kgm_paddr = $kgm_pt_paddr + ($kgm_vaddr & 0xfffULL)
+        set kdp_trans_off = 1
+        set kdp_src_high32 = $kgm_paddr >> 32
+        set $kgm_value = *($kgm_paddr & 0x0ffffffffULL)
+        set kdp_trans_off = 0
+        set kdp_src_high32 = 0
+        printf "phys 0x%016llx: 0x%08x\n", $kgm_paddr, $kgm_value
+    else
+        set $kgm_paddr = 0
+        printf "(no translation)\n"
+    end
+end
+
+define pmap_walk
+    if $kgm_mtype != 7
+        printf "Not available for current architecture.\n"
+    else
+        if $argc != 2
+            printf "pmap_walk <pmap> <vaddr>\n"
+        else
+            if !$kgm_pt_verbose
+                set $kgm_pt_verbose = 1
+            else
+                if $kgm_pt_verbose != 2
+                    set $kgm_pt_verbose = 1
+                end
+            end
+            _pmap_walk $arg0 $arg1
+        end
+    end
+end
+
+document pmap_walk
+Syntax: (gdb) pmap_walk <pmap> <virtual_address>
+| Perform a page-table walk in <pmap> for <virtual_address>.
+| Set $kgm_pt_verbose=2 for full hex dump of page tables.
+end
+
+define pmap_vtop
+    if $kgm_mtype != 7
+        printf "Not available for current architecture.\n"
+    else
+        if $argc != 2
+            printf "pmap_vtop <pamp> <vaddr>\n"
+        else
+            set $kgm_pt_verbose = 0
+            _pmap_walk $arg0 $arg1
+        end
+    end
+end
+
+document pmap_vtop
+Syntax: (gdb) pmap_vtop <pmap> <virtual_address>
+| For page-tables in <pmap> translate <virtual_address> to physical address.
+end
+
+define zstack
+       set $index = $arg0
+
+       if (log_records == 0)
+               set $count = 0
+               printf "Zone logging not enabled.  Add 'zlog=<zone name>' to boot-args.\n"
+       else 
+               if ($argc == 2)
+                       set $count = $arg1
+               else
+                       set $count = 1
+               end
+       end
+
+       while ($count)
+               printf "\n--------------- "
+
+               if (zrecords[$index].z_opcode == 1)
+                       printf "ALLOC "
+               else
+                       printf "FREE "
+               end
+
+               printf " 0x%x : index %d  :  ztime %d -------------\n", zrecords[$index].z_element, $index, zrecords[$index].z_time
+
+               set $frame = 0
+
+               while ($frame < 15)
+                       set $frame_pc = zrecords[$index].z_pc[$frame]
+
+                       if ($frame_pc == 0)
+                               loop_break
+                       end
+
+                       x/i $frame_pc
+                       set $frame = $frame + 1
+               end
+
+               set $index = $index + 1
+               set $count = $count - 1
+       end
+end
+
+document zstack
+Syntax: (gdb) zstack <index> [<count>]
+| Zone leak debugging: print the stack trace of log element at <index>.
+| If a <count> is supplied, it prints <count> log elements starting at <index>.
+|
+| The suggested usage is to look at indexes below zcurrent and look for common stack traces.
+| The stack trace that occurs the most is probably the cause of the leak.  Find the pc of the
+| function calling into zalloc and use the countpcs kgmacro to find out how often that pc occurs in the log.
+| The pc occuring in a high percentage of records is most likely the source of the leak.
+|
+| The findoldest kgmacro is also useful for leak debugging since it identifies the oldest record
+| in the log, which may indicate the leaker.
+end
+
+define findoldest
+       set $index = 0
+       set $count = log_records
+       set $cur_min = 2000000000
+       set $cur_index = 0
+
+       if (log_records == 0)
+               printf "Zone logging not enabled.  Add 'zlog=<zone name>' to boot-args.\n"
+       else
+
+               while ($count)
+                       if (zrecords[$index].z_element && zrecords[$index].z_time < $cur_min)
+                               set $cur_index = $index
+                               set $cur_min = zrecords[$index].z_time
+                       end
+       
+                       set $count = $count - 1
+                       set $index = $index + 1
+               end
+       
+               printf "oldest record is at log index %d:\n", $cur_index
+               zstack $cur_index
+       end
+end
+
+document findoldest
+Syntax: (gdb) findoldest
+| Zone leak debugging: find and print the oldest record in the log.  Note that this command
+| can take several minutes to run since it uses linear search.
+|
+| Once it prints a stack trace, find the pc of the caller above all the zalloc, kalloc and
+| IOKit layers.  Then use the countpcs kgmacro to see how often this caller has allocated
+| memory.  A caller with a high percentage of records in the log is probably the leaker.
+end
+
+define countpcs
+       set $target_pc = $arg0
+       set $index = 0
+       set $count = log_records
+       set $found = 0
+
+       if (log_records == 0)
+               printf "Zone logging not enabled.  Add 'zlog=<zone name>' to boot-args.\n"
+       else
+
+               while ($count)
+                       set $frame = 0
+       
+                       if (zrecords[$index].z_element != 0)
+                               while ($frame < 15)
+                                       if (zrecords[$index].z_pc[$frame] == $target_pc)
+                                               set $found = $found + 1
+                                               set $frame = 15
+                                       end
+               
+                                       set $frame = $frame + 1
+                               end
+                       end
+       
+                       set $index = $index + 1
+                       set $count = $count - 1
+               end
+       
+               printf "occurred %d times in log (%d%c of records)\n", $found, ($found * 100) / zrecorded, '%'
+       end
+end
+
+document countpcs
+Syntax: (gdb) countpcs <pc>
+| Zone leak debugging: search the log and print a count of all log entries that contain the given <pc>
+| in the stack trace.  This is useful for verifying a suspected <pc> as being the source of
+| the leak.  If a high percentage of the log entries contain the given <pc>, then it's most
+| likely the source of the leak.  Note that this command can take several minutes to run.
+end
+
+define findelem
+       set $fe_index = zcurrent
+       set $fe_count = log_records
+       set $fe_elem = $arg0
+       set $fe_prev_op = -1
+
+       if (log_records == 0)
+               printf "Zone logging not enabled.  Add 'zlog=<zone name>' to boot-args.\n"
+       end
+
+       while ($fe_count)
+               if (zrecords[$fe_index].z_element == $fe_elem)
+                       zstack $fe_index
+
+                       if (zrecords[$fe_index].z_opcode == $fe_prev_op)
+                               printf "***************   DOUBLE OP!   *********************\n
+                       end
+
+                       set $fe_prev_op = zrecords[$fe_index].z_opcode
+               end
+
+               set $fe_count = $fe_count - 1
+               set $fe_index = $fe_index + 1
+
+               if ($fe_index >= log_records)
+                       set $fe_index = 0
+               end
+       end
+end
+
+document findelem
+Syntax: (gdb) findelem <elem addr>
+| Zone corruption debugging: search the log and print out the stack traces for all log entries that
+| refer to the given zone element.  When the kernel panics due to a corrupted zone element, get the
+| element address and use this macro.  This will show you the stack traces of all logged zalloc and
+| zfree operations which tells you who touched the element in the recent past.  This also makes
+| double-frees readily apparent.
+end