Skip to content

Commit 39f2263

Browse files
committed
parsing a jitlog can abort the parsing and return the half read forest + tests. this is useful when the jitlog has not been generated fully (vm termination)
1 parent 183dc31 commit 39f2263

File tree

4 files changed

+66
-10
lines changed

4 files changed

+66
-10
lines changed

jitlog/parser.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
JITLOG_MIN_VERSION = 1
88
JITLOG_VERSION = 1
99

10+
class ParseException(Exception):
11+
pass
12+
1013
def read_jitlog_data(filename):
1114
with open(str(filename), 'rb') as fileobj:
1215
return fileobj.read()
@@ -20,29 +23,49 @@ def parse_jitlog(filename, data=None):
2023
return f
2124

2225
def _parse_jitlog(fileobj):
26+
""" JitLog is parsed. if an exception is raised after
27+
the header has been read, it is saved in the field 'exc' on
28+
the forest returned.
29+
30+
This allows to parse an incomplete log file an still display
31+
everything that was readable!
32+
If a ParseException is directly raised, this is an error that cannot
33+
be recovered from!
34+
"""
2335
is_jit_log = fileobj.read(1) == const.MARK_JITLOG_HEADER
2436
version = ord(fileobj.read(1)) | (ord(fileobj.read(1)) << 8)
2537
is_32bit = ord(fileobj.read(1))
2638
machine = read_string(fileobj, True)
2739
forest = TraceForest(version, is_32bit, machine)
28-
assert is_jit_log, "Missing header. Data might not be a jitlog!"
29-
assert version >= JITLOG_MIN_VERSION, \
30-
"Version does not match. Log is version %d%d is not satisfied" % \
31-
(version, JITLOG_VERSION)
40+
if not is_jit_log:
41+
raise ParseException("Missing header. Provided input might not be a jitlog!")
42+
if version < JITLOG_MIN_VERSION:
43+
raise ParseException("Version %d is not supported" % version)
3244
while True:
3345
marker = fileobj.read(1)
3446
if len(marker) == 0:
3547
break # end of file!
36-
assert forest.is_jitlog_marker(marker), \
37-
"marker unkown: 0x%x at pos 0x%x" % (ord(marker), fileobj.tell())
48+
if not forest.is_jitlog_marker(marker):
49+
msg = "marker unknown: 0x%x at pos 0x%x" % \
50+
(ord(marker), fileobj.tell())
51+
forest.exc = ParseException(msg)
52+
break
3853
trace = forest.last_trace
3954
try:
4055
read = marks.get_reader(version, marker)
4156
read(forest, trace, fileobj)
4257
forest.time_tick()
43-
except KeyError:
44-
print("failed at", hex(fileobj.tell()), "with marker", marker)
45-
raise
58+
except KeyError as e:
59+
forest.exc = e
60+
break
61+
except ParseException as e:
62+
forest.exc = e
63+
break
64+
except Exception as e:
65+
msg = "failed at %s with marker %s with exc %s" %\
66+
(hex(fileobj.tell()), marker, str(e))
67+
forest.exc = ParseException(msg)
68+
break
4669

4770
return forest
4871

jitlog/test/test_jitlog.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import struct, py, sys
2+
import pytest
23
from vmprof import reader
34
from jitlog import constants as const
45
from jitlog import marks
6+
from jitlog.parser import ParseException
57
from jitlog.parser import _parse_jitlog
68
from jitlog.objects import (FlatOp, TraceForest, Trace,
79
MergePoint, PointInTrace, iter_ranges)
@@ -278,3 +280,31 @@ def test_v2_start_trace():
278280
fw = FileObjWrapper(fobj)
279281
forest = construct_forest(fw, version=2)
280282
assert forest.get_trace(0x15).jd_name == 'jd_is_a_hippy'
283+
284+
def test_exception_recover():
285+
# weird file provided, fails without returning anything
286+
fobj = FileObj([0x0])
287+
with pytest.raises(ParseException):
288+
_parse_jitlog(fobj)
289+
290+
# incomplete log, bails and adds exception
291+
fobj = FileObj([const.MARK_JITLOG_HEADER,
292+
b'\x01\x00\x00', encode_str('x86_64'),
293+
b'\x00'
294+
])
295+
f = _parse_jitlog(fobj)
296+
assert hasattr(f, 'exc')
297+
assert "marker unknown" in f.exc.args[0]
298+
299+
# some valid data, but most of it missing
300+
fobj = FileObj([const.MARK_JITLOG_HEADER,
301+
b'\x01\x00\x00', encode_str('x86_64'),
302+
const.MARK_START_TRACE, encode_le_u64(0xffaa), encode_str('loop'), encode_le_u64(0),
303+
const.MARK_TRACE, encode_le_u64(0xffaa),
304+
const.MARK_START_TRACE # uff, trace ends here, data missing
305+
])
306+
f = _parse_jitlog(fobj)
307+
assert len(f.traces) == 1
308+
assert hasattr(f, 'exc')
309+
310+

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
name='vmprof',
3434
author='vmprof team',
3535
author_email='[email protected]',
36-
version="0.3.5",
36+
version="0.3.6.dev0",
3737
packages=find_packages(),
3838
description="Python's vmprof client",
3939
long_description='See https://vmprof.readthedocs.org/',

vmprof/test/test_reader.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ def read(self, count):
2424
def write(self, s):
2525
self.s += s
2626

27+
def tell(self):
28+
return 0
29+
2730
def test_fileobj():
2831
f = FileObj()
2932
f.write(b'foo')

0 commit comments

Comments
 (0)