]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - scripts/leaking_addresses.pl
leaking_addresses: add summary reporting options
[mirror_ubuntu-bionic-kernel.git] / scripts / leaking_addresses.pl
index 0aac03a020a8f2a37a053377c7e3a8b8174e1914..4610ad3c80c2121ad7dbf6f8f649f995dc9959c7 100755 (executable)
@@ -31,6 +31,13 @@ my @DIRS = ('/proc', '/sys');
 # Command line options.
 my $help = 0;
 my $debug = 0;
+my $raw = 0;
+my $output_raw = "";   # Write raw results to file.
+my $input_raw = "";    # Read raw results from file instead of scanning.
+
+my $suppress_dmesg = 0;                # Don't show dmesg in output.
+my $squash_by_path = 0;                # Summary report grouped by absolute path.
+my $squash_by_filename = 0;    # Summary report grouped by filename.
 
 # Do not parse these files (absolute path).
 my @skip_parse_files_abs = ('/proc/kmsg',
@@ -73,13 +80,31 @@ sub help
        my ($exitcode) = @_;
 
        print << "EOM";
+
 Usage: $P [OPTIONS]
 Version: $V
 
 Options:
 
-       -d, --debug                Display debugging output.
-       -h, --help, --version      Display this help and exit.
+       -o, --output-raw=<file>  Save results for future processing.
+       -i, --input-raw=<file>   Read results from file instead of scanning.
+           --raw                Show raw results (default).
+           --suppress-dmesg     Do not show dmesg results.
+           --squash-by-path     Show one result per unique path.
+           --squash-by-filename Show one result per unique filename.
+       -d, --debug              Display debugging output.
+       -h, --help, --version    Display this help and exit.
+
+Examples:
+
+       # Scan kernel and dump raw results.
+       $0
+
+       # Scan kernel and save results to file.
+       $0 --output-raw scan.out
+
+       # View summary report.
+       $0 --input-raw scan.out --squash-by-filename
 
 Scans the running (64 bit) kernel for potential leaking addresses.
 
@@ -90,11 +115,33 @@ EOM
 GetOptions(
        'd|debug'               => \$debug,
        'h|help'                => \$help,
-       'version'               => \$help
+       'version'               => \$help,
+       'o|output-raw=s'        => \$output_raw,
+       'i|input-raw=s'         => \$input_raw,
+       'suppress-dmesg'        => \$suppress_dmesg,
+       'squash-by-path'        => \$squash_by_path,
+       'squash-by-filename'    => \$squash_by_filename,
+       'raw'                   => \$raw,
 ) or help(1);
 
 help(0) if ($help);
 
+if ($input_raw) {
+       format_output($input_raw);
+       exit(0);
+}
+
+if (!$input_raw and ($squash_by_path or $squash_by_filename)) {
+       printf "\nSummary reporting only available with --input-raw=<file>\n";
+       printf "(First run scan with --output-raw=<file>.)\n";
+       exit(128);
+}
+
+if ($output_raw) {
+       open my $fh, '>', $output_raw or die "$0: $output_raw: $!\n";
+       select $fh;
+}
+
 parse_dmesg();
 walk(@DIRS);
 
@@ -239,3 +286,141 @@ sub walk
                }
        }
 }
+
+sub format_output
+{
+       my ($file) = @_;
+
+       # Default is to show raw results.
+       if ($raw or (!$squash_by_path and !$squash_by_filename)) {
+               dump_raw_output($file);
+               return;
+       }
+
+       my ($total, $dmesg, $paths, $files) = parse_raw_file($file);
+
+       printf "\nTotal number of results from scan (incl dmesg): %d\n", $total;
+
+       if (!$suppress_dmesg) {
+               print_dmesg($dmesg);
+       }
+
+       if ($squash_by_filename) {
+               squash_by($files, 'filename');
+       }
+
+       if ($squash_by_path) {
+               squash_by($paths, 'path');
+       }
+}
+
+sub dump_raw_output
+{
+       my ($file) = @_;
+
+       open (my $fh, '<', $file) or die "$0: $file: $!\n";
+       while (<$fh>) {
+               if ($suppress_dmesg) {
+                       if ("dmesg:" eq substr($_, 0, 6)) {
+                               next;
+                       }
+               }
+               print $_;
+       }
+       close $fh;
+}
+
+sub parse_raw_file
+{
+       my ($file) = @_;
+
+       my $total = 0;          # Total number of lines parsed.
+       my @dmesg;              # dmesg output.
+       my %files;              # Unique filenames containing leaks.
+       my %paths;              # Unique paths containing leaks.
+
+       open (my $fh, '<', $file) or die "$0: $file: $!\n";
+       while (my $line = <$fh>) {
+               $total++;
+
+               if ("dmesg:" eq substr($line, 0, 6)) {
+                       push @dmesg, $line;
+                       next;
+               }
+
+               cache_path(\%paths, $line);
+               cache_filename(\%files, $line);
+       }
+
+       return $total, \@dmesg, \%paths, \%files;
+}
+
+sub print_dmesg
+{
+       my ($dmesg) = @_;
+
+       print "\ndmesg output:\n";
+
+       if (@$dmesg == 0) {
+               print "<no results>\n";
+               return;
+       }
+
+       foreach(@$dmesg) {
+               my $index = index($_, ': ');
+               $index += 2;    # skid ': '
+               print substr($_, $index);
+       }
+}
+
+sub squash_by
+{
+       my ($ref, $desc) = @_;
+
+       print "\nResults squashed by $desc (excl dmesg). ";
+       print "Displaying [<number of results> <$desc>], <example result>\n";
+
+       if (keys %$ref == 0) {
+               print "<no results>\n";
+               return;
+       }
+
+       foreach(keys %$ref) {
+               my $lines = $ref->{$_};
+               my $length = @$lines;
+               printf "[%d %s] %s", $length, $_, @$lines[0];
+       }
+}
+
+sub cache_path
+{
+       my ($paths, $line) = @_;
+
+       my $index = index($line, ': ');
+       my $path = substr($line, 0, $index);
+
+       $index += 2;            # skip ': '
+       add_to_cache($paths, $path, substr($line, $index));
+}
+
+sub cache_filename
+{
+       my ($files, $line) = @_;
+
+       my $index = index($line, ': ');
+       my $path = substr($line, 0, $index);
+       my $filename = basename($path);
+
+       $index += 2;            # skip ': '
+       add_to_cache($files, $filename, substr($line, $index));
+}
+
+sub add_to_cache
+{
+       my ($cache, $key, $value) = @_;
+
+       if (!$cache->{$key}) {
+               $cache->{$key} = ();
+       }
+       push @{$cache->{$key}}, $value;
+}