/
bt_cores
executable file
·157 lines (130 loc) · 3.6 KB
/
bt_cores
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
#! /usr/bin/perl
use strict;
use warnings;
use Digest::MD5 qw(md5_hex);
use File::Basename qw(basename dirname);
use File::Temp qw(tempdir);
use Getopt::Long;
use Pod::Usage;
our $VERSION = 0.02;
my ($opt_help, $opt_version, $opt_unlink_core);
my $opt_delay = 10;
GetOptions(
help => \$opt_help,
version => \$opt_version,
'unlink-core' => \$opt_unlink_core,
) or pod2usage(1);
if ($opt_help) {
pod2usage(0);
} elsif ($opt_version) {
print "$VERSION\n";
exit 0;
}
if ($^O =~ /^darwin$/i) {
chdir '/cores'
or die "failed to chdir to /cores, the directory where OSX saves core files:$!";
} elsif ($^O =~ /^linux$/i) {
my $core_pattern = do {
open my $fh, '<', '/proc/sys/kernel/core_pattern'
or die "failed to open file:/proc/sys/kernel/core_pattern:$!";
<$fh>;
};
chomp $core_pattern;
die "/proc/sys/kernel/core_pattern should be set to /<core-dir>/core.%p"
unless basename($core_pattern) =~ m|core\.%p$|;
if ($core_pattern =~ m|^/|) {
my $dir = dirname $core_pattern;
chdir $dir
or die "failed to chdir to dir:$dir:$!";
}
} else {
warn "unknown OS, bt_cores may not work as expected";
}
my $tempdir = tempdir(CLEANUP => 1);
{ # prepare gdb command file (gdb on ubuntu 9 does not seem to read from STDIN)
open my $fh, '>', "$tempdir/gdb.cmd"
or die "failed to open file:$tempdir/gdb.cmd:$!";
print $fh "thread apply all bt\n";
close $fh;
}
while (1) {
# build list of files (with checksum)
my %core_files = map {
$_ => md5_file($_)
} grep {
! -e bt_filename($_)
} <core.*>;
sleep 5;
next unless %core_files;
for my $core_file (sort keys %core_files) {
# skip if the checksum of the core file has changed
next unless $core_files{$core_file} eq md5_file($core_file);
# take backtrace
print "taking backtrace of core file:$core_file\n";
my $gdb_out = call_gdb("-x $tempdir/gdb.cmd -c $core_file");
if ($^O =~ /^linux$/i
&& $gdb_out =~ m|^Core was generated by `(.*?)'\.$|mi) {
# need to find prog on linux
my $prog = $1;
if (-e $prog) {
$gdb_out = call_gdb("-x $tempdir/gdb.cmd -c $core_file $prog");
} else {
$gdb_out = "could not find program: $prog\n\n$gdb_out";
}
}
# save backtrace
open my $fh, '>', bt_filename($core_file)
or die "failed to open file:@{[bt_filename($core_file)]}:$!";
print $fh $gdb_out;
close $fh;
# unlink if necessary
if ($opt_unlink_core) {
unlink $core_file
or die "failed to unlink core file:$core_file:$!";
}
}
}
sub bt_filename {
my $fn = shift;
$fn =~ s/^core/bt/;
$fn;
}
sub call_gdb {
my $args = shift;
open(
my $fh,
'-|',
"gdb -batch $args 2>&1",
) or die "failed to invoke gdb:$!";
my $out = join '', <$fh>;
close $fh;
$out .= "gdb exitted with exit code:$?"
if $?;
$out;
}
sub md5_file {
my $fn = shift;
open my $fh, '<', $fn
or die "failed to open $fn:$!";
my $md5 = Digest::MD5->new;
$md5->addfile($fh);
close $fh;
$md5->hexdigest;
}
__END__
=head1 NAME
bt_cores - a daemon that takes stack backtrace of core files automatically
=head1 SYNOPSIS
# setup (as root)
echo /tmp/core.%p > /proc/sys/kernel/core_pattern
# run the daemon (converts /tmp/core.<pid> to /tmp/bt.<pid>)
bt_cores -u
=over 4
=item -u, --unlink-core
Removes the core file after taking backtrace.
=back
=head1 DESCRIPTION
Bt_cores is a daemon that waits for core files (that match pattern "core.<pid>") to appear in current directory, and takes backtraces of the images.
=head1 AUTHOR
Kazuho Oku
=cut