3 from core
.lazytarget
import *
5 from kcdata
import kcdata_item_iterator
, KCObject
, GetTypeForName
, KCCompressedBufferObject
6 from collections
import namedtuple
9 # From the defines in bsd/sys/kdebug.h:
49 def GetKdebugClassName(class_num
):
50 return (KdebugClassNames
[class_num
] + ' ({})'.format(class_num) if class_num in KdebugClassNames else 'unknown ({})'.format(class_num
))
52 @lldb_type_summary(['typefilter_t'])
53 @header('{0: <20s}'.format("class") + ' '.join(map('{:02x}'.format
, xrange(0, 255, 8))))
54 def GetKdebugTypefilter(typefilter
):
55 """ Summarizes the provided typefilter.
58 subclasses_per_class
= 256
61 subclasses_per_element
= 64
62 cur_typefilter
= cast(typefilter
, 'uint64_t *')
63 subclasses_fmts
= ' '.join(['{:02x}'] * 8)
65 elements_per_class
= subclasses_per_class
/ subclasses_per_element
68 for i
in xrange(0, classes
):
70 subclasses
= [0] * elements_per_class
72 # check subclass ranges for set bits, remember those subclasses
73 for j
in xrange(0, elements_per_class
):
74 element
= unsigned(cur_typefilter
[i
* elements_per_class
+ j
])
78 subclasses
[j
] = element
80 ## if any of the bits were set in a class, print the entire class
82 out_str
+= '{:<20s}'.format(GetKdebugClassName(i
))
83 for element
in subclasses
:
84 # split up the 64-bit values into byte-sized pieces
85 bytes = [unsigned((element
>> i
) & 0xff) for i
in (0, 8, 16, 24, 32, 40, 48, 56)]
86 out_str
+= subclasses_fmts
.format(*bytes)
93 @lldb_command('showkdebugtypefilter')
94 def ShowKdebugTypefilter(cmd_args
=None):
95 """ Show the current kdebug typefilter (or the typefilter at an address)
97 usage: showkdebugtypefilter [<address>]
101 typefilter
= kern
.GetValueFromAddress(cmd_args
[0], 'typefilter_t')
102 if unsigned(typefilter
) == 0:
103 raise ArgumentError('argument provided is NULL')
105 print GetKdebugTypefilter
.header
106 print '-' * len(GetKdebugTypefilter
.header
)
108 print GetKdebugTypefilter(typefilter
)
111 typefilter
= kern
.globals.kdbg_typefilter
112 if unsigned(typefilter
) == 0:
113 raise ArgumentError('no argument provided and active typefilter is not set')
115 print GetKdebugTypefilter
.header
116 print '-' * len(GetKdebugTypefilter
.header
)
117 print GetKdebugTypefilter(typefilter
)
119 def GetKdebugStatus():
120 """ Get a string summary of the kdebug subsystem.
124 kdebug_flags
= kern
.globals.kd_ctrl_page
.kdebug_flags
125 out
+= 'kdebug flags: {}\n'.format(xnudefines
.GetStateString(xnudefines
.kdebug_flags_strings
, kdebug_flags
))
126 events
= kern
.globals.nkdbufs
127 buf_mb
= events
* (64 if kern
.arch
== 'x86_64' or kern
.arch
.startswith('arm64') else 32) / 1000000
128 out
+= 'events allocated: {:<d} ({:<d} MB)\n'.format(events
, buf_mb
)
129 out
+= 'enabled: {}\n'.format('yes' if kern
.globals.kdebug_enable
!= 0 else 'no')
130 if kdebug_flags
& xnudefines
.kdebug_typefilter_check
:
131 out
+= 'typefilter:\n'
132 out
+= GetKdebugTypefilter
.header
+ '\n'
133 out
+= '-' * len(GetKdebugTypefilter
.header
) + '\n'
134 typefilter
= kern
.globals.kdbg_typefilter
135 if unsigned(typefilter
) != 0:
136 out
+= GetKdebugTypefilter(typefilter
)
140 @lldb_command('showkdebug')
141 def ShowKdebug(cmd_args
=None):
142 """ Show the current kdebug state.
147 print GetKdebugStatus()
149 @lldb_type_summary(['kperf_timer'])
150 @header('{:<10s} {:<7s} {:<20s} {:<20s}'.format('period-ns', 'action', 'deadline', 'fire-time'))
151 def GetKperfTimerSummary(timer
):
152 """ Get a string summary of a kperf timer.
155 timer: the kptimer object to get a summary of
158 fire_time
= timer
.kt_fire_time
161 return '{:<10d} {:<7d} {:<20d} {:<20d}\n'.format(
162 kern
.GetNanotimeFromAbstime(timer
.kt_period_abs
), timer
.kt_actionid
,
163 timer
.kt_cur_deadline
, fire_time
)
165 @lldb_type_summary(['action'])
166 @header('{:<10s} {:<20s} {:<20s}'.format('pid-filter', 'user-data', 'samplers'))
167 def GetKperfActionSummary(action
):
168 """ Get a string summary of a kperf action.
171 action: the action object to get a summary of
173 samplers
= xnudefines
.GetStateString(xnudefines
.kperf_samplers_strings
, action
.sample
)
174 return '{:<10s} {:<20x} {:<20s}\n'.format(
175 '-' if action
.pid_filter
< 0 else str(action
.pid_filter
), action
.userdata
, samplers
)
177 def GetKperfStatus():
178 """ Get a string summary of the kperf subsystem.
182 kperf_status
= int(kern
.globals.kperf_status
)
184 if kperf_status
== GetEnumValue('kperf_sampling::KPERF_SAMPLING_OFF'):
186 elif kperf_status
== GetEnumValue('kperf_sampling::KPERF_SAMPLING_SHUTDOWN'):
187 out
+= 'shutting down\n'
188 elif kperf_status
== GetEnumValue('kperf_sampling::KPERF_SAMPLING_ON'):
193 pet
= kern
.globals.kptimer
.g_pet_active
194 pet_timer_id
= kern
.globals.kptimer
.g_pet_active
196 pet_idle_rate
= kern
.globals.pet_idle_rate
197 out
+= 'legacy PET is active (timer = {:<d}, idle rate = {:<d})\n'.format(pet_timer_id
, pet_idle_rate
)
199 out
+= 'legacy PET is off\n'
201 lw_pet
= kern
.globals.kppet
.g_lightweight
203 lw_pet_gen
= kern
.globals.kppet_gencount
204 out
+= 'lightweight PET is active (timer = {:<d}, generation count = {:<d})\n'.format(pet_timer_id
, lw_pet_gen
)
206 out
+= 'lightweight PET is off\n'
208 actions
= kern
.globals.actionc
209 actions_arr
= kern
.globals.actionv
212 out
+= '{:<5s} '.format('id') + GetKperfActionSummary
.header
+ '\n'
213 for i
in xrange(0, actions
):
214 out
+= '{:<5d} '.format(i
) + GetKperfActionSummary(actions_arr
[i
])
216 timers
= kern
.globals.kptimer
.g_ntimers
217 timers_arr
= kern
.globals.kptimer
.g_timers
220 out
+= '{:<5s} '.format('id') + GetKperfTimerSummary
.header
+ '\n'
221 for i
in xrange(0, timers
):
222 out
+= '{:<5d} '.format(i
) + GetKperfTimerSummary(timers_arr
[i
])
227 def GetKtraceStatus():
228 """ Get a string summary of the ktrace subsystem.
232 state
= kern
.globals.ktrace_state
233 if state
== GetEnumValue('ktrace_state::KTRACE_STATE_OFF'):
234 out
+= 'ktrace is off\n'
236 out
+= 'ktrace is active ('
237 if state
== GetEnumValue('ktrace_state::KTRACE_STATE_FG'):
242 owner
= kern
.globals.ktrace_last_owner_execname
243 out
+= 'owned by: {0: <s}\n'.format(owner
)
244 active_mask
= kern
.globals.ktrace_active_mask
245 out
+= 'active systems: {:<#x}\n'.format(active_mask
)
250 @lldb_command('showktrace')
251 def ShowKtrace(cmd_args
=None):
252 """ Show the current ktrace state, including subsystems.
257 print GetKtraceStatus()
260 print GetKdebugStatus()
263 print GetKperfStatus()
266 class KDEvent(object):
268 Wrapper around kevent pointer that handles sorting logic.
270 def __init__(self
, timestamp
, kevent
):
272 self
.timestamp
= timestamp
274 def get_kevent(self
):
277 def __eq__(self
, other
):
278 return self
.timestamp
== other
.timestamp
280 def __lt__(self
, other
):
281 return self
.timestamp
< other
.timestamp
283 def __gt__(self
, other
):
284 return self
.timestamp
> other
.timestamp
289 Represents all events from a single CPU.
291 def __init__(self
, cpuid
):
293 self
.iter_store
= None
295 kdstoreinfo
= kern
.globals.kdbip
[cpuid
]
296 self
.kdstorep
= kdstoreinfo
.kd_list_head
298 if self
.kdstorep
.raw
== xnudefines
.KDS_PTR_NULL
:
299 # Returns an empty iterrator. It will immediatelly stop at
300 # first call to __next__().
303 self
.iter_store
= self
.get_kdstore(self
.kdstorep
)
305 # XXX Doesn't have the same logic to avoid un-mergeable events
306 # (respecting barrier_min and bufindx) as the C code.
308 self
.iter_idx
= self
.iter_store
.kds_readlast
310 def get_kdstore(self
, kdstorep
):
312 See POINTER_FROM_KDSPTR.
314 buf
= kern
.globals.kd_bufs
[kdstorep
.buffer_index
]
315 return addressof(buf
.kdsb_addr
[kdstorep
.offset
])
317 # Event iterator implementation returns KDEvent instance
323 # This CPU is out of events
324 if self
.iter_store
is None:
327 if self
.iter_idx
== self
.iter_store
.kds_bufindx
:
328 self
.iter_store
= None
331 keventp
= addressof(self
.iter_store
.kds_records
[self
.iter_idx
])
332 timestamp
= unsigned(keventp
.timestamp
)
334 # check for writer overrun
335 if timestamp
< self
.iter_store
.kds_timestamp
:
341 if self
.iter_idx
== xnudefines
.EVENTS_PER_STORAGE_UNIT
:
342 snext
= self
.iter_store
.kds_next
343 if snext
.raw
== xnudefines
.KDS_PTR_NULL
:
344 # Terminate iteration in next loop. Current element is the
345 # last one in this CPU buffer.
346 self
.iter_store
= None
348 self
.iter_store
= self
.get_kdstore(snext
)
349 self
.iter_idx
= self
.iter_store
.kds_readlast
351 return KDEvent(timestamp
, keventp
)
353 # Python 2 compatibility
355 return self
.__next
__()
358 def IterateKdebugEvents():
360 Yield events from the in-memory kdebug trace buffers.
362 ctrl
= kern
.globals.kd_ctrl_page
364 if (ctrl
.kdebug_flags
& xnudefines
.KDBG_BFINIT
) == 0:
367 barrier_min
= ctrl
.oldest_time
369 if (ctrl
.kdebug_flags
& xnudefines
.KDBG_WRAPPED
) != 0:
370 # TODO Yield a wrap event with the barrier_min timestamp.
373 # Merge sort all events from all CPUs.
374 cpus
= [KDCPU(cpuid
) for cpuid
in range(ctrl
.kdebug_cpus
)]
376 for event
in heapq
.merge(*cpus
):
377 yield event
.get_kevent()
380 def GetKdebugEvent(event
):
382 Return a string representing a kdebug trace event.
384 return '{:16} {:8} {:8x} {:16} {:16} {:16} {:16} {:4} {:8} {}'.format(
385 unsigned(event
.timestamp
), 0, unsigned(event
.debugid
),
386 unsigned(event
.arg1
), unsigned(event
.arg2
),
387 unsigned(event
.arg3
), unsigned(event
.arg4
), unsigned(event
.cpuid
),
388 unsigned(event
.arg5
), "")
391 @lldb_command('showkdebugtrace')
392 def ShowKdebugTrace(cmd_args
=None):
394 List the events present in the kdebug trace buffers.
396 (lldb) showkdebugtrace
399 * Events from IOPs may be missing or cut-off -- they weren't informed
400 of this kind of buffer collection.
402 for event
in IterateKdebugEvents():
403 print(GetKdebugEvent(event
))
406 @lldb_command('savekdebugtrace', 'N:')
407 def SaveKdebugTrace(cmd_args
=None, cmd_options
={}):
409 Save any valid ktrace events to a file.
411 (lldb) savekdebugtrace [-N <n-events>] <file-to-write>
414 * 32-bit kernels are unsupported.
415 * The trace file will be missing machine and config chunks, which might
416 prevent tools from analyzing it.
419 if kern
.arch
not in ['x86_64', 'x86_64h', 'arm64', 'arm64e']:
420 print('32-bit kernels are unsupported')
423 if len(cmd_args
) != 1:
424 raise ArgumentError('path to trace file is required')
426 nevents
= unsigned(kern
.globals.nkdbufs
)
428 print('kdebug buffers are not set up')
431 limit_nevents
= nevents
432 if '-N' in cmd_options
:
433 limit_nevents
= unsigned(cmd_options
['-N'])
434 if limit_nevents
> nevents
:
435 limit_nevents
= nevents
436 verbose
= config
['verbosity'] > vHUMAN
439 with open(cmd_args
[0], 'w+b') as f
:
440 FILE_MAGIC
= 0x55aa0300
441 EVENTS_TAG
= 0x00001e00
443 CHUNKHDR_PACK
= 'IHHQ'
444 FILEHDR_PACK
= CHUNKHDR_PACK
+ 'IIQQIIII'
448 numer
, denom
= GetTimebaseInfo()
450 # XXX The kernel doesn't have a solid concept of the wall time.
457 event_size
= unsigned(64)
459 file_hdr
= struct
.pack(
460 FILEHDR_PACK
, FILE_MAGIC
, 0, 0, FILEHDR_SIZE
,
461 numer
, denom
, wall_abstime
, wall_secs
, wall_usecs
, 0, 0,
464 file_offset
+= 16 + FILEHDR_SIZE
# chunk header plus file header
466 skip_nevents
= nevents
- limit_nevents
if limit_nevents
else 0
468 print('omitting {} events from the beginning'.format(skip_nevents
))
470 events_hdr
= struct
.pack(
471 CHUNKHDR_PACK
, EVENTS_TAG
, 0, 0, 0) # size will be filled in later
473 file_offset
+= 16 # header size
474 event_size_offset
= file_offset
- FUTURE_SIZE
475 # Future events timestamp -- doesn't need to be set for merged events.
476 f
.write(struct
.pack('Q', 0))
477 file_offset
+= FUTURE_SIZE
480 print('events start at offset {}'.format(file_offset
))
482 process
= LazyTarget().GetProcess()
483 error
= lldb
.SBError()
487 for event
in IterateKdebugEvents():
489 if skip_nevents
>= seen_nevents
:
490 if seen_nevents
% 1000 == 0:
491 sys
.stderr
.write('skipped {}/{} ({:4.2f}%) events'.format(
492 seen_nevents
, skip_nevents
,
493 float(seen_nevents
) / skip_nevents
* 100.0))
494 sys
.stderr
.write('\r')
498 event
= process
.ReadMemory(
499 unsigned(event
), event_size
, error
)
500 file_offset
+= event_size
503 # Periodically update the CLI with progress.
504 if written_nevents
% 1000 == 0:
505 sys
.stderr
.write('wrote {}/{} ({:4.2f}%) events'.format(
506 written_nevents
, limit_nevents
,
507 float(written_nevents
) / nevents
* 100.0))
508 sys
.stderr
.write('\r')
509 sys
.stderr
.write('\n')
510 print('wrote {} events'.format(written_nevents
))
512 print('events end at offset {}'.format(file_offset
))
514 # Normally, the chunk would need to be padded to 8, but events are
517 kcdata
= kern
.globals.kc_panic_data
518 kcdata_addr
= unsigned(kcdata
.kcd_addr_begin
)
519 kcdata_length
= unsigned(kcdata
.kcd_length
)
520 if kcdata_addr
!= 0 and kcdata_length
!= 0:
521 print('writing stackshot')
523 print('stackshot starts at offset {}'.format(file_offset
))
524 print('stackshot is {} bytes long'.format(kcdata_length
))
525 ssdata
= process
.ReadMemory(kcdata_addr
, kcdata_length
, error
)
526 magic
= struct
.unpack('I', ssdata
[:4])
527 if magic
[0] == GetTypeForName('KCDATA_BUFFER_BEGIN_COMPRESSED'):
529 print('found compressed stackshot')
530 iterator
= kcdata_item_iterator(ssdata
)
531 for item
in iterator
:
532 kcdata_buffer
= KCObject
.FromKCItem(item
)
533 if isinstance(kcdata_buffer
, KCCompressedBufferObject
):
534 kcdata_buffer
.ReadItems(iterator
)
535 decompressed
= kcdata_buffer
.Decompress(ssdata
)
536 ssdata
= decompressed
537 kcdata_length
= len(ssdata
)
540 'compressed stackshot is {} bytes long'.
541 format(kcdata_length
))
543 f
.write(struct
.pack(CHUNKHDR_PACK
, SSHOT_TAG
, 1, 0, kcdata_length
))
547 file_offset
+= kcdata_length
549 print('stackshot ends at offset {}'.format(file_offset
))
551 print('stackshot is not available, trace file may not be usable!')
553 # After the number of events is known, fix up the events chunk size.
554 events_data_size
= unsigned(written_nevents
* event_size
) + FUTURE_SIZE
555 f
.seek(event_size_offset
)
556 f
.write(struct
.pack('Q', events_data_size
))