com.eg.agent.android.trace.TracerMachine Maven / Gradle / Ivy
The newest version!
package com.eg.agent.android.trace;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Stack;
import java.util.concurrent.CopyOnWriteArrayList;
import com.eg.agent.android.harvest.ExceptionMarker;
import com.eg.agent.android.harvest.HistoryTransformer;
import com.eg.agent.android.harvest.Reaper;
import com.eg.agent.android.harvest.ReaperAdapter;
import com.eg.agent.android.harvest.VisibilityTransformer;
import com.eg.agent.android.instrumentation.MetricType;
import com.eg.agent.android.logging.EGAgentLog;
import com.eg.agent.android.logging.EGAgentLogManager;
import com.eg.agent.android.DataMeasurements;
import com.eg.agent.android.Features;
import com.eg.agent.android.MobileAgentUpload;
import com.eg.agent.android.Queue;
public class TracerMachine extends ReaperAdapter {
public static final String ACTIVITY_METRIC_PREFIX = "Mobile/Activity/Name/";
public static final String ACTIVITY_BACKGROUND_METRIC_PREFIX = "Mobile/Activity/Background/Name/";
public static final String ACTIVTY_DISPLAY_NAME_PREFIX = "";
private static final Collection traceListeners = new CopyOnWriteArrayList();
private static final List activityHistory = new CopyOnWriteArrayList();
private static final Object TRACE_MACHINE_LOCK = new Object();
private static final ThreadLocal threadLocalTrace = new ThreadLocal();
private static final ThreadLocal threadLocalTraceStack = new ThreadLocal();
private static TracerMachine traceMachine = null;
private static TracerFieldInterface traceMachineInterface;
static EGAgentLog log = EGAgentLogManager.getAgentLog();
public static int HEALTHY_TRACE_TIMEOUT = 500000;
public static int UNHEALTHY_TRACE_TIMEOUT = 6000000;
static long entryTime;
static long exitTime;
static String status;
static String method = "";
static String category = "";
static String APItype = "";
private ActivityTracer activityTrace;
public static void setAPIType(String api) {
log.debug("TracerMachine:setAPIType::" + api);
APItype = api;
}
protected TracerMachine(Tracer rootTrace) {
this.activityTrace = new ActivityTracer(rootTrace);
Reaper.addHarvestListener(this);
}
public static void startTracing(String name) {
TracerMachine.startTracing(name, false);
}
public static void startTracing(String name, boolean customName) {
TracerMachine.startTracing(name, customName, false);
}
public static void clearActivityHistory() {
activityHistory.clear();
}
public static void startTracing(String name, boolean customName, boolean customInteraction) {
try {
if (!Features.featureEnabled(Features.DefaultInteractions)) {
return;
}
Object object = TRACE_MACHINE_LOCK;
synchronized (object) {
threadLocalTrace.remove();
threadLocalTraceStack.set(new TracerStack());
Tracer rootTrace = new Tracer();
rootTrace.activityClassName = name;
rootTrace.displayName = customName ? name : TracerMachine.formatActivityDisplayName(name);
rootTrace.metricName = TracerMachine.formatActivityMetricName(rootTrace.displayName);
rootTrace.metricBackgroundName = TracerMachine
.formatActivityBackgroundMetricName(rootTrace.displayName);
rootTrace.entryTimestamp = System.currentTimeMillis();
log.debug("Started trace of " + name + ":" + rootTrace.myUUID.toString());
rootTrace.traceMachine = traceMachine = new TracerMachine(rootTrace);
TracerMachine.pushTraceContext(rootTrace);
TracerMachine.traceMachine.activityTrace.previousActivity = TracerMachine.getLastVisibilityTransformer();
activityHistory.add(new VisibilityTransformer(rootTrace.entryTimestamp, rootTrace.displayName));
for (TracerLifecycle listener : traceListeners) {
listener.onTraceStart(TracerMachine.traceMachine.activityTrace);
}
}
} catch (Exception e) {
log.error("Caught error while initializing TraceMachine, shutting it down", e);
ExceptionMarker.noticeException(e);
traceMachine = null;
threadLocalTrace.remove();
threadLocalTraceStack.remove();
}
}
public static void haltTracing() {
Object object = TRACE_MACHINE_LOCK;
synchronized (object) {
TracerMachine finishedMachine = traceMachine;
traceMachine = null;
finishedMachine.activityTrace.discard();
TracerMachine.endLastVisibilityTransformer();
Reaper.removeHarvestListener(finishedMachine);
threadLocalTrace.remove();
threadLocalTraceStack.remove();
}
}
public void storeCompletedTrace(Tracer trace) {
try {
if (TracerMachine.isTracingInactive()) {
log.debug("Attempted to store a completed trace with no trace machine!");
return;
}
this.activityTrace.addCompletedTrace(trace);
} catch (Exception e) {
log.error("Caught error while calling storeCompletedTrace()", e);
throw new RuntimeException();
}
}
public static void endTrace() {
traceMachine.completeActivityTrace();
}
public static void endTrace(String id) {
try {
if (TracerMachine.getActivityTrace().rootTrace.myUUID.toString().equals(id)) {
traceMachine.completeActivityTrace();
}
} catch (Exception Exception) {
// empty catch block
}
}
public static ActivityTracer getActivityTrace() throws Exception {
try {
return TracerMachine.traceMachine.activityTrace;
} catch (NullPointerException e) {
throw new Exception();
}
}
public static boolean isTracingActive() {
return traceMachine != null;
}
public static boolean isTracingInactive() {
return !TracerMachine.isTracingActive();
}
public static Tracer getCurrentTrace() throws Exception {
if (TracerMachine.isTracingInactive()) {
throw new Exception();
}
Tracer trace = threadLocalTrace.get();
if (trace != null) {
return trace;
}
return TracerMachine.getRootTrace();
}
public static Tracer getRootTrace() {
try {
return TracerMachine.traceMachine.activityTrace.rootTrace;
} catch (NullPointerException e) {
log.error(e.getMessage(), e);
}
return null;
}
private static Tracer registerNewTrace(String name) throws Exception {
if (TracerMachine.isTracingInactive()) {
log.debug("Tried to register a new trace but tracing is inactive!");
throw new Exception();
}
Tracer parentTrace = TracerMachine.getCurrentTrace();
Tracer childTrace = new Tracer(name, parentTrace.myUUID, traceMachine);
try {
TracerMachine.traceMachine.activityTrace.addTrace(childTrace);
} catch (Exception e) {
throw new Exception();
}
log.verbose("Registering trace of " + name + " with parent " + parentTrace.displayName);
parentTrace.addChild(childTrace);
return childTrace;
}
/*
* WARNING - Removed try catching itself - possible behaviour change.
*/
protected void completeActivityTrace() {
Object object = TRACE_MACHINE_LOCK;
synchronized (object) {
if (TracerMachine.isTracingInactive()) {
return;
}
TracerMachine finishedMachine = traceMachine;
traceMachine = null;
finishedMachine.activityTrace.complete();
TracerMachine.endLastVisibilityTransformer();
for (TracerLifecycle listener : traceListeners) {
listener.onTraceComplete(finishedMachine.activityTrace);
}
Reaper.removeHarvestListener(finishedMachine);
}
}
public static void enterNetworkSegment(String name) {
try {
if (TracerMachine.isTracingInactive()) {
return;
}
Tracer currentTrace = TracerMachine.getCurrentTrace();
if (currentTrace.getTracerType() == TracerTypes.NETWORK) {
TracerMachine.exitMethod();
}
TracerMachine.enterMethod(name);
Tracer networkTrace = TracerMachine.getCurrentTrace();
networkTrace.setTracerType(TracerTypes.NETWORK);
}
catch (Exception e) {
log.error("Caught error while calling enterNetworkSegment()", e);
ExceptionMarker.noticeException(e);
}
}
public static void enterMethod(String name) {
TracerMachine.enterMethod(name, null);
}
public static void enterMethod(String name, ArrayList annotationParams) {
TracerMachine.enterMethod(null, name, annotationParams);
}
public static void enterMethod(String method, String category, String APItype) {
entryTime = System.currentTimeMillis();
TracerMachine.method = method;
TracerMachine.category = category;
TracerMachine.APItype = APItype;
ArrayList categoryParams = new ArrayList(Arrays.asList(category, MetricType.class.getName(), APItype));
enterMethod(method, categoryParams);
}
public static void enterMethod(Tracer trace, String name, ArrayList annotationParams) {
try {
log.debug("TransactionUtil.enterMethod:::TracerMachine.isTracingInactive()::::"+TracerMachine.isTracingInactive());
if (TracerMachine.isTracingInactive()) {
return;
}
entryTime = System.currentTimeMillis();
long currentTime = System.currentTimeMillis();
long lastUpdatedAt = TracerMachine.traceMachine.activityTrace.lastUpdatedAt;
long inception = TracerMachine.traceMachine.activityTrace.startedAt;
if (lastUpdatedAt + (long) HEALTHY_TRACE_TIMEOUT < currentTime
&& !TracerMachine.traceMachine.activityTrace.hasMissingChildren()) {
log.error(String.format("LastUpdated[%d] CurrentTime[%d] Trigger[%d]", lastUpdatedAt, currentTime,
currentTime - lastUpdatedAt));
log.debug("Completing activity trace after hitting healthy timeout (" + HEALTHY_TRACE_TIMEOUT + "ms)");
if (TracerMachine.isTracingActive()) {
traceMachine.completeActivityTrace();
}
return;
}
if (inception + (long) UNHEALTHY_TRACE_TIMEOUT < currentTime) {
log.debug("Completing activity trace after hitting unhealthy timeout (" + UNHEALTHY_TRACE_TIMEOUT
+ "ms)");
if (TracerMachine.isTracingActive()) {
traceMachine.completeActivityTrace();
}
return;
}
TracerMachine.loadTraceContext(trace);
Tracer childTrace = TracerMachine.registerNewTrace(name);
log.debug("TracerMachine:enterMethod::annotationParams::" + annotationParams);
if (annotationParams != null && annotationParams.size() > 1) {
childTrace.metricName = annotationParams.get(2);
}
log.debug("TracerMachine:enterMethod::childTrace.metricName::" + childTrace.metricName);
TracerMachine.pushTraceContext(childTrace);
childTrace.scope = TracerMachine.getCurrentScope();
childTrace.setAnnotationParams(annotationParams);
for (TracerLifecycle listener : traceListeners) {
listener.onEnterMethod();
}
childTrace.entryTimestamp = System.currentTimeMillis();
}
catch (Exception e) {
log.error("Caught error while calling enterMethod()", e);
ExceptionMarker.noticeException(e);
}
}
private static void loadTraceContext(Tracer trace) {
if (TracerMachine.isTracingInactive()) {
return;
}
if (threadLocalTrace.get() == null) {
threadLocalTrace.set(trace);
threadLocalTraceStack.set(new TracerStack());
if (trace == null) {
return;
}
threadLocalTraceStack.get().push(trace);
} else if (trace == null) {
if (threadLocalTraceStack.get().isEmpty()) {
log.debug("No context to load!");
threadLocalTrace.set(null);
return;
}
trace = (Tracer) threadLocalTraceStack.get().peek();
threadLocalTrace.set(trace);
}
log.verbose("Trace " + trace.myUUID.toString() + " is now active");
}
public static void exitMethod() {
try {
if (TracerMachine.isTracingInactive()) {
return;
}
exitTime = System.currentTimeMillis();
Tracer trace = threadLocalTrace.get();
log.debug("TracerMachine::exitMethod:::trace::::" + trace);
if (trace == null) {
log.debug("threadLocalTrace is null");
return;
}
trace.exitTimestamp = exitTime;
if (trace.threadId == 0L && traceMachineInterface != null) {
trace.threadId = traceMachineInterface.getCurrentThreadId();
trace.threadName = traceMachineInterface.getCurrentThreadName();
}
for (TracerLifecycle listener : traceListeners) {
listener.onExitMethod();
}
if (trace.getActivityClassName() == null) {
trace.activityClassName = TracerMachine.getRootTrace().activityClassName;
}
if (trace.getFullActivityClassName() == null) {
trace.setFullActivityClassName(TracerMachine.getRootTrace().getFullActivityClassName());
}
if (trace.getActivityClassName() == null) {
trace.activityClassName = TracerMachine.getRootTrace().getFragmentClassName();
}
if (trace.getFullActivityClassName() == null) {
trace.setFullActivityClassName(TracerMachine.getRootTrace().getFullFragmentClassName());
}
log.debug("TracerMachine::exitMethod:::trace.getMetricName()::::" + trace.getMetricName());
if ("trace".equalsIgnoreCase(trace.getMetricName())) {
setMetricName(trace);
}
try {
trace.complete();
} catch (Exception e) {
e.printStackTrace();
threadLocalTrace.remove();
threadLocalTraceStack.remove();
if (trace.getTracerType() == TracerTypes.TRACE) {
if(!Queue.isExcluded(trace.getFullActivityClassName())) {
trace.setMainJson(MobileAgentUpload.getJSONObject(trace.getActivityClassName(), trace.displayName,
trace.getMetricName(), trace.exclusiveTime, trace.getMetricName()));
Queue.queue(trace);
}
}
return;
}
threadLocalTraceStack.get().pop();
if (threadLocalTraceStack.get().empty()) {
threadLocalTrace.set(null);
} else {
Tracer parentTrace = (Tracer) threadLocalTraceStack.get().peek();
threadLocalTrace.set(parentTrace);
parentTrace.childExclusiveTime += trace.getDurationAsMilliseconds();
}
if (trace.getTracerType() == TracerTypes.TRACE) {
log.debug("TracerMahine:exitMethod::trace == " + trace.toString());
Queue.queue(trace);
}
} catch (Exception e) {
log.error("Caught error while calling exitMethod()", e);
ExceptionMarker.noticeException(e);
}
}
private static void setMetricName(Tracer tracer) {
log.debug("TracerMachie:setMetricName:displayName::::" + tracer.displayName);
if (tracer != null && tracer.displayName != null) {
if (tracer.displayName.endsWith("onCreate") || tracer.displayName.endsWith("onCreateView")) {
tracer.metricName = TracerMachine.APItype;
} else {
tracer.metricName = "trace";
if (tracer.getAnnotationParameters() == null || tracer.getAnnotationParameters().size() > 1) {
tracer.metricName = "trace";
} else {
tracer.metricName = tracer.getAnnotationParameters().get(2);
}
}
}
log.debug("TracerMachie:setMetricName:metricName::::" + tracer.metricName);
}
public static void exit(Tracer trace) {
exitTime = System.currentTimeMillis();
long excution_time = exitTime - entryTime;
try {
log.debug("TracerMachine:::exit::trace.getMetricName():::"+trace.getMetricName());
log.debug("TracerMachine:::exit::TracerMachine.APItype:::"+TracerMachine.APItype);
log.debug("TracerMachine:::exit::Queue.isExcluded("+trace.getFullActivityClassName()+"):::"+Queue.isExcluded(trace.getFullActivityClassName()));
if(!Queue.isExcluded(trace.getFullActivityClassName())) {
if (trace.getActivityClassName() != null && "trace".equalsIgnoreCase(TracerMachine.APItype)) {
Queue.queue(MobileAgentUpload.getJSONObject(trace.getActivityClassName(), trace.displayName,
trace.getMetricName(), excution_time, trace.getMetricName()));
} else if (!"trace".equalsIgnoreCase(TracerMachine.APItype)) {
Queue.queue(MobileAgentUpload.getJSONObject(trace.getActivityClassName(), trace.displayName,
trace.getMetricName(), excution_time, trace.getMetricName()));
}
}
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
public static void setCurrentTraceParam(String key, Object value) {
}
public static void setMethodName(String name) {
log.debug("TracerMachine:setMethodName::" + name);
TracerMachine.method = name;
}
private static void pushTraceContext(Tracer trace) {
if (trace == null) {
return;
}
TracerStack traceStack = threadLocalTraceStack.get();
if (traceStack.empty()) {
traceStack.push(trace);
} else if (traceStack.peek() != trace) {
traceStack.push(trace);
}
threadLocalTrace.set(trace);
}
public static String getStatus() {
return status;
}
public static void setStatus(String status) {
TracerMachine.status = status;
}
public static long getEntryTime() {
return entryTime;
}
public static void setEntryTime(long entryTime) {
TracerMachine.entryTime = entryTime;
}
public static long getExitTime() {
return exitTime;
}
public static void setExitTime(long exitTime) {
TracerMachine.exitTime = exitTime;
}
public static String getCurrentScope() {
try {
} catch (Exception e) {
return null;
}
return null;
}
public static String formatActivityMetricName(String name) {
return ACTIVITY_METRIC_PREFIX + name;
}
public static String formatActivityBackgroundMetricName(String name) {
return ACTIVITY_BACKGROUND_METRIC_PREFIX + name;
}
public static String formatActivityDisplayName(String name) {
return ACTIVTY_DISPLAY_NAME_PREFIX + name;
}
public static void addTraceListener(TracerLifecycle listener) {
traceListeners.add(listener);
}
public static void removeTraceListener(TracerLifecycle listener) {
traceListeners.remove(listener);
}
public static VisibilityTransformer getLastVisibilityTransformer() {
if (activityHistory.isEmpty()) {
return null;
}
return activityHistory.get(activityHistory.size() - 1);
}
public static void endLastVisibilityTransformer() {
VisibilityTransformer activitySighting = TracerMachine.getLastVisibilityTransformer();
if (activitySighting != null) {
activitySighting.end(System.currentTimeMillis());
}
}
public static HistoryTransformer getActivityHistory() {
return new HistoryTransformer(activityHistory);
}
public static void setFragmentClassName(String currentActiviteName) {
try {
getCurrentTrace().setFragmentClassName(currentActiviteName);
} catch (Exception e) {
log.error("Tracing is inactive::"+e.getMessage());
}
}
public static void setCurrentActivityFullName(String currentActiviteName) {
try {
getCurrentTrace().setFullActivityClassName(currentActiviteName);
} catch (Exception e) {
log.error("Tracing is inactive::"+e.getMessage());
}
}
public static void setFullFragmentClassName(String currentActiviteName) {
try {
getCurrentTrace().setFullFragmentClassName(currentActiviteName);
} catch (Exception e) {
log.error("Tracing is inactive::"+e.getMessage());
}
}
public static void setCurrentActivity(String currentActiviteName) {
try {
getCurrentTrace().setActivityClassName(currentActiviteName);
} catch (Exception e) {
log.error("Tracing is inactive::"+e.getMessage());
}
}
public static String getCurrentActivity() {
try {
return getCurrentTrace().getCurrentActiviteName();
} catch (Exception e) {
log.error("Tracing is inactive::"+e.getMessage());
}
return "";
}
public static String getCurrentActivityFullName() {
try {
return getCurrentTrace().getFullActivityClassName();
} catch (Exception e) {
log.error("Tracing is inactive::"+e.getMessage());
}
return "";
}
public static void unloadTraceContext(Object object) {
try {
if (TracerMachine.isTracingInactive()) {
return;
}
if (traceMachineInterface != null && traceMachineInterface.isUIThread()) {
return;
}
if (threadLocalTrace.get() != null) {
log.verbose("Trace " + TracerMachine.threadLocalTrace.get().myUUID.toString() + " is now inactive");
}
threadLocalTrace.remove();
threadLocalTraceStack.remove();
try {
TracerInterface tfi = (TracerInterface) object;
tfi.setTrace(null);
} catch (ClassCastException e) {
log.error("Not a TraceFieldInterface: " + e.getMessage());
}
} catch (Exception e) {
log.error("Caught error while calling unloadTraceContext()", e);
}
}
public static void setRootDisplayName(String name) {
if (isTracingInactive())
return;
try {
Tracer rootTrace = getRootTrace();
DataMeasurements.renameActivity(rootTrace.displayName, name);
renameActivityHistory(rootTrace.displayName, name);
rootTrace.metricName = formatActivityMetricName(name);
rootTrace.metricBackgroundName = formatActivityBackgroundMetricName(name);
rootTrace.displayName = name;
Tracer currentTrace = getCurrentTrace();
currentTrace.scope = getCurrentScope();
} catch (Exception e) {
return;
}
}
private static void renameActivityHistory(String oldName, String newName) {
for (VisibilityTransformer activitySighting : activityHistory) {
if (activitySighting.getName().equals(oldName))
activitySighting.setName(newName);
}
}
private static class TracerStack extends Stack {
private TracerStack() {
}
}
}