Home | History | Annotate | Download | only in kati
      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 
     15 package kati
     16 
     17 import (
     18 	"fmt"
     19 	"io"
     20 	"os"
     21 	"sort"
     22 	"sync"
     23 	"time"
     24 )
     25 
     26 type traceEventT struct {
     27 	mu  sync.Mutex
     28 	f   io.WriteCloser
     29 	t0  time.Time
     30 	pid int
     31 }
     32 
     33 const (
     34 	traceEventMain = iota + 1
     35 	// add new ones to use new goroutine.
     36 )
     37 
     38 var traceEvent traceEventT
     39 
     40 // TraceEventStart starts trace event.
     41 func TraceEventStart(f io.WriteCloser) {
     42 	traceEvent.start(f)
     43 }
     44 
     45 // TraceEventStop stops trace event.
     46 func TraceEventStop() {
     47 	traceEvent.stop()
     48 }
     49 
     50 func (t *traceEventT) start(f io.WriteCloser) {
     51 	t.f = f
     52 	t.t0 = time.Now()
     53 	fmt.Fprint(t.f, "[ ")
     54 }
     55 
     56 func (t *traceEventT) enabled() bool {
     57 	return t.f != nil
     58 }
     59 
     60 func (t *traceEventT) stop() {
     61 	fmt.Fprint(t.f, "\n]\n")
     62 	t.f.Close()
     63 }
     64 
     65 type event struct {
     66 	name, v string
     67 	tid     int
     68 	t       time.Time
     69 	emit    bool
     70 }
     71 
     72 func (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 
     89 func (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 
    108 func (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 
    117 type statsData struct {
    118 	Name    string
    119 	Count   int
    120 	Longest time.Duration
    121 	Total   time.Duration
    122 }
    123 
    124 type statsT struct {
    125 	mu   sync.Mutex
    126 	data map[string]statsData
    127 }
    128 
    129 var stats = &statsT{
    130 	data: make(map[string]statsData),
    131 }
    132 
    133 func (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.
    151 func 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 
    167 type byTotalTime []statsData
    168 
    169 func (b byTotalTime) Len() int      { return len(b) }
    170 func (b byTotalTime) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
    171 func (b byTotalTime) Less(i, j int) bool {
    172 	return b[i].Total > b[j].Total
    173 }
    174 
    175 type shellStatsT struct {
    176 	mu       sync.Mutex
    177 	duration time.Duration
    178 	count    int
    179 }
    180 
    181 var shellStats = &shellStatsT{}
    182 
    183 func (s *shellStatsT) add(d time.Duration) {
    184 	s.mu.Lock()
    185 	s.duration += d
    186 	s.count++
    187 	s.mu.Unlock()
    188 }
    189 
    190 func (s *shellStatsT) Duration() time.Duration {
    191 	s.mu.Lock()
    192 	defer s.mu.Unlock()
    193 	return s.duration
    194 }
    195 
    196 func (s *shellStatsT) Count() int {
    197 	s.mu.Lock()
    198 	defer s.mu.Unlock()
    199 	return s.count
    200 }
    201