nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 1 | # Android Debugging Instructions |
| 2 | |
| 3 | Chrome on Android has java and c/c++ code. Each "side" have its own set of tools |
| 4 | for debugging. Here's some tips. |
| 5 | |
| 6 | [TOC] |
| 7 | |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 8 | ## Launching the app |
| 9 | |
Andrew Grieve | c81af4a | 2017-07-26 18:02:13 | [diff] [blame] | 10 | You can launch the app by using one of the wrappers. |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 11 | |
| 12 | ```shell |
Andrew Grieve | c81af4a | 2017-07-26 18:02:13 | [diff] [blame] | 13 | out/Default/bin/content_shell_apk launch [--args='--foo --bar'] 'data:text/html;utf-8,<html>Hello World!</html>' |
| 14 | out/Default/bin/chrome_public_apk launch [--args='--foo --bar'] 'data:text/html;utf-8,<html>Hello World!</html>' |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 15 | ``` |
| 16 | |
| 17 | ## Log output |
| 18 | |
| 19 | [Chromium logging from LOG(INFO)](https://chromium.googlesource.com/chromium/src/+/master/docs/android_logging.md) |
| 20 | etc., is directed to the Android logcat logging facility. You can filter the |
| 21 | messages, e.g. view chromium verbose logging, everything else at warning level |
| 22 | with: |
| 23 | |
| 24 | ```shell |
| 25 | adb logcat chromium:V cr.SomeComponent:V *:W |
Andrew Grieve | c81af4a | 2017-07-26 18:02:13 | [diff] [blame] | 26 | # or: |
| 27 | out/Default/bin/chrome_public_apk logcat |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 28 | ``` |
| 29 | |
| 30 | ### Warnings for Blink developers |
| 31 | |
| 32 | * **Do not use fprintf or printf debugging!** This does not |
| 33 | redirect to logcat. |
| 34 | |
| 35 | * Redirecting stdio to logcat, as documented |
| 36 | [here](https://developer.android.com/studio/command-line/logcat.html#viewingStd), |
| 37 | has a bad side-effect that it breaks `adb_install.py`. See |
| 38 | [here for details](http://stackoverflow.com/questions/28539676/android-adb-fails-to-install-apk-to-nexus-5-on-windows-8-1). |
| 39 | |
| 40 | ## Take a screenshot |
| 41 | |
| 42 | While your phone is plugged into USB, use the `screenshot.py` tool in |
| 43 | `build/android`. `envsetup.sh` should have put it in your path. |
| 44 | |
| 45 | ```shell |
| 46 | build/android/screenshot.py /tmp/screenshot.png |
| 47 | ``` |
| 48 | |
| 49 | ## Inspecting the view hierarchy |
| 50 | |
| 51 | You can use either |
| 52 | [hierarchy viewer](https://developer.android.com/studio/profile/hierarchy-viewer-setup.html) |
| 53 | or [monitor](https://developer.android.com/studio/profile/monitor.html) to see |
| 54 | the Android view hierarchy and see the layout and drawing properties associated |
| 55 | with it. |
| 56 | |
| 57 | While your phone is plugged into USB, you can inspect the Android view hierarchy |
| 58 | using the following command: |
| 59 | |
| 60 | ```shell |
| 61 | ANDROID_HVPROTO=ddm monitor |
| 62 | ``` |
| 63 | |
| 64 | Setting `ANDROID_HVPROTO` allows you to inspect debuggable apps on non-rooted |
| 65 | devices. When building a local version of Chromium, the build tools |
| 66 | automatically add `android:debuggable=true` to the `AndroidManifest.xml`, which |
| 67 | will allow you to inspect them on rooted devices. |
| 68 | |
| 69 | Want to add some additional information to your Views? You can do that by |
| 70 | adding the |
| 71 | [@ViewDebug.ExportedProperty](https://developer.android.com/reference/android/view/ViewDebug.ExportedProperty.html) |
| 72 | annotation. |
| 73 | |
| 74 | Example: |
| 75 | |
| 76 | ```java |
| 77 | @ViewDebug.ExportedProperty(category="chrome") |
| 78 | private int mSuperNiftyDrawingProperty; |
| 79 | ``` |
| 80 | |
| 81 | ## Debugging Java |
| 82 | |
Andrew Grieve | 4fe9974 | 2017-11-23 19:43:16 | [diff] [blame] | 83 | For both apk and test targets, pass `--wait-for-java-debugger` to the wrapper |
| 84 | scripts. |
| 85 | |
| 86 | Examples: |
| 87 | |
| 88 | ```shell |
| 89 | # Install, launch, and wait: |
| 90 | out/Default/bin/chrome_public_apk run --wait-for-java-debugger |
| 91 | |
| 92 | # Launch, and have GPU process wait rather than Browser process: |
| 93 | out/Default/bin/chrome_public_apk launch --wait-for-java-debugger --debug-process-name privileged_process0 |
| 94 | |
| 95 | # Have Renderers wait: |
| 96 | out/Default/bin/chrome_public_apk launch --args="--renderer-wait-for-java-debugger" |
| 97 | |
| 98 | # Have tests wait: |
| 99 | out/Default/bin/run_chrome_public_test_apk --wait-for-java-debugger |
| 100 | out/Default/bin/run_chrome_junit_tests --wait-for-java-debugger # Specify custom port via --debug-socket=9999 |
| 101 | ``` |
| 102 | |
| 103 | ### Android Studio |
| 104 | * Open Android Studio ([instructions](android_studio.md)) |
| 105 | * Click "Run"->"Attach debugger to Android process" (see |
Wei-Yin Chen (陳威尹) | 0f8750b3 | 2017-12-08 21:42:15 | [diff] [blame^] | 106 | [here](https://developer.android.com/studio/debug/index.html) for more). |
| 107 | Click "Run"->"Attach to Local Process..." for Robolectric junit tests. |
Andrew Grieve | 4fe9974 | 2017-11-23 19:43:16 | [diff] [blame] | 108 | |
estevenson | 8c9318ff | 2017-03-10 22:16:35 | [diff] [blame] | 109 | ### Eclipse |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 110 | * In Eclipse, make a debug configuration of type "Remote Java Application". |
| 111 | Choose a "Name" and set "Port" to `8700`. |
| 112 | |
| 113 | * Make sure Eclipse Preferences > Run/Debug > Launching > "Build (if required) |
| 114 | before launching" is unchecked. |
| 115 | |
| 116 | * Run Android Device Monitor: |
| 117 | |
| 118 | ```shell |
| 119 | third_party/android_tools/sdk/tools/monitor |
| 120 | ``` |
| 121 | |
| 122 | * Now select the process you want to debug in Device Monitor (the port column |
| 123 | should now mention 8700 or xxxx/8700). |
| 124 | |
| 125 | * Run your debug configuration, and switch to the Debug perspective. |
| 126 | |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 127 | ## Debugging C/C++ |
| 128 | |
Andrew Grieve | 4fe9974 | 2017-11-23 19:43:16 | [diff] [blame] | 129 | Use the wrapper script `gdb` command to enter into a gdb shell. |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 130 | |
| 131 | ```shell |
Andrew Grieve | 4fe9974 | 2017-11-23 19:43:16 | [diff] [blame] | 132 | # Attaches to browser process. |
Andrew Grieve | c81af4a | 2017-07-26 18:02:13 | [diff] [blame] | 133 | out/Default/bin/content_shell_apk gdb |
| 134 | out/Default/bin/chrome_public_apk gdb |
Andrew Grieve | 4fe9974 | 2017-11-23 19:43:16 | [diff] [blame] | 135 | |
| 136 | # Attaches to gpu process. |
| 137 | out/Default/bin/chrome_public_apk gdb --debug-process-name privileged_process0 |
| 138 | |
| 139 | # Attach to other processes ("chrome_public_apk ps" to show pids). |
| 140 | out/Default/bin/chrome_public_apk gdb --pid $PID |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 141 | ``` |
| 142 | |
Andrew Grieve | 4fe9974 | 2017-11-23 19:43:16 | [diff] [blame] | 143 | ### Waiting for Debugger on Early Startup |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 144 | |
| 145 | Set the target command line flag with `--wait-for-debugger`. |
| 146 | |
Andrew Grieve | c81af4a | 2017-07-26 18:02:13 | [diff] [blame] | 147 | Launch the debugger using one of the scripts from above. |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 148 | |
| 149 | Type `info threads` and look for a line like: |
| 150 | |
| 151 | ``` |
| 152 | 11 Thread 2564 clock_gettime () at bionic/libc/arch-arm/syscalls/clock_gettime.S:11 |
| 153 | ``` |
| 154 | |
| 155 | or perhaps: |
| 156 | |
| 157 | ``` |
| 158 | 1 Thread 10870 0x40127050 in nanosleep () from /tmp/user-adb-gdb-libs/system/lib/libc.so |
| 159 | ``` |
| 160 | |
| 161 | We need to jump out of its sleep routine: |
| 162 | |
| 163 | ``` |
| 164 | (gdb) thread 11 |
| 165 | (gdb) up |
| 166 | (gdb) up |
| 167 | (gdb) return |
| 168 | Make base::debug::BreakDebugger() return now? (y or n) y |
| 169 | (gdb) continue |
| 170 | ``` |
| 171 | |
| 172 | ## Symbolizing Crash Stacks and Tombstones (C++) |
| 173 | |
| 174 | If a crash has generated a tombstone in your device, use: |
| 175 | |
| 176 | ```shell |
| 177 | build/android/tombstones.py --output-directory out/Default |
| 178 | ``` |
| 179 | |
| 180 | If you have a stack trace (from `adb logcat`) that needs to be symbolized, copy |
| 181 | it into a text file and symbolize with the following command (run from |
| 182 | `${CHROME_SRC}`): |
| 183 | |
| 184 | ```shell |
| 185 | third_party/android_platform/development/scripts/stack --output-directory out/Default [tombstone file | dump file] |
| 186 | ``` |
| 187 | |
| 188 | `stack` can also take its input from `stdin`: |
| 189 | |
| 190 | ```shell |
| 191 | adb logcat -d | third_party/android_platform/development/scripts/stack --output-directory out/Default |
| 192 | ``` |
| 193 | |
| 194 | Example: |
| 195 | |
| 196 | ```shell |
| 197 | third_party/android_platform/development/scripts/stack --output-directory out/Default ~/crashlogs/tombstone_07-build231.txt |
| 198 | ``` |
| 199 | |
| 200 | ## Deobfuscating Stack Traces (Java) |
| 201 | |
| 202 | You will need the ProGuard mapping file that was generated when the application |
| 203 | that crashed was built. When building locally, these are found in: |
| 204 | |
| 205 | ```shell |
| 206 | out/Default/apks/ChromePublic.apk.mapping |
agrieve | a350dbdb | 2017-07-05 15:27:17 | [diff] [blame] | 207 | out/Default/apks/ChromeModernPublic.apk.mapping |
| 208 | etc. |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 209 | ``` |
| 210 | |
agrieve | a350dbdb | 2017-07-05 15:27:17 | [diff] [blame] | 211 | Build the `java_deobfuscate` tool: |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 212 | |
| 213 | ```shell |
agrieve | a350dbdb | 2017-07-05 15:27:17 | [diff] [blame] | 214 | ninja -C out/Default java_deobfuscate |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 215 | ``` |
| 216 | |
agrieve | a350dbdb | 2017-07-05 15:27:17 | [diff] [blame] | 217 | Then run it via: |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 218 | |
| 219 | ```shell |
agrieve | a350dbdb | 2017-07-05 15:27:17 | [diff] [blame] | 220 | # For a file: |
| 221 | out/Default/bin/java_deobfuscate PROGUARD_MAPPING_FILE.mapping < FILE |
| 222 | # For logcat: |
| 223 | adb logcat | out/Default/bin/java_deobfuscate PROGUARD_MAPPING_FILE.mapping |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 224 | ``` |
| 225 | |
| 226 | ## Get WebKit code to output to the adb log |
| 227 | |
| 228 | In your build environment: |
| 229 | |
| 230 | ```shell |
| 231 | adb root |
| 232 | adb shell stop |
| 233 | adb shell setprop log.redirect-stdio true |
| 234 | adb shell start |
| 235 | ``` |
| 236 | |
| 237 | In the source itself, use `fprintf(stderr, "message");` whenever you need to |
| 238 | output a message. |
| 239 | |
| 240 | ## Debug unit tests with GDB |
| 241 | |
| 242 | To run unit tests use the following command: |
| 243 | |
| 244 | ```shell |
jbudorick | 6a94be3 | 2017-05-11 22:38:43 | [diff] [blame] | 245 | out/Debug/bin/run_test_name -f <test_filter_if_any> --wait-for-debugger -t 6000 |
nyquist | c75738d | 2016-09-13 19:25:01 | [diff] [blame] | 246 | ``` |
| 247 | |
| 248 | That command will cause the test process to wait until a debugger is attached. |
| 249 | |
| 250 | To attach a debugger: |
| 251 | |
| 252 | ```shell |
| 253 | build/android/adb_gdb --output-directory=out/Default --package-name=org.chromium.native_test |
| 254 | ``` |
| 255 | |
| 256 | After attaching gdb to the process you can use it normally. For example: |
| 257 | |
| 258 | ``` |
| 259 | (gdb) break main |
| 260 | Breakpoint 1 at 0x9750793c: main. (2 locations) |
| 261 | (gdb) continue |
| 262 | ``` |