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