github.com/bir3/gocompiler@v0.9.2202/src/internal/trace/traceviewer/http.go (about) 1 // Copyright 2023 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 traceviewer 6 7 import ( 8 "embed" 9 "fmt" 10 "html/template" 11 "net/http" 12 "strings" 13 ) 14 15 func MainHandler(views []View) http.Handler { 16 return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { 17 if err := templMain.Execute(w, views); err != nil { 18 http.Error(w, err.Error(), http.StatusInternalServerError) 19 return 20 } 21 }) 22 } 23 24 const CommonStyle = ` 25 /* See https://github.com/golang/pkgsite/blob/master/static/shared/typography/typography.css */ 26 body { 27 font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji'; 28 font-size: 1rem; 29 line-height: normal; 30 max-width: 9in; 31 margin: 1em; 32 } 33 h1 { font-size: 1.5rem; } 34 h2 { font-size: 1.375rem; } 35 h1,h2 { 36 font-weight: 600; 37 line-height: 1.25em; 38 word-break: break-word; 39 } 40 p { color: grey85; font-size:85%; } 41 code, 42 pre, 43 textarea.code { 44 font-family: SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace; 45 font-size: 0.875rem; 46 line-height: 1.5em; 47 } 48 49 pre, 50 textarea.code { 51 background-color: var(--color-background-accented); 52 border: var(--border); 53 border-radius: var(--border-radius); 54 color: var(--color-text); 55 overflow-x: auto; 56 padding: 0.625rem; 57 tab-size: 4; 58 white-space: pre; 59 } 60 ` 61 62 var templMain = template.Must(template.New("").Parse(` 63 <html> 64 <style>` + CommonStyle + `</style> 65 <body> 66 <h1>cmd/trace: the Go trace event viewer</h1> 67 <p> 68 This web server provides various visualizations of an event log gathered during 69 the execution of a Go program that uses the <a href='https://pkg.go.dev/runtime/trace'>runtime/trace</a> package. 70 </p> 71 72 <h2>Event timelines for running goroutines</h2> 73 {{range $i, $view := $}} 74 {{if $view.Ranges}} 75 {{if eq $i 0}} 76 <p> 77 Large traces are split into multiple sections of equal data size 78 (not duration) to avoid overwhelming the visualizer. 79 </p> 80 {{end}} 81 <ul> 82 {{range $index, $e := $view.Ranges}} 83 <li><a href="{{$view.URL $index}}">View trace by {{$view.Type}} ({{$e.Name}})</a></li> 84 {{end}} 85 </ul> 86 {{else}} 87 <ul> 88 <li><a href="{{$view.URL -1}}">View trace by {{$view.Type}}</a></li> 89 </ul> 90 {{end}} 91 {{end}} 92 <p> 93 This view displays a series of timelines for a type of resource. 94 The "by proc" view consists of a timeline for each of the GOMAXPROCS 95 logical processors, showing which goroutine (if any) was running on that 96 logical processor at each moment. 97 The "by thread" view (if available) consists of a similar timeline for each 98 OS thread. 99 100 Each goroutine has an identifying number (e.g. G123), main function, 101 and color. 102 103 A colored bar represents an uninterrupted span of execution. 104 105 Execution of a goroutine may migrate from one logical processor to another, 106 causing a single colored bar to be horizontally continuous but 107 vertically displaced. 108 </p> 109 <p> 110 Clicking on a span reveals information about it, such as its 111 duration, its causal predecessors and successors, and the stack trace 112 at the final moment when it yielded the logical processor, for example 113 because it made a system call or tried to acquire a mutex. 114 115 Directly underneath each bar, a smaller bar or more commonly a fine 116 vertical line indicates an event occurring during its execution. 117 Some of these are related to garbage collection; most indicate that 118 a goroutine yielded its logical processor but then immediately resumed execution 119 on the same logical processor. Clicking on the event displays the stack trace 120 at the moment it occurred. 121 </p> 122 <p> 123 The causal relationships between spans of goroutine execution 124 can be displayed by clicking the Flow Events button at the top. 125 </p> 126 <p> 127 At the top ("STATS"), there are three additional timelines that 128 display statistical information. 129 130 "Goroutines" is a time series of the count of existing goroutines; 131 clicking on it displays their breakdown by state at that moment: 132 running, runnable, or waiting. 133 134 "Heap" is a time series of the amount of heap memory allocated (in orange) 135 and (in green) the allocation limit at which the next GC cycle will begin. 136 137 "Threads" shows the number of kernel threads in existence: there is 138 always one kernel thread per logical processor, and additional threads 139 are created for calls to non-Go code such as a system call or a 140 function written in C. 141 </p> 142 <p> 143 Above the event trace for the first logical processor are 144 traces for various runtime-internal events. 145 146 The "GC" bar shows when the garbage collector is running, and in which stage. 147 Garbage collection may temporarily affect all the logical processors 148 and the other metrics. 149 150 The "Network", "Timers", and "Syscalls" traces indicate events in 151 the runtime that cause goroutines to wake up. 152 </p> 153 <p> 154 The visualization allows you to navigate events at scales ranging from several 155 seconds to a handful of nanoseconds. 156 157 Consult the documentation for the Chromium <a href='https://www.chromium.org/developers/how-tos/trace-event-profiling-tool/'>Trace Event Profiling Tool<a/> 158 for help navigating the view. 159 </p> 160 161 <ul> 162 <li><a href="/goroutines">Goroutine analysis</a></li> 163 </ul> 164 <p> 165 This view displays information about each set of goroutines that 166 shares the same main function. 167 168 Clicking on a main function shows links to the four types of 169 blocking profile (see below) applied to that subset of goroutines. 170 171 It also shows a table of specific goroutine instances, with various 172 execution statistics and a link to the event timeline for each one. 173 174 The timeline displays only the selected goroutine and any others it 175 interacts with via block/unblock events. (The timeline is 176 goroutine-oriented rather than logical processor-oriented.) 177 </p> 178 179 <h2>Profiles</h2> 180 <p> 181 Each link below displays a global profile in zoomable graph form as 182 produced by <a href='https://go.dev/blog/pprof'>pprof</a>'s "web" command. 183 184 In addition there is a link to download the profile for offline 185 analysis with pprof. 186 187 All four profiles represent causes of delay that prevent a goroutine 188 from running on a logical processor: because it was waiting for the network, 189 for a synchronization operation on a mutex or channel, for a system call, 190 or for a logical processor to become available. 191 </p> 192 <ul> 193 <li><a href="/io">Network blocking profile</a> (<a href="/io?raw=1" download="io.profile">⬇</a>)</li> 194 <li><a href="/block">Synchronization blocking profile</a> (<a href="/block?raw=1" download="block.profile">⬇</a>)</li> 195 <li><a href="/syscall">Syscall profile</a> (<a href="/syscall?raw=1" download="syscall.profile">⬇</a>)</li> 196 <li><a href="/sched">Scheduler latency profile</a> (<a href="/sched?raw=1" download="sched.profile">⬇</a>)</li> 197 </ul> 198 199 <h2>User-defined tasks and regions</h2> 200 <p> 201 The trace API allows a target program to annotate a <a 202 href='https://pkg.go.dev/runtime/trace#Region'>region</a> of code 203 within a goroutine, such as a key function, so that its performance 204 can be analyzed. 205 206 <a href='https://pkg.go.dev/runtime/trace#Log'>Log events</a> may be 207 associated with a region to record progress and relevant values. 208 209 The API also allows annotation of higher-level 210 <a href='https://pkg.go.dev/runtime/trace#Task'>tasks</a>, 211 which may involve work across many goroutines. 212 </p> 213 <p> 214 The links below display, for each region and task, a histogram of its execution times. 215 216 Each histogram bucket contains a sample trace that records the 217 sequence of events such as goroutine creations, log events, and 218 subregion start/end times. 219 220 For each task, you can click through to a logical-processor or 221 goroutine-oriented view showing the tasks and regions on the 222 timeline. 223 224 Such information may help uncover which steps in a region are 225 unexpectedly slow, or reveal relationships between the data values 226 logged in a request and its running time. 227 </p> 228 <ul> 229 <li><a href="/usertasks">User-defined tasks</a></li> 230 <li><a href="/userregions">User-defined regions</a></li> 231 </ul> 232 233 <h2>Garbage collection metrics</h2> 234 <ul> 235 <li><a href="/mmu">Minimum mutator utilization</a></li> 236 </ul> 237 <p> 238 This chart indicates the maximum GC pause time (the largest x value 239 for which y is zero), and more generally, the fraction of time that 240 the processors are available to application goroutines ("mutators"), 241 for any time window of a specified size, in the worst case. 242 </p> 243 </body> 244 </html> 245 `)) 246 247 type View struct { 248 Type ViewType 249 Ranges []Range 250 } 251 252 type ViewType string 253 254 const ( 255 ViewProc ViewType = "proc" 256 ViewThread ViewType = "thread" 257 ) 258 259 func (v View) URL(rangeIdx int) string { 260 if rangeIdx < 0 { 261 return fmt.Sprintf("/trace?view=%s", v.Type) 262 } 263 return v.Ranges[rangeIdx].URL(v.Type) 264 } 265 266 type Range struct { 267 Name string 268 Start int 269 End int 270 StartTime int64 271 EndTime int64 272 } 273 274 func (r Range) URL(viewType ViewType) string { 275 return fmt.Sprintf("/trace?view=%s&start=%d&end=%d", viewType, r.Start, r.End) 276 } 277 278 func TraceHandler() http.Handler { 279 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 280 if err := r.ParseForm(); err != nil { 281 http.Error(w, err.Error(), http.StatusInternalServerError) 282 return 283 } 284 html := strings.ReplaceAll(templTrace, "{{PARAMS}}", r.Form.Encode()) 285 w.Write([]byte(html)) 286 }) 287 } 288 289 // https://chromium.googlesource.com/catapult/+/9508452e18f130c98499cb4c4f1e1efaedee8962/tracing/docs/embedding-trace-viewer.md 290 // This is almost verbatim copy of https://chromium-review.googlesource.com/c/catapult/+/2062938/2/tracing/bin/index.html 291 var templTrace = ` 292 <html> 293 <head> 294 <script src="/static/webcomponents.min.js"></script> 295 <script> 296 'use strict'; 297 298 function onTraceViewerImportFail() { 299 document.addEventListener('DOMContentLoaded', function() { 300 document.body.textContent = 301 '/static/trace_viewer_full.html is missing. File a bug in https://golang.org/issue'; 302 }); 303 } 304 </script> 305 306 <link rel="import" href="/static/trace_viewer_full.html" 307 onerror="onTraceViewerImportFail(event)"> 308 309 <style type="text/css"> 310 html, body { 311 box-sizing: border-box; 312 overflow: hidden; 313 margin: 0px; 314 padding: 0; 315 width: 100%; 316 height: 100%; 317 } 318 #trace-viewer { 319 width: 100%; 320 height: 100%; 321 } 322 #trace-viewer:focus { 323 outline: none; 324 } 325 </style> 326 <script> 327 'use strict'; 328 (function() { 329 var viewer; 330 var url; 331 var model; 332 333 function load() { 334 var req = new XMLHttpRequest(); 335 var isBinary = /[.]gz$/.test(url) || /[.]zip$/.test(url); 336 req.overrideMimeType('text/plain; charset=x-user-defined'); 337 req.open('GET', url, true); 338 if (isBinary) 339 req.responseType = 'arraybuffer'; 340 341 req.onreadystatechange = function(event) { 342 if (req.readyState !== 4) 343 return; 344 345 window.setTimeout(function() { 346 if (req.status === 200) 347 onResult(isBinary ? req.response : req.responseText); 348 else 349 onResultFail(req.status); 350 }, 0); 351 }; 352 req.send(null); 353 } 354 355 function onResultFail(err) { 356 var overlay = new tr.ui.b.Overlay(); 357 overlay.textContent = err + ': ' + url + ' could not be loaded'; 358 overlay.title = 'Failed to fetch data'; 359 overlay.visible = true; 360 } 361 362 function onResult(result) { 363 model = new tr.Model(); 364 var opts = new tr.importer.ImportOptions(); 365 opts.shiftWorldToZero = false; 366 var i = new tr.importer.Import(model, opts); 367 var p = i.importTracesWithProgressDialog([result]); 368 p.then(onModelLoaded, onImportFail); 369 } 370 371 function onModelLoaded() { 372 viewer.model = model; 373 viewer.viewTitle = "trace"; 374 375 if (!model || model.bounds.isEmpty) 376 return; 377 var sel = window.location.hash.substr(1); 378 if (sel === '') 379 return; 380 var parts = sel.split(':'); 381 var range = new (tr.b.Range || tr.b.math.Range)(); 382 range.addValue(parseFloat(parts[0])); 383 range.addValue(parseFloat(parts[1])); 384 viewer.trackView.viewport.interestRange.set(range); 385 } 386 387 function onImportFail(err) { 388 var overlay = new tr.ui.b.Overlay(); 389 overlay.textContent = tr.b.normalizeException(err).message; 390 overlay.title = 'Import error'; 391 overlay.visible = true; 392 } 393 394 document.addEventListener('WebComponentsReady', function() { 395 var container = document.createElement('track-view-container'); 396 container.id = 'track_view_container'; 397 398 viewer = document.createElement('tr-ui-timeline-view'); 399 viewer.track_view_container = container; 400 Polymer.dom(viewer).appendChild(container); 401 402 viewer.id = 'trace-viewer'; 403 viewer.globalMode = true; 404 Polymer.dom(document.body).appendChild(viewer); 405 406 url = '/jsontrace?{{PARAMS}}'; 407 load(); 408 }); 409 }()); 410 </script> 411 </head> 412 <body> 413 </body> 414 </html> 415 ` 416 417 //go:embed static/trace_viewer_full.html static/webcomponents.min.js 418 var staticContent embed.FS 419 420 func StaticHandler() http.Handler { 421 return http.FileServer(http.FS(staticContent)) 422 }