Skip to content

MDEV-30281 MTR usability improvements (config file and misc features) - #5467

Open
midenok wants to merge 18 commits into
bb-10.11-midenokfrom
10.11-midenok-MDEV-30281-mtr
Open

MDEV-30281 MTR usability improvements (config file and misc features)#5467
midenok wants to merge 18 commits into
bb-10.11-midenokfrom
10.11-midenok-MDEV-30281-mtr

Conversation

@midenok

@midenok midenok commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

A set of independent quality-of-life improvements to the MTR harness (mariadb-test-run.pl), covering four areas: reading options from a standard config file, trimming noisy failure reports, running test features, and debugger options.

Each item is self-contained and lands as its own commit; command-line options are off by default and preserve existing behaviour unless requested.

1. Configuration from an option file

MTR now reads its own options from the standard MariaDB option files, from the [mtr] section. Command-line options take precedence over the file.

  • Default lookup follows Configuring MariaDB with Option Files (global, server and user configs); ~/.my.cnf is read last.
  • The default search covers the Unix locations only (no Windows directories, no my.ini); on Windows, or any non-standard setup, use an explicit MTR_CONFIG / --defaults-file, which work on every platform.
  • MTR_CONFIG env var selects a custom config location; when set, no other file is read.
  • Reuses the in-tree My::Config parser (no third-party module). A new load_defaults() in mariadb-test-run.pl merges [mtr] options into @ARGV, separated from command-line options by an ---end-of-config--- marker so error reporting can name the source.
  • My::Config extended to support !includedir, skip whitespace-only lines, and ignore unknown ! directives instead of failing.

Standard defaults options are wired in, mirroring libmariadb's my_load_defaults():

Option Meaning
--no-defaults do not read any option file
--defaults-file read [mtr] from this file only
--defaults-extra-file read this file in addition
--defaults-group-suffix also read [mtr<suffix>]
--print-defaults print the [mtr] options and exit
--mtr-config-only (alias -M) read [mtr] from --defaults-file/--defaults-extra-file only, without also using them as the server template

--defaults-file / --defaults-extra-file keep their existing MTR meaning as the server config template too; the two consumers read different groups of the same file. Command line overrides MTR_CONFIG/MTR_CONFIG_EXTRA and the group-suffix env vars.

--mtr-config-only (-M) opts out of that dual use: the file is then read for [mtr] only and dropped from @ARGV, so it is not also applied as the server template. It may be given on the command line, or set inside the [mtr] section itself (applied on the fly while [mtr] is read) — so a single mtr-config-only line turns any --defaults-file into an MTR-only config.

-M is only relevant for a command-line --defaults-file / --defaults-extra-file (the dual-use options). The MTR_CONFIG / MTR_CONFIG_EXTRA env vars are read only for [mtr] and never reach the template consumer, so they are mtr-config-only already and -M is a no-op for them.

Config error reporting

Every wrong setting in a config file now fails with a clean file:line: reason message, without a Perl stack trace or source-code location, and it is clear whether a bad option came from the config file or the command line.

2. Trimming noisy failure reports

Crash reports and shutdown-warning dumps could emit the entire server error log and every suspicious line with no limit. New options bound them.

Head/tail trimming

Option Meaning
--head-log=N keep first N lines of the server error log
--tail-log=N keep last N lines; combine with --head-log to keep both ends
--head-warnings=N keep first N suspicious shutdown-warning lines
--tail-warnings=N keep last N; combine with --head-warnings
--head=N shortcut: head-* = N, tail-* = 0 (!)
--tail=N shortcut: tail-* = N, head-* = 0 (!)

N=0 keeps nothing; a negative value keeps everything; trimmed output is marked with < snip N lines >.

Stripping boilerplate

Option Removes
--strip-hints bug-reporting hint paragraphs: the server log's, My::CoreDump's "Output from gdb follows", and MTR's own end-of-run "report this error" bug-tracker block
--strip-limits the "Resource Limits" table read from /proc/self/limits
--strip-backtrace the server's own my_print_stacktrace backtrace (the gdb/lldb core backtrace is kept)
--strip-log convenience alias enabling all three

All stripping runs before head/tail trimming, so those line counts apply to the already-stripped log. --strip-backtrace treats the backtrace as a bounded block (state machine) rather than matching lines everywhere.

3. Running test features

  • --suites=!NAME — exclude a suite. Positive names (or the default set) form the base; every !-name is removed from it. Accumulates across the config file and command line, so a command-line exclusion trims the file-configured set. Ignores the -<overlay> suffix (so !rpl removes rpl-).
  • --combination-select=N (alias -c) — run only the Nth combination of a .combinations file (1-based; negative counts from the end, -1 = last). Applied in the single point where every .combinations file is read. Ignored when --combination is given.
  • --list-combinations (alias --lc) — print a test's available combinations in the selectable test,combination form and exit. Each printed line round-trips back into mtr. Multi-dimension tests list the full product.
  • --exit-line=N (alias -l) — stop the test before the command at line N of the test file, exactly as if an --exit directive were placed there. Implemented as a mysqltest option and forwarded by mtr; gated to the top-level test file so line numbers of sourced includes never trigger it.

4. Debugger options

Running external tools under a debugger

New --exec-rr / --exec-gdb options run every mysqltest --exec command under a wrapper (rr record, or gdb --args in a terminal), so external tools invoked by tests (myisampack, myisamchk, ...) can be traced or debugged without naming them.

  • My::Debugger: any debugger with an exec template auto-registers an --exec-<dbg> option; pre_setup() exports MYSQLTEST_EXEC_WRAP (plus _RR_TRACE_DIR for rr).
  • do_exec() injects the wrapper into the command line after any leading shell NAME=VALUE assignments, so the tool — not the assignment word — is wrapped. The implicit /bin/sh -c from popen() provides the shell layer.

Configurable terminal

The terminal emulator for interactive debuggers (--gdb, --exec-gdb, ...) was hard-coded to xterm; it is now configurable via the --terminal option or the MTR_TERM environment variable (the option takes precedence). The template expands two placeholders — {title} (window title) and {command} (the debugger invocation) — and the default xterm -title {title} -e {command} preserves the previous behaviour. For example: --terminal='gnome-terminal --title={title} -- {command}'.

5. Faster test collection (~3x)

Locating each test's .result/.rdiff files used a per-test glob keyed on the test name, which forced a full read of the suite's result directory on every test — and twice over, once for each extension — so collection cost grew with tests × directory size. Each result directory is now read once and its .result/.rdiff files indexed by base test name (the leading run before the first . or ,); every test then looks its files up in that cache. The glob was only a coarse prefilter — the existing regex still does the real matching — so the candidate set, and the resulting test list, is byte-for-byte identical.

Collecting all default suites (6548 tests, warm cache): collection time 13.1s → 4.5s (~2.9×), directory-open syscalls 23026 → 5284.

midenok added 3 commits July 24, 2026 14:03
Document each parameter as in, out or in-out, and note that *argv is
replaced and must be freed with free_defaults(). Also fix a couple of typos.
Failure reports could dump the entire per-test server error log
(get_log_from_proc) and every suspicious line collected during shutdown
(check_warnings_post_shutdown) with no limit, which is very noisy on
tests that produce large logs.

Add seven options:

  --head-log=N       keep the first N lines of the server error log in a
                     crash report
  --tail-log=N       keep the last N lines of the server error log in a
                     crash report; combine with --head-log to keep both ends
                     with the middle snipped
  --head-warnings=N  keep the first N suspicious lines of the shutdown-
                     warnings report
  --tail-warnings=N  keep the last N suspicious lines of the shutdown-warnings
                     report; combine with --head-warnings to keep both ends
                     with the middle snipped
  --head=N           shortcut: set the head-* options to N and the tail-*
                     options to 0
  --tail=N           shortcut: set the tail-* options to N and the head-*
                     options to 0

For each option N=0 keeps nothing and a negative value includes
everything; trimmed output is prefixed with a "< snip N lines >" marker.
For both the server error log and the shutdown-warnings report the whole
text is kept unless the corresponding --head-*/--tail-* option is given,
and giving one end alone drops the opposite end. A shared splice_lines()
helper implements the trimming for both.

The generic "negative values aren't meaningful on integer options" guard
in command_line_setup() is updated to exempt the --tail*/--head-* options,
so their negative "everything" default is accepted instead of aborting
startup. --tail-lines now also accepts a negative value, mapped to the
mysqltest maximum of 10000, since the mysqltest binary itself rejects
negatives.
Add module-level POD to My::ConfigFactory explaining how a test run's
server configuration is built from a single .cnf template:

- template selection precedence (<testname>.cnf, suite my.cnf, defaults)
  done in mtr_cases.pm, and --defaults-file/--defaults-extra-file
- my.cnf/INI parsing and recursive !include/!includedir merging by
  My::Config, with last-value-wins override semantics
- generated settings applied on top via the rule sets
- related .opt and .combinations files handled elsewhere

Every subroutine in the module is documented too: the public new_config
entry point, the rule engine, the pre-rules, the fix_* value generators
and the post_* checks.

The documentation is standard POD, so it can be rendered with the usual
Perl tools. To read it in the terminal:

  perldoc mysql-test/lib/My/ConfigFactory.pm

To produce a man page, plain text or HTML respectively:

  pod2man mysql-test/lib/My/ConfigFactory.pm | man -l -
  pod2text mysql-test/lib/My/ConfigFactory.pm
  pod2html mysql-test/lib/My/ConfigFactory.pm

No functional change.
@midenok
midenok requested a review from Copilot July 28, 2026 12:15
@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements a set of usability and quality-of-life improvements to the MariaDB Test Runner (MTR) harness, primarily in mysql-test/mariadb-test-run.pl and supporting modules, to make runs more configurable, output less noisy, suite/combination selection more flexible, and test collection faster.

Changes:

  • Add support for reading MTR options from standard MariaDB option files ([mtr] group) and improve invalid-option attribution between config vs command line.
  • Add crash-log and shutdown-warning trimming/stripping options, plus --list-combinations and suite negation (--suite=!NAME).
  • Add --exec-<debugger> wrapping for mysqltest --exec commands, and speed up result-file discovery via directory indexing.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
mysys/my_default.c Improves parameter documentation for option-file loading API.
mysql-test/suite/mtr/t/feat.test New integration test exercising several new MTR features via child MTR runs.
mysql-test/suite/mtr/t/feat.result Expected output for the new feat.test.
mysql-test/suite/mtr/misc/warn.test New helper test to generate real shutdown warnings for trimming tests.
mysql-test/suite/mtr/misc/empty.test New placeholder test used for combination listing.
mysql-test/suite/mtr/misc/empty.combinations New combinations file used by --list-combinations testing.
mysql-test/suite/mtr/misc/crash.test New helper test that intentionally crashes the server to exercise crash-report trimming/stripping.
mysql-test/mariadb-test-run.pl Core implementation: config-file option merging, suite negation, combination selection/listing, trimming/stripping logic, and improved error reporting.
mysql-test/lib/My/Debugger.pm Adds exec debugger templates and exports MYSQLTEST_EXEC_WRAP for wrapping mysqltest --exec.
mysql-test/lib/My/CoreDump.pm Makes “Output from gdb/lldb follows…” hint text optional (controlled by --strip-hints).
mysql-test/lib/My/ConfigFactory.pm Adds substantial internal POD documentation clarifying config templating and rule generation.
mysql-test/lib/My/Config.pm Enhances config parsing: !includedir, whitespace-only lines, unknown ! directives ignored, and cleaner parse error messages.
mysql-test/lib/mtr_cases.pm Adds --combination-select support and speeds up result file discovery by indexing result directories once.
client/mysqltest.cc Injects optional MYSQLTEST_EXEC_WRAP wrapper into --exec commands after leading NAME=VALUE assignments.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread mysql-test/mariadb-test-run.pl
Comment thread mysql-test/lib/My/Config.pm
@midenok
midenok force-pushed the 10.11-midenok-MDEV-30281-mtr branch from 5e60e3c to 5200f82 Compare July 29, 2026 08:30
@midenok
midenok requested a review from Copilot July 29, 2026 08:31

This comment was marked as resolved.

@midenok
midenok force-pushed the 10.11-midenok-MDEV-30281-mtr branch from 5200f82 to 2a1d651 Compare July 29, 2026 11:47
@midenok
midenok requested a review from Copilot July 29, 2026 11:48

This comment was marked as resolved.

midenok added 15 commits July 29, 2026 16:51
MTR options are read from standard MariaDB conf file from [mtr]
section. Command-line options take precedence over configuration file.

There are two variants of config file location:

1. Default behaviour to look for global, server and user configs as
   described in

   https://mariadb.com/kb/en/configuring-mariadb-with-option-files/

2. Custom config location specified by MTR_CONFIG environment
   variable. None of other config files are read in this case.

If there is wrong setting in configuration file MTR fails and displays
the error message. It is clear from the error message from what source
the wrong option comes: config file or command-line. The config file
name and the line number is not printed which is the subject for
further improvement.

Implementation reuses the in-tree My::Config parser instead of adding a
third-party module. A new load_defaults() in mariadb-test-run.pl parses
the config files with My::Config and merges the [mtr] options into the
@argv array for further validation in Getopt::Long:

- Config options are merged into the beginning of the array so that
  command-line options take precedence. They are separated from the
  command-line options by ---end-of-config--- which is used as a marker
  for the source of failed options after Getopt::Long;

- a value-less [mtr] option that actually requires a value would let
  Getopt::Long swallow that marker (or the following option) as its
  value; this is detected and reported instead of failing obscurely, and
  the invalid-option message prints the bare option name;

- Global, server and user config files are searched; only the first
  existing among the global and server locations is used, then the
  user's ~/.my.cnf. A config file can also be specified explicitly via
  MTR_CONFIG, in which case no other file is read;

- Argument-less options are passed as is (My::Config::Option::option()
  renders them without a value);

- My::Config already honours !include; it was extended to also support
  !includedir, to skip whitespace-only lines and to ignore any other
  unknown ! directive instead of failing. A line beginning with '#' is
  treated as a comment, as in any my.cnf, not as an option named '#...'.
  (An earlier version of this patch was based on the CPAN MySQL::Config
  module, which ignored all ! directives.)
…roak

Make every wrong setting in a server config file fail with a
human-friendly message and no Perl stack trace or source-code line.

My::Config now treats parse errors as data errors instead of caller
bugs: each parse failure dies with a clean "file:line: reason" message
naming the config file and the offending line number, and without the
"at FILE line N." Perl location. This also delivers the file name and
line number that were previously missing. The internal misuse/invariant
assertions keep using croak, which is the right tool there.

load_defaults() (in mariadb-test-run.pl) catches that die and re-throws
it through mtr_error(), which flushes output, prints the uniform mtr
error format and exits in a controlled way. This replaces the earlier
fragile approach of stripping the location suffix off a croak with a
regexp.

mariadb-test-run.pl: validate the parallel option value before the
numeric comparison, so a non-numeric or negative value reports a clean
error instead of an "Argument isn't numeric" warning with a code line.
This path is shared with the command line, so it is fixed there too.
Wire the standard MariaDB defaults options into MTR's own option loading,
mirroring libmariadb's get_defaults_options()/my_load_defaults():

  --no-defaults            do not read any option file
  --defaults-file          read [mtr] from this file only
  --defaults-extra-file    read this file in addition (extra-file slot)
  --defaults-group-suffix  also read [mtr<suffix>]
  --print-defaults         print the [mtr] options and exit

--defaults-file and --defaults-extra-file keep their existing MTR meaning
as the server config *template* too: the two consumers read different
groups of the same file - [mysqld]/[client]/... for the template (via
collect_option), [mtr] here. A command-line --defaults-file /
--defaults-extra-file overrides the MTR_CONFIG / MTR_CONFIG_EXTRA
environment variable; --defaults-group-suffix overrides
MARIADB_GROUP_SUFFIX / MYSQL_GROUP_SUFFIX.

An explicitly named file (--defaults-file or $MTR_CONFIG) that does not
exist is an error, matching libmariadb; a missing file in the standard
search order is still skipped silently.

Order of execution in command_line_setup():

  1. get_defaults_options() runs first (before the main GetOptions). It
     consumes the MTR-only options (--no-defaults, --defaults-group-suffix,
     --print-defaults) out of @argv, and *peeks* --defaults-file /
     --defaults-extra-file off a copy of @argv so they stay in @argv for
     the second consumer.

  2. load_defaults() reads the [mtr] group from the selected files and
     prepends the resulting options into @argv, ahead of the
     ---end-of-config--- marker.

  3. My::Debugger::fix_options() adjusts optional-argument options.

  4. The main GetOptions(%options) parses everything - the config options
     (before the marker) and the command line (after it). Command-line
     options come later and therefore take precedence. --defaults-file is
     handed to collect_option for the template. Options still unrecognized
     afterwards are validated in a manual loop that uses the marker to
     report whether a bad option came from the config file or the command
     line.

Getopt::Long is configured "pass_through", so a parse leaves any option it
does not declare in @argv instead of erroring. Because pass_through is on,
step 1 skims off only the defaults options without touching the rest, and
step 4 does the real parse; neither errors on options it does not declare.
--mtr-config-only (-M) makes --defaults-file / --defaults-extra-file be
read for MTR's own [mtr] options only, without also being applied as the
server config template.

Normally those two options serve two consumers of the same file: the
[mtr] group configures MTR itself (load_defaults), while the remaining
groups - [mysqld]/[client]/... - become the test servers' config template
(collect_option). -M opts out of the second use: once load_defaults has
read [mtr], it removes --defaults-file / --defaults-extra-file from @argv,
so the main GetOptions/collect_option never turns them into a template.

The switch may be given on the command line, or set inside the [mtr]
section of the config file itself. In the latter case load_defaults
notices mtr-config-only while reading [mtr] and drops the file options on
the fly, so

    [mtr]
    mtr-config-only

turns any file passed via --defaults-file into an MTR-only config.

get_defaults_options() consumes -M out of @argv like the other MTR-only
defaults options; it is a directive, not passed through to GetOptions.

-M is only relevant for a command-line --defaults-file /
--defaults-extra-file (the dual-use options). The MTR_CONFIG /
MTR_CONFIG_EXTRA environment variables are read only for [mtr] and never
reach collect_option, so they are mtr-config-only already and -M is a no-op
for them.
Extend the --suite[s] option so a suite name prefixed with '!' excludes
that suite instead of adding it. This makes "run everything except X"
easy and lets a config-file suite list be trimmed on the command line.

Semantics:

  --suites=A,B        run suites A and B (as before)
  --suites=!A         run the default set minus A
  --suites=A,B,!B     run A (a '!' name is removed from the positive names)
  [mtr] suites=A,B
    + --suites=!B      run A (command-line exclusion trims the file set)

Rules:

  - If any plain (positive) names are given, they are the base set;
    otherwise the base is the default suite set.
  - Every '!'-prefixed name is then removed from the base.
  - Names accumulate across the [mtr] config file and the command line,
    so a '!' exclusion on the command line applies to the set configured
    in the file (see below).

To make the cross-source case work, --suites now accumulates its values
into @opt_suites instead of a last-wins scalar: the option handler pushes
each comma-separated value, so the [mtr] config file value and the
command-line value are both collected. The positive/negative resolution
then runs once in main(), producing the final suite list.

Exclusion matching ignores the "-<overlay>" suffix used in the default
list (e.g. "rpl-"), so --suites=!rpl removes "rpl-", and slashes are
preserved (--suites=!compat/oracle works).
Add --combination-select=N (short alias -c) to run only a single
combination from a .combinations file instead of every one.

N is the position of the combination in the file, counting the []
sections in file order:

  1   first combination      -1  last combination
  2   second combination     -2  last but one
  ...                        ...

So --combination-select=-1 runs only the last combination, -c 2 runs
the second one, etc. N must be a non-zero integer; 0, a non-integer, or
a value outside the range of a given file is an error.

The integer format is validated up front in command_line_setup(), so a
bad value is rejected even for tests that have no .combinations file. The
range and the selection itself are applied in combinations_from_file(),
which is the single point through which every .combinations file is read
(both the suite-level "combinations" file and per-test
"<test>.combinations"), so it always follows the [] section order of the
file. When a test draws combinations from more than one file, N selects
within each file.

It is ignored when --combination is given (that already replaces the
.combinations files with command-line combinations).
Add --list-combinations (alias --lc) which, for the specified test(s),
prints the available combinations in the selectable "test,combination"
form and exits without running anything.

For each collected test it prints a summary line followed by one line
per combination, e.g.:

  $ mtr --list-combinations versioning.foreign
  Combinations for versioning.foreign: timestamp,trx_id
  versioning.foreign,timestamp
  versioning.foreign,trx_id

Each printed line is exactly what you pass back to mtr to run that one
combination (it round-trips). When a test draws combinations from more
than one .combinations file (several dimensions), the lines list the
full product, e.g. "test,dim1_value,dim2_value".
A crash report contains, besides the actual server log and backtrace,
several explanatory "hint" paragraphs - bug-reporting boilerplate,
instructions for producing a better stack trace, "Attempting backtrace",
"Output from gdb follows", etc. They are useful once but noise for
someone who reads these reports all day.

Add --strip-hints (off by default). When given, these hints are omitted
and only the real content is kept.

Three sources are handled:

 - The paragraphs the server writes to its error log (via the crash
   signal handler) are dropped from the "Server log from this test"
   excerpt by strip_crash_hints(). Each hint is a paragraph running from
   a recognizable opener line to the next blank line, so trimming is
   robust across server-version wording changes; the surrounding data
   (CURRENT_TEST, the signal line, "Server version", the backtrace,
   thread pointer, ...) is kept.

 - My::CoreDump prints its own "Output from gdb/lldb follows ..." header;
   it is suppressed too, leaving just the debugger output.

 - mariadb-test-run.pl's own end-of-run boilerplate in mtr_report_stats()
   ("The log files in var/log may give you some hint ..." and the
   MariaDB bug-tracker URL) is skipped as well.

$opt_strip_hints is declared with "our" so My::CoreDump and
mtr_report.pm can read it as $::opt_strip_hints.
On a crash the server writes a "Resource Limits" table (read from
/proc/self/limits) to its error log, which MTR echoes in the crash
report. Like the bug-reporting hints it is rarely useful when reading
these reports routinely.

Add --strip-limits (off by default). When given, the table is dropped
from the "Server log from this test" excerpt by strip_resource_limits().

Unlike a hint paragraph the table has no trailing blank line - it runs
straight into the next section ("Core pattern:", ...). So the filter
removes the opener line (matching both "Resource Limits:" and the newer
"Resource Limits (excludes unlimited resources):"), the
"Limit ... Soft Limit ..." header and the "Max ..." rows, and stops at
the first line that is neither, keeping the surrounding data.

This is independent of --strip-hints.
On a crash the server writes its own stack backtrace (my_print_stacktrace)
to the error log, which MTR echoes in the crash report. When a core file
is available the gdb/lldb backtrace from My::CoreDump is more useful, so
this server-side report is often just noise.

Add --strip-backtrace (off by default). When given, it is dropped from
the "Server log from this test" excerpt by strip_backtrace(). The
gdb/lldb backtrace from a core file is a separate thing and is not
affected.

strip_backtrace() treats the backtrace as a bounded block rather than
matching lines everywhere, so a line outside a backtrace that merely ends
in an [0x...] address is kept:

  - a "Thread pointer:" or "Attempting backtrace" line starts a block;
  - inside a block every recognisable backtrace line is dropped: the
    intro prose, the thread pointer, blank lines, a "stack_bottom" or
    "Stack range" header, frame lines (both "...[0x...]" symbol frames
    and the bare "0x..." addresses printed by the frame-pointer walker)
    and the interleaved addr2line / my_addr_resolve diagnostics;
  - the first line that is not recognisable backtrace content ends the
    block and is kept, so pass-through resumes and a later backtrace (or
    the rest of the log) survives.

Because anything unrecognised ends the block rather than being dropped, a
backtrace with no "stack_bottom" line and no bracketed frames - as the
frame-pointer-walker and aborted-backtrace builds produce - does not
swallow the remainder of the log.

Also add --strip-log, a convenience alias that turns on all three of
--strip-hints, --strip-limits and --strip-backtrace at once.

All stripping runs before the --head-log/--tail-log trimming, so those
line counts apply to the already-stripped log.
Run a child mariadb-test-run.pl on misc.crash (SIGSEGV) and misc.warn
(system-versioned SYSTEM_TIME LIMIT overflow) and check that the crash and
shutdown-warnings reports are trimmed by --head-log / --tail-log / --strip-log
and --tail-warnings.  Also exercise --list-combinations and --suite negation.

The child's volatile output (pids, timestamps, addresses, an unbounded
backtrace, ...) is reduced to a deterministic skeleton with include/grep.inc
and normalized with --replace_regex: the two anchor backtrace frames
(my_print_stacktrace, main) are kept while the variable middle frames and the
per-line values are cut down to "<...cut...>".  The test is gated behind
--big-test.
New debugger options run every mysqltest '--exec' command line under a
wrapper (rr record, or gdb --args in an xterm), so external tools invoked
by tests (myisampack, myisamchk, ...) can be traced or debugged without
naming them.

My::Debugger: each debugger with an 'exec' template gets an --exec-<dbg>
option auto-registered.  pre_setup() detects the requested one, runs its
one-time 'pre' hook, and exports MYSQLTEST_EXEC_WRAP (plus _RR_TRACE_DIR
for rr, and an xterm wrapper for terminal debuggers like gdb).

do_exec(): when MYSQLTEST_EXEC_WRAP is set it is injected into the command
line, after any leading shell NAME=VALUE assignments (quotes and
backslashes honoured), so the tool - not the assignment word - is what
gets wrapped.  The implicit /bin/sh -c that popen() supplies provides the
shell layer.
collect_one_test_case() located each test's .result/.rdiff files with a
per-test glob:

  for (<{$resdirglob}/$tname*.{rdiff,result}>) { ... }

Because of the "$tname*" wildcard, glob had to opendir and read the whole
result directory on every call, and the "{rdiff,result}" brace made it do
so twice (once per extension).  Run once per test, this is O(tests *
dirsize) - the suite's result directory is re-read ~2x for every test in
the suite.  Profiling the collection of all default suites showed this
single line accounting for 8.3s of the 13.1s phase (63%), and the phase
issuing 408k stat() calls (339k of them ENOENT).

Read each result directory once instead, indexing its .result/.rdiff
files by base test name (the leading run of characters before the first
'.' or ','), and look a test's files up in that cache.  The "$tname*"
glob was only a coarse prefilter anyway - the existing regex
"^$tname((?:,combo)*)\.(rdiff|result)$" does the real matching - so
indexing by base name yields exactly the same candidate set.

Stats (collecting all default suites, 6548 tests, warm cache):

  collection time         13.1s  ->  4.5s   (~2.9x)
  directory-open syscalls  23026 ->  5284

The collected test list (result_file, base_result, skip and comment for
every test) is byte-for-byte identical before and after.
The terminal emulator for interactive debuggers (--gdb, --exec-gdb, ...)
was hard-coded to xterm.  It is now configurable via the --terminal
option or the MTR_TERM environment variable (the option takes
precedence).  The template understands two placeholders, {title} (window
title) and {command} (the debugger invocation, expanded as separate argv
words); the default "xterm -title {title} -e {command}" reproduces the
previous behaviour.

My::Debugger::term_argv() expands the template into an argv list for the
mysqld/client/boot debuggers; the same template drives the --exec-<dbg>
shell-string wrapper, where {command} must be last.

For example, debug the bootstrap server for main.1st under gdb in a
KDE Konsole window:

  mtr --boot-gdb --terminal='konsole -p tabtitle={title} -e {command}' main.1st
--exit-line=N stops a test before the command at line N of the test
file, exactly as if an --exit directive were placed there.  Handy for
bisecting where a test starts to misbehave, and for debugging a test
without extracting a standalone test case: record the run under rr
(--rr), then reverse-replay from the end of the trace straight to the
SQL command of interest.

mariadb-test-run.pl gains --exit-line (-l) and forwards it to mysqltest.
The option is global: it applies to every test in the run, each stopping
at line N of its own test file (so N is per-file).  A test whose file has
fewer than N lines is unaffected and runs to completion.

mysqltest gains the --exit-line (-l) option: after reading each command,
if we are in the top-level test file (cur_file == file_stack) and the
command starts at or past the requested line, it aborts like Q_EXIT.
Gating to the main file keeps line numbers of sourced includes from
triggering it.

suite/mtr/feat exercises it by running a child mtr with --exit-line=5 on
main.1st (line 4 "show databases;", line 5 "show tables in mysql;"): the
run stops before line 5, so the recorded result's tables section is
missing and the test fails with a length mismatch - proving the exit
landed exactly at the requested line.  The child's whole output is kept
(no grep); volatile banner/footer lines are cut by prefix via $NORM_RUN.
@midenok
midenok force-pushed the 10.11-midenok-MDEV-30281-mtr branch from 2a1d651 to 723252a Compare July 29, 2026 13:56
@midenok
midenok requested a review from Copilot July 29, 2026 13:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants