Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Colorize weblist output #271

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
48 changes: 27 additions & 21 deletions internal/driver/testdata/pprof.cpu.flat.addresses.weblist
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ background-color: #eeeeee;
color: #008800;
display: none;
}
.ptile_95 {
background-color: #fe5252
}
.ptile_80 {
background-color: #ffb6c1
}
</style>
<script type="text/javascript">
function pprof_toggle_asm(e) {
Expand All @@ -63,41 +69,41 @@ Type: cpu<br>
Duration: 10s, Total samples = 1.12s (11.20%)<br>Total: 1.12s</div><h2>line1000</h2><p class="filename">testdata/file1000.src</p>
<pre onClick="pprof_toggle_asm(event)">
Total: 1.10s 1.10s (flat, cum) 98.21%
<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=unimportant>file1000.src:1</span>
<span class=line> 1</span> <span class="deadsrc ptile_80"> 1.10s 1.10s line1 </span><span class=asm> 1.10s 1.10s 1000: instruction one <span class=unimportant>file1000.src:1</span>
. . 1001: instruction two <span class=unimportant>file1000.src:1</span>
. . 1003: instruction four <span class=unimportant>file1000.src:1</span>
</span>
<span class=line> 2</span> <span class=deadsrc> . . line2 </span><span class=asm> . . 1002: instruction three <span class=unimportant>file1000.src:2</span>
<span class=line> 2</span> <span class="deadsrc"> . . line2 </span><span class=asm> . . 1002: instruction three <span class=unimportant>file1000.src:2</span>
</span>
<span class=line> 3</span> <span class=nop> . . line3 </span>
<span class=line> 4</span> <span class=nop> . . line4 </span>
<span class=line> 5</span> <span class=nop> . . line5 </span>
<span class=line> 6</span> <span class=nop> . . line6 </span>
<span class=line> 7</span> <span class=nop> . . line7 </span>
<span class=line> 3</span> <span class="nop"> . . line3 </span>
<span class=line> 4</span> <span class="nop"> . . line4 </span>
<span class=line> 5</span> <span class="nop"> . . line5 </span>
<span class=line> 6</span> <span class="nop"> . . line6 </span>
<span class=line> 7</span> <span class="nop"> . . line7 </span>
</pre>
<h2>line3000</h2><p class="filename">testdata/file3000.src</p>
<pre onClick="pprof_toggle_asm(event)">
Total: 10ms 1.12s (flat, cum) 100%
<span class=line> 1</span> <span class=nop> . . line1 </span>
<span class=line> 2</span> <span class=nop> . . line2 </span>
<span class=line> 3</span> <span class=nop> . . line3 </span>
<span class=line> 4</span> <span class=nop> . . line4 </span>
<span class=line> 5</span> <span class=nop> . . line5 </span>
<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>
<span class=line> 1</span> <span class="nop"> . . line1 </span>
<span class=line> 2</span> <span class="nop"> . . line2 </span>
<span class=line> 3</span> <span class="nop"> . . line3 </span>
<span class=line> 4</span> <span class="nop"> . . line4 </span>
<span class=line> 5</span> <span class="nop"> . . line5 </span>
<span class=line> 6</span> <span class="deadsrc ptile_80"> 10ms 1.01s line6 </span><span class=asm> 10ms 1.01s 3000: instruction one <span class=unimportant>file3000.src:6</span>
</span>
<span class=line> 7</span> <span class=nop> . . line7 </span>
<span class=line> 8</span> <span class=nop> . . line8 </span>
<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>
<span class=line> 7</span> <span class="nop"> . . line7 </span>
<span class=line> 8</span> <span class="nop"> . . line8 </span>
<span class=line> 9</span> <span class="deadsrc ptile_80"> . 110ms line9 </span><span class=asm> . 100ms 3001: instruction two <span class=unimportant>file3000.src:9</span>
. 10ms 3002: instruction three <span class=unimportant>file3000.src:9</span>
. . 3003: instruction four <span class=unimportant></span>
. . 3004: instruction five <span class=unimportant></span>
</span>
<span class=line> 10</span> <span class=nop> . . line0 </span>
<span class=line> 11</span> <span class=nop> . . line1 </span>
<span class=line> 12</span> <span class=nop> . . line2 </span>
<span class=line> 13</span> <span class=nop> . . line3 </span>
<span class=line> 14</span> <span class=nop> . . line4 </span>
<span class=line> 10</span> <span class="nop"> . . line0 </span>
<span class=line> 11</span> <span class="nop"> . . line1 </span>
<span class=line> 12</span> <span class="nop"> . . line2 </span>
<span class=line> 13</span> <span class="nop"> . . line3 </span>
<span class=line> 14</span> <span class="nop"> . . line4 </span>
</pre>

</body>
Expand Down
63 changes: 59 additions & 4 deletions internal/report/source.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
"io"
"os"
"path/filepath"
"sort"
"strconv"
"strings"

Expand Down Expand Up @@ -214,14 +215,66 @@ func PrintWebList(w io.Writer, rpt *Report, obj plugin.ObjTool, maxFiles int) er
}

printFunctionHeader(w, ff.functionName, path, n.Flat, n.Cum, rpt)
ptiles := calculatePtiles(fnodes)
for _, fn := range fnodes {
printFunctionSourceLine(w, fn, asm[fn.Info.Lineno], reader, rpt)
printFunctionSourceLine(w, fn, asm[fn.Info.Lineno], reader, getPtileCSSClassName(fn.Cum, ptiles), rpt)
}
printFunctionClosing(w)
}
return nil
}

func getPtileCSSClassName(cumSum int64, ptiles map[int64]int64) string {
if cumSum == 0 {
return ""
}
for key, value := range ptiles {
if cumSum > value {
return " ptile_" + strconv.FormatInt(key, 10)
}
}
return ""
}

// calculatePtile expects cumSums to be sorted and
// to contain unique elements. It also expects the ptile to be between 0 and 99.
func calculatePtile(ptile int64, cumSums []int64) int64 {
rank := float64(ptile) / 100 * float64(len(cumSums))
return cumSums[int64(rank)]
}

func getArrayOfCumValues(fnodes graph.Nodes) []int64 {
arrayOfCumValues := make([]int64, 0, len(fnodes))

for _, fn := range fnodes {
arrayOfCumValues = append(arrayOfCumValues, fn.Cum)
}

return arrayOfCumValues
}

type int64Slice []int64

func (a int64Slice) Len() int { return len(a) }
func (a int64Slice) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a int64Slice) Less(i, j int) bool { return a[i] < a[j] }

// calculatePtiles returns nil when the fnodes length is 0
// because its result will never be used in such a case.
func calculatePtiles(fnodes graph.Nodes) map[int64]int64 {
if len(fnodes) == 0 {
return nil
}
arrayOfCumValues := getArrayOfCumValues(fnodes)
ptiles := map[int64]int64{95: 0, 80: 0}
sort.Sort(int64Slice(arrayOfCumValues))
for key := range ptiles {
ptiles[key] = calculatePtile(key, arrayOfCumValues)
}

return ptiles
}

// sourceCoordinates returns the lowest and highest line numbers from
// a set of assembly statements.
func sourceCoordinates(asm map[int][]assemblyInstruction) (start, end int) {
Expand Down Expand Up @@ -348,19 +401,21 @@ func printFunctionHeader(w io.Writer, name, path string, flatSum, cumSum int64,
}

// printFunctionSourceLine prints a source line and the corresponding assembly.
func printFunctionSourceLine(w io.Writer, fn *graph.Node, assembly []assemblyInstruction, reader *sourceReader, rpt *Report) {
func printFunctionSourceLine(w io.Writer, fn *graph.Node, assembly []assemblyInstruction, reader *sourceReader, ptileCSSClassName string, rpt *Report) {
if len(assembly) == 0 {
fmt.Fprintf(w,
"<span class=line> %6d</span> <span class=nop> %10s %10s %8s %s </span>\n",
"<span class=line> %6d</span> <span class=\"nop%s\"> %10s %10s %8s %s </span>\n",
fn.Info.Lineno,
ptileCSSClassName,
valueOrDot(fn.Flat, rpt), valueOrDot(fn.Cum, rpt),
"", template.HTMLEscapeString(fn.Info.Name))
return
}

fmt.Fprintf(w,
"<span class=line> %6d</span> <span class=deadsrc> %10s %10s %8s %s </span>",
"<span class=line> %6d</span> <span class=\"deadsrc%s\"> %10s %10s %8s %s </span>",
fn.Info.Lineno,
ptileCSSClassName,
valueOrDot(fn.Flat, rpt), valueOrDot(fn.Cum, rpt),
"", template.HTMLEscapeString(fn.Info.Name))
srcIndent := indentation(fn.Info.Name)
Expand Down
6 changes: 6 additions & 0 deletions internal/report/source_html.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,12 @@ background-color: #eeeeee;
color: #008800;
display: none;
}
.ptile_95 {
background-color: #fe5252
}
.ptile_80 {
background-color: #ffb6c1
}
</style>`

const weblistPageScript = `<script type="text/javascript">
Expand Down
74 changes: 74 additions & 0 deletions internal/report/source_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,89 @@ import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"regexp"
"runtime"
"strconv"
"strings"
"testing"

"github.com/google/pprof/internal/binutils"
"github.com/google/pprof/internal/graph"
"github.com/google/pprof/profile"
)

type prettyNodes graph.Nodes

func (nodes prettyNodes) String() string {
strs := make([]string, 0, len(nodes)+2)
strs = append(strs, "[")
for _, node := range nodes {
strs = append(strs, "Cum: "+strconv.FormatInt(node.Cum, 10))
}
strs = append(strs, "]")
return strings.Join(strs, " ")
}

func TestCalculatePtiles(t *testing.T) {
for _, testCase := range []struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there should be at least these additional test cases:

  • Duplicate values.
  • All equal values.

nodes graph.Nodes
want map[int64]int64
}{
{
nodes: nil,
want: nil,
},
{
nodes: []*graph.Node{
{Cum: 0},
{Cum: 8},
},
want: map[int64]int64{80: 8, 95: 8},
},
{
nodes: []*graph.Node{
{Cum: 10},
{Cum: 5},
{Cum: 8},
},
want: map[int64]int64{80: 10, 95: 10},
},
{
nodes: []*graph.Node{
{Cum: 0},
{Cum: 0},
{Cum: 0},
{Cum: 0},
{Cum: 11},
{Cum: 2},
{Cum: 9},
{Cum: 4},
{Cum: 8},
{Cum: 2},
{Cum: 16},
{Cum: 22},
{Cum: 13},
},
want: map[int64]int64{80: 13, 95: 22},
},
{
nodes: []*graph.Node{
{Cum: 10},
{Cum: 10},
{Cum: 10},
{Cum: 10},
{Cum: 10},
},
want: map[int64]int64{80: 10, 95: 10},
},
} {
if ptiles := calculatePtiles(testCase.nodes); !reflect.DeepEqual(ptiles, testCase.want) {
t.Errorf("calculatePtiles(%v) = %v; want %v", prettyNodes(testCase.nodes), ptiles, testCase.want)
}
}
}

func TestWebList(t *testing.T) {
if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
t.Skip("weblist only tested on x86-64 linux")
Expand Down