github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/src/cmd/compile/internal/ssa/html.go (about) 1 // Copyright 2015 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 ssa 6 7 import ( 8 "bytes" 9 "cmd/internal/src" 10 "fmt" 11 "html" 12 "io" 13 "os" 14 "strings" 15 ) 16 17 type HTMLWriter struct { 18 Logger 19 w io.WriteCloser 20 } 21 22 func NewHTMLWriter(path string, logger Logger, funcname string) *HTMLWriter { 23 out, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) 24 if err != nil { 25 logger.Fatalf(src.NoXPos, "%v", err) 26 } 27 html := HTMLWriter{w: out, Logger: logger} 28 html.start(funcname) 29 return &html 30 } 31 32 func (w *HTMLWriter) start(name string) { 33 if w == nil { 34 return 35 } 36 w.WriteString("<html>") 37 w.WriteString(`<head> 38 <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> 39 <style> 40 41 #helplink { 42 margin-bottom: 15px; 43 display: block; 44 margin-top: -15px; 45 } 46 47 #help { 48 display: none; 49 } 50 51 .stats { 52 font-size: 60%; 53 } 54 55 table { 56 border: 1px solid black; 57 table-layout: fixed; 58 width: 300px; 59 } 60 61 th, td { 62 border: 1px solid black; 63 overflow: hidden; 64 width: 400px; 65 vertical-align: top; 66 padding: 5px; 67 } 68 69 td.ssa-prog { 70 width: 600px; 71 word-wrap: break-word; 72 } 73 74 li { 75 list-style-type: none; 76 } 77 78 li.ssa-long-value { 79 text-indent: -2em; /* indent wrapped lines */ 80 } 81 82 li.ssa-value-list { 83 display: inline; 84 } 85 86 li.ssa-start-block { 87 padding: 0; 88 margin: 0; 89 } 90 91 li.ssa-end-block { 92 padding: 0; 93 margin: 0; 94 } 95 96 ul.ssa-print-func { 97 padding-left: 0; 98 } 99 100 dl.ssa-gen { 101 padding-left: 0; 102 } 103 104 dt.ssa-prog-src { 105 padding: 0; 106 margin: 0; 107 float: left; 108 width: 4em; 109 } 110 111 dd.ssa-prog { 112 padding: 0; 113 margin-right: 0; 114 margin-left: 4em; 115 } 116 117 .dead-value { 118 color: gray; 119 } 120 121 .dead-block { 122 opacity: 0.5; 123 } 124 125 .depcycle { 126 font-style: italic; 127 } 128 129 .line-number { 130 font-style: italic; 131 font-size: 11px; 132 } 133 134 .highlight-yellow { background-color: yellow; } 135 .highlight-aquamarine { background-color: aquamarine; } 136 .highlight-coral { background-color: coral; } 137 .highlight-lightpink { background-color: lightpink; } 138 .highlight-lightsteelblue { background-color: lightsteelblue; } 139 .highlight-palegreen { background-color: palegreen; } 140 .highlight-powderblue { background-color: powderblue; } 141 .highlight-lightgray { background-color: lightgray; } 142 143 .outline-blue { outline: blue solid 2px; } 144 .outline-red { outline: red solid 2px; } 145 .outline-blueviolet { outline: blueviolet solid 2px; } 146 .outline-darkolivegreen { outline: darkolivegreen solid 2px; } 147 .outline-fuchsia { outline: fuchsia solid 2px; } 148 .outline-sienna { outline: sienna solid 2px; } 149 .outline-gold { outline: gold solid 2px; } 150 151 </style> 152 153 <script type="text/javascript"> 154 // ordered list of all available highlight colors 155 var highlights = [ 156 "highlight-aquamarine", 157 "highlight-coral", 158 "highlight-lightpink", 159 "highlight-lightsteelblue", 160 "highlight-palegreen", 161 "highlight-lightgray", 162 "highlight-yellow" 163 ]; 164 165 // state: which value is highlighted this color? 166 var highlighted = {}; 167 for (var i = 0; i < highlights.length; i++) { 168 highlighted[highlights[i]] = ""; 169 } 170 171 // ordered list of all available outline colors 172 var outlines = [ 173 "outline-blue", 174 "outline-red", 175 "outline-blueviolet", 176 "outline-darkolivegreen", 177 "outline-fuchsia", 178 "outline-sienna", 179 "outline-gold" 180 ]; 181 182 // state: which value is outlined this color? 183 var outlined = {}; 184 for (var i = 0; i < outlines.length; i++) { 185 outlined[outlines[i]] = ""; 186 } 187 188 window.onload = function() { 189 var ssaElemClicked = function(elem, event, selections, selected) { 190 event.stopPropagation() 191 192 // TODO: pushState with updated state and read it on page load, 193 // so that state can survive across reloads 194 195 // find all values with the same name 196 var c = elem.classList.item(0); 197 var x = document.getElementsByClassName(c); 198 199 // if selected, remove selections from all of them 200 // otherwise, attempt to add 201 202 var remove = ""; 203 for (var i = 0; i < selections.length; i++) { 204 var color = selections[i]; 205 if (selected[color] == c) { 206 remove = color; 207 break; 208 } 209 } 210 211 if (remove != "") { 212 for (var i = 0; i < x.length; i++) { 213 x[i].classList.remove(remove); 214 } 215 selected[remove] = ""; 216 return; 217 } 218 219 // we're adding a selection 220 // find first available color 221 var avail = ""; 222 for (var i = 0; i < selections.length; i++) { 223 var color = selections[i]; 224 if (selected[color] == "") { 225 avail = color; 226 break; 227 } 228 } 229 if (avail == "") { 230 alert("out of selection colors; go add more"); 231 return; 232 } 233 234 // set that as the selection 235 for (var i = 0; i < x.length; i++) { 236 x[i].classList.add(avail); 237 } 238 selected[avail] = c; 239 }; 240 241 var ssaValueClicked = function(event) { 242 ssaElemClicked(this, event, highlights, highlighted); 243 } 244 245 var ssaBlockClicked = function(event) { 246 ssaElemClicked(this, event, outlines, outlined); 247 } 248 249 var ssavalues = document.getElementsByClassName("ssa-value"); 250 for (var i = 0; i < ssavalues.length; i++) { 251 ssavalues[i].addEventListener('click', ssaValueClicked); 252 } 253 254 var ssalongvalues = document.getElementsByClassName("ssa-long-value"); 255 for (var i = 0; i < ssalongvalues.length; i++) { 256 // don't attach listeners to li nodes, just the spans they contain 257 if (ssalongvalues[i].nodeName == "SPAN") { 258 ssalongvalues[i].addEventListener('click', ssaValueClicked); 259 } 260 } 261 262 var ssablocks = document.getElementsByClassName("ssa-block"); 263 for (var i = 0; i < ssablocks.length; i++) { 264 ssablocks[i].addEventListener('click', ssaBlockClicked); 265 } 266 }; 267 268 function toggle_visibility(id) { 269 var e = document.getElementById(id); 270 if(e.style.display == 'block') 271 e.style.display = 'none'; 272 else 273 e.style.display = 'block'; 274 } 275 </script> 276 277 </head>`) 278 w.WriteString("<body>") 279 w.WriteString("<h1>") 280 w.WriteString(html.EscapeString(name)) 281 w.WriteString("</h1>") 282 w.WriteString(` 283 <a href="#" onclick="toggle_visibility('help');" id="helplink">help</a> 284 <div id="help"> 285 286 <p> 287 Click on a value or block to toggle highlighting of that value/block 288 and its uses. (Values and blocks are highlighted by ID, and IDs of 289 dead items may be reused, so not all highlights necessarily correspond 290 to the clicked item.) 291 </p> 292 293 <p> 294 Faded out values and blocks are dead code that has not been eliminated. 295 </p> 296 297 <p> 298 Values printed in italics have a dependency cycle. 299 </p> 300 301 </div> 302 `) 303 w.WriteString("<table>") 304 w.WriteString("<tr>") 305 } 306 307 func (w *HTMLWriter) Close() { 308 if w == nil { 309 return 310 } 311 io.WriteString(w.w, "</tr>") 312 io.WriteString(w.w, "</table>") 313 io.WriteString(w.w, "</body>") 314 io.WriteString(w.w, "</html>") 315 w.w.Close() 316 } 317 318 // WriteFunc writes f in a column headed by title. 319 func (w *HTMLWriter) WriteFunc(title string, f *Func) { 320 if w == nil { 321 return // avoid generating HTML just to discard it 322 } 323 w.WriteColumn(title, "", f.HTML()) 324 // TODO: Add visual representation of f's CFG. 325 } 326 327 // WriteColumn writes raw HTML in a column headed by title. 328 // It is intended for pre- and post-compilation log output. 329 func (w *HTMLWriter) WriteColumn(title, class, html string) { 330 if w == nil { 331 return 332 } 333 if class == "" { 334 w.WriteString("<td>") 335 } else { 336 w.WriteString("<td class=\"" + class + "\">") 337 } 338 w.WriteString("<h2>" + title + "</h2>") 339 w.WriteString(html) 340 w.WriteString("</td>") 341 } 342 343 func (w *HTMLWriter) Printf(msg string, v ...interface{}) { 344 if _, err := fmt.Fprintf(w.w, msg, v...); err != nil { 345 w.Fatalf(src.NoXPos, "%v", err) 346 } 347 } 348 349 func (w *HTMLWriter) WriteString(s string) { 350 if _, err := io.WriteString(w.w, s); err != nil { 351 w.Fatalf(src.NoXPos, "%v", err) 352 } 353 } 354 355 func (v *Value) HTML() string { 356 // TODO: Using the value ID as the class ignores the fact 357 // that value IDs get recycled and that some values 358 // are transmuted into other values. 359 s := v.String() 360 return fmt.Sprintf("<span class=\"%s ssa-value\">%s</span>", s, s) 361 } 362 363 func (v *Value) LongHTML() string { 364 // TODO: Any intra-value formatting? 365 // I'm wary of adding too much visual noise, 366 // but a little bit might be valuable. 367 // We already have visual noise in the form of punctuation 368 // maybe we could replace some of that with formatting. 369 s := fmt.Sprintf("<span class=\"%s ssa-long-value\">", v.String()) 370 371 linenumber := "<span class=\"line-number\">(?)</span>" 372 if v.Pos.IsKnown() { 373 linenumber = fmt.Sprintf("<span class=\"line-number\">(%d)</span>", v.Pos.Line()) 374 } 375 376 s += fmt.Sprintf("%s %s = %s", v.HTML(), linenumber, v.Op.String()) 377 378 s += " <" + html.EscapeString(v.Type.String()) + ">" 379 s += html.EscapeString(v.auxString()) 380 for _, a := range v.Args { 381 s += fmt.Sprintf(" %s", a.HTML()) 382 } 383 r := v.Block.Func.RegAlloc 384 if int(v.ID) < len(r) && r[v.ID] != nil { 385 s += " : " + html.EscapeString(r[v.ID].String()) 386 } 387 var names []string 388 for name, values := range v.Block.Func.NamedValues { 389 for _, value := range values { 390 if value == v { 391 names = append(names, name.String()) 392 break // drop duplicates. 393 } 394 } 395 } 396 if len(names) != 0 { 397 s += " (" + strings.Join(names, ", ") + ")" 398 } 399 400 s += "</span>" 401 return s 402 } 403 404 func (b *Block) HTML() string { 405 // TODO: Using the value ID as the class ignores the fact 406 // that value IDs get recycled and that some values 407 // are transmuted into other values. 408 s := html.EscapeString(b.String()) 409 return fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", s, s) 410 } 411 412 func (b *Block) LongHTML() string { 413 // TODO: improve this for HTML? 414 s := fmt.Sprintf("<span class=\"%s ssa-block\">%s</span>", html.EscapeString(b.String()), html.EscapeString(b.Kind.String())) 415 if b.Aux != nil { 416 s += html.EscapeString(fmt.Sprintf(" {%v}", b.Aux)) 417 } 418 if b.Control != nil { 419 s += fmt.Sprintf(" %s", b.Control.HTML()) 420 } 421 if len(b.Succs) > 0 { 422 s += " →" // right arrow 423 for _, e := range b.Succs { 424 c := e.b 425 s += " " + c.HTML() 426 } 427 } 428 switch b.Likely { 429 case BranchUnlikely: 430 s += " (unlikely)" 431 case BranchLikely: 432 s += " (likely)" 433 } 434 if b.Pos.IsKnown() { 435 // TODO does not begin to deal with the full complexity of line numbers. 436 // Maybe we want a string/slice instead, of outer-inner when inlining. 437 s += fmt.Sprintf(" (line %d)", b.Pos.Line()) 438 } 439 return s 440 } 441 442 func (f *Func) HTML() string { 443 var buf bytes.Buffer 444 fmt.Fprint(&buf, "<code>") 445 p := htmlFuncPrinter{w: &buf} 446 fprintFunc(p, f) 447 448 // fprintFunc(&buf, f) // TODO: HTML, not text, <br /> for line breaks, etc. 449 fmt.Fprint(&buf, "</code>") 450 return buf.String() 451 } 452 453 type htmlFuncPrinter struct { 454 w io.Writer 455 } 456 457 func (p htmlFuncPrinter) header(f *Func) {} 458 459 func (p htmlFuncPrinter) startBlock(b *Block, reachable bool) { 460 // TODO: Make blocks collapsable? 461 var dead string 462 if !reachable { 463 dead = "dead-block" 464 } 465 fmt.Fprintf(p.w, "<ul class=\"%s ssa-print-func %s\">", b, dead) 466 fmt.Fprintf(p.w, "<li class=\"ssa-start-block\">%s:", b.HTML()) 467 if len(b.Preds) > 0 { 468 io.WriteString(p.w, " ←") // left arrow 469 for _, e := range b.Preds { 470 pred := e.b 471 fmt.Fprintf(p.w, " %s", pred.HTML()) 472 } 473 } 474 io.WriteString(p.w, "</li>") 475 if len(b.Values) > 0 { // start list of values 476 io.WriteString(p.w, "<li class=\"ssa-value-list\">") 477 io.WriteString(p.w, "<ul>") 478 } 479 } 480 481 func (p htmlFuncPrinter) endBlock(b *Block) { 482 if len(b.Values) > 0 { // end list of values 483 io.WriteString(p.w, "</ul>") 484 io.WriteString(p.w, "</li>") 485 } 486 io.WriteString(p.w, "<li class=\"ssa-end-block\">") 487 fmt.Fprint(p.w, b.LongHTML()) 488 io.WriteString(p.w, "</li>") 489 io.WriteString(p.w, "</ul>") 490 // io.WriteString(p.w, "</span>") 491 } 492 493 func (p htmlFuncPrinter) value(v *Value, live bool) { 494 var dead string 495 if !live { 496 dead = "dead-value" 497 } 498 fmt.Fprintf(p.w, "<li class=\"ssa-long-value %s\">", dead) 499 fmt.Fprint(p.w, v.LongHTML()) 500 io.WriteString(p.w, "</li>") 501 } 502 503 func (p htmlFuncPrinter) startDepCycle() { 504 fmt.Fprintln(p.w, "<span class=\"depcycle\">") 505 } 506 507 func (p htmlFuncPrinter) endDepCycle() { 508 fmt.Fprintln(p.w, "</span>") 509 } 510 511 func (p htmlFuncPrinter) named(n LocalSlot, vals []*Value) { 512 fmt.Fprintf(p.w, "<li>name %s: ", n) 513 for _, val := range vals { 514 fmt.Fprintf(p.w, "%s ", val.HTML()) 515 } 516 fmt.Fprintf(p.w, "</li>") 517 }