18c2ecf20Sopenharmony_ci#!/usr/bin/env perl
28c2ecf20Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0-only
38c2ecf20Sopenharmony_ci#
48c2ecf20Sopenharmony_ci# (C) Copyright IBM Corporation 2006.
58c2ecf20Sopenharmony_ci#	Author : Ram Pai (linuxram@us.ibm.com)
68c2ecf20Sopenharmony_ci#
78c2ecf20Sopenharmony_ci# Usage: export_report.pl -k Module.symvers [-o report_file ] -f *.mod.c
88c2ecf20Sopenharmony_ci#
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ciuse warnings;
118c2ecf20Sopenharmony_ciuse Getopt::Std;
128c2ecf20Sopenharmony_ciuse strict;
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_cisub numerically {
158c2ecf20Sopenharmony_ci	my $no1 = (split /\s+/, $a)[1];
168c2ecf20Sopenharmony_ci	my $no2 = (split /\s+/, $b)[1];
178c2ecf20Sopenharmony_ci	return $no1 <=> $no2;
188c2ecf20Sopenharmony_ci}
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cisub alphabetically {
218c2ecf20Sopenharmony_ci	my ($module1, $value1) = @{$a};
228c2ecf20Sopenharmony_ci	my ($module2, $value2) = @{$b};
238c2ecf20Sopenharmony_ci	return $value1 <=> $value2 || $module2 cmp $module1;
248c2ecf20Sopenharmony_ci}
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_cisub print_depends_on {
278c2ecf20Sopenharmony_ci	my ($href) = @_;
288c2ecf20Sopenharmony_ci	print "\n";
298c2ecf20Sopenharmony_ci	for my $mod (sort keys %$href) {
308c2ecf20Sopenharmony_ci		my $list = $href->{$mod};
318c2ecf20Sopenharmony_ci		print "\t$mod:\n";
328c2ecf20Sopenharmony_ci		foreach my $sym (sort numerically @{$list}) {
338c2ecf20Sopenharmony_ci			my ($symbol, $no) = split /\s+/, $sym;
348c2ecf20Sopenharmony_ci			printf("\t\t%-25s\n", $symbol);
358c2ecf20Sopenharmony_ci		}
368c2ecf20Sopenharmony_ci		print "\n";
378c2ecf20Sopenharmony_ci	}
388c2ecf20Sopenharmony_ci	print "\n";
398c2ecf20Sopenharmony_ci	print "~"x80 , "\n";
408c2ecf20Sopenharmony_ci}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_cisub usage {
438c2ecf20Sopenharmony_ci        print "Usage: @_ -h -k Module.symvers  [ -o outputfile ] \n",
448c2ecf20Sopenharmony_ci	      "\t-f: treat all the non-option argument as .mod.c files. ",
458c2ecf20Sopenharmony_ci	      "Recommend using this as the last option\n",
468c2ecf20Sopenharmony_ci	      "\t-h: print detailed help\n",
478c2ecf20Sopenharmony_ci	      "\t-k: the path to Module.symvers file. By default uses ",
488c2ecf20Sopenharmony_ci	      "the file from the current directory\n",
498c2ecf20Sopenharmony_ci	      "\t-o outputfile: output the report to outputfile\n";
508c2ecf20Sopenharmony_ci	exit 0;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cisub collectcfiles {
548c2ecf20Sopenharmony_ci    my @file;
558c2ecf20Sopenharmony_ci    open my $fh, '< modules.order' or die "cannot open modules.order: $!\n";
568c2ecf20Sopenharmony_ci    while (<$fh>) {
578c2ecf20Sopenharmony_ci	s/\.ko$/.mod.c/;
588c2ecf20Sopenharmony_ci	push (@file, $_)
598c2ecf20Sopenharmony_ci    }
608c2ecf20Sopenharmony_ci    close($fh);
618c2ecf20Sopenharmony_ci    chomp @file;
628c2ecf20Sopenharmony_ci    return @file;
638c2ecf20Sopenharmony_ci}
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cimy (%SYMBOL, %MODULE, %opt, @allcfiles);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ciif (not getopts('hk:o:f',\%opt) or defined $opt{'h'}) {
688c2ecf20Sopenharmony_ci        usage($0);
698c2ecf20Sopenharmony_ci}
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ciif (defined $opt{'f'}) {
728c2ecf20Sopenharmony_ci	@allcfiles = @ARGV;
738c2ecf20Sopenharmony_ci} else {
748c2ecf20Sopenharmony_ci	@allcfiles = collectcfiles();
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ciif (not defined $opt{'k'}) {
788c2ecf20Sopenharmony_ci	$opt{'k'} = "Module.symvers";
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ciopen (my $module_symvers, '<', $opt{'k'})
828c2ecf20Sopenharmony_ci    or die "Sorry, cannot open $opt{'k'}: $!\n";
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ciif (defined $opt{'o'}) {
858c2ecf20Sopenharmony_ci    open (my $out, '>', $opt{'o'})
868c2ecf20Sopenharmony_ci	or die "Sorry, cannot open $opt{'o'} $!\n";
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci    select $out;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci#
928c2ecf20Sopenharmony_ci# collect all the symbols and their attributes from the
938c2ecf20Sopenharmony_ci# Module.symvers file
948c2ecf20Sopenharmony_ci#
958c2ecf20Sopenharmony_ciwhile ( <$module_symvers> ) {
968c2ecf20Sopenharmony_ci	chomp;
978c2ecf20Sopenharmony_ci	my (undef, $symbol, $module, $gpl, $namespace) = split('\t');
988c2ecf20Sopenharmony_ci	$SYMBOL { $symbol } =  [ $module , "0" , $symbol, $gpl];
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ciclose($module_symvers);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci#
1038c2ecf20Sopenharmony_ci# collect the usage count of each symbol.
1048c2ecf20Sopenharmony_ci#
1058c2ecf20Sopenharmony_cimy $modversion_warnings = 0;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ciforeach my $thismod (@allcfiles) {
1088c2ecf20Sopenharmony_ci	my $module;
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	unless (open ($module, '<', $thismod)) {
1118c2ecf20Sopenharmony_ci		warn "Sorry, cannot open $thismod: $!\n";
1128c2ecf20Sopenharmony_ci		next;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	my $state=0;
1168c2ecf20Sopenharmony_ci	while ( <$module> ) {
1178c2ecf20Sopenharmony_ci		chomp;
1188c2ecf20Sopenharmony_ci		if ($state == 0) {
1198c2ecf20Sopenharmony_ci			$state = 1 if ($_ =~ /static const struct modversion_info/);
1208c2ecf20Sopenharmony_ci			next;
1218c2ecf20Sopenharmony_ci		}
1228c2ecf20Sopenharmony_ci		if ($state == 1) {
1238c2ecf20Sopenharmony_ci			$state = 2 if ($_ =~ /__attribute__\(\(section\("__versions"\)\)\)/);
1248c2ecf20Sopenharmony_ci			next;
1258c2ecf20Sopenharmony_ci		}
1268c2ecf20Sopenharmony_ci		if ($state == 2) {
1278c2ecf20Sopenharmony_ci			if ( $_ !~ /0x[0-9a-f]+,/ ) {
1288c2ecf20Sopenharmony_ci				next;
1298c2ecf20Sopenharmony_ci			}
1308c2ecf20Sopenharmony_ci			my $sym = (split /([,"])/,)[4];
1318c2ecf20Sopenharmony_ci			my ($module, $value, $symbol, $gpl) = @{$SYMBOL{$sym}};
1328c2ecf20Sopenharmony_ci			$SYMBOL{ $sym } =  [ $module, $value+1, $symbol, $gpl];
1338c2ecf20Sopenharmony_ci			push(@{$MODULE{$thismod}} , $sym);
1348c2ecf20Sopenharmony_ci		}
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci	if ($state != 2) {
1378c2ecf20Sopenharmony_ci		warn "WARNING:$thismod is not built with CONFIG_MODVERSIONS enabled\n";
1388c2ecf20Sopenharmony_ci		$modversion_warnings++;
1398c2ecf20Sopenharmony_ci	}
1408c2ecf20Sopenharmony_ci	close($module);
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ciprint "\tThis file reports the exported symbols usage patterns by in-tree\n",
1448c2ecf20Sopenharmony_ci	"\t\t\t\tmodules\n";
1458c2ecf20Sopenharmony_ciprintf("%s\n\n\n","x"x80);
1468c2ecf20Sopenharmony_ciprintf("\t\t\t\tINDEX\n\n\n");
1478c2ecf20Sopenharmony_ciprintf("SECTION 1: Usage counts of all exported symbols\n");
1488c2ecf20Sopenharmony_ciprintf("SECTION 2: List of modules and the exported symbols they use\n");
1498c2ecf20Sopenharmony_ciprintf("%s\n\n\n","x"x80);
1508c2ecf20Sopenharmony_ciprintf("SECTION 1:\tThe exported symbols and their usage count\n\n");
1518c2ecf20Sopenharmony_ciprintf("%-25s\t%-25s\t%-5s\t%-25s\n", "Symbol", "Module", "Usage count",
1528c2ecf20Sopenharmony_ci	"export type");
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci#
1558c2ecf20Sopenharmony_ci# print the list of unused exported symbols
1568c2ecf20Sopenharmony_ci#
1578c2ecf20Sopenharmony_ciforeach my $list (sort alphabetically values(%SYMBOL)) {
1588c2ecf20Sopenharmony_ci	my ($module, $value, $symbol, $gpl) = @{$list};
1598c2ecf20Sopenharmony_ci	printf("%-25s\t%-25s\t%-10s\t", $symbol, $module, $value);
1608c2ecf20Sopenharmony_ci	if (defined $gpl) {
1618c2ecf20Sopenharmony_ci		printf("%-25s\n",$gpl);
1628c2ecf20Sopenharmony_ci	} else {
1638c2ecf20Sopenharmony_ci		printf("\n");
1648c2ecf20Sopenharmony_ci	}
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ciprintf("%s\n\n\n","x"x80);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ciprintf("SECTION 2:\n\tThis section reports export-symbol-usage of in-kernel
1698c2ecf20Sopenharmony_cimodules. Each module lists the modules, and the symbols from that module that
1708c2ecf20Sopenharmony_ciit uses.  Each listed symbol reports the number of modules using it\n");
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ciprint "\nNOTE: Got $modversion_warnings CONFIG_MODVERSIONS warnings\n\n"
1738c2ecf20Sopenharmony_ci    if $modversion_warnings;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ciprint "~"x80 , "\n";
1768c2ecf20Sopenharmony_cifor my $thismod (sort keys %MODULE) {
1778c2ecf20Sopenharmony_ci	my $list = $MODULE{$thismod};
1788c2ecf20Sopenharmony_ci	my %depends;
1798c2ecf20Sopenharmony_ci	$thismod =~ s/\.mod\.c/.ko/;
1808c2ecf20Sopenharmony_ci	print "\t\t\t$thismod\n";
1818c2ecf20Sopenharmony_ci	foreach my $symbol (@{$list}) {
1828c2ecf20Sopenharmony_ci		my ($module, $value, undef, $gpl) = @{$SYMBOL{$symbol}};
1838c2ecf20Sopenharmony_ci		push (@{$depends{"$module"}}, "$symbol $value");
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci	print_depends_on(\%depends);
1868c2ecf20Sopenharmony_ci}
187