Intel for Android* Developers Learning Series #6: Debugging on Android* OS

The challenges and approaches to identifying runtime issues and possible problem code in an Android* OS–targeted application or in the Android* system software stack is going to be remarkably similar, regardless of whether the underlying platform architecture is based on Intel® architecture and the latest generation Intel® Atom™ processor or on ARM* architecture. In this chapter we will provide an overview of the available debug methodologies for Android*-targeted software debug on Intel architecture. We will also touch on the setup and configuration of application and system software debug environments targeting Android* and Intel architecture. In the process of doing so we will also point out where differences to the experience when developing for ARM architecture are.

1. Prerequisites

In this chapter we will cover the Intel® USB Driver that is necessary to enable remote application debugging on an Intel® Atom™ Processor based device running Android* OS.

Additionally we will also look at the Intel® Atom™ x86 System Image for Android Emulator. If no physical debug target is available, having a virtual Intel® Atom™ Processor based device emulated inside the Android* SDKs device emulation layer is the next best option.

These two prerequisites in conjunction with the Android* SDK build the foundation for cross-development targeting an Intel® Atom™ processor based tablet of smartphone.    

1.1. Intel® USB Driver for Android Devices

Let us first look at the Intel® Android USB Driver package, which enables connecting your host development machine to your Android device that contains an Intel Atom™ processor inside. In this first example we assume a Windows* development host. Similar principles apply to Linux* or OS X* host systems.

  • Download the installer package from http://www.intel.com/software/android

  • Run the installer and accept the Windows User Account Control (UAC) prompt, if applicable.

  • You will see the following screen (Figure 1). Click Next to continue. (If the installer detects an older version of the driver, accept to uninstall it.)

Figure 1: USB device driver installation start screen

  • Read and agree to the Intel Android USB Driver End-User License Agreement (EULA).
  • You will be prompted to select components. Click the Next button to proceed
  • Choose the path for the installation and click Install.
  • The installer will proceed to install Android USB drivers. This may take a few minutes to complete (Figure 2).

Figure 2: USB device driver installation progress screen

1. After the driver installation is completed, click OK on the pop-up note and then click on Finish to close the installation program.

1.2. Installing the Intel® Atom™ x86 System Image for Android Emulator

For the alternative of debugging on the development host using the Android* Virtual Device Manager, the first prerequisite is the availability of the appropriate system image.

Move or Protect or Flag

Close

The Intel Android x86 System Image Add-on requires the Android SDK to be installed. Please refer to the Android developer Web site (http://developer.android.com/sdk/installing.html) for Android SDK installation instructions. The Android SDK Manager allows you to download and install the Intel Atom Processor Android x86 Emulator Image Add-on.

Follow the steps below:

1. Start the Android SDK Manager program.

2. Under Packages → Android 4.x.x (API 1x), check the box to select Intel Atom x86 System Image by Intel Corporation.

3. Once selected, click the Install Package button, as shown in Figure 3.

(Note: You may have more than one package to install, based on other packages that you or the Android SDK Manager program preselect.)

Figure 3: Android SDK Manager selection for x86 System Image

1. Review the Intel Corporation license agreement. If you accept the terms, select the Accept option and click the Install button, as shown in Figure 4.

Figure 4: Android SDK Manager – Accepting licensing terms

1. At this point, the Android SDK Manager will download and install the add-on to your Android SDK add-ons folder (<sdk>/add-ons/). The download and install will take several minutes, depending on your connection speed.

2. Select Manage AVDs from the Tools menu (Figure 5).

Figure 5: Android SDK Manager – Manage Android Virtual Devices

1. The Android Virtual Device Manager window should appear. Click New (Figure 6).

Figure 6: Adding new Android* Virtual Device

1. Enter a name for your virtual device in the Name field. Note: spaces are not allowed in the name.

2. Select Intel Atomx86 System Image (Intel Corporation) – API Level 10 from the dropdown list of the Target field (Figure 7).

Figure 7: The Intel Atom x86 System Image as a Virtual Device Target

1. Once you select your configuration settings, click the Create AVD button:

2. The new virtual device should appear on the Android Virtual Device Manager. Select the new device and click the Start button, as shown in Figure 8.

Figure 8: Starting Android* Virtual Device

1. The Launch Options window should appear. Select the screen size and dpi for your system. Otherwise, the emulator might exceed the dimensions of your viewing screen. Click the Launch button (Figure 9).

Figure 9: Virtual Device Launch Options

1. After a few moments, the emulator will launch and show you the screen in Figure 10.

Figure 10: AVD* Emulation of Intel® Architecture based Android* Device

1.3.   Application Debug using the Android Debug Bridge

The Android Debug Bridge (ADB) is a command line tool that handles debug communication between a debugger on the host (usually GDB* or DDMS* (Dalvik* Debug Monitor Server) as well as ADT) and an Android image running on the target. The target image could be running on device emulation or running on a physical development device, which you communicate with via USB-OTG (On-The-Go) or USB-to-Ethernet dongle. In short, ADB is the glue that makes application debug on Android possible.

The device you are connecting to or emulating could cover a wide range of form factors. Typically it would be a smartphone or tablet. It could however just as well be a medical tablet or an embedded device in industry, home energy management, warehousing, or any number of intelligent systems applications.

Setting up the Android Debug Bridge to allow remote debugging of a platform based on the Intel Atom processor does not differ very much from debugging on other architectures.

1.4.  Setting Up ADB

First you will need the Android SDK including ADB installed on the development host. Instructions for this can be found at http://developer.android.com/sdk/installing.html.

If your target image is running on a physical device, the first thing that needs to be done is to include USB-OTG or USB-to-Ethernet support. For USB-to-Ethernet support a kernel configuration change and rebuild is required. Your OEM will provide you with the necessary information if desired

The standard method for remote application debug is to use the existing USB-OTG interface of most Android devices. The setup is described in quite some detail at the Android developer Web site: http://developer.android.com/guide/developing/device.html:

The key steps as outlined there are

1. Declare your application as "debuggable" in your Android Manifest.

2. Turn on "USB Debugging" on your device.

3. On the device, go to Settings > Applications > Development and enable USB debugging (on an Android 4.x.x device, the setting is located in Settings > Developer options).

4. Set up your system to detect your device.

·      If you’re developing on Windows, you need to install a USB driver for ADB—see prerequisites.

·      If you’re developing on Ubuntu* Linux, you need to add a udev rules file that contains a USB configuration for each type of device you want to use for development. In the rules file, each device manufacturer is identified by a unique vendor ID, as specified by the ATTR{idVendor} property. For a list of vendor IDs, see http://developer.android.com/tools
/device.html#VendorIds.

·      To set up device detection on Ubuntu Linux, log in as root and create this file:

/etc/udev/rules.d/51-android.rules

·      Use this format to add each vendor to the file:

SUBSYSTEM=="usb", ATTR{idVendor}=="????", MODE="0666", GROUP="plugdev"

The MODE assignment specifies read/write permissions, and GROUP defines which Unix group owns the device node.

·       Execute:

chmod a+r /etc/udev/rules.d/51-android.rules

·      When plugged in over USB, you can verify that your device is connected by executing ADB devices from your SDK platform-tools/ directory. If connected, you'll see the device name listed as a “device.”

With the Android OS booted on the CDK, connect a USB-OTG cable to the (USB mini b) port on the CDK and connect the other end of the cable (USB A) to your development host.

If everything is working you should be able to run the following command to see the attached device:

$ adb devices

* daemon not running. starting it now *

* daemon started successfully *

List of devices attached

0123456789ABCDEF     device

Note: To see which device name is assigned to this connection on the Linux dev. host you can look at dmesg to find the address of the "usb-storage: device found at <num>" and then do an "ls -l /dev/bus/usb/*" listing to find that number.

1.5.               ADB on Windows

Download and install Eclipse Classic from http://www.eclipse.org/downloads/.

Download the Android SDK package for Windows from http://developer.android.com/sdk/index.html (android-sdk_r18-windows.zip, or installer_r18-windows.exe).

After installing the Android SDK, adb.exe will be located at <install-dir>\android-sdk\platform-tools.

1.6.               ADB Host-Client Communication

Thus far we focused on installing ADB on the development host. In reality it is a client-server program that includes three components:

·      A client, which runs on your development machine. You can invoke a client from a shell by issuing an ADB command. Other Android tools such as the ADT plugin and DDMS also create ADB clients.

·      A server, which runs as a background process on your development machine. The server manages communication between the client and the ADB daemon running on an emulator or device.

·      A daemon, which runs as a background process on each emulator or device instance.

When you start an ADB client, the client first checks whether there is an ADB server process already running. If there isn’t, it starts the server process. When the server starts, it binds to local TCP port 5037 and listens for commands sent from ADB clients—all ADB clients use port 5037 to communicate with the ADB server.

The server then sets up connections to all running emulator/device instances. It locates emulator/device instances by scanning odd-numbered ports in the range 5555 to 5585, the range used by emulators/devices. Where the server finds an ADB daemon, it sets up a connection to that port. Note that each emulator/device instance acquires a pair of sequential ports—an even-numbered port for console connections and an odd-numbered port for ADB connections. For example:

 Emulator 1, console: 5554

 Emulator 1, adb: 5555

 Emulator 2, console: 5556

 Emulator 2, adb: 5557 ...

As shown, the emulator instance connected to ADB on port 5555 is the same as the instance whose console listens on port 5554.

Once the server has set up connections to all emulator instances, you can use ADB commands to control and access those instances. Because the server manages connections to emulator/device instances and handles commands from multiple ADB clients, you can control any emulator/device instance from any client (or from a script).

1.7.  Startup ADB

Type "adb shell". You will get a # sign to indicate connection is successful.

$ adb shell

1.8.               Key ADB Device Commands

The commands listed below help to transfer the debuggee application onto the target device or emulation from the command line. This can be very helpful, especially if no ssh terminal connection is available.

  adb push <local> <remote>    - copy file/dir to device

  adb pull <remote> [<local>]  - copy file/dir from device

  adb sync [ <directory> ]     - copy host->device only if changed

                                 (-l means list but don't copy)

                                 (see 'adb help all')

  adb shell                    - run remote shell interactively

  adb shell <command>          - run remote shell command

  adb emu <command>            - run emulator console command

  adb logcat [ <filter-spec> ] - View device log

  adb forward <local> <remote> - forward socket connections

                                 forward specs are one of:

                                   tcp:<port>

                                   localabstract:<unix domain socket

     name>

                                   localreserved:<unix domain socket

                       name>

localfilesystem:<unix domain socket name>

                                   dev:<character device name>

                                   jdwp:<process pid> (remote only)

  adb jdwp                     - list PIDs of processes hosting a JDWP

     transport

  adb install [-l] [-r] [-s] <file> - push this package file to the

     device and install it

                                 ('-l' means forward-lock the app)

                                 ('-r' means reinstall the app, keeping

     its data)

                                 ('-s' means install on SD card instead

     of internal storage)

  adb uninstall [-k] <package> - remove this app package from device

                                 ('-k' means keep the data and cache

     directories)

More details on ADB setup and usage can be found at http://developer.android.com/guide/developing/
tools/adb.html.

1.9.               Using the Android Debug Tools Plugin for Eclipse

For devices based on Intel architecture, the setup process does not vary significantly from what is described at http://developer.android.com/sdk/eclipse-adt.html#installing.

The Android Debug Tools (ADT) plugin provides full Eclipse IDE integrated application debug for emulators based on Intel architecture as well as target devices. It provides two different debug perspectives with different feature sets.

You can switch between either one as needed and they both provide different strengths when debugging applications.

1.10.  The Debug Perspective in Eclipse

The Debug Perspective in Eclipse gives you access to the following tabs:

·      Debug - Displays previously and currently debugged Android applications and its currently running threads

·      Variables - When breakpoints are set, displays variable values during code execution

·      Breakpoints - Displays a list of the set breakpoints in your application code

·      LogCat - Allows you to view system log messages in real time. The LogCat tab is also available in the DDMS perspective.

You can access the Debug Perspective by clicking Window > Open Perspective > Debug. Refer to the appropriate documentation for the Eclipse debugger for more information.

1.11.  The DDMS Perspective

The DDMS Perspective in Eclipse lets you access all of the features of DDMS from within the Eclipse IDE. The following sections of DDMS are available to you:

Devices           - Shows the list of devices and AVDs that are

              connected to ADB.

Emulator Control  - Lets you carry out device functions.

LogCat            - Lets you view system log messages in real time.

Threads           - Shows currently running threads within a VM.

Heap              - Shows heap usage for a VM.

Allocation Tracker - Shows the memory allocation of objects.

File Explorer     - Lets you explore the device's file system.

1.12.  Application Runtime Environment for Debug

The difference when debugging an Android application targeted for a device based on Intel architecture comes in when setting up the debug target device.

Selecting the target device using the Android Virtual Device Manager that is part of the Android SDK, you go to Window > AVD Manager in the Eclipse IDE’s pulldown menu. There you need to make sure to select Intel Atom (x86) as the EABI target for the OS image and the device emulation (Figure 11).

Figure 11: Selection of a device based on the Intel® Atom™ processor in Android Virtual Device Manager

If you followed the steps outlined at the beginning of the chapter for setting up ADB and establishing a debug bridge to a physical device, you will see a device chooser entry in the Eclipse IDE from which you can pick the target for application deployment and debug.

Otherwise, debugging an Android application targeted for Intel architecture is indeed not different from debugging an Android application that targets ARM architecture.

2.    Intel® Hardware Accelerated Execution Manager

The Intel Hardware Accelerated Execution Manager (Intel® HAXM) is a hardware-assisted virtualization engine (hypervisor) that uses Intel Virtualization Technology (Intel® VT) to speed up Android app emulation on a host machine. In combination with Android x86 emulator images provided by Intel and the official Android SDK Manager, Intel HAXM allows for faster Android emulation on Intel VT–enabled systems.

The x86 Android* 4.0.4 (Ice Cream Sandwich) emulator system image enables you to run an emulation of Android on your development machine. In combination with the Android SDK, you can test out your Android applications on a virtual Android device based on Intel architecture taking full advantage of the underlying Intel architecture and Intel Virtualization Technology.

In order to install the emulator system image, you can use the Android SDK Manager.

Intel HAXM can be installed through the Android SDK Manager (Figure 12). Intel HAXM requires the Android SDK to be installed (version 17 or higher). For more information, refer to the Android developer Web site (http://developer.android.com/sdk/).

Figure 12: Intel® Hardware Accelerated Execution Manager Download

Intel HAXM is available for Linux, Windows, and iOS host. As an example we outline the installation on Ubuntu 64-bit OS below, as this is Google’s main validated and supported platform for Android builds.

Below are the quick steps on how to install, enable KVM on Ubuntu host platform, and start the Intel Android x86 emulator with Intel hardware-assisted virtualization (hypervisor). AVD taking advantage of Intel HAXM runs significantly faster and smoother than without hypervisor.

2.1.               KVM Installation

1. To see if your processor supports hardware virtualization, you can review the output from this command:

$ egrep -c '(vmx|svm)' /proc/cpuinfo

If this command returns 0, your CPU doesn’t support hardware virtualization.

2. Next install CPU checker:

$ sudo apt-get install cpu-checker

3. Now you can check if your CPU supports KVM:

$kvm-ok

a. If you see:

"INFO: Your CPU supports KVM extensions
INFO: /dev/kvm exists
KVM acceleration can be used"

you can run your virtual machine faster with the KVM extensions.

b. If you see:

"INFO: KVM is disabled by your BIOS
HINT: Enter your BIOS setup and enable Virtualization Technology (VT),
and then hard poweroff/poweron your system
KVM acceleration can NOT be used"

 

you need to go to BIOS setup and enable Intel VT.

2.2.               Using a 64-Bit Kernel

Running a 64-bit kernel on the host operating system is recommended but not required. To serve more than 2 GB of RAM for your VMs, you must use a 64-bit kernel. On a 32-bit kernel install, you’ll be limited to 2 GB RAM at maximum for a given VM. Also, a 64-bit system can host both 32-bit and 64-bit guests. A 32-bit system can only host 32-bit guests.

1. To see if your processor is 64-bit, you can run this command:

$ egrep -c ' lm ' /proc/cpuinfo

If 0 is printed, it means that your CPU is not 64-bit. If 1 or higher, it is. Note: lm stands for Long Mode, which equates to a 64-bit CPU.

2. To see whether your running kernel is 64-bit, just issue the following command:

$ uname -m

The return value x86_64 indicates a running 64-bit kernel. If you see i386, i486, i586, or i686, you’re running a 32-bit kernel.

2.3.               Install KVM

To install KVM, follow these steps.

1. For Ubuntu 10.04 or later:

$ sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils

You may ignore the Postfix Configuration request shown in Figure 13 by selecting "No Configuration"

Figure 13: KVM install Postfix configuration setting:

 

1. Next, add your <username> account to the group kvm and libvirtd:

$ sudo adduser your_user_name kvm
$ sudo adduser your_user_name libvirtd

After the installation, you need to login again so that your user account becomes an active member of the kvm and libvirtd user groups. The members of this group can run virtual machines.

2. To verify installation, you can test whether your install has been successful with the following command:

$ sudo virsh -c qemu:///system list

2.4.               Starting the Android Virtual Device

The Android for x86 Intel Emulator (Figure 14) can be started using the following command:

$ <SDK directory>/tools/emulator-x86 -avd Your_AVD_Name -qemu -m 2047 -enable-kvm

with Your_AVD_Name being a name of your choice; '-qemu' provides the options to qemu, and '-m' specifies the amount of memory for the emulated Android (that is, guest). If you use too small a value for your memory, it is possible that performance will suffer because of frequent swapping activities.

Figure 14: AVD running Android in Intel® Architecture Emulation layer

2.5.               Using AVD Manager in Eclipse to Launch a Virtual Device

The following steps are recommended by Google to start debugging an application using AVD from within the Eclipse IDE:

1. In Eclipse, click your Android project folder and then select Run > Run Configurations.

2. In the left panel of the Run Configurations dialog, select your Android project run configuration or create a new configuration.

3. Click the Target tab.

4. Select the Intel architecture–based AVD you created previously.

5. In the Additional Emulator Command Line Options field, enter:

-qemu -m 2047 -enable-kvm

6. Run your Android project using this run configuration.

2.6.    Running Android within Oracle* VirtualBox*

Running a full Android OS image on a desktop PC inside an Oracle VirtualBox virtual machine can be a valuable alternative to KVM and QEMU on Windows host systems, especially for those developers looking to develop and debug native code on Android.

In this section we will share some details on

·      building the Android 4.0.x VirtualBox installer for x86  from the official Google x86 VirtualBox target vbox-x86-eng (included in Android 4.0.1 source tree), 

·      using a Linux 2.6 kernel provided by Intel to add specific features for VirtualBox,

·      and how to use installer.vdi to install Android ICS 4.0 into VirtualBox.

In addition to Google’s AVD emulator supported by Intel Hardware Accelerated Execution Manager (Intel HAXM) technology, Android for x86 VirtualBox is running a true virtualized Intel architecture–based environment. It thus provides another fast, high performance tool for developers and partners for quick application development and testing. Booting Android 4.0.x in VirtualBox on a typical Intel® Core™ i5 host system takes about 20 seconds. Speed, performance, and user experience account for the popularity of VirtualBox among Android developers, especially when targeting platforms based on Intel architecture. Availability of Android tablets and smartphones on the market based on Intel architecture still tends to be somewhat limited, and it can be more convenient to start development using a virtual environment on the development host system instead of relying on USB debug communication. Especially with both the development host and the Android target device based on Intel architecture, this becomes a valid and interesting alternative.

2.7.               Google x86 VirtualBox Build Targets for Android 4.x

If you have been using Google’s Android 4.0.x source repository before, you probably noticed that Google provides an x86 version of the VirtualBox target vbox_x86-eng. Using the lunch command before starting the build, the first three targets that Google provides for Android 4.0.x are:

$ lunch

1. full-eng
2. full_x86-eng
3. vbox_x86-eng

With the vbox_x86-eng (#3) target, application developers and system developers alike can create android_disk.vdi and android installer.vdi packages. These can then be used to run Android 4.x inside VirtualBox for application development and system integration on Windows, Linux, and OS X* platform.

2.7.1.  Downloading the Source Tree and Installing the Repository

To install, initialize, and configure the repository, follow these steps (More details can be found at http://source.android.com/source/downloading.html):

$ mkdir ~/bin
$ PATH=~/bin:$PATH
$ curl https://dl-ssl.google.com/dl/googlesource/git-repo/repo > ~/bin/repo
$ chmod a+x ~/bin/repo
$ mkdir ANDROID_TOP_PATH
$ cd ANDROID_TOP_PATH

To get a list of available branches, (from your Android repository checkout root), use this command:

$ git --git-dir .repo/manifests/.git/ branch -a

Run repo init to obtain a current list of the available repository sub-branches with all the most recent updates:

$ repo init -u https://android.googlesource.com/platform/manifest -b android-4.0.1_r1

 

To use the Gerrit* code-review tool, you will need an email address that is connected with a registered Google account. Make sure this is a live address at which you can receive messages. The Kernel version and Build number will be assigned to your build, and information will be displayed in Android/settings/about phone/ page.

A successful initialization will end with a message stating that Repo is initialized in your working directory. Your client directory should now contain a .repo directory where files such as the manifest will be kept.

To pull down files to your working directory from the repositories as specified in the default manifest, run

$ repo sync

By default, access to the Android source code is anonymous. To protect the servers against excessive usage, each IP address is associated with a quota.

2.7.2. Building a Custom Kernel with Mouse Support

Since Android is meant for touchscreen devices, it doesn’t include support for a mouse pointer by default. Additionally it may not include a driver for hardwired Ethernet support as most Android devices are exclusively using wireless radios for network communication. To add these capabilities you will need to rebuild the kernel with mouse support as well as any additional features that you may require. To do so, follow these steps:

1. Download the Android x86 Emulator Image Add-on through the Android SDK Manager. 

2. Create a new folder and untar kernel_sdk_x86.tar.gz into it to create a folder with the kernel source tree.

3. Switch to the directory that holds your kernel files.

Now that we have the kernel source, the configuration needs to be modified to match the hardware being used as the VirtualBox host system and rebuilt. The menuconfig  graphical user interface provided by the kernel sources will allow to do this conveniently:

$ cp ANDROID_TOP_PATH/your_kernel_path/arch/x86/configs/vbox_defconfig  .config

$ make CC=gcc-4.4 CXX=g++-4.4 ARCH=x86 menuconfig

This will take a few seconds to compile and load.

Once it does, use

  • Up/Down arrows to navigate,
  • enter to select (to expand),
  • ‘y’ (or space) to include.

To enable mouse support navigate to: Device Driver > Input Device Support > Mice.

Note: menuconfig is the application that can be used to check and ensure that all features required to support your application or system integration are available. For application developers, it is however equally important to also test and validate the application on default Android builds. Only then can maximum compatibility to Android devices from multiple different device manufacturers be guaranteed.

Having made the necessary change to the kernel configuration, we can now compile it. It doesn’t take too long, so I picked a low ‘j’ value. It is important to note that if you omit the CC and CCX parameters, that the compile will terminate prematurely (on this setup), without an explicit error, as it will use version 4.6.

$ make CC=gcc-4.4 CXX=g++-4.4 ARCH=x86 –j8

Where the –j parameter provides the number of available cores for compilation. The above example assumes a quad-core system with Intel® Hyper-Threading Technology enabled.

After the build is completed successfully, the last line of the build log will say

Kernel: arch/x86/boot/bzImage is ready

2.7.1.  Add Patched Kernel

The kernel image bzImage needs to be renamed to kernel-vbox and copied to /ANDROID_TOP_PATH/prebuilt/android-x86/kernel/kernel-vbox:

$ cp /ANDROID_TOP_PATH/kernel/arch/x86/boot/bzImage   /ANDROID_TOP_PATH/prebuilt/android-x86/kernel/kernel-vbox

2.7.2.  Reduce Compile Time Using CCACHE

You can greatly reduce the compile time for subsequent compilations by using the compiler cache. To set up a 50 GB cache, do the following:

1. Install CCcache program and create a directory for your ccache

$ sudo apt-get install ccache
$ sudo mkdir /ANDROID_TOP_PATH/ccache
$ sudo chown $LOGNAME  /ANDROID_TOP_PATH/ccache

2. Set up your environment variables for ccache support by modifying ~/.bashrc

$ sudo gedit ~/.bashrc

3. Add:

export CCACHE_DIR=/ANDROID_TOP_PATH/ccache
export USE_CCACHE=1

4. Set the ccache sizes.

$ ccache -F 100000
$ ccache -M 50G

2.7.3.  Build Android 4.0.x with New Kernel

Setting up Environment:

$ /ANDROID_TOP_PATH/> source build/envsetup.sh

For ICS 4.0.1, you will see:

including device/samsung/maguro/vendorsetup.sh
including device/samsung/tuna/vendorsetup.sh
including device/ti/panda/vendorsetup.sh
including sdk/bash_completion/adb.bash

To ensure picking a valid target when using the lunch command, it is convenient to simply do the following:

$ lunch

and pick the desired target from the list:
1. full-eng
2. full_x86-eng
3. vbox_x86-eng
4. full_maguro-userdebug
5. full_tuna-userdebug
6. full_panda-eng

Which would you like? [full-eng]   

Note: make sure you select 3. vbox_x86-eng.

PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.0.1
TARGET_PRODUCT=vbox_x86
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=x86
TARGET_ARCH_VARIANT=x86
HOST_ARCH=x86 HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=ITL41D

Run make

$ make -j8

2.8.               Build the VirtualBox Disk and Android Installer

Finally, build the Android_disk.vdi and installer_vdi together:

$ make android_disk_vdi  installer_vdi  -j8

If the build is successful, you will see the output below in the build log:

Done with VirtualBox bootable disk image -[ out/target/product/vbox_x86/android_disk.vdi ]-
Done with VirtualBox bootable installer image -[ out/target/product/vbox_x86/installer.vdi ]-

 

With the thus created android_disk.vdi, you are now be able to select New > Create New Virtual Machine in VirtualBox and use android_disk.vdi as an existing disk image to base the virtual machine on, that will now automatically boot Android 4.0.x.

Set up of the start parameters for VirtualBox* is straightforward. Go to Settings > About this phone to see your build information (Figure 15):

 

Figure 15: Android 4.0.x build information inside Oracle VirtualBox

2.9.               Using an Android Installer Disk to Create a Large Virtual Partition

Even if the file size in VirtualBox is expanded to ~550 MB, it is still too small to load applications for testing. An Android installer disk is needed to install Android into a much larger partition (preferably larger than 4 GB).

First select Machine > New from the VirtualBox menu bar and use the Create New Virtual Machine Wizard to create a larger disk under the IDE controller configuration entry.

Then in Settings > Storage of your virtual machine, add the installer.vdi as the Primary IDE Slave as shown in Figure 16. Now you can get started to install Android on the larger 4 GB hard disk you just created.

Figure 16: Configuring storage settings for virtual machine

1. Start the emulator

2. Use F12 to get to the BIOS boot menu. Boot to the secondary drive

3. Use grub to select the Install option: 2. Boot from Primary Slave (Figure 17)

Figure 17: Booting installer image inside VirtualBox

When you see “Done processing installer config”, type "reboot"

Note: The first time you install on your target virtual disk, the install may fail. A message will be printed that tells you to run the installer again. Do so by using the following command:

$ installer

After reboot, you can see that your Android is running from the larger disk that you create, and you can safely remove installer.vdi from Storage under the VirtualBox IDE Controller settings.

2.10.               Serial Port

Serial port support in the virtual machine is enabled by default. However, the COM1 serial port needs to be initialized and configured before it can be used. The following instructions will cause VirtualBox to create a named pipe called .vbox_pipe in your home directory. On the command line enter:

$ VBoxManage modifyvm Android --uart1 0x03f8 4

$ VBoxManage modifyvm Android --uartmode1 server /home/user/.vbox_pipe

 

Alternativley from in the VirtualBox GUI, use the Serial Ports tab in the Virtual Machine Settings menu to enable COM1 as a "Host Pipe" and select Create Pipe for it to be created as /home/user/.vbox_pipe.

To connect to this named pipe, use:

$ socat unix-client:$HOME/.vbox_pipe stdout

Note: Environment variables (such as $HOME) might not be understood by VirtualBox, so you will have to specify a full explicit path, such as /home/user/.vbox_pipe.

2.11.  Ethernet

The Ethernet port (eth0) is enabled for DHCP in the image. To connect to it via ADB you will need to look up the DHCP address that has been assigned.

If you are using a bridged Ethernet, you may obtain this address from a shell prompt either from the serial port or from Developer Tools > Terminal Emulator using the command:

$ netcfg

If you are using a Host-only adapter, 'vboxnet0', you should use the address 192.168.56.101.

2.12.  Final Notes

Now you have a VirtualBox image running Android 4.0.x completely built from Google’s official vbox-x86 target (Figure 18).

Figure 18: Android OS fully booted inside VirtualBox virtual machine

3.    Debugging with GDB, the GNU Project Debugger

The Android NDK includes the GDB, the GNU debugger, which 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. Let us first look at its basic usage model before looking at the Eclipse CDT integration as well.

When debugging with GDB, gdbserver running on the device is used to handle debug communication, but you may still be using a an underlying USB-to-Ethernet dongle driver with ADB to handle to communication transport layer on which gdbserver communicates via TCP/IP with GDB running on the development host.

There is a gdbclient application that sets up the debug communication environment and launches gdbserver on the debuggee device.

usage: gdbclient EXECUTABLE :PORT [PROG_PATH]

EXECUTABLE  executable name (default app_process)

PORT        commection port (default :1234)

PROG_PATH   executable full path on target (ex /system/bin/mediaserver)

If PROG_PATH is set, gdclient tries to launch gdbserver and attach it to the running PROG_PATH.

To launch gdbserver explicitly, the following command can be used:

# gdbserver :1234 --attach 269

Attached; pid = 269

Listening on port 1234

 

The step-by-step debug session launch instructions below illustrate how ADB is still underlying the debug communication even if GDB and not ADT or DDMS are used for debug. Let us assume that port 1234 is being used.

Launch process:

gdbserver :1234 /system/bin/executable

or attach to an existing process.

gdbserver :1234 --attach pid

On your workstation, forward port 1234 to the device with adb:

adb forward tcp:1234 tcp:1234

Start a special version of gdb that lives in the “prebuilt” area of the source tree:

prebuilt/Linux/toolchain-eabi-4.x.x/bin/i686-android-linux-gdb (for Linux)

prebuilt/darwin-x86/toolchain-eabi-4.x.x/bin/i686-android-linux-gdb (for Darwin)

If you can’t find either special version of GDB, run find prebuilt –name i686-android-linux-gdbin on your source tree to find and run the latest version. Make sure to use the copy of the executable in the symbols directory, not the primary Android directory, because the one in the primary directory has been stripped of its symbol information.

In GDB, tell GDB where to find the shared libraries that will get loaded:

set solib-absolute-prefix /absolute-source-path/out/target/product/product-name/symbols

set solib-search-path /absolute-source-path/out/target/product/product-name/symbols/system/lib

The path to your source tree is absolute-source-path. Make sure you specify the correct directories—GDB may not tell you if you make a mistake. Connect to the device by issuing the GDB command:

(gdb) target remote :1234

The :1234 tells GDB to connect to the localhost port 1234, which is bridged to the device by ADB.

Now you can start debugging native C/C++ code running on Android with GDB the same way you are used to. If you also have Eclipse installed, which you probably do if you are using the Android SDK for Dalvik*/Java*-based application development, Eclipse and the GDB integration of Eclipse can be used directly to add breakpoints and inspect a program.

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. First, enable debugging mode in our application by following these steps:

1. An important thing to do, but something that is 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:

<?xml version="1.0" encoding="utf-8"?> <manifest ...> <uses-sdk android:minSdkVersion="10"/> <application ... android:debuggable="true"> ...

2. Enabling the debug flag in the 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\):

$ ndk-gdb

This command should not return a message, but will create three files in the obj\local\x86 directory:

·      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):

(gdb) target remote :1234

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 (Figure 19), 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 19: Debug configurations for C/C++ application

1. Switch the launcher type to Standard Create Process Launcher (Figure 20) using the link Select other... at the bottom of the window.

Figure 20: Select Preferred Launcher

1. 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. The GDB command file (Figure 21) needs to point to the gdb2.setup file located in \obj\local\x86 (you can use either an absolute or a relative path).

Figure 21: Debugger Setting Panel

1. Go to the Connection tab (Figure 22) and set Type to TCP. Keep the default values for Host name or IP address and Port number (localhost, 5039).

Figure 22: Connection setting on Debugger setting panel

1. 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 out because GDB client is going to be run by Eclipse itself:

#$GDBCLIENT -x `native_path $GDBSETUP`

2. In the Eclipse main menu, go to Run | External Tools | External Tools | Configurations... (Figure 23), and create a new configuration GL2JNIActivity_GDB. This configuration will launch GDB server on the device.

3. 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 the 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 23: External Tools Configurations

1. Now, launch your application as usual.

2. 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. The GDB server receives debug commands sent by the remote GDB client and debugs your application locally.

3. Open jni\gl_code.cpp and set a breakpoint (Figure 24) in setupGraphics by double-clicking the left margin of the text editor (or right-clicking and selecting Toggle breakpoint).

Figure 24: Setting breakpoints

1. Finally, launch GL2JNIActivity Default C/C++ application configuration to start the GDB client. It relays debug commands from Eclipse CDT to the GDB server over a socket connection. From the developer’s point of view, this is almost like debugging a local application.

3.    The Intel® Graphics Performance Analyzer (Intel® GPA)

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 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 (Figure 25). 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 25: Select connected device

1. Select the desired application from the list of available ones. The Application List (Figure 26) screen displays all user and system applications installed on the Android device.

Figure 26: Applications list

  1. The application will be launched and you will see its data in the Intel GPA System Analyzer window.
  2. To switch to a different application, click Back. Note that the running application will be forced to close.
  3. 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 27).

Figure 27: Intel® GPA System Analyzer window

4.    System Debug of Android OS Running on an Intel® Atom™ Processor

Until now we focused on developing and debugging applications, whether they use Android’s Java* runtime alone or run natively as x86 Intel® Architecture binaries and shared objects.

For the system integrator and device manufacturer it may however very well be necessary to work on the device driver and system software stack layer as well. This is especially true if additional platform specific peripheral device support needs to be implemented or if the first operating system port to a new Intel® Atom™ processor based device is undertaken.

In the following chapters we will look at IEEE 1149.1 (JTAG) standard based debug solutions for this purpose as well as architectural differences between ARM* architecture and Intel® Architecture that may impact system level debugging.

4.1. JTAG Debugging

 For true firmware, OS level system and device driver debug, using a JTAG interface is the most commonly used method in the embedded intelligent systems world. The Joint Test Action Group (JTAG) IEEE 1149.1 standard defines a “Standard Test Access Port and Boundary-Scan Architecture for test access ports used for testing printed circuit boards.” This standard is commonly simply referred to as the JTAG debug interface. From its beginnings as a standard for circuit board testing it has developed into the de facto interface standard for OS independent and OS system level platform debug.

More background information on JTAG and its usage in modern system software stack debugging is available at in the article “JTAG 101; IEEE 1149.x and Software Debug” by Randy Johnson and Stewart Christie (http://download.intel.com/design/intarch/papers/321095.pdf).

From the OEM’s perspective and that of their partner application and driver developers, understanding the interaction between the driver and software stack components running on the different parts of the system-on-chip (SoC) integrated intelligent system or smartphone form factor device is critical for determining platform stability. From a silicon validator’s perspective, the low level software stack provides the test environment that exposes the kind of stress factors the platform will be exposed to in real-world use cases. In short, modern SoCs require understanding the complete package and its complex real-world interactions, not just positive unit test results for individual hardware components. This is the level of insight a JTAG-based system software debug approach can provide. This can be achieved by merging the in-depth hardware awareness JTAG inherently provides with the ability to export state information of the Android OS running on the target.

Especially for device driver debug, it is important to understand both the exact state of the peripheral device on the chipset and the interaction of the device driver with the OS layer and the rest of the software stack.

If you are looking at Android from the perspective of system debugging, looking at device drivers and the OS kernel, it is really just a specialized branch of Linux. Thus it can be treated like any 2.6.3x or higher Linux.

The Intel® Atom™ Processor Z2460 supports IEEE-1149.1 and IEEE-1149.7 (JTAG) Boundary Scan and MPI Parallel Trace Interface (PTI) as well as Branch Trace Storage (BTS), Last Branch Record (LBR) and Architectural Event Trace (AET) based instruction tracing through Intel’s JTAG-compliant eXtended Debug Port (XDP).

Various JTAG vendors offer system debug solutions with Android support including:
• Wind River (http://www.windriver.com/products/JTAG-debugging/)
• Lauterbach (http://www.lauterbach.com)
• Intel (http://software.intel.com - access restrictions may apply)

4.2. Android OS Debugging

What complicates debugging an Android-based platform is that Android usually very aggressively takes advantage of low power idle states and sleep states to optimize for power consumption. Thus the real challenge becomes debugging through low power states and

• either maintaining JTAG functionality through some of the low power states
• or, where this is not possible, reattaching JTAG as soon as the chipset power domain for JTAG is re-enabled.

Many OS level issues on these types of platforms tend to center around power mode changes and sleep/wake-up sequences.

A system debugger, whether debug agent based or using a JTAG device interface, is a very useful tool to help satisfy several of the key objectives of OS development.

The debugger can be used to validate the boot process and to analyze and correct stability issues like runtime errors, segmentation faults, or services not being started correctly during boot.

It can also be used to identify and correct OS configuration issues by providing detailed access and representations of page tables, descriptor tables, and also instruction trace. The combination of instruction trace and memory table access can be a very powerful tool to identify the root causes for stack overflow, memory leak, or even data abort scenarios.

Figure 28 shows the page table translation from physical to virtual memory addresses. With the high level of flexibility that is available on x86 in defining the depth of translation tables and granularity of the addressed memory blocks, this level of easy access and visibility of the memory layout becomes even more important for system development on the OS level.


Figure 28: Logical Address to Linear Address Translation

To translate a logical address into a linear address, the processor does the following:

  1. Uses the offset in the segment selector to locate the segment descriptor for the segment in the GDT or LDT and reads it into the processor. (This step is needed only when a new segment selector is loaded into a segment register.)
  2. Examines the segment descriptor to check the access rights and range of the segment to insure that the segment is accessible and that the offset is within the limits of the segment.
  3. Adds the base address of the segment from the segment descriptor to the offset to form a linear address.
  4. If paging is not used, the processor maps the linear address directly to a physical address (that is, the linear address goes out on the processor’s address bus). If the linear address space is paged, a second level of address translation is used to translate the linear address into a physical address.

When operating in protected mode, the Intel Architecture permits the linear address space to be mapped directly into a large physical memory (for example, 4 GBytes of RAM) or indirectly (using paging) into a smaller physical memory and disk storage. This latter method of mapping the linear address space is commonly referred to as virtual memory or demand-paged virtual memory.
When paging is used, the processor divides the linear address space into fixed-size pages (generally 4 KBytes in length) that can be mapped into physical memory and/or disk storage. When a program (or task) references a logical address in memory, the processor translates the address into a linear address and then uses its paging mechanism to translate the linear address into a corresponding physical address. If the page containing the linear address is not currently in physical memory, the processor generates a page-fault exception (#PF). The exception handler for the page-fault exception typically directs the operating system or executive to load the page from disk storage into physical memory (perhaps writing a different page from physical memory out to disk in the process). When the page has been loaded in physical memory, a return from the exception handler causes the instruction that generated the exception to be restarted. The information that the processor uses to map linear addresses into the physical address space and to generate page-fault exceptions (when necessary) is contained in page directories and page tables stored in memory.
Paging is different from segmentation through its use of fixed-size pages. Unlike segments, which usually are the same size as the code or data structures they hold, pages have a fixed size. If segmentation is the only form of address translation used, a data structure present in physical memory will have all of its parts in memory. If paging is used, a data structure can be partly in memory and partly in disk storage.
To minimize the number of bus cycles required for address translation, the most recently accessed page-directory and page-table entries are cached in the processor in devices called translation lookaside buffers (TLBs). The TLBs satisfy most requests for reading the current page directory and page tables without requiring a bus cycle. Extra bus cycles occur only when the TLBs do not contain a page-table entry, which typically happens when a page has not been accessed for a long time.

This highlights two key differences between developing and configuring the Android OS software stack on Intel architecture and many other architectures. The selector base and offset addressing model, combined with the local descriptor table (LDT) and global descriptor table (GDT) allow for deep, multilayered address translation from physical to virtual memory with variable address chunk granularity as well. This is a powerful capability for custom memory configuration in a compartmentalized environment with protected isolated memory spaces. If used incorrectly it can, however, also increase memory access times. Thus the good visibility of memory page translation is desirable.

One other difference between Intel architecture and others is the handling of system interrupts. On ARM, for instance, you have a predefined set of hardware interrupts in the reserved address space from 0x0 through 0x20. These locations then contain jump instructions to the interrupt handler. On Intel architecture a dedicated hardware interrupt controller is employed. The hardware interrupts are not accessed directly through memory space, but by accessing the Intel® 8529 interrupt controller. The advantage of this approach is that the interrupt handler already allows for direct handling for I/O interrupts for attached devices. In architectures that don’t use a dedicated interrupt controller, usually the IRQ interrupt has be overloaded with a more complex interrupt handler routine to accomplish this.

 

5.1. Device Driver Debugging

A good JTAG debugger solution for OS level debug should furthermore provide visibility of kernel threads and active kernel modules along with other information exported by the kernel. To allow for debugging dynamically loaded services and device drivers, a kernel patch or a kernel module that exports the memory location of a driver’s initialization method and destruction method may be used.

Especially for system configuration and device driver debugging, it is also important to be able to directly access and check the contents of device configuration registers. These registers and their contents can be simply listed with their register hex values or visualized as bitfields as shown in Figure 29. A bitwise visualization makes it easier to catch and understand changes to a device state during debug, while the associated device driver is interacting with it.

Figure 29: Device Register Bitfield View

Analyzing the code after the Android compressed zImage kernel image has been unpacked into memory is possible by simply releasing run control in the debugger until start_kernel is reached. This implies of course that the vmlinux file that contains the kernel symbol information has been loaded. At this point the use of software breakpoints is possible. Prior to this point in the boot process only breakpoint-register–based hardware breakpoints should be used, to avoid the debugger attempting to write breakpoint instructions into uninitialized memory. The operating system is then successfully booted once the idle loop mwait_idle has been reached.

Additionally, if your debug solution provides access to Last Branch Storage (LBR) based instruction trace, this capability can, in conjunction with all the regular run control features of a JTAG debugger, be used to force execution stop at an exception and analyze the execution flow in reverse identifying the root cause for runtime issues.

Last Branch Records can be used to trace code execution from target reset. Since discontinuities in code execution are stored in these MSRs, debuggers can reconstruct executed code by reading the ‘To’ and ‘From’ addresses, access memory between the specific locations, and disassemble the code. The disassembly is usually displayed in a trace GUI in the debugger interface. This may be useful for seeing what code was executed before a  System Management Interrupt (SMI) or other exception if a breakpoint is set on the interrupt.

5.2. Hardware Breakpoints

Just as on ARM architecture, processors based on Intel architecture support breakpoint instructions for software breakpoints as well as hardware breakpoints for data as well as code. On ARM architecture you usually have a set of dedicated registers for breakpoints and data breakpoints (watchpoints). The common implementation tends to provide two of each. When these registers contain a value, the processor checks against accesses to the set memory address by the program counter register or a memory read/write. As soon as the access happens, execution is halted. This is different from software breakpoints in that their execution is halted as soon as a breakpoint instruction is encountered. Since the breakpoint instruction replaces the assembly instruction that would normally be at a given memory address, the execution effectively halts before the instruction that normally would be at the breakpoint location is executed.

The implementation of hardware breakpoints on Intel architecture is very similar to that on ARM, although it is a bit more flexible.

On all Intel Atom Processor cores, there are four DR registers that store addresses, which are compared against the fetched address on the memory bus before (sometimes after) a memory fetch.

You can use all four of these registers to   provide addresses that trigger any of the following debug run control events:

  • 00 – break on instruction execution
  • 01 – break on data write only
  • 10 – Undefined OR (if architecture allows it) break on I/O reads or writes
  • 11 – break on data reads or writes but not instruction fetch

Thus, all four hardware breakpoints can be used to be either breakpoints or watchpoints. Watchpoints can be either Write-Only or Read-Write (or I/O) watchpoints.

5.2.Cross-Debug: Intel® Atom™ Processor and ARM Architecture

Many developers targeting the Intel Atom processor may have experience developing primarily for RISC architectures with fixed instruction length. MIPS and ARM are prime examples of ISAs with a fixed length. In general, the cross-debug usage model between an Intel Atom processor and ARM architecture processor is very similar. Many of the conceptual debug methods and issues are the same.

Developing on an Intel architecture-based development host for an Intel Atom processor target does, however, offer two big advantages, especially when the embedded operating system of choice is a derivative of one of the common standard operating systems like Linux or Windows. The first advantage is the rich ecosystem of performance, power analysis, and debug tools available for the broader software development market on Intel architecture. The second advantage is that debugging functional correctness and multithreading behavior of the application may be accomplished locally. This advantage will be discussed later in the chapter.

There are a few differences between Intel Atom processors and ARM processors that developers should know. These differences are summarized in the next two subsections.

5.3. Variable Length Instructions

The IA-32 and Intel 64 instruction sets have variable instruction length. The impact on the debugger is that it cannot just inspect the code in fixed 32-bit intervals, but must interpret and disassemble the machine instructions of the application based on the context of these instructions; the location of the next instruction depends on the location, size, and correct decoding of the previous. In contrast, on ARM architecture all the debugger needs to monitor is the code sequence that switches from ARM mode to Thumb mode or enhanced Thumb mode and back. Once in a specific mode, all instructions and memory addresses are either 32-bit or 16-bit in size. Firmware developers and device driver developers who need to precisely align calls to specific device registers and may want to rely on understanding the debugger’s memory window printout should understand the potential impact of variable length instructions.

 

5.4. Hardware Interrupts

One other architectural difference that may be relevant when debugging system code is how hardware interrupts are handled. On ARM architecture the exception vectors

  • 0 Reset
  •  1 Abort
  • 2 Data Abort
  •  3 Prefetch Abort
  • 4 Undefined Instruction
  •  5 Interrupt (IRQ)
  •  6 Fast Interrupt (FIRQ)

are mapped from address 0x0 to address 0x20. This memory area is protected and cannot normally be remapped. Commonly, all of the vector locations at 0x0 through 0x20 contain jumps to the memory address where the real exception handler code resides. For the reset vector that implies that at 0x0 will be a jump to the location of the firmware or platform boot code. This approach makes the implementation of hardware interrupts and OS signal handlers less flexible on ARM architecture, but also more standardized. It is easy to trap an interrupt in the debugger by simply setting a hardware breakpoint at the location of the vector in the 0x0 through 0x20 address range.

On Intel architecture a dedicated hardware interrupt controller is employed. The interrupts

  •  0 System timer
  • 1 Keyboard
  • 2 Cascaded second interrupt controller
  •  3 COM2 - serial interface
  •  4 COM1 - serial interface
  • 5 LPT - parallel interface
  •  6 Floppy disk controller
  •  7 Available
  •  8 CMOS real-time clock
  •  9 Sound card
  • 10 Network adapter
  • 11 Available
  • 12 Available
  • 13 Numeric processor
  • 14 IDE -- Hard disk interface
  •  15 IDE -- Hard disk interface

cannot be accessed directly through the processor memory address space, but are handled by accessing the Intel 8259 Interrupt Controller. As can be seen from the list of interrupts, the controller already allows for direct handling of hardware I/O interrupts of attached devices, which are handled through the IRQ interrupt or fast interrupt on an ARM platform. This feature makes the implementation of proper interrupt handling at the operating system level easier on Intel architecture especially for device I/O. The mapping of software exceptions like data aborts or segmentation faults is more flexible on Intel architecture as well and corresponds to an interrupt controller port that is addressed via the Interrupt Descriptor Table (IDT). The mapping of the IDT to the hardware interrupts definable by the software stack. In addition, trapping these exceptions cannot as easily be done from a software stack agnostic debug implementation. In order to trap software events that trigger hardware interrupts on Intel architecture, some knowledge of the OS layer is required. It is necessary to know how the OS signals for these exceptions map to the underlying interrupt controller. Most commonly, even in a system level debugger a memory mapped signal table from the operating system will trap exceptions instead of attempting to trap exceptions directly on the hardware level.

5.5. Single Step

ARM architecture does not have an explicit single step instruction. On Intel architecture, an assembly level single step is commonly implemented in the debugger directly via such an instruction. On ARM, a single instruction step is implemented as a “run until break” command. The debugger is required to do some code inspection to ensure that all possible code paths (especially if stepping away from a branch instruction or such) are covered. From a debugger implementation standpoint this does generate a slight overhead but is not excessive, since this “run until break” implementation will be frequently needed for high level language stepping anyways. Software developers in general should be aware of this difference since this can lead to slightly different stepping behavior.

5.6. Virtual Memory Mapping

The descriptor table and page translation implementation for virtual memory mapping is surprisingly similar, at least conceptually. On Intel architecture, the Global Descriptor Table (GDT) and Local Descriptor Table (LDT) enable nested coarseness adjustments to memory pages are mapped into the virtual address space. Figure 30 illustrates the linear to physical address translation on Intel architecture.

Figure 30: Page Translation on Intel® Architecture

On ARM, the first level and second level page tables define a more direct and at maximum, a one- or two-level–deep page search for virtual memory. Figure 31 shows a sample linear address to physical address translation.

Figure 31: Page Translation on ARM

Intel architecture offers multiple levels of coarseness for the descriptor tables, page tables, 32-bit address space access in real mode, and 64-bit addressing in protected mode that’s dependent on the selector base:offset model. ARM does not employ base:offset in its various modes. On Intel architecture, the page table search can implicitly be deeper. On ARM, the defined set is two page tables. On Intel architecture, the descriptor tables can actually mask nested tables and thus the true depth of a page table run can easily reach twice or three times the depth on ARM.

The page translation mechanism on Intel architecture provides for greater flexibility in the system memory layout and mechanisms used by the OS layer to allocate specific memory chunks as protected blocks for application execution. However, it does add challenges for the developer to have a full overview of the memory virtualization and thus avoid memory leaks and memory access violations (segmentation faults). On a full featured OS with plenty of memory, this issue is less of a concern. Real-time operating systems with more visibility into memory handling may be more exposed to this issue. 

6   Considerations for Intel® Hyper-Threading Technology

From a debugging perspective there is really no practical difference between a physical processor core and a logical core that has been enabled via Intel Hyper-Threading Technology. Enabling hyper-threading occurs as part of the platform initialization process in your BIOS. Therefore, there is no noticeable difference from the application standpoint between a true physical processor core and an additional logical processor core. Since this technology enables concurrent execution of multiple threads, the debugging challenges are similar to true multicore debug. 

7  SoC and Interaction of Heterogeneous Multi-Core

Dozens of software components and hardware components interacting on SoCs increase the amount of time it takes to root-cause issues during debug. Interactions between the different software components are often timing sensitive. When trying to debug a code base with many interactions between components single-stepping through one specific component is usually not a viable option. Traditional printf debugging is also not effective in this context because the debugging changes can adversely affect timing behavior and cause even worse problems (also known as “Heisenbugs”).

7.1. Event Trace Debugging

There are a variety of static software instrumentation based data event tracing technologies that help address this issue. The common principle is that they utilize a small amount of DRAM buffer memory to capture event data as it is being created and then uses some kind of logging mechanism to write the trace result into a log file. Data trace monitoring can be real time by interfacing directly with the trace logging API or can be done offline by using a variety of trace viewers for analyzing more complex software stack component interactions. 

LTTng*, Ftrace* and SVEN* are 3 of the most common such implementations.

Below is a comparison table of  those 3 that are focused primarily on Linux* type operating systems and can thus be made applicable to Android* as well.

Figure 31: Different Data Event Tracing Solutions

Please check out the respective product websites to find out more:

7. Summary

In this article we went over the configuration and installation details of the necessary drivers and debug tools. In addition we highlighted some of the underlying architectural differences that may impact debug, but usually only for those developers who are interested in development very close to the system layer.

As we have seen in our overview over available debug solutions and debug configurations for Android running on systems based on Intel architecture, there is a full set of debug environments available covering the needs of the Java application developer, the native C/C++ code developer, as well as the system software stack developer.

Debugging on Intel architecture is supported with the standard Android SDK and Android NDK tool sets provided by Google. In addition Intel as well as other ecosystem players provide debug solutions that expand on these available debug tools and provide solutions for system software stack debug as well as graphics performance debug.

If you are familiar debugging and developing for Android running on ARM architecture the same debug methods apply for Intel architecture as well. The available debug tools and development tools infrastructure is based on the Android* SDK and extended by solutions from Intel as well as ecosystem partners that are often also familiar from ARM*. Thus there should be few surprises debugging software on an Intel® Atom™ Processor based Android* devices versus Arm* based devices.

8.    References

 

 

 

如需更全面地了解编译器优化,请参阅优化注意事项