xref: /xnu-8019.80.24/tools/lldbmacros/misc.py (revision a325d9c4a84054e40bbe985afedcb50ab80993ea)
1"""
2Miscellaneous (Intel) platform-specific commands.
3"""
4
5from xnu import *
6import xnudefines
7
8from scheduler import *
9
10@lldb_command('showlogstream')
11def showLogStream(cmd_args=None):
12    """
13    Dump the state of the kernel log stream
14    """
15    mbp = kern.globals.oslog_streambufp
16    print "streaming buffer space avail: {0:>#x} of {1:>#x} bytes\n".format(kern.globals.oslog_stream_buf_bytesavail, kern.globals.oslog_stream_buf_size)
17    print " read head: offset {0:>#x}\nwrite head: offset {1:>#x}\n".format(mbp.msg_bufr, mbp.msg_bufx)
18    count = 0
19    print "  id  timestamp   offset size off+size type metadata"
20    for entry in IterateSTAILQ_HEAD(kern.globals.oslog_stream_buf_head, "buf_entries"):
21        next_start = entry.offset + entry.size
22        if (next_start > 0x1000):
23            next_start = next_start - 0x1000
24        print "{0:>4d}: {1:<d}  {2:>5x} {3:>4d} {4:>5x} {5:<d}    {6:<d}".format(count, entry.timestamp, entry.offset, entry.size, next_start, entry.type, entry.metadata)
25        count = count + 1
26    print "found {} entries".format(count)
27
28    count = 0
29    for entry in IterateSTAILQ_HEAD(kern.globals.oslog_stream_free_head, "buf_entries"):
30        count = count + 1
31    print "free list: {} entries".format(count)
32
33    count = 0
34    for outer in IterateSTAILQ_HEAD(kern.globals.oslog_stream_buf_head, "buf_entries"):
35        for inner in IterateSTAILQ_HEAD(kern.globals.oslog_stream_buf_head, "buf_entries"):
36            if ((outer.offset > inner.offset) and
37                (outer.offset < inner.offset + inner.size)):
38                print "error: overlapping entries: {:>3x} <--> {:>3x}".format(outer.offset, inner.offset)
39        count = count + 1
40
41@lldb_command('showmcastate')
42def showMCAstate(cmd_args=None):
43    """
44    Print machine-check register state after MC exception.
45    """
46    if kern.arch != 'x86_64':
47        print "Not available for current architecture."
48        return
49
50    present = ["not present", "present"]
51    print 'MCA {:s}, control MSR {:s}, threshold status {:s}'.format(
52    present[int(kern.globals.mca_MCA_present)],
53    present[int(kern.globals.mca_control_MSR_present)],
54    present[int(kern.globals.mca_threshold_status_present)])
55    print '{:d} error banks, family code {:#0x}, machine-check dump state: {:d}'.format(
56        kern.globals.mca_error_bank_count,
57        kern.globals.mca_dump_state,
58        kern.globals.mca_family)
59    cpu = 0
60    while kern.globals.cpu_data_ptr[cpu]:
61        cd = kern.globals.cpu_data_ptr[cpu]
62        mc = cd.cpu_mca_state
63        if mc:
64            print 'CPU {:d}: mca_mcg_ctl: {:#018x} mca_mcg_status {:#018x}'.format(cpu, mc.mca_mcg_ctl, mc.mca_mcg_status.u64)
65            hdr = '{:<4s} {:<18s} {:<18s} {:<18s} {:<18s}'
66            val = '{:>3d}: {:#018x} {:#018x} {:#018x} {:#018x}'
67            print hdr.format('bank',
68                    'mca_mci_ctl',
69                    'mca_mci_status',
70                    'mca_mci_addr',
71                    'mca_mci_misc')
72            for i in range(int(kern.globals.mca_error_bank_count)):
73                bank = mc.mca_error_bank[i]
74                print val.format(i,
75                    bank.mca_mci_ctl,
76                    bank.mca_mci_status.u64,
77                    bank.mca_mci_addr,
78                    bank.mca_mci_misc)
79        print 'register state:'
80        reg = cd.cpu_desc_index.cdi_ktss.ist1 - sizeof('x86_saved_state_t')
81        print lldb_run_command('p/x *(x86_saved_state_t *) ' + hex(reg))
82        cpu = cpu + 1
83
84def dumpTimerList(mpqueue):
85    """
86    Utility function to dump the timer entries in list (anchor).
87    anchor is a struct mpqueue_head.
88    """
89
90    if mpqueue.count == 0:
91        print '(empty)'
92        return
93
94    thdr = ' {:<24s}{:<17s}{:<16s} {:<14s} {:<18s} count: {:d} '
95    tval = ' {:#018x}: {:16d} {:16d} {:s}{:3d}.{:09d}  ({:#018x})({:#018x}, {:#018x}) ({:s}) {:s}'
96
97    print thdr.format('Entry', 'Deadline', 'soft_deadline', 'Secs To Go', '(*func)(param0, param1)', mpqueue.count)
98
99    for timer_call in ParanoidIterateLinkageChain(mpqueue.head, 'struct timer_call *', 'tc_qlink'):
100        recent_timestamp = GetRecentTimestamp()
101        if (recent_timestamp < timer_call.tc_pqlink.deadline):
102            delta_sign = ' '
103            timer_fire = timer_call.tc_pqlink.deadline - recent_timestamp
104        else:
105            delta_sign = '-'
106            timer_fire = recent_timestamp - timer_call.tc_pqlink.deadline
107
108        func_name = kern.Symbolicate(timer_call.tc_func)
109
110        extra_string = ""
111
112        strip_func = kern.StripKernelPAC(unsigned(timer_call.tc_func))
113
114        func_syms = kern.SymbolicateFromAddress(strip_func)
115        # returns an array of SBSymbol
116
117        if func_syms and func_syms[0] :
118            func_sym = func_syms[0]
119            func_name = func_sym.GetName()
120            try :
121
122                if "thread_call_delayed_timer" in func_name :
123                    group = Cast(timer_call.tc_param0, 'struct thread_call_group *')
124                    flavor = Cast(timer_call.tc_param1, 'thread_call_flavor_t')
125
126                    # There's got to be a better way to stringify the enum
127                    flavorname = str(flavor).partition(" = ")[2]
128
129                    extra_string += "{:s} {:s}".format(group.tcg_name, flavorname)
130
131                if "thread_timer_expire" in func_name :
132                    thread = Cast(timer_call.tc_param0, 'thread_t')
133
134                    tid = thread.thread_id
135                    name = GetThreadName(thread)
136                    pid = GetProcPIDForTask(thread.t_tro.tro_task)
137                    procname = GetProcNameForTask(thread.t_tro.tro_task)
138
139                    extra_string += "thread: 0x{:x} {:s} task:{:s}[{:d}]".format(
140                            tid, name, procname, pid)
141            except:
142                print "exception generating extra_string for call: {:#018x}".format(timer_call)
143                if dumpTimerList.enable_debug :
144                    raise
145
146        tval = ' {:#018x}: {:16d} {:16d} {:s}{:3d}.{:09d}  ({:#018x})({:#018x},{:#018x}) ({:s}) {:s}'
147        print tval.format(timer_call,
148            timer_call.tc_pqlink.deadline,
149            timer_call.tc_soft_deadline,
150            delta_sign,
151            timer_fire/1000000000,
152            timer_fire%1000000000,
153            timer_call.tc_func,
154            timer_call.tc_param0,
155            timer_call.tc_param1,
156            func_name, extra_string)
157
158dumpTimerList.enable_debug = False
159
160def GetCpuDataForCpuID(cpu_id):
161    """
162    Find struct cpu_data for a CPU
163    ARM is complicated
164    """
165    if kern.arch == 'x86_64':
166        cpu_data = kern.globals.cpu_data_ptr[cpu_id]
167        return cpu_data
168    elif kern.arch.startswith('arm'):
169        data_entries_addr = kern.GetLoadAddressForSymbol('CpuDataEntries')
170        data_entries = kern.GetValueFromAddress(data_entries_addr, 'cpu_data_entry_t *')
171        data_entry = data_entries[cpu_id];
172        cpu_data_addr = data_entry.cpu_data_vaddr
173        return Cast(cpu_data_addr, 'cpu_data_t*')
174
175@lldb_command('longtermtimers')
176def longtermTimers(cmd_args=None):
177    """
178    Print details of long-term timers and stats.
179    """
180
181    lt = kern.globals.timer_longterm
182    ltt = lt.threshold
183    EndofAllTime = long(-1)
184    if long(ltt.interval) == EndofAllTime:
185        print "Longterm timers disabled"
186        return
187
188    if lt.escalates > 0:
189        ratio = lt.enqueues / lt.escalates
190    else:
191        ratio = lt.enqueues
192    print     'Longterm timer object: {:#018x}'.format(addressof(lt))
193    print     ' queue count         : {:d}'    .format(lt.queue.count)
194    print     ' number of enqueues  : {:d}'    .format(lt.enqueues)
195    print     ' number of dequeues  : {:d}'    .format(lt.dequeues)
196    print     ' number of escalates : {:d}'    .format(lt.escalates)
197    print     ' enqueues/escalates  : {:d}'    .format(ratio)
198    print     ' threshold.interval  : {:d}'    .format(ltt.interval)
199    print     ' threshold.margin    : {:d}'    .format(ltt.margin)
200    print     ' scan_time           : {:#018x} ({:d})'.format(lt.scan_time, lt.scan_time)
201    if long(ltt.preempted) == EndofAllTime:
202        print ' threshold.preempted : None'
203    else:
204        print ' threshold.preempted : {:#018x} ({:d})'.format(ltt.preempted, ltt.preempted)
205    if long(ltt.deadline) == EndofAllTime:
206        print ' threshold.deadline  : None'
207    else:
208        print ' threshold.deadline  : {:#018x} ({:d})'.format(ltt.deadline, ltt.deadline)
209        print ' threshold.call      : {:#018x}'.format(ltt.call)
210        print ' actual deadline set : {:#018x} ({:d})'.format(ltt.deadline_set, ltt.deadline_set)
211    print     ' threshold.scans     : {:d}'    .format(ltt.scans)
212    print     ' threshold.preempts  : {:d}'    .format(ltt.preempts)
213    print     ' threshold.latency   : {:d}'    .format(ltt.latency)
214    print     '               - min : {:d}'    .format(ltt.latency_min)
215    print     '               - max : {:d}'    .format(ltt.latency_max)
216    dumpTimerList(lt.queue)
217
218
219@lldb_command('processortimers')
220def processorTimers(cmd_args=None):
221    """
222    Print details of processor timers, noting anything suspicious
223    Also include long-term timer details
224    """
225    hdr = '{:15s}{:<18s} {:<18s} {:<18s} {:<18s}'
226    print hdr.format('Processor #', 'Processor pointer', 'Last dispatch', 'Next deadline', 'Difference')
227    print "=" * 82
228    p = kern.globals.processor_list
229    EndOfAllTime = long(-1)
230    while p:
231        cpu = p.cpu_id
232        cpu_data = GetCpuDataForCpuID(cpu)
233        rt_timer = cpu_data.rtclock_timer
234        diff = long(rt_timer.deadline) - long(p.last_dispatch)
235        valid_deadline = long(rt_timer.deadline) != EndOfAllTime
236        tmr = 'Processor {:<3d}: {:#018x} {:#018x} {:18s} {:18s} {:s}'
237        print tmr.format(cpu,
238            p,
239            p.last_dispatch,
240            "{:#018x}".format(rt_timer.deadline) if valid_deadline else "None",
241            "{:#018x}".format(diff) if valid_deadline else "N/A",
242            ['(PAST DEADLINE)', '(ok)'][int(diff > 0)] if valid_deadline else "")
243        if valid_deadline:
244            if kern.arch == 'x86_64':
245                print 'Next deadline set at: {:#018x}. Timer call list:'.format(rt_timer.when_set)
246            dumpTimerList(rt_timer.queue)
247        p = p.processor_list
248    print "-" * 82
249    longtermTimers()
250    ShowRunningTimers()
251
252
253@lldb_command('showtimerwakeupstats')
254def showTimerWakeupStats(cmd_args=None):
255    """
256    Displays interrupt and platform idle wakeup frequencies
257    associated with each thread, timer time-to-deadline frequencies, and
258    CPU time with user/system break down where applicable, with thread tags.
259    """
260    for task in kern.tasks:
261        proc = Cast(task.bsd_info, 'proc_t')
262        print dereference(task)
263        print '{:d}({:s}), terminated thread timer wakeups: {:d} {:d} 2ms: {:d} 5ms: {:d} UT: {:d} ST: {:d}'.format(
264            GetProcPID(proc),
265            GetProcName(proc),
266# Commented-out references below to be addressed by rdar://13009660.
267            0, #task.task_interrupt_wakeups,
268            0, #task.task_platform_idle_wakeups,
269            task.task_timer_wakeups_bin_1,
270            task.task_timer_wakeups_bin_2,
271            task.total_user_time,
272            task.total_system_time)
273        tot_wakes = 0 #task.task_interrupt_wakeups
274        tot_platform_wakes = 0 #task.task_platform_idle_wakeups
275        for thread in IterateQueue(task.threads, 'thread_t', 'task_threads'):
276##        if thread.thread_interrupt_wakeups == 0:
277##              continue
278            print '\tThread ID 0x{:x}, Tag 0x{:x}, timer wakeups: {:d} {:d} {:d} {:d} <2ms: {:d}, <5ms: {:d} UT: {:d} ST: {:d}'.format(
279                thread.thread_id,
280                thread.thread_tag,
281                0, #thread.thread_interrupt_wakeups,
282                0, #thread.thread_platform_idle_wakeups,
283                0, #thread.thread_callout_interrupt_wakeups,
284                0, #thread.thread_callout_platform_idle_wakeups,
285                0,0,0,0,
286                thread.thread_timer_wakeups_bin_1,
287                thread.thread_timer_wakeups_bin_2,
288                thread.user_timer.all_bits,
289                thread.system_timer.all_bits)
290            tot_wakes += 0 #thread.thread_interrupt_wakeups
291            tot_platform_wakes += 0 #thread.thread_platform_idle_wakeups
292        print 'Task total wakeups: {:d} {:d}'.format(
293            tot_wakes, tot_platform_wakes)
294
295@lldb_command('showrunningtimers')
296def ShowRunningTimers(cmd_args=None):
297    """
298    Print the state of all running timers.
299
300    Usage: showrunningtimers
301    """
302    pset = addressof(kern.globals.pset0)
303    processor_array = kern.globals.processor_array
304
305    i = 0
306    while processor_array[i] != 0:
307        processor = processor_array[i]
308        print('{}: {}'.format(
309                i, 'on' if processor.running_timers_active else 'off'))
310        print('\tquantum: {}'.format(
311                unsigned(processor.running_timers[0].tc_pqlink.deadline)))
312        print('\tkperf: {}'.format(
313                unsigned(processor.running_timers[1].tc_pqlink.deadline)))
314        i += 1
315
316def DoReadMsr64(msr_address, lcpu):
317    """ Read a 64-bit MSR from the specified CPU
318        Params:
319            msr_address: int - MSR index to read from
320            lcpu: int - CPU identifier
321        Returns:
322            64-bit value read from the MSR
323    """
324    result = 0xbad10ad
325
326    if "kdp" != GetConnectionProtocol():
327        print "Target is not connected over kdp. Cannot read MSR."
328        return result
329
330    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
331    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
332    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
333    if not WriteInt32ToMemoryAddress(0, input_address):
334        print "DoReadMsr64() failed to write 0 to input_address"
335        return result
336
337    kdp_pkt_size = GetType('kdp_readmsr64_req_t').GetByteSize()
338    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
339        print "DoReadMsr64() failed to write kdp_pkt_size"
340        return result
341
342    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_readmsr64_req_t *')
343    header_value = GetKDPPacketHeaderInt(
344        request=GetEnumValue('kdp_req_t::KDP_READMSR64'),
345        length=kdp_pkt_size)
346
347    if not WriteInt64ToMemoryAddress(header_value, int(addressof(kgm_pkt.hdr))):
348        print "DoReadMsr64() failed to write header_value"
349        return result
350    if not WriteInt32ToMemoryAddress(msr_address, int(addressof(kgm_pkt.address))):
351        print "DoReadMsr64() failed to write msr_address"
352        return result
353    if not WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))):
354        print "DoReadMsr64() failed to write lcpu"
355        return result
356    if not WriteInt32ToMemoryAddress(1, input_address):
357        print "DoReadMsr64() failed to write to input_address"
358        return result
359
360    result_pkt = Cast(addressof(kern.globals.manual_pkt.data),
361        'kdp_readmsr64_reply_t *')
362    if (result_pkt.error == 0):
363        result = dereference(Cast(addressof(result_pkt.data), 'uint64_t *'))
364    else:
365        print "DoReadMsr64() result_pkt.error != 0"
366    return result
367
368def DoWriteMsr64(msr_address, lcpu, data):
369    """ Write a 64-bit MSR
370        Params:
371            msr_address: int - MSR index to write to
372            lcpu: int - CPU identifier
373            data: int - value to write
374        Returns:
375            True upon success, False if error
376    """
377    if "kdp" != GetConnectionProtocol():
378        print "Target is not connected over kdp. Cannot write MSR."
379        return False
380
381    input_address = unsigned(addressof(kern.globals.manual_pkt.input))
382    len_address = unsigned(addressof(kern.globals.manual_pkt.len))
383    data_address = unsigned(addressof(kern.globals.manual_pkt.data))
384    if not WriteInt32ToMemoryAddress(0, input_address):
385        print "DoWriteMsr64() failed to write 0 to input_address"
386        return False
387
388    kdp_pkt_size = GetType('kdp_writemsr64_req_t').GetByteSize()
389    if not WriteInt32ToMemoryAddress(kdp_pkt_size, len_address):
390        print "DoWriteMsr64() failed to kdp_pkt_size"
391        return False
392
393    kgm_pkt = kern.GetValueFromAddress(data_address, 'kdp_writemsr64_req_t *')
394    header_value = GetKDPPacketHeaderInt(
395        request=GetEnumValue('kdp_req_t::KDP_WRITEMSR64'),
396        length=kdp_pkt_size)
397
398    if not WriteInt64ToMemoryAddress(header_value, int(addressof(kgm_pkt.hdr))):
399        print "DoWriteMsr64() failed to write header_value"
400        return False
401    if not WriteInt32ToMemoryAddress(msr_address, int(addressof(kgm_pkt.address))):
402        print "DoWriteMsr64() failed to write msr_address"
403        return False
404    if not WriteInt16ToMemoryAddress(lcpu, int(addressof(kgm_pkt.lcpu))):
405        print "DoWriteMsr64() failed to write lcpu"
406        return False
407    if not WriteInt64ToMemoryAddress(data, int(addressof(kgm_pkt.data))):
408        print "DoWriteMsr64() failed to write data"
409        return False
410    if not WriteInt32ToMemoryAddress(1, input_address):
411        print "DoWriteMsr64() failed to write to input_address"
412        return False
413
414    result_pkt = Cast(addressof(kern.globals.manual_pkt.data),
415        'kdp_writemsr64_reply_t *')
416    if not result_pkt.error == 0:
417        print "DoWriteMsr64() error received in reply packet"
418        return False
419
420    return True
421
422@lldb_command('readmsr64')
423def ReadMsr64(cmd_args=None):
424    """ Read the specified MSR. The CPU can be optionally specified
425        Syntax: readmsr64 <msr> [lcpu]
426    """
427    if cmd_args == None or len(cmd_args) < 1:
428        print ReadMsr64.__doc__
429        return
430
431    msr_address = ArgumentStringToInt(cmd_args[0])
432    if len(cmd_args) > 1:
433        lcpu = ArgumentStringToInt(cmd_args[1])
434    else:
435        lcpu = int(xnudefines.lcpu_self)
436
437    msr_value = DoReadMsr64(msr_address, lcpu)
438    print "MSR[{:x}]: {:#016x}".format(msr_address, msr_value)
439
440@lldb_command('writemsr64')
441def WriteMsr64(cmd_args=None):
442    """ Write the specified MSR. The CPU can be optionally specified
443        Syntax: writemsr64 <msr> <value> [lcpu]
444    """
445    if cmd_args == None or len(cmd_args) < 2:
446        print WriteMsr64.__doc__
447        return
448    msr_address = ArgumentStringToInt(cmd_args[0])
449    write_val = ArgumentStringToInt(cmd_args[1])
450    if len(cmd_args) > 2:
451        lcpu = ArgumentStringToInt(cmd_args[2])
452    else:
453        lcpu = xnudefines.lcpu_self
454
455    if not DoWriteMsr64(msr_address, lcpu, write_val):
456        print "writemsr64 FAILED"
457
458def GetKernelDebugBufferEntry(kdbg_entry):
459    """ Extract the information from given kernel debug buffer entry and return the summary
460        params:
461            kdebug_entry - kd_buf - address of kernel debug buffer entry
462        returns:
463            str - formatted output information of kd_buf entry
464    """
465    out_str = ""
466    code_info_str = ""
467    kdebug_entry = kern.GetValueFromAddress(kdbg_entry, 'kd_buf *')
468    debugid     = kdebug_entry.debugid
469    kdebug_arg1 = kdebug_entry.arg1
470    kdebug_arg2 = kdebug_entry.arg2
471    kdebug_arg3 = kdebug_entry.arg3
472    kdebug_arg4 = kdebug_entry.arg4
473
474    if kern.arch == 'x86_64' or kern.arch.startswith('arm64'):
475        kdebug_cpu   = kdebug_entry.cpuid
476        ts_hi        = (kdebug_entry.timestamp >> 32) & 0xFFFFFFFF
477        ts_lo        = kdebug_entry.timestamp & 0xFFFFFFFF
478    else:
479        kdebug_cpu   = (kdebug_entry.timestamp >> 56)
480        ts_hi        = (kdebug_entry.timestamp >> 32) & 0x00FFFFFF
481        ts_lo        = kdebug_entry.timestamp & 0xFFFFFFFF
482
483    kdebug_class    = (debugid >> 24) & 0x000FF
484    kdebug_subclass = (debugid >> 16) & 0x000FF
485    kdebug_code     = (debugid >>  2) & 0x03FFF
486    kdebug_qual     = (debugid) & 0x00003
487
488    if kdebug_qual == 0:
489        kdebug_qual = '-'
490    elif kdebug_qual == 1:
491        kdebug_qual = 'S'
492    elif kdebug_qual == 2:
493        kdebug_qual = 'E'
494    elif kdebug_qual == 3:
495        kdebug_qual = '?'
496
497    # preamble and qual
498    out_str += "{:<#20x} {:>6d} {:>#12x} ".format(kdebug_entry, kdebug_cpu, kdebug_entry.arg5)
499    out_str += " {:#010x}{:08x} {:>6s} ".format(ts_hi, ts_lo, kdebug_qual)
500
501    # class
502    kdbg_class = ""
503    if kdebug_class == 1:
504        kdbg_class = "MACH"
505    elif kdebug_class == 2:
506        kdbg_class = "NET "
507    elif kdebug_class == 3:
508        kdbg_class = "FS  "
509    elif kdebug_class == 4:
510        kdbg_class = "BSD "
511    elif kdebug_class == 5:
512        kdbg_class = "IOK "
513    elif kdebug_class == 6:
514        kdbg_class = "DRVR"
515    elif kdebug_class == 7:
516        kdbg_class = "TRAC"
517    elif kdebug_class == 8:
518        kdbg_class = "DLIL"
519    elif kdebug_class == 9:
520        kdbg_class = "WQ  "
521    elif kdebug_class == 10:
522        kdbg_class = "CS  "
523    elif kdebug_class == 11:
524        kdbg_class = "CG  "
525    elif kdebug_class == 20:
526        kdbg_class = "MISC"
527    elif kdebug_class == 30:
528        kdbg_class = "SEC "
529    elif kdebug_class == 31:
530        kdbg_class = "DYLD"
531    elif kdebug_class == 32:
532        kdbg_class = "QT  "
533    elif kdebug_class == 33:
534        kdbg_class = "APPS"
535    elif kdebug_class == 34:
536        kdbg_class = "LAUN"
537    elif kdebug_class == 36:
538        kdbg_class = "PPT "
539    elif kdebug_class == 37:
540        kdbg_class = "PERF"
541    elif kdebug_class == 38:
542        kdbg_class = "IMP "
543    elif kdebug_class == 39:
544        kdbg_class = "PCTL"
545    elif kdebug_class == 40:
546        kdbg_class = "BANK"
547    elif kdebug_class == 41:
548        kdbg_class = "XPC "
549    elif kdebug_class == 42:
550        kdbg_class = "ATM "
551    elif kdebug_class == 128:
552        kdbg_class = "ANS "
553    elif kdebug_class == 129:
554        kdbg_class = "SIO "
555    elif kdebug_class == 130:
556        kdbg_class = "SEP "
557    elif kdebug_class == 131:
558        kdbg_class = "ISP "
559    elif kdebug_class == 132:
560        kdbg_class = "OSCA"
561    elif kdebug_class == 133:
562        kdbg_class = "EGFX"
563    elif kdebug_class == 255:
564        kdbg_class = "MIG "
565    else:
566        out_str += "{:^#10x} ".format(kdebug_class)
567
568    if kdbg_class:
569        out_str += "{:^10s} ".format(kdbg_class)
570
571    # subclass and code
572    out_str += " {:>#5x} {:>8d}   ".format(kdebug_subclass, kdebug_code)
573
574    # space for debugid-specific processing
575    code_info_str += "arg1={:#010x} ".format(kdebug_arg1)
576    code_info_str += "arg2={:#010x} ".format(kdebug_arg2)
577    code_info_str += "arg3={:#010x} ".format(kdebug_arg3)
578    code_info_str += "arg4={:#010x} ".format(kdebug_arg4)
579
580    # finish up
581    out_str += "{:<25s}\n".format(code_info_str)
582    return out_str
583
584@lldb_command('showkerneldebugbuffercpu')
585@header("{0: ^20s} {1: >6s} {2: >12s} {3: ^20s} {4: >6s} {5: ^10s} {6: >5s} {7: >8s} {8: ^25s}".
586    format('kd_buf', 'CPU', 'Thread', 'Timestamp', 'S/E', 'Class', 'Sub', 'Code', 'Code Specific Info'))
587def ShowKernelDebugBufferCPU(cmd_args=None):
588    """ Prints the last N entries in the kernel debug buffer for specified cpu
589        Syntax: showkerneldebugbuffercpu <cpu_num> <count>
590    """
591    if cmd_args == None or len(cmd_args) < 2:
592        raise ArgumentError("Invalid arguments passed.")
593
594    out_str = ""
595    kdbg_str = ""
596    cpu_number = ArgumentStringToInt(cmd_args[0])
597    entry_count = ArgumentStringToInt(cmd_args[1])
598    debugentriesfound = 0
599    if (kern.globals.kd_ctrl_page_trace.kdebug_flags & xnudefines.KDBG_BUFINIT):
600        out_str += ShowKernelDebugBufferCPU.header + "\n"
601        if entry_count == 0:
602            out_str += "<count> is 0, dumping 50 entries\n"
603            entry_count = 50
604
605        if cpu_number >= kern.globals.kd_ctrl_page_trace.kdebug_cpus:
606            kdbg_str += "cpu number too big\n"
607        else:
608            kdbp = addressof(kern.globals.kd_data_page_trace.kdbip[cpu_number])
609            kdsp = kdbp.kd_list_head
610            while ((kdsp.raw != 0 and kdsp.raw != 0x00000000ffffffff) and (entry_count > 0)):
611                kd_buffer = kern.globals.kd_data_page_trace.kd_bufs[kdsp.buffer_index]
612                kdsp_actual = addressof(kd_buffer.kdsb_addr[kdsp.offset])
613                if kdsp_actual.kds_readlast != kdsp_actual.kds_bufindx:
614                    kds_buf = kdsp_actual.kds_records[kdsp_actual.kds_bufindx]
615                    kds_bufptr = addressof(kds_buf)
616                    while (entry_count > 0) and \
617                        (unsigned(kds_bufptr) > unsigned(addressof(kdsp_actual.kds_records[kdsp_actual.kds_readlast]))):
618                        kds_bufptr = kds_bufptr - sizeof(kds_buf)
619                        entry_count = entry_count - 1
620                        kdbg_str += GetKernelDebugBufferEntry(kds_bufptr)
621                kdsp = kdsp_actual.kds_next
622    else:
623        kdbg_str += "Trace buffer not enabled for CPU {:d}\n".format(cpu_number)
624
625    if kdbg_str:
626        out_str += kdbg_str
627        print out_str
628
629@lldb_command('showkerneldebugbuffer')
630def ShowKernelDebugBuffer(cmd_args=None):
631    """ Prints the last N entries in the kernel debug buffer per cpu
632        Syntax: showkerneldebugbuffer <count>
633    """
634    if cmd_args == None or len(cmd_args) < 1:
635        raise ArgumentError("Invalid arguments passed.")
636
637    if (kern.globals.kd_ctrl_page_trace.kdebug_flags & xnudefines.KDBG_BUFINIT):
638        entrycount = ArgumentStringToInt(cmd_args[0])
639        if entrycount == 0:
640            print "<count> is 0, dumping 50 entries per cpu\n"
641            entrycount = 50
642        cpu_num = 0
643        while cpu_num < kern.globals.kd_ctrl_page_trace.kdebug_cpus:
644            ShowKernelDebugBufferCPU([str(cpu_num), str(entrycount)])
645            cpu_num += 1
646    else:
647        print "Trace buffer not enabled\n"
648
649@lldb_command('dumprawtracefile','U:')
650def DumpRawTraceFile(cmd_args=[], cmd_options={}):
651    """
652        support for ktrace(1)
653
654        NB: trace is not wordsize flexible, so use ktrace(1) compiled for the compatible model,
655        e.g. if you dump from __LP64__ system, you will need to run ktrace(1) compiled __LP64__ to process the raw data file.
656
657        read the kernel's debug trace buffer, and dump to a "raw" ktrace(1) file
658        Usage: dumprawtracefile <output_filename>
659            -U <uptime> : specify system uptime in nsec, obtained e.g. from paniclog
660        Be patient, it is teh slow.
661
662        cf. kdbg_read()\bsd/kern/kdebug.c
663    """
664
665    if (kern.globals.kd_ctrl_page_trace.kdebug_flags & xnudefines.KDBG_BUFINIT) == 0 :
666        print "Trace buffer not enabled\n"
667        return
668
669    if ((kern.arch == "x86_64") or kern.arch.startswith("arm64")) :
670        lp64 = True
671    elif kern.arch == "arm" :
672        lp64 = False
673    else :
674        print "unknown kern.arch {:s}\n".format(kern.arch)
675        return
676
677    # Various kern.globals are hashed by address, to
678    #  a) avoid redundant kdp fetch from, and
679    #  b) avoid all stores to
680    # the target system kernel structures.
681    # Stores to hashed structures remain strictly local to the lldb host,
682    # they are never written back to the target.
683    htab = {}
684
685    if lp64 :
686        KDBG_TIMESTAMP_MASK = 0xffffffffffffffff
687        KDBG_CPU_SHIFT      = 0
688    else :
689        KDBG_TIMESTAMP_MASK = 0x00ffffffffffffff
690        KDBG_CPU_SHIFT      = 56
691
692    barrier_min     = 0
693    barrier_max     = 0
694    out_of_events       = False
695    lostevents      = False
696    lostevent_timestamp = 0
697    lostevent_debugid   = (((xnudefines.DBG_TRACE & 0xff) << 24) | ((xnudefines.DBG_TRACE_INFO & 0xff) << 16) | ((2 & 0x3fff)  << 2)) # 0x01020008
698    events_count_lost   = 0
699    events_count_found  = 0
700
701    opt_verbose = config['verbosity']
702    opt_progress = (opt_verbose > vHUMAN) and (opt_verbose < vDETAIL)
703    progress_count = 0
704    progress_stride = 32
705
706    output_filename = str(cmd_args[0])
707    if opt_verbose > vHUMAN :
708        print "output file : {:s}".format(output_filename)
709    wfd = open(output_filename, "wb")
710
711    uptime = long(-1)
712    if "-U" in cmd_options:
713        uptime = long(cmd_options["-U"])
714    if opt_verbose > vHUMAN :
715        print "uptime : {:d}".format(uptime)
716
717    nkdbufs = kern.globals.kd_data_page_trace.nkdbufs
718
719    kd_ctrl_page_trace = kern.globals.kd_ctrl_page_trace
720    if not kd_ctrl_page_trace in htab :
721        htab[kd_ctrl_page_trace] = kern.globals.kd_ctrl_page_trace
722
723    if opt_verbose > vHUMAN :
724        print "kd_data_page_trace_nkdbufs {0:#x}, enabled {1:#x}, flags {2:#x}, cpus {3:#x}".format(nkdbufs, htab[kd_ctrl_page_trace].enabled, htab[kd_ctrl_page_trace].kdebug_flags, htab[kd_ctrl_page_trace].kdebug_cpus)
725
726    if nkdbufs == 0 :
727        print "0 kd_data_page_trace_nkdbufs, nothing extracted"
728        return
729
730    if htab[kd_ctrl_page_trace].enabled != 0 :
731        barrier_max = uptime & KDBG_TIMESTAMP_MASK
732
733        f = htab[kd_ctrl_page_trace].kdebug_flags
734        wrapped = f & xnudefines.KDBG_WRAPPED
735    if wrapped != 0 :
736        barrier_min = htab[kd_ctrl_page_trace].oldest_time
737        htab[kd_ctrl_page_trace].kdebug_flags = htab[kd_ctrl_page_trace].kdebug_flags & ~xnudefines.KDBG_WRAPPED
738        htab[kd_ctrl_page_trace].oldest_time = 0
739
740        for cpu in range(htab[kd_ctrl_page_trace].kdebug_cpus) :
741            kdbp = unsigned(addressof(kern.globals.kd_data_page_trace.kdbip[cpu]))
742            if not kdbp in htab :
743                htab[kdbp] = kern.globals.kd_data_page_trace.kdbip[cpu]
744
745            kdsp = htab[kdbp].kd_list_head.raw
746            if kdsp == xnudefines.KDS_PTR_NULL :
747                continue
748
749            ix = htab[kdbp].kd_list_head.buffer_index
750            off = htab[kdbp].kd_list_head.offset
751            kdsp_actual = unsigned(addressof(kern.globals.kd_data_page_trace.kd_bufs[ix].kdsb_addr[off]))
752            if not kdsp_actual in htab :
753                htab[kdsp_actual] = kern.globals.kd_data_page_trace.kd_bufs[ix].kdsb_addr[off]
754            htab[kdsp_actual].kds_lostevents = False
755
756
757    # generate trace file header; threadmap is stubbed/TBD
758    version_no = xnudefines.RAW_VERSION1
759    thread_count = 0
760    TOD_secs = uptime
761    TOD_usecs = 0
762    header = struct.pack('IIqI', version_no, thread_count, TOD_secs, TOD_usecs)
763    pad_bytes = 4096 - (len(header) & 4095)
764    header += "\x00" * pad_bytes
765    wfd.write(buffer(header))
766
767    count = kern.globals.kd_data_page_trace.nkdbufs
768    while count != 0 :
769        tempbuf = ""
770        tempbuf_number = 0
771        tempbuf_count = min(count, kern.globals.kd_ctrl_page_trace.kdebug_kdcopybuf_count)
772
773        # while space
774        while tempbuf_count != 0 :
775
776            if opt_progress == True :
777                progress_count += 1
778                if (progress_count % progress_stride) == 0 :
779                    sys.stderr.write('.')
780                    sys.stderr.flush()
781
782            earliest_time = 0xffffffffffffffff
783            min_kdbp = None
784            min_cpu = 0
785
786            # Check all CPUs
787            for cpu in range(htab[kd_ctrl_page_trace].kdebug_cpus) :
788
789                kdbp = unsigned(addressof(kern.globals.kd_data_page_trace.kdbip[cpu]))
790                if not kdbp in htab :
791                    htab[kdbp] = kern.globals.kd_data_page_trace.kdbip[cpu]
792
793                # Skip CPUs without data.
794                kdsp = htab[kdbp].kd_list_head
795                if kdsp.raw == xnudefines.KDS_PTR_NULL :
796                    continue
797
798                kdsp_shadow = kdsp
799
800                # Get from cpu data to buffer header to buffer
801                ix = kdsp.buffer_index
802                off = kdsp.offset
803                kdsp_actual = unsigned(addressof(kern.globals.kd_data_page_trace.kd_bufs[ix].kdsb_addr[off]))
804                if not kdsp_actual in htab :
805                    htab[kdsp_actual] = kern.globals.kd_data_page_trace.kd_bufs[ix].kdsb_addr[off]
806
807                kdsp_actual_shadow = kdsp_actual
808
809                # Skip buffer if there are no events left.
810                rcursor = htab[kdsp_actual].kds_readlast
811                if rcursor == htab[kdsp_actual].kds_bufindx :
812                    continue
813
814                t = htab[kdsp_actual].kds_records[rcursor].timestamp & KDBG_TIMESTAMP_MASK
815
816                # Ignore events that have aged out due to wrapping.
817                goto_next_cpu = False;
818                while (t < unsigned(barrier_min)) :
819                    r = htab[kdsp_actual].kds_readlast
820                    htab[kdsp_actual].kds_readlast = r + 1
821                    rcursor = r + 1
822
823                    if rcursor >= xnudefines.EVENTS_PER_STORAGE_UNIT :
824
825                        kdsp = htab[kdbp].kd_list_head
826                        if kdsp.raw == xnudefines.KDS_PTR_NULL :
827                            goto_next_cpu = True
828                            break
829
830                        kdsp_shadow = kdsp;
831
832                        ix  = kdsp.buffer_index
833                        off = kdsp.offset
834                        kdsp_actual = unsigned(addressof(kern.globals.kd_data_page_trace.kd_bufs[ix].kdsb_addr[off]))
835
836                        kdsp_actual_shadow = kdsp_actual;
837                        rcursor = htab[kdsp_actual].kds_readlast;
838
839                    t = htab[kdsp_actual].kds_records[rcursor].timestamp & KDBG_TIMESTAMP_MASK
840
841                if goto_next_cpu == True :
842                    continue
843
844                if (t > barrier_max) and (barrier_max > 0) :
845                    # Need to flush IOPs again before we
846                    # can sort any more data from the
847                    # buffers.
848                    out_of_events = True
849                    break
850
851                if t < (htab[kdsp_actual].kds_timestamp & KDBG_TIMESTAMP_MASK) :
852                    # indicates we've not yet completed filling
853                    # in this event...
854                    # this should only occur when we're looking
855                    # at the buf that the record head is utilizing
856                    # we'll pick these events up on the next
857                    # call to kdbg_read
858                    # we bail at this point so that we don't
859                    # get an out-of-order timestream by continuing
860                    # to read events from the other CPUs' timestream(s)
861                    out_of_events = True
862                    break
863
864                if t < earliest_time :
865                    earliest_time = t
866                    min_kdbp = kdbp
867                    min_cpu = cpu
868
869
870            if (min_kdbp is None) or (out_of_events == True) :
871                # all buffers ran empty
872                                out_of_events = True
873                                break
874
875            kdsp = htab[min_kdbp].kd_list_head
876
877            ix = kdsp.buffer_index
878            off = kdsp.offset
879            kdsp_actual = unsigned(addressof(kern.globals.kd_data_page_trace.kd_bufs[ix].kdsb_addr[off]))
880            if not kdsp_actual in htab :
881                htab[kdsp_actual] = kern.globals.kd_data_page_trace.kd_bufs[ix].kdsb_addr[off]
882
883            # Copy earliest event into merged events scratch buffer.
884            r = htab[kdsp_actual].kds_readlast
885            htab[kdsp_actual].kds_readlast = r + 1
886            e = htab[kdsp_actual].kds_records[r]
887
888            # Concatenate event into buffer
889            # XXX condition here is on __LP64__
890            if lp64 :
891                tempbuf += struct.pack('QQQQQQIIQ',
892                        unsigned(e.timestamp),
893                        unsigned(e.arg1),
894                        unsigned(e.arg2),
895                        unsigned(e.arg3),
896                        unsigned(e.arg4),
897                        unsigned(e.arg5),
898                        unsigned(e.debugid),
899                        unsigned(e.cpuid),
900                        unsigned(e.unused))
901            else :
902                tempbuf += struct.pack('QIIIIII',
903                        unsigned(e.timestamp),
904                        unsigned(e.arg1),
905                        unsigned(e.arg2),
906                        unsigned(e.arg3),
907                        unsigned(e.arg4),
908                        unsigned(e.arg5),
909                        unsigned(e.debugid))
910
911            # Watch for out of order timestamps
912            if earliest_time < (htab[min_kdbp].kd_prev_timebase & KDBG_TIMESTAMP_MASK) :
913                ## if so, use the previous timestamp + 1 cycle
914                htab[min_kdbp].kd_prev_timebase += 1
915
916                e.timestamp = htab[min_kdbp].kd_prev_timebase & KDBG_TIMESTAMP_MASK
917                if not lp64:
918                    e.timestamp |= (min_cpu << KDBG_CPU_SHIFT)
919            else :
920                htab[min_kdbp].kd_prev_timebase = earliest_time
921
922            if opt_verbose >= vDETAIL :
923                print "{0:#018x} {1:#018x} {2:#018x} {3:#018x} {4:#018x} {5:#018x} {6:#010x} {7:#010x} {8:#018x}".format(
924                    e.timestamp, e.arg1, e.arg2, e.arg3, e.arg4, e.arg5, e.debugid, e.cpuid, e.unused)
925
926            events_count_found += 1
927
928            # nextevent:
929            tempbuf_count -= 1
930            tempbuf_number += 1
931
932        if opt_progress == True :
933            sys.stderr.write('\n')
934            sys.stderr.flush()
935
936        if opt_verbose > vHUMAN :
937            print "events_count_lost {0:#x}, events_count_found {1:#x}, progress_count {2:#x}".format(events_count_lost, events_count_found, progress_count)
938
939        # write trace events to output file
940        if tempbuf_number != 0 :
941            count -= tempbuf_number
942            wfd.write(buffer(tempbuf))
943
944        if out_of_events == True :
945            # all trace buffers are empty
946            if opt_verbose > vHUMAN :
947                print "out of events"
948            break
949
950    wfd.close()
951
952    return
953
954
955def GetTimebaseInfo():
956    timebase_key = 'kern.rtc_timebase'
957    numer, denom = caching.GetStaticCacheData(timebase_key, (None, None))
958    if not numer or not denom:
959        if kern.arch == 'x86_64':
960            numer = 1
961            denom = 1
962        else:
963            rtclockdata_addr = kern.GetLoadAddressForSymbol('RTClockData')
964            rtc = kern.GetValueFromAddress(
965                rtclockdata_addr, 'struct _rtclock_data_ *')
966            tb = rtc.rtc_timebase_const
967            numer = tb.numer
968            denom = tb.denom
969        caching.SaveStaticCacheData(timebase_key, (numer, denom))
970    return numer, denom
971
972
973def PrintIteratedElem(i, elem, elem_type, do_summary, summary, regex):
974    try:
975        if do_summary and summary:
976            s = summary(elem)
977            if regex:
978                if regex.match(s):
979                    print "[{:d}] {:s}".format(i, s)
980            else:
981                print "[{:d}] {:s}".format(i, s)
982        else:
983            if regex:
984                if regex.match(str(elem)):
985                    print "[{:4d}] ({:s}){:#x}".format(i, elem_type, unsigned(elem))
986            else:
987                print "[{:4d}] ({:s}){:#x}".format(i, elem_type, unsigned(elem))
988    except:
989        print "Exception while looking at elem {:#x}".format(unsigned(elem))
990        return
991
992@lldb_command('q_iterate', "LQSG:")
993def QIterate(cmd_args=None, cmd_options={}):
994    """ Iterate over a LinkageChain or Queue (osfmk/kern/queue.h method 1 or 2 respectively)
995        This is equivalent to the qe_foreach_element() macro
996        usage:
997            iterate [options] {queue_head_ptr} {element_type} {field_name}
998        option:
999            -L    iterate over a linkage chain (method 1) [default]
1000            -Q    iterate over a queue         (method 2)
1001
1002            -S    auto-summarize known types
1003            -G    regex to filter the output
1004        e.g.
1005            iterate_linkage `&coalitions_q` 'coalition *' coalitions
1006    """
1007    if not cmd_args:
1008        raise ArgumentError("usage: iterate_linkage {queue_head_ptr} {element_type} {field_name}")
1009
1010    qhead = kern.GetValueFromAddress(cmd_args[0], 'struct queue_entry *')
1011    if not qhead:
1012        raise ArgumentError("Unknown queue_head pointer: %r" % cmd_args)
1013    elem_type = cmd_args[1]
1014    field_name = cmd_args[2]
1015    if not elem_type or not field_name:
1016        raise ArgumentError("usage: iterate_linkage {queue_head_ptr} {element_type} {field_name}")
1017
1018    do_queue_iterate = False
1019    do_linkage_iterate = True
1020    if "-Q" in cmd_options:
1021        do_queue_iterate = True
1022        do_linkage_iterate = False
1023    if "-L" in cmd_options:
1024        do_queue_iterate = False
1025        do_linkage_iterate = True
1026
1027    do_summary = False
1028    if "-S" in cmd_options:
1029        do_summary = True
1030    regex = None
1031    if "-G" in cmd_options:
1032        regex = re.compile(".*{:s}.*".format(cmd_options["-G"]))
1033        print "Looking for: {:s}".format(regex.pattern)
1034
1035    global lldb_summary_definitions
1036    summary = None
1037    if elem_type in lldb_summary_definitions:
1038        summary = lldb_summary_definitions[elem_type]
1039        if do_summary:
1040            print summary.header
1041
1042    try:
1043        i = 0
1044        if do_linkage_iterate:
1045            for elem in IterateLinkageChain(qhead, elem_type, field_name):
1046                PrintIteratedElem(i, elem, elem_type, do_summary, summary, regex)
1047                i = i + 1
1048        elif do_queue_iterate:
1049            for elem in IterateQueue(qhead, elem_type, field_name):
1050                PrintIteratedElem(i, elem, elem_type, do_summary, summary, regex)
1051                i = i + 1
1052    except:
1053        print "Exception while looking at queue_head: {:#x}".format(unsigned(qhead))
1054
1055@lldb_command('lbrbt')
1056def LBRBacktrace(cmd_args=None):
1057    """
1058        Prints symbolicated last branch records captured on Intel systems
1059        from a core file. Will not work on a live system.
1060        usage:
1061            lbrbt
1062        options:
1063            None
1064    """
1065    DecoratedLBRStack = SymbolicateLBR()
1066    if (DecoratedLBRStack):
1067        print(DecoratedLBRStack)
1068
1069def SymbolicateLBR():
1070    lbr_size_offset = 5
1071    cpu_num_offset = 4
1072    LBRMagic = 0x5352424C
1073
1074    try:
1075        phys_carveout_addr = kern.GetLoadAddressForSymbol("phys_carveout")
1076    except LookupError:
1077        print("Last Branch Recoreds not present in this core file")
1078        return None
1079    try:
1080        phys_carveout_md_addr = kern.GetLoadAddressForSymbol("phys_carveout_metadata")
1081    except LookupError:
1082        print("Last Branch Recoreds not present in this core file")
1083        return None
1084
1085    metadata_ptr = kern.GetValueFromAddress(phys_carveout_md_addr, "uint64_t *")
1086    metadata = kern.GetValueFromAddress(unsigned(metadata_ptr[0]), "uint8_t *")
1087    carveout_ptr = kern.GetValueFromAddress(phys_carveout_addr, "uint64_t *")
1088
1089    metadata_hdr = kern.GetValueFromAddress(unsigned(metadata_ptr[0]), "uint32_t *")
1090    if not (unsigned(metadata_hdr[0]) == LBRMagic):
1091        print("'LBRS' not found at beginning of phys_carveout section, cannot proceed.")
1092        return None
1093
1094    lbr_records = unsigned(carveout_ptr[0])
1095
1096    num_lbrs = int(metadata[lbr_size_offset])
1097
1098    header_line = "".join("{:49s} -> {:s}\n".format("From", "To"))
1099    ncpus = int(metadata[cpu_num_offset])
1100
1101    output_lines = []
1102
1103    target = LazyTarget.GetTarget()
1104
1105    for cpu in range(ncpus):
1106        start_addr_from = lbr_records + num_lbrs * 8 * cpu
1107        start_addr_to = start_addr_from + num_lbrs * 8 * ncpus
1108        from_lbr = kern.GetValueFromAddress(start_addr_from, "uint64_t *")
1109        to_lbr = kern.GetValueFromAddress(start_addr_to, "uint64_t *")
1110        for i in range(num_lbrs):
1111            if (from_lbr[i] == 0x0 or to_lbr[i] == 0x0):
1112                break
1113            ## Replace newline with space to include inlined functions
1114            ## in a trade off for longer output lines.
1115            fprint = str(target.ResolveLoadAddress(int(from_lbr[i]))).replace('\n', ' ')
1116            tprint = str(target.ResolveLoadAddress(int(to_lbr[i]))).replace('\n', ' ')
1117            output_lines.append(''.join("({:x}) {:30s} -> ({:x}) {:30s}\n".format(from_lbr[i], fprint, to_lbr[i], tprint)))
1118
1119    return header_line + ''.join(output_lines)
1120