Skip to content

Commit e9be542

Browse files
committed
MDEV-28931 MTR prints detailed stack trace unconditionally
66832e3 introduced change that prints core dumps in very detailed format. That's completely out of user-friendliness but serves as a measure for debugging hard-reproducible bugs. The proper way to implement this: 1. it must be controlled by command-line and environment variable; 2. detailed traces must be default for buildbots only, for user invocations normal stack traces should be printed. Options for control are: MTR_PRINT_CORE and --print-core that accept the following values: no Don't print core short Print stack trace of failed thread medium Print stack traces of all threads detailed Print all stack traces with debug context custom:<code> Use debugger commands <code> to print stack trace Default setting is: short (see env_or_default() call in pre_setup()) For environment variable wrong values are silently ignored (falls back to default setting, see env_or_default()). Command-line option --print-core (or -C) overrides environment variable. Its default value is 'short' if not specified explicitly (same env_or_default() call in pre_setup()). Explicit values are checked for validity. --print-method option can specify by which debugger we print cores. For Windows there is only one choice: cdb. For Unix the values are: gdb, dbx, lldb, auto. Default value is: auto In 'auto' we try to use all possible debuggers until success.
1 parent 220fb67 commit e9be542

File tree

2 files changed

+209
-35
lines changed

2 files changed

+209
-35
lines changed

mysql-test/lib/My/CoreDump.pm

Lines changed: 204 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,141 @@ package My::CoreDump;
1919
use strict;
2020
use Carp;
2121
use My::Platform;
22+
use Text::Wrap;
23+
use Data::Dumper;
2224

2325
use File::Temp qw/ tempfile tempdir /;
2426
use mtr_results;
27+
use mtr_report;
28+
29+
my %opts;
30+
my %config;
31+
my $help = "\n\nOptions for printing core dumps\n\n";
32+
33+
sub register_opt($$$) {
34+
my ($name, $format, $msg)= @_;
35+
my @names= split(/\|/, $name);
36+
my $option_name= $names[0];
37+
$option_name=~ s/-/_/;
38+
$opts{$name. $format}= \$config{$option_name};
39+
$help.= wrap(sprintf(" %-23s", join(', ', @names)), ' 'x25, "$msg\n");
40+
}
41+
42+
# To preserve order we use array instead of hash
43+
my @print_formats= (
44+
short => {
45+
description => "Failing stack trace",
46+
codes => {}
47+
},
48+
medium => {
49+
description => "All stack traces",
50+
codes => {}
51+
},
52+
detailed => {
53+
description => "All stack traces with debug context",
54+
codes => {}
55+
},
56+
custom => {
57+
description => "Custom debugger script for printing stack"
58+
},
59+
# 'no' must be last (check generated help)
60+
no => {
61+
description => "Skip stack trace printing"
62+
}
63+
);
64+
65+
# TODO: make class for each {method, get_code}
66+
my @print_methods= (IS_WINDOWS) ? (cdb => { method => \&_cdb }) : (
67+
gdb => {
68+
method => \&_gdb,
69+
get_code => \&_gdb_format,
70+
},
71+
dbx => {
72+
method => \&_dbx
73+
},
74+
lldb => {
75+
method => \&_lldb
76+
},
77+
# 'auto' must be last (check generated help)
78+
auto => {
79+
method => \&_auto
80+
}
81+
);
82+
83+
# But we also use hash
84+
my %print_formats= @print_formats;
85+
my %print_methods= @print_methods;
86+
87+
# and scalar
88+
my $x= 0;
89+
my $print_formats= join(', ', grep { ++$x % 2 } @print_formats);
90+
$x= 0;
91+
my $print_methods= join(', ', grep { ++$x % 2 } @print_methods);
92+
93+
# Fill 'short' and 'detailed' formats per each print_method
94+
# that has interface for that
95+
for my $f (keys %print_formats)
96+
{
97+
next unless exists $print_formats{$f}->{codes};
98+
for my $m (keys %print_methods)
99+
{
100+
next unless exists $print_methods{$m}->{get_code};
101+
# That calls f.ex. _gdb_format('short')
102+
# and assigns { gdb => value-of-_gdb_format } into $print_formats{short}->{format}:
103+
$print_formats{$f}->{codes}->{$m}= $print_methods{$m}->{get_code}->($f);
104+
}
105+
}
106+
107+
register_opt('print-core|C', ':s',
108+
"Print core dump format: ". $print_formats. " (for not printing cores). ".
109+
"Defaults to value of MTR_PRINT_CORE or 'short'");
110+
if (!IS_WINDOWS)
111+
{
112+
register_opt('print-method', '=s',
113+
"Print core method: ". join(', ', $print_methods). " (try each method until success). ".
114+
"Defaults to 'auto'");
115+
}
116+
117+
sub options() { %opts }
118+
sub help() { $help }
119+
120+
121+
sub env_or_default($$) {
122+
my ($default, $env)= @_;
123+
if (exists $ENV{$env}) {
124+
my $f= $ENV{$env};
125+
$f= 'custom'
126+
if $f =~ m/^custom:/;
127+
return $ENV{$env}
128+
if exists $print_formats{$f};
129+
mtr_verbose("$env value ignored: $ENV{$env}");
130+
}
131+
return $default;
132+
}
133+
134+
sub pre_setup() {
135+
$config{print_core}= env_or_default('short', 'MTR_PRINT_CORE')
136+
if not defined $config{print_core};
137+
$config{print_method}= (IS_WINDOWS) ? 'cdb' : 'auto'
138+
if not defined $config{print_method};
139+
# If the user has specified 'custom' we fill appropriate print_format
140+
# and that will be used automatically
141+
# Note: this can assign 'custom' to method 'auto'.
142+
if ($config{print_core} =~ m/^custom:(.+)$/) {
143+
$config{print_core}= 'custom';
144+
$print_formats{'custom'}= {
145+
$config{print_method} => $1
146+
}
147+
}
148+
mtr_error "Wrong value for --print-core: $config{print_core}"
149+
if not exists $print_formats{$config{print_core}};
150+
mtr_error "Wrong value for --print-method: $config{print_method}"
151+
if not exists $print_methods{$config{print_method}};
152+
153+
mtr_debug(Data::Dumper->Dump(
154+
[\%config, \%print_formats, \%print_methods],
155+
[qw(config print_formats print_methods)]));
156+
}
25157

26158
my $hint_mysqld; # Last resort guess for executable path
27159

@@ -50,8 +182,38 @@ sub _verify_binpath {
50182
return $binpath;
51183
}
52184

185+
186+
# Returns GDB code according to specified format
187+
188+
# Note: this is like simple hash, separate interface was made
189+
# in advance for implementing below TODO
190+
191+
# TODO: _gdb_format() and _gdb() should be separate class
192+
# (like the other printing methods)
193+
194+
sub _gdb_format($) {
195+
my ($format)= @_;
196+
my %formats= (
197+
short => "bt\n",
198+
medium => "thread apply all bt\n",
199+
detailed =>
200+
"bt\n".
201+
"set print sevenbit on\n".
202+
"set print static-members off\n".
203+
"set print frame-arguments all\n".
204+
"thread apply all bt full\n".
205+
"quit\n"
206+
);
207+
confess "Unknown format: ". $format
208+
unless exists $formats{$format};
209+
return $formats{$format};
210+
}
211+
212+
53213
sub _gdb {
54-
my ($core_name)= @_;
214+
my ($core_name, $code)= @_;
215+
confess "Undefined format"
216+
unless defined $code;
55217

56218
# Check that gdb exists
57219
`gdb --version`;
@@ -61,7 +223,7 @@ sub _gdb {
61223
}
62224

63225
if (-f $core_name) {
64-
print "\nTrying 'gdb' to get a backtrace from coredump $core_name\n";
226+
mtr_verbose("Trying 'gdb' to get a backtrace from coredump $core_name");
65227
} else {
66228
print "\nCoredump $core_name does not exist, cannot run 'gdb'\n";
67229
return;
@@ -76,13 +238,7 @@ sub _gdb {
76238

77239
# Create tempfile containing gdb commands
78240
my ($tmp, $tmp_name) = tempfile();
79-
print $tmp
80-
"bt\n",
81-
"set print sevenbit on\n",
82-
"set print static-members off\n",
83-
"set print frame-arguments all\n",
84-
"thread apply all bt full\n",
85-
"quit\n";
241+
print $tmp $code;
86242
close $tmp or die "Error closing $tmp_name: $!";
87243

88244
# Run gdb
@@ -105,7 +261,7 @@ EOF
105261

106262

107263
sub _dbx {
108-
my ($core_name)= @_;
264+
my ($core_name, $format)= @_;
109265

110266
print "\nTrying 'dbx' to get a backtrace\n";
111267

@@ -167,7 +323,7 @@ sub cdb_check {
167323

168324

169325
sub _cdb {
170-
my ($core_name)= @_;
326+
my ($core_name, $format)= @_;
171327
print "\nTrying 'cdb' to get a backtrace\n";
172328
return unless -f $core_name;
173329

@@ -304,32 +460,47 @@ EOF
304460
}
305461

306462

463+
sub _auto
464+
{
465+
my ($core_name, $code, $rest)= @_;
466+
# We use ordered array @print_methods and omit auto itself
467+
my @valid_methods= @print_methods[0 .. $#print_methods - 2];
468+
my $x= 0;
469+
my @methods= grep { ++$x % 2} @valid_methods;
470+
my $f= $config{print_core};
471+
foreach my $m (@methods)
472+
{
473+
my $debugger= $print_methods{$m};
474+
confess "Broken @print_methods"
475+
if $debugger->{method} == \&_auto;
476+
# If we didn't find format for 'auto' (that is only possible for 'custom')
477+
# we get format for specific debugger
478+
if (not defined $code && defined $print_formats{$f} and
479+
exists $print_formats{$f}->{codes}->{$m})
480+
{
481+
$code= $print_formats{$f}->{codes}->{$m};
482+
}
483+
mtr_verbose2("Trying to print with method ${m}:${f}");
484+
if ($debugger->{method}->($core_name, $code)) {
485+
return;
486+
}
487+
}
488+
}
489+
307490

308491
sub show {
309492
my ($class, $core_name, $exe_mysqld, $parallel)= @_;
310-
$hint_mysqld= $exe_mysqld;
311-
312-
# On Windows, rely on cdb to be there...
313-
if (IS_WINDOWS)
314-
{
315-
_cdb($core_name);
316-
return;
317-
}
318-
319-
my @debuggers =
320-
(
321-
\&_gdb,
322-
\&_dbx,
323-
\&_lldb,
324-
# TODO...
325-
);
326-
327-
# Try debuggers until one succeeds
328-
329-
foreach my $debugger (@debuggers){
330-
if ($debugger->($core_name)){
331-
return;
493+
if ($config{print_core} ne 'no') {
494+
my $f= $config{print_core};
495+
my $m= $config{print_method};
496+
my $code= undef;
497+
if (exists $print_formats{$f}->{codes} and
498+
exists $print_formats{$f}->{codes}->{$m}) {
499+
$code= $print_formats{$f}->{codes}->{$m};
332500
}
501+
mtr_verbose2("Printing core with method ${m}:${f}");
502+
mtr_debug("code: ${code}");
503+
$print_methods{$m}->{method}->($core_name, $code);
333504
}
334505
return;
335506
}

mysql-test/mysql-test-run.pl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,7 +1346,8 @@ sub command_line_setup {
13461346
'skip-test-list=s' => \@opt_skip_test_list,
13471347
'xml-report=s' => \$opt_xml_report,
13481348

1349-
My::Debugger::options()
1349+
My::Debugger::options(),
1350+
My::CoreDump::options()
13501351
);
13511352

13521353
# fix options (that take an optional argument and *only* after = sign
@@ -1761,6 +1762,8 @@ sub command_line_setup {
17611762
$opt_debug= 1;
17621763
$debug_d= "d,query,info,error,enter,exit";
17631764
}
1765+
1766+
My::CoreDump::pre_setup();
17641767
}
17651768

17661769

@@ -5764,7 +5767,7 @@ ($)
57645767

57655768
local $"= ','; # for @DEFAULT_SUITES below
57665769

5767-
print <<HERE . My::Debugger::help() . <<HERE;
5770+
print <<HERE . My::Debugger::help() . My::CoreDump::help() . <<HERE;
57685771
57695772
$0 [ OPTIONS ] [ TESTCASE ]
57705773

0 commit comments

Comments
 (0)