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    
020    package org.crsh.text.renderers;
021    
022    import org.crsh.text.Color;
023    import org.crsh.text.Decoration;
024    import org.crsh.text.LineRenderer;
025    import org.crsh.text.Renderer;
026    import org.crsh.text.ui.LabelElement;
027    import org.crsh.text.ui.Overflow;
028    import org.crsh.text.ui.RowElement;
029    import org.crsh.text.ui.TableElement;
030    import org.crsh.util.Utils;
031    
032    import java.lang.management.ManagementFactory;
033    import java.lang.management.ThreadMXBean;
034    import java.util.Collections;
035    import java.util.Comparator;
036    import java.util.EnumMap;
037    import java.util.HashMap;
038    import java.util.Iterator;
039    import java.util.List;
040    import java.util.Map;
041    
042    public 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    }