package com.onpositive.gae.profiler.core;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.IdentityHashMap;
import com.onpositive.gae.profiler.IProfilerSnaphotModel;
import com.onpositive.gae.profiler.ITrace;
import com.onpositive.gae.profiler.SnapshotFilter;
import com.onpositive.gae.profiling.rpc.DataStoreCallStorageAttachment;
import com.onpositive.gae.profiling.rpc.MemCacheStorageAttachment;
import com.onpositive.instrumentation.tasks.CallDictionary;
import com.onpositive.instrumentation.tasks.CallEntry;
import com.onpositive.traps.CallStackHandler;
import com.onpositive.traps.CallStorageV1;
import com.onpositive.traps.CallStorageV1.StorageIterator;
public class Snapshot implements IProfilerSnaphotModel {
private CallEntry[] dictionary;
private int totalValue;
private int apitotalValue;
private ArrayList<AccumulatedMethod> roots;
ArrayList<AccumulatedMethod> methods;
private ArrayList<AccumulatedMethod> methodGroups;
public AccumulatedMethod[] getMethods() {
if (filter != null) {
ArrayList<AccumulatedMethod> result = new ArrayList<AccumulatedMethod>();
for (AccumulatedMethod m : methods) {
if (filter.accept(m)) {
result.add(m);
}
}
return result.toArray(new AccumulatedMethod[result.size()]);
}
return methods.toArray(new AccumulatedMethod[methods.size()]);
}
public void setMethods(ArrayList<AccumulatedMethod> methods) {
this.methods = methods;
}
SnapshotFilter filter;
private ArrayList<AccumulatedMethod> componentsList;
private CallStorageV1 storage;
private DataStoreCallStorageAttachment dataStoreInfo;
private MemCacheStorageAttachment memCacheInfo;
int length;
private CallEntry[] jsp;
public Snapshot(InputStream stream) throws IOException {
DataInputStream di = new DataInputStream(stream);
length = di.readInt();
int v1 = di.readInt();
if (v1 == 1) {
storage = new CallStorageV1(di);
long readLong = di.readLong();
int readInt = di.readInt();
for (int a = 0; a < readInt; a++) {
CallStackHandler handler = new CallStackHandler(di);
handler.apply(storage, readLong);
}
}
dictionary = CallDictionary.read(di);
ArrayList<String> jsps2 = storage.getJSPS();
this.jsp = new CallEntry[jsps2.size()];
int size = jsps2.size();
for (int a = 0; a < size; a++) {
jsp[a] = new CallEntry(jsps2.get(a), "", "");
}
roots = new ArrayList<AccumulatedMethod>();
methods = new ArrayList<AccumulatedMethod>();
dataStoreInfo = (DataStoreCallStorageAttachment) storage
.getAttachment(DataStoreCallStorageAttachment.class);
memCacheInfo = (MemCacheStorageAttachment) storage
.getAttachment(MemCacheStorageAttachment.class);
final IdentityHashMap<CallEntry, AccumulatedMethod> ps = new IdentityHashMap<CallEntry, AccumulatedMethod>();
storage.iterate(new StorageIterator() {
public void accept(int from, int to, int index, int[] values) {
if (to == 0) {
CallEntry root = null;
if (from > Integer.MAX_VALUE / 2) {
root = jsp[Integer.MAX_VALUE - from];
} else {
if(from -1 > dictionary.length -1){
return;
}
root = dictionary[from - 1];
}
AccumulatedMethod accumulatedMethod = new AccumulatedMethod(
root);
accumulatedMethod.count = values[index];
accumulatedMethod.time = values[index + 1];
accumulatedMethod.apicpuTime = values[index + 2];
roots.add(accumulatedMethod);
} else {
CallEntry entry = null;
if (from > Integer.MAX_VALUE / 2) {
entry = jsp[Integer.MAX_VALUE - from];
} else {
if(from -1 > dictionary.length -1){
return;
}
entry = dictionary[from - 1];
}
AccumulatedMethod accumulatedMethod = ps.get(entry);
if (accumulatedMethod == null) {
AccumulatedMethod m = new AccumulatedMethod(entry);
accumulatedMethod = m;
methods.add(m);
ps.put(entry, m);
}
accumulatedMethod.count += values[index];
accumulatedMethod.time += values[index + 1];
accumulatedMethod.selftime += values[index + 1];
accumulatedMethod.apicpuTime = values[index + 2];
}
}
});
storage.iterate(new StorageIterator() {
public void accept(int from, int to, int index, int[] values) {
if (to == 0) {
} else {
CallEntry entry = null;
if (to > Integer.MAX_VALUE / 2) {
entry = jsp[Integer.MAX_VALUE - to];
} else {
if(to -1 > dictionary.length -1){
return;
}
entry = dictionary[to - 1];
}
AccumulatedMethod accumulatedMethod = ps.get(entry);
if (accumulatedMethod == null) {
accumulatedMethod = new AccumulatedMethod(entry);
ps.put(entry, accumulatedMethod);
accumulatedMethod.count += values[index];
accumulatedMethod.time += values[index + 1];
accumulatedMethod.apicpuTime = values[index + 2];
accumulatedMethod.selftime += values[index + 1];
}
accumulatedMethod.selftime -= values[index + 1];
if (accumulatedMethod.selftime < 0) {
accumulatedMethod.selftime = 0;
}
}
}
});
methods.addAll(roots);
roots.trimToSize();
for (AccumulatedMethod m : roots) {
totalValue += m.time;
apitotalValue += m.time;
}
if (apitotalValue == 0) {
apitotalValue = 1;
}
HashMap<String, MethodGroup> topLevel = new HashMap<String, MethodGroup>();
HashMap<String, MethodGroup> components = new HashMap<String, MethodGroup>();
for (AccumulatedMethod m : methods) {
if (m.getMethodName()==null||m.getMethodName().length()==0){
continue;
}
String className = m.getClassName();
int firstDot = className.indexOf('.');
if (firstDot != -1) {
String topLevelS = className.substring(0, firstDot);
MethodGroup methodGroup = topLevel.get(topLevelS);
if (methodGroup == null) {
methodGroup = new MethodGroup(topLevelS, this);
topLevel.put(topLevelS, methodGroup);
}
methodGroup.count += m.count;
methodGroup.selftime += m.selftime;
}
totalValue = Math.max(totalValue, m.time);
}
for (AccumulatedMethod m : methods) {
String className = m.getClassName();
if (m.getMethodName()==null||m.getMethodName().length()==0){
continue;
}
int firstDot = className.lastIndexOf('.');
String topLevelS = className.substring(0, firstDot);
if (topLevelS.length()==0){
continue;
}
MethodGroup methodGroup = components.get(topLevelS);
if (methodGroup == null) {
methodGroup = new MethodGroup(topLevelS, this);
components.put(topLevelS, methodGroup);
}
methodGroup.count += m.count;
methodGroup.selftime += m.selftime;
}
methodGroups = new ArrayList<AccumulatedMethod>(topLevel.values());
methods.trimToSize();
componentsList = new ArrayList<AccumulatedMethod>(components.values());
Comparator<AccumulatedMethod> c = new Comparator<AccumulatedMethod>() {
public int compare(AccumulatedMethod o1, AccumulatedMethod o2) {
return o1.selftime - o2.selftime;
}
};
Collections.sort(methods, c);
Collections.sort(componentsList, c);
}
public double getTotalValue() {
return totalValue;
}
public Object[] getTopLevelPackages() {
if (filter != null) {
ArrayList<AccumulatedMethod> result = new ArrayList<AccumulatedMethod>();
for (AccumulatedMethod m : methodGroups) {
if (m.isVisible()) {
if (filter.accept0(m)) {
result.add(m);
}
}
}
return result.toArray(new AccumulatedMethod[result.size()]);
}
return methodGroups.toArray(new AccumulatedMethod[methodGroups.size()]);
}
public SnapshotFilter getModelFilter() {
return filter;
}
public void setModelFilter(SnapshotFilter resourceSnapshotFilter) {
this.filter = resourceSnapshotFilter;
}
public ITrace[] getHotSpotsByModule() {
ArrayList<AccumulatedMethod> result = new ArrayList<AccumulatedMethod>();
long sum = 0;
for (AccumulatedMethod m : componentsList) {
sum += m.getSelfTime();
if (result.size() > 10) {
continue;
}
if (m.getSelfTime() / totalValue > 0.1) {
result.add(m);
}
}
totalValue = (int) Math.max(sum, totalValue);
return result.toArray(new ITrace[result.size()]);
}
public ITrace[] getHotSpotsByMethod() {
ArrayList<AccumulatedMethod> result = new ArrayList<AccumulatedMethod>();
for (AccumulatedMethod m : methods) {
if (m.getSelfTime() / totalValue > 0.1) {
result.add(m);
}
if (result.size() > 10) {
break;
}
}
return result.toArray(new ITrace[result.size()]);
}
public Object[] getRoots() {
return roots.toArray(new ITrace[roots.size()]);
}
public Object[] getCalles(final AccumulatedMethod ma) {
final IdentityHashMap<CallEntry, AccumulatedMethod> mz = new IdentityHashMap<CallEntry, AccumulatedMethod>();
storage.iterate(new StorageIterator() {
public void accept(int from, int to, int index, int[] values) {
if (to != 0) {
CallEntry entry = null;
if (to > Integer.MAX_VALUE/2) {
entry = jsp[Integer.MAX_VALUE - to];
} else {
if(to -1 > dictionary.length -1){
return;
}
entry = dictionary[to - 1];
}
if (isFrom(ma, entry)) {
AccumulatedMethod m = new AccumulatedMethod(
dictionary[from - 1]);
mz.put(dictionary[from - 1], m);
m.count += values[index];
m.time += values[index + 1];
m.selftime += values[index + 1];
m.apicpuTime += values[index + 2];
}
}
}
});
storage.iterate(new StorageIterator() {
public void accept(int from, int to, int index, int[] values) {
if (to != 0) {
CallEntry entry = null;
if (to > Integer.MAX_VALUE/2) {
entry = jsp[Integer.MAX_VALUE - to];
} else {
entry = dictionary[to - 1];
}
AccumulatedMethod accumulatedMethod = mz.get(entry);
if (accumulatedMethod != null) {
accumulatedMethod.selftime -= values[index + 2];
}
}
}
});
return mz.values().toArray(new Object[mz.size()]);
}
private boolean isFrom(AccumulatedMethod ma, CallEntry e) {
if (ma.getEntry()==e){
return true;
}
if (ma.entry.className.equals(e.className.replace('/', '.'))) {
if (ma.entry.methodName.equals(e.methodName)) {
if (ma.entry.descriptor.equals(e.descriptor)) {
return true;
}
}
}
return false;
}
public Object getDataStoreInfo() {
if (dataStoreInfo != null) {
return dataStoreInfo.getInfo();
}
return null;
}
public Object getMemCacheInfo() {
if (memCacheInfo != null) {
return memCacheInfo.getInfo();
}
return null;
}
public int getApiTotal() {
return apitotalValue;
}
}