pykdebugparser 1.4.0__py2.py3-none-any.whl → 1.5.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,134 @@
1
+ """Syntax highlighting of trace lines, as pygments' C lexer and `stata-dark` true color formatter do it.
2
+
3
+ pygments lexes each line through a general state machine, which formats about 19k lines/s: too slow for a live
4
+ trace. Trace lines use a small part of C's syntax, so `highlight` tokenizes them with one regular expression that
5
+ follows the same rules, and produces the same escape sequences. A line with anything outside that part (a label at its
6
+ start, `;` or `{`, comments, character literals, string escapes, floating-point numbers, ...) goes to pygments, so the
7
+ result is always pygments' own.
8
+ """
9
+ import re
10
+ from functools import lru_cache
11
+
12
+ from pygments import highlight as pygments_highlight
13
+ from pygments import lexers
14
+ from pygments.formatters import TerminalTrueColorFormatter
15
+ from pygments.token import Keyword, Name, Number, Operator, Punctuation, String, Whitespace
16
+
17
+ c_lexer = lexers.CLexer()
18
+ color_formatter = TerminalTrueColorFormatter(style='stata-dark')
19
+
20
+ # pygments.lexers.c_cpp.CFamilyLexer's 'keywords', 'types' and builtins, the names it does not lex as `Name`.
21
+ _KEYWORDS = {
22
+ 'asm', 'auto', 'break', 'const', 'constexpr', 'continue', 'countof', 'default', 'defer', 'do', 'else', 'enum',
23
+ 'extern', 'for', 'goto', 'if', 'register', 'restricted', 'return', 'sizeof', 'static', 'switch', 'typedef',
24
+ 'typeof', 'typeof_unqual', 'volatile', 'while', 'thread_local', 'alignas', 'alignof', 'static_assert', '_Pragma',
25
+ 'fortran',
26
+ }
27
+ _RESERVED = {'inline', '_inline', '__inline', 'naked', 'restrict', 'thread', '__int8', '__int16', '__int32',
28
+ '__int64', '__wchar_t', '__m128i', '__m128d', '__m128', '__m64'} | {
29
+ '__' + name for name in ('asm', 'based', 'except', 'stdcall', 'cdecl', 'fastcall', 'declspec', 'finally', 'try',
30
+ 'leave', 'w64', 'unaligned', 'raise', 'noop', 'identifier', 'forceinline', 'assume',
31
+ 'null')}
32
+ _TYPES = {'bool', 'int', 'long', 'float', 'short', 'double', 'char', 'unsigned', 'signed', 'void', '_BitInt',
33
+ '__int128'}
34
+ _BUILTINS = {'true', 'false', 'NULL', 'nullptr'}
35
+ # Names that start another lexer state, or a string with an affix (`L"..."`): left to pygments.
36
+ _STATEFUL = {'struct', 'union', 'case', 'L', 'u', 'U', 'u8'}
37
+
38
+ _INT_SUFFIX = r'(?:[uU][lL]{0,2}|[lL]{1,2}[uU]?)?'
39
+ _DEC = r'\d(?:\'?\d)*'
40
+ _HEX = r'[0-9a-fA-F](?:\'?[0-9a-fA-F])*'
41
+ # The alternatives are tried in the lexer's order at each token start. `unsupported` marks what pygments lexes
42
+ # differently from this tokenizer (comments and floating-point numbers); characters no alternative matches (`'`, `\\`,
43
+ # `#`, `$`, a string with an escape, ...) also send the line to pygments.
44
+ _TOKEN = re.compile(
45
+ r'(?P<unsupported>/[/*]'
46
+ r'|0[xX](?:' + _HEX + r'\.' + _HEX + r'|\.' + _HEX + r'|' + _HEX + r')[pP]'
47
+ r'|-?(?:' + _DEC + r'\.' + _DEC + r'|\.' + _DEC + r'|' + _DEC + r')[eE][+-]?' + _DEC +
48
+ r'|-?(?:' + _DEC + r'\.|\.' + _DEC + r'|' + _DEC + r'[fFlL])'
49
+ r')'
50
+ r'|(?P<string>")(?P<content>[^\\"\n]*)(?P<close>")'
51
+ r'|(?P<hex>-?0[xX]' + _HEX + _INT_SUFFIX + r')'
52
+ r'|(?P<bin>-?0[bB][01](?:\'?[01])*' + _INT_SUFFIX + r')'
53
+ r'|(?P<oct>-?0(?:\'?[0-7])+' + _INT_SUFFIX + r')'
54
+ r'|(?P<int>-?' + _DEC + _INT_SUFFIX + r')'
55
+ r'|(?P<op>[~!%^&*+=|?:<>/-])'
56
+ r'|(?P<punct>[()\[\],.])'
57
+ r'|(?P<name>[a-zA-Z_]\w*)'
58
+ r'|(?P<ws>[^\S\n]+)'
59
+ )
60
+ # Whole-line rules of the lexer: a label at the start of a line, and functions (their rules need a `;` or `{`).
61
+ _UNSUPPORTED = re.compile(r'^\s*[a-zA-Z_$]\w*\s*:(?!:)|[;{]')
62
+
63
+
64
+ def _style(ttype):
65
+ # The formatter uses the style of the token type, or of its closest parent that has one.
66
+ while str(ttype) not in color_formatter.style_string and ttype.parent is not None:
67
+ ttype = ttype.parent
68
+ return color_formatter.style_string.get(str(ttype), ('', ''))
69
+
70
+
71
+ _GROUPS = {'hex': _style(Number.Hex), 'bin': _style(Number.Bin), 'oct': _style(Number.Oct),
72
+ 'int': _style(Number.Integer), 'op': _style(Operator), 'punct': _style(Punctuation)}
73
+ _NAME_STYLES = {}
74
+ for _names, _ttype in ((_KEYWORDS, Keyword), (_RESERVED, Keyword.Reserved), (_TYPES, Keyword.Type),
75
+ (_BUILTINS, Name.Builtin)):
76
+ for _name in _names:
77
+ _NAME_STYLES[_name] = _style(_ttype)
78
+ _NAME = _style(Name)
79
+ _STRING = _style(String)
80
+ # Whitespace only gets a foreground color, which a space does not show: it can stay in the style before it.
81
+ _FG_RESET = '\x1b[39m'
82
+ assert _style(Whitespace)[1] == _FG_RESET
83
+
84
+
85
+ def _pygments(text: str) -> str:
86
+ return pygments_highlight(text, c_lexer, color_formatter).strip()
87
+
88
+
89
+ # Traces repeat (the same calls with the same arguments): in a busy `--bsc` trace, 77% of the lines were among the
90
+ # 16k most recent ones.
91
+ @lru_cache(maxsize=1 << 14)
92
+ def highlight(text: str) -> str:
93
+ """`text` colored as `pygments.highlight(text, CLexer(), TerminalTrueColorFormatter('stata-dark'))` shows it.
94
+
95
+ pygments starts and ends the escape sequence of every token, even between tokens of the same color, and colors
96
+ spaces. Here a sequence starts only where the color changes, which renders the same in about half the bytes (the
97
+ terminal is the bottleneck of a busy trace).
98
+ """
99
+ if _UNSUPPORTED.search(text):
100
+ return _pygments(text)
101
+ out = []
102
+ style = None # the (on, off) in effect
103
+ pos = 0
104
+ for match in _TOKEN.finditer(text):
105
+ if match.start() != pos:
106
+ return _pygments(text)
107
+ pos = match.end()
108
+ kind = match.lastgroup
109
+ if kind == 'ws':
110
+ out.append(match.group())
111
+ continue
112
+ if kind == 'unsupported':
113
+ return _pygments(text)
114
+ if kind == 'close':
115
+ token_style = _STRING
116
+ elif kind == 'name':
117
+ name = match.group()
118
+ if name in _STATEFUL:
119
+ return _pygments(text)
120
+ token_style = _NAME_STYLES.get(name, _NAME)
121
+ else:
122
+ token_style = _GROUPS[kind]
123
+ if style is None or token_style[0] != style[0]:
124
+ # Ending a foreground color is not needed when the next one replaces it.
125
+ if style is not None and style[1] != _FG_RESET:
126
+ out.append(style[1])
127
+ out.append(token_style[0])
128
+ style = token_style
129
+ out.append(match.group())
130
+ if pos != len(text):
131
+ return _pygments(text)
132
+ if style is not None:
133
+ out.append(style[1])
134
+ return ''.join(out).strip()
@@ -2,18 +2,17 @@ from datetime import datetime
2
2
  import io
3
3
  import math
4
4
 
5
- from pygments import highlight, lexers, formatters
6
5
  from termcolor import colored
7
6
 
8
7
  from pykdebugparser.callstacks_parser import CallstacksParser
8
+ from pykdebugparser.highlight import highlight
9
9
  from pykdebugparser.kd_buf_parser import KdBufParser
10
10
  from pykdebugparser.kevent import DgbFuncQual
11
11
  from pykdebugparser.trace_codes import default_trace_codes
12
+ from pykdebugparser.trace_handlers.trace import TRACE_LOST_EVENTS
12
13
  from pykdebugparser.traces_parser import TracesParser
13
14
  from pykdebugparser.os_log_event import OsLogEvent
14
15
 
15
- c_lexer = lexers.CLexer()
16
- color_formatter = formatters.TerminalTrueColorFormatter(style='stata-dark')
17
16
 
18
17
  DBG_TRACE = 7
19
18
  DBG_FSYSTEM = 3
@@ -93,7 +92,9 @@ class PyKdebugParser:
93
92
  if self.filter_process is not None:
94
93
  trace_generator = filter(self._filter_process_callback, trace_generator)
95
94
  if add_trace_class:
96
- trace_generator = filter(lambda t: t.ktraces[0].eventid >> 24 != DBG_TRACE, trace_generator)
95
+ # Lost events stay: they tell the reader the trace has a gap.
96
+ trace_generator = filter(lambda t: t.ktraces[0].eventid >> 24 != DBG_TRACE
97
+ or t.ktraces[0].eventid == TRACE_LOST_EVENTS, trace_generator)
97
98
  if add_fs_class:
98
99
  trace_generator = filter(lambda t: t.ktraces[0].eventid >> 24 != DBG_FSYSTEM, trace_generator)
99
100
  return trace_generator
@@ -187,7 +188,7 @@ class PyKdebugParser:
187
188
  formatted_data += f'{self._format_process(tid):<34}'
188
189
  event_rep = str(trace)
189
190
  if self.color:
190
- event_rep = highlight(event_rep, c_lexer, color_formatter).strip()
191
+ event_rep = highlight(event_rep)
191
192
 
192
193
  return formatted_data + event_rep
193
194
 
@@ -378,11 +378,18 @@ def serialize_stat_flags(flags: int) -> List[StatFlags]:
378
378
  return stat_flags
379
379
 
380
380
 
381
+ # bsd/sys/errno.h: codes the kernel uses internally, which a syscall end event can carry.
382
+ KERNEL_ERRNOS = {-1: 'ERESTART', -2: 'EJUSTRETURN'}
383
+
384
+
381
385
  def serialize_result(end_event, success_name='', fmt=lambda x: x) -> str:
382
386
  error_code = end_event.values[0]
383
387
  res = end_event.values[1]
388
+ signed = ctypes.c_int64(error_code).value
384
389
  if error_code in errno.errorcode:
385
390
  err = f'errno: {errno.errorcode[error_code]}({error_code})'
391
+ elif signed in KERNEL_ERRNOS:
392
+ err = f'errno: {KERNEL_ERRNOS[signed]}({signed})'
386
393
  else:
387
394
  err = f'errno: {error_code}'
388
395
  success = f'{success_name}: {fmt(res)}' if success_name else ''
@@ -2823,6 +2830,55 @@ class BscGeteuid:
2823
2830
  return f'geteuid(), uid: {self.uid}'
2824
2831
 
2825
2832
 
2833
+ # Darwin's signal numbers, which differ from Linux's.
2834
+ DARWIN_SIGNALS = (None, 'SIGHUP', 'SIGINT', 'SIGQUIT', 'SIGILL', 'SIGTRAP', 'SIGABRT', 'SIGEMT', 'SIGFPE', 'SIGKILL',
2835
+ 'SIGBUS', 'SIGSEGV', 'SIGSYS', 'SIGPIPE', 'SIGALRM', 'SIGTERM', 'SIGURG', 'SIGSTOP', 'SIGTSTP',
2836
+ 'SIGCONT', 'SIGCHLD', 'SIGTTIN', 'SIGTTOU', 'SIGIO', 'SIGXCPU', 'SIGXFSZ', 'SIGVTALRM', 'SIGPROF',
2837
+ 'SIGWINCH', 'SIGINFO', 'SIGUSR1', 'SIGUSR2')
2838
+
2839
+
2840
+ def format_wait_status(status: int) -> str:
2841
+ """Describe a wait status (`W_EXITCODE(code, signal)`) as its exit code or terminating signal."""
2842
+ signal = status & 0x7f
2843
+ if not signal:
2844
+ return f'exit code: {(status >> 8) & 0xff}'
2845
+ name = DARWIN_SIGNALS[signal] if signal < len(DARWIN_SIGNALS) else str(signal)
2846
+ return f'signal: {name}' + (', core dumped' if status & 0x80 else '')
2847
+
2848
+
2849
+ @dataclass
2850
+ class ProcExit:
2851
+ """A process exiting (`proc_exit`). An exec also ends the process image it replaces: xnu gives the new image a new
2852
+ proc and exits the old one, with the same pid and status 0."""
2853
+ ktraces: List
2854
+ pid: int
2855
+ status: int
2856
+
2857
+ def __str__(self):
2858
+ return f'proc_exit, pid: {self.pid}, {format_wait_status(self.status)}'
2859
+
2860
+
2861
+ @dataclass
2862
+ class BscExit:
2863
+ ktraces: List
2864
+ rval: int
2865
+
2866
+ def __str__(self):
2867
+ return f'exit({self.rval})'
2868
+
2869
+
2870
+ @dataclass
2871
+ class BscBsdthreadTerminate:
2872
+ ktraces: List
2873
+ stackaddr: int
2874
+ freesize: int
2875
+ port: int
2876
+ sem: int
2877
+
2878
+ def __str__(self):
2879
+ return f'bsdthread_terminate({hex(self.stackaddr)}, {hex(self.freesize)}, {self.port}, {self.sem})'
2880
+
2881
+
2826
2882
  @dataclass
2827
2883
  class BscWait4:
2828
2884
  ktraces: List
@@ -2970,6 +3026,35 @@ class BscSync:
2970
3026
  return 'sync()'
2971
3027
 
2972
3028
 
3029
+ @dataclass
3030
+ class BscPthreadKill:
3031
+ ktraces: List
3032
+ thread_port: int
3033
+ sig: int
3034
+ result: str
3035
+
3036
+ def __str__(self):
3037
+ rep = f'pthread_kill({self.thread_port}, {self.sig})'
3038
+ if self.result:
3039
+ rep += f', {self.result}'
3040
+ return rep
3041
+
3042
+
3043
+ @dataclass
3044
+ class BscSigreturn:
3045
+ ktraces: List
3046
+ uctx: int
3047
+ infostyle: int
3048
+ token: int
3049
+ result: str
3050
+
3051
+ def __str__(self):
3052
+ rep = f'sigreturn({hex(self.uctx)}, {self.infostyle}, {hex(self.token)})'
3053
+ if self.result:
3054
+ rep += f', {self.result}'
3055
+ return rep
3056
+
3057
+
2973
3058
  @dataclass
2974
3059
  class BscKill:
2975
3060
  ktraces: List
@@ -5384,6 +5469,20 @@ def handle_geteuid(parser, events):
5384
5469
  return BscGeteuid(events, events[-1].values[1])
5385
5470
 
5386
5471
 
5472
+ def handle_proc_exit(parser, events):
5473
+ args = events[0].values
5474
+ return ProcExit(events, args[0], ctypes.c_int32(args[1]).value)
5475
+
5476
+
5477
+ def handle_exit(parser, events):
5478
+ return BscExit(events, ctypes.c_int32(events[0].values[0]).value)
5479
+
5480
+
5481
+ def handle_bsdthread_terminate(parser, events):
5482
+ args = events[0].values
5483
+ return BscBsdthreadTerminate(events, args[0], args[1], args[2] & 0xffffffff, args[3] & 0xffffffff)
5484
+
5485
+
5387
5486
  def handle_wait4(parser, events, no_cancel=False):
5388
5487
  args = events[0].values
5389
5488
  return BscWait4(events, args[0], args[1], args[2], args[3], serialize_result(events[-1], 'pid'), no_cancel)
@@ -5438,6 +5537,16 @@ def handle_sync(parser, events):
5438
5537
  return BscSync(events)
5439
5538
 
5440
5539
 
5540
+ def handle_pthread_kill(parser, events):
5541
+ args = events[0].values
5542
+ return BscPthreadKill(events, args[0] & 0xffffffff, args[1], serialize_result(events[-1]))
5543
+
5544
+
5545
+ def handle_sigreturn(parser, events):
5546
+ args = events[0].values
5547
+ return BscSigreturn(events, args[0], args[1], args[2], serialize_result(events[-1]))
5548
+
5549
+
5441
5550
  def handle_kill(parser, events):
5442
5551
  return BscKill(events, events[0].values[0], events[0].values[1], serialize_result(events[-1]))
5443
5552
 
@@ -7133,6 +7242,8 @@ handlers = {
7133
7242
  'BSC_fchflags': handle_fchflags,
7134
7243
  'BSC_sync': handle_sync,
7135
7244
  'BSC_kill': handle_kill,
7245
+ 'BSC_pthread_kill': handle_pthread_kill,
7246
+ 'BSC_sigreturn': handle_sigreturn,
7136
7247
  'BSC_getppid': handle_getppid,
7137
7248
  'BSC_sys_dup': handle_sys_dup,
7138
7249
  'BSC_pipe': handle_pipe,
@@ -7472,6 +7583,9 @@ handlers = {
7472
7583
  'BSC_sys_crossarch_trap': handle_sys_crossarch_trap,
7473
7584
  'BSC_map_with_linking_np': handle_map_with_linking_np,
7474
7585
  'BSC_wait4': handle_wait4,
7586
+ 'proc_exit': handle_proc_exit,
7587
+ 'BSC_exit': handle_exit,
7588
+ 'BSC_bsdthread_terminate': handle_bsdthread_terminate,
7475
7589
  'BSC_mknodat': handle_mknodat,
7476
7590
  'BSC_mkfifoat': handle_mkfifoat,
7477
7591
  'BSC_freadlink': handle_freadlink,
@@ -18,6 +18,10 @@ def to_rtld_flags(flags: int):
18
18
  return [r for r in RtldFlag if r.value & flags]
19
19
 
20
20
 
21
+ def _image(path: str) -> str:
22
+ return f', image: "{path}"' if path else ''
23
+
24
+
21
25
  @dataclass
22
26
  class DyldUuidMapA:
23
27
  ktraces: List
@@ -34,9 +38,11 @@ class DyldUuidMapB:
34
38
  ktraces: List
35
39
  fid_objno: int
36
40
  fid_generation: int
41
+ path: str = ''
37
42
 
38
43
  def __str__(self):
39
- return f'DYLD_uuid_map_b, fid_objno: {self.fid_objno}, fid_generation: {hex(self.fid_generation)}'
44
+ rep = f'DYLD_uuid_map_b, fid_objno: {self.fid_objno}, fid_generation: {hex(self.fid_generation)}'
45
+ return rep + f', path: "{self.path}"' if self.path else rep
40
46
 
41
47
 
42
48
  @dataclass
@@ -55,9 +61,11 @@ class DyldUuidUnmapB:
55
61
  ktraces: List
56
62
  fid_objno: int
57
63
  fid_generation: int
64
+ path: str = ''
58
65
 
59
66
  def __str__(self):
60
- return f'DYLD_uuid_unmap_b, fid_objno: {self.fid_objno}, fid_generation: {hex(self.fid_generation)}'
67
+ rep = f'DYLD_uuid_unmap_b, fid_objno: {self.fid_objno}, fid_generation: {hex(self.fid_generation)}'
68
+ return rep + f', path: "{self.path}"' if self.path else rep
61
69
 
62
70
 
63
71
  @dataclass
@@ -77,9 +85,11 @@ class DyldUuidSharedCacheB:
77
85
  ktraces: List
78
86
  fid_objno: int
79
87
  fid_generation: int
88
+ path: str = ''
80
89
 
81
90
  def __str__(self):
82
- return f'DYLD_uuid_shared_cache_b, fid_objno: {self.fid_objno}, fid_generation: {hex(self.fid_generation)}'
91
+ rep = f'DYLD_uuid_shared_cache_b, fid_objno: {self.fid_objno}, fid_generation: {hex(self.fid_generation)}'
92
+ return rep + f', path: "{self.path}"' if self.path else rep
83
93
 
84
94
 
85
95
  @dataclass
@@ -106,9 +116,53 @@ class DyldFuncForAddImage:
106
116
  ktraces: List
107
117
  addr: int
108
118
  func: int
119
+ image: str = ''
120
+
121
+ def __str__(self):
122
+ return f'DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, addr: {hex(self.addr)}{_image(self.image)}, func: {hex(self.func)}'
123
+
124
+
125
+ @dataclass
126
+ class DyldFuncForRemoveImage:
127
+ ktraces: List
128
+ addr: int
129
+ func: int
130
+ image: str = ''
131
+
132
+ def __str__(self):
133
+ return (f'DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE, addr: {hex(self.addr)}{_image(self.image)}, '
134
+ f'func: {hex(self.func)}')
135
+
136
+
137
+ @dataclass
138
+ class DyldStaticInitializer:
139
+ ktraces: List
140
+ mh: int
141
+ func: int
142
+ image: str = ''
143
+
144
+ def __str__(self):
145
+ return f'DBG_DYLD_TIMING_STATIC_INITIALIZER, mh: {hex(self.mh)}{_image(self.image)}, func: {hex(self.func)}'
146
+
147
+
148
+ @dataclass
149
+ class DyldObjcInit:
150
+ ktraces: List
151
+ mh: int
152
+ image: str = ''
153
+
154
+ def __str__(self):
155
+ return f'DBG_DYLD_TIMING_OBJC_INIT, mh: {hex(self.mh)}{_image(self.image)}'
156
+
157
+
158
+ @dataclass
159
+ class DyldTiming:
160
+ """A dyld phase traced without arguments (applying fixups, mapping ObjC images, ...)."""
161
+ ktraces: List
162
+ name: str
109
163
 
110
164
  def __str__(self):
111
- return f'DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE, addr: {hex(self.addr)}, func: {hex(self.func)}'
165
+ return self.name
112
166
 
113
167
 
114
168
  @dataclass
@@ -171,34 +225,55 @@ class Dladdr:
171
225
  return f'dladdr({hex(self.addr)}), ret: {self.ret}'
172
226
 
173
227
 
174
- def handle_uuid_map_a(parser, events):
228
+ # dyld traces each image (un)load as an `_a` event with its UUID and load address, followed on the same thread by a
229
+ # `_b` event with the id of its path string. The parser keeps the loaded images per process, so the events that only
230
+ # carry a mach header (initializers, ObjC init, ...) can name their image.
231
+
232
+ def _image_path(parser, events, mh):
233
+ return parser.dyld_images.get((parser.threads_pids.get(events[0].tid, -1), mh), '')
234
+
235
+
236
+ def _map_a(parser, events, cls):
175
237
  args = events[0].values
176
- return DyldUuidMapA(events, UUID(bytes=events[0].data[:16]), args[2], args[3])
238
+ parser.dyld_pending_load_addr[events[0].tid] = args[2]
239
+ return cls(events, UUID(bytes=events[0].data[:16]), args[2], args[3])
240
+
241
+
242
+ def _map_b(parser, events, cls, loaded):
243
+ args = events[0].values
244
+ path = parser.global_strings.get(args[1], '') if args[1] else ''
245
+ load_addr = parser.dyld_pending_load_addr.pop(events[0].tid, None)
246
+ if load_addr is not None:
247
+ key = (parser.threads_pids.get(events[0].tid, -1), load_addr)
248
+ if loaded and path:
249
+ parser.dyld_images[key] = path
250
+ elif not loaded:
251
+ parser.dyld_images.pop(key, None)
252
+ return cls(events, args[0] & 0xffffffff, args[0] >> 32, path)
253
+
254
+
255
+ def handle_uuid_map_a(parser, events):
256
+ return _map_a(parser, events, DyldUuidMapA)
177
257
 
178
258
 
179
259
  def handle_uuid_map_b(parser, events):
180
- arg = events[0].values[0]
181
- return DyldUuidMapB(events, arg & 0xffffffff, arg >> 32)
260
+ return _map_b(parser, events, DyldUuidMapB, loaded=True)
182
261
 
183
262
 
184
263
  def handle_uuid_unmap_a(parser, events):
185
- args = events[0].values
186
- return DyldUuidUnmapA(events, UUID(bytes=events[0].data[:16]), args[2], args[3])
264
+ return _map_a(parser, events, DyldUuidUnmapA)
187
265
 
188
266
 
189
267
  def handle_uuid_unmap_b(parser, events):
190
- arg = events[0].values[0]
191
- return DyldUuidUnmapB(events, arg & 0xffffffff, arg >> 32)
268
+ return _map_b(parser, events, DyldUuidUnmapB, loaded=False)
192
269
 
193
270
 
194
271
  def handle_uuid_shared_cache_a(parser, events):
195
- args = events[0].values
196
- return DyldUuidSharedCacheA(events, UUID(bytes=events[0].data[:16]), args[2], args[3])
272
+ return _map_a(parser, events, DyldUuidSharedCacheA)
197
273
 
198
274
 
199
275
  def handle_uuid_shared_cache_b(parser, events):
200
- arg = events[0].values[0]
201
- return DyldUuidSharedCacheB(events, arg & 0xffffffff, arg >> 32)
276
+ return _map_b(parser, events, DyldUuidSharedCacheB, loaded=True)
202
277
 
203
278
 
204
279
  def handle_timing_launch_executable(parser, events):
@@ -217,7 +292,26 @@ def handle_timing_map_image(parser, events):
217
292
 
218
293
  def handle_timing_func_for_add_image(parser, events):
219
294
  args = events[0].values
220
- return DyldFuncForAddImage(events, args[1], args[2])
295
+ return DyldFuncForAddImage(events, args[1], args[2], _image_path(parser, events, args[1]))
296
+
297
+
298
+ def handle_timing_func_for_remove_image(parser, events):
299
+ args = events[0].values
300
+ return DyldFuncForRemoveImage(events, args[1], args[2], _image_path(parser, events, args[1]))
301
+
302
+
303
+ def handle_timing_static_initializer(parser, events):
304
+ args = events[0].values
305
+ return DyldStaticInitializer(events, args[1], args[2], _image_path(parser, events, args[1]))
306
+
307
+
308
+ def handle_timing_objc_init(parser, events):
309
+ args = events[0].values
310
+ return DyldObjcInit(events, args[1], _image_path(parser, events, args[1]))
311
+
312
+
313
+ def handle_timing(parser, events):
314
+ return DyldTiming(events, parser.trace_codes[events[0].eventid])
221
315
 
222
316
 
223
317
  def handle_timing_bootstrap_start(parser, events):
@@ -257,6 +351,14 @@ handlers = {
257
351
  'DBG_DYLD_TIMING_LAUNCH_EXECUTABLE': handle_timing_launch_executable,
258
352
  'DBG_DYLD_TIMING_MAP_IMAGE': handle_timing_map_image,
259
353
  'DBG_DYLD_TIMING_FUNC_FOR_ADD_IMAGE': handle_timing_func_for_add_image,
354
+ 'DBG_DYLD_TIMING_FUNC_FOR_REMOVE_IMAGE': handle_timing_func_for_remove_image,
355
+ 'DBG_DYLD_TIMING_STATIC_INITIALIZER': handle_timing_static_initializer,
356
+ 'DBG_DYLD_TIMING_OBJC_INIT': handle_timing_objc_init,
357
+ 'DBG_DYLD_TIMING_OBJC_MAP': handle_timing,
358
+ 'DBG_DYLD_TIMING_APPLY_FIXUPS': handle_timing,
359
+ 'DBG_DYLD_TIMING_ATTACH_CODESIGNATURE': handle_timing,
360
+ 'DBG_DYLD_TIMING_BUILD_CLOSURE': handle_timing,
361
+ 'DBG_DYLD_REMOTE_IMAGE_NOTIFIER': handle_timing,
260
362
  'DBG_DYLD_TIMING_BOOTSTRAP_START': handle_timing_bootstrap_start,
261
363
  'DBG_DYLD_TIMING_DLOPEN': handle_timing_dlopen,
262
364
  'DBG_DYLD_TIMING_DLOPEN_PREFLIGHT': handle_timing_dlopen_preflight,
@@ -1,5 +1,11 @@
1
1
  from dataclasses import dataclass
2
- from typing import List
2
+ from typing import List, Optional
3
+
4
+ from pykdebugparser.trace_codes import default_trace_codes
5
+
6
+ DBG_DKRW = 0x302
7
+ # bsd/sys/kdebug.h: the code of a disk I/O event is these flags (`P_RdMetaAsync`, `P_WrDataDone`, ...).
8
+ DKIO_DONE = 0x01
3
9
 
4
10
 
5
11
  @dataclass
@@ -12,11 +18,47 @@ class VfsLookup:
12
18
  return f'lookup("{self.path}"), vnode id: {self.vnode_id}'
13
19
 
14
20
 
21
+ @dataclass
22
+ class DiskIo:
23
+ """A disk I/O, from its issue (device, block, size) to its completion (bytes not transferred, error)."""
24
+ ktraces: List
25
+ name: str
26
+ dev: Optional[int]
27
+ blkno: Optional[int]
28
+ size: Optional[int]
29
+ resid: int
30
+ error: int
31
+
32
+ def __str__(self):
33
+ rep = self.name
34
+ if self.dev is not None:
35
+ rep += f', dev: {self.dev >> 24},{self.dev & 0xffffff}, blkno: {hex(self.blkno)}, size: {self.size}'
36
+ return rep + f', resid: {self.resid}, error: {self.error}'
37
+
38
+
15
39
  def handle_vfs_lookup(parser, events):
16
40
  node = parser.parse_vnode(events)
17
41
  return VfsLookup(events, node.path, node.vnode_id)
18
42
 
19
43
 
44
+ def handle_disk_io(parser, events):
45
+ # xnu traces the issue as (buf, dev, blkno, size) and the completion, often on another thread, as
46
+ # (buf, vnode, resid, error); the buf address pairs them. Print one line when the I/O completes.
47
+ event = events[0]
48
+ buf = event.values[0]
49
+ if not (event.eventid >> 2) & DKIO_DONE:
50
+ parser.pending_disk_io[buf] = event
51
+ return None
52
+ start = parser.pending_disk_io.pop(buf, None)
53
+ resid, error = event.values[2], event.values[3]
54
+ if start is None:
55
+ return DiskIo(events, parser.trace_codes[event.eventid].removesuffix('Done'), None, None, None, resid, error)
56
+ args = start.values
57
+ return DiskIo([start, event], parser.trace_codes[start.eventid], args[1] & 0xffffffff, args[2], args[3], resid,
58
+ error)
59
+
60
+
20
61
  handlers = {
21
62
  'VFS_LOOKUP': handle_vfs_lookup,
22
63
  }
64
+ handlers.update({name: handle_disk_io for eventid, name in default_trace_codes().items() if eventid >> 16 == DBG_DKRW})
@@ -2,10 +2,14 @@ import ctypes
2
2
  from dataclasses import dataclass
3
3
  from enum import Enum
4
4
  from functools import partial
5
- from typing import List
5
+ from typing import List, Optional
6
6
 
7
+ from pykdebugparser.trace_codes import default_trace_codes
7
8
  from pykdebugparser.trace_handlers import parse_enum
8
9
 
10
+ # bsd/sys/kdebug.h: (DBG_MACH << 8) | DBG_MACH_EXCP_SYNC_ARM
11
+ DBG_MACH_EXCP_SYNC_ARM = 0x103
12
+
9
13
 
10
14
  class AsynchronousSystemTrapsReason(Enum):
11
15
  AST_NONE = 0x00
@@ -59,10 +63,13 @@ class ExceptionSyndromeRegisterClass(Enum):
59
63
  ESR_EC_TRAP_SIMD_FP = 0x07
60
64
  ESR_EC_PTRAUTH_INSTR_TRAP = 0x09
61
65
  ESR_EC_MCRR_MRRC_CP14_TRAP = 0x0c
66
+ ESR_EC_BTI_FAIL = 0x0d
62
67
  ESR_EC_ILLEGAL_INSTR_SET = 0x0e
63
68
  ESR_EC_SVC_32 = 0x11
64
69
  ESR_EC_SVC_64 = 0x15
70
+ ESR_EC_HVC_64 = 0x16
65
71
  ESR_EC_MSR_TRAP = 0x18
72
+ ESR_EC_PAC_FAIL = 0x1c
66
73
  ESR_EC_IABORT_EL0 = 0x20
67
74
  ESR_EC_IABORT_EL1 = 0x21
68
75
  ESR_EC_PC_ALIGN = 0x22
@@ -281,6 +288,25 @@ class KernelUncategorizedExcArm:
281
288
  return f'KernelUncategorizedExcArm, class: {esr_class}, far: {hex(self.far)}, pc: {hex(self.pc)}'
282
289
 
283
290
 
291
+ @dataclass
292
+ class ArmException:
293
+ """An arm64 synchronous exception (`sleh.c`): its ESR, fault address and PC, for the classes without their own
294
+ trace class (breakpoints, `brk` traps, pointer authentication failures, alignment faults, ...)."""
295
+ ktraces: List
296
+ name: str
297
+ esr: int
298
+ far: int
299
+ pc: int
300
+
301
+ def __str__(self):
302
+ esr_class = self.esr >> 26
303
+ try:
304
+ esr_class = ExceptionSyndromeRegisterClass(esr_class).name
305
+ except ValueError:
306
+ pass
307
+ return f'{self.name}, class: {esr_class}, far: {hex(self.far)}, pc: {hex(self.pc)}'
308
+
309
+
284
310
  @dataclass
285
311
  class KernelDataAbortSameElExcArm:
286
312
  ktraces: List
@@ -594,6 +620,58 @@ class SemaphoreTimedwait:
594
620
  return f'semaphore_timedwait({hex(self.wait_name)}, {self.sec}, {self.nsec}), result: {self.result.name}'
595
621
 
596
622
 
623
+ @dataclass
624
+ class TaskForPid:
625
+ ktraces: List
626
+ target_tport: int
627
+ pid: int
628
+ t: int
629
+ result: KernReturn
630
+
631
+ def __str__(self):
632
+ return f'task_for_pid({self.target_tport}, {self.pid}, {hex(self.t)}), result: {self.result.name}'
633
+
634
+
635
+ @dataclass
636
+ class TaskNameForPid:
637
+ ktraces: List
638
+ target_tport: int
639
+ pid: int
640
+ tn: int
641
+ result: KernReturn
642
+
643
+ def __str__(self):
644
+ return f'task_name_for_pid({self.target_tport}, {self.pid}, {hex(self.tn)}), result: {self.result.name}'
645
+
646
+
647
+ @dataclass
648
+ class PidForTask:
649
+ ktraces: List
650
+ task: int
651
+ pid_addr: int
652
+ result: KernReturn
653
+
654
+ def __str__(self):
655
+ return f'pid_for_task({self.task}, {hex(self.pid_addr)}), result: {self.result.name}'
656
+
657
+
658
+ @dataclass
659
+ class TaskSuspension:
660
+ """A task or thread suspended (by `task_suspend`, a debugger, ...) or resumed (osfmk `DBG_MACH_SUSPENSION`)."""
661
+ ktraces: List
662
+ name: str
663
+ target: str
664
+ target_id: int
665
+ user_stop_count: Optional[int] = None
666
+ pidsuspended: Optional[int] = None
667
+
668
+ def __str__(self):
669
+ rep = f'{self.name}, {self.target}: {self.target_id}'
670
+ if self.user_stop_count is not None:
671
+ rep += f', user_stop_count: {self.user_stop_count}, pidsuspended: {bool(self.pidsuspended)}'
672
+ return rep
673
+
674
+
597
675
  @dataclass
598
676
  class MachPortGetAttributes:
599
677
  ktraces: List
@@ -1017,6 +1095,11 @@ def handle_user_instr_abort_lower_el_exc_arm(parser, events):
1017
1095
  return UserInstrAbortLowerElExcArm(events, *events[0].values[:3])
1018
1096
 
1019
1097
 
1098
+ def handle_arm_exception(parser, events):
1099
+ args = events[0].values
1100
+ return ArmException(events, parser.trace_codes[events[0].eventid], args[0], args[1], args[2])
1101
+
1102
+
1020
1103
  def handle_user_data_abort_lower_el_exc_arm(parser, events):
1021
1104
  args = events[0].values
1022
1105
  return UserDataAbortLowerElExcArm(events, args[0], args[1], args[2])
@@ -1057,6 +1140,41 @@ def handle_msc_mach_vm_map_trap(parser, events):
1057
1140
  return MachVmMap(events, *events[0].values)
1058
1141
 
1059
1142
 
1143
+ def handle_msc_task_for_pid(parser, events):
1144
+ args = events[0].values
1145
+ return TaskForPid(events, args[0] & 0xffffffff, ctypes.c_int32(args[1]).value, args[2],
1146
+ parse_enum(KernReturn, events[-1].values[0]))
1147
+
1148
+
1149
+ def handle_msc_task_name_for_pid(parser, events):
1150
+ args = events[0].values
1151
+ return TaskNameForPid(events, args[0] & 0xffffffff, ctypes.c_int32(args[1]).value, args[2],
1152
+ parse_enum(KernReturn, events[-1].values[0]))
1153
+
1154
+
1155
+ def handle_msc_pid_for_task(parser, events):
1156
+ args = events[0].values
1157
+ return PidForTask(events, args[0] & 0xffffffff, args[1], parse_enum(KernReturn, events[-1].values[0]))
1158
+
1159
+
1160
+ def handle_task_suspend(parser, events):
1161
+ args = events[0].values
1162
+ return TaskSuspension(events, 'task_suspend', 'pid', args[0], args[1], args[2])
1163
+
1164
+
1165
+ def handle_task_resume(parser, events):
1166
+ return TaskSuspension(events, 'task_resume', 'pid', events[0].values[0])
1167
+
1168
+
1169
+ def handle_thread_suspend(parser, events):
1170
+ args = events[0].values
1171
+ return TaskSuspension(events, 'thread_suspend', 'tid', args[0], args[1], args[2])
1172
+
1173
+
1174
+ def handle_thread_resume(parser, events):
1175
+ return TaskSuspension(events, 'thread_resume', 'tid', events[0].values[0])
1176
+
1177
+
1060
1178
  def handle_msc_mach_port_allocate_trap(parser, events):
1061
1179
  args = events[0].values
1062
1180
  return MachPortAllocate(events, args[0], parse_enum(MachPortRight, args[1]), args[2])
@@ -1306,6 +1424,13 @@ handlers = {
1306
1424
  'MSC_semaphore_signal_trap': handle_msc_semaphore_signal_trap,
1307
1425
  'MSC_semaphore_wait_trap': handle_msc_semaphore_wait_trap,
1308
1426
  'MSC_semaphore_timedwait_trap': handle_msc_semaphore_timedwait_trap,
1427
+ 'MSC_task_for_pid': handle_msc_task_for_pid,
1428
+ 'MSC_task_name_for_pid': handle_msc_task_name_for_pid,
1429
+ 'MSC_pid_for_task': handle_msc_pid_for_task,
1430
+ 'MACH_task_suspend': handle_task_suspend,
1431
+ 'MACH_task_resume': handle_task_resume,
1432
+ 'MACH_thread_suspend': handle_thread_suspend,
1433
+ 'MACH_thread_resume': handle_thread_resume,
1309
1434
  'MSC_mach_port_get_attributes_trap': handle_msc_mach_port_get_attributes_trap,
1310
1435
  'MSC_mach_port_guard_trap': handle_msc_mach_port_guard_trap,
1311
1436
  'MSC_mach_port_unguard_trap': handle_msc_mach_port_unguard_trap,
@@ -1341,3 +1466,6 @@ handlers = {
1341
1466
  'SCHED_CLUTCH_CPU_THREAD_SELECT': handle_sched_clutch_cpu_thread_select,
1342
1467
  'SCHED_CLUTCH_TG_BUCKET_PRI': handle_sched_clutch_tg_bucket_pri,
1343
1468
  }
1469
+ # Every other arm64 synchronous exception class shares the same arguments.
1470
+ handlers.update({name: handle_arm_exception for eventid, name in default_trace_codes().items()
1471
+ if eventid >> 16 == DBG_MACH_EXCP_SYNC_ARM and name.endswith('_Exc_ARM') and name not in handlers})
@@ -189,6 +189,23 @@ def handle_trace_string_threadname_prev(parser, events):
189
189
  return event
190
190
 
191
191
 
192
+ # bsd/sys/kdebug.h
193
+ TRACE_LOST_EVENTS = 0x7020008
194
+
195
+
196
+ @dataclass
197
+ class TraceLostEvents:
198
+ """The kernel's trace buffer filled up and dropped events before this point."""
199
+ ktraces: List
200
+
201
+ def __str__(self):
202
+ return 'TRACE_LOST_EVENTS: the kernel dropped trace events here'
203
+
204
+
205
+ def handle_trace_lost_events(parser, events):
206
+ return TraceLostEvents(events)
207
+
208
+
192
209
  handlers = {
193
210
  'TRACE_DATA_NEWTHREAD': handle_trace_data_newthread,
194
211
  'TRACE_DATA_EXEC': handle_trace_data_exec,
@@ -200,4 +217,5 @@ handlers = {
200
217
  'TRACE_STRING_PROC_EXIT': handle_trace_string_proc_exit,
201
218
  'TRACE_STRING_THREADNAME': handle_trace_string_threadname,
202
219
  'TRACE_STRING_THREADNAME_PREV': handle_trace_string_threadname_prev,
220
+ 'TRACE_LOST_EVENTS': handle_trace_lost_events,
203
221
  }
@@ -29,6 +29,9 @@ class RawTrace:
29
29
  return rep
30
30
 
31
31
 
32
+ NO_RETURN_CALLS = ('BSC_exit', 'BSC_bsdthread_terminate')
33
+
34
+
32
35
  class TracesParser:
33
36
  def __init__(self, trace_codes_map, threads_pids, pids_names):
34
37
  self.trace_codes = trace_codes_map
@@ -46,6 +49,13 @@ class TracesParser:
46
49
  }
47
50
  self.last_data_newthread = None
48
51
  self.last_data_exec = None
52
+ # Calls that never return, parsed from their start event.
53
+ self.no_return_eventids = {eventid for eventid, name in trace_codes_map.items() if name in NO_RETURN_CALLS}
54
+ # Disk I/Os issued and not completed yet, by their buf address.
55
+ self.pending_disk_io = {}
56
+ # Images dyld loaded, by (pid, load address), and the load address of each thread's last `DYLD_uuid_*_a` event.
57
+ self.dyld_images = {}
58
+ self.dyld_pending_load_addr = {}
49
59
  # Event ids to show as `RawTrace` when they have no handler.
50
60
  self.raw_eventids = set()
51
61
  self.handlers = {}
@@ -109,6 +119,9 @@ class TracesParser:
109
119
  return list(self.vnode_generator([e for e in events if self.trace_codes.get(e.eventid) == 'VFS_LOOKUP']))
110
120
 
111
121
  def _feed_start_event(self, event, state):
122
+ if event.eventid in self.no_return_eventids:
123
+ # The thread or process ends inside the call, so its end is never traced.
124
+ return self._feed_single_event(event, state)
112
125
  tid_state = state.get(event.tid)
113
126
  if tid_state is None:
114
127
  # New tid
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pykdebugparser
3
- Version: 1.4.0
3
+ Version: 1.5.0
4
4
  Summary: Python parser for kdebug events
5
5
  Author: Matan Perelman
6
6
  Author-email: matan1008@gmail.com
@@ -0,0 +1,25 @@
1
+ pykdebugparser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ pykdebugparser/__main__.py,sha256=WfgLv5SGr0hQlUeM68ai1g0AqtYDgDFqYlbvvm5rua4,3711
3
+ pykdebugparser/callstacks_parser.py,sha256=i2nfhx0olZZ65UV_ZV3Tw9E_6SWtre8CAbfRBSig4-E,1654
4
+ pykdebugparser/highlight.py,sha256=i-mjfyak0VTo8P_7EeZLIUsUH08Qs-1yAjHBJVWW448,6439
5
+ pykdebugparser/kd_buf_parser.py,sha256=CpifFeBnQ_MZSokjO44lzhXO-KNnZvO-teaPyQqSfio,10991
6
+ pykdebugparser/kevent.py,sha256=DudovIuES4QGxosYeEFJm3AW_FWGbJJBH1kcLnL7UFM,1779
7
+ pykdebugparser/os_log_event.py,sha256=OQUlHpS44oUNwma_bxvnK0SKh_RFeEUdH1RmyrM4nLc,12356
8
+ pykdebugparser/pykdebugparser.py,sha256=RipUcCh6dChgtdh_owvlprjLRMF1dvgic6ZyCqC5I8I,10320
9
+ pykdebugparser/trace.codes,sha256=S6ZN5l9FSGrlSon8nKoVaqRfLdCPl3HL93XSjuoCUnM,103995
10
+ pykdebugparser/trace_codes.py,sha256=4ImGc_jNpY-12cGXdwy6NMCEYcI24oYn1NBOaKNdWNo,1984
11
+ pykdebugparser/traces_parser.py,sha256=LyHVTi4P2OeOLNG2Tahu1x7roQXkC-Jx8LS-E4mBcAQ,5799
12
+ pykdebugparser/trace_handlers/__init__.py,sha256=r1dzmNTxiEtgENq9WFRVQX0g9f9dRxgAgVxjDXSc7s4,679
13
+ pykdebugparser/trace_handlers/bsd.py,sha256=XJjKxDzHSD2VkwEPOqizdTA6ZydFXq7piEVJKCAa0ZY,189267
14
+ pykdebugparser/trace_handlers/dyld.py,sha256=LDgWzOl90qXzcc_q8OvttV2gJ4JNodXFwMemSiV8_ps,10154
15
+ pykdebugparser/trace_handlers/fsystem.py,sha256=AE9L_00-11VyZAYd6dzkmTCP6EVFmXzyae2VikrsiJo,2082
16
+ pykdebugparser/trace_handlers/mach.py,sha256=_2YEgUfKYxaB7NfA2jCVxUbdKDrU21KJKhxtc5yP1M8,39172
17
+ pykdebugparser/trace_handlers/perf.py,sha256=CBffheZFlHa9IOVUZ-UcjNtKmjamv-un44bRJYYo9dw,5070
18
+ pykdebugparser/trace_handlers/trace.py,sha256=s3Kr0wDvWZL2JZVKhxcvA1uvqsdePlEWiXfqSI76znw,5797
19
+ pykdebugparser/trace_handlers/turnstile.py,sha256=8zZfba2Obq7a6qdhLIkItmZxRzzOe1PTTeHnrLuJUx0,8233
20
+ pykdebugparser-1.5.0.dist-info/licenses/LICENSE,sha256=KjZXYP_CGYzDTtVfcxPT6wlGRoQKHPSJu0vh55_2QSo,1071
21
+ pykdebugparser-1.5.0.dist-info/METADATA,sha256=biG_5LGv2RvIABkToh09hXR3eQ8Pm6BUlsI59SZPvdA,18995
22
+ pykdebugparser-1.5.0.dist-info/WHEEL,sha256=4YBfCYNH4wlLpv3pzq1hbEuIlXA4WJabKLFurZ7eTL0,109
23
+ pykdebugparser-1.5.0.dist-info/entry_points.txt,sha256=NrVIOgPR6v2MbpTZ3aKedP1jQ93nUQus6sgP4wAD0_E,63
24
+ pykdebugparser-1.5.0.dist-info/top_level.txt,sha256=FcHHXR9VKmqufFw0a6UP4c8NyS-u4ORY-yXQ6ZzP_28,15
25
+ pykdebugparser-1.5.0.dist-info/RECORD,,
@@ -1,24 +0,0 @@
1
- pykdebugparser/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- pykdebugparser/__main__.py,sha256=WfgLv5SGr0hQlUeM68ai1g0AqtYDgDFqYlbvvm5rua4,3711
3
- pykdebugparser/callstacks_parser.py,sha256=i2nfhx0olZZ65UV_ZV3Tw9E_6SWtre8CAbfRBSig4-E,1654
4
- pykdebugparser/kd_buf_parser.py,sha256=CpifFeBnQ_MZSokjO44lzhXO-KNnZvO-teaPyQqSfio,10991
5
- pykdebugparser/kevent.py,sha256=DudovIuES4QGxosYeEFJm3AW_FWGbJJBH1kcLnL7UFM,1779
6
- pykdebugparser/os_log_event.py,sha256=OQUlHpS44oUNwma_bxvnK0SKh_RFeEUdH1RmyrM4nLc,12356
7
- pykdebugparser/pykdebugparser.py,sha256=87c7HT6NXl820A7bv4g2iMD3kWYKpq6ya49eT8p46nE,10238
8
- pykdebugparser/trace.codes,sha256=S6ZN5l9FSGrlSon8nKoVaqRfLdCPl3HL93XSjuoCUnM,103995
9
- pykdebugparser/trace_codes.py,sha256=4ImGc_jNpY-12cGXdwy6NMCEYcI24oYn1NBOaKNdWNo,1984
10
- pykdebugparser/traces_parser.py,sha256=2nerqDgGLkaHWZaqHDE28gZvlRrbNcYANaBbcYwSlLk,5064
11
- pykdebugparser/trace_handlers/__init__.py,sha256=r1dzmNTxiEtgENq9WFRVQX0g9f9dRxgAgVxjDXSc7s4,679
12
- pykdebugparser/trace_handlers/bsd.py,sha256=2dg4wFdfiZ79jARUcB1VCfCPw2z8ni11eWFsMQtQk-E,185848
13
- pykdebugparser/trace_handlers/dyld.py,sha256=ydyFhEkXKJzgo-1X5bH7UqiiLAenUa-KpBDfgSXMuAM,6839
14
- pykdebugparser/trace_handlers/fsystem.py,sha256=yOILOEuQDdjPDKq6HhzsjI27z36fkT6TqgCBddTvENg,417
15
- pykdebugparser/trace_handlers/mach.py,sha256=JL1v4rCCdGULsnVm3CYOLegL3JqNt3u7X-lJw1xzBRQ,35023
16
- pykdebugparser/trace_handlers/perf.py,sha256=CBffheZFlHa9IOVUZ-UcjNtKmjamv-un44bRJYYo9dw,5070
17
- pykdebugparser/trace_handlers/trace.py,sha256=Lu8s9XT1jb5avtrVXF6SQ6FUj93xYc0TwJd7yoIjV9U,5377
18
- pykdebugparser/trace_handlers/turnstile.py,sha256=8zZfba2Obq7a6qdhLIkItmZxRzzOe1PTTeHnrLuJUx0,8233
19
- pykdebugparser-1.4.0.dist-info/licenses/LICENSE,sha256=KjZXYP_CGYzDTtVfcxPT6wlGRoQKHPSJu0vh55_2QSo,1071
20
- pykdebugparser-1.4.0.dist-info/METADATA,sha256=RcvGghbwZc-Xd7KHUzdlOdrnNH9i58vS0oHcqBWYedo,18995
21
- pykdebugparser-1.4.0.dist-info/WHEEL,sha256=4YBfCYNH4wlLpv3pzq1hbEuIlXA4WJabKLFurZ7eTL0,109
22
- pykdebugparser-1.4.0.dist-info/entry_points.txt,sha256=NrVIOgPR6v2MbpTZ3aKedP1jQ93nUQus6sgP4wAD0_E,63
23
- pykdebugparser-1.4.0.dist-info/top_level.txt,sha256=FcHHXR9VKmqufFw0a6UP4c8NyS-u4ORY-yXQ6ZzP_28,15
24
- pykdebugparser-1.4.0.dist-info/RECORD,,