NULL should never be passed to ether_poll_deregister(). Try catching any
[dragonfly/port-amd64.git] / tools / LibraryReport / LibraryReport.tcl
blobe7a689e6f14411e5a3b8920959591b41e0061848
1 #!/bin/sh
2 # tcl magic \
3 exec tclsh $0 $*
4 ################################################################################
5 # Copyright (C) 1997
6 # Michael Smith. All rights reserved.
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions
10 # are met:
11 # 1. Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # 2. Redistributions in binary form must reproduce the above copyright
14 # notice, this list of conditions and the following disclaimer in the
15 # documentation and/or other materials provided with the distribution.
16 # 3. Neither the name of the author nor the names of any co-contributors
17 # may be used to endorse or promote products derived from this software
18 # without specific prior written permission.
20 # THIS SOFTWARE IS PROVIDED BY Michael Smith AND CONTRIBUTORS ``AS IS'' AND
21 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 # ARE DISCLAIMED. IN NO EVENT SHALL Michael Smith OR CONTRIBUTORS BE LIABLE
24 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 # SUCH DAMAGE.
31 ################################################################################
33 # LibraryReport; produce a list of shared libraries on the system, and a list of
34 # all executables that use them.
36 ################################################################################
38 # Stage 1 looks for shared libraries; the output of 'ldconfig -r' is examined
39 # for hints as to where to look for libraries (but not trusted as a complete
40 # list).
42 # These libraries each get an entry in the global 'Libs()' array.
44 # Stage 2 walks the entire system directory heirachy looking for executable
45 # files, applies 'ldd' to them and attempts to determine which libraries are
46 # used. The path of the executable is then added to the 'Libs()' array
47 # for each library used.
49 # Stage 3 reports on the day's findings.
51 ################################################################################
53 # $FreeBSD: src/tools/LibraryReport/LibraryReport.tcl,v 1.5 1999/08/28 00:54:21 peter Exp $
54 # $DragonFly: src/tools/LibraryReport/LibraryReport.tcl,v 1.2 2003/06/17 04:29:11 dillon Exp $
57 #########################################################################################
58 # findLibs
60 # Ask ldconfig where it thinks libraries are to be found. Go look for them, and
61 # add an element to 'Libs' for everything that looks like a library.
63 proc findLibs {} {
65 global Libs stats verbose;
67 # Older ldconfigs return a junk value when asked for a report
68 if {[catch {set liblist [exec ldconfig -r]} err]} { # get ldconfig output
69 puts stderr "ldconfig returned nonzero, persevering.";
70 set liblist $err; # there's junk in this
73 # remove hintsfile name, convert to list
74 set liblist [lrange [split $liblist "\n"] 1 end];
76 set libdirs ""; # no directories yet
77 foreach line $liblist {
78 # parse ldconfig output
79 if {[scan $line "%s => %s" junk libname] == 2} {
80 # find directory name
81 set libdir [file dirname $libname];
82 # have we got this one already?
83 if {[lsearch -exact $libdirs $libdir] == -1} {
84 lappend libdirs $libdir;
86 } else {
87 puts stderr "Unparseable ldconfig output line :";
88 puts stderr $line;
92 # libdirs is now a list of directories that we might find libraries in
93 foreach dir $libdirs {
94 # get the names of anything that looks like a library
95 set libnames [glob -nocomplain "$dir/lib*.so.*"]
96 foreach lib $libnames {
97 set type [file type $lib]; # what is it?
98 switch $type {
99 file { # looks like a library
100 # may have already been referenced by a symlink
101 if {![info exists Libs($lib)]} {
102 set Libs($lib) ""; # add it to our list
103 if {$verbose} {puts "+ $lib";}
106 link { # symlink; probably to another library
107 # If the readlink fails, the symlink is stale
108 if {[catch {set ldest [file readlink $lib]}]} {
109 puts stderr "Symbolic link points to nothing : $lib";
110 } else {
111 # may have already been referenced by another symlink
112 if {![info exists Libs($lib)]} {
113 set Libs($lib) ""; # add it to our list
114 if {$verbose} {puts "+ $lib";}
116 # list the symlink as a consumer of this library
117 lappend Libs($ldest) "($lib)";
118 if {$verbose} {puts "-> $ldest";}
124 set stats(libs) [llength [array names Libs]];
127 ################################################################################
128 # findLibUsers
130 # Look in the directory (dir) for executables. If we find any, call
131 # examineExecutable to see if it uses any shared libraries. Call ourselves
132 # on any directories we find.
134 # Note that the use of "*" as a glob pattern means we miss directories and
135 # executables starting with '.'. This is a Feature.
137 proc findLibUsers {dir} {
139 global stats verbose;
141 if {[catch {
142 set ents [glob -nocomplain "$dir/*"];
143 } msg]} {
144 if {$msg == ""} {
145 set msg "permission denied";
147 puts stderr "Can't search under '$dir' : $msg";
148 return ;
151 if {$verbose} {puts "===>> $dir";}
152 incr stats(dirs);
154 # files?
155 foreach f $ents {
156 # executable?
157 if {[file executable $f]} {
158 # really a file?
159 if {[file isfile $f]} {
160 incr stats(files);
161 examineExecutable $f;
165 # subdirs?
166 foreach f $ents {
167 # maybe a directory with more files?
168 # don't use 'file isdirectory' because that follows symlinks
169 if {[catch {set type [file type $f]}]} {
170 continue ; # may not be able to stat
172 if {$type == "directory"} {
173 findLibUsers $f;
178 ################################################################################
179 # examineExecutable
181 # Look at (fname) and see if ldd thinks it references any shared libraries.
182 # If it does, update Libs with the information.
184 proc examineExecutable {fname} {
186 global Libs stats verbose;
188 # ask Mr. Ldd.
189 if {[catch {set result [exec ldd $fname]} msg]} {
190 return ; # not dynamic
193 if {$verbose} {puts -nonewline "$fname : ";}
194 incr stats(execs);
196 # For a non-shared executable, we get a single-line error message.
197 # For a shared executable, we get a heading line, so in either case
198 # we can discard the first line and any subsequent lines are libraries
199 # that are required.
200 set llist [lrange [split $result "\n"] 1 end];
201 set uses "";
203 foreach line $llist {
204 if {[scan $line "%s => %s %s" junk1 lib junk2] == 3} {
205 if {$lib == "not"} { # "not found" error
206 set mlname [string range $junk1 2 end];
207 puts stderr "$fname : library '$mlname' not known.";
208 } else {
209 lappend Libs($lib) $fname;
210 lappend uses $lib;
212 } else {
213 puts stderr "Unparseable ldd output line :";
214 puts stderr $line;
217 if {$verbose} {puts "$uses";}
220 ################################################################################
221 # emitLibDetails
223 # Emit a listing of libraries and the executables that use them.
225 proc emitLibDetails {} {
227 global Libs;
229 # divide into used/unused
230 set used "";
231 set unused "";
232 foreach lib [array names Libs] {
233 if {$Libs($lib) == ""} {
234 lappend unused $lib;
235 } else {
236 lappend used $lib;
240 # emit used list
241 puts "== Current Shared Libraries ==================================================";
242 foreach lib [lsort $used] {
243 # sort executable names
244 set users [lsort $Libs($lib)];
245 puts [format "%-30s %s" $lib $users];
247 # emit unused
248 puts "== Stale Shared Libraries ====================================================";
249 foreach lib [lsort $unused] {
250 # sort executable names
251 set users [lsort $Libs($lib)];
252 puts [format "%-30s %s" $lib $users];
256 ################################################################################
257 # Run the whole shebang
259 proc main {} {
261 global stats verbose argv;
263 set verbose 0;
264 foreach arg $argv {
265 switch -- $arg {
266 -v {
267 set verbose 1;
269 default {
270 puts stderr "Unknown option '$arg'.";
271 exit ;
276 set stats(libs) 0;
277 set stats(dirs) 0;
278 set stats(files) 0;
279 set stats(execs) 0
281 findLibs;
282 findLibUsers "/";
283 emitLibDetails;
285 puts [format "Searched %d directories, %d executables (%d dynamic) for %d libraries." \
286 $stats(dirs) $stats(files) $stats(execs) $stats(libs)];
289 ################################################################################
290 main;