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