xref: /xnu-10002.81.5/tools/lldbmacros/misc.py (revision 5e3eaea39dcf651e66cb99ba7d70e32cc4a99587)
1"""
2Miscellaneous (Intel) platform-specific commands.
3"""
4from __future__ import absolute_import, division, print_function
5
6from builtins import hex
7from builtins import range
8from builtins import memoryview
9
10from core import caching
11from xnu import *
12import xnudefines
13
14from scheduler import *
15
16@lldb_command('showmcastate')
17def showMCAstate(cmd_args=None):
18    """
19    Print machine-check register state after MC exception.
20    """
21    if kern.arch != 'x86_64':
22        print("Not available for current architecture.")
23        return
24
25    present = ["not present", "present"]
26    print('MCA {:s}, control MSR {:s}, threshold status {:s}'.format(
27    present[int(kern.globals.mca_MCA_present)],
28    present[int(kern.globals.mca_control_MSR_present)],
29    present[int(kern.globals.mca_threshold_status_present)]))
30    print('{:d} error banks, family code {:#0x}, machine-check dump state: {:d}'.format(
31        kern.globals.mca_error_bank_count,
32        kern.globals.mca_dump_state,
33        kern.globals.mca_family))
34    cpu = 0
35    while kern.globals.cpu_data_ptr[cpu]:
36        cd = kern.globals.cpu_data_ptr[cpu]
37        mc = cd.cpu_mca_state
38        if mc:
39            print('CPU {:d}: mca_mcg_ctl: {:#018x} mca_mcg_status {:#018x}'.format(cpu, mc.mca_mcg_ctl, mc.mca_mcg_status.u64))
40            hdr = '{:<4s} {:<18s} {:<18s} {:<18s} {:<18s}'
41            val = '{:>3d}: {:#018x} {:#018x} {:#018x} {:#018x}'
42            print(hdr.format('bank',
43                    'mca_mci_ctl',
44                    'mca_mci_status',
45                    'mca_mci_addr',
46                    'mca_mci_misc'))
47            for i in range(int(kern.globals.mca_error_bank_count)):
48                bank = mc.mca_error_bank[i]
49                print(val.format(i,
50                    bank.mca_mci_ctl,
51                    bank.mca_mci_status.u64,
52                    bank.mca_mci_addr,
53                    bank.mca_mci_misc))
54        print('register state:')
55        reg = cd.cpu_desc_index.cdi_ktss.ist1 - sizeof('x86_saved_state_t')
56        print(lldb_run_command('p/x *(x86_saved_state_t *) ' + hex(reg)))
57        cpu = cpu + 1
58
59def dumpTimerList(mpqueue, processor=None):
60    """
61    Utility function to dump the timer entries in list (anchor).
62    anchor is a struct mpqueue_head.
63    """
64
65    if mpqueue.count == 0:
66        print('(empty)')
67        return
68
69    thdr = ' {:<24s}{:^17s}{:^18s} {:^18s} {:^18s} {:^18s} {:^18s} {:9s} {:^18s} count: {:d} '
70    tval = ' {:#018x}{:s} {:18d} {:18d} {:18.06f} {:18.06f} {:18.06f} {:18.06f} {:>9s}  ({:#018x})({:#018x}, {:#018x}) ({:s}) {:s}'
71
72    print(thdr.format('Entry', 'Soft Deadline', 'Deadline', 'Soft To Go', 'Hard To Go', 'Duration', 'Leeway', 'Flags', '(*func)(param0, param1)', mpqueue.count))
73
74    recent_timestamp = GetRecentTimestamp()
75
76    for timer_call in ParanoidIterateLinkageChain(mpqueue.head, 'struct timer_call *', 'tc_qlink'):
77
78        func_name = kern.Symbolicate(timer_call.tc_func)
79
80        extra_string = ""
81
82        strip_func = kern.StripKernelPAC(unsigned(timer_call.tc_func))
83
84        func_syms = kern.SymbolicateFromAddress(strip_func)
85        # returns an array of SBSymbol
86
87        if func_syms and func_syms[0] :
88            func_sym = func_syms[0]
89            func_name = func_sym.GetName()
90            try :
91
92                if "thread_call_delayed_timer" in func_name :
93                    group = Cast(timer_call.tc_param0, 'struct thread_call_group *')
94                    flavor = Cast(timer_call.tc_param1, 'thread_call_flavor_t')
95
96                    # There's got to be a better way to stringify the enum
97                    flavorname = str(flavor).partition(" = ")[2]
98
99                    extra_string += "{:s} {:s}".format(group.tcg_name, flavorname)
100
101                if "thread_timer_expire" in func_name :
102                    thread = Cast(timer_call.tc_param0, 'thread_t')
103
104                    tid = thread.thread_id
105                    name = GetThreadName(thread)
106                    pid = GetProcPIDForTask(thread.t_tro.tro_task)
107                    procname = GetProcNameForTask(thread.t_tro.tro_task)
108
109                    otherprocessor = ""
110                    if processor :
111                        if thread.last_processor != processor:
112                            otherprocessor = " (Not same processor - was on {:d})".format(thread.last_processor.cpu_id)
113
114                    extra_string += "thread: 0x{:x} {:s} task:{:s}[{:d}]{:s}".format(
115                            tid, name, procname, pid, otherprocessor)
116            except:
117                print("exception generating extra_string for call: {:#018x}".format(timer_call))
118                if dumpTimerList.enable_debug :
119                    raise
120
121        timer_fire = timer_call.tc_pqlink.deadline - recent_timestamp
122        timer_fire_s = kern.GetNanotimeFromAbstime(timer_fire) / 1000000000.0
123
124        soft_timer_fire = timer_call.tc_soft_deadline - recent_timestamp
125        soft_timer_fire_s = kern.GetNanotimeFromAbstime(soft_timer_fire) / 1000000000.0
126
127        leeway = timer_call.tc_pqlink.deadline - timer_call.tc_soft_deadline
128        leeway_s = kern.GetNanotimeFromAbstime(leeway) / 1000000000.0
129
130        tc_ttd_s = kern.GetNanotimeFromAbstime(timer_call.tc_ttd) / 1000000000.0
131
132        flags = int(timer_call.tc_flags)
133        timer_call_flags = {0x0:'', 0x1:'C', 0x2:'B', 0x4:'X', 0x8:'X', 0x10:'U', 0x20:'E',
134                0x40:'L', 0x80:'R'}
135
136        flags_str = ''
137        mask = 0x1
138        while mask <= 0x80 :
139            flags_str += timer_call_flags[int(flags & mask)]
140            mask = mask << 1
141
142        colon = ":"
143
144        if addressof(timer_call.tc_pqlink) == mpqueue.mpq_pqhead.pq_root :
145            colon = "*"
146
147        print(tval.format(timer_call, colon,
148            timer_call.tc_soft_deadline,
149            timer_call.tc_pqlink.deadline,
150            soft_timer_fire_s,
151            timer_fire_s,
152            tc_ttd_s,
153            leeway_s,
154            flags_str,
155            timer_call.tc_func,
156            timer_call.tc_param0,
157            timer_call.tc_param1,
158            func_name, extra_string))
159
160dumpTimerList.enable_debug = False
161
162def GetCpuDataForCpuID(cpu_id):
163    """
164    Find struct cpu_data for a CPU
165    ARM is complicated
166    """
167    if kern.arch == 'x86_64':
168        cpu_data = kern.globals.cpu_data_ptr[cpu_id]
169        return cpu_data
170    elif kern.arch.startswith('arm'):
171        data_entries_addr = kern.GetLoadAddressForSymbol('CpuDataEntries')
172        data_entries = kern.GetValueFromAddress(data_entries_addr, 'cpu_data_entry_t *')
173        data_entry = data_entries[cpu_id];
174        cpu_data_addr = data_entry.cpu_data_vaddr
175        return Cast(cpu_data_addr, 'cpu_data_t*')
176
177@lldb_command('longtermtimers')
178def longtermTimers(cmd_args=None):
179    """
180    Print details of long-term timers and stats.
181    """
182
183    lt = kern.globals.timer_longterm
184    ltt = lt.threshold
185    EndofAllTime = signed(-1)
186    if signed(ltt.interval) == EndofAllTime:
187        print("Longterm timers disabled")
188        return
189
190    if lt.escalates > 0:
191        ratio = lt.enqueues // lt.escalates
192    else:
193        ratio = lt.enqueues
194    print('Longterm timer object: {:#018x}'.format(addressof(lt)))
195    print(' queue count         : {:d}'    .format(lt.queue.count))
196    print(' number of enqueues  : {:d}'    .format(lt.enqueues))
197    print(' number of dequeues  : {:d}'    .format(lt.dequeues))
198    print(' number of escalates : {:d}'    .format(lt.escalates))
199    print(' enqueues/escalates  : {:d}'    .format(ratio))
200    print(' threshold.interval  : {:d}'    .format(ltt.interval))
201    print(' threshold.margin    : {:d}'    .format(ltt.margin))
202    print(' scan_time           : {:#018x} ({:d})'.format(lt.scan_time, lt.scan_time))
203    if signed(ltt.preempted) == EndofAllTime:
204        print(' threshold.preempted : None')
205    else:
206        print(' threshold.preempted : {:#018x} ({:d})'.format(ltt.preempted, ltt.preempted))
207    if signed(ltt.deadline) == EndofAllTime:
208        print(' threshold.deadline  : None')
209    else:
210        print(' threshold.deadline  : {:#018x} ({:d})'.format(ltt.deadline, ltt.deadline))
211        print(' threshold.call      : {:#018x}'.format(ltt.call))
212        print(' actual deadline set : {:#018x} ({:d})'.format(ltt.deadline_set, ltt.deadline_set))
213    print(' threshold.scans     : {:d}'    .format(ltt.scans))
214    print(' threshold.preempts  : {:d}'    .format(ltt.preempts))
215    print(' threshold.latency   : {:d}'    .format(ltt.latency))
216    print('               - min : {:d}'    .format(ltt.latency_min))
217    print('               - max : {:d}'    .format(ltt.latency_max))
218    dumpTimerList(lt.queue)
219
220
221@lldb_command('processortimers')
222def processorTimers(cmd_args=None):
223    """
224    Print details of processor timers, noting anything suspicious
225    Also include long-term timer details
226
227        Callout flags:
228
229        C - Critical
230        B - Background
231        U - User timer
232        E - Explicit Leeway
233        L - Local
234        R - Rate-limited - (App Nap)
235    """
236
237    recent_timestamp = GetRecentTimestamp()
238
239    hdr = '{:15s}{:<18s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s} Recent Timestamp: {:d}'
240    print(hdr.format('Processor #', 'Processor pointer', 'Last dispatch', 'Soft deadline', 'Soft To Go', 'Hard deadline', 'Hard To Go', 'Current Leeway', recent_timestamp))
241    print("=" * 82)
242    p = kern.globals.processor_list
243    EndOfAllTime = signed(-1)
244    while p:
245        cpu = p.cpu_id
246        cpu_data = GetCpuDataForCpuID(cpu)
247        rt_timer = cpu_data.rtclock_timer
248        diff = signed(rt_timer.deadline) - signed(recent_timestamp)
249        diff_s = kern.GetNanotimeFromAbstime(diff) / 1000000000.0
250        valid_deadline = signed(rt_timer.deadline) != EndOfAllTime
251        soft_deadline = rt_timer.queue.earliest_soft_deadline
252        soft_diff = signed(soft_deadline) - signed(recent_timestamp)
253        soft_diff_s = kern.GetNanotimeFromAbstime(soft_diff) / 1000000000.0
254        valid_soft_deadline = signed(soft_deadline) != EndOfAllTime
255        leeway_s = kern.GetNanotimeFromAbstime(rt_timer.deadline - soft_deadline) / 1000000000.0
256        tmr = 'Processor {:<3d}: {:#018x} {:<18d} {:<18s} {:<18s} {:<18s} {:<18s} {:<18s} {:s} {:s}'
257        print(tmr.format(cpu,
258            p,
259            p.last_dispatch,
260            "{:d}".format(soft_deadline) if valid_soft_deadline else "None",
261            "{:<16.06f}".format(soft_diff_s) if valid_soft_deadline else "N/A",
262            "{:d}".format(rt_timer.deadline) if valid_deadline else "None",
263            "{:<16.06f}".format(diff_s) if valid_deadline else "N/A",
264            "{:<16.06f}".format(leeway_s) if valid_soft_deadline and valid_deadline else "N/A",
265            ['(PAST SOFT DEADLINE)', '(soft deadline ok)'][int(soft_diff > 0)] if valid_soft_deadline else "",
266            ['(PAST DEADLINE)', '(deadline ok)'][int(diff > 0)] if valid_deadline else ""))
267        if valid_deadline:
268            if kern.arch == 'x86_64':
269                print('Next deadline set at: {:#018x}. Timer call list:'.format(rt_timer.when_set))
270            dumpTimerList(rt_timer.queue, p)
271        p = p.processor_list
272    print("-" * 82)
273    longtermTimers()
274    print("Running timers:")
275    ShowRunningTimers()
276
277@header("{:<6s}  {:^18s} {:^18s}".format("cpu_id", "Processor", "cpu_data") )
278@lldb_command('showcpudata')
279def ShowCPUData(cmd_args=[]):
280    """ Prints the CPU Data struct of each processor
281        Passing a CPU ID prints the CPU Data of just that CPU
282        Usage: (lldb) showcpudata [cpu id]
283    """
284
285    format_string = "{:>#6d}: {: <#018x} {: <#018x}"
286
287    find_cpu_id = None
288
289    if cmd_args:
290        find_cpu_id = ArgumentStringToInt(cmd_args[0])
291
292    print (ShowCPUData.header)
293
294    processors = [p for p in IterateLinkedList(kern.globals.processor_list, 'processor_list')]
295
296    processors.sort(key=lambda p: p.cpu_id)
297
298    for processor in processors:
299        cpu_id = int(processor.cpu_id)
300
301        if find_cpu_id and cpu_id != find_cpu_id:
302            continue
303
304        cpu_data = GetCpuDataForCpuID(cpu_id)
305
306        print (format_string.format(cpu_id, processor, cpu_data))
307
308@lldb_command('showtimerwakeupstats')
309def showTimerWakeupStats(cmd_args=None):
310    """
311    Displays interrupt and platform idle wakeup frequencies
312    associated with each thread, timer time-to-deadline frequencies, and
313    CPU time with user/system break down where applicable, with thread tags.
314    """
315    for task in kern.tasks:
316        proc = GetProcFromTask(task)
317        print(dereference(task))
318        (user_time, sys_time) = GetTaskTerminatedUserSysTime(task)
319        print('{:d}({:s}), terminated thread timer wakeups: {:d} {:d} 2ms: {:d} 5ms: {:d} UT: {:d} ST: {:d}'.format(
320            GetProcPID(proc),
321            GetProcName(proc),
322# Commented-out references below to be addressed by rdar://13009660.
323            0, #task.task_interrupt_wakeups,
324            0, #task.task_platform_idle_wakeups,
325            task.task_timer_wakeups_bin_1,
326            task.task_timer_wakeups_bin_2,
327            user_time, sys_time))
328        tot_wakes = 0 #task.task_interrupt_wakeups
329        tot_platform_wakes = 0 #task.task_platform_idle_wakeups
330        for thread in IterateQueue(task.threads, 'thread_t', 'task_threads'):
331##        if thread.thread_interrupt_wakeups == 0:
332##              continue
333            (user_time, sys_time) = GetThreadUserSysTime(thread)
334            print('\tThread ID 0x{:x}, Tag 0x{:x}, timer wakeups: {:d} {:d} {:d} {:d} <2ms: {:d}, <5ms: {:d} UT: {:d} ST: {:d}'.format(
335                thread.thread_id,
336                thread.thread_tag,
337                0, #thread.thread_interrupt_wakeups,
338                0, #thread.thread_platform_idle_wakeups,
339                0, #thread.thread_callout_interrupt_wakeups,
340                0, #thread.thread_callout_platform_idle_wakeups,
341                0,0,0,0,
342                thread.thread_timer_wakeups_bin_1,
343                thread.thread_timer_wakeups_bin_2,
344                user_time, sys_time))
345            tot_wakes += 0 #thread.thread_interrupt_wakeups
346            tot_platform_wakes += 0 #thread.thread_platform_idle_wakeups
347        print('Task total wakeups: {:d} {:d}'.format(
348            tot_wakes, tot_platform_wakes))
349
350@lldb_command('showrunningtimers')
351def ShowRunningTimers(cmd_args=None):
352    """
353    Print the state of all running timers.
354
355    Usage: showrunningtimers
356    """
357    processor_array = kern.globals.processor_array
358
359    recent_timestamp = GetRecentTimestamp()
360
361    hdr = '{:4s} {:^10s} {:^18s} {:^18s} {:^18s} {:^18s}'
362    print(hdr.format('CPU', 'State', 'Quantum', 'To Go', 'kperf', 'To Go', 'Hard To Go'))
363
364    cpu = '{:3d}: {:^10s} {:18d} {:16.06f} {:18d} {:16.06f}'
365
366    i = 0
367    while processor_array[i] != 0:
368        processor = processor_array[i]
369
370        statestr = 'runnning' if processor.running_timers_active else 'idle'
371
372        quantum = unsigned(processor.running_timers[0].tc_pqlink.deadline)
373        quantumdiff = signed(quantum) - signed(recent_timestamp)
374        quantumdiff_s = kern.GetNanotimeFromAbstime(quantumdiff) / 1000000000.0
375
376        kperf = unsigned(processor.running_timers[1].tc_pqlink.deadline)
377        kperfdiff = signed(kperf) - signed(recent_timestamp)
378        kperfdiff_s = kern.GetNanotimeFromAbstime(kperfdiff) / 1000000000.0
379
380        print (cpu.format(i, statestr, quantum, quantumdiff_s, kperf, kperfdiff_s))
381        i += 1
382
383def DoReadMsr64(msr_address, lcpu):
384    """ Read a 64-bit MSR from the specified CPU
385        Params:
386            msr_address: int - MSR index to read from
387            lcpu: int - CPU identifier
388        Returns:
389            64-bit value read from the MSR
390    """
391    result = 0xbad10ad
392
393    if "kdp" != GetConnectionProtocol():
394        print("Target is not connected over kdp. Cannot read MSR.")
395        return result
396
397    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
398    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
399    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
400    if not WriteInt32ToMemoryAddress(0, input_address):
401        print("DoReadMsr64() failed to write 0 to input_address")
402        return result
403
404    kdp_pkt_size = GetType('kdp_readmsr64_req_t').GetByteSize()
405    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
406        print("DoReadMsr64() failed to write kdp_pkt_size")
407        return result
408
409    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_readmsr64_req_t *')
410    header_value = GetKDPPacketHeaderInt(
411        request=GetEnumValue('kdp_req_t::KDP_READMSR64'),
412        length=kdp_pkt_size)
413
414    if not WriteInt64ToMemoryAddress(header_value, int(addressof(kgm_pkt.hdr))):
415        print("DoReadMsr64() failed to write header_value")
416        return result
417    if not WriteInt32ToMemoryAddress(msr_address, int(addressof(kgm_pkt.address))):
418        print("DoReadMsr64() failed to write msr_address")
419        return result
420    if not WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))):
421        print("DoReadMsr64() failed to write lcpu")
422        return result
423    if not WriteInt32ToMemoryAddress(1, input_address):
424        print("DoReadMsr64() failed to write to input_address")
425        return result
426
427    result_pkt = Cast(addressof(kern.globals.manual_pkt.data),
428        'kdp_readmsr64_reply_t *')
429    if (result_pkt.error == 0):
430        result = dereference(Cast(addressof(result_pkt.data), 'uint64_t *'))
431    else:
432        print("DoReadMsr64() result_pkt.error != 0")
433    return result
434
435def DoWriteMsr64(msr_address, lcpu, data):
436    """ Write a 64-bit MSR
437        Params:
438            msr_address: int - MSR index to write to
439            lcpu: int - CPU identifier
440            data: int - value to write
441        Returns:
442            True upon success, False if error
443    """
444    if "kdp" != GetConnectionProtocol():
445        print("Target is not connected over kdp. Cannot write MSR.")
446        return False
447
448    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
449    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
450    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
451    if not WriteInt32ToMemoryAddress(0, input_address):
452        print("DoWriteMsr64() failed to write 0 to input_address")
453        return False
454
455    kdp_pkt_size = GetType('kdp_writemsr64_req_t').GetByteSize()
456    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
457        print("DoWriteMsr64() failed to kdp_pkt_size")
458        return False
459
460    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_writemsr64_req_t *')
461    header_value = GetKDPPacketHeaderInt(
462        request=GetEnumValue('kdp_req_t::KDP_WRITEMSR64'),
463        length=kdp_pkt_size)
464
465    if not WriteInt64ToMemoryAddress(header_value, int(addressof(kgm_pkt.hdr))):
466        print("DoWriteMsr64() failed to write header_value")
467        return False
468    if not WriteInt32ToMemoryAddress(msr_address, int(addressof(kgm_pkt.address))):
469        print("DoWriteMsr64() failed to write msr_address")
470        return False
471    if not WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))):
472        print("DoWriteMsr64() failed to write lcpu")
473        return False
474    if not WriteInt64ToMemoryAddress(data, int(addressof(kgm_pkt.data))):
475        print("DoWriteMsr64() failed to write data")
476        return False
477    if not WriteInt32ToMemoryAddress(1, input_address):
478        print("DoWriteMsr64() failed to write to input_address")
479        return False
480
481    result_pkt = Cast(addressof(kern.globals.manual_pkt.data),
482        'kdp_writemsr64_reply_t *')
483    if not result_pkt.error == 0:
484        print("DoWriteMsr64() error received in reply packet")
485        return False
486
487    return True
488
489@lldb_command('readmsr64')
490def ReadMsr64(cmd_args=None):
491    """ Read the specified MSR. The CPU can be optionally specified
492        Syntax: readmsr64 <msr> [lcpu]
493    """
494    if cmd_args is None or len(cmd_args) < 1:
495        print(ReadMsr64.__doc__)
496        return
497
498    msr_address = ArgumentStringToInt(cmd_args[0])
499    if len(cmd_args) > 1:
500        lcpu = ArgumentStringToInt(cmd_args[1])
501    else:
502        lcpu = int(xnudefines.lcpu_self)
503
504    msr_value = DoReadMsr64(msr_address, lcpu)
505    print("MSR[{:x}]: {:#016x}".format(msr_address, msr_value))
506
507@lldb_command('writemsr64')
508def WriteMsr64(cmd_args=None):
509    """ Write the specified MSR. The CPU can be optionally specified
510        Syntax: writemsr64 <msr> <value> [lcpu]
511    """
512    if cmd_args is None or len(cmd_args) < 2:
513        print(WriteMsr64.__doc__)
514        return
515    msr_address = ArgumentStringToInt(cmd_args[0])
516    write_val = ArgumentStringToInt(cmd_args[1])
517    if len(cmd_args) > 2:
518        lcpu = ArgumentStringToInt(cmd_args[2])
519    else:
520        lcpu = xnudefines.lcpu_self
521
522    if not DoWriteMsr64(msr_address, lcpu, write_val):
523        print("writemsr64 FAILED")
524
525
526@caching.cache_statically
527def GetTimebaseInfo(target=None):
528    if kern.arch == 'x86_64':
529        return 1, 1
530
531    rtclockdata_addr = kern.GetLoadAddressForSymbol('RTClockData')
532    rtc = kern.GetValueFromAddress(
533        rtclockdata_addr, 'struct _rtclock_data_ *')
534    tb = rtc.rtc_timebase_const
535    return int(tb.numer), int(tb.denom)
536
537
538def PrintIteratedElem(i, elem, elem_type, do_summary, summary, regex):
539    try:
540        if do_summary and summary:
541            s = summary(elem)
542            if regex:
543                if regex.match(s):
544                    print("[{:d}] {:s}".format(i, s))
545            else:
546                print("[{:d}] {:s}".format(i, s))
547        else:
548            if regex:
549                if regex.match(str(elem)):
550                    print("[{:4d}] ({:s}){:#x}".format(i, elem_type, unsigned(elem)))
551            else:
552                print("[{:4d}] ({:s}){:#x}".format(i, elem_type, unsigned(elem)))
553    except:
554        print("Exception while looking at elem {:#x}".format(unsigned(elem)))
555        return
556
557@lldb_command('q_iterate', "LQSG:")
558def QIterate(cmd_args=None, cmd_options={}):
559    """ Iterate over a LinkageChain or Queue (osfmk/kern/queue.h method 1 or 2 respectively)
560        This is equivalent to the qe_foreach_element() macro
561        usage:
562            iterate [options] {queue_head_ptr} {element_type} {field_name}
563        option:
564            -L    iterate over a linkage chain (method 1) [default]
565            -Q    iterate over a queue         (method 2)
566
567            -S    auto-summarize known types
568            -G    regex to filter the output
569        e.g.
570            iterate_linkage `&coalitions_q` 'coalition *' coalitions
571    """
572    if not cmd_args:
573        raise ArgumentError("usage: iterate_linkage {queue_head_ptr} {element_type} {field_name}")
574
575    qhead = kern.GetValueFromAddress(cmd_args[0], 'struct queue_entry *')
576    if not qhead:
577        raise ArgumentError("Unknown queue_head pointer: %r" % cmd_args)
578    elem_type = cmd_args[1]
579    field_name = cmd_args[2]
580    if not elem_type or not field_name:
581        raise ArgumentError("usage: iterate_linkage {queue_head_ptr} {element_type} {field_name}")
582
583    do_queue_iterate = False
584    do_linkage_iterate = True
585    if "-Q" in cmd_options:
586        do_queue_iterate = True
587        do_linkage_iterate = False
588    if "-L" in cmd_options:
589        do_queue_iterate = False
590        do_linkage_iterate = True
591
592    do_summary = False
593    if "-S" in cmd_options:
594        do_summary = True
595    regex = None
596    if "-G" in cmd_options:
597        regex = re.compile(".*{:s}.*".format(cmd_options["-G"]))
598        print("Looking for: {:s}".format(regex.pattern))
599
600    global lldb_summary_definitions
601    summary = None
602    if elem_type in lldb_summary_definitions:
603        summary = lldb_summary_definitions[elem_type]
604        if do_summary:
605            print(summary.header)
606
607    try:
608        i = 0
609        if do_linkage_iterate:
610            for elem in IterateLinkageChain(qhead, elem_type, field_name):
611                PrintIteratedElem(i, elem, elem_type, do_summary, summary, regex)
612                i = i + 1
613        elif do_queue_iterate:
614            for elem in IterateQueue(qhead, elem_type, field_name):
615                PrintIteratedElem(i, elem, elem_type, do_summary, summary, regex)
616                i = i + 1
617    except:
618        print("Exception while looking at queue_head: {:#x}".format(unsigned(qhead)))
619
620@lldb_command('lbrbt')
621def LBRBacktrace(cmd_args=None):
622    """
623        Prints symbolicated last branch records captured on Intel systems
624        from a core file. Will not work on a live system.
625        usage:
626            lbrbt
627        options:
628            None
629    """
630    DecoratedLBRStack = SymbolicateLBR()
631    if (DecoratedLBRStack):
632        print(DecoratedLBRStack)
633
634def SymbolicateLBR():
635    lbr_size_offset = 5
636    cpu_num_offset = 4
637    LBRMagic = 0x5352424C
638
639    try:
640        phys_carveout_addr = kern.GetLoadAddressForSymbol("phys_carveout")
641    except LookupError:
642        print("Last Branch Recoreds not present in this core file")
643        return None
644    try:
645        phys_carveout_md_addr = kern.GetLoadAddressForSymbol("panic_lbr_header")
646    except LookupError:
647        print("Last Branch Recoreds not present in this core file")
648        return None
649
650    metadata_ptr = kern.GetValueFromAddress(phys_carveout_md_addr, "uint64_t *")
651    metadata = kern.GetValueFromAddress(unsigned(metadata_ptr[0]), "uint8_t *")
652    carveout_ptr = kern.GetValueFromAddress(phys_carveout_addr, "uint64_t *")
653
654    metadata_hdr = kern.GetValueFromAddress(unsigned(metadata_ptr[0]), "uint32_t *")
655    if not (unsigned(metadata_hdr[0]) == LBRMagic):
656        print("'LBRS' not found at beginning of phys_carveout section, cannot proceed.")
657        return None
658
659    lbr_records = unsigned(carveout_ptr[0])
660
661    num_lbrs = int(metadata[lbr_size_offset])
662
663    header_line = "".join("{:49s} -> {:s}\n".format("From", "To"))
664    ncpus = int(metadata[cpu_num_offset])
665
666    output_lines = []
667
668    target = LazyTarget.GetTarget()
669
670    for cpu in range(ncpus):
671        start_addr_from = lbr_records + num_lbrs * 8 * cpu
672        start_addr_to = start_addr_from + num_lbrs * 8 * ncpus
673        from_lbr = kern.GetValueFromAddress(start_addr_from, "uint64_t *")
674        to_lbr = kern.GetValueFromAddress(start_addr_to, "uint64_t *")
675        for i in range(num_lbrs):
676            if (from_lbr[i] == 0x0 or to_lbr[i] == 0x0):
677                break
678            ## Replace newline with space to include inlined functions
679            ## in a trade off for longer output lines.
680            fprint = str(target.ResolveLoadAddress(int(from_lbr[i]))).replace('\n', ' ')
681            tprint = str(target.ResolveLoadAddress(int(to_lbr[i]))).replace('\n', ' ')
682            output_lines.append(''.join("({:x}) {:30s} -> ({:x}) {:30s}\n".format(from_lbr[i], fprint, to_lbr[i], tprint)))
683
684    return header_line + ''.join(output_lines)
685