Where does VMError::print_native_stack and os::get_sender_for_C_frame load/use the frame pointer?

Julian Waters tanksherman27 at gmail.com
Tue Jul 9 05:14:58 UTC 2024


Hi David,

I just looked at the code for both, and it weirdly doesn't seem that
fetch_frame_from_context is used in either. Out of curiosity I tried
removing HAVE_PLATFORM_PRINT_NATIVE_STACK from Windows/x64 and
deliberately crashed HotSpot after compiling the JDK, and the
resulting hs_err file had almost no frame information as a result:

---------------  S U M M A R Y ------------

Command Line: --enable-preview Crash

Host: AMD Ryzen 9 7845HX with Radeon Graphics        , 24 cores, 15G,
Windows 11 , 64 bit Build 22621 (10.0.22621.3672)
Time: Tue Jul  9 02:41:20 2024 Malay Peninsula Standard Time elapsed
time: 0.070338 seconds (0d 0h 0m 0s)

---------------  T H R E A D  ---------------

Current thread (0x0000017e8cead250):  JavaThread "main"
[_thread_in_vm, id=33760, stack(0x0000005c11f00000,0x0000005c12000000)
(1024K)]

Stack: [0x0000005c11f00000,0x0000005c12000000],
sp=0x0000005c11fff000,  free space=1020k
Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)
V  [jvm.dll+0x195ab57]
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
j  jdk.internal.misc.Unsafe.putLong(Ljava/lang/Object;JJ)V+0
java.base at 24-internal
j  jdk.internal.misc.Unsafe.putAddress(Ljava/lang/Object;JJ)V+24
java.base at 24-internal
j  jdk.internal.misc.Unsafe.putAddress(JJ)V+4 java.base at 24-internal
j  sun.misc.Unsafe.putAddress(JJ)V+8 jdk.unsupported at 24-internal
j  Crash.main()V+53
v  ~StubRoutines::call_stub 0x0000017e9fc70fcd

siginfo: EXCEPTION_ACCESS_VIOLATION (0xc0000005), writing address
0x0000000000000000

Native frames only has 1 frame in it, indicative of further frames not
being found. I can't really tell what else is required to get it to
work with the regular VMError::print_native_stack without requiring
the Windows specific os::win32::platform_print_native_stack. I
compiled HotSpot with gcc and verified that the frame pointer is
indeed saved, so this not working is a little odd to me (Was testing
in the off chance that the Microsoft compiler could be forced to
preserve the frame pointer). There has to somehow be a way to walk the
frames on Windows when the frame pointer is available for use

best regards,
Julian

P.S. The attempted patch is attached below, if anyone is curious

diff --git a/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp
b/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp
index 7e0814c014b..a4fa45ed78f 100644
--- a/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp
+++ b/src/hotspot/os_cpu/windows_x86/os_windows_x86.cpp
@@ -71,7 +71,7 @@ extern LONG WINAPI
topLevelExceptionFilter(_EXCEPTION_POINTERS* );

 // Install a win32 structured exception handler around thread.
 void os::os_exception_wrapper(java_call_t f, JavaValue* value, const
methodHandle& method, JavaCallArguments* args, JavaThread* thread) {
-  __try {
+  WIN32_TRY {

 #ifndef AMD64
     // We store the current thread in this wrapperthread location
@@ -111,7 +111,7 @@ void os::os_exception_wrapper(java_call_t f,
JavaValue* value, const methodHandl
 #endif // !AMD64

     f(value, method, args, thread);
-  } __except(topLevelExceptionFilter((_EXCEPTION_POINTERS*)_exception_info()))
{
+  } WIN32_EXCEPT (topLevelExceptionFilter(GetExceptionInformation())) {
       // Nothing to do.
   }
 }
@@ -396,16 +396,32 @@ bool
os::win32::get_frame_at_stack_banging_point(JavaThread* thread,


 // VC++ does not save frame pointer on stack in optimized build. It
-// can be turned off by /Oy-. If we really want to walk C frames,
+// can be turned off by -Oy-. If we really want to walk C frames,
 // we can use the StackWalk() API.
 frame os::get_sender_for_C_frame(frame* fr) {
+#ifdef __GNUC__
+  return frame(fr->sender_sp(), fr->link(), fr->sender_pc());
+#elif defined(_MSC_VER)
   ShouldNotReachHere();
   return frame();
+#endif
 }

 frame os::current_frame() {
+#ifdef __GNUC__
+  frame f(reinterpret_cast<intptr_t*>(os::current_stack_pointer()),
+          reinterpret_cast<intptr_t*>(__builtin_frame_address(1)),
+          CAST_FROM_FN_PTR(address, &os::current_frame));
+  if (os::is_first_C_frame(&f)) {
+    // stack is not walkable
+    return frame();
+  } else {
+    return os::get_sender_for_C_frame(&f);
+  }
+#elif defined(_MSC_VER)
   return frame();  // cannot walk Windows frames this way.  See
os::get_native_stack
                    // and os::platform_print_native_stack
+#endif
 }

 void os::print_context(outputStream *st, const void *context) {
diff --git a/src/hotspot/os_cpu/windows_x86/os_windows_x86.inline.hpp
b/src/hotspot/os_cpu/windows_x86/os_windows_x86.inline.hpp
index f7622611da7..3461cd4c0b0 100644
--- a/src/hotspot/os_cpu/windows_x86/os_windows_x86.inline.hpp
+++ b/src/hotspot/os_cpu/windows_x86/os_windows_x86.inline.hpp
@@ -29,12 +29,14 @@
 #include "os_windows.hpp"

 #ifdef AMD64
+#ifdef _MSC_VER
 #define HAVE_PLATFORM_PRINT_NATIVE_STACK 1
 inline bool os::platform_print_native_stack(outputStream* st, const
void* context,
                                      char *buf, int buf_size,
address& lastpc) {
   return os::win32::platform_print_native_stack(st, context, buf,
buf_size, lastpc);
 }
 #endif
+#endif

 inline jlong os::rdtsc() {
   // 32 bit: 64 bit result in edx:eax
diff --git a/src/hotspot/share/runtime/os.cpp b/src/hotspot/share/runtime/os.cpp
index 7b766707b0d..d3613652f45 100644
--- a/src/hotspot/share/runtime/os.cpp
+++ b/src/hotspot/share/runtime/os.cpp
@@ -179,7 +179,7 @@ char* os::iso8601_time(jlong
milliseconds_since_19700101, char* buffer, size_t b
   // No offset when dealing with UTC
   time_t UTC_to_local = 0;
   if (!utc) {
-#if (defined(_ALLBSD_SOURCE) || defined(_GNU_SOURCE)) && !defined(AIX)
+#if (defined(_ALLBSD_SOURCE) || defined(_GNU_SOURCE)) &&
!defined(AIX) && !defined(_WIN32)
     UTC_to_local = -(time_struct.tm_gmtoff);
 #elif defined(_WINDOWS)
     long zone;
@@ -1349,7 +1349,9 @@ static bool is_pointer_bad(intptr_t* ptr) {
 bool os::is_first_C_frame(frame* fr) {

 #ifdef _WINDOWS
+#ifdef _MSC_VER
   return true; // native stack isn't walkable on windows this way.
+#endif
 #endif
   // Load up sp, fp, sender sp and sender fp, check for reasonable values.
   // Check usp first, because if that's bad the other accessors may fault


On Mon, Jul 8, 2024 at 4:49 PM David Holmes <david.holmes at oracle.com> wrote:
>
> On 8/07/2024 5:59 pm, Julian Waters wrote:
> > Hi David,
> >
> > Ah, I think you misunderstood me, I'm aware that the frame pointer is
> > saved as required by the compiler (With the exception of the Microsoft
> > compiler, which doesn't save it at all). What I meant was that the
> > comments in Windows code imply that VMError::print_native_stack and
> > os::get_sender_for_C_frame need to use the frame pointer, yet I can't
> > seem to find where or how either of them obtain the frame pointer for
> > whatever they use it for on platforms and compilers where the frame
> > pointer is saved (For instance, on Linux), whether through handwritten
> > assembly code or some other means. It follows that if they need to use
> > the frame pointer, then they must grab it from somewhere, after all
>
> Ah sorry. AFAICS we just create the frame() objects and wallk the stack
> via those. We use fetch_frame_from_context to kick things off in the
> case of a crash.
>
> David
>
> > best regards,
> > Julian


More information about the hotspot-dev mailing list