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 }