mirror of
https://github.com/async-profiler/async-profiler.git
synced 2026-04-28 10:53:49 +00:00
Compare commits
13 Commits
8aab346c3b
...
v1.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50fccae0fc | ||
|
|
f9afcaf5e7 | ||
|
|
a0c301859f | ||
|
|
56d736b65c | ||
|
|
e76219683d | ||
|
|
9341b201b8 | ||
|
|
c43912b903 | ||
|
|
7ed21187e9 | ||
|
|
09b5ec0b63 | ||
|
|
a12c291423 | ||
|
|
32997bb645 | ||
|
|
9b11014b25 | ||
|
|
516d717d49 |
28
CHANGELOG.md
28
CHANGELOG.md
@@ -1,5 +1,33 @@
|
||||
# Changelog
|
||||
|
||||
## [1.8.8] - 2022-09-10
|
||||
|
||||
### Bug fixes
|
||||
- Could not find NativeLibrary_load on JDK 11.0.15
|
||||
|
||||
## [1.8.7] - 2021-10-01
|
||||
|
||||
### Bug fixes
|
||||
- Workaround for JDK-8173361
|
||||
- Backported fix for "Accept timed out" exception
|
||||
|
||||
## [1.8.6] - 2021-07-08
|
||||
|
||||
### Improvements
|
||||
- `log=none` option to suppress warnings about missing JVM symbols
|
||||
- Sign macOS binaries
|
||||
|
||||
### Bug fixes
|
||||
- Workaround for JDK-8212160
|
||||
|
||||
## [1.8.5] - 2021-03-22
|
||||
|
||||
### Improvements
|
||||
- Backported JFR to FlameGraph converter
|
||||
|
||||
### Bug fixes
|
||||
- Stricter `safemode` to avoid stack walking in suspicious cases
|
||||
|
||||
## [1.8.4] - 2021-02-24
|
||||
|
||||
### Improvements
|
||||
|
||||
6
Makefile
6
Makefile
@@ -1,4 +1,4 @@
|
||||
PROFILER_VERSION=1.8.4
|
||||
PROFILER_VERSION=1.8.8
|
||||
JATTACH_VERSION=1.5
|
||||
JAVAC_RELEASE_VERSION=6
|
||||
|
||||
@@ -33,11 +33,13 @@ ifeq ($(OS), Darwin)
|
||||
CXXFLAGS += -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE
|
||||
INCLUDES += -I$(JAVA_HOME)/include/darwin
|
||||
SOEXT=dylib
|
||||
CODESIGN=codesign -s "Developer ID" -o runtime --timestamp -v $(1)
|
||||
OS_TAG=macos
|
||||
else
|
||||
LIBS += -lrt
|
||||
INCLUDES += -I$(JAVA_HOME)/include/linux
|
||||
SOEXT=so
|
||||
CODESIGN=
|
||||
ifeq ($(findstring musl,$(shell ldd /bin/ls)),musl)
|
||||
OS_TAG=linux-musl
|
||||
else
|
||||
@@ -70,6 +72,8 @@ release: build $(PACKAGE_NAME).tar.gz
|
||||
$(PACKAGE_NAME).tar.gz: build/$(LIB_PROFILER) build/$(JATTACH) \
|
||||
build/$(API_JAR) build/$(CONVERTER_JAR) \
|
||||
profiler.sh LICENSE NOTICE *.md
|
||||
$(call CODESIGN,build/$(LIB_PROFILER))
|
||||
$(call CODESIGN,build/$(JATTACH))
|
||||
mkdir -p $(PACKAGE_DIR)
|
||||
cp -RP build profiler.sh LICENSE NOTICE *.md $(PACKAGE_DIR)
|
||||
chmod -R 755 $(PACKAGE_DIR)
|
||||
|
||||
23
README.md
23
README.md
@@ -14,21 +14,16 @@ async-profiler can trace the following kinds of events:
|
||||
|
||||
## Download
|
||||
|
||||
Latest release (1.8.4):
|
||||
Latest v1 release (1.8.8):
|
||||
|
||||
- Linux x64 (glibc): [async-profiler-1.8.4-linux-x64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.4/async-profiler-1.8.4-linux-x64.tar.gz)
|
||||
- Linux x86 (glibc): [async-profiler-1.8.4-linux-x86.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.4/async-profiler-1.8.4-linux-x86.tar.gz)
|
||||
- Linux x64 (musl): [async-profiler-1.8.4-linux-musl-x64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.4/async-profiler-1.8.4-linux-musl-x64.tar.gz)
|
||||
- Linux ARM: [async-profiler-1.8.4-linux-arm.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.4/async-profiler-1.8.4-linux-arm.tar.gz)
|
||||
- Linux AArch64: [async-profiler-1.8.4-linux-aarch64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.4/async-profiler-1.8.4-linux-aarch64.tar.gz)
|
||||
- macOS x64: [async-profiler-1.8.4-macos-x64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.4/async-profiler-1.8.4-macos-x64.tar.gz)
|
||||
- Linux x64 (glibc): [async-profiler-1.8.8-linux-x64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.8/async-profiler-1.8.8-linux-x64.tar.gz)
|
||||
- Linux x86 (glibc): [async-profiler-1.8.8-linux-x86.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.8/async-profiler-1.8.8-linux-x86.tar.gz)
|
||||
- Linux x64 (musl): [async-profiler-1.8.8-linux-musl-x64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.8/async-profiler-1.8.8-linux-musl-x64.tar.gz)
|
||||
- Linux ARM: [async-profiler-1.8.8-linux-arm.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.8/async-profiler-1.8.8-linux-arm.tar.gz)
|
||||
- Linux AArch64: [async-profiler-1.8.8-linux-aarch64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.8/async-profiler-1.8.8-linux-aarch64.tar.gz)
|
||||
- macOS x64: [async-profiler-1.8.8-macos-x64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.8/async-profiler-1.8.8-macos-x64.tar.gz)
|
||||
|
||||
[Early access](https://github.com/jvm-profiling-tools/async-profiler/releases/tag/v2.0-b1) (2.0-b1):
|
||||
|
||||
- Linux x64 (glibc): [async-profiler-2.0-b1-linux-x64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v2.0-b1/async-profiler-2.0-b1-linux-x64.tar.gz)
|
||||
- macOS x64: [async-profiler-2.0-b1-macos-x64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v2.0-b1/async-profiler-2.0-b1-macos-x64.tar.gz)
|
||||
|
||||
[Previous releases](https://github.com/jvm-profiling-tools/async-profiler/releases)
|
||||
[Other releases](https://github.com/jvm-profiling-tools/async-profiler/releases)
|
||||
|
||||
Note: async-profiler also comes bundled with IntelliJ IDEA Ultimate 2018.3 and later.
|
||||
For more information refer to [IntelliJ IDEA documentation](https://www.jetbrains.com/help/idea/cpu-profiler.html).
|
||||
@@ -241,7 +236,7 @@ $ java -agentpath:/path/to/libasyncProfiler.so=start,file=profile.svg ...
|
||||
|
||||
Agent library is configured through the JVMTI argument interface.
|
||||
The format of the arguments string is described
|
||||
[in the source code](https://github.com/jvm-profiling-tools/async-profiler/blob/v1.8.4/src/arguments.cpp#L49).
|
||||
[in the source code](https://github.com/jvm-profiling-tools/async-profiler/blob/v1.8.8/src/arguments.cpp#L49).
|
||||
The `profiler.sh` script actually converts command line arguments to that format.
|
||||
|
||||
For instance, `-e alloc` is converted to `event=alloc`, `-f profile.svg`
|
||||
|
||||
2
pom.xml
2
pom.xml
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>tools.profiler</groupId>
|
||||
<artifactId>async-profiler</artifactId>
|
||||
<version>1.8.4</version>
|
||||
<version>1.8.8</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>async-profiler</name>
|
||||
|
||||
@@ -201,6 +201,9 @@ Error Arguments::parse(const char* args) {
|
||||
CASE("threads")
|
||||
_threads = true;
|
||||
|
||||
CASE("log")
|
||||
_log = value == NULL || (strcmp(value, "none") != 0 && strcmp(value, "/dev/null") != 0);
|
||||
|
||||
CASE("allkernel")
|
||||
_ring = RING_KERNEL;
|
||||
|
||||
|
||||
@@ -123,6 +123,7 @@ class Arguments {
|
||||
int _include;
|
||||
int _exclude;
|
||||
bool _threads;
|
||||
bool _log;
|
||||
int _style;
|
||||
CStack _cstack;
|
||||
Output _output;
|
||||
@@ -150,6 +151,7 @@ class Arguments {
|
||||
_include(0),
|
||||
_exclude(0),
|
||||
_threads(false),
|
||||
_log(true),
|
||||
_style(0),
|
||||
_cstack(CSTACK_DEFAULT),
|
||||
_output(OUTPUT_NONE),
|
||||
|
||||
@@ -14,8 +14,10 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.PrintStream;
|
||||
@@ -69,32 +71,36 @@ public class FlameGraph {
|
||||
|
||||
String[] trace = line.substring(0, space).split(";");
|
||||
long ticks = Long.parseLong(line.substring(space + 1));
|
||||
|
||||
depth = Math.max(depth, trace.length);
|
||||
|
||||
Frame frame = root;
|
||||
if (reverse) {
|
||||
for (int i = trace.length; --i >= skip; ) {
|
||||
frame.total += ticks;
|
||||
frame = frame.child(trace[i]);
|
||||
}
|
||||
} else {
|
||||
for (int i = skip; i < trace.length; i++) {
|
||||
frame.total += ticks;
|
||||
frame = frame.child(trace[i]);
|
||||
}
|
||||
}
|
||||
frame.total += ticks;
|
||||
frame.self += ticks;
|
||||
addSample(trace, ticks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addSample(String[] trace, long ticks) {
|
||||
Frame frame = root;
|
||||
if (reverse) {
|
||||
for (int i = trace.length; --i >= skip; ) {
|
||||
frame.total += ticks;
|
||||
frame = frame.child(trace[i]);
|
||||
}
|
||||
} else {
|
||||
for (int i = skip; i < trace.length; i++) {
|
||||
frame.total += ticks;
|
||||
frame = frame.child(trace[i]);
|
||||
}
|
||||
}
|
||||
frame.total += ticks;
|
||||
frame.self += ticks;
|
||||
|
||||
depth = Math.max(depth, trace.length);
|
||||
}
|
||||
|
||||
public void dump() throws IOException {
|
||||
if (output == null) {
|
||||
dump(System.out);
|
||||
} else {
|
||||
try (PrintStream out = new PrintStream(output, "UTF-8")) {
|
||||
try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(output), 32768);
|
||||
PrintStream out = new PrintStream(bos, false, "UTF-8")) {
|
||||
dump(out);
|
||||
}
|
||||
}
|
||||
@@ -137,7 +143,7 @@ public class FlameGraph {
|
||||
private void printFrame(PrintStream out, String title, Frame frame, int level, long x) {
|
||||
int type = frameType(title);
|
||||
title = stripSuffix(title);
|
||||
if (title.indexOf('"') >= 0) {
|
||||
if (title.indexOf('\'') >= 0) {
|
||||
title = title.replace("'", "\\'");
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ public class Main {
|
||||
System.out.println();
|
||||
System.out.println("Available converters:");
|
||||
System.out.println(" FlameGraph input.collapsed output.html");
|
||||
System.out.println(" jfr2flame input.jfr output.html");
|
||||
System.out.println(" jfr2nflx input.jfr output.nflx");
|
||||
}
|
||||
}
|
||||
|
||||
89
src/converter/jfr2flame.java
Normal file
89
src/converter/jfr2flame.java
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2021 Andrei Pangin
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import one.jfr.ClassRef;
|
||||
import one.jfr.Frame;
|
||||
import one.jfr.JfrReader;
|
||||
import one.jfr.MethodRef;
|
||||
import one.jfr.StackTrace;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Converts .jfr output produced by async-profiler to HTML Flame Graph.
|
||||
*/
|
||||
public class jfr2flame {
|
||||
|
||||
private static final int FRAME_KERNEL = 6;
|
||||
|
||||
private final JfrReader jfr;
|
||||
private final Map<Long, String> methodNames = new HashMap<>();
|
||||
|
||||
public jfr2flame(JfrReader jfr) {
|
||||
this.jfr = jfr;
|
||||
}
|
||||
|
||||
public void convert(final FlameGraph fg) {
|
||||
for (StackTrace stackTrace : jfr.stackTraces.values()) {
|
||||
Frame[] frames = stackTrace.frames;
|
||||
String[] trace = new String[frames.length];
|
||||
for (int i = 0; i < frames.length; i++) {
|
||||
trace[trace.length - 1 - i] = getMethodName(frames[i].method, frames[i].type);
|
||||
}
|
||||
fg.addSample(trace, stackTrace.samples);
|
||||
}
|
||||
}
|
||||
|
||||
private String getMethodName(long methodId, int type) {
|
||||
String result = methodNames.get(methodId);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
|
||||
MethodRef method = jfr.methods.get(methodId);
|
||||
ClassRef cls = jfr.classes.get(method.cls);
|
||||
byte[] className = jfr.symbols.get(cls.name);
|
||||
byte[] methodName = jfr.symbols.get(method.name);
|
||||
|
||||
if (className == null || className.length == 0) {
|
||||
String methodStr = new String(methodName, StandardCharsets.UTF_8);
|
||||
result = type == FRAME_KERNEL ? methodStr + "_[k]" : methodStr;
|
||||
} else {
|
||||
String classStr = new String(className, StandardCharsets.UTF_8);
|
||||
String methodStr = new String(methodName, StandardCharsets.UTF_8);
|
||||
result = classStr + '.' + methodStr + "_[j]";
|
||||
}
|
||||
|
||||
methodNames.put(methodId, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
FlameGraph fg = new FlameGraph(args);
|
||||
if (fg.input == null) {
|
||||
System.out.println("Usage: java " + jfr2flame.class.getName() + " [options] input.jfr [output.html]");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
try (JfrReader jfr = new JfrReader(fg.input)) {
|
||||
new jfr2flame(jfr).convert(fg);
|
||||
}
|
||||
|
||||
fg.dump();
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import one.jfr.Frame;
|
||||
import one.jfr.JfrReader;
|
||||
import one.jfr.MethodRef;
|
||||
import one.jfr.Sample;
|
||||
import one.jfr.StackTrace;
|
||||
import one.proto.Proto;
|
||||
|
||||
import java.io.File;
|
||||
@@ -60,10 +61,10 @@ public class jfr2nflx {
|
||||
Proto nodes = new Proto(10000);
|
||||
Proto node = new Proto(10000);
|
||||
|
||||
for (Map.Entry<Integer, Frame[]> entry : jfr.stackTraces.entrySet()) {
|
||||
for (Map.Entry<Integer, StackTrace> entry : jfr.stackTraces.entrySet()) {
|
||||
profile.field(5, nodes
|
||||
.field(1, entry.getKey())
|
||||
.field(2, packNode(node, entry.getValue())));
|
||||
.field(2, packNode(node, entry.getValue().frames)));
|
||||
nodes.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public class JfrReader implements Closeable {
|
||||
|
||||
public final long startNanos;
|
||||
public final long stopNanos;
|
||||
public final Map<Integer, Frame[]> stackTraces = new HashMap<>();
|
||||
public final Map<Integer, StackTrace> stackTraces = new HashMap<>();
|
||||
public final Map<Long, MethodRef> methods = new HashMap<>();
|
||||
public final Map<Long, ClassRef> classes = new HashMap<>();
|
||||
public final Map<Long, byte[]> symbols = new HashMap<>();
|
||||
@@ -82,11 +82,7 @@ public class JfrReader implements Closeable {
|
||||
int size = buf.getInt();
|
||||
int type = buf.getInt();
|
||||
if (type == EVENT_EXECUTION_SAMPLE) {
|
||||
long time = buf.getLong();
|
||||
int tid = buf.getInt();
|
||||
int stackTraceId = (int) buf.getLong();
|
||||
short threadState = buf.getShort();
|
||||
samples.add(new Sample(time, tid, stackTraceId, threadState));
|
||||
readExecutionSample();
|
||||
} else {
|
||||
buf.position(buf.position() + size - 8);
|
||||
}
|
||||
@@ -95,6 +91,19 @@ public class JfrReader implements Closeable {
|
||||
Collections.sort(samples);
|
||||
}
|
||||
|
||||
private void readExecutionSample() {
|
||||
long time = buf.getLong();
|
||||
int tid = buf.getInt();
|
||||
int stackTraceId = (int) buf.getLong();
|
||||
short threadState = buf.getShort();
|
||||
samples.add(new Sample(time, tid, stackTraceId, threadState));
|
||||
|
||||
StackTrace stackTrace = stackTraces.get(stackTraceId);
|
||||
if (stackTrace != null) {
|
||||
stackTrace.samples++;
|
||||
}
|
||||
}
|
||||
|
||||
private void readCheckpoint(int checkpointOffset) {
|
||||
buf.position(checkpointOffset + 24);
|
||||
|
||||
@@ -135,7 +144,7 @@ public class JfrReader implements Closeable {
|
||||
byte type = buf.get();
|
||||
frames[j] = new Frame(method, type);
|
||||
}
|
||||
stackTraces.put(id, frames);
|
||||
stackTraces.put(id, new StackTrace(frames));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
26
src/converter/one/jfr/StackTrace.java
Normal file
26
src/converter/one/jfr/StackTrace.java
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2021 Andrei Pangin
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package one.jfr;
|
||||
|
||||
public class StackTrace {
|
||||
public final Frame[] frames;
|
||||
public long samples;
|
||||
|
||||
public StackTrace(Frame[] frames) {
|
||||
this.frames = frames;
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "engine.h"
|
||||
#include "stackFrame.h"
|
||||
#include "vmStructs.h"
|
||||
|
||||
|
||||
Error Engine::check(Arguments& args) {
|
||||
@@ -26,8 +27,7 @@ CStack Engine::cstack() {
|
||||
return CSTACK_FP;
|
||||
}
|
||||
|
||||
int Engine::getNativeTrace(void* ucontext, int tid, const void** callchain, int max_depth,
|
||||
CodeCache* java_methods, CodeCache* runtime_stubs) {
|
||||
int Engine::getNativeTrace(void* ucontext, int tid, const void** callchain, int max_depth) {
|
||||
const void* pc;
|
||||
uintptr_t fp;
|
||||
uintptr_t prev_fp = (uintptr_t)&fp;
|
||||
@@ -47,7 +47,7 @@ int Engine::getNativeTrace(void* ucontext, int tid, const void** callchain, int
|
||||
|
||||
// Walk until the bottom of the stack or until the first Java frame
|
||||
while (depth < max_depth && pc >= valid_pc) {
|
||||
if (java_methods->contains(pc) || runtime_stubs->contains(pc)) {
|
||||
if (CodeHeap::contains(pc)) {
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#define _ENGINE_H
|
||||
|
||||
#include "arguments.h"
|
||||
#include "codeCache.h"
|
||||
|
||||
|
||||
class Engine {
|
||||
@@ -34,8 +33,7 @@ class Engine {
|
||||
virtual void onThreadEnd(int tid) {}
|
||||
|
||||
virtual CStack cstack();
|
||||
virtual int getNativeTrace(void* ucontext, int tid, const void** callchain, int max_depth,
|
||||
CodeCache* java_methods, CodeCache* runtime_stubs);
|
||||
virtual int getNativeTrace(void* ucontext, int tid, const void** callchain, int max_depth);
|
||||
};
|
||||
|
||||
#endif // _ENGINE_H
|
||||
|
||||
@@ -57,8 +57,7 @@ class PerfEvents : public Engine {
|
||||
destroyForThread(tid);
|
||||
}
|
||||
|
||||
int getNativeTrace(void* ucontext, int tid, const void** callchain, int max_depth,
|
||||
CodeCache* java_methods, CodeCache* runtime_stubs);
|
||||
int getNativeTrace(void* ucontext, int tid, const void** callchain, int max_depth);
|
||||
|
||||
static bool supported();
|
||||
static const char* getEventName(int event_id);
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
#include "spinLock.h"
|
||||
#include "stackFrame.h"
|
||||
#include "symbols.h"
|
||||
#include "vmStructs.h"
|
||||
|
||||
|
||||
// Ancient fcntl.h does not define F_SETOWN_EX constants and structures
|
||||
@@ -535,9 +536,11 @@ Error PerfEvents::start(Arguments& args) {
|
||||
|
||||
_ring = args._ring;
|
||||
if (_ring != RING_USER && !Symbols::haveKernelSymbols()) {
|
||||
fprintf(stderr, "WARNING: Kernel symbols are unavailable due to restrictions. Try\n"
|
||||
" echo 0 > /proc/sys/kernel/kptr_restrict\n"
|
||||
" echo 1 > /proc/sys/kernel/perf_event_paranoid\n");
|
||||
if (args._log) {
|
||||
fprintf(stderr, "WARNING: Kernel symbols are unavailable due to restrictions. Try\n"
|
||||
" echo 0 > /proc/sys/kernel/kptr_restrict\n"
|
||||
" echo 1 > /proc/sys/kernel/perf_event_paranoid\n");
|
||||
}
|
||||
_ring = RING_USER;
|
||||
}
|
||||
_cstack = args._cstack;
|
||||
@@ -576,8 +579,7 @@ void PerfEvents::stop() {
|
||||
}
|
||||
}
|
||||
|
||||
int PerfEvents::getNativeTrace(void* ucontext, int tid, const void** callchain, int max_depth,
|
||||
CodeCache* java_methods, CodeCache* runtime_stubs) {
|
||||
int PerfEvents::getNativeTrace(void* ucontext, int tid, const void** callchain, int max_depth) {
|
||||
PerfEvent* event = &_events[tid];
|
||||
if (!event->tryLock()) {
|
||||
return 0; // the event is being destroyed
|
||||
@@ -601,7 +603,7 @@ int PerfEvents::getNativeTrace(void* ucontext, int tid, const void** callchain,
|
||||
u64 ip = ring.next();
|
||||
if (ip < PERF_CONTEXT_MAX) {
|
||||
const void* iptr = (const void*)ip;
|
||||
if (java_methods->contains(iptr) || runtime_stubs->contains(iptr) || depth >= max_depth) {
|
||||
if (CodeHeap::contains(iptr) || depth >= max_depth) {
|
||||
// Stop at the first Java frame
|
||||
goto stack_complete;
|
||||
}
|
||||
@@ -614,7 +616,7 @@ int PerfEvents::getNativeTrace(void* ucontext, int tid, const void** callchain,
|
||||
|
||||
// Last userspace PC is stored right after branch stack
|
||||
const void* pc = (const void*)ring.peek(bnr * 3 + 2);
|
||||
if (java_methods->contains(pc) || runtime_stubs->contains(pc) || depth >= max_depth) {
|
||||
if (CodeHeap::contains(pc) || depth >= max_depth) {
|
||||
goto stack_complete;
|
||||
}
|
||||
callchain[depth++] = pc;
|
||||
@@ -624,12 +626,12 @@ int PerfEvents::getNativeTrace(void* ucontext, int tid, const void** callchain,
|
||||
const void* to = (const void*)ring.next();
|
||||
ring.next();
|
||||
|
||||
if (java_methods->contains(to) || runtime_stubs->contains(to) || depth >= max_depth) {
|
||||
if (CodeHeap::contains(to) || depth >= max_depth) {
|
||||
goto stack_complete;
|
||||
}
|
||||
callchain[depth++] = to;
|
||||
|
||||
if (java_methods->contains(from) || runtime_stubs->contains(from) || depth >= max_depth) {
|
||||
if (CodeHeap::contains(from) || depth >= max_depth) {
|
||||
goto stack_complete;
|
||||
}
|
||||
callchain[depth++] = from;
|
||||
|
||||
@@ -46,8 +46,7 @@ Error PerfEvents::start(Arguments& args) {
|
||||
void PerfEvents::stop() {
|
||||
}
|
||||
|
||||
int PerfEvents::getNativeTrace(void* ucontext, int tid, const void** callchain, int max_depth,
|
||||
CodeCache* java_methods, CodeCache* runtime_stubs) {
|
||||
int PerfEvents::getNativeTrace(void* ucontext, int tid, const void** callchain, int max_depth) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
105
src/profiler.cpp
105
src/profiler.cpp
@@ -166,6 +166,8 @@ void Profiler::addJavaMethod(const void* address, int length, jmethodID method)
|
||||
_jit_lock.lock();
|
||||
_java_methods.add(address, length, method, true);
|
||||
_jit_lock.unlock();
|
||||
|
||||
CodeHeap::updateBounds(address, (const char*)address + length);
|
||||
}
|
||||
|
||||
void Profiler::removeJavaMethod(const void* address, jmethodID method) {
|
||||
@@ -178,6 +180,8 @@ void Profiler::addRuntimeStub(const void* address, int length, const char* name)
|
||||
_stubs_lock.lock();
|
||||
_runtime_stubs.add(address, length, name, true);
|
||||
_stubs_lock.unlock();
|
||||
|
||||
CodeHeap::updateBounds(address, (const char*)address + length);
|
||||
}
|
||||
|
||||
void Profiler::onThreadStart(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread) {
|
||||
@@ -262,53 +266,26 @@ const char* Profiler::findNativeMethod(const void* address) {
|
||||
return lib == NULL ? NULL : lib->binarySearch(address);
|
||||
}
|
||||
|
||||
// When thread is in Java state, it should have Java frame somewhere on the top of the stack
|
||||
bool Profiler::checkWalkable(void* ucontext) {
|
||||
// When a thread in Java state has a Java frame on the top of the stack,
|
||||
// it is known to be safe for stack walking
|
||||
bool Profiler::inJavaCode(void* ucontext) {
|
||||
if (ucontext == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
StackFrame frame(ucontext);
|
||||
const void* pc = (const void*)frame.pc();
|
||||
uintptr_t fp = frame.fp();
|
||||
uintptr_t prev_fp = (uintptr_t)&fp;
|
||||
uintptr_t bottom = prev_fp + 0x40000;
|
||||
|
||||
const void* const valid_pc = (const void*)0x1000;
|
||||
|
||||
// Walk until the bottom of the stack or until the first Java frame
|
||||
for (int depth = 0; depth < 5 && pc >= valid_pc; depth++) {
|
||||
if (_runtime_stubs.contains(pc)) {
|
||||
_stubs_lock.lockShared();
|
||||
jmethodID method = _runtime_stubs.find(pc);
|
||||
_stubs_lock.unlockShared();
|
||||
return method == NULL || strcmp((const char*)method, "call_stub") != 0;
|
||||
} else if (_java_methods.contains(pc)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if the next frame is below on the current stack
|
||||
if (fp <= prev_fp || fp >= bottom) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Frame pointer must be word aligned
|
||||
if ((fp & (sizeof(uintptr_t) - 1)) != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
prev_fp = fp;
|
||||
pc = ((const void**)fp)[1];
|
||||
fp = ((uintptr_t*)fp)[0];
|
||||
const void* pc = (const void*)StackFrame(ucontext).pc();
|
||||
if (_runtime_stubs.contains(pc)) {
|
||||
_stubs_lock.lockShared();
|
||||
jmethodID method = _runtime_stubs.find(pc);
|
||||
_stubs_lock.unlockShared();
|
||||
return method == NULL || strcmp((const char*)method, "call_stub") != 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
return CodeHeap::contains(pc);
|
||||
}
|
||||
|
||||
int Profiler::getNativeTrace(void* ucontext, ASGCT_CallFrame* frames, int tid) {
|
||||
const void* native_callchain[MAX_NATIVE_FRAMES];
|
||||
int native_frames = _engine->getNativeTrace(ucontext, tid, native_callchain, MAX_NATIVE_FRAMES,
|
||||
&_java_methods, &_runtime_stubs);
|
||||
int native_frames = _engine->getNativeTrace(ucontext, tid, native_callchain, MAX_NATIVE_FRAMES);
|
||||
|
||||
int depth = 0;
|
||||
jmethodID prev_method = NULL;
|
||||
@@ -336,7 +313,7 @@ int Profiler::getJavaTraceAsync(void* ucontext, ASGCT_CallFrame* frames, int max
|
||||
|
||||
if (_safe_mode & CHECK_STATE) {
|
||||
int state = vm_thread->state();
|
||||
if ((state == 8 || state == 9) && !checkWalkable(ucontext)) {
|
||||
if ((state == 8 || state == 9) && !inJavaCode(ucontext)) {
|
||||
// Thread is in Java state, but does not have a valid Java frame on top of the stack
|
||||
return 0;
|
||||
}
|
||||
@@ -575,7 +552,7 @@ void Profiler::recordSample(void* ucontext, u64 counter, jint event_type, jmetho
|
||||
|
||||
if (event_type == 0) {
|
||||
// Need to reset PerfEvents ring buffer, even though we discard the collected trace
|
||||
_engine->getNativeTrace(ucontext, tid, NULL, 0, &_java_methods, &_runtime_stubs);
|
||||
_engine->getNativeTrace(ucontext, tid, NULL, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -619,16 +596,16 @@ void Profiler::recordSample(void* ucontext, u64 counter, jint event_type, jmetho
|
||||
_locks[lock_index].unlock();
|
||||
}
|
||||
|
||||
jboolean JNICALL Profiler::NativeLibraryLoadTrap(JNIEnv* env, jobject self, jstring name, jboolean builtin) {
|
||||
jboolean result = ((jboolean JNICALL (*)(JNIEnv*, jobject, jstring, jboolean))
|
||||
_instance._original_NativeLibrary_load)(env, self, name, builtin);
|
||||
jboolean JNICALL Profiler::NativeLibraryLoadTrap(JNIEnv* env, jobject self, jstring name, jboolean builtin, jboolean throws) {
|
||||
jboolean result = ((jboolean JNICALL (*)(JNIEnv*, jobject, jstring, jboolean, jboolean))
|
||||
_instance._original_NativeLibrary_load)(env, self, name, builtin, throws);
|
||||
_instance.updateSymbols(false);
|
||||
return result;
|
||||
}
|
||||
|
||||
jboolean JNICALL Profiler::NativeLibrariesLoadTrap(JNIEnv* env, jobject self, jobject lib, jstring name, jboolean builtin, jboolean jni) {
|
||||
jboolean result = ((jboolean JNICALL (*)(JNIEnv*, jobject, jobject, jstring, jboolean, jboolean))
|
||||
_instance._original_NativeLibrary_load)(env, self, lib, name, builtin, jni);
|
||||
jboolean JNICALL Profiler::NativeLibrariesLoadTrap(JNIEnv* env, jobject self, jobject lib, jstring name, jboolean builtin, jboolean jni, jboolean throws) {
|
||||
jboolean result = ((jboolean JNICALL (*)(JNIEnv*, jobject, jobject, jstring, jboolean, jboolean, jboolean))
|
||||
_instance._original_NativeLibrary_load)(env, self, lib, name, builtin, jni, throws);
|
||||
_instance.updateSymbols(false);
|
||||
return result;
|
||||
}
|
||||
@@ -639,6 +616,8 @@ void JNICALL Profiler::ThreadSetNativeNameTrap(JNIEnv* env, jobject self, jstrin
|
||||
}
|
||||
|
||||
void Profiler::bindNativeLibraryLoad(JNIEnv* env, bool enable) {
|
||||
static unsigned int warning_reported = 0;
|
||||
|
||||
jclass NativeLibrary;
|
||||
|
||||
if (_original_NativeLibrary_load == NULL) {
|
||||
@@ -648,15 +627,25 @@ void Profiler::bindNativeLibraryLoad(JNIEnv* env, bool enable) {
|
||||
strcpy(original_jni_name, "Java_jdk_internal_loader_NativeLibraries_");
|
||||
_trapped_NativeLibrary_load = (void*)NativeLibrariesLoadTrap;
|
||||
|
||||
// JDK 15+
|
||||
_load_method.name = (char*)"load";
|
||||
_load_method.signature = (char*)"(Ljdk/internal/loader/NativeLibraries$NativeLibraryImpl;Ljava/lang/String;ZZ)Z";
|
||||
if (env->GetStaticMethodID(NativeLibrary, "load", "(Ljdk/internal/loader/NativeLibraries$NativeLibraryImpl;Ljava/lang/String;ZZZ)Z") != NULL) {
|
||||
// JDK 17+
|
||||
_load_method.name = (char*)"load";
|
||||
_load_method.signature = (char*)"(Ljdk/internal/loader/NativeLibraries$NativeLibraryImpl;Ljava/lang/String;ZZZ)Z";
|
||||
} else {
|
||||
// JDK 15-16
|
||||
_load_method.name = (char*)"load";
|
||||
_load_method.signature = (char*)"(Ljdk/internal/loader/NativeLibraries$NativeLibraryImpl;Ljava/lang/String;ZZ)Z";
|
||||
}
|
||||
|
||||
} else if ((NativeLibrary = env->FindClass("java/lang/ClassLoader$NativeLibrary")) != NULL) {
|
||||
strcpy(original_jni_name, "Java_java_lang_ClassLoader_00024NativeLibrary_");
|
||||
_trapped_NativeLibrary_load = (void*)NativeLibraryLoadTrap;
|
||||
|
||||
if (env->GetMethodID(NativeLibrary, "load0", "(Ljava/lang/String;Z)Z") != NULL) {
|
||||
if (env->GetMethodID(NativeLibrary, "load0", "(Ljava/lang/String;ZZ)Z") != NULL) {
|
||||
// JDK 11.0.15+ (workaround for JDK-8288547)
|
||||
_load_method.name = (char*)"load0";
|
||||
_load_method.signature = (char*)"(Ljava/lang/String;ZZ)Z";
|
||||
} else if (env->GetMethodID(NativeLibrary, "load0", "(Ljava/lang/String;Z)Z") != NULL) {
|
||||
// JDK 9-14
|
||||
_load_method.name = (char*)"load0";
|
||||
_load_method.signature = (char*)"(Ljava/lang/String;Z)Z";
|
||||
@@ -671,13 +660,13 @@ void Profiler::bindNativeLibraryLoad(JNIEnv* env, bool enable) {
|
||||
}
|
||||
|
||||
} else {
|
||||
fprintf(stderr, "WARNING: Failed to intercept NativeLibraries.load()\n");
|
||||
if (!warning_reported++) fprintf(stderr, "WARNING: async-profiler failed to intercept NativeLibraries.load()\n");
|
||||
return;
|
||||
}
|
||||
|
||||
strcat(original_jni_name, _load_method.name);
|
||||
if ((_original_NativeLibrary_load = dlsym(VM::_libjava, original_jni_name)) == NULL) {
|
||||
fprintf(stderr, "WARNING: Could not find %s\n", original_jni_name);
|
||||
if (!warning_reported++) fprintf(stderr, "WARNING: async-profiler could not find %s\n", original_jni_name);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -686,7 +675,7 @@ void Profiler::bindNativeLibraryLoad(JNIEnv* env, bool enable) {
|
||||
? "jdk/internal/loader/NativeLibraries"
|
||||
: "java/lang/ClassLoader$NativeLibrary";
|
||||
if ((NativeLibrary = env->FindClass(class_name)) == NULL) {
|
||||
fprintf(stderr, "WARNING: Could not find %s\n", class_name);
|
||||
if (!warning_reported++) fprintf(stderr, "WARNING: async-profiler could not find %s\n", class_name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -815,7 +804,7 @@ Engine* Profiler::selectEngine(const char* event_name) {
|
||||
}
|
||||
}
|
||||
|
||||
Error Profiler::checkJvmCapabilities() {
|
||||
Error Profiler::checkJvmCapabilities(bool print_warnings) {
|
||||
if (VMStructs::libjvm() == NULL) {
|
||||
return Error("Could not find libjvm among loaded libraries. Unsupported JVM?");
|
||||
}
|
||||
@@ -824,7 +813,7 @@ Error Profiler::checkJvmCapabilities() {
|
||||
return Error("Could not find VMThread bridge. Unsupported JVM?");
|
||||
}
|
||||
|
||||
if (VMStructs::_get_stack_trace == NULL) {
|
||||
if (VMStructs::_get_stack_trace == NULL && print_warnings) {
|
||||
fprintf(stderr, "WARNING: Install JVM debug symbols to improve profile accuracy\n");
|
||||
}
|
||||
|
||||
@@ -837,7 +826,7 @@ Error Profiler::start(Arguments& args, bool reset) {
|
||||
return Error("Profiler already started");
|
||||
}
|
||||
|
||||
Error error = checkJvmCapabilities();
|
||||
Error error = checkJvmCapabilities(args._log);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
@@ -894,7 +883,7 @@ Error Profiler::start(Arguments& args, bool reset) {
|
||||
|
||||
updateSymbols(args._ring != RING_USER);
|
||||
|
||||
_safe_mode = args._safe_mode;
|
||||
_safe_mode = VM::_safe_mode | args._safe_mode;
|
||||
if (VM::hotspot_version() < 8) {
|
||||
// Cannot use JVM TI stack walker during GC on non-HotSpot JVMs or with PermGen
|
||||
_safe_mode |= GC_TRACES | LAST_JAVA_PC;
|
||||
@@ -960,7 +949,7 @@ Error Profiler::check(Arguments& args) {
|
||||
return Error("Profiler already started");
|
||||
}
|
||||
|
||||
Error error = checkJvmCapabilities();
|
||||
Error error = checkJvmCapabilities(args._log);
|
||||
if (error) {
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -143,8 +143,8 @@ class Profiler {
|
||||
JNINativeMethod _load_method;
|
||||
void* _original_NativeLibrary_load;
|
||||
void* _trapped_NativeLibrary_load;
|
||||
static jboolean JNICALL NativeLibraryLoadTrap(JNIEnv* env, jobject self, jstring name, jboolean builtin);
|
||||
static jboolean JNICALL NativeLibrariesLoadTrap(JNIEnv* env, jobject self, jobject lib, jstring name, jboolean builtin, jboolean jni);
|
||||
static jboolean JNICALL NativeLibraryLoadTrap(JNIEnv* env, jobject self, jstring name, jboolean builtin, jboolean throws);
|
||||
static jboolean JNICALL NativeLibrariesLoadTrap(JNIEnv* env, jobject self, jobject lib, jstring name, jboolean builtin, jboolean jni, jboolean throws);
|
||||
void bindNativeLibraryLoad(JNIEnv* env, bool enable);
|
||||
|
||||
// Support for intercepting Thread.setNativeName()
|
||||
@@ -162,7 +162,7 @@ class Profiler {
|
||||
void onThreadEnd(jvmtiEnv* jvmti, JNIEnv* jni, jthread thread);
|
||||
|
||||
const char* asgctError(int code);
|
||||
bool checkWalkable(void* ucontext);
|
||||
bool inJavaCode(void* ucontext);
|
||||
int getNativeTrace(void* ucontext, ASGCT_CallFrame* frames, int tid);
|
||||
int getJavaTraceAsync(void* ucontext, ASGCT_CallFrame* frames, int max_depth);
|
||||
int getJavaTraceJvmti(jvmtiFrameInfo* jvmti_frames, ASGCT_CallFrame* frames, int max_depth);
|
||||
@@ -180,7 +180,7 @@ class Profiler {
|
||||
void updateNativeThreadNames();
|
||||
bool excludeTrace(FrameName* fn, CallTraceSample* trace);
|
||||
Engine* selectEngine(const char* event_name);
|
||||
Error checkJvmCapabilities();
|
||||
Error checkJvmCapabilities(bool print_warnings);
|
||||
|
||||
public:
|
||||
static Profiler _instance;
|
||||
|
||||
@@ -122,7 +122,7 @@ bool StackFrame::checkInterruptedSyscall() {
|
||||
if (retval() == (uintptr_t)-EINTR) {
|
||||
// Workaround for JDK-8237858: restart the interrupted poll() manually.
|
||||
// Check if the previous instruction is mov eax, SYS_poll with infinite timeout
|
||||
if (arg2() == (uintptr_t)-1) {
|
||||
if ((int)arg2() == -1) {
|
||||
uintptr_t pc = this->pc();
|
||||
if ((pc & 0xfff) >= 7 && *(unsigned char*)(pc - 7) == 0xb8 && *(int*)(pc - 6) == SYS_poll) {
|
||||
this->pc() = pc - 7;
|
||||
|
||||
@@ -33,6 +33,8 @@ static Arguments _agent_args;
|
||||
JavaVM* VM::_vm;
|
||||
jvmtiEnv* VM::_jvmti = NULL;
|
||||
int VM::_hotspot_version = 0;
|
||||
int VM::_hotspot_minor = 0;
|
||||
int VM::_safe_mode = 0;
|
||||
void* VM::_libjvm;
|
||||
void* VM::_libjava;
|
||||
AsyncGetCallTrace VM::_asyncGetCallTrace;
|
||||
@@ -64,6 +66,11 @@ void VM::init(JavaVM* vm, bool attach) {
|
||||
_hotspot_version = 6;
|
||||
} else if ((_hotspot_version = atoi(prop)) < 9) {
|
||||
_hotspot_version = 9;
|
||||
} else {
|
||||
const char* p = strchr(prop, '.');
|
||||
if (p != NULL && (p = strchr(p + 1, '.')) != NULL) {
|
||||
_hotspot_minor = atoi(p + 1);
|
||||
}
|
||||
}
|
||||
_jvmti->Deallocate((unsigned char*)prop);
|
||||
}
|
||||
@@ -109,12 +116,16 @@ void VM::init(JavaVM* vm, bool attach) {
|
||||
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_VM_DEATH, NULL);
|
||||
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_LOAD, NULL);
|
||||
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_CLASS_PREPARE, NULL);
|
||||
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
|
||||
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
|
||||
if ((_safe_mode & 14) != 14) { // POP_FRAME + SCAN_STACK + LAST_JAVA_PC
|
||||
// Workaround for JDK-8173361: avoid CompiledMethodLoad events when they are not needed
|
||||
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_LOAD, NULL);
|
||||
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_COMPILED_METHOD_UNLOAD, NULL);
|
||||
}
|
||||
_jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_DYNAMIC_CODE_GENERATED, NULL);
|
||||
|
||||
if (attach) {
|
||||
loadAllMethodIDs(jvmti(), jni());
|
||||
DisableSweeper ds;
|
||||
_jvmti->GenerateEvents(JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
|
||||
_jvmti->GenerateEvents(JVMTI_EVENT_COMPILED_METHOD_LOAD);
|
||||
}
|
||||
@@ -263,7 +274,24 @@ Agent_OnAttach(JavaVM* vm, char* options, void* reserved) {
|
||||
|
||||
extern "C" JNIEXPORT jint JNICALL
|
||||
JNI_OnLoad(JavaVM* vm, void* reserved) {
|
||||
JNIEnv* jni;
|
||||
if (vm->GetEnv((void**)&jni, JNI_VERSION_1_6) != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
jclass cls = jni->FindClass("java/lang/System");
|
||||
jmethodID getProperty = jni->GetStaticMethodID(cls, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;");
|
||||
jstring prop = (jstring)jni->CallStaticObjectMethod(cls, getProperty, jni->NewStringUTF("AsyncProfiler.safemode"));
|
||||
|
||||
const char* chars;
|
||||
if (prop != NULL && (chars = jni->GetStringUTFChars(prop, NULL)) != NULL) {
|
||||
VM::_safe_mode = strtol(chars, NULL, 0);
|
||||
jni->ReleaseStringUTFChars(prop, chars);
|
||||
} else {
|
||||
jni->ExceptionClear();
|
||||
}
|
||||
|
||||
VM::init(vm, true);
|
||||
JavaAPI::registerNatives(VM::jvmti(), VM::jni());
|
||||
JavaAPI::registerNatives(VM::jvmti(), jni);
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ class VM {
|
||||
static jvmtiError (JNICALL *_orig_RetransformClasses)(jvmtiEnv*, jint, const jclass* classes);
|
||||
static volatile int _in_redefine_classes;
|
||||
static int _hotspot_version;
|
||||
static int _hotspot_minor;
|
||||
|
||||
static void ready();
|
||||
static void* getLibraryHandle(const char* name);
|
||||
@@ -97,6 +98,7 @@ class VM {
|
||||
static void loadAllMethodIDs(jvmtiEnv* jvmti, JNIEnv* jni);
|
||||
|
||||
public:
|
||||
static int _safe_mode;
|
||||
static void* _libjvm;
|
||||
static void* _libjava;
|
||||
static AsyncGetCallTrace _asyncGetCallTrace;
|
||||
@@ -120,6 +122,10 @@ class VM {
|
||||
return _hotspot_version;
|
||||
}
|
||||
|
||||
static int hotspot_minor() {
|
||||
return _hotspot_minor;
|
||||
}
|
||||
|
||||
static bool inRedefineClasses() {
|
||||
return _in_redefine_classes > 0;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include "vmStructs.h"
|
||||
#include "vmEntry.h"
|
||||
|
||||
@@ -43,6 +44,8 @@ int VMStructs::_anchor_pc_offset = -1;
|
||||
int VMStructs::_frame_size_offset = -1;
|
||||
int VMStructs::_is_gc_active_offset = -1;
|
||||
char* VMStructs::_collected_heap_addr = NULL;
|
||||
const void* VMStructs::_code_heap_low = NO_MIN_ADDRESS;
|
||||
const void* VMStructs::_code_heap_high = NO_MAX_ADDRESS;
|
||||
|
||||
jfieldID VMStructs::_eetop;
|
||||
jfieldID VMStructs::_tid;
|
||||
@@ -55,6 +58,8 @@ VMStructs::UnsafeParkFunc VMStructs::_unsafe_park = NULL;
|
||||
VMStructs::FindBlobFunc VMStructs::_find_blob = NULL;
|
||||
VMStructs::LockFunc VMStructs::_lock_func;
|
||||
VMStructs::LockFunc VMStructs::_unlock_func;
|
||||
char* VMStructs::_method_flushing = NULL;
|
||||
int* VMStructs::_sweep_started = NULL;
|
||||
|
||||
|
||||
uintptr_t VMStructs::readSymbol(const char* symbol_name) {
|
||||
@@ -90,6 +95,11 @@ void VMStructs::initOffsets() {
|
||||
return;
|
||||
}
|
||||
|
||||
char* code_heap_addr = NULL;
|
||||
int code_heap_memory_offset = -1;
|
||||
int vs_low_offset = -1;
|
||||
int vs_high_offset = -1;
|
||||
|
||||
while (true) {
|
||||
const char* type = *(const char**)(entry + type_offset);
|
||||
const char* field = *(const char**)(entry + field_offset);
|
||||
@@ -142,6 +152,24 @@ void VMStructs::initOffsets() {
|
||||
if (strcmp(field, "_frame_size") == 0) {
|
||||
_frame_size_offset = *(int*)(entry + offset_offset);
|
||||
}
|
||||
} else if (strcmp(type, "CodeCache") == 0) {
|
||||
if (strcmp(field, "_heap") == 0) {
|
||||
code_heap_addr = **(char***)(entry + address_offset);
|
||||
} else if (strcmp(field, "_high_bound") == 0) {
|
||||
_code_heap_high = **(const void***)(entry + address_offset);
|
||||
} else if (strcmp(field, "_low_bound") == 0) {
|
||||
_code_heap_low = **(const void***)(entry + address_offset);
|
||||
}
|
||||
} else if (strcmp(type, "CodeHeap") == 0) {
|
||||
if (strcmp(field, "_memory") == 0) {
|
||||
code_heap_memory_offset = *(int*)(entry + offset_offset);
|
||||
}
|
||||
} else if (strcmp(type, "VirtualSpace") == 0) {
|
||||
if (strcmp(field, "_low_boundary") == 0) {
|
||||
vs_low_offset = *(int*)(entry + offset_offset);
|
||||
} else if (strcmp(field, "_high_boundary") == 0) {
|
||||
vs_high_offset = *(int*)(entry + offset_offset);
|
||||
}
|
||||
} else if (strcmp(type, "Universe") == 0) {
|
||||
if (strcmp(field, "_collectedHeap") == 0) {
|
||||
_collected_heap_addr = **(char***)(entry + address_offset);
|
||||
@@ -161,6 +189,11 @@ void VMStructs::initOffsets() {
|
||||
&& (_symbol_length_offset >= 0 || _symbol_length_and_refcount_offset >= 0)
|
||||
&& _symbol_body_offset >= 0
|
||||
&& _klass != NULL;
|
||||
|
||||
if (code_heap_addr != NULL && code_heap_memory_offset >= 0 && vs_low_offset >= 0 && vs_high_offset >= 0) {
|
||||
_code_heap_low = *(const void**)(code_heap_addr + code_heap_memory_offset + vs_low_offset);
|
||||
_code_heap_high = *(const void**)(code_heap_addr + code_heap_memory_offset + vs_high_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void VMStructs::initJvmFunctions() {
|
||||
@@ -187,6 +220,12 @@ void VMStructs::initJvmFunctions() {
|
||||
_unlock_func = (LockFunc)_libjvm->findSymbol("_ZN7Monitor6unlockEv");
|
||||
_has_class_loader_data = _lock_func != NULL && _unlock_func != NULL;
|
||||
}
|
||||
|
||||
if ((VM::hotspot_version() > 0 && VM::hotspot_version() < 11) ||
|
||||
(VM::hotspot_version() == 11 && VM::hotspot_minor() < 10)) {
|
||||
_method_flushing = (char*)_libjvm->findSymbol("MethodFlushing");
|
||||
_sweep_started = (int*)_libjvm->findSymbol("_ZN14NMethodSweeper14_sweep_startedE");
|
||||
}
|
||||
}
|
||||
|
||||
void VMStructs::initThreadBridge(JNIEnv* env) {
|
||||
@@ -238,3 +277,27 @@ void VMStructs::initLogging(JNIEnv* env) {
|
||||
VMThread* VMThread::current() {
|
||||
return (VMThread*)pthread_getspecific((pthread_key_t)_tls_index);
|
||||
}
|
||||
|
||||
DisableSweeper::DisableSweeper() {
|
||||
// Workaround for JDK-8212160: Temporarily disable MethodFlushing
|
||||
// while generating initial set of CompiledMethodLoad events
|
||||
_enabled = _method_flushing != NULL && *_method_flushing;
|
||||
if (!_enabled) return;
|
||||
|
||||
*_method_flushing = 0;
|
||||
__sync_synchronize();
|
||||
|
||||
// Wait a bit in case sweeping has already started
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (_sweep_started == NULL || *_sweep_started) {
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisableSweeper::~DisableSweeper() {
|
||||
if (!_enabled) return;
|
||||
|
||||
*_method_flushing = 1;
|
||||
__sync_synchronize();
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ class VMStructs {
|
||||
static int _frame_size_offset;
|
||||
static int _is_gc_active_offset;
|
||||
static char* _collected_heap_addr;
|
||||
static const void* _code_heap_low;
|
||||
static const void* _code_heap_high;
|
||||
|
||||
static jfieldID _eetop;
|
||||
static jfieldID _tid;
|
||||
@@ -60,6 +62,9 @@ class VMStructs {
|
||||
static LockFunc _lock_func;
|
||||
static LockFunc _unlock_func;
|
||||
|
||||
static char* _method_flushing;
|
||||
static int* _sweep_started;
|
||||
|
||||
static uintptr_t readSymbol(const char* symbol_name);
|
||||
static void initOffsets();
|
||||
static void initJvmFunctions();
|
||||
@@ -238,6 +243,18 @@ class RuntimeStub : VMStructs {
|
||||
}
|
||||
};
|
||||
|
||||
class CodeHeap : VMStructs {
|
||||
public:
|
||||
static bool contains(const void* pc) {
|
||||
return _code_heap_low <= pc && pc < _code_heap_high;
|
||||
}
|
||||
|
||||
static void updateBounds(const void* start, const void* end) {
|
||||
if (start < _code_heap_low) _code_heap_low = start;
|
||||
if (end > _code_heap_high) _code_heap_high = end;
|
||||
}
|
||||
};
|
||||
|
||||
class CollectedHeap : VMStructs {
|
||||
public:
|
||||
static bool isGCActive() {
|
||||
@@ -246,4 +263,13 @@ class CollectedHeap : VMStructs {
|
||||
}
|
||||
};
|
||||
|
||||
class DisableSweeper : VMStructs {
|
||||
private:
|
||||
bool _enabled;
|
||||
|
||||
public:
|
||||
DisableSweeper();
|
||||
~DisableSweeper();
|
||||
};
|
||||
|
||||
#endif // _VMSTRUCTS_H
|
||||
|
||||
Reference in New Issue
Block a user