1 /* 2 * Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8284161 8287008 27 * @summary Basic test for jcmd Thread.dump_to_file 28 * @modules jdk.jcmd 29 * @library /test/lib 30 * @run junit/othervm ThreadDumpToFileTest 31 */ 32 33 import java.io.IOException; 34 import java.nio.file.Files; 35 import java.nio.file.Path; 36 import java.util.stream.Stream; 37 import jdk.test.lib.dcmd.PidJcmdExecutor; 38 import jdk.test.lib.process.OutputAnalyzer; 39 import jdk.test.lib.threaddump.ThreadDump; 40 41 import org.junit.jupiter.api.Test; 42 import static org.junit.jupiter.api.Assertions.*; 43 44 class ThreadDumpToFileTest { 45 46 /** 47 * Test thread dump, should be in plain text format. 48 */ 49 @Test 50 void testThreadDump() throws IOException { 51 Path file = genThreadDumpPath(".txt"); 52 testPlainThreadDump(file); 53 } 54 55 /** 56 * Test thread dump in plain text format. 57 */ 58 @Test 59 void testPlainThreadDump() throws IOException { 60 Path file = genThreadDumpPath(".txt"); 61 testPlainThreadDump(file, "-format=plain"); 62 } 63 64 /** 65 * Test thread dump in JSON format. 66 */ 67 @Test 68 void testJsonThreadDump() throws IOException { 69 Path file = genThreadDumpPath(".json"); 70 jcmdThreadDumpToFile(file, "-format=json").shouldMatch("Created"); 71 72 // parse the JSON text 73 String jsonText = Files.readString(file); 74 ThreadDump threadDump = ThreadDump.parse(jsonText); 75 76 // test that the process id is this process 77 assertTrue(threadDump.processId() == ProcessHandle.current().pid()); 78 79 // test that the current thread is in the root thread container 80 var rootContainer = threadDump.rootThreadContainer(); 81 var tid = Thread.currentThread().threadId(); 82 rootContainer.findThread(tid).orElseThrow(); 83 } 84 85 /** 86 * Test that an existing file is not overwritten. 87 */ 88 @Test 89 void testDoNotOverwriteFile() throws IOException { 90 Path file = genThreadDumpPath(".txt"); 91 Files.writeString(file, "xxx"); 92 93 jcmdThreadDumpToFile(file, "").shouldMatch("exists"); 94 95 // file should not be overridden 96 assertEquals("xxx", Files.readString(file)); 97 } 98 99 /** 100 * Test overwriting an existing file. 101 */ 102 @Test 103 void testOverwriteFile() throws IOException { 104 Path file = genThreadDumpPath(".txt"); 105 Files.writeString(file, "xxx"); 106 jcmdThreadDumpToFile(file, "-overwrite"); 107 } 108 109 /** 110 * Test thread dump in plain text format. 111 */ 112 private void testPlainThreadDump(Path file, String... options) throws IOException { 113 jcmdThreadDumpToFile(file, options).shouldMatch("Created"); 114 115 // test that thread dump contains the name and id of the current thread 116 String name = Thread.currentThread().getName(); 117 long tid = Thread.currentThread().threadId(); 118 String expected = "#" + tid + " \"" + name + "\""; 119 assertTrue(find(file, expected), expected + " not found in " + file); 120 } 121 122 /** 123 * Generate a file path with the given suffix to use for the thread dump. 124 */ 125 private Path genThreadDumpPath(String suffix) throws IOException { 126 Path dir = Path.of(".").toAbsolutePath(); 127 Path file = Files.createTempFile(dir, "threads-", suffix); 128 Files.delete(file); 129 return file; 130 } 131 132 /** 133 * Launches jcmd Thread.dump_to_file to obtain a thread dump of this VM. 134 */ 135 private OutputAnalyzer jcmdThreadDumpToFile(Path file, String... options) { 136 String cmd = "Thread.dump_to_file"; 137 for (String option : options) { 138 cmd += " " + option; 139 } 140 return new PidJcmdExecutor().execute(cmd + " " + file); 141 } 142 143 /** 144 * Returns true if the given file contains a line with the string. 145 */ 146 private boolean find(Path file, String text) throws IOException { 147 try (Stream<String> stream = Files.lines(file)) { 148 return stream.anyMatch(line -> line.indexOf(text) >= 0); 149 } 150 } 151 }