package com.onpositive.traps;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.WeakHashMap;
import com.google.appengine.api.datastore.Blob;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.EntityNotFoundException;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.memcache.MemcacheService;
import com.google.appengine.api.memcache.MemcacheServiceFactory;
import com.google.apphosting.api.ApiProxy;
import com.google.apphosting.api.ApiProxy.Delegate;
import com.google.apphosting.api.ApiProxy.Environment;
import com.onpositive.gae.profiling.rpc.DataStoreCallProcessor;
import com.onpositive.gae.profiling.rpc.MemCacheProcessor;
import com.onpositive.gae.profiling.rpc.UniveralDelegate;
public class Profiler {
private static final String DATA = "data";
private static final String COM_ONPOSITIVE_TRAPS_CALL_STORAGE = "com.onpositive.traps.CallStorage";
static CallStorageV1 storage;
private static int counter = -1;
private static boolean delegateInited = false;
private static WeakHashMap<Thread, CallStackHandler> currentMap = new WeakHashMap<Thread, CallStackHandler>();
static ThreadLocal<CallStackHandler> stack = new ThreadLocal<CallStackHandler>() {
protected CallStackHandler initialValue() {
CallStackHandler callStackHandler = new CallStackHandler();
currentMap.put(Thread.currentThread(), callStackHandler);
return callStackHandler;
}
};
public static boolean trace = false;
static {
initStorage();
}
public static void enterBinaryCall(int call) {
CallStackHandler callStackHandler = stack.get();
if (trace) {
Throwable fillInStackTrace = new Exception().fillInStackTrace();
StackTraceElement[] stackTrace = fillInStackTrace.getStackTrace();
System.out.println("Enter Binary:" + stackTrace[1] + ":"
+ callStackHandler.depth() + ":" + call);
}
callStackHandler.enteredBinary(call);
}
public static void incCPU(int delta) {
CallStackHandler callStackHandler = stack.get();
callStackHandler.incCPU(delta);
}
public static void exitBinaryCall(int call) {
// long l0=System.currentTimeMillis();
CallStackHandler callStackHandler = stack.get();
if (trace) {
Throwable fillInStackTrace = new Exception().fillInStackTrace();
StackTraceElement[] stackTrace = fillInStackTrace.getStackTrace();
System.out.println("Exit Binary:" + stackTrace[1] + ":"
+ callStackHandler.depth() + ":" + call);
}
callStackHandler.exitBinary(call);
// long l1=System.currentTimeMillis();
// System.out.println(l1-l0);
}
public static synchronized int getJSPId(String path) {
if (storage == null) {
initStorage();
}
return storage.getJspId(path);
}
public static void enter(int call) {
CallStackHandler callStackHandler = stack.get();
if (callStackHandler.depth() < 0) {
synchronized (Profiler.class) {
counter++;
}
}
if (trace) {
Throwable fillInStackTrace = new Exception().fillInStackTrace();
StackTraceElement[] stackTrace = fillInStackTrace.getStackTrace();
System.out.println(stackTrace[1] + ":" + callStackHandler.depth());
}
callStackHandler.entered(call);
if (!delegateInited) {
UniveralDelegate.install();
delegateInited = true;
}
}
public static void exit(int call) {
// long l0=System.currentTimeMillis();
CallStackHandler callStackHandler = stack.get();
if (trace) {
Throwable fillInStackTrace = new Exception().fillInStackTrace();
StackTraceElement[] stackTrace = fillInStackTrace.getStackTrace();
System.out.println("Exit:" + stackTrace[1] + ":"
+ callStackHandler.depth());
}
if (callStackHandler.exited(call)) {
synchronized (Profiler.class) {
dumpStorage();
unInstalDelegate();
}
}
// else{
// //long l1=System.currentTimeMillis();
// //System.out.println(l1-l0);
// }
}
private static void unInstalDelegate() {
counter--;
if (counter < 0) {
Delegate<Environment> nd = ApiProxy.getDelegate();
if (nd instanceof UniveralDelegate) {
Delegate<Environment> old = ((UniveralDelegate) nd)
.getDelegate();
ApiProxy.setDelegate(old);
delegateInited = false;
}
}
}
public static void exception(int call) {
CallStackHandler callStackHandler = stack.get();
if (trace) {
Throwable fillInStackTrace = new Exception().fillInStackTrace();
StackTraceElement[] stackTrace = fillInStackTrace.getStackTrace();
System.out.println("Exception:" + stackTrace[1] + ":"
+ callStackHandler.depth());
}
if (callStackHandler.exited(call)) {
synchronized (Profiler.class) {
dumpStorage();
unInstalDelegate();
}
}
}
public static void clearStorage() {
storage = new CallStorageV1();
dumpStorage();
DataStoreCallProcessor.clear();
MemCacheProcessor.clear();
}
private static void initStorage() {
MemcacheService memcacheService = MemcacheServiceFactory
.getMemcacheService();
String version = getVersion();
byte[] object = (byte[]) memcacheService
.get(COM_ONPOSITIVE_TRAPS_CALL_STORAGE + version);
if (object != null) {
DataInputStream dataInputStream = new DataInputStream(
new ByteArrayInputStream(object));
try {
int readInt = dataInputStream.readInt();
if (readInt == 1) {
storage = new CallStorageV1(dataInputStream);
return;
}
} catch (IOException e) {
e.printStackTrace();
}
}
try {
Entity asSingleEntity = DatastoreServiceFactory
.getDatastoreService().get(getKey());
if (asSingleEntity != null) {
Blob bls = (Blob) asSingleEntity.getProperty(DATA);
object = bls.getBytes();
DataInputStream dataInputStream = new DataInputStream(
new ByteArrayInputStream(object));
try {
int readInt = dataInputStream.readInt();
if (readInt == 1) {
storage = new CallStorageV1(dataInputStream);
}
return;
} catch (IOException e) {
e.printStackTrace();
}
}
} catch (EntityNotFoundException e) {
storage = new CallStorageV1();
}
}
private static Key getKey() {
String version = getVersion();
return KeyFactory.createKey(COM_ONPOSITIVE_TRAPS_CALL_STORAGE, version);
}
private static String getVersion() {
Environment env = ApiProxy.getCurrentEnvironment();
String vId = env.getVersionId();
String[] splitted = vId.split("\\.");
String v = splitted[0];
String version = "v" + v;
return version;
}
public static volatile boolean dumping;
private static synchronized void dumpStorage() {
try {
dumping = true;
ByteArrayOutputStream st = new ByteArrayOutputStream();
DataOutputStream stream = new DataOutputStream(st);
storage.store(stream);
stream.close();
byte[] byteArray = st.toByteArray();
String version = getVersion();
MemcacheServiceFactory.getMemcacheService().put(
COM_ONPOSITIVE_TRAPS_CALL_STORAGE + version, byteArray);
Entity entity = new Entity(getKey());
entity.setProperty(DATA, new Blob(byteArray));
DatastoreServiceFactory.getDatastoreService().put(entity);
} catch (Throwable e) {
e.printStackTrace();
} finally {
dumping = false;
}
}
public static synchronized byte[] getData() {
long l0 = System.currentTimeMillis();
ByteArrayOutputStream st = new ByteArrayOutputStream();
DataOutputStream stream = new DataOutputStream(st);
try {
storage.store(stream);
// storing call stack
ArrayList<CallStackHandler> arrayList = new ArrayList<CallStackHandler>(
currentMap.values());
stream.writeLong(l0);
stream.writeInt(arrayList.size());
for (CallStackHandler h : arrayList) {
h.store(stream);
}
stream.close();
} catch (Exception e) {
e.printStackTrace();
}
return st.toByteArray();
}
public synchronized static void record(int from, int to, int systemTime) {
if (storage == null) {
initStorage();
}
storage.record(from, to, systemTime);
}
public static synchronized ICallStorageAttachment getAttachment(
Class<? extends ICallStorageAttachment> a) {
if (storage == null) {
initStorage();
}
return storage.getAttachment(a);
}
public static synchronized void putAttachment(ICallStorageAttachment a) {
if (storage == null) {
initStorage();
}
storage.putAttachment(a);
}
public synchronized static void incCpu(int k0, int i, int delta) {
if (storage == null) {
initStorage();
}
storage.incCpu(k0, i, delta);
}
}