summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Purdie <richard.purdie@linuxfoundation.org>2013-05-08 18:14:53 +0100
committerRichard Purdie <richard.purdie@linuxfoundation.org>2013-05-09 14:06:52 +0100
commitbc95ddec6da0d2ae5da2637f2d065b89594ed041 (patch)
treeadf3c30ffd592b0a074c311646e0abb5ae20f337
parentf58e82b9e9cab8e497b70920c77fcac4f355bb91 (diff)
downloadpoky-bc95ddec6da0d2ae5da2637f2d065b89594ed041.tar.gz
bitbake: utils: Improve better_exec traceback handling
The current bitbake tracebacks are hard to read/confusing and sometimes incomplete. This patch attempts to do better by: * Moving the note about the exact exception to the end to make things read in sequence * Merged the initial stack trace to become part of the code dump * Added handling for "/xxxx" file paths since we can load these files and include the data as part of the trace * Dropped the ERROR: prefix to every line, allowing the error messages to be spacially accosicated in the UIs * Moved the "From:" line to the top of each code block and ensured its present consistently With the complexity now in this funciton, I've added try/except wrapping around it to ensure we catch exceptions in the exception handler too. Example before: """ ERROR: Error executing a python function in /media/build1/poky/meta/recipes-core/eglibc/eglibc-initial_2.17.bb: TypeError: 'filter' object is not subscriptable ERROR: The stack trace of python calls that resulted in this exception/failure was: ERROR: File "do_populate_lic", line 13, in <module> ERROR: ERROR: File "do_populate_lic", line 6, in do_populate_lic ERROR: ERROR: File "license.bbclass", line 99, in find_license_files ERROR: ERROR: File "/media/build1/poky/meta/lib/oe/license.py", line 38, in visit_string ERROR: if pos > 0 and license_pattern.match(elements[pos-1]): ERROR: ERROR: The code that was being executed was: ERROR: 0009: destdir = os.path.join(d.getVar('LICSSTATEDIR', True), d.getVar('PN', True)) ERROR: 0010: copy_license_files(lic_files_paths, destdir) ERROR: 0011: ERROR: 0012: ERROR: *** 0013:do_populate_lic(d) ERROR: 0014: ERROR: [From file: 'do_populate_lic', lineno: 13, function: <module>] ERROR: 0002:def do_populate_lic(d): ERROR: 0003: """ ERROR: 0004: Populate LICENSE_DIRECTORY with licenses. ERROR: 0005: """ ERROR: *** 0006: lic_files_paths = find_license_files(d) ERROR: 0007: ERROR: 0008: # The base directory we wrangle licenses to ERROR: 0009: destdir = os.path.join(d.getVar('LICSSTATEDIR', True), d.getVar('PN', True)) ERROR: 0010: copy_license_files(lic_files_paths, destdir) ERROR: [From file: 'do_populate_lic', lineno: 6, function: do_populate_lic] ERROR: 0095: lic_files_paths.append((os.path.basename(path), srclicfile)) ERROR: 0096: ERROR: 0097: v = FindVisitor() ERROR: 0098: try: ERROR: *** 0099: v.visit_string(license_types) ERROR: 0100: except oe.license.InvalidLicense as exc: ERROR: 0101: bb.fatal('%s: %s' % (d.getVar('PF', True), exc)) ERROR: 0102: except SyntaxError: ERROR: 0103: bb.warn("%s: Failed to parse it's LICENSE field." % (d.getVar('PF', True))) ERROR: [From file: 'license.bbclass', lineno: 99, function: find_license_files] ERROR: Function failed: do_populate_lic ERROR: Logfile of failure stored in: /media/build1/poky/build/tmp/work/i586-poky-linux/eglibc-initial/2.17-r3/temp/log.do_populate_lic.17442 """ Example after: """ ERROR: Error executing a python function in /media/build1/poky/meta/recipes-core/eglibc/eglibc-initial_2.17.bb: The stack trace of python calls that resulted in this exception/failure was: File: 'do_populate_lic', lineno: 13, function: <module> 0009: destdir = os.path.join(d.getVar('LICSSTATEDIR', True), d.getVar('PN', True)) 0010: copy_license_files(lic_files_paths, destdir) 0011: 0012: *** 0013:do_populate_lic(d) 0014: File: 'do_populate_lic', lineno: 6, function: do_populate_lic 0002:def do_populate_lic(d): 0003: """ 0004: Populate LICENSE_DIRECTORY with licenses. 0005: """ *** 0006: lic_files_paths = find_license_files(d) 0007: 0008: # The base directory we wrangle licenses to 0009: destdir = os.path.join(d.getVar('LICSSTATEDIR', True), d.getVar('PN', True)) 0010: copy_license_files(lic_files_paths, destdir) File: 'license.bbclass', lineno: 99, function: find_license_files 0095: lic_files_paths.append((os.path.basename(path), srclicfile)) 0096: 0097: v = FindVisitor() 0098: try: *** 0099: v.visit_string(license_types) 0100: except oe.license.InvalidLicense as exc: 0101: bb.fatal('%s: %s' % (d.getVar('PF', True), exc)) 0102: except SyntaxError: 0103: bb.warn("%s: Failed to parse it's LICENSE field." % (d.getVar('PF', True))) File: '/media/build1/poky/meta/lib/oe/license.py', lineno: 38, function: visit_string 0034: new_elements = [] 0035: elements = filter(lambda x: x.strip(), license_operator.split(licensestr)) 0036: for pos, element in enumerate(elements): 0037: if license_pattern.match(element): *** 0038: if pos > 0 and license_pattern.match(elements[pos-1]): 0039: new_elements.append('&') 0040: element = '"' + element + '"' 0041: elif not license_operator.match(element): 0042: raise InvalidLicense(element) Exception: TypeError: 'filter' object is not subscriptable ERROR: Function failed: do_populate_lic ERROR: Logfile of failure stored in: /media/build1/poky/build/tmp/work/i586-poky-linux/eglibc-initial/2.17-r3/temp/log.do_populate_lic.3275 ERROR: Task 9 (/media/build1/poky/meta/recipes-core/eglibc/eglibc-initial_2.17.bb, do_populate_lic) failed with exit code '1 """ (Bitbake rev: c5de66b870406d9bd1161a9b7e2b04fe6eb065fe) Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r--bitbake/lib/bb/utils.py96
1 files changed, 57 insertions, 39 deletions
diff --git a/bitbake/lib/bb/utils.py b/bitbake/lib/bb/utils.py
index d671f56b50..9d7a32fb25 100644
--- a/bitbake/lib/bb/utils.py
+++ b/bitbake/lib/bb/utils.py
@@ -236,14 +236,16 @@ def _print_trace(body, line):
236 """ 236 """
237 Print the Environment of a Text Body 237 Print the Environment of a Text Body
238 """ 238 """
239 error = []
239 # print the environment of the method 240 # print the environment of the method
240 min_line = max(1, line-4) 241 min_line = max(1, line-4)
241 max_line = min(line + 4, len(body)) 242 max_line = min(line + 4, len(body))
242 for i in xrange(min_line, max_line + 1): 243 for i in range(min_line, max_line + 1):
243 if line == i: 244 if line == i:
244 logger.error(' *** %.4d:%s', i, body[i-1]) 245 error.append(' *** %.4d:%s' % (i, body[i-1].rstrip()))
245 else: 246 else:
246 logger.error(' %.4d:%s', i, body[i-1]) 247 error.append(' %.4d:%s' % (i, body[i-1].rstrip()))
248 return error
247 249
248def better_compile(text, file, realfile, mode = "exec"): 250def better_compile(text, file, realfile, mode = "exec"):
249 """ 251 """
@@ -260,7 +262,7 @@ def better_compile(text, file, realfile, mode = "exec"):
260 if e.lineno: 262 if e.lineno:
261 logger.error("The lines leading to this error were:") 263 logger.error("The lines leading to this error were:")
262 logger.error("\t%d:%s:'%s'", e.lineno, e.__class__.__name__, body[e.lineno-1]) 264 logger.error("\t%d:%s:'%s'", e.lineno, e.__class__.__name__, body[e.lineno-1])
263 _print_trace(body, e.lineno) 265 logger.error("\n".join(_print_trace(body, e.lineno)))
264 else: 266 else:
265 logger.error("The function causing this error was:") 267 logger.error("The function causing this error was:")
266 for line in body: 268 for line in body:
@@ -269,68 +271,84 @@ def better_compile(text, file, realfile, mode = "exec"):
269 e = bb.BBHandledException(e) 271 e = bb.BBHandledException(e)
270 raise e 272 raise e
271 273
272def better_exec(code, context, text = None, realfile = "<code>"): 274def _print_exception(t, value, tb, realfile, text, context):
273 """ 275 error = []
274 Similiar to better_compile, better_exec will
275 print the lines that are responsible for the
276 error.
277 """
278 import bb.parse
279 if not text:
280 text = code
281 if not hasattr(code, "co_filename"):
282 code = better_compile(code, realfile, realfile)
283 try: 276 try:
284 exec(code, _context, context)
285 except Exception as e:
286 (t, value, tb) = sys.exc_info()
287
288 if t in [bb.parse.SkipPackage, bb.build.FuncFailed]:
289 raise
290
291 import traceback 277 import traceback
292 exception = traceback.format_exception_only(t, value) 278 exception = traceback.format_exception_only(t, value)
293 logger.error('Error executing a python function in %s:\n%s', 279 error.append('Error executing a python function in %s:\n' % realfile)
294 realfile, ''.join(exception))
295 280
296 # Strip 'us' from the stack (better_exec call) 281 # Strip 'us' from the stack (better_exec call)
297 tb = tb.tb_next 282 tb = tb.tb_next
298 283
299 textarray = text.split('\n') 284 textarray = text.split('\n')
300 linefailed = traceback.tb_lineno(tb)
301 285
302 tbextract = traceback.extract_tb(tb) 286 linefailed = tb.tb_lineno
303 tbformat = "\n".join(traceback.format_list(tbextract))
304 logger.error("The stack trace of python calls that resulted in this exception/failure was:")
305 for line in tbformat.split('\n'):
306 logger.error(line)
307 287
308 logger.error("The code that was being executed was:") 288 tbextract = traceback.extract_tb(tb)
309 _print_trace(textarray, linefailed) 289 tbformat = traceback.format_list(tbextract)
310 logger.error("[From file: '%s', lineno: %s, function: %s]", tbextract[0][0], tbextract[0][1], tbextract[0][2]) 290 error.append("The stack trace of python calls that resulted in this exception/failure was:")
291 error.append("File: '%s', lineno: %s, function: %s" % (tbextract[0][0], tbextract[0][1], tbextract[0][2]))
292 error.extend(_print_trace(textarray, linefailed))
311 293
312 # See if this is a function we constructed and has calls back into other functions in 294 # See if this is a function we constructed and has calls back into other functions in
313 # "text". If so, try and improve the context of the error by diving down the trace 295 # "text". If so, try and improve the context of the error by diving down the trace
314 level = 0 296 level = 0
315 nexttb = tb.tb_next 297 nexttb = tb.tb_next
316 while nexttb is not None and (level+1) < len(tbextract): 298 while nexttb is not None and (level+1) < len(tbextract):
299 error.append("File: '%s', lineno: %s, function: %s" % (tbextract[level+1][0], tbextract[level+1][1], tbextract[level+1][2]))
317 if tbextract[level][0] == tbextract[level+1][0] and tbextract[level+1][2] == tbextract[level][0]: 300 if tbextract[level][0] == tbextract[level+1][0] and tbextract[level+1][2] == tbextract[level][0]:
318 _print_trace(textarray, tbextract[level+1][1]) 301 # The code was possibly in the string we compiled ourselves
319 logger.error("[From file: '%s', lineno: %s, function: %s]", tbextract[level+1][0], tbextract[level+1][1], tbextract[level+1][2]) 302 error.extend(_print_trace(textarray, tbextract[level+1][1]))
303 elif tbextract[level+1][0].startswith("/"):
304 # The code looks like it might be in a file, try and load it
305 try:
306 with open(tbextract[level+1][0], "r") as f:
307 text = f.readlines()
308 error.extend(_print_trace(text, tbextract[level+1][1]))
309 except:
310 error.append(tbformat[level+1])
320 elif "d" in context and tbextract[level+1][2]: 311 elif "d" in context and tbextract[level+1][2]:
312 # Try and find the code in the datastore based on the functionname
321 d = context["d"] 313 d = context["d"]
322 functionname = tbextract[level+1][2] 314 functionname = tbextract[level+1][2]
323 text = d.getVar(functionname, True) 315 text = d.getVar(functionname, True)
324 if text: 316 if text:
325 _print_trace(text.split('\n'), tbextract[level+1][1]) 317 error.extend(_print_trace(text.split('\n'), tbextract[level+1][1]))
326 logger.error("[From file: '%s', lineno: %s, function: %s]", tbextract[level+1][0], tbextract[level+1][1], tbextract[level+1][2])
327 else: 318 else:
328 break 319 error.append(tbformat[level+1])
329 else: 320 else:
330 break 321 error.append(tbformat[level+1])
331 nexttb = tb.tb_next 322 nexttb = tb.tb_next
332 level = level + 1 323 level = level + 1
333 324
325 error.append("Exception: %s" % ''.join(exception))
326 finally:
327 logger.error("\n".join(error))
328
329def better_exec(code, context, text = None, realfile = "<code>"):
330 """
331 Similiar to better_compile, better_exec will
332 print the lines that are responsible for the
333 error.
334 """
335 import bb.parse
336 if not text:
337 text = code
338 if not hasattr(code, "co_filename"):
339 code = better_compile(code, realfile, realfile)
340 try:
341 exec(code, _context, context)
342 except Exception as e:
343 (t, value, tb) = sys.exc_info()
344
345 if t in [bb.parse.SkipPackage, bb.build.FuncFailed]:
346 raise
347 try:
348 _print_exception(t, value, tb, realfile, text, context)
349 except Exception as e:
350 logger.error("Exception handler error: %s" % str(e))
351
334 e = bb.BBHandledException(e) 352 e = bb.BBHandledException(e)
335 raise e 353 raise e
336 354