@@ -102,42 +102,144 @@ public String getOverview() {
102102 * creates the overview information for this thread dump.
103103 */
104104 private void createOverview () {
105- StringBuffer statData = new StringBuffer ("<body bgcolor=\" #ffffff\" ><font face=System " +
106- "><table border=0><tr bgcolor=\" #dddddd\" ><td><font face=System " +
107- ">Overall Thread Count</td><td width=\" 150\" ></td><td><b><font face=System>" );
108- statData .append (getThreads () == null ? 0 : getThreads ().getNodeCount ());
109- statData .append ("</b></td></tr>\n \n <tr bgcolor=\" #eeeeee\" ><td><font face=System" +
110- ">Overall Monitor Count</td><td></td><td><b><font face=System>" );
111- statData .append (getMonitors () == null ? 0 : getMonitors ().getNodeCount ());
112- statData .append ("</b></td></tr>\n \n <tr bgcolor=\" #dddddd\" ><td><font face=System " +
113- ">Number of threads waiting for a monitor</td><td></td><td><b><font face=System>" );
114- statData .append (getWaitingThreads () == null ? 0 : getWaitingThreads ().getNodeCount ());
115- statData .append ("</b></td></tr>\n \n <tr bgcolor=\" #eeeeee\" ><td><font face=System " +
116- ">Number of threads locking a monitor</td><td></td><td><b><font face=System size>" );
117- statData .append (getLockingThreads () == null ? 0 : getLockingThreads ().getNodeCount ());
118- statData .append ("</b></td></tr>\n \n <tr bgcolor=\" #dddddd\" ><td><font face=System " +
119- ">Number of threads sleeping on a monitor</td><td></td><td><b><font face=System>" );
120- statData .append (getSleepingThreads () == null ? 0 : getSleepingThreads ().getNodeCount ());
121- statData .append ("</b></td></tr>\n \n <tr bgcolor=\" #eeeeee\" ><td><font face=System " +
122- ">Number of deadlocks</td><td></td><td><b><font face=System>" );
123- statData .append (getDeadlocks () == null ? 0 : getDeadlocks ().getNodeCount ());
124- statData .append ("</b></td></tr>\n \n <tr bgcolor=\" #dddddd\" ><td><font face=System " +
125- ">Number of Monitors without locking threads</td><td></td><td><b><font face=System>" );
126- statData .append (getMonitorsWithoutLocks () == null ? 0 : getMonitorsWithoutLocks ().getNodeCount ());
127- statData .append ("</b></td></tr>" );
105+ int threadsCount = getThreads () == null ? 0 : getThreads ().getNodeCount ();
106+ int monitorsCount = getMonitors () == null ? 0 : getMonitors ().getNodeCount ();
107+ int waitingCount = getWaitingThreads () == null ? 0 : getWaitingThreads ().getNodeCount ();
108+ int lockingCount = getLockingThreads () == null ? 0 : getLockingThreads ().getNodeCount ();
109+ int sleepingCount = getSleepingThreads () == null ? 0 : getSleepingThreads ().getNodeCount ();
110+ int deadlocksCount = getDeadlocks () == null ? 0 : getDeadlocks ().getNodeCount ();
111+ int monitorsNoLockCount = getMonitorsWithoutLocks () == null ? 0 : getMonitorsWithoutLocks ().getNodeCount ();
112+
113+ StringBuilder statData = new StringBuilder ();
114+ statData .append ("<html><body style=\" background-color: #ffffff; font-family: sans-serif; margin: 20px; color: #333;\" >" );
115+
116+ statData .append ("<h2 style=\" color: #2c3e50; border-bottom: 2px solid #3498db; padding-bottom: 10px;\" >Thread Dump Overview</h2>" );
117+
118+ // Thread State Distribution (Visual Chart)
119+ if (threadsCount > 0 ) {
120+ java .util .Map <String , Integer > stateDistribution = new java .util .HashMap <>();
121+ Category threadsCat = getThreads ();
122+ for (int i = 0 ; i < threadsCount ; i ++) {
123+ javax .swing .tree .DefaultMutableTreeNode node = (javax .swing .tree .DefaultMutableTreeNode ) threadsCat .getNodeAt (i );
124+ ThreadInfo ti = (ThreadInfo ) node .getUserObject ();
125+ String [] tokens = ti .getTokens ();
126+ String state = "UNKNOWN" ;
127+ if (tokens != null ) {
128+ if (tokens .length >= 7 ) {
129+ // For prio= formats, state information might be harder to extract from tokens alone if not parsed.
130+ // But SunJDKParser puts it in title if missing.
131+ // Actually, tokens[2] is prio, [3] is tid, [4] is nid...
132+ // If it's the 3-token format: [0]=name, [1]=id, [2]=state
133+ state = tokens .length == 3 ? tokens [2 ] : "OTHER" ;
134+ } else if (tokens .length == 3 ) {
135+ state = tokens [2 ];
136+ }
137+ }
138+
139+ // Try to extract state from title if it's "state=..."
140+ if ("UNKNOWN" .equals (state ) || "OTHER" .equals (state )) {
141+ String name = ti .getName ();
142+ if (name .contains ("state=" )) {
143+ int start = name .indexOf ("state=" ) + 6 ;
144+ int end = name .indexOf (' ' , start );
145+ state = end > start ? name .substring (start , end ) : name .substring (start );
146+ } else if (ti .getContent ().contains ("java.lang.Thread.State: " )) {
147+ String content = ti .getContent ();
148+ int start = content .indexOf ("java.lang.Thread.State: " ) + 24 ;
149+ int end = content .indexOf ('\n' , start );
150+ state = content .substring (start , end ).trim ();
151+ if (state .indexOf (' ' ) > 0 ) {
152+ state = state .substring (0 , state .indexOf (' ' ));
153+ }
154+ }
155+ }
156+
157+ state = state .toUpperCase ();
158+ stateDistribution .put (state , stateDistribution .getOrDefault (state , 0 ) + 1 );
159+ }
160+
161+ statData .append ("<div style=\" margin-bottom: 25px; padding: 15px; background-color: #f8f9fa; border-radius: 8px; border: 1px solid #e9ecef;\" >" );
162+ statData .append ("<h4 style=\" margin-top: 0; color: #495057;\" >Thread State Distribution (" ).append (threadsCount ).append (" threads)</h4>" );
163+ statData .append ("<table width=\" 100%\" cellspacing=\" 0\" cellpadding=\" 0\" style=\" height: 30px; border: 1px solid #dee2e6; border-radius: 4px; overflow: hidden;\" ><tr>" );
164+
165+ String [] commonStates = {"RUNNABLE" , "WAITING" , "TIMED_WAITING" , "BLOCKED" , "PARKING" };
166+ String [] colors = {"#28a745" , "#ffc107" , "#fd7e14" , "#dc3545" , "#6f42c1" };
167+
168+ int accounted = 0 ;
169+ for (int i = 0 ; i < commonStates .length ; i ++) {
170+ int count = stateDistribution .getOrDefault (commonStates [i ], 0 );
171+ if (count > 0 ) {
172+ double percent = (count * 100.0 ) / threadsCount ;
173+ statData .append ("<td width=\" " ).append (percent ).append ("%\" bgcolor=\" " ).append (colors [i ])
174+ .append ("\" title=\" " ).append (commonStates [i ]).append (": " ).append (count ).append ("\" ></td>" );
175+ accounted += count ;
176+ }
177+ }
178+
179+ int otherCount = threadsCount - accounted ;
180+ if (otherCount > 0 ) {
181+ double percent = (otherCount * 100.0 ) / threadsCount ;
182+ statData .append ("<td width=\" " ).append (percent ).append ("%\" bgcolor=\" #6c757d\" title=\" OTHER: " ).append (otherCount ).append ("\" ></td>" );
183+ }
184+ statData .append ("</tr></table>" );
185+
186+ // Legend
187+ statData .append ("<div style=\" margin-top: 10px; font-size: 11px;\" >" );
188+ for (int i = 0 ; i < commonStates .length ; i ++) {
189+ int count = stateDistribution .getOrDefault (commonStates [i ], 0 );
190+ if (count > 0 ) {
191+ statData .append ("<span style=\" display: inline-block; width: 10px; height: 10px; background-color: " ).append (colors [i ]).append ("; margin-right: 4px;\" ></span>" )
192+ .append (commonStates [i ]).append (" (" ).append (count ).append (") " );
193+ }
194+ }
195+ if (otherCount > 0 ) {
196+ statData .append ("<span style=\" display: inline-block; width: 10px; height: 10px; background-color: #6c757d; margin-right: 4px;\" ></span>" )
197+ .append ("OTHER (" ).append (otherCount ).append (")" );
198+ }
199+ statData .append ("</div></div>" );
200+ }
201+
202+ // Statistics Table
203+ statData .append ("<table width=\" 100%\" style=\" border-collapse: collapse; margin-bottom: 20px;\" >" );
128204
129- // add hints concerning possible hot spots found in this thread dump.
130- statData .append (getDumpAnalyzer ().analyzeDump ());
205+ statData .append ("<tr>" );
206+ statData .append ("<td width=\" 50%\" style=\" padding: 8px; border-bottom: 1px solid #eee; background-color: #fcfcfc;\" ><b>Overall Monitor Count:</b> " ).append (monitorsCount ).append ("</td>" );
207+ statData .append ("<td width=\" 50%\" style=\" padding: 8px; border-bottom: 1px solid #eee; background-color: #fcfcfc;\" ><b>Deadlocks:</b> <span style=\" " ).append (deadlocksCount > 0 ? "color: #dc3545; font-weight: bold;" : "" ).append ("\" >" ).append (deadlocksCount ).append ("</span></td>" );
208+ statData .append ("</tr>" );
209+
210+ statData .append ("<tr>" );
211+ statData .append ("<td style=\" padding: 8px; border-bottom: 1px solid #eee;\" ><b>Threads locking:</b> " ).append (lockingCount ).append ("</td>" );
212+ statData .append ("<td style=\" padding: 8px; border-bottom: 1px solid #eee;\" ><b>Monitors without locking:</b> " ).append (monitorsNoLockCount ).append ("</td>" );
213+ statData .append ("</tr>" );
214+
215+ statData .append ("<tr>" );
216+ statData .append ("<td style=\" padding: 8px; border-bottom: 1px solid #eee; background-color: #fcfcfc;\" ><b>Threads waiting:</b> " ).append (waitingCount ).append ("</td>" );
217+ statData .append ("<td style=\" padding: 8px; border-bottom: 1px solid #eee; background-color: #fcfcfc;\" ><b>Threads sleeping:</b> " ).append (sleepingCount ).append ("</td>" );
218+ statData .append ("</tr>" );
219+
220+ statData .append ("</table>" );
221+
222+ // Hints and Heap Info
223+ String hints = getDumpAnalyzer ().analyzeDump ();
224+ if (hints != null && !hints .isEmpty ()) {
225+ statData .append ("<div style=\" margin-top: 20px; padding: 15px; background-color: #fff3cd; border: 1px solid #ffeeba; border-radius: 8px; color: #856404;\" >" );
226+ statData .append ("<h4 style=\" margin-top: 0;\" >Analysis Hints</h4>" );
227+ statData .append ("<table border=\" 0\" width=\" 100%\" style=\" color: #856404;\" >" ).append (hints ).append ("</table>" );
228+ statData .append ("</div>" );
229+ }
131230
132- if (getHeapInfo () != null ) {
231+ if (getHeapInfo () != null ) {
232+ statData .append ("<div style=\" margin-top: 20px; padding: 15px; background-color: #e2e3e5; border: 1px solid #d6d8db; border-radius: 8px;\" >" );
233+ statData .append ("<h4 style=\" margin-top: 0;\" >Heap Information</h4>" );
133234 statData .append (getHeapInfo ());
235+ statData .append ("</div>" );
134236 }
135237
136- statData .append ("</table >" );
238+ statData .append ("</body></html >" );
137239
138240 setOverview (statData .toString ());
139-
140241 }
242+
141243
142244 /**
143245 * generate a monitor info node from the given information.
@@ -147,41 +249,44 @@ private void createOverview() {
147249 * @return a info node for the monitor.
148250 */
149251 public static String getMonitorInfo (int locks , int waits , int sleeps ) {
150- StringBuffer statData = new StringBuffer ( "<body bgcolor= \" ffffff \" ><table border=0 bgcolor= \" #dddddd \" ><tr><td><font face=System" +
151- ">Threads locking monitor</td><td><b><font face=System >" );
152- statData .append (locks );
153- statData . append ( "</b></td></tr> \n \n <tr bgcolor= \" #eeeeee \" ><td>" );
154- statData . append ( "<font face=System> Threads sleeping on monitor</td><td><b><font face=System> " );
155- statData . append ( sleeps );
156- statData . append ( "</b></td></tr> \n \n <tr><td> " );
157- statData . append ( "<font face=System>Threads waiting to lock monitor</td><td><b><font face=System>" );
158- statData .append (waits );
159- statData . append ( "</b></td></tr> \n \n " );
252+ StringBuilder statData = new StringBuilder ();
253+ statData . append ( "<html><body style= \" background-color: #ffffff; font-family: sans-serif; margin: 10px; color: #333; \" >" );
254+ statData .append ("<table width= \" 100% \" style= \" border-collapse: collapse; background-color: #f8f9fa; border: 1px solid #dee2e6; \" >" );
255+
256+ addMonitorStatRow ( statData , " Threads locking monitor" , locks , "#ffffff " );
257+ addMonitorStatRow ( statData , "Threads sleeping on monitor" , sleeps , "#f2f2f2" );
258+ addMonitorStatRow ( statData , "Threads waiting to lock monitor" , waits , "#ffffff " );
259+
260+ statData .append ("</table>" );
261+
160262 if (locks == 0 ) {
161- statData .append ("<tr bgcolor=\" #ffffff\" <td></td></tr>" );
162- // See http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5086475
163- statData .append ("<tr bgcolor=\" #cccccc\" ><td><font face=System> " +
164- "<p>This monitor doesn't have a thread locking it. This means one of the following is true:</p>" +
165- "<ul><li>a VM Thread is holding it." +
166- "<li>This lock is a <tt>java.util.concurrent</tt> lock and the thread holding it is not reported in the stack trace " +
167- "because the JVM option -XX:+PrintConcurrentLocks is not present." +
168- "<li>This lock is a custom java.util.concurrent lock either not based off of" +
169- " <tt>AbstractOwnableSynchronizer</tt> or not setting the exclusive owner when a lock is granted.</ul>" );
170- statData .append ("If you see many monitors having no locking thread (and the latter two conditions above do " +
171- "not apply), this usually means the garbage collector is running.<br>" );
172- statData .append ("In this case you should consider analyzing the Garbage Collector output. If the dump has many monitors with no locking thread<br>" );
173- statData .append ("a click on the <a href=\" dump://\" >dump node</a> will give you additional information.<br></td></tr>" );
263+ statData .append ("<div style=\" margin-top: 15px; padding: 10px; background-color: #e9ecef; border-left: 5px solid #6c757d; font-size: 12px;\" >" );
264+ statData .append ("<p><b>No locking thread detected.</b> Possible reasons:</p>" );
265+ statData .append ("<ul><li>A VM Thread is holding it.</li>" );
266+ statData .append ("<li>It is a <tt>java.util.concurrent</tt> lock and -XX:+PrintConcurrentLocks is missing.</li>" );
267+ statData .append ("<li>It is a custom lock not based on <tt>AbstractOwnableSynchronizer</tt>.</li></ul>" );
268+ statData .append ("<p>If many monitors have no locking thread, the garbage collector might be running.</p>" );
269+ statData .append ("<p>Check the <a href=\" dump://\" >dump node</a> for more info.</p>" );
270+ statData .append ("</div>" );
174271 }
272+
175273 if (areALotOfWaiting (waits )) {
176- statData .append ("<tr bgcolor=\" #ffffff\" <td></td></tr>" );
177- statData .append ("<tr bgcolor=\" #cccccc\" ><td><font face=System " +
178- "<p>A lot of threads are waiting for this monitor to become available again.</p><br>" );
179- statData .append ("This might indicate a congestion. You also should analyze other locks blocked by threads waiting<br>" );
180- statData .append ("for this monitor as there might be much more threads waiting for it.<br></td></tr>" );
274+ statData .append ("<div style=\" margin-top: 15px; padding: 10px; background-color: #f8d7da; border-left: 5px solid #dc3545; color: #721c24; font-size: 12px;\" >" );
275+ statData .append ("<p><b>High congestion!</b> A lot of threads are waiting for this monitor.</p>" );
276+ statData .append ("<p>Analyze other blocked locks as well, as there might be a chain of waiting threads.</p>" );
277+ statData .append ("</div>" );
181278 }
182- statData .append ("</table>" );
279+
280+ statData .append ("</body></html>" );
281+
282+ return statData .toString ();
283+ }
183284
184- return (statData .toString ());
285+ private static void addMonitorStatRow (StringBuilder sb , String label , int value , String bgColor ) {
286+ sb .append ("<tr style=\" background-color: " ).append (bgColor ).append (";\" >" );
287+ sb .append ("<td style=\" padding: 8px; border-bottom: 1px solid #dee2e6;\" >" ).append (label ).append ("</td>" );
288+ sb .append ("<td style=\" padding: 8px; border-bottom: 1px solid #dee2e6; text-align: right;\" ><b>" ).append (value ).append ("</b></td>" );
289+ sb .append ("</tr>" );
185290 }
186291
187292 /**
0 commit comments