1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.testsuite.util;
17
18 import io.netty.util.CharsetUtil;
19 import io.netty.util.internal.ObjectUtil;
20 import io.netty.util.internal.logging.InternalLogger;
21 import io.netty.util.internal.logging.InternalLoggerFactory;
22 import org.junit.jupiter.api.TestInfo;
23 import org.tukaani.xz.LZMA2Options;
24 import org.tukaani.xz.XZOutputStream;
25
26 import javax.management.MBeanServer;
27 import java.io.File;
28 import java.io.FilenameFilter;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.OutputStream;
32 import java.lang.management.ManagementFactory;
33 import java.lang.management.ThreadInfo;
34 import java.lang.reflect.Method;
35 import java.nio.channels.Channel;
36 import java.nio.file.Files;
37 import java.nio.file.Paths;
38 import java.text.SimpleDateFormat;
39 import java.util.Date;
40 import java.util.Locale;
41 import java.util.concurrent.TimeUnit;
42 import java.util.function.Function;
43
44 public final class TestUtils {
45
46 private static final InternalLogger logger = InternalLoggerFactory.getInstance(TestUtils.class);
47
48 private static final Method hotspotMXBeanDumpHeap;
49 private static final Object hotspotMXBean;
50
51 private static final long DUMP_PROGRESS_LOGGING_INTERVAL = TimeUnit.SECONDS.toNanos(5);
52
53 static {
54
55 Object mxBean;
56 Method mxBeanDumpHeap;
57 try {
58 Class<?> clazz = Class.forName("com.sun.management.HotSpotDiagnosticMXBean");
59 MBeanServer server = ManagementFactory.getPlatformMBeanServer();
60 mxBean = ManagementFactory.newPlatformMXBeanProxy(
61 server, "com.sun.management:type=HotSpotDiagnostic", clazz);
62 mxBeanDumpHeap = clazz.getMethod("dumpHeap", String.class, boolean.class);
63 } catch (Exception ignored) {
64 mxBean = null;
65 mxBeanDumpHeap = null;
66 }
67
68 hotspotMXBean = mxBean;
69 hotspotMXBeanDumpHeap = mxBeanDumpHeap;
70 }
71
72
73
74
75
76 public static boolean isSctpSupported() {
77 String os = System.getProperty("os.name").toLowerCase(Locale.US);
78 if ("unix".equals(os) || "linux".equals(os) || "sun".equals(os) || "solaris".equals(os)) {
79 try {
80
81
82 Class<?> sctpChannelClass = Class.forName("com.sun.nio.sctp.SctpChannel");
83 Channel channel = (Channel) sctpChannelClass.getMethod("open").invoke(null);
84 try {
85 channel.close();
86 } catch (IOException e) {
87
88 }
89 } catch (UnsupportedOperationException e) {
90
91
92 System.out.print("Not supported: " + e.getMessage());
93 return false;
94 } catch (Throwable t) {
95 if (!(t instanceof IOException)) {
96 return false;
97 }
98 }
99 return true;
100 }
101 return false;
102 }
103
104
105
106
107 public static String testMethodName(TestInfo testInfo) {
108 String testMethodName = testInfo.getTestMethod().map(new Function<Method, String>() {
109 @Override
110 public String apply(Method method) {
111 return method.getName();
112 }
113 }).orElse("[unknown method]");
114 if (testMethodName.contains("[")) {
115 testMethodName = testMethodName.substring(0, testMethodName.indexOf('['));
116 }
117 return testMethodName;
118 }
119
120 public static void dump(String filenamePrefix) throws IOException {
121
122 ObjectUtil.checkNotNull(filenamePrefix, "filenamePrefix");
123
124 final String timestamp = timestamp();
125 final File heapDumpFile = new File(filenamePrefix + '.' + timestamp + ".hprof");
126 if (heapDumpFile.exists()) {
127 if (!heapDumpFile.delete()) {
128 throw new IOException("Failed to remove the old heap dump: " + heapDumpFile);
129 }
130 }
131
132 final File threadDumpFile = new File(filenamePrefix + '.' + timestamp + ".threads");
133 if (threadDumpFile.exists()) {
134 if (!threadDumpFile.delete()) {
135 throw new IOException("Failed to remove the old thread dump: " + threadDumpFile);
136 }
137 }
138
139 dumpHeap(heapDumpFile);
140 dumpThreads(threadDumpFile);
141 }
142
143 public static void compressHeapDumps() throws IOException {
144 final File[] files = new File(System.getProperty("user.dir")).listFiles(new FilenameFilter() {
145 @Override
146 public boolean accept(File dir, String name) {
147 return name.endsWith(".hprof");
148 }
149 });
150 if (files == null) {
151 logger.warn("failed to find heap dump due to I/O error!");
152 return;
153 }
154
155 final byte[] buf = new byte[65536];
156 final LZMA2Options options = new LZMA2Options(LZMA2Options.PRESET_DEFAULT);
157
158 for (File file: files) {
159 final String filename = file.toString();
160 final String xzFilename = filename + ".xz";
161 final long fileLength = file.length();
162
163 logger.info("Compressing the heap dump: {}", xzFilename);
164
165 long lastLogTime = System.nanoTime();
166 long counter = 0;
167
168 try (InputStream in = Files.newInputStream(Paths.get(filename));
169 OutputStream out = new XZOutputStream(Files.newOutputStream(Paths.get(xzFilename)), options)) {
170 for (;;) {
171 int readBytes = in.read(buf);
172 if (readBytes < 0) {
173 break;
174 }
175 if (readBytes == 0) {
176 continue;
177 }
178
179 out.write(buf, 0, readBytes);
180 counter += readBytes;
181
182 long currentTime = System.nanoTime();
183 if (currentTime - lastLogTime > DUMP_PROGRESS_LOGGING_INTERVAL) {
184 logger.info("Compressing the heap dump: {} ({}%)",
185 xzFilename, counter * 100 / fileLength);
186 lastLogTime = currentTime;
187 }
188 }
189 } catch (Throwable t) {
190 logger.warn("Failed to compress the heap dump: {}", xzFilename, t);
191 }
192
193
194 if (!file.delete()) {
195 logger.warn("Failed to delete the uncompressed heap dump: {}", filename);
196 }
197 }
198 }
199
200 private static String timestamp() {
201 return new SimpleDateFormat("HHmmss.SSS").format(new Date());
202 }
203
204 private static void dumpHeap(File file) {
205 if (hotspotMXBean == null) {
206 logger.warn("Can't dump heap: HotSpotDiagnosticMXBean unavailable");
207 return;
208 }
209
210 final String filename = file.toString();
211 logger.info("Dumping heap: {}", filename);
212 try {
213 hotspotMXBeanDumpHeap.invoke(hotspotMXBean, filename, true);
214 } catch (Exception e) {
215 logger.warn("Failed to dump heap: {}", filename, e);
216 }
217 }
218
219 private static void dumpThreads(File file) {
220 final String filename = file.toString();
221 try {
222 logger.info("Dumping threads: {}", filename);
223 final StringBuilder buf = new StringBuilder(8192);
224 try {
225 for (ThreadInfo info : ManagementFactory.getThreadMXBean().dumpAllThreads(true, true)) {
226 buf.append(info);
227 }
228 buf.append('\n');
229 } catch (UnsupportedOperationException ignored) {
230 logger.warn("Can't dump threads: ThreadMXBean.dumpAllThreads() unsupported");
231 return;
232 }
233
234 try (OutputStream out = Files.newOutputStream(file.toPath())) {
235 out.write(buf.toString().getBytes(CharsetUtil.UTF_8));
236 }
237 } catch (Exception e) {
238 logger.warn("Failed to dump threads: {}", filename, e);
239 }
240 }
241
242 private TestUtils() { }
243 }