Source file
src/cmd/trace/jsontrace.go
1
2
3
4
5 package main
6
7 import (
8 "cmp"
9 "log"
10 "math"
11 "net/http"
12 "slices"
13 "strconv"
14 "time"
15
16 "internal/trace"
17 "internal/trace/traceviewer"
18 )
19
20 func JSONTraceHandler(parsed *parsedTrace) http.Handler {
21 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
22 opts := defaultGenOpts()
23
24 switch r.FormValue("view") {
25 case "thread":
26 opts.mode = traceviewer.ModeThreadOriented
27 }
28 if goids := r.FormValue("goid"); goids != "" {
29
30
31 id, err := strconv.ParseUint(goids, 10, 64)
32 if err != nil {
33 log.Printf("failed to parse goid parameter %q: %v", goids, err)
34 return
35 }
36 goid := trace.GoID(id)
37 g, ok := parsed.summary.Goroutines[goid]
38 if !ok {
39 log.Printf("failed to find goroutine %d", goid)
40 return
41 }
42 opts.mode = traceviewer.ModeGoroutineOriented
43 if g.StartTime != 0 {
44 opts.startTime = g.StartTime.Sub(parsed.startTime())
45 } else {
46 opts.startTime = 0
47 }
48 if g.EndTime != 0 {
49 opts.endTime = g.EndTime.Sub(parsed.startTime())
50 } else {
51 opts.endTime = parsed.endTime().Sub(parsed.startTime())
52 }
53 opts.focusGoroutine = goid
54 opts.goroutines = trace.RelatedGoroutinesV2(parsed.events, goid)
55 } else if taskids := r.FormValue("focustask"); taskids != "" {
56 taskid, err := strconv.ParseUint(taskids, 10, 64)
57 if err != nil {
58 log.Printf("failed to parse focustask parameter %q: %v", taskids, err)
59 return
60 }
61 task, ok := parsed.summary.Tasks[trace.TaskID(taskid)]
62 if !ok || (task.Start == nil && task.End == nil) {
63 log.Printf("failed to find task with id %d", taskid)
64 return
65 }
66 opts.setTask(parsed, task)
67 } else if taskids := r.FormValue("taskid"); taskids != "" {
68 taskid, err := strconv.ParseUint(taskids, 10, 64)
69 if err != nil {
70 log.Printf("failed to parse taskid parameter %q: %v", taskids, err)
71 return
72 }
73 task, ok := parsed.summary.Tasks[trace.TaskID(taskid)]
74 if !ok {
75 log.Printf("failed to find task with id %d", taskid)
76 return
77 }
78
79 opts.mode = traceviewer.ModeGoroutineOriented
80 opts.setTask(parsed, task)
81
82
83
84
85 var firstEv *trace.Event
86 if task.Start != nil {
87 firstEv = task.Start
88 } else {
89 for _, logEv := range task.Logs {
90 if firstEv == nil || logEv.Time() < firstEv.Time() {
91 firstEv = logEv
92 }
93 }
94 if task.End != nil && (firstEv == nil || task.End.Time() < firstEv.Time()) {
95 firstEv = task.End
96 }
97 }
98 if firstEv == nil || firstEv.Goroutine() == trace.NoGoroutine {
99 log.Printf("failed to find task with id %d", taskid)
100 return
101 }
102
103
104 goid := firstEv.Goroutine()
105 opts.focusGoroutine = goid
106 goroutines := make(map[trace.GoID]struct{})
107 for _, task := range opts.tasks {
108
109 for id := range task.Goroutines {
110 goroutines[id] = struct{}{}
111 }
112 }
113 opts.goroutines = goroutines
114 }
115
116
117 start := int64(0)
118 end := int64(math.MaxInt64)
119 if startStr, endStr := r.FormValue("start"), r.FormValue("end"); startStr != "" && endStr != "" {
120 var err error
121 start, err = strconv.ParseInt(startStr, 10, 64)
122 if err != nil {
123 log.Printf("failed to parse start parameter %q: %v", startStr, err)
124 return
125 }
126
127 end, err = strconv.ParseInt(endStr, 10, 64)
128 if err != nil {
129 log.Printf("failed to parse end parameter %q: %v", endStr, err)
130 return
131 }
132 }
133
134 c := traceviewer.ViewerDataTraceConsumer(w, start, end)
135 if err := generateTrace(parsed, opts, c); err != nil {
136 log.Printf("failed to generate trace: %v", err)
137 }
138 })
139 }
140
141
142
143 type traceContext struct {
144 *traceviewer.Emitter
145 startTime trace.Time
146 endTime trace.Time
147 }
148
149
150
151 func (ctx *traceContext) elapsed(now trace.Time) time.Duration {
152 return now.Sub(ctx.startTime)
153 }
154
155 type genOpts struct {
156 mode traceviewer.Mode
157 startTime time.Duration
158 endTime time.Duration
159
160
161 focusGoroutine trace.GoID
162 goroutines map[trace.GoID]struct{}
163 tasks []*trace.UserTaskSummary
164 }
165
166
167 func (opts *genOpts) setTask(parsed *parsedTrace, task *trace.UserTaskSummary) {
168 opts.mode |= traceviewer.ModeTaskOriented
169 if task.Start != nil {
170 opts.startTime = task.Start.Time().Sub(parsed.startTime())
171 } else {
172 opts.startTime = 0
173 }
174 if task.End != nil {
175 opts.endTime = task.End.Time().Sub(parsed.startTime())
176 } else {
177 opts.endTime = parsed.endTime().Sub(parsed.startTime())
178 }
179 opts.tasks = task.Descendents()
180 slices.SortStableFunc(opts.tasks, func(a, b *trace.UserTaskSummary) int {
181 aStart, bStart := parsed.startTime(), parsed.startTime()
182 if a.Start != nil {
183 aStart = a.Start.Time()
184 }
185 if b.Start != nil {
186 bStart = b.Start.Time()
187 }
188 if a.Start != b.Start {
189 return cmp.Compare(aStart, bStart)
190 }
191
192 aEnd, bEnd := parsed.endTime(), parsed.endTime()
193 if a.End != nil {
194 aEnd = a.End.Time()
195 }
196 if b.End != nil {
197 bEnd = b.End.Time()
198 }
199 return cmp.Compare(aEnd, bEnd)
200 })
201 }
202
203 func defaultGenOpts() *genOpts {
204 return &genOpts{
205 startTime: time.Duration(0),
206 endTime: time.Duration(math.MaxInt64),
207 }
208 }
209
210 func generateTrace(parsed *parsedTrace, opts *genOpts, c traceviewer.TraceConsumer) error {
211 ctx := &traceContext{
212 Emitter: traceviewer.NewEmitter(c, opts.startTime, opts.endTime),
213 startTime: parsed.events[0].Time(),
214 endTime: parsed.events[len(parsed.events)-1].Time(),
215 }
216 defer ctx.Flush()
217
218 var g generator
219 if opts.mode&traceviewer.ModeGoroutineOriented != 0 {
220 g = newGoroutineGenerator(ctx, opts.focusGoroutine, opts.goroutines)
221 } else if opts.mode&traceviewer.ModeThreadOriented != 0 {
222 g = newThreadGenerator()
223 } else {
224 g = newProcGenerator()
225 }
226 runGenerator(ctx, g, parsed, opts)
227 return nil
228 }
229
View as plain text