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