1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
"""
Utilities for working with Valgrind.
"""
from lnt.util import logger
# See:
# http://valgrind.org/docs/manual/cl-format.html#cl-format.overview
# for reference on the calltree data format specification.
class CalltreeParseError(Exception):
pass
class CalltreeData(object):
@staticmethod
def frompath(path):
with open(path) as file:
return CalltreeData.fromfile(file, path)
@staticmethod
def fromfile(file, path):
# I love valgrind, but this is really a horribly lame data format. Oh
# well.
it = iter(file)
# Read the header.
description_lines = []
command = None
events = None
positions = initial_positions = ['line']
for ln in it:
# If there is no colon in the line, we have reached the end of the
# header.
if ':' not in ln:
break
key, value = ln.split(':', 1)
if key == 'desc':
description_lines.append(value.strip())
elif key == 'cmd':
if command is not None:
logger.warning("unexpected multiple 'cmd' keys in %r" %
(path,))
command = value.strip()
elif key == 'events':
if events is not None:
logger.warning("unexpected multiple 'events' keys in %r" %
(path,))
events = value.split()
elif key == 'positions':
if positions is not initial_positions:
logger.warning(
"unexpected multiple 'positions' keys in %r" %
(path,))
positions = value.split()
else:
logger.warning("found unknown key %r in %r" % (key, path))
# Validate that required fields were present.
if events is None:
raise CalltreeParseError("missing required 'events' key in header")
# Construct an instance.
data = CalltreeData(events, "\n".join(description_lines), command)
# Read the file data.
num_samples = len(positions) + len(events)
current_file = None
current_function = None
summary_samples = None
for ln in it:
# Check if this is the closing summary line.
if ln.startswith('summary'):
key, value = ln.split(':', 1)
summary_samples = list(map(int, value.split()))
break
# Check if this is an update to the current file or function.
if ln.startswith('fl='):
current_file = ln[3:-1]
elif ln.startswith('fn='):
current_function = ln[3:-1]
else:
# Otherwise, this is a data record.
samples = list(map(int, ln.split()))
if len(samples) != num_samples:
raise CalltreeParseError(
"invalid record line, unexpected sample count")
data.records.append((current_file,
current_function,
samples))
# Validate that there are no more remaining records.
for ln in it:
raise CalltreeParseError("unexpected line in footer: %r" % (ln,))
# Validate that the summary line was present.
if summary_samples is None:
raise CalltreeParseError(
"missing required 'summary' key in footer")
data.summary = summary_samples
return data
def __init__(self, events, description=None, command=None):
self.events = events
self.description = description
self.command = command
self.records = []
self.summary = None
|