324 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			324 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Java
		
	
	
	
| package tools;
 | |
| 
 | |
| import java.io.IOException;
 | |
| import java.io.Writer;
 | |
| import java.util.Set;
 | |
| import java.util.Collections;
 | |
| import java.util.Map.Entry;
 | |
| import java.util.ArrayList;
 | |
| import java.util.Iterator;
 | |
| import java.util.HashMap;
 | |
| import java.util.LinkedList;
 | |
| import java.util.Map;
 | |
| import java.util.List;
 | |
| 
 | |
| public class CPUSampler {
 | |
|     private List<String> included;
 | |
|     private static CPUSampler instance = new CPUSampler();
 | |
|     private long interval;
 | |
|     private SamplerThread sampler;
 | |
|     private Map<StackTrace, Integer> recorded;
 | |
|     private int totalSamples;
 | |
| 
 | |
|     public CPUSampler() {
 | |
|         this.included = (List<String>) new LinkedList();
 | |
|         this.interval = 5L;
 | |
|         this.sampler = null;
 | |
|         this.recorded = (Map<StackTrace, Integer>) new HashMap();
 | |
|         this.totalSamples = 0;
 | |
|     }
 | |
| 
 | |
|     public static CPUSampler getInstance() {
 | |
|         return CPUSampler.instance;
 | |
|     }
 | |
| 
 | |
|     public void setInterval(final long millis) {
 | |
|         this.interval = millis;
 | |
|     }
 | |
| 
 | |
|     public void addIncluded(final String include) {
 | |
|         for (final String alreadyIncluded : this.included) {
 | |
|             if (include.startsWith(alreadyIncluded)) {
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|         this.included.add(include);
 | |
|     }
 | |
| 
 | |
|     public void reset() {
 | |
|         this.recorded.clear();
 | |
|         this.totalSamples = 0;
 | |
|     }
 | |
| 
 | |
|     public void start() {
 | |
|         if (this.sampler == null) {
 | |
|             (this.sampler = new SamplerThread()).start();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public void stop() {
 | |
|         if (this.sampler != null) {
 | |
|             this.sampler.stop();
 | |
|             this.sampler = null;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public SampledStacktraces getTopConsumers() {
 | |
|         final List<StacktraceWithCount> ret = (List<StacktraceWithCount>) new ArrayList();
 | |
|         final Set<Entry<StackTrace, Integer>> entrySet = this.recorded.entrySet();
 | |
|         for (final Entry<StackTrace, Integer> entry : entrySet) {
 | |
|             ret.add(new StacktraceWithCount(((Integer) entry.getValue()).intValue(), (StackTrace) entry.getKey()));
 | |
|         }
 | |
|         Collections.sort(ret);
 | |
|         return new SampledStacktraces(ret, this.totalSamples);
 | |
|     }
 | |
| 
 | |
|     public void save(final Writer writer, final int minInvocations, final int topMethods) throws IOException {
 | |
|         final SampledStacktraces topConsumers = this.getTopConsumers();
 | |
|         final StringBuilder builder = new StringBuilder();
 | |
|         builder.append("Top Methods:\n");
 | |
|         for (int i = 0; i < topMethods && i < topConsumers.getTopConsumers().size(); ++i) {
 | |
|             builder.append(((StacktraceWithCount) topConsumers.getTopConsumers().get(i)).toString(topConsumers.getTotalInvocations(), 1));
 | |
|         }
 | |
|         builder.append("\nStack Traces:\n");
 | |
|         writer.write(builder.toString());
 | |
|         writer.write(topConsumers.toString(minInvocations));
 | |
|         writer.flush();
 | |
|     }
 | |
| 
 | |
|     private void consumeStackTraces(final Map<Thread, StackTraceElement[]> traces) {
 | |
|         for (final Entry<Thread, StackTraceElement[]> trace : traces.entrySet()) {
 | |
|             final int relevant = this.findRelevantElement((StackTraceElement[]) trace.getValue());
 | |
|             if (relevant != -1) {
 | |
|                 final StackTrace st = new StackTrace((StackTraceElement[]) trace.getValue(), relevant, ((Thread) trace.getKey()).getState());
 | |
|                 final Integer i = (Integer) this.recorded.get(st);
 | |
|                 ++this.totalSamples;
 | |
|                 if (i == null) {
 | |
|                     this.recorded.put(st, Integer.valueOf(1));
 | |
|                 } else {
 | |
|                     this.recorded.put(st, Integer.valueOf(i.intValue() + 1));
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private int findRelevantElement(final StackTraceElement[] trace) {
 | |
|         if (trace.length == 0) {
 | |
|             return -1;
 | |
|         }
 | |
|         if (this.included.size() == 0) {
 | |
|             return 0;
 | |
|         }
 | |
|         int firstIncluded = -1;
 | |
|         for (final String myIncluded : this.included) {
 | |
|             for (int i = 0; i < trace.length; ++i) {
 | |
|                 final StackTraceElement ste = trace[i];
 | |
|                 if (ste.getClassName().startsWith(myIncluded) && (i < firstIncluded || firstIncluded == -1)) {
 | |
|                     firstIncluded = i;
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if (firstIncluded >= 0 && trace[firstIncluded].getClassName().equals("net.sf.odinms.tools.performance.CPUSampler$SamplerThread")) {
 | |
|             return -1;
 | |
|         }
 | |
|         return firstIncluded;
 | |
|     }
 | |
| 
 | |
|     private static class StackTrace {
 | |
|         private StackTraceElement[] trace;
 | |
|         private java.lang.Thread.State state;
 | |
| 
 | |
|         public StackTrace(final StackTraceElement[] trace, final int startAt, final java.lang.Thread.State state) {
 | |
|             this.state = state;
 | |
|             if (startAt == 0) {
 | |
|                 this.trace = trace;
 | |
|             } else {
 | |
|                 System.arraycopy(trace, startAt, this.trace = new StackTraceElement[trace.length - startAt], 0, this.trace.length);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public boolean equals(final Object obj) {
 | |
|             if (!(obj instanceof StackTrace)) {
 | |
|                 return false;
 | |
|             }
 | |
|             final StackTrace other = (StackTrace) obj;
 | |
|             if (other.trace.length != this.trace.length) {
 | |
|                 return false;
 | |
|             }
 | |
|             if (other.state != this.state) {
 | |
|                 return false;
 | |
|             }
 | |
|             for (int i = 0; i < this.trace.length; ++i) {
 | |
|                 if (!this.trace[i].equals(other.trace[i])) {
 | |
|                     return false;
 | |
|                 }
 | |
|             }
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public int hashCode() {
 | |
|             int ret = 13 * this.trace.length + this.state.hashCode();
 | |
|             for (final StackTraceElement ste : this.trace) {
 | |
|                 ret ^= ste.hashCode();
 | |
|             }
 | |
|             return ret;
 | |
|         }
 | |
| 
 | |
|         public StackTraceElement[] getTrace() {
 | |
|             return this.trace;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public String toString() {
 | |
|             return this.toString(-1);
 | |
|         }
 | |
| 
 | |
|         public String toString(final int traceLength) {
 | |
|             final StringBuilder ret = new StringBuilder("State: ");
 | |
|             ret.append(this.state.name());
 | |
|             if (traceLength > 1) {
 | |
|                 ret.append("\n");
 | |
|             } else {
 | |
|                 ret.append(" ");
 | |
|             }
 | |
|             int i = 0;
 | |
|             for (final StackTraceElement ste : this.trace) {
 | |
|                 if (++i > traceLength) {
 | |
|                     break;
 | |
|                 }
 | |
|                 ret.append(ste.getClassName());
 | |
|                 ret.append("#");
 | |
|                 ret.append(ste.getMethodName());
 | |
|                 ret.append(" (Line: ");
 | |
|                 ret.append(ste.getLineNumber());
 | |
|                 ret.append(")\n");
 | |
|             }
 | |
|             return ret.toString();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     private class SamplerThread implements Runnable {
 | |
|         private boolean running;
 | |
|         private boolean shouldRun;
 | |
|         private Thread rthread;
 | |
| 
 | |
|         private SamplerThread() {
 | |
|             this.running = false;
 | |
|             this.shouldRun = false;
 | |
|         }
 | |
| 
 | |
|         public void start() {
 | |
|             if (!this.running) {
 | |
|                 this.shouldRun = true;
 | |
|                 (this.rthread = new Thread((Runnable) this, "CPU Sampling Thread")).start();
 | |
|                 this.running = true;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public void stop() {
 | |
|             this.shouldRun = false;
 | |
|             this.rthread.interrupt();
 | |
|             try {
 | |
|                 this.rthread.join();
 | |
|             } catch (InterruptedException e) {
 | |
|                 e.printStackTrace();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public void run() {
 | |
|             while (this.shouldRun) {
 | |
|                 CPUSampler.this.consumeStackTraces(Thread.getAllStackTraces());
 | |
|                 try {
 | |
|                     Thread.sleep(CPUSampler.this.interval);
 | |
|                     continue;
 | |
|                 } catch (InterruptedException e) {
 | |
|                     return;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public static class StacktraceWithCount implements Comparable<StacktraceWithCount> {
 | |
|         private int count;
 | |
|         private StackTrace trace;
 | |
| 
 | |
|         public StacktraceWithCount(final int count, final StackTrace trace) {
 | |
|             this.count = count;
 | |
|             this.trace = trace;
 | |
|         }
 | |
| 
 | |
|         public int getCount() {
 | |
|             return this.count;
 | |
|         }
 | |
| 
 | |
|         public StackTraceElement[] getTrace() {
 | |
|             return this.trace.getTrace();
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public int compareTo(final StacktraceWithCount o) {
 | |
|             return -Integer.valueOf(this.count).compareTo(Integer.valueOf(o.count));
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public boolean equals(final Object oth) {
 | |
|             if (!(oth instanceof StacktraceWithCount)) {
 | |
|                 return false;
 | |
|             }
 | |
|             final StacktraceWithCount o = (StacktraceWithCount) oth;
 | |
|             return this.count == o.count;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public String toString() {
 | |
|             return this.count + " Sampled Invocations\n" + this.trace.toString();
 | |
|         }
 | |
| 
 | |
|         private double getPercentage(final int total) {
 | |
|             return (double) Math.round((double) this.count / (double) total * 10000.0) / 100.0;
 | |
|         }
 | |
| 
 | |
|         public String toString(final int totalInvoations, final int traceLength) {
 | |
|             return this.count + "/" + totalInvoations + " Sampled Invocations (" + this.getPercentage(totalInvoations) + "%) " + this.trace.toString(traceLength);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     public static class SampledStacktraces {
 | |
|         List<StacktraceWithCount> topConsumers;
 | |
|         int totalInvocations;
 | |
| 
 | |
|         public SampledStacktraces(final List<StacktraceWithCount> topConsumers, final int totalInvocations) {
 | |
|             this.topConsumers = topConsumers;
 | |
|             this.totalInvocations = totalInvocations;
 | |
|         }
 | |
| 
 | |
|         public List<StacktraceWithCount> getTopConsumers() {
 | |
|             return this.topConsumers;
 | |
|         }
 | |
| 
 | |
|         public int getTotalInvocations() {
 | |
|             return this.totalInvocations;
 | |
|         }
 | |
| 
 | |
|         @Override
 | |
|         public String toString() {
 | |
|             return this.toString(0);
 | |
|         }
 | |
| 
 | |
|         public String toString(final int minInvocation) {
 | |
|             final StringBuilder ret = new StringBuilder();
 | |
|             for (final StacktraceWithCount swc : this.topConsumers) {
 | |
|                 if (swc.getCount() >= minInvocation) {
 | |
|                     ret.append(swc.toString(this.totalInvocations, Integer.MAX_VALUE));
 | |
|                     ret.append("\n");
 | |
|                 }
 | |
|             }
 | |
|             return ret.toString();
 | |
|         }
 | |
|     }
 | |
| }
 |