Compare commits

...

8 Commits

Author SHA1 Message Date
Andrey Pangin
9341b201b8 Release 1.8.6 2021-07-08 22:53:04 +03:00
Andrey Pangin
c43912b903 Sign macOS binaries 2021-07-08 21:45:57 +03:00
Andrey Pangin
7ed21187e9 Extend CompiledMethodLoad bug workaround to JDK < 11.0.10 2021-07-07 02:10:29 +03:00
Andrey Pangin
09b5ec0b63 'log=none' option to suppress warnings in logs 2021-07-05 03:42:30 +03:00
Andrey Pangin
a12c291423 Workaround JDK bug related to posting CompiledMethodLoad event 2021-07-05 00:57:47 +03:00
Andrey Pangin
32997bb645 Release 1.8.5 2021-03-22 02:31:30 +03:00
Andrey Pangin
9b11014b25 Simplified safe mode check 2021-03-13 17:03:03 +03:00
Andrey Pangin
516d717d49 Backported JFR-FlameGraph converter 2021-03-04 20:09:56 +03:00
19 changed files with 276 additions and 90 deletions

View File

@@ -1,5 +1,22 @@
# Changelog
## [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

View File

@@ -1,4 +1,4 @@
PROFILER_VERSION=1.8.4
PROFILER_VERSION=1.8.6
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)

View File

@@ -14,21 +14,16 @@ async-profiler can trace the following kinds of events:
## Download
Latest release (1.8.4):
Latest v1 release (1.8.6):
- 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.6-linux-x64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.6/async-profiler-1.8.6-linux-x64.tar.gz)
- Linux x86 (glibc): [async-profiler-1.8.6-linux-x86.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.6/async-profiler-1.8.6-linux-x86.tar.gz)
- Linux x64 (musl): [async-profiler-1.8.6-linux-musl-x64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.6/async-profiler-1.8.6-linux-musl-x64.tar.gz)
- Linux ARM: [async-profiler-1.8.6-linux-arm.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.6/async-profiler-1.8.6-linux-arm.tar.gz)
- Linux AArch64: [async-profiler-1.8.6-linux-aarch64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.6/async-profiler-1.8.6-linux-aarch64.tar.gz)
- macOS x64: [async-profiler-1.8.6-macos-x64.tar.gz](https://github.com/jvm-profiling-tools/async-profiler/releases/download/v1.8.6/async-profiler-1.8.6-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.6/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`

View File

@@ -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.6</version>
<packaging>jar</packaging>
<name>async-profiler</name>

View File

@@ -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;

View File

@@ -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),

View File

@@ -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("'", "\\'");
}

View File

@@ -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");
}
}

View 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();
}
}

View File

@@ -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();
}

View File

@@ -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));
}
}

View 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;
}
}

View File

@@ -535,9 +535,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;

View File

@@ -262,47 +262,21 @@ 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 _java_methods.contains(pc);
}
int Profiler::getNativeTrace(void* ucontext, ASGCT_CallFrame* frames, int tid) {
@@ -336,7 +310,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;
}
@@ -815,7 +789,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 +798,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 +811,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;
}
@@ -960,7 +934,7 @@ Error Profiler::check(Arguments& args) {
return Error("Profiler already started");
}
Error error = checkJvmCapabilities();
Error error = checkJvmCapabilities(args._log);
if (error) {
return error;
}

View File

@@ -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;

View File

@@ -33,6 +33,7 @@ static Arguments _agent_args;
JavaVM* VM::_vm;
jvmtiEnv* VM::_jvmti = NULL;
int VM::_hotspot_version = 0;
int VM::_hotspot_minor = 0;
void* VM::_libjvm;
void* VM::_libjava;
AsyncGetCallTrace VM::_asyncGetCallTrace;
@@ -64,6 +65,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);
}
@@ -115,6 +121,7 @@ void VM::init(JavaVM* vm, bool attach) {
if (attach) {
loadAllMethodIDs(jvmti(), jni());
DisableSweeper ds;
_jvmti->GenerateEvents(JVMTI_EVENT_DYNAMIC_CODE_GENERATED);
_jvmti->GenerateEvents(JVMTI_EVENT_COMPILED_METHOD_LOAD);
}

View File

@@ -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);
@@ -120,6 +121,10 @@ class VM {
return _hotspot_version;
}
static int hotspot_minor() {
return _hotspot_minor;
}
static bool inRedefineClasses() {
return _in_redefine_classes > 0;
}

View File

@@ -17,6 +17,7 @@
#include <pthread.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include "vmStructs.h"
#include "vmEntry.h"
@@ -55,6 +56,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) {
@@ -187,6 +190,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 +247,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();
}

View File

@@ -60,6 +60,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();
@@ -246,4 +249,13 @@ class CollectedHeap : VMStructs {
}
};
class DisableSweeper : VMStructs {
private:
bool _enabled;
public:
DisableSweeper();
~DisableSweeper();
};
#endif // _VMSTRUCTS_H