@@ -21,6 +21,7 @@ import (
2121 "fmt"
2222 "image/color"
2323 "io"
24+ "sort"
2425 "strings"
2526 "text/template"
2627 "time"
@@ -35,38 +36,66 @@ var ganttTemplate = `
3536 <head>
3637 <script type="text/javascript" src="https://www.gstatic.com/charts/loader.js"></script>
3738 <script type="text/javascript">
38- google.charts.load('current', {'packages':['timeline']});
39-
39+ google.charts.load('current', {'packages': ['timeline', 'controls']});
4040 google.charts.setOnLoadCallback(drawTimeline);
41- function drawTimeline() {
42- var container = document.getElementById('timeline');
43- var chart = new google.visualization.Timeline(container);
41+
42+ function dataTable() {
4443 var dataTable = new google.visualization.DataTable();
44+
4545 dataTable.addColumn({ type: 'string', id: 'Layer' });
4646 dataTable.addColumn({ type: 'string', id: 'Function' });
4747 dataTable.addColumn({ type: 'string', id: 'style', role: 'style' });
4848 dataTable.addColumn({ type: 'date', id: 'Start' });
4949 dataTable.addColumn({ type: 'date', id: 'End' });
50-
50+
5151 dataTable.addRows([
52- {{ range $g := .TL.Goroutines }}
52+ {{ range $g := .TL.Goroutines | Sorted }}
5353 {{ range $index, $layer := .Layers}}
5454 {{ range $layer.Calls }}
55- [ '{{ $g.ID }}: {{ $g.Signature | Creator }}', '{{ .Name }}', '{{ Color .Package $index }}', new Date({{ .StartDelta | Milliseconds }}), new Date({{ .EndDelta | Milliseconds }}) ],
55+ [ '{{ $g.ID }}: {{ $g.Signature | Creator }}', '{{ .Name }}', '{{ Color .Package $index }}', new Date({{ .StartDelta | Milliseconds }}), new Date({{ .EndDelta | Milliseconds }}) ],
5656 {{ end }}
5757 {{ end }}
5858 {{ end }}
5959 ]);
60+ return dataTable;
61+ }
62+
63+ function drawTimeline() {
64+ var container = document.getElementById('dashboard');
65+ var dashboard = new google.visualization.Dashboard(container);
66+ var picker = new google.visualization.ControlWrapper({
67+ controlType: 'CategoryFilter',
68+ containerId: 'picker',
69+ options: {
70+ filterColumnIndex: 0,
71+ ui: {
72+ selectedValuesLayout: 'below',
73+ label: "Goroutines to display:",
74+ sortValues: false,
75+ },
76+ },
77+ }
78+ );
79+
80+ var timeline = new google.visualization.ChartWrapper({
81+ chartType: 'Timeline',
82+ containerId: 'timeline',
83+ });
84+
85+ dashboard.bind(picker, timeline);
6086 var options = {
6187 avoidOverlappingGridLines: false,
6288 };
63- chart .draw(dataTable, options);
89+ dashboard .draw(dataTable() , options);
6490 }
6591 </script>
6692 </head>
6793 <body>
68- <h1>SlowJam for {{ .Duration}} ({{ .TL.Samples }} samples) - <a href="/">full</a> | <a href="/simple">simple</a></h1>
69- <div id="timeline" style="width: 3200px; height: 1024px;"></div>
94+ <h1>SlowJam for {{ .Duration}} ({{ .TL.Samples }} samples, {{ len .TL.Goroutines }} goroutines) - <a href="/">full</a> | <a href="/simple">simple</a></h1>
95+ <div id="dashboard">
96+ <div id="picker"></div>
97+ <div id="timeline" style="width: 3200px; height: 1024px;"></div>
98+ </div>
7099 </body>
71100</html>
72101`
@@ -80,6 +109,7 @@ func Render(w io.Writer, tl *stackparse.Timeline) error {
80109 "Creator" : creator ,
81110 "Height" : height ,
82111 "Color" : callColor ,
112+ "Sorted" : sorted ,
83113 }
84114
85115 t , err := template .New ("timeline" ).Funcs (fmap ).Parse (ganttTemplate )
@@ -107,6 +137,21 @@ func milliseconds(d time.Duration) string {
107137 return fmt .Sprintf ("%d" , d .Milliseconds ())
108138}
109139
140+ func sorted (grs map [int ]* stackparse.GoroutineTimeline ) []* stackparse.GoroutineTimeline {
141+ rt := []* stackparse.GoroutineTimeline {}
142+ ids := []int {}
143+ for id := range grs {
144+ ids = append (ids , id )
145+ }
146+ sort .Ints (ids )
147+ for _ , id := range ids {
148+ if gr := grs [id ]; gr != nil {
149+ rt = append (rt , gr )
150+ }
151+ }
152+ return rt
153+ }
154+
110155func creator (s * stack.Signature ) string {
111156 c := s .CreatedBy .Func .PkgDotName ()
112157 if c == "" {
0 commit comments