added support for string type
[govpp.git] / vendor / github.com / google / gopacket / pcap / gopacket_benchmark / benchmark.go
1 // Copyright 2012 Google, Inc. All rights reserved.
2 //
3 // Use of this source code is governed by a BSD-style license
4 // that can be found in the LICENSE file in the root of the source
5 // tree.
6
7 // This benchmark reads in file <tempdir>/gopacket_benchmark.pcap and measures
8 // the time it takes to decode all packets from that file.  If the file doesn't
9 // exist, it's pulled down from a publicly available location.  However, you can
10 // feel free to substitute your own file at that location, in which case the
11 // benchmark will run on your own data.
12 //
13 // It's also useful for figuring out which packets may be causing errors.  Pass
14 // in the --printErrors flag, and it'll print out error layers for each packet
15 // that has them.  This includes any packets that it's just unable to decode,
16 // which is a great way to find new protocols to decode, and get test packets to
17 // write tests for them.
18 package main
19
20 import (
21         "compress/gzip"
22         "encoding/hex"
23         "flag"
24         "fmt"
25         "github.com/google/gopacket"
26         "github.com/google/gopacket/layers"
27         "github.com/google/gopacket/pcap"
28         "github.com/google/gopacket/tcpassembly"
29         "io"
30         "io/ioutil"
31         "net/http"
32         "os"
33         "runtime"
34         "runtime/pprof"
35         "time"
36 )
37
38 var decodeLazy *bool = flag.Bool("lazy", false, "If true, use lazy decoding")
39 var decodeNoCopy *bool = flag.Bool("nocopy", true, "If true, avoid an extra copy when decoding packets")
40 var printErrors *bool = flag.Bool("printErrors", false, "If true, check for and print error layers.")
41 var printLayers *bool = flag.Bool("printLayers", false, "If true, print out the layers of each packet")
42 var repeat *int = flag.Int("repeat", 5, "Read over the file N times")
43 var cpuProfile *string = flag.String("cpuprofile", "", "If set, write CPU profile to filename")
44 var url *string = flag.String("url", "http://www.ll.mit.edu/mission/communications/cyber/CSTcorpora/ideval/data/1999/training/week1/tuesday/inside.tcpdump.gz", "URL to gzip'd pcap file")
45
46 type BufferPacketSource struct {
47         index int
48         data  [][]byte
49         ci    []gopacket.CaptureInfo
50 }
51
52 func NewBufferPacketSource(p gopacket.PacketDataSource) *BufferPacketSource {
53         start := time.Now()
54         b := &BufferPacketSource{}
55         for {
56                 data, ci, err := p.ReadPacketData()
57                 if err == io.EOF {
58                         break
59                 }
60                 b.data = append(b.data, data)
61                 b.ci = append(b.ci, ci)
62         }
63         duration := time.Since(start)
64         fmt.Printf("Reading packet data into memory: %d packets in %v, %v per packet\n", len(b.data), duration, duration/time.Duration(len(b.data)))
65         return b
66 }
67
68 func (b *BufferPacketSource) ReadPacketData() (data []byte, ci gopacket.CaptureInfo, err error) {
69         if b.index >= len(b.data) {
70                 err = io.EOF
71                 return
72         }
73         data = b.data[b.index]
74         ci = b.ci[b.index]
75         b.index++
76         return
77 }
78
79 func (b *BufferPacketSource) Reset() {
80         runtime.GC()
81         b.index = 0
82 }
83
84 func main() {
85         flag.Parse()
86         filename := os.TempDir() + string(os.PathSeparator) + "gopacket_benchmark.pcap"
87         if _, err := os.Stat(filename); err != nil {
88                 // This URL points to a publicly available packet data set from a DARPA
89                 // intrusion detection evaluation.  See
90                 // http://www.ll.mit.edu/mission/communications/cyber/CSTcorpora/ideval/data/1999/training/week1/index.html
91                 // for more details.
92                 fmt.Println("Local pcap file", filename, "doesn't exist, reading from", *url)
93                 if resp, err := http.Get(*url); err != nil {
94                         panic(err)
95                 } else if out, err := os.Create(filename); err != nil {
96                         panic(err)
97                 } else if gz, err := gzip.NewReader(resp.Body); err != nil {
98                         panic(err)
99                 } else if n, err := io.Copy(out, gz); err != nil {
100                         panic(err)
101                 } else if err := gz.Close(); err != nil {
102                         panic(err)
103                 } else if err := out.Close(); err != nil {
104                         panic(err)
105                 } else {
106                         fmt.Println("Successfully read", n, "bytes from url, unzipped to local storage")
107                 }
108         }
109         fmt.Println("Reading file once through to hopefully cache most of it")
110         if f, err := os.Open(filename); err != nil {
111                 panic(err)
112         } else if n, err := io.Copy(ioutil.Discard, f); err != nil {
113                 panic(err)
114         } else if err := f.Close(); err != nil {
115                 panic(err)
116         } else {
117                 fmt.Println("Read in file", filename, ", total of", n, "bytes")
118         }
119         if *cpuProfile != "" {
120                 if cpu, err := os.Create(*cpuProfile); err != nil {
121                         panic(err)
122                 } else if err := pprof.StartCPUProfile(cpu); err != nil {
123                         panic(err)
124                 } else {
125                         defer func() {
126                                 pprof.StopCPUProfile()
127                                 cpu.Close()
128                         }()
129                 }
130         }
131         var packetDataSource *BufferPacketSource
132         var packetSource *gopacket.PacketSource
133         fmt.Printf("Opening file %q for read\n", filename)
134         if h, err := pcap.OpenOffline(filename); err != nil {
135                 panic(err)
136         } else {
137                 fmt.Println("Reading all packets into memory with BufferPacketSource.")
138                 start := time.Now()
139                 packetDataSource = NewBufferPacketSource(h)
140                 duration := time.Since(start)
141                 fmt.Printf("Time to read packet data into memory from file: %v\n", duration)
142                 packetSource = gopacket.NewPacketSource(packetDataSource, h.LinkType())
143                 packetSource.DecodeOptions.Lazy = *decodeLazy
144                 packetSource.DecodeOptions.NoCopy = *decodeNoCopy
145         }
146         fmt.Println()
147         for i := 0; i < *repeat; i++ {
148                 packetDataSource.Reset()
149                 fmt.Printf("Benchmarking decode %d/%d\n", i+1, *repeat)
150                 benchmarkPacketDecode(packetSource)
151         }
152         fmt.Println()
153         for i := 0; i < *repeat; i++ {
154                 packetDataSource.Reset()
155                 fmt.Printf("Benchmarking decoding layer parser %d/%d\n", i+1, *repeat)
156                 benchmarkLayerDecode(packetDataSource, false)
157         }
158         fmt.Println()
159         for i := 0; i < *repeat; i++ {
160                 packetDataSource.Reset()
161                 fmt.Printf("Benchmarking decoding layer parser with assembly %d/%d\n", i+1, *repeat)
162                 benchmarkLayerDecode(packetDataSource, true)
163         }
164 }
165
166 func benchmarkPacketDecode(packetSource *gopacket.PacketSource) {
167         count, errors := 0, 0
168         start := time.Now()
169         for packet, err := packetSource.NextPacket(); err != io.EOF; packet, err = packetSource.NextPacket() {
170                 if err != nil {
171                         fmt.Println("Error reading in packet:", err)
172                         continue
173                 }
174                 count++
175                 var hasError bool
176                 if *printErrors && packet.ErrorLayer() != nil {
177                         fmt.Println("\n\n\nError decoding packet:", packet.ErrorLayer().Error())
178                         fmt.Println(hex.Dump(packet.Data()))
179                         fmt.Printf("%#v\n", packet.Data())
180                         errors++
181                         hasError = true
182                 }
183                 if *printLayers || hasError {
184                         fmt.Printf("\n=== PACKET %d ===\n", count)
185                         for _, l := range packet.Layers() {
186                                 fmt.Printf("--- LAYER %v ---\n%#v\n\n", l.LayerType(), l)
187                         }
188                         fmt.Println()
189                 }
190         }
191         duration := time.Since(start)
192         fmt.Printf("\tRead in %v packets in %v, %v per packet\n", count, duration, duration/time.Duration(count))
193         if *printErrors {
194                 fmt.Printf("%v errors, successfully decoded %.02f%%\n", errors, float64(count-errors)*100.0/float64(count))
195         }
196 }
197
198 type streamFactory struct {
199 }
200
201 func (s *streamFactory) New(netFlow, tcpFlow gopacket.Flow) tcpassembly.Stream {
202         return s
203 }
204 func (s *streamFactory) Reassembled([]tcpassembly.Reassembly) {
205 }
206 func (s *streamFactory) ReassemblyComplete() {
207 }
208
209 func benchmarkLayerDecode(source *BufferPacketSource, assemble bool) {
210         var tcp layers.TCP
211         var ip layers.IPv4
212         var eth layers.Ethernet
213         var udp layers.UDP
214         var icmp layers.ICMPv4
215         var payload gopacket.Payload
216         parser := gopacket.NewDecodingLayerParser(
217                 layers.LayerTypeEthernet,
218                 &eth, &ip, &icmp, &tcp, &udp, &payload)
219         pool := tcpassembly.NewStreamPool(&streamFactory{})
220         assembler := tcpassembly.NewAssembler(pool)
221         var decoded []gopacket.LayerType
222         start := time.Now()
223         packets, decodedlayers, assembled := 0, 0, 0
224         for {
225                 packets++
226                 data, ci, err := source.ReadPacketData()
227                 if err == io.EOF {
228                         break
229                 } else if err != nil {
230                         fmt.Println("Error reading packet: ", err)
231                         continue
232                 }
233                 err = parser.DecodeLayers(data, &decoded)
234                 for _, typ := range decoded {
235                         decodedlayers++
236                         if typ == layers.LayerTypeTCP && assemble {
237                                 assembled++
238                                 assembler.AssembleWithTimestamp(ip.NetworkFlow(), &tcp, ci.Timestamp)
239                         }
240                 }
241         }
242         if assemble {
243                 assembler.FlushAll()
244         }
245         duration := time.Since(start)
246         fmt.Printf("\tRead in %d packets in %v, decoded %v layers, assembled %v packets: %v per packet\n", packets, duration, decodedlayers, assembled, duration/time.Duration(packets))
247 }