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,155 @@
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 <unistd.h>
18
+ #include <sys/mman.h>
19
+ #include "allocTracer.h"
20
+ #include "os.h"
21
+ #include "profiler.h"
22
+ #include "stackFrame.h"
23
+ #include "vmStructs.h"
24
+
25
+
26
+ // JDK 7-9
27
+ Trap AllocTracer::_in_new_tlab("_ZN11AllocTracer33send_allocation_in_new_tlab_event");
28
+ Trap AllocTracer::_outside_tlab("_ZN11AllocTracer34send_allocation_outside_tlab_event");
29
+ // JDK 10+
30
+ Trap AllocTracer::_in_new_tlab2("_ZN11AllocTracer27send_allocation_in_new_tlab");
31
+ Trap AllocTracer::_outside_tlab2("_ZN11AllocTracer28send_allocation_outside_tlab");
32
+
33
+ u64 AllocTracer::_interval;
34
+ volatile u64 AllocTracer::_allocated_bytes;
35
+
36
+
37
+ // Resolve the address of the intercepted function
38
+ bool Trap::resolve(NativeCodeCache* libjvm) {
39
+ if (_entry != NULL) {
40
+ return true;
41
+ }
42
+
43
+ _entry = (instruction_t*)libjvm->findSymbolByPrefix(_func_name);
44
+ if (_entry != NULL) {
45
+ // Make the entry point writable, so we can rewrite instructions
46
+ long page_size = sysconf(_SC_PAGESIZE);
47
+ uintptr_t page_start = (uintptr_t)_entry & -page_size;
48
+ mprotect((void*)page_start, page_size, PROT_READ | PROT_WRITE | PROT_EXEC);
49
+ return true;
50
+ }
51
+
52
+ return false;
53
+ }
54
+
55
+ // Insert breakpoint at the very first instruction
56
+ void Trap::install() {
57
+ if (_entry != NULL) {
58
+ _saved_insn = *_entry;
59
+ *_entry = BREAKPOINT;
60
+ flushCache(_entry);
61
+ }
62
+ }
63
+
64
+ // Clear breakpoint - restore the original instruction
65
+ void Trap::uninstall() {
66
+ if (_entry != NULL) {
67
+ *_entry = _saved_insn;
68
+ flushCache(_entry);
69
+ }
70
+ }
71
+
72
+
73
+ // Called whenever our breakpoint trap is hit
74
+ void AllocTracer::signalHandler(int signo, siginfo_t* siginfo, void* ucontext) {
75
+ StackFrame frame(ucontext);
76
+
77
+ // PC points either to BREAKPOINT instruction or to the next one
78
+ if (frame.pc() - (uintptr_t)_in_new_tlab._entry <= sizeof(instruction_t)) {
79
+ // send_allocation_in_new_tlab_event(KlassHandle klass, size_t tlab_size, size_t alloc_size)
80
+ recordAllocation(ucontext, frame.arg0(), frame.arg1(), false);
81
+ } else if (frame.pc() - (uintptr_t)_outside_tlab._entry <= sizeof(instruction_t)) {
82
+ // send_allocation_outside_tlab_event(KlassHandle klass, size_t alloc_size);
83
+ recordAllocation(ucontext, frame.arg0(), frame.arg1(), true);
84
+ } else if (frame.pc() - (uintptr_t)_in_new_tlab2._entry <= sizeof(instruction_t)) {
85
+ // send_allocation_in_new_tlab(Klass* klass, HeapWord* obj, size_t tlab_size, size_t alloc_size, Thread* thread)
86
+ recordAllocation(ucontext, frame.arg0(), frame.arg2(), false);
87
+ } else if (frame.pc() - (uintptr_t)_outside_tlab2._entry <= sizeof(instruction_t)) {
88
+ // send_allocation_outside_tlab(Klass* klass, HeapWord* obj, size_t alloc_size, Thread* thread)
89
+ recordAllocation(ucontext, frame.arg0(), frame.arg2(), true);
90
+ } else {
91
+ // Not our trap; nothing to do
92
+ return;
93
+ }
94
+
95
+ // Leave the trapped function by simulating "ret" instruction
96
+ frame.ret();
97
+ }
98
+
99
+ void AllocTracer::recordAllocation(void* ucontext, uintptr_t rklass, uintptr_t rsize, bool outside_tlab) {
100
+ if (_interval) {
101
+ // Do not record allocation unless allocated at least _interval bytes
102
+ while (true) {
103
+ u64 prev = _allocated_bytes;
104
+ u64 next = prev + rsize;
105
+ if (next < _interval) {
106
+ if (__sync_bool_compare_and_swap(&_allocated_bytes, prev, next)) {
107
+ return;
108
+ }
109
+ } else {
110
+ if (__sync_bool_compare_and_swap(&_allocated_bytes, prev, next % _interval)) {
111
+ break;
112
+ }
113
+ }
114
+ }
115
+ }
116
+
117
+ VMSymbol* symbol = VMKlass::fromHandle(rklass)->name();
118
+ if (outside_tlab) {
119
+ // Invert the last bit to distinguish jmethodID from the allocation in new TLAB
120
+ Profiler::_instance.recordSample(ucontext, rsize, BCI_SYMBOL_OUTSIDE_TLAB, (jmethodID)((uintptr_t)symbol ^ 1));
121
+ } else {
122
+ Profiler::_instance.recordSample(ucontext, rsize, BCI_SYMBOL, (jmethodID)symbol);
123
+ }
124
+ }
125
+
126
+ Error AllocTracer::start(Arguments& args) {
127
+ if (!VMStructs::available()) {
128
+ return Error("VMStructs unavailable. Unsupported JVM?");
129
+ }
130
+
131
+ NativeCodeCache* libjvm = Profiler::_instance.jvmLibrary();
132
+ if (!(_in_new_tlab.resolve(libjvm) || _in_new_tlab2.resolve(libjvm)) ||
133
+ !(_outside_tlab.resolve(libjvm) || _outside_tlab2.resolve(libjvm))) {
134
+ return Error("No AllocTracer symbols found. Are JDK debug symbols installed?");
135
+ }
136
+
137
+ _interval = args._interval;
138
+ _allocated_bytes = 0;
139
+
140
+ OS::installSignalHandler(SIGTRAP, signalHandler);
141
+
142
+ _in_new_tlab.install();
143
+ _outside_tlab.install();
144
+ _in_new_tlab2.install();
145
+ _outside_tlab2.install();
146
+
147
+ return Error::OK;
148
+ }
149
+
150
+ void AllocTracer::stop() {
151
+ _in_new_tlab.uninstall();
152
+ _outside_tlab.uninstall();
153
+ _in_new_tlab2.uninstall();
154
+ _outside_tlab2.uninstall();
155
+ }
@@ -0,0 +1,74 @@
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 _ALLOCTRACER_H
18
+ #define _ALLOCTRACER_H
19
+
20
+ #include <signal.h>
21
+ #include <stdint.h>
22
+ #include "arch.h"
23
+ #include "codeCache.h"
24
+ #include "engine.h"
25
+
26
+
27
+ // Describes OpenJDK function being intercepted
28
+ class Trap {
29
+ private:
30
+ const char* _func_name;
31
+ instruction_t* _entry;
32
+ instruction_t _saved_insn;
33
+
34
+ public:
35
+ Trap(const char* func_name) : _func_name(func_name), _entry(NULL) {
36
+ }
37
+
38
+ bool resolve(NativeCodeCache* libjvm);
39
+ void install();
40
+ void uninstall();
41
+
42
+ friend class AllocTracer;
43
+ };
44
+
45
+
46
+ class AllocTracer : public Engine {
47
+ private:
48
+ // JDK 7-9
49
+ static Trap _in_new_tlab;
50
+ static Trap _outside_tlab;
51
+ // JDK 10+
52
+ static Trap _in_new_tlab2;
53
+ static Trap _outside_tlab2;
54
+
55
+ static u64 _interval;
56
+ static volatile u64 _allocated_bytes;
57
+
58
+ static void signalHandler(int signo, siginfo_t* siginfo, void* ucontext);
59
+ static void recordAllocation(void* ucontext, uintptr_t rklass, uintptr_t rsize, bool outside_tlab);
60
+
61
+ public:
62
+ const char* name() {
63
+ return "alloc";
64
+ }
65
+
66
+ const char* units() {
67
+ return "bytes";
68
+ }
69
+
70
+ Error start(Arguments& args);
71
+ void stop();
72
+ };
73
+
74
+ #endif // _ALLOCTRACER_H
@@ -0,0 +1,69 @@
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 _ARCH_H
18
+ #define _ARCH_H
19
+
20
+
21
+ typedef unsigned long long u64;
22
+
23
+ static inline u64 atomicInc(volatile u64& var, u64 increment = 1) {
24
+ return __sync_fetch_and_add(&var, increment);
25
+ }
26
+
27
+ static inline int atomicInc(volatile int& var, int increment = 1) {
28
+ return __sync_fetch_and_add(&var, increment);
29
+ }
30
+
31
+
32
+ #if defined(__x86_64__) || defined(__i386__)
33
+
34
+ typedef unsigned char instruction_t;
35
+ const instruction_t BREAKPOINT = 0xcc;
36
+
37
+ #define spinPause() asm volatile("pause")
38
+ #define rmb() asm volatile("lfence" : : : "memory")
39
+ #define flushCache(addr) asm volatile("mfence; clflush (%0); mfence" : : "r"(addr) : "memory")
40
+
41
+ #elif defined(__arm__) || defined(__thumb__)
42
+
43
+ typedef unsigned int instruction_t;
44
+ const instruction_t BREAKPOINT = 0xe7f001f0;
45
+
46
+ #define spinPause() asm volatile("yield")
47
+ #define rmb() asm volatile("dmb ish" : : : "memory")
48
+ #define flushCache(addr) __builtin___clear_cache((char*)(addr), (char*)(addr) + sizeof(instruction_t))
49
+
50
+ #elif defined(__aarch64__)
51
+
52
+ typedef unsigned int instruction_t;
53
+ const instruction_t BREAKPOINT = 0xd4200000;
54
+
55
+ #define spinPause() asm volatile("yield")
56
+ #define rmb() asm volatile("dmb ish" : : : "memory")
57
+ #define flushCache(addr) __builtin___clear_cache((char*)(addr), (char*)(addr) + sizeof(instruction_t))
58
+
59
+ #else
60
+ #warning "Compiling on unsupported arch"
61
+
62
+ #define spinPause()
63
+ #define rmb() __sync_synchronize()
64
+ #define flushCache(addr) __builtin___clear_cache((char*)(addr), (char*)(addr) + sizeof(instruction_t))
65
+
66
+ #endif
67
+
68
+
69
+ #endif // _ARCH_H
@@ -0,0 +1,265 @@
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 <limits.h>
18
+ #include <stdio.h>
19
+ #include <stdlib.h>
20
+ #include <string.h>
21
+ #include <time.h>
22
+ #include <sys/types.h>
23
+ #include <unistd.h>
24
+ #include "arguments.h"
25
+
26
+
27
+ // Predefined value that denotes successful operation
28
+ const Error Error::OK(NULL);
29
+
30
+ // Extra buffer space for expanding file pattern
31
+ const size_t EXTRA_BUF_SIZE = 512;
32
+
33
+
34
+ // Parses agent arguments.
35
+ // The format of the string is:
36
+ // arg[,arg...]
37
+ // where arg is one of the following options:
38
+ // start - start profiling
39
+ // resume - start or resume profiling without resetting collected data
40
+ // stop - stop profiling
41
+ // status - print profiling status (inactive / running for X seconds)
42
+ // list - show the list of available profiling events
43
+ // version - display the agent version
44
+ // event=EVENT - which event to trace (cpu, alloc, lock, cache-misses etc.)
45
+ // collapsed[=C] - dump collapsed stacks (the format used by FlameGraph script)
46
+ // svg[=C] - produce Flame Graph in SVG format
47
+ // tree[=C] - produce call tree in HTML format
48
+ // C is counter type: 'samples' or 'total'
49
+ // jfr - dump events in Java Flight Recorder format
50
+ // summary - dump profiling summary (number of collected samples of each type)
51
+ // traces[=N] - dump top N call traces
52
+ // flat[=N] - dump top N methods (aka flat profile)
53
+ // interval=N - sampling interval in ns (default: 10'000'000, i.e. 10 ms)
54
+ // jstackdepth=N - maximum Java stack depth (default: 2048)
55
+ // framebuf=N - size of the buffer for stack frames (default: 1'000'000)
56
+ // threads - profile different threads separately
57
+ // syncwalk - use synchronous JVMTI stack walker instead of AsyncGetCallTrace
58
+ // allkernel - include only kernel-mode events
59
+ // alluser - include only user-mode events
60
+ // simple - simple class names instead of FQN
61
+ // dot - dotted class names
62
+ // sig - print method signatures
63
+ // ann - annotate Java method names
64
+ // title=TITLE - FlameGraph title
65
+ // width=PX - FlameGraph image width
66
+ // height=PX - FlameGraph frame height
67
+ // minwidth=PX - FlameGraph minimum frame width
68
+ // reverse - generate stack-reversed FlameGraph / Call tree
69
+ // file=FILENAME - output file name for dumping
70
+ //
71
+ // It is possible to specify multiple dump options at the same time
72
+
73
+ Error Arguments::parse(const char* args) {
74
+ if (args == NULL) {
75
+ return Error::OK;
76
+ }
77
+
78
+ size_t len = strlen(args);
79
+ free(_buf);
80
+ _buf = (char*)malloc(len + EXTRA_BUF_SIZE);
81
+ if (_buf == NULL) {
82
+ return Error("Not enough memory to parse arguments");
83
+ }
84
+ strcpy(_buf, args);
85
+
86
+ for (char* arg = strtok(_buf, ","); arg != NULL; arg = strtok(NULL, ",")) {
87
+ char* value = strchr(arg, '=');
88
+ if (value != NULL) *value++ = 0;
89
+
90
+ if (strcmp(arg, "start") == 0) {
91
+ _action = ACTION_START;
92
+ } else if (strcmp(arg, "resume") == 0) {
93
+ _action = ACTION_RESUME;
94
+ } else if (strcmp(arg, "stop") == 0) {
95
+ _action = ACTION_STOP;
96
+ } else if (strcmp(arg, "status") == 0) {
97
+ _action = ACTION_STATUS;
98
+ } else if (strcmp(arg, "list") == 0) {
99
+ _action = ACTION_LIST;
100
+ } else if (strcmp(arg, "version") == 0) {
101
+ _action = ACTION_VERSION;
102
+ } else if (strcmp(arg, "event") == 0) {
103
+ if (value == NULL || value[0] == 0) {
104
+ return Error("event must not be empty");
105
+ }
106
+ _event = value;
107
+ } else if (strcmp(arg, "collapsed") == 0 || strcmp(arg, "folded") == 0) {
108
+ _output = OUTPUT_COLLAPSED;
109
+ _counter = value == NULL || strcmp(value, "samples") == 0 ? COUNTER_SAMPLES : COUNTER_TOTAL;
110
+ } else if (strcmp(arg, "flamegraph") == 0 || strcmp(arg, "svg") == 0) {
111
+ _output = OUTPUT_FLAMEGRAPH;
112
+ _counter = value == NULL || strcmp(value, "samples") == 0 ? COUNTER_SAMPLES : COUNTER_TOTAL;
113
+ } else if (strcmp(arg, "tree") == 0) {
114
+ _output = OUTPUT_TREE;
115
+ _counter = value == NULL || strcmp(value, "samples") == 0 ? COUNTER_SAMPLES : COUNTER_TOTAL;
116
+ } else if (strcmp(arg, "jfr") == 0) {
117
+ _output = OUTPUT_JFR;
118
+ } else if (strcmp(arg, "summary") == 0) {
119
+ _output = OUTPUT_TEXT;
120
+ } else if (strcmp(arg, "traces") == 0) {
121
+ _output = OUTPUT_TEXT;
122
+ _dump_traces = value == NULL ? INT_MAX : atoi(value);
123
+ } else if (strcmp(arg, "flat") == 0) {
124
+ _output = OUTPUT_TEXT;
125
+ _dump_flat = value == NULL ? INT_MAX : atoi(value);
126
+ } else if (strcmp(arg, "interval") == 0) {
127
+ if (value == NULL || (_interval = parseUnits(value)) <= 0) {
128
+ return Error("interval must be > 0");
129
+ }
130
+ } else if (strcmp(arg, "jstackdepth") == 0) {
131
+ if (value == NULL || (_jstackdepth = atoi(value)) <= 0) {
132
+ return Error("jstackdepth must be > 0");
133
+ }
134
+ } else if (strcmp(arg, "framebuf") == 0) {
135
+ if (value == NULL || (_framebuf = atoi(value)) <= 0) {
136
+ return Error("framebuf must be > 0");
137
+ }
138
+ } else if (strcmp(arg, "threads") == 0) {
139
+ _threads = true;
140
+ } else if (strcmp(arg, "syncwalk") == 0) {
141
+ _sync_walk = true;
142
+ } else if (strcmp(arg, "allkernel") == 0) {
143
+ _ring = RING_KERNEL;
144
+ } else if (strcmp(arg, "alluser") == 0) {
145
+ _ring = RING_USER;
146
+ } else if (strcmp(arg, "simple") == 0) {
147
+ _style |= STYLE_SIMPLE;
148
+ } else if (strcmp(arg, "dot") == 0) {
149
+ _style |= STYLE_DOTTED;
150
+ } else if (strcmp(arg, "sig") == 0) {
151
+ _style |= STYLE_SIGNATURES;
152
+ } else if (strcmp(arg, "ann") == 0) {
153
+ _style |= STYLE_ANNOTATE;
154
+ } else if (strcmp(arg, "title") == 0 && value != NULL) {
155
+ _title = value;
156
+ } else if (strcmp(arg, "width") == 0 && value != NULL) {
157
+ _width = atoi(value);
158
+ } else if (strcmp(arg, "height") == 0 && value != NULL) {
159
+ _height = atoi(value);
160
+ } else if (strcmp(arg, "minwidth") == 0 && value != NULL) {
161
+ _minwidth = atof(value);
162
+ } else if (strcmp(arg, "reverse") == 0) {
163
+ _reverse = true;
164
+ } else if (strcmp(arg, "file") == 0) {
165
+ if (value == NULL || value[0] == 0) {
166
+ return Error("file must not be empty");
167
+ }
168
+ _file = value;
169
+ }
170
+ }
171
+
172
+ if (_file != NULL && strchr(_file, '%') != NULL) {
173
+ _file = expandFilePattern(_buf + len + 1, EXTRA_BUF_SIZE - 1, _file);
174
+ }
175
+
176
+ if (_file != NULL && _output == OUTPUT_NONE) {
177
+ _output = detectOutputFormat(_file);
178
+ _dump_traces = 200;
179
+ _dump_flat = 200;
180
+ }
181
+
182
+ if (_output != OUTPUT_NONE && (_action == ACTION_NONE || _action == ACTION_STOP)) {
183
+ _action = ACTION_DUMP;
184
+ }
185
+
186
+ return Error::OK;
187
+ }
188
+
189
+ // Expands %p to the process id
190
+ // %t to the timestamp
191
+ const char* Arguments::expandFilePattern(char* dest, size_t max_size, const char* pattern) {
192
+ char* ptr = dest;
193
+ char* end = dest + max_size - 1;
194
+
195
+ while (ptr < end && *pattern != 0) {
196
+ char c = *pattern++;
197
+ if (c == '%') {
198
+ c = *pattern++;
199
+ if (c == 0) {
200
+ break;
201
+ } else if (c == 'p') {
202
+ ptr += snprintf(ptr, end - ptr, "%d", getpid());
203
+ continue;
204
+ } else if (c == 't') {
205
+ time_t timestamp = time(NULL);
206
+ struct tm t;
207
+ localtime_r(&timestamp, &t);
208
+ ptr += snprintf(ptr, end - ptr, "%d%02d%02d-%02d%02d%02d",
209
+ t.tm_year + 1900, t.tm_mon + 1, t.tm_mday,
210
+ t.tm_hour, t.tm_min, t.tm_sec);
211
+ continue;
212
+ }
213
+ }
214
+ *ptr++ = c;
215
+ }
216
+
217
+ *ptr = 0;
218
+ return dest;
219
+ }
220
+
221
+ Output Arguments::detectOutputFormat(const char* file) {
222
+ const char* ext = strrchr(file, '.');
223
+ if (ext != NULL) {
224
+ if (strcmp(ext, ".svg") == 0) {
225
+ return OUTPUT_FLAMEGRAPH;
226
+ } else if (strcmp(ext, ".html") == 0) {
227
+ return OUTPUT_TREE;
228
+ } else if (strcmp(ext, ".jfr") == 0) {
229
+ return OUTPUT_JFR;
230
+ } else if (strcmp(ext, ".collapsed") == 0 || strcmp(ext, ".folded") == 0) {
231
+ return OUTPUT_COLLAPSED;
232
+ }
233
+ }
234
+ return OUTPUT_TEXT;
235
+ }
236
+
237
+ long Arguments::parseUnits(const char* str) {
238
+ char* end;
239
+ long result = strtol(str, &end, 0);
240
+
241
+ if (*end) {
242
+ switch (*end) {
243
+ case 'K': case 'k':
244
+ case 'U': case 'u': // microseconds
245
+ return result * 1000;
246
+ case 'M': case 'm': // million, megabytes or milliseconds
247
+ return result * 1000000;
248
+ case 'G': case 'g':
249
+ case 'S': case 's': // seconds
250
+ return result * 1000000000;
251
+ }
252
+ }
253
+
254
+ return result;
255
+ }
256
+
257
+ Arguments::~Arguments() {
258
+ free(_buf);
259
+ }
260
+
261
+ void Arguments::save(Arguments& other) {
262
+ free(_buf);
263
+ *this = other;
264
+ other._buf = NULL;
265
+ }