mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
1390 lines
50 KiB
Python
1390 lines
50 KiB
Python
# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
import copy
|
|
import json
|
|
import re
|
|
import struct
|
|
|
|
import espytrace.apptrace as apptrace
|
|
|
|
SYSVIEW_EVTID_NOP = 0 # Dummy packet.
|
|
SYSVIEW_EVTID_OVERFLOW = 1
|
|
SYSVIEW_EVTID_ISR_ENTER = 2
|
|
SYSVIEW_EVTID_ISR_EXIT = 3
|
|
SYSVIEW_EVTID_TASK_START_EXEC = 4
|
|
SYSVIEW_EVTID_TASK_STOP_EXEC = 5
|
|
SYSVIEW_EVTID_TASK_START_READY = 6
|
|
SYSVIEW_EVTID_TASK_STOP_READY = 7
|
|
SYSVIEW_EVTID_TASK_CREATE = 8
|
|
SYSVIEW_EVTID_TASK_INFO = 9
|
|
SYSVIEW_EVTID_TRACE_START = 10
|
|
SYSVIEW_EVTID_TRACE_STOP = 11
|
|
SYSVIEW_EVTID_SYSTIME_CYCLES = 12
|
|
SYSVIEW_EVTID_SYSTIME_US = 13
|
|
SYSVIEW_EVTID_SYSDESC = 14
|
|
SYSVIEW_EVTID_USER_START = 15
|
|
SYSVIEW_EVTID_USER_STOP = 16
|
|
SYSVIEW_EVTID_IDLE = 17
|
|
SYSVIEW_EVTID_ISR_TO_SCHEDULER = 18
|
|
SYSVIEW_EVTID_TIMER_ENTER = 19
|
|
SYSVIEW_EVTID_TIMER_EXIT = 20
|
|
SYSVIEW_EVTID_STACK_INFO = 21
|
|
SYSVIEW_EVTID_MODULEDESC = 22
|
|
SYSVIEW_EVTID_INIT = 24
|
|
SYSVIEW_EVENT_ID_PREDEF_LEN_MAX = SYSVIEW_EVTID_INIT
|
|
SYSVIEW_EVTID_NAME_RESOURCE = 25
|
|
SYSVIEW_EVTID_PRINT_FORMATTED = 26
|
|
SYSVIEW_EVTID_NUMMODULES = 27
|
|
SYSVIEW_EVENT_ID_PREDEF_MAX = SYSVIEW_EVTID_NUMMODULES
|
|
|
|
SYSVIEW_EVENT_ID_MAX = 200
|
|
|
|
SYSVIEW_MODULE_EVENT_OFFSET = 512
|
|
|
|
SYSVIEW_SYNC_LEN = 10
|
|
|
|
_sysview_events_map = {
|
|
'SYS_NOP': SYSVIEW_EVTID_NOP,
|
|
'SYS_OVERFLOW': SYSVIEW_EVTID_OVERFLOW,
|
|
'SYS_ISR_ENTER': SYSVIEW_EVTID_ISR_ENTER,
|
|
'SYS_ISR_EXIT': SYSVIEW_EVTID_ISR_EXIT,
|
|
'SYS_TASK_START_EXEC': SYSVIEW_EVTID_TASK_START_EXEC,
|
|
'SYS_TASK_STOP_EXEC': SYSVIEW_EVTID_TASK_STOP_EXEC,
|
|
'SYS_TASK_START_READY': SYSVIEW_EVTID_TASK_START_READY,
|
|
'SYS_TASK_STOP_READY': SYSVIEW_EVTID_TASK_STOP_READY,
|
|
'SYS_TASK_CREATE': SYSVIEW_EVTID_TASK_CREATE,
|
|
'SYS_TASK_INFO': SYSVIEW_EVTID_TASK_INFO,
|
|
'SYS_TRACE_START': SYSVIEW_EVTID_TRACE_START,
|
|
'SYS_TRACE_STOP': SYSVIEW_EVTID_TRACE_STOP,
|
|
'SYS_SYSTIME_CYCLES': SYSVIEW_EVTID_SYSTIME_CYCLES,
|
|
'SYS_SYSTIME_US': SYSVIEW_EVTID_SYSTIME_US,
|
|
'SYS_SYSDESC': SYSVIEW_EVTID_SYSDESC,
|
|
'SYS_USER_START': SYSVIEW_EVTID_USER_START,
|
|
'SYS_USER_STOP': SYSVIEW_EVTID_USER_STOP,
|
|
'SYS_IDLE': SYSVIEW_EVTID_IDLE,
|
|
'SYS_ISR_TO_SCHEDULER': SYSVIEW_EVTID_ISR_TO_SCHEDULER,
|
|
'SYS_TIMER_ENTER': SYSVIEW_EVTID_TIMER_ENTER,
|
|
'SYS_TIMER_EXIT': SYSVIEW_EVTID_TIMER_EXIT,
|
|
'SYS_STACK_INFO': SYSVIEW_EVTID_STACK_INFO,
|
|
'SYS_MODULEDESC': SYSVIEW_EVTID_INIT,
|
|
'SYS_INIT': SYSVIEW_EVTID_INIT,
|
|
'SYS_NAME_RESOURCE': SYSVIEW_EVTID_NAME_RESOURCE,
|
|
'SYS_PRINT_FORMATTED': SYSVIEW_EVTID_PRINT_FORMATTED,
|
|
'SYS_NUMMODULES': SYSVIEW_EVTID_NUMMODULES
|
|
}
|
|
|
|
_os_events_map = {}
|
|
|
|
|
|
def parse_trace(reader, parser, os_evt_map_file=''):
|
|
"""
|
|
Parses trace.
|
|
|
|
Parameters
|
|
----------
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
parser : SysViewTraceDataParser
|
|
Top level parser object.
|
|
os_evt_map_file : string
|
|
Path to file containg events format description.
|
|
"""
|
|
global _os_events_map
|
|
# parse OS events formats file
|
|
_os_events_map = _read_events_map(os_evt_map_file)
|
|
parser.esp_ext = ('; ESP_Extension\n' in _read_file_header(reader))
|
|
_read_init_seq(reader)
|
|
while True:
|
|
event = parser.read_event(reader, _os_events_map)
|
|
parser.on_new_event(event)
|
|
|
|
|
|
def _read_events_map(os_evt_map_file):
|
|
"""
|
|
Reads SystemView events format description from file.
|
|
|
|
Parameters
|
|
----------
|
|
os_evt_map_file : string
|
|
Path to file containg events format description.
|
|
|
|
Returns
|
|
-------
|
|
dict
|
|
a dict with event IDs as keys and values as tuples containg event name and a list of parameters.
|
|
"""
|
|
os_evt_map = {}
|
|
with open(os_evt_map_file) as f:
|
|
for line in f:
|
|
comps = line.split()
|
|
if len(comps) < 2:
|
|
continue
|
|
params = []
|
|
if len(comps) > 2:
|
|
for p in comps[2:]:
|
|
sp = p.split('=')
|
|
if sp[1].startswith('%'):
|
|
sp[1] = sp[1][1:]
|
|
if sp[1] == 'u':
|
|
params.append(SysViewEventParamSimple(sp[0], _decode_u32))
|
|
elif sp[1] == 's':
|
|
params.append(SysViewEventParamSimple(sp[0], _decode_str))
|
|
elif sp[1] == 't' or sp[1] == 'T' or sp[1] == 'I' or sp[1] == 'p':
|
|
# TODO: handle shrinked task/queue ID and addresses
|
|
params.append(SysViewEventParamSimple(sp[0], _decode_u32))
|
|
os_evt_map[int(comps[0])] = (comps[1], params)
|
|
return os_evt_map
|
|
|
|
|
|
def _read_file_header(reader):
|
|
"""
|
|
Reads SystemView trace file header.
|
|
|
|
Parameters
|
|
----------
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
|
|
Returns
|
|
-------
|
|
list
|
|
a list of header lines.
|
|
"""
|
|
empty_count = 0
|
|
lines = []
|
|
while empty_count < 2:
|
|
lines.append(reader.readline(linesep='\n'))
|
|
if lines[-1] == ';\n':
|
|
empty_count += 1
|
|
return lines
|
|
|
|
|
|
def _read_init_seq(reader):
|
|
"""
|
|
Reads SystemView trace initial synchronisation sequence of bytes.
|
|
|
|
Parameters
|
|
----------
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
|
|
Raises
|
|
-------
|
|
SysViewTraceParseError
|
|
If sync sequence is broken.
|
|
"""
|
|
SYNC_SEQ_FMT = '<%dB' % SYSVIEW_SYNC_LEN
|
|
sync_bytes = struct.unpack(SYNC_SEQ_FMT, reader.read(struct.calcsize(SYNC_SEQ_FMT)))
|
|
for b in sync_bytes:
|
|
if b != 0:
|
|
raise SysViewTraceParseError('Invalid sync sequense!')
|
|
|
|
|
|
def _decode_u32(reader):
|
|
"""
|
|
Reads and decodes unsigned 32-bit integer.
|
|
|
|
Parameters
|
|
----------
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
|
|
Returns
|
|
-------
|
|
tuple
|
|
a tuple containg number of read bytes and decoded value.
|
|
"""
|
|
sz = 0
|
|
val = 0
|
|
while True:
|
|
b, = struct.unpack('<B', reader.read(1))
|
|
if b & 0x80:
|
|
val |= (b & 0x7F) << (7 * sz)
|
|
else:
|
|
val |= b << (7 * sz)
|
|
break
|
|
sz += 1
|
|
return (sz + 1,val)
|
|
|
|
|
|
def _decode_id(reader):
|
|
"""
|
|
Reads and decodes ID (task ID, queue handle, semaphore handle etc.).
|
|
|
|
Parameters
|
|
----------
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
|
|
Returns
|
|
-------
|
|
tuple
|
|
a tuple containg number of read bytes and decoded value.
|
|
"""
|
|
return _decode_u32(reader)
|
|
|
|
|
|
def _decode_u64(reader):
|
|
"""
|
|
Reads and decodes unsigned 64-bit integer.
|
|
|
|
Parameters
|
|
----------
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
|
|
Returns
|
|
-------
|
|
tuple
|
|
a tuple containg number of read bytes and decoded value.
|
|
"""
|
|
sz,val = _decode_u32(reader)
|
|
sz2,high = _decode_u32(reader)
|
|
sz += sz2
|
|
return sz,(val | (high << 32))
|
|
|
|
|
|
def _decode_str(reader):
|
|
"""
|
|
Reads and decodes string.
|
|
|
|
Parameters
|
|
----------
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
|
|
Returns
|
|
-------
|
|
tuple
|
|
a tuple containg number of read bytes and decoded value.
|
|
"""
|
|
sz = 0
|
|
val = ''
|
|
sz, = struct.unpack('<B', reader.read(1))
|
|
if sz == 0xFF:
|
|
buf = struct.unpack('<2B', reader.read(2))
|
|
sz = (buf[1] << 8) | buf[0]
|
|
val, = struct.unpack('<%ds' % sz, reader.read(sz))
|
|
val = val.decode('utf-8')
|
|
if sz < 0xFF:
|
|
return (sz + 1,val) # one extra byte for length
|
|
return (sz + 3,val) # 3 extra bytes for length
|
|
|
|
|
|
def _decode_plen(reader):
|
|
"""
|
|
Reads and decodes event's payload length.
|
|
|
|
Parameters
|
|
----------
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
|
|
Returns
|
|
-------
|
|
int
|
|
decoded value.
|
|
"""
|
|
plen = 0
|
|
b0, = struct.unpack('<B', reader.read(1))
|
|
if b0 & 0x80:
|
|
b1, = struct.unpack('<B', reader.read(1))
|
|
plen = b1 # higher part
|
|
plen = (plen << 7) | (b0 & ~0x80) # lower 7 bits
|
|
else:
|
|
plen = b0
|
|
return plen
|
|
|
|
|
|
class SysViewTraceParseError(apptrace.ParseError):
|
|
"""
|
|
SystemView parse error exception.
|
|
"""
|
|
pass
|
|
|
|
|
|
class SysViewEvent(apptrace.TraceEvent):
|
|
"""
|
|
Generic SystemView event class. This is a base class for all events.
|
|
"""
|
|
def __init__(self, evt_id, core_id, reader, events_fmt_map=None):
|
|
"""
|
|
Constructor. Reads and optionally decodes event.
|
|
|
|
Parameters
|
|
----------
|
|
evt_id : int
|
|
Event ID.
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
core_id : int
|
|
Core ID event has been generated on.
|
|
events_fmt_map : dict
|
|
see return value of _read_events_map()
|
|
"""
|
|
apptrace.TraceEvent.__init__(self, 'SysViewEvent', core_id, evt_id)
|
|
self.plen = 0
|
|
if self.id >= SYSVIEW_EVENT_ID_PREDEF_LEN_MAX:
|
|
self.plen = _decode_plen(reader)
|
|
if events_fmt_map:
|
|
self._read_payload(reader, events_fmt_map)
|
|
else:
|
|
reader.forward(self.plen)
|
|
_,self.ts = _decode_u32(reader)
|
|
|
|
def _read_payload(self, reader, events_fmt_map):
|
|
"""
|
|
Reads event's payload and populates its parameters dictionary.
|
|
|
|
Parameters
|
|
----------
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
events_fmt_map : dict
|
|
see return value of _read_events_map()
|
|
|
|
Raises
|
|
-------
|
|
SysViewTraceParseError
|
|
if event has unknown or invalid format.
|
|
"""
|
|
if self.id not in events_fmt_map:
|
|
raise SysViewTraceParseError('Unknown event ID %d!' % self.id)
|
|
self.name = events_fmt_map[self.id][0]
|
|
evt_params_templates = events_fmt_map[self.id][1]
|
|
params_len = 0
|
|
for i in range(len(evt_params_templates)):
|
|
event_param = copy.deepcopy(evt_params_templates[i])
|
|
try:
|
|
cur_pos = reader.get_pos()
|
|
sz,param_val = event_param.decode(reader, self.plen - params_len)
|
|
except Exception as e:
|
|
raise SysViewTraceParseError('Failed to decode event {}({:d}) {:d} param @ 0x{:x}! {}'.format(self.name, self.id, self.plen, cur_pos, e))
|
|
event_param.idx = i
|
|
event_param.value = param_val
|
|
self.params[event_param.name] = event_param
|
|
params_len += sz
|
|
if self.id >= SYSVIEW_EVENT_ID_PREDEF_LEN_MAX and self.plen != params_len:
|
|
raise SysViewTraceParseError('Invalid event {}({:d}) payload len {:d}! Must be {:d}.'.format(self.name, self.id, self.plen, params_len))
|
|
|
|
def __str__(self):
|
|
params = ''
|
|
for param in sorted(self.params.values(), key=lambda x: x.idx):
|
|
params += '{}, '.format(param)
|
|
if len(params):
|
|
params = params[:-2] # remove trailing ', '
|
|
return '{:.9f} - core[{:d}].{}({:d}), plen {:d}: [{}]'.format(self.ts, self.core_id, self.name, self.id, self.plen, params)
|
|
|
|
|
|
class SysViewEventParam:
|
|
"""
|
|
Abstract base SystemView event's parameter class. This is a base class for all event's parameters.
|
|
"""
|
|
def __init__(self, name, decode_func):
|
|
"""
|
|
Constructor.
|
|
|
|
Parameters
|
|
----------
|
|
name : string
|
|
Event parameter name.
|
|
decode_func : callable object
|
|
Parameter decoding function.
|
|
"""
|
|
self.name = name
|
|
self.decode_func = decode_func
|
|
self.value = None
|
|
# positional index, used for sorting parameters before printing to make them looking as they appear in the event
|
|
self.idx = 0
|
|
|
|
def decode(self, reader, max_sz):
|
|
"""
|
|
Reads and decodes events parameter.
|
|
|
|
Parameters
|
|
----------
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
max_sz : int
|
|
Maximum number of bytes to read.
|
|
|
|
Returns
|
|
-------
|
|
tuple
|
|
a tuple containg number of read bytes and decoded value.
|
|
"""
|
|
pass
|
|
|
|
def __str__(self):
|
|
return '{}: {}'.format(self.name, self.value)
|
|
|
|
def to_jsonable(self):
|
|
return {self.name: self.value}
|
|
|
|
|
|
class SysViewEventParamSimple(SysViewEventParam):
|
|
"""
|
|
Simple SystemView event's parameter class.
|
|
"""
|
|
def decode(self, reader, max_sz):
|
|
"""
|
|
see SysViewEventParam.decode()
|
|
"""
|
|
return self.decode_func(reader)
|
|
|
|
|
|
class SysViewEventParamArray(SysViewEventParamSimple):
|
|
"""
|
|
Array SystemView event's parameter class.
|
|
"""
|
|
def __init__(self, name, decode_func, size=-1):
|
|
"""
|
|
Constructor.
|
|
|
|
Parameters
|
|
----------
|
|
name : string
|
|
see SysViewEventParam.__init__()
|
|
decode_func : callable object
|
|
see SysViewEventParam.__init__()
|
|
size : int
|
|
Array's size. If -1 decode() will try to read all bytes from reader.
|
|
"""
|
|
SysViewEventParamSimple.__init__(self, name, decode_func)
|
|
self.arr_size = size
|
|
|
|
def decode(self, reader, max_sz):
|
|
"""
|
|
see SysViewEventParam.decode()
|
|
"""
|
|
tottal_sz = 0
|
|
vals = []
|
|
i = 0
|
|
while tottal_sz < max_sz:
|
|
sz,val = self.decode_func(reader)
|
|
vals.append(val)
|
|
tottal_sz += sz
|
|
i += 1
|
|
if self.arr_size != -1 and i == self.arr_size:
|
|
break
|
|
return tottal_sz,vals
|
|
|
|
|
|
class SysViewPredefinedEvent(SysViewEvent):
|
|
"""
|
|
Pre-defined SystemView events class.
|
|
"""
|
|
_predef_events_fmt = {
|
|
SYSVIEW_EVTID_NOP: ('svNop', []),
|
|
SYSVIEW_EVTID_OVERFLOW: ('svOverflow', [SysViewEventParamSimple('drop_cnt', _decode_u32)]),
|
|
SYSVIEW_EVTID_ISR_ENTER: ('svIsrEnter', [SysViewEventParamSimple('irq_num', _decode_u32)]),
|
|
SYSVIEW_EVTID_ISR_EXIT: ('svIsrExit', []),
|
|
SYSVIEW_EVTID_TASK_START_EXEC: ('svTaskStartExec', [SysViewEventParamSimple('tid', _decode_id)]),
|
|
SYSVIEW_EVTID_TASK_STOP_EXEC: ('svTaskStopExec', []),
|
|
SYSVIEW_EVTID_TASK_START_READY: ('svTaskStartReady', [SysViewEventParamSimple('tid', _decode_id)]),
|
|
SYSVIEW_EVTID_TASK_STOP_READY: ('svTaskStopReady', [SysViewEventParamSimple('tid', _decode_id),
|
|
SysViewEventParamSimple('cause', _decode_u32)]),
|
|
SYSVIEW_EVTID_TASK_CREATE: ('svTaskCreate', [SysViewEventParamSimple('tid', _decode_id)]),
|
|
SYSVIEW_EVTID_TASK_INFO: ('svTaskInfo', [SysViewEventParamSimple('tid', _decode_id),
|
|
SysViewEventParamSimple('prio', _decode_u32),
|
|
SysViewEventParamSimple('name', _decode_str)]),
|
|
SYSVIEW_EVTID_TRACE_START: ('svTraceStart', []),
|
|
SYSVIEW_EVTID_TRACE_STOP: ('svTraceStop', []),
|
|
SYSVIEW_EVTID_SYSTIME_CYCLES: ('svSysTimeCycles', [SysViewEventParamSimple('cycles', _decode_u32)]),
|
|
SYSVIEW_EVTID_SYSTIME_US: ('svSysTimeUs', [SysViewEventParamSimple('time', _decode_u64)]),
|
|
SYSVIEW_EVTID_SYSDESC: ('svSysDesc', [SysViewEventParamSimple('desc', _decode_str)]),
|
|
SYSVIEW_EVTID_USER_START: ('svUserStart', [SysViewEventParamSimple('user_id', _decode_u32)]),
|
|
SYSVIEW_EVTID_USER_STOP: ('svUserStart', [SysViewEventParamSimple('user_id', _decode_u32)]),
|
|
SYSVIEW_EVTID_IDLE: ('svIdle', []),
|
|
SYSVIEW_EVTID_ISR_TO_SCHEDULER: ('svExitIsrToScheduler', []),
|
|
SYSVIEW_EVTID_TIMER_ENTER: ('svTimerEnter', [SysViewEventParamSimple('tim_id', _decode_u32)]),
|
|
SYSVIEW_EVTID_TIMER_EXIT: ('svTimerExit', []),
|
|
SYSVIEW_EVTID_STACK_INFO: ('svStackInfo', [SysViewEventParamSimple('tid', _decode_id),
|
|
SysViewEventParamSimple('base', _decode_u32),
|
|
SysViewEventParamSimple('sz', _decode_u32),
|
|
SysViewEventParamSimple('unused', _decode_u32)]),
|
|
SYSVIEW_EVTID_MODULEDESC: ('svModuleDesc', [SysViewEventParamSimple('mod_id', _decode_u32),
|
|
SysViewEventParamSimple('evt_off', _decode_u32),
|
|
SysViewEventParamSimple('desc', _decode_str)]),
|
|
SYSVIEW_EVTID_INIT: ('svInit', [SysViewEventParamSimple('sys_freq', _decode_u32),
|
|
SysViewEventParamSimple('cpu_freq', _decode_u32),
|
|
SysViewEventParamSimple('ram_base', _decode_u32),
|
|
SysViewEventParamSimple('id_shift', _decode_u32)]),
|
|
SYSVIEW_EVTID_NAME_RESOURCE: ('svNameResource', [SysViewEventParamSimple('res_id', _decode_u32),
|
|
SysViewEventParamSimple('name', _decode_str)]),
|
|
SYSVIEW_EVTID_PRINT_FORMATTED: ('svPrint', [SysViewEventParamSimple('msg', _decode_str),
|
|
SysViewEventParamSimple('lvl', _decode_u32),
|
|
SysViewEventParamSimple('unused', _decode_u32)]),
|
|
SYSVIEW_EVTID_NUMMODULES: ('svNumModules', [SysViewEventParamSimple('mod_cnt', _decode_u32)]),
|
|
}
|
|
|
|
def __init__(self, evt_id, core_id, reader):
|
|
"""
|
|
see SysViewEvent.__init__()
|
|
"""
|
|
SysViewEvent.__init__(self, evt_id, core_id, reader, self._predef_events_fmt)
|
|
# self.name = 'SysViewPredefinedEvent'
|
|
|
|
|
|
class SysViewOSEvent(SysViewEvent):
|
|
"""
|
|
OS related SystemView events class.
|
|
"""
|
|
def __init__(self, evt_id, core_id, reader, events_fmt_map):
|
|
"""
|
|
see SysViewEvent.__init__()
|
|
"""
|
|
SysViewEvent.__init__(self, evt_id, core_id, reader, events_fmt_map)
|
|
# self.name = 'SysViewOSEvent'
|
|
|
|
|
|
class SysViewHeapEvent(SysViewEvent):
|
|
"""
|
|
Heap related SystemView events class.
|
|
|
|
Attributes
|
|
----------
|
|
events_fmt : dict
|
|
see return value of _read_events_map()
|
|
"""
|
|
events_fmt = {
|
|
0: ('esp_sysview_heap_trace_alloc', [SysViewEventParamSimple('addr', _decode_u32),
|
|
SysViewEventParamSimple('size', _decode_u32),
|
|
SysViewEventParamArray('callers', _decode_u32)]),
|
|
1: ('esp_sysview_heap_trace_free', [SysViewEventParamSimple('addr', _decode_u32),
|
|
SysViewEventParamArray('callers', _decode_u32)]),
|
|
}
|
|
|
|
def __init__(self, evt_id, core_id, events_off, reader):
|
|
"""
|
|
Constructor. Reads and optionally decodes event.
|
|
|
|
Parameters
|
|
----------
|
|
evt_id : int
|
|
see SysViewEvent.__init__()
|
|
events_off : int
|
|
Offset for heap events IDs. Greater or equal to SYSVIEW_MODULE_EVENT_OFFSET.
|
|
reader : apptrace.Reader
|
|
see SysViewEvent.__init__()
|
|
core_id : int
|
|
see SysViewEvent.__init__()
|
|
"""
|
|
cur_events_map = {}
|
|
for id in self.events_fmt:
|
|
cur_events_map[events_off + id] = self.events_fmt[id]
|
|
SysViewEvent.__init__(self, evt_id, core_id, reader, cur_events_map)
|
|
# self.name = 'SysViewHeapEvent'
|
|
|
|
|
|
class SysViewTraceDataParser(apptrace.TraceDataProcessor):
|
|
"""
|
|
Base SystemView trace data parser class.
|
|
|
|
Attributes
|
|
----------
|
|
STREAMID_SYS : int
|
|
system events stream ID. Reserved for internal uses.
|
|
STREAMID_LOG : int
|
|
log events stream ID.
|
|
STREAMID_HEAP : int
|
|
heap events stream ID.
|
|
"""
|
|
STREAMID_SYS = -1
|
|
STREAMID_LOG = 0
|
|
STREAMID_HEAP = 1
|
|
|
|
def __init__(self, print_events=False, core_id=0):
|
|
"""
|
|
Constructor.
|
|
|
|
Parameters
|
|
----------
|
|
print_events : bool
|
|
see apptrace.TraceDataProcessor.__init__()
|
|
core_id : int
|
|
id of the core this parser object relates to.
|
|
"""
|
|
apptrace.TraceDataProcessor.__init__(self, print_events=print_events, keep_all_events=True)
|
|
self.sys_info = None
|
|
self._last_ts = 0
|
|
self.irqs_info = {}
|
|
self.tasks_info = {}
|
|
self.core_id = core_id
|
|
self.esp_ext = False
|
|
|
|
def _parse_irq_desc(self, desc):
|
|
"""
|
|
Parses IRQ description.
|
|
|
|
Parameters
|
|
----------
|
|
desc : string
|
|
IRQ description string.
|
|
|
|
Returns
|
|
-------
|
|
tuple
|
|
a tuple with IRQ number and name or None on error.
|
|
"""
|
|
m = re.match('I#([0-9]+)=(.+)', desc)
|
|
if m:
|
|
return m.group(2),m.group(1)
|
|
return None
|
|
|
|
def _update_ts(self, ts):
|
|
"""
|
|
Calculates real event timestamp.
|
|
|
|
Parameters
|
|
----------
|
|
ts : int
|
|
Event timestamp offset.
|
|
|
|
Returns
|
|
-------
|
|
float
|
|
real event timestamp.
|
|
"""
|
|
self._last_ts += ts
|
|
return float(self._last_ts) / self.sys_info.params['sys_freq'].value
|
|
|
|
def read_extension_event(self, evt_id, core_id, reader):
|
|
"""
|
|
Reads extension event.
|
|
Default implementation which just reads out event.
|
|
|
|
Parameters
|
|
----------
|
|
evt_id : int
|
|
Event ID.
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
|
|
Returns
|
|
-------
|
|
SysViewEvent
|
|
if this is top level parser returns object for generic event,
|
|
otherwise returns None indicating to the calling top level parser
|
|
that extension event are not supported.
|
|
"""
|
|
if self.root_proc == self:
|
|
# by default just read out and skip unknown event
|
|
return SysViewEvent(evt_id, core_id, reader)
|
|
return None # let decide to root parser
|
|
|
|
@staticmethod
|
|
def _decode_core_id(high_b):
|
|
if high_b & (1 << 6):
|
|
core_id = 1
|
|
high_b &= ~(1 << 6)
|
|
else:
|
|
core_id = 0
|
|
return high_b,core_id
|
|
|
|
def read_event(self, reader, os_evt_map):
|
|
"""
|
|
Reads pre-defined or OS-related event.
|
|
|
|
Parameters
|
|
----------
|
|
reader : apptrace.Reader
|
|
Trace reader object.
|
|
os_evt_map : dict
|
|
see return value of _read_events_map()
|
|
|
|
Returns
|
|
-------
|
|
SysViewEvent
|
|
pre-defined, OS-related or extension event object.
|
|
"""
|
|
evt_hdr, = struct.unpack('<B', reader.read(1))
|
|
# read ID and core num
|
|
evt_id = 0
|
|
if evt_hdr & 0x80:
|
|
# evt_id (2 bytes)
|
|
b, = struct.unpack('<B', reader.read(1))
|
|
# higher part
|
|
if self.esp_ext:
|
|
evt_id,core_id = self._decode_core_id(b)
|
|
else:
|
|
evt_id = b
|
|
core_id = self.core_id
|
|
evt_id = (evt_id << 7) | (evt_hdr & ~0x80) # lower 7 bits
|
|
else:
|
|
# evt_id (1 byte)
|
|
if self.esp_ext:
|
|
evt_id,core_id = self._decode_core_id(evt_hdr)
|
|
else:
|
|
evt_id = evt_hdr
|
|
core_id = self.core_id
|
|
if evt_id <= SYSVIEW_EVENT_ID_PREDEF_MAX:
|
|
return SysViewPredefinedEvent(evt_id, core_id, reader)
|
|
elif evt_id < SYSVIEW_MODULE_EVENT_OFFSET:
|
|
return SysViewOSEvent(evt_id, core_id, reader, os_evt_map)
|
|
else:
|
|
return self.read_extension_event(evt_id, core_id, reader)
|
|
|
|
def event_supported(self, event):
|
|
return False
|
|
|
|
def on_new_event(self, event):
|
|
"""
|
|
Does essential processing of event. Must be called for every read event.
|
|
Assigns real timestamp to event. Updates global system info: system description,
|
|
mapping of tasks IDs to names and IRQ names to numbers.
|
|
|
|
Parameters
|
|
----------
|
|
event : SysViewEvent
|
|
Event object.
|
|
"""
|
|
if event.id == SYSVIEW_EVTID_TRACE_START:
|
|
event.ts = 0
|
|
self._last_ts = 0
|
|
elif self.sys_info:
|
|
event.ts = self._update_ts(event.ts)
|
|
|
|
if event.id == SYSVIEW_EVTID_INIT:
|
|
self.sys_info = event
|
|
event.ts = self._update_ts(event.ts)
|
|
elif event.id == SYSVIEW_EVTID_TASK_INFO:
|
|
self.tasks_info[event.params['tid'].value] = event.params['name'].value
|
|
elif event.id == SYSVIEW_EVTID_SYSDESC:
|
|
irq = self._parse_irq_desc(event.params['desc'].value)
|
|
if irq:
|
|
irq_num = int(irq[1])
|
|
self.irqs_info[irq_num] = irq[0]
|
|
# count event and save it to the list
|
|
apptrace.TraceDataProcessor.on_new_event(self, event)
|
|
|
|
|
|
class SysViewTraceDataExtEventParser(SysViewTraceDataParser):
|
|
def __init__(self, events_num, print_events=False, core_id=0):
|
|
"""
|
|
Constructor.
|
|
|
|
Parameters
|
|
----------
|
|
print_events : bool
|
|
see apptrace.TraceDataProcessor.__init__()
|
|
core_id : int
|
|
id of the core this parser object relates to.
|
|
"""
|
|
SysViewTraceDataParser.__init__(self, core_id=core_id, print_events=print_events)
|
|
self.events_off = 0
|
|
self.events_num = events_num
|
|
|
|
def event_supported(self, event):
|
|
return False if (self.events_off < SYSVIEW_MODULE_EVENT_OFFSET or event.id < self.events_off or
|
|
event.id >= (self.events_off + self.events_num)) else True
|
|
|
|
|
|
class SysViewMultiTraceDataParser(SysViewTraceDataParser):
|
|
"""
|
|
SystemView trace data parser supporting multiple event streams.
|
|
"""
|
|
def __init__(self, print_events=False, core_id=0):
|
|
"""
|
|
see SysViewTraceDataParser.__init__()
|
|
"""
|
|
SysViewTraceDataParser.__init__(self, print_events, core_id)
|
|
self.stream_parsers = {}
|
|
|
|
def add_stream_parser(self, stream_id, parser):
|
|
"""
|
|
Assigns parser for events stream.
|
|
|
|
Parameters
|
|
----------
|
|
stream_id : int
|
|
stream ID. See SysViewTraceDataParser.STREAMID_xxx.
|
|
Parsers for SysViewTraceDataParser.STREAMID_SYS are ignored.
|
|
Top level parser is the default for SysViewTraceDataParser.STREAMID_SYS.
|
|
parser : SysViewTraceDataParser
|
|
parser object.
|
|
"""
|
|
if stream_id == SysViewTraceDataParser.STREAMID_SYS:
|
|
return
|
|
parser.root_proc = self
|
|
self.stream_parsers[stream_id] = parser
|
|
|
|
def read_extension_event(self, evt_id, core_id, reader):
|
|
"""
|
|
Reads extension event.
|
|
Iterates over registered stream parsers trying to find one which supports that type of event.
|
|
|
|
Parameters
|
|
----------
|
|
evt_id : int
|
|
see SysViewTraceDataParser.read_extension_event()
|
|
reader : apptrace.Reader
|
|
see SysViewTraceDataParser.read_extension_event()
|
|
|
|
Returns
|
|
-------
|
|
SysViewEvent
|
|
object for extension event, if extension event is not supported return SysViewEvent instance.
|
|
"""
|
|
for stream_id in self.stream_parsers:
|
|
evt = self.stream_parsers[stream_id].read_extension_event(evt_id, core_id, reader)
|
|
if evt:
|
|
return evt
|
|
return SysViewTraceDataParser.read_extension_event(self, evt_id, core_id, reader)
|
|
|
|
def on_new_event(self, event):
|
|
"""
|
|
Iterates over registered stream parsers allowing them to do
|
|
essential processing of event. Must be called for every read event.
|
|
|
|
Parameters
|
|
----------
|
|
event : SysViewEvent
|
|
Event object.
|
|
"""
|
|
SysViewTraceDataParser.on_new_event(self, event)
|
|
for stream_id in self.stream_parsers:
|
|
self.stream_parsers[stream_id].on_new_event(event)
|
|
|
|
|
|
class SysViewEventContext():
|
|
"""
|
|
SystemView event context.
|
|
"""
|
|
def __init__(self, handle, irq, name=''):
|
|
"""
|
|
Constructor.
|
|
|
|
Parameters
|
|
----------
|
|
handle : int
|
|
handle of the context: task ID or IRQ number.
|
|
irq : bool
|
|
flag indicating whether this is IRQ or task context.
|
|
name : string
|
|
name of the context: task or IRQ name. Empty if context is unknown.
|
|
"""
|
|
self.handle = handle
|
|
self.irq = irq
|
|
self.name = name
|
|
|
|
|
|
class SysViewTraceDataProcessor(apptrace.TraceDataProcessor):
|
|
"""
|
|
Base SystemView trace data processor class.
|
|
"""
|
|
def __init__(self, traces, root_proc=None, print_events=False, keep_all_events=False):
|
|
"""
|
|
Constructor.
|
|
|
|
Parameters
|
|
----------
|
|
traces : list
|
|
list of parsers to process data from.
|
|
print_events : bool
|
|
see apptrace.TraceDataProcessor.__init__()
|
|
keep_all_events : bool
|
|
see apptrace.TraceDataProcessor.__init__()
|
|
"""
|
|
apptrace.TraceDataProcessor.__init__(self, print_events, keep_all_events)
|
|
self.event_ids = {}
|
|
self.name = ''
|
|
self.root_proc = root_proc if root_proc else self
|
|
self.traces = {}
|
|
self.ctx_stack = {}
|
|
self.prev_ctx = {}
|
|
self.no_ctx_events = []
|
|
for t in traces:
|
|
self.traces[t.core_id] = t
|
|
# current context item is a tuple of task ID or IRQ num and 'in_irq' flag
|
|
# empty list means IDLE context or self.start_ctx
|
|
self.ctx_stack[t.core_id] = []
|
|
# context is undefined, we do not know have we started the tracing in task/IDLE or IRQ context
|
|
# in general there are three scenarious when we can start tracing: when core is in task, IDLE task or IRQ context
|
|
self.prev_ctx[t.core_id] = None
|
|
|
|
def _get_curr_context(self, core_id):
|
|
"""
|
|
Returns current context.
|
|
|
|
Parameters
|
|
----------
|
|
core_id : int
|
|
core ID for requested context.
|
|
|
|
Returns
|
|
-------
|
|
SysViewEventContext
|
|
context object
|
|
None
|
|
if there current is undefined
|
|
"""
|
|
if len(self.root_proc.ctx_stack[core_id]):
|
|
return self.root_proc.ctx_stack[core_id][-1]
|
|
if self._get_prev_context(core_id):
|
|
return SysViewEventContext(None, False, 'IDLE%d' % core_id)
|
|
return None
|
|
|
|
def _get_prev_context(self, core_id):
|
|
"""
|
|
Returns current context.
|
|
|
|
Parameters
|
|
----------
|
|
core_id : int
|
|
core ID for requested context.
|
|
|
|
Returns
|
|
-------
|
|
SysViewEventContext
|
|
context object
|
|
"""
|
|
return self.root_proc.prev_ctx[core_id]
|
|
|
|
def get_trace_stream(self, core_id, stream_id):
|
|
"""
|
|
Retrieves parser for specified stream and core.
|
|
|
|
Parameters
|
|
----------
|
|
core_id : int
|
|
Parser's core ID.
|
|
stream_id : int
|
|
Parser's stream ID.
|
|
|
|
Returns
|
|
-------
|
|
SysViewTraceDataParser
|
|
parser object for specified stream and core
|
|
"""
|
|
if self.root_proc == self:
|
|
return self.traces[core_id]
|
|
return self.root_proc.get_trace_stream(core_id, stream_id)
|
|
|
|
def event_supported(self, e):
|
|
"""
|
|
Should be overriden in child class.
|
|
"""
|
|
return False
|
|
|
|
def handle_event(self, e):
|
|
"""
|
|
Should be overriden in child class.
|
|
"""
|
|
pass
|
|
|
|
def print_report(self):
|
|
"""
|
|
see apptrace.TraceDataProcessor.print_report()
|
|
"""
|
|
apptrace.TraceDataProcessor.print_report(self)
|
|
|
|
def _process_event(self, event):
|
|
"""
|
|
Processes event.
|
|
Keeps track of execution context on every core.
|
|
|
|
Parameters
|
|
----------
|
|
event : SysViewEvent
|
|
Event object.
|
|
|
|
Raises
|
|
----------
|
|
SysViewTraceParseError
|
|
if there is no parser for event's core or
|
|
if SYSVIEW_EVTID_ISR_ENTER is received for unknown IRQ or
|
|
if SYSVIEW_EVTID_TASK_START_EXEC or SYSVIEW_EVTID_TASK_STOP_READY is received for unknown task.
|
|
"""
|
|
if event.core_id not in self.traces:
|
|
if 0 in self.traces and self.traces[0].esp_ext:
|
|
# for Espressif extension there is one trace for all cores
|
|
trace = self.traces[0]
|
|
if event.core_id not in self.ctx_stack:
|
|
self.ctx_stack[event.core_id] = []
|
|
if event.core_id not in self.prev_ctx:
|
|
self.prev_ctx[event.core_id] = None
|
|
else:
|
|
raise SysViewTraceParseError('Event for unknown core %d' % event.core_id)
|
|
else:
|
|
trace = self.traces[event.core_id]
|
|
if event.id == SYSVIEW_EVTID_ISR_ENTER:
|
|
if event.params['irq_num'].value not in trace.irqs_info:
|
|
raise SysViewTraceParseError('Enter unknown ISR %d' % event.params['irq_num'].value)
|
|
if len(self.ctx_stack[event.core_id]):
|
|
self.prev_ctx[event.core_id] = self.ctx_stack[event.core_id][-1]
|
|
else:
|
|
# the 1st context switching event after trace start is SYSVIEW_EVTID_ISR_ENTER, so we have been in IDLE context
|
|
self.prev_ctx[event.core_id] = SysViewEventContext(None, False, 'IDLE%d' % event.core_id)
|
|
# put new ISR context on top of the stack (the last in the list)
|
|
self.ctx_stack[event.core_id].append(SysViewEventContext(event.params['irq_num'].value, True, trace.irqs_info[event.params['irq_num'].value]))
|
|
elif event.id == SYSVIEW_EVTID_ISR_EXIT or event.id == SYSVIEW_EVTID_ISR_TO_SCHEDULER:
|
|
if len(self.ctx_stack[event.core_id]):
|
|
# return to the previous context (the last in the list)
|
|
self.prev_ctx[event.core_id] = self.ctx_stack[event.core_id].pop()
|
|
else:
|
|
# the 1st context switching event after trace start is SYSVIEW_EVTID_ISR_EXIT, so we have been in ISR context,
|
|
# but we do not know which one because SYSVIEW_EVTID_ISR_EXIT do not include the IRQ number
|
|
self.prev_ctx[event.core_id] = SysViewEventContext(None, True, 'IRQ_oncore%d' % event.core_id)
|
|
elif event.id == SYSVIEW_EVTID_TASK_START_EXEC:
|
|
if event.params['tid'].value not in trace.tasks_info:
|
|
raise SysViewTraceParseError('Start exec unknown task 0x%x' % event.params['tid'].value)
|
|
if len(self.ctx_stack[event.core_id]):
|
|
# return to the previous context (the last in the list)
|
|
self.prev_ctx[event.core_id] = self.ctx_stack[event.core_id][-1]
|
|
else:
|
|
# the 1st context switching event after trace start is SYSVIEW_EVTID_TASK_START_EXEC, so we have been in IDLE context
|
|
self.prev_ctx[event.core_id] = SysViewEventContext(None, False, 'IDLE%d' % event.core_id)
|
|
# only one task at a time in context stack (can be interrupted by a bunch of ISRs)
|
|
self.ctx_stack[event.core_id] = [SysViewEventContext(event.params['tid'].value, False, trace.tasks_info[event.params['tid'].value])]
|
|
elif event.id == SYSVIEW_EVTID_TASK_STOP_EXEC:
|
|
# delete task from context stack
|
|
for ctx in self.ctx_stack[event.core_id]:
|
|
if not ctx.irq:
|
|
if len(self.ctx_stack[event.core_id]) == 1:
|
|
# if this is the only ctx in context stack
|
|
self.prev_ctx[event.core_id] = ctx
|
|
del ctx
|
|
break
|
|
elif event.id == SYSVIEW_EVTID_TASK_STOP_READY:
|
|
if event.params['tid'].value not in trace.tasks_info:
|
|
raise SysViewTraceParseError('Stop ready unknown task 0x%x' % event.params['tid'].value)
|
|
if len(self.ctx_stack[event.core_id]):
|
|
if (not self.ctx_stack[event.core_id][-1].irq and event.params['tid'].value == self.ctx_stack[event.core_id][-1].handle):
|
|
# return to the previous context (the last in the list)
|
|
self.prev_ctx[event.core_id] = self.ctx_stack[event.core_id].pop()
|
|
else:
|
|
# the 1st context switching event after trace start is SYSVIEW_EVTID_TASK_STOP_READY, so we have been in task context
|
|
self.prev_ctx[event.core_id] = SysViewEventContext(event.params['tid'].value, False, trace.tasks_info[event.params['tid'].value])
|
|
|
|
def on_new_event(self, event):
|
|
"""
|
|
Processes heap events.
|
|
"""
|
|
if self.root_proc == self:
|
|
SysViewTraceDataProcessor._process_event(self, event)
|
|
curr_ctx = self._get_curr_context(event.core_id)
|
|
if not curr_ctx:
|
|
# postpone events handling till their context is known
|
|
self.no_ctx_events.append(event)
|
|
return
|
|
event.in_irq = curr_ctx.irq
|
|
event.ctx_name = curr_ctx.name
|
|
# here we know the previous context: we switched from it or implied upon the 1st context switching event
|
|
prev_ctx = self._get_prev_context(event.core_id)
|
|
if len(self.no_ctx_events):
|
|
for cached_evt in self.no_ctx_events:
|
|
cached_evt.ctx_name = prev_ctx.name
|
|
cached_evt.in_irq = prev_ctx.irq
|
|
# count and save the event
|
|
apptrace.TraceDataProcessor.on_new_event(self, cached_evt)
|
|
if self.event_supported(event):
|
|
self.handle_event(event)
|
|
del self.no_ctx_events[:]
|
|
# count and save the event
|
|
apptrace.TraceDataProcessor.on_new_event(self, event)
|
|
if self.event_supported(event):
|
|
self.handle_event(event)
|
|
|
|
def merge_and_process(self):
|
|
"""
|
|
Merges events from all registered parsers, sorts them by timestamp and processes them.
|
|
"""
|
|
all_events = []
|
|
for t in self.traces.values():
|
|
all_events.extend(t.events)
|
|
all_events.sort(key=lambda x: x.ts)
|
|
for event in all_events:
|
|
self.on_new_event(event)
|
|
|
|
|
|
class SysViewMultiStreamTraceDataProcessor(SysViewTraceDataProcessor):
|
|
"""
|
|
SystemView trace data processor supporting multiple event streams.
|
|
"""
|
|
def __init__(self, traces, print_events=False, keep_all_events=False):
|
|
"""
|
|
see SysViewTraceDataProcessor.__init__()
|
|
"""
|
|
SysViewTraceDataProcessor.__init__(self, traces, print_events=print_events, keep_all_events=keep_all_events)
|
|
self.stream_procs = {}
|
|
|
|
def add_stream_processor(self, stream_id, proc):
|
|
"""
|
|
Assigns processor for events stream.
|
|
|
|
Parameters
|
|
----------
|
|
stream_id : int
|
|
stream ID. See SysViewTraceDataParser.STREAMID_xxx.
|
|
Parsers for SysViewTraceDataParser.STREAMID_SYS are ignored.
|
|
Top level parser is the default for SysViewTraceDataParser.STREAMID_SYS.
|
|
proc : SysViewTraceDataProcessor
|
|
processor object.
|
|
"""
|
|
if stream_id == SysViewTraceDataParser.STREAMID_SYS:
|
|
return
|
|
proc.root_proc = self # common processing data is kept by this processor
|
|
self.stream_procs[stream_id] = proc
|
|
|
|
def get_trace_stream(self, core_id, stream_id):
|
|
"""
|
|
Retrieves parser for specified stream and core.
|
|
|
|
Parameters
|
|
----------
|
|
core_id : int
|
|
Parser's core ID.
|
|
stream_id : int
|
|
Parser's stream ID.
|
|
|
|
Returns
|
|
-------
|
|
SysViewTraceDataParser
|
|
parser object for specified stream and core
|
|
"""
|
|
if core_id not in self.traces and 0 in self.traces and self.traces[0].esp_ext:
|
|
# for Espressif extension there is one trace for all cores
|
|
trace = self.traces[0]
|
|
else:
|
|
trace = self.traces[core_id]
|
|
if stream_id == SysViewTraceDataParser.STREAMID_SYS:
|
|
return trace
|
|
if isinstance(trace, SysViewMultiTraceDataParser):
|
|
return trace.stream_parsers[stream_id]
|
|
return trace
|
|
|
|
def print_report(self):
|
|
"""
|
|
Iterates over registered stream processors and prints their reports.
|
|
"""
|
|
SysViewTraceDataProcessor.print_report(self)
|
|
# need to sort stream procs by keys to print reports in the same order regardless of Python version
|
|
stream_ids = list(self.stream_procs.keys())
|
|
stream_ids.sort()
|
|
for stream_id in stream_ids:
|
|
self.stream_procs[stream_id].print_report()
|
|
|
|
def cleanup(self):
|
|
"""
|
|
Iterates over registered stream processors and cleans them up.
|
|
"""
|
|
for stream_id in self.stream_procs:
|
|
self.stream_procs[stream_id].cleanup()
|
|
SysViewTraceDataProcessor.cleanup(self)
|
|
|
|
def on_new_event(self, event):
|
|
"""
|
|
Iterates over registered stream processors allowing them to do
|
|
the processing of event.
|
|
|
|
Parameters
|
|
----------
|
|
event : SysViewEvent
|
|
Event object.
|
|
"""
|
|
SysViewTraceDataProcessor.on_new_event(self, event)
|
|
for stream_id in self.stream_procs:
|
|
self.stream_procs[stream_id].on_new_event(event)
|
|
|
|
|
|
class SysViewTraceDataJsonEncoder(json.JSONEncoder):
|
|
JSON_TRACE_VER = '1.0'
|
|
|
|
def default(self, obj):
|
|
global _sysview_events_map
|
|
global _os_events_map
|
|
if isinstance(obj, SysViewMultiStreamTraceDataProcessor):
|
|
json_event_ids = {'system': _sysview_events_map, 'os': {}}
|
|
for eid in _os_events_map:
|
|
ename = _os_events_map[eid][0]
|
|
json_event_ids['os'][ename] = eid
|
|
for stream in obj.stream_procs.values():
|
|
json_event_ids[stream.name] = stream.event_ids
|
|
json_events = []
|
|
for e in obj.events:
|
|
for stream in obj.stream_procs.values():
|
|
if stream.event_supported(e):
|
|
json_events.append(e)
|
|
break
|
|
# include also OS and pre-defined events
|
|
if isinstance(e, SysViewPredefinedEvent) or isinstance(e, SysViewOSEvent):
|
|
json_events.append(e)
|
|
return {'version': self.JSON_TRACE_VER, 'streams': json_event_ids, 'events': json_events}
|
|
if isinstance(obj, SysViewHeapEvent):
|
|
blk_size = 0
|
|
if 'size' in obj.params:
|
|
blk_size = obj.params['size'].value
|
|
blk_addr = '0x{:x}'.format(obj.params['addr'].value)
|
|
callers = []
|
|
for addr in obj.params['callers'].value:
|
|
callers.append('0x{:x}'.format(addr))
|
|
return {'ctx_name': obj.ctx_name, 'in_irq': obj.in_irq, 'id': obj.id, 'core_id': obj.core_id,
|
|
'ts': obj.ts, 'addr': blk_addr, 'size': blk_size, 'callers': callers}
|
|
if isinstance(obj, SysViewPredefinedEvent) and obj.id == SYSVIEW_EVTID_PRINT_FORMATTED:
|
|
return {'ctx_name': obj.ctx_name, 'in_irq': obj.in_irq, 'id': obj.id, 'core_id': obj.core_id,
|
|
'ts': obj.ts, 'msg': obj.params['msg'].value, 'lvl': obj.params['lvl'].value}
|
|
if isinstance(obj, SysViewEvent):
|
|
jobj = obj.to_jsonable()
|
|
# remove unused fields
|
|
if 'name' in jobj:
|
|
del jobj['name']
|
|
if 'plen' in jobj:
|
|
del jobj['plen']
|
|
return jobj
|
|
# Let the base class default method raise the TypeError
|
|
return json.JSONEncoder.default(self, obj)
|
|
|
|
|
|
class SysViewHeapTraceDataParser(SysViewTraceDataExtEventParser):
|
|
"""
|
|
SystemView trace data parser supporting heap events.
|
|
"""
|
|
def __init__(self, print_events=False, core_id=0):
|
|
"""
|
|
SystemView trace data parser supporting multiple event streams.
|
|
see SysViewTraceDataExtEventParser.__init__()
|
|
"""
|
|
SysViewTraceDataExtEventParser.__init__(self, events_num=len(SysViewHeapEvent.events_fmt.keys()), core_id=core_id, print_events=print_events)
|
|
|
|
def read_extension_event(self, evt_id, core_id, reader):
|
|
"""
|
|
Reads heap event.
|
|
see SysViewTraceDataParser.read_extension_event()
|
|
"""
|
|
if (self.events_off >= SYSVIEW_MODULE_EVENT_OFFSET and evt_id >= self.events_off and
|
|
evt_id < self.events_off + self.events_num):
|
|
return SysViewHeapEvent(evt_id, core_id, self.events_off, reader)
|
|
return SysViewTraceDataParser.read_extension_event(self, evt_id, core_id, reader)
|
|
|
|
def on_new_event(self, event):
|
|
"""
|
|
Keeps track of heap module descriptions.
|
|
"""
|
|
if self.root_proc == self:
|
|
SysViewTraceDataParser.on_new_event(self, event)
|
|
if event.id == SYSVIEW_EVTID_MODULEDESC and event.params['desc'].value == 'ESP32 SystemView Heap Tracing Module':
|
|
self.events_off = event.params['evt_off'].value
|
|
|
|
|
|
class SysViewHeapTraceDataProcessor(SysViewTraceDataProcessor, apptrace.BaseHeapTraceDataProcessorImpl):
|
|
"""
|
|
SystemView trace data processor supporting heap events.
|
|
"""
|
|
def __init__(self, toolchain_pref, elf_path, root_proc=None, traces=[], print_events=False, print_heap_events=False):
|
|
"""
|
|
Constructor.
|
|
see SysViewTraceDataProcessor.__init__()
|
|
see apptrace.BaseHeapTraceDataProcessorImpl.__init__()
|
|
"""
|
|
SysViewTraceDataProcessor.__init__(self, traces, root_proc=root_proc, print_events=print_events)
|
|
apptrace.BaseHeapTraceDataProcessorImpl.__init__(self, print_heap_events)
|
|
self.toolchain = toolchain_pref
|
|
self.elf_path = elf_path
|
|
# self.no_ctx_events = []
|
|
self.name = 'heap'
|
|
stream = self.root_proc.get_trace_stream(0, SysViewTraceDataParser.STREAMID_HEAP)
|
|
self.event_ids = {'alloc': stream.events_off, 'free': stream.events_off + 1}
|
|
|
|
def event_supported(self, event):
|
|
heap_stream = self.root_proc.get_trace_stream(event.core_id, SysViewTraceDataParser.STREAMID_HEAP)
|
|
return heap_stream.event_supported(event)
|
|
|
|
def handle_event(self, event):
|
|
heap_stream = self.root_proc.get_trace_stream(event.core_id, SysViewTraceDataParser.STREAMID_HEAP)
|
|
if (event.id - heap_stream.events_off) == 0:
|
|
heap_event = apptrace.HeapTraceEvent(event, True, toolchain=self.toolchain,
|
|
elf_path=self.elf_path)
|
|
else:
|
|
heap_event = apptrace.HeapTraceEvent(event, False, toolchain=self.toolchain,
|
|
elf_path=self.elf_path)
|
|
apptrace.BaseHeapTraceDataProcessorImpl.on_new_event(self, heap_event)
|
|
|
|
def print_report(self):
|
|
"""
|
|
see apptrace.TraceDataProcessor.print_report()
|
|
"""
|
|
if self.root_proc == self:
|
|
SysViewTraceDataProcessor.print_report(self)
|
|
apptrace.BaseHeapTraceDataProcessorImpl.print_report(self)
|
|
|
|
|
|
class SysViewLogTraceEvent(apptrace.LogTraceEvent):
|
|
"""
|
|
SystemView log event.
|
|
"""
|
|
def __init__(self, ts, msg):
|
|
"""
|
|
Constructor.
|
|
|
|
Parameters
|
|
----------
|
|
msg : string
|
|
Log message string.
|
|
"""
|
|
self.msg = msg
|
|
self.ts = ts
|
|
|
|
def get_message(self, unused):
|
|
"""
|
|
Retrieves log message.
|
|
|
|
Returns
|
|
-------
|
|
string
|
|
formatted log message
|
|
"""
|
|
return '[{:.9f}] LOG: {}'.format(self.ts, self.msg)
|
|
|
|
|
|
class SysViewLogTraceDataParser(SysViewTraceDataParser):
|
|
"""
|
|
SystemView trace data parser supporting log events.
|
|
"""
|
|
def event_supported(self, event):
|
|
return event.id == SYSVIEW_EVTID_PRINT_FORMATTED
|
|
|
|
def on_new_event(self, event):
|
|
"""
|
|
see SysViewTraceDataParser.on_new_event()
|
|
"""
|
|
if self.root_proc == self:
|
|
SysViewTraceDataParser.on_new_event(self, event)
|
|
|
|
|
|
class SysViewLogTraceDataProcessor(SysViewTraceDataProcessor, apptrace.BaseLogTraceDataProcessorImpl):
|
|
"""
|
|
SystemView trace data processor supporting heap events.
|
|
"""
|
|
def __init__(self, traces=[], root_proc=None, print_events=False, print_log_events=False):
|
|
"""
|
|
Constructor.
|
|
see SysViewTraceDataProcessor.__init__()
|
|
see apptrace.BaseLogTraceDataProcessorImpl.__init__()
|
|
"""
|
|
SysViewTraceDataProcessor.__init__(self, traces, root_proc=root_proc, print_events=print_events)
|
|
apptrace.BaseLogTraceDataProcessorImpl.__init__(self, print_log_events)
|
|
self.name = 'log'
|
|
self.event_ids = {'print': SYSVIEW_EVTID_PRINT_FORMATTED}
|
|
|
|
def event_supported(self, event):
|
|
return event.id == SYSVIEW_EVTID_PRINT_FORMATTED
|
|
|
|
def on_new_event(self, event):
|
|
"""
|
|
Processes log events.
|
|
"""
|
|
if self.root_proc == self:
|
|
SysViewTraceDataProcessor.on_new_event(self, event)
|
|
if event.id == SYSVIEW_EVTID_PRINT_FORMATTED:
|
|
log_evt = SysViewLogTraceEvent(event.ts, event.params['msg'].value)
|
|
apptrace.BaseLogTraceDataProcessorImpl.on_new_event(self, log_evt)
|
|
|
|
def print_report(self):
|
|
"""
|
|
see apptrace.TraceDataProcessor.print_report()
|
|
"""
|
|
if self.root_proc == self:
|
|
SysViewTraceDataProcessor.print_report(self)
|
|
apptrace.BaseLogTraceDataProcessorImpl.print_report(self)
|