Skip to content

Commit cbba55b

Browse files
ghemawataalexand
andauthored
Revamp how source listing is produced so it works for inlined functions. (#599)
* Revamp how source listing is produced so it works for inlined functions. Some other things improved due to these changes: 1. Produced output does not contain long runs of uninteresting source. 2. Speed of producing weblist page for a large binary goes from ~ 57s to ~ 5.5s. * use keyed literals to satisfy extra checks * Fix up file names for Windows (to use backslash instead of slash as separator). * Fix nil dereference when we attempt to close after encountering a missing object file * Limit number of address ranges we process to avoid unbounded hangs Stop printing address ranges after processing 25 of them. These ranges are sorted by the number of samples that fell within them. Change back to printing inner-most file:line next to an instruction to reduce caller/callee confusion. * Fix comment typo Co-authored-by: Alexey Alexandrov <[email protected]>
1 parent c5db671 commit cbba55b

File tree

8 files changed

+669
-217
lines changed

8 files changed

+669
-217
lines changed

internal/driver/driver.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ func applyCommandOverrides(cmd string, outputFormat int, cfg config) config {
163163
trim := cfg.Trim
164164

165165
switch cmd {
166-
case "disasm", "weblist":
166+
case "disasm":
167167
trim = false
168168
cfg.Granularity = "addresses"
169169
// Force the 'noinlines' mode so that source locations for a given address
@@ -172,6 +172,10 @@ func applyCommandOverrides(cmd string, outputFormat int, cfg config) config {
172172
// This is because the merge is done by address and in case of an inlined
173173
// stack each of the inlined entries is a separate callgraph node.
174174
cfg.NoInlines = true
175+
case "weblist":
176+
trim = false
177+
cfg.Granularity = "addresses"
178+
cfg.NoInlines = false // Need inline info to support call expansion
175179
case "peek":
176180
trim = false
177181
case "list":

internal/driver/driver_test.go

Lines changed: 68 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func TestParse(t *testing.T) {
9595
{"dot,inuse_space,flat,tagfocus=30kb:,tagignore=1mb:2mb", "heap"},
9696
{"disasm=line[13],addresses,flat", "cpu"},
9797
{"peek=line.*01", "cpu"},
98-
{"weblist=line[13],addresses,flat", "cpu"},
98+
{"weblist=line(1000|3000)$,addresses,flat", "cpu"},
9999
{"tags,tagfocus=400kb:", "heap_request"},
100100
{"tags,tagfocus=+400kb:", "heap_request"},
101101
{"dot", "long_name_funcs"},
@@ -1585,24 +1585,28 @@ func (*mockObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFi
15851585
}
15861586

15871587
func (m *mockObjTool) Disasm(file string, start, end uint64, intelSyntax bool) ([]plugin.Inst, error) {
1588-
switch start {
1589-
case 0x1000:
1590-
return []plugin.Inst{
1591-
{Addr: 0x1000, Text: "instruction one", File: "file1000.src", Line: 1},
1592-
{Addr: 0x1001, Text: "instruction two", File: "file1000.src", Line: 1},
1593-
{Addr: 0x1002, Text: "instruction three", File: "file1000.src", Line: 2},
1594-
{Addr: 0x1003, Text: "instruction four", File: "file1000.src", Line: 1},
1595-
}, nil
1596-
case 0x3000:
1597-
return []plugin.Inst{
1598-
{Addr: 0x3000, Text: "instruction one"},
1599-
{Addr: 0x3001, Text: "instruction two"},
1600-
{Addr: 0x3002, Text: "instruction three"},
1601-
{Addr: 0x3003, Text: "instruction four"},
1602-
{Addr: 0x3004, Text: "instruction five"},
1603-
}, nil
1588+
const fn1 = "line1000"
1589+
const fn3 = "line3000"
1590+
const file1 = "testdata/file1000.src"
1591+
const file3 = "testdata/file3000.src"
1592+
data := []plugin.Inst{
1593+
{Addr: 0x1000, Text: "instruction one", Function: fn1, File: file1, Line: 1},
1594+
{Addr: 0x1001, Text: "instruction two", Function: fn1, File: file1, Line: 1},
1595+
{Addr: 0x1002, Text: "instruction three", Function: fn1, File: file1, Line: 2},
1596+
{Addr: 0x1003, Text: "instruction four", Function: fn1, File: file1, Line: 1},
1597+
{Addr: 0x3000, Text: "instruction one", Function: fn3, File: file3},
1598+
{Addr: 0x3001, Text: "instruction two", Function: fn3, File: file3},
1599+
{Addr: 0x3002, Text: "instruction three", Function: fn3, File: file3},
1600+
{Addr: 0x3003, Text: "instruction four", Function: fn3, File: file3},
1601+
{Addr: 0x3004, Text: "instruction five", Function: fn3, File: file3},
16041602
}
1605-
return nil, fmt.Errorf("unimplemented")
1603+
var result []plugin.Inst
1604+
for _, inst := range data {
1605+
if inst.Addr >= start && inst.Addr <= end {
1606+
result = append(result, inst)
1607+
}
1608+
}
1609+
return result, nil
16061610
}
16071611

16081612
type mockFile struct {
@@ -1630,7 +1634,52 @@ func (m *mockFile) BuildID() string {
16301634
// is in general a list of positions representing a call stack,
16311635
// with the leaf function first.
16321636
func (*mockFile) SourceLine(addr uint64) ([]plugin.Frame, error) {
1633-
return nil, fmt.Errorf("unimplemented")
1637+
// Return enough data to support the SourceLine() calls needed for
1638+
// weblist on cpuProfile() contents.
1639+
frame := func(fn, file string, line int) plugin.Frame {
1640+
return plugin.Frame{Func: fn, File: file, Line: line}
1641+
}
1642+
switch addr {
1643+
case 0x1000:
1644+
return []plugin.Frame{
1645+
frame("mangled1000", "testdata/file1000.src", 1),
1646+
}, nil
1647+
case 0x1001:
1648+
return []plugin.Frame{
1649+
frame("mangled1000", "testdata/file1000.src", 1),
1650+
}, nil
1651+
case 0x1002:
1652+
return []plugin.Frame{
1653+
frame("mangled1000", "testdata/file1000.src", 2),
1654+
}, nil
1655+
case 0x1003:
1656+
return []plugin.Frame{
1657+
frame("mangled1000", "testdata/file1000.src", 1),
1658+
}, nil
1659+
case 0x2000:
1660+
return []plugin.Frame{
1661+
frame("mangled2001", "testdata/file2000.src", 9),
1662+
frame("mangled2000", "testdata/file2000.src", 4),
1663+
}, nil
1664+
case 0x3000:
1665+
return []plugin.Frame{
1666+
frame("mangled3002", "testdata/file3000.src", 2),
1667+
frame("mangled3001", "testdata/file3000.src", 5),
1668+
frame("mangled3000", "testdata/file3000.src", 6),
1669+
}, nil
1670+
case 0x3001:
1671+
return []plugin.Frame{
1672+
frame("mangled3001", "testdata/file3000.src", 8),
1673+
frame("mangled3000", "testdata/file3000.src", 9),
1674+
}, nil
1675+
case 0x3002:
1676+
return []plugin.Frame{
1677+
frame("mangled3002", "testdata/file3000.src", 5),
1678+
frame("mangled3000", "testdata/file3000.src", 9),
1679+
}, nil
1680+
}
1681+
1682+
return nil, nil
16341683
}
16351684

16361685
// Symbols returns a list of symbols in the object file.

internal/driver/interactive_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,7 @@ func TestInteractiveCommands(t *testing.T) {
226226
"weblist find -test",
227227
map[string]string{
228228
"granularity": "addresses",
229-
"noinlines": "true",
229+
"noinlines": "false",
230230
"nodecount": "0",
231231
"sort": "flat",
232232
"ignore": "test",

internal/driver/testdata/pprof.cpu.flat.addresses.disasm

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ Total: 1.12s
22
ROUTINE ======================== line1000
33
1.10s 1.10s (flat, cum) 98.21% of Total
44
1.10s 1.10s 1000: instruction one ;line1000 file1000.src:1
5-
. . 1001: instruction two ;file1000.src:1
6-
. . 1002: instruction three ;file1000.src:2
7-
. . 1003: instruction four ;file1000.src:1
5+
. . 1001: instruction two
6+
. . 1002: instruction three ;line1000 file1000.src:2
7+
. . 1003: instruction four ;line1000 file1000.src:1
88
ROUTINE ======================== line3000
99
10ms 1.12s (flat, cum) 100% of Total
1010
10ms 1.01s 3000: instruction one ;line3000 file3000.src:6
1111
. 100ms 3001: instruction two ;line3000 file3000.src:9
1212
. 10ms 3002: instruction three
13-
. . 3003: instruction four
13+
. . 3003: instruction four ;line3000 file3000.src
1414
. . 3004: instruction five

internal/driver/testdata/pprof.cpu.flat.addresses.weblist

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,18 @@ Duration: 10s, Total samples = 1.12s (11.20%)<br>Total: 1.12s</div><h2>line1000<
8484
<span class=line> 3</span> <span class=nop> . . line3 </span>
8585
<span class=line> 4</span> <span class=nop> . . line4 </span>
8686
<span class=line> 5</span> <span class=nop> . . line5 </span>
87-
<span class=line> 6</span> <span class=deadsrc> 10ms 1.01s line6 </span><span class=asm> 10ms 1.01s 3000: instruction one <span class=unimportant>file3000.src:6</span>
87+
<span class=line> 6</span> <span class=deadsrc> 10ms 1.01s line6 </span><span class=asm> <span class=inlinesrc> line5 </span> <span class=unimportant>file3000.src:5</span>
88+
<span class=inlinesrc> line2 </span> <span class=unimportant>file3000.src:2</span>
89+
10ms 1.01s 3000: instruction one <span class=unimportant>file3000.src:2</span>
8890
</span>
8991
<span class=line> 7</span> <span class=nop> . . line7 </span>
9092
<span class=line> 8</span> <span class=nop> . . line8 </span>
91-
<span class=line> 9</span> <span class=deadsrc> . 110ms line9 </span><span class=asm> . 100ms 3001: instruction two <span class=unimportant>file3000.src:9</span>
92-
. 10ms 3002: instruction three <span class=unimportant>file3000.src:9</span>
93-
. . 3003: instruction four <span class=unimportant></span>
94-
. . 3004: instruction five <span class=unimportant></span>
93+
<span class=line> 9</span> <span class=deadsrc> . 110ms line9 </span><span class=asm> <span class=inlinesrc> line8 </span> <span class=unimportant>file3000.src:8</span>
94+
. 100ms 3001: instruction two <span class=unimportant>file3000.src:8</span>
95+
<span class=inlinesrc> line5 </span> <span class=unimportant>file3000.src:5</span>
96+
. 10ms 3002: instruction three <span class=unimportant>file3000.src:5</span>
97+
. . 3003: instruction four <span class=unimportant></span>
98+
. . 3004: instruction five <span class=unimportant></span>
9599
</span>
96100
<span class=line> 10</span> <span class=nop> . . line0 </span>
97101
<span class=line> 11</span> <span class=nop> . . line1 </span>

internal/driver/webui_test.go

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,12 @@ func TestWebInterface(t *testing.T) {
7575
testcases := []testCase{
7676
{"/", []string{"F1", "F2", "F3", "testbin", "cpu"}, true},
7777
{"/top", []string{`"Name":"F2","InlineLabel":"","Flat":200,"Cum":300,"FlatFormat":"200ms","CumFormat":"300ms"}`}, false},
78-
{"/source?f=" + url.QueryEscape("F[12]"),
79-
[]string{"F1", "F2", "300ms +line1"}, false},
78+
{"/source?f=" + url.QueryEscape("F[12]"), []string{
79+
"F1",
80+
"F2",
81+
`\. +300ms .*f1:asm`, // Cumulative count for F1
82+
"200ms +300ms .*f2:asm", // Flat + cumulative count for F2
83+
}, false},
8084
{"/peek?f=" + url.QueryEscape("F[12]"),
8185
[]string{"300ms.*F1", "200ms.*300ms.*F2"}, false},
8286
{"/disasm?f=" + url.QueryEscape("F[12]"),
@@ -174,9 +178,9 @@ func (obj fakeObjTool) Open(file string, start, limit, offset uint64) (plugin.Ob
174178

175179
func (obj fakeObjTool) Disasm(file string, start, end uint64, intelSyntax bool) ([]plugin.Inst, error) {
176180
return []plugin.Inst{
177-
{Addr: addrBase + 0, Text: "f1:asm", Function: "F1"},
178-
{Addr: addrBase + 10, Text: "f2:asm", Function: "F2"},
179-
{Addr: addrBase + 20, Text: "d3:asm", Function: "F3"},
181+
{Addr: addrBase + 10, Text: "f1:asm", Function: "F1", Line: 3},
182+
{Addr: addrBase + 20, Text: "f2:asm", Function: "F2", Line: 11},
183+
{Addr: addrBase + 30, Text: "d3:asm", Function: "F3", Line: 22},
180184
}, nil
181185
}
182186

@@ -196,7 +200,7 @@ func makeFakeProfile() *profile.Profile {
196200
{
197201
ID: 1,
198202
Start: addrBase,
199-
Limit: addrBase + 10,
203+
Limit: addrBase + 100,
200204
Offset: 0,
201205
File: "testbin",
202206
HasFunctions: true,

0 commit comments

Comments
 (0)