WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit d12c373

Browse files
chris-laplanterpurdie
authored andcommitted
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. E.g.: | /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.87873: line 168: no_exist: command not found | WARNING: /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.87873:168 exit 127 from 'no_exist' | WARNING: Backtrace (BB generated script): | #0: 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.87873, line 168 | #1: do_something, /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.87873, line 157 | #2: actually_fail, /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.87873, line 150 | #3: 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.87873, line 152 | #4: do_compile, /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.87873, line 138 | #5: main, /home/laplante/repos/oe-core/build/tmp-glibc/work/core2-64-oe-linux/libsolv/0.7.14-r0/temp/run.do_compile.87873, line 181 | | Backtrace (metadata-relative locations): | #0: myclass_do_something, /home/laplante/repos/oe-core/meta/classes/myclass.bbclass, line 2 | #1: do_something, autogenerated, line 2 | #2: actually_fail, /home/laplante/repos/oe-core/meta/recipes-extended/libsolv/libsolv_0.7.14.bb, line 36 | #3: my_compile_extra, /home/laplante/repos/oe-core/meta/recipes-extended/libsolv/libsolv_0.7.14.bb, line 38 | #4: do_compile, autogenerated, line 3 Signed-off-by: Chris Laplante <[email protected]> Signed-off-by: Richard Purdie <[email protected]>
1 parent 32d036d commit d12c373

File tree

2 files changed

+60
-1
lines changed

2 files changed

+60
-1
lines changed

lib/bb/build.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
import sys
1717
import logging
1818
import glob
19+
import itertools
1920
import time
21+
import re
2022
import stat
2123
import bb
2224
import bb.msg
@@ -470,6 +472,62 @@ def readfifo(data):
470472
bb.debug(2, "Executing shell function %s" % func)
471473
with open(os.devnull, 'r+') as stdin, logfile:
472474
bb.process.run(cmd, shell=False, stdin=stdin, log=logfile, extrafiles=[(fifo,readfifo)])
475+
except bb.process.ExecutionError as exe:
476+
# Find the backtrace that the shell trap generated
477+
backtrace_marker_regex = re.compile(r"WARNING: Backtrace \(BB generated script\)")
478+
stdout_lines = (exe.stdout or "").split("\n")
479+
backtrace_start_line = None
480+
for i, line in enumerate(reversed(stdout_lines)):
481+
if backtrace_marker_regex.search(line):
482+
backtrace_start_line = len(stdout_lines) - i
483+
break
484+
485+
# Read the backtrace frames, starting at the location we just found
486+
backtrace_entry_regex = re.compile(r"#(?P<frameno>\d+): (?P<funcname>[^\s]+), (?P<file>.+?), line ("
487+
r"?P<lineno>\d+)")
488+
backtrace_frames = []
489+
if backtrace_start_line:
490+
for line in itertools.islice(stdout_lines, backtrace_start_line, None):
491+
match = backtrace_entry_regex.search(line)
492+
if match:
493+
backtrace_frames.append(match.groupdict())
494+
495+
with open(runfile, "r") as script:
496+
script_lines = [line.rstrip() for line in script.readlines()]
497+
498+
# For each backtrace frame, search backwards in the script (from the line number called out by the frame),
499+
# to find the comment that emit_vars injected when it wrote the script. This will give us the metadata
500+
# filename (e.g. .bb or .bbclass) and line number where the shell function was originally defined.
501+
script_metadata_comment_regex = re.compile(r"# line: (?P<lineno>\d+), file: (?P<file>.+)")
502+
better_frames = []
503+
# Skip the very last frame since it's just the call to the shell task in the body of the script
504+
for frame in backtrace_frames[:-1]:
505+
# Check whether the frame corresponds to a function defined in the script vs external script.
506+
if os.path.samefile(frame["file"], runfile):
507+
# Search backwards from the frame lineno to locate the comment that BB injected
508+
i = int(frame["lineno"]) - 1
509+
while i >= 0:
510+
match = script_metadata_comment_regex.match(script_lines[i])
511+
if match:
512+
# Calculate the relative line in the function itself
513+
relative_line_in_function = int(frame["lineno"]) - i - 2
514+
# Calculate line in the function as declared in the metadata
515+
metadata_function_line = relative_line_in_function + int(match["lineno"])
516+
better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format(
517+
frameno=frame["frameno"],
518+
funcname=frame["funcname"],
519+
file=match["file"],
520+
lineno=metadata_function_line
521+
))
522+
break
523+
i -= 1
524+
else:
525+
better_frames.append("#{frameno}: {funcname}, {file}, line {lineno}".format(**frame))
526+
527+
if better_frames:
528+
better_frames = ("\t{0}".format(frame) for frame in better_frames)
529+
exe.extra_message = "\nBacktrace (metadata-relative locations):\n{0}".format("\n".join(better_frames))
530+
raise
473531
finally:
474532
os.unlink(fifopath)
475533

lib/bb/process.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def __init__(self, command, exitcode, stdout = None, stderr = None):
4141
self.exitcode = exitcode
4242
self.stdout = stdout
4343
self.stderr = stderr
44+
self.extra_message = None
4445

4546
def __str__(self):
4647
message = ""
@@ -51,7 +52,7 @@ def __str__(self):
5152
if message:
5253
message = ":\n" + message
5354
return (CmdError.__str__(self) +
54-
" with exit code %s" % self.exitcode + message)
55+
" with exit code %s" % self.exitcode + message + (self.extra_message or ""))
5556

5657
class Popen(subprocess.Popen):
5758
defaults = {

0 commit comments

Comments
 (0)