Merge "Extend am profile command to also collect lowoverhead traces." into main
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 5e69ec1..fb7030f 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -6859,21 +6859,44 @@
final void handleProfilerControl(boolean start, ProfilerInfo profilerInfo, int profileType) {
if (start) {
- try {
- switch (profileType) {
- default:
+ switch (profileType) {
+ case ProfilerInfo.PROFILE_TYPE_LOW_OVERHEAD:
+ if (!com.android.art.flags.Flags.alwaysEnableProfileCode()) {
+ Slog.w(TAG, "Low overhead tracing feature is not enabled");
+ break;
+ }
+ VMDebug.startLowOverheadTrace();
+ break;
+ default:
+ try {
mProfiler.setProfiler(profilerInfo);
mProfiler.startProfiling();
break;
- }
- } catch (RuntimeException e) {
- Slog.w(TAG, "Profiling failed on path " + profilerInfo.profileFile
- + " -- can the process access this path?");
- } finally {
- profilerInfo.closeFd();
+ } catch (RuntimeException e) {
+ Slog.w(TAG, "Profiling failed on path " + profilerInfo.profileFile
+ + " -- can the process access this path?");
+ } finally {
+ profilerInfo.closeFd();
+ }
}
} else {
switch (profileType) {
+ case ProfilerInfo.PROFILE_TYPE_LOW_OVERHEAD:
+ if (!com.android.art.flags.Flags.alwaysEnableProfileCode()) {
+ if (profilerInfo != null) {
+ profilerInfo.closeFd();
+ }
+ Slog.w(TAG, "Low overhead tracing feature is not enabled");
+ break;
+ }
+ if (profilerInfo != null) {
+ FileDescriptor fd = profilerInfo.profileFd.getFileDescriptor();
+ VMDebug.TraceDestination dst =
+ VMDebug.TraceDestination.fromFileDescriptor(fd);
+ VMDebug.dumpLowOverheadTrace(dst);
+ }
+ VMDebug.stopLowOverheadTrace();
+ break;
default:
mProfiler.stopProfiling();
break;
diff --git a/core/java/android/app/ProfilerInfo.java b/core/java/android/app/ProfilerInfo.java
index bcae22a..0348b6d 100644
--- a/core/java/android/app/ProfilerInfo.java
+++ b/core/java/android/app/ProfilerInfo.java
@@ -32,6 +32,12 @@
* {@hide}
*/
public class ProfilerInfo implements Parcelable {
+ // Regular profiling which provides different modes of profiling at some performance cost.
+ public static final int PROFILE_TYPE_REGULAR = 0;
+
+ // Low overhead profiling that captures a simple sliding window of past events.
+ public static final int PROFILE_TYPE_LOW_OVERHEAD = 1;
+
// Version of the profiler output
public static final int OUTPUT_VERSION_DEFAULT = 1;
// CLOCK_TYPE_DEFAULT chooses the default used by ART. ART uses CLOCK_TYPE_DUAL by default (see
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 4a4a1b4..0152f94 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -7609,7 +7609,7 @@
}
void setProfileApp(ApplicationInfo app, String processName, ProfilerInfo profilerInfo,
- ApplicationInfo sdkSandboxClientApp) {
+ ApplicationInfo sdkSandboxClientApp, int profileType) {
synchronized (mAppProfiler.mProfilerLock) {
if (!Build.IS_DEBUGGABLE) {
boolean isAppDebuggable = (app.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
@@ -7625,7 +7625,7 @@
+ "and not profileable by shell: " + app.packageName);
}
}
- mAppProfiler.setProfileAppLPf(processName, profilerInfo);
+ mAppProfiler.setProfileAppLPf(processName, profilerInfo, profileType);
}
}
@@ -17883,7 +17883,8 @@
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
- if (start && (profilerInfo == null || profilerInfo.profileFd == null)) {
+ if (start && profileType == ProfilerInfo.PROFILE_TYPE_REGULAR
+ && (profilerInfo == null || profilerInfo.profileFd == null)) {
throw new IllegalArgumentException("null profile info or fd");
}
@@ -19499,7 +19500,9 @@
}
if (profilerInfo != null) {
- setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo, null);
+ // We only support normal method tracing along with app startup for now.
+ setProfileApp(aInfo.applicationInfo, aInfo.processName, profilerInfo,
+ null, /*profileType= */ ProfilerInfo.PROFILE_TYPE_REGULAR);
}
wmLock.notify();
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 955437b..1853973 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -1081,7 +1081,7 @@
String profileFile = null;
boolean start = false;
int userId = UserHandle.USER_CURRENT;
- int profileType = 0;
+ int profileType = ProfilerInfo.PROFILE_TYPE_REGULAR;
mSamplingInterval = 0;
mStreaming = false;
mClockType = ProfilerInfo.CLOCK_TYPE_DEFAULT;
@@ -1123,6 +1123,18 @@
}
}
process = getNextArgRequired();
+ } else if ("lowoverhead".equals(cmd)) {
+ // This is an experimental low overhead profiling.
+ profileType = ProfilerInfo.PROFILE_TYPE_LOW_OVERHEAD;
+ cmd = getNextArgRequired();
+ if ("start".equals(cmd)) {
+ start = true;
+ } else if ("stop".equals(cmd)) {
+ start = false;
+ } else {
+ throw new IllegalArgumentException("Profile command not valid");
+ }
+ process = getNextArgRequired();
} else {
// Compatibility with old syntax: process is specified first.
process = cmd;
@@ -1142,7 +1154,12 @@
ParcelFileDescriptor fd = null;
ProfilerInfo profilerInfo = null;
- if (start) {
+ // For regular method tracing profileFile should be provided with the start command. For
+ // low overhead method tracing the profileFile is optional and provided with the stop
+ // command.
+ if ((start && profileType == ProfilerInfo.PROFILE_TYPE_REGULAR)
+ || (profileType == ProfilerInfo.PROFILE_TYPE_LOW_OVERHEAD
+ && !start && getRemainingArgsCount() > 0)) {
profileFile = getNextArgRequired();
fd = openFileForSystem(profileFile, "w");
if (fd == null) {
diff --git a/services/core/java/com/android/server/am/AppProfiler.java b/services/core/java/com/android/server/am/AppProfiler.java
index dda48ad..79a0d73 100644
--- a/services/core/java/com/android/server/am/AppProfiler.java
+++ b/services/core/java/com/android/server/am/AppProfiler.java
@@ -1990,7 +1990,7 @@
}
@GuardedBy("mProfilerLock")
- private void stopProfilerLPf(ProcessRecord proc, int profileType) {
+ private void stopProfilerLPf(ProcessRecord proc, ProfilerInfo profilerInfo, int profileType) {
if (proc == null || proc == mProfileData.getProfileProc()) {
proc = mProfileData.getProfileProc();
profileType = mProfileType;
@@ -2004,7 +2004,7 @@
return;
}
try {
- thread.profilerControl(false, null, profileType);
+ thread.profilerControl(false, profilerInfo, profileType);
} catch (RemoteException e) {
throw new IllegalStateException("Process disappeared");
}
@@ -2039,41 +2039,58 @@
ProfilerInfo profilerInfo, int profileType) {
try {
if (start) {
- stopProfilerLPf(null, 0);
+ boolean needsFile = (profileType == ProfilerInfo.PROFILE_TYPE_REGULAR);
+ stopProfilerLPf(null, null, 0);
mService.setProfileApp(proc.info, proc.processName, profilerInfo,
- proc.isSdkSandbox ? proc.getClientInfoForSdkSandbox() : null);
+ proc.isSdkSandbox ? proc.getClientInfoForSdkSandbox() : null, profileType);
mProfileData.setProfileProc(proc);
mProfileType = profileType;
- ParcelFileDescriptor fd = profilerInfo.profileFd;
- try {
- fd = fd.dup();
- } catch (IOException e) {
- fd = null;
- }
- profilerInfo.profileFd = fd;
- proc.mProfile.getThread().profilerControl(start, profilerInfo, profileType);
- fd = null;
- try {
- mProfileData.getProfilerInfo().profileFd.close();
- } catch (IOException e) {
- }
- mProfileData.getProfilerInfo().profileFd = null;
- if (proc.getPid() == mService.MY_PID) {
- // When profiling the system server itself, avoid closing the file
- // descriptor, as profilerControl will not create a copy.
- // Note: it is also not correct to just set profileFd to null, as the
- // whole ProfilerInfo instance is passed down!
- profilerInfo = null;
- }
- } else {
- stopProfilerLPf(proc, profileType);
- if (profilerInfo != null && profilerInfo.profileFd != null) {
+ ParcelFileDescriptor fd = null;
+ if (needsFile) {
+ fd = profilerInfo.profileFd;
try {
- profilerInfo.profileFd.close();
+ fd = fd.dup();
+ } catch (IOException e) {
+ fd = null;
+ }
+ profilerInfo.profileFd = fd;
+ }
+
+ proc.mProfile.getThread().profilerControl(start, profilerInfo, profileType);
+
+ if (needsFile) {
+ fd = null;
+ try {
+ mProfileData.getProfilerInfo().profileFd.close();
} catch (IOException e) {
}
+ mProfileData.getProfilerInfo().profileFd = null;
+
+ if (proc.getPid() == mService.MY_PID) {
+ // When profiling the system server itself, avoid closing the file
+ // descriptor, as profilerControl will not create a copy.
+ // Note: it is also not correct to just set profileFd to null, as the
+ // whole ProfilerInfo instance is passed down!
+ profilerInfo = null;
+ }
}
+ } else {
+ boolean mayNeedFile = (profileType == ProfilerInfo.PROFILE_TYPE_LOW_OVERHEAD);
+ if (profilerInfo != null && profilerInfo.profileFd != null) {
+ ParcelFileDescriptor fd = profilerInfo.profileFd;
+ try {
+ if (mayNeedFile) {
+ fd = fd.dup();
+ } else {
+ fd.close();
+ }
+ } catch (IOException e) {
+ fd = null;
+ }
+ profilerInfo.profileFd = fd;
+ }
+ stopProfilerLPf(proc, profilerInfo, profileType);
}
return true;
@@ -2090,7 +2107,7 @@
}
@GuardedBy("mProfilerLock")
- void setProfileAppLPf(String processName, ProfilerInfo profilerInfo) {
+ void setProfileAppLPf(String processName, ProfilerInfo profilerInfo, int profileType) {
mProfileData.setProfileApp(processName);
if (mProfileData.getProfilerInfo() != null) {
@@ -2101,8 +2118,10 @@
}
}
}
- mProfileData.setProfilerInfo(new ProfilerInfo(profilerInfo));
- mProfileType = 0;
+ if (profilerInfo != null) {
+ mProfileData.setProfilerInfo(new ProfilerInfo(profilerInfo));
+ }
+ mProfileType = profileType;
}
@GuardedBy("mProfilerLock")