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.formatter;
021    
022    import org.crsh.text.Color;
023    import org.crsh.text.Decoration;
024    import org.crsh.text.Renderable;
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 ThreadRenderable extends Renderable<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 Renderer 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        RowElement header = new RowElement();
135        header.style(Decoration.bold.fg(Color.black).bg(Color.white));
136        header.add(new LabelElement("ID"));
137        header.add(new LabelElement("NAME"));
138        header.add(new LabelElement("GROUP"));
139        header.add(new LabelElement("PRIORITY"));
140        header.add(new LabelElement("STATE"));
141        header.add(new LabelElement("%CPU"));
142        header.add(new LabelElement("TIME"));
143        header.add(new LabelElement("INTERRUPTED"));
144        header.add(new LabelElement("DAEMON"));
145        table.add(header);
146    
147        //
148        for (Thread thread : threads) {
149          Color c = colorMapping.get(thread.getState());
150          long seconds = times2.get(thread.getId()) / 1000000000;
151          long min = seconds / 60;
152          String time = min + ":" + (seconds % 60);
153          long cpu = cpus.get(thread);
154    
155          //
156          ThreadGroup group = thread.getThreadGroup();
157    
158          //
159          RowElement row = new RowElement();
160          row.add(new LabelElement(thread.getId()));
161          row.add(new LabelElement(thread.getName()));
162          row.add(new LabelElement(group == null ? "" : group.getName()));
163          row.add(new LabelElement(thread.getPriority()));
164          row.add(new LabelElement(thread.getState()).style(c.fg()));
165          row.add(new LabelElement(cpu));
166          row.add(new LabelElement(time));
167          row.add(new LabelElement(thread.isInterrupted()));
168          row.add(new LabelElement(thread.isDaemon()));
169          table.add(row);
170        }
171    
172        //
173        return table.renderer();
174      }
175    }