github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/state/stats.go (about) 1 // Copyright 2018 The gVisor Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package state 16 17 import ( 18 "bytes" 19 "fmt" 20 "sort" 21 "time" 22 ) 23 24 type statEntry struct { 25 count uint 26 total time.Duration 27 } 28 29 // Stats tracks encode / decode timing. 30 // 31 // This currently provides a meaningful String function and no other way to 32 // extract stats about individual types. 33 // 34 // All exported receivers accept nil. 35 type Stats struct { 36 // byType contains a breakdown of time spent by type. 37 // 38 // This is indexed *directly* by typeID, including zero. 39 byType []statEntry 40 41 // stack contains objects in progress. 42 stack []typeID 43 44 // names contains type names. 45 // 46 // This is also indexed *directly* by typeID, including zero, which we 47 // hard-code as "state.default". This is only resolved by calling fini 48 // on the stats object. 49 names []string 50 51 // last is the last start time. 52 last time.Time 53 } 54 55 // init initializes statistics. 56 func (s *Stats) init() { 57 s.last = time.Now() 58 s.stack = append(s.stack, 0) 59 } 60 61 // fini finalizes statistics. 62 func (s *Stats) fini(resolve func(id typeID) string) { 63 s.done() 64 65 // Resolve all type names. 66 s.names = make([]string, len(s.byType)) 67 s.names[0] = "state.default" // See above. 68 for id := typeID(1); int(id) < len(s.names); id++ { 69 s.names[id] = resolve(id) 70 } 71 } 72 73 // sample adds the samples to the given object. 74 func (s *Stats) sample(id typeID) { 75 now := time.Now() 76 if len(s.byType) <= int(id) { 77 // Allocate all the missing entries in one fell swoop. 78 s.byType = append(s.byType, make([]statEntry, 1+int(id)-len(s.byType))...) 79 } 80 s.byType[id].total += now.Sub(s.last) 81 s.last = now 82 } 83 84 // start starts a sample. 85 func (s *Stats) start(id typeID) { 86 last := s.stack[len(s.stack)-1] 87 s.sample(last) 88 s.stack = append(s.stack, id) 89 } 90 91 // done finishes the current sample. 92 func (s *Stats) done() { 93 last := s.stack[len(s.stack)-1] 94 s.sample(last) 95 s.byType[last].count++ 96 s.stack = s.stack[:len(s.stack)-1] 97 } 98 99 type sliceEntry struct { 100 name string 101 entry *statEntry 102 } 103 104 // String returns a table representation of the stats. 105 func (s *Stats) String() string { 106 // Build a list of stat entries. 107 ss := make([]sliceEntry, 0, len(s.byType)) 108 for id := 0; id < len(s.names); id++ { 109 ss = append(ss, sliceEntry{ 110 name: s.names[id], 111 entry: &s.byType[id], 112 }) 113 } 114 115 // Sort by total time (descending). 116 sort.Slice(ss, func(i, j int) bool { 117 return ss[i].entry.total > ss[j].entry.total 118 }) 119 120 // Print the stat results. 121 var ( 122 buf bytes.Buffer 123 count uint 124 total time.Duration 125 ) 126 buf.WriteString("\n") 127 buf.WriteString(fmt.Sprintf("% 16s | % 8s | % 16s | %s\n", "total", "count", "per", "type")) 128 buf.WriteString("-----------------+----------+------------------+----------------\n") 129 for _, se := range ss { 130 if se.entry.count == 0 { 131 // Since we store all types linearly, we are not 132 // guaranteed that any entry actually has time. 133 continue 134 } 135 count += se.entry.count 136 total += se.entry.total 137 per := se.entry.total / time.Duration(se.entry.count) 138 buf.WriteString(fmt.Sprintf("% 16s | %8d | % 16s | %s\n", 139 se.entry.total, se.entry.count, per, se.name)) 140 } 141 buf.WriteString("-----------------+----------+------------------+----------------\n") 142 buf.WriteString(fmt.Sprintf("% 16s | % 8d | % 16s | [all]", 143 total, count, total/time.Duration(count))) 144 return string(buf.Bytes()) 145 }