< prev index next >

src/java.base/share/classes/jdk/internal/vm/ThreadDumper.java

Print this page
*** 62,17 ***
       * Generate a thread dump in plain text format to a file or byte array, UTF-8 encoded.
       * This method is invoked by the VM for the Thread.dump_to_file diagnostic command.
       *
       * @param file the file path to the file, null or "-" to return a byte array
       * @param okayToOverwrite true to overwrite an existing file
       * @return the UTF-8 encoded thread dump or message to return to the tool user
       */
!     public static byte[] dumpThreads(String file, boolean okayToOverwrite) {
          if (file == null || file.equals("-")) {
!             return dumpThreadsToByteArray(false, MAX_BYTE_ARRAY_SIZE);
          } else {
!             return dumpThreadsToFile(file, okayToOverwrite, false);
          }
      }
  
      /**
       * Generate a thread dump in JSON format to a file or byte array, UTF-8 encoded.
--- 62,18 ---
       * Generate a thread dump in plain text format to a file or byte array, UTF-8 encoded.
       * This method is invoked by the VM for the Thread.dump_to_file diagnostic command.
       *
       * @param file the file path to the file, null or "-" to return a byte array
       * @param okayToOverwrite true to overwrite an existing file
+      * @param ignore not used
       * @return the UTF-8 encoded thread dump or message to return to the tool user
       */
!     public static byte[] dumpThreads(String file, boolean okayToOverwrite, boolean ignore) {
          if (file == null || file.equals("-")) {
!             return dumpThreadsToByteArray(false, false, MAX_BYTE_ARRAY_SIZE);
          } else {
!             return dumpThreadsToFile(file, okayToOverwrite, false, false);
          }
      }
  
      /**
       * Generate a thread dump in JSON format to a file or byte array, UTF-8 encoded.

*** 80,29 ***
       *
       * @param file the file path to the file, null or "-" to return a byte array
       * @param okayToOverwrite true to overwrite an existing file
       * @return the UTF-8 encoded thread dump or message to return to the tool user
       */
!     public static byte[] dumpThreadsToJson(String file, boolean okayToOverwrite) {
          if (file == null || file.equals("-")) {
!             return dumpThreadsToByteArray(true, MAX_BYTE_ARRAY_SIZE);
          } else {
!             return dumpThreadsToFile(file, okayToOverwrite, true);
          }
      }
  
      /**
       * Generate a thread dump in plain text or JSON format to a byte array, UTF-8 encoded.
       * This method is the implementation of the Thread.dump_to_file diagnostic command
       * when a file path is not specified. It returns the thread dump and/or message to
       * send to the tool user.
       */
!     private static byte[] dumpThreadsToByteArray(boolean json, int maxSize) {
          var out = new BoundedByteArrayOutputStream(maxSize);
          try (out; var writer = new TextWriter(out)) {
              if (json) {
!                 dumpThreadsToJson(writer);
              } else {
                  dumpThreads(writer);
              }
          } catch (Exception ex) {
              if (ex instanceof UncheckedIOException ioe) {
--- 81,29 ---
       *
       * @param file the file path to the file, null or "-" to return a byte array
       * @param okayToOverwrite true to overwrite an existing file
       * @return the UTF-8 encoded thread dump or message to return to the tool user
       */
!     public static byte[] dumpThreadsToJson(String file, boolean okayToOverwrite, boolean minify) {
          if (file == null || file.equals("-")) {
!             return dumpThreadsToByteArray(true, !minify, MAX_BYTE_ARRAY_SIZE);
          } else {
!             return dumpThreadsToFile(file, okayToOverwrite, true, minify);
          }
      }
  
      /**
       * Generate a thread dump in plain text or JSON format to a byte array, UTF-8 encoded.
       * This method is the implementation of the Thread.dump_to_file diagnostic command
       * when a file path is not specified. It returns the thread dump and/or message to
       * send to the tool user.
       */
!     private static byte[] dumpThreadsToByteArray(boolean json,  boolean minify, int maxSize) {
          var out = new BoundedByteArrayOutputStream(maxSize);
          try (out; var writer = new TextWriter(out)) {
              if (json) {
!                 dumpThreadsToJson(writer, minify);
              } else {
                  dumpThreads(writer);
              }
          } catch (Exception ex) {
              if (ex instanceof UncheckedIOException ioe) {

*** 117,20 ***
      /**
       * Generate a thread dump in plain text or JSON format to the given file, UTF-8 encoded.
       * This method is the implementation of the Thread.dump_to_file diagnostic command.
       * It returns the thread dump and/or message to send to the tool user.
       */
!     private static byte[] dumpThreadsToFile(String file, boolean okayToOverwrite, boolean json) {
          Path path = Path.of(file).toAbsolutePath();
          OpenOption[] options = (okayToOverwrite)
                  ? new OpenOption[0]
                  : new OpenOption[] { StandardOpenOption.CREATE_NEW };
          String reply;
          try (OutputStream out = Files.newOutputStream(path, options)) {
              try (var writer = new TextWriter(out)) {
                  if (json) {
!                     dumpThreadsToJson(writer);
                  } else {
                      dumpThreads(writer);
                  }
                  reply = String.format("Created %s%n", path);
              } catch (UncheckedIOException e) {
--- 118,23 ---
      /**
       * Generate a thread dump in plain text or JSON format to the given file, UTF-8 encoded.
       * This method is the implementation of the Thread.dump_to_file diagnostic command.
       * It returns the thread dump and/or message to send to the tool user.
       */
!     private static byte[] dumpThreadsToFile(String file,
+                                             boolean okayToOverwrite,
+                                             boolean json,
+                                             boolean minify) {
          Path path = Path.of(file).toAbsolutePath();
          OpenOption[] options = (okayToOverwrite)
                  ? new OpenOption[0]
                  : new OpenOption[] { StandardOpenOption.CREATE_NEW };
          String reply;
          try (OutputStream out = Files.newOutputStream(path, options)) {
              try (var writer = new TextWriter(out)) {
                  if (json) {
!                     dumpThreadsToJson(writer, minify);
                  } else {
                      dumpThreads(writer);
                  }
                  reply = String.format("Created %s%n", path);
              } catch (UncheckedIOException e) {

*** 178,11 ***
      }
  
      private static boolean dumpThread(Thread thread, TextWriter writer) {
          ThreadSnapshot snapshot = ThreadSnapshot.of(thread);
          if (snapshot == null) {
!             return false; // thread terminated
          }
          Instant now = Instant.now();
          Thread.State state = snapshot.threadState();
          writer.println("#" + thread.threadId() + " \"" + snapshot.threadName()
                  + "\" " + (thread.isVirtual() ? "virtual " : "") + state + " " + now);
--- 182,11 ---
      }
  
      private static boolean dumpThread(Thread thread, TextWriter writer) {
          ThreadSnapshot snapshot = ThreadSnapshot.of(thread);
          if (snapshot == null) {
!             return false; // thread not alive
          }
          Instant now = Instant.now();
          Thread.State state = snapshot.threadState();
          writer.println("#" + thread.threadId() + " \"" + snapshot.threadName()
                  + "\" " + (thread.isVirtual() ? "virtual " : "") + state + " " + now);

*** 240,11 ***
       * @throws IOException if an I/O error occurs
       */
      public static void dumpThreadsToJson(OutputStream out) throws IOException {
          var writer = new TextWriter(out);
          try {
!             dumpThreadsToJson(writer);
              writer.flush();
          } catch (UncheckedIOException e) {
              IOException ioe = e.getCause();
              throw ioe;
          }
--- 244,11 ---
       * @throws IOException if an I/O error occurs
       */
      public static void dumpThreadsToJson(OutputStream out) throws IOException {
          var writer = new TextWriter(out);
          try {
!             dumpThreadsToJson(writer, /*prettyPrint*/ true);
              writer.flush();
          } catch (UncheckedIOException e) {
              IOException ioe = e.getCause();
              throw ioe;
          }

*** 252,12 ***
  
      /**
       * Generate a thread dump to the given text stream in JSON format.
       * @throws UncheckedIOException if an I/O error occurs
       */
!     private static void dumpThreadsToJson(TextWriter textWriter) {
!         var jsonWriter = new JsonWriter(textWriter);
  
          jsonWriter.startObject();  // top-level object
  
          jsonWriter.startObject("threadDump");
  
--- 256,12 ---
  
      /**
       * Generate a thread dump to the given text stream in JSON format.
       * @throws UncheckedIOException if an I/O error occurs
       */
!     private static void dumpThreadsToJson(TextWriter textWriter, boolean minify) {
!         var jsonWriter = new JsonWriter(textWriter, minify);
  
          jsonWriter.startObject();  // top-level object
  
          jsonWriter.startObject("threadDump");
  

*** 316,11 ***
       */
      private static boolean dumpThread(Thread thread, JsonWriter jsonWriter) {
          Instant now = Instant.now();
          ThreadSnapshot snapshot = ThreadSnapshot.of(thread);
          if (snapshot == null) {
!             return false; // thread terminated
          }
          Thread.State state = snapshot.threadState();
          StackTraceElement[] stackTrace = snapshot.stackTrace();
  
          jsonWriter.startObject();
--- 320,11 ---
       */
      private static boolean dumpThread(Thread thread, JsonWriter jsonWriter) {
          Instant now = Instant.now();
          ThreadSnapshot snapshot = ThreadSnapshot.of(thread);
          if (snapshot == null) {
!             return false; // thread not alive
          }
          Thread.State state = snapshot.threadState();
          StackTraceElement[] stackTrace = snapshot.stackTrace();
  
          jsonWriter.startObject();

*** 410,35 ***
                  return old;
              }
          }
          private final Deque<Node> stack = new ArrayDeque<>();
          private final TextWriter writer;
  
!         JsonWriter(TextWriter writer) {
              this.writer = writer;
          }
  
          private void indent() {
!             int indent = stack.size() * 2;
!             writer.print(" ".repeat(indent));
          }
  
          /**
           * Start of object or array.
           */
          private void startObject(String name, boolean isArray) {
              if (!stack.isEmpty()) {
                  Node node = stack.peek();
                  if (node.getAndIncrementPropertyCount() > 0) {
!                     writer.println(",");
                  }
              }
              indent();
              if (name != null) {
!                 writer.print("\"" + name + "\": ");
              }
!             writer.println(isArray ? "[" : "{");
              stack.push(new Node(isArray));
          }
  
          /**
           * End of object or array.
--- 414,58 ---
                  return old;
              }
          }
          private final Deque<Node> stack = new ArrayDeque<>();
          private final TextWriter writer;
+         private final boolean prettyPrint;  // pretty print or minify
  
!         JsonWriter(TextWriter writer, boolean minify) {
              this.writer = writer;
+             this.prettyPrint = !minify;
+         }
+ 
+         private void print(Object obj) {
+             writer.print(obj);
+         }
+ 
+         private void println(Object obj) {
+             if (prettyPrint) {
+                 writer.println(obj);
+             } else {
+                 writer.print(obj);
+             }
+         }
+ 
+         private void println() {
+             if (prettyPrint) {
+                 writer.println();
+             }
          }
  
          private void indent() {
!             if (prettyPrint) {
!                 int indent = stack.size() * 2;
+                 writer.print(" ".repeat(indent));
+             }
          }
  
          /**
           * Start of object or array.
           */
          private void startObject(String name, boolean isArray) {
              if (!stack.isEmpty()) {
                  Node node = stack.peek();
                  if (node.getAndIncrementPropertyCount() > 0) {
!                     println(",");
                  }
              }
              indent();
              if (name != null) {
!                 String gap = prettyPrint ? " " : "";
+                 print("\"" + name + "\":" + gap);
              }
!             println(isArray ? "[" : "{");
              stack.push(new Node(isArray));
          }
  
          /**
           * End of object or array.

*** 446,37 ***
          private void endObject(boolean isArray) {
              Node node = stack.pop();
              if (node.isArray() != isArray)
                  throw new IllegalStateException();
              if (node.propertyCount() > 0) {
!                 writer.println();
              }
              indent();
!             writer.print(isArray ? "]" : "}");
          }
  
          /**
           * Write a property.
           * @param name the property name, null for an unnamed property
           * @param obj the value or null
           */
          void writeProperty(String name, Object obj) {
              Node node = stack.peek();
              if (node.getAndIncrementPropertyCount() > 0) {
!                 writer.println(",");
              }
              indent();
              if (name != null) {
!                 writer.print("\"" + name + "\": ");
              }
              switch (obj) {
                  // Long may be larger than safe range of JSON integer value
!                 case Long   _  -> writer.print("\"" + obj + "\"");
!                 case Number _  -> writer.print(obj);
!                 case Boolean _ -> writer.print(obj);
!                 case null      -> writer.print("null");
!                 default        -> writer.print("\"" + escape(obj.toString()) + "\"");
              }
          }
  
          /**
           * Write an unnamed property.
--- 473,38 ---
          private void endObject(boolean isArray) {
              Node node = stack.pop();
              if (node.isArray() != isArray)
                  throw new IllegalStateException();
              if (node.propertyCount() > 0) {
!                 println();
              }
              indent();
!             print(isArray ? "]" : "}");
          }
  
          /**
           * Write a property.
           * @param name the property name, null for an unnamed property
           * @param obj the value or null
           */
          void writeProperty(String name, Object obj) {
              Node node = stack.peek();
+             assert node != null;
              if (node.getAndIncrementPropertyCount() > 0) {
!                 println(",");
              }
              indent();
              if (name != null) {
!                 print("\"" + name + "\": ");
              }
              switch (obj) {
                  // Long may be larger than safe range of JSON integer value
!                 case Long   _  -> print("\"" + obj + "\"");
!                 case Number _  -> print(obj);
!                 case Boolean _ -> print(obj);
!                 case null      -> print("null");
!                 default        -> print("\"" + escape(obj.toString()) + "\"");
              }
          }
  
          /**
           * Write an unnamed property.
< prev index next >