From b61cd484f501a1fe7d49c336878a4b8398e727d9 Mon Sep 17 00:00:00 2001
From: eapen <zhangyipeng7@huawei.com>
Date: Thu, 15 Dec 2022 14:28:05 +0800
Subject: [PATCH 20/33] I68TO2: 8219584: Try to dump error file by thread which causes
 safepoint timeout
---
 hotspot/src/os/posix/vm/os_posix.cpp               | 31 ++++++-
 hotspot/src/os/windows/vm/os_windows.cpp           |  9 ++
 hotspot/src/share/vm/runtime/globals.hpp           |  2 +-
 hotspot/src/share/vm/runtime/os.hpp                |  4 +
 hotspot/src/share/vm/runtime/safepoint.cpp         | 21 +++--
 hotspot/src/share/vm/runtime/vmThread.cpp          | 36 +++++---
 hotspot/src/share/vm/runtime/vmThread.hpp          | 10 ++-
 hotspot/src/share/vm/utilities/vmError.cpp         |  3 +
 .../Safepoint/TestAbortVMOnSafepointTimeout.java   | 97 ++++++++++++++++++++++
 9 files changed, 195 insertions(+), 18 deletions(-)
 create mode 100644 hotspot/test/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java

diff --git a/hotspot/src/os/posix/vm/os_posix.cpp b/hotspot/src/os/posix/vm/os_posix.cpp
index e7f1fdd..d2663bd 100644
--- a/hotspot/src/os/posix/vm/os_posix.cpp
+++ b/hotspot/src/os/posix/vm/os_posix.cpp
@@ -26,6 +26,7 @@
 #include "prims/jvm.h"
 #include "runtime/frame.inline.hpp"
 #include "runtime/os.hpp"
+#include "utilities/events.hpp"
 #include "utilities/vmError.hpp"

 #include <signal.h>
@@ -814,6 +815,15 @@ static bool get_signal_code_description(const siginfo_t* si, enum_sigcode_desc_t
   return true;
 }

+bool os::signal_sent_by_kill(const void* siginfo) {
+  const siginfo_t* const si = (const siginfo_t*)siginfo;
+  return si->si_code == SI_USER || si->si_code == SI_QUEUE
+#ifdef SI_TKILL
+         || si->si_code == SI_TKILL
+#endif
+  ;
+}
+
 // A POSIX conform, platform-independend siginfo print routine.
 // Short print out on one line.
 void os::Posix::print_siginfo_brief(outputStream* os, const siginfo_t* si) {
@@ -844,7 +854,7 @@ void os::Posix::print_siginfo_brief(outputStream* os, const siginfo_t* si) {
   const int me = (int) ::getpid();
   const int pid = (int) si->si_pid;

-  if (si->si_code == SI_USER || si->si_code == SI_QUEUE) {
+  if (signal_sent_by_kill(si)) {
     if (IS_VALID_PID(pid) && pid != me) {
       os->print(", sent from pid: %d (uid: %d)", pid, (int) si->si_uid);
     }
@@ -860,6 +870,25 @@ void os::Posix::print_siginfo_brief(outputStream* os, const siginfo_t* si) {
   }
 }

+bool os::signal_thread(Thread* thread, int sig, const char* reason) {
+  OSThread* osthread = thread->osthread();
+  if (osthread) {
+#if defined (SOLARIS)
+    // Note: we cannot use pthread_kill on Solaris - not because
+    // its missing, but because we do not have the pthread_t id.
+    int status = thr_kill(osthread->thread_id(), sig);
+#else
+    int status = pthread_kill(osthread->pthread_id(), sig);
+#endif
+    if (status == 0) {
+      Events::log(Thread::current(), "sent signal %d to Thread " INTPTR_FORMAT " because %s.",
+                  sig, p2i(thread), reason);
+      return true;
+    }
+  }
+  return false;
+}
+
 bool os::Posix::is_root(uid_t uid){
     return ROOT_UID == uid;
 }
diff --git a/hotspot/src/os/windows/vm/os_windows.cpp b/hotspot/src/os/windows/vm/os_windows.cpp
index cc31126..cf1036c 100644
--- a/hotspot/src/os/windows/vm/os_windows.cpp
+++ b/hotspot/src/os/windows/vm/os_windows.cpp
@@ -1877,6 +1877,11 @@ void os::print_memory_info(outputStream* st) {
   st->cr();
 }

+bool os::signal_sent_by_kill(const void* siginfo) {
+  // TODO: Is this possible?
+  return false;
+}
+
 void os::print_siginfo(outputStream *st, void *siginfo) {
   EXCEPTION_RECORD* er = (EXCEPTION_RECORD*)siginfo;
   st->print("siginfo:");
@@ -1911,6 +1916,10 @@ void os::print_siginfo(outputStream *st, void *siginfo) {
   st->cr();
 }

+bool os::signal_thread(Thread* thread, int sig, const char* reason) {
+  // TODO: Can we kill thread?
+  return false;
+}

 int os::vsnprintf(char* buf, size_t len, const char* fmt, va_list args) {
 #if _MSC_VER >= 1900
diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp
index 10e4e7f..64d40e0 100644
--- a/hotspot/src/share/vm/runtime/globals.hpp
+++ b/hotspot/src/share/vm/runtime/globals.hpp
@@ -650,7 +650,7 @@ class CommandLineFlags {
           "Print out every time compilation is longer than "                \
           "a given threshold")                                              \
                                                                             \
-  develop(bool, SafepointALot, false,                                       \
+  diagnostic(bool, SafepointALot, false,                                    \
           "Generate a lot of safepoints. This works with "                  \
           "GuaranteedSafepointInterval")                                    \
                                                                             \
diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp
index 5f41e96..092459c 100644
--- a/hotspot/src/share/vm/runtime/os.hpp
+++ b/hotspot/src/share/vm/runtime/os.hpp
@@ -492,6 +492,9 @@ class os: AllStatic {
   static void pd_start_thread(Thread* thread);
   static void start_thread(Thread* thread);

+  // Returns true if successful.
+  static bool signal_thread(Thread* thread, int sig, const char* reason);
+
   static void initialize_thread(Thread* thr);
   static void free_thread(OSThread* osthread);

@@ -653,6 +656,7 @@ class os: AllStatic {
   static void print_environment_variables(outputStream* st, const char** env_list, char* buffer, int len);
   static void print_context(outputStream* st, void* context);
   static void print_register_info(outputStream* st, void* context);
+  static bool signal_sent_by_kill(const void* siginfo);
   static void print_siginfo(outputStream* st, void* siginfo);
   static void print_signal_handlers(outputStream* st, char* buf, size_t buflen);
   static void print_date_and_time(outputStream* st, char* buf, size_t buflen);
diff --git a/hotspot/src/share/vm/runtime/safepoint.cpp b/hotspot/src/share/vm/runtime/safepoint.cpp
index 440617c..8408bed 100644
--- a/hotspot/src/share/vm/runtime/safepoint.cpp
+++ b/hotspot/src/share/vm/runtime/safepoint.cpp
@@ -476,8 +476,7 @@ void SafepointSynchronize::begin() {
   GC_locker::set_jni_lock_count(_current_jni_active_count);

   if (TraceSafepoint) {
-    VM_Operation *op = VMThread::vm_operation();
-    tty->print_cr("Entering safepoint region: %s", (op != NULL) ? op->name() : "no vm operation");
+    tty->print_cr("Entering safepoint region: %s", VMThread::vm_safepoint_description());
   }

   RuntimeService::record_safepoint_synchronized();
@@ -929,11 +928,23 @@ void SafepointSynchronize::print_safepoint_timeout(SafepointTimeoutReason reason
   // To debug the long safepoint, specify both AbortVMOnSafepointTimeout &
   // ShowMessageBoxOnError.
   if (AbortVMOnSafepointTimeout) {
+    // Send the blocking thread a signal to terminate and write an error file.
+    for (JavaThread *cur_thread = Threads::first(); cur_thread;
+         cur_thread = cur_thread->next()) {
+      ThreadSafepointState *cur_state = cur_thread->safepoint_state();
+      if (cur_thread->thread_state() != _thread_blocked &&
+          ((reason == _spinning_timeout && cur_state->is_running()) ||
+             (reason == _blocking_timeout && !cur_state->has_called_back()))) {
+        if (!os::signal_thread(cur_thread, SIGILL, "blocking a safepoint")) {
+          break; // Could not send signal. Report fatal error.
+        }
+        // Give cur_thread a chance to report the error and terminate the VM.
+        os::sleep(Thread::current(), 3000, false);
+      }
+    }
     char msg[1024];
-    VM_Operation *op = VMThread::vm_operation();
     sprintf(msg, "Safepoint sync time longer than " INTX_FORMAT "ms detected when executing %s.",
-            SafepointTimeoutDelay,
-            op != NULL ? op->name() : "no vm operation");
+            SafepointTimeoutDelay, VMThread::vm_safepoint_description());
     fatal(msg);
   }
 }
diff --git a/hotspot/src/share/vm/runtime/vmThread.cpp b/hotspot/src/share/vm/runtime/vmThread.cpp
index b27c287..4f1695e 100644
--- a/hotspot/src/share/vm/runtime/vmThread.cpp
+++ b/hotspot/src/share/vm/runtime/vmThread.cpp
@@ -217,6 +217,7 @@ VMThread*         VMThread::_vm_thread          = NULL;
 VM_Operation*     VMThread::_cur_vm_operation   = NULL;
 VMOperationQueue* VMThread::_vm_queue           = NULL;
 PerfCounter*      VMThread::_perf_accumulated_vm_operation_time = NULL;
+const char*       VMThread::_no_op_reason       = NULL;


 void VMThread::create() {
@@ -290,6 +291,7 @@ void VMThread::run() {
   }

   // 4526887 let VM thread exit at Safepoint
+  _no_op_reason = "Halt";
   SafepointSynchronize::begin();

   if (VerifyBeforeExit) {
@@ -422,6 +424,25 @@ void VMThread::evaluate_operation(VM_Operation* op) {
   }
 }

+bool VMThread::no_op_safepoint_needed(bool check_time) {
+  if (SafepointALot) {
+    _no_op_reason = "SafepointALot";
+    return true;
+  }
+  if (!SafepointSynchronize::is_cleanup_needed()) {
+    return false;
+  }
+  if (check_time) {
+    long interval = SafepointSynchronize::last_non_safepoint_interval();
+    bool max_time_exceeded = GuaranteedSafepointInterval != 0 &&
+                             (interval > GuaranteedSafepointInterval);
+    if (!max_time_exceeded) {
+      return false;
+    }
+  }
+  _no_op_reason = "Cleanup";
+  return true;
+}

 void VMThread::loop() {
   assert(_cur_vm_operation == NULL, "no current one should be executing");
@@ -460,8 +481,7 @@ void VMThread::loop() {
           exit(-1);
         }

-        if (timedout && (SafepointALot ||
-                         SafepointSynchronize::is_cleanup_needed())) {
+        if (timedout && VMThread::no_op_safepoint_needed(false)) {
           MutexUnlockerEx mul(VMOperationQueue_lock,
                               Mutex::_no_safepoint_check_flag);
           // Force a safepoint since we have not had one for at least
@@ -585,14 +605,10 @@ void VMThread::loop() {
     //
     // We want to make sure that we get to a safepoint regularly.
     //
-    if (SafepointALot || SafepointSynchronize::is_cleanup_needed()) {
-      long interval          = SafepointSynchronize::last_non_safepoint_interval();
-      bool max_time_exceeded = GuaranteedSafepointInterval != 0 && (interval > GuaranteedSafepointInterval);
-      if (SafepointALot || max_time_exceeded) {
-        HandleMark hm(VMThread::vm_thread());
-        SafepointSynchronize::begin();
-        SafepointSynchronize::end();
-      }
+    if (VMThread::no_op_safepoint_needed(true)) {
+      HandleMark hm(VMThread::vm_thread());
+      SafepointSynchronize::begin();
+      SafepointSynchronize::end();
     }
   }
 }
diff --git a/hotspot/src/share/vm/runtime/vmThread.hpp b/hotspot/src/share/vm/runtime/vmThread.hpp
index a6d1ad3..d8af0d9 100644
--- a/hotspot/src/share/vm/runtime/vmThread.hpp
+++ b/hotspot/src/share/vm/runtime/vmThread.hpp
@@ -100,7 +100,12 @@ class VMThread: public NamedThread {
   static Monitor * _terminate_lock;
   static PerfCounter* _perf_accumulated_vm_operation_time;

+  static const char* _no_op_reason;
+
+  static bool no_op_safepoint_needed(bool check_time);
+
   void evaluate_operation(VM_Operation* op);
+
  public:
   // Constructor
   VMThread();
@@ -123,7 +128,10 @@ class VMThread: public NamedThread {
   static void execute(VM_Operation* op);

   // Returns the current vm operation if any.
-  static VM_Operation* vm_operation()             { return _cur_vm_operation;   }
+  static VM_Operation* vm_operation()             { return _cur_vm_operation; }
+
+  // Returns the current vm operation name or set reason
+  static const char* vm_safepoint_description()   { return _cur_vm_operation != NULL ? _cur_vm_operation->name() : _no_op_reason; };

   // Returns the single instance of VMThread.
   static VMThread* vm_thread()                    { return _vm_thread; }
diff --git a/hotspot/src/share/vm/utilities/vmError.cpp b/hotspot/src/share/vm/utilities/vmError.cpp
index 9b40a34..261591d 100644
--- a/hotspot/src/share/vm/utilities/vmError.cpp
+++ b/hotspot/src/share/vm/utilities/vmError.cpp
@@ -460,6 +460,9 @@ void VMError::report(outputStream* st) {
        st->print("%s", buf);
        st->print(" (0x%x)", _id);                // signal number
        st->print(" at pc=" PTR_FORMAT, _pc);
+       if (_siginfo != NULL && os::signal_sent_by_kill(_siginfo)) {
+         st->print(" (sent by kill)");
+       }
      } else {
        if (should_report_bug(_id)) {
          st->print("Internal Error");
diff --git a/hotspot/test/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java b/hotspot/test/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java
new file mode 100644
index 0000000..a097bdc
--- /dev/null
+++ b/hotspot/test/runtime/Safepoint/TestAbortVMOnSafepointTimeout.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2019, SAP SE. All rights reserved.
+ * Copyright (c) 2021, Huawei Technologies Co., Ltd. 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.
+ */
+
+import com.oracle.java.testlibrary.*;
+
+/*
+ * @test TestAbortVMOnSafepointTimeout
+ * @summary Check if VM can kill thread which doesn't reach safepoint.
+ * @bug 8219584 8227528
+ * @library /testlibrary
+ *
+ */
+
+public class TestAbortVMOnSafepointTimeout {
+
+    public static void main(String[] args) throws Exception {
+        if (args.length > 0) {
+            int result = test_loop(3);
+            System.out.println("This message would occur after some time with result " + result);
+            return;
+        }
+
+        testWith(500, 500);
+    }
+
+    static int test_loop(int x) {
+        int sum = 0;
+        if (x != 0) {
+            // Long running loop without safepoint.
+            for (int y = 1; y < Integer.MAX_VALUE; ++y) {
+                if (y % x == 0) ++sum;
+            }
+        }
+        return sum;
+    }
+
+    public static void testWith(int sfpt_interval, int timeout_delay) throws Exception {
+        // -XX:-UseCountedLoopSafepoints - is used to prevent the loop
+        // in test_loop() to poll for safepoints.
+        // -XX:LoopStripMiningIter=0 and -XX:LoopUnrollLimit=0 - are
+        // used to prevent optimizations over the loop in test_loop()
+        // since we actually want it to provoke a safepoint timeout.
+        // -XX:-UseBiasedLocking - is used to prevent biased locking
+        // handshakes from changing the timing of this test.
+        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+                "-XX:+UnlockDiagnosticVMOptions",
+                "-XX:-UseBiasedLocking",
+                "-XX:+SafepointTimeout",
+                "-XX:+SafepointALot",
+                "-XX:+AbortVMOnSafepointTimeout",
+                "-XX:SafepointTimeoutDelay=" + timeout_delay,
+                "-XX:GuaranteedSafepointInterval=" + sfpt_interval,
+                "-XX:-TieredCompilation",
+                "-XX:-UseCountedLoopSafepoints",
+                "-XX:LoopUnrollLimit=0",
+                "-XX:CompileCommand=compileonly,TestAbortVMOnSafepointTimeout::test_loop",
+                "-Xcomp",
+                "-XX:-CreateMinidumpOnCrash",
+                "-Xms64m",
+                "TestAbortVMOnSafepointTimeout",
+                "runTestLoop"
+        );
+
+        OutputAnalyzer output = new OutputAnalyzer(pb.start());
+        if (Platform.isWindows()) {
+            output.shouldMatch("Safepoint sync time longer than");
+        } else {
+            output.shouldMatch("SIGILL");
+            if (Platform.isLinux()) {
+                output.shouldMatch("(sent by kill)");
+            }
+            output.shouldMatch("TestAbortVMOnSafepointTimeout.test_loop");
+        }
+        output.shouldNotHaveExitValue(0);
+    }
+}
--
1.8.3.1
