diff options
author | Mike Ma <yanmin@google.com> | 2017-10-31 12:30:42 -0700 |
---|---|---|
committer | Mike Ma <yanmin@google.com> | 2017-11-01 14:20:31 -0700 |
commit | d2239828d608a936e8a92319ce9c2b415998acb8 (patch) | |
tree | 853e1ee7885b54411e32bdefb933655c2c16c661 /cmds/am | |
parent | f8a9169949c589755d300530f7b2390e687a9f8b (diff) |
Record proto to file in am instrument
Add an option -f to record instrumentdata proto produced by am instrument
to a file in addition to printing to stdout. Default path is
/sdcard/instrument-logs/log-yyyyMMdd-hhmmss-SSS.instrumentation_data_proto.
If the file exits, it will be deleted before writing. Path can be changed
via optional <FILE> argument after -f.
If -f and -m are both present, proto will be written to a file and print
to stdout.
Test: build, flash and run:
bit -bi FrameworksCoreTests
adb shell am instrument -w -r -f tmp/tmp.log \
-e class com.android.internal.os.BatteryStatsNoteTest \
com.android.frameworks.coretests/android.support.test.runner.AndroidJUnitRunner
Change-Id: Iabc320c066d5995eee842c26416623eeb3d403f4
Diffstat (limited to 'cmds/am')
-rw-r--r-- | cmds/am/proto/instrumentation_data.proto | 1 | ||||
-rw-r--r-- | cmds/am/src/com/android/commands/am/Am.java | 6 | ||||
-rw-r--r-- | cmds/am/src/com/android/commands/am/Instrument.java | 135 |
3 files changed, 98 insertions, 44 deletions
diff --git a/cmds/am/proto/instrumentation_data.proto b/cmds/am/proto/instrumentation_data.proto index 12a18a2a035f..8e29f9645568 100644 --- a/cmds/am/proto/instrumentation_data.proto +++ b/cmds/am/proto/instrumentation_data.proto @@ -28,6 +28,7 @@ message ResultsBundleEntry { optional double value_double = 5; optional sint64 value_long = 6; optional ResultsBundle value_bundle = 7; + optional bytes value_bytes = 8; } message ResultsBundle { diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 79e7fac11bb1..813335a688ab 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -160,7 +160,11 @@ public class Am extends BaseCommand { } else if (opt.equals("-r")) { instrument.rawMode = true; } else if (opt.equals("-m")) { - instrument.proto = true; + instrument.protoStd = true; + } else if (opt.equals("-f")) { + instrument.protoFile = true; + if (peekNextArg() != null && !peekNextArg().startsWith("-")) + instrument.logPath = nextArg(); } else if (opt.equals("-e")) { final String argKey = nextArgRequired(); final String argValue = nextArgRequired(); diff --git a/cmds/am/src/com/android/commands/am/Instrument.java b/cmds/am/src/com/android/commands/am/Instrument.java index b69ef1c2fca5..93b9f58d51d3 100644 --- a/cmds/am/src/com/android/commands/am/Instrument.java +++ b/cmds/am/src/com/android/commands/am/Instrument.java @@ -25,23 +25,32 @@ import android.content.pm.IPackageManager; import android.content.pm.InstrumentationInfo; import android.os.Build; import android.os.Bundle; +import android.os.Environment; import android.os.ServiceManager; import android.os.UserHandle; import android.util.AndroidException; import android.util.proto.ProtoOutputStream; import android.view.IWindowManager; +import java.io.File; +import java.io.FileOutputStream; import java.io.IOException; +import java.io.OutputStream; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Date; import java.util.List; +import java.util.Locale; /** * Runs the am instrument command */ public class Instrument { + public static final String DEFAULT_LOG_DIR = "instrument-logs"; + private final IActivityManager mAm; private final IPackageManager mPm; private final IWindowManager mWm; @@ -50,7 +59,9 @@ public class Instrument { public String profileFile = null; public boolean wait = false; public boolean rawMode = false; - public boolean proto = false; + boolean protoStd = false; // write proto to stdout + boolean protoFile = false; // write proto to a file + String logPath = null; public boolean noWindowAnimation = false; public String abi = null; public int userId = UserHandle.USER_CURRENT; @@ -178,18 +189,49 @@ public class Instrument { * Printer for the protobuf based status reporting. */ private class ProtoStatusReporter implements StatusReporter { + + private File mLog; + + ProtoStatusReporter() { + if (protoFile) { + if (logPath == null) { + File logDir = new File(Environment.getLegacyExternalStorageDirectory(), + DEFAULT_LOG_DIR); + if (!logDir.exists() && !logDir.mkdirs()) { + System.err.format("Unable to create log directory: %s\n", + logDir.getAbsolutePath()); + protoFile = false; + return; + } + SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd-hhmmss-SSS", Locale.US); + String fileName = String.format("log-%s.instrumentation_data_proto", + format.format(new Date())); + mLog = new File(logDir, fileName); + } else { + mLog = new File(Environment.getLegacyExternalStorageDirectory(), logPath); + File logDir = mLog.getParentFile(); + if (!logDir.exists() && !logDir.mkdirs()) { + System.err.format("Unable to create log directory: %s\n", + logDir.getAbsolutePath()); + protoFile = false; + return; + } + } + if (mLog.exists()) mLog.delete(); + } + } + @Override public void onInstrumentationStatusLocked(ComponentName name, int resultCode, Bundle results) { final ProtoOutputStream proto = new ProtoOutputStream(); - final long token = proto.startRepeatedObject(InstrumentationData.Session.TEST_STATUS); - - proto.writeSInt32(InstrumentationData.TestStatus.RESULT_CODE, resultCode); + final long token = proto.start(InstrumentationData.Session.TEST_STATUS); + proto.write(InstrumentationData.TestStatus.RESULT_CODE, resultCode); writeBundle(proto, InstrumentationData.TestStatus.RESULTS, results); + proto.end(token); - proto.endRepeatedObject(token); - writeProtoToStdout(proto); + outputProto(proto); } @Override @@ -197,80 +239,87 @@ public class Instrument { Bundle results) { final ProtoOutputStream proto = new ProtoOutputStream(); - final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS); - - proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE, + final long token = proto.start(InstrumentationData.Session.SESSION_STATUS); + proto.write(InstrumentationData.SessionStatus.STATUS_CODE, InstrumentationData.SESSION_FINISHED); - proto.writeSInt32(InstrumentationData.SessionStatus.RESULT_CODE, resultCode); + proto.write(InstrumentationData.SessionStatus.RESULT_CODE, resultCode); writeBundle(proto, InstrumentationData.SessionStatus.RESULTS, results); + proto.end(token); - proto.endObject(token); - writeProtoToStdout(proto); + outputProto(proto); } @Override public void onError(String errorText, boolean commandError) { final ProtoOutputStream proto = new ProtoOutputStream(); - final long token = proto.startObject(InstrumentationData.Session.SESSION_STATUS); - - proto.writeEnum(InstrumentationData.SessionStatus.STATUS_CODE, + final long token = proto.start(InstrumentationData.Session.SESSION_STATUS); + proto.write(InstrumentationData.SessionStatus.STATUS_CODE, InstrumentationData.SESSION_ABORTED); - proto.writeString(InstrumentationData.SessionStatus.ERROR_TEXT, errorText); + proto.write(InstrumentationData.SessionStatus.ERROR_TEXT, errorText); + proto.end(token); - proto.endObject(token); - writeProtoToStdout(proto); + outputProto(proto); } private void writeBundle(ProtoOutputStream proto, long fieldId, Bundle bundle) { - final long bundleToken = proto.startObject(fieldId); + final long bundleToken = proto.start(fieldId); for (final String key: sorted(bundle.keySet())) { final long entryToken = proto.startRepeatedObject( InstrumentationData.ResultsBundle.ENTRIES); - proto.writeString(InstrumentationData.ResultsBundleEntry.KEY, key); + proto.write(InstrumentationData.ResultsBundleEntry.KEY, key); final Object val = bundle.get(key); if (val instanceof String) { - proto.writeString(InstrumentationData.ResultsBundleEntry.VALUE_STRING, + proto.write(InstrumentationData.ResultsBundleEntry.VALUE_STRING, (String)val); } else if (val instanceof Byte) { - proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT, + proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT, ((Byte)val).intValue()); } else if (val instanceof Double) { - proto.writeDouble(InstrumentationData.ResultsBundleEntry.VALUE_DOUBLE, - ((Double)val).doubleValue()); + proto.write(InstrumentationData.ResultsBundleEntry.VALUE_DOUBLE, (double)val); } else if (val instanceof Float) { - proto.writeFloat(InstrumentationData.ResultsBundleEntry.VALUE_FLOAT, - ((Float)val).floatValue()); + proto.write(InstrumentationData.ResultsBundleEntry.VALUE_FLOAT, (float)val); } else if (val instanceof Integer) { - proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT, - ((Integer)val).intValue()); + proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT, (int)val); } else if (val instanceof Long) { - proto.writeSInt64(InstrumentationData.ResultsBundleEntry.VALUE_LONG, - ((Long)val).longValue()); + proto.write(InstrumentationData.ResultsBundleEntry.VALUE_LONG, (long)val); } else if (val instanceof Short) { - proto.writeSInt32(InstrumentationData.ResultsBundleEntry.VALUE_INT, - ((Short)val).intValue()); + proto.write(InstrumentationData.ResultsBundleEntry.VALUE_INT, (short)val); } else if (val instanceof Bundle) { writeBundle(proto, InstrumentationData.ResultsBundleEntry.VALUE_BUNDLE, (Bundle)val); + } else if (val instanceof byte[]) { + proto.write(InstrumentationData.ResultsBundleEntry.VALUE_BYTES, (byte[])val); } - proto.endRepeatedObject(entryToken); + proto.end(entryToken); } - proto.endObject(bundleToken); + proto.end(bundleToken); } - private void writeProtoToStdout(ProtoOutputStream proto) { - try { - System.out.write(proto.getBytes()); - System.out.flush(); - } catch (IOException ex) { - System.err.println("Error writing finished response: "); - ex.printStackTrace(System.err); + private void outputProto(ProtoOutputStream proto) { + byte[] out = proto.getBytes(); + if (protoStd) { + try { + System.out.write(out); + System.out.flush(); + } catch (IOException ex) { + System.err.println("Error writing finished response: "); + ex.printStackTrace(System.err); + } + } + if (protoFile) { + try (OutputStream os = new FileOutputStream(mLog, true)) { + os.write(proto.getBytes()); + os.flush(); + } catch (IOException ex) { + System.err.format("Cannot write to %s:\n", mLog.getAbsolutePath()); + ex.printStackTrace(); + } } } } @@ -374,7 +423,7 @@ public class Instrument { try { // Choose which output we will do. - if (proto) { + if (protoFile || protoStd) { reporter = new ProtoStatusReporter(); } else if (wait) { reporter = new TextStatusReporter(rawMode); @@ -396,7 +445,7 @@ public class Instrument { mWm.setAnimationScale(2, 0.0f); } - // Figure out which component we are tring to do. + // Figure out which component we are trying to do. final ComponentName cn = parseComponentName(componentNameArg); // Choose an ABI if necessary |