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();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|