Skip to content
This repository has been archived by the owner. It is now read-only.
Permalink
Browse files
8268717: Upstream: 8268673: Stack walk across optimized entry frame o…
…n fresh native thread fails

Reviewed-by: mcimadamore, erikj
  • Loading branch information
JornVernee committed Jun 21, 2021
1 parent 22ebd19 commit f25e7197fef76cc87a15da7cc96a42b84d69bbfe
Showing 12 changed files with 198 additions and 1 deletion.
@@ -53,6 +53,8 @@ BUILD_JDK_JTREG_EXECUTABLES_CFLAGS_exeJliLaunchTest := \
-I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS_TYPE)/native/libjli \
-I$(TOPDIR)/src/java.base/$(OPENJDK_TARGET_OS)/native/libjli

BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libAsyncStackWalk := $(LIBCXX)

# Platform specific setup
ifeq ($(call isTargetOs, windows), true)
BUILD_JDK_JTREG_EXCLUDE += libDirectIO.c libInheritedChannel.c exelauncher.c
@@ -63,6 +65,7 @@ ifeq ($(call isTargetOs, windows), true)
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeJliLaunchTest := $(WIN_LIB_JLI)
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exeCallerAccessTest := jvm.lib
BUILD_JDK_JTREG_EXECUTABLES_LIBS_exerevokeall := advapi32.lib
BUILD_JDK_JTREG_LIBRARIES_CFLAGS_libAsyncStackWalk := /EHsc
else
BUILD_JDK_JTREG_LIBRARIES_LIBS_libstringPlatformChars := -ljava
BUILD_JDK_JTREG_LIBRARIES_LIBS_libDirectIO := -ljava
@@ -367,6 +367,11 @@ JavaFrameAnchor* OptimizedEntryBlob::jfa_for_frame(const frame& frame) const {
return nullptr;
}

bool frame::optimized_entry_frame_is_first() const {
ShouldNotCallThis();
return false;
}

frame frame::sender_for_optimized_entry_frame(RegisterMap* map) const {
ShouldNotCallThis();
return {};
@@ -313,6 +313,11 @@ frame frame::sender_for_entry_frame(RegisterMap* map) const {
return fr;
}

bool frame::optimized_entry_frame_is_first() const {
ShouldNotCallThis();
return false;
}

//------------------------------------------------------------------------------
// frame::verify_deopt_original_pc
//
@@ -197,6 +197,11 @@ frame frame::sender_for_entry_frame(RegisterMap *map) const {
return fr;
}

bool frame::optimized_entry_frame_is_first() const {
ShouldNotCallThis();
return false;
}

frame frame::sender_for_interpreter_frame(RegisterMap *map) const {
// Pass callers initial_caller_sp as unextended_sp.
return frame(sender_sp(), sender_pc(), (intptr_t*)get_ijava_state()->sender_sp);
@@ -208,6 +208,11 @@ frame frame::sender_for_entry_frame(RegisterMap *map) const {
return fr;
}

bool frame::optimized_entry_frame_is_first() const {
ShouldNotCallThis();
return false;
}

frame frame::sender_for_interpreter_frame(RegisterMap *map) const {
// Pass callers sender_sp as unextended_sp.
return frame(sender_sp(), sender_pc(), (intptr_t*)(ijava_state()->sender_sp));
@@ -358,12 +358,20 @@ JavaFrameAnchor* OptimizedEntryBlob::jfa_for_frame(const frame& frame) const {
return reinterpret_cast<JavaFrameAnchor*>(reinterpret_cast<char*>(frame.unextended_sp()) + in_bytes(jfa_sp_offset()));
}

bool frame::optimized_entry_frame_is_first() const {
assert(is_optimized_entry_frame(), "must be optimzed entry frame");
OptimizedEntryBlob* blob = _cb->as_optimized_entry_blob();
JavaFrameAnchor* jfa = blob->jfa_for_frame(*this);
return jfa->last_Java_sp() == NULL;
}

frame frame::sender_for_optimized_entry_frame(RegisterMap* map) const {
assert(map != NULL, "map must be set");
OptimizedEntryBlob* blob = _cb->as_optimized_entry_blob();
// Java frame called from C; skip all C frames and return top C
// frame of that chunk as the sender
JavaFrameAnchor* jfa = blob->jfa_for_frame(*this);
assert(!optimized_entry_frame_is_first(), "must have a frame anchor to go back to");
assert(jfa->last_Java_sp() > sp(), "must be above this frame on stack");
// Since we are walking the stack now this nested anchor is obviously walkable
// even if it wasn't when it was stacked.
@@ -61,6 +61,11 @@ frame frame::sender_for_entry_frame(RegisterMap *map) const {
return frame(zeroframe()->next(), sender_sp());
}

bool frame::optimized_entry_frame_is_first() const {
ShouldNotCallThis();
return false;
}

frame frame::sender_for_nonentry_frame(RegisterMap *map) const {
assert(zeroframe()->is_interpreter_frame() ||
zeroframe()->is_fake_stub_frame(), "wrong type of frame");
@@ -2306,6 +2306,7 @@ WB_ENTRY(void, WB_VerifyFrames(JNIEnv* env, jobject wb, jboolean log, jboolean u
tty_token = ttyLocker::hold_tty();
tty->print_cr("[WhiteBox::VerifyFrames] Walking Frames");
}
ResourceMark rm; // for verify
for (StackFrameStream fst(JavaThread::current(), update_map, true); !fst.is_done(); fst.next()) {
frame* current_frame = fst.current();
if (log) {
@@ -342,6 +342,7 @@ class frame {

// tells whether there is another chunk of Delta stack above
bool entry_frame_is_first() const;
bool optimized_entry_frame_is_first() const;

// Safepoints

@@ -50,7 +50,9 @@ inline bool frame::is_stub_frame() const {
}

inline bool frame::is_first_frame() const {
return is_entry_frame() && entry_frame_is_first();
return (is_entry_frame() && entry_frame_is_first())
// Optimized entry frames are only present on certain platforms
|| (is_optimized_entry_frame() && optimized_entry_frame_is_first());
}

inline bool frame::is_optimized_entry_frame() const {
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

/*
* @test
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
* @library /test/lib
* @build sun.hotspot.WhiteBox
* @run driver jdk.test.lib.helpers.ClassFileInstaller sun.hotspot.WhiteBox
*
* @run main/othervm
* -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions
* -XX:+WhiteBoxAPI
* -Djdk.internal.foreign.ProgrammableInvoker.USE_INTRINSICS=true
* --enable-native-access=ALL-UNNAMED
* -Xbatch
* TestAsyncStackWalk
*
* @run main/othervm
* -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions
* -XX:+WhiteBoxAPI
* -Djdk.internal.foreign.ProgrammableInvoker.USE_INTRINSICS=false
* --enable-native-access=ALL-UNNAMED
* -Xbatch
* TestAsyncStackWalk
*/

import jdk.incubator.foreign.CLinker;
import jdk.incubator.foreign.FunctionDescriptor;
import jdk.incubator.foreign.SymbolLookup;
import jdk.incubator.foreign.MemoryAddress;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodType;

import jdk.incubator.foreign.ResourceScope;
import sun.hotspot.WhiteBox;

import static java.lang.invoke.MethodHandles.lookup;
import static jdk.incubator.foreign.CLinker.C_POINTER;
import static jdk.test.lib.Asserts.assertTrue;

public class TestAsyncStackWalk {
static final WhiteBox WB = WhiteBox.getWhiteBox();

static final CLinker linker = CLinker.getInstance();

static final MethodHandle MH_asyncStackWalk;
static final MethodHandle MH_m;

static {
try {
System.loadLibrary("AsyncStackWalk");
SymbolLookup lookup = SymbolLookup.loaderLookup();
MH_asyncStackWalk = linker.downcallHandle(
lookup.lookup("asyncStackWalk").get(),
MethodType.methodType(void.class, MemoryAddress.class),
FunctionDescriptor.ofVoid(C_POINTER));
MH_m = lookup().findStatic(TestAsyncStackWalk.class, "m", MethodType.methodType(void.class));
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}

static int invocations;
static boolean didStackWalk;

public static void main(String[] args) throws Throwable {
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
MemoryAddress stub = linker.upcallStub(MH_m, FunctionDescriptor.ofVoid(), scope);
MemoryAddress stubAddress = stub.address();
invocations = 0;
didStackWalk = false;
payload(stubAddress);
assertTrue(didStackWalk);
}
}

static void payload(MemoryAddress cb) throws Throwable {
MH_asyncStackWalk.invokeExact(cb);
}

static void m() {
if (invocations++ >= 20000) { // warmup
didStackWalk = true;
WB.verifyFrames(/*log=*/true, /*updateRegisterMap=*/true);
WB.verifyFrames(/*log=*/true, /*updateRegisterMap=*/false); // triggers different code paths
}
}

}
@@ -0,0 +1,44 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/

#include <thread>

#ifdef _WIN64
#define EXPORT __declspec(dllexport)
#else
#define EXPORT
#endif

static void start(void (*cb)(void)) {
for (int i = 0; i < 25000; i++) {
cb();
}
}

extern "C" {
EXPORT void asyncStackWalk(void (*cb)(void)) {
std::thread thrd(start, cb);
thrd.join();
}
}

1 comment on commit f25e719

@openjdk-notifier
Copy link

@openjdk-notifier openjdk-notifier bot commented on f25e719 Jun 21, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.