-
Notifications
You must be signed in to change notification settings - Fork 8
/
main.go
153 lines (129 loc) · 4.87 KB
/
main.go
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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package main
// For comparison, try
// sudo ss -timep | grep -A1 -v -e 127.0.0.1 -e skmem | tail
import (
"context"
"flag"
"log"
"os"
"runtime"
"runtime/trace"
"github.com/m-lab/tcp-info/eventsocket"
"github.com/m-lab/go/anonymize"
"github.com/m-lab/go/prometheusx"
"github.com/m-lab/go/rtx"
"github.com/m-lab/go/flagx"
_ "net/http/pprof" // Support profiling
"github.com/m-lab/tcp-info/collector"
"github.com/m-lab/tcp-info/netlink"
"github.com/m-lab/tcp-info/saver"
)
/*
Some performance numbers
flat flat% sum% cum cum%
3.99s 79.48% 79.48% 4s 79.68% syscall.Syscall6
0.11s 2.19% 81.67% 0.41s 8.17% runtime.mallocgc
0.10s 1.99% 83.67% 0.10s 1.99% runtime.heapBitsSetType
0.08s 1.59% 85.26% 0.08s 1.59% runtime.futex
0.06s 1.20% 86.45% 0.06s 1.20% runtime.memclrNoHeapPointers
0.06s 1.20% 87.65% 0.08s 1.59% runtime.scanobject
0.06s 1.20% 88.84% 0.06s 1.20% syscall.RawSyscall
0.04s 0.8% 89.64% 0.07s 1.39% github.com/m-lab/tcp-info/delta.(*ArchivalRecord).IsSame
0.04s 0.8% 90.44% 0.12s 2.39% runtime.(*mcentral).cacheSpan
0.04s 0.8% 91.24% 0.04s 0.8% runtime.duffcopy
0.04s 0.8% 92.03% 0.04s 0.8% runtime.memmove
0.04s 0.8% 92.83% 0.04s 0.8% syscall.Syscall
0.03s 0.6% 93.43% 0.03s 0.6% runtime.cmpbody
0.03s 0.6% 94.02% 0.03s 0.6% runtime.heapBitsForObject
0.02s 0.4% 94.42% 0.20s 3.98% github.com/vishvananda/netlink/nl.ParseRouteAttr
0.02s 0.4% 94.82% 0.14s 2.79% runtime.(*mcache).refill
0.02s 0.4% 95.22% 0.35s 6.97% runtime.growslice
0.01s 0.2% 95.42% 0.38s 7.57% github.com/m-lab/tcp-info/delta.(*Cache).Update
0.01s 0.2% 95.62% 0.10s 1.99% runtime.gcDrain
0.01s 0.2% 95.82% 0.07s 1.39% runtime.makeslice
*/
var (
reps int
enableTrace bool
outputDir string
excludeSrcPorts = flagx.StringArray{}
excludeDstIPs = flagx.StringArray{}
)
func init() {
// Always prepend the filename and line number.
log.SetFlags(log.LstdFlags | log.Lshortfile)
flag.IntVar(&reps, "reps", 0, "How many cycles should be recorded, 0 means continuous")
flag.BoolVar(&enableTrace, "trace", false, "Enable trace")
flag.StringVar(&outputDir, "output", "", "Directory in which to put the resulting tree of data. Default is the current directory.")
flag.Var(&excludeSrcPorts, "exclude-srcport", "Exclude snapshots with these local ports from saved archives.")
flag.Var(&excludeDstIPs, "exclude-dstip", "Exclude snapshots with these remote IPs from saved archives.")
}
// NOTES:
// 1. zstd is much better than gzip
// 2. the go zstd wrapper doesn't seem to work well - poor compression and slow.
// 3. zstd seems to result in similar file size using proto or raw output.
var (
ctx, cancel = context.WithCancel(context.Background())
)
func main() {
flag.Parse()
flagx.ArgsFromEnv(flag.CommandLine)
defer cancel()
if outputDir != "" {
rtx.PanicOnError(os.MkdirAll(outputDir, 0755), "Could not create the output dir %s", outputDir)
rtx.Must(os.Chdir(outputDir), "Could not change to the directory %s", outputDir)
}
// Performance instrumentation.
runtime.SetBlockProfileRate(1000000) // 1 sample/msec
runtime.SetMutexProfileFraction(1000)
// Expose prometheus and pprof metrics on a separate port.
promSrv := prometheusx.MustServeMetrics()
defer promSrv.Shutdown(ctx)
if enableTrace {
traceFile, err := os.Create("trace")
rtx.Must(err, "Could not create trace file")
rtx.Must(trace.Start(traceFile), "failed to start trace: %v", err)
defer trace.Stop()
}
// Make and start the event server.
eventSrv := eventsocket.NullServer()
if *eventsocket.Filename != "" {
eventSrv = eventsocket.New(*eventsocket.Filename)
}
rtx.Must(eventSrv.Listen(), "Could not listen on", *eventsocket.Filename)
go eventSrv.Serve(ctx)
ex := &netlink.ExcludeConfig{
Local: true,
}
if len(excludeDstIPs) != 0 {
for _, dip := range excludeDstIPs {
err := ex.AddDstIP(dip)
if err != nil {
log.Printf("skipping; cannot convert ip %q; %v", dip, err)
continue
}
}
}
if len(excludeSrcPorts) != 0 {
for _, port := range excludeSrcPorts {
err := ex.AddSrcPort(port)
if err != nil {
log.Printf("skipping; cannot convert port %q; %v", port, err)
continue
}
}
}
// Make the saver and construct the message channel, buffering up to 2 batches
// of messages without stalling producer. We may want to increase the buffer if
// we observe main() stalling.
svrChan := make(chan netlink.MessageBlock, 2)
anon := anonymize.New(anonymize.IPAnonymizationFlag)
svr := saver.NewSaver("host", "pod", 3, eventSrv, anon, ex)
go svr.MessageSaverLoop(svrChan)
// Run the collector, possibly forever.
totalSeen, totalErr := collector.Run(ctx, reps, svrChan, svr, true)
// Shut down and clean up after the collector terminates.
close(svrChan)
svr.Done.Wait()
svr.LogCacheStats(totalSeen, totalErr)
}