As we know, an Android* developer wears many hats: designer, code monkey, and, inevitably, troubleshooter. Bugs in code are inevitable-so it's important to know the debugging tools and how to quickly and effectively track them down and fix them, whether you created the bugs initially or otherwise. Therefore, effective debugging techniques are an essential skill for today’s Android developers. This article provides a walk-through of Android application debugging tools and is designed to help developers new to the Android SDK and related tools get up to speed faster and resolve defects more effectively on Android x86 platform.
2. SDK app debugging tools
The Android SDK provides most of the tools that you need to debug your applications. You need a JDWP-compliant debugger if you want to be able to do things such as step through code, view variable values, and pause execution of an application. If you are using Eclipse, a JDWP-compliant debugger is already included, and no setup is required. If you are using another IDE, you can use the debugger that comes with it and attach the debugger to a special port so it can communicate with the application VMs on your devices.
If you are developing in Eclipse with the ADT (Android development tools) plug-in, you can use the built-in Java* Debugger, along with DDMS (Dalvik Debug Monitor Server), to debug your applications. To access the debugger and DDMS, Eclipse displays the debugger and DDMS features as perspectives, which are customized Eclipse views that display certain tabs and windows depending on the perspective that you are in. Eclipse also takes care of starting the ADB (Android debug bridge) host daemon for you, so you do not have to run this manually. If you debug with another IDE, you can take advantage of all the tools that the Android SDK provides for debugging, like ADB, DDMS, Java debugger, etc.
Figure 1. Dalvik Debug Monitor Server
With DDMS, developers can view heap usage for a process, track memory allocation of objects, work with an emulator or the device’s file system, examine thread information, catch method profiling, use the Network Traffic tool (in Android 4.0), use LogCat to trace code messages, and emulate phone operations and location. For more information, please read http://developer.android.com/guide/developing/debugging/ddms.html. The Android SDK also provides the Hierarchy Viewer and layoutopt tools to help developers debug layout issues.
The Hierarchy Viewer application allows you to debug and optimize your user interface (UI). It provides a visual representation of the layout's View hierarchy (the View Hierarchy window) and a magnified view of the display (the Pixel Perfect window).
Figure 2. Hierarchy Viewer
The View Hierarchy window displays the View objects that form the UI of the activity that is running on your device or emulator. You use it to look at individual View objects within the context of the entire View tree. For each View object, the View Hierarchy window also displays rendering performance data. When you select a node, additional information for the View appears in a small window above the node. When you click one of the nodes, you can see information about the image, view count, and render times.
Figure 3. View object information window
Pixel Perfect is a tool for examining pixel properties and laying out UIs from a design drawing. The Pixel Perfect window displays a magnified image of the screen that is currently visible on the emulator or device. In it, you can examine the properties of individual pixels in the screen image. You can also use the Pixel Perfect window to help you lay out your application’s UI based on a bitmap design.
Figure 4. Pixel Perfect window
The layoutopt tool lets you analyze the XML files that define your application's UI to find inefficiencies in the view hierarchy. To run the tool, open a terminal and launch layoutopt <xmlfiles> from your SDK tools/ directory. The <xmlfiles> argument is a space- delimited list of resources you want to analyze, either uncompiled resource XML files or directories of such files. The tool loads the specified XML files and analyzes their definitions and hierarchies according to a set of predefined rules. The following is a sample of the output from the tool:
$ layoutopt samples/ samples/compound.xml 7:23 The root-level <FrameLayout/> can be
replaced with <merge/> 11:21 This LinearLayout layout or its FrameLayout parent is
useless samples/simple.xml 7:7 The root-level <FrameLayout/> can be replaced with
<merge/> samples/too_deep.xml -1:-1 This layout has too many nested layouts: 13
levels, it should have <= 10! 20:81 This LinearLayout layout or its LinearLayout
parent is useless 24:79 This LinearLayout layout or its LinearLayout parent is
useless 28:77 This LinearLayout layout or its LinearLayout parent is useless 32:75
This LinearLayout layout or its LinearLayout parent is useless 36:73 This LinearLayout
layout or its LinearLayout parent is useless 40:71 This LinearLayout layout or its
LinearLayout parent is useless 44:69 This LinearLayout layout or its LinearLayout parent is
useless 48:67 This LinearLayout layout or its LinearLayout parent is useless
52:65 This LinearLayout layout or its LinearLayout parent is useless 56:63 This
LinearLayout layout or its LinearLayout parent is useless samples/too_many.xml 7:413
The root-level <FrameLayout/> can be replaced with <merge/> -1:-1 This
layout has too many views: 81 views, it should have <= 80! samples/useless.xml 7:19
The root-level <FrameLayout/> can be replaced with <merge/> 11:17
This LinearLayout layout or its FrameLayout parent is useless
Traceview is a graphical viewer for execution logs that you create using the
Debug class to log tracing information in your code. Traceview can help you debug your application and profile its performance. Traceview loads the log files and displays their data in a window that visualizes your application in two panels as shown in Figures 5 and 6:
Figure 5. The Timeline panel describes when each thread and method started and stopped
Figure 6. The Profile panel provides a summary of all the time spent in a method.
dmtracedump is a tool that gives you an alternate way of generating graphical call-stack diagrams from trace log files. The tool uses the Graphviz Dot utility to create the graphical output, so you need to install Graphviz before running dmtracedump. The dmtracedump tool generates the call stack data as a tree diagram, with each call represented as a node. It shows call flow (from parent node to child nodes) using arrows. Figure 7 shows an example of dmtracedump output.
Figure 7. dmtracedump
3. NDK app debugging tools
Because the Android NDK is based on the GCC tool chain, the Android NDK includes the GDB, the GNU debugger, that allows you to start, pause, examine, and alter a program. On Android devices and more generally on embedded devices, GDB is configured in client/server mode. The program runs on a device as a server and a remote client. The developer's workstation connects to it and sends debugging commands similar to a local application. GDB itself is a command-line utility and can be cumbersome to use manually. Luckily, GDB is handled by most IDEs and especially CDT. Thus, Eclipse can be used directly to add breakpoints and inspect a program, but only if it has already been properly configured!
Indeed, Eclipse can insert breakpoints easily in Java as well as C/C++ source files by clicking in the left margin of the text editor. Java breakpoints work out of the box thanks to the ADT plug-in, which manages debugging through the Android Debug Bridge. This is not true for CDT, which is, of course, not Android-aware. Thus, inserting a breakpoint will do nothing unless we configure CDT to use the NDK's GDB, which itself needs to be bound to the native Android application in order to debug it. Debugger support has improved in NDK releases (for example, debugging purely native threads was not working before). Although NDK is getting more usable, NDK R5 (and even R7), is far from perfect. But, it can still help! Now let's see how to debug a native application.
First, enable debugging mode in our application by following these steps:
1) An important thing to do, but really easy to forget, is to activate the debugging flag in your Android project. This is done in the application manifest AndroidManifest.xml. Do not forget to use the appropriate SDK version for native code:
="1.0" ="utf-8" <manifest ...> <uses-sdk android:minSdkVersion="10"/> <application ... android:debuggable="true"> ...
2) Enabling the debug flag in manifest automatically activates debug mode in native code. However, the
APP_OPTIM flag also controls debug mode. If it has been manually set in Android.mk, then check that its value is set to debug (and not release) or simply remove it:
APP_OPTIM := debug
3) Now let's configure the GDB client that will connect to the device. Recompile the project and plug your device in or launch the emulator. Run and leave your application. Ensure the application is loaded and its PID available. You can check it by listing processes using the following command (use Cygwin in Windows):
$ adb shell ps |grep gl2jni
One line should be returned:
app_75 13178 1378 201108 68672 ffffffff 80118883 S com.android.gl2jni
4) Open a terminal window and go to your project directory. Run the ndk-gdb command (located in the android NDK folder, for example android-ndk-r8\):
This command should not return a message, but will create three files in the obj\local\x86 directory (obj\local\armeabi directory for arm device):
- gdb.setup: This is a configuration file generated for GDB client.
- app_process: This file is retrieved directly from your device. It is a system executable file, launched when the system starts up and forked to start a new application. GBD needs this reference file to find its marks. In some ways, it is the binary entry point of your app.
- libc.so: This is also retrieved from your device. It is the Android standard C library (commonly referred as bionic) used by GDB to keep track of all the native threads created during runtime.
5) In your project directory, copy obj\local\x86\gdb.setup and name it gdb2.setup. Open it and remove the following line that requests the GDB client to connect to the GDB server running on the device (to be performed by Eclipse itself):
target remote :5039
6) In the Eclipse main menu, go to Run | Debug Configurations... and create a new Debug configuration in the C/C++ application item called GL2JNIActivityDefault. This configuration will start GDB client on your computer and connect to the GDB Server running on the device.
7) In the Main tab, set project to your own project directory, C/C++ application to point to obj\local\ x86\app_process using the Browse button (you can use either an absolute or a relative path).
Figure 8. debug configurations for C/C++ Application
8) Switch the launcher type to Standard Create Process Launcher using the link Select other... at the bottom of the window:
Figure 9. Select Preferred Launcher
9) Go to the debugger file and set the debugger type to gdbserver, set the GDB debugger to android-ndk-r8\toolchains\x86-4.4.3\prebuilt\windows\bin\i686-android-linux-gdb.exe or android-ndk-r8\toolchains\arm-linux-androideabi-4.4.3\prebuilt\linux-x86\bin\arm-linux-androideabi-gdb for arm, set the GDB command file needs to point to the gdb2.setup file located in \obj\local\x86 or obj\local\armeabi\ for arm (you can use either an absolute or a relative path).
Figure 10. Debugger setting panel
10) Go to the Connection tab and set Type to TCP. Keep the default values for Host name or IP address and Port number (localhost d 5039).
Figure 11. Connection setting on Debugger setting panel
11) Now, let's configure Eclipse to run GDB server on the device. Make a copy of android-ndk-r8\ndk-gdb and open it with a text editor. Find the following line:
$GDBCLIENT -x `native_path $GDBSETUP`
Comment it because GDB client is going to be run by Eclipse itself:
#$GDBCLIENT -x `native_path $GDBSETUP`
12) In the Eclipse main menu, go to Run | External Tools | External Tools
Configurations... and create a new configuration GL2JNIActivity_GDB.
This configuration will launch GDB server on the device.
13) On the Main tab, set the Location pointing to our modified ndk-gdb in android-ndk-r8. Set working directory to your application directory location
Optionally, set the Arguments textbox:
- verbose: To see in detail what happens in the Eclipse console.
- force: To automatically kill any previous session.
- start: To let GDB Server start the application instead of getting attached to the application after it has been started. This option is interesting if you debug native code only and not Java.
Figure 12. External Tools Configurations
14) Now, launch your application as usual.
15) Once the application starts, you could launch ndk-gdb by console directly or launch the external tool configuration
GL2JNIActivity_GDB, which is going to start GDB server on the device. GDB server receives debug commands sent by the remote GDB client and debugs your application locally.
16) Open jni\gl_code.cpp and set a breakpoint in setupGraphics by double-clicking on the left margin of the text editor (or right-clicking and selecting Toggle breakpoint ).
Figure 13. Breakpoint setting
17) Finally, launch GL2JNIActivity Default C/C++ application configuration to start GDB client. It relays debug commands from Eclipse CDT to GDB server over a socket connection. From the developer's point of view, this is almost like debugging a local application.
There are also some specific tools for debugging graphics performance, the Intel® GPA System Analyzer is one of the Intel® Graphics Performance Analyzers (Intel® GPA) with new support for Intel-based Android devices, and is intended for application and driver engineers to optimize their OpenGL* ES workloads.
This section provides instructions on how to configure and use Intel GPA with your Android device over a USB connection. When connected to an Android device, the Intel GPA System Analyzer provides OpenGL ES API, CPU, and GPU performance metrics, and also provides multiple graphics pipeline state overrides to aid with your analysis of OpenGL ES application performance.
To use the Intel GPA System Analyzer on Android x86-based devices, you need to check the target machine and firmware/version from the document.
To start collecting metrics you need to install the Intel GPA System Analyzer on the client system and connect it to the target device:
1) Install Intel GPA 2012 R3 on the Windows*/Linux* client machine.
2) Launch the Intel GPA System Analyzer.
3) Make sure that the Android device(s) is connected to the client system using a USB cable.
4) Wait up to 10 seconds while your client system is detecting the target device(s). Found devices appear in the dialog window. The list of the target devices refreshes every 5 to 6 seconds.
5) Find the device you want to connect to and click Connect. The Intel GPA System Analyzer will copy required components to the target device and generate the list of installed applications. You can interrupt the connection process by clicking Stop.
Figure 14. Select connected device
6) Select the desired application from the list of available ones. The Application List screen displays all user and system applications installed on the Android device.
Figure 15. Applications list
7) The application will be launched and you will see its data in the Intel GPA System Analyzer window.
8) To switch to a different application, click Back. Note that the running application will be forced to close.
9) To switch to a different target device, click Back.
PowerVR graphics architecture consists of the following core modules that convert the submitted 3D application data into a rendered image: Tile Accelerator (TA), Image Synthesis Processor (ISP), and the Texture & Shading Processor (TSP). Intel GPA metrics in the “GPU” group correspond to one of these core modules, and the order of metrics in the Metrics List depends on the order of the core modules in the graphics pipeline.
Figure 16. Intel GPA System Analyzer window
Perf is a very useful tool in Linux since 2.6.30, and it can be used for both hardware- and software-related performance analysis. Though Android is built on top of Linux, Android doesn't include perf like it does many other components and libraries. You have to push a statically built perf into it. If you've got one, just put it under /system/bin/, and it can work well. A brief instruction of perf, the basic usage, and a tutorial is provided in https://perf.wiki.kernel.org/index.php/Main_Page.
With traceview, developers can catch performance information of Java code; with perf, developers can get the performance information about native and system-level code as shown in Figure 17.
Figure 17. Performance Statistics
Figure 18. function call stack
UxTune is an engineering tool for Android user interaction analysis and optimization. It is an enhanced pyTimeChart tool.
UxTune design features are:
- Vertical correlation: Map system events across layers to user-level activities, e.g., events, gestures, frames, etc.
- Horizontal correlation: Correlate runtime activities between different system entities, e.g., a thread triggers a garbage collection.
- Visualization based on pyTimeChart.
To use UxTune to analyze responsiveness, the developer needs to be familiar with some important processes (shown as rows in pyTimeChart) of the Android system, namely:
- InputReader row: This row displays all touch events with touch coordinate. And the events will be sent to InputDispatcher.
- InputDispatcher row: InputDispatcher will pack serial touch events and send the package to the application’s uiThread.
- uiThread row: This row displays the head touch event of the received package from InputDispatcher. uiThread will draw(render) its surface according to specific actions. “D” represents the drawing process.
- Surface row: uiThread will lock its Surface in the beginning of drawing and unlock its Surface when drawing done. “S” and “E” represent the start and end of application render.
- SurfaceFlinger row: After the application rendering is done, the application will inform SurfaceFlinger to composite and update the screen. “S” represents that SurfaceFlinger starts to handle the application request, and “E” represents composition done (framebuffers swap finished).
Figure 19. UxTune analysis window
Meter-FPS is a tool to measure the FPS value of the system, which intercepts the graphics processing paths to get the logs of every frame, including other metrics like maximal frame time, frame time variance, #long-time-frames, and frame drop rate. There are two patterns for fps monitor. Real Time Pattern is real-time display fps for all running applications. Measure Pattern measures fps and other parameters during user-defined start and stop times. The device should be rooted when using the tool.
setprop debug.graphic_log 1
Figure 20 shows the configuration interface where developers can configure the tool for their debug target.
20. Meter-FPS configuration
In Real Time pattern, click the monitor button. The fps tool will monitor all running applications, and update fps to the float window on the screen.
Figure 21. Meter-FPS Real Time pattern
In Measure Pattern, click the monitor button. It will display a float window with "Click to start ..."; click the float window to start monitoring.
Figure 22. Meter-FPS Real Measure Pattern-1
Now Measure Pattern is running:
Figure 23. Meter-FPS Real Measure Pattern-1
Click the float window to stop monitoring; it will display the result:
Figure 24. Meter-FPS analysis result list
Click every item in the above list to get detailed logs:
Figure 25. Meter-FPS analysis detail logs
Related Articles and Resources
To learn more about Intel tools for the Android developer, visit Intel® Developer Zone for Android.