jruby-async-profiler 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.gitmodules +3 -0
- data/Gemfile +4 -0
- data/README.md +35 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/Rakefile +6 -0
- data/ext/async-profiler/.gitattributes +1 -0
- data/ext/async-profiler/.gitignore +6 -0
- data/ext/async-profiler/.travis.yml +11 -0
- data/ext/async-profiler/CHANGELOG.md +107 -0
- data/ext/async-profiler/JavaHome.class +0 -0
- data/ext/async-profiler/LICENSE +201 -0
- data/ext/async-profiler/Makefile +66 -0
- data/ext/async-profiler/README.md +487 -0
- data/ext/async-profiler/demo/SwingSet2.svg +2247 -0
- data/ext/async-profiler/docs/cddl1.txt +358 -0
- data/ext/async-profiler/profiler.sh +240 -0
- data/ext/async-profiler/src/allocTracer.cpp +155 -0
- data/ext/async-profiler/src/allocTracer.h +74 -0
- data/ext/async-profiler/src/arch.h +69 -0
- data/ext/async-profiler/src/arguments.cpp +265 -0
- data/ext/async-profiler/src/arguments.h +152 -0
- data/ext/async-profiler/src/codeCache.cpp +128 -0
- data/ext/async-profiler/src/codeCache.h +99 -0
- data/ext/async-profiler/src/engine.cpp +50 -0
- data/ext/async-profiler/src/engine.h +38 -0
- data/ext/async-profiler/src/flameGraph.cpp +770 -0
- data/ext/async-profiler/src/flameGraph.h +118 -0
- data/ext/async-profiler/src/flightRecorder.cpp +727 -0
- data/ext/async-profiler/src/flightRecorder.h +39 -0
- data/ext/async-profiler/src/frameName.cpp +189 -0
- data/ext/async-profiler/src/frameName.h +56 -0
- data/ext/async-profiler/src/itimer.cpp +49 -0
- data/ext/async-profiler/src/itimer.h +43 -0
- data/ext/async-profiler/src/jattach/jattach.c +437 -0
- data/ext/async-profiler/src/java/one/profiler/AsyncProfiler.java +160 -0
- data/ext/async-profiler/src/java/one/profiler/AsyncProfilerMXBean.java +43 -0
- data/ext/async-profiler/src/java/one/profiler/Counter.java +25 -0
- data/ext/async-profiler/src/java/one/profiler/Events.java +28 -0
- data/ext/async-profiler/src/javaApi.cpp +124 -0
- data/ext/async-profiler/src/lockTracer.cpp +161 -0
- data/ext/async-profiler/src/lockTracer.h +55 -0
- data/ext/async-profiler/src/mutex.cpp +33 -0
- data/ext/async-profiler/src/mutex.h +49 -0
- data/ext/async-profiler/src/os.h +45 -0
- data/ext/async-profiler/src/os_linux.cpp +129 -0
- data/ext/async-profiler/src/os_macos.cpp +115 -0
- data/ext/async-profiler/src/perfEvents.h +60 -0
- data/ext/async-profiler/src/perfEvents_linux.cpp +550 -0
- data/ext/async-profiler/src/perfEvents_macos.cpp +64 -0
- data/ext/async-profiler/src/profiler.cpp +952 -0
- data/ext/async-profiler/src/profiler.h +238 -0
- data/ext/async-profiler/src/spinLock.h +66 -0
- data/ext/async-profiler/src/stackFrame.h +57 -0
- data/ext/async-profiler/src/stackFrame_aarch64.cpp +75 -0
- data/ext/async-profiler/src/stackFrame_arm.cpp +58 -0
- data/ext/async-profiler/src/stackFrame_i386.cpp +82 -0
- data/ext/async-profiler/src/stackFrame_x64.cpp +113 -0
- data/ext/async-profiler/src/symbols.h +37 -0
- data/ext/async-profiler/src/symbols_linux.cpp +354 -0
- data/ext/async-profiler/src/symbols_macos.cpp +156 -0
- data/ext/async-profiler/src/vmEntry.cpp +173 -0
- data/ext/async-profiler/src/vmEntry.h +105 -0
- data/ext/async-profiler/src/vmStructs.cpp +104 -0
- data/ext/async-profiler/src/vmStructs.h +112 -0
- data/ext/async-profiler/src/wallClock.cpp +96 -0
- data/ext/async-profiler/src/wallClock.h +56 -0
- data/ext/async-profiler/test/AllocatingTarget.java +26 -0
- data/ext/async-profiler/test/LoadLibraryTest.java +21 -0
- data/ext/async-profiler/test/Target.java +31 -0
- data/ext/async-profiler/test/ThreadsTarget.java +35 -0
- data/ext/async-profiler/test/alloc-smoke-test.sh +36 -0
- data/ext/async-profiler/test/load-library-test.sh +35 -0
- data/ext/async-profiler/test/smoke-test.sh +37 -0
- data/ext/async-profiler/test/thread-smoke-test.sh +32 -0
- data/jruby-async-profiler.gemspec +32 -0
- data/lib/jruby/async/profiler.rb +10 -0
- data/lib/jruby/async/profiler/version.rb +7 -0
- metadata +155 -0
@@ -0,0 +1,118 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright 2018 Andrei Pangin
|
3
|
+
*
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
* you may not use this file except in compliance with the License.
|
6
|
+
* You may obtain a copy of the License at
|
7
|
+
*
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
*
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
* See the License for the specific language governing permissions and
|
14
|
+
* limitations under the License.
|
15
|
+
*/
|
16
|
+
|
17
|
+
#ifndef _FLAMEGRAPH_H
|
18
|
+
#define _FLAMEGRAPH_H
|
19
|
+
|
20
|
+
#include <map>
|
21
|
+
#include <string>
|
22
|
+
#include <iostream>
|
23
|
+
#include "arch.h"
|
24
|
+
#include "arguments.h"
|
25
|
+
|
26
|
+
|
27
|
+
class Trie {
|
28
|
+
public:
|
29
|
+
std::map<std::string, Trie> _children;
|
30
|
+
u64 _total;
|
31
|
+
u64 _self;
|
32
|
+
|
33
|
+
Trie() : _children(), _total(0), _self(0) {
|
34
|
+
}
|
35
|
+
|
36
|
+
Trie* addChild(const std::string& key, u64 value) {
|
37
|
+
_total += value;
|
38
|
+
return &_children[key];
|
39
|
+
}
|
40
|
+
|
41
|
+
void addLeaf(u64 value) {
|
42
|
+
_total += value;
|
43
|
+
_self += value;
|
44
|
+
}
|
45
|
+
|
46
|
+
int depth(u64 cutoff) const {
|
47
|
+
if (_total < cutoff) {
|
48
|
+
return 0;
|
49
|
+
}
|
50
|
+
|
51
|
+
int max_depth = 0;
|
52
|
+
for (std::map<std::string, Trie>::const_iterator it = _children.begin(); it != _children.end(); ++it) {
|
53
|
+
int d = it->second.depth(cutoff);
|
54
|
+
if (d > max_depth) max_depth = d;
|
55
|
+
}
|
56
|
+
return max_depth + 1;
|
57
|
+
}
|
58
|
+
};
|
59
|
+
|
60
|
+
class Node {
|
61
|
+
public:
|
62
|
+
std::string _name;
|
63
|
+
const Trie* _trie;
|
64
|
+
|
65
|
+
Node(std::string name, const Trie& trie) : _name(name), _trie(&trie) {
|
66
|
+
}
|
67
|
+
|
68
|
+
bool operator<(const Node& other) const {
|
69
|
+
return _trie->_total > other._trie->_total;
|
70
|
+
}
|
71
|
+
};
|
72
|
+
|
73
|
+
|
74
|
+
class Palette;
|
75
|
+
|
76
|
+
|
77
|
+
class FlameGraph {
|
78
|
+
private:
|
79
|
+
Trie _root;
|
80
|
+
char _buf[4096];
|
81
|
+
|
82
|
+
const char* _title;
|
83
|
+
Counter _counter;
|
84
|
+
int _imagewidth;
|
85
|
+
int _imageheight;
|
86
|
+
int _frameheight;
|
87
|
+
double _minwidth;
|
88
|
+
double _scale;
|
89
|
+
double _pct;
|
90
|
+
bool _reverse;
|
91
|
+
|
92
|
+
void printHeader(std::ostream& out);
|
93
|
+
void printFooter(std::ostream& out);
|
94
|
+
double printFrame(std::ostream& out, const std::string& name, const Trie& f, double x, double y);
|
95
|
+
void printTreeHeader(std::ostream& out);
|
96
|
+
void printTreeFooter(std::ostream& out);
|
97
|
+
bool printTreeFrame(std::ostream& out, const Trie& f, int depth);
|
98
|
+
const Palette& selectFramePalette(std::string& name);
|
99
|
+
|
100
|
+
public:
|
101
|
+
FlameGraph(const char* title, Counter counter, int width, int height, double minwidth, bool reverse) :
|
102
|
+
_root(),
|
103
|
+
_title(title),
|
104
|
+
_counter(counter),
|
105
|
+
_imagewidth(width),
|
106
|
+
_frameheight(height),
|
107
|
+
_minwidth(minwidth),
|
108
|
+
_reverse(reverse) {
|
109
|
+
}
|
110
|
+
|
111
|
+
Trie* root() {
|
112
|
+
return &_root;
|
113
|
+
}
|
114
|
+
|
115
|
+
void dump(std::ostream& out, bool tree);
|
116
|
+
};
|
117
|
+
|
118
|
+
#endif // _FLAMEGRAPH_H
|
@@ -0,0 +1,727 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright 2018 Andrei Pangin
|
3
|
+
*
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
* you may not use this file except in compliance with the License.
|
6
|
+
* You may obtain a copy of the License at
|
7
|
+
*
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
*
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
* See the License for the specific language governing permissions and
|
14
|
+
* limitations under the License.
|
15
|
+
*/
|
16
|
+
|
17
|
+
#include <map>
|
18
|
+
#include <string>
|
19
|
+
#include <arpa/inet.h>
|
20
|
+
#include <cxxabi.h>
|
21
|
+
#include <fcntl.h>
|
22
|
+
#include <stdint.h>
|
23
|
+
#include <stdlib.h>
|
24
|
+
#include <string.h>
|
25
|
+
#include <sys/types.h>
|
26
|
+
#include <unistd.h>
|
27
|
+
#include "flightRecorder.h"
|
28
|
+
#include "os.h"
|
29
|
+
#include "profiler.h"
|
30
|
+
#include "vmStructs.h"
|
31
|
+
|
32
|
+
|
33
|
+
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
34
|
+
|
35
|
+
const int RECORDING_BUFFER_SIZE = 65536;
|
36
|
+
const int RECORDING_LIMIT = RECORDING_BUFFER_SIZE - 4096;
|
37
|
+
|
38
|
+
|
39
|
+
enum DataType {
|
40
|
+
T_BOOLEAN,
|
41
|
+
T_BYTE,
|
42
|
+
T_U1,
|
43
|
+
T_SHORT,
|
44
|
+
T_U2,
|
45
|
+
T_INTEGER,
|
46
|
+
T_U4,
|
47
|
+
T_LONG,
|
48
|
+
T_U8,
|
49
|
+
T_FLOAT,
|
50
|
+
T_DOUBLE,
|
51
|
+
T_UTF8,
|
52
|
+
T_STRING,
|
53
|
+
T_ARRAY,
|
54
|
+
T_STRUCT,
|
55
|
+
T_STRUCTARRAY,
|
56
|
+
};
|
57
|
+
|
58
|
+
enum EventTypeId {
|
59
|
+
EVENT_METADATA = 0,
|
60
|
+
EVENT_CHECKPOINT = 1,
|
61
|
+
EVENT_RECORDING = 10,
|
62
|
+
EVENT_RECORDING_SETTINGS = 11,
|
63
|
+
EVENT_EXECUTION_SAMPLE = 20,
|
64
|
+
};
|
65
|
+
|
66
|
+
enum ContentTypeId {
|
67
|
+
CONTENT_NONE = 0,
|
68
|
+
CONTENT_MEMORY = 1,
|
69
|
+
CONTENT_EPOCHMILLIS = 2,
|
70
|
+
CONTENT_MILLIS = 3,
|
71
|
+
CONTENT_NANOS = 4,
|
72
|
+
CONTENT_THREAD = 7,
|
73
|
+
CONTENT_STACKTRACE = 9,
|
74
|
+
CONTENT_CLASS = 10,
|
75
|
+
CONTENT_METHOD = 32,
|
76
|
+
CONTENT_SYMBOL = 33,
|
77
|
+
CONTENT_STATE = 34,
|
78
|
+
CONTENT_FRAME_TYPE = 47,
|
79
|
+
};
|
80
|
+
|
81
|
+
enum FrameTypeId {
|
82
|
+
FRAME_INTERPRETED = 1,
|
83
|
+
FRAME_JIT_COMPILED = 2,
|
84
|
+
FRAME_INLINED = 3,
|
85
|
+
FRAME_NATIVE = 4,
|
86
|
+
FRAME_CPP = 5,
|
87
|
+
FRAME_KERNEL = 6,
|
88
|
+
FRAME_TOTAL_COUNT = 6
|
89
|
+
};
|
90
|
+
|
91
|
+
enum ThreadStateId {
|
92
|
+
STATE_RUNNABLE = 1,
|
93
|
+
STATE_TOTAL_COUNT = 1
|
94
|
+
};
|
95
|
+
|
96
|
+
|
97
|
+
struct DataStructure {
|
98
|
+
const char* id;
|
99
|
+
const char* name;
|
100
|
+
DataType data_type;
|
101
|
+
ContentTypeId content_type;
|
102
|
+
int data_struct_index;
|
103
|
+
};
|
104
|
+
|
105
|
+
struct EventType {
|
106
|
+
EventTypeId id;
|
107
|
+
const char* name;
|
108
|
+
const char* description;
|
109
|
+
const char* path;
|
110
|
+
bool has_start_time;
|
111
|
+
bool has_thread;
|
112
|
+
bool can_have_stacktrace;
|
113
|
+
bool is_requestable;
|
114
|
+
int data_structure;
|
115
|
+
};
|
116
|
+
|
117
|
+
struct ContentType {
|
118
|
+
ContentTypeId id;
|
119
|
+
const char* name;
|
120
|
+
const char* description;
|
121
|
+
DataType data_type;
|
122
|
+
int data_structure;
|
123
|
+
};
|
124
|
+
|
125
|
+
|
126
|
+
const DataStructure
|
127
|
+
ds_recording[] = {
|
128
|
+
{"id", "Id", T_LONG},
|
129
|
+
{"name", "Name", T_STRING},
|
130
|
+
{"destination", "Destination", T_STRING},
|
131
|
+
{"startTime", "Start Time", T_LONG, CONTENT_EPOCHMILLIS},
|
132
|
+
{"duration", "Recording Duration", T_LONG, CONTENT_MILLIS},
|
133
|
+
{"maxSize", "Max Size", T_LONG, CONTENT_MEMORY},
|
134
|
+
{"maxAge", "Max Age", T_LONG, CONTENT_MILLIS},
|
135
|
+
},
|
136
|
+
ds_recording_settings[] = {
|
137
|
+
{"id", "Id", T_INTEGER},
|
138
|
+
{"name", "Name", T_STRING},
|
139
|
+
{"path", "Event Path", T_STRING},
|
140
|
+
{"enabled", "Enabled", T_BOOLEAN},
|
141
|
+
{"stacktrace", "Stack Trace", T_BOOLEAN},
|
142
|
+
{"period", "Period", T_LONG, CONTENT_MILLIS},
|
143
|
+
{"threshold", "Threshold", T_LONG, CONTENT_NANOS},
|
144
|
+
};
|
145
|
+
|
146
|
+
const EventType et_recording[] = {
|
147
|
+
{EVENT_RECORDING, "Flight Recording", "", "recordings/recording", false, false, false, false, 0},
|
148
|
+
{EVENT_RECORDING_SETTINGS, "Recording Setting", "", "recordings/recording_setting", false, false, false, true, 1},
|
149
|
+
};
|
150
|
+
|
151
|
+
const DataStructure
|
152
|
+
ds_utf8[] = {
|
153
|
+
{"utf8", "UTF8 data", T_UTF8},
|
154
|
+
},
|
155
|
+
ds_thread[] = {
|
156
|
+
{"name", "Thread name", T_UTF8},
|
157
|
+
},
|
158
|
+
ds_frame_type[] = {
|
159
|
+
{"desc", "Description", T_UTF8},
|
160
|
+
},
|
161
|
+
ds_state[] = {
|
162
|
+
{"name", "Name", T_UTF8},
|
163
|
+
},
|
164
|
+
ds_class[] = {
|
165
|
+
{"loaderClass", "ClassLoader", T_U8, CONTENT_CLASS},
|
166
|
+
{"name", "Name", T_U8, CONTENT_SYMBOL},
|
167
|
+
{"modifiers", "Access modifiers", T_SHORT},
|
168
|
+
},
|
169
|
+
ds_method[] = {
|
170
|
+
{"class", "Class", T_U8, CONTENT_CLASS},
|
171
|
+
{"name", "Name", T_U8, CONTENT_SYMBOL},
|
172
|
+
{"signature", "Signature", T_U8, CONTENT_SYMBOL},
|
173
|
+
{"modifiers", "Access modifiers", T_SHORT},
|
174
|
+
{"hidden", "Hidden", T_BOOLEAN},
|
175
|
+
},
|
176
|
+
ds_frame[] = {
|
177
|
+
{"method", "Java Method", T_U8, CONTENT_METHOD},
|
178
|
+
{"bci", "Byte code index", T_INTEGER},
|
179
|
+
{"type", "Frame type", T_U1, CONTENT_FRAME_TYPE},
|
180
|
+
},
|
181
|
+
ds_stacktrace[] = {
|
182
|
+
{"truncated", "Truncated", T_BOOLEAN},
|
183
|
+
{"frames", "Stack frames", T_STRUCTARRAY, CONTENT_NONE, /* ds_frame */ 6},
|
184
|
+
},
|
185
|
+
ds_method_sample[] = {
|
186
|
+
{"sampledThread", "Thread", T_U4, CONTENT_THREAD},
|
187
|
+
{"stackTrace", "Stack Trace", T_U8, CONTENT_STACKTRACE},
|
188
|
+
{"state", "Thread State", T_U2, CONTENT_STATE},
|
189
|
+
};
|
190
|
+
|
191
|
+
const EventType et_profile[] = {
|
192
|
+
{EVENT_EXECUTION_SAMPLE, "Method Profiling Sample", "Snapshot of a threads state", "vm/prof/execution_sample", false, false, false, true, /* ds_method_sample */ 8},
|
193
|
+
};
|
194
|
+
|
195
|
+
const ContentType ct_profile[] = {
|
196
|
+
{CONTENT_SYMBOL, "UTFConstant", "UTF constant", T_U8, 0},
|
197
|
+
{CONTENT_THREAD, "Thread", "Thread", T_U4, 1},
|
198
|
+
{CONTENT_FRAME_TYPE, "FrameType", "Frame type", T_U1, 2},
|
199
|
+
{CONTENT_STATE, "ThreadState", "Java Thread State", T_U2, 3},
|
200
|
+
{CONTENT_CLASS, "Class", "Java class", T_U8, 4},
|
201
|
+
{CONTENT_METHOD, "Method", "Java method", T_U8, 5},
|
202
|
+
{CONTENT_STACKTRACE, "StackTrace", "Stacktrace", T_U8, 7},
|
203
|
+
};
|
204
|
+
|
205
|
+
|
206
|
+
class MethodInfo {
|
207
|
+
public:
|
208
|
+
MethodInfo() : _key(0) {
|
209
|
+
}
|
210
|
+
|
211
|
+
int _key;
|
212
|
+
int _class;
|
213
|
+
int _name;
|
214
|
+
int _sig;
|
215
|
+
short _modifiers;
|
216
|
+
FrameTypeId _type;
|
217
|
+
};
|
218
|
+
|
219
|
+
|
220
|
+
class Buffer {
|
221
|
+
private:
|
222
|
+
int _offset;
|
223
|
+
char _data[RECORDING_BUFFER_SIZE - sizeof(int)];
|
224
|
+
|
225
|
+
public:
|
226
|
+
Buffer() : _offset(0) {
|
227
|
+
}
|
228
|
+
|
229
|
+
const char* data() const {
|
230
|
+
return _data;
|
231
|
+
}
|
232
|
+
|
233
|
+
int offset() const {
|
234
|
+
return _offset;
|
235
|
+
}
|
236
|
+
|
237
|
+
void reset() {
|
238
|
+
_offset = 0;
|
239
|
+
}
|
240
|
+
|
241
|
+
void put(const char* v, int len) {
|
242
|
+
memcpy(_data + _offset, v, len);
|
243
|
+
_offset += len;
|
244
|
+
}
|
245
|
+
|
246
|
+
void put8(char v) {
|
247
|
+
_data[_offset++] = v;
|
248
|
+
}
|
249
|
+
|
250
|
+
void put16(short v) {
|
251
|
+
*(short*)(_data + _offset) = htons(v);
|
252
|
+
_offset += 2;
|
253
|
+
}
|
254
|
+
|
255
|
+
void put32(int v) {
|
256
|
+
*(int*)(_data + _offset) = htonl(v);
|
257
|
+
_offset += 4;
|
258
|
+
}
|
259
|
+
|
260
|
+
void put64(u64 v) {
|
261
|
+
*(u64*)(_data + _offset) = OS::hton64(v);
|
262
|
+
_offset += 8;
|
263
|
+
}
|
264
|
+
|
265
|
+
void put32(int offset, int v) {
|
266
|
+
*(int*)(_data + offset) = htonl(v);
|
267
|
+
}
|
268
|
+
|
269
|
+
void putUtf8(const char* v) {
|
270
|
+
putUtf8(v, strlen(v));
|
271
|
+
}
|
272
|
+
|
273
|
+
void putUtf8(const char* v, int len) {
|
274
|
+
put16((short)len);
|
275
|
+
put(v, len);
|
276
|
+
}
|
277
|
+
|
278
|
+
void putUtf16(const char* v) {
|
279
|
+
putUtf16(v, strlen(v));
|
280
|
+
}
|
281
|
+
|
282
|
+
void putUtf16(const char* v, int len) {
|
283
|
+
put32(len);
|
284
|
+
for (int i = 0; i < len; i++) {
|
285
|
+
put16(v[i]);
|
286
|
+
}
|
287
|
+
}
|
288
|
+
};
|
289
|
+
|
290
|
+
|
291
|
+
class Recording {
|
292
|
+
private:
|
293
|
+
Buffer _buf[CONCURRENCY_LEVEL];
|
294
|
+
int _fd;
|
295
|
+
std::map<std::string, int> _symbol_map;
|
296
|
+
std::map<std::string, int> _class_map;
|
297
|
+
std::map<jmethodID, MethodInfo> _method_map;
|
298
|
+
u64 _start_time;
|
299
|
+
u64 _start_nanos;
|
300
|
+
u64 _stop_time;
|
301
|
+
u64 _stop_nanos;
|
302
|
+
|
303
|
+
public:
|
304
|
+
Recording(int fd) : _fd(fd), _symbol_map(), _class_map(), _method_map() {
|
305
|
+
_start_time = OS::millis();
|
306
|
+
_start_nanos = OS::nanotime();
|
307
|
+
|
308
|
+
writeHeader(_buf);
|
309
|
+
flush(_buf);
|
310
|
+
}
|
311
|
+
|
312
|
+
~Recording() {
|
313
|
+
_stop_nanos = OS::nanotime();
|
314
|
+
_stop_time = OS::millis();
|
315
|
+
|
316
|
+
for (int i = 0; i < CONCURRENCY_LEVEL; i++) {
|
317
|
+
flush(&_buf[i]);
|
318
|
+
}
|
319
|
+
|
320
|
+
writeRecordingInfo(_buf);
|
321
|
+
flush(_buf);
|
322
|
+
|
323
|
+
off_t checkpoint_offset = lseek(_fd, 0, SEEK_CUR);
|
324
|
+
writeCheckpoint(_buf);
|
325
|
+
flush(_buf);
|
326
|
+
|
327
|
+
off_t metadata_offset = lseek(_fd, 0, SEEK_CUR);
|
328
|
+
writeMetadata(_buf, checkpoint_offset);
|
329
|
+
flush(_buf);
|
330
|
+
|
331
|
+
// Patch checkpoint size field
|
332
|
+
int checkpoint_size = htonl((int)(metadata_offset - checkpoint_offset));
|
333
|
+
ssize_t result = pwrite(_fd, &checkpoint_size, sizeof(checkpoint_size), checkpoint_offset);
|
334
|
+
(void)result;
|
335
|
+
|
336
|
+
// Patch metadata offset
|
337
|
+
u64 metadata_start = OS::hton64(metadata_offset);
|
338
|
+
result = pwrite(_fd, &metadata_start, sizeof(metadata_start), 8);
|
339
|
+
(void)result;
|
340
|
+
|
341
|
+
close(_fd);
|
342
|
+
}
|
343
|
+
|
344
|
+
int lookup(std::map<std::string, int>& map, std::string key) {
|
345
|
+
int* value = &map[key];
|
346
|
+
if (*value == 0) *value = map.size();
|
347
|
+
return *value;
|
348
|
+
}
|
349
|
+
|
350
|
+
FrameTypeId demangle(const char* name, std::string& result) {
|
351
|
+
if (name == NULL) {
|
352
|
+
result = "unknown";
|
353
|
+
return FRAME_NATIVE;
|
354
|
+
}
|
355
|
+
|
356
|
+
if (name[0] == '_' && name[1] == 'Z') {
|
357
|
+
int status;
|
358
|
+
char* demangled = abi::__cxa_demangle(name, NULL, NULL, &status);
|
359
|
+
if (demangled != NULL) {
|
360
|
+
char* p = strchr(demangled, '(');
|
361
|
+
if (p != NULL) *p = 0;
|
362
|
+
result = demangled;
|
363
|
+
free(demangled);
|
364
|
+
return FRAME_CPP;
|
365
|
+
}
|
366
|
+
}
|
367
|
+
|
368
|
+
int len = strlen(name);
|
369
|
+
if (len >= 4 && strcmp(name + len - 4, "_[k]") == 0) {
|
370
|
+
result = std::string(name, len - 4);
|
371
|
+
return FRAME_KERNEL;
|
372
|
+
}
|
373
|
+
|
374
|
+
result = name;
|
375
|
+
return FRAME_NATIVE;
|
376
|
+
}
|
377
|
+
|
378
|
+
MethodInfo* resolveMethod(ASGCT_CallFrame& frame) {
|
379
|
+
jmethodID method = frame.method_id;
|
380
|
+
MethodInfo* mi = &_method_map[method];
|
381
|
+
|
382
|
+
if (mi->_key == 0) {
|
383
|
+
mi->_key = _method_map.size();
|
384
|
+
|
385
|
+
if (frame.bci == BCI_NATIVE_FRAME || frame.bci == BCI_ERROR || method == NULL) {
|
386
|
+
std::string name;
|
387
|
+
FrameTypeId type = demangle((const char*)method, name);
|
388
|
+
mi->_class = lookup(_class_map, "");
|
389
|
+
mi->_name = lookup(_symbol_map, name);
|
390
|
+
mi->_sig = lookup(_symbol_map, type == FRAME_KERNEL ? "(Lk;)L;" : "()L;");
|
391
|
+
mi->_modifiers = 0x100;
|
392
|
+
mi->_type = type;
|
393
|
+
|
394
|
+
} else if (frame.bci == BCI_SYMBOL || frame.bci == BCI_SYMBOL_OUTSIDE_TLAB) {
|
395
|
+
VMSymbol* symbol = (VMSymbol*)((intptr_t)method & ~1);
|
396
|
+
mi->_class = lookup(_class_map, std::string(symbol->body(), symbol->length()));
|
397
|
+
mi->_name = lookup(_symbol_map, "new");
|
398
|
+
mi->_sig = lookup(_symbol_map, "()L;");
|
399
|
+
mi->_modifiers = 0x100;
|
400
|
+
mi->_type = FRAME_NATIVE;
|
401
|
+
|
402
|
+
} else {
|
403
|
+
jvmtiEnv* jvmti = VM::jvmti();
|
404
|
+
jclass method_class;
|
405
|
+
char* class_name = NULL;
|
406
|
+
char* method_name = NULL;
|
407
|
+
char* method_sig = NULL;
|
408
|
+
int modifiers = 0;
|
409
|
+
|
410
|
+
if (jvmti->GetMethodDeclaringClass(method, &method_class) == 0 &&
|
411
|
+
jvmti->GetClassSignature(method_class, &class_name, NULL) == 0 &&
|
412
|
+
jvmti->GetMethodName(method, &method_name, &method_sig, NULL) == 0) {
|
413
|
+
jvmti->GetMethodModifiers(method, &modifiers);
|
414
|
+
mi->_class = lookup(_class_map, std::string(class_name + 1, strlen(class_name) - 2));
|
415
|
+
mi->_name = lookup(_symbol_map, method_name);
|
416
|
+
mi->_sig = lookup(_symbol_map, method_sig);
|
417
|
+
} else {
|
418
|
+
mi->_class = lookup(_class_map, "");
|
419
|
+
mi->_name = lookup(_symbol_map, "jvmtiError");
|
420
|
+
mi->_sig = lookup(_symbol_map, "()L;");
|
421
|
+
}
|
422
|
+
|
423
|
+
mi->_modifiers = (short)modifiers;
|
424
|
+
mi->_type = FRAME_INTERPRETED;
|
425
|
+
|
426
|
+
jvmti->Deallocate((unsigned char*)method_sig);
|
427
|
+
jvmti->Deallocate((unsigned char*)method_name);
|
428
|
+
jvmti->Deallocate((unsigned char*)class_name);
|
429
|
+
}
|
430
|
+
}
|
431
|
+
|
432
|
+
return mi;
|
433
|
+
}
|
434
|
+
|
435
|
+
void flush(Buffer* buf) {
|
436
|
+
ssize_t result = write(_fd, buf->data(), buf->offset());
|
437
|
+
(void)result;
|
438
|
+
buf->reset();
|
439
|
+
}
|
440
|
+
|
441
|
+
void flushIfNeeded(Buffer* buf) {
|
442
|
+
if (buf->offset() >= RECORDING_LIMIT) {
|
443
|
+
flush(buf);
|
444
|
+
}
|
445
|
+
}
|
446
|
+
|
447
|
+
void writeHeader(Buffer* buf) {
|
448
|
+
buf->put("FLR\0", 4); // magic
|
449
|
+
buf->put16(0); // major
|
450
|
+
buf->put16(9); // minor
|
451
|
+
buf->put64(0); // metadata offset
|
452
|
+
}
|
453
|
+
|
454
|
+
void writeRecordingInfo(Buffer* buf) {
|
455
|
+
int recording_start = buf->offset();
|
456
|
+
buf->put32(0); // size
|
457
|
+
buf->put32(EVENT_RECORDING);
|
458
|
+
buf->put64(_stop_nanos);
|
459
|
+
buf->put64(1); // id
|
460
|
+
buf->putUtf16("Async-profiler");
|
461
|
+
buf->putUtf16("async-profiler.jfr");
|
462
|
+
buf->put64(_start_time);
|
463
|
+
buf->put64(_stop_time - _start_time);
|
464
|
+
buf->put64(0x7fffffff);
|
465
|
+
buf->put64(0x7fffffff);
|
466
|
+
buf->put32(recording_start, buf->offset() - recording_start);
|
467
|
+
|
468
|
+
int recording_settings_start = buf->offset();
|
469
|
+
buf->put32(0); // size
|
470
|
+
buf->put32(EVENT_RECORDING_SETTINGS);
|
471
|
+
buf->put64(_stop_nanos);
|
472
|
+
buf->put32(1); // id
|
473
|
+
buf->putUtf16("Method Profiling Sample");
|
474
|
+
buf->putUtf16("vm/prof/execution_sample");
|
475
|
+
buf->put8(1);
|
476
|
+
buf->put8(0);
|
477
|
+
buf->put64(1);
|
478
|
+
buf->put64(0);
|
479
|
+
buf->put32(recording_settings_start, buf->offset() - recording_settings_start);
|
480
|
+
}
|
481
|
+
|
482
|
+
void writeFixedTables(Buffer* buf) {
|
483
|
+
// Frame types
|
484
|
+
buf->put32(CONTENT_FRAME_TYPE);
|
485
|
+
buf->put32(FRAME_TOTAL_COUNT);
|
486
|
+
buf->put8(FRAME_INTERPRETED); buf->putUtf8("Interpreted");
|
487
|
+
buf->put8(FRAME_JIT_COMPILED); buf->putUtf8("JIT compiled");
|
488
|
+
buf->put8(FRAME_INLINED); buf->putUtf8("Inlined");
|
489
|
+
buf->put8(FRAME_NATIVE); buf->putUtf8("Native");
|
490
|
+
buf->put8(FRAME_CPP); buf->putUtf8("C++");
|
491
|
+
buf->put8(FRAME_KERNEL); buf->putUtf8("Kernel");
|
492
|
+
|
493
|
+
// Thread states
|
494
|
+
buf->put32(CONTENT_STATE);
|
495
|
+
buf->put32(STATE_TOTAL_COUNT);
|
496
|
+
buf->put16(STATE_RUNNABLE); buf->putUtf8("STATE_RUNNABLE");
|
497
|
+
}
|
498
|
+
|
499
|
+
void writeStackTraces(Buffer* buf) {
|
500
|
+
CallTraceSample* traces = Profiler::_instance._traces;
|
501
|
+
ASGCT_CallFrame* frame_buffer = Profiler::_instance._frame_buffer;
|
502
|
+
|
503
|
+
int count = 0;
|
504
|
+
for (int i = 0; i < MAX_CALLTRACES; i++) {
|
505
|
+
if (traces[i]._samples != 0) count++;
|
506
|
+
}
|
507
|
+
|
508
|
+
buf->put32(CONTENT_STACKTRACE);
|
509
|
+
buf->put32(count);
|
510
|
+
for (int i = 0; i < MAX_CALLTRACES; i++) {
|
511
|
+
CallTraceSample& trace = traces[i];
|
512
|
+
if (trace._samples != 0) {
|
513
|
+
buf->put64(i); // stack trace key
|
514
|
+
buf->put8(0); // truncated
|
515
|
+
buf->put32(trace._num_frames);
|
516
|
+
for (int j = 0; j < trace._num_frames; j++) {
|
517
|
+
MethodInfo* mi = resolveMethod(frame_buffer[trace._start_frame + j]);
|
518
|
+
buf->put64(mi->_key); // method key
|
519
|
+
buf->put32(0); // bci
|
520
|
+
buf->put8(mi->_type); // frame type
|
521
|
+
flushIfNeeded(buf);
|
522
|
+
}
|
523
|
+
flushIfNeeded(buf);
|
524
|
+
}
|
525
|
+
}
|
526
|
+
}
|
527
|
+
|
528
|
+
void writeMethods(Buffer* buf) {
|
529
|
+
buf->put32(CONTENT_METHOD);
|
530
|
+
buf->put32(_method_map.size());
|
531
|
+
for (std::map<jmethodID, MethodInfo>::const_iterator it = _method_map.begin(); it != _method_map.end(); ++it) {
|
532
|
+
const MethodInfo& mi = it->second;
|
533
|
+
buf->put64(mi._key);
|
534
|
+
buf->put64(mi._class);
|
535
|
+
buf->put64(mi._name);
|
536
|
+
buf->put64(mi._sig);
|
537
|
+
buf->put16(mi._modifiers);
|
538
|
+
buf->put8(0); // hidden
|
539
|
+
flushIfNeeded(buf);
|
540
|
+
}
|
541
|
+
}
|
542
|
+
|
543
|
+
void writeClasses(Buffer* buf) {
|
544
|
+
buf->put32(CONTENT_CLASS);
|
545
|
+
buf->put32(_class_map.size());
|
546
|
+
for (std::map<std::string, int>::const_iterator it = _class_map.begin(); it != _class_map.end(); ++it) {
|
547
|
+
buf->put64(it->second);
|
548
|
+
buf->put64(0); // loader class
|
549
|
+
buf->put64(lookup(_symbol_map, it->first));
|
550
|
+
buf->put16(0); // access flags
|
551
|
+
flushIfNeeded(buf);
|
552
|
+
}
|
553
|
+
}
|
554
|
+
|
555
|
+
void writeSymbols(Buffer* buf) {
|
556
|
+
buf->put32(CONTENT_SYMBOL);
|
557
|
+
buf->put32(_symbol_map.size());
|
558
|
+
for (std::map<std::string, int>::const_iterator it = _symbol_map.begin(); it != _symbol_map.end(); ++it) {
|
559
|
+
buf->put64(it->second);
|
560
|
+
buf->putUtf8(it->first.c_str());
|
561
|
+
flushIfNeeded(buf);
|
562
|
+
}
|
563
|
+
}
|
564
|
+
|
565
|
+
void writeThreads(Buffer* buf) {
|
566
|
+
buf->put32(CONTENT_THREAD);
|
567
|
+
buf->put32(0);
|
568
|
+
}
|
569
|
+
|
570
|
+
void writeCheckpoint(Buffer* buf) {
|
571
|
+
buf->put32(0); // size will be patched later
|
572
|
+
buf->put32(EVENT_CHECKPOINT);
|
573
|
+
buf->put64(_stop_nanos);
|
574
|
+
buf->put64(0); // previous checkpoint
|
575
|
+
|
576
|
+
writeFixedTables(buf);
|
577
|
+
writeStackTraces(buf);
|
578
|
+
writeMethods(buf);
|
579
|
+
writeClasses(buf);
|
580
|
+
writeSymbols(buf);
|
581
|
+
writeThreads(buf);
|
582
|
+
}
|
583
|
+
|
584
|
+
void writeDataStructure(Buffer* buf, int count, const DataStructure* ds) {
|
585
|
+
buf->put32(count);
|
586
|
+
for (int i = 0; i < count; i++, ds++) {
|
587
|
+
buf->putUtf8(ds->id);
|
588
|
+
buf->putUtf8(ds->name);
|
589
|
+
buf->putUtf8("");
|
590
|
+
buf->put8(0);
|
591
|
+
buf->put8(ds->data_type);
|
592
|
+
buf->put32(ds->content_type);
|
593
|
+
buf->put32(ds->data_struct_index);
|
594
|
+
buf->put32(0);
|
595
|
+
}
|
596
|
+
}
|
597
|
+
|
598
|
+
void writeEventTypes(Buffer* buf, int count, const EventType* et) {
|
599
|
+
buf->put32(count);
|
600
|
+
for (int i = 0; i < count; i++, et++) {
|
601
|
+
buf->put32(et->id);
|
602
|
+
buf->putUtf8(et->name);
|
603
|
+
buf->putUtf8(et->description);
|
604
|
+
buf->putUtf8(et->path);
|
605
|
+
buf->put8(et->has_start_time ? 1 : 0);
|
606
|
+
buf->put8(et->has_thread ? 1 : 0);
|
607
|
+
buf->put8(et->can_have_stacktrace ? 1 : 0);
|
608
|
+
buf->put8(et->is_requestable ? 1 : 0);
|
609
|
+
buf->put32(et->data_structure);
|
610
|
+
buf->put32(0);
|
611
|
+
}
|
612
|
+
}
|
613
|
+
|
614
|
+
void writeContentTypes(Buffer* buf, int count, const ContentType* ct) {
|
615
|
+
buf->put32(count);
|
616
|
+
for (int i = 0; i < count; i++, ct++) {
|
617
|
+
buf->put32(ct->id);
|
618
|
+
buf->putUtf8(ct->name);
|
619
|
+
buf->putUtf8(ct->description);
|
620
|
+
buf->put8(ct->data_type);
|
621
|
+
buf->put32(ct->data_structure);
|
622
|
+
}
|
623
|
+
}
|
624
|
+
|
625
|
+
void writeRecordingMetadata(Buffer* buf) {
|
626
|
+
buf->put32(1);
|
627
|
+
buf->putUtf8("JFR Metadata");
|
628
|
+
buf->putUtf8("Information about Recordings and Settings");
|
629
|
+
buf->putUtf8("http://www.oracle.com/hotspot/jfr-info/");
|
630
|
+
|
631
|
+
// Relations
|
632
|
+
buf->put32(0);
|
633
|
+
|
634
|
+
// Data structures
|
635
|
+
buf->put32(2);
|
636
|
+
writeDataStructure(buf, ARRAY_SIZE(ds_recording), ds_recording);
|
637
|
+
writeDataStructure(buf, ARRAY_SIZE(ds_recording_settings), ds_recording_settings);
|
638
|
+
|
639
|
+
// Event types and content types
|
640
|
+
writeEventTypes(buf, ARRAY_SIZE(et_recording), et_recording);
|
641
|
+
writeContentTypes(buf, 0, NULL);
|
642
|
+
}
|
643
|
+
|
644
|
+
void writeProfileMetadata(Buffer* buf) {
|
645
|
+
buf->put32(2);
|
646
|
+
buf->putUtf8("HotSpot JVM");
|
647
|
+
buf->putUtf8("Oracle Hotspot JVM");
|
648
|
+
buf->putUtf8("http://www.oracle.com/hotspot/jvm/");
|
649
|
+
|
650
|
+
// Relations
|
651
|
+
buf->put32(0);
|
652
|
+
|
653
|
+
// Data structures
|
654
|
+
buf->put32(9);
|
655
|
+
writeDataStructure(buf, ARRAY_SIZE(ds_utf8), ds_utf8);
|
656
|
+
writeDataStructure(buf, ARRAY_SIZE(ds_thread), ds_thread);
|
657
|
+
writeDataStructure(buf, ARRAY_SIZE(ds_frame_type), ds_frame_type);
|
658
|
+
writeDataStructure(buf, ARRAY_SIZE(ds_state), ds_state);
|
659
|
+
writeDataStructure(buf, ARRAY_SIZE(ds_class), ds_class);
|
660
|
+
writeDataStructure(buf, ARRAY_SIZE(ds_method), ds_method);
|
661
|
+
writeDataStructure(buf, ARRAY_SIZE(ds_frame), ds_frame);
|
662
|
+
writeDataStructure(buf, ARRAY_SIZE(ds_stacktrace), ds_stacktrace);
|
663
|
+
writeDataStructure(buf, ARRAY_SIZE(ds_method_sample), ds_method_sample);
|
664
|
+
|
665
|
+
// Event types and content types
|
666
|
+
writeEventTypes(buf, ARRAY_SIZE(et_profile), et_profile);
|
667
|
+
writeContentTypes(buf, ARRAY_SIZE(ct_profile), ct_profile);
|
668
|
+
}
|
669
|
+
|
670
|
+
void writeMetadata(Buffer* buf, off_t checkpoint_offset) {
|
671
|
+
int metadata_start = buf->offset();
|
672
|
+
buf->put32(0);
|
673
|
+
buf->put32(EVENT_METADATA);
|
674
|
+
|
675
|
+
// Producers
|
676
|
+
buf->put32(2);
|
677
|
+
writeRecordingMetadata(buf);
|
678
|
+
writeProfileMetadata(buf);
|
679
|
+
|
680
|
+
buf->put64(_start_time);
|
681
|
+
buf->put64(_stop_time);
|
682
|
+
buf->put64(_start_nanos);
|
683
|
+
buf->put64(1000000000); // ticks per second
|
684
|
+
buf->put64(checkpoint_offset);
|
685
|
+
|
686
|
+
buf->put32(metadata_start, buf->offset() - metadata_start);
|
687
|
+
}
|
688
|
+
|
689
|
+
void recordExecutionSample(int lock_index, int tid, int call_trace_id) {
|
690
|
+
Buffer* buf = &_buf[lock_index];
|
691
|
+
buf->put32(30);
|
692
|
+
buf->put32(EVENT_EXECUTION_SAMPLE);
|
693
|
+
buf->put64(OS::nanotime());
|
694
|
+
buf->put32(tid);
|
695
|
+
buf->put64(call_trace_id);
|
696
|
+
buf->put16(STATE_RUNNABLE);
|
697
|
+
flushIfNeeded(buf);
|
698
|
+
}
|
699
|
+
};
|
700
|
+
|
701
|
+
|
702
|
+
Error FlightRecorder::start(const char* file) {
|
703
|
+
if (file == NULL || file[0] == 0) {
|
704
|
+
return Error("Flight Recorder output file is not specified");
|
705
|
+
}
|
706
|
+
|
707
|
+
int fd = open(file, O_CREAT | O_WRONLY | O_TRUNC, 0644);
|
708
|
+
if (fd == -1) {
|
709
|
+
return Error("Cannot open Flight Recorder output file");
|
710
|
+
}
|
711
|
+
|
712
|
+
_rec = new Recording(fd);
|
713
|
+
return Error::OK;
|
714
|
+
}
|
715
|
+
|
716
|
+
void FlightRecorder::stop() {
|
717
|
+
if (_rec != NULL) {
|
718
|
+
delete _rec;
|
719
|
+
_rec = NULL;
|
720
|
+
}
|
721
|
+
}
|
722
|
+
|
723
|
+
void FlightRecorder::recordExecutionSample(int lock_index, int tid, int call_trace_id) {
|
724
|
+
if (_rec != NULL && call_trace_id != 0) {
|
725
|
+
_rec->recordExecutionSample(lock_index, tid, call_trace_id);
|
726
|
+
}
|
727
|
+
}
|