-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlines.go
More file actions
114 lines (103 loc) · 2.78 KB
/
lines.go
File metadata and controls
114 lines (103 loc) · 2.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package main
import (
"fmt"
"os"
"sort"
"github.com/spf13/cobra"
)
func newLinesCmd() *cobra.Command {
var shared sharedFlags
var method string
var top int
var fqn bool
cmd := &cobra.Command{
Use: "lines <file>",
Short: "Source-line breakdown inside a method (-m required)",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if method == "" {
return fmt.Errorf("-m/--method required")
}
pctx, err := preprocessProfile(shared.toOpts(args[0], "lines"))
if err != nil {
return err
}
return cmdLines(pctx.sf, method, top, fqn)
},
}
shared.register(cmd)
cmd.Flags().StringVarP(&method, "method", "m", "", "Substring match on method name (required)")
cmd.Flags().IntVar(&top, "top", 0, "Limit output rows (default: unlimited)")
cmd.Flags().BoolVar(&fqn, "fqn", false, "Show fully-qualified names")
return cmd
}
type lineEntry struct {
name string
line uint32
samples int
}
// computeLines returns the per-source-line sample counts for the given method.
// hasMethod is true when frames match but no line info is available.
func computeLines(sf *stackFile, method string, top int, fqn bool) (result []lineEntry, hasMethod bool) {
if sf.totalSamples == 0 {
return nil, false
}
type lineKey struct {
name string
line uint32
}
lineCounts := make(map[lineKey]int)
foundAny := false
for i := range sf.stacks {
st := &sf.stacks[i]
seen := make(map[lineKey]bool)
for j, fr := range st.frames {
if matchesMethod(fr, method) && st.lines[j] > 0 {
key := lineKey{displayName(fr, fqn), st.lines[j]}
if !seen[key] {
lineCounts[key] += st.count
seen[key] = true
}
foundAny = true
}
}
}
if !foundAny {
for i := range sf.stacks {
for _, fr := range sf.stacks[i].frames {
if matchesMethod(fr, method) {
return nil, true
}
}
}
return nil, false
}
var ranked []lineEntry
for k, c := range lineCounts {
ranked = append(ranked, lineEntry{k.name, k.line, c})
}
sort.Slice(ranked, func(i, j int) bool { return ranked[i].samples > ranked[j].samples })
ranked = ranked[:truncate(len(ranked), top)]
return ranked, true
}
func cmdLines(sf *stackFile, method string, top int, fqn bool) error {
if sf.totalSamples == 0 {
fmt.Fprintln(os.Stdout, "no samples (empty profile or all filtered out)")
return nil
}
ranked, hasMethod := computeLines(sf, method, top, fqn)
if ranked == nil {
if hasMethod {
return fmt.Errorf("no line info for frames matching '%s'", method)
}
noMatchMessage(os.Stdout, sf, method)
return nil
}
fmt.Printf("%-40s %9s %7s\n", "SOURCE:LINE", "SAMPLES", "PCT")
for _, e := range ranked {
pct := pctOf(e.samples, sf.totalSamples)
loc := fmt.Sprintf("%s:%d", e.name, e.line)
fmt.Printf("%-40s %9d %6.1f%%\n", loc, e.samples, pct)
}
return nil
}