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