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,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
+ }