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,156 @@
|
|
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
|
+
#ifdef __APPLE__
|
18
|
+
|
19
|
+
#include <stdio.h>
|
20
|
+
#include <errno.h>
|
21
|
+
#include <fcntl.h>
|
22
|
+
#include <unistd.h>
|
23
|
+
#include <sys/mman.h>
|
24
|
+
#include <arpa/inet.h>
|
25
|
+
#include <mach-o/dyld.h>
|
26
|
+
#include <mach-o/loader.h>
|
27
|
+
#include <mach-o/fat.h>
|
28
|
+
#include <mach-o/nlist.h>
|
29
|
+
#include "symbols.h"
|
30
|
+
|
31
|
+
|
32
|
+
class MachOParser {
|
33
|
+
private:
|
34
|
+
NativeCodeCache* _cc;
|
35
|
+
const mach_header* _image_base;
|
36
|
+
|
37
|
+
static const char* add(const void* base, uint32_t offset) {
|
38
|
+
return (const char*)base + offset;
|
39
|
+
}
|
40
|
+
|
41
|
+
void loadSymbols(mach_header_64* header, symtab_command* symtab) {
|
42
|
+
nlist_64* symbol_table = (nlist_64*)add(header, symtab->symoff);
|
43
|
+
const char* str_table = add(header, symtab->stroff);
|
44
|
+
|
45
|
+
for (uint32_t i = 0; i < symtab->nsyms; i++) {
|
46
|
+
nlist_64 sym = symbol_table[i];
|
47
|
+
if ((sym.n_type & 0xee) == 0x0e && sym.n_value != 0) {
|
48
|
+
const char* addr = add(_image_base, sym.n_value);
|
49
|
+
const char* name = str_table + sym.n_un.n_strx;
|
50
|
+
if (name[0] == '_') name++;
|
51
|
+
_cc->add(addr, 0, name);
|
52
|
+
}
|
53
|
+
}
|
54
|
+
}
|
55
|
+
|
56
|
+
void parseMachO(mach_header_64* header) {
|
57
|
+
load_command* lc = (load_command*)(header + 1);
|
58
|
+
|
59
|
+
for (uint32_t i = 0; i < header->ncmds; i++) {
|
60
|
+
if (lc->cmd == LC_SYMTAB) {
|
61
|
+
loadSymbols(header, (symtab_command*)lc);
|
62
|
+
break;
|
63
|
+
}
|
64
|
+
lc = (load_command*)add(lc, lc->cmdsize);
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
void parseFatObject(fat_header* header) {
|
69
|
+
int narch = header->nfat_arch;
|
70
|
+
fat_arch* arch = (fat_arch*)(header + 1);
|
71
|
+
|
72
|
+
for (uint32_t i = 0; i < narch; i++) {
|
73
|
+
if (arch[i].cputype == _image_base->cputype &&
|
74
|
+
arch[i].cpusubtype == _image_base->cpusubtype) {
|
75
|
+
parseMachO((mach_header_64*)add(header, arch[i].offset));
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
|
80
|
+
// The same as parseFatObject, but fields are big-endian
|
81
|
+
void parseFatObjectBE(fat_header* header) {
|
82
|
+
int narch = htonl(header->nfat_arch);
|
83
|
+
fat_arch* arch = (fat_arch*)(header + 1);
|
84
|
+
|
85
|
+
for (uint32_t i = 0; i < narch; i++) {
|
86
|
+
if (htonl(arch[i].cputype) == _image_base->cputype &&
|
87
|
+
htonl(arch[i].cpusubtype) == _image_base->cpusubtype) {
|
88
|
+
parseMachO((mach_header_64*)add(header, htonl(arch[i].offset)));
|
89
|
+
}
|
90
|
+
}
|
91
|
+
}
|
92
|
+
|
93
|
+
void parse(mach_header* header) {
|
94
|
+
uint32_t magic = header->magic;
|
95
|
+
if (magic == MH_MAGIC_64) {
|
96
|
+
parseMachO((mach_header_64*)header);
|
97
|
+
} else if (magic == FAT_MAGIC) {
|
98
|
+
parseFatObject((fat_header*)header);
|
99
|
+
} else if (magic == FAT_CIGAM) {
|
100
|
+
parseFatObjectBE((fat_header*)header);
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
public:
|
105
|
+
MachOParser(NativeCodeCache* cc, const mach_header* image_base) : _cc(cc), _image_base(image_base) {
|
106
|
+
}
|
107
|
+
|
108
|
+
static void parseFile(NativeCodeCache* cc, const mach_header* image_base, const char* file_name) {
|
109
|
+
int fd = open(file_name, O_RDONLY);
|
110
|
+
if (fd == -1) {
|
111
|
+
return;
|
112
|
+
}
|
113
|
+
|
114
|
+
size_t length = (size_t)lseek(fd, 0, SEEK_END);
|
115
|
+
void* addr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
|
116
|
+
close(fd);
|
117
|
+
|
118
|
+
if (addr == MAP_FAILED) {
|
119
|
+
fprintf(stderr, "Could not parse symbols from %s: %s\n", file_name, strerror(errno));
|
120
|
+
} else {
|
121
|
+
MachOParser parser(cc, image_base);
|
122
|
+
parser.parse((mach_header*)addr);
|
123
|
+
munmap(addr, length);
|
124
|
+
}
|
125
|
+
}
|
126
|
+
};
|
127
|
+
|
128
|
+
|
129
|
+
Mutex Symbols::_parse_lock;
|
130
|
+
std::set<const void*> Symbols::_parsed_libraries;
|
131
|
+
|
132
|
+
void Symbols::parseKernelSymbols(NativeCodeCache* cc) {
|
133
|
+
}
|
134
|
+
|
135
|
+
void Symbols::parseLibraries(NativeCodeCache** array, volatile int& count, int size) {
|
136
|
+
MutexLocker ml(_parse_lock);
|
137
|
+
uint32_t images = _dyld_image_count();
|
138
|
+
|
139
|
+
for (uint32_t i = 0; i < images && count < size; i++) {
|
140
|
+
const mach_header* image_base = _dyld_get_image_header(i);
|
141
|
+
if (!_parsed_libraries.insert(image_base).second) {
|
142
|
+
continue; // the library was already parsed
|
143
|
+
}
|
144
|
+
|
145
|
+
const char* path = _dyld_get_image_name(i);
|
146
|
+
|
147
|
+
NativeCodeCache* cc = new NativeCodeCache(path);
|
148
|
+
MachOParser::parseFile(cc, image_base, path);
|
149
|
+
|
150
|
+
cc->sort();
|
151
|
+
array[count] = cc;
|
152
|
+
atomicInc(count);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
156
|
+
#endif // __APPLE__
|
@@ -0,0 +1,173 @@
|
|
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 <dlfcn.h>
|
19
|
+
#include <string.h>
|
20
|
+
#include "vmEntry.h"
|
21
|
+
#include "arguments.h"
|
22
|
+
#include "os.h"
|
23
|
+
#include "profiler.h"
|
24
|
+
#include "lockTracer.h"
|
25
|
+
|
26
|
+
|
27
|
+
static Arguments _agent_args;
|
28
|
+
|
29
|
+
JavaVM* VM::_vm;
|
30
|
+
jvmtiEnv* VM::_jvmti = NULL;
|
31
|
+
bool VM::_hotspot;
|
32
|
+
void* VM::_libjvm;
|
33
|
+
void* VM::_libjava;
|
34
|
+
AsyncGetCallTrace VM::_asyncGetCallTrace;
|
35
|
+
|
36
|
+
|
37
|
+
void VM::init(JavaVM* vm, bool attach) {
|
38
|
+
if (_jvmti != NULL) return;
|
39
|
+
|
40
|
+
_vm = vm;
|
41
|
+
_vm->GetEnv((void**)&_jvmti, JVMTI_VERSION_1_0);
|
42
|
+
|
43
|
+
char* vm_name;
|
44
|
+
if (_jvmti->GetSystemProperty("java.vm.name", &vm_name) == 0) {
|
45
|
+
_hotspot = strstr(vm_name, "Zing") == NULL;
|
46
|
+
_jvmti->Deallocate((unsigned char*)vm_name);
|
47
|
+
} else {
|
48
|
+
_hotspot = false;
|
49
|
+
}
|
50
|
+
|
51
|
+
jvmtiCapabilities capabilities = {0};
|
52
|
+
capabilities.can_generate_all_class_hook_events = 1;
|
53
|
+
capabilities.can_get_bytecodes = 1;
|
54
|
+
capabilities.can_get_constant_pool = 1;
|
55
|
+
capabilities.can_get_source_file_name = 1;
|
56
|
+
capabilities.can_get_line_numbers = 1;
|
57
|
+
capabilities.can_generate_compiled_method_load_events = 1;
|
58
|
+
capabilities.can_generate_monitor_events = 1;
|
59
|
+
capabilities.can_tag_objects = 1;
|
60
|
+
_jvmti->AddCapabilities(&capabilities);
|
61
|
+
|
62
|
+
jvmtiEventCallbacks callbacks = {0};
|
63
|
+
callbacks.VMInit = VMInit;
|
64
|
+
callbacks.VMDeath = VMDeath;
|
65
|
+
callbacks.ClassLoad = ClassLoad;
|
66
|
+
callbacks.ClassPrepare = ClassPrepare;
|
67
|
+
callbacks.CompiledMethodLoad = Profiler::CompiledMethodLoad;
|
68
|
+
callbacks.CompiledMethodUnload = Profiler::CompiledMethodUnload;
|
69
|
+
callbacks.DynamicCodeGenerated = Profiler::DynamicCodeGenerated;
|
70
|
+
callbacks.ThreadStart = Profiler::ThreadStart;
|
71
|
+
callbacks.ThreadEnd = Profiler::ThreadEnd;
|
72
|
+
callbacks.MonitorContendedEnter = LockTracer::MonitorContendedEnter;
|
73
|
+
callbacks.MonitorContendedEntered = LockTracer::MonitorContendedEntered;
|
74
|
+
_jvmti->SetEventCallbacks(&callbacks, sizeof(callbacks));
|
75
|
+
|
76
|
+
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_INIT, NULL);
|
77
|
+
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL);
|
78
|
+
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
|
79
|
+
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL);
|
80
|
+
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
|
81
|
+
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
|
82
|
+
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
|
83
|
+
|
84
|
+
_libjvm = getLibraryHandle("libjvm.so");
|
85
|
+
_libjava = getLibraryHandle("libjava.so");
|
86
|
+
_asyncGetCallTrace = (AsyncGetCallTrace)dlsym(_libjvm, "AsyncGetCallTrace");
|
87
|
+
|
88
|
+
if (attach) {
|
89
|
+
loadAllMethodIDs(_jvmti);
|
90
|
+
_jvmti->GenerateEvents(JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
|
91
|
+
_jvmti->GenerateEvents(JVMTI_EVENT_COMPILED_METHOD_LOAD);
|
92
|
+
}
|
93
|
+
}
|
94
|
+
|
95
|
+
void* VM::getLibraryHandle(const char* name) {
|
96
|
+
if (!OS::isJavaLibraryVisible()) {
|
97
|
+
void* handle = dlopen(name, RTLD_LAZY);
|
98
|
+
if (handle != NULL) {
|
99
|
+
return handle;
|
100
|
+
}
|
101
|
+
std::cerr << "Failed to load " << name << ": " << dlerror() << std::endl;
|
102
|
+
}
|
103
|
+
return RTLD_DEFAULT;
|
104
|
+
}
|
105
|
+
|
106
|
+
void VM::loadMethodIDs(jvmtiEnv* jvmti, jclass klass) {
|
107
|
+
jint method_count;
|
108
|
+
jmethodID* methods;
|
109
|
+
if (jvmti->GetClassMethods(klass, &method_count, &methods) == 0) {
|
110
|
+
jvmti->Deallocate((unsigned char*)methods);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
void VM::loadAllMethodIDs(jvmtiEnv* jvmti) {
|
115
|
+
jint class_count;
|
116
|
+
jclass* classes;
|
117
|
+
if (jvmti->GetLoadedClasses(&class_count, &classes) == 0) {
|
118
|
+
for (int i = 0; i < class_count; i++) {
|
119
|
+
loadMethodIDs(jvmti, classes[i]);
|
120
|
+
}
|
121
|
+
jvmti->Deallocate((unsigned char*)classes);
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
void JNICALL VM::VMInit(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
|
126
|
+
loadAllMethodIDs(jvmti);
|
127
|
+
// Delayed start of profiler if agent has been loaded at VM bootstrap
|
128
|
+
Profiler::_instance.run(_agent_args);
|
129
|
+
}
|
130
|
+
|
131
|
+
void JNICALL VM::VMDeath(jvmtiEnv* jvmti, JNIEnv* jni) {
|
132
|
+
Profiler::_instance.shutdown(_agent_args);
|
133
|
+
}
|
134
|
+
|
135
|
+
|
136
|
+
extern "C" JNIEXPORT jint JNICALL
|
137
|
+
Agent_OnLoad(JavaVM* vm, char* options, void* reserved) {
|
138
|
+
VM::init(vm, false);
|
139
|
+
|
140
|
+
Error error = _agent_args.parse(options);
|
141
|
+
if (error) {
|
142
|
+
std::cerr << error.message() << std::endl;
|
143
|
+
return -1;
|
144
|
+
}
|
145
|
+
|
146
|
+
return 0;
|
147
|
+
}
|
148
|
+
|
149
|
+
extern "C" JNIEXPORT jint JNICALL
|
150
|
+
Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
|
151
|
+
VM::init(vm, true);
|
152
|
+
|
153
|
+
Arguments args;
|
154
|
+
Error error = args.parse(options);
|
155
|
+
if (error) {
|
156
|
+
std::cerr << error.message() << std::endl;
|
157
|
+
return -1;
|
158
|
+
}
|
159
|
+
|
160
|
+
// Save the arguments in case of shutdown
|
161
|
+
if (args._action == ACTION_START || args._action == ACTION_RESUME) {
|
162
|
+
_agent_args.save(args);
|
163
|
+
}
|
164
|
+
Profiler::_instance.run(args);
|
165
|
+
|
166
|
+
return 0;
|
167
|
+
}
|
168
|
+
|
169
|
+
extern "C" JNIEXPORT jint JNICALL
|
170
|
+
JNI_OnLoad(JavaVM* vm, void* reserved) {
|
171
|
+
VM::init(vm, true);
|
172
|
+
return JNI_VERSION_1_6;
|
173
|
+
}
|
@@ -0,0 +1,105 @@
|
|
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
|
+
#ifndef _VMENTRY_H
|
18
|
+
#define _VMENTRY_H
|
19
|
+
|
20
|
+
#include <jvmti.h>
|
21
|
+
|
22
|
+
|
23
|
+
// Denotes ASGCT_CallFrame where method_id has special meaning (not jmethodID)
|
24
|
+
enum ASGCT_CallFrameType {
|
25
|
+
BCI_NATIVE_FRAME = -10, // method_id is native function name (char*)
|
26
|
+
BCI_SYMBOL = -11, // method_id is VMSymbol*
|
27
|
+
BCI_SYMBOL_OUTSIDE_TLAB = -12, // VMSymbol* specifically for allocations outside TLAB
|
28
|
+
BCI_THREAD_ID = -13, // method_id designates a thread
|
29
|
+
BCI_ERROR = -14, // method_id is error string
|
30
|
+
};
|
31
|
+
|
32
|
+
// See hotspot/src/share/vm/prims/forte.cpp
|
33
|
+
enum ASGCT_Failure {
|
34
|
+
ticks_no_Java_frame = 0,
|
35
|
+
ticks_no_class_load = -1,
|
36
|
+
ticks_GC_active = -2,
|
37
|
+
ticks_unknown_not_Java = -3,
|
38
|
+
ticks_not_walkable_not_Java = -4,
|
39
|
+
ticks_unknown_Java = -5,
|
40
|
+
ticks_not_walkable_Java = -6,
|
41
|
+
ticks_unknown_state = -7,
|
42
|
+
ticks_thread_exit = -8,
|
43
|
+
ticks_deopt = -9,
|
44
|
+
ticks_safepoint = -10,
|
45
|
+
ticks_skipped = -11,
|
46
|
+
ASGCT_FAILURE_TYPES = 12
|
47
|
+
};
|
48
|
+
|
49
|
+
typedef struct {
|
50
|
+
jint bci;
|
51
|
+
jmethodID method_id;
|
52
|
+
} ASGCT_CallFrame;
|
53
|
+
|
54
|
+
typedef struct {
|
55
|
+
JNIEnv* env;
|
56
|
+
jint num_frames;
|
57
|
+
ASGCT_CallFrame* frames;
|
58
|
+
} ASGCT_CallTrace;
|
59
|
+
|
60
|
+
typedef void (*AsyncGetCallTrace)(ASGCT_CallTrace*, jint, void*);
|
61
|
+
|
62
|
+
|
63
|
+
class VM {
|
64
|
+
private:
|
65
|
+
static JavaVM* _vm;
|
66
|
+
static jvmtiEnv* _jvmti;
|
67
|
+
static bool _hotspot;
|
68
|
+
|
69
|
+
static void* getLibraryHandle(const char* name);
|
70
|
+
static void loadMethodIDs(jvmtiEnv* jvmti, jclass klass);
|
71
|
+
static void loadAllMethodIDs(jvmtiEnv* jvmti);
|
72
|
+
|
73
|
+
public:
|
74
|
+
static void* _libjvm;
|
75
|
+
static void* _libjava;
|
76
|
+
static AsyncGetCallTrace _asyncGetCallTrace;
|
77
|
+
|
78
|
+
static void init(JavaVM* vm, bool attach);
|
79
|
+
|
80
|
+
static jvmtiEnv* jvmti() {
|
81
|
+
return _jvmti;
|
82
|
+
}
|
83
|
+
|
84
|
+
static JNIEnv* jni() {
|
85
|
+
JNIEnv* jni;
|
86
|
+
return _vm->GetEnv((void**)&jni, JNI_VERSION_1_6) == 0 ? jni : NULL;
|
87
|
+
}
|
88
|
+
|
89
|
+
static bool is_hotspot() {
|
90
|
+
return _hotspot;
|
91
|
+
}
|
92
|
+
|
93
|
+
static void JNICALL VMInit(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread);
|
94
|
+
static void JNICALL VMDeath(jvmtiEnv* jvmti, JNIEnv* jni);
|
95
|
+
|
96
|
+
static void JNICALL ClassLoad(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass) {
|
97
|
+
// Needed only for AsyncGetCallTrace support
|
98
|
+
}
|
99
|
+
|
100
|
+
static void JNICALL ClassPrepare(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread, jclass klass) {
|
101
|
+
loadMethodIDs(jvmti, klass);
|
102
|
+
}
|
103
|
+
};
|
104
|
+
|
105
|
+
#endif // _VMENTRY_H
|
@@ -0,0 +1,104 @@
|
|
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 <stdint.h>
|
18
|
+
#include <string.h>
|
19
|
+
#include "vmStructs.h"
|
20
|
+
#include "vmEntry.h"
|
21
|
+
|
22
|
+
|
23
|
+
int VMStructs::_klass_name_offset = -1;
|
24
|
+
int VMStructs::_symbol_length_offset = -1;
|
25
|
+
int VMStructs::_symbol_length_and_refcount_offset = -1;
|
26
|
+
int VMStructs::_symbol_body_offset = -1;
|
27
|
+
int VMStructs::_class_klass_offset = -1;
|
28
|
+
int VMStructs::_thread_osthread_offset = -1;
|
29
|
+
int VMStructs::_osthread_id_offset = -1;
|
30
|
+
bool VMStructs::_has_perm_gen = false;
|
31
|
+
jfieldID VMStructs::_eetop = NULL;
|
32
|
+
|
33
|
+
static uintptr_t readSymbol(NativeCodeCache* lib, const char* symbol_name) {
|
34
|
+
const void* symbol = lib->findSymbol(symbol_name);
|
35
|
+
if (symbol == NULL) {
|
36
|
+
// Avoid JVM crash in case of missing symbols
|
37
|
+
return 0;
|
38
|
+
}
|
39
|
+
return *(uintptr_t*)symbol;
|
40
|
+
}
|
41
|
+
|
42
|
+
void VMStructs::init(NativeCodeCache* libjvm) {
|
43
|
+
if (available()) {
|
44
|
+
return;
|
45
|
+
}
|
46
|
+
|
47
|
+
uintptr_t entry = readSymbol(libjvm, "gHotSpotVMStructs");
|
48
|
+
uintptr_t stride = readSymbol(libjvm, "gHotSpotVMStructEntryArrayStride");
|
49
|
+
uintptr_t type_offset = readSymbol(libjvm, "gHotSpotVMStructEntryTypeNameOffset");
|
50
|
+
uintptr_t field_offset = readSymbol(libjvm, "gHotSpotVMStructEntryFieldNameOffset");
|
51
|
+
uintptr_t offset_offset = readSymbol(libjvm, "gHotSpotVMStructEntryOffsetOffset");
|
52
|
+
uintptr_t address_offset = readSymbol(libjvm, "gHotSpotVMStructEntryAddressOffset");
|
53
|
+
|
54
|
+
if (entry == 0 || stride == 0) {
|
55
|
+
return;
|
56
|
+
}
|
57
|
+
|
58
|
+
while (true) {
|
59
|
+
const char* type = *(const char**)(entry + type_offset);
|
60
|
+
const char* field = *(const char**)(entry + field_offset);
|
61
|
+
if (type == NULL || field == NULL) {
|
62
|
+
break;
|
63
|
+
}
|
64
|
+
|
65
|
+
if (strcmp(type, "Klass") == 0) {
|
66
|
+
if (strcmp(field, "_name") == 0) {
|
67
|
+
_klass_name_offset = *(int*)(entry + offset_offset);
|
68
|
+
}
|
69
|
+
} else if (strcmp(type, "Symbol") == 0) {
|
70
|
+
if (strcmp(field, "_length") == 0) {
|
71
|
+
_symbol_length_offset = *(int*)(entry + offset_offset);
|
72
|
+
} else if (strcmp(field, "_length_and_refcount") == 0) {
|
73
|
+
_symbol_length_and_refcount_offset = *(int*)(entry + offset_offset);
|
74
|
+
} else if (strcmp(field, "_body") == 0) {
|
75
|
+
_symbol_body_offset = *(int*)(entry + offset_offset);
|
76
|
+
}
|
77
|
+
} else if (strcmp(type, "java_lang_Class") == 0) {
|
78
|
+
if (strcmp(field, "_klass_offset") == 0) {
|
79
|
+
_class_klass_offset = **(int**)(entry + address_offset);
|
80
|
+
}
|
81
|
+
} else if (strcmp(type, "JavaThread") == 0) {
|
82
|
+
if (strcmp(field, "_osthread") == 0) {
|
83
|
+
_thread_osthread_offset = *(int*)(entry + offset_offset);
|
84
|
+
}
|
85
|
+
} else if (strcmp(type, "OSThread") == 0) {
|
86
|
+
if (strcmp(field, "_thread_id") == 0) {
|
87
|
+
_osthread_id_offset = *(int*)(entry + offset_offset);
|
88
|
+
}
|
89
|
+
} else if (strcmp(type, "PermGen") == 0) {
|
90
|
+
_has_perm_gen = true;
|
91
|
+
}
|
92
|
+
|
93
|
+
entry += stride;
|
94
|
+
}
|
95
|
+
|
96
|
+
// Get eetop field - a bridge from Java Thread to VMThread
|
97
|
+
if (_thread_osthread_offset >= 0 && _osthread_id_offset >= 0) {
|
98
|
+
JNIEnv* env = VM::jni();
|
99
|
+
jclass threadClass = env->FindClass("java/lang/Thread");
|
100
|
+
if (threadClass != NULL) {
|
101
|
+
_eetop = env->GetFieldID(threadClass, "eetop", "J");
|
102
|
+
}
|
103
|
+
}
|
104
|
+
}
|