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.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 }
083
084 // Resample
085 Map<Long, Long> times2 = new HashMap<Long, Long>(threads.size());
086 for (Thread thread : threads) {
087 long cpu = threadMXBean.getThreadCpuTime(thread.getId());
088 times2.put(thread.getId(), cpu);
089 }
090
091 // Compute delta map and total time
092 long total = 0;
093 Map<Long, Long> deltas = new HashMap<Long, Long>(threads.size());
094 for (Long id : times2.keySet()) {
095 long time1 = times2.get(id);
096 long time2 = times1.get(id);
097 if (time1 == -1) {
098 time1 = time2;
099 } else if (time2 == -1) {
100 time2 = time1;
101 }
102 long delta = time2 - time1;
103 deltas.put(id, delta);
104 total += delta;
105 }
106
107 // Compute cpu
108 final HashMap<Thread, Long> cpus = new HashMap<Thread, Long>(threads.size());
109 for (Thread thread : threads) {
110 long cpu = total == 0 ? 0 : Math.round((deltas.get(thread.getId()) * 100) / total);
111 cpus.put(thread, cpu);
112 }
113
114 // Sort by CPU time : should be a rendering hint...
115 Collections.sort(threads, new Comparator<Thread>() {
116 public int compare(Thread o1, Thread o2) {
117 long l1 = cpus.get(o1);
118 long l2 = cpus.get(o2);
119 if (l1 < l2) {
120 return 1;
121 } else if (l1 > l2) {
122 return -1;
123 } else {
124 return 0;
125 }
126 }
127 });
128
129 //
130 TableElement table = new TableElement(1,3,2,1,1,1,1,1,1).overflow(Overflow.HIDDEN).rightCellPadding(1);
131
132 // Header
133 RowElement header = new RowElement();
134 header.style(Decoration.bold.fg(Color.black).bg(Color.white));
135 header.add(new LabelElement("ID"));
136 header.add(new LabelElement("NAME"));
137 header.add(new LabelElement("GROUP"));
138 header.add(new LabelElement("PRIORITY"));
139 header.add(new LabelElement("STATE"));
140 header.add(new LabelElement("%CPU"));
141 header.add(new LabelElement("TIME"));
142 header.add(new LabelElement("INTERRUPTED"));
143 header.add(new LabelElement("DAEMON"));
144 table.add(header);
145
146 //
147 for (Thread thread : threads) {
148 Color c = colorMapping.get(thread.getState());
149 long seconds = times2.get(thread.getId()) / 1000000000;
150 long min = seconds / 60;
151 String time = min + ":" + (seconds % 60);
152 long cpu = cpus.get(thread);
153
154 //
155 ThreadGroup group = thread.getThreadGroup();
156
157 //
158 RowElement row = new RowElement();
159 row.add(new LabelElement(thread.getId()));
160 row.add(new LabelElement(thread.getName()));
161 row.add(new LabelElement(group == null ? "" : group.getName()));
162 row.add(new LabelElement(thread.getPriority()));
163 row.add(new LabelElement(thread.getState()).style(c.fg()));
164 row.add(new LabelElement(cpu));
165 row.add(new LabelElement(time));
166 row.add(new LabelElement(thread.isInterrupted()));
167 row.add(new LabelElement(thread.isDaemon()));
168 table.add(row);
169 }
170
171 //
172 return table.renderer();
173 }
174 }