1// Copyright 2015 Google Inc. All rights reserved 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 15package kati 16 17import ( 18 "fmt" 19 "io" 20 "os" 21 "sort" 22 "sync" 23 "time" 24) 25 26type traceEventT struct { 27 mu sync.Mutex 28 f io.WriteCloser 29 t0 time.Time 30 pid int 31} 32 33const ( 34 traceEventMain = iota + 1 35 // add new ones to use new goroutine. 36) 37 38var traceEvent traceEventT 39 40// TraceEventStart starts trace event. 41func TraceEventStart(f io.WriteCloser) { 42 traceEvent.start(f) 43} 44 45// TraceEventStop stops trace event. 46func TraceEventStop() { 47 traceEvent.stop() 48} 49 50func (t *traceEventT) start(f io.WriteCloser) { 51 t.f = f 52 t.t0 = time.Now() 53 fmt.Fprint(t.f, "[ ") 54} 55 56func (t *traceEventT) enabled() bool { 57 return t.f != nil 58} 59 60func (t *traceEventT) stop() { 61 fmt.Fprint(t.f, "\n]\n") 62 t.f.Close() 63} 64 65type event struct { 66 name, v string 67 tid int 68 t time.Time 69 emit bool 70} 71 72func (t *traceEventT) begin(name string, v Value, tid int) event { 73 var e event 74 e.tid = tid 75 e.t = time.Now() 76 if t.f != nil || EvalStatsFlag { 77 e.name = name 78 e.v = v.String() 79 } 80 if t.f != nil { 81 e.emit = name == "include" || name == "shell" 82 if e.emit { 83 t.emit("B", e, e.t.Sub(t.t0)) 84 } 85 } 86 return e 87} 88 89func (t *traceEventT) emit(ph string, e event, ts time.Duration) { 90 t.mu.Lock() 91 defer t.mu.Unlock() 92 93 if t.pid == 0 { 94 t.pid = os.Getpid() 95 } else { 96 fmt.Fprintf(t.f, ",\n") 97 } 98 fmt.Fprintf(t.f, `{"pid":%d,"tid":%d,"ts":%d,"ph":%q,"cat":%q,"name":%q,"args":{}}`, 99 t.pid, 100 e.tid, 101 ts.Nanoseconds()/1e3, 102 ph, 103 e.name, 104 e.v, 105 ) 106} 107 108func (t *traceEventT) end(e event) { 109 if t.f != nil { 110 if e.emit { 111 t.emit("E", e, time.Since(t.t0)) 112 } 113 } 114 stats.add(e.name, e.v, e.t) 115} 116 117type statsData struct { 118 Name string 119 Count int 120 Longest time.Duration 121 Total time.Duration 122} 123 124type statsT struct { 125 mu sync.Mutex 126 data map[string]statsData 127} 128 129var stats = &statsT{ 130 data: make(map[string]statsData), 131} 132 133func (s *statsT) add(name, v string, t time.Time) { 134 if !EvalStatsFlag { 135 return 136 } 137 d := time.Since(t) 138 key := fmt.Sprintf("%s:%s", name, v) 139 s.mu.Lock() 140 sd := s.data[key] 141 if d > sd.Longest { 142 sd.Longest = d 143 } 144 sd.Total += d 145 sd.Count++ 146 s.data[key] = sd 147 s.mu.Unlock() 148} 149 150// DumpStats dumps statistics collected if EvalStatsFlag is set. 151func DumpStats() { 152 if !EvalStatsFlag { 153 return 154 } 155 var sv byTotalTime 156 for k, v := range stats.data { 157 v.Name = k 158 sv = append(sv, v) 159 } 160 sort.Sort(sv) 161 fmt.Println("count,longest(ns),total(ns),longest,total,name") 162 for _, s := range sv { 163 fmt.Printf("%d,%d,%d,%v,%v,%s\n", s.Count, s.Longest, s.Total, s.Longest, s.Total, s.Name) 164 } 165} 166 167type byTotalTime []statsData 168 169func (b byTotalTime) Len() int { return len(b) } 170func (b byTotalTime) Swap(i, j int) { b[i], b[j] = b[j], b[i] } 171func (b byTotalTime) Less(i, j int) bool { 172 return b[i].Total > b[j].Total 173} 174 175type shellStatsT struct { 176 mu sync.Mutex 177 duration time.Duration 178 count int 179} 180 181var shellStats = &shellStatsT{} 182 183func (s *shellStatsT) add(d time.Duration) { 184 s.mu.Lock() 185 s.duration += d 186 s.count++ 187 s.mu.Unlock() 188} 189 190func (s *shellStatsT) Duration() time.Duration { 191 s.mu.Lock() 192 defer s.mu.Unlock() 193 return s.duration 194} 195 196func (s *shellStatsT) Count() int { 197 s.mu.Lock() 198 defer s.mu.Unlock() 199 return s.count 200} 201