Fix bug 1829561 ("am profile" with bad filename kills process).

The am command is now the one that takes care of opening the target file,
handling the opened file descriptor to the process that will be profiled.
This allows you to send profile data to anywhere the shell can access, and
avoids any problems coming up from the target process trying to open the
file.
This commit is contained in:
Dianne Hackborn
2009-06-23 19:22:52 -07:00
parent 0bc7b8490b
commit 9c8dd55a9d
8 changed files with 132 additions and 55 deletions

View File

@ -26,10 +26,13 @@ import android.content.ComponentName;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.view.IWindowManager;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Iterator;
import java.util.Set;
@ -446,6 +449,8 @@ public class Am {
return;
}
ParcelFileDescriptor fd = null;
String cmd = nextArg();
if ("start".equals(cmd)) {
start = true;
@ -455,6 +460,16 @@ public class Am {
showUsage();
return;
}
try {
fd = ParcelFileDescriptor.open(
new File(profileFile),
ParcelFileDescriptor.MODE_CREATE |
ParcelFileDescriptor.MODE_TRUNCATE |
ParcelFileDescriptor.MODE_READ_WRITE);
} catch (FileNotFoundException e) {
System.err.println("Error: Unable to open file: " + profileFile);
return;
}
} else if (!"stop".equals(cmd)) {
System.err.println("Error: Profile command " + cmd + " not valid");
showUsage();
@ -462,8 +477,8 @@ public class Am {
}
try {
if (!mAm.profileControl(process, start, profileFile)) {
System.out.println("PROFILE FAILED on process " + process);
if (!mAm.profileControl(process, start, profileFile, fd)) {
System.err.println("PROFILE FAILED on process " + process);
return;
}
} catch (IllegalArgumentException e) {

View File

@ -986,7 +986,9 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
String process = data.readString();
boolean start = data.readInt() != 0;
String path = data.readString();
boolean res = profileControl(process, start, path);
ParcelFileDescriptor fd = data.readInt() != 0
? data.readFileDescriptor() : null;
boolean res = profileControl(process, start, path, fd);
reply.writeNoException();
reply.writeInt(res ? 1 : 0);
return true;
@ -2232,7 +2234,7 @@ class ActivityManagerProxy implements IActivityManager
}
public boolean profileControl(String process, boolean start,
String path) throws RemoteException
String path, ParcelFileDescriptor fd) throws RemoteException
{
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
@ -2240,6 +2242,12 @@ class ActivityManagerProxy implements IActivityManager
data.writeString(process);
data.writeInt(start ? 1 : 0);
data.writeString(path);
if (fd != null) {
data.writeInt(1);
fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
mRemote.transact(PROFILE_CONTROL_TRANSACTION, data, reply, 0);
reply.readException();
boolean res = reply.readInt() != 0;

View File

@ -48,6 +48,7 @@ import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.ServiceManager;
@ -74,6 +75,7 @@ import org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@ -1236,6 +1238,11 @@ public final class ActivityThread {
String who;
}
private static final class ProfilerControlData {
String path;
ParcelFileDescriptor fd;
}
private final class ApplicationThread extends ApplicationThreadNative {
private static final String HEAP_COLUMN = "%17s %8s %8s %8s %8s";
private static final String ONE_COUNT_COLUMN = "%17s %8d";
@ -1494,8 +1501,11 @@ public final class ActivityThread {
}
}
public void profilerControl(boolean start, String path) {
queueOrSendMessage(H.PROFILER_CONTROL, path, start ? 1 : 0);
public void profilerControl(boolean start, String path, ParcelFileDescriptor fd) {
ProfilerControlData pcd = new ProfilerControlData();
pcd.path = path;
pcd.fd = fd;
queueOrSendMessage(H.PROFILER_CONTROL, pcd, start ? 1 : 0);
}
public void setSchedulingGroup(int group) {
@ -1838,7 +1848,7 @@ public final class ActivityThread {
handleActivityConfigurationChanged((IBinder)msg.obj);
break;
case PROFILER_CONTROL:
handleProfilerControl(msg.arg1 != 0, (String)msg.obj);
handleProfilerControl(msg.arg1 != 0, (ProfilerControlData)msg.obj);
break;
case CREATE_BACKUP_AGENT:
handleCreateBackupAgent((CreateBackupAgentData)msg.obj);
@ -3618,15 +3628,20 @@ public final class ActivityThread {
performConfigurationChanged(r.activity, mConfiguration);
}
final void handleProfilerControl(boolean start, String path) {
final void handleProfilerControl(boolean start, ProfilerControlData pcd) {
if (start) {
File file = new File(path);
file.getParentFile().mkdirs();
try {
Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024);
Debug.startMethodTracing(pcd.path, pcd.fd.getFileDescriptor(),
8 * 1024 * 1024, 0);
} catch (RuntimeException e) {
Log.w(TAG, "Profiling failed on path " + path
Log.w(TAG, "Profiling failed on path " + pcd.path
+ " -- can the process access this path?");
} finally {
try {
pcd.fd.close();
} catch (IOException e) {
Log.w(TAG, "Failure closing profile fd", e);
}
}
} else {
Debug.stopMethodTracing();

View File

@ -26,6 +26,7 @@ import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.os.Binder;
import android.os.Bundle;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.Parcel;
@ -331,7 +332,9 @@ public abstract class ApplicationThreadNative extends Binder
data.enforceInterface(IApplicationThread.descriptor);
boolean start = data.readInt() != 0;
String path = data.readString();
profilerControl(start, path);
ParcelFileDescriptor fd = data.readInt() != 0
? data.readFileDescriptor() : null;
profilerControl(start, path, fd);
return true;
}
@ -711,11 +714,18 @@ class ApplicationThreadProxy implements IApplicationThread {
data.recycle();
}
public void profilerControl(boolean start, String path) throws RemoteException {
public void profilerControl(boolean start, String path,
ParcelFileDescriptor fd) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor);
data.writeInt(start ? 1 : 0);
data.writeString(path);
if (fd != null) {
data.writeInt(1);
fd.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
data.writeInt(0);
}
mRemote.transact(PROFILER_CONTROL_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY);
data.recycle();

View File

@ -250,7 +250,7 @@ public interface IActivityManager extends IInterface {
// Turn on/off profiling in a particular process.
public boolean profileControl(String process, boolean start,
String path) throws RemoteException;
String path, ParcelFileDescriptor fd) throws RemoteException;
public boolean shutdown(int timeout) throws RemoteException;

View File

@ -25,6 +25,7 @@ import android.content.pm.ProviderInfo;
import android.content.pm.ServiceInfo;
import android.content.res.Configuration;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.IBinder;
import android.os.IInterface;
@ -92,7 +93,8 @@ public interface IApplicationThread extends IInterface {
void scheduleLowMemory() throws RemoteException;
void scheduleActivityConfigurationChanged(IBinder token) throws RemoteException;
void requestPss() throws RemoteException;
void profilerControl(boolean start, String path) throws RemoteException;
void profilerControl(boolean start, String path, ParcelFileDescriptor fd)
throws RemoteException;
void setSchedulingGroup(int group) throws RemoteException;
String descriptor = "android.app.IApplicationThread";

View File

@ -21,6 +21,7 @@ import com.android.internal.util.TypedProperties;
import android.util.Config;
import android.util.Log;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
@ -377,6 +378,20 @@ href="{@docRoot}guide/developing/tools/traceview.html">Traceview: A Graphical Lo
VMDebug.startMethodTracing(pathName, bufferSize, flags);
}
/**
* Like startMethodTracing(String, int, int), but taking an already-opened
* FileDescriptor in which the trace is written. The file name is also
* supplied simply for logging. Makes a dup of the file descriptor.
*
* Not exposed in the SDK unless we are really comfortable with supporting
* this and find it would be useful.
* @hide
*/
public static void startMethodTracing(String traceName, FileDescriptor fd,
int bufferSize, int flags) {
VMDebug.startMethodTracing(traceName, fd, bufferSize, flags);
}
/**
* Determine whether method tracing is currently active.
* @hide

View File

@ -12601,51 +12601,63 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
}
public boolean profileControl(String process, boolean start,
String path) throws RemoteException {
String path, ParcelFileDescriptor fd) throws RemoteException {
synchronized (this) {
// note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
// its own permission.
if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
ProcessRecord proc = null;
try {
int pid = Integer.parseInt(process);
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pid);
try {
synchronized (this) {
// note: hijacking SET_ACTIVITY_WATCHER, but should be changed to
// its own permission.
if (checkCallingPermission(android.Manifest.permission.SET_ACTIVITY_WATCHER)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires permission "
+ android.Manifest.permission.SET_ACTIVITY_WATCHER);
}
} catch (NumberFormatException e) {
}
if (proc == null) {
HashMap<String, SparseArray<ProcessRecord>> all
= mProcessNames.getMap();
SparseArray<ProcessRecord> procs = all.get(process);
if (procs != null && procs.size() > 0) {
proc = procs.valueAt(0);
if (start && fd == null) {
throw new IllegalArgumentException("null fd");
}
}
if (proc == null || proc.thread == null) {
throw new IllegalArgumentException("Unknown process: " + process);
}
boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0"));
if (isSecure) {
if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
throw new SecurityException("Process not debuggable: " + proc);
ProcessRecord proc = null;
try {
int pid = Integer.parseInt(process);
synchronized (mPidsSelfLocked) {
proc = mPidsSelfLocked.get(pid);
}
} catch (NumberFormatException e) {
}
if (proc == null) {
HashMap<String, SparseArray<ProcessRecord>> all
= mProcessNames.getMap();
SparseArray<ProcessRecord> procs = all.get(process);
if (procs != null && procs.size() > 0) {
proc = procs.valueAt(0);
}
}
if (proc == null || proc.thread == null) {
throw new IllegalArgumentException("Unknown process: " + process);
}
boolean isSecure = "1".equals(SystemProperties.get(SYSTEM_SECURE, "0"));
if (isSecure) {
if ((proc.info.flags&ApplicationInfo.FLAG_DEBUGGABLE) == 0) {
throw new SecurityException("Process not debuggable: " + proc);
}
}
}
try {
proc.thread.profilerControl(start, path);
proc.thread.profilerControl(start, path, fd);
fd = null;
return true;
} catch (RemoteException e) {
throw new IllegalStateException("Process disappeared");
}
} catch (RemoteException e) {
throw new IllegalStateException("Process disappeared");
} finally {
if (fd != null) {
try {
fd.close();
} catch (IOException e) {
}
}
}
}