Kaynağa Gözat

Improve weblist assembly output (#124)

* Align file names in weblist disassembly

The disassembly displayed by weblist attempts to align file:line
information after each instruction, but two things interfere with this
alignment: 1) the instruction text often has tabs in it, which defeats
the length-based alignment and generally interacts poorly with HTML
rendering and 2) the instruction text often has special HTML
characters like < and > in it, but we pad the string after escaping
it, so the length is again not representative of how it will display.
For example, the following shows what this looks like for disassembly
that contains < and >:

                   .          .   41c634: cmp    $0x200,%rdx                               mgcsweepbuf.go:130
                   .          .   41c63b: jae    41c64f <runtime.(*gcSweepBuf).pop+0x4f> mgcsweepbuf.go:130
                   .          .   41c63d: mov    (%rax,%rdx,8),%rcx                        mgcsweepbuf.go:130
                   .          .   41c64f: callq  424800 <runtime.panicindex>             mgcsweepbuf.go:130
                   .          .   41c654: ud2                                              mgcsweepbuf.go:130

Fix these problems by replacing tab characters with spaces and padding
the string before escaping it. After this, the file names are properly
aligned:

                   .          .   41c634: cmp    $0x200,%rdx                               mgcsweepbuf.go:130
                   .          .   41c63b: jae    41c64f <runtime.(*gcSweepBuf).pop+0x4f>   mgcsweepbuf.go:130
                   .          .   41c63d: mov    (%rax,%rdx,8),%rcx                        mgcsweepbuf.go:130
                   .          .   41c64f: callq  424800 <runtime.panicindex>               mgcsweepbuf.go:130
                   .          .   41c654: ud2                                              mgcsweepbuf.go:130

* Separate discontiguous assembly blocks

Currently, weblist's disassembled output for each line simply lists
all instructions that are marked with that line number. This leads to
confusing disassembly because a lot of things look like straight-line
control flow when they aren't. For example, index panics look like
they always happen:

   .          .   41c62c: test   %al,(%rax)
   .          .   41c62e: and    $0x1ff,%edx
   .          .   41c634: cmp    $0x200,%rdx
   .          .   41c63b: jae    41c64f <runtime.(*gcSweepBuf).pop+0x4f>
   .          .   41c63d: mov    (%rax,%rdx,8),%rcx
   .          .   41c64f: callq  424800 <runtime.panicindex>
   .          .   41c654: ud2

In reality, 41c64f is at the end of the function, but it looks like we
call it immediately after a successful index operation.

Fix this by adding a vertical ellipses to separate blocks of assembly
that have other instructions between them. With this change, the above
looks like:

   .          .   41c62c: test   %al,(%rax)
   .          .   41c62e: and    $0x1ff,%edx
   .          .   41c634: cmp    $0x200,%rdx
   .          .   41c63b: jae    41c64f <runtime.(*gcSweepBuf).pop+0x4f>
   .          .   41c63d: mov    (%rax,%rdx,8),%rcx
                       ⋮
   .          .   41c64f: callq  424800 <runtime.panicindex>
   .          .   41c654: ud2
Austin Clements 8 yıl önce
ebeveyn
işleme
5c63cc7022

+ 4
- 4
internal/driver/driver_test.go Dosyayı Görüntüle

@@ -1063,10 +1063,10 @@ func (m *mockObjTool) Disasm(file string, start, end uint64) ([]plugin.Inst, err
1063 1063
 	switch start {
1064 1064
 	case 0x1000:
1065 1065
 		return []plugin.Inst{
1066
-			{Addr: 0x1000, Text: "instruction one"},
1067
-			{Addr: 0x1001, Text: "instruction two"},
1068
-			{Addr: 0x1002, Text: "instruction three"},
1069
-			{Addr: 0x1003, Text: "instruction four"},
1066
+			{Addr: 0x1000, Text: "instruction one", File: "file1000.src", Line: 1},
1067
+			{Addr: 0x1001, Text: "instruction two", File: "file1000.src", Line: 1},
1068
+			{Addr: 0x1002, Text: "instruction three", File: "file1000.src", Line: 2},
1069
+			{Addr: 0x1003, Text: "instruction four", File: "file1000.src", Line: 1},
1070 1070
 		}, nil
1071 1071
 	case 0x3000:
1072 1072
 		return []plugin.Inst{

+ 3
- 3
internal/driver/testdata/pprof.cpu.flat.addresses.disasm Dosyayı Görüntüle

@@ -2,9 +2,9 @@ Total: 1.12s
2 2
 ROUTINE ======================== line1000
3 3
      1.10s      1.10s (flat, cum) 98.21% of Total
4 4
      1.10s      1.10s       1000: instruction one                         ;line1000 file1000.src:1
5
-         .          .       1001: instruction two
6
-         .          .       1002: instruction three
7
-         .          .       1003: instruction four
5
+         .          .       1001: instruction two                         ;file1000.src:1
6
+         .          .       1002: instruction three                       ;file1000.src:2
7
+         .          .       1003: instruction four                        ;file1000.src:1
8 8
 ROUTINE ======================== line3000
9 9
       10ms      1.12s (flat, cum)   100% of Total
10 10
       10ms      1.01s       3000: instruction one                         ;line3000 file3000.src:6

+ 7
- 4
internal/driver/testdata/pprof.cpu.flat.addresses.weblist Dosyayı Görüntüle

@@ -2,6 +2,7 @@
2 2
 <!DOCTYPE html>
3 3
 <html>
4 4
 <head>
5
+<meta charset="UTF-8">
5 6
 <title>Pprof listing</title>
6 7
 <style type="text/css">
7 8
 body {
@@ -70,15 +71,17 @@ Duration: 10s, Total samples = 1.12s (11.20%)<br>Total: 1.12s</div><h1>line1000<
70 71
 <pre onClick="pprof_toggle_asm(event)">
71 72
   Total:       1.10s      1.10s (flat, cum) 98.21%
72 73
 <span class=line>      1</span> <span class=deadsrc>       1.10s      1.10s line1 </span><span class=asm>               1.10s      1.10s     1000: instruction one                                  <span class=disasmloc>file1000.src:1</span>
73
-                   .          .     1001: instruction two                                  <span class=disasmloc></span>
74
-                   .          .     1002: instruction three                                <span class=disasmloc></span>
75
-                   .          .     1003: instruction four                                 <span class=disasmloc></span>
74
+                   .          .     1001: instruction two                                  <span class=disasmloc>file1000.src:1</span>
75
+                                       ⋮
76
+                   .          .     1003: instruction four                                 <span class=disasmloc>file1000.src:1</span>
77
+</span>
78
+<span class=line>      2</span> <span class=deadsrc>           .          . line2 </span><span class=asm>                   .          .     1002: instruction three                                <span class=disasmloc>file1000.src:2</span>
76 79
 </span>
77
-<span class=line>      2</span> <span class=nop>           .          . line2 </span>
78 80
 <span class=line>      3</span> <span class=nop>           .          . line3 </span>
79 81
 <span class=line>      4</span> <span class=nop>           .          . line4 </span>
80 82
 <span class=line>      5</span> <span class=nop>           .          . line5 </span>
81 83
 <span class=line>      6</span> <span class=nop>           .          . line6 </span>
84
+<span class=line>      7</span> <span class=nop>           .          . line7 </span>
82 85
 </pre>
83 86
 <h1>line3000</h1>testdata/file3000.src
84 87
 <pre onClick="pprof_toggle_asm(event)">

+ 1
- 0
internal/report/report.go Dosyayı Görüntüle

@@ -532,6 +532,7 @@ type assemblyInstruction struct {
532 532
 	line            int
533 533
 	flat, cum       int64
534 534
 	flatDiv, cumDiv int64
535
+	startsBlock     bool
535 536
 }
536 537
 
537 538
 func (a *assemblyInstruction) flatValue() int64 {

+ 15
- 3
internal/report/source.go Dosyayı Görüntüle

@@ -236,11 +236,18 @@ func assemblyPerSourceLine(objSyms []*objSymbol, rs graph.Nodes, src string, obj
236 236
 	srcBase := filepath.Base(src)
237 237
 	anodes := annotateAssembly(insts, rs, o.base)
238 238
 	var lineno = 0
239
+	var prevline = 0
239 240
 	for _, an := range anodes {
240 241
 		if filepath.Base(an.file) == srcBase {
241 242
 			lineno = an.line
242 243
 		}
243 244
 		if lineno != 0 {
245
+			if lineno != prevline {
246
+				// This instruction starts a new block
247
+				// of contiguous instructions on this line.
248
+				an.startsBlock = true
249
+			}
250
+			prevline = lineno
244 251
 			assembly[lineno] = append(assembly[lineno], an)
245 252
 		}
246 253
 	}
@@ -306,7 +313,12 @@ func printFunctionSourceLine(w io.Writer, fn *graph.Node, assembly []assemblyIns
306 313
 		valueOrDot(fn.Flat, rpt), valueOrDot(fn.Cum, rpt),
307 314
 		template.HTMLEscapeString(fn.Info.Name))
308 315
 	fmt.Fprint(w, "<span class=asm>")
309
-	for _, an := range assembly {
316
+	for i, an := range assembly {
317
+		if an.startsBlock && i != 0 {
318
+			// Insert a separator between discontiguous blocks.
319
+			fmt.Fprintf(w, " %8s %30s\n", "", "⋮")
320
+		}
321
+
310 322
 		var fileline string
311 323
 		class := "disasmloc"
312 324
 		if an.file != "" {
@@ -322,10 +334,10 @@ func printFunctionSourceLine(w io.Writer, fn *graph.Node, assembly []assemblyIns
322 334
 		if an.cumDiv != 0 {
323 335
 			cum = cum / an.cumDiv
324 336
 		}
325
-		fmt.Fprintf(w, " %8s %10s %10s %8x: %-48s <span class=%s>%s</span>\n", "",
337
+		fmt.Fprintf(w, " %8s %10s %10s %8x: %s <span class=%s>%s</span>\n", "",
326 338
 			valueOrDot(flat, rpt), valueOrDot(cum, rpt),
327 339
 			an.address,
328
-			template.HTMLEscapeString(an.instruction),
340
+			template.HTMLEscapeString(fmt.Sprintf("%-48s", strings.Replace(an.instruction, "\t", " ", -1))),
329 341
 			class,
330 342
 			template.HTMLEscapeString(fileline))
331 343
 	}

+ 1
- 0
internal/report/source_html.go Dosyayı Görüntüle

@@ -18,6 +18,7 @@ const weblistPageHeader = `
18 18
 <!DOCTYPE html>
19 19
 <html>
20 20
 <head>
21
+<meta charset="UTF-8">
21 22
 <title>Pprof listing</title>
22 23
 <style type="text/css">
23 24
 body {