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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.gitmodules +3 -0
- data/Gemfile +4 -0
- data/README.md +35 -0
- data/Rakefile +2 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/ext/Rakefile +6 -0
- data/ext/async-profiler/.gitattributes +1 -0
- data/ext/async-profiler/.gitignore +6 -0
- data/ext/async-profiler/.travis.yml +11 -0
- data/ext/async-profiler/CHANGELOG.md +107 -0
- data/ext/async-profiler/JavaHome.class +0 -0
- data/ext/async-profiler/LICENSE +201 -0
- data/ext/async-profiler/Makefile +66 -0
- data/ext/async-profiler/README.md +487 -0
- data/ext/async-profiler/demo/SwingSet2.svg +2247 -0
- data/ext/async-profiler/docs/cddl1.txt +358 -0
- data/ext/async-profiler/profiler.sh +240 -0
- data/ext/async-profiler/src/allocTracer.cpp +155 -0
- data/ext/async-profiler/src/allocTracer.h +74 -0
- data/ext/async-profiler/src/arch.h +69 -0
- data/ext/async-profiler/src/arguments.cpp +265 -0
- data/ext/async-profiler/src/arguments.h +152 -0
- data/ext/async-profiler/src/codeCache.cpp +128 -0
- data/ext/async-profiler/src/codeCache.h +99 -0
- data/ext/async-profiler/src/engine.cpp +50 -0
- data/ext/async-profiler/src/engine.h +38 -0
- data/ext/async-profiler/src/flameGraph.cpp +770 -0
- data/ext/async-profiler/src/flameGraph.h +118 -0
- data/ext/async-profiler/src/flightRecorder.cpp +727 -0
- data/ext/async-profiler/src/flightRecorder.h +39 -0
- data/ext/async-profiler/src/frameName.cpp +189 -0
- data/ext/async-profiler/src/frameName.h +56 -0
- data/ext/async-profiler/src/itimer.cpp +49 -0
- data/ext/async-profiler/src/itimer.h +43 -0
- data/ext/async-profiler/src/jattach/jattach.c +437 -0
- data/ext/async-profiler/src/java/one/profiler/AsyncProfiler.java +160 -0
- data/ext/async-profiler/src/java/one/profiler/AsyncProfilerMXBean.java +43 -0
- data/ext/async-profiler/src/java/one/profiler/Counter.java +25 -0
- data/ext/async-profiler/src/java/one/profiler/Events.java +28 -0
- data/ext/async-profiler/src/javaApi.cpp +124 -0
- data/ext/async-profiler/src/lockTracer.cpp +161 -0
- data/ext/async-profiler/src/lockTracer.h +55 -0
- data/ext/async-profiler/src/mutex.cpp +33 -0
- data/ext/async-profiler/src/mutex.h +49 -0
- data/ext/async-profiler/src/os.h +45 -0
- data/ext/async-profiler/src/os_linux.cpp +129 -0
- data/ext/async-profiler/src/os_macos.cpp +115 -0
- data/ext/async-profiler/src/perfEvents.h +60 -0
- data/ext/async-profiler/src/perfEvents_linux.cpp +550 -0
- data/ext/async-profiler/src/perfEvents_macos.cpp +64 -0
- data/ext/async-profiler/src/profiler.cpp +952 -0
- data/ext/async-profiler/src/profiler.h +238 -0
- data/ext/async-profiler/src/spinLock.h +66 -0
- data/ext/async-profiler/src/stackFrame.h +57 -0
- data/ext/async-profiler/src/stackFrame_aarch64.cpp +75 -0
- data/ext/async-profiler/src/stackFrame_arm.cpp +58 -0
- data/ext/async-profiler/src/stackFrame_i386.cpp +82 -0
- data/ext/async-profiler/src/stackFrame_x64.cpp +113 -0
- data/ext/async-profiler/src/symbols.h +37 -0
- data/ext/async-profiler/src/symbols_linux.cpp +354 -0
- data/ext/async-profiler/src/symbols_macos.cpp +156 -0
- data/ext/async-profiler/src/vmEntry.cpp +173 -0
- data/ext/async-profiler/src/vmEntry.h +105 -0
- data/ext/async-profiler/src/vmStructs.cpp +104 -0
- data/ext/async-profiler/src/vmStructs.h +112 -0
- data/ext/async-profiler/src/wallClock.cpp +96 -0
- data/ext/async-profiler/src/wallClock.h +56 -0
- data/ext/async-profiler/test/AllocatingTarget.java +26 -0
- data/ext/async-profiler/test/LoadLibraryTest.java +21 -0
- data/ext/async-profiler/test/Target.java +31 -0
- data/ext/async-profiler/test/ThreadsTarget.java +35 -0
- data/ext/async-profiler/test/alloc-smoke-test.sh +36 -0
- data/ext/async-profiler/test/load-library-test.sh +35 -0
- data/ext/async-profiler/test/smoke-test.sh +37 -0
- data/ext/async-profiler/test/thread-smoke-test.sh +32 -0
- data/jruby-async-profiler.gemspec +32 -0
- data/lib/jruby/async/profiler.rb +10 -0
- data/lib/jruby/async/profiler/version.rb +7 -0
- 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(×tamp, &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
|
+
}
|