Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,7 @@ hooks to PATH.
- ``-T, --trace``: Enable function tracing; adds CONFIG_TRACE and
CONFIG_TRACE_EARLY (use with -b)
- ``--no-trace-early``: Disable TRACE_EARLY when using -T (use with -b)
- ``--leak-check``: Check for memory leaks around each test using mallinfo()
- ``--malloc-dump FILE``: Write malloc heap dump on exit; ``%d`` in the filename
is expanded to a sequence number
- ``--no-timeout``: Disable test timeout
Expand Down Expand Up @@ -866,6 +867,8 @@ without going through pytest. This is faster for quick iteration on C code.
- ``-j, --jobs JOBS``: Number of parallel jobs (use with -b)
- ``-l, --list``: List available tests
- ``-L, --lto``: Enable LTO when building (use with -b)
- ``--leak-check``: Check for memory leaks around each test using mallinfo()
- ``--show-leaks N``: Show top N leaks by bytes (default: 10, 0 to disable)
- ``--legacy``: Use legacy result parsing (for old U-Boot)
- ``--malloc-dump FILE``: Write malloc heap dump on exit; ``%d`` in the filename
is expanded to a sequence number
Expand Down
25 changes: 19 additions & 6 deletions uman_pkg/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,23 @@ def get_cmd(args, board, build_dir):
return ['buildman'] + get_buildman_args(args, board, build_dir)


def base_bm_args(board, build_dir, lto=True):
"""Build the common buildman arguments

Args:
board (str): Board name to build
build_dir (str): Path to build directory
lto (bool): Enable LTO (default True)

Returns:
list: Base arguments for buildman
"""
bm_args = ['-I', '-w', '-W', '--boards', board, '-o', build_dir]
if not lto:
bm_args.insert(0, '-L')
return bm_args


def get_buildman_args(args, board, build_dir):
"""Build the buildman arguments

Expand All @@ -188,9 +205,7 @@ def get_buildman_args(args, board, build_dir):
if args.in_tree:
bm_args = ['-i', '--boards', board]
else:
bm_args = ['-I', '-w', '--boards', board, '-o', build_dir]
if not args.lto:
bm_args.insert(0, '-L')
bm_args = base_bm_args(board, build_dir, args.lto)
if args.target:
bm_args.extend(['--target', args.target])
if args.jobs:
Expand Down Expand Up @@ -234,9 +249,7 @@ def build_board(board, dry_run=False, lto=False, adjust_cfg=None,

tout.progress(f'Building {board}')

bm_args = ['-I', '-w', '--boards', board, '-o', build_dir]
if not lto:
bm_args.insert(0, '-L')
bm_args = base_bm_args(board, build_dir, lto)
if force_reconfig:
bm_args.append('-C')
if jobs:
Expand Down
4 changes: 2 additions & 2 deletions uman_pkg/cc.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@
ALSA_PULSE_CONF = '/etc/alsa-pulse.conf'

# Default packages to install in containers
DEFAULT_PACKAGES = ('build-essential gh glab libasound2-plugins'
' libsox-fmt-pulse pylint sox xclip')
DEFAULT_PACKAGES = ('build-essential gdb-multiarch gh glab'
' libasound2-plugins libsox-fmt-pulse pylint sox xclip')


def get_log_path(name):
Expand Down
25 changes: 19 additions & 6 deletions uman_pkg/cmdline.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,23 @@ def add_selftest_subparser(subparsers):
return stest


def add_leak_opts(parser):
"""Add leak-check and malloc-dump options to a parser

Args:
parser: Argument parser to add options to
"""
parser.add_argument(
'-M', '--leak-check', action='store_true', dest='leak_check',
help='Check for memory leaks around each test')
parser.add_argument(
'--show-leaks', type=int, default=10, metavar='N', dest='show_leaks',
help='Show top N leaks by bytes (default: 10, 0 to disable)')
parser.add_argument(
'--malloc-dump', metavar='FILE', dest='malloc_dump',
help='Write malloc dump to FILE on exit (use %%d for sequence number)')


def add_test_opts(parser, board_help=None, board_default=None):
"""Add common test options to a parser

Expand Down Expand Up @@ -204,9 +221,7 @@ def add_test_opts(parser, board_help=None, board_default=None):
parser.add_argument(
'-x', '--exitfirst', action='store_true',
help='Stop on first test failure')
parser.add_argument(
'--malloc-dump', metavar='FILE', dest='malloc_dump',
help='Write malloc dump to FILE on exit (use %%d for sequence number)')
add_leak_opts(parser)


def add_build_opts(parser):
Expand Down Expand Up @@ -400,12 +415,10 @@ def add_test_subparser(subparsers):
test.add_argument(
'-s', '--suites', action='store_true', dest='list_suites',
help='List available test suites')
test.add_argument(
'--malloc-dump', metavar='FILE', dest='malloc_dump',
help='Write malloc dump to FILE on exit (use %%d for sequence number)')
test.add_argument(
'-V', '--test-verbose', action='store_true', dest='test_verbose',
help='Enable verbose test output')
add_leak_opts(test)
add_build_opts(test)
return test

Expand Down
43 changes: 34 additions & 9 deletions uman_pkg/cmdpy.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@

from uman_pkg import build as build_mod
from uman_pkg import settings
from uman_pkg.cmdtest import get_sandbox_path
from uman_pkg.util import exec_cmd, get_uboot_dir, show_summary
from uman_pkg.cmdtest import get_sandbox_path, parse_results
from uman_pkg.util import (exec_cmd, get_uboot_dir, show_leak_top,
show_summary)

# Fallback hostname directory for test hooks (has standard QEMU board configs)
HOOKS_FALLBACK = 'travis-ci'
Expand Down Expand Up @@ -471,6 +472,8 @@ def build_pytest_cmd(args):
cmd.append('--no-full')
if args.malloc_dump:
cmd.extend(['--malloc-dump', args.malloc_dump])
if args.leak_check:
cmd.append('--leak-check')

# Add extra pytest arguments (after --)
if args.extra_args:
Expand Down Expand Up @@ -1154,9 +1157,8 @@ def do_pollute(args):
base_dir = settings.get('build_dir', '/tmp/b')
build_dir = f'{base_dir}/{args.board}-pollute'
tout.notice(f'Building to {build_dir}...')
cmd = ['buildman', '-I', '-w', '--boards', args.board, '-o', build_dir]
if not args.lto:
cmd.insert(1, '-L')
cmd = ['buildman'] + build_mod.base_bm_args(args.board, build_dir,
args.lto)
result = exec_cmd(cmd, args.dry_run, capture=False)
if result and result.return_code != 0:
tout.error('Build failed')
Expand Down Expand Up @@ -1364,7 +1366,9 @@ def do_pytest(args): # pylint: disable=too-many-return-statements,too-many-bran
if args.gdb:
return run_with_gdb(args)

start_time = time.time()
result = exec_cmd(cmd, args.dry_run, env=env, capture=False)
elapsed = time.time() - start_time

if result is None: # dry-run
qemu_cmd = get_qemu_command(board, args)
Expand All @@ -1377,8 +1381,29 @@ def do_pytest(args): # pylint: disable=too-many-return-statements,too-many-bran
print(result.stderr, file=sys.stderr)
if not args.quiet:
tout.error('pytest failed')
return result.return_code
else:
if not args.quiet:
tout.notice('pytest passed')

if not args.quiet:
tout.notice('pytest passed')
return 0
# Show leak summary from test log if leak-check was enabled
if args.leak_check:
if args.output_dir:
build_dir = args.output_dir
else:
base_dir = settings.get('build_dir', '/tmp/b')
build_dir = f'{base_dir}/{args.board}'
log_path = os.path.join(build_dir, 'test-log.html')
if os.path.exists(log_path):
import html
with open(log_path) as fh:
text = fh.read()
# Strip HTML tags first, then unescape entities
text = re.sub(r'<[^>]+>', '\n', text)
text = html.unescape(text)
res = parse_results(text)
if res and res.leaked:
show_summary(res.passed, res.failed, res.skipped, elapsed,
res.leaked, res.leak_bytes)
if res.leak_top and args.show_leaks:
show_leak_top(res.leak_top, args.show_leaks)
return result.return_code
Loading
Loading