xref: /aosp_15_r20/build/soong/ui/tracer/tracer.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright 2017 Google Inc. All rights reserved.
2*333d2b36SAndroid Build Coastguard Worker//
3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License");
4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License.
5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at
6*333d2b36SAndroid Build Coastguard Worker//
7*333d2b36SAndroid Build Coastguard Worker//     http://www.apache.org/licenses/LICENSE-2.0
8*333d2b36SAndroid Build Coastguard Worker//
9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software
10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS,
11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and
13*333d2b36SAndroid Build Coastguard Worker// limitations under the License.
14*333d2b36SAndroid Build Coastguard Worker
15*333d2b36SAndroid Build Coastguard Worker// This package implements a trace file writer, whose files can be opened in
16*333d2b36SAndroid Build Coastguard Worker// chrome://tracing.
17*333d2b36SAndroid Build Coastguard Worker//
18*333d2b36SAndroid Build Coastguard Worker// It implements the JSON Array Format defined here:
19*333d2b36SAndroid Build Coastguard Worker// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
20*333d2b36SAndroid Build Coastguard Workerpackage tracer
21*333d2b36SAndroid Build Coastguard Worker
22*333d2b36SAndroid Build Coastguard Workerimport (
23*333d2b36SAndroid Build Coastguard Worker	"bytes"
24*333d2b36SAndroid Build Coastguard Worker	"compress/gzip"
25*333d2b36SAndroid Build Coastguard Worker	"encoding/json"
26*333d2b36SAndroid Build Coastguard Worker	"fmt"
27*333d2b36SAndroid Build Coastguard Worker	"io"
28*333d2b36SAndroid Build Coastguard Worker	"os"
29*333d2b36SAndroid Build Coastguard Worker	"strings"
30*333d2b36SAndroid Build Coastguard Worker	"sync"
31*333d2b36SAndroid Build Coastguard Worker	"time"
32*333d2b36SAndroid Build Coastguard Worker
33*333d2b36SAndroid Build Coastguard Worker	"android/soong/ui/logger"
34*333d2b36SAndroid Build Coastguard Worker	"android/soong/ui/status"
35*333d2b36SAndroid Build Coastguard Worker)
36*333d2b36SAndroid Build Coastguard Worker
37*333d2b36SAndroid Build Coastguard Workertype Thread uint64
38*333d2b36SAndroid Build Coastguard Worker
39*333d2b36SAndroid Build Coastguard Workerconst (
40*333d2b36SAndroid Build Coastguard Worker	MainThread     = Thread(iota)
41*333d2b36SAndroid Build Coastguard Worker	MaxInitThreads = Thread(iota)
42*333d2b36SAndroid Build Coastguard Worker)
43*333d2b36SAndroid Build Coastguard Worker
44*333d2b36SAndroid Build Coastguard Workertype Tracer interface {
45*333d2b36SAndroid Build Coastguard Worker	Begin(name string, thread Thread)
46*333d2b36SAndroid Build Coastguard Worker	End(thread Thread)
47*333d2b36SAndroid Build Coastguard Worker	Complete(name string, thread Thread, begin, end uint64)
48*333d2b36SAndroid Build Coastguard Worker
49*333d2b36SAndroid Build Coastguard Worker	CountersAtTime(name string, thread Thread, time uint64, counters []Counter)
50*333d2b36SAndroid Build Coastguard Worker
51*333d2b36SAndroid Build Coastguard Worker	ImportMicrofactoryLog(filename string)
52*333d2b36SAndroid Build Coastguard Worker
53*333d2b36SAndroid Build Coastguard Worker	StatusTracer() status.StatusOutput
54*333d2b36SAndroid Build Coastguard Worker
55*333d2b36SAndroid Build Coastguard Worker	NewThread(name string) Thread
56*333d2b36SAndroid Build Coastguard Worker}
57*333d2b36SAndroid Build Coastguard Worker
58*333d2b36SAndroid Build Coastguard Workertype tracerImpl struct {
59*333d2b36SAndroid Build Coastguard Worker	lock sync.Mutex
60*333d2b36SAndroid Build Coastguard Worker	log  logger.Logger
61*333d2b36SAndroid Build Coastguard Worker
62*333d2b36SAndroid Build Coastguard Worker	buf  bytes.Buffer
63*333d2b36SAndroid Build Coastguard Worker	file *os.File
64*333d2b36SAndroid Build Coastguard Worker	w    io.WriteCloser
65*333d2b36SAndroid Build Coastguard Worker
66*333d2b36SAndroid Build Coastguard Worker	firstEvent bool
67*333d2b36SAndroid Build Coastguard Worker	nextTid    uint64
68*333d2b36SAndroid Build Coastguard Worker}
69*333d2b36SAndroid Build Coastguard Worker
70*333d2b36SAndroid Build Coastguard Workervar _ Tracer = &tracerImpl{}
71*333d2b36SAndroid Build Coastguard Worker
72*333d2b36SAndroid Build Coastguard Workertype viewerEvent struct {
73*333d2b36SAndroid Build Coastguard Worker	Name  string      `json:"name,omitempty"`
74*333d2b36SAndroid Build Coastguard Worker	Phase string      `json:"ph"`
75*333d2b36SAndroid Build Coastguard Worker	Scope string      `json:"s,omitempty"`
76*333d2b36SAndroid Build Coastguard Worker	Time  uint64      `json:"ts"`
77*333d2b36SAndroid Build Coastguard Worker	Dur   uint64      `json:"dur,omitempty"`
78*333d2b36SAndroid Build Coastguard Worker	Pid   uint64      `json:"pid"`
79*333d2b36SAndroid Build Coastguard Worker	Tid   uint64      `json:"tid"`
80*333d2b36SAndroid Build Coastguard Worker	ID    uint64      `json:"id,omitempty"`
81*333d2b36SAndroid Build Coastguard Worker	Arg   interface{} `json:"args,omitempty"`
82*333d2b36SAndroid Build Coastguard Worker}
83*333d2b36SAndroid Build Coastguard Worker
84*333d2b36SAndroid Build Coastguard Workertype nameArg struct {
85*333d2b36SAndroid Build Coastguard Worker	Name string `json:"name"`
86*333d2b36SAndroid Build Coastguard Worker}
87*333d2b36SAndroid Build Coastguard Worker
88*333d2b36SAndroid Build Coastguard Workertype nopCloser struct{ io.Writer }
89*333d2b36SAndroid Build Coastguard Worker
90*333d2b36SAndroid Build Coastguard Workerfunc (nopCloser) Close() error { return nil }
91*333d2b36SAndroid Build Coastguard Worker
92*333d2b36SAndroid Build Coastguard Worker// New creates a new Tracer, storing log in order to log errors later.
93*333d2b36SAndroid Build Coastguard Worker// Events are buffered in memory until SetOutput is called.
94*333d2b36SAndroid Build Coastguard Workerfunc New(log logger.Logger) *tracerImpl {
95*333d2b36SAndroid Build Coastguard Worker	ret := &tracerImpl{
96*333d2b36SAndroid Build Coastguard Worker		log: log,
97*333d2b36SAndroid Build Coastguard Worker
98*333d2b36SAndroid Build Coastguard Worker		firstEvent: true,
99*333d2b36SAndroid Build Coastguard Worker		nextTid:    uint64(MaxInitThreads),
100*333d2b36SAndroid Build Coastguard Worker	}
101*333d2b36SAndroid Build Coastguard Worker	ret.startBuffer()
102*333d2b36SAndroid Build Coastguard Worker
103*333d2b36SAndroid Build Coastguard Worker	return ret
104*333d2b36SAndroid Build Coastguard Worker}
105*333d2b36SAndroid Build Coastguard Worker
106*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) startBuffer() {
107*333d2b36SAndroid Build Coastguard Worker	t.w = nopCloser{&t.buf}
108*333d2b36SAndroid Build Coastguard Worker	fmt.Fprintln(t.w, "[")
109*333d2b36SAndroid Build Coastguard Worker
110*333d2b36SAndroid Build Coastguard Worker	t.defineThread(MainThread, "main")
111*333d2b36SAndroid Build Coastguard Worker}
112*333d2b36SAndroid Build Coastguard Worker
113*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) close() {
114*333d2b36SAndroid Build Coastguard Worker	if t.file != nil {
115*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintln(t.w, "]")
116*333d2b36SAndroid Build Coastguard Worker
117*333d2b36SAndroid Build Coastguard Worker		if err := t.w.Close(); err != nil {
118*333d2b36SAndroid Build Coastguard Worker			t.log.Println("Error closing trace writer:", err)
119*333d2b36SAndroid Build Coastguard Worker		}
120*333d2b36SAndroid Build Coastguard Worker
121*333d2b36SAndroid Build Coastguard Worker		if err := t.file.Close(); err != nil {
122*333d2b36SAndroid Build Coastguard Worker			t.log.Println("Error closing trace file:", err)
123*333d2b36SAndroid Build Coastguard Worker		}
124*333d2b36SAndroid Build Coastguard Worker		t.file = nil
125*333d2b36SAndroid Build Coastguard Worker		t.startBuffer()
126*333d2b36SAndroid Build Coastguard Worker	}
127*333d2b36SAndroid Build Coastguard Worker}
128*333d2b36SAndroid Build Coastguard Worker
129*333d2b36SAndroid Build Coastguard Worker// SetOutput creates the output file (rotating old files).
130*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) SetOutput(filename string) {
131*333d2b36SAndroid Build Coastguard Worker	t.lock.Lock()
132*333d2b36SAndroid Build Coastguard Worker	defer t.lock.Unlock()
133*333d2b36SAndroid Build Coastguard Worker
134*333d2b36SAndroid Build Coastguard Worker	t.close()
135*333d2b36SAndroid Build Coastguard Worker
136*333d2b36SAndroid Build Coastguard Worker	// chrome://tracing requires that compressed trace files end in .gz
137*333d2b36SAndroid Build Coastguard Worker	if !strings.HasSuffix(filename, ".gz") {
138*333d2b36SAndroid Build Coastguard Worker		filename += ".gz"
139*333d2b36SAndroid Build Coastguard Worker	}
140*333d2b36SAndroid Build Coastguard Worker
141*333d2b36SAndroid Build Coastguard Worker	f, err := logger.CreateFileWithRotation(filename, 5)
142*333d2b36SAndroid Build Coastguard Worker	if err != nil {
143*333d2b36SAndroid Build Coastguard Worker		t.log.Println("Failed to create trace file:", err)
144*333d2b36SAndroid Build Coastguard Worker		return
145*333d2b36SAndroid Build Coastguard Worker	}
146*333d2b36SAndroid Build Coastguard Worker	// Save the file, since closing the gzip Writer doesn't close the
147*333d2b36SAndroid Build Coastguard Worker	// underlying file.
148*333d2b36SAndroid Build Coastguard Worker	t.file = f
149*333d2b36SAndroid Build Coastguard Worker	t.w = gzip.NewWriter(f)
150*333d2b36SAndroid Build Coastguard Worker
151*333d2b36SAndroid Build Coastguard Worker	// Write out everything that happened since the start
152*333d2b36SAndroid Build Coastguard Worker	if _, err := io.Copy(t.w, &t.buf); err != nil {
153*333d2b36SAndroid Build Coastguard Worker		t.log.Println("Failed to write trace buffer to file:", err)
154*333d2b36SAndroid Build Coastguard Worker	}
155*333d2b36SAndroid Build Coastguard Worker	t.buf = bytes.Buffer{}
156*333d2b36SAndroid Build Coastguard Worker}
157*333d2b36SAndroid Build Coastguard Worker
158*333d2b36SAndroid Build Coastguard Worker// Close closes the output file. Any future events will be buffered until the
159*333d2b36SAndroid Build Coastguard Worker// next call to SetOutput.
160*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) Close() {
161*333d2b36SAndroid Build Coastguard Worker	t.lock.Lock()
162*333d2b36SAndroid Build Coastguard Worker	defer t.lock.Unlock()
163*333d2b36SAndroid Build Coastguard Worker
164*333d2b36SAndroid Build Coastguard Worker	t.close()
165*333d2b36SAndroid Build Coastguard Worker}
166*333d2b36SAndroid Build Coastguard Worker
167*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) writeEvent(event *viewerEvent) {
168*333d2b36SAndroid Build Coastguard Worker	t.lock.Lock()
169*333d2b36SAndroid Build Coastguard Worker	defer t.lock.Unlock()
170*333d2b36SAndroid Build Coastguard Worker
171*333d2b36SAndroid Build Coastguard Worker	t.writeEventLocked(event)
172*333d2b36SAndroid Build Coastguard Worker}
173*333d2b36SAndroid Build Coastguard Worker
174*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) writeEventLocked(event *viewerEvent) {
175*333d2b36SAndroid Build Coastguard Worker	bytes, err := json.Marshal(event)
176*333d2b36SAndroid Build Coastguard Worker	if err != nil {
177*333d2b36SAndroid Build Coastguard Worker		t.log.Println("Failed to marshal event:", err)
178*333d2b36SAndroid Build Coastguard Worker		t.log.Verbosef("Event: %#v", event)
179*333d2b36SAndroid Build Coastguard Worker		return
180*333d2b36SAndroid Build Coastguard Worker	}
181*333d2b36SAndroid Build Coastguard Worker
182*333d2b36SAndroid Build Coastguard Worker	if !t.firstEvent {
183*333d2b36SAndroid Build Coastguard Worker		fmt.Fprintln(t.w, ",")
184*333d2b36SAndroid Build Coastguard Worker	} else {
185*333d2b36SAndroid Build Coastguard Worker		t.firstEvent = false
186*333d2b36SAndroid Build Coastguard Worker	}
187*333d2b36SAndroid Build Coastguard Worker
188*333d2b36SAndroid Build Coastguard Worker	if _, err = t.w.Write(bytes); err != nil {
189*333d2b36SAndroid Build Coastguard Worker		t.log.Println("Trace write error:", err)
190*333d2b36SAndroid Build Coastguard Worker	}
191*333d2b36SAndroid Build Coastguard Worker}
192*333d2b36SAndroid Build Coastguard Worker
193*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) defineThread(thread Thread, name string) {
194*333d2b36SAndroid Build Coastguard Worker	t.writeEventLocked(&viewerEvent{
195*333d2b36SAndroid Build Coastguard Worker		Name:  "thread_name",
196*333d2b36SAndroid Build Coastguard Worker		Phase: "M",
197*333d2b36SAndroid Build Coastguard Worker		Pid:   0,
198*333d2b36SAndroid Build Coastguard Worker		Tid:   uint64(thread),
199*333d2b36SAndroid Build Coastguard Worker		Arg: &nameArg{
200*333d2b36SAndroid Build Coastguard Worker			Name: name,
201*333d2b36SAndroid Build Coastguard Worker		},
202*333d2b36SAndroid Build Coastguard Worker	})
203*333d2b36SAndroid Build Coastguard Worker}
204*333d2b36SAndroid Build Coastguard Worker
205*333d2b36SAndroid Build Coastguard Worker// NewThread returns a new Thread with an unused tid, writing the name out to
206*333d2b36SAndroid Build Coastguard Worker// the trace file.
207*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) NewThread(name string) Thread {
208*333d2b36SAndroid Build Coastguard Worker	t.lock.Lock()
209*333d2b36SAndroid Build Coastguard Worker	defer t.lock.Unlock()
210*333d2b36SAndroid Build Coastguard Worker
211*333d2b36SAndroid Build Coastguard Worker	ret := Thread(t.nextTid)
212*333d2b36SAndroid Build Coastguard Worker	t.nextTid += 1
213*333d2b36SAndroid Build Coastguard Worker
214*333d2b36SAndroid Build Coastguard Worker	t.defineThread(ret, name)
215*333d2b36SAndroid Build Coastguard Worker	return ret
216*333d2b36SAndroid Build Coastguard Worker}
217*333d2b36SAndroid Build Coastguard Worker
218*333d2b36SAndroid Build Coastguard Worker// Begin starts a new Duration Event. More than one Duration Event may be active
219*333d2b36SAndroid Build Coastguard Worker// at a time on each Thread, but they're nested.
220*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) Begin(name string, thread Thread) {
221*333d2b36SAndroid Build Coastguard Worker	t.writeEvent(&viewerEvent{
222*333d2b36SAndroid Build Coastguard Worker		Name:  name,
223*333d2b36SAndroid Build Coastguard Worker		Phase: "B",
224*333d2b36SAndroid Build Coastguard Worker		Time:  uint64(time.Now().UnixNano()) / 1000,
225*333d2b36SAndroid Build Coastguard Worker		Pid:   0,
226*333d2b36SAndroid Build Coastguard Worker		Tid:   uint64(thread),
227*333d2b36SAndroid Build Coastguard Worker	})
228*333d2b36SAndroid Build Coastguard Worker}
229*333d2b36SAndroid Build Coastguard Worker
230*333d2b36SAndroid Build Coastguard Worker// End finishes the most recent active Duration Event on the thread.
231*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) End(thread Thread) {
232*333d2b36SAndroid Build Coastguard Worker	t.writeEvent(&viewerEvent{
233*333d2b36SAndroid Build Coastguard Worker		Phase: "E",
234*333d2b36SAndroid Build Coastguard Worker		Time:  uint64(time.Now().UnixNano()) / 1000,
235*333d2b36SAndroid Build Coastguard Worker		Pid:   0,
236*333d2b36SAndroid Build Coastguard Worker		Tid:   uint64(thread),
237*333d2b36SAndroid Build Coastguard Worker	})
238*333d2b36SAndroid Build Coastguard Worker}
239*333d2b36SAndroid Build Coastguard Worker
240*333d2b36SAndroid Build Coastguard Worker// Complete writes a Complete Event, which are like Duration Events, but include
241*333d2b36SAndroid Build Coastguard Worker// a begin and end timestamp in the same event.
242*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) Complete(name string, thread Thread, begin, end uint64) {
243*333d2b36SAndroid Build Coastguard Worker	t.writeEvent(&viewerEvent{
244*333d2b36SAndroid Build Coastguard Worker		Name:  name,
245*333d2b36SAndroid Build Coastguard Worker		Phase: "X",
246*333d2b36SAndroid Build Coastguard Worker		Time:  begin / 1000,
247*333d2b36SAndroid Build Coastguard Worker		Dur:   (end - begin) / 1000,
248*333d2b36SAndroid Build Coastguard Worker		Pid:   0,
249*333d2b36SAndroid Build Coastguard Worker		Tid:   uint64(thread),
250*333d2b36SAndroid Build Coastguard Worker	})
251*333d2b36SAndroid Build Coastguard Worker}
252*333d2b36SAndroid Build Coastguard Worker
253*333d2b36SAndroid Build Coastguard Workertype Counter struct {
254*333d2b36SAndroid Build Coastguard Worker	Name  string
255*333d2b36SAndroid Build Coastguard Worker	Value int64
256*333d2b36SAndroid Build Coastguard Worker}
257*333d2b36SAndroid Build Coastguard Worker
258*333d2b36SAndroid Build Coastguard Workertype countersMarshaller []Counter
259*333d2b36SAndroid Build Coastguard Worker
260*333d2b36SAndroid Build Coastguard Workervar _ json.Marshaler = countersMarshaller(nil)
261*333d2b36SAndroid Build Coastguard Worker
262*333d2b36SAndroid Build Coastguard Workerfunc (counters countersMarshaller) MarshalJSON() ([]byte, error) {
263*333d2b36SAndroid Build Coastguard Worker	// This produces similar output to a map[string]int64, but maintains the order of the slice.
264*333d2b36SAndroid Build Coastguard Worker	buf := bytes.Buffer{}
265*333d2b36SAndroid Build Coastguard Worker	buf.WriteRune('{')
266*333d2b36SAndroid Build Coastguard Worker	for i, counter := range counters {
267*333d2b36SAndroid Build Coastguard Worker		name, err := json.Marshal(counter.Name)
268*333d2b36SAndroid Build Coastguard Worker		if err != nil {
269*333d2b36SAndroid Build Coastguard Worker			return nil, err
270*333d2b36SAndroid Build Coastguard Worker		}
271*333d2b36SAndroid Build Coastguard Worker		buf.Write(name)
272*333d2b36SAndroid Build Coastguard Worker		buf.WriteByte(':')
273*333d2b36SAndroid Build Coastguard Worker		value, err := json.Marshal(counter.Value)
274*333d2b36SAndroid Build Coastguard Worker		if err != nil {
275*333d2b36SAndroid Build Coastguard Worker			return nil, err
276*333d2b36SAndroid Build Coastguard Worker		}
277*333d2b36SAndroid Build Coastguard Worker		buf.Write(value)
278*333d2b36SAndroid Build Coastguard Worker		if i != len(counters)-1 {
279*333d2b36SAndroid Build Coastguard Worker			buf.WriteRune(',')
280*333d2b36SAndroid Build Coastguard Worker		}
281*333d2b36SAndroid Build Coastguard Worker	}
282*333d2b36SAndroid Build Coastguard Worker	buf.WriteRune('}')
283*333d2b36SAndroid Build Coastguard Worker	return buf.Bytes(), nil
284*333d2b36SAndroid Build Coastguard Worker}
285*333d2b36SAndroid Build Coastguard Worker
286*333d2b36SAndroid Build Coastguard Worker// CountersAtTime writes a Counter event at the given timestamp in nanoseconds.
287*333d2b36SAndroid Build Coastguard Workerfunc (t *tracerImpl) CountersAtTime(name string, thread Thread, time uint64, counters []Counter) {
288*333d2b36SAndroid Build Coastguard Worker	t.writeEvent(&viewerEvent{
289*333d2b36SAndroid Build Coastguard Worker		Name:  name,
290*333d2b36SAndroid Build Coastguard Worker		Phase: "C",
291*333d2b36SAndroid Build Coastguard Worker		Time:  time / 1000,
292*333d2b36SAndroid Build Coastguard Worker		Pid:   0,
293*333d2b36SAndroid Build Coastguard Worker		Tid:   uint64(thread),
294*333d2b36SAndroid Build Coastguard Worker		Arg:   countersMarshaller(counters),
295*333d2b36SAndroid Build Coastguard Worker	})
296*333d2b36SAndroid Build Coastguard Worker}
297