2006-06-24 03:44:38 +04:00
#!/usr/bin/perl -w
#
# (C) Copyright IBM Corporation 2006.
# Released under GPL v2.
# Author : Ram Pai (linuxram@us.ibm.com)
#
# Usage: export_report.pl -k Module.symvers [-o report_file ] -f *.mod.c
#
use Getopt::Std ;
use strict ;
sub numerically {
my $ no1 = ( split /\s+/ , $ a ) [ 1 ] ;
my $ no2 = ( split /\s+/ , $ b ) [ 1 ] ;
return $ no1 <=> $ no2 ;
}
sub alphabetically {
my ( $ module1 , $ value1 ) = @ { $ a } ;
my ( $ module2 , $ value2 ) = @ { $ b } ;
return $ value1 <=> $ value2 || $ module2 cmp $ module1 ;
}
sub print_depends_on {
my ( $ href ) = @ _ ;
print "\n" ;
while ( my ( $ mod , $ list ) = each %$ href ) {
print "\t$mod:\n" ;
foreach my $ sym ( sort numerically @ { $ list } ) {
my ( $ symbol , $ no ) = split /\s+/ , $ sym ;
printf ( "\t\t%-25s\t%-25d\n" , $ symbol , $ no ) ;
}
print "\n" ;
}
print "\n" ;
print "~" x80 , "\n" ;
}
sub usage {
print "Usage: @_ -h -k Module.symvers [ -o outputfile ] \n" ,
"\t-f: treat all the non-option argument as .mod.c files. " ,
"Recommend using this as the last option\n" ,
"\t-h: print detailed help\n" ,
"\t-k: the path to Module.symvers file. By default uses " ,
"the file from the current directory\n" ,
"\t-o outputfile: output the report to outputfile\n" ;
exit 0 ;
}
sub collectcfiles {
2011-05-23 22:44:55 +04:00
my @ file ;
while ( <.tmp_versions/*.mod> ) {
open my $ fh , '<' , $ _ or die "cannot open $_: $!\n" ;
push ( @ file ,
grep s/\.ko/.mod.c/ , # change the suffix
grep m/.+\.ko/ , # find the .ko path
<$fh> ) ; # lines in opened file
}
2010-02-23 02:17:22 +03:00
chomp @ file ;
return @ file ;
2006-06-24 03:44:38 +04:00
}
my ( % SYMBOL , % MODULE , % opt , @ allcfiles ) ;
if ( not getopts ( 'hk:o:f' , \ % opt ) or defined $ opt { 'h' } ) {
usage ( $ 0 ) ;
}
if ( defined $ opt { 'f' } ) {
@ allcfiles = @ ARGV ;
} else {
@ allcfiles = collectcfiles ( ) ;
}
if ( not defined $ opt { 'k' } ) {
$ opt { 'k' } = "Module.symvers" ;
}
2010-02-23 02:17:22 +03:00
open ( my $ module_symvers , '<' , $ opt { 'k' } )
or die "Sorry, cannot open $opt{'k'}: $!\n" ;
2006-06-24 03:44:38 +04:00
if ( defined $ opt { 'o' } ) {
2010-02-23 02:17:22 +03:00
open ( my $ out , '>' , $ opt { 'o' } )
or die "Sorry, cannot open $opt{'o'} $!\n" ;
select $ out ;
2006-06-24 03:44:38 +04:00
}
2010-02-23 02:17:22 +03:00
2006-06-24 03:44:38 +04:00
#
# collect all the symbols and their attributes from the
# Module.symvers file
#
2010-02-23 02:17:22 +03:00
while ( <$module_symvers> ) {
2006-06-24 03:44:38 +04:00
chomp ;
my ( undef , $ symbol , $ module , $ gpl ) = split ;
$ SYMBOL { $ symbol } = [ $ module , "0" , $ symbol , $ gpl ] ;
}
2010-02-23 02:17:22 +03:00
close ( $ module_symvers ) ;
2006-06-24 03:44:38 +04:00
#
# collect the usage count of each symbol.
#
foreach my $ thismod ( @ allcfiles ) {
2010-02-23 02:17:22 +03:00
my $ module ;
unless ( open ( $ module , '<' , $ thismod ) ) {
warn "Sorry, cannot open $thismod: $!\n" ;
2006-06-24 03:44:38 +04:00
next ;
}
2010-02-23 02:17:22 +03:00
2006-06-24 03:44:38 +04:00
my $ state = 0 ;
2010-02-23 02:17:22 +03:00
while ( <$module> ) {
2006-06-24 03:44:38 +04:00
chomp ;
2007-08-25 02:32:22 +04:00
if ( $ state == 0 ) {
2006-06-24 03:44:38 +04:00
$ state = 1 if ( $ _ =~ /static const struct modversion_info/ ) ;
next ;
}
2007-08-25 02:32:22 +04:00
if ( $ state == 1 ) {
2006-06-24 03:44:38 +04:00
$ state = 2 if ( $ _ =~ /__attribute__\(\(section\("__versions"\)\)\)/ ) ;
next ;
}
2007-08-25 02:32:22 +04:00
if ( $ state == 2 ) {
2007-08-25 01:04:51 +04:00
if ( $ _ !~ /0x[0-9a-f]+,/ ) {
2006-06-24 03:44:38 +04:00
next ;
}
my $ sym = ( split /([,"])/ , ) [ 4 ] ;
my ( $ module , $ value , $ symbol , $ gpl ) = @ { $ SYMBOL { $ sym } } ;
$ SYMBOL { $ sym } = [ $ module , $ value + 1 , $ symbol , $ gpl ] ;
push ( @ { $ MODULE { $ thismod } } , $ sym ) ;
}
}
2007-08-25 02:32:22 +04:00
if ( $ state != 2 ) {
2006-06-24 03:44:38 +04:00
print "WARNING:$thismod is not built with CONFIG_MODVERSION enabled\n" ;
}
2010-02-23 02:17:22 +03:00
close ( $ module ) ;
2006-06-24 03:44:38 +04:00
}
print "\tThis file reports the exported symbols usage patterns by in-tree\n" ,
"\t\t\t\tmodules\n" ;
printf ( "%s\n\n\n" , "x" x80 ) ;
printf ( "\t\t\t\tINDEX\n\n\n" ) ;
printf ( "SECTION 1: Usage counts of all exported symbols\n" ) ;
printf ( "SECTION 2: List of modules and the exported symbols they use\n" ) ;
printf ( "%s\n\n\n" , "x" x80 ) ;
printf ( "SECTION 1:\tThe exported symbols and their usage count\n\n" ) ;
printf ( "%-25s\t%-25s\t%-5s\t%-25s\n" , "Symbol" , "Module" , "Usage count" ,
"export type" ) ;
#
# print the list of unused exported symbols
#
foreach my $ list ( sort alphabetically values ( % SYMBOL ) ) {
my ( $ module , $ value , $ symbol , $ gpl ) = @ { $ list } ;
printf ( "%-25s\t%-25s\t%-10s\t" , $ symbol , $ module , $ value ) ;
if ( defined $ gpl ) {
printf ( "%-25s\n" , $ gpl ) ;
} else {
printf ( "\n" ) ;
}
}
printf ( "%s\n\n\n" , "x" x80 ) ;
printf ( " SECTION 2 : \ n \ tThis section reports export - symbol - usage of in - kernel
modules . Each module lists the modules , and the symbols from that module that
it uses . Each listed symbol reports the number of modules using it \ n " ) ;
print "~" x80 , "\n" ;
while ( my ( $ thismod , $ list ) = each % MODULE ) {
my % depends ;
$ thismod =~ s/\.mod\.c/.ko/ ;
print "\t\t\t$thismod\n" ;
foreach my $ symbol ( @ { $ list } ) {
my ( $ module , $ value , undef , $ gpl ) = @ { $ SYMBOL { $ symbol } } ;
push ( @ { $ depends { "$module" } } , "$symbol $value" ) ;
}
print_depends_on ( \ % depends ) ;
}