jruby-async-profiler 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ }