diff options
author | Chris Laplante <chris.laplante@agilent.com> | 2020-08-14 16:55:57 -0400 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2020-08-15 11:44:20 +0100 |
commit | 0e15931688322f9422c4f128965f050b6e030c29 (patch) | |
tree | 28d9f414604fb6738c05dc52eaa63579ab87309e | |
parent | b1cd7723c4d81081c6d1935be7373c7f06ae1b97 (diff) | |
download | poky-0e15931688322f9422c4f128965f050b6e030c29.tar.gz |
bitbake: build: print a backtrace with the original metadata locations of Bash shell funcs
Leverage the comments that emit_var writes and the backtrace that
the shell func writes to generate an additional metadata-relative
backtrace. This will help the user troubleshoot shell funcs much
more easily.
Example:
| WARNING: /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.68955:171 exit 1 from 'exit 1'
| WARNING: Backtrace (BB generated script):
| #1: myclass_do_something, /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.68955, line 171
| #2: do_something, /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.68955, line 166
| #3: actually_fail, /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.68955, line 153
| #4: my_compile_extra, /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.68955, line 155
| #5: do_compile, /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.68955, line 141
| #6: main, /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.68955, line 184
|
| Backtrace (metadata-relative locations):
| #1: myclass_do_something, /home/laplante/repos/oe-core/meta/classes/myclass.bbclass, line 2
| #2: do_something, autogenerated, line 2
| #3: actually_fail, /home/laplante/repos/oe-core/meta/recipes-extended/libsolv/libsolv_0.7.14.bb, line 36
| #4: my_compile_extra, /home/laplante/repos/oe-core/meta/recipes-extended/libsolv/libsolv_0.7.14.bb, line 38
| #5: do_compile, autogenerated, line 3
ERROR: Task (/home/laplante/repos/oe-core/meta/recipes-extended/libsolv/libsolv_0.7.14.bb:do_compile) failed with exit code '1'
NOTE: Tasks Summary: Attempted 542 tasks of which 541 didn't need to be rerun and 1 failed.
Summary: 1 task failed:
/home/laplante/repos/oe-core/meta/recipes-extended/libsolv/libsolv_0.7.14.bb:do_compile
Summary: There was 1 ERROR message shown, returning a non-zero exit code.
(Bitbake rev: ae1aa4ea79826c32b20e1e7abdf77a15b601c6f2)
Signed-off-by: Chris Laplante <chris.laplante@agilent.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
-rw-r--r-- | bitbake/lib/bb/build.py | 58 | ||||
-rw-r--r-- | bitbake/lib/bb/process.py | 3 |
2 files changed, 60 insertions, 1 deletions
diff --git a/bitbake/lib/bb/build.py b/bitbake/lib/bb/build.py index 98cbc48cd4..5479a53d7e 100644 --- a/bitbake/lib/bb/build.py +++ b/bitbake/lib/bb/build.py | |||
@@ -16,7 +16,9 @@ import os | |||
16 | import sys | 16 | import sys |
17 | import logging | 17 | import logging |
18 | import glob | 18 | import glob |
19 | import itertools | ||
19 | import time | 20 | import time |
21 | import re | ||
20 | import stat | 22 | import stat |
21 | import bb | 23 | import bb |
22 | import bb.msg | 24 | import bb.msg |
@@ -495,6 +497,62 @@ exit $ret | |||
495 | bb.debug(2, "Executing shell function %s" % func) | 497 | bb.debug(2, "Executing shell function %s" % func) |
496 | with open(os.devnull, 'r+') as stdin, logfile: | 498 | with open(os.devnull, 'r+') as stdin, logfile: |
497 | bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)]) | 499 | bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)]) |
500 | except bb.process.ExecutionError as exe: | ||
501 | # Find the backtrace that the shell trap generated | ||
502 | backtrace_marker_regex = re.compile(r"WARNING: Backtrace \(BB generated script\)") | ||
503 | stdout_lines = (exe.stdout or "").split("\n") | ||
504 | backtrace_start_line = None | ||
505 | for i, line in enumerate(reversed(stdout_lines)): | ||
506 | if backtrace_marker_regex.search(line): | ||
507 | backtrace_start_line = len(stdout_lines) - i | ||
508 | break | ||
509 | |||
510 | # Read the backtrace frames, starting at the location we just found | ||
511 | backtrace_entry_regex = re.compile(r"#(?P<frameno>\d+): (?P<funcname>[^\s]+), (?P<file>.+?), line (" | ||
512 | r"?P<lineno>\d+)") | ||
513 | backtrace_frames = [] | ||
514 | if backtrace_start_line: | ||
515 | for line in itertools.islice(stdout_lines, backtrace_start_line, None): | ||
516 | match = backtrace_entry_regex.search(line) | ||
517 | if match: | ||
518 | backtrace_frames.append(match.groupdict()) | ||
519 | |||
520 | with open(runfile, "r") as script: | ||
521 | script_lines = [line.rstrip() for line in script.readlines()] | ||
522 | |||
523 | # For each backtrace frame, search backwards in the script (from the line number called out by the frame), | ||
524 | # to find the comment that emit_vars injected when it wrote the script. This will give us the metadata | ||
525 | # filename (e.g. .bb or .bbclass) and line number where the shell function was originally defined. | ||
526 | script_metadata_comment_regex = re.compile(r"# line: (?P<lineno>\d+), file: (?P<file>.+)") | ||
527 | better_frames = [] | ||
528 | # Skip the very last frame since it's just the call to the shell task in the body of the script | ||
529 | for frame in backtrace_frames[:-1]: | ||
530 | # Check whether the frame corresponds to a function defined in the script vs external script. | ||
531 | if os.path.samefile(frame["file"], runfile): | ||
532 | # Search backwards from the frame lineno to locate the comment that BB injected | ||
533 | i = int(frame["lineno"]) - 1 | ||
534 | while i >= 0: | ||
535 | match = script_metadata_comment_regex.match(script_lines[i]) | ||
536 | if match: | ||
537 | # Calculate the relative line in the function itself | ||
538 | relative_line_in_function = int(frame["lineno"]) - i - 2 | ||
539 | # Calculate line in the function as declared in the metadata | ||
540 | metadata_function_line = relative_line_in_function + int(match["lineno"]) | ||
541 | better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format( | ||
542 | frameno=frame["frameno"], | ||
543 | funcname=frame["funcname"], | ||
544 | file=match["file"], | ||
545 | lineno=metadata_function_line | ||
546 | )) | ||
547 | break | ||
548 | i -= 1 | ||
549 | else: | ||
550 | better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format(**frame)) | ||
551 | |||
552 | if better_frames: | ||
553 | better_frames = ("\t{0}".format(frame) for frame in better_frames) | ||
554 | exe.extra_message = "\nBacktrace (metadata-relative locations):\n{0}".format("\n".join(better_frames)) | ||
555 | raise | ||
498 | finally: | 556 | finally: |
499 | os.unlink(fifopath) | 557 | os.unlink(fifopath) |
500 | 558 | ||
diff --git a/bitbake/lib/bb/process.py b/bitbake/lib/bb/process.py index 2dc472a86f..f36c929d25 100644 --- a/bitbake/lib/bb/process.py +++ b/bitbake/lib/bb/process.py | |||
@@ -41,6 +41,7 @@ class ExecutionError(CmdError): | |||
41 | self.exitcode = exitcode | 41 | self.exitcode = exitcode |
42 | self.stdout = stdout | 42 | self.stdout = stdout |
43 | self.stderr = stderr | 43 | self.stderr = stderr |
44 | self.extra_message = None | ||
44 | 45 | ||
45 | def __str__(self): | 46 | def __str__(self): |
46 | message = "" | 47 | message = "" |
@@ -51,7 +52,7 @@ class ExecutionError(CmdError): | |||
51 | if message: | 52 | if message: |
52 | message = ":\n" + message | 53 | message = ":\n" + message |
53 | return (CmdError.__str__(self) + | 54 | return (CmdError.__str__(self) + |
54 | " with exit code %s" % self.exitcode + message) | 55 | " with exit code %s" % self.exitcode + message + (self.extra_message or "")) |
55 | 56 | ||
56 | class Popen(subprocess.Popen): | 57 | class Popen(subprocess.Popen): |
57 | defaults = { | 58 | defaults = { |