8214975: No hs-err file if fatal error is raised during dynamic initialization
authorstuefe
Sat, 08 Dec 2018 12:09:59 +0100
changeset 53599 0de1f006d3c3
parent 53598 69ccc5584e12
child 53600 13e19ab0d687
child 57138 78ff2c6ad68d
8214975: No hs-err file if fatal error is raised during dynamic initialization Reviewed-by: dholmes, dcubed
src/hotspot/share/utilities/debug.cpp
src/hotspot/share/utilities/vmError.cpp
src/hotspot/share/utilities/vmError.hpp
test/hotspot/jtreg/runtime/ErrorHandling/VeryEarlyAssertTest.java
--- a/src/hotspot/share/utilities/debug.cpp	Fri Feb 01 10:27:45 2019 +0100
+++ b/src/hotspot/share/utilities/debug.cpp	Sat Dec 08 12:09:59 2018 +0100
@@ -92,6 +92,21 @@
 #  endif
 #endif // PRODUCT
 
+#ifdef ASSERT
+// This is to test that error reporting works if we assert during dynamic
+// initialization of the hotspot. See JDK-8214975.
+struct Crasher {
+  Crasher() {
+    // Using getenv - no other mechanism would work yet.
+    const char* s = ::getenv("HOTSPOT_FATAL_ERROR_DURING_DYNAMIC_INITIALIZATION");
+    if (s != NULL && ::strcmp(s, "1") == 0) {
+      fatal("HOTSPOT_FATAL_ERROR_DURING_DYNAMIC_INITIALIZATION");
+    }
+  }
+};
+static Crasher g_crasher;
+#endif // ASSERT
+
 ATTRIBUTE_PRINTF(1, 2)
 void warning(const char* format, ...) {
   if (PrintWarnings) {
--- a/src/hotspot/share/utilities/vmError.cpp	Fri Feb 01 10:27:45 2019 +0100
+++ b/src/hotspot/share/utilities/vmError.cpp	Sat Dec 08 12:09:59 2018 +0100
@@ -1189,16 +1189,6 @@
 
 volatile intptr_t VMError::first_error_tid = -1;
 
-// An error could happen before tty is initialized or after it has been
-// destroyed.
-// Please note: to prevent large stack allocations, the log- and
-// output-stream use a global scratch buffer for format printing.
-// (see VmError::report_and_die(). Access to those streams is synchronized
-// in  VmError::report_and_die() - there is only one reporting thread at
-// any given time.
-fdStream VMError::out(defaultStream::output_fd());
-fdStream VMError::log; // error log used by VMError::report_and_die()
-
 /** Expand a pattern into a buffer starting at pos and open a file using constructed path */
 static int expand_and_open(const char* pattern, char* buf, size_t buflen, size_t pos) {
   int fd = -1;
@@ -1303,9 +1293,25 @@
                              Thread* thread, address pc, void* siginfo, void* context, const char* filename,
                              int lineno, size_t size)
 {
-  // Don't allocate large buffer on stack
+  // A single scratch buffer to be used from here on.
+  // Do not rely on it being preserved across function calls.
   static char buffer[O_BUFLEN];
+
+  // File descriptor to tty to print an error summary to.
+  // Hard wired to stdout; see JDK-8215004 (compatibility concerns).
+  static const int fd_out = 1; // stdout
+
+  // File descriptor to the error log file.
+  static int fd_log = -1;
+
+  // Use local fdStream objects only. Do not use global instances whose initialization
+  // relies on dynamic initialization (see JDK-8214975). Do not rely on these instances
+  // to carry over into recursions or invocations from other threads.
+  fdStream out(fd_out);
   out.set_scratch_buffer(buffer, sizeof(buffer));
+
+  // Depending on the re-entrance depth at this point, fd_log may be -1 or point to an open hs-err file.
+  fdStream log(fd_log);
   log.set_scratch_buffer(buffer, sizeof(buffer));
 
   // How many errors occurred in error handler when reporting first_error.
@@ -1451,15 +1457,15 @@
     // see if log file is already open
     if (!log.is_open()) {
       // open log file
-      int fd = prepare_log_file(ErrorFile, "hs_err_pid%p.log", buffer, sizeof(buffer));
-      if (fd != -1) {
+      fd_log = prepare_log_file(ErrorFile, "hs_err_pid%p.log", buffer, sizeof(buffer));
+      if (fd_log != -1) {
         out.print_raw("# An error report file with more information is saved as:\n# ");
         out.print_raw_cr(buffer);
 
-        log.set_fd(fd);
+        log.set_fd(fd_log);
       } else {
         out.print_raw_cr("# Can not save log file, dump to screen..");
-        log.set_fd(defaultStream::output_fd());
+        log.set_fd(fd_out);
       }
     }
 
@@ -1468,8 +1474,9 @@
     _current_step = 0;
     _current_step_info = "";
 
-    if (log.fd() != defaultStream::output_fd()) {
-      close(log.fd());
+    if (fd_log != -1) {
+      close(fd_log);
+      fd_log = -1;
     }
 
     log.set_fd(-1);
--- a/src/hotspot/share/utilities/vmError.hpp	Fri Feb 01 10:27:45 2019 +0100
+++ b/src/hotspot/share/utilities/vmError.hpp	Sat Dec 08 12:09:59 2018 +0100
@@ -122,9 +122,6 @@
                              void* context, const char* detail_fmt, ...) ATTRIBUTE_PRINTF(6, 7);
   static void report_and_die(const char* message, const char* detail_fmt, ...) ATTRIBUTE_PRINTF(2, 3);
 
-  static fdStream out;
-  static fdStream log; // error log used by VMError::report_and_die()
-
   // Timeout handling.
   // Hook functions for platform dependend functionality:
   static void reporting_started();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/ErrorHandling/VeryEarlyAssertTest.java	Sat Dec 08 12:09:59 2018 +0100
@@ -0,0 +1,118 @@
+/*
+ * Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018, SAP. 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
+ * @bug 8214975
+ * @summary No hs-err file if fatal error is raised during dynamic initialization.
+ * @library /test/lib
+ * @modules java.base/jdk.internal.misc
+ * @requires (vm.debug == true)
+ * @requires os.family == "linux"
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.util.regex.Pattern;
+import java.util.Map;
+
+import jdk.test.lib.process.OutputAnalyzer;
+import jdk.test.lib.process.ProcessTools;
+
+public class VeryEarlyAssertTest {
+
+  public static void main(String[] args) throws Exception {
+
+
+    ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+            "-Xmx64M",
+            "-XX:-CreateCoredumpOnCrash",
+            "-version");
+    Map<String, String> env = pb.environment();
+    env.put("HOTSPOT_FATAL_ERROR_DURING_DYNAMIC_INITIALIZATION", "1");
+
+    OutputAnalyzer output_detail = new OutputAnalyzer(pb.start());
+
+    // we should have crashed with an assert with a specific message:
+    output_detail.shouldMatch("# A fatal error has been detected by the Java Runtime Environment:.*");
+    output_detail.shouldMatch("#.*HOTSPOT_FATAL_ERROR_DURING_DYNAMIC_INITIALIZATION.*");
+
+    // extract hs-err file
+    String hs_err_file = output_detail.firstMatch("# *(\\S*hs_err_pid\\d+\\.log)", 1);
+    if (hs_err_file == null) {
+      throw new RuntimeException("Did not find hs-err file in output.\n");
+    }
+
+    // scan hs-err file: File should contain the same assertion message. Other than that,
+    // do not expect too much: file will be littered with secondary errors. The test
+    // should test that we get a hs-err file at all.
+    File f = new File(hs_err_file);
+    if (!f.exists()) {
+      throw new RuntimeException("hs-err file missing at "
+              + f.getAbsolutePath() + ".\n");
+    }
+
+    System.out.println("Found hs_err file. Scanning...");
+
+    FileInputStream fis = new FileInputStream(f);
+    BufferedReader br = new BufferedReader(new InputStreamReader(fis));
+    String line = null;
+
+    Pattern[] pattern = new Pattern[]{
+            Pattern.compile(".*HOTSPOT_FATAL_ERROR_DURING_DYNAMIC_INITIALIZATION.*")
+    };
+    int currentPattern = 0;
+
+    String lastLine = null;
+    while ((line = br.readLine()) != null) {
+      if (currentPattern < pattern.length) {
+        if (pattern[currentPattern].matcher(line).matches()) {
+          System.out.println("Found: " + line + ".");
+          currentPattern++;
+        }
+      }
+      lastLine = line;
+    }
+    br.close();
+
+    if (currentPattern < pattern.length) {
+      throw new RuntimeException("hs-err file incomplete (first missing pattern: " + currentPattern + ")");
+    }
+
+    if (!lastLine.equals("END.")) {
+      throw new RuntimeException("hs-err file incomplete (missing END marker.)");
+    } else {
+      System.out.println("End marker found.");
+    }
+
+    System.out.println("OK.");
+
+  }
+
+}
+
+