跳转至

am set-debug-app 介绍

一、源码分析

代码基于android11。am命令的实现见debug:am、cmd命令。书接上文,

1.1、命令行参数设置

ActivityManagerShellCommand#onCommand

frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand.java

 176     @Override
 177     public int onCommand(String cmd) {      
 183             switch (cmd) {
 184                 case "start":
 185                 case "start-activity":
 186                     return runStartActivity(pw);
......
 209                 case "set-debug-app":                                                   
 210                     return runSetDebugApp(pw);
 213                 case "clear-debug-app":
 214                     return runClearDebugApp(pw);

209行设置debug,对应的214行去掉debug

ActivityManagerShellCommand#runSetDebugApp

frameworks/base/services/core/java/com/android/server/am/ActivityManagerShellCommand.java

 980     int runSetDebugApp(PrintWriter pw) throws RemoteException {
 981         boolean wait = false;
 982         boolean persistent = false;
 983 
 984         String opt;
 985         while ((opt=getNextOption()) != null) {
 986             if (opt.equals("-w")) {
 987                 wait = true;
 988             } else if (opt.equals("--persistent")) {
 989                 persistent = true;
 990             } else {                                                                   
 991                 getErrPrintWriter().println("Error: Unknown option: " + opt);
 992                 return -1;
 993             }
 994         }
 995 
 996         String pkg = getNextArgRequired();
 997         mInterface.setDebugApp(pkg, wait, persistent);
 998         return 0;
 999     }
-------------------------------------------------------------------------
1008     int runClearDebugApp(PrintWriter pw) throws RemoteException {
1009         mInterface.setDebugApp(null, false, true);
1010         return 0;
1011     }

runSetDebugApp仅记录了俩参数,-w--persistent。分别代表:下次等待app启动、总是等待app启动

997行,实现与之前分析的am命令一样,转到ams处理

1009行,去掉debug。调用同样的方法,只是入参不一致。继续跟踪

ActivityManagerService.java#setDebugApp

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

 8391     public void setDebugApp(String packageName, boolean waitForDebugger,
 8392             boolean persistent) {
 8393         enforceCallingPermission(android.Manifest.permission.SET_DEBUG_APP,
 8394                 "setDebugApp()");
 8396         long ident = Binder.clearCallingIdentity();
 8397         try {
 8401             if (persistent) {
 8402                 final ContentResolver resolver = mContext.getContentResolver();
 8403                 Settings.Global.putString(
 8404                     resolver, Settings.Global.DEBUG_APP,
 8405                     packageName);
 8406                 Settings.Global.putInt(
 8407                     resolver, Settings.Global.WAIT_FOR_DEBUGGER,
 8408                     waitForDebugger ? 1 : 0);
 8409             }
 8410 
 8411             synchronized (this) {
 8412                 if (!persistent) {
 8413                     mOrigDebugApp = mDebugApp;
 8414                     mOrigWaitForDebugger = mWaitForDebugger;
 8415                 }
 8416                 mDebugApp = packageName;
 8417                 mWaitForDebugger = waitForDebugger;
 8418                 mDebugTransient = !persistent;
 8419                 if (packageName != null) {
 8420                     forceStopPackageLocked(packageName, -1, false, false, true, true,
 8421                             false, UserHandle.USER_ALL, "set debug app");

命令的设置仅有三点内容:settings数据库、全局变量、停止app进程

参数的组合有点乱,使用时一般-w --persistent一起用,这样每次app启动都会等待AS的debug了。

1.2、Setting中的debug设置

需要注意的是,上小节设置的数据库仅仅是为了同步信息。。。,除此之外没有任何用处

没有人监听这个数据库,只是在设置里检查该值,决定设置里开发者选项的UI,如何显示。

另外说下设置里的实现,如何设置debug app 和debugger的,答案:同命令行一样调用接口ActivityManagerService.java#setDebugApp

WaitForDebuggerPreferenceController.java#writeDebuggerAppOptions

packages/apps/Settings/src/com/android/settings/development/WaitForDebuggerPreferenceController.java

103     private void writeDebuggerAppOptions(String packageName, boolean waitForDebugger,
104             boolean persistent) {
105         try {
106             getActivityManagerService().setDebugApp(packageName, waitForDebugger, persistent);

所以对流程有影响的是AMS里的几个全局变量。看下面

1.3、进入等待

ActivityManagerService.java#attachApplicationLocked

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

 5011     private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
 5012             int pid, int callingUid, long startSeq) {
 5144         try {
 5145             int testMode = ApplicationThreadConstants.DEBUG_OFF;
 5146             if (mDebugApp != null && mDebugApp.equals(processName)) {
 5147                 testMode = mWaitForDebugger
 5148                     ? ApplicationThreadConstants.DEBUG_WAIT
 5149                     : ApplicationThreadConstants.DEBUG_ON;
 5150                 app.setDebugging(true);
 5151                 if (mDebugTransient) {
 5152                     mDebugApp = mOrigDebugApp;
 5153                     mWaitForDebugger = mOrigWaitForDebugger;
 ......
 5318                 thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
 5319                         null, null, null, testMode,

假设当前是-w --persistent,5147行,testMode为DEBUG_WAIT

5150给ProcessRecord设置标志位mDebugging = ture

5318行,binder调用到app进程bindApplication阶段

ActivityThread.java$ApplicationThread#bindApplication

frameworks/base/core/java/android/app/ActivityThread.java

         private class ApplicationThread extends IApplicationThread.Stub {    
1036         public final void bindApplication(String processName, ApplicationInfo appInfo, 
1040                 IUiAutomationConnection instrumentationUiConnection, int debugMode,
1074             AppBindData data = new AppBindData();
1082             data.debugMode = debugMode;
1094             sendMessage(H.BIND_APPLICATION, data);

1094行,转到主线程

ActivityThread.java#handleBindApplication

frameworks/base/core/java/android/app/ActivityThread.java

1907         public void handleMessage(Message msg) {
1909             switch (msg.what) {
1910                 case BIND_APPLICATION:
1911                     Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
1912                     AppBindData data = (AppBindData)msg.obj;
1913                     handleBindApplication(data);
------------------------------------------------------------------
6500         if (data.debugMode != ApplicationThreadConstants.DEBUG_OFF) {
6501             // XXX should have option to change the port.
6502             Debug.changeDebugPort(8100);
6503             if (data.debugMode == ApplicationThreadConstants.DEBUG_WAIT) {
6504                 Slog.w(TAG, "Application " + data.info.getPackageName()
6505                       + " is waiting for the debugger on port 8100...");
6506 
6507                 IActivityManager mgr = ActivityManager.getService();
6508                 try {
6509                     mgr.showWaitingForDebugger(mAppThread, true);
6510                 } catch (RemoteException ex) {
6511                     throw ex.rethrowFromSystemServer();
6512                 }
6513 
6514                 Debug.waitForDebugger();
6515 
6516                 try {
6517                     mgr.showWaitingForDebugger(mAppThread, false);
6518                 } catch (RemoteException ex) {
6519                     throw ex.rethrowFromSystemServer();
6520                 }
6521 
6522             } else {
6523                 Slog.w(TAG, "Application " + data.info.getPackageName()
6524                       + " can be debugged on port 8100...");

有两种debug链接方式,一是6514行DEBUG_WAIT,二是通过6502行设置的端口。我们一般通过第一种。

二、使用

命令提示

generic_x86_64:/ # am
Activity manager (activity) commands:
  set-debug-app [-w] [--persistent] <PACKAGE>
      Set application <PACKAGE> to debug.  Options are:
      -w: wait for debugger when application starts
      --persistent: retain this value
  clear-debug-app
      Clear the previously set-debug-app.

官方指导

见链接Debug your app

示例

  • 敲命令
generic_x86_64:/ # am set-debug-app -w --persistent com.example.myapplication
  • 然后点击启动app。此时会出现弹窗等待AS上的操作

  • AS上**Attach Debugger to Android Process**

    在AndroidStudio中的操作路径为Attach Debugger to Android Process → 选择链接设备上的进程。如下图

选中我们的进程,点ok就好了。如果有正确断点,则代码会走到断点处。

三、总结

1、设置开发者选项和命令行实现完全一致

2、等待debugger的点在Activity冷启动bindeApplication阶段。

我们知道,app进程启动后经历初始化→attach到AMS,然后才是AMS回来bindeApplication阶段。所以之前的代码流程用这个工具是断点不到的,可以自己动手,将Debug.waitForDebugger();放在你希望的地方。
比如断点调试SystemServer时可以这样写

frameworks/base/services/java/com/android/server/SystemServer.java

 413     public static void main(String[] args) {
 414         boolean bootDebug = "1".equals(SystemProperties.get("persist.boot.debug"));
 415         if(bootDebug){
 416             android.ddm.DdmHandleAppName.setAppName("system_process",
 417                     UserHandle.myUserId());
 418             android.os.Debug.waitForDebugger();                                         
 419         }
 420         new SystemServer().run();
 421     }

3、关于AS调试app的其他资料,可以看官网Debug your app

评论