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}