aboutsummaryrefslogtreecommitdiffstats
path: root/meta/classes/report-error.bbclass
blob: 497321cda9662fcfc0de34b2eb5050b20fec5ba7 (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
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#
# Collects debug information in order to create error report files.
#
# Copyright (C) 2013 Intel Corporation
# Author: Andreea Brandusa Proca <andreea.b.proca@intel.com>
#
# Licensed under the MIT license, see COPYING.MIT for details

ERR_REPORT_DIR ?= "${LOG_DIR}/error-report"
ERR_REPORT_PORT ?= "80"

ERR_REPORT_UPLOAD_FAILURES[type] = "boolean"
ERR_REPORT_UPLOAD_FAILURES ?= "0"
ERR_REPORT_UPLOAD_ALL[type] = "boolean"
ERR_REPORT_UPLOAD_ALL ?= "0"

def errorreport_getdata(e):
    import codecs
    logpath = e.data.getVar('ERR_REPORT_DIR')
    datafile = os.path.join(logpath, "error-report.txt")
    with codecs.open(datafile, 'r', 'utf-8') as f:
        data = f.read()
    return data

def errorreport_savedata(e, newdata, file):
    import json
    import codecs
    logpath = e.data.getVar('ERR_REPORT_DIR')
    datafile = os.path.join(logpath, file)
    with codecs.open(datafile, 'w', 'utf-8') as f:
        json.dump(newdata, f, indent=4, sort_keys=True)
    return datafile

def get_conf_data(e, filename):
    builddir = e.data.getVar('TOPDIR')
    filepath = os.path.join(builddir, "conf", filename)
    jsonstring = ""
    if os.path.exists(filepath):
        with open(filepath, 'r') as f:
            for line in f.readlines():
                if line.startswith("#") or len(line.strip()) == 0:
                    continue
                else:
                    jsonstring=jsonstring + line
    jsonstring = jsonstring.replace("<", "&lt;").replace(">", "&gt;")
    return jsonstring

def errorreport_get_user_info(e):
    """
    Read user info from variables or from git config
    """
    import subprocess
    username = e.data.getVar('ERR_REPORT_USERNAME', True)
    email = e.data.getVar('ERR_REPORT_EMAIL', True)
    if not username or not email:
        # try to read them from git config
        username = str(subprocess.check_output(['git', 'config', '--get', 'user.name']).decode("utf-8")).strip()
        email = str(subprocess.check_output(['git', 'config', '--get', 'user.email']).decode("utf-8")).strip()
    return (username, email)

def errorreport_get_submit_info(e):
    """
    Read submit info from ~/.oe-send-error or ask interactively and save it there.
    """
    home = os.path.expanduser("~")
    userfile = os.path.join(home, ".oe-send-error")
    if os.path.isfile(userfile):
        with open(userfile) as g:
            username = g.readline()
            email = g.readline()
    else:
        print("Please enter your name and your email (optionally), they'll be saved in the file you send.")
        username = raw_input("Name: ")
        email = raw_input("E-mail (not required): ")
        server = raw_input("Server: ")
        port = raw_input("Port: ")
        if len(username) > 0 and len(username) < 50:
            with open(userfile, "w") as g:
                g.write(username + "\n")
                g.write(email + "\n")
                g.write(server + "\n")
                g.write(port + "\n")
        else:
            print("Invalid inputs, try again.")
            return errorreport_get_submit_info()
        return (username, email, server, port)

def errorreport_senddata(e, json_file):
    """
    From scripts/send-error-report to automate report submissions.
    """

    import os, json

    if os.path.isfile(json_file):
        server = e.data.getVar('ERR_REPORT_SERVER', True)
        port = e.data.getVar('ERR_REPORT_PORT', True)
        bb.note("Uploading the report %s to %s:%s" % (json_file, server, port))

        with open(json_file) as f:
            data = f.read()

        try:
            jsondata = json.loads(data)
            if not jsondata['username'] or not server:
                (username, email, server, port) = errorreport_get_submit_info(e)
                jsondata['username'] = username.strip()
                jsondata['email'] = email.strip()
            jsondata['link_back'] = ""
            #json.dump(jsondata, json_file, indent=4, sort_keys=True)
            # data = json.dumps(jsondata, indent=4, sort_keys=True)
        except Exception as e:
            bb.error("Invalid json data: %s" % e)
            return

        try:
            url = "http://%s/ClientPost/" % (server)
            import urllib.request, urllib.error
            version = "0.3"
            headers={'Content-type': 'application/json', 'User-Agent': "send-error-report/"+version}
            #headers={'Content-type': 'application/json', 'User-Agent': "report-error.bbclass/"+version}
            with open(json_file, 'r') as json_fp:
                data = json_fp.read().encode('utf-8')
            req = urllib.request.Request(url, data=data, headers=headers)
            try:
                response = urllib.request.urlopen(req)
                if response.getcode() == 200:
                    bb.note("Report submitted: %s %s" % (response.getcode(), response.read().decode('utf-8')))
                else:
                    bb.warn("There was a problem submiting your data: %s" % response.read())
            except urllib.error.HTTPError as e:
                bb.warn("There was a problem submiting your data: %s" % e)
        except Exception as e:
            bb.warn("Server connection failed %s" % e)

    else:
        bb.warn("No data file found.")

python errorreport_handler () {
        import json
        import codecs

        def nativelsb():
            nativelsbstr = e.data.getVar("NATIVELSBSTRING")
            # provide a bit more host info in case of uninative build
            if e.data.getVar('UNINATIVE_URL') != 'unset':
                return '/'.join([nativelsbstr, lsb_distro_identifier(e.data)])
            return nativelsbstr

        logpath = e.data.getVar('ERR_REPORT_DIR')
        datafile = os.path.join(logpath, "error-report.txt")

        if isinstance(e, bb.event.BuildStarted):
            bb.utils.mkdirhier(logpath)
            data = {}
            machine = e.data.getVar("MACHINE")
            data['machine'] = machine
            data['build_sys'] = e.data.getVar("BUILD_SYS")
            data['nativelsb'] = nativelsb()
            data['distro'] = e.data.getVar("DISTRO")
            data['target_sys'] = e.data.getVar("TARGET_SYS")
            data['failures'] = []
            data['component'] = " ".join(e.getPkgs())
            data['branch_commit'] = str(oe.buildcfg.detect_branch(e.data)) + ": " + str(oe.buildcfg.detect_revision(e.data))
            data['bitbake_version'] = e.data.getVar("BB_VERSION")
            data['layer_version'] = get_layers_branch_rev(e.data)
            data['local_conf'] = get_conf_data(e, 'local.conf')
            data['auto_conf'] = get_conf_data(e, 'auto.conf')
            lock = bb.utils.lockfile(datafile + '.lock')
            (username, email) = errorreport_get_user_info(e)
            data['username'] = username.strip()
            data['email'] = email.strip()
            errorreport_savedata(e, data, "error-report.txt")
            bb.utils.unlockfile(lock)

        elif isinstance(e, bb.build.TaskFailed):
            task = e.task
            taskdata={}
            log = e.data.getVar('BB_LOGFILE')
            taskdata['package'] = e.data.expand("${PF}")
            taskdata['task'] = task
            if log:
                try:
                    with codecs.open(log, encoding='utf-8') as logFile:
                        logdata = logFile.read()
                    # Replace host-specific paths so the logs are cleaner
                    for d in ("TOPDIR", "TMPDIR"):
                        s = e.data.getVar(d)
                        if s:
                            logdata = logdata.replace(s, d)
                except:
                    logdata = "Unable to read log file"
            else:
                logdata = "No Log"

            # server will refuse failures longer than param specified in project.settings.py
            # MAX_UPLOAD_SIZE = "5242880"
            # use lower value, because 650 chars can be spent in task, package, version
            max_logdata_size = 5242000
            # upload last max_logdata_size characters
            if len(logdata) > max_logdata_size:
                logdata = "..." + logdata[-max_logdata_size:]
            taskdata['log'] = logdata
            lock = bb.utils.lockfile(datafile + '.lock')
            jsondata = json.loads(errorreport_getdata(e))
            jsondata['failures'].append(taskdata)
            errorreport_savedata(e, jsondata, "error-report.txt")
            bb.utils.unlockfile(lock)

        elif isinstance(e, bb.event.BuildCompleted):
            lock = bb.utils.lockfile(datafile + '.lock')
            jsondata = json.loads(errorreport_getdata(e))
            bb.utils.unlockfile(lock)
            upload_failures = oe.data.typed_value('ERR_REPORT_UPLOAD_FAILURES', e.data)
            upload_all = oe.data.typed_value('ERR_REPORT_UPLOAD_ALL', e.data)
            failures = jsondata['failures']
            if(len(failures) > 0 or upload_all):
                filename = "error_report_" + e.data.getVar("BUILDNAME")+".txt"
                datafile = errorreport_savedata(e, jsondata, filename)
                if upload_all or (failures and upload_failures):
                    errorreport_senddata(e, datafile)
                else:
                    bb.note("The errors for this build are stored in %s\nYou can send the errors to a reports server by running:\n  send-error-report %s [-s server]" % (datafile, datafile))
                    bb.note("The contents of these logs will be posted in public if you use the above command with the default server. Please ensure you remove any identifying or proprietary information when prompted before sending.")
}

addhandler errorreport_handler
errorreport_handler[eventmask] = "bb.event.BuildStarted bb.event.BuildCompleted bb.build.TaskFailed"