1# 2# Collects debug information in order to create error report files. 3# 4# Copyright (C) 2013 Intel Corporation 5# Author: Andreea Brandusa Proca <andreea.b.proca@intel.com> 6# 7# SPDX-License-Identifier: MIT 8# 9 10ERR_REPORT_DIR ?= "${LOG_DIR}/error-report" 11 12def errorreport_getdata(e): 13 import codecs 14 logpath = e.data.getVar('ERR_REPORT_DIR') 15 datafile = os.path.join(logpath, "error-report.txt") 16 with codecs.open(datafile, 'r', 'utf-8') as f: 17 data = f.read() 18 return data 19 20def errorreport_savedata(e, newdata, file): 21 import json 22 import codecs 23 logpath = e.data.getVar('ERR_REPORT_DIR') 24 datafile = os.path.join(logpath, file) 25 with codecs.open(datafile, 'w', 'utf-8') as f: 26 json.dump(newdata, f, indent=4, sort_keys=True) 27 return datafile 28 29def get_conf_data(e, filename): 30 builddir = e.data.getVar('TOPDIR') 31 filepath = os.path.join(builddir, "conf", filename) 32 jsonstring = "" 33 if os.path.exists(filepath): 34 with open(filepath, 'r') as f: 35 for line in f.readlines(): 36 if line.startswith("#") or len(line.strip()) == 0: 37 continue 38 else: 39 jsonstring=jsonstring + line 40 return jsonstring 41 42def get_common_data(e): 43 data = {} 44 data['machine'] = e.data.getVar("MACHINE") 45 data['build_sys'] = e.data.getVar("BUILD_SYS") 46 data['distro'] = e.data.getVar("DISTRO") 47 data['target_sys'] = e.data.getVar("TARGET_SYS") 48 data['branch_commit'] = str(oe.buildcfg.detect_branch(e.data)) + ": " + str(oe.buildcfg.detect_revision(e.data)) 49 data['bitbake_version'] = e.data.getVar("BB_VERSION") 50 data['layer_version'] = get_layers_branch_rev(e.data) 51 data['local_conf'] = get_conf_data(e, 'local.conf') 52 data['auto_conf'] = get_conf_data(e, 'auto.conf') 53 return data 54 55python errorreport_handler () { 56 import json 57 import codecs 58 59 def nativelsb(): 60 nativelsbstr = e.data.getVar("NATIVELSBSTRING") 61 # provide a bit more host info in case of uninative build 62 if e.data.getVar('UNINATIVE_URL') != 'unset': 63 return '/'.join([nativelsbstr, lsb_distro_identifier(e.data)]) 64 return nativelsbstr 65 66 logpath = e.data.getVar('ERR_REPORT_DIR') 67 datafile = os.path.join(logpath, "error-report.txt") 68 69 if isinstance(e, bb.event.BuildStarted): 70 bb.utils.mkdirhier(logpath) 71 data = {} 72 data = get_common_data(e) 73 data['nativelsb'] = nativelsb() 74 data['failures'] = [] 75 data['component'] = " ".join(e.getPkgs()) 76 lock = bb.utils.lockfile(datafile + '.lock') 77 errorreport_savedata(e, data, "error-report.txt") 78 bb.utils.unlockfile(lock) 79 80 elif isinstance(e, bb.build.TaskFailed): 81 task = e.task 82 taskdata={} 83 log = e.data.getVar('BB_LOGFILE') 84 taskdata['recipe'] = e.data.expand("${PN}") 85 taskdata['package'] = e.data.expand("${PF}") 86 taskdata['task'] = task 87 if log: 88 try: 89 with codecs.open(log, encoding='utf-8') as logFile: 90 logdata = logFile.read() 91 # Replace host-specific paths so the logs are cleaner 92 for d in ("TOPDIR", "TMPDIR"): 93 s = e.data.getVar(d) 94 if s: 95 logdata = logdata.replace(s, d) 96 except: 97 logdata = "Unable to read log file" 98 else: 99 logdata = "No Log" 100 101 # server will refuse failures longer than param specified in project.settings.py 102 # MAX_UPLOAD_SIZE = "5242880" 103 # use lower value, because 650 chars can be spent in task, package, version 104 max_logdata_size = 5242000 105 # upload last max_logdata_size characters 106 if len(logdata) > max_logdata_size: 107 logdata = "..." + logdata[-max_logdata_size:] 108 taskdata['log'] = logdata 109 lock = bb.utils.lockfile(datafile + '.lock') 110 jsondata = json.loads(errorreport_getdata(e)) 111 jsondata['failures'].append(taskdata) 112 errorreport_savedata(e, jsondata, "error-report.txt") 113 bb.utils.unlockfile(lock) 114 115 elif isinstance(e, bb.event.NoProvider): 116 bb.utils.mkdirhier(logpath) 117 data = {} 118 data = get_common_data(e) 119 data['nativelsb'] = nativelsb() 120 data['failures'] = [] 121 data['component'] = str(e._item) 122 taskdata={} 123 taskdata['log'] = str(e) 124 taskdata['package'] = str(e._item) 125 taskdata['task'] = "Nothing provides " + "'" + str(e._item) + "'" 126 data['failures'].append(taskdata) 127 lock = bb.utils.lockfile(datafile + '.lock') 128 errorreport_savedata(e, data, "error-report.txt") 129 bb.utils.unlockfile(lock) 130 131 elif isinstance(e, bb.event.ParseError): 132 bb.utils.mkdirhier(logpath) 133 data = {} 134 data = get_common_data(e) 135 data['nativelsb'] = nativelsb() 136 data['failures'] = [] 137 data['component'] = "parse" 138 taskdata={} 139 taskdata['log'] = str(e._msg) 140 taskdata['task'] = str(e._msg) 141 data['failures'].append(taskdata) 142 lock = bb.utils.lockfile(datafile + '.lock') 143 errorreport_savedata(e, data, "error-report.txt") 144 bb.utils.unlockfile(lock) 145 146 elif isinstance(e, bb.event.BuildCompleted): 147 lock = bb.utils.lockfile(datafile + '.lock') 148 jsondata = json.loads(errorreport_getdata(e)) 149 bb.utils.unlockfile(lock) 150 failures = jsondata['failures'] 151 if(len(failures) > 0): 152 filename = "error_report_" + e.data.getVar("BUILDNAME")+".txt" 153 datafile = errorreport_savedata(e, jsondata, filename) 154 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)) 155 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.") 156} 157 158addhandler errorreport_handler 159errorreport_handler[eventmask] = "bb.event.BuildStarted bb.event.BuildCompleted bb.build.TaskFailed bb.event.NoProvider bb.event.ParseError" 160