blob: 482b3e0d5d50c3237ba34df1d3943df010a5586f (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
#!/usr/bin/env tclsh
#
# This script detects file descriptors that have leaked from a parent process.
#
# Our goal is to detect file descriptors that were opened by the parent and
# not cleaned up prior to exec(), but not file descriptors that were inherited
# from the grandparent which the parent knows nothing about. To do that, we
# look up every potential leak and try to match it against open files by the
# grandparent process.
# Get PID of parent process
proc get_parent_pid {_pid} {
set fd [open "/proc/$_pid/status" "r"]
set content [read $fd]
close $fd
if {[regexp {\nPPid:\s+(\d+)} $content _ ppid]} {
return $ppid
}
error "failed to get parent pid"
}
# Read symlink to get info about the specified fd of the specified process.
# The result can be the file name or an arbitrary string that identifies it.
# When not able to read, an empty string is returned.
proc get_fdlink {_pid fd} {
if { [catch {set fdlink [file readlink "/proc/$_pid/fd/$fd"]} err] } {
return ""
}
return $fdlink
}
# Linux only
set os [exec uname]
if {$os != "Linux"} {
puts "Only Linux is supported."
exit 0
}
if {![info exists env(LEAKED_FDS_FILE)]} {
puts "Missing LEAKED_FDS_FILE environment variable."
exit 0
}
set outfile $::env(LEAKED_FDS_FILE)
set parent_pid [get_parent_pid [pid]]
set grandparent_pid [get_parent_pid $parent_pid]
set leaked_fds {}
# Look for fds that were directly inherited from our parent but not from
# our grandparent (tcl)
foreach fd [glob -tails -directory "/proc/self/fd" *] {
# Ignore stdin/stdout/stderr
if {$fd == 0 || $fd == 1 || $fd == 2} {
continue
}
set fdlink [get_fdlink "self" $fd]
if {$fdlink == ""} {
continue
}
# We ignore fds that existed in the grandparent, or fds that don't exist
# in our parent (Sentinel process).
if {[get_fdlink $grandparent_pid $fd] == $fdlink ||
[get_fdlink $parent_pid $fd] != $fdlink} {
continue
}
lappend leaked_fds [list $fd $fdlink]
}
# Produce report only if we found leaks
if {[llength $leaked_fds] > 0} {
set fd [open $outfile "w"]
puts $fd [join $leaked_fds "\n"]
close $fd
}
|