[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH v2 09/12] simpletrace: Refactor to separate responsibilities
|
From: |
Mads Ynddal |
|
Subject: |
[PATCH v2 09/12] simpletrace: Refactor to separate responsibilities |
|
Date: |
Tue, 2 May 2023 11:23:36 +0200 |
From: Mads Ynddal <m.ynddal@samsung.com>
NOTE: `process` changes function signature
Moved event_mapping and event_id_to_name down one level in the function
call-stack to keep variable instantiation and usage closer (`process`
and `run` has no use of the variables; `read_trace_records` does).
Instead of passing event_mapping and event_id_to_name to the bottom of
the call-stack, we move their use to `read_trace_records`. This
separates responsibility and ownership of the information.
`read_record` now just reads the arguments from the file-object by
knowning the total number of bytes. Parsing it to specific arguments is
moved up to `read_trace_records`.
Special handling of dropped events removed, as they can be handled
by the general code.
Signed-off-by: Mads Ynddal <m.ynddal@samsung.com>
---
scripts/simpletrace.py | 130 ++++++++++++++++++++---------------------
1 file changed, 63 insertions(+), 67 deletions(-)
diff --git a/scripts/simpletrace.py b/scripts/simpletrace.py
index 3cf34103e4..2fcbcb77d0 100755
--- a/scripts/simpletrace.py
+++ b/scripts/simpletrace.py
@@ -24,6 +24,7 @@
log_header_fmt = '=QQQ' # trace/simple.c::TraceLogHeader
rec_header_fmt = '=QQII' # trace/simple.c::TraceRecord
+rec_header_fmt_len = struct.calcsize(rec_header_fmt)
class SimpleException(Exception):
pass
@@ -36,35 +37,6 @@ def read_header(fobj, hfmt):
raise SimpleException('Error reading header. Wrong filetype provided?')
return struct.unpack(hfmt, hdr)
-def get_record(event_mapping, event_id_to_name, rechdr, fobj):
- """Deserialize a trace record from a file into a tuple
- (name, timestamp, pid, arg1, ..., arg6)."""
- event_id, timestamp_ns, length, pid = rechdr
- if event_id != dropped_event_id:
- name = event_id_to_name[event_id]
- try:
- event = event_mapping[name]
- except KeyError as e:
- raise SimpleException(
- f'{e} event is logged but is not declared in the trace events'
- 'file, try using trace-events-all instead.'
- )
-
- rec = (name, timestamp_ns, pid)
- for type, name in event.args:
- if is_string(type):
- l = fobj.read(4)
- (len,) = struct.unpack('=L', l)
- s = fobj.read(len)
- rec = rec + (s,)
- else:
- (value,) = struct.unpack('=Q', fobj.read(8))
- rec = rec + (value,)
- else:
- (dropped_count,) = struct.unpack('=Q', fobj.read(8))
- rec = ("dropped", timestamp_ns, pid, dropped_count)
- return rec
-
def get_mapping(fobj):
(event_id, ) = struct.unpack('=Q', fobj.read(8))
(len, ) = struct.unpack('=L', fobj.read(4))
@@ -72,10 +44,11 @@ def get_mapping(fobj):
return (event_id, name)
-def read_record(event_mapping, event_id_to_name, fobj):
- """Deserialize a trace record from a file into a tuple (event_num,
timestamp, pid, arg1, ..., arg6)."""
- rechdr = read_header(fobj, rec_header_fmt)
- return get_record(event_mapping, event_id_to_name, rechdr, fobj)
+def read_record(fobj):
+ """Deserialize a trace record from a file into a tuple (event_num,
timestamp, pid, args)."""
+ event_id, timestamp_ns, record_length, record_pid = read_header(fobj,
rec_header_fmt)
+ args_payload = fobj.read(record_length - rec_header_fmt_len)
+ return (event_id, timestamp_ns, record_pid, args_payload)
def read_trace_header(fobj):
"""Read and verify trace file header"""
@@ -90,30 +63,60 @@ def read_trace_header(fobj):
if log_version != 4:
raise ValueError(f'Log format {log_version} not supported with this
QEMU release!')
-def read_trace_records(event_mapping, event_id_to_name, fobj):
- """Deserialize trace records from a file, yielding record tuples
(event_num, timestamp, pid, arg1, ..., arg6).
-
- Note that `event_id_to_name` is modified if the file contains mapping
records.
+def read_trace_records(events, fobj, read_header):
+ """Deserialize trace records from a file, yielding record tuples (event,
event_num, timestamp, pid, arg1, ..., arg6).
Args:
event_mapping (str -> Event): events dict, indexed by name
- event_id_to_name (int -> str): event names dict, indexed by event ID
fobj (file): input file
+ read_header (bool): whether headers were read from fobj
"""
- while True:
- t = fobj.read(8)
- if len(t) == 0:
- break
+ frameinfo = inspect.getframeinfo(inspect.currentframe())
+ dropped_event = Event.build("Dropped_Event(uint64_t num_events_dropped)",
+ frameinfo.lineno + 1, frameinfo.filename)
+
+ event_mapping = {e.name: e for e in events}
+ event_mapping["dropped"] = dropped_event
+ event_id_to_name = {dropped_event_id: "dropped"}
+ # If there is no header assume event ID mapping matches events list
+ if not read_header:
+ for event_id, event in enumerate(events):
+ event_id_to_name[event_id] = event.name
+
+ while t := fobj.read(8):
(rectype, ) = struct.unpack('=Q', t)
if rectype == record_type_mapping:
- event_id, name = get_mapping(fobj)
- event_id_to_name[event_id] = name
+ event_id, event_name = get_mapping(fobj)
+ event_id_to_name[event_id] = event_name
else:
- rec = read_record(event_mapping, event_id_to_name, fobj)
-
- yield rec
+ event_id, timestamp_ns, pid, args_payload = read_record(fobj)
+ event_name = event_id_to_name[event_id]
+
+ try:
+ event = event_mapping[event_name]
+ except KeyError as e:
+ raise SimpleException(
+ f'{e} event is logged but is not declared in the trace
events'
+ 'file, try using trace-events-all instead.'
+ )
+
+ offset = 0
+ args = []
+ for type, _ in event.args:
+ if is_string(type):
+ (len,) = struct.unpack_from('=L', args_payload,
offset=offset)
+ offset += 4
+ s = args_payload[offset:offset+len]
+ offset += len
+ args.append(s)
+ else:
+ (value,) = struct.unpack_from('=Q', args_payload,
offset=offset)
+ offset += 8
+ args.append(value)
+
+ yield (event_mapping[event_name], event_name, timestamp_ns, pid) +
tuple(args)
class Analyzer:
"""A trace file analyzer which processes trace records.
@@ -173,28 +176,22 @@ def __call__(self):
With this function, it will work in both cases."""
return self
-def process(events, log, analyzer_class, read_header=True):
- """Invoke an analyzer on each event in a log."""
- if read_header:
- read_trace_header(log)
+def process(events_fobj, log_fobj, analyzer_class, read_header=True):
+ """Invoke an analyzer on each event in a log.
- frameinfo = inspect.getframeinfo(inspect.currentframe())
- dropped_event = Event.build("Dropped_Event(uint64_t num_events_dropped)",
- frameinfo.lineno + 1, frameinfo.filename)
- event_mapping = {"dropped": dropped_event}
- event_id_to_name = {dropped_event_id: "dropped"}
-
- for event in events:
- event_mapping[event.name] = event
+ Args:
+ events_fobj (file): file-object to read event data from
+ log_fobj (file): file-object to read log data from
+ analyzer_class (Analyzer): the Analyzer to interpret the event data
+ read_header (bool, optional): Whether to read header data from the log
data. Defaults to True.
+ """
+ if read_header:
+ read_trace_header(log_fobj)
- # If there is no header assume event ID mapping matches events list
- if not read_header:
- for event_id, event in enumerate(events):
- event_id_to_name[event_id] = event.name
+ events = read_events(events_fobj, events_fobj.name)
with analyzer_class() as analyzer:
- for event_id, timestamp_ns, record_pid, *rec_args in
read_trace_records(event_mapping, event_id_to_name, log):
- event = event_mapping[event_id]
+ for event, event_id, timestamp_ns, record_pid, *rec_args in
read_trace_records(events, log_fobj, read_header):
fn = getattr(analyzer, event.name, analyzer.catchall)
fn(*rec_args, event=event, event_id=event_id,
timestamp_ns=timestamp_ns, pid=record_pid)
@@ -213,8 +210,7 @@ def run(analyzer):
raise SimpleException(f'usage: {sys.argv[0]} [--no-header]
<trace-events> <trace-file>\n')
with open(trace_event_path, 'r') as events_fobj, open(trace_file_path,
'rb') as log_fobj:
- events = read_events(events_fobj, trace_event_path)
- process(events, log_fobj, analyzer, read_header=not no_header)
+ process(events_fobj, log_fobj, analyzer, read_header=not no_header)
if __name__ == '__main__':
class Formatter(Analyzer):
--
2.38.1
- [PATCH v2 03/12] simpletrace: changed naming of edict and idtoname to improve readability, (continued)
- [PATCH v2 03/12] simpletrace: changed naming of edict and idtoname to improve readability, Mads Ynddal, 2023/05/02
- [PATCH v2 05/12] simpletrace: Changed Analyzer class to become context-manager, Mads Ynddal, 2023/05/02
- [PATCH v2 06/12] simpletrace: Simplify construction of tracing methods, Mads Ynddal, 2023/05/02
- [PATCH v2 07/12] simpletrace: Improved error handling on struct unpack, Mads Ynddal, 2023/05/02
- [PATCH v2 08/12] simpletrace: define exception and add handling, Mads Ynddal, 2023/05/02
- [PATCH v2 09/12] simpletrace: Refactor to separate responsibilities,
Mads Ynddal <=
- [PATCH v2 10/12] MAINTAINERS: add maintainer of simpletrace.py, Mads Ynddal, 2023/05/02
- [PATCH v2 11/12] scripts/analyse-locks-simpletrace.py: changed iteritems() to items(), Mads Ynddal, 2023/05/02
- [PATCH v2 12/12] scripts/analyse-locks-simpletrace.py: reflect changes to process in simpletrace.py, Mads Ynddal, 2023/05/02
- Re: [PATCH v2 00/12] simpletrace: refactor and general improvements, John Snow, 2023/05/03
- Re: [PATCH v2 00/12] simpletrace: refactor and general improvements, Stefan Hajnoczi, 2023/05/04