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