xref: /xnu-8019.80.24/tools/lldbmacros/zonetriage.py (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1"""
2    Triage Macros for zone related panics
3
4    Supported panic strings from xnu/osfmk/kern/zalloc.c:
5        "a freed zone element has been modified in zone %s: expected %p but found %p, bits changed %p, at offset %d of %d in element %p, cookies %p %p" and
6        "zalloc: zone map exhausted while allocating from zone %s, likely due to memory leak in zone %s (%lu total bytes, %d elements allocated)"
7    These macros are dependant on the above panic strings. If the strings are modified in any way, this script must be updated to reflect the change.
8
9    To support more zone panic strings:
10        1.  Add the panic string regex to the globals and include in the named capture group 'zone' (the zone to be
11            logged) as well as any other info necessary to parse out of the panic string.
12        2.  Add a check for the panic string regex in ZoneTriage(), which then calls into the function you create.
13        3.  Add a check for the panic string regex in CheckZoneBootArgs() which sets the variable panic_string_regex to your
14            panic string regex if found.
15        4.  Create a function that can be called either through the zonetriage macro ZoneTriage() or using its own macro.
16            This function should handle all lldb commands you want to run for this type of zone panic.
17"""
18from xnu import *
19import sys, shlex
20from utils import *
21import xnudefines
22import re
23import os.path
24
25## Globals
26panic_string = None
27## If the following panic strings are modified in xnu/osfmk/kern/zalloc.c, they must be updated here to reflect the change.
28zone_element_modified = ".*a freed zone element has been modified in zone (?P<zone>.+): expected (0x)?([0-9A-Fa-f]*)? but found (0x)?([0-9A-Fa-f]*)?, bits changed (0x)?([0-9A-Fa-f]*)?, at offset ([0-9]*)? of ([0-9]*)? in element (?P<element>0x[0-9A-Fa-f]*), cookies (0x)?([0-9A-Fa-f]*)? (0x)?([0-9A-Fa-f]*)?.*"
29zone_map_exhausted = ".*zalloc: zone map exhausted while allocating from zone .+, likely due to memory leak in zone (?P<zone>.+) \(([0-9]*)? total bytes, ([0-9]*)? elements allocated\).*"
30
31# Macro: zonetriage, zonetriage_freedelement, zonetriage_memoryleak
32@lldb_command('zonetriage')
33def ZoneTriage(cmd_args=None):
34    """ Calls function specific to type of zone panic based on the panic string
35    """
36    global panic_string
37    if panic_string is None:
38        try:
39            panic_string = lldb_run_command("paniclog").split('\n', 1)[0]
40        except:
41            return
42    if re.match(zone_element_modified, panic_string) is not None:
43        ZoneTriageFreedElement()
44    elif re.match(zone_map_exhausted, panic_string) is not None:
45        ZoneTriageMemoryLeak()
46    else:
47        print "zonetriage does not currently support this panic string."
48
49@lldb_command('zonetriage_freedelement')
50def ZoneTriageFreedElement(cmd_args=None):
51    """ Runs zstack_findelem on the element and zone being logged based on the panic string regex
52    """
53    global panic_string
54    if panic_string is None:
55        try:
56            panic_string = lldb_run_command("paniclog").split('\n', 1)[0]
57        except:
58            return
59    CheckZoneBootArgs()
60    ## Run showzonesbeinglogged.
61    print "(lldb) zstack_showzonesbeinglogged\n%s\n" % lldb_run_command("zstack_showzonesbeinglogged")
62    ## Capture zone and element from panic string.
63    values = re.search(zone_element_modified, panic_string)
64    if values is None or 'zone' not in values.group() or 'element' not in values.group():
65        return
66    element = values.group('element')
67    zone = values.group('zone')
68    btlog = FindZoneBTLog(zone)
69    if btlog is not None:
70        print "(lldb) zstack_findelem " + btlog + " " + element
71        findelem_output = lldb_run_command("zstack_findelem " + btlog + " " + element)
72        findelem_output = re.sub('Scanning is ongoing. [0-9]* items scanned since last check.\n', '', findelem_output)
73        print findelem_output
74
75@lldb_command('zonetriage_memoryleak')
76def ZoneTriageMemoryLeak(cmd_args=None):
77    """ Runs zstack_findtop and zstack_findleak on all zones being logged
78    """
79    global kern
80    CheckZoneBootArgs()
81    ## Run showzonesbeinglogged.
82    print "(lldb) zstack_showzonesbeinglogged\n%s\n" % lldb_run_command("zstack_showzonesbeinglogged")
83    for zval in kern.zones:
84        if zval.zlog_btlog:
85            print '%s:' % zval.z_name
86            print "(lldb) zstack_findtop -N 5 0x%lx" % zval.zlog_btlog
87            print lldb_run_command("zstack_findtop -N 5 0x%lx" % zval.zlog_btlog)
88            print "(lldb) zstack_findleak 0x%lx" % zval.zlog_btlog
89            print lldb_run_command("zstack_findleak 0x%lx" % zval.zlog_btlog)
90
91def CheckZoneBootArgs(cmd_args=None):
92    """ Check boot args to see if zone is being logged, if not, suggest new boot args
93    """
94    global panic_string
95    if panic_string is None:
96        try:
97            panic_string = lldb_run_command("paniclog").split('\n', 1)[0]
98        except:
99            return
100    panic_string_regex = ""
101    if re.match(zone_element_modified, panic_string) is not None:
102        panic_string_regex = zone_element_modified
103    if re.match(zone_map_exhausted, panic_string) is not None:
104        panic_string_regex = zone_map_exhausted
105    values = re.search(panic_string_regex, panic_string)
106    if values is None or 'zone' not in values.group():
107        return
108    zone = values.group('zone')
109    bootargs = lldb_run_command("showbootargs")
110    correct_boot_args = re.search('zlog([1-9]|10)?=' + re.sub(' ', '.', zone), bootargs)
111    if correct_boot_args is None:
112        print "Current boot-args:\n" + bootargs
113        print "You may need to include: -zc -zp zlog([1-9]|10)?=" + re.sub(' ', '.', zone)
114
115def FindZoneBTLog(zone):
116    """ Returns the btlog address in the format 0x%lx for the zone name passed as a parameter
117    """
118    global kern
119    for zval in kern.zones:
120        if zval.zlog_btlog:
121            if zone == "%s" % zval.z_name:
122                return "0x%lx" % zval.zlog_btlog
123    return None
124# EndMacro: zonetriage, zonetriage_freedelement, zonetriage_memoryleak
125