• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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