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 /bitbake/lib/bb | |
| 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>
Diffstat (limited to 'bitbake/lib/bb')
| -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 = { |
