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 java.io.File;
27 import java.io.FileInputStream;
28 import java.io.FileOutputStream;
29 import java.io.FilenameFilter;
30 import java.io.IOException;
31 import java.io.InputStream;
32 import java.io.OutputStream;
33 import java.lang.management.ManagementFactory;
34 import java.lang.management.ThreadInfo;
35 import java.lang.reflect.Method;
36 import java.nio.channels.Channel;
37 import java.text.SimpleDateFormat;
38 import java.util.Date;
39 import java.util.Locale;
40 import java.util.concurrent.TimeUnit;
41 import java.util.function.Function;
42 import javax.management.MBeanServer;
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 InputStream in = null;
169 OutputStream out = null;
170 try {
171 in = new FileInputStream(filename);
172 out = new XZOutputStream(new FileOutputStream(xzFilename), options);
173 for (;;) {
174 int readBytes = in.read(buf);
175 if (readBytes < 0) {
176 break;
177 }
178 if (readBytes == 0) {
179 continue;
180 }
181
182 out.write(buf, 0, readBytes);
183 counter += readBytes;
184
185 long currentTime = System.nanoTime();
186 if (currentTime - lastLogTime > DUMP_PROGRESS_LOGGING_INTERVAL) {
187 logger.info("Compressing the heap dump: {} ({}%)",
188 xzFilename, counter * 100 / fileLength);
189 lastLogTime = currentTime;
190 }
191 }
192 out.close();
193 in.close();
194 } catch (Throwable t) {
195 logger.warn("Failed to compress the heap dump: {}", xzFilename, t);
196 } finally {
197 if (in != null) {
198 try {
199 in.close();
200 } catch (IOException ignored) {
201
202 }
203 }
204 if (out != null) {
205 try {
206 out.close();
207 } catch (IOException ignored) {
208
209 }
210 }
211 }
212
213
214 if (!file.delete()) {
215 logger.warn("Failed to delete the uncompressed heap dump: {}", filename);
216 }
217 }
218 }
219
220 private static String timestamp() {
221 return new SimpleDateFormat("HHmmss.SSS").format(new Date());
222 }
223
224 private static void dumpHeap(File file) {
225 if (hotspotMXBean == null) {
226 logger.warn("Can't dump heap: HotSpotDiagnosticMXBean unavailable");
227 return;
228 }
229
230 final String filename = file.toString();
231 logger.info("Dumping heap: {}", filename);
232 try {
233 hotspotMXBeanDumpHeap.invoke(hotspotMXBean, filename, true);
234 } catch (Exception e) {
235 logger.warn("Failed to dump heap: {}", filename, e);
236 }
237 }
238
239 private static void dumpThreads(File file) {
240 final String filename = file.toString();
241 OutputStream out = null;
242 try {
243 logger.info("Dumping threads: {}", filename);
244 final StringBuilder buf = new StringBuilder(8192);
245 try {
246 for (ThreadInfo info : ManagementFactory.getThreadMXBean().dumpAllThreads(true, true)) {
247 buf.append(info);
248 }
249 buf.append('\n');
250 } catch (UnsupportedOperationException ignored) {
251 logger.warn("Can't dump threads: ThreadMXBean.dumpAllThreads() unsupported");
252 return;
253 }
254
255 out = new FileOutputStream(file);
256 out.write(buf.toString().getBytes(CharsetUtil.UTF_8));
257 } catch (Exception e) {
258 logger.warn("Failed to dump threads: {}", filename, e);
259 } finally {
260 if (out != null) {
261 try {
262 out.close();
263 } catch (IOException ignored) {
264
265 }
266 }
267 }
268 }
269
270 private TestUtils() { }
271 }