Browse Source

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 years ago
parent
commit
5c63cc7022

+ 4
- 4
internal/driver/driver_test.go View File

1063
 	switch start {
1063
 	switch start {
1064
 	case 0x1000:
1064
 	case 0x1000:
1065
 		return []plugin.Inst{
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
 		}, nil
1070
 		}, nil
1071
 	case 0x3000:
1071
 	case 0x3000:
1072
 		return []plugin.Inst{
1072
 		return []plugin.Inst{

+ 3
- 3
internal/driver/testdata/pprof.cpu.flat.addresses.disasm View File

2
 ROUTINE ======================== line1000
2
 ROUTINE ======================== line1000
3
      1.10s      1.10s (flat, cum) 98.21% of Total
3
      1.10s      1.10s (flat, cum) 98.21% of Total
4
      1.10s      1.10s       1000: instruction one                         ;line1000 file1000.src:1
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
 ROUTINE ======================== line3000
8
 ROUTINE ======================== line3000
9
       10ms      1.12s (flat, cum)   100% of Total
9
       10ms      1.12s (flat, cum)   100% of Total
10
       10ms      1.01s       3000: instruction one                         ;line3000 file3000.src:6
10
       10ms      1.01s       3000: instruction one                         ;line3000 file3000.src:6

+ 7
- 4
internal/driver/testdata/pprof.cpu.flat.addresses.weblist View File

2
 <!DOCTYPE html>
2
 <!DOCTYPE html>
3
 <html>
3
 <html>
4
 <head>
4
 <head>
5
+<meta charset="UTF-8">
5
 <title>Pprof listing</title>
6
 <title>Pprof listing</title>
6
 <style type="text/css">
7
 <style type="text/css">
7
 body {
8
 body {
70
 <pre onClick="pprof_toggle_asm(event)">
71
 <pre onClick="pprof_toggle_asm(event)">
71
   Total:       1.10s      1.10s (flat, cum) 98.21%
72
   Total:       1.10s      1.10s (flat, cum) 98.21%
72
 <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
 <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
 </span>
79
 </span>
77
-<span class=line>      2</span> <span class=nop>           .          . line2 </span>
78
 <span class=line>      3</span> <span class=nop>           .          . line3 </span>
80
 <span class=line>      3</span> <span class=nop>           .          . line3 </span>
79
 <span class=line>      4</span> <span class=nop>           .          . line4 </span>
81
 <span class=line>      4</span> <span class=nop>           .          . line4 </span>
80
 <span class=line>      5</span> <span class=nop>           .          . line5 </span>
82
 <span class=line>      5</span> <span class=nop>           .          . line5 </span>
81
 <span class=line>      6</span> <span class=nop>           .          . line6 </span>
83
 <span class=line>      6</span> <span class=nop>           .          . line6 </span>
84
+<span class=line>      7</span> <span class=nop>           .          . line7 </span>
82
 </pre>
85
 </pre>
83
 <h1>line3000</h1>testdata/file3000.src
86
 <h1>line3000</h1>testdata/file3000.src
84
 <pre onClick="pprof_toggle_asm(event)">
87
 <pre onClick="pprof_toggle_asm(event)">

+ 1
- 0
internal/report/report.go View File

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

+ 15
- 3
internal/report/source.go View File

236
 	srcBase := filepath.Base(src)
236
 	srcBase := filepath.Base(src)
237
 	anodes := annotateAssembly(insts, rs, o.base)
237
 	anodes := annotateAssembly(insts, rs, o.base)
238
 	var lineno = 0
238
 	var lineno = 0
239
+	var prevline = 0
239
 	for _, an := range anodes {
240
 	for _, an := range anodes {
240
 		if filepath.Base(an.file) == srcBase {
241
 		if filepath.Base(an.file) == srcBase {
241
 			lineno = an.line
242
 			lineno = an.line
242
 		}
243
 		}
243
 		if lineno != 0 {
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
 			assembly[lineno] = append(assembly[lineno], an)
251
 			assembly[lineno] = append(assembly[lineno], an)
245
 		}
252
 		}
246
 	}
253
 	}
306
 		valueOrDot(fn.Flat, rpt), valueOrDot(fn.Cum, rpt),
313
 		valueOrDot(fn.Flat, rpt), valueOrDot(fn.Cum, rpt),
307
 		template.HTMLEscapeString(fn.Info.Name))
314
 		template.HTMLEscapeString(fn.Info.Name))
308
 	fmt.Fprint(w, "<span class=asm>")
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
 		var fileline string
322
 		var fileline string
311
 		class := "disasmloc"
323
 		class := "disasmloc"
312
 		if an.file != "" {
324
 		if an.file != "" {
322
 		if an.cumDiv != 0 {
334
 		if an.cumDiv != 0 {
323
 			cum = cum / an.cumDiv
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
 			valueOrDot(flat, rpt), valueOrDot(cum, rpt),
338
 			valueOrDot(flat, rpt), valueOrDot(cum, rpt),
327
 			an.address,
339
 			an.address,
328
-			template.HTMLEscapeString(an.instruction),
340
+			template.HTMLEscapeString(fmt.Sprintf("%-48s", strings.Replace(an.instruction, "\t", " ", -1))),
329
 			class,
341
 			class,
330
 			template.HTMLEscapeString(fileline))
342
 			template.HTMLEscapeString(fileline))
331
 	}
343
 	}

+ 1
- 0
internal/report/source_html.go View File

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