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