Skip to content

Commit daaf361

Browse files
committed
internal/traceparser: provide parser that uses less space and parses segments of runtime trace files
Traceparser generally takes 20-30% less space than internal/trace. The only user of these pakcages is cmd/trace, and the new package lets it handle some trace files that were too large. The new parser will also convert segments of the raw trace file (e.g. the last 10 seconds) to Events. Trace files from go 1.8 and before are not supported. Change-Id: If83fa183246db8f75182ccd3ba8df07673c0ebd0 Reviewed-on: https://go-review.googlesource.com/c/137635 Run-TryBot: Peter Weinberger <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Hyang-Ah Hana Kim <[email protected]>
1 parent 5ddb209 commit daaf361

34 files changed

+3057
-2
lines changed

misc/nacl/README

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ scheme.
2626
# Download NaCl
2727

2828
Download nacl_sdk.zip file from
29-
https://developers.google.com/native-client/dev/sdk/download
29+
https://developer.chrome.com/native-client/sdk/download
3030
and unpack it. I chose /opt/nacl_sdk.
3131

3232
# Update
@@ -37,7 +37,7 @@ sdk. These are released every 6-8 weeks, in line with Chrome releases.
3737
% cd /opt/nacl_sdk
3838
% ./naclsdk update
3939

40-
At this time pepper_40 is the stable version. The NaCl port needs at least pepper_39
40+
At this time pepper_49 is the stable version. The NaCl port needs at least pepper_39
4141
to work. If naclsdk downloads a later version, please adjust accordingly.
4242

4343
The cmd/go helper scripts expect that the loaders sel_ldr_{x86_{32,64},arm} and

misc/nacl/testzip.proto

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,9 @@ go src=..
154154
trace
155155
testdata
156156
+
157+
traceparser
158+
testdata
159+
+
157160
io
158161
+
159162
mime

src/go/build/deps_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ var pkgDeps = map[string][]string{
272272
"internal/goroot": {"L4", "OS"},
273273
"internal/singleflight": {"sync"},
274274
"internal/trace": {"L4", "OS"},
275+
"internal/traceparser": {"L4", "internal/traceparser/filebuf"},
276+
"internal/traceparser/filebuf": {"L4", "OS"},
275277
"math/big": {"L4"},
276278
"mime": {"L4", "OS", "syscall", "internal/syscall/windows/registry"},
277279
"mime/quotedprintable": {"L4"},
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
// Copyright 2018 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package traceparser
6+
7+
// postProcess is a final check of consistency, and if all is well,
8+
// adds links to Events
9+
10+
import (
11+
"fmt"
12+
)
13+
14+
type gStatus int
15+
16+
const (
17+
gDead gStatus = iota
18+
gRunnable
19+
gRunning
20+
gWaiting
21+
)
22+
23+
// This code is copied from internal/trace/parser.go. With greater understanding it could be
24+
// simplified. Sets ev.P for GCStart, and set various Link fields
25+
func (p *Parsed) postProcess(events []*Event) error {
26+
type gdesc struct {
27+
state gStatus
28+
ev *Event
29+
evStart *Event
30+
evCreate *Event
31+
evMarkAssist *Event
32+
}
33+
type pdesc struct {
34+
running bool
35+
g uint64
36+
evSTW *Event
37+
evSweep *Event
38+
}
39+
40+
gs := make(map[uint64]gdesc)
41+
ps := make(map[int]pdesc)
42+
tasks := make(map[uint64]*Event) // task id to task creation events
43+
activeRegions := make(map[uint64][]*Event) // goroutine id to stack of spans
44+
gs[0] = gdesc{state: gRunning}
45+
var evGC, evSTW *Event
46+
47+
checkRunning := func(pd pdesc, g gdesc, ev *Event, allowG0 bool) error {
48+
if g.state != gRunning {
49+
return fmt.Errorf("saw %v, but g %d is not running", ev, ev.G)
50+
}
51+
if pd.g != ev.G {
52+
return fmt.Errorf("saw %v, but it's P is running %d, not %d", ev, pd.g, ev.G)
53+
}
54+
if !allowG0 && ev.G == 0 {
55+
return fmt.Errorf("saw %v with unexpected g==0", ev)
56+
}
57+
return nil
58+
}
59+
for i, ev := range events {
60+
g := gs[ev.G]
61+
px := ps[int(ev.P)]
62+
switch ev.Type {
63+
case EvProcStart:
64+
if px.running {
65+
return fmt.Errorf("%d: running before start %s", i, ev)
66+
}
67+
px.running = true
68+
case EvProcStop:
69+
if !px.running {
70+
return fmt.Errorf("%d: p %d not running %s", i, ev.P, ev)
71+
}
72+
if px.g != 0 {
73+
return fmt.Errorf("p %d is running a goroutine %s", ev.P, ev)
74+
}
75+
px.running = false
76+
case EvGCStart:
77+
if evGC != nil {
78+
return fmt.Errorf("GC already running %s, was %s", ev, evGC)
79+
}
80+
evGC = ev
81+
// Attribute this to the global GC state.
82+
ev.P = GCP
83+
case EvGCDone:
84+
if evGC == nil {
85+
return fmt.Errorf("%d:%s bogus GC end", i, ev)
86+
}
87+
evGC.Link = ev
88+
evGC = nil
89+
case EvGCSTWStart:
90+
evp := &evSTW
91+
if p.Version < 1010 {
92+
// Before 1.10, EvGCSTWStart was per-P.
93+
evp = &px.evSTW
94+
}
95+
if *evp != nil {
96+
return fmt.Errorf("STW %s still running at %s", *evp, ev)
97+
}
98+
*evp = ev
99+
case EvGCSTWDone:
100+
evp := &evSTW
101+
if p.Version < 1010 {
102+
// Before 1.10, EvGCSTWDone was per-P.
103+
evp = &px.evSTW
104+
}
105+
if *evp == nil {
106+
return fmt.Errorf("%d: no STW running %s", i, ev)
107+
}
108+
(*evp).Link = ev
109+
*evp = nil
110+
case EvGCMarkAssistStart:
111+
if g.evMarkAssist != nil {
112+
return fmt.Errorf("%d: MarkAssist %s is still running at %s",
113+
i, g.evMarkAssist, ev)
114+
}
115+
g.evMarkAssist = ev
116+
case EvGCMarkAssistDone:
117+
// Unlike most events, mark assists can be in progress when a
118+
// goroutine starts tracing, so we can't report an error here.
119+
if g.evMarkAssist != nil {
120+
g.evMarkAssist.Link = ev
121+
g.evMarkAssist = nil
122+
}
123+
case EvGCSweepStart:
124+
if px.evSweep != nil {
125+
return fmt.Errorf("sweep not done %d: %s", i, ev)
126+
}
127+
px.evSweep = ev
128+
case EvGCSweepDone:
129+
if px.evSweep == nil {
130+
return fmt.Errorf("%d: no sweep happening %s", i, ev)
131+
}
132+
px.evSweep.Link = ev
133+
px.evSweep = nil
134+
case EvGoWaiting:
135+
if g.state != gRunnable {
136+
return fmt.Errorf("not runnable before %d:%s", i, ev)
137+
}
138+
g.state = gWaiting
139+
g.ev = ev
140+
case EvGoInSyscall:
141+
if g.state != gRunnable {
142+
return fmt.Errorf("not runnable before %d:%s", i, ev)
143+
}
144+
g.state = gWaiting
145+
g.ev = ev
146+
case EvGoCreate:
147+
if err := checkRunning(px, g, ev, true); err != nil {
148+
return err
149+
}
150+
if _, ok := gs[ev.Args[0]]; ok {
151+
return fmt.Errorf("%d: already exists %s", i, ev)
152+
}
153+
gs[ev.Args[0]] = gdesc{state: gRunnable, ev: ev, evCreate: ev}
154+
case EvGoStart, EvGoStartLabel:
155+
if g.state != gRunnable {
156+
return fmt.Errorf("not runnable before start %d:%s %+v", i, ev, g)
157+
}
158+
if px.g != 0 {
159+
return fmt.Errorf("%d: %s has p running %d already %v", i, ev, px.g, px)
160+
}
161+
g.state = gRunning
162+
g.evStart = ev
163+
px.g = ev.G
164+
if g.evCreate != nil {
165+
if p.Version < 1007 {
166+
// +1 because symbolizer expects return pc.
167+
//PJW: aren't doing < 1007. ev.stk = []*Frame{{PC: g.evCreate.args[1] + 1}}
168+
} else {
169+
ev.StkID = uint32(g.evCreate.Args[1])
170+
}
171+
g.evCreate = nil
172+
}
173+
174+
if g.ev != nil {
175+
g.ev.Link = ev
176+
g.ev = nil
177+
}
178+
case EvGoEnd, EvGoStop:
179+
if err := checkRunning(px, g, ev, false); err != nil {
180+
return fmt.Errorf("%d: %s", i, err)
181+
}
182+
g.evStart.Link = ev
183+
g.evStart = nil
184+
g.state = gDead
185+
px.g = 0
186+
187+
if ev.Type == EvGoEnd { // flush all active Regions
188+
spans := activeRegions[ev.G]
189+
for _, s := range spans {
190+
s.Link = ev
191+
}
192+
delete(activeRegions, ev.G)
193+
}
194+
case EvGoSched, EvGoPreempt:
195+
if err := checkRunning(px, g, ev, false); err != nil {
196+
return err
197+
}
198+
g.state = gRunnable
199+
g.evStart.Link = ev
200+
g.evStart = nil
201+
px.g = 0
202+
g.ev = ev
203+
case EvGoUnblock:
204+
if g.state != gRunning { // PJW, error message
205+
return fmt.Errorf("Event %d (%s) is not running at unblock %s", i, ev, g.state)
206+
207+
}
208+
if ev.P != TimerP && px.g != ev.G {
209+
// PJW: do better here.
210+
return fmt.Errorf("%d: %s p %d is not running g", i, ev, px.g)
211+
}
212+
g1 := gs[ev.Args[0]]
213+
if g1.state != gWaiting {
214+
return fmt.Errorf("g %v is not waiting before unpark i=%d g1=%v %s",
215+
ev.Args[0], i, g1, ev)
216+
}
217+
if g1.ev != nil && g1.ev.Type == EvGoBlockNet && ev.P != TimerP {
218+
ev.P = NetpollP
219+
}
220+
if g1.ev != nil {
221+
g1.ev.Link = ev
222+
}
223+
g1.state = gRunnable
224+
g1.ev = ev
225+
gs[ev.Args[0]] = g1
226+
case EvGoSysCall:
227+
if err := checkRunning(px, g, ev, false); err != nil {
228+
return err
229+
}
230+
g.ev = ev
231+
case EvGoSysBlock:
232+
if err := checkRunning(px, g, ev, false); err != nil {
233+
return err
234+
}
235+
g.state = gWaiting
236+
g.evStart.Link = ev
237+
g.evStart = nil
238+
px.g = 0
239+
case EvGoSysExit:
240+
if g.state != gWaiting {
241+
return fmt.Errorf("not waiting when %s", ev)
242+
}
243+
if g.ev != nil && g.ev.Type == EvGoSysCall {
244+
g.ev.Link = ev
245+
}
246+
g.state = gRunnable
247+
g.ev = ev
248+
case EvGoSleep, EvGoBlock, EvGoBlockSend, EvGoBlockRecv,
249+
EvGoBlockSelect, EvGoBlockSync, EvGoBlockCond, EvGoBlockNet, EvGoBlockGC:
250+
if err := checkRunning(px, g, ev, false); err != nil {
251+
return err
252+
}
253+
g.state = gWaiting
254+
g.ev = ev
255+
g.evStart.Link = ev
256+
g.evStart = nil
257+
px.g = 0
258+
case EvUserTaskCreate:
259+
taskid := ev.Args[0]
260+
if prevEv, ok := tasks[taskid]; ok {
261+
return fmt.Errorf("task id conflicts (id:%d), %q vs %q", taskid, ev, prevEv)
262+
}
263+
tasks[ev.Args[0]] = ev
264+
case EvUserTaskEnd:
265+
if prevEv, ok := tasks[ev.Args[0]]; ok {
266+
prevEv.Link = ev
267+
ev.Link = prevEv
268+
}
269+
case EvUserRegion:
270+
mode := ev.Args[1]
271+
spans := activeRegions[ev.G]
272+
if mode == 0 { // span start
273+
activeRegions[ev.G] = append(spans, ev) // push
274+
} else if mode == 1 { // span end
275+
n := len(spans)
276+
if n > 0 { // matching span start event is in the trace.
277+
s := spans[n-1]
278+
if s.Args[0] != ev.Args[0] || s.SArgs[0] != ev.SArgs[0] { // task id, span name mismatch
279+
return fmt.Errorf("misuse of span in goroutine %d: span end %q when the inner-most active span start event is %q",
280+
ev.G, ev, s)
281+
}
282+
// Link span start event with span end event
283+
s.Link = ev
284+
ev.Link = s
285+
286+
if n > 1 {
287+
activeRegions[ev.G] = spans[:n-1]
288+
} else {
289+
delete(activeRegions, ev.G)
290+
}
291+
}
292+
} else {
293+
return fmt.Errorf("invalid user region, mode: %q", ev)
294+
}
295+
}
296+
gs[ev.G] = g
297+
ps[int(ev.P)] = px
298+
}
299+
return nil
300+
}
301+
func (g gStatus) String() string {
302+
switch g {
303+
case gDead:
304+
return "gDead"
305+
case gRunnable:
306+
return "gRunnable"
307+
case gRunning:
308+
return "gRunning"
309+
case gWaiting:
310+
return "gWaiting"
311+
}
312+
return fmt.Sprintf("gStatus?%d", g)
313+
}

0 commit comments

Comments
 (0)