1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package graph
17
18 import (
19 "fmt"
20 "math"
21 "path/filepath"
22 "regexp"
23 "sort"
24 "strconv"
25 "strings"
26
27 "github.com/google/pprof/profile"
28 )
29
30 var (
31
32
33 javaRegExp = regexp.MustCompile(`^(?:[a-z]\w*\.)*([A-Z][\w\$]*\.(?:<init>|[a-z][\w\$]*(?:\$\d+)?))(?:(?:\()|$)`)
34
35
36 goRegExp = regexp.MustCompile(`^(?:[\w\-\.]+\/)+([^.]+\..+)`)
37
38 goVerRegExp = regexp.MustCompile(`^(.*?)/v(?:[2-9]|[1-9][0-9]+)([./].*)$`)
39
40
41
42
43
44 cppRegExp = regexp.MustCompile(`^(?:[_a-zA-Z]\w*::)+(_*[A-Z]\w*::~?[_a-zA-Z]\w*(?:<.*>)?)`)
45 cppAnonymousPrefixRegExp = regexp.MustCompile(`^\(anonymous namespace\)::`)
46 )
47
48
49
50 type Graph struct {
51 Nodes Nodes
52 }
53
54
55 type Options struct {
56 SampleValue func(s []int64) int64
57 SampleMeanDivisor func(s []int64) int64
58 FormatTag func(int64, string) string
59 ObjNames bool
60 OrigFnNames bool
61
62 CallTree bool
63 DropNegative bool
64
65 KeptNodes NodeSet
66 }
67
68
69 type Nodes []*Node
70
71
72
73 type Node struct {
74
75 Info NodeInfo
76
77
78
79
80
81
82 Function *Node
83
84
85
86 Flat, FlatDiv, Cum, CumDiv int64
87
88
89
90 In, Out EdgeMap
91
92
93 LabelTags TagMap
94
95
96
97
98
99 NumericTags map[string]TagMap
100 }
101
102
103
104 func (n *Node) FlatValue() int64 {
105 if n.FlatDiv == 0 {
106 return n.Flat
107 }
108 return n.Flat / n.FlatDiv
109 }
110
111
112
113 func (n *Node) CumValue() int64 {
114 if n.CumDiv == 0 {
115 return n.Cum
116 }
117 return n.Cum / n.CumDiv
118 }
119
120
121
122 func (n *Node) AddToEdge(to *Node, v int64, residual, inline bool) {
123 n.AddToEdgeDiv(to, 0, v, residual, inline)
124 }
125
126
127
128 func (n *Node) AddToEdgeDiv(to *Node, dv, v int64, residual, inline bool) {
129 if n.Out[to] != to.In[n] {
130 panic(fmt.Errorf("asymmetric edges %v %v", *n, *to))
131 }
132
133 if e := n.Out[to]; e != nil {
134 e.WeightDiv += dv
135 e.Weight += v
136 if residual {
137 e.Residual = true
138 }
139 if !inline {
140 e.Inline = false
141 }
142 return
143 }
144
145 info := &Edge{Src: n, Dest: to, WeightDiv: dv, Weight: v, Residual: residual, Inline: inline}
146 n.Out[to] = info
147 to.In[n] = info
148 }
149
150
151 type NodeInfo struct {
152 Name string
153 OrigName string
154 Address uint64
155 File string
156 StartLine, Lineno int
157 Columnno int
158 Objfile string
159 }
160
161
162 func (i *NodeInfo) PrintableName() string {
163 return strings.Join(i.NameComponents(), " ")
164 }
165
166
167 func (i *NodeInfo) NameComponents() []string {
168 var name []string
169 if i.Address != 0 {
170 name = append(name, fmt.Sprintf("%016x", i.Address))
171 }
172 if fun := i.Name; fun != "" {
173 name = append(name, fun)
174 }
175
176 switch {
177 case i.Lineno != 0:
178 s := fmt.Sprintf("%s:%d", i.File, i.Lineno)
179 if i.Columnno != 0 {
180 s += fmt.Sprintf(":%d", i.Columnno)
181 }
182
183 name = append(name, s)
184 case i.File != "":
185
186 name = append(name, i.File)
187 case i.Name != "":
188
189 case i.Objfile != "":
190
191 name = append(name, "["+filepath.Base(i.Objfile)+"]")
192 default:
193
194 name = append(name, "<unknown>")
195 }
196 return name
197 }
198
199
200
201 type NodeMap map[NodeInfo]*Node
202
203
204 type NodeSet map[NodeInfo]bool
205
206
207
208
209
210
211
212
213
214 type NodePtrSet map[*Node]bool
215
216
217
218
219 func (nm NodeMap) FindOrInsertNode(info NodeInfo, kept NodeSet) *Node {
220 if kept != nil {
221 if _, ok := kept[info]; !ok {
222 return nil
223 }
224 }
225
226 if n, ok := nm[info]; ok {
227 return n
228 }
229
230 n := &Node{
231 Info: info,
232 In: make(EdgeMap),
233 Out: make(EdgeMap),
234 LabelTags: make(TagMap),
235 NumericTags: make(map[string]TagMap),
236 }
237 nm[info] = n
238 if info.Address == 0 && info.Lineno == 0 {
239
240
241 n.Function = n
242 return n
243 }
244
245 info.Address = 0
246 info.Lineno = 0
247 info.Columnno = 0
248 n.Function = nm.FindOrInsertNode(info, nil)
249 return n
250 }
251
252
253 type EdgeMap map[*Node]*Edge
254
255
256 type Edge struct {
257 Src, Dest *Node
258
259 Weight, WeightDiv int64
260
261
262
263 Residual bool
264
265 Inline bool
266 }
267
268
269
270 func (e *Edge) WeightValue() int64 {
271 if e.WeightDiv == 0 {
272 return e.Weight
273 }
274 return e.Weight / e.WeightDiv
275 }
276
277
278 type Tag struct {
279 Name string
280 Unit string
281 Value int64
282 Flat, FlatDiv int64
283 Cum, CumDiv int64
284 }
285
286
287
288 func (t *Tag) FlatValue() int64 {
289 if t.FlatDiv == 0 {
290 return t.Flat
291 }
292 return t.Flat / t.FlatDiv
293 }
294
295
296
297 func (t *Tag) CumValue() int64 {
298 if t.CumDiv == 0 {
299 return t.Cum
300 }
301 return t.Cum / t.CumDiv
302 }
303
304
305 type TagMap map[string]*Tag
306
307
308 func SortTags(t []*Tag, flat bool) []*Tag {
309 ts := tags{t, flat}
310 sort.Sort(ts)
311 return ts.t
312 }
313
314
315 func New(prof *profile.Profile, o *Options) *Graph {
316 if o.CallTree {
317 return newTree(prof, o)
318 }
319 g, _ := newGraph(prof, o)
320 return g
321 }
322
323
324
325
326 func newGraph(prof *profile.Profile, o *Options) (*Graph, map[uint64]Nodes) {
327 nodes, locationMap := CreateNodes(prof, o)
328 seenNode := make(map[*Node]bool)
329 seenEdge := make(map[nodePair]bool)
330 for _, sample := range prof.Sample {
331 var w, dw int64
332 w = o.SampleValue(sample.Value)
333 if o.SampleMeanDivisor != nil {
334 dw = o.SampleMeanDivisor(sample.Value)
335 }
336 if dw == 0 && w == 0 {
337 continue
338 }
339 for k := range seenNode {
340 delete(seenNode, k)
341 }
342 for k := range seenEdge {
343 delete(seenEdge, k)
344 }
345 var parent *Node
346
347 residual := false
348
349 labels := joinLabels(sample)
350
351 for i := len(sample.Location) - 1; i >= 0; i-- {
352 l := sample.Location[i]
353 locNodes := locationMap[l.ID]
354 for ni := len(locNodes) - 1; ni >= 0; ni-- {
355 n := locNodes[ni]
356 if n == nil {
357 residual = true
358 continue
359 }
360
361 if _, ok := seenNode[n]; !ok {
362 seenNode[n] = true
363 n.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, false)
364 }
365
366 if _, ok := seenEdge[nodePair{n, parent}]; !ok && parent != nil && n != parent {
367 seenEdge[nodePair{n, parent}] = true
368 parent.AddToEdgeDiv(n, dw, w, residual, ni != len(locNodes)-1)
369 }
370 parent = n
371 residual = false
372 }
373 }
374 if parent != nil && !residual {
375
376 parent.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, true)
377 }
378 }
379
380 return selectNodesForGraph(nodes, o.DropNegative), locationMap
381 }
382
383 func selectNodesForGraph(nodes Nodes, dropNegative bool) *Graph {
384
385 gNodes := make(Nodes, 0, len(nodes))
386 for _, n := range nodes {
387 if n == nil {
388 continue
389 }
390 if n.Cum == 0 && n.Flat == 0 {
391 continue
392 }
393 if dropNegative && isNegative(n) {
394 continue
395 }
396 gNodes = append(gNodes, n)
397 }
398 return &Graph{gNodes}
399 }
400
401 type nodePair struct {
402 src, dest *Node
403 }
404
405 func newTree(prof *profile.Profile, o *Options) (g *Graph) {
406 parentNodeMap := make(map[*Node]NodeMap, len(prof.Sample))
407 for _, sample := range prof.Sample {
408 var w, dw int64
409 w = o.SampleValue(sample.Value)
410 if o.SampleMeanDivisor != nil {
411 dw = o.SampleMeanDivisor(sample.Value)
412 }
413 if dw == 0 && w == 0 {
414 continue
415 }
416 var parent *Node
417 labels := joinLabels(sample)
418
419 for i := len(sample.Location) - 1; i >= 0; i-- {
420 l := sample.Location[i]
421 lines := l.Line
422 if len(lines) == 0 {
423 lines = []profile.Line{{}}
424 }
425 for lidx := len(lines) - 1; lidx >= 0; lidx-- {
426 nodeMap := parentNodeMap[parent]
427 if nodeMap == nil {
428 nodeMap = make(NodeMap)
429 parentNodeMap[parent] = nodeMap
430 }
431 n := nodeMap.findOrInsertLine(l, lines[lidx], o)
432 if n == nil {
433 continue
434 }
435 n.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, false)
436 if parent != nil {
437 parent.AddToEdgeDiv(n, dw, w, false, lidx != len(lines)-1)
438 }
439 parent = n
440 }
441 }
442 if parent != nil {
443 parent.addSample(dw, w, labels, sample.NumLabel, sample.NumUnit, o.FormatTag, true)
444 }
445 }
446
447 nodes := make(Nodes, 0, len(prof.Location))
448 for _, nm := range parentNodeMap {
449 nodes = append(nodes, nm.nodes()...)
450 }
451 return selectNodesForGraph(nodes, o.DropNegative)
452 }
453
454
455 func ShortenFunctionName(f string) string {
456 f = cppAnonymousPrefixRegExp.ReplaceAllString(f, "")
457 f = goVerRegExp.ReplaceAllString(f, `${1}${2}`)
458 for _, re := range []*regexp.Regexp{goRegExp, javaRegExp, cppRegExp} {
459 if matches := re.FindStringSubmatch(f); len(matches) >= 2 {
460 return strings.Join(matches[1:], "")
461 }
462 }
463 return f
464 }
465
466
467
468 func (g *Graph) TrimTree(kept NodePtrSet) {
469
470 oldNodes := g.Nodes
471 g.Nodes = make(Nodes, 0, len(kept))
472
473 for _, cur := range oldNodes {
474
475 if len(cur.In) > 1 {
476 panic("TrimTree only works on trees")
477 }
478
479
480 if _, ok := kept[cur]; ok {
481 g.Nodes = append(g.Nodes, cur)
482 continue
483 }
484
485
486
487 if len(cur.In) == 0 {
488 for _, outEdge := range cur.Out {
489 delete(outEdge.Dest.In, cur)
490 }
491 continue
492 }
493
494
495
496 if len(cur.In) != 1 {
497 panic("Get parent assertion failed. cur.In expected to be of length 1.")
498 }
499 var parent *Node
500 for _, edge := range cur.In {
501 parent = edge.Src
502 }
503
504 parentEdgeInline := parent.Out[cur].Inline
505
506
507 delete(parent.Out, cur)
508
509
510 for _, outEdge := range cur.Out {
511 child := outEdge.Dest
512
513 delete(child.In, cur)
514 child.In[parent] = outEdge
515 parent.Out[child] = outEdge
516
517 outEdge.Src = parent
518 outEdge.Residual = true
519
520
521
522 outEdge.Inline = parentEdgeInline && outEdge.Inline
523 }
524 }
525 g.RemoveRedundantEdges()
526 }
527
528 func joinLabels(s *profile.Sample) string {
529 if len(s.Label) == 0 {
530 return ""
531 }
532
533 var labels []string
534 for key, vals := range s.Label {
535 for _, v := range vals {
536 labels = append(labels, key+":"+v)
537 }
538 }
539 sort.Strings(labels)
540 return strings.Join(labels, `\n`)
541 }
542
543
544
545 func isNegative(n *Node) bool {
546 switch {
547 case n.Flat < 0:
548 return true
549 case n.Flat == 0 && n.Cum < 0:
550 return true
551 default:
552 return false
553 }
554 }
555
556
557
558
559 func CreateNodes(prof *profile.Profile, o *Options) (Nodes, map[uint64]Nodes) {
560 locations := make(map[uint64]Nodes, len(prof.Location))
561 nm := make(NodeMap, len(prof.Location))
562 for _, l := range prof.Location {
563 lines := l.Line
564 if len(lines) == 0 {
565 lines = []profile.Line{{}}
566 }
567 nodes := make(Nodes, len(lines))
568 for ln := range lines {
569 nodes[ln] = nm.findOrInsertLine(l, lines[ln], o)
570 }
571 locations[l.ID] = nodes
572 }
573 return nm.nodes(), locations
574 }
575
576 func (nm NodeMap) nodes() Nodes {
577 nodes := make(Nodes, 0, len(nm))
578 for _, n := range nm {
579 nodes = append(nodes, n)
580 }
581 return nodes
582 }
583
584 func (nm NodeMap) findOrInsertLine(l *profile.Location, li profile.Line, o *Options) *Node {
585 var objfile string
586 if m := l.Mapping; m != nil && m.File != "" {
587 objfile = m.File
588 }
589
590 if ni := nodeInfo(l, li, objfile, o); ni != nil {
591 return nm.FindOrInsertNode(*ni, o.KeptNodes)
592 }
593 return nil
594 }
595
596 func nodeInfo(l *profile.Location, line profile.Line, objfile string, o *Options) *NodeInfo {
597 if line.Function == nil {
598 return &NodeInfo{Address: l.Address, Objfile: objfile}
599 }
600 ni := &NodeInfo{
601 Address: l.Address,
602 Lineno: int(line.Line),
603 Columnno: int(line.Column),
604 Name: line.Function.Name,
605 }
606 if fname := line.Function.Filename; fname != "" {
607 ni.File = filepath.Clean(fname)
608 }
609 if o.OrigFnNames {
610 ni.OrigName = line.Function.SystemName
611 }
612 if o.ObjNames || (ni.Name == "" && ni.OrigName == "") {
613 ni.Objfile = objfile
614 ni.StartLine = int(line.Function.StartLine)
615 }
616 return ni
617 }
618
619 type tags struct {
620 t []*Tag
621 flat bool
622 }
623
624 func (t tags) Len() int { return len(t.t) }
625 func (t tags) Swap(i, j int) { t.t[i], t.t[j] = t.t[j], t.t[i] }
626 func (t tags) Less(i, j int) bool {
627 if !t.flat {
628 if t.t[i].Cum != t.t[j].Cum {
629 return abs64(t.t[i].Cum) > abs64(t.t[j].Cum)
630 }
631 }
632 if t.t[i].Flat != t.t[j].Flat {
633 return abs64(t.t[i].Flat) > abs64(t.t[j].Flat)
634 }
635 return t.t[i].Name < t.t[j].Name
636 }
637
638
639 func (ns Nodes) Sum() (flat int64, cum int64) {
640 for _, n := range ns {
641 flat += n.Flat
642 cum += n.Cum
643 }
644 return
645 }
646
647 func (n *Node) addSample(dw, w int64, labels string, numLabel map[string][]int64, numUnit map[string][]string, format func(int64, string) string, flat bool) {
648
649 if flat {
650 n.FlatDiv += dw
651 n.Flat += w
652 } else {
653 n.CumDiv += dw
654 n.Cum += w
655 }
656
657
658 if labels != "" {
659 t := n.LabelTags.findOrAddTag(labels, "", 0)
660 if flat {
661 t.FlatDiv += dw
662 t.Flat += w
663 } else {
664 t.CumDiv += dw
665 t.Cum += w
666 }
667 }
668
669 numericTags := n.NumericTags[labels]
670 if numericTags == nil {
671 numericTags = TagMap{}
672 n.NumericTags[labels] = numericTags
673 }
674
675 if format == nil {
676 format = defaultLabelFormat
677 }
678 for k, nvals := range numLabel {
679 units := numUnit[k]
680 for i, v := range nvals {
681 var t *Tag
682 if len(units) > 0 {
683 t = numericTags.findOrAddTag(format(v, units[i]), units[i], v)
684 } else {
685 t = numericTags.findOrAddTag(format(v, k), k, v)
686 }
687 if flat {
688 t.FlatDiv += dw
689 t.Flat += w
690 } else {
691 t.CumDiv += dw
692 t.Cum += w
693 }
694 }
695 }
696 }
697
698 func defaultLabelFormat(v int64, key string) string {
699 return strconv.FormatInt(v, 10)
700 }
701
702 func (m TagMap) findOrAddTag(label, unit string, value int64) *Tag {
703 l := m[label]
704 if l == nil {
705 l = &Tag{
706 Name: label,
707 Unit: unit,
708 Value: value,
709 }
710 m[label] = l
711 }
712 return l
713 }
714
715
716 func (g *Graph) String() string {
717 var s []string
718
719 nodeIndex := make(map[*Node]int, len(g.Nodes))
720
721 for i, n := range g.Nodes {
722 nodeIndex[n] = i + 1
723 }
724
725 for i, n := range g.Nodes {
726 name := n.Info.PrintableName()
727 var in, out []int
728
729 for _, from := range n.In {
730 in = append(in, nodeIndex[from.Src])
731 }
732 for _, to := range n.Out {
733 out = append(out, nodeIndex[to.Dest])
734 }
735 s = append(s, fmt.Sprintf("%d: %s[flat=%d cum=%d] %x -> %v ", i+1, name, n.Flat, n.Cum, in, out))
736 }
737 return strings.Join(s, "\n")
738 }
739
740
741
742 func (g *Graph) DiscardLowFrequencyNodes(nodeCutoff int64) NodeSet {
743 return makeNodeSet(g.Nodes, nodeCutoff)
744 }
745
746
747
748 func (g *Graph) DiscardLowFrequencyNodePtrs(nodeCutoff int64) NodePtrSet {
749 cutNodes := getNodesAboveCumCutoff(g.Nodes, nodeCutoff)
750 kept := make(NodePtrSet, len(cutNodes))
751 for _, n := range cutNodes {
752 kept[n] = true
753 }
754 return kept
755 }
756
757 func makeNodeSet(nodes Nodes, nodeCutoff int64) NodeSet {
758 cutNodes := getNodesAboveCumCutoff(nodes, nodeCutoff)
759 kept := make(NodeSet, len(cutNodes))
760 for _, n := range cutNodes {
761 kept[n.Info] = true
762 }
763 return kept
764 }
765
766
767
768 func getNodesAboveCumCutoff(nodes Nodes, nodeCutoff int64) Nodes {
769 cutoffNodes := make(Nodes, 0, len(nodes))
770 for _, n := range nodes {
771 if abs64(n.Cum) < nodeCutoff {
772 continue
773 }
774 cutoffNodes = append(cutoffNodes, n)
775 }
776 return cutoffNodes
777 }
778
779
780
781 func (g *Graph) TrimLowFrequencyTags(tagCutoff int64) {
782
783 for _, n := range g.Nodes {
784 n.LabelTags = trimLowFreqTags(n.LabelTags, tagCutoff)
785 for s, nt := range n.NumericTags {
786 n.NumericTags[s] = trimLowFreqTags(nt, tagCutoff)
787 }
788 }
789 }
790
791 func trimLowFreqTags(tags TagMap, minValue int64) TagMap {
792 kept := TagMap{}
793 for s, t := range tags {
794 if abs64(t.Flat) >= minValue || abs64(t.Cum) >= minValue {
795 kept[s] = t
796 }
797 }
798 return kept
799 }
800
801
802
803 func (g *Graph) TrimLowFrequencyEdges(edgeCutoff int64) int {
804 var droppedEdges int
805 for _, n := range g.Nodes {
806 for src, e := range n.In {
807 if abs64(e.Weight) < edgeCutoff {
808 delete(n.In, src)
809 delete(src.Out, n)
810 droppedEdges++
811 }
812 }
813 }
814 return droppedEdges
815 }
816
817
818 func (g *Graph) SortNodes(cum bool, visualMode bool) {
819
820 switch {
821 case visualMode:
822
823 g.Nodes.Sort(EntropyOrder)
824 case cum:
825 g.Nodes.Sort(CumNameOrder)
826 default:
827 g.Nodes.Sort(FlatNameOrder)
828 }
829 }
830
831
832 func (g *Graph) SelectTopNodePtrs(maxNodes int, visualMode bool) NodePtrSet {
833 set := make(NodePtrSet)
834 for _, node := range g.selectTopNodes(maxNodes, visualMode) {
835 set[node] = true
836 }
837 return set
838 }
839
840
841 func (g *Graph) SelectTopNodes(maxNodes int, visualMode bool) NodeSet {
842 return makeNodeSet(g.selectTopNodes(maxNodes, visualMode), 0)
843 }
844
845
846 func (g *Graph) selectTopNodes(maxNodes int, visualMode bool) Nodes {
847 if maxNodes > 0 {
848 if visualMode {
849 var count int
850
851
852 for i, n := range g.Nodes {
853 tags := countTags(n)
854 if tags > maxNodelets {
855 tags = maxNodelets
856 }
857 if count += tags + 1; count >= maxNodes {
858 maxNodes = i + 1
859 break
860 }
861 }
862 }
863 }
864 if maxNodes > len(g.Nodes) {
865 maxNodes = len(g.Nodes)
866 }
867 return g.Nodes[:maxNodes]
868 }
869
870
871
872 func countTags(n *Node) int {
873 count := 0
874 for _, e := range n.LabelTags {
875 if e.Flat != 0 {
876 count++
877 }
878 }
879 for _, t := range n.NumericTags {
880 for _, e := range t {
881 if e.Flat != 0 {
882 count++
883 }
884 }
885 }
886 return count
887 }
888
889
890
891
892 func (g *Graph) RemoveRedundantEdges() {
893
894
895 for i := len(g.Nodes); i > 0; i-- {
896 n := g.Nodes[i-1]
897 in := n.In.Sort()
898 for j := len(in); j > 0; j-- {
899 e := in[j-1]
900 if !e.Residual {
901
902
903 break
904 }
905 if isRedundantEdge(e) {
906 delete(e.Src.Out, e.Dest)
907 delete(e.Dest.In, e.Src)
908 }
909 }
910 }
911 }
912
913
914
915 func isRedundantEdge(e *Edge) bool {
916 src, n := e.Src, e.Dest
917 seen := map[*Node]bool{n: true}
918 queue := Nodes{n}
919 for len(queue) > 0 {
920 n := queue[0]
921 queue = queue[1:]
922 for _, ie := range n.In {
923 if e == ie || seen[ie.Src] {
924 continue
925 }
926 if ie.Src == src {
927 return true
928 }
929 seen[ie.Src] = true
930 queue = append(queue, ie.Src)
931 }
932 }
933 return false
934 }
935
936
937
938 type nodeSorter struct {
939 rs Nodes
940 less func(l, r *Node) bool
941 }
942
943 func (s nodeSorter) Len() int { return len(s.rs) }
944 func (s nodeSorter) Swap(i, j int) { s.rs[i], s.rs[j] = s.rs[j], s.rs[i] }
945 func (s nodeSorter) Less(i, j int) bool { return s.less(s.rs[i], s.rs[j]) }
946
947
948
949
950
951 func (ns Nodes) Sort(o NodeOrder) error {
952 var s nodeSorter
953
954 switch o {
955 case FlatNameOrder:
956 s = nodeSorter{ns,
957 func(l, r *Node) bool {
958 if iv, jv := abs64(l.Flat), abs64(r.Flat); iv != jv {
959 return iv > jv
960 }
961 if iv, jv := l.Info.PrintableName(), r.Info.PrintableName(); iv != jv {
962 return iv < jv
963 }
964 if iv, jv := abs64(l.Cum), abs64(r.Cum); iv != jv {
965 return iv > jv
966 }
967 return compareNodes(l, r)
968 },
969 }
970 case FlatCumNameOrder:
971 s = nodeSorter{ns,
972 func(l, r *Node) bool {
973 if iv, jv := abs64(l.Flat), abs64(r.Flat); iv != jv {
974 return iv > jv
975 }
976 if iv, jv := abs64(l.Cum), abs64(r.Cum); iv != jv {
977 return iv > jv
978 }
979 if iv, jv := l.Info.PrintableName(), r.Info.PrintableName(); iv != jv {
980 return iv < jv
981 }
982 return compareNodes(l, r)
983 },
984 }
985 case NameOrder:
986 s = nodeSorter{ns,
987 func(l, r *Node) bool {
988 if iv, jv := l.Info.Name, r.Info.Name; iv != jv {
989 return iv < jv
990 }
991 return compareNodes(l, r)
992 },
993 }
994 case FileOrder:
995 s = nodeSorter{ns,
996 func(l, r *Node) bool {
997 if iv, jv := l.Info.File, r.Info.File; iv != jv {
998 return iv < jv
999 }
1000 if iv, jv := l.Info.StartLine, r.Info.StartLine; iv != jv {
1001 return iv < jv
1002 }
1003 return compareNodes(l, r)
1004 },
1005 }
1006 case AddressOrder:
1007 s = nodeSorter{ns,
1008 func(l, r *Node) bool {
1009 if iv, jv := l.Info.Address, r.Info.Address; iv != jv {
1010 return iv < jv
1011 }
1012 return compareNodes(l, r)
1013 },
1014 }
1015 case CumNameOrder, EntropyOrder:
1016
1017 var score map[*Node]int64
1018 scoreOrder := func(l, r *Node) bool {
1019 if iv, jv := abs64(score[l]), abs64(score[r]); iv != jv {
1020 return iv > jv
1021 }
1022 if iv, jv := l.Info.PrintableName(), r.Info.PrintableName(); iv != jv {
1023 return iv < jv
1024 }
1025 if iv, jv := abs64(l.Flat), abs64(r.Flat); iv != jv {
1026 return iv > jv
1027 }
1028 return compareNodes(l, r)
1029 }
1030
1031 switch o {
1032 case CumNameOrder:
1033 score = make(map[*Node]int64, len(ns))
1034 for _, n := range ns {
1035 score[n] = n.Cum
1036 }
1037 s = nodeSorter{ns, scoreOrder}
1038 case EntropyOrder:
1039 score = make(map[*Node]int64, len(ns))
1040 for _, n := range ns {
1041 score[n] = entropyScore(n)
1042 }
1043 s = nodeSorter{ns, scoreOrder}
1044 }
1045 default:
1046 return fmt.Errorf("report: unrecognized sort ordering: %d", o)
1047 }
1048 sort.Sort(s)
1049 return nil
1050 }
1051
1052
1053
1054 func compareNodes(l, r *Node) bool {
1055 return fmt.Sprint(l.Info) < fmt.Sprint(r.Info)
1056 }
1057
1058
1059
1060
1061
1062
1063
1064
1065 func entropyScore(n *Node) int64 {
1066 score := float64(0)
1067
1068 if len(n.In) == 0 {
1069 score++
1070 } else {
1071 score += edgeEntropyScore(n, n.In, 0)
1072 }
1073
1074 if len(n.Out) == 0 {
1075 score++
1076 } else {
1077 score += edgeEntropyScore(n, n.Out, n.Flat)
1078 }
1079
1080 return int64(score*float64(n.Cum)) + n.Flat
1081 }
1082
1083
1084
1085
1086
1087
1088 func edgeEntropyScore(n *Node, edges EdgeMap, self int64) float64 {
1089 score := float64(0)
1090 total := self
1091 for _, e := range edges {
1092 if e.Weight > 0 {
1093 total += abs64(e.Weight)
1094 }
1095 }
1096 if total != 0 {
1097 for _, e := range edges {
1098 frac := float64(abs64(e.Weight)) / float64(total)
1099 score += -frac * math.Log2(frac)
1100 }
1101 if self > 0 {
1102 frac := float64(abs64(self)) / float64(total)
1103 score += -frac * math.Log2(frac)
1104 }
1105 }
1106 return score
1107 }
1108
1109
1110 type NodeOrder int
1111
1112
1113 const (
1114 FlatNameOrder NodeOrder = iota
1115 FlatCumNameOrder
1116 CumNameOrder
1117 NameOrder
1118 FileOrder
1119 AddressOrder
1120 EntropyOrder
1121 )
1122
1123
1124
1125
1126 func (e EdgeMap) Sort() []*Edge {
1127 el := make(edgeList, 0, len(e))
1128 for _, w := range e {
1129 el = append(el, w)
1130 }
1131
1132 sort.Sort(el)
1133 return el
1134 }
1135
1136
1137 func (e EdgeMap) Sum() int64 {
1138 var ret int64
1139 for _, edge := range e {
1140 ret += edge.Weight
1141 }
1142 return ret
1143 }
1144
1145 type edgeList []*Edge
1146
1147 func (el edgeList) Len() int {
1148 return len(el)
1149 }
1150
1151 func (el edgeList) Less(i, j int) bool {
1152 if el[i].Weight != el[j].Weight {
1153 return abs64(el[i].Weight) > abs64(el[j].Weight)
1154 }
1155
1156 from1 := el[i].Src.Info.PrintableName()
1157 from2 := el[j].Src.Info.PrintableName()
1158 if from1 != from2 {
1159 return from1 < from2
1160 }
1161
1162 to1 := el[i].Dest.Info.PrintableName()
1163 to2 := el[j].Dest.Info.PrintableName()
1164
1165 return to1 < to2
1166 }
1167
1168 func (el edgeList) Swap(i, j int) {
1169 el[i], el[j] = el[j], el[i]
1170 }
1171
1172 func abs64(i int64) int64 {
1173 if i < 0 {
1174 return -i
1175 }
1176 return i
1177 }
1178
View as plain text