зеркало из https://github.com/mozilla/gecko-dev.git
Bug 826053 - Add main ANRReporter logic; r=blassey
This commit is contained in:
Родитель
73c2a74c31
Коммит
7dd6a24fee
|
@ -260,6 +260,188 @@ public final class ANRReporter extends BroadcastReceiver
|
|||
return 0L;
|
||||
}
|
||||
|
||||
/*
|
||||
a saved telemetry ping file consists of JSON in the following format,
|
||||
{
|
||||
"slug": "<uuid-string>",
|
||||
"payload": "<escaped-json-data-string>",
|
||||
"checksum": "<base64-sha-256-string>"
|
||||
}
|
||||
for Android ANR, our unescaped JSON payload should look like,
|
||||
{
|
||||
"ver": 1,
|
||||
"simpleMeasurements": {
|
||||
"uptime": <uptime>
|
||||
},
|
||||
"info": {
|
||||
"reason": "android-anr-report",
|
||||
"OS": "Android",
|
||||
...
|
||||
},
|
||||
"androidANR": "...",
|
||||
"androidLogcat": "..."
|
||||
}
|
||||
*/
|
||||
|
||||
private static int writePingPayload(OutputStream ping,
|
||||
MessageDigest checksum,
|
||||
String payload) throws IOException {
|
||||
|
||||
byte [] data = payload.getBytes(PING_CHARSET);
|
||||
checksum.update(data);
|
||||
|
||||
data = JSONObject.quote(payload).getBytes(PING_CHARSET);
|
||||
// first and last bytes are quotes inserted by JSONObject.quote; discard them
|
||||
ping.write(data, 1, data.length - 2);
|
||||
return data.length - 2;
|
||||
}
|
||||
|
||||
private static void fillPingHeader(OutputStream ping, MessageDigest checksum, String slug)
|
||||
throws IOException {
|
||||
|
||||
// ping file header
|
||||
byte [] data = ("{" +
|
||||
"\"slug\":" + JSONObject.quote(slug) + "," +
|
||||
"\"payload\":\"").getBytes(PING_CHARSET);
|
||||
ping.write(data);
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "wrote ping header, size = " + String.valueOf(data.length));
|
||||
}
|
||||
|
||||
// payload start
|
||||
int size = writePingPayload(ping, checksum, ("{" +
|
||||
"\"ver\":1," +
|
||||
"\"simpleMeasurements\":{" +
|
||||
"\"uptime\":" + String.valueOf(getUptimeMins()) +
|
||||
"}," +
|
||||
"\"info\":{" +
|
||||
"\"reason\":\"android-anr-report\"," +
|
||||
"\"OS\":" + JSONObject.quote(GeckoAppInfo.getOS()) + "," +
|
||||
"\"version\":\"" + String.valueOf(Build.VERSION.SDK_INT) + "\"," +
|
||||
"\"appID\":" + JSONObject.quote(GeckoAppInfo.getID()) + "," +
|
||||
"\"appVersion\":" + JSONObject.quote(GeckoAppInfo.getVersion()) + "," +
|
||||
"\"appName\":" + JSONObject.quote(GeckoAppInfo.getName()) + "," +
|
||||
"\"appBuildID\":" + JSONObject.quote(GeckoAppInfo.getBuildID()) + "," +
|
||||
"\"appUpdateChannel\":" + JSONObject.quote(GeckoAppInfo.getUpdateChannel()) + "," +
|
||||
"\"platformBuildID\":" + JSONObject.quote(GeckoAppInfo.getPlatformBuildID()) + "," +
|
||||
"\"locale\":" + JSONObject.quote(GeckoAppInfo.getLocale()) + "," +
|
||||
"\"cpucount\":" + String.valueOf(Runtime.getRuntime().availableProcessors()) + "," +
|
||||
"\"memsize\":" + String.valueOf(getTotalMem()) + "," +
|
||||
"\"arch\":" + JSONObject.quote(System.getProperty("os.arch")) + "," +
|
||||
"\"kernel_version\":" + JSONObject.quote(System.getProperty("os.version")) + "," +
|
||||
"\"device\":" + JSONObject.quote(Build.MODEL) + "," +
|
||||
"\"manufacturer\":" + JSONObject.quote(Build.MANUFACTURER) + "," +
|
||||
"\"hardware\":" + JSONObject.quote(Build.VERSION.SDK_INT < 8 ? "" :
|
||||
Build.HARDWARE) +
|
||||
"}," +
|
||||
"\"androidANR\":\""));
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "wrote metadata, size = " + String.valueOf(size));
|
||||
}
|
||||
|
||||
// We are at the start of ANR data
|
||||
}
|
||||
|
||||
private static int fillPingBlock(OutputStream ping, MessageDigest checksum, Reader reader)
|
||||
throws IOException {
|
||||
|
||||
int total = 0;
|
||||
char [] block = new char[TRACES_BLOCK_SIZE];
|
||||
for (int size = reader.read(block); size >= 0; size = reader.read(block)) {
|
||||
String quoted = JSONObject.quote(new String(block, 0, size));
|
||||
total += writePingPayload(ping, checksum, quoted.substring(1, quoted.length() - 1));
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
private static void fillPingFooter(OutputStream ping, MessageDigest checksum)
|
||||
throws IOException {
|
||||
|
||||
// We are at the end of ANR data
|
||||
|
||||
int total = writePingPayload(ping, checksum, ("\"," +
|
||||
"\"androidLogcat\":\""));
|
||||
|
||||
try {
|
||||
// get the last 200 lines of logcat
|
||||
Process proc = (new ProcessBuilder())
|
||||
.command("/system/bin/logcat", "-v", "threadtime", "-t", "200", "-d", "*:D")
|
||||
.redirectErrorStream(true)
|
||||
.start();
|
||||
try {
|
||||
Reader procOut = new InputStreamReader(proc.getInputStream(), TRACES_CHARSET);
|
||||
int size = fillPingBlock(ping, checksum, procOut);
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "wrote logcat, size = " + String.valueOf(size));
|
||||
}
|
||||
} finally {
|
||||
proc.destroy();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// ignore because logcat is not essential
|
||||
Log.w(LOGTAG, e);
|
||||
}
|
||||
|
||||
total += writePingPayload(ping, checksum, "\"}");
|
||||
|
||||
String base64Checksum = Base64.encodeToString(checksum.digest(), Base64.NO_WRAP);
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "checksum: " + base64Checksum);
|
||||
}
|
||||
byte [] data = (
|
||||
"\"," +
|
||||
"\"checksum\":" + JSONObject.quote(base64Checksum) +
|
||||
"}").getBytes(PING_CHARSET);
|
||||
ping.write(data);
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "wrote ping footer, size = " + String.valueOf(data.length + total));
|
||||
}
|
||||
}
|
||||
|
||||
private static void processTraces(Reader traces, File pingFile) {
|
||||
try {
|
||||
OutputStream ping = new BufferedOutputStream(
|
||||
new FileOutputStream(pingFile), TRACES_BLOCK_SIZE);
|
||||
try {
|
||||
MessageDigest checksum = MessageDigest.getInstance("SHA-256");
|
||||
fillPingHeader(ping, checksum, pingFile.getName());
|
||||
int size = fillPingBlock(ping, checksum, traces);
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "wrote traces, size = " + String.valueOf(size));
|
||||
}
|
||||
fillPingFooter(ping, checksum);
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "finished creating ping file");
|
||||
}
|
||||
return;
|
||||
} finally {
|
||||
ping.close();
|
||||
}
|
||||
} catch (GeneralSecurityException e) {
|
||||
Log.w(LOGTAG, e);
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, e);
|
||||
}
|
||||
// exception; delete ping file
|
||||
if (pingFile.exists()) {
|
||||
pingFile.delete();
|
||||
}
|
||||
}
|
||||
|
||||
private static void processTraces(File tracesFile, File pingFile) {
|
||||
try {
|
||||
Reader traces = new InputStreamReader(
|
||||
new FileInputStream(tracesFile), TRACES_CHARSET);
|
||||
try {
|
||||
processTraces(traces, pingFile);
|
||||
} finally {
|
||||
traces.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Log.w(LOGTAG, e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (DEBUG) {
|
||||
|
@ -268,6 +450,32 @@ public final class ANRReporter extends BroadcastReceiver
|
|||
if (!ANR_ACTION.equals(intent.getAction())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// make sure we have a good save location first
|
||||
File pingFile = getPingFile();
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "using ping file: " + String.valueOf(pingFile));
|
||||
}
|
||||
if (pingFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
File tracesFile = getTracesFile();
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "using traces file: " + String.valueOf(tracesFile));
|
||||
}
|
||||
if (tracesFile == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We get ANR intents from all ANRs in the system, but we only want Gecko ANRs
|
||||
if (!isGeckoTraces(context.getPackageName(), tracesFile)) {
|
||||
if (DEBUG) {
|
||||
Log.d(LOGTAG, "traces is not Gecko ANR");
|
||||
}
|
||||
return;
|
||||
}
|
||||
Log.i(LOGTAG, "processing Gecko ANR");
|
||||
processTraces(tracesFile, pingFile);
|
||||
}
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче