summaryrefslogtreecommitdiffstats
path: root/bin/toaster-eventreplay
blob: a1072988ac9cb307abd6fc1efdbd28707631a11d (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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/usr/bin/env python3
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
# Copyright (C) 2014        Alex Damian
#
# This file re-uses code spread throughout other Bitbake source files.
# As such, all other copyrights belong to their own right holders.
#
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


# This command takes a filename as a single parameter. The filename is read
# as a build eventlog, and the ToasterUI is used to process events in the file
# and log data in the database

from __future__ import print_function
import os
import sys, logging
import codecs

# mangle syspath to allow easy import of modules
sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
                                'lib'))


import bb.cooker
from bb.ui import toasterui
import sys
import logging

import json, pickle


class FileReadEventsServerConnection():
    """  Emulates a connection to a bitbake server that feeds
        events coming actually read from a saved log file.
    """

    class MockConnection():
        """ fill-in for the proxy to the server. we just return generic data
        """
        def __init__(self, sc):
            self._sc = sc
            self.eventmask = []

        def runCommand(self, commandArray):
            """ emulates running a command on the server; only read-only commands are accepted """
            command_name = commandArray[0]

            if command_name == "getVariable":
                if commandArray[1] in self._sc._variables:
                    return (self._sc._variables[commandArray[1]]['v'], None)
                return (None, "Missing variable")

            elif command_name == "getAllKeysWithFlags":
                dump = {}
                flaglist = commandArray[1]
                for k in self._sc._variables.keys():
                    try:
                        if not k.startswith("__"):
                            v = self._sc._variables[k]['v']
                            dump[k] = {
                                'v' : v ,
                                'history' : self._sc._variables[k]['history'],
                            }
                            for d in flaglist:
                                dump[k][d] = self._sc._variables[k][d]
                    except Exception as e:
                        print(e)
                return (dump, None)

            elif command_name == 'setEventMask':
                self.eventmask = commandArray[-1]
                return True, None

            else:
                raise Exception("Command %s not implemented" % commandArray[0])

        def terminateServer(self):
            """ do not do anything """
            pass

        def getEventHandle(self):
            pass


    class EventReader():
        def __init__(self, sc):
            self._sc = sc
            self.firstraise = 0

        def _create_event(self, line):
            def _import_class(name):
                assert len(name) > 0
                assert "." in name, name

                components = name.strip().split(".")
                modulename = ".".join(components[:-1])
                moduleklass = components[-1]

                module = __import__(modulename, fromlist=[str(moduleklass)])
                return getattr(module, moduleklass)

            # we build a toaster event out of current event log line
            try:
                event_data = json.loads(line.strip())
                event_class = _import_class(event_data['class'])
                event_str = event_data['vars'].encode('utf-8')
                event_object = pickle.loads(codecs.decode(event_str, 'base64'))
            except ValueError as e:
                print("Failed loading ", line)
                raise e

            if not isinstance(event_object, event_class):
                raise Exception("Error loading objects %s class %s ", event_object, event_class)

            return event_object

        def waitEvent(self, timeout):

            nextline = self._sc._eventfile.readline()
            if len(nextline) == 0:
                # the build data ended, while toasterui still waits for events.
                # this happens when the server was abruptly stopped, so we simulate this
                self.firstraise += 1
                if self.firstraise == 1:
                    raise KeyboardInterrupt()
                else:
                    return None
            else:
                self._sc.lineno += 1
            return self._create_event(nextline)


    def _readVariables(self, variableline):
        self._variables = json.loads(variableline.strip())['allvariables']


    def __init__(self, file_name):
        self.connection = FileReadEventsServerConnection.MockConnection(self)
        self._eventfile = open(file_name, "r")

        # we expect to have the variable dump at the start of the file
        self.lineno = 1
        self._readVariables(self._eventfile.readline())

        self.events = FileReadEventsServerConnection.EventReader(self)





class MockConfigParameters():
    """ stand-in for cookerdata.ConfigParameters; as we don't really config a cooker, this
        serves just to supply needed interfaces for the toaster ui to work """
    def __init__(self):
        self.observe_only = True            # we can only read files


# run toaster ui on our mock bitbake class
if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: %s event.log " % sys.argv[0])
        sys.exit(1)

    file_name = sys.argv[-1]
    mock_connection = FileReadEventsServerConnection(file_name)
    configParams = MockConfigParameters()

    # run the main program and set exit code to the returned value
    sys.exit(toasterui.main(mock_connection.connection, mock_connection.events, configParams))