1from xnu import * 2from utils import * 3from core.lazytarget import * 4from misc import * 5from scheduler import * 6from kcdata import kcdata_item_iterator, KCObject, GetTypeForName, KCCompressedBufferObject 7from collections import namedtuple 8import heapq 9import os 10import plistlib 11import struct 12import subprocess 13import sys 14import tempfile 15import time 16import operator 17 18# From the defines in bsd/sys/kdebug.h: 19 20KdebugClassNames = { 21 1: "MACH", 22 2: "NETWORK", 23 3: "FSYSTEM", 24 4: "BSD", 25 5: "IOKIT", 26 6: "DRIVERS", 27 7: "TRACE", 28 8: "DLIL", 29 9: "WORKQUEUE", 30 10: "CORESTORAGE", 31 11: "CG", 32 20: "MISC", 33 30: "SECURITY", 34 31: "DYLD", 35 32: "QT", 36 33: "APPS", 37 34: "LAUNCHD", 38 36: "PPT", 39 37: "PERF", 40 38: "IMPORTANCE", 41 39: "PERFCTRL", 42 40: "BANK", 43 41: "XPC", 44 42: "ATM", 45 43: "ARIADNE", 46 44: "DAEMON", 47 45: "ENERGYTRACE", 48 49: "IMG", 49 50: "CLPC", 50 128: "ANS", 51 129: "SIO", 52 130: "SEP", 53 131: "ISP", 54 132: "OSCAR", 55 133: "EMBEDDEDGFX" 56} 57 58def GetKdebugClassName(class_num): 59 return (KdebugClassNames[class_num] + ' ({})'.format(class_num) if class_num in KdebugClassNames else 'unknown ({})'.format(class_num)) 60 61@lldb_type_summary(['typefilter_t']) 62@header('{0: <20s}'.format("class") + ' '.join(map('{:02x}'.format, list(range(0, 255, 8))))) 63def GetKdebugTypefilter(typefilter): 64 """ Summarizes the provided typefilter. 65 """ 66 classes = 256 67 subclasses_per_class = 256 68 69 # 8 bits at a time 70 subclasses_per_element = 64 71 cur_typefilter = cast(typefilter, 'uint64_t *') 72 subclasses_fmts = ' '.join(['{:02x}'] * 8) 73 74 elements_per_class = subclasses_per_class // subclasses_per_element 75 76 out_str = '' 77 for i in range(0, classes): 78 print_class = False 79 subclasses = [0] * elements_per_class 80 81 # check subclass ranges for set bits, remember those subclasses 82 for j in range(0, elements_per_class): 83 element = unsigned(cur_typefilter[i * elements_per_class + j]) 84 if element != 0: 85 print_class = True 86 if print_class: 87 subclasses[j] = element 88 89 ## if any of the bits were set in a class, print the entire class 90 if print_class: 91 out_str += '{:<20s}'.format(GetKdebugClassName(i)) 92 for element in subclasses: 93 # split up the 64-bit values into byte-sized pieces 94 bytes = [unsigned((element >> i) & 0xff) for i in (0, 8, 16, 24, 32, 40, 48, 56)] 95 out_str += subclasses_fmts.format(*bytes) 96 out_str += ' ' 97 98 out_str += '\n' 99 100 return out_str 101 102@lldb_command('showkdebugtypefilter') 103def ShowKdebugTypefilter(cmd_args=None): 104 """ Show the current kdebug typefilter (or the typefilter at an address) 105 106 usage: showkdebugtypefilter [<address>] 107 """ 108 109 if cmd_args: 110 typefilter = kern.GetValueFromAddress(cmd_args[0], 'typefilter_t') 111 if unsigned(typefilter) == 0: 112 raise ArgumentError('argument provided is NULL') 113 114 print(GetKdebugTypefilter.header) 115 print('-' * len(GetKdebugTypefilter.header)) 116 117 print(GetKdebugTypefilter(typefilter)) 118 return 119 120 typefilter = kern.globals.kdbg_typefilter 121 if unsigned(typefilter) == 0: 122 raise ArgumentError('no argument provided and active typefilter is not set') 123 124 print(GetKdebugTypefilter.header) 125 print('-' * len(GetKdebugTypefilter.header)) 126 print(GetKdebugTypefilter(typefilter)) 127 128def GetKdebugStatus(): 129 """ Get a string summary of the kdebug subsystem. 130 """ 131 out = '' 132 133 kdc_flags = kern.globals.kd_control_trace.kdc_flags 134 out += 'kdebug flags: {}\n'.format(xnudefines.GetStateString(xnudefines.kdebug_flags_strings, kdc_flags)) 135 events = kern.globals.kd_buffer_trace.kdb_event_count 136 buf_mb = events * (64 if kern.arch == 'x86_64' or kern.arch.startswith('arm64') else 32) // 1000000 137 out += 'events allocated: {:<d} ({:<d} MB)\n'.format(events, buf_mb) 138 out += 'enabled: {}\n'.format('yes' if kern.globals.kdebug_enable != 0 else 'no') 139 if kdc_flags & xnudefines.KDBG_TYPEFILTER_CHECK: 140 out += 'typefilter:\n' 141 out += GetKdebugTypefilter.header + '\n' 142 out += '-' * len(GetKdebugTypefilter.header) + '\n' 143 typefilter = kern.globals.kdbg_typefilter 144 if unsigned(typefilter) != 0: 145 out += GetKdebugTypefilter(typefilter) 146 147 return out 148 149@lldb_command('showkdebug') 150def ShowKdebug(cmd_args=None): 151 """ Show the current kdebug state. 152 153 usage: showkdebug 154 """ 155 156 print(GetKdebugStatus()) 157 158@lldb_type_summary(['kperf_timer']) 159@header('{:<10s} {:<7s} {:<20s} {:<20s}'.format('period-ns', 'action', 'deadline', 'fire-time')) 160def GetKperfTimerSummary(timer): 161 """ Get a string summary of a kperf timer. 162 163 params: 164 timer: the kptimer object to get a summary of 165 """ 166 try: 167 fire_time = timer.kt_fire_time 168 except: 169 fire_time = 0 170 return '{:<10d} {:<7d} {:<20d} {:<20d}\n'.format( 171 kern.GetNanotimeFromAbstime(timer.kt_period_abs), timer.kt_actionid, 172 timer.kt_cur_deadline, fire_time) 173 174@lldb_type_summary(['action']) 175@header('{:<10s} {:<20s} {:<20s}'.format('pid-filter', 'user-data', 'samplers')) 176def GetKperfActionSummary(action): 177 """ Get a string summary of a kperf action. 178 179 params: 180 action: the action object to get a summary of 181 """ 182 samplers = xnudefines.GetStateString(xnudefines.kperf_samplers_strings, action.sample) 183 return '{:<10s} {:<20x} {:<20s}\n'.format( 184 '-' if action.pid_filter < 0 else str(action.pid_filter), action.userdata, samplers) 185 186def GetKperfKdebugFilterDescription(): 187 kdebug_filter = kern.globals.kperf_kdebug_filter 188 desc = '' 189 for i in range(kdebug_filter.n_debugids): 190 filt_index = 1 if i >= 16 else 0 191 filt_type = (kdebug_filter.types[filt_index] >> ((i % 16) * 4)) & 0xf 192 debugid = kdebug_filter.debugids[i] 193 if filt_type < 2: 194 prefix = 'C' 195 width = 2 196 id = debugid >> 24 197 elif filt_type < 4: 198 prefix = 'S' 199 width = 4 200 id = debugid >> 16 201 else: 202 prefix = 'D' 203 width = 8 204 id = debugid 205 206 suffix = '' 207 if (filt_type % 2) == 1: 208 if debugid & xnudefines.DBG_FUNC_START: 209 suffix = 's' 210 elif debugid & xnudefines.DBG_FUNC_END: 211 suffix = 'r' 212 else: 213 suffix = 'n' 214 215 if i > 0: 216 desc += ',' 217 desc += '{prefix}{id:0{width}x}{suffix}'.format( 218 prefix=prefix, id=id, width=width, suffix=suffix) 219 220 return desc 221 222def GetKperfStatus(): 223 """ Get a string summary of the kperf subsystem. 224 """ 225 out = '' 226 227 kperf_status = int(kern.globals.kperf_status) 228 out += 'sampling: ' 229 if kperf_status == GetEnumValue('kperf_sampling::KPERF_SAMPLING_OFF'): 230 out += 'off\n' 231 elif kperf_status == GetEnumValue('kperf_sampling::KPERF_SAMPLING_SHUTDOWN'): 232 out += 'shutting down\n' 233 elif kperf_status == GetEnumValue('kperf_sampling::KPERF_SAMPLING_ON'): 234 out += 'on\n' 235 else: 236 out += 'unknown\n' 237 238 pet = kern.globals.kptimer.g_pet_active 239 pet_timer_id = kern.globals.kptimer.g_pet_active 240 if pet != 0: 241 pet_idle_rate = kern.globals.pet_idle_rate 242 out += 'legacy PET is active (timer = {:<d}, idle rate = {:<d})\n'.format(pet_timer_id, pet_idle_rate) 243 else: 244 out += 'legacy PET is off\n' 245 246 lw_pet = kern.globals.kppet.g_lightweight 247 if lw_pet != 0: 248 out += 'lightweight PET is active (timer = {:<d})\n'.format(pet_timer_id) 249 else: 250 out += 'lightweight PET is off\n' 251 252 actions = kern.globals.actionc 253 actions_arr = kern.globals.actionv 254 255 out += 'actions:\n' 256 out += '{:<5s} '.format('id') + GetKperfActionSummary.header + '\n' 257 for i in range(0, actions): 258 out += '{:<5d} '.format(i) + GetKperfActionSummary(actions_arr[i]) 259 260 timers = kern.globals.kptimer.g_ntimers 261 timers_arr = kern.globals.kptimer.g_timers 262 263 out += 'timers:\n' 264 out += '{:<5s} '.format('id') + GetKperfTimerSummary.header + '\n' 265 for i in range(0, timers): 266 out += '{:<5d} '.format(i) + GetKperfTimerSummary(timers_arr[i]) 267 268 return out 269 270 271def GetKtraceStatus(): 272 """ Get a string summary of the ktrace subsystem. 273 """ 274 out = '' 275 276 state = kern.globals.ktrace_state 277 if state == GetEnumValue('ktrace_state_t::KTRACE_STATE_OFF'): 278 out += 'ktrace is off\n' 279 else: 280 out += 'ktrace is active (' 281 if state == GetEnumValue('ktrace_state_t::KTRACE_STATE_FG'): 282 out += 'foreground)' 283 else: 284 out += 'background)' 285 out += '\n' 286 owner = kern.globals.ktrace_last_owner_execname 287 owner_pid = kern.globals.ktrace_owning_pid 288 out += 'owned by: {:<s} [{}]\n'.format(owner, unsigned(owner_pid)) 289 active_mask = kern.globals.ktrace_active_mask 290 out += 'active systems: {:<#x}\n'.format(active_mask) 291 292 return out 293 294 295def GetKtraceConfig(): 296 kdebug_state = 0 297 if (kern.globals.kd_control_trace.kdc_flags & xnudefines.KDBG_BUFINIT) != 0: 298 kdebug_state = 1 299 if kern.globals.kdebug_enable: 300 kdebug_state = 3 301 kdebug_wrapping = True 302 if (kern.globals.kd_control_trace.kdc_live_flags & xnudefines.KDBG_NOWRAP): 303 kdebug_wrapping = False 304 305 kperf_state = 3 if ( 306 unsigned(kern.globals.kperf_status) == 307 GetEnumValue('kperf_sampling::KPERF_SAMPLING_ON')) else 0 308 309 action_count = kern.globals.actionc 310 actions = kern.globals.actionv 311 action_samplers = [] 312 action_user_datas = [] 313 action_pid_filters = [] 314 for i in range(action_count): 315 action = actions[i] 316 action_samplers.append(unsigned(action.sample)) 317 action_user_datas.append(unsigned(action.userdata)) 318 action_pid_filters.append(unsigned(action.pid_filter)) 319 320 timer_count = kern.globals.kptimer.g_ntimers 321 timers = kern.globals.kptimer.g_timers 322 timer_actions = [] 323 timer_periods_ns = [] 324 325 for i in range(timer_count): 326 timer = timers[i] 327 timer_actions.append(unsigned(timer.kt_actionid)) 328 timer_periods_ns.append( 329 kern.GetNanotimeFromAbstime(unsigned(timer.kt_period_abs))) 330 331 pet_mode = 0 332 if kern.globals.kppet.g_lightweight: 333 pet_mode = 2 334 elif kern.globals.kptimer.g_pet_active: 335 pet_mode = 1 336 337 return { 338 'owner_name': str(kern.globals.ktrace_last_owner_execname), 339 'owner_kind': unsigned(kern.globals.ktrace_state), 340 'owner_pid': int(kern.globals.ktrace_owning_pid), 341 342 'kdebug_state': kdebug_state, 343 'kdebug_buffer_size': unsigned(kern.globals.kd_buffer_trace.kdb_event_count), 344 'kdebug_typefilter': plist_data(struct.pack('B', 0xff) * 4096 * 2), # XXX 345 'kdebug_procfilt_mode': 0, # XXX 346 'kdebug_procfilt': [], # XXX 347 'kdebug_wrapping': kdebug_wrapping, 348 349 'kperf_state': kperf_state, 350 'kperf_actions_sampler': action_samplers, 351 'kperf_actions_user_data': action_user_datas, 352 'kperf_actions_pid_filter': action_pid_filters, 353 'kperf_timers_action_id': timer_actions, 354 'kperf_timers_period_ns': timer_periods_ns, 355 'kperf_pet_mode': pet_mode, 356 'kperf_pet_timer_id': unsigned(kern.globals.kptimer.g_pet_timerid), 357 'kperf_pet_idle_rate': unsigned(kern.globals.kppet.g_idle_rate), 358 359 'kperf_kdebug_action_id': unsigned(kern.globals.kperf_kdebug_action), 360 'kperf_kdebug_filter': GetKperfKdebugFilterDescription(), 361 362 # XXX 363 'kpc_state': 0, 364 'kpc_classes': 0, 365 'kpc_thread_classes': 0, 366 'kpc_periods': [], 367 'kpc_action_ids': [], 368 'kpc_config': [], 369 370 'context_kind': 2, # backwards-facing 371 'reason': 'core file debugging', 372 'command': '(lldb) savekdebugtrace', 373 'trigger_kind': 2, # diagnostics trigger 374 } 375 376 377@lldb_command('showktrace') 378def ShowKtrace(cmd_args=None): 379 """ Show the current ktrace state, including subsystems. 380 381 usage: showktrace 382 """ 383 384 print(GetKtraceStatus()) 385 print(' ') 386 print('kdebug:') 387 print(GetKdebugStatus()) 388 print(' ') 389 print('kperf:') 390 print(GetKperfStatus()) 391 392 393# This is what a kd_event looks like 394# 395# 0000,[ 64] (struct kd_buf)) { 396# 0000,[ 8] (uint64_t) timestamp 397# 0008,[ 8] (kd_buf_argtype) arg1 398# 0016,[ 8] (kd_buf_argtype) arg2 399# 0024,[ 8] (kd_buf_argtype) arg3 400# 0032,[ 8] (kd_buf_argtype) arg4 401# 0040,[ 8] (kd_buf_argtype) arg5 402# 0048,[ 4] (uint32_t) debugid 403# 0052,[ 4] (uint32_t) cpuid 404# 0056,[ 8] (kd_buf_argtype) unused 405# } 406 407U64_STRUCT = struct.Struct('Q') 408KDE_STRUCT = struct.Struct('QQQQQQIIQ') 409KDE_SIZE = 64 # we no longer support k32 410 411class KDEvent64(object): 412 """ 413 Wrapper around kevent pointer that handles sorting logic. 414 """ 415 416 __slots__ = ('timestamp', 'data') 417 418 def __init__(self, buf, offs): 419 self.timestamp = int(U64_STRUCT.unpack_from(buf, offs)[0]) 420 self.data = buf[offs:offs + KDE_SIZE] 421 422class KDCPU(object): 423 """ 424 Represents all events from a single CPU. 425 """ 426 427 def __init__(self, cpuid, verbose, humanize=None, starting_timestamp=None, tid=None, triage=False): 428 self.cpuid = cpuid 429 self.iter_store = None 430 self.verbose = verbose 431 self.humanize = humanize 432 self.disabled = False 433 self.last_timestamp = 0 434 self.err = lldb.SBError() # avoid making it all the time, it's slow 435 self.buffer = kern.globals.kd_buffer_triage if triage else kern.globals.kd_buffer_trace 436 self.kd_bufs = self.buffer.kd_bufs.GetSBValue() 437 self.tid = tid 438 439 kdstorep = self.buffer.kdb_info[cpuid].kd_list_head 440 self.load_kdstore(kdstorep.GetSBValue()) 441 442 skipped_storage_count = 0 443 while self.iter_store is not None and starting_timestamp: 444 offs = self.iter_offs_max - KDE_SIZE 445 if offs >= self.iter_offs: 446 event = KDEvent64(self.iter_buf, offs) 447 if event.timestamp >= starting_timestamp: 448 break 449 450 skipped_storage_count += 1 451 self.load_kdstore(self.iter_store.GetChildMemberWithName('kds_next')) 452 453 if verbose: 454 if skipped_storage_count > 0: 455 print('CPU {} skipped {} storage units'.format( 456 cpuid, skipped_storage_count)) 457 458 if self.iter_store is None: 459 print('found no valid events from CPU {}'.format(cpuid)) 460 461 def load_kdstore(self, kds_next): 462 """ 463 See POINTER_FROM_KDSPTR. 464 """ 465 if kds_next.xGetScalarByName('raw') == xnudefines.KDS_PTR_NULL: 466 self.iter_store = None 467 self.iter_buf = None 468 self.iter_offs = 0 469 self.iter_offs_max = 0 470 self.iter_stamp_max = 0 471 return 472 473 idx = kds_next.xGetScalarByName('buffer_index') 474 offs = kds_next.xGetScalarByName('offset') 475 476 store = self.kd_bufs.GetValueForExpressionPath('[{}].kdr_addr[{}]'.format(idx, offs)) 477 base = store.chkGetChildMemberWithName('kds_records') 478 start = store.xGetScalarByName('kds_readlast') 479 end = store.xGetScalarByName('kds_bufindx') 480 addr = base.GetLoadAddress() + start * KDE_SIZE 481 proc = LazyTarget.GetProcess() 482 delta = end - start 483 484 self.iter_store = store 485 self.iter_buf = proc.ReadMemory(addr, delta * KDE_SIZE, self.err) if delta > 0 else None 486 self.iter_offs = 0 487 self.iter_offs_max = delta * KDE_SIZE 488 self.iter_stamp_max = store.xGetScalarByName('kds_timestamp') 489 490 # Event iterator implementation returns KDEvent64 instance 491 def __iter__(self): 492 return self 493 494 def __next__(self): 495 offs = self.iter_offs 496 while True: 497 while offs >= self.iter_offs_max: 498 # This CPU is out of events. 499 if self.iter_store is None or self.disabled: 500 raise StopIteration 501 502 self.load_kdstore(self.iter_store.GetChildMemberWithName('kds_next')) 503 offs = self.iter_offs 504 505 self.iter_offs = offs + KDE_SIZE 506 kdevent = KDEvent64(self.iter_buf, offs) 507 if self.tid is None or unsigned(kdevent.arg5) == self.tid: 508 break 509 510 if self.verbose and self.last_timestamp == 0: 511 print('first event from CPU {} is at time {}'.format( 512 self.cpuid, kdevent.timestamp)) 513 514 if kdevent.timestamp < self.last_timestamp: 515 # Human-readable output might have garbage on the rest of the line. 516 # Use a CSI escape sequence to clear it out. 517 clear_line_csi = '\033[K\n' if self.humanize else '\n' 518 sys.stderr.write(( 519 'warning: events from CPU {} are out-of-order, ' + 520 'last timestamp = {}, ' + 521 'current timestamp = {}, disabling{}').format( 522 GetKdebugCPUName(self.cpuid), 523 self.last_timestamp, kdevent.timestamp, clear_line_csi)) 524 self.iter_store = None 525 self.disabled = True 526 raise StopIteration 527 528 # Check for writer overrun. 529 if kdevent.timestamp < self.iter_stamp_max: 530 self.iter_store = None 531 raise StopIteration 532 533 self.last_timestamp = kdevent.timestamp 534 return kdevent 535 536 # Python 2 compatibility 537 # pragma pylint: disable=next-method-defined 538 def next(self): 539 return self.__next__() 540 541 542def GetKdebugCPUName(cpu_id): 543 current_iop = kern.globals.kd_control_trace.kdc_coprocs 544 while current_iop: 545 if current_iop.cpu_id == cpu_id: 546 return '{}({})'.format(cpu_id, str(current_iop.full_name)) 547 current_iop = current_iop.next 548 return '{}(unknown)'.format(cpu_id) 549 550 551def IterateKdebugEvents(verbose=False, humanize=False, last=None, tid=None, 552 include_coprocessors=True, triage=False): 553 """ 554 Yield events from the in-memory kdebug trace buffers. 555 """ 556 ctrl = kern.globals.kd_control_trace 557 558 if not triage and (ctrl.kdc_flags & xnudefines.KDBG_BUFINIT) == 0: 559 return 560 561 barrier_min = ctrl.kdc_oldest_time 562 563 if not triage and (ctrl.kdc_live_flags & xnudefines.KDBG_WRAPPED) != 0: 564 # TODO Yield a wrap event with the barrier_min timestamp. 565 pass 566 567 cpu_count = kern.globals.machine_info.logical_cpu_max 568 if include_coprocessors and not triage: 569 cpu_count = ctrl.kdebug_cpus 570 571 start_timestamp = None 572 if last is not None: 573 (numer, denom) = GetTimebaseInfo() 574 duration = (last * 1e9) * denom / numer 575 now = GetRecentTimestamp() 576 start_timestamp = unsigned(now - duration) 577 if verbose: 578 print('starting at time {} ({} - {})'.format( 579 start_timestamp, now, duration)) 580 581 # Merge sort all events from all CPUs. 582 cpus = [KDCPU(cpuid, verbose, starting_timestamp=start_timestamp, humanize=humanize, tid=tid, triage=triage) 583 for cpuid in range(cpu_count)] 584 return heapq.merge(*cpus, key=operator.attrgetter('timestamp')) 585 586@header('{:>12s} {:>10s} {:>18s} {:>18s} {:>18s} {:>18s} {:>5s} {:>8s}'.format( 587 'timestamp', 'debugid', 'arg1', 'arg2', 'arg3', 'arg4', 'cpuid', 588 'tid')) 589def GetKdebugEvent(event, symbolicate=False, O=None): 590 """ 591 Return a string representing a kdebug trace event. 592 """ 593 def fmt_arg(a): 594 return '0x{:016x}'.format(a) 595 def sym_arg(a): 596 slid_addr = kern.globals.vm_kernel_slide + a 597 syms = kern.SymbolicateFromAddress(slid_addr) 598 return syms[0].GetName() if syms else fmt_arg(a) 599 600 fn = sym_arg if symbolicate else fmt_arg 601 602 ts, arg1, arg2, arg3, arg4, arg5, debugid, cpuid, _ = KDE_STRUCT.unpack(event.data) 603 604 return O.format( 605 '{:12d} 0x{:08x} {:18s} {:18s} {:18s} {:18s} {:5d} {:8d}', 606 ts, debugid, fn(arg1), fn(arg2), fn(arg3), fn(arg4), cpuid, arg5) 607 608 609def KtriageDecode(event): 610 subsystem = event.arg4 >> 24 611 strings = kern.globals.ktriage_subsystems_strings[subsystem] 612 code = (event.arg4 >> 2) & 0x3fff 613 if code > strings.num_strings: 614 return '' 615 try: 616 return str(strings.strings[code]).strip() 617 except IndexError: 618 return '' 619 620 621@header('{:>12s} {:>18s} {:>18s} {:>5s} {:>8s} {:<8s}'.format( 622 'timestamp', 'debugid', 'arg1', 'cpuid', 'tid', 'message')) 623def GetKtriageEvent(event, O=None): 624 """ 625 Return a string representing a kdebug trace event. 626 """ 627 timestamp = event.timestamp 628 cpuid = event.cpuid 629 def fmt_arg(a): 630 return '0x{:016x}'.format(unsigned(a)) 631 args = list(map( 632 fmt_arg, 633 [event.arg1, event.arg2, event.arg3, event.arg4])) 634 return O.format( 635 '{:12d} {:18s} {:18s} {:5d} {:8d} {:s}', 636 unsigned(timestamp), args[3], args[0], unsigned(cpuid), 637 unsigned(event.arg5), KtriageDecode(event)) 638 639 640@lldb_command('showkdebugtrace', 'L:S', fancy=True) 641def ShowKdebugTrace(cmd_args=None, cmd_options={}, O=None): 642 """ 643 List the events present in the kdebug trace buffers. 644 645 (lldb) showkdebugtrace [-S] [-L <last-seconds>] 646 647 -L <last-seconds>: only show events from the last <last-seconds> seconds 648 -S: attempt to symbolicate arguments as kernel addresses 649 650 Caveats: 651 * Events from IOPs may be missing or cut-off -- they weren't informed 652 of this kind of buffer collection. 653 """ 654 last = cmd_options.get('-L', None) 655 if last: 656 try: 657 last = float(last) 658 except ValueError: 659 raise ArgumentError( 660 'error: -L argument must be a number, not {}'.format(last)) 661 with O.table(GetKdebugEvent.header): 662 for event in IterateKdebugEvents( 663 config['verbosity'] > vHUMAN, humanize=True, last=last): 664 print(GetKdebugEvent( 665 event, symbolicate='-S' in cmd_options, O=O)) 666 667 668@lldb_command('showktriage', 'L:', fancy=True) 669def ShowKtriage(cmd_args=None, cmd_options={}, O=None): 670 """ 671 List the events present in the ktriage buffers. 672 673 (lldb) showktriage [-L <last-seconds>] [<thread-id>] 674 675 -L <last-seconds>: only show events from the last <last-seconds> seconds 676 """ 677 tid = None if len(cmd_args) == 0 else cmd_args[0] 678 if tid: 679 tid = unsigned(tid) 680 last = cmd_options.get('-L', None) 681 if last: 682 try: 683 last = float(last) 684 except ValueError: 685 raise ArgumentError( 686 'error: -L argument must be a number, not {}'.format(last)) 687 with O.table(GetKtriageEvent.header): 688 for event in IterateKdebugEvents( 689 config['verbosity'] > vHUMAN, humanize=True, last=last, tid=tid, 690 triage=True): 691 print(GetKtriageEvent(event, O=O)) 692 693 694def binary_plist(o): 695 return plistlib.dumps(o, fmt=plistlib.FMT_BINARY) 696 697 698def plist_data(d): 699 return d 700 701 702def align_next_chunk(f, offset, verbose): 703 trailing = offset % 8 704 padding = 0 705 if trailing != 0: 706 padding = 8 - trailing 707 f.write(b'\x00' * padding) 708 if verbose: 709 print('aligned next chunk with {} padding bytes'.format(padding)) 710 return padding 711 712 713def GetKtraceMachine(): 714 """ 715 This is best effort -- several fields are only available to user space or 716 are difficult to determine from a core file. 717 """ 718 master_cpu_data = GetCpuDataForCpuID(0) 719 if kern.arch == 'x86_64': 720 cpu_family = unsigned(kern.globals.cpuid_cpu_info.cpuid_cpufamily) 721 else: 722 cpu_family = 0 # XXX 723 724 page_size = 4 * 4096 725 726 return { 727 'kern_version': str(kern.globals.version), 728 'boot_args': '', # XXX 729 'hw_memsize': unsigned(kern.globals.max_mem), 730 'hw_pagesize': page_size, # XXX 731 'vm_pagesize': page_size, # XXX 732 'os_name': 'Unknown', # XXX 733 'os_version': 'Unknown', # XXX 734 'os_build': str(kern.globals.osversion), 735 'arch': 'Unknown', # XXX 736 'hw_model': 'Unknown', # XXX 737 'cpu_type': unsigned(master_cpu_data.cpu_type), 738 'cpu_subtype': unsigned(master_cpu_data.cpu_subtype), 739 'cpu_family': cpu_family, 740 'active_cpus': unsigned(kern.globals.processor_avail_count), 741 'max_cpus': unsigned(kern.globals.machine_info.logical_cpu_max), 742 'apple_internal': True, # XXX 743 } 744 745 746CHUNKHDR_PACK = 'IHHQ' 747 748 749def append_chunk(f, file_offset, tag, major, minor, data, verbose): 750 f.write(struct.pack(CHUNKHDR_PACK, tag, major, minor, len(data))) 751 f.write(data) 752 offset = 16 + len(data) 753 return offset + align_next_chunk(f, file_offset + offset, verbose) 754 755 756@lldb_command('savekdebugtrace', 'IL:MN:') 757def SaveKdebugTrace(cmd_args=None, cmd_options={}): 758 """ 759 Save any valid ktrace events to a file. 760 761 (lldb) savekdebugtrace [-IM] [-N <n-events> | -L <last-secs>] <output-path> 762 763 -I: ignore coprocessor (IOP) events entirely 764 -L <last-seconds>: only save events from the <last-seconds> seconds 765 -N <n-events>: only save the last <n-events> events 766 -M: ensure output is machine-friendly 767 768 Tips: 769 * To speed up the process, use the -I and -L options to avoid 770 processing events based on source or time. 771 772 Caveats: 773 * Fewer than the requested number of events may end up in the file. 774 * The machine and config chunks may be missing crucial information 775 required for tools to analyze them. 776 * Events from coprocessors may be missing or cut-off -- they weren't 777 informed of this kind of buffer collection. 778 * Chunks related to post-processing, like symbolication data or a 779 catalog will need to be added manually. 780 """ 781 782 if len(cmd_args) != 1: 783 raise ArgumentError('error: path to trace file is required') 784 785 last = cmd_options.get('-L', None) 786 if last and '-N' in cmd_options: 787 raise ArgumentError('error: -L and -N are mutually exclusive') 788 if last: 789 try: 790 last = float(last) 791 except ValueError: 792 raise ArgumentError( 793 'error: -L argument must be a number, not {}'.format(last)) 794 795 nevents = unsigned(kern.globals.kd_buffer_trace.kdb_event_count) 796 if nevents == 0: 797 print('error: kdebug buffers are not set up') 798 return 799 800 limit_nevents = nevents 801 if '-N' in cmd_options: 802 limit_nevents = unsigned(cmd_options['-N']) 803 if limit_nevents > nevents: 804 limit_nevents = nevents 805 verbose = config['verbosity'] > vHUMAN 806 humanize = '-M' not in cmd_options 807 808 file_offset = 0 809 with open(cmd_args[0], 'w+b') as f: 810 FILE_MAGIC = 0x55aa0300 811 EVENTS_TAG = 0x00001e00 812 SSHOT_TAG = 0x8002 813 CONFIG_TAG = 0x8006 814 MACHINE_TAG = 0x8c00 815 CHUNKHDR_PACK = 'IHHQ' 816 FILEHDR_PACK = CHUNKHDR_PACK + 'IIQQIIII' 817 FILEHDR_SIZE = 40 818 FUTURE_SIZE = 8 819 820 numer, denom = GetTimebaseInfo() 821 822 # XXX The kernel doesn't have a solid concept of the wall time. 823 wall_abstime = 0 824 wall_secs = 0 825 wall_usecs = 0 826 827 file_hdr = struct.pack( 828 FILEHDR_PACK, FILE_MAGIC, 0, 0, FILEHDR_SIZE, 829 numer, denom, wall_abstime, wall_secs, wall_usecs, 0, 0, 830 0x1) 831 f.write(file_hdr) 832 header_size_offset = file_offset + 8 833 file_offset += 16 + FILEHDR_SIZE # chunk header plus file header 834 835 if verbose: 836 print('writing machine chunk at offset 0x{:x}'.format(file_offset)) 837 machine_data = GetKtraceMachine() 838 machine_bytes = binary_plist(machine_data) 839 file_offset += append_chunk( 840 f, file_offset, MACHINE_TAG, 0, 0, machine_bytes, verbose) 841 842 if verbose: 843 print('writing config chunk at offset 0x{:x}'.format(file_offset)) 844 config_data = GetKtraceConfig() 845 config_bytes = binary_plist(config_data) 846 file_offset += append_chunk( 847 f, file_offset, CONFIG_TAG, 0, 0, config_bytes, verbose) 848 849 events_hdr = struct.pack( 850 CHUNKHDR_PACK, EVENTS_TAG, 0, 0, 0) # size will be filled in later 851 f.write(events_hdr) 852 file_offset += 16 # header size 853 event_size_offset = file_offset - FUTURE_SIZE 854 # Future events timestamp -- doesn't need to be set for merged events. 855 f.write(struct.pack('Q', 0)) 856 file_offset += FUTURE_SIZE 857 858 if verbose: 859 print('events start at offset 0x{:x}'.format(file_offset)) 860 861 skip_nevents = nevents - limit_nevents if limit_nevents else 0 862 if skip_nevents > 0: 863 print('omitting {} events from the beginning'.format(skip_nevents)) 864 865 written_nevents = 0 866 seen_nevents = 0 867 start_time = time.time() 868 update_every = 100000 869 for event in IterateKdebugEvents( 870 verbose, include_coprocessors='-I' not in cmd_options, 871 humanize=humanize, last=last): 872 seen_nevents += 1 873 if skip_nevents >= seen_nevents: 874 if seen_nevents % update_every == 0: 875 sys.stderr.write('skipped {}/{} ({:4.2f}%) events'.format( 876 seen_nevents, skip_nevents, 877 float(seen_nevents) / skip_nevents * 100.0)) 878 sys.stderr.write('\r') 879 880 continue 881 882 file_offset += KDE_SIZE 883 f.write(event.data) 884 885 written_nevents += 1 886 # Periodically update the CLI with progress. 887 if written_nevents % update_every == 0: 888 sys.stderr.write('{}: wrote {}/{} ({:4.2f}%) events'.format( 889 time.strftime('%H:%M:%S'), written_nevents, 890 limit_nevents, 891 float(written_nevents) / nevents * 100.0)) 892 if humanize: 893 sys.stderr.write('\r') 894 else: 895 sys.stderr.write('\n') 896 sys.stderr.write('\n') 897 elapsed = time.time() - start_time 898 print('wrote {} ({}MB) events in {:.3f}s'.format( 899 written_nevents, written_nevents * KDE_SIZE >> 20, elapsed)) 900 if verbose: 901 print('events end at offset 0x{:x}'.format(file_offset)) 902 903 # Normally, the chunk would need to be padded to 8, but events are 904 # already aligned. 905 906 kcdata = kern.globals.kc_panic_data 907 kcdata_addr = unsigned(kcdata.kcd_addr_begin) 908 kcdata_length = unsigned(kcdata.kcd_length) 909 if kcdata_addr != 0 and kcdata_length != 0: 910 print('writing stackshot') 911 if verbose: 912 print('stackshot is 0x{:x} bytes at offset 0x{:x}'.format( 913 kcdata_length, file_offset)) 914 915 process = LazyTarget().GetProcess() 916 ssdata = process.ReadMemory(kcdata_addr, kcdata_length, lldb.SBError()) 917 magic = struct.unpack('I', ssdata[:4]) 918 if magic[0] == GetTypeForName('KCDATA_BUFFER_BEGIN_COMPRESSED'): 919 if verbose: 920 print('found compressed stackshot') 921 iterator = kcdata_item_iterator(ssdata) 922 for item in iterator: 923 kcdata_buffer = KCObject.FromKCItem(item) 924 if isinstance(kcdata_buffer, KCCompressedBufferObject): 925 kcdata_buffer.ReadItems(iterator) 926 decompressed = kcdata_buffer.Decompress(ssdata) 927 ssdata = decompressed 928 kcdata_length = len(ssdata) 929 if verbose: 930 print( 931 'compressed stackshot is 0x{:x} bytes long'. 932 format(kcdata_length)) 933 934 file_offset += append_chunk( 935 f, file_offset, SSHOT_TAG, 1, 0, ssdata, verbose) 936 if verbose: 937 print('stackshot ends at offset 0x{:x}'.format(file_offset)) 938 else: 939 print('warning: no stackshot; trace file may not be usable') 940 941 # After the number of events is known, fix up the events chunk size. 942 events_data_size = unsigned(written_nevents * KDE_SIZE) + FUTURE_SIZE 943 f.seek(event_size_offset) 944 f.write(struct.pack('Q', events_data_size)) 945 if verbose: 946 print('wrote {:x} bytes at offset 0x{:x} for event size'.format( 947 events_data_size, event_size_offset)) 948 949 # Fix up the size of the header chunks, too. 950 f.seek(header_size_offset) 951 f.write(struct.pack('Q', FILEHDR_SIZE + 16 + len(machine_bytes))) 952 if verbose: 953 print(( 954 'wrote 0x{:x} bytes at offset 0x{:x} for ' + 955 'file header size').format( 956 len(machine_bytes), header_size_offset)) 957 958 return 959 960# Obsolete commands. 961 962@lldb_command('showkerneldebugbuffercpu') 963def ShowKernelDebugBufferCPU(cmd_args=None): 964 """ REMOVED: Use showkdebugtrace instead. """ 965 raise NotImplementedError("Use showkdebugtrace instead") 966 967 968@lldb_command('showkerneldebugbuffer') 969def ShowKernelDebugBuffer(cmd_args=None): 970 """ REMOVED: Use showkdebugtrace instead. """ 971 raise NotImplementedError("Use showkdebugtrace instead") 972 973 974@lldb_command('dumprawtracefile') 975def DumpRawTraceFile(cmd_args=[], cmd_options={}): 976 """ REMOVED: Use savekdebugtrace instead. """ 977 raise NotImplementedError("Use savekdebugtrace instead") 978