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,112 @@
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
+ #ifndef _VMSTRUCTS_H
18
+ #define _VMSTRUCTS_H
19
+
20
+ #include <jvmti.h>
21
+ #include <stdint.h>
22
+ #include "codeCache.h"
23
+
24
+
25
+ class VMStructs {
26
+ protected:
27
+ static int _klass_name_offset;
28
+ static int _symbol_length_offset;
29
+ static int _symbol_length_and_refcount_offset;
30
+ static int _symbol_body_offset;
31
+ static int _class_klass_offset;
32
+ static int _thread_osthread_offset;
33
+ static int _osthread_id_offset;
34
+ static bool _has_perm_gen;
35
+ static jfieldID _eetop;
36
+
37
+ const char* at(int offset) {
38
+ return (const char*)this + offset;
39
+ }
40
+
41
+ public:
42
+ static void init(NativeCodeCache* libjvm);
43
+
44
+ static bool available() {
45
+ return _klass_name_offset >= 0
46
+ && (_symbol_length_offset >= 0 || _symbol_length_and_refcount_offset >= 0)
47
+ && _symbol_body_offset >= 0
48
+ && _class_klass_offset >= 0;
49
+ }
50
+
51
+ static bool hasPermGen() {
52
+ return _has_perm_gen;
53
+ }
54
+ };
55
+
56
+
57
+ class VMSymbol : VMStructs {
58
+ public:
59
+ unsigned short length() {
60
+ if (_symbol_length_offset >= 0) {
61
+ return *(unsigned short*) at(_symbol_length_offset);
62
+ } else {
63
+ int length_and_refcount = *(unsigned int*) at(_symbol_length_and_refcount_offset);
64
+ return (length_and_refcount >> 16) & 0xffff;
65
+ }
66
+ }
67
+
68
+ const char* body() {
69
+ return at(_symbol_body_offset);
70
+ }
71
+ };
72
+
73
+ class VMKlass : VMStructs {
74
+ public:
75
+ static VMKlass* fromHandle(uintptr_t handle) {
76
+ if (_has_perm_gen) {
77
+ // On JDK 7 KlassHandle is a pointer to klassOop, hence one more indirection
78
+ return (VMKlass*)(*(uintptr_t**)handle + 2);
79
+ } else {
80
+ return (VMKlass*)handle;
81
+ }
82
+ }
83
+
84
+ VMSymbol* name() {
85
+ return *(VMSymbol**) at(_klass_name_offset);
86
+ }
87
+ };
88
+
89
+ class java_lang_Class : VMStructs {
90
+ public:
91
+ VMKlass* klass() {
92
+ return *(VMKlass**) at(_class_klass_offset);
93
+ }
94
+ };
95
+
96
+ class VMThread : VMStructs {
97
+ public:
98
+ static bool available() {
99
+ return _eetop != NULL;
100
+ }
101
+
102
+ static VMThread* fromJavaThread(JNIEnv* env, jthread thread) {
103
+ return (VMThread*)(uintptr_t)env->GetLongField(thread, _eetop);
104
+ }
105
+
106
+ int osThreadId() {
107
+ const char* osthread = *(const char**) at(_thread_osthread_offset);
108
+ return *(int*)(osthread + _osthread_id_offset);
109
+ }
110
+ };
111
+
112
+ #endif // _VMSTRUCTS_H
@@ -0,0 +1,96 @@
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 <poll.h>
18
+ #include <string.h>
19
+ #include <signal.h>
20
+ #include <sys/types.h>
21
+ #include <unistd.h>
22
+ #include "wallClock.h"
23
+ #include "os.h"
24
+ #include "profiler.h"
25
+
26
+
27
+ const int THREADS_PER_TICK = 8;
28
+
29
+ long WallClock::_interval;
30
+ bool WallClock::_sample_idle_threads;
31
+
32
+ void WallClock::signalHandler(int signo, siginfo_t* siginfo, void* ucontext) {
33
+ Profiler::_instance.recordSample(ucontext, _interval, 0, NULL);
34
+ }
35
+
36
+ Error WallClock::start(Arguments& args) {
37
+ if (args._interval < 0) {
38
+ return Error("interval must be positive");
39
+ }
40
+ _interval = args._interval ? args._interval : DEFAULT_INTERVAL;
41
+ _sample_idle_threads = strcmp(args._event, EVENT_WALL) == 0;
42
+
43
+ OS::installSignalHandler(SIGPROF, signalHandler);
44
+
45
+ if (pipe(_pipefd) != 0) {
46
+ return Error("Unable to create poll pipe");
47
+ }
48
+
49
+ if (pthread_create(&_thread, NULL, threadEntry, this) != 0) {
50
+ close(_pipefd[1]);
51
+ close(_pipefd[0]);
52
+ return Error("Unable to create timer thread");
53
+ }
54
+
55
+ return Error::OK;
56
+ }
57
+
58
+ void WallClock::stop() {
59
+ char val = 1;
60
+ ssize_t r = write(_pipefd[1], &val, sizeof(val));
61
+ (void)r;
62
+
63
+ close(_pipefd[1]);
64
+ pthread_join(_thread, NULL);
65
+ close(_pipefd[0]);
66
+ }
67
+
68
+ void WallClock::timerLoop() {
69
+ ThreadList* thread_list = NULL;
70
+
71
+ int self = OS::threadId();
72
+ bool sample_idle_threads = _sample_idle_threads;
73
+ struct pollfd fds = {_pipefd[0], POLLIN, 0};
74
+ int timeout = _interval > 1000000 ? (int)(_interval / 1000000) : 1;
75
+
76
+ while (poll(&fds, 1, timeout) == 0) {
77
+ if (thread_list == NULL) {
78
+ thread_list = OS::listThreads();
79
+ }
80
+
81
+ for (int count = 0; count < THREADS_PER_TICK; ) {
82
+ int thread_id = thread_list->next();
83
+ if (thread_id == -1) {
84
+ delete thread_list;
85
+ thread_list = NULL;
86
+ break;
87
+ }
88
+ if (thread_id != self && (sample_idle_threads || OS::isThreadRunning(thread_id))) {
89
+ OS::sendSignalToThread(thread_id, SIGPROF);
90
+ count++;
91
+ }
92
+ }
93
+ }
94
+
95
+ delete thread_list;
96
+ }
@@ -0,0 +1,56 @@
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 _WALLCLOCK_H
18
+ #define _WALLCLOCK_H
19
+
20
+ #include <jvmti.h>
21
+ #include <signal.h>
22
+ #include <pthread.h>
23
+ #include "engine.h"
24
+
25
+
26
+ class WallClock : public Engine {
27
+ private:
28
+ static long _interval;
29
+ static bool _sample_idle_threads;
30
+
31
+ int _pipefd[2];
32
+ pthread_t _thread;
33
+
34
+ void timerLoop();
35
+
36
+ static void* threadEntry(void* wall_clock) {
37
+ ((WallClock*)wall_clock)->timerLoop();
38
+ return NULL;
39
+ }
40
+
41
+ static void signalHandler(int signo, siginfo_t* siginfo, void* ucontext);
42
+
43
+ public:
44
+ const char* name() {
45
+ return _sample_idle_threads ? EVENT_WALL : EVENT_CPU;
46
+ }
47
+
48
+ const char* units() {
49
+ return "ns";
50
+ }
51
+
52
+ Error start(Arguments& args);
53
+ void stop();
54
+ };
55
+
56
+ #endif // _WALLCLOCK_H
@@ -0,0 +1,26 @@
1
+ import java.util.Random;
2
+
3
+ public class AllocatingTarget implements Runnable {
4
+ public static volatile Object sink;
5
+
6
+ public static void main(String[] args) {
7
+ new Thread(new AllocatingTarget(), "AllocThread-1").start();
8
+ new Thread(new AllocatingTarget(), "AllocThread-2").start();
9
+ }
10
+
11
+ @Override
12
+ public void run() {
13
+ Random random = new Random();
14
+ while (true) {
15
+ allocate(random);
16
+ }
17
+ }
18
+
19
+ private static void allocate(Random random) {
20
+ if (random.nextBoolean()) {
21
+ sink = new int[128 * 1000];
22
+ } else {
23
+ sink = new Integer[128 * 1000];
24
+ }
25
+ }
26
+ }
@@ -0,0 +1,21 @@
1
+ import java.lang.management.ClassLoadingMXBean;
2
+ import java.lang.management.ManagementFactory;
3
+
4
+ class LoadLibraryTest {
5
+
6
+ public static void main(String[] args) throws Exception {
7
+ for (int i = 0; i < 200; i++) {
8
+ Thread.sleep(10);
9
+ }
10
+
11
+ // Late load of libmanagement.so
12
+ ClassLoadingMXBean bean = ManagementFactory.getClassLoadingMXBean();
13
+
14
+ long n = 0;
15
+ while (n >= 0) {
16
+ n += bean.getLoadedClassCount();
17
+ n += bean.getTotalLoadedClassCount();
18
+ n += bean.getUnloadedClassCount();
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,31 @@
1
+ import java.io.File;
2
+
3
+ class Target {
4
+ private static volatile int value;
5
+
6
+ private static void method1() {
7
+ for (int i = 0; i < 1000000; ++i)
8
+ ++value;
9
+ }
10
+
11
+ private static void method2() {
12
+ for (int i = 0; i < 1000000; ++i)
13
+ ++value;
14
+ }
15
+
16
+ private static void method3() throws Exception {
17
+ for (int i = 0; i < 1000; ++i) {
18
+ for (String s : new File("/tmp").list()) {
19
+ value += s.hashCode();
20
+ }
21
+ }
22
+ }
23
+
24
+ public static void main(String[] args) throws Exception {
25
+ while (true) {
26
+ method1();
27
+ method2();
28
+ method3();
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,35 @@
1
+ import java.math.BigInteger;
2
+
3
+ public class ThreadsTarget {
4
+ public static void main(String[] args) {
5
+ new Thread(new Runnable() {
6
+ @Override
7
+ public void run() {
8
+ methodForThreadEarlyEnd();
9
+ }
10
+ }, "ThreadEarlyEnd").start();
11
+ new Thread(new Runnable() {
12
+ @Override
13
+ public void run() {
14
+ Thread.currentThread().setName("RenamedThread");
15
+ methodForRenamedThread();
16
+ }
17
+ }, "ThreadWillBeRenamed").start();
18
+ }
19
+
20
+ static void methodForThreadEarlyEnd() {
21
+ long now = System.currentTimeMillis();
22
+ BigInteger counter = BigInteger.ZERO;
23
+ while (System.currentTimeMillis() - now < 300) {
24
+ counter = counter.nextProbablePrime();
25
+ }
26
+ }
27
+
28
+ static void methodForRenamedThread() {
29
+ long now = System.currentTimeMillis();
30
+ BigInteger counter = BigInteger.ZERO;
31
+ while (System.currentTimeMillis() - now < 1000) {
32
+ counter = counter.nextProbablePrime();
33
+ }
34
+ }
35
+ }
@@ -0,0 +1,36 @@
1
+ #!/bin/bash
2
+
3
+ set -e # exit on any failure
4
+ set -x # print all executed lines
5
+
6
+ if [ -z "${JAVA_HOME}" ]; then
7
+ echo "JAVA_HOME is not set"
8
+ exit 1
9
+ fi
10
+
11
+ (
12
+ cd $(dirname $0)
13
+
14
+ if [ "AllocatingTarget.class" -ot "AllocatingTarget.java" ]; then
15
+ ${JAVA_HOME}/bin/javac AllocatingTarget.java
16
+ fi
17
+
18
+ ${JAVA_HOME}/bin/java AllocatingTarget &
19
+
20
+ FILENAME=/tmp/java.trace
21
+ JAVAPID=$!
22
+
23
+ sleep 1 # allow the Java runtime to initialize
24
+ ../profiler.sh -f $FILENAME -o collapsed -d 5 -e alloc -t $JAVAPID
25
+
26
+ kill $JAVAPID
27
+
28
+ function assert_string() {
29
+ if ! grep -q "$1" $FILENAME; then
30
+ exit 1
31
+ fi
32
+ }
33
+
34
+ assert_string "\[AllocThread-1 tid=[0-9]\+\];.*AllocatingTarget.allocate;.*java.lang.Integer\[\]"
35
+ assert_string "\[AllocThread-2 tid=[0-9]\+\];.*AllocatingTarget.allocate;.*int\[\]"
36
+ )
@@ -0,0 +1,35 @@
1
+ #!/bin/bash
2
+
3
+ set -e # exit on any failure
4
+ set -x # print all executed lines
5
+
6
+ if [ -z "${JAVA_HOME}" ]; then
7
+ echo "JAVA_HOME is not set"
8
+ exit 1
9
+ fi
10
+
11
+ (
12
+ cd $(dirname $0)
13
+
14
+ if [ "LoadLibraryTest.class" -ot "LoadLibraryTest.java" ]; then
15
+ ${JAVA_HOME}/bin/javac LoadLibraryTest.java
16
+ fi
17
+
18
+ ${JAVA_HOME}/bin/java LoadLibraryTest &
19
+
20
+ FILENAME=/tmp/java.trace
21
+ JAVAPID=$!
22
+
23
+ sleep 1 # allow the Java runtime to initialize
24
+ ../profiler.sh -f $FILENAME -o collapsed -d 5 -i 1ms $JAVAPID
25
+
26
+ kill $JAVAPID
27
+
28
+ function assert_string() {
29
+ if ! grep -q "$1" $FILENAME; then
30
+ exit 1
31
+ fi
32
+ }
33
+
34
+ assert_string "Java_sun_management"
35
+ )