Skip to content

Commit 5c949d3

Browse files
committed
fdtrack: temporary hack for tracking file descriptor usage
Package fdtrack logs statistics about open file descriptors. This should help identify the source of ethereum#1549.
1 parent bf48ed3 commit 5c949d3

File tree

15 files changed

+314
-7
lines changed

15 files changed

+314
-7
lines changed

Godeps/_workspace/src/github.com/huin/goupnp/httpu/httpu.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Godeps/_workspace/src/github.com/huin/goupnp/soap/soap.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Godeps/_workspace/src/github.com/jackpal/go-nat-pmp/natpmp.go

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Godeps/_workspace/src/github.com/syndtr/goleveldb/leveldb/storage/file_storage.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/geth/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import (
3838
"github.com/ethereum/go-ethereum/core/types"
3939
"github.com/ethereum/go-ethereum/eth"
4040
"github.com/ethereum/go-ethereum/ethdb"
41+
"github.com/ethereum/go-ethereum/fdtrack"
4142
"github.com/ethereum/go-ethereum/logger"
4243
"github.com/ethereum/go-ethereum/logger/glog"
4344
"github.com/ethereum/go-ethereum/metrics"
@@ -528,6 +529,9 @@ func startEth(ctx *cli.Context, eth *eth.Ethereum) {
528529
// Start Ethereum itself
529530
utils.StartEthereum(eth)
530531

532+
// Start logging file descriptor stats.
533+
fdtrack.Start()
534+
531535
am := eth.AccountManager()
532536
account := ctx.GlobalString(utils.UnlockedAccountFlag.Name)
533537
accounts := strings.Split(account, " ")

fdtrack/fdtrack.go

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
// Copyright 2015 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
// Package fdtrack logs statistics about open file descriptors.
18+
package fdtrack
19+
20+
import (
21+
"fmt"
22+
"net"
23+
"sort"
24+
"sync"
25+
"time"
26+
27+
"github.com/ethereum/go-ethereum/logger/glog"
28+
)
29+
30+
var (
31+
mutex sync.Mutex
32+
all = make(map[string]int)
33+
)
34+
35+
func Open(desc string) {
36+
mutex.Lock()
37+
all[desc] += 1
38+
mutex.Unlock()
39+
}
40+
41+
func Close(desc string) {
42+
mutex.Lock()
43+
defer mutex.Unlock()
44+
if c, ok := all[desc]; ok {
45+
if c == 1 {
46+
delete(all, desc)
47+
} else {
48+
all[desc]--
49+
}
50+
}
51+
}
52+
53+
func WrapListener(desc string, l net.Listener) net.Listener {
54+
Open(desc)
55+
return &wrappedListener{l, desc}
56+
}
57+
58+
type wrappedListener struct {
59+
net.Listener
60+
desc string
61+
}
62+
63+
func (w *wrappedListener) Accept() (net.Conn, error) {
64+
c, err := w.Listener.Accept()
65+
if err == nil {
66+
c = WrapConn(w.desc, c)
67+
}
68+
return c, err
69+
}
70+
71+
func (w *wrappedListener) Close() error {
72+
err := w.Listener.Close()
73+
if err == nil {
74+
Close(w.desc)
75+
}
76+
return err
77+
}
78+
79+
func WrapConn(desc string, conn net.Conn) net.Conn {
80+
Open(desc)
81+
return &wrappedConn{conn, desc}
82+
}
83+
84+
type wrappedConn struct {
85+
net.Conn
86+
desc string
87+
}
88+
89+
func (w *wrappedConn) Close() error {
90+
err := w.Conn.Close()
91+
if err == nil {
92+
Close(w.desc)
93+
}
94+
return err
95+
}
96+
97+
func Start() {
98+
go func() {
99+
for range time.Tick(15 * time.Second) {
100+
mutex.Lock()
101+
var sum, tracked = 0, []string{}
102+
for what, n := range all {
103+
sum += n
104+
tracked = append(tracked, fmt.Sprintf("%s:%d", what, n))
105+
}
106+
mutex.Unlock()
107+
used, _ := fdusage()
108+
sort.Strings(tracked)
109+
glog.Infof("fd usage %d/%d, tracked %d %v", used, fdlimit(), sum, tracked)
110+
}
111+
}()
112+
}

fdtrack/fdusage.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2015 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
// +build !linux,!darwin
18+
19+
package fdtrack
20+
21+
import "errors"
22+
23+
func fdlimit() int {
24+
return 0
25+
}
26+
27+
func fdusage() (int, error) {
28+
return 0, errors.New("not implemented")
29+
}

fdtrack/fdusage_darwin.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright 2015 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
// +build darwin
18+
19+
package fdtrack
20+
21+
import (
22+
"os"
23+
"syscall"
24+
"unsafe"
25+
)
26+
27+
// #cgo CFLAGS: -lproc
28+
// #include <libproc.h>
29+
// #include <stdlib.h>
30+
import "C"
31+
32+
func fdlimit() int {
33+
var nofile syscall.Rlimit
34+
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &nofile); err != nil {
35+
return 0
36+
}
37+
return int(nofile.Cur)
38+
}
39+
40+
func fdusage() (int, error) {
41+
pid := C.int(os.Getpid())
42+
// Query for a rough estimate on the amout of data that
43+
// proc_pidinfo will return.
44+
rlen, err := C.proc_pidinfo(pid, C.PROC_PIDLISTFDS, 0, nil, 0)
45+
if rlen <= 0 {
46+
return 0, err
47+
}
48+
// Load the list of file descriptors. We don't actually care about
49+
// the content, only about the size. Since the number of fds can
50+
// change while we're reading them, the loop enlarges the buffer
51+
// until proc_pidinfo says the result fitted.
52+
var buf unsafe.Pointer
53+
defer func() {
54+
if buf != nil {
55+
C.free(buf)
56+
}
57+
}()
58+
for buflen := rlen; ; buflen *= 2 {
59+
buf, err = C.reallocf(buf, C.size_t(buflen))
60+
if buf == nil {
61+
return 0, err
62+
}
63+
rlen, err = C.proc_pidinfo(pid, C.PROC_PIDLISTFDS, 0, buf, buflen)
64+
if rlen <= 0 {
65+
return 0, err
66+
} else if rlen == buflen {
67+
continue
68+
}
69+
return int(rlen / C.PROC_PIDLISTFD_SIZE), nil
70+
}
71+
panic("unreachable")
72+
}

fdtrack/fdusage_linux.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// Copyright 2015 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
// +build linux
18+
19+
package fdtrack
20+
21+
import (
22+
"io"
23+
"os"
24+
"syscall"
25+
)
26+
27+
func fdlimit() int {
28+
var nofile syscall.Rlimit
29+
if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &nofile); err != nil {
30+
return 0
31+
}
32+
return int(nofile.Cur)
33+
}
34+
35+
func fdusage() (int, error) {
36+
f, err := os.Open("/proc/self/fd")
37+
if err != nil {
38+
return 0, err
39+
}
40+
defer f.Close()
41+
const batchSize = 100
42+
n := 0
43+
for {
44+
list, err := f.Readdirnames(batchSize)
45+
n += len(list)
46+
if err == io.EOF {
47+
break
48+
} else if err != nil {
49+
return 0, err
50+
}
51+
}
52+
return n, nil
53+
}

0 commit comments

Comments
 (0)