Add clock-type to choose how timestamps are provided when profiling
Add an option to am start-activity and am profile to be able to choose
the clock source for the timestamps. The clock source can be wall /
thread-cpu / dual. wall provides the wall clock timestamps which is
fast. thread-cpu provides the time the actual time thread was on cpu.
This is slow and should be only used when actually required. Dual
provides both these timestamps.
Bug: 259258187
Test: am start-activity --start-profiler --clock-type wall / thread-cpu
Change-Id: I719395d673c337f577ba5245814338e35d69c372
Merged-In: I23a01ff91e1a5c7e8a39339d06d0bfad8a6c4c37
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 1310b83..9d6aef6 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -922,6 +922,7 @@
int samplingInterval;
boolean autoStopProfiler;
boolean streamingOutput;
+ int mClockType;
boolean profiling;
boolean handlingProfiling;
public void setProfiler(ProfilerInfo profilerInfo) {
@@ -948,6 +949,7 @@
samplingInterval = profilerInfo.samplingInterval;
autoStopProfiler = profilerInfo.autoStopProfiler;
streamingOutput = profilerInfo.streamingOutput;
+ mClockType = profilerInfo.clockType;
}
public void startProfiling() {
if (profileFd == null || profiling) {
@@ -956,8 +958,8 @@
try {
int bufferSize = SystemProperties.getInt("debug.traceview-buffer-size-mb", 8);
VMDebug.startMethodTracing(profileFile, profileFd.getFileDescriptor(),
- bufferSize * 1024 * 1024, 0, samplingInterval != 0, samplingInterval,
- streamingOutput);
+ bufferSize * 1024 * 1024, mClockType, samplingInterval != 0,
+ samplingInterval, streamingOutput);
profiling = true;
} catch (RuntimeException e) {
Slog.w(TAG, "Profiling failed on path " + profileFile, e);
@@ -6479,6 +6481,7 @@
mProfiler.samplingInterval = data.initProfilerInfo.samplingInterval;
mProfiler.autoStopProfiler = data.initProfilerInfo.autoStopProfiler;
mProfiler.streamingOutput = data.initProfilerInfo.streamingOutput;
+ mProfiler.mClockType = data.initProfilerInfo.clockType;
if (data.initProfilerInfo.attachAgentDuringBind) {
agent = data.initProfilerInfo.agent;
}
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index 854406c..f7a3d78 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -33,6 +33,17 @@
*/
public class ProfilerInfo implements Parcelable {
+ // CLOCK_TYPE_DEFAULT chooses the default used by ART. ART uses CLOCK_TYPE_DUAL by default (see
+ // kDefaultTraceClockSource in art/runtime/runtime_globals.h).
+ public static final int CLOCK_TYPE_DEFAULT = 0x000;
+ // The values of these constants are chosen such that they correspond to the flags passed to
+ // VMDebug.startMethodTracing to choose the corresponding clock type (see
+ // core/java/android/app/ActivityThread.java).
+ // The flag values are defined in ART (see TraceFlag in art/runtime/trace.h).
+ public static final int CLOCK_TYPE_WALL = 0x010;
+ public static final int CLOCK_TYPE_THREAD_CPU = 0x100;
+ public static final int CLOCK_TYPE_DUAL = 0x110;
+
private static final String TAG = "ProfilerInfo";
/* Name of profile output file. */
@@ -66,13 +77,20 @@
*/
public final boolean attachAgentDuringBind;
+ /**
+ * Indicates the clock source to be used for profiling. The source could be wallclock, thread
+ * cpu or both
+ */
+ public final int clockType;
+
public ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop,
- boolean streaming, String agent, boolean attachAgentDuringBind) {
+ boolean streaming, String agent, boolean attachAgentDuringBind, int clockType) {
profileFile = filename;
profileFd = fd;
samplingInterval = interval;
autoStopProfiler = autoStop;
streamingOutput = streaming;
+ this.clockType = clockType;
this.agent = agent;
this.attachAgentDuringBind = attachAgentDuringBind;
}
@@ -85,6 +103,25 @@
streamingOutput = in.streamingOutput;
agent = in.agent;
attachAgentDuringBind = in.attachAgentDuringBind;
+ clockType = in.clockType;
+ }
+
+ /**
+ * Get the value for the clock type corresponding to the option string passed to the activity
+ * manager. am profile start / am start-activity start-profiler commands accept clock-type
+ * option to choose the source of timestamps when profiling. This function maps the option
+ * string to the value of flags that is used when calling VMDebug.startMethodTracing
+ */
+ public static int getClockTypeFromString(String type) {
+ if ("thread-cpu".equals(type)) {
+ return CLOCK_TYPE_THREAD_CPU;
+ } else if ("wall".equals(type)) {
+ return CLOCK_TYPE_WALL;
+ } else if ("dual".equals(type)) {
+ return CLOCK_TYPE_DUAL;
+ } else {
+ return CLOCK_TYPE_DEFAULT;
+ }
}
/**
@@ -93,7 +130,8 @@
*/
public ProfilerInfo setAgent(String agent, boolean attachAgentDuringBind) {
return new ProfilerInfo(this.profileFile, this.profileFd, this.samplingInterval,
- this.autoStopProfiler, this.streamingOutput, agent, attachAgentDuringBind);
+ this.autoStopProfiler, this.streamingOutput, agent, attachAgentDuringBind,
+ this.clockType);
}
/**
@@ -133,6 +171,7 @@
out.writeInt(streamingOutput ? 1 : 0);
out.writeString(agent);
out.writeBoolean(attachAgentDuringBind);
+ out.writeInt(clockType);
}
/** @hide */
@@ -146,6 +185,7 @@
proto.write(ProfilerInfoProto.AUTO_STOP_PROFILER, autoStopProfiler);
proto.write(ProfilerInfoProto.STREAMING_OUTPUT, streamingOutput);
proto.write(ProfilerInfoProto.AGENT, agent);
+ proto.write(ProfilerInfoProto.CLOCK_TYPE, clockType);
proto.end(token);
}
@@ -170,6 +210,7 @@
streamingOutput = in.readInt() != 0;
agent = in.readString();
attachAgentDuringBind = in.readBoolean();
+ clockType = in.readInt();
}
@Override
@@ -186,7 +227,8 @@
&& autoStopProfiler == other.autoStopProfiler
&& samplingInterval == other.samplingInterval
&& streamingOutput == other.streamingOutput
- && Objects.equals(agent, other.agent);
+ && Objects.equals(agent, other.agent)
+ && clockType == other.clockType;
}
@Override
@@ -197,6 +239,7 @@
result = 31 * result + (autoStopProfiler ? 1 : 0);
result = 31 * result + (streamingOutput ? 1 : 0);
result = 31 * result + Objects.hashCode(agent);
+ result = 31 * result + clockType;
return result;
}
}
diff --git a/core/proto/android/app/profilerinfo.proto b/core/proto/android/app/profilerinfo.proto
index d318533..86261ec 100644
--- a/core/proto/android/app/profilerinfo.proto
+++ b/core/proto/android/app/profilerinfo.proto
@@ -35,4 +35,5 @@
optional bool streaming_output = 5;
// Denotes an agent (and its parameters) to attach for profiling.
optional string agent = 6;
+ optional int32 clock_type = 7;
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 869c6a4..625ae58 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -90,7 +90,6 @@
import android.os.ShellCommand;
import android.os.StrictMode;
import android.os.SystemClock;
-import android.os.SystemProperties;
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
@@ -175,6 +174,7 @@
private boolean mStreaming; // Streaming the profiling output to a file.
private String mAgent; // Agent to attach on startup.
private boolean mAttachAgentDuringBind; // Whether agent should be attached late.
+ private int mClockType; // Whether we need thread cpu / wall clock / both.
private int mDisplayId;
private int mTaskDisplayAreaFeatureId;
private int mWindowingMode;
@@ -398,6 +398,9 @@
mAutoStop = false;
} else if (opt.equals("--sampling")) {
mSamplingInterval = Integer.parseInt(getNextArgRequired());
+ } else if (opt.equals("--clock-type")) {
+ String clock_type = getNextArgRequired();
+ mClockType = ProfilerInfo.getClockTypeFromString(clock_type);
} else if (opt.equals("--streaming")) {
mStreaming = true;
} else if (opt.equals("--attach-agent")) {
@@ -546,7 +549,7 @@
}
}
profilerInfo = new ProfilerInfo(mProfileFile, fd, mSamplingInterval, mAutoStop,
- mStreaming, mAgent, mAttachAgentDuringBind);
+ mStreaming, mAgent, mAttachAgentDuringBind, mClockType);
}
pw.println("Starting: " + intent);
@@ -876,24 +879,15 @@
return 0;
}
- static void removeWallOption() {
- String props = SystemProperties.get("dalvik.vm.extra-opts");
- if (props != null && props.contains("-Xprofile:wallclock")) {
- props = props.replace("-Xprofile:wallclock", "");
- props = props.trim();
- SystemProperties.set("dalvik.vm.extra-opts", props);
- }
- }
-
private int runProfile(PrintWriter pw) throws RemoteException {
final PrintWriter err = getErrPrintWriter();
String profileFile = null;
boolean start = false;
- boolean wall = false;
int userId = UserHandle.USER_CURRENT;
int profileType = 0;
mSamplingInterval = 0;
mStreaming = false;
+ mClockType = ProfilerInfo.CLOCK_TYPE_DEFAULT;
String process = null;
@@ -905,8 +899,9 @@
while ((opt=getNextOption()) != null) {
if (opt.equals("--user")) {
userId = UserHandle.parseUserArg(getNextArgRequired());
- } else if (opt.equals("--wall")) {
- wall = true;
+ } else if (opt.equals("--clock-type")) {
+ String clock_type = getNextArgRequired();
+ mClockType = ProfilerInfo.getClockTypeFromString(clock_type);
} else if (opt.equals("--streaming")) {
mStreaming = true;
} else if (opt.equals("--sampling")) {
@@ -954,29 +949,12 @@
return -1;
}
profilerInfo = new ProfilerInfo(profileFile, fd, mSamplingInterval, false, mStreaming,
- null, false);
+ null, false, mClockType);
}
- try {
- if (wall) {
- // XXX doesn't work -- this needs to be set before booting.
- String props = SystemProperties.get("dalvik.vm.extra-opts");
- if (props == null || !props.contains("-Xprofile:wallclock")) {
- props = props + " -Xprofile:wallclock";
- //SystemProperties.set("dalvik.vm.extra-opts", props);
- }
- } else if (start) {
- //removeWallOption();
- }
- if (!mInterface.profileControl(process, userId, start, profilerInfo, profileType)) {
- wall = false;
- err.println("PROFILE FAILED on process " + process);
- return -1;
- }
- } finally {
- if (!wall) {
- //removeWallOption();
- }
+ if (!mInterface.profileControl(process, userId, start, profilerInfo, profileType)) {
+ err.println("PROFILE FAILED on process " + process);
+ return -1;
}
return 0;
}
@@ -3449,8 +3427,9 @@
pw.println(" help");
pw.println(" Print this help text.");
pw.println(" start-activity [-D] [-N] [-W] [-P <FILE>] [--start-profiler <FILE>]");
- pw.println(" [--sampling INTERVAL] [--streaming] [-R COUNT] [-S]");
- pw.println(" [--track-allocation] [--user <USER_ID> | current] <INTENT>");
+ pw.println(" [--sampling INTERVAL] [--clock-type <TYPE>] [--streaming]");
+ pw.println(" [-R COUNT] [-S] [--track-allocation]");
+ pw.println(" [--user <USER_ID> | current] <INTENT>");
pw.println(" Start an Activity. Options are:");
pw.println(" -D: enable debugging");
pw.println(" -N: enable native debugging");
@@ -3458,6 +3437,9 @@
pw.println(" --start-profiler <FILE>: start profiler and send results to <FILE>");
pw.println(" --sampling INTERVAL: use sample profiling with INTERVAL microseconds");
pw.println(" between samples (use with --start-profiler)");
+ pw.println(" --clock-type <TYPE>: type can be wall / thread-cpu / dual. Specify");
+ pw.println(" the clock that is used to report the timestamps when profiling");
+ pw.println(" The default value is dual. (use with --start-profiler)");
pw.println(" --streaming: stream the profiling output to the specified file");
pw.println(" (use with --start-profiler)");
pw.println(" -P <FILE>: like above, but profiling stops when app goes idle");
@@ -3535,12 +3517,16 @@
pw.println(" stop: stop tracing IPC transactions and dump the results to file.");
pw.println(" --dump-file <FILE>: Specify the file the trace should be dumped to.");
pw.println(" profile start [--user <USER_ID> current]");
+ pw.println(" [--clock-type <TYPE>]");
pw.println(" [--sampling INTERVAL | --streaming] <PROCESS> <FILE>");
pw.println(" Start profiler on a process. The given <PROCESS> argument");
pw.println(" may be either a process name or pid. Options are:");
pw.println(" --user <USER_ID> | current: When supplying a process name,");
pw.println(" specify user of process to profile; uses current user if not");
pw.println(" specified.");
+ pw.println(" --clock-type <TYPE>: use the specified clock to report timestamps.");
+ pw.println(" The type can be one of wall | thread-cpu | dual. The default");
+ pw.println(" value is dual.");
pw.println(" --sampling INTERVAL: use sample profiling with INTERVAL microseconds");
pw.println(" between samples.");
pw.println(" --streaming: stream the profiling output to the specified file.");
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index e97654c..15efb7d 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -2043,7 +2043,7 @@
}
} else if (instr != null && instr.mProfileFile != null) {
profilerInfo = new ProfilerInfo(instr.mProfileFile, null, 0, false, false,
- null, false);
+ null, false, 0);
}
if (mAppAgentMap != null && mAppAgentMap.containsKey(processName)) {
// We need to do a debuggable check here. See setAgentApp for why the check is
@@ -2053,7 +2053,7 @@
// Do not overwrite already requested agent.
if (profilerInfo == null) {
profilerInfo = new ProfilerInfo(null, null, 0, false, false,
- mAppAgentMap.get(processName), true);
+ mAppAgentMap.get(processName), true, 0);
} else if (profilerInfo.agent == null) {
profilerInfo = profilerInfo.setAgent(mAppAgentMap.get(processName), true);
}
@@ -2185,7 +2185,9 @@
+ " mAutoStopProfiler="
+ mProfileData.getProfilerInfo().autoStopProfiler
+ " mStreamingOutput="
- + mProfileData.getProfilerInfo().streamingOutput);
+ + mProfileData.getProfilerInfo().streamingOutput
+ + " mClockType="
+ + mProfileData.getProfilerInfo().clockType);
pw.println(" mProfileType=" + mProfileType);
}
}