#!/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(\"\\n\") print_syms(stack) printf(\"\\n\") printf(\"%d\\n\", kmalloc_stack[stack]) } delete kmalloc_stack } "; open STREAM, "stap $options -e '$script' $command|" or die "Couldn't get output stream $!"; while () { if (/(.*?)<\/hashval>/) { update_hash($key, $1); $key = ""; } elsif ($_ !~ (/|<\/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"); }