概述
Framework开发是一项非常繁琐复杂的工作,需要阅读大量的源代码,分析及其多的LOG信息来定位错误位置。这个时候如果使用一些工具或者知道如何定位重要LOG信息,就可以使一些复杂的工作变的简单很多,使我们分析问题的效率变得更快,不再为阅读大量的源代码而感到一筹莫展。本文将针对一些场景讲解如何分析系统LOG信息,如何添加LOG定位错误信息,以及常用工具以及使用方法。
常用工具
HierarchyViewer
HierarchyViewer是随Android SDK发布的工具,位置在sdk的tools文件夹下,名为hierarchyviewer。它是Android自带的非常有用而且使用简单的工具,能够让我们从可视化的角度直观地获得UI布局设计结构和各种属性的信息,帮助我们优化布局设计。打开HierarchyViewer后我们可以看到当前view的布局,如下图所示。
DDMS
DDMS 的全称是Dalvik Debug Monitor Service,是 Android 开发环境中的Dalvik虚拟机调试监控服务。它为我们提供例如:为测试设备截屏,针对特定的进程查看正在运行的线程以及堆信息、Logcat、广播状态信息、模拟电话呼叫、接收SMS、虚拟地理坐标等等。DDMS打开界面如下:
左侧的Devices一栏会列出已连接的设备,点击设备名称左侧三角符号图标会列出该设备运行中的进程的app包名。
模拟按键
使用命令adb shell input keyevent + 对应的键值,可以模拟对应的操作。手机系统中主要的键值如下:
-
-
- public static final int KEYCODE_HOME = 3;
-
- public static final int KEYCODE_BACK = 4;
-
- public static final int KEYCODE_CALL = 5;
-
- public static final int KEYCODE_ENDCALL = 6;
-
-
- public static final int KEYCODE_VOLUME_UP = 24;
-
-
- public static final int KEYCODE_VOLUME_DOWN = 25;
-
- public static final int KEYCODE_POWER = 26;
-
-
- public static final int KEYCODE_CAMERA = 27;
-
- public static final int KEYCODE_CLEAR = 28;
-
- public static final int KEYCODE_SPACE = 62;
-
-
- public static final int KEYCODE_DEL = 67;
-
- public static final int KEYCODE_MENU = 82;
当我们要模拟power键亮灭屏事件就可以执行下面命令:
- adb shell input keyevent 26
模拟别的按键事件同理。
而adb shell getevent / adb shell sendevent是发送对应的事件,包括触屏,按键等事件。
am命令
有时候我们在调试系统时可以在终端使用am命令来发送广播,打开Activity,启动Service等操作,十分方便。am的详细操作如下,我们可以通过adb shell am + 对应命令,就可操作。
- usage: am [subcommand] [options]
- usage: am start [-D] [-N] [-W] [-P ] [--start-profiler ]
- [--sampling INTERVAL] [-R COUNT] [-S]
- [--track-allocation] [--user | current]
- am startservice [--user | current]
- am stopservice [--user | current]
- am force-stop [--user | all | current]
- am kill [--user | all | current]
- am kill-all
- am broadcast [--user | all | current]
- am instrument [-r] [-e ] [-p ] [-w]
- [--user | current]
- [--no-window-animation] [--abi ]
- am profile start [--user current] [--sampling INTERVAL]
- am profile stop [--user current] []
- am dumpheap [--user current] [-n]
- am set-debug-app [-w] [--persistent]
- am clear-debug-app
- am set-watch-heap
- am clear-watch-heap
- am bug-report [--progress]
- am monitor [--gdb ]
- am hang [--allow-restart]
- am restart
- am idle-maintenance
- am screen-compat [on|off]
- am package-importance
- am to-uri [INTENT]
- am to-intent-uri [INTENT]
- am to-app-uri [INTENT]
- am switch-user
- am start-user
- am unlock-user [TOKEN_HEX]
- am stop-user [-w] [-f]
- am stack start
- am stack movetask [true|false]
- am stack resize
- am stack resize-animated
- am stack resize-docked-stack []
- am stack size-docked-stack-test: [DELAY_MS]
- am stack move-top-activity-to-pinned-stack:
- am stack positiontask
- am stack list
- am stack info
- am stack remove
- am task lock
- am task lock stop
- am task resizeable [0 (unresizeable) | 1 (crop_windows) | 2 (resizeable) | 3 (resizeable_and_pipable)]
- am task resize
- am task drag-task-test [DELAY_MS]
- am task size-task-test [DELAY_MS]
- am get-config
- am suppress-resize-config-changes <true|false>
- am set-inactive [--user ] true|false
- am get-inactive [--user ]
- am send-trim-memory [--user ]
- [HIDDEN|RUNNING_MODERATE|BACKGROUND|RUNNING_LOW|MODERATE|RUNNING_CRITICAL|COMPLETE]
- am get-current-user
-
- am start: start an Activity. Options are:
- -D: enable debugging
- -N: enable native debugging
- -W: wait for launch to complete
- --start-profiler : start profiler and send results to
- --sampling INTERVAL: use sample profiling with INTERVAL microseconds
- between samples (use with --start-profiler)
- -P : like above, but profiling stops when app goes idle
- -R: repeat the activity launch times. Prior to each repeat,
- the top activity will be finished.
- -S: force stop the target app before starting the activity
- --track-allocation: enable tracking of object allocations
- --user | current: Specify which user to run as; if not
- specified then run as the current user.
- --stack : Specify into which stack should the activity be put.
- am startservice: start a Service. Options are:
- --user | current: Specify which user to run as; if not
- specified then run as the current user.
-
- am stopservice: stop a Service. Options are:
- --user | current: Specify which user to run as; if not
- specified then run as the current user.
-
- am force-stop: force stop everything associated with .
- --user | all | current: Specify user to force stop;
- all users if not specified.
-
- am kill: Kill all processes associated with . Only kills.
- processes that are safe to kill -- that is, will not impact the user
- experience.
- --user | all | current: Specify user whose processes to kill;
- all users if not specified.
-
- am kill-all: Kill all background processes.
-
- am broadcast: send a broadcast Intent. Options are:
- --user | all | current: Specify which user to send to; if not
- specified then send to all users.
- --receiver-permission : Require receiver to hold permission.
-
- am instrument: start an Instrumentation. Typically this target
- is the form / or only if there
- is only one instrumentation. Options are:
- -r: print raw results (otherwise decode REPORT_KEY_STREAMRESULT). Use with
- [-e perf true] to generate raw output for performance measurements.
- -e : set argument to . For test runners a
- common form is [-e [,...]].
- -p : write profiling data to
- -w: wait for instrumentation to finish before returning. Required for
- test runners.
- --user | current: Specify user instrumentation runs in;
- current user if not specified.
- --no-window-animation: turn off window animations while running.
- --abi : Launch the instrumented process with the selected ABI.
- This assumes that the process supports the selected ABI.
-
- am trace-ipc: Trace IPC transactions.
- start: start tracing IPC transactions.
- stop: stop tracing IPC transactions and dump the results to file.
- --dump-file : Specify the file the trace should be dumped to.
-
- am profile: start and stop profiler on a process. The given argument
- may be either a process name or pid. Options are:
- --user | current: When supplying a process name,
- specify user of process to profile; uses current user if not specified.
-
- am dumpheap: dump the heap of a process. The given argument may
- be either a process name or pid. Options are:
- -n: dump native heap instead of managed heap
- --user | current: When supplying a process name,
- specify user of process to dump; uses current user if not specified.
-
- am set-debug-app: set application to debug. Options are:
- -w: wait for debugger when application starts
- --persistent: retain this value
-
- am clear-debug-app: clear the previously set-debug-app.
-
- am set-watch-heap: start monitoring pss size of , if it is at or
- above then a heap dump is collected for the user to report
-
- am clear-watch-heap: clear the previously set-watch-heap.
-
- am bug-report: request bug report generation; will launch a notification
- when done to select where it should be delivered. Options are:
- --progress: will launch a notification right away to show its progress.
-
- am monitor: start monitoring for crashes or ANRs.
- --gdb: start gdbserv on the given port at crash/ANR
-
- am hang: hang the system.
- --allow-restart: allow watchdog to perform normal system restart
-
- am restart: restart the user-space system.
-
- am idle-maintenance: perform idle maintenance now.
-
- am screen-compat: control screen compatibility mode of .
-
- am package-importance: print current importance of .
-
- am to-uri: print the given Intent specification as a URI.
-
- am to-intent-uri: print the given Intent specification as an intent: URI.
-
- am to-app-uri: print the given Intent specification as an android-app: URI.
-
- am switch-user: switch to put USER_ID in the foreground, starting
- execution of that user if it is currently stopped.
-
- am start-user: start USER_ID in background if it is currently stopped,
- use switch-user if you want to start the user in foreground.
-
- am stop-user: stop execution of USER_ID, not allowing it to run any
- code until a later explicit start or switch to it.
- -w: wait for stop-user to complete.
- -f: force stop even if there are related users that cannot be stopped.
-
- am stack start: start a new activity on using .
-
- am stack movetask: move from its current stack to the top (true) or bottom (false) of .
-
- am stack resize: change size and position to .
-
- am stack resize-docked-stack: change docked stack to
- and supplying temporary different task bounds indicated by
-
-
- am stack size-docked-stack-test: test command for sizing docked stack by
- increments from the side eft, op, ight, or ottom
- applying the optional [DELAY_MS] between each step.
-
- am stack move-top-activity-to-pinned-stack: moves the top activity from
- to the pinned stack using for the
- bounds of the pinned stack.
-
- am stack positiontask: place in at
- am stack list: list all of the activity stacks and their sizes.
-
- am stack info: display the information about activity stack .
-
- am stack remove: remove stack .
-
- am task lock: bring to the front and don't allow other tasks to run.
-
- am task lock stop: end the current task lock.
-
- am task resizeable: change resizeable mode of .
- 0 (unresizeable) | 1 (crop_windows) | 2 (resizeable) | 3 (resizeable_and_pipable)
-
- am task resize: makes sure is in a stack with the specified bounds.
- Forces the task to be resizeable and creates a stack if no existing stack
- has the specified bounds.
-
- am task drag-task-test: test command for dragging/moving by
- increments around the screen applying the optional [DELAY_MS]
- between each step.
-
- am task size-task-test: test command for sizing by increments within the screen applying the optional [DELAY_MS] between
- each step.
-
- am get-config: retrieve the configuration and any recent configurations
- of the device.
- am suppress-resize-config-changes: suppresses configuration changes due to
- user resizing an activity/task.
-
- am set-inactive: sets the inactive state of an app.
-
- am get-inactive: returns the inactive state of an app.
-
- am send-trim-memory: send a memory trim event to a .
-
- am get-current-user: returns id of the current foreground user.
-
-
- specifications include these flags and arguments:
- [-a ] [-d ] [-t ]
- [-c [-c ] ...]
- [-e|--es ...]
- [--esn ...]
- [--ez ...]
- [--ei ...]
- [--el ...]
- [--ef ...]
- [--eu ...]
- [--ecn ]
- [--eia [,
- (mutiple extras passed as Integer[])
- [--eial [,
- (mutiple extras passed as List)
- [--ela [,
- (mutiple extras passed as Long[])
- [--elal [,
- (mutiple extras passed as List)
- [--efa [,
- (mutiple extras passed as Float[])
- [--efal [,
- (mutiple extras passed as List)
- [--esa [,
- (mutiple extras passed as String[]; to embed a comma into a string,
- escape it using "\,")
- [--esal [,
- (mutiple extras passed as List; to embed a comma into a string,
- escape it using "\,")
- [--f ]
- [--grant-read-uri-permission] [--grant-write-uri-permission]
- [--grant-persistable-uri-permission] [--grant-prefix-uri-permission]
- [--debug-log-resolution] [--exclude-stopped-packages]
- [--include-stopped-packages]
- [--activity-brought-to-front] [--activity-clear-top]
- [--activity-clear-when-task-reset] [--activity-exclude-from-recents]
- [--activity-launched-from-history] [--activity-multiple-task]
- [--activity-no-animation] [--activity-no-history]
- [--activity-no-user-action] [--activity-previous-is-top]
- [--activity-reorder-to-front] [--activity-reset-task-if-needed]
- [--activity-single-top] [--activity-clear-task]
- [--activity-task-on-home]
- [--receiver-registered-only] [--receiver-replace-pending]
- [--receiver-foreground]
- [--selector]
- [ | | ]
pm命令
当要查询系统中某个应用是否存在,或者存在路径,就可以根据对应的包名来查找对应的apk信息。
- usage: pm path [--user USER_ID] PACKAGE
- pm dump PACKAGE
- pm install [-lrtsfd] [-i PACKAGE] [--user USER_ID] [PATH]
- pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]
- [--install-location 0/1/2]
- [--force-uuid internal|UUID]
- pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]
- pm install-commit SESSION_ID
- pm install-abandon SESSION_ID
- pm uninstall [-k] [--user USER_ID] PACKAGE
- pm set-installer PACKAGE INSTALLER
- pm move-package PACKAGE [internal|UUID]
- pm move-primary-storage [internal|UUID]
- pm clear [--user USER_ID] PACKAGE
- pm enable [--user USER_ID] PACKAGE_OR_COMPONENT
- pm disable [--user USER_ID] PACKAGE_OR_COMPONENT
- pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT
- pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT
- pm default-state [--user USER_ID] PACKAGE_OR_COMPONENT
- pm hide [--user USER_ID] PACKAGE_OR_COMPONENT
- pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT
- pm grant [--user USER_ID] PACKAGE PERMISSION
- pm revoke [--user USER_ID] PACKAGE PERMISSION
- pm reset-permissions
- pm set-app-link [--user USER_ID] PACKAGE {always|ask|never|undefined}
- pm get-app-link [--user USER_ID] PACKAGE
- pm set-install-location [0/auto] [1/internal] [2/external]
- pm get-install-location 获取安装路径
- pm set-permission-enforced PERMISSION [true|false]
- pm trim-caches DESIRED_FREE_SPACE [internal|UUID]
- pm create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral] [--guest] USER_NAME
- pm remove-user USER_ID
- pm get-max-users
-
- NOTE: 'pm list' commands have moved! Run 'adb shell cmd package'
- to display the new commands.
-
- pm path: print the path to the .apk of the given PACKAGE.
-
- pm dump: print system state associated with the given PACKAGE.
-
- pm install: install a single legacy package
- pm install-create: create an install session
- -l: forward lock application
- -r: replace existing application
- -t: allow test packages
- -i: specify the installer package name
- -s: install application on sdcard
- -f: install application on internal flash
- -d: allow version code downgrade (debuggable packages only)
- -p: partial application install
- -g: grant all runtime permissions
- -S: size in bytes of entire session
-
- pm install-write: write a package into existing session; path may
- be '-' to read from stdin
- -S: size in bytes of package, required for stdin
-
- pm install-commit: perform install of fully staged session
- pm install-abandon: abandon session
-
- pm set-installer: set installer package name
-
- pm uninstall: removes a package from the system. Options:
- -k: keep the data and cache directories around after package removal.
-
- pm clear: deletes all data associated with a package.
-
- pm enable, disable, disable-user, disable-until-used, default-state:
- these commands change the enabled state of a given package or
- component (written as "package/class").
-
- pm grant, revoke: these commands either grant or revoke permissions
- to apps. The permissions must be declared as used in the app's
- manifest, be runtime permissions (protection level dangerous),
- and the app targeting SDK greater than Lollipop MR1.
-
- pm reset-permissions: revert all runtime permissions to their default state.
-
- pm get-install-location: returns the current install location.
- 0 [auto]: Let system decide the best location
- 1 [internal]: Install on internal device storage
- 2 [external]: Install on external media
-
- pm set-install-location: changes the default install location.
- NOTE: this is only intended for debugging; using this can cause
- applications to break and other undersireable behavior.
- 0 [auto]: Let system decide the best location
- 1 [internal]: Install on internal device storage
- 2 [external]: Install on external media
-
- pm trim-caches: trim cache files to reach the given free space.
-
- pm create-user: create a new user with the given USER_NAME,
- printing the new user identifier of the user.
-
- pm remove-user: remove the user with the given USER_IDENTIFIER,
- deleting all data associated with that user
dumpsys
该命令用于打印出当前系统信息,默认打印出设备中所有service的信息,可以在命令后面加指定的service name。
可以通过adb shell dumpsys + services可以打印出对应系统service的dump信息。可以将某个模块详细信息输出到控制台,可以更加直观的分析。
例如要打印Activity的dump信息:adb shell dumpsys activity
wakelock
wakelock主要是系统为上层提供控制屏幕亮度显示,以及cpu唤醒的一个接口。我们可以通过上面学习的dumpsys命令来查看系统此时存在那些wakelock。
adb shell dumpsys power
- Wake Locks: size=1
- SCREEN_BRIGHT_WAKE_LOCK 'WindowManager' ON_AFTER_RELEASE ACQ=-1s234ms (uid=1000 pid=1495 ws=WorkSource{1000})
此时系统只有一个wakelock,类型为SCREEN_BRIGHT_WAKE_LOCK,所属模块为WIndowManager。下面讲解一下如何使用wakelock。
- PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
- sWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK
- | PowerManager.ACQUIRE_CAUSES_WAKEUP
- | PowerManager.ON_AFTER_RELEASE, TAG);
- sWakeLock.acquire();
任何应用程序使用WakeLock,必须要求android.permission.WAKE_LOCK许可在应用程序的清单上使用元素。下面为我们可以调用的公共方法。
- Public Methods
- void acquire() --->当你创建这个WakeLock时,确保这个设备按照你的要求保持唤醒的状态。
- void acquire(long timeout) --->当你创建这个WakeLock时,确保这个设备按照你的要求保持唤醒的状态。
- void isHeld()
- void release() ---> 按照你的要求释放cpu或屏幕。
- void setReferenceCounted(boolean value)--->如果需要使用引用计数,则将value设置为true。默认设置为 true,表示当用户申请多少次锁时,就必须释放多少次。
- void setWorkSource(WorkSource ws)
- String toString() --->返回一个简洁的字符串用来将这个对象描述为人们可读懂的。
- void Finalize() --->当垃圾收集器检测到该实例不再运行调用。
wakelock有不同的类型,我们可以根据需要选择不同的wakelock类型。
Flag Value |
CPU |
Screen |
Keyboard |
PARTIAL_WAKE_LOCK |
On |
Off |
Off |
SCREEN_DIM_WAKE_LOCK |
On |
Dim |
Off |
SCREEN_BRIGHT_WAKE_LOCK |
On |
Bright |
Off |
FULL_WAKE_LOCK |
On |
Bright |
Bright |
如果你持有的partial wakelock,CPU将继续运行,不论何时,即使当用户按下电源按钮。对于所有其他wakelocks,但用户使用电源按钮时,系统将进入睡眠,cpu将不再继续运行。
PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。
SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯
SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯
FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度
ACQUIRE_CAUSES_WAKEUP:正常唤醒锁实际上并不打开照明。相反,一旦打开他们会一直仍然保持(例如来世user的activity)。当获得wakelock,这个标志会使屏幕或/和键盘立即打开。一个典型的使用就是可以立即看到那些对用户重要的通知。
ON_AFTER_RELEASE:设置了这个标志,当wakelock释放时用户activity计时器会被重置,导致照明持续一段时间。如果你在wacklock条件中循环,这个可以用来减少闪烁
我们可以创建多个锁,并hold它,即使对同一类型,也如此,对于某类型的wakelock只要有一个被hold,那么它所对应的电源状态(illumination),就将不会超时,将被延续(hold).在上表中我们把越往下的,称为更高一级的wakelocks.当高级和低级wakelocks相遇的时候,高级起作用。在上面的flag上还再加上如下的2个flag,但是他们和PARTIAL_WAKE_LOCK.组合没任何意义ACQUIRE_CAUSES_WAKEUP默认情况下wake locks并不是马上开启CPU或Screen或Keyboard的(对于Screen是Dim或Bright,Keyboard是Bright. wake locks只是在被开启后(比如用户的活动),让设备延续(保存)你设定开启的状态. 但是如果加上ACQUIRE_CAUSES_WAKEUP就可以让Screen或Keyboar没开启的情况,马上开启它们。 典型的应用就是在收到一个重要的notifications时,需要马上点亮屏幕。
addr2line
在分析framework稳定性问题时会遇到Native crash的发生,而Native crash与java crash的堆栈信息不同。下面是一段Native 堆栈的片段。
- 03-19 14:58:50.034 319 3145 D kevin_Render: #01 pc 0005f8a5 /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::setAnchorTime(long long, long long, long long, bool)+72)
- 03-19 14:58:50.034 319 3145 D kevin_Render: #02 pc 000618c5 /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::onNewAudioMediaTime(long long)+88)
- 03-19 14:58:50.035 319 3145 D kevin_Render: #03 pc 00062249 /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::onDrainAudioQueue()+764)
- 03-19 14:58:50.035 319 3145 D kevin_Render: #04 pc 00062ec3 /system/lib/libmediaplayerservice.so (android::NuPlayer::Renderer::onMessageReceived(android::sp const&)+866)
- 03-19 14:58:50.035 319 3145 D kevin_Render: #05 pc 0000d12d /system/lib/libstagefright_foundation.so (android::ALooperRoster::deliverMessage(android::sp const&)+164)
- 03-19 14:58:50.035 319 3145 D kevin_Render: #06 pc 0000cac1 /system/lib/libstagefright_foundation.so (android::ALooper::loop()+216)
- 03-19 14:58:50.035 319 3145 D kevin_Render: #07 pc 00010977 /system/lib/libutils.so (android::Thread::_threadLoop(void*)+110)
- 03-19 14:58:50.035 319 3145 D kevin_Render: #08 pc 00016f5b /system/lib/libc.so (__pthread_start(void*)+30)
- 03-19 14:58:50.035 319 3145 D kevin_Render: #09 pc 00014f7b /system/lib/libc.so (__start_thread+6)
根据上面的堆栈,我们并不能看到具体哪个函数,哪行代码出现了问题,这个时候我们就需要解析Native函数的地址值,如上面 00062ec3这样的符号,我们需要通过addr2line来进行解析。我们可以使用linux系统提供的addr2line命令,也可以使用源代码sdk中的工具。具体命令如下:
addr2line -e out/target/product/项目名/symbols/system/lib/libxxx.so -f -C
如果我们想要解析03行的地址值00062249
- kevin@kevin-All-Series:~/work/$ addr2line -e out/target/product/miki8163_9003/symbols/system/lib/libmediaplayerservice.so -f -C 00062249
- android::NuPlayer::Renderer::onDrainAudioQueue()
- /home/kevin/work/source/sm8163/sm8163/frameworks/av/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp:868
解析后我们看到该行代码属于函数android::NuPlayer::Renderer::onDrainAudioQueue(),在NuPlayerRenderer.cpp的868行,这样就十分方便了。
meminfo
当我们查询系统的内存信息除了使用dumpsys命令,还可以直接使用adb shell cat /proc/meminfo
- kevin@kevin-All-Series:~/work$ adb shell cat /proc/meminfo
- MemTotal: 1844152 kB
- MemFree: 41364 kB
- MemAvailable: 230892 kB
- Buffers: 3520 kB
- Cached: 230312 kB
- SwapCached: 6484 kB
- Active: 538568 kB
- Inactive: 257680 kB
- Active(anon): 426264 kB
- Inactive(anon): 144884 kB
- Active(file): 112304 kB
- Inactive(file): 112796 kB
- Unevictable: 256 kB
- Mlocked: 256 kB
- SwapTotal: 524284 kB
- SwapFree: 156 kB
- Dirty: 44 kB
- Writeback: 0 kB
- AnonPages: 559864 kB
- Mapped: 216980 kB
- Shmem: 8608 kB
- Slab: 354744 kB
- SReclaimable: 56820 kB
- SUnreclaim: 297924 kB
- KernelStack: 35488 kB
- PageTables: 34172 kB
- NFS_Unstable: 0 kB
- Bounce: 0 kB
- WritebackTmp: 0 kB
- CommitLimit: 1446360 kB
- Committed_AS: 63267272 kB
- VmallocTotal: 244318144 kB
- VmallocUsed: 116780 kB
- VmallocChunk: 244056036 kB
fastboot
Android系统编译后会生成很多的image文件,我们可以通过刷机工具将image烧制到手机中,我们也可以使用fastboot命令很方便的将image文件烧制到系统中。下面为fastboot的使用方法:
1.开机状态下执行
adb reboot bootloader(执行命令后系统会关机重启,进入fastboot模式)
2.烧写system.img
fastboot flash system system.img
如果要烧制boot.img,那么就执行fastboot flash boot boot.img,其他image也一样。
3.重启
fastboot reboot
简单几步就可以快速将我们编译出来的文件烧制到手机中。
bootchart
bootchart是一个用于linux启动过程性能分析的开源软件工具,在系统启动过程自动收集CPU占用率、进程等信息,并以图形方式显示分析结果,可用作指导优化系统启动过程。bootchart详细使用说明请参考《Android7.0 bootchart工具使用说明》
LOG分析
在调试Android系统时如果要实时的查看输出的log信息就可以使用adb logcat -vthreadtime或者adb logcat在终端输出需要的log信息,在5.1及其之前的版本需要adb logcat后需要加上-vthreadtime 意思是将输出时间以及进程线程信息,5.1之后的版本系统自动添加了时间与进程线程信息,直接用adb logcat就可以。
而更多的时候我们需要分析log文件,也就是在系统运行时会将log信息输出在log文件中,以便在出现问题时让开发人员进行分析,下面讲几个场景的关键LOG信息。
开机LOG
在分析开机log时通过根据开机流程进行分段分析,这里省略bootloader中log,首先从执行kernel完成分析,到启动launcher分析结束,来看其中执行了那些过程,如果开机流程出现问题,就可以定位哪个流程出现问题。
- //开始启动init进程,也就是kernel启动完成,取时间戳2.172934s,为启动kernel所耗时.
- [01-01 08:30:04.349] <13>[ 2.172934] c5 init: init started!
-
- //开始启动zygote,使用时间戳相减4.281027-2.172934计算出启动init进程到启动zygote耗时2.1s.
- [01-01 08:30:04.367] <13>[ 4.281027] c0 init: Starting service 'zygote'...
-
- //进入zygote,由于kernel时间与android不能相减,所以无法计算启动zygote到进入zygote所花费时间.
- A001-01 08:30:05.036 2810 2810 D AndroidRuntime: >>>>>> START com.android.internal.os.ZygoteInit uid 0 <<<<<<
-
- //进入SystemServer,使用android时间08:30:08.629-08:30:05.036 获得,从进入zygote到SystemServer耗时
- A101-01 08:30:08.629 3285 3285 I SystemServer: Entered the Android system server!
-
- //开始初始化Package, 使用android时间相减,获取进入SystemServer到初始化Package的耗时.
- A101-01 08:30:09.021 3285 3285 I SystemServer: Package Manager
- //扫描解析应用耗时,可以直接获取1.544 seconds
- A101-01 08:30:10.813 3285 3285 I PackageManager: Time to scan packages: 1.544 seconds
-
- //开始执行network systemReady函数,使用android时间相减,获取初始化Package完成到开始执行network systemReady函数的耗时.
- A101-01 08:30:14.285 3285 3285 D NetworkManagement: enabling bandwidth control
-
- //network执行完相关命令,使用android时间相减,获取执行network执行相关命令耗时.
- A101-01 08:30:15.475 3285 3285 I SystemServiceManager: Starting phase 600
-
- //启动launcher, 使用android时间相减,获取执行network命令完成,到启动launcher耗时
- A101-01 08:30:15.915 3285 3285 I ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.HOME] flg=0x10000000 cmp=com.android.launcher3/.Launcher} from uid 0 on display 0
关机LOG
在分析关机log时,是从长按power键,点击确认关机,到灭屏震动关机。关机主要经过的流程为发送关机广播,关闭AMS,PMS,关闭移动数据,卸载sd卡等流程,最后调用power往底层系统配置中写入对应的信息,底层读取信息,进行关机。
- //按确认键,开始关机
- 01-01 12:49:10.410 1147 1226 I ShutdownThread: shutdown goto shutdownInner
-
- //发送关机广播,使用android时间相减,获得关机到发送关机广播之间耗时
- 01-01 12:49:10.498 1147 2504 I ShutdownThread: Sending shutdown broadcast...
-
- //开始shutdown activity manager, 使用android时间相减,获得关机广播处理耗时
- 01-01 12:49:10.637 1147 2504 I ShutdownThread: Shutting down activity manager...
-
- //开始关闭蜂窝网,使用android时间相减,获得开机shutdown activity manager到关闭蜂窝网耗时
- 01-01 12:49:10.746 1147 2522 W ShutdownThread: Turning off cellular radios...
-
- //开始关闭Radio,获得关闭蜂窝网到开始关闭Radio耗时
- 01-01 12:49:10.753 1147 2522 I ShutdownThread: Waiting for NFC, Bluetooth and Radio...
-
- //关闭Radio完成,使用android时间相减,获得关闭Radio耗时,该流程可能耗时较长.重点
- 01-01 12:49:11.255 1147 2522 I ShutdownThread: NFC, Radio and Bluetooth shutdown complete.
-
- //开始关闭MountService,使用android时间相减,获得关闭Radio完成到关闭MountService耗时.
- 01-01 12:49:11.257 1147 2504 I ShutdownThread: Shutting down MountService
-
- //关闭MountService完成,使用android时间相减,获得关闭MountService耗时.该流程可能耗时较长.重点
- 01-01 12:49:11.268 1147 1304 W ShutdownThread: Result code 0 from MountService.shutdown
-
- //调用底层接口,关机完成.
- 01-01 12:49:11.776 1147 2504 I ShutdownThread: Performing low-level shutdown...
首启动应用LOG
在分析分析应用启动问题时,我们通常分析event log信息,使用命令adb logcat -b events,其中的log信息主要为应用的生命周期信息。通过查看生命周期信息来分析,应用是否行为异常。
首次启动
- //开始启动activity
- 07-29 10:26:57.033 1173 1995 I am_create_activity: [0,102422011,29,com.android.dialer/.DialtactsActivity,android.intent.action.MAIN,NULL,NULL,270532608]
-
- //launcher pause完成。
- 07-29 10:26:57.109 2009 2009 I am_on_paused_called: [0,com.android.launcher3.Launcher]
-
- //进程开始创建,与开始启动activity时间相减,获得准备工作耗时.
- 07-29 10:26:57.142 1173 1996 I am_proc_start: [0,5164,10038,com.android.dialer,activity,com.android.dialer/.DialtactsActivity]
-
- //进程bound完成,与进程创建之间这个过程可以理解为进程创建的时间。为创建进程耗时.
- 07-29 10:26:57.195 1173 1404 I am_proc_bound: [0,5164,com.android.dialer]
-
- //ams向应用发消息进行启动activity操作
- 07-29 10:26:57.199 1173 1404 I am_restart_activity: [0,102422011,29,com.android.dialer/.DialtactsActivity]
-
- //activity resume执行完成,与restart_activity之间这个可以理解为Activity的生命周期执行时间。为生命周期耗时
- 07-29 10:26:57.394 5164 5164 I am_on_resume_called: [0,com.android.dialer.DialtactsActivity]
-
- //这里activity显示完成,当前Activity显示过程644ms,整个显示过程644ms。
- //从activity resume完成到这个log之间可以理解为广义上的绘制过程(measure,layout,draw,render)。
- 07-29 10:26:57.757 1173 1356 I am_activity_launch_time: [0,102422011,com.android.dialer/.DialtactsActivity,644,644]
非首次启动
- //开始启动activity,到launcher pause完成。
- 01-01 13:26:59.645 1162 1733 I am_create_activity: [0,233918075,10,com.android.messaging/.ui.conversationlist.ConversationListActivity,android.intent.action.MAIN,NULL,NULL,270532608]
-
- //launcher pause完成。
- 07-29 10:26:57.109 2009 2009 I am_on_paused_called: [0,com.android.launcher3.Launcher]
-
- //ams向应用发消息进行启动activity操作,准备工作
- 01-01 13:26:59.710 1162 1439 I am_restart_activity: [0,233918075,10,com.android.messaging/.ui.conversationlist.ConversationListActivity]
-
- //activity resume执行完成,与restart_activity之间这个可以理解为Activity的生命周期执行时间。
- 01-01 13:26:59.782 2189 2189 I am_on_resume_called: [0,com.android.messaging.ui.conversationlist.ConversationListActivity]
-
- //这里activity显示完成,当前Activity显示过程168ms,整个显示过程168ms。
- //从activity resume完成到这个log之间可以理解为广义上的绘制过程(measure,layout,draw,render)。
- 01-01 13:26:59.871 1162 1283 I am_activity_launch_time: [0,233918075,com.android.messaging/.ui.conversationlist.ConversationListActivity,168,168]
亮屏关键LOG
- //kernel中断key事件,ScanCode:116为Power键,value:1为down事件
- <6>[41051.961488] c1 [SPRD_KEY_INFO]Key:Power Key ScanCode:116 value:1
-
- //InputReader获取power键事件,code=116为power键
- 01-02 08:04:44.016 600 643 D InputReader: processEventsLocked: type=1 Count=2 code=116 value=1 deviceId=2
-
- //Power收到亮屏调用,开始亮屏
- 01-02 08:04:44.037 600 643 I PowerManagerService: Waking up from sleep (uid 1000)...
-
- //开始block亮屏,直到keyguard与windows绘制完成
- 01-02 08:04:44.052 600 622 I DisplayPowerController: Blocking screen on until initial contents have been drawn.
-
- //开始绘制keyguard
- 01-02 08:04:44.052 600 622 I WindowManager: Screen turning on...
-
- //tosuspend_disable()耗时283s,有时为setPowerMode耗时长
- 01-02 08:04:44.347 600 676 D PowerManagerService-JNI: Excessive delay in autosuspend_disable() while
-
- //yguard绘制完成
- 01-02 08:04:44.504 600 700 D WindowManager: mKeyguardDelegate.ShowListener.onDrawn.
- 01-02 08:04:44.504 600 614 W WindowManager: Setting mKeyguardDrawComplete
-
- //windows绘制完成
- 01-02 08:04:44.551 600 620 I WindowManager: All windows ready for display!
- 01-02 08:04:44.551 600 614 W WindowManager: Setting mWindowManagerDrawComplete
-
- //block亮屏,总共blocked亮屏505ms
- 01-02 08:04:44.557 600 622 I DisplayPowerController: Unblocked screen on after 505 ms
-
- //屏幕亮度,往节点中写亮度值
- 01-02 08:04:44.558 600 676 D LIGHTS : file:vendor/sprd/modules/lights/lights.c, func:set_light_backlight, brightness=25
- 01-02 08:04:44.559 600 676 E LIGHTS : file:vendor/sprd/modules/lights/lights.c, func:write_int, path=/sys/class/backlight/sprd_backlight/brightness, value=25
来电亮屏LOG
- //创建dialer进程
- 12-22 15:46:23.704 595 595 I ActivityManager: Start proc 5067:com.android.dialer/u0a1 for service com.android.dialer/com.android.incallui.InCallServiceImpl
-
- //来电响铃
- 12-22 15:46:23.736 595 5083 I Telecom : AsyncRingtonePlayer: Play ringtone.
-
- //启动InCallActivity
- 12-22 15:46:24.410 595 1016 I ActivityManager: START u0 {act=android.intent.action.MAIN flg=0x10040000 cmp=com.android.dialer/com.android.incallui.InCallActivity (has extras)} from uid 10001 on display 0
-
- //显示InCallActivity
- 12-22 15:46:25.715 595 615 I am_activity_launch_time: [0,134650581,com.android.dialer/com.android.incallui.InCallActivity,1293,1299]
-
- //power收到亮屏调用
- 12-22 15:46:25.730 595 615 I PowerManagerService: Waking up from sleep due toandroid android.server.wm:TURN_ON (uid 1000)...
-
- //点亮屏幕
- A012-22 15:46:26.220 595 679 D LIGHTS : file:vendor/sprd/modules/lights/lights.c, func:set_light_backlight, brightness=25
- A012-22 15:46:26.221 595 679 E LIGHTS : file:vendor/sprd/modules/lights/lights.c, func:write_int, path=/sys/class/backlight/sprd_backlight/brightness, value=25
灭屏LOG
- //kernel中断key事件,ScanCode:116为Power键,value:1为down事件
- <6>[42586.507314] c1 [SPRD_KEY_INFO]Key:Power Key ScanCode:116 value:1
-
- //value:0为up事件
- <6>[42586.660288] c1 [SPRD_KEY_INFO]Key:Power Key ScanCode:116 value:0
-
- //Input收到power键down事件
- 01-06 13:25:48.460 3339 3447 D InputReader: processEventsLocked: type=1 Count=2 code=116 value=1 deviceId=2
-
- //Input收到power键up事件
- 01-06 13:25:48.643 3339 3447 D InputReader: processEventsLocked: type=1 Count=2 code=116 value=0 deviceId=2
-
- //短按power键,power开始灭屏
- 01-06 13:25:48.643 3339 3447 I PowerManagerService: Going to sleep due to power button (uid 1000)...
-
- //设置全局变量,通知keyguard灭屏了
- 01-06 13:25:48.645 3339 3361 I WindowManager: Screen turned off...
-
- //设置屏幕亮度为0
- 01-06 13:25:48.645 3339 3493 D LIGHTS : file:vendor/sprd/modules/lights/lights.c, func:set_light_backlight, brightness=0
- 01-06 13:25:48.645 3339 3493 E LIGHTS : file:vendor/sprd/modules/lights/lights.c, func:write_int, path=/sys/class/backlight/sprd_backlight/brightness, value=0
添加LOG信息
java层添加
当在调试系统代码时,如果要确认代码是否走了某个流程,就可以添加log信息,如果执行某项操作可以将该行log打印出来就说明走了该流程。
- import android.util.Log;
-
- private static String getActiveSubscriberId(Context context, int subId) {
- final TelephonyManager tele = TelephonyManager.from(context);
- String retVal = tele.getSubscriberId(subId);
- Log.d(TAG, "getActiveSubscriberId=" + retVal + " subId=" + subId);
- return retVal;
- }
如果想知道某行代码是怎么调用过来的,他的调用关系是什么,就可以通过打印堆栈的方式来确认。具体方式如下;
- try {
- throw new Exception("Call Stack Trace");
- } catch (Exception e) {
- Log.i("kevin", "xxx", e);
- }
例如在唤醒屏幕流程,如果想要分析wakeUpInternal是怎么调用过来的,就可以打印堆栈的方式来确认调用关系。
- private void wakeUpInternal(long eventTime, String reason, int uid, String opPackageName,
- int opUid) {
- synchronized (mLock) {
-
- try {
- throw new Exception("Call Stack Trace");
- } catch (Exception e) {
- Log.i("kevin", "xxx", e);
- }
-
- if (wakeUpNoUpdateLocked(eventTime, reason, uid, opPackageName, opUid)) {
- updatePowerStateLocked();
- }
- }
- }
当我们修改好代码后编译services.jar,将services.jar push到手机中,重启,按power键亮灭屏就可以看到如下信息。
C++层添加
如果想要在C++层添加log信息,需要在对应的Android.mk文件中添加下面代码,添加共享库。
- LOCAL_SHARED_LIBRARIES := libutils
用如下log可以打印C++层的信息。
- ALOGD("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
如果要在C++层中打印对应堆栈,使用如下代码:
- #include
-
- CallStack stack;
- stack.update();
- stack.log("kevin");
示例代码如下:
- void NuPlayer::start() {
- CallStack stack;
- stack.update();
- stack.log("kevin");
- (new AMessage(kWhatStart, this))->post();
- }
输出堆栈如下:
可以使用前面学习的addr2line
可以看到具体的代码调用。