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,160 @@
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
+ package one.profiler;
18
+
19
+ /**
20
+ * Java API for in-process profiling. Serves as a wrapper around
21
+ * async-profiler native library. This class is a singleton.
22
+ * The first call to {@link #getInstance()} initiates loading of
23
+ * libasyncProfiler.so.
24
+ */
25
+ public class AsyncProfiler implements AsyncProfilerMXBean {
26
+ private static AsyncProfiler instance;
27
+
28
+ private final String version;
29
+
30
+ private AsyncProfiler() {
31
+ this.version = version0();
32
+ }
33
+
34
+ public static AsyncProfiler getInstance() {
35
+ return getInstance(null);
36
+ }
37
+
38
+ public static synchronized AsyncProfiler getInstance(String libPath) {
39
+ if (instance != null) {
40
+ return instance;
41
+ }
42
+
43
+ if (libPath == null) {
44
+ System.loadLibrary("asyncProfiler");
45
+ } else {
46
+ System.load(libPath);
47
+ }
48
+
49
+ instance = new AsyncProfiler();
50
+ return instance;
51
+ }
52
+
53
+ /**
54
+ * Start profiling
55
+ *
56
+ * @param event Profiling event, see {@link Events}
57
+ * @param interval Sampling interval, e.g. nanoseconds for Events.CPU
58
+ * @throws IllegalStateException If profiler is already running
59
+ */
60
+ @Override
61
+ public void start(String event, long interval) throws IllegalStateException {
62
+ start0(event, interval, true);
63
+ }
64
+
65
+ /**
66
+ * Start or resume profiling without resetting collected data.
67
+ * Note that event and interval may change since the previous profiling session.
68
+ *
69
+ * @param event Profiling event, see {@link Events}
70
+ * @param interval Sampling interval, e.g. nanoseconds for Events.CPU
71
+ * @throws IllegalStateException If profiler is already running
72
+ */
73
+ @Override
74
+ public void resume(String event, long interval) throws IllegalStateException {
75
+ start0(event, interval, false);
76
+ }
77
+
78
+ /**
79
+ * Stop profiling (without dumping results)
80
+ *
81
+ * @throws IllegalStateException If profiler is not running
82
+ */
83
+ @Override
84
+ public void stop() throws IllegalStateException {
85
+ stop0();
86
+ }
87
+
88
+ /**
89
+ * Get the number of samples collected during the profiling session
90
+ *
91
+ * @return Number of samples
92
+ */
93
+ @Override
94
+ public native long getSamples();
95
+
96
+ /**
97
+ * Get profiler agent version, e.g. "1.0"
98
+ *
99
+ * @return Version string
100
+ */
101
+ @Override
102
+ public String getVersion() {
103
+ return version;
104
+ }
105
+
106
+ /**
107
+ * Execute an agent-compatible profiling command -
108
+ * the comma-separated list of arguments described in arguments.cpp
109
+ *
110
+ * @param command Profiling command
111
+ * @return The command result
112
+ * @throws IllegalArgumentException If failed to parse the command
113
+ * @throws java.io.IOException If failed to create output file
114
+ */
115
+ @Override
116
+ public String execute(String command) throws IllegalArgumentException, java.io.IOException {
117
+ return execute0(command);
118
+ }
119
+
120
+ /**
121
+ * Dump profile in 'collapsed stacktraces' format
122
+ *
123
+ * @param counter Which counter to display in the output
124
+ * @return Textual representation of the profile
125
+ */
126
+ @Override
127
+ public String dumpCollapsed(Counter counter) {
128
+ return dumpCollapsed0(counter.ordinal());
129
+ }
130
+
131
+ /**
132
+ * Dump collected stack traces
133
+ *
134
+ * @param maxTraces Maximum number of stack traces to dump. 0 means no limit
135
+ * @return Textual representation of the profile
136
+ */
137
+ @Override
138
+ public String dumpTraces(int maxTraces) {
139
+ return dumpTraces0(maxTraces);
140
+ }
141
+
142
+ /**
143
+ * Dump flat profile, i.e. the histogram of the hottest methods
144
+ *
145
+ * @param maxMethods Maximum number of methods to dump. 0 means no limit
146
+ * @return Textual representation of the profile
147
+ */
148
+ @Override
149
+ public String dumpFlat(int maxMethods) {
150
+ return dumpFlat0(maxMethods);
151
+ }
152
+
153
+ private native void start0(String event, long interval, boolean reset) throws IllegalStateException;
154
+ private native void stop0() throws IllegalStateException;
155
+ private native String execute0(String command) throws IllegalArgumentException, java.io.IOException;
156
+ private native String dumpCollapsed0(int counter);
157
+ private native String dumpTraces0(int maxTraces);
158
+ private native String dumpFlat0(int maxMethods);
159
+ private native String version0();
160
+ }
@@ -0,0 +1,43 @@
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
+ package one.profiler;
18
+
19
+ /**
20
+ * AsyncProfiler interface for JMX server.
21
+ * How to register AsyncProfiler MBean:
22
+ *
23
+ * <pre>{@code
24
+ * ManagementFactory.getPlatformMBeanServer().registerMBean(
25
+ * AsyncProfiler.getInstance(),
26
+ * new ObjectName("one.profiler:type=AsyncProfiler")
27
+ * );
28
+ * }</pre>
29
+ */
30
+ public interface AsyncProfilerMXBean {
31
+ void start(String event, long interval) throws IllegalStateException;
32
+ void resume(String event, long interval) throws IllegalStateException;
33
+ void stop() throws IllegalStateException;
34
+
35
+ long getSamples();
36
+ String getVersion();
37
+
38
+ String execute(String command) throws IllegalArgumentException, java.io.IOException;
39
+
40
+ String dumpCollapsed(Counter counter);
41
+ String dumpTraces(int maxTraces);
42
+ String dumpFlat(int maxMethods);
43
+ }
@@ -0,0 +1,25 @@
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
+ package one.profiler;
18
+
19
+ /**
20
+ * Which metrics to use when generating profile in collapsed stack traces format.
21
+ */
22
+ public enum Counter {
23
+ SAMPLES,
24
+ TOTAL
25
+ }
@@ -0,0 +1,28 @@
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
+ package one.profiler;
18
+
19
+ /**
20
+ * Predefined event names to use in {@link AsyncProfiler#start(String, long)}
21
+ */
22
+ public class Events {
23
+ public static final String CPU = "cpu";
24
+ public static final String ALLOC = "alloc";
25
+ public static final String LOCK = "lock";
26
+ public static final String WALL = "wall";
27
+ public static final String ITIMER = "itimer";
28
+ }
@@ -0,0 +1,124 @@
1
+ /*
2
+ * Copyright 2016 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 <fstream>
18
+ #include <sstream>
19
+ #include <errno.h>
20
+ #include <string.h>
21
+ #include "arguments.h"
22
+ #include "profiler.h"
23
+
24
+
25
+ static void throw_new(JNIEnv* env, const char* exception_class, const char* message) {
26
+ jclass cls = env->FindClass(exception_class);
27
+ if (cls != NULL) {
28
+ env->ThrowNew(cls, message);
29
+ }
30
+ }
31
+
32
+
33
+ extern "C" JNIEXPORT void JNICALL
34
+ Java_one_profiler_AsyncProfiler_start0(JNIEnv* env, jobject unused, jstring event, jlong interval, jboolean reset) {
35
+ Arguments args;
36
+ args._event = env->GetStringUTFChars(event, NULL);
37
+ args._interval = interval;
38
+ Error error = Profiler::_instance.start(args, reset);
39
+ env->ReleaseStringUTFChars(event, args._event);
40
+
41
+ if (error) {
42
+ throw_new(env, "java/lang/IllegalStateException", error.message());
43
+ }
44
+ }
45
+
46
+ extern "C" JNIEXPORT void JNICALL
47
+ Java_one_profiler_AsyncProfiler_stop0(JNIEnv* env, jobject unused) {
48
+ Error error = Profiler::_instance.stop();
49
+
50
+ if (error) {
51
+ throw_new(env, "java/lang/IllegalStateException", error.message());
52
+ }
53
+ }
54
+
55
+ extern "C" JNIEXPORT jlong JNICALL
56
+ Java_one_profiler_AsyncProfiler_getSamples(JNIEnv* env, jobject unused) {
57
+ return (jlong)Profiler::_instance.total_samples();
58
+ }
59
+
60
+ extern "C" JNIEXPORT jstring JNICALL
61
+ Java_one_profiler_AsyncProfiler_execute0(JNIEnv* env, jobject unused, jstring command) {
62
+ Arguments args;
63
+ const char* command_str = env->GetStringUTFChars(command, NULL);
64
+ Error error = args.parse(command_str);
65
+ env->ReleaseStringUTFChars(command, command_str);
66
+
67
+ if (error) {
68
+ throw_new(env, "java/lang/IllegalArgumentException", error.message());
69
+ return NULL;
70
+ }
71
+
72
+ if (args._file == NULL || args._output == OUTPUT_JFR) {
73
+ std::ostringstream out;
74
+ Profiler::_instance.runInternal(args, out);
75
+ return env->NewStringUTF(out.str().c_str());
76
+ } else {
77
+ std::ofstream out(args._file, std::ios::out | std::ios::trunc);
78
+ if (out.is_open()) {
79
+ Profiler::_instance.runInternal(args, out);
80
+ out.close();
81
+ return env->NewStringUTF("OK");
82
+ } else {
83
+ throw_new(env, "java/io/IOException", strerror(errno));
84
+ return NULL;
85
+ }
86
+ }
87
+ }
88
+
89
+ extern "C" JNIEXPORT jstring JNICALL
90
+ Java_one_profiler_AsyncProfiler_dumpCollapsed0(JNIEnv* env, jobject unused, jint counter) {
91
+ Arguments args;
92
+ args._counter = counter == COUNTER_SAMPLES ? COUNTER_SAMPLES : COUNTER_TOTAL;
93
+
94
+ std::ostringstream out;
95
+ Profiler::_instance.dumpCollapsed(out, args);
96
+ return env->NewStringUTF(out.str().c_str());
97
+ }
98
+
99
+ extern "C" JNIEXPORT jstring JNICALL
100
+ Java_one_profiler_AsyncProfiler_dumpTraces0(JNIEnv* env, jobject unused, jint max_traces) {
101
+ Arguments args;
102
+ args._dump_traces = max_traces ? max_traces : MAX_CALLTRACES;
103
+
104
+ std::ostringstream out;
105
+ Profiler::_instance.dumpSummary(out);
106
+ Profiler::_instance.dumpTraces(out, args);
107
+ return env->NewStringUTF(out.str().c_str());
108
+ }
109
+
110
+ extern "C" JNIEXPORT jstring JNICALL
111
+ Java_one_profiler_AsyncProfiler_dumpFlat0(JNIEnv* env, jobject unused, jint max_methods) {
112
+ Arguments args;
113
+ args._dump_flat = max_methods ? max_methods : MAX_CALLTRACES;
114
+
115
+ std::ostringstream out;
116
+ Profiler::_instance.dumpSummary(out);
117
+ Profiler::_instance.dumpFlat(out, args);
118
+ return env->NewStringUTF(out.str().c_str());
119
+ }
120
+
121
+ extern "C" JNIEXPORT jstring JNICALL
122
+ Java_one_profiler_AsyncProfiler_version0(JNIEnv* env, jobject unused) {
123
+ return env->NewStringUTF(PROFILER_VERSION);
124
+ }
@@ -0,0 +1,161 @@
1
+ /*
2
+ * Copyright 2017 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 <string.h>
18
+ #include "lockTracer.h"
19
+ #include "profiler.h"
20
+ #include "vmStructs.h"
21
+
22
+
23
+ jlong LockTracer::_start_time = 0;
24
+ jclass LockTracer::_LockSupport = NULL;
25
+ jmethodID LockTracer::_getBlocker = NULL;
26
+ UnsafeParkFunc LockTracer::_original_Unsafe_Park = NULL;
27
+ bool LockTracer::_supports_lock_names = false;
28
+
29
+ Error LockTracer::start(Arguments& args) {
30
+ // PermGen in JDK 7 makes difficult to get symbol name from jclass.
31
+ // Also some JVMs do not support VMStructs at all.
32
+ // Let's just record stack traces without lock names in these cases.
33
+ _supports_lock_names = VMStructs::available() && !VMStructs::hasPermGen();
34
+
35
+ // Enable Java Monitor events
36
+ jvmtiEnv* jvmti = VM::jvmti();
37
+ jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTER, NULL);
38
+ jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, NULL);
39
+ jvmti->GetTime(&_start_time);
40
+
41
+ if (_getBlocker == NULL) {
42
+ JNIEnv* env = VM::jni();
43
+ _LockSupport = (jclass)env->NewGlobalRef(env->FindClass("java/util/concurrent/locks/LockSupport"));
44
+ _getBlocker = env->GetStaticMethodID(_LockSupport, "getBlocker", "(Ljava/lang/Thread;)Ljava/lang/Object;");
45
+ }
46
+
47
+ if (_original_Unsafe_Park == NULL) {
48
+ NativeCodeCache* libjvm = Profiler::_instance.jvmLibrary();
49
+ _original_Unsafe_Park = (UnsafeParkFunc)libjvm->findSymbol("Unsafe_Park");
50
+ if (_original_Unsafe_Park == NULL) {
51
+ // In some macOS builds of JDK 11 Unsafe_Park appears to have a C++ decorated name
52
+ _original_Unsafe_Park = (UnsafeParkFunc)libjvm->findSymbol("_ZL11Unsafe_ParkP7JNIEnv_P8_jobjecthl");
53
+ }
54
+ }
55
+
56
+ // Intercent Unsafe.park() for tracing contended ReentrantLocks
57
+ if (_original_Unsafe_Park != NULL) {
58
+ bindUnsafePark(UnsafeParkTrap);
59
+ }
60
+
61
+ return Error::OK;
62
+ }
63
+
64
+ void LockTracer::stop() {
65
+ // Disable Java Monitor events
66
+ jvmtiEnv* jvmti = VM::jvmti();
67
+ jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTER, NULL);
68
+ jvmti->SetEventNotificationMode(JVMTI_DISABLE, JVMTI_EVENT_MONITOR_CONTENDED_ENTERED, NULL);
69
+
70
+ // Reset Unsafe.park() trap
71
+ if (_original_Unsafe_Park != NULL) {
72
+ bindUnsafePark(_original_Unsafe_Park);
73
+ }
74
+ }
75
+
76
+ void JNICALL LockTracer::MonitorContendedEnter(jvmtiEnv* jvmti, JNIEnv* env, jthread thread, jobject object) {
77
+ jlong enter_time;
78
+ jvmti->GetTime(&enter_time);
79
+ jvmti->SetTag(thread, enter_time);
80
+ }
81
+
82
+ void JNICALL LockTracer::MonitorContendedEntered(jvmtiEnv* jvmti, JNIEnv* env, jthread thread, jobject object) {
83
+ jlong enter_time, entered_time;
84
+ jvmti->GetTime(&entered_time);
85
+ jvmti->GetTag(thread, &enter_time);
86
+
87
+ // Time is meaningless if lock attempt has started before profiling
88
+ if (enter_time >= _start_time) {
89
+ recordContendedLock(env->GetObjectClass(object), entered_time - enter_time);
90
+ }
91
+ }
92
+
93
+ void JNICALL LockTracer::UnsafeParkTrap(JNIEnv* env, jobject instance, jboolean isAbsolute, jlong time) {
94
+ jvmtiEnv* jvmti = VM::jvmti();
95
+ jclass lock_class = getParkBlockerClass(jvmti, env);
96
+ jlong park_start_time, park_end_time;
97
+
98
+ if (lock_class != NULL) {
99
+ jvmti->GetTime(&park_start_time);
100
+ }
101
+
102
+ _original_Unsafe_Park(env, instance, isAbsolute, time);
103
+
104
+ if (lock_class != NULL) {
105
+ jvmti->GetTime(&park_end_time);
106
+ recordContendedLock(lock_class, park_end_time - park_start_time);
107
+ }
108
+ }
109
+
110
+ jclass LockTracer::getParkBlockerClass(jvmtiEnv* jvmti, JNIEnv* env) {
111
+ jthread thread;
112
+ if (jvmti->GetCurrentThread(&thread) != 0) {
113
+ return NULL;
114
+ }
115
+
116
+ // Call LockSupport.getBlocker(Thread.currentThread())
117
+ jobject park_blocker = env->CallStaticObjectMethod(_LockSupport, _getBlocker, thread);
118
+ if (park_blocker == NULL) {
119
+ return NULL;
120
+ }
121
+
122
+ jclass lock_class = env->GetObjectClass(park_blocker);
123
+ char* class_name;
124
+ if (jvmti->GetClassSignature(lock_class, &class_name, NULL) != 0) {
125
+ return NULL;
126
+ }
127
+
128
+ // Do not count synchronizers other than ReentrantLock, ReentrantReadWriteLock and Semaphore
129
+ if (strncmp(class_name, "Ljava/util/concurrent/locks/ReentrantLock", 41) != 0 &&
130
+ strncmp(class_name, "Ljava/util/concurrent/locks/ReentrantReadWriteLock", 50) != 0 &&
131
+ strncmp(class_name, "Ljava/util/concurrent/Semaphore", 31) != 0) {
132
+ lock_class = NULL;
133
+ }
134
+
135
+ jvmti->Deallocate((unsigned char*)class_name);
136
+ return lock_class;
137
+ }
138
+
139
+ void LockTracer::recordContendedLock(jclass lock_class, jlong time) {
140
+ if (_supports_lock_names) {
141
+ VMSymbol* lock_name = (*(java_lang_Class**)lock_class)->klass()->name();
142
+ Profiler::_instance.recordSample(NULL, time, BCI_SYMBOL, (jmethodID)lock_name);
143
+ } else {
144
+ Profiler::_instance.recordSample(NULL, time, BCI_SYMBOL, NULL);
145
+ }
146
+ }
147
+
148
+ void LockTracer::bindUnsafePark(UnsafeParkFunc entry) {
149
+ JNIEnv* env = VM::jni();
150
+
151
+ // Try JDK 9+ package first, then fallback to JDK 8 package
152
+ jclass unsafe = env->FindClass("jdk/internal/misc/Unsafe");
153
+ if (unsafe == NULL) unsafe = env->FindClass("sun/misc/Unsafe");
154
+
155
+ if (unsafe != NULL) {
156
+ const JNINativeMethod unsafe_park = {(char*)"park", (char*)"(ZJ)V", (void*)entry};
157
+ env->RegisterNatives(unsafe, &unsafe_park, 1);
158
+ }
159
+
160
+ env->ExceptionClear();
161
+ }