scripts/trace: create trace provider abstraction
This abstraction will make it possible to support different sources for the traces. The current JSON implementation is moved to a provdider and a new one using the trace_parse library will be added in the following patches. Signed-off-by: Konrad Sztyber <konrad.sztyber@intel.com> Change-Id: I322e7984ffe19ce8b8e1bb551e8339655383623f Reviewed-on: https://review.spdk.io/gerrit/c/spdk/spdk/+/9437 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Community-CI: Broadcom CI <spdk-ci.pdl@broadcom.com> Reviewed-by: Jim Harris <james.r.harris@intel.com> Reviewed-by: Krzysztof Karas <krzysztof.karas@intel.com> Reviewed-by: Aleksey Marchuk <alexeymar@mellanox.com>
This commit is contained in:
parent
69390766a4
commit
d1732fcedc
@ -165,19 +165,33 @@ class TraceEntry:
|
|||||||
args: Dict[str, TypeVar('ArgumentType', str, int)]
|
args: Dict[str, TypeVar('ArgumentType', str, int)]
|
||||||
|
|
||||||
|
|
||||||
class Trace:
|
class TraceProvider:
|
||||||
"""Stores, parses, and prints out SPDK traces"""
|
"""Defines interface for objects providing traces and tracepoint definitions"""
|
||||||
|
|
||||||
|
def tpoints(self):
|
||||||
|
"""Returns tracepoint definitions as a dict of (tracepoint_name, tracepoint)"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def entries(self):
|
||||||
|
"""Generator returning subsequent trace entries"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
def tsc_rate(self):
|
||||||
|
"""Returns the TSC rate that was in place when traces were collected"""
|
||||||
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
|
class JsonProvider(TraceProvider):
|
||||||
|
"""Trace provider based on JSON-formatted output produced by spdk_trace app"""
|
||||||
def __init__(self, file):
|
def __init__(self, file):
|
||||||
self._parser = ijson.parse(file)
|
self._parser = ijson.parse(file)
|
||||||
self._objects = []
|
self._tpoints = {}
|
||||||
self._argfmt = {TracepointArgument.TYPE_PTR: lambda a: f'0x{a:x}'}
|
|
||||||
self.tpoints = {}
|
|
||||||
self._parse_defs()
|
self._parse_defs()
|
||||||
|
|
||||||
def _parse_tpoints(self, tpoints):
|
def _parse_tpoints(self, tpoints):
|
||||||
for tpoint in tpoints:
|
for tpoint in tpoints:
|
||||||
tpoint_id = tpoint['id']
|
tpoint_id = tpoint['id']
|
||||||
self.tpoints[tpoint_id] = Tracepoint(
|
self._tpoints[tpoint_id] = Tracepoint(
|
||||||
name=tpoint['name'], id=tpoint_id,
|
name=tpoint['name'], id=tpoint_id,
|
||||||
new_object=tpoint['new_object'],
|
new_object=tpoint['new_object'],
|
||||||
args=[TracepointArgument(name=a['name'],
|
args=[TracepointArgument(name=a['name'],
|
||||||
@ -191,7 +205,7 @@ class Trace:
|
|||||||
if prefix == 'entries':
|
if prefix == 'entries':
|
||||||
break
|
break
|
||||||
elif prefix == 'tsc_rate':
|
elif prefix == 'tsc_rate':
|
||||||
self.tsc_rate = value
|
self._tsc_rate = value
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if (prefix, event) == ('tpoints', 'start_array'):
|
if (prefix, event) == ('tpoints', 'start_array'):
|
||||||
@ -203,7 +217,7 @@ class Trace:
|
|||||||
builder = None
|
builder = None
|
||||||
|
|
||||||
def _parse_entry(self, entry):
|
def _parse_entry(self, entry):
|
||||||
tpoint = self.tpoints[entry['tpoint']]
|
tpoint = self._tpoints[entry['tpoint']]
|
||||||
obj = entry.get('object', {})
|
obj = entry.get('object', {})
|
||||||
return TraceEntry(tpoint=tpoint, lcore=entry['lcore'], tsc=entry['tsc'],
|
return TraceEntry(tpoint=tpoint, lcore=entry['lcore'], tsc=entry['tsc'],
|
||||||
size=entry.get('size'), object_id=obj.get('id'),
|
size=entry.get('size'), object_id=obj.get('id'),
|
||||||
@ -211,7 +225,13 @@ class Trace:
|
|||||||
poller=entry.get('poller'),
|
poller=entry.get('poller'),
|
||||||
args={n.name: v for n, v in zip(tpoint.args, entry.get('args', []))})
|
args={n.name: v for n, v in zip(tpoint.args, entry.get('args', []))})
|
||||||
|
|
||||||
def _entries(self):
|
def tsc_rate(self):
|
||||||
|
return self._tsc_rate
|
||||||
|
|
||||||
|
def tpoints(self):
|
||||||
|
return self._tpoints
|
||||||
|
|
||||||
|
def entries(self):
|
||||||
builder = None
|
builder = None
|
||||||
for prefix, event, value in self._parser:
|
for prefix, event, value in self._parser:
|
||||||
if (prefix, event) == ('entries.item', 'start_map'):
|
if (prefix, event) == ('entries.item', 'start_map'):
|
||||||
@ -222,6 +242,15 @@ class Trace:
|
|||||||
yield self._parse_entry(builder.value)
|
yield self._parse_entry(builder.value)
|
||||||
builder = None
|
builder = None
|
||||||
|
|
||||||
|
|
||||||
|
class Trace:
|
||||||
|
"""Stores, parses, and prints out SPDK traces"""
|
||||||
|
def __init__(self, file):
|
||||||
|
self._provider = JsonProvider(file)
|
||||||
|
self._objects = []
|
||||||
|
self._argfmt = {TracepointArgument.TYPE_PTR: lambda a: f'0x{a:x}'}
|
||||||
|
self.tpoints = self._provider.tpoints()
|
||||||
|
|
||||||
def _annotate_args(self, entry):
|
def _annotate_args(self, entry):
|
||||||
annotations = {}
|
annotations = {}
|
||||||
for obj in self._objects:
|
for obj in self._objects:
|
||||||
@ -248,10 +277,10 @@ class Trace:
|
|||||||
|
|
||||||
def print(self):
|
def print(self):
|
||||||
def get_us(tsc, off):
|
def get_us(tsc, off):
|
||||||
return ((tsc - off) * 10 ** 6) / self.tsc_rate
|
return ((tsc - off) * 10 ** 6) / self._provider.tsc_rate()
|
||||||
|
|
||||||
offset = None
|
offset = None
|
||||||
for e in self._entries():
|
for e in self._provider.entries():
|
||||||
offset = e.tsc if offset is None else offset
|
offset = e.tsc if offset is None else offset
|
||||||
timestamp = get_us(e.tsc, offset)
|
timestamp = get_us(e.tsc, offset)
|
||||||
diff = get_us(e.time, 0) if e.time is not None else None
|
diff = get_us(e.time, 0) if e.time is not None else None
|
||||||
|
Loading…
Reference in New Issue
Block a user