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