1 /*
  2  * Copyright (c) 2015, 2017, 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 package utils;
 24 
 25 import java.util.Map;
 26 import java.util.Scanner;
 27 import java.util.regex.MatchResult;
 28 
 29 /**
 30  *
 31  * jstack default format 2008-03-05 18:36:26 Full thread dump Java HotSpot(TM)
 32  * Client VM (11.0-b11 mixed mode):
 33  *
 34  * "Thread-16" #10 daemon prio=3 os_prio=0 tid=0x0814d800 nid=0x1d runnable
 35  * [0xf394d000..0xf394d9f0] java.lang.Thread.State: RUNNABLE at
 36  * java.net.SocketInputStream.socketRead0(Native Method) at
 37  * java.net.SocketInputStream.read(SocketInputStream.java:129) at
 38  * java.net.SocketInputStream.read(SocketInputStream.java:182) at
 39  * java.io.ObjectInputStream$PeekInputStream.peek(ObjectInputStream.java:2249)
 40  * at
 41  * java.io.ObjectInputStream$BlockDataInputStream.peek(ObjectInputStream.java:2542)
 42  * at
 43  * java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2552)
 44  * at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1297) at
 45  * java.io.ObjectInputStream.readObject(ObjectInputStream.java:351) at
 46  * tmtools.share.debuggee.DebuggeeProtocolHandler.run(DebuggeeProtocolHandler.java:32)
 47  *
 48  * Locked ownable synchronizers: - None ....
 49  *
 50  * Note that os_prio field is optional and will be printed only if JVM was able
 51  * to get native thread priority.
 52  */
 53 public class DefaultFormat implements Format {
 54 
 55     protected String threadInfoPattern() {
 56         return "^\"(.*)\"\\s(#\\d+\\s|)\\[(\\d+)\\]\\s(daemon\\s|)prio=(.+)\\s(os_prio=(.+)\\s|)tid=(.+)\\snid=(.+)\\s("
 57                 + Consts.UNKNOWN
 58                 + "|runnable|sleeping|waiting\\son\\scondition|in\\sObject\\.wait\\(\\)|waiting\\sfor\\smonitor\\sentry)((.*))$";
 59     }
 60 
 61     protected String methodInfoPattern() {
 62         return "^\\s+at\\s(.+)\\((.*?)(\\:|\\))((.*?))\\)?$";
 63     }
 64 
 65     protected String extendedStatusPattern() {
 66         return "\\s+java\\.lang\\.Thread\\.State\\:\\s((.+))$";
 67     }
 68 
 69     protected String jniGlobalRefInfoPattern() {
 70         return "^JNI\\sglobal\\sreferences:\\s((.+))$";
 71     }
 72 
 73     // Sample string that matches the pattern:
 74     // waiting on <0x000000008f64e6d0> (a java.lang.Object)
 75     protected String monitorInfoPattern() {
 76         return "^\\s+\\-\\s(locked|waiting\\son|waiting\\sto\\slock)\\s\\<(.*)\\>\\s\\(((.*))\\)$";
 77     }
 78 
 79     // Sample string that matches the pattern:
 80     // waiting on <no object reference available>
 81     protected String monitorInfoNoObjectRefPattern() {
 82         return "^\\s+\\-\\s(locked|waiting\\son|waiting\\sto\\slock)\\s\\<(.*)\\>$";
 83     }
 84 
 85     protected String vmVersionInfoPattern() {
 86         return "Full\\sthread\\sdump\\s.*";
 87     }
 88 
 89     protected String ownableSynchronizersPattern() {
 90         return "^\\s+\\-\\s(\\<.*\\>\\s\\(((.*))\\)|None)$";
 91     }
 92 
 93     public JStack parse(String stack) {
 94         JStack result = new JStack();
 95         Scanner scanner = new Scanner(stack);
 96 
 97         // parsing thread stacks
 98         ThreadStack currentThreadStack = null;
 99         MethodInfo currentMethodInfo = null;
100 
101         try {
102             while (scanner.hasNextLine()) {
103                 String line = scanner.nextLine();
104                 if (line.matches(threadInfoPattern())) {
105                     currentThreadStack = parseThreadInfo(line);
106                     result.addThreadStack(currentThreadStack.getThreadName(), currentThreadStack);
107                 } else if (line.matches(methodInfoPattern())) {
108                     currentMethodInfo = parseMethodInfo(line);
109                     currentThreadStack.addMethod(currentMethodInfo);
110                 } else if (line.matches(monitorInfoPattern())) {
111                     MonitorInfo mi = parseMonitorInfo(line, monitorInfoPattern());
112                     currentMethodInfo.getLocks().add(mi);
113                 } else if (line.matches(monitorInfoNoObjectRefPattern())) {
114                     MonitorInfo mi = parseMonitorInfo(line, monitorInfoNoObjectRefPattern());
115                     currentMethodInfo.getLocks().add(mi);
116                 } else if (line.matches(extendedStatusPattern())) {
117                     currentThreadStack.setExtendedStatus(parseExtendedStatus(line));
118                 } else if (line.matches(vmVersionInfoPattern())) {
119                     result.setVmVersion(line);
120                 } else if (line.matches(ownableSynchronizersPattern())) {
121                     currentThreadStack.getLockOSList().add(parseLockInfo(line));
122                 } else if (line.matches(jniGlobalRefInfoPattern())) {
123                     result.setJniGlobalReferences(parseJNIGlobalRefs(line));
124                 } else if (line.length() != 0) {
125                     System.err.println("[Warning] Unknown string: " + line);
126                 }
127             }
128 
129             scanner.close();
130 
131         } catch (NullPointerException e) {
132             e.printStackTrace();
133             throw new RuntimeException("Unexpected format in jstack output");
134         }
135 
136         return result;
137     }
138 
139     private MonitorInfo parseMonitorInfo(String line, String pattern) {
140         Scanner s = new Scanner(line);
141         s.findInLine(pattern);
142         MonitorInfo mi = new MonitorInfo();
143         MatchResult res = s.match();
144 
145         mi.setType(res.group(1));
146         mi.setMonitorAddress(res.group(2));
147         if (res.groupCount() > 2) {
148             mi.setMonitorClass(res.group(3));
149         }
150         return mi;
151     }
152 
153     protected String parseExtendedStatus(String line) {
154         Scanner s = new Scanner(line);
155         s.findInLine(extendedStatusPattern());
156         String result = s.match().group(1);
157         s.close();
158         return result;
159     }
160 
161     protected String parseJNIGlobalRefs(String line) {
162         Scanner s = new Scanner(line);
163         s.findInLine(jniGlobalRefInfoPattern());
164         String result = s.match().group(1);
165         s.close();
166         return result;
167     }
168 
169     protected ThreadStack parseThreadInfo(String threadInfo) {
170         Scanner s = new Scanner(threadInfo);
171         ThreadStack result = new ThreadStack();
172 
173         // parsing thread info
174         s.findInLine(threadInfoPattern());
175         MatchResult res = s.match();
176 
177         result.setThreadName(res.group(1));
178 
179         result.setType(res.group(4));
180 
181         result.setPriority(res.group(5));
182         result.setTid(res.group(8));
183         result.setNid(res.group(9));
184         result.setStatus(res.group(10));
185 
186         s.close();
187         return result;
188     }
189 
190     protected MethodInfo parseMethodInfo(String line) {
191 
192         MethodInfo result = new MethodInfo();
193         Scanner s = new Scanner(line);
194 
195         s.findInLine(methodInfoPattern());
196         MatchResult rexp = s.match();
197         if (rexp.group(4) != null && rexp.group(4).length() > 0) {
198             // line "  at tmtools.jstack.share.utils.Utils.sleep(Utils.java:29)"
199             result.setName(rexp.group(1));
200             result.setCompilationUnit(rexp.group(2));
201             result.setLine(rexp.group(4));
202 
203         } else {
204             // line "  at java.lang.Thread.sleep(Native Method)"
205             result.setName(rexp.group(1));
206         }
207 
208         s.close();
209         return result;
210     }
211 
212     public String dumpStackTraces() {
213         StringBuffer result = new StringBuffer();
214         Map<Thread, StackTraceElement[]> stacks = Thread.getAllStackTraces();
215 
216         // adding data and vm version
217         result.append(Consts.UNKNOWN + "\n");
218         result.append(Consts.UNKNOWN + "\n\n");
219 
220         for (Thread t : stacks.keySet()) {
221 
222             result.append("\"" + t.getName() + "\"");
223             result.append(Consts.SEPARATOR);
224 
225             // status
226             if (t.isDaemon()) {
227                 result.append("daemon");
228                 result.append(Consts.SEPARATOR);
229             }
230 
231             // priority
232             result.append("prio=" + t.getPriority());
233             result.append(Consts.SEPARATOR);
234 
235             // tid
236             result.append("tid=" + Consts.UNKNOWN);
237             result.append(Consts.SEPARATOR);
238 
239             // nid
240             result.append("nid=" + Consts.UNKNOWN);
241             result.append(Consts.SEPARATOR);
242 
243             // status
244             result.append(Consts.UNKNOWN);
245             result.append(Consts.SEPARATOR);
246 
247             result.append("\n");
248 
249             // extended status
250             result.append("   java.lang.Thread.State: "
251                     + String.valueOf(Thread.currentThread().getState()));
252             result.append(Consts.SEPARATOR);
253             result.append("\n");
254 
255             for (StackTraceElement st : stacks.get(t)) {
256                 result.append("  at " + st.toString() + "\n");
257             }
258             result.append("\n");
259         }
260 
261         result.append(Consts.JNI_GLOBAL_REF + Consts.UNKNOWN + "\n");
262         return result.toString();
263     }
264 
265     protected LockInfo parseLockInfo(String line) {
266         LockInfo res = new LockInfo();
267 
268         Scanner s = new Scanner(line);
269         s.findInLine(ownableSynchronizersPattern());
270 
271         MatchResult matchRes = s.match();
272         String lock = matchRes.group(1).equals("None") ? matchRes.group(1) : matchRes.group(2);
273         res.setLock(lock);
274 
275         return res;
276     }
277 
278 }