jruby-async-profiler 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/.gitmodules +3 -0
  4. data/Gemfile +4 -0
  5. data/README.md +35 -0
  6. data/Rakefile +2 -0
  7. data/bin/console +14 -0
  8. data/bin/setup +8 -0
  9. data/ext/Rakefile +6 -0
  10. data/ext/async-profiler/.gitattributes +1 -0
  11. data/ext/async-profiler/.gitignore +6 -0
  12. data/ext/async-profiler/.travis.yml +11 -0
  13. data/ext/async-profiler/CHANGELOG.md +107 -0
  14. data/ext/async-profiler/JavaHome.class +0 -0
  15. data/ext/async-profiler/LICENSE +201 -0
  16. data/ext/async-profiler/Makefile +66 -0
  17. data/ext/async-profiler/README.md +487 -0
  18. data/ext/async-profiler/demo/SwingSet2.svg +2247 -0
  19. data/ext/async-profiler/docs/cddl1.txt +358 -0
  20. data/ext/async-profiler/profiler.sh +240 -0
  21. data/ext/async-profiler/src/allocTracer.cpp +155 -0
  22. data/ext/async-profiler/src/allocTracer.h +74 -0
  23. data/ext/async-profiler/src/arch.h +69 -0
  24. data/ext/async-profiler/src/arguments.cpp +265 -0
  25. data/ext/async-profiler/src/arguments.h +152 -0
  26. data/ext/async-profiler/src/codeCache.cpp +128 -0
  27. data/ext/async-profiler/src/codeCache.h +99 -0
  28. data/ext/async-profiler/src/engine.cpp +50 -0
  29. data/ext/async-profiler/src/engine.h +38 -0
  30. data/ext/async-profiler/src/flameGraph.cpp +770 -0
  31. data/ext/async-profiler/src/flameGraph.h +118 -0
  32. data/ext/async-profiler/src/flightRecorder.cpp +727 -0
  33. data/ext/async-profiler/src/flightRecorder.h +39 -0
  34. data/ext/async-profiler/src/frameName.cpp +189 -0
  35. data/ext/async-profiler/src/frameName.h +56 -0
  36. data/ext/async-profiler/src/itimer.cpp +49 -0
  37. data/ext/async-profiler/src/itimer.h +43 -0
  38. data/ext/async-profiler/src/jattach/jattach.c +437 -0
  39. data/ext/async-profiler/src/java/one/profiler/AsyncProfiler.java +160 -0
  40. data/ext/async-profiler/src/java/one/profiler/AsyncProfilerMXBean.java +43 -0
  41. data/ext/async-profiler/src/java/one/profiler/Counter.java +25 -0
  42. data/ext/async-profiler/src/java/one/profiler/Events.java +28 -0
  43. data/ext/async-profiler/src/javaApi.cpp +124 -0
  44. data/ext/async-profiler/src/lockTracer.cpp +161 -0
  45. data/ext/async-profiler/src/lockTracer.h +55 -0
  46. data/ext/async-profiler/src/mutex.cpp +33 -0
  47. data/ext/async-profiler/src/mutex.h +49 -0
  48. data/ext/async-profiler/src/os.h +45 -0
  49. data/ext/async-profiler/src/os_linux.cpp +129 -0
  50. data/ext/async-profiler/src/os_macos.cpp +115 -0
  51. data/ext/async-profiler/src/perfEvents.h +60 -0
  52. data/ext/async-profiler/src/perfEvents_linux.cpp +550 -0
  53. data/ext/async-profiler/src/perfEvents_macos.cpp +64 -0
  54. data/ext/async-profiler/src/profiler.cpp +952 -0
  55. data/ext/async-profiler/src/profiler.h +238 -0
  56. data/ext/async-profiler/src/spinLock.h +66 -0
  57. data/ext/async-profiler/src/stackFrame.h +57 -0
  58. data/ext/async-profiler/src/stackFrame_aarch64.cpp +75 -0
  59. data/ext/async-profiler/src/stackFrame_arm.cpp +58 -0
  60. data/ext/async-profiler/src/stackFrame_i386.cpp +82 -0
  61. data/ext/async-profiler/src/stackFrame_x64.cpp +113 -0
  62. data/ext/async-profiler/src/symbols.h +37 -0
  63. data/ext/async-profiler/src/symbols_linux.cpp +354 -0
  64. data/ext/async-profiler/src/symbols_macos.cpp +156 -0
  65. data/ext/async-profiler/src/vmEntry.cpp +173 -0
  66. data/ext/async-profiler/src/vmEntry.h +105 -0
  67. data/ext/async-profiler/src/vmStructs.cpp +104 -0
  68. data/ext/async-profiler/src/vmStructs.h +112 -0
  69. data/ext/async-profiler/src/wallClock.cpp +96 -0
  70. data/ext/async-profiler/src/wallClock.h +56 -0
  71. data/ext/async-profiler/test/AllocatingTarget.java +26 -0
  72. data/ext/async-profiler/test/LoadLibraryTest.java +21 -0
  73. data/ext/async-profiler/test/Target.java +31 -0
  74. data/ext/async-profiler/test/ThreadsTarget.java +35 -0
  75. data/ext/async-profiler/test/alloc-smoke-test.sh +36 -0
  76. data/ext/async-profiler/test/load-library-test.sh +35 -0
  77. data/ext/async-profiler/test/smoke-test.sh +37 -0
  78. data/ext/async-profiler/test/thread-smoke-test.sh +32 -0
  79. data/jruby-async-profiler.gemspec +32 -0
  80. data/lib/jruby/async/profiler.rb +10 -0
  81. data/lib/jruby/async/profiler/version.rb +7 -0
  82. metadata +155 -0
@@ -0,0 +1,770 @@
1
+ /*
2
+ * Copyright 2018 Andrei Pangin
3
+ *
4
+ * This is a specialized C++ port of the FlameGraph script available at
5
+ * https://github.com/brendangregg/FlameGraph/blob/master/flamegraph.pl
6
+ *
7
+ * Copyright 2016 Netflix, Inc.
8
+ * Copyright 2011 Joyent, Inc. All rights reserved.
9
+ * Copyright 2011 Brendan Gregg. All rights reserved.
10
+ *
11
+ * CDDL HEADER START
12
+ *
13
+ * The contents of this file are subject to the terms of the
14
+ * Common Development and Distribution License (the "License").
15
+ * You may not use this file except in compliance with the License.
16
+ *
17
+ * You can obtain a copy of the license at docs/cddl1.txt or
18
+ * http://opensource.org/licenses/CDDL-1.0.
19
+ * See the License for the specific language governing permissions
20
+ * and limitations under the License.
21
+ *
22
+ * When distributing Covered Code, include this CDDL HEADER in each
23
+ * file and include the License file at docs/cddl1.txt.
24
+ * If applicable, add the following below this CDDL HEADER, with the
25
+ * fields enclosed by brackets "[]" replaced with your own identifying
26
+ * information: Portions Copyright [yyyy] [name of copyright owner]
27
+ *
28
+ * CDDL HEADER END
29
+ */
30
+
31
+ #include <iomanip>
32
+ #include <vector>
33
+ #include <algorithm>
34
+ #include <math.h>
35
+ #include <stdio.h>
36
+ #include <stdlib.h>
37
+ #include "flameGraph.h"
38
+
39
+
40
+ static const char SVG_HEADER[] =
41
+ "<?xml version=\"1.0\" standalone=\"no\"?>\n"
42
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n"
43
+ "<svg version=\"1.1\" width=\"%d\" height=\"%d\" onload=\"init(evt)\" viewBox=\"0 0 %d %d\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n"
44
+ "<style type=\"text/css\">\n"
45
+ "\ttext { font-family:Verdana; font-size:12px; fill:rgb(0,0,0); }\n"
46
+ "\t#search { opacity:0.1; cursor:pointer; }\n"
47
+ "\t#search:hover, #search.show { opacity:1; }\n"
48
+ "\t#subtitle { text-anchor:middle; font-color:rgb(160,160,160); }\n"
49
+ "\t#title { text-anchor:middle; font-size:17px}\n"
50
+ "\t#unzoom { cursor:pointer; }\n"
51
+ "\t#frames > *:hover { stroke:black; stroke-width:0.5; cursor:pointer; }\n"
52
+ "\t.hide { display:none; }\n"
53
+ "\t.parent { opacity:0.5; }\n"
54
+ "</style>\n"
55
+ "<script type=\"text/ecmascript\">\n"
56
+ "<![CDATA[\n"
57
+ "\t\"use strict\";\n"
58
+ "\tvar details, searchbtn, unzoombtn, matchedtxt, svg, searching;\n"
59
+ "\tfunction init(evt) {\n"
60
+ "\t\tdetails = document.getElementById(\"details\").firstChild;\n"
61
+ "\t\tsearchbtn = document.getElementById(\"search\");\n"
62
+ "\t\tunzoombtn = document.getElementById(\"unzoom\");\n"
63
+ "\t\tmatchedtxt = document.getElementById(\"matched\");\n"
64
+ "\t\tsvg = document.getElementsByTagName(\"svg\")[0];\n"
65
+ "\t\tsearching = 0;\n"
66
+ "\t}\n"
67
+ "\n"
68
+ "\twindow.addEventListener(\"click\", function(e) {\n"
69
+ "\t\tvar target = find_group(e.target);\n"
70
+ "\t\tif (target) {\n"
71
+ "\t\t\tif (target.nodeName == \"a\") {\n"
72
+ "\t\t\t\tif (e.ctrlKey === false) return;\n"
73
+ "\t\t\t\te.preventDefault();\n"
74
+ "\t\t\t}\n"
75
+ "\t\t\tif (target.classList.contains(\"parent\")) unzoom();\n"
76
+ "\t\t\tzoom(target);\n"
77
+ "\t\t}\n"
78
+ "\t\telse if (e.target.id == \"unzoom\") unzoom();\n"
79
+ "\t\telse if (e.target.id == \"search\") search_prompt();\n"
80
+ "\t}, false)\n"
81
+ "\n"
82
+ "\t// mouse-over for info\n"
83
+ "\t// show\n"
84
+ "\twindow.addEventListener(\"mouseover\", function(e) {\n"
85
+ "\t\tvar target = find_group(e.target);\n"
86
+ "\t\tif (target) details.nodeValue = \"Function: \" + g_to_text(target);\n"
87
+ "\t}, false)\n"
88
+ "\n"
89
+ "\t// clear\n"
90
+ "\twindow.addEventListener(\"mouseout\", function(e) {\n"
91
+ "\t\tvar target = find_group(e.target);\n"
92
+ "\t\tif (target) details.nodeValue = ' ';\n"
93
+ "\t}, false)\n"
94
+ "\n"
95
+ "\t// ctrl-F for search\n"
96
+ "\twindow.addEventListener(\"keydown\",function (e) {\n"
97
+ "\t\tif (e.keyCode === 114 || (e.ctrlKey && e.keyCode === 70)) {\n"
98
+ "\t\t\te.preventDefault();\n"
99
+ "\t\t\tsearch_prompt();\n"
100
+ "\t\t}\n"
101
+ "\t}, false)\n"
102
+ "\n"
103
+ "\t// functions\n"
104
+ "\tfunction find_child(node, selector) {\n"
105
+ "\t\tvar children = node.querySelectorAll(selector);\n"
106
+ "\t\tif (children.length) return children[0];\n"
107
+ "\t\treturn;\n"
108
+ "\t}\n"
109
+ "\tfunction find_group(node) {\n"
110
+ "\t\tvar parent = node.parentElement;\n"
111
+ "\t\tif (!parent) return;\n"
112
+ "\t\tif (parent.id == \"frames\") return node;\n"
113
+ "\t\treturn find_group(parent);\n"
114
+ "\t}\n"
115
+ "\tfunction orig_save(e, attr, val) {\n"
116
+ "\t\tif (e.attributes[\"_orig_\" + attr] != undefined) return;\n"
117
+ "\t\tif (e.attributes[attr] == undefined) return;\n"
118
+ "\t\tif (val == undefined) val = e.attributes[attr].value;\n"
119
+ "\t\te.setAttribute(\"_orig_\" + attr, val);\n"
120
+ "\t}\n"
121
+ "\tfunction orig_load(e, attr) {\n"
122
+ "\t\tif (e.attributes[\"_orig_\"+attr] == undefined) return;\n"
123
+ "\t\te.attributes[attr].value = e.attributes[\"_orig_\" + attr].value;\n"
124
+ "\t\te.removeAttribute(\"_orig_\"+attr);\n"
125
+ "\t}\n"
126
+ "\tfunction g_to_text(e) {\n"
127
+ "\t\tvar text = find_child(e, \"title\").firstChild.nodeValue;\n"
128
+ "\t\treturn (text)\n"
129
+ "\t}\n"
130
+ "\tfunction g_to_func(e) {\n"
131
+ "\t\tvar func = g_to_text(e);\n"
132
+ "\t\t// if there's any manipulation we want to do to the function\n"
133
+ "\t\t// name before it's searched, do it here before returning.\n"
134
+ "\t\treturn (func);\n"
135
+ "\t}\n"
136
+ "\tfunction update_text(e) {\n"
137
+ "\t\tvar r = find_child(e, \"rect\");\n"
138
+ "\t\tvar t = find_child(e, \"text\");\n"
139
+ "\t\tvar w = parseFloat(r.attributes.width.value) -3;\n"
140
+ "\t\tvar txt = find_child(e, \"title\").textContent.replace(/\\([^(]*\\)$/,\"\");\n"
141
+ "\t\tt.attributes.x.value = parseFloat(r.attributes.x.value) + 3;\n"
142
+ "\n"
143
+ "\t\t// Smaller than this size won't fit anything\n"
144
+ "\t\tif (w < 2 * 12 * 0.59) {\n"
145
+ "\t\t\tt.textContent = \"\";\n"
146
+ "\t\t\treturn;\n"
147
+ "\t\t}\n"
148
+ "\n"
149
+ "\t\tt.textContent = txt;\n"
150
+ "\t\t// Fit in full text width\n"
151
+ "\t\tif (/^ *$/.test(txt) || t.getSubStringLength(0, txt.length) < w)\n"
152
+ "\t\t\treturn;\n"
153
+ "\n"
154
+ "\t\tfor (var x = txt.length - 2; x > 0; x--) {\n"
155
+ "\t\t\tif (t.getSubStringLength(0, x + 2) <= w) {\n"
156
+ "\t\t\t\tt.textContent = txt.substring(0, x) + \"..\";\n"
157
+ "\t\t\t\treturn;\n"
158
+ "\t\t\t}\n"
159
+ "\t\t}\n"
160
+ "\t\tt.textContent = \"\";\n"
161
+ "\t}\n"
162
+ "\n"
163
+ "\t// zoom\n"
164
+ "\tfunction zoom_reset(e) {\n"
165
+ "\t\tif (e.attributes != undefined) {\n"
166
+ "\t\t\torig_load(e, \"x\");\n"
167
+ "\t\t\torig_load(e, \"width\");\n"
168
+ "\t\t}\n"
169
+ "\t\tif (e.childNodes == undefined) return;\n"
170
+ "\t\tfor (var i = 0, c = e.childNodes; i < c.length; i++) {\n"
171
+ "\t\t\tzoom_reset(c[i]);\n"
172
+ "\t\t}\n"
173
+ "\t}\n"
174
+ "\tfunction zoom_child(e, x, ratio) {\n"
175
+ "\t\tif (e.attributes != undefined) {\n"
176
+ "\t\t\tif (e.attributes.x != undefined) {\n"
177
+ "\t\t\t\torig_save(e, \"x\");\n"
178
+ "\t\t\t\te.attributes.x.value = (parseFloat(e.attributes.x.value) - x - 10) * ratio + 10;\n"
179
+ "\t\t\t\tif (e.tagName == \"text\")\n"
180
+ "\t\t\t\t\te.attributes.x.value = find_child(e.parentNode, \"rect[x]\").attributes.x.value + 3;\n"
181
+ "\t\t\t}\n"
182
+ "\t\t\tif (e.attributes.width != undefined) {\n"
183
+ "\t\t\t\torig_save(e, \"width\");\n"
184
+ "\t\t\t\te.attributes.width.value = parseFloat(e.attributes.width.value) * ratio;\n"
185
+ "\t\t\t}\n"
186
+ "\t\t}\n"
187
+ "\n"
188
+ "\t\tif (e.childNodes == undefined) return;\n"
189
+ "\t\tfor (var i = 0, c = e.childNodes; i < c.length; i++) {\n"
190
+ "\t\t\tzoom_child(c[i], x - 10, ratio);\n"
191
+ "\t\t}\n"
192
+ "\t}\n"
193
+ "\tfunction zoom_parent(e) {\n"
194
+ "\t\tif (e.attributes) {\n"
195
+ "\t\t\tif (e.attributes.x != undefined) {\n"
196
+ "\t\t\t\torig_save(e, \"x\");\n"
197
+ "\t\t\t\te.attributes.x.value = 10;\n"
198
+ "\t\t\t}\n"
199
+ "\t\t\tif (e.attributes.width != undefined) {\n"
200
+ "\t\t\t\torig_save(e, \"width\");\n"
201
+ "\t\t\t\te.attributes.width.value = parseInt(svg.width.baseVal.value) - (10 * 2);\n"
202
+ "\t\t\t}\n"
203
+ "\t\t}\n"
204
+ "\t\tif (e.childNodes == undefined) return;\n"
205
+ "\t\tfor (var i = 0, c = e.childNodes; i < c.length; i++) {\n"
206
+ "\t\t\tzoom_parent(c[i]);\n"
207
+ "\t\t}\n"
208
+ "\t}\n"
209
+ "\tfunction zoom(node) {\n"
210
+ "\t\tvar attr = find_child(node, \"rect\").attributes;\n"
211
+ "\t\tvar width = parseFloat(attr.width.value);\n"
212
+ "\t\tvar xmin = parseFloat(attr.x.value);\n"
213
+ "\t\tvar xmax = parseFloat(xmin + width);\n"
214
+ "\t\tvar ymin = parseFloat(attr.y.value);\n"
215
+ "\t\tvar ratio = (svg.width.baseVal.value - 2 * 10) / width;\n"
216
+ "\n"
217
+ "\t\t// XXX: Workaround for JavaScript float issues (fix me)\n"
218
+ "\t\tvar fudge = 0.0001;\n"
219
+ "\n"
220
+ "\t\tunzoombtn.classList.remove(\"hide\");\n"
221
+ "\n"
222
+ "\t\tvar el = document.getElementById(\"frames\").children;\n"
223
+ "\t\tfor (var i = 0; i < el.length; i++) {\n"
224
+ "\t\t\tvar e = el[i];\n"
225
+ "\t\t\tvar a = find_child(e, \"rect\").attributes;\n"
226
+ "\t\t\tvar ex = parseFloat(a.x.value);\n"
227
+ "\t\t\tvar ew = parseFloat(a.width.value);\n"
228
+ "\t\t\tvar upstack;\n"
229
+ "\t\t\t// Is it an ancestor\n"
230
+ "\t\t\tif (%d == 0) {\n"
231
+ "\t\t\t\tupstack = parseFloat(a.y.value) > ymin;\n"
232
+ "\t\t\t} else {\n"
233
+ "\t\t\t\tupstack = parseFloat(a.y.value) < ymin;\n"
234
+ "\t\t\t}\n"
235
+ "\t\t\tif (upstack) {\n"
236
+ "\t\t\t\t// Direct ancestor\n"
237
+ "\t\t\t\tif (ex <= xmin && (ex+ew+fudge) >= xmax) {\n"
238
+ "\t\t\t\t\te.classList.add(\"parent\");\n"
239
+ "\t\t\t\t\tzoom_parent(e);\n"
240
+ "\t\t\t\t\tupdate_text(e);\n"
241
+ "\t\t\t\t}\n"
242
+ "\t\t\t\t// not in current path\n"
243
+ "\t\t\t\telse\n"
244
+ "\t\t\t\t\te.classList.add(\"hide\");\n"
245
+ "\t\t\t}\n"
246
+ "\t\t\t// Children maybe\n"
247
+ "\t\t\telse {\n"
248
+ "\t\t\t\t// no common path\n"
249
+ "\t\t\t\tif (ex < xmin || ex + fudge >= xmax) {\n"
250
+ "\t\t\t\t\te.classList.add(\"hide\");\n"
251
+ "\t\t\t\t}\n"
252
+ "\t\t\t\telse {\n"
253
+ "\t\t\t\t\tzoom_child(e, xmin, ratio);\n"
254
+ "\t\t\t\t\tupdate_text(e);\n"
255
+ "\t\t\t\t}\n"
256
+ "\t\t\t}\n"
257
+ "\t\t}\n"
258
+ "\t}\n"
259
+ "\tfunction unzoom() {\n"
260
+ "\t\tunzoombtn.classList.add(\"hide\");\n"
261
+ "\t\tvar el = document.getElementById(\"frames\").children;\n"
262
+ "\t\tfor(var i = 0; i < el.length; i++) {\n"
263
+ "\t\t\tel[i].classList.remove(\"parent\");\n"
264
+ "\t\t\tel[i].classList.remove(\"hide\");\n"
265
+ "\t\t\tzoom_reset(el[i]);\n"
266
+ "\t\t\tupdate_text(el[i]);\n"
267
+ "\t\t}\n"
268
+ "\t}\n"
269
+ "\n"
270
+ "\t// search\n"
271
+ "\tfunction reset_search() {\n"
272
+ "\t\tvar el = document.querySelectorAll(\"#frames rect\");\n"
273
+ "\t\tfor (var i = 0; i < el.length; i++) {\n"
274
+ "\t\t\torig_load(el[i], \"fill\")\n"
275
+ "\t\t}\n"
276
+ "\t}\n"
277
+ "\tfunction search_prompt() {\n"
278
+ "\t\tif (!searching) {\n"
279
+ "\t\t\tvar term = prompt(\"Enter a search term (regexp \" +\n"
280
+ "\t\t\t \"allowed, eg: ^ext4_)\", \"\");\n"
281
+ "\t\t\tif (term != null) {\n"
282
+ "\t\t\t\tsearch(term)\n"
283
+ "\t\t\t}\n"
284
+ "\t\t} else {\n"
285
+ "\t\t\treset_search();\n"
286
+ "\t\t\tsearching = 0;\n"
287
+ "\t\t\tsearchbtn.classList.remove(\"show\");\n"
288
+ "\t\t\tsearchbtn.firstChild.nodeValue = \"Search\"\n"
289
+ "\t\t\tmatchedtxt.classList.add(\"hide\");\n"
290
+ "\t\t\tmatchedtxt.firstChild.nodeValue = \"\"\n"
291
+ "\t\t}\n"
292
+ "\t}\n"
293
+ "\tfunction search(term) {\n"
294
+ "\t\tvar re = new RegExp(term);\n"
295
+ "\t\tvar el = document.getElementById(\"frames\").children;\n"
296
+ "\t\tvar matches = new Object();\n"
297
+ "\t\tvar maxwidth = 0;\n"
298
+ "\t\tfor (var i = 0; i < el.length; i++) {\n"
299
+ "\t\t\tvar e = el[i];\n"
300
+ "\t\t\tvar func = g_to_func(e);\n"
301
+ "\t\t\tvar rect = find_child(e, \"rect\");\n"
302
+ "\t\t\tif (func == null || rect == null)\n"
303
+ "\t\t\t\tcontinue;\n"
304
+ "\n"
305
+ "\t\t\t// Save max width. Only works as we have a root frame\n"
306
+ "\t\t\tvar w = parseFloat(rect.attributes.width.value);\n"
307
+ "\t\t\tif (w > maxwidth)\n"
308
+ "\t\t\t\tmaxwidth = w;\n"
309
+ "\n"
310
+ "\t\t\tif (func.match(re)) {\n"
311
+ "\t\t\t\t// highlight\n"
312
+ "\t\t\t\tvar x = parseFloat(rect.attributes.x.value);\n"
313
+ "\t\t\t\torig_save(rect, \"fill\");\n"
314
+ "\t\t\t\trect.attributes.fill.value = \"rgb(230,0,230)\";\n"
315
+ "\n"
316
+ "\t\t\t\t// remember matches\n"
317
+ "\t\t\t\tif (matches[x] == undefined) {\n"
318
+ "\t\t\t\t\tmatches[x] = w;\n"
319
+ "\t\t\t\t} else {\n"
320
+ "\t\t\t\t\tif (w > matches[x]) {\n"
321
+ "\t\t\t\t\t\t// overwrite with parent\n"
322
+ "\t\t\t\t\t\tmatches[x] = w;\n"
323
+ "\t\t\t\t\t}\n"
324
+ "\t\t\t\t}\n"
325
+ "\t\t\t\tsearching = 1;\n"
326
+ "\t\t\t}\n"
327
+ "\t\t}\n"
328
+ "\t\tif (!searching)\n"
329
+ "\t\t\treturn;\n"
330
+ "\n"
331
+ "\t\tsearchbtn.classList.add(\"show\");\n"
332
+ "\t\tsearchbtn.firstChild.nodeValue = \"Reset Search\";\n"
333
+ "\n"
334
+ "\t\t// calculate percent matched, excluding vertical overlap\n"
335
+ "\t\tvar count = 0;\n"
336
+ "\t\tvar lastx = -1;\n"
337
+ "\t\tvar lastw = 0;\n"
338
+ "\t\tvar keys = Array();\n"
339
+ "\t\tfor (k in matches) {\n"
340
+ "\t\t\tif (matches.hasOwnProperty(k))\n"
341
+ "\t\t\t\tkeys.push(k);\n"
342
+ "\t\t}\n"
343
+ "\t\t// sort the matched frames by their x location\n"
344
+ "\t\t// ascending, then width descending\n"
345
+ "\t\tkeys.sort(function(a, b){\n"
346
+ "\t\t\treturn a - b;\n"
347
+ "\t\t});\n"
348
+ "\t\t// Step through frames saving only the biggest bottom-up frames\n"
349
+ "\t\t// thanks to the sort order. This relies on the tree property\n"
350
+ "\t\t// where children are always smaller than their parents.\n"
351
+ "\t\tvar fudge = 0.0001;\t// JavaScript floating point\n"
352
+ "\t\tfor (var k in keys) {\n"
353
+ "\t\t\tvar x = parseFloat(keys[k]);\n"
354
+ "\t\t\tvar w = matches[keys[k]];\n"
355
+ "\t\t\tif (x >= lastx + lastw - fudge) {\n"
356
+ "\t\t\t\tcount += w;\n"
357
+ "\t\t\t\tlastx = x;\n"
358
+ "\t\t\t\tlastw = w;\n"
359
+ "\t\t\t}\n"
360
+ "\t\t}\n"
361
+ "\t\t// display matched percent\n"
362
+ "\t\tmatchedtxt.classList.remove(\"hide\");\n"
363
+ "\t\tvar pct = 100 * count / maxwidth;\n"
364
+ "\t\tif (pct != 100) pct = pct.toFixed(1)\n"
365
+ "\t\tmatchedtxt.firstChild.nodeValue = \"Matched: \" + pct + \"%%\";\n"
366
+ "\t}\n"
367
+ "]]>\n"
368
+ "</script>\n"
369
+ "<rect x=\"0\" y=\"0\" width=\"100%%\" height=\"100%%\" fill=\"rgb(240,240,220)\"/>\n"
370
+ "<text id=\"title\" x=\"%d\" y=\"%d\">%s</text>\n"
371
+ "<text id=\"details\" x=\"%d\" y=\"%d\"> </text>\n"
372
+ "<text id=\"unzoom\" x=\"%d\" y=\"%d\" class=\"hide\">Reset Zoom</text>\n"
373
+ "<text id=\"search\" x=\"%d\" y=\"%d\">Search</text>\n"
374
+ "<text id=\"matched\" x=\"%d\" y=\"%d\"> </text>\n"
375
+ "<g id=\"frames\">\n";
376
+
377
+ static const char TREE_HEADER[] =
378
+ "<!DOCTYPE html>\n"
379
+ "<html lang=\"en\">\n"
380
+ "<head>\n"
381
+ "<title>Tree view</title>\n"
382
+ "<meta charset=\"utf-8\"/>\n"
383
+ "<style>\n"
384
+ "body {\n"
385
+ " font-family: Arial;\n"
386
+ "}\n"
387
+ "ul.tree li {\n"
388
+ " list-style-type: none;\n"
389
+ " position: relative;\n"
390
+ "}\n"
391
+ "ul.tree ul {\n"
392
+ " margin-left: 20px; padding-left: 0;\n"
393
+ "}\n"
394
+ "ul.tree li ul {\n"
395
+ " display: none;\n"
396
+ "}\n"
397
+ "ul.tree li.open > ul {\n"
398
+ " display: block;\n"
399
+ "}\n"
400
+ "ul.tree li div:before {\n"
401
+ " height: 1em;\n"
402
+ " padding:0 .1em;\n"
403
+ " font-size: .8em;\n"
404
+ " display: block;\n"
405
+ " position: absolute;\n"
406
+ " left: -1.3em;\n"
407
+ " top: .2em;\n"
408
+ "}\n"
409
+ "ul.tree li > div:not(:nth-last-child(2)):before {\n"
410
+ " content: '+';\n"
411
+ "}\n"
412
+ "ul.tree li.open > div:not(:nth-last-child(2)):before {\n"
413
+ " content: '-';\n"
414
+ "}\n"
415
+ ".sc {\n"
416
+ " text-decoration: underline;\n"
417
+ " text-decoration-color: black;\n"
418
+ " font-weight: bold;\n"
419
+ " background-color: #D9D9D9;\n"
420
+ "}\n"
421
+ ".green {\n"
422
+ " color: #32c832;\n"
423
+ "}\n"
424
+ ".aqua {\n"
425
+ " color: #32a5a5;\n"
426
+ "}\n"
427
+ ".brown {\n"
428
+ " color: #be5a00;\n"
429
+ "}\n"
430
+ ".yellow {\n"
431
+ " color: #afaf32;\n"
432
+ "}\n"
433
+ ".red {\n"
434
+ " color: #c83232;\n"
435
+ "}\n"
436
+ "ul.tree li > div {\n"
437
+ " display: inline;\n"
438
+ " cursor: pointer;\n"
439
+ " color: black;\n"
440
+ " text-decoration: none;\n"
441
+ "}\n"
442
+ "</style>\n"
443
+ "<script>\n"
444
+ "function treeView(opt) {\n"
445
+ " var tree = document.querySelectorAll('ul.tree div:not(:last-child)');\n"
446
+ " for(var i = 0; i < tree.length; i++){\n"
447
+ " var parent = tree[i].parentElement;\n"
448
+ " var classList = parent.classList;\n"
449
+ " if(opt == 0) {\n"
450
+ " classList.add('open');\n"
451
+ " } else {\n"
452
+ " classList.remove('open');\n"
453
+ " }\n"
454
+ " }\n"
455
+ "}\n"
456
+ "function openParent(p,t) {\n"
457
+ " if(p.parentElement.classList.contains(\"tree\")) {\n"
458
+ " return;\n"
459
+ " }\n"
460
+ " p.parentElement.classList.add('open');\n"
461
+ " openParent(p.parentElement,t);\n"
462
+ "}\n"
463
+ "function search() {\n"
464
+ " var tree = document.querySelectorAll('ul.tree span');\n"
465
+ " var check = document.getElementById('check');\n"
466
+ " for(var i = 0; i < tree.length; i++){\n"
467
+ " tree[i].classList.remove('sc');\n"
468
+ " if(tree[i].innerHTML.includes(document.getElementById(\"search\").value)) {\n"
469
+ " tree[i].classList.add('sc');\n"
470
+ " openParent(tree[i].parentElement,tree);\n"
471
+ " }\n"
472
+ " }\n"
473
+ "}\n"
474
+ "function openUL(n) {\n"
475
+ " var children = n.children;\n"
476
+ " if(children.length == 1) {\n"
477
+ " openNode(children[0]);\n"
478
+ " }\n"
479
+ "}\n"
480
+ "function openNode(n) {\n"
481
+ " var children = n.children;\n"
482
+ " for(var i = 0; i < children.length; i++){\n"
483
+ " if(children[i].nodeName == 'UL') {\n"
484
+ " n.classList.add('open');\n"
485
+ " openUL(children[i]);\n"
486
+ " }\n"
487
+ " }\n"
488
+ "}\n"
489
+ "function addClickActions() {\n"
490
+ "var tree = document.querySelectorAll('ul.tree div:not(:last-child)');\n"
491
+ "for(var i = 0; i < tree.length; i++){\n"
492
+ " tree[i].addEventListener('click', function(e) {\n"
493
+ " var parent = e.target.parentElement;\n"
494
+ " var classList = parent.classList;\n"
495
+ " if(classList.contains(\"open\")) {\n"
496
+ " classList.remove('open');\n"
497
+ " var opensubs = parent.querySelectorAll(':scope .open');\n"
498
+ " for(var i = 0; i < opensubs.length; i++){\n"
499
+ " opensubs[i].classList.remove('open');\n"
500
+ " }\n"
501
+ " } else {\n"
502
+ " if(e.altKey) {\n"
503
+ " classList.add('open');\n"
504
+ " var opensubs = parent.querySelectorAll('li');\n"
505
+ " for(var i = 0; i < opensubs.length; i++){\n"
506
+ " opensubs[i].classList.add('open');\n"
507
+ " }\n"
508
+ " } else {\n"
509
+ " openNode(parent);\n"
510
+ " }\n"
511
+ " }\n"
512
+ " });\n"
513
+ "}\n"
514
+ "}\n"
515
+ "</script>\n"
516
+ "</head>\n"
517
+ "<body>\n"
518
+ "<div style=\"padding-left: 25px;\">%s view, total %s: %s </div>\n"
519
+ "<div style=\"padding-left: 25px;\"><button type='button' onclick='treeView(0)'>++</button><button type='button' onclick='treeView(1)'>--</button>\n"
520
+ "<input type='text' id='search' value='' size='35' onkeypress=\"if(event.keyCode == 13) document.getElementById('searchBtn').click()\">\n"
521
+ "<button type='button' id='searchBtn' onclick='search()'>search</button></div>\n"
522
+ "<ul class=\"tree\">\n";
523
+
524
+ static const char TREE_FOOTER[] =
525
+ "<script>\n"
526
+ "addClickActions();\n"
527
+ "</script>\n"
528
+ "</ul>\n"
529
+ "</body>\n"
530
+ "</html>\n";
531
+
532
+
533
+ class StringUtils {
534
+ public:
535
+ static bool endsWith(const std::string& s, const char* suffix, size_t suffixlen) {
536
+ size_t len = s.length();
537
+ return len >= suffixlen && s.compare(len - suffixlen, suffixlen, suffix) == 0;
538
+ }
539
+
540
+ static std::string trim(const std::string& s, size_t maxchars) {
541
+ if (maxchars < 3) {
542
+ return "";
543
+ } else if (s.length() > maxchars) {
544
+ return s.substr(0, maxchars - 2) + "..";
545
+ } else {
546
+ return s;
547
+ }
548
+ }
549
+
550
+ static void replace(std::string& s, char c, const char* replacement) {
551
+ for (size_t i = 0; (i = s.find(c, i)) != std::string::npos; i++) {
552
+ s.replace(i, 1, replacement);
553
+ }
554
+ }
555
+
556
+ static void escape(std::string& s) {
557
+ replace(s, '&', "&amp;");
558
+ replace(s, '<', "&lt;");
559
+ replace(s, '>', "&gt;");
560
+ }
561
+ };
562
+
563
+
564
+ class Format {
565
+ private:
566
+ char _buf[32];
567
+
568
+ public:
569
+ const char* thousands(u64 value) {
570
+ char* p = _buf + sizeof(_buf) - 1;
571
+ *p = 0;
572
+
573
+ while (value >= 1000) {
574
+ p -= 4;
575
+ p[0] = ',';
576
+ p[1] = '0' + char(value % 1000 / 100);
577
+ p[2] = '0' + char(value % 100 / 10);
578
+ p[3] = '0' + char(value % 10);
579
+ value /= 1000;
580
+ }
581
+
582
+ do {
583
+ *--p = '0' + char(value % 10);
584
+ } while ((value /= 10) > 0);
585
+
586
+ return p;
587
+ }
588
+ };
589
+
590
+
591
+ class Palette {
592
+ private:
593
+ const char* _name;
594
+ int _base;
595
+ int _r, _g, _b;
596
+
597
+ public:
598
+ Palette(const char* name, int base, int r, int g, int b) : _name(name), _base(base), _r(r), _g(g), _b(b) {
599
+ }
600
+
601
+ const char* name() const {
602
+ return _name;
603
+ }
604
+
605
+ int pickColor() const {
606
+ double value = double(rand()) / RAND_MAX;
607
+ return _base + (int(_r * value) << 16 | int(_g * value) << 8 | int(_b * value));
608
+ }
609
+ };
610
+
611
+
612
+ void FlameGraph::dump(std::ostream& out, bool tree) {
613
+ _scale = (_imagewidth - 20) / (double)_root._total;
614
+ _pct = 100 / (double)_root._total;
615
+
616
+ u64 cutoff = (u64)ceil(_minwidth / _scale);
617
+ _imageheight = _frameheight * _root.depth(cutoff) + 70;
618
+
619
+ if (tree) {
620
+ printTreeHeader(out);
621
+ printTreeFrame(out, _root, 0);
622
+ printTreeFooter(out);
623
+ } else {
624
+ printHeader(out);
625
+ printFrame(out, "all", _root, 10, _reverse ? 35 : (_imageheight - _frameheight - 35));
626
+ printFooter(out);
627
+ }
628
+ }
629
+
630
+ void FlameGraph::printHeader(std::ostream& out) {
631
+ char buf[sizeof(SVG_HEADER) + 256];
632
+ int x0 = _imagewidth / 2;
633
+ int x1 = 10;
634
+ int x2 = _imagewidth - 110;
635
+ int y0 = 24;
636
+ int y1 = _imageheight - 17;
637
+
638
+ sprintf(buf, SVG_HEADER,
639
+ _imagewidth, _imageheight, _imagewidth, _imageheight, _reverse,
640
+ x0, y0, _title, x1, y1, x1, y0, x2, y0, x2, y1);
641
+ out << buf;
642
+ }
643
+
644
+ void FlameGraph::printFooter(std::ostream& out) {
645
+ out << "</g>\n</svg>\n";
646
+ }
647
+
648
+ double FlameGraph::printFrame(std::ostream& out, const std::string& name, const Trie& f, double x, double y) {
649
+ double framewidth = f._total * _scale;
650
+
651
+ // Skip too narrow frames, they are not important
652
+ if (framewidth >= _minwidth) {
653
+ std::string full_title = name;
654
+ int color = selectFramePalette(full_title).pickColor();
655
+ std::string short_title = StringUtils::trim(full_title, size_t(framewidth / 7));
656
+ StringUtils::escape(full_title);
657
+ StringUtils::escape(short_title);
658
+
659
+ // Compensate rounding error in frame width
660
+ double w = (round((x + framewidth) * 10) - round(x * 10)) / 10.0;
661
+
662
+ snprintf(_buf, sizeof(_buf),
663
+ "<g>\n"
664
+ "<title>%s (%s samples, %.2f%%)</title><rect x=\"%.1f\" y=\"%.1f\" width=\"%.1f\" height=\"%d\" fill=\"#%06x\" rx=\"2\" ry=\"2\"/>\n"
665
+ "<text x=\"%.1f\" y=\"%.1f\">%s</text>\n"
666
+ "</g>\n",
667
+ full_title.c_str(), Format().thousands(f._total), f._total * _pct, x, y, w, _frameheight - 1, color,
668
+ x + 3, y + 3 + _frameheight * 0.5, short_title.c_str());
669
+ out << _buf;
670
+
671
+ x += f._self * _scale;
672
+ y += _reverse ? _frameheight : -_frameheight;
673
+
674
+ for (std::map<std::string, Trie>::const_iterator it = f._children.begin(); it != f._children.end(); ++it) {
675
+ x += printFrame(out, it->first, it->second, x, y);
676
+ }
677
+ }
678
+
679
+ return framewidth;
680
+ }
681
+
682
+ void FlameGraph::printTreeHeader(std::ostream& out) {
683
+ char buf[sizeof(TREE_HEADER) + 256];
684
+ const char* title = _reverse ? "Backtrace" : "Call tree";
685
+ const char* counter = _counter == COUNTER_SAMPLES ? "samples" : "counter";
686
+ sprintf(buf, TREE_HEADER, title, counter, Format().thousands(_root._total));
687
+ out << buf;
688
+ }
689
+
690
+ void FlameGraph::printTreeFooter(std::ostream& out) {
691
+ out << TREE_FOOTER;
692
+ }
693
+
694
+ bool FlameGraph::printTreeFrame(std::ostream& out, const Trie& f, int depth) {
695
+ double framewidth = f._total * _scale;
696
+ if (framewidth < _minwidth) {
697
+ return false;
698
+ }
699
+
700
+ std::vector<Node> subnodes;
701
+ for (std::map<std::string, Trie>::const_iterator it = f._children.begin(); it != f._children.end(); ++it) {
702
+ subnodes.push_back(Node(it->first, it->second));
703
+ }
704
+ std::sort(subnodes.begin(), subnodes.end());
705
+
706
+ for (size_t i = 0; i < subnodes.size(); i++) {
707
+ std::string full_title = subnodes[i]._name;
708
+ const Trie* trie = subnodes[i]._trie;
709
+ const char* color = selectFramePalette(full_title).name();
710
+ StringUtils::escape(full_title);
711
+
712
+ if (_reverse) {
713
+ snprintf(_buf, sizeof(_buf),
714
+ "<li><div>[%d] %.2f%% %s</div><span class=\"%s\"> %s</span>\n",
715
+ depth,
716
+ trie->_total * _pct, Format().thousands(trie->_total),
717
+ color, full_title.c_str());
718
+ } else {
719
+ snprintf(_buf, sizeof(_buf),
720
+ "<li><div>[%d] %.2f%% %s self: %.2f%% %s</div><span class=\"%s\"> %s</span>\n",
721
+ depth,
722
+ trie->_total * _pct, Format().thousands(trie->_total),
723
+ trie->_self * _pct, Format().thousands(trie->_self),
724
+ color, full_title.c_str());
725
+ }
726
+ out << _buf;
727
+
728
+ if (trie->_children.size() > 0) {
729
+ out << "<ul>\n";
730
+ if (!printTreeFrame(out, *trie, depth + 1)) {
731
+ out << "<li>...\n";
732
+ }
733
+ out << "</ul>\n";
734
+ }
735
+ }
736
+
737
+ return true;
738
+ }
739
+
740
+ const Palette& FlameGraph::selectFramePalette(std::string& name) {
741
+ static const Palette
742
+ green ("green", 0x50e150, 30, 30, 30),
743
+ aqua ("aqua", 0x50bebe, 30, 30, 30),
744
+ brown ("brown", 0xe17d00, 30, 30, 0),
745
+ yellow("yellow", 0xc8c83c, 30, 30, 10),
746
+ red ("red", 0xe15a5a, 30, 40, 40);
747
+
748
+ if (StringUtils::endsWith(name, "_[j]", 4)) {
749
+ // Java compiled frame
750
+ name = name.substr(0, name.length() - 4);
751
+ return green;
752
+ } else if (StringUtils::endsWith(name, "_[i]", 4)) {
753
+ // Java inlined frame
754
+ name = name.substr(0, name.length() - 4);
755
+ return aqua;
756
+ } else if (StringUtils::endsWith(name, "_[k]", 4)) {
757
+ // Kernel function
758
+ name = name.substr(0, name.length() - 4);
759
+ return brown;
760
+ } else if (name.find("::") != std::string::npos || name.compare(0, 2, "-[") == 0 || name.compare(0, 2, "+[") == 0) {
761
+ // C++ function or Objective C method
762
+ return yellow;
763
+ } else if ((int)name.find('/') > 0 || ((int)name.find('.') > 0 && name[0] >= 'A' && name[0] <= 'Z')) {
764
+ // Java regular method
765
+ return green;
766
+ } else {
767
+ // Other native code
768
+ return red;
769
+ }
770
+ }