mirror of
https://git.yoctoproject.org/poky
synced 2026-05-08 05:09:24 +00:00
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>
This commit is contained in:
committed by
Richard Purdie
parent
b1cd7723c4
commit
0e15931688
@@ -16,7 +16,9 @@ import os
|
||||
import sys
|
||||
import logging
|
||||
import glob
|
||||
import itertools
|
||||
import time
|
||||
import re
|
||||
import stat
|
||||
import bb
|
||||
import bb.msg
|
||||
@@ -495,6 +497,62 @@ exit $ret
|
||||
bb.debug(2, "Executing shell function %s" % func)
|
||||
with open(os.devnull, 'r+') as stdin, logfile:
|
||||
bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
|
||||
except bb.process.ExecutionError as exe:
|
||||
# Find the backtrace that the shell trap generated
|
||||
backtrace_marker_regex = re.compile(r"WARNING: Backtrace \(BB generated script\)")
|
||||
stdout_lines = (exe.stdout or "").split("\n")
|
||||
backtrace_start_line = None
|
||||
for i, line in enumerate(reversed(stdout_lines)):
|
||||
if backtrace_marker_regex.search(line):
|
||||
backtrace_start_line = len(stdout_lines) - i
|
||||
break
|
||||
|
||||
# Read the backtrace frames, starting at the location we just found
|
||||
backtrace_entry_regex = re.compile(r"#(?P<frameno>\d+): (?P<funcname>[^\s]+), (?P<file>.+?), line ("
|
||||
r"?P<lineno>\d+)")
|
||||
backtrace_frames = []
|
||||
if backtrace_start_line:
|
||||
for line in itertools.islice(stdout_lines, backtrace_start_line, None):
|
||||
match = backtrace_entry_regex.search(line)
|
||||
if match:
|
||||
backtrace_frames.append(match.groupdict())
|
||||
|
||||
with open(runfile, "r") as script:
|
||||
script_lines = [line.rstrip() for line in script.readlines()]
|
||||
|
||||
# For each backtrace frame, search backwards in the script (from the line number called out by the frame),
|
||||
# to find the comment that emit_vars injected when it wrote the script. This will give us the metadata
|
||||
# filename (e.g. .bb or .bbclass) and line number where the shell function was originally defined.
|
||||
script_metadata_comment_regex = re.compile(r"# line: (?P<lineno>\d+), file: (?P<file>.+)")
|
||||
better_frames = []
|
||||
# Skip the very last frame since it's just the call to the shell task in the body of the script
|
||||
for frame in backtrace_frames[:-1]:
|
||||
# Check whether the frame corresponds to a function defined in the script vs external script.
|
||||
if os.path.samefile(frame["file"], runfile):
|
||||
# Search backwards from the frame lineno to locate the comment that BB injected
|
||||
i = int(frame["lineno"]) - 1
|
||||
while i >= 0:
|
||||
match = script_metadata_comment_regex.match(script_lines[i])
|
||||
if match:
|
||||
# Calculate the relative line in the function itself
|
||||
relative_line_in_function = int(frame["lineno"]) - i - 2
|
||||
# Calculate line in the function as declared in the metadata
|
||||
metadata_function_line = relative_line_in_function + int(match["lineno"])
|
||||
better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format(
|
||||
frameno=frame["frameno"],
|
||||
funcname=frame["funcname"],
|
||||
file=match["file"],
|
||||
lineno=metadata_function_line
|
||||
))
|
||||
break
|
||||
i -= 1
|
||||
else:
|
||||
better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format(**frame))
|
||||
|
||||
if better_frames:
|
||||
better_frames = ("\t{0}".format(frame) for frame in better_frames)
|
||||
exe.extra_message = "\nBacktrace (metadata-relative locations):\n{0}".format("\n".join(better_frames))
|
||||
raise
|
||||
finally:
|
||||
os.unlink(fifopath)
|
||||
|
||||
|
||||
@@ -41,6 +41,7 @@ class ExecutionError(CmdError):
|
||||
self.exitcode = exitcode
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
self.extra_message = None
|
||||
|
||||
def __str__(self):
|
||||
message = ""
|
||||
@@ -51,7 +52,7 @@ class ExecutionError(CmdError):
|
||||
if message:
|
||||
message = ":\n" + message
|
||||
return (CmdError.__str__(self) +
|
||||
" with exit code %s" % self.exitcode + message)
|
||||
" with exit code %s" % self.exitcode + message + (self.extra_message or ""))
|
||||
|
||||
class Popen(subprocess.Popen):
|
||||
defaults = {
|
||||
|
||||
Reference in New Issue
Block a user