From f5e7f1271f17d9fabb65ea6bfeec5c62b18afd46 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Tue, 21 Jul 2026 20:45:09 +0300 Subject: [PATCH 01/19] MDEV-30281 Comment cleanup for my_load_defaults() 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. --- mysys/my_default.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/mysys/my_default.c b/mysys/my_default.c index 0b50235aa695d..0c7eeeba95eea 100644 --- a/mysys/my_default.c +++ b/mysys/my_default.c @@ -370,14 +370,18 @@ int load_defaults(const char *conf_file, const char **groups, SYNOPSIS my_load_defaults() - conf_file Basename for configuration file to search for. - If this is a path, then only this file is read. - groups Which [group] entrys to read. - Points to an null terminated array of pointers - argc Pointer to argc of original program - argv Pointer to argv of original program - default_directories Pointer to a location where a pointer to the list - of default directories will be stored + conf_file [in] Basename of the option file to look for (e.g. + "my"), searched for in each standard directory. + If it instead contains a directory part (i.e. + it is a path), only that single file is read. + groups [in] Which [group] entries to read. + Points to a null terminated array of pointers. + argc [in,out] Pointer to argc: original count in, count of the + merged argument vector out. + argv [in,out] Pointer to argv: original vector in, new merged + vector out. Free it with free_defaults(). + default_directories [out] Optional; may be NULL. If not NULL, receives a + pointer to the list of searched directories. IMPLEMENTATION From 5b160ac3935e964b017c674dc1b92db4c05beac2 Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 20 Jul 2026 19:50:58 +0300 Subject: [PATCH 02/19] MDEV-30281 MTR: cut server error log and shutdown warnings 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. --- mysql-test/mariadb-test-run.pl | 96 +++++++++++++++++++++++++++++++--- 1 file changed, 89 insertions(+), 7 deletions(-) diff --git a/mysql-test/mariadb-test-run.pl b/mysql-test/mariadb-test-run.pl index 7461c2aa0d0df..68f9ac732822c 100755 --- a/mysql-test/mariadb-test-run.pl +++ b/mysql-test/mariadb-test-run.pl @@ -240,6 +240,10 @@ END my $opt_stress; my $opt_tail_lines= 20; +my $opt_head_log; +my $opt_tail_log; +my $opt_head_warnings; +my $opt_tail_warnings; my $opt_dry_run; @@ -1271,7 +1275,18 @@ sub command_line_setup { 'report-times' => \$opt_report_times, 'result-file' => \$opt_resfile, 'stress=s' => \$opt_stress, + 'head=i' => sub { $opt_head_log= $opt_head_warnings= + $_[1]; + $opt_tail_lines= $opt_tail_log= + $opt_tail_warnings= 0 }, + 'tail=i' => sub { $opt_tail_lines= $opt_tail_log= + $opt_tail_warnings= $_[1]; + $opt_head_log= $opt_head_warnings= 0 }, 'tail-lines=i' => \$opt_tail_lines, + 'head-log=i' => \$opt_head_log, + 'tail-log=i' => \$opt_tail_log, + 'head-warnings=i' => \$opt_head_warnings, + 'tail-warnings=i' => \$opt_tail_warnings, 'dry-run' => \$opt_dry_run, 'help|h' => \$opt_usage, @@ -1299,9 +1314,13 @@ sub command_line_setup { report_option('verbose', $opt_verbose); } - # Negative values aren't meaningful on integer options + # Negative values aren't meaningful on integer options, except the tail-* + # options where a negative value means "everything". + my %tail_opt= map { $_ => 1 } + qw(head=i tail=i tail-lines=i head-log=i tail-log=i head-warnings=i tail-warnings=i); foreach(grep(/=i$/, keys %options)) { + next if $tail_opt{$_}; if (defined ${$options{$_}} && do { no warnings "numeric"; int ${$options{$_}} < 0}) { @@ -1310,6 +1329,32 @@ sub command_line_setup { } } + # mysqltest caps --tail-lines at 10000 and rejects negatives, so map a + # negative ("everything") to that maximum. + $opt_tail_lines= 10000 if $opt_tail_lines < 0; + + # --head-log/--tail-log trim the server error log in a crash report to its + # first/last lines. With neither given the whole log is kept; giving one + # alone switches the other end off. + if (defined $opt_head_log || defined $opt_tail_log) { + $opt_head_log //= 0; + $opt_tail_log //= 0; + } + else { + $opt_head_log= 0; + $opt_tail_log= -1; + } + + # Same for --head-warnings/--tail-warnings on the shutdown-warnings report. + if (defined $opt_head_warnings || defined $opt_tail_warnings) { + $opt_head_warnings //= 0; + $opt_tail_warnings //= 0; + } + else { + $opt_head_warnings= 0; + $opt_tail_warnings= -1; + } + # Find the absolute path to the test directory $glob_mysql_test_dir= cwd(); if ($glob_mysql_test_dir =~ / /) @@ -4392,13 +4437,32 @@ ($$) # Return as a single string # +# Trim an excerpt (arrayref of lines) to its first $head and last $tail lines. +# For each of $head/$tail: <0 = unbounded, 0 = none, N = that many lines. +# A line is kept if it falls within the first $head or the last $tail; the +# omitted middle is replaced with a "< snip M lines >" marker. +sub splice_lines { + my ($lines, $head, $tail)= @_; + my $total= @$lines; + return @$lines if $head < 0 || $tail < 0 || $head + $tail >= $total; + return () if $head == 0 && $tail == 0; + my $snipped= $total - $head - $tail; + return (@$lines[0 .. $head - 1], + "< snip $snipped lines >\n", + @$lines[$total - $tail .. $total - 1]); +} + sub get_log_from_proc ($$) { my ($proc, $name)= @_; my $srv_log= ""; + return $srv_log if $opt_head_log == 0 && $opt_tail_log == 0; + foreach my $mysqld (all_servers()) { if ($mysqld->{proc} eq $proc) { - my @srv_lines= extract_server_log($mysqld->if_exist('log-error'), $name); + my @srv_lines= + splice_lines([extract_server_log($mysqld->if_exist('log-error'), $name)], + $opt_head_log, $opt_tail_log); $srv_log= "\nServer log from this test:\n" . "----------SERVER LOG START-----------\n". join ("", @srv_lines) . "----------SERVER LOG END-------------\n"; @@ -4782,18 +4846,19 @@ ($) sub check_warnings_post_shutdown { my ($server_socket)= @_; my $testname_hash= { }; - my $report= ''; + my @match_all; foreach my $mysqld ( mysqlds()) { my ($testlist, $match_lines)= extract_warning_lines($mysqld->value('log-error'), 1); $testname_hash->{$_}= 1 for @$testlist; - $report.= join('', @$match_lines); + push @match_all, @$match_lines; } my @warning_tests= keys(%$testname_hash); if (@warning_tests) { my $fake_test= My::Test->new(testnames => \@warning_tests); - $fake_test->{'warnings'}= $report; + $fake_test->{'warnings'}= + join('', splice_lines(\@match_all, $opt_head_warnings, $opt_tail_warnings)); $fake_test->write_test($server_socket, 'WARNINGS'); } } @@ -6101,8 +6166,25 @@ ($) stress=ARGS Run stress test, providing options to mysql-stress-test.pl. Options are separated by comma. xml-report= Output jUnit xml file of the results. - tail-lines=N Number of lines of the result to include in a failure - report. + tail-lines=N Number of lines of the mysqltest result to include in + a failure report. 0 disables it, negative includes all + (max 10000); default 20. + head=N Shortcut to set the head-* options to N and the tail-* + options to 0. + tail=N Shortcut to set the tail-* options to N and the head-* + options to 0. + head-log=N Keep the first N lines of the server error log in a + crash report (0 none, negative all). With neither + head-log nor tail-log given the whole log is kept; + giving one alone drops the opposite end, giving both + keeps both ends with the middle snipped. + tail-log=N Like head-log but keeps the last N lines. + head-warnings=N Keep the first N suspicious lines of the shutdown- + warnings report (0 none, negative all). With neither + head-warnings nor tail-warnings given the whole report + is kept; giving one alone drops the opposite end, + giving both keeps both ends with the middle snipped. + tail-warnings=N Like head-warnings but keeps the last N lines. Some options that control enabling a feature for normal test runs, can be turned off by prepending 'no' to the option, e.g. --notimer. From 13e231a83aea02f3107b0051df9e6b2dde2c7e1e Mon Sep 17 00:00:00 2001 From: Aleksey Midenkov Date: Mon, 20 Jul 2026 20:54:16 +0300 Subject: [PATCH 03/19] MDEV-30281 MTR conf: document My::ConfigFactory config file handling 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 (.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. --- mysql-test/lib/My/ConfigFactory.pm | 456 ++++++++++++++++++++++++++++- 1 file changed, 454 insertions(+), 2 deletions(-) diff --git a/mysql-test/lib/My/ConfigFactory.pm b/mysql-test/lib/My/ConfigFactory.pm index 5a5b691998ca5..7b40779bb3676 100644 --- a/mysql-test/lib/My/ConfigFactory.pm +++ b/mysql-test/lib/My/ConfigFactory.pm @@ -27,11 +27,214 @@ use My::Platform; use File::Basename; +=head1 NAME + +My::ConfigFactory - build a runtime server configuration for a test run + +=head1 SYNOPSIS + + use My::ConfigFactory; + + my $config = My::ConfigFactory->new_config({ + basedir => $basedir, + vardir => $opt_vardir, + baseport => $baseport, + template_path => "include/default_my.cnf", + # ... other args ... + }); + + # $config is a My::Config object, later written out as var/my.cnf + +=head1 DESCRIPTION + +C turns a single my.cnf-format B