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,82 @@
|
|
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 __i386__
|
18
|
+
|
19
|
+
#include "stackFrame.h"
|
20
|
+
|
21
|
+
|
22
|
+
uintptr_t& StackFrame::pc() {
|
23
|
+
return (uintptr_t&)_ucontext->uc_mcontext.gregs[REG_EIP];
|
24
|
+
}
|
25
|
+
|
26
|
+
uintptr_t& StackFrame::sp() {
|
27
|
+
return (uintptr_t&)_ucontext->uc_mcontext.gregs[REG_ESP];
|
28
|
+
}
|
29
|
+
|
30
|
+
uintptr_t& StackFrame::fp() {
|
31
|
+
return (uintptr_t&)_ucontext->uc_mcontext.gregs[REG_EBP];
|
32
|
+
}
|
33
|
+
|
34
|
+
uintptr_t StackFrame::arg0() {
|
35
|
+
return stackAt(1);
|
36
|
+
}
|
37
|
+
|
38
|
+
uintptr_t StackFrame::arg1() {
|
39
|
+
return stackAt(2);
|
40
|
+
}
|
41
|
+
|
42
|
+
uintptr_t StackFrame::arg2() {
|
43
|
+
return stackAt(3);
|
44
|
+
}
|
45
|
+
|
46
|
+
uintptr_t StackFrame::arg3() {
|
47
|
+
return stackAt(4);
|
48
|
+
}
|
49
|
+
|
50
|
+
void StackFrame::ret() {
|
51
|
+
pc() = stackAt(0);
|
52
|
+
sp() += 4;
|
53
|
+
}
|
54
|
+
|
55
|
+
|
56
|
+
static inline bool withinCurrentStack(uintptr_t value) {
|
57
|
+
// Check that value is not too far from stack pointer of current context
|
58
|
+
void* real_sp;
|
59
|
+
return value - (uintptr_t)&real_sp <= 0xffff;
|
60
|
+
}
|
61
|
+
|
62
|
+
bool StackFrame::pop(bool trust_frame_pointer) {
|
63
|
+
if (!withinCurrentStack(sp())) {
|
64
|
+
return false;
|
65
|
+
}
|
66
|
+
|
67
|
+
if (trust_frame_pointer && withinCurrentStack(fp())) {
|
68
|
+
sp() = fp() + 8;
|
69
|
+
fp() = stackAt(-2);
|
70
|
+
pc() = stackAt(-1);
|
71
|
+
} else if (fp() == sp() || withinCurrentStack(stackAt(0))) {
|
72
|
+
fp() = stackAt(0);
|
73
|
+
pc() = stackAt(1);
|
74
|
+
sp() += 8;
|
75
|
+
} else {
|
76
|
+
pc() = stackAt(0);
|
77
|
+
sp() += 4;
|
78
|
+
}
|
79
|
+
return true;
|
80
|
+
}
|
81
|
+
|
82
|
+
#endif // __i386__
|
@@ -0,0 +1,113 @@
|
|
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 __x86_64__
|
18
|
+
|
19
|
+
#include "stackFrame.h"
|
20
|
+
|
21
|
+
|
22
|
+
#ifdef __APPLE__
|
23
|
+
# define REG(l, m) _ucontext->uc_mcontext->__ss.m
|
24
|
+
#else
|
25
|
+
# define REG(l, m) _ucontext->uc_mcontext.gregs[l]
|
26
|
+
#endif
|
27
|
+
|
28
|
+
|
29
|
+
uintptr_t& StackFrame::pc() {
|
30
|
+
return (uintptr_t&)REG(REG_RIP, __rip);
|
31
|
+
}
|
32
|
+
|
33
|
+
uintptr_t& StackFrame::sp() {
|
34
|
+
return (uintptr_t&)REG(REG_RSP, __rsp);
|
35
|
+
}
|
36
|
+
|
37
|
+
uintptr_t& StackFrame::fp() {
|
38
|
+
return (uintptr_t&)REG(REG_RBP, __rbp);
|
39
|
+
}
|
40
|
+
|
41
|
+
uintptr_t StackFrame::arg0() {
|
42
|
+
return (uintptr_t)REG(REG_RDI, __rdi);
|
43
|
+
}
|
44
|
+
|
45
|
+
uintptr_t StackFrame::arg1() {
|
46
|
+
return (uintptr_t)REG(REG_RSI, __rsi);
|
47
|
+
}
|
48
|
+
|
49
|
+
uintptr_t StackFrame::arg2() {
|
50
|
+
return (uintptr_t)REG(REG_RDX, __rdx);
|
51
|
+
}
|
52
|
+
|
53
|
+
uintptr_t StackFrame::arg3() {
|
54
|
+
return (uintptr_t)REG(REG_RCX, __rcx);
|
55
|
+
}
|
56
|
+
|
57
|
+
void StackFrame::ret() {
|
58
|
+
pc() = stackAt(0);
|
59
|
+
sp() += 8;
|
60
|
+
}
|
61
|
+
|
62
|
+
|
63
|
+
static inline bool withinCurrentStack(uintptr_t value) {
|
64
|
+
// Check that value is not too far from stack pointer of current context
|
65
|
+
void* real_sp;
|
66
|
+
return value - (uintptr_t)&real_sp <= 0xffff;
|
67
|
+
}
|
68
|
+
|
69
|
+
static inline bool isFramePrologueEpilogue(uintptr_t pc) {
|
70
|
+
if (pc & 0xfff) {
|
71
|
+
// Make sure we are not at the page boundary, so that reading [pc - 1] is safe
|
72
|
+
unsigned int opcode = *(unsigned int*)(pc - 1);
|
73
|
+
if (opcode == 0xec834855) {
|
74
|
+
// push rbp
|
75
|
+
// sub rsp, $const
|
76
|
+
return true;
|
77
|
+
} else if (opcode == 0xec8b4855) {
|
78
|
+
// push rbp
|
79
|
+
// mov rbp, rsp
|
80
|
+
return true;
|
81
|
+
}
|
82
|
+
}
|
83
|
+
|
84
|
+
if (*(unsigned char*)pc == 0x5d && *(unsigned short*)(pc + 1) == 0x0585) {
|
85
|
+
// pop rbp
|
86
|
+
// test [polling_page], eax
|
87
|
+
return true;
|
88
|
+
}
|
89
|
+
|
90
|
+
return false;
|
91
|
+
}
|
92
|
+
|
93
|
+
bool StackFrame::pop(bool trust_frame_pointer) {
|
94
|
+
if (!withinCurrentStack(sp())) {
|
95
|
+
return false;
|
96
|
+
}
|
97
|
+
|
98
|
+
if (trust_frame_pointer && withinCurrentStack(fp())) {
|
99
|
+
sp() = fp() + 16;
|
100
|
+
fp() = stackAt(-2);
|
101
|
+
pc() = stackAt(-1);
|
102
|
+
} else if (fp() == sp() || withinCurrentStack(stackAt(0)) || isFramePrologueEpilogue(pc())) {
|
103
|
+
fp() = stackAt(0);
|
104
|
+
pc() = stackAt(1);
|
105
|
+
sp() += 16;
|
106
|
+
} else {
|
107
|
+
pc() = stackAt(0);
|
108
|
+
sp() += 8;
|
109
|
+
}
|
110
|
+
return true;
|
111
|
+
}
|
112
|
+
|
113
|
+
#endif // __x86_64__
|
@@ -0,0 +1,37 @@
|
|
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 _SYMBOLS_H
|
18
|
+
#define _SYMBOLS_H
|
19
|
+
|
20
|
+
#include <set>
|
21
|
+
#include "arch.h"
|
22
|
+
#include "codeCache.h"
|
23
|
+
#include "mutex.h"
|
24
|
+
|
25
|
+
|
26
|
+
class Symbols {
|
27
|
+
private:
|
28
|
+
static Mutex _parse_lock;
|
29
|
+
static std::set<const void*> _parsed_libraries;
|
30
|
+
|
31
|
+
static void parseKernelSymbols(NativeCodeCache* cc);
|
32
|
+
|
33
|
+
public:
|
34
|
+
static void parseLibraries(NativeCodeCache** array, volatile int& count, int size);
|
35
|
+
};
|
36
|
+
|
37
|
+
#endif // _SYMBOLS_H
|
@@ -0,0 +1,354 @@
|
|
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 __linux__
|
18
|
+
|
19
|
+
#include <stdio.h>
|
20
|
+
#include <stdlib.h>
|
21
|
+
#include <string.h>
|
22
|
+
#include <sys/types.h>
|
23
|
+
#include <sys/stat.h>
|
24
|
+
#include <sys/mman.h>
|
25
|
+
#include <elf.h>
|
26
|
+
#include <errno.h>
|
27
|
+
#include <unistd.h>
|
28
|
+
#include <fcntl.h>
|
29
|
+
#include <linux/limits.h>
|
30
|
+
#include <fstream>
|
31
|
+
#include <iostream>
|
32
|
+
#include <string>
|
33
|
+
#include "symbols.h"
|
34
|
+
|
35
|
+
|
36
|
+
class SymbolDesc {
|
37
|
+
private:
|
38
|
+
const char* _addr;
|
39
|
+
const char* _type;
|
40
|
+
|
41
|
+
public:
|
42
|
+
SymbolDesc(const char* s) {
|
43
|
+
_addr = s;
|
44
|
+
_type = strchr(_addr, ' ') + 1;
|
45
|
+
}
|
46
|
+
|
47
|
+
const char* addr() { return (const char*)strtoul(_addr, NULL, 16); }
|
48
|
+
char type() { return _type[0]; }
|
49
|
+
const char* name() { return _type + 2; }
|
50
|
+
};
|
51
|
+
|
52
|
+
class MemoryMapDesc {
|
53
|
+
private:
|
54
|
+
const char* _addr;
|
55
|
+
const char* _end;
|
56
|
+
const char* _perm;
|
57
|
+
const char* _offs;
|
58
|
+
const char* _device;
|
59
|
+
const char* _inode;
|
60
|
+
const char* _file;
|
61
|
+
|
62
|
+
public:
|
63
|
+
MemoryMapDesc(const char* s) {
|
64
|
+
_addr = s;
|
65
|
+
_end = strchr(_addr, '-') + 1;
|
66
|
+
_perm = strchr(_end, ' ') + 1;
|
67
|
+
_offs = strchr(_perm, ' ') + 1;
|
68
|
+
_device = strchr(_offs, ' ') + 1;
|
69
|
+
_inode = strchr(_device, ' ') + 1;
|
70
|
+
_file = strchr(_inode, ' ');
|
71
|
+
|
72
|
+
if (_file != NULL) {
|
73
|
+
while (*_file == ' ') _file++;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
const char* file() { return _file; }
|
78
|
+
bool isExecutable() { return _perm[0] == 'r' && _perm[2] == 'x'; }
|
79
|
+
const char* addr() { return (const char*)strtoul(_addr, NULL, 16); }
|
80
|
+
const char* end() { return (const char*)strtoul(_end, NULL, 16); }
|
81
|
+
unsigned long offs() { return strtoul(_offs, NULL, 16); }
|
82
|
+
unsigned long inode() { return strtoul(_inode, NULL, 10); }
|
83
|
+
};
|
84
|
+
|
85
|
+
|
86
|
+
#ifdef __LP64__
|
87
|
+
const unsigned char ELFCLASS_SUPPORTED = ELFCLASS64;
|
88
|
+
typedef Elf64_Ehdr ElfHeader;
|
89
|
+
typedef Elf64_Shdr ElfSection;
|
90
|
+
typedef Elf64_Nhdr ElfNote;
|
91
|
+
typedef Elf64_Sym ElfSymbol;
|
92
|
+
#else
|
93
|
+
const unsigned char ELFCLASS_SUPPORTED = ELFCLASS32;
|
94
|
+
typedef Elf32_Ehdr ElfHeader;
|
95
|
+
typedef Elf32_Shdr ElfSection;
|
96
|
+
typedef Elf32_Nhdr ElfNote;
|
97
|
+
typedef Elf32_Sym ElfSymbol;
|
98
|
+
#endif // __LP64__
|
99
|
+
|
100
|
+
|
101
|
+
class ElfParser {
|
102
|
+
private:
|
103
|
+
NativeCodeCache* _cc;
|
104
|
+
const char* _base;
|
105
|
+
const char* _file_name;
|
106
|
+
ElfHeader* _header;
|
107
|
+
const char* _sections;
|
108
|
+
|
109
|
+
ElfParser(NativeCodeCache* cc, const char* base, const void* addr, const char* file_name = NULL) {
|
110
|
+
_cc = cc;
|
111
|
+
_base = base;
|
112
|
+
_file_name = file_name;
|
113
|
+
_header = (ElfHeader*)addr;
|
114
|
+
_sections = (const char*)addr + _header->e_shoff;
|
115
|
+
}
|
116
|
+
|
117
|
+
bool valid_header() {
|
118
|
+
unsigned char* ident = _header->e_ident;
|
119
|
+
return ident[0] == 0x7f && ident[1] == 'E' && ident[2] == 'L' && ident[3] == 'F'
|
120
|
+
&& ident[4] == ELFCLASS_SUPPORTED && ident[5] == ELFDATA2LSB && ident[6] == EV_CURRENT
|
121
|
+
&& _header->e_shstrndx != SHN_UNDEF;
|
122
|
+
}
|
123
|
+
|
124
|
+
ElfSection* section(int index) {
|
125
|
+
return (ElfSection*)(_sections + index * _header->e_shentsize);
|
126
|
+
}
|
127
|
+
|
128
|
+
const char* at(ElfSection* section) {
|
129
|
+
return (const char*)_header + section->sh_offset;
|
130
|
+
}
|
131
|
+
|
132
|
+
ElfSection* findSection(uint32_t type, const char* name);
|
133
|
+
|
134
|
+
void loadSymbols(bool use_debug);
|
135
|
+
bool loadSymbolsUsingBuildId();
|
136
|
+
bool loadSymbolsUsingDebugLink();
|
137
|
+
void loadSymbolTable(ElfSection* symtab);
|
138
|
+
|
139
|
+
public:
|
140
|
+
static bool parseFile(NativeCodeCache* cc, const char* base, const char* file_name, bool use_debug);
|
141
|
+
static void parseMem(NativeCodeCache* cc, const char* base);
|
142
|
+
};
|
143
|
+
|
144
|
+
|
145
|
+
ElfSection* ElfParser::findSection(uint32_t type, const char* name) {
|
146
|
+
const char* strtab = at(section(_header->e_shstrndx));
|
147
|
+
|
148
|
+
for (int i = 0; i < _header->e_shnum; i++) {
|
149
|
+
ElfSection* section = this->section(i);
|
150
|
+
if (section->sh_type == type && section->sh_name != 0) {
|
151
|
+
if (strcmp(strtab + section->sh_name, name) == 0) {
|
152
|
+
return section;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
|
157
|
+
return NULL;
|
158
|
+
}
|
159
|
+
|
160
|
+
bool ElfParser::parseFile(NativeCodeCache* cc, const char* base, const char* file_name, bool use_debug) {
|
161
|
+
int fd = open(file_name, O_RDONLY);
|
162
|
+
if (fd == -1) {
|
163
|
+
return false;
|
164
|
+
}
|
165
|
+
|
166
|
+
size_t length = (size_t)lseek64(fd, 0, SEEK_END);
|
167
|
+
void* addr = mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0);
|
168
|
+
close(fd);
|
169
|
+
|
170
|
+
if (addr == MAP_FAILED) {
|
171
|
+
if (strcmp(file_name, "/") == 0) {
|
172
|
+
// https://bugs.launchpad.net/ubuntu/+source/linux/+bug/1843018
|
173
|
+
fprintf(stderr, "Could not parse symbols due to the OS bug\n");
|
174
|
+
} else {
|
175
|
+
fprintf(stderr, "Could not parse symbols from %s: %s\n", file_name, strerror(errno));
|
176
|
+
}
|
177
|
+
} else {
|
178
|
+
ElfParser elf(cc, base, addr, file_name);
|
179
|
+
elf.loadSymbols(use_debug);
|
180
|
+
munmap(addr, length);
|
181
|
+
}
|
182
|
+
return true;
|
183
|
+
}
|
184
|
+
|
185
|
+
void ElfParser::parseMem(NativeCodeCache* cc, const char* base) {
|
186
|
+
ElfParser elf(cc, base, base);
|
187
|
+
elf.loadSymbols(false);
|
188
|
+
}
|
189
|
+
|
190
|
+
void ElfParser::loadSymbols(bool use_debug) {
|
191
|
+
if (!valid_header()) {
|
192
|
+
return;
|
193
|
+
}
|
194
|
+
|
195
|
+
// Look for debug symbols in the original .so
|
196
|
+
ElfSection* section = findSection(SHT_SYMTAB, ".symtab");
|
197
|
+
if (section != NULL) {
|
198
|
+
loadSymbolTable(section);
|
199
|
+
return;
|
200
|
+
}
|
201
|
+
|
202
|
+
// Try to load symbols from an external debuginfo library
|
203
|
+
if (use_debug) {
|
204
|
+
if (loadSymbolsUsingBuildId() || loadSymbolsUsingDebugLink()) {
|
205
|
+
return;
|
206
|
+
}
|
207
|
+
}
|
208
|
+
|
209
|
+
// If everything else fails, load only exported symbols
|
210
|
+
section = findSection(SHT_DYNSYM, ".dynsym");
|
211
|
+
if (section != NULL) {
|
212
|
+
loadSymbolTable(section);
|
213
|
+
}
|
214
|
+
}
|
215
|
+
|
216
|
+
// Load symbols from /usr/lib/debug/.build-id/ab/cdef1234.debug, where abcdef1234 is Build ID
|
217
|
+
bool ElfParser::loadSymbolsUsingBuildId() {
|
218
|
+
ElfSection* section = findSection(SHT_NOTE, ".note.gnu.build-id");
|
219
|
+
if (section == NULL || section->sh_size <= 16) {
|
220
|
+
return false;
|
221
|
+
}
|
222
|
+
|
223
|
+
ElfNote* note = (ElfNote*)at(section);
|
224
|
+
if (note->n_namesz != 4 || note->n_descsz < 2 || note->n_descsz > 64) {
|
225
|
+
return false;
|
226
|
+
}
|
227
|
+
|
228
|
+
const char* build_id = (const char*)note + sizeof(*note) + 4;
|
229
|
+
int build_id_len = note->n_descsz;
|
230
|
+
|
231
|
+
char path[PATH_MAX];
|
232
|
+
char* p = path + sprintf(path, "/usr/lib/debug/.build-id/%02hhx/", build_id[0]);
|
233
|
+
for (int i = 1; i < build_id_len; i++) {
|
234
|
+
p += sprintf(p, "%02hhx", build_id[i]);
|
235
|
+
}
|
236
|
+
strcpy(p, ".debug");
|
237
|
+
|
238
|
+
return parseFile(_cc, _base, path, false);
|
239
|
+
}
|
240
|
+
|
241
|
+
// Look for debuginfo file specified in .gnu_debuglink section
|
242
|
+
bool ElfParser::loadSymbolsUsingDebugLink() {
|
243
|
+
ElfSection* section = findSection(SHT_PROGBITS, ".gnu_debuglink");
|
244
|
+
if (section == NULL || section->sh_size <= 4) {
|
245
|
+
return false;
|
246
|
+
}
|
247
|
+
|
248
|
+
const char* basename = strrchr(_file_name, '/');
|
249
|
+
if (basename == NULL) {
|
250
|
+
return false;
|
251
|
+
}
|
252
|
+
|
253
|
+
char* dirname = strndup(_file_name, basename - _file_name);
|
254
|
+
if (dirname == NULL) {
|
255
|
+
return false;
|
256
|
+
}
|
257
|
+
|
258
|
+
const char* debuglink = at(section);
|
259
|
+
char path[PATH_MAX];
|
260
|
+
bool result = false;
|
261
|
+
|
262
|
+
// 1. /path/to/libjvm.so.debug
|
263
|
+
if (strcmp(debuglink, basename + 1) != 0 &&
|
264
|
+
snprintf(path, PATH_MAX, "%s/%s", dirname, debuglink) < PATH_MAX) {
|
265
|
+
result = parseFile(_cc, _base, path, false);
|
266
|
+
}
|
267
|
+
|
268
|
+
// 2. /path/to/.debug/libjvm.so.debug
|
269
|
+
if (!result && snprintf(path, PATH_MAX, "%s/.debug/%s", dirname, debuglink) < PATH_MAX) {
|
270
|
+
result = parseFile(_cc, _base, path, false);
|
271
|
+
}
|
272
|
+
|
273
|
+
// 3. /usr/lib/debug/path/to/libjvm.so.debug
|
274
|
+
if (!result && snprintf(path, PATH_MAX, "/usr/lib/debug%s/%s", dirname, debuglink) < PATH_MAX) {
|
275
|
+
result = parseFile(_cc, _base, path, false);
|
276
|
+
}
|
277
|
+
|
278
|
+
free(dirname);
|
279
|
+
return result;
|
280
|
+
}
|
281
|
+
|
282
|
+
void ElfParser::loadSymbolTable(ElfSection* symtab) {
|
283
|
+
ElfSection* strtab = section(symtab->sh_link);
|
284
|
+
const char* strings = at(strtab);
|
285
|
+
|
286
|
+
const char* symbols = at(symtab);
|
287
|
+
const char* symbols_end = symbols + symtab->sh_size;
|
288
|
+
for (; symbols < symbols_end; symbols += symtab->sh_entsize) {
|
289
|
+
ElfSymbol* sym = (ElfSymbol*)symbols;
|
290
|
+
if (sym->st_name != 0 && sym->st_value != 0) {
|
291
|
+
_cc->add(_base + sym->st_value, (int)sym->st_size, strings + sym->st_name);
|
292
|
+
}
|
293
|
+
}
|
294
|
+
}
|
295
|
+
|
296
|
+
|
297
|
+
Mutex Symbols::_parse_lock;
|
298
|
+
std::set<const void*> Symbols::_parsed_libraries;
|
299
|
+
|
300
|
+
void Symbols::parseKernelSymbols(NativeCodeCache* cc) {
|
301
|
+
std::ifstream maps("/proc/kallsyms");
|
302
|
+
std::string str;
|
303
|
+
|
304
|
+
while (std::getline(maps, str)) {
|
305
|
+
str += "_[k]";
|
306
|
+
SymbolDesc symbol(str.c_str());
|
307
|
+
char type = symbol.type();
|
308
|
+
if (type == 'T' || type == 't' || type == 'W' || type == 'w') {
|
309
|
+
const char* addr = symbol.addr();
|
310
|
+
if (addr != NULL) {
|
311
|
+
cc->add(addr, 0, symbol.name());
|
312
|
+
}
|
313
|
+
}
|
314
|
+
}
|
315
|
+
}
|
316
|
+
|
317
|
+
void Symbols::parseLibraries(NativeCodeCache** array, volatile int& count, int size) {
|
318
|
+
MutexLocker ml(_parse_lock);
|
319
|
+
|
320
|
+
if (count == 0) {
|
321
|
+
NativeCodeCache* cc = new NativeCodeCache("[kernel]");
|
322
|
+
parseKernelSymbols(cc);
|
323
|
+
cc->sort();
|
324
|
+
array[count] = cc;
|
325
|
+
atomicInc(count);
|
326
|
+
}
|
327
|
+
|
328
|
+
std::ifstream maps("/proc/self/maps");
|
329
|
+
std::string str;
|
330
|
+
|
331
|
+
while (count < size && std::getline(maps, str)) {
|
332
|
+
MemoryMapDesc map(str.c_str());
|
333
|
+
if (map.isExecutable() && map.file() != NULL && map.file()[0] != 0) {
|
334
|
+
const char* image_base = map.addr();
|
335
|
+
if (!_parsed_libraries.insert(image_base).second) {
|
336
|
+
continue; // the library was already parsed
|
337
|
+
}
|
338
|
+
|
339
|
+
NativeCodeCache* cc = new NativeCodeCache(map.file(), image_base, map.end());
|
340
|
+
|
341
|
+
if (map.inode() != 0) {
|
342
|
+
ElfParser::parseFile(cc, image_base - map.offs(), map.file(), true);
|
343
|
+
} else if (strcmp(map.file(), "[vdso]") == 0) {
|
344
|
+
ElfParser::parseMem(cc, image_base);
|
345
|
+
}
|
346
|
+
|
347
|
+
cc->sort();
|
348
|
+
array[count] = cc;
|
349
|
+
atomicInc(count);
|
350
|
+
}
|
351
|
+
}
|
352
|
+
}
|
353
|
+
|
354
|
+
#endif // __linux__
|