001/* 002 * Copyright (C) 2012 eXo Platform SAS. 003 * 004 * This is free software; you can redistribute it and/or modify it 005 * under the terms of the GNU Lesser General Public License as 006 * published by the Free Software Foundation; either version 2.1 of 007 * the License, or (at your option) any later version. 008 * 009 * This software is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * You should have received a copy of the GNU Lesser General Public 015 * License along with this software; if not, write to the Free 016 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 017 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 018 */ 019 020package org.crsh.text.renderers; 021 022import org.crsh.text.Color; 023import org.crsh.text.Decoration; 024import org.crsh.text.LineRenderer; 025import org.crsh.text.Renderer; 026import org.crsh.text.ui.LabelElement; 027import org.crsh.text.ui.Overflow; 028import org.crsh.text.ui.RowElement; 029import org.crsh.text.ui.TableElement; 030import org.crsh.util.Utils; 031 032import java.lang.management.ManagementFactory; 033import java.lang.management.ThreadMXBean; 034import java.util.Collections; 035import java.util.Comparator; 036import java.util.EnumMap; 037import java.util.HashMap; 038import java.util.Iterator; 039import java.util.List; 040import java.util.Map; 041 042public class ThreadRenderer extends Renderer<Thread> { 043 044 /** . */ 045 private static final EnumMap<Thread.State, Color> colorMapping = new EnumMap<Thread.State, Color>(Thread.State.class); 046 047 static { 048 colorMapping.put(Thread.State.NEW, Color.cyan); 049 colorMapping.put(Thread.State.RUNNABLE, Color.green); 050 colorMapping.put(Thread.State.BLOCKED, Color.red); 051 colorMapping.put(Thread.State.WAITING, Color.yellow); 052 colorMapping.put(Thread.State.TIMED_WAITING, Color.magenta); 053 colorMapping.put(Thread.State.TERMINATED, Color.blue); 054 } 055 056 @Override 057 public Class<Thread> getType() { 058 return Thread.class; 059 } 060 061 @Override 062 public LineRenderer renderer(Iterator<Thread> stream) { 063 064 // 065 ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); 066 067 // 068 List<Thread> threads = Utils.list(stream); 069 070 // Sample CPU 071 Map<Long, Long> times1 = new HashMap<Long, Long>(); 072 for (Thread thread : threads) { 073 long cpu = threadMXBean.getThreadCpuTime(thread.getId()); 074 times1.put(thread.getId(), cpu); 075 } 076 077 try { 078 // Sleep 100ms 079 Thread.sleep(100); 080 } 081 catch (InterruptedException e) { 082 Thread.currentThread().interrupt(); 083 } 084 085 // Resample 086 Map<Long, Long> times2 = new HashMap<Long, Long>(threads.size()); 087 for (Thread thread : threads) { 088 long cpu = threadMXBean.getThreadCpuTime(thread.getId()); 089 times2.put(thread.getId(), cpu); 090 } 091 092 // Compute delta map and total time 093 long total = 0; 094 Map<Long, Long> deltas = new HashMap<Long, Long>(threads.size()); 095 for (Long id : times2.keySet()) { 096 long time1 = times2.get(id); 097 long time2 = times1.get(id); 098 if (time1 == -1) { 099 time1 = time2; 100 } else if (time2 == -1) { 101 time2 = time1; 102 } 103 long delta = time2 - time1; 104 deltas.put(id, delta); 105 total += delta; 106 } 107 108 // Compute cpu 109 final HashMap<Thread, Long> cpus = new HashMap<Thread, Long>(threads.size()); 110 for (Thread thread : threads) { 111 long cpu = total == 0 ? 0 : Math.round((deltas.get(thread.getId()) * 100) / total); 112 cpus.put(thread, cpu); 113 } 114 115 // Sort by CPU time : should be a rendering hint... 116 Collections.sort(threads, new Comparator<Thread>() { 117 public int compare(Thread o1, Thread o2) { 118 long l1 = cpus.get(o1); 119 long l2 = cpus.get(o2); 120 if (l1 < l2) { 121 return 1; 122 } else if (l1 > l2) { 123 return -1; 124 } else { 125 return 0; 126 } 127 } 128 }); 129 130 // 131 TableElement table = new TableElement(1,3,2,1,1,1,1,1,1).overflow(Overflow.HIDDEN).rightCellPadding(1); 132 133 // Header 134 table.add( 135 new RowElement().style(Decoration.bold.fg(Color.black).bg(Color.white)).add( 136 "ID", 137 "NAME", 138 "GROUP", 139 "PRIORITY", 140 "STATE", 141 "%CPU", 142 "TIME", 143 "INTERRUPTED", 144 "DAEMON" 145 ) 146 ); 147 148 // 149 for (Thread thread : threads) { 150 Color c = colorMapping.get(thread.getState()); 151 long seconds = times2.get(thread.getId()) / 1000000000; 152 long min = seconds / 60; 153 String time = min + ":" + (seconds % 60); 154 long cpu = cpus.get(thread); 155 ThreadGroup group = thread.getThreadGroup(); 156 table.row( 157 new LabelElement(thread.getId()), 158 new LabelElement(thread.getName()), 159 new LabelElement(group == null ? "" : group.getName()), 160 new LabelElement(thread.getPriority()), 161 new LabelElement(thread.getState()).style(c.fg()), 162 new LabelElement(cpu), 163 new LabelElement(time), 164 new LabelElement(thread.isInterrupted()), 165 new LabelElement(thread.isDaemon()) 166 ); 167 } 168 169 // 170 return table.renderer(); 171 } 172}