blob: 5e0f6b31f138b7c86ef5411385ebf3ee51ab6aca [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.base.test;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.chromium.base.Log;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
/**
* TestTraceEvent is a modified version of TraceEvent, intended for tracing test runs.
*/
public class TestTraceEvent {
private static final String TAG = "TestTraceEvent";
/** The event types understood by the trace scripts. */
private enum EventType {
BEGIN("B"),
END("E"),
INSTANT("I");
private final String mTypeStr;
EventType(String typeStr) {
mTypeStr = typeStr;
}
@Override
public String toString() {
return mTypeStr;
}
}
// Locks internal fields.
private static final Object sLock = new Object();
private static File sOutputFile;
private static boolean sEnabled;
// A list of trace event strings.
private static JSONArray sTraceStrings;
/**
* Enable tracing, and set a specific output file. If tracing was previously enabled and
* disabled, that data is cleared.
*
* @param file Which file to append the trace data to.
*/
public static void enable(File outputFile) {
synchronized (sLock) {
if (sEnabled) return;
sEnabled = true;
sOutputFile = outputFile;
sTraceStrings = new JSONArray();
}
}
/**
* Disabling of tracing will dump trace data to the system log.
*/
public static void disable() {
synchronized (sLock) {
if (!sEnabled) return;
sEnabled = false;
dumpTraceOutput();
sTraceStrings = null;
}
}
/**
* @return True if tracing is enabled, false otherwise.
*/
public static boolean isEnabled() {
synchronized (sLock) {
return sEnabled;
}
}
/**
* Record an "instant" trace event. E.g. "screen update happened".
*/
public static void instant(String name) {
synchronized (sLock) {
if (!sEnabled) return;
saveTraceString(name, name.hashCode(), EventType.INSTANT);
}
}
/**
* Record an "begin" trace event. Begin trace events should have a matching end event (recorded
* by calling {@link #end(String)}).
*/
public static void begin(String name) {
synchronized (sLock) {
if (!sEnabled) return;
saveTraceString(name, name.hashCode(), EventType.BEGIN);
}
}
/**
* Record an "end" trace event, to match a begin event (recorded by calling {@link
* #begin(String)}). The time delta between begin and end is usually interesting to graph code.
*/
public static void end(String name) {
synchronized (sLock) {
if (!sEnabled) return;
saveTraceString(name, name.hashCode(), EventType.END);
}
}
/**
* Save a trace event as a JSON dict.
*
* @param name The trace data.
* @param id An identifier for the event, to be saved as the thread ID.
* @param type the type of trace event (B, E, I).
*/
private static void saveTraceString(String name, long id, EventType type) {
// We use System.currentTimeMillis() because it agrees with the value of
// the $EPOCHREALTIME environment variable. The Python test runner code
// uses that variable to synchronize timing.
long timeMicroseconds = System.currentTimeMillis() * 1000;
try {
JSONObject traceObj = new JSONObject();
traceObj.put("cat", "Java");
traceObj.put("ts", timeMicroseconds);
traceObj.put("ph", type);
traceObj.put("name", name);
traceObj.put("tid", id);
sTraceStrings.put(traceObj);
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
/**
* Dump all tracing data we have saved up to the log.
* Output as JSON for parsing convenience.
*/
private static void dumpTraceOutput() {
try {
PrintStream stream = new PrintStream(new FileOutputStream(sOutputFile, true));
try {
stream.print(sTraceStrings);
} finally {
if (stream != null) stream.close();
}
} catch (FileNotFoundException ex) {
Log.e(TAG, "Unable to dump trace data to output file.");
}
}
}