1cb77f0d6SKamil Rytarowski#! /usr/bin/env perl 2*b2441318SGreg Kroah-Hartman# SPDX-License-Identifier: GPL-2.0 3179efcb4SVegard Nossum# 4179efcb4SVegard Nossum# Detect cycles in the header file dependency graph 5179efcb4SVegard Nossum# Vegard Nossum <vegardno@ifi.uio.no> 6179efcb4SVegard Nossum# 7179efcb4SVegard Nossum 8179efcb4SVegard Nossumuse strict; 9179efcb4SVegard Nossumuse warnings; 10179efcb4SVegard Nossum 11179efcb4SVegard Nossumuse Getopt::Long; 12179efcb4SVegard Nossum 13179efcb4SVegard Nossummy $opt_all; 14179efcb4SVegard Nossummy @opt_include; 15179efcb4SVegard Nossummy $opt_graph; 16179efcb4SVegard Nossum 17179efcb4SVegard Nossum&Getopt::Long::Configure(qw(bundling pass_through)); 18179efcb4SVegard Nossum&GetOptions( 19179efcb4SVegard Nossum help => \&help, 20179efcb4SVegard Nossum version => \&version, 21179efcb4SVegard Nossum 22179efcb4SVegard Nossum all => \$opt_all, 2379ff807cSUwe Kleine-König "I=s" => \@opt_include, 24179efcb4SVegard Nossum graph => \$opt_graph, 25179efcb4SVegard Nossum); 26179efcb4SVegard Nossum 27179efcb4SVegard Nossumpush @opt_include, 'include'; 28179efcb4SVegard Nossummy %deps = (); 29179efcb4SVegard Nossummy %linenos = (); 30179efcb4SVegard Nossum 31179efcb4SVegard Nossummy @headers = grep { strip($_) } @ARGV; 32179efcb4SVegard Nossum 33179efcb4SVegard Nossumparse_all(@headers); 34179efcb4SVegard Nossum 35179efcb4SVegard Nossumif($opt_graph) { 36179efcb4SVegard Nossum graph(); 37179efcb4SVegard Nossum} else { 38179efcb4SVegard Nossum detect_cycles(@headers); 39179efcb4SVegard Nossum} 40179efcb4SVegard Nossum 41179efcb4SVegard Nossum 42179efcb4SVegard Nossumsub help { 43179efcb4SVegard Nossum print "Usage: $0 [options] file...\n"; 44179efcb4SVegard Nossum print "\n"; 45179efcb4SVegard Nossum print "Options:\n"; 46179efcb4SVegard Nossum print " --all\n"; 47179efcb4SVegard Nossum print " --graph\n"; 48179efcb4SVegard Nossum print "\n"; 49179efcb4SVegard Nossum print " -I includedir\n"; 50179efcb4SVegard Nossum print "\n"; 51179efcb4SVegard Nossum print "To make nice graphs, try:\n"; 52179efcb4SVegard Nossum print " $0 --graph include/linux/kernel.h | dot -Tpng -o graph.png\n"; 53179efcb4SVegard Nossum exit; 54179efcb4SVegard Nossum} 55179efcb4SVegard Nossum 56179efcb4SVegard Nossumsub version { 57179efcb4SVegard Nossum print "headerdep version 2\n"; 58179efcb4SVegard Nossum exit; 59179efcb4SVegard Nossum} 60179efcb4SVegard Nossum 61179efcb4SVegard Nossum# Get a file name that is relative to our include paths 62179efcb4SVegard Nossumsub strip { 63179efcb4SVegard Nossum my $filename = shift; 64179efcb4SVegard Nossum 65179efcb4SVegard Nossum for my $i (@opt_include) { 66179efcb4SVegard Nossum my $stripped = $filename; 67179efcb4SVegard Nossum $stripped =~ s/^$i\///; 68179efcb4SVegard Nossum 69179efcb4SVegard Nossum return $stripped if $stripped ne $filename; 70179efcb4SVegard Nossum } 71179efcb4SVegard Nossum 72179efcb4SVegard Nossum return $filename; 73179efcb4SVegard Nossum} 74179efcb4SVegard Nossum 75179efcb4SVegard Nossum# Search for the file name in the list of include paths 76179efcb4SVegard Nossumsub search { 77179efcb4SVegard Nossum my $filename = shift; 78179efcb4SVegard Nossum return $filename if -f $filename; 79179efcb4SVegard Nossum 80179efcb4SVegard Nossum for my $i (@opt_include) { 81179efcb4SVegard Nossum my $path = "$i/$filename"; 82179efcb4SVegard Nossum return $path if -f $path; 83179efcb4SVegard Nossum } 841dcd8100SStephen Hemminger return; 85179efcb4SVegard Nossum} 86179efcb4SVegard Nossum 87179efcb4SVegard Nossumsub parse_all { 88179efcb4SVegard Nossum # Parse all the headers. 89179efcb4SVegard Nossum my @queue = @_; 90179efcb4SVegard Nossum while(@queue) { 91179efcb4SVegard Nossum my $header = pop @queue; 92179efcb4SVegard Nossum next if exists $deps{$header}; 93179efcb4SVegard Nossum 94179efcb4SVegard Nossum $deps{$header} = [] unless exists $deps{$header}; 95179efcb4SVegard Nossum 96179efcb4SVegard Nossum my $path = search($header); 97179efcb4SVegard Nossum next unless $path; 98179efcb4SVegard Nossum 99179efcb4SVegard Nossum open(my $file, '<', $path) or die($!); 100179efcb4SVegard Nossum chomp(my @lines = <$file>); 101179efcb4SVegard Nossum close($file); 102179efcb4SVegard Nossum 103179efcb4SVegard Nossum for my $i (0 .. $#lines) { 104179efcb4SVegard Nossum my $line = $lines[$i]; 105179efcb4SVegard Nossum if(my($dep) = ($line =~ m/^#\s*include\s*<(.*?)>/)) { 106179efcb4SVegard Nossum push @queue, $dep; 107179efcb4SVegard Nossum push @{$deps{$header}}, [$i + 1, $dep]; 108179efcb4SVegard Nossum } 109179efcb4SVegard Nossum } 110179efcb4SVegard Nossum } 111179efcb4SVegard Nossum} 112179efcb4SVegard Nossum 113179efcb4SVegard Nossumsub print_cycle { 114179efcb4SVegard Nossum # $cycle[n] includes $cycle[n + 1]; 115179efcb4SVegard Nossum # $cycle[-1] will be the culprit 116179efcb4SVegard Nossum my $cycle = shift; 117179efcb4SVegard Nossum 118179efcb4SVegard Nossum # Adjust the line numbers 119179efcb4SVegard Nossum for my $i (0 .. $#$cycle - 1) { 120179efcb4SVegard Nossum $cycle->[$i]->[0] = $cycle->[$i + 1]->[0]; 121179efcb4SVegard Nossum } 122179efcb4SVegard Nossum $cycle->[-1]->[0] = 0; 123179efcb4SVegard Nossum 124179efcb4SVegard Nossum my $first = shift @$cycle; 125179efcb4SVegard Nossum my $last = pop @$cycle; 126179efcb4SVegard Nossum 127179efcb4SVegard Nossum my $msg = "In file included"; 128179efcb4SVegard Nossum printf "%s from %s,\n", $msg, $last->[1] if defined $last; 129179efcb4SVegard Nossum 130179efcb4SVegard Nossum for my $header (reverse @$cycle) { 131179efcb4SVegard Nossum printf "%s from %s:%d%s\n", 132179efcb4SVegard Nossum " " x length $msg, 133179efcb4SVegard Nossum $header->[1], $header->[0], 134179efcb4SVegard Nossum $header->[1] eq $last->[1] ? ' <-- here' : ''; 135179efcb4SVegard Nossum } 136179efcb4SVegard Nossum 137179efcb4SVegard Nossum printf "%s:%d: warning: recursive header inclusion\n", 138179efcb4SVegard Nossum $first->[1], $first->[0]; 139179efcb4SVegard Nossum} 140179efcb4SVegard Nossum 141179efcb4SVegard Nossum# Find and print the smallest cycle starting in the specified node. 142179efcb4SVegard Nossumsub detect_cycles { 143179efcb4SVegard Nossum my @queue = map { [[0, $_]] } @_; 144179efcb4SVegard Nossum while(@queue) { 145179efcb4SVegard Nossum my $top = pop @queue; 146179efcb4SVegard Nossum my $name = $top->[-1]->[1]; 147179efcb4SVegard Nossum 148179efcb4SVegard Nossum for my $dep (@{$deps{$name}}) { 149179efcb4SVegard Nossum my $chain = [@$top, [$dep->[0], $dep->[1]]]; 150179efcb4SVegard Nossum 151179efcb4SVegard Nossum # If the dep already exists in the chain, we have a 152179efcb4SVegard Nossum # cycle... 153179efcb4SVegard Nossum if(grep { $_->[1] eq $dep->[1] } @$top) { 154179efcb4SVegard Nossum print_cycle($chain); 155179efcb4SVegard Nossum next if $opt_all; 156179efcb4SVegard Nossum return; 157179efcb4SVegard Nossum } 158179efcb4SVegard Nossum 159179efcb4SVegard Nossum push @queue, $chain; 160179efcb4SVegard Nossum } 161179efcb4SVegard Nossum } 162179efcb4SVegard Nossum} 163179efcb4SVegard Nossum 164179efcb4SVegard Nossumsub mangle { 165179efcb4SVegard Nossum $_ = shift; 166179efcb4SVegard Nossum s/\//__/g; 167179efcb4SVegard Nossum s/\./_/g; 168179efcb4SVegard Nossum s/-/_/g; 169179efcb4SVegard Nossum $_; 170179efcb4SVegard Nossum} 171179efcb4SVegard Nossum 172179efcb4SVegard Nossum# Output dependency graph in GraphViz language. 173179efcb4SVegard Nossumsub graph { 174179efcb4SVegard Nossum print "digraph {\n"; 175179efcb4SVegard Nossum 176179efcb4SVegard Nossum print "\t/* vertices */\n"; 177179efcb4SVegard Nossum for my $header (keys %deps) { 178179efcb4SVegard Nossum printf "\t%s [label=\"%s\"];\n", 179179efcb4SVegard Nossum mangle($header), $header; 180179efcb4SVegard Nossum } 181179efcb4SVegard Nossum 182179efcb4SVegard Nossum print "\n"; 183179efcb4SVegard Nossum 184179efcb4SVegard Nossum print "\t/* edges */\n"; 185179efcb4SVegard Nossum for my $header (keys %deps) { 186179efcb4SVegard Nossum for my $dep (@{$deps{$header}}) { 187179efcb4SVegard Nossum printf "\t%s -> %s;\n", 188179efcb4SVegard Nossum mangle($header), mangle($dep->[1]); 189179efcb4SVegard Nossum } 190179efcb4SVegard Nossum } 191179efcb4SVegard Nossum 192179efcb4SVegard Nossum print "}\n"; 193179efcb4SVegard Nossum} 194