aboutsummaryrefslogtreecommitdiff
path: root/lnt/testing/util/valgrind.py
blob: 51414be9593f226c2dec51eab816427bea0d6446 (plain)
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