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 158 159 160 161 162 163 164 165 166 167 168 169 170 171
|
#!/usr/bin/perl
#
# This script accumulates the execution paths of all calls to kmalloc
# in the kernel. On Ctrl-C (or exit of the command provided by -c option),
# it sorts, filters and displays them on
# stdout.
#
# The -e (exclude) option can be used to specify a comma-separated list
# - any stack with contents matching any of the items in the list will
# be excluded from the output.
#
# The -m (min) option can be used to specify the minimum number of
# occurrences a stack needs to be included in the output.
#
# The -t (top) option can be used to specify printing only the
# top N stack traces.
#
# The -c (command) option runs starts the command and script
# only runs for the duration of the command.
#
# The -o (options) option passes the options to systemap.
#
# Usage: ./kmalloc-top [-m min] [-e exclude list] [-t top_n] [-c command]
# [-o options]
# Ctrl-c
use Getopt::Std;
my $kmalloc_stacks;
my $total_kmallocs;
my $filtered_kmallocs;
my $sorted_stacks;
my $first_n = 1000000000;
my $min_count = 1;
my $exclude;
my $options;
$SIG{INT} = \&sigint_handler;
getopts('c:e:m:t:o:');
if ($opt_e) {
$exclude = join('|', split(/,/, $opt_e));
print "Will exclude stacks containing: $exclude\n";
}
if ($opt_t) {
$first_n = $opt_t;
print "Will print only the top $first_n stacks.\n";
}
if ($opt_m) {
$min_count = $opt_m;
}
if ($opt_c) {
$command="-c \"$opt_c\""
}
if ($opt_o) {
$options=$opt_o
}
print "Will print stacks with counts >= $min_count.\n";
print STDERR "Press Ctrl-C to stop.\n";
#The systemtap script that instruments the kmalloc
$script="
global kmalloc_stack
probe kernel.function(\"__kmalloc\") { kmalloc_stack[backtrace()]++ }
probe timer.ms(100), end
{
foreach (stack in kmalloc_stack) {
printf(\"<hashkey>\\n\")
print_syms(stack)
printf(\"</hashkey>\\n\")
printf(\"<hashval>%d</hashval>\\n\", kmalloc_stack[stack])
}
delete kmalloc_stack
}
";
open STREAM, "stap $options -e '$script' $command|" or die "Couldn't get output stream $!";
while (<STREAM>) {
if (/<hashval>(.*?)<\/hashval>/) {
update_hash($key, $1);
$key = "";
} elsif ($_ !~ (/<hashkey>|<\/hashkey>/)) {
$key .= $_;
}
}
$num_keys_before_filtering = scalar keys %kmalloc_stacks;
$total_kmallocs = count_kmallocs();
filter_stacks();
sort_stacks();
top_stacks();
sort_stacks();
$num_keys_after_filtering = scalar keys %kmalloc_stacks;
$filtered_kmallocs = count_kmallocs();
summarize();
exit();
sub update_hash
{
my($key, $val) = @_;
$kmalloc_stacks{$key} += $val;
}
sub filter_stacks
{
while (($stack, $count) = each %kmalloc_stacks) {
if ($count < $min_count) {
delete $kmalloc_stacks{$stack};
} elsif ($exclude && $stack =~ /$exclude/) {
delete $kmalloc_stacks{$stack};
}
}
}
sub top_stacks
{
$count=0;
foreach $stack(@sorted_stacks) {
$count+=1;
if ($count > $first_n) {
delete $kmalloc_stacks{$stack};
}
}
}
sub sort_stacks
{
@sorted_stacks = sort { $kmalloc_stacks{$b} <=> $kmalloc_stacks{$a} } keys %kmalloc_stacks;
}
sub count_kmallocs {
$count = 0;
foreach $stack(%kmalloc_stacks) {
$count += $kmalloc_stacks{$stack};
}
return $count;
}
sub summarize {
print "\n";
foreach $stack(@sorted_stacks) {
print "This path seen $kmalloc_stacks{$stack} times:\n$stack\n";
}
if ($total_kmallocs > 0) {
$percent = ($filtered_kmallocs)*100/$total_kmallocs;
} else {
$percent = 0;
}
print "Num stacks before filtering: $num_keys_before_filtering\n";
print "Num stacks after filtering: $num_keys_after_filtering\n";
print "Total kmallocs (before filtering): $total_kmallocs\n";
print "Total kmallocs (after filtering): $filtered_kmallocs\n";
print "The filter stacks have $percent of the total kmallocs\n";
close(STREAM);
}
sub sigint_handler
{
system("pkill kmalloc-stacks");
}
|