• Mterp Interpreter Structure
  • Getting Started With Mterp
  • Testing and Debugging Instrumentation Testing
  • Instrumentation Framework
  • Understanding the am Command
  • All Tests with Default TestRunner behavior
  • Running all Tests Under Single Package
  • Running a Single Test Suite
  • Attaching a debugger to your test
  • New Instrumentation TestRunner
  • Debugging with GDB In this document
  • Just-In-Time Debug Feature
  • Debugging Native Code In this document
  • Crash but no exit...stuck
  • Debugging with tcpdump and other tools
  • Including tcpdump in the build image
  • Real time packet monitoring
  • Other network debugging commands
  • Android Platform Developer's Guide




    Download 1,28 Mb.
    bet10/95
    Sana22.12.2019
    Hajmi1,28 Mb.
    #4580
    1   ...   6   7   8   9   10   11   12   13   ...   95

    Dalvik

    In this document


    • Core Libraries

    • JNI Call Bridge

    • Interpreter

    The Dalvik virtual machine is intended to run on a variety of platforms. The baseline system is expected to be a variant of UNIX (Linux, BSD, Mac OS X) running the GNU C compiler. Little-endian CPUs have been exercised the most heavily, but big-endian systems are explicitly supported.

    There are two general categories of work: porting to a Linux system with a previously unseen CPU architecture, and porting to a different operating system. This document covers the former.


    Core Libraries


    The native code in the core libraries (chiefly dalvik/libcore, but also dalvik/vm/native) is written in C/C++ and is expected to work without modification in a Linux environment. Much of the code comes directly from the Apache Harmony project.

    The core libraries pull in code from many other projects, including OpenSSL, zlib, and ICU. These will also need to be ported before the VM can be used.


    JNI Call Bridge


    Most of the Dalvik VM runtime is written in portable C. The one non-portable component of the runtime is the JNI call bridge. Simply put, this converts an array of integers into function arguments of various types, and calls a function. This must be done according to the C calling conventions for the platform. The task could be as simple as pushing all of the arguments onto the stack, or involve complex rules for register assignment and stack alignment.

    To ease porting to new platforms, the open-source FFI library (Foreign Function Interface) is used when a custom bridge is unavailable. FFI is not as fast as a native implementation, and the optional performance improvements it does offer are not used, so writing a replacement is a good first step.

    The code lives in dalvik/vm/arch/*, with the FFI-based version in the "generic" directory. There are two source files for each architecture. One defines the call bridge itself:

    void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc, const u4* argv, const char* signature, void* func, JValue* pReturn)

    This will invoke a C/C++ function declared:

    return_type func(JNIEnv* pEnv, Object* this [, args])

    or (for a "static" method):

    return_type func(JNIEnv* pEnv, ClassObject* clazz [, args])

    The role of dvmPlatformInvoke is to convert the values in argv into C-style calling conventions, call the method, and then place the return type into pReturn (a union that holds all of the basic JNI types). The code may use the method signature (a DEX "shorty" signature, with one character for the return type and one per argument) to determine how to handle the values.

    The other source file involved here defines a 32-bit "hint". The hint is computed when the method's class is loaded, and passed in as the "argInfo" argument. The hint can be used to avoid scanning the ASCII method signature for things like the return value, total argument size, or inter-argument 64-bit alignment restrictions.


    Interpreter


    The Dalvik runtime includes two interpreters, labeled "portable" and "fast". The portable interpreter is largely contained within a single C function, and should compile on any system that supports gcc. (If you don't have gcc, you may need to disable the "threaded" execution model, which relies on gcc's "goto table" implementation; look for the THREADED_INTERP define.)

    The fast interpreter uses hand-coded assembly fragments. If none are available for the current architecture, the build system will create an interpreter out of C "stubs". The resulting "all stubs" interpreter is quite a bit slower than the portable interpreter, making "fast" something of a misnomer.

    The fast interpreter is enabled by default. On platforms without native support, you may want to switch to the portable interpreter. This can be controlled with the dalvik.vm.execution-mode system property. For example, if you:

    adb shell "echo dalvik.vm.execution-mode = int:portable >> /data/local.prop"

    and reboot, the Android app framework will start the VM with the portable interpreter enabled.

    Mterp Interpreter Structure


    There may be significant performance advantages to rewriting the interpreter core in assembly language, using architecture-specific optimizations. In Dalvik this can be done one instruction at a time.

    The simplest way to implement an interpreter is to have a large "switch" statement. After each instruction is handled, the interpreter returns to the top of the loop, fetches the next instruction, and jumps to the appropriate label.

    An improvement on this is called "threaded" execution. The instruction fetch and dispatch are included at the end of every instruction handler. This makes the interpreter a little larger overall, but you get to avoid the (potentially expensive) branch back to the top of the switch statement.

    Dalvik mterp goes one step further, using a computed goto instead of a goto table. Instead of looking up the address in a table, which requires an extra memory fetch on every instruction, mterp multiplies the opcode number by a fixed value. By default, each handler is allowed 64 bytes of space.

    Not all handlers fit in 64 bytes. Those that don't can have subroutines or simply continue on to additional code outside the basic space. Some of this is handled automatically by Dalvik, but there's no portable way to detect overflow of a 64-byte handler until the VM starts executing.

    The choice of 64 bytes is somewhat arbitrary, but has worked out well for ARM and x86.

    In the course of development it's useful to have C and assembly implementations of each handler, and be able to flip back and forth between them when hunting problems down. In mterp this is relatively straightforward. You can always see the files being fed to the compiler and assembler for your platform by looking in the dalvik/vm/mterp/out directory.

    The interpreter sources live in dalvik/vm/mterp. If you haven't yet, you should read dalvik/vm/mterp/README.txt now.


    Getting Started With Mterp


    Getting started:

    1. Decide on the name of your architecture. For the sake of discussion, let's call it myarch.

    2. Make a copy of dalvik/vm/mterp/config-allstubs to dalvik/vm/mterp/config-myarch.

    3. Create a dalvik/vm/mterp/myarch directory to hold your source files.

    4. Add myarch to the list in dalvik/vm/mterp/rebuild.sh.

    5. Make sure dalvik/vm/Android.mk will find the files for your architecture. If $(TARGET_ARCH) is configured this will happen automatically.

    You now have the basic framework in place. Whenever you make a change, you need to perform two steps: regenerate the mterp output, and build the core VM library. (It's two steps because we didn't want the build system to require Python 2.5. Which, incidentally, you need to have.)

    1. In the dalvik/vm/mterp directory, regenerate the contents of the files in dalvik/vm/mterp/out by executing ./rebuild.sh. Note there are two files, one in C and one in assembly.

    2. In the dalvik directory, regenerate the libdvm.so library with mm. You can also use make libdvm from the top of the tree.

    This will leave you with an updated libdvm.so, which can be pushed out to a device with adb sync or adb push. If you're using the emulator, you need to add make snod (System image, NO Dependency check) to rebuild the system image file. You should not need to do a top-level "make" and rebuild the dependent binaries.

    At this point you have an "all stubs" interpreter. You can see how it works by examining dalvik/vm/mterp/cstubs/entry.c. The code runs in a loop, pulling out the next opcode, and invoking the handler through a function pointer. Each handler takes a "glue" argument that contains all of the useful state.

    Your goal is to replace the entry method, exit method, and each individual instruction with custom implementations. The first thing you need to do is create an entry function that calls the handler for the first instruction. After that, the instructions chain together, so you don't need a loop. (Look at the ARM or x86 implementation to see how they work.)

    Once you have that, you need something to jump to. You can't branch directly to the C stub because it's expecting to be called with a "glue" argument and then return. We need a C stub "wrapper" that does the setup and jumps directly to the next handler. We write this in assembly and then add it to the config file definition.

    To see how this works, create a file called dalvik/vm/mterp/myarch/stub.S that contains one line:

    /* stub for ${opcode} */

    Then, in dalvik/vm/mterp/config-myarch, add this below the handler-size directive:

    # source for the instruction table stub

    asm-stub myarch/stub.S

    Regenerate the sources with ./rebuild.sh, and take a look inside dalvik/vm/mterp/out/InterpAsm-myarch.S. You should see 256 copies of the stub function in a single large block after the dvmAsmInstructionStart label. The stub.S code will be used anywhere you don't provide an assembly implementation.

    Note that each block begins with a .balign 64 directive. This is what pads each handler out to 64 bytes. Note also that the ${opcode} text changed into an opcode name, which should be used to call the C implementation (dvmMterp_${opcode}).

    The actual contents of stub.S are up to you to define. See entry.S and stub.S in the armv5te or x86 directories for working examples.

    If you're working on a variation of an existing architecture, you may be able to use most of the existing code and just provide replacements for a few instructions. Look at the armv4t implementation as an example.

    Replacing Stubs


    There are roughly 230 Dalvik opcodes, including some that are inserted by dexopt and aren't described in the Dalvik bytecode documentation. Each one must perform the appropriate actions, fetch the next opcode, and branch to the next handler. The actions performed by the assembly version must exactly match those performed by the C version (in dalvik/vm/mterp/c/OP_*).

    It is possible to customize the set of "optimized" instructions for your platform. This is possible because optimized DEX files are not expected to work on multiple devices. Adding, removing, or redefining instructions is beyond the scope of this document, and for simplicity it's best to stick with the basic set defined by the portable interpreter.

    Once you have written a handler that looks like it should work, add it to the config file. For example, suppose we have a working version of OP_NOP. For demonstration purposes, fake it for now by putting this into dalvik/vm/mterp/myarch/OP_NOP.S:

    /* This is my NOP handler */

    Then, in the op-start section of config-myarch, add:

    op OP_NOP myarch

    This tells the generation script to use the assembly version from the myarch directory instead of the C version from the c directory.

    Execute ./rebuild.sh. Look at InterpAsm-myarch.S and InterpC-myarch.c in the out directory. You will see that the OP_NOP stub wrapper has been replaced with our new code in the assembly file, and the C stub implementation is no longer included.

    As you implement instructions, the C version and corresponding stub wrapper will disappear from the output files. Eventually you will have a 100% assembly interpreter.

    Interpreter Switching


    The Dalvik VM actually includes a third interpreter implementation: the debug interpreter. This is a variation of the portable interpreter that includes support for debugging and profiling.

    When a debugger attaches, or a profiling feature is enabled, the VM will switch interpreters at a convenient point. This is done at the same time as the GC safe point check: on a backward branch, a method return, or an exception throw. Similarly, when the debugger detaches or profiling is discontinued, execution transfers back to the "fast" or "portable" interpreter.

    Your entry function needs to test the "entryPoint" value in the "glue" pointer to determine where execution should begin. Your exit function will need to return a boolean that indicates whether the interpreter is exiting (because we reached the "bottom" of a thread stack) or wants to switch to the other implementation.

    See the entry.S file in x86 or armv5te for examples.


    Testing


    A number of VM tests can be found in dalvik/tests. The most useful during interpreter development is 003-omnibus-opcodes, which tests many different instructions.

    The basic invocation is:

    $ cd dalvik/tests

    $ ./run-test 003

    This will run test 003 on an attached device or emulator. You can run the test against your desktop VM by specifying --reference if you suspect the test may be faulty. You can also use --portable and --fast to explictly specify one Dalvik interpreter or the other.

    Some instructions are replaced by dexopt, notably when "quickening" field accesses and method invocations. To ensure that you are testing the basic form of the instruction, add the --no-optimize option.

    There is no in-built instruction tracing mechanism. If you want to know for sure that your implementation of an opcode handler is being used, the easiest approach is to insert a "printf" call. For an example, look at common_squeak in dalvik/vm/mterp/armv5te/footer.S.

    At some point you need to ensure that debuggers and profiling work with your interpreter. The easiest way to do this is to simply connect a debugger or toggle profiling. (A future test suite may include some tests for this.)


    Testing and Debugging

    Instrumentation Testing

    In this document


    • Instrumentation Framework

    • Platform Test Suites

    • Running Tests

    • Writing Tests

    • Troubleshooting

    This document describes how to use the Instrumentation Framework to write test cases. Instrumentation testing allows you to verify a particular feature or behavior with an automated JUnit TestCase. You can launch activities and providers within an application, send key events, and make assertions about various UI elements.

    You should have a working knowledge of the following:



    • Android Application Framework

    • Using adb, am and various logging functionality

    • A brief understanding of the application of interest, that is, the names of the classes which handle the intents etc.

    • JUnit testing.

    Each Android application runs in its own process. Instrumentation kills the application process and restarts the process with Instrumentation. Instrumentation gives a handle to the application context used to poke around the application to validate test assertions, allowing you to write test cases to test applications at a much lower level than UI screen shot tests. Note that Instrumentation cannot catch UI bugs.

    Instrumentation Framework

    Classes


    The following classes help glue together Instrumentation with JUnit testing.

    Class

    Description

    InstrumentationTestCase

    This extends the standard JUnit TestCase and offers access to an Instrumentation class. Write tests inside your instrumentation class any way you see fit. For example, your test might launch activities and send key events. For this to work properly, the instrumentation needs to be injected into the test case.

    InstrumentationTestRunner

    The instrumentation test runner is an instrumentation that runs instrumentation test cases and injects itself into each test case. Instrumentation test cases need to be grouped together with an instrumentation test runner with the appropriate target package.

    InstrumentationTestSuite

    The instrumentation test suite is a simple extension of the standard JUnit TestSuite that keeps a member Instrumentation variable on hand to inject into each TestCase before running them. It is used by InstrumentationTestRunner.

    Three additional base classes extend InstrumentationTestCase to allow you to test Activity and Provider classes:

    Class

    Description

    ActivityTestCase

    This class can be used to write tests for a specific activity. An activity is launched in its setUp() method and finished with tearDown. If you write a test case that extends ActivityTestCase, you can write tests that access the activity using getActivity() and assume it has been set up properly.

    ServiceTestCase

    This test case provides a framework in which you can test Service classes in a controlled environment. It provides basic support for the lifecycle of a Service, and hooks by which you can inject various dependencies and control the environment in which your Service is tested.

    SingleLaunchActivityTestCase

    This class is similar to ActivityTestCase except that the activity is launched once per class instead of every time the test case calls setup.

    ProviderTestCase

    This class is similar to ActivityTestCase except that it will setup, tear down, and provide access to the Provider of your choice.

    Understanding the am Command


    The am command is a command-line interface to the ActivityManager (see http://code.google.com/android/reference/android/app/ActivityManager.html for details). am is used to start and instrument activities using the adb shell command, as shown in the snippet below:

    > adb shell am

    usage: am [start|instrument]

    am start [-a ] [-d ] [-t ]

    [-c [-c ] ...]

    [-e [-e ...]

    [-n ] [-D] []

    am instrument [-e ] [-p


    ]

    [-w]

    For example, to start the Contacts application you can use

    > adb shell am start -n com.google.android.contacts/.ContactsActivity


    Platform Test Suites


    This section provides an overview for various unit and functional test cases that can be executed through the instrumentation framework.

    Framework Tests


    Framework test cases test the Android application framework or specific Android application functionality that requires an Android runtime context. These tests can be found in //device/tests and //device/apps/AndroidTests.

    Core Library


    Core library test cases test the Android library functionality that does not require an Android runtime context. These tests are split into Android library (android.* package space) tests at //device/java/tests and Java library (java.*, javax.*, etc. packages) tests at //device/dalvik/libcore/.../tests.

    Running Tests


    Each instrumentation test case is similar to an Android application with the distinction that it starts another application. For example, have a look in the tests/Contacts directory.

    • There should be a Makefile and an Android Manifest file.

    • Tests are located in tests/Contacts/src/com/google/android/contactstests.

    • The Instrumentation Test Runner is located at tests/Contacts/src/com/google/android/contactstests/functional/ContactsInstrumentationTestRunner.java.

    Suppose you have a makefile with Contactstests as the target.

    • make Contactstests: Compiles the test cases.

    • adb install Contactstests.apk: Installs the apk on the device.

    • Use the adb shell am command to run them.

    To run your tests, use the am instrument command with your InstrumentationTestRunner as its argument. Results are printed as a result of the instrumentation. For example, the following snippet displays the output after running the framework tests with one test failing (note the unusual syntax caused by how instrumentations are run via am):

    $ adb shell am instrument -w com.google.android.frameworktest/.tests.FrameworkInstrumentationTestRunner

    INSTRUMENTATION_RESULT: test results:=.......F.......

    Time: 6.837

    There was 1 failure:

    1) testSetUpConditions(com.google.android.frameworktest.tests.focus.RequestFocusTest)junit.framework.AssertionFailedError: requestFocus() should work from onCreate.

    at com.google.android.frameworktest.tests.focus.RequestFocusTest.testSetUpConditions(RequestFocusTest.java:66)

    at java.lang.reflect.Method.invokeNative(Native Method)

    at android.test.InstrumentationTestSuite.runTest(InstrumentationTestSuite.java:73)

    at android.test.InstrumentationTestSuite.runTest(InstrumentationTestSuite.java:73)

    at android.test.InstrumentationTestRunner.onStart(InstrumentationTestRunner.java:151)

    at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1088)


    FAILURES!!!

    Tests run: 14, Failures: 1, Errors: 0


    to continue
    INSTRUMENTATION_CODE: -1

    $

    All Tests with Default TestRunner behavior


    If no class or package is passed in to run, InstrumentationTestRunner will automatically find and run all tests under the package of the test application (as defined by the android:targetPackage attribute of the instrumentation defined in its manifest file).

    $ adb shell am instrument -w \

    com.android.samples.tests/android.test.InstrumentationTestRunner

    INSTRUMENTATION_RESULT: Test results for InstrumentationTestRunner=..........

    Time: 2.317

    OK (10 tests)

    INSTRUMENTATION_CODE: -1

    Running all Tests Under Single Package


    If you have many tests under one package, use the -e package
    option to run all tests under that package without having to manually create a test suite.

    $ adb shell am instrument -w \

    -e package com.android.samples.view \

    com.android.samples.tests/android.test.InstrumentationTestRunner

    INSTRUMENTATION_RESULT: Test results for InstrumentationTestRunner=........

    Time: 1.587

    OK (8 tests)

    Running a Single Test Suite


    If you prefer to explicitly state which tests comprise all of your tests, you can define a test suite and run that directly. By convention, all test packages in your system should have at least one suite called AllTests (see AllTests.java). To run all of the tests using the AllTests suite for the api demos test app:

    $ adb shell am instrument -w \

    -e class com.android.samples.AllTests \

    com.android.samples.tests/android.test.InstrumentationTestRunner

    INSTRUMENTATION_RESULT: Test results for AllTests=..........

    Time: 2.286

    OK (10 tests)

    INSTRUMENTATION_CODE: -1


    A Single Test Case


    $ adb shell am instrument -w \

    -e class com.android.samples.view.Focus2ActivityTest \

    com.android.samples.tests/android.test.InstrumentationTestRunner

    INSTRUMENTATION_RESULT: Test results for Focus2ActivityTest=....

    Time: 1.359

    OK (4 tests)

    INSTRUMENTATION_CODE: -1


    A Single Test


    $ adb shell am instrument -w \

    -e class com.android.samples.view.Focus2ActivityTest#testGoingLeftFromRightButtonGoesToCenter \

    com.android.samples.tests/android.test.InstrumentationTestRunner

    INSTRUMENTATION_RESULT: Test results for Focus2ActivityTest=.

    Time: 0.51

    OK (1 test)

    INSTRUMENTATION_CODE: -1


    Attaching a debugger to your test


    In order to debug your test code, instruct the controller to stop and wait for the debugger by adding -e debug true to your command line. This causes the test runner to stop and wait for the debugger just before calling your setUp() method. For example,

    $ adb shell am instrument -w \

    -e debug true \

    com.android.samples.tests/android.test.InstrumentationTestRunner


    Writing Tests


    When writing tests, refer to the ApiDemos tests as models (located at //device/samples/ApiDemos). This section provides an overview of the test structure with ApiDemos.

    Location of Files


    Test packages should use the following structure and include Android.mk, AndroidManifest.xml, AllTests.java, and a src directory that mirrors the src directory of the tested application.

    Files are located within a tests directory found in the root directory:

    $ find samples/ApiDemos/tests

    samples/ApiDemos/tests

    samples/ApiDemos/tests/Android.mk

    samples/ApiDemos/tests/AndroidManifest.xml

    samples/ApiDemos/tests/src

    samples/ApiDemos/tests/src/com

    samples/ApiDemos/tests/src/com/google

    samples/ApiDemos/tests/src/com/google/android

    samples/ApiDemos/tests/src/com/google/android/samples

    samples/ApiDemos/tests/src/com/google/android/samples/AllTests.java

    samples/ApiDemos/tests/src/com/google/android/samples/ApiDemosTest.java

    samples/ApiDemos/tests/src/com/google/android/samples/os

    samples/ApiDemos/tests/src/com/google/android/samples/os/MorseCodeConverterTest.java

    samples/ApiDemos/tests/src/com/google/android/samples/view

    samples/ApiDemos/tests/src/com/google/android/samples/view/Focus2ActivityTest.java

    samples/ApiDemos/tests/src/com/google/android/samples/view/Focus2AndroidTest.java


    Contents of makefile


    The contents of the makefile are similar to a normal application with the addition of a LOCAL_INSTRUMENTATION_FOR declaration.

    # Add appropriate copyright banner here

    LOCAL_PATH:= $(call my-dir)

    include $(CLEAR_VARS)

    # We only want this apk build for tests.

    LOCAL_MODULE_TAGS := tests

    # Include all test java files.

    LOCAL_SRC_FILES := $(call all-java-files-under, src)

    # Notice that we don't have to include the src files of ApiDemos because, by

    # running the tests using an instrumentation targeting ApiDemos, we

    # automatically get all of its classes loaded into our environment.

    LOCAL_PACKAGE_NAME := ApiDemosTests

    LOCAL_INSTRUMENTATION_FOR := ApiDemos

    include $(BUILD_PACKAGE)


    Content of Manifest


    Use the following example to create an AndroidManifest.xml file that declares the instrumentation. Specify that the framework supplied Instrumentation TestRunner targest the package of your application, allowing the tests that are run with the instrumentation to get access to all of the classes of your application without having to build the source into the test app. The name of the test application is typically the same as your target application with .tests appended.

    # Add appropriate copyright banner here



    package="com.android.samples.tests">



    This declares that this app uses the instrumentation test runner targeting

    the package of com.android.samples. To run the tests use the command:

    "adb shell am instrument -w com.android.samples.tests/android.test.InstrumentationTestRunner"

    -->

    android:targetPackage="com.android.samples"

    android:label="Tests for Api Demos."/>

     

    The following snippet will prefix the /android.test.InstrumentationTestRunner when running tests from the command line:



    $ adb shell am instrument -w \

    com.android.samples.tests/android.test.InstrumentationTestRunner


    New Instrumentation TestRunner


    Create a class that derives from this class. You must override two abstract methods; one that returns the class loader of the target package, and another that defines all of the tests within the package. For example, the snippet below displays the test runner for the framework tests.

    public class FrameworkInstrumentationTestRunner extends InstrumentationTestRunner {




    Debugging with GDB

    In this document


    • Debugging

    • Just-In-Time Debug Feature

    The current version of envsetup.sh has a gdbclient command that handles much of the setup. For example, to attach the already-running globaltime application, execute the following, making sure that: 1) you do this from the same window used to build the software on the device you are debugging and 2) verify that the symbols in the object files in the build tree match up with what is installed on the device or emulator.

    gdbclient app_process :5039 globaltime


    Debugging

    Short Instructions


    Android runs gdbserver on the device and an ARM aware gdb, named arm-eabi-gdb, on the desktop machine.

    1. First you need to run gdbserver on the device:

    2. gdbserver :5039 /system/bin/executable

    The :5039 tells gdbserver to listen on port 5039 on the localhost, which adb bridges from the host to the device. executable represents the command to debug, a common one being runtime -s which starts the entire system all running in a single process.



    1. Launch gdb on the desktop. This can be done easily with the following command in the shell from which you built:

    2. gdbclient executable

    At this point gdb will connect with your device and you should be able to enter c to have the device start executing inside of the desktop gdb session.

    Detailed Instructions


    If the short instructions don't work, these detailed instructions should:

    1. On the device, launch a new command:

    gdbserver :5039 /system/bin/executable

    or attach to an existing process:

    gdbserver :5039 --attach pid


    1. On your workstation, forward port 5039 to the device with adb:

    adb forward tcp:5039 tcp:5039

    1. Start a special version of gdb that lives in the "prebuilt" area of the source tree:
      prebuilt/Linux/toolchain-eabi-4.2.1/bin/arm-eabi-gdb (for Linux)
      prebuilt/darwin-x86/toolchain-eabi-4.2.1/bin/arm-eabi-gdb (for Darwin)

    2. If you can't find either special version of gdb, run find prebuilt -name arm-eabi-gdb in your source tree to find and run the latest version:

    3. prebuilt/Linux/toolchain-eabi-4.2.1/bin/arm-eabi-gdb out/target/product/product-name/symbols/system/bin/executable

    Where product-name is the name of the device product that you're building (for example, sooner), and executable is the program to debug (usually app_process for an application).

    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 debugging information.


    1. In gdb, Tell gdb where to find the shared libraries that will get loaded:

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

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


    absolute-source-path is the path to your source tree; for example, /work/device or /Users/hoser/android/device.
    product-name is the same as above; for example, sooner.

    Make sure you specify the correct directories—gdb may not tell you if you make a mistake.



    1. Connect to the device by issuing the gdb command:

    2. target remote :5039

    The :5039 tells gdb to connect to the localhost port 5039, which is bridged to the device by adb.

    You may need to inspire gdb to load some symbols by typing:

    shared


    You should be connected and able to debug as you normally would. You can ignore the error about not finding the location for the thread creation breakpoint. It will be found when the linker loads libc into your process before hitting main(). Also note that the gdb remote protocol doesn't have a way for the device to tell the host about newly created threads so you will not always see notifications about newly created threads. Info about other threads will be queried from the device when a breakpoint is hit or you ask for it by running info thread.

    Just-In-Time Debug Feature


    If you see the red LED flashing it means a process is in that new state (crashed and waiting for GDB connection). If this happens to the system process, most likely your device will be frozen at this point. Do not press the home key. Bring the device to someone who can debug native crashes and ask for advice. If you're in the field and just want your device to continue as it would have without this feature (like cylonning), press home (a tombstone will be recorded as usual). To enable a process to be debugged this way, you need to set a property:

    adb shell setprop debug.db.uid 10000

    and all processes with a uid <= 10000 will be trapped in this manner. When one of them crashes, the tombstone is processed as usual, an explicit message is printed into the log, and the red LED starts flashing waiting for the Home key to be depressed (in which case it continues execution as usual).

    I/DEBUG ( 27): ********************************************************

    I/DEBUG ( 27): * process 82 crashed. debuggerd waiting for gdbserver

    I/DEBUG ( 27): *

    I/DEBUG ( 27): * adb shell gdbserver :port --attach 82 &

    I/DEBUG ( 27): *

    I/DEBUG ( 27): * and press the HOME key.

    I/DEBUG ( 27): ********************************************************

    When you see the entry above, make sure adb is forwarding port 5039 (you only need to do this once, unless the ADB server dies) and execute:

    % adb forward tcp:5039 tcp:5039

    Execute the line shown in the debug output, substituting 5039 for the proper port:

    % adb shell gdbserver :5039 --attach 82 &

    If the crashing process is based off zygote (that is, system_server and all applications), the default values for the gdbclient command, app_process binary and port 5039, are correct, so you can execute:

    % cd

    % gdbclient

    Otherwise you need to determine the path of the crashing binary and follow the steps as mentioned above (for example, gdbclient hoser :5039 if the hoser command has failed).


    Debugging Native Code

    In this document

    Capturing logs


    To capture log output:

    1. Produce a process list with ps (ps -t if you want verbose thread feedback).

    2. Dump kernel messages with dmesg.

    3. Get verbose log messages with logcat '*:v' & (running in bg with & is important).

    Debug Scenarios


    # command to device shell (via adb)

    % command to host pc shell


    Crash but no exit...stuck


    In this scenario, the GTalk app crashed but did not actually exit or seems stuck. Check the debug logs to see if there is anything unusual:

    # logcat &


    ...

    E/WindowManager( 182): Window client android.util.BinderProxy@4089f948 has died!! Removing window.

    W/WindowManager( 182): **** WINDOW CLIENT android.view.WindowProxy@40882248 DIED!

    W/ActivityManager( 182): **** APPLICATION com.google.android.gtalk DIED!

    I/ServiceManager( 257): Executing: /android/bin/app_process (link=/tmp/android-servicemanager/com.google.android.gtalk, wrapper=/tmp/android-servi

    cemanager/com.google.android.gtalk)

    I/appproc ( 257): App process is starting with pid=257, class=android/activity/ActivityThread.

    I/ ( 257): java.io.FileDescriptor: class initialization

    I/SurfaceFlinger.HW( 182): About to give-up screen

    I/SurfaceFlinger.HW( 182): screen given-up

    I/SurfaceFlinger.HW( 182): Screen about to return

    I/SurfaceFlinger.HW( 182): screen returned

    I/SurfaceFlinger.HW( 182): About to give-up screen

    I/SurfaceFlinger.HW( 182): screen given-up

    I/SurfaceFlinger.HW( 182): Screen about to return

    ...


    The logs indicate that the system launched a replacement GTalk process but that it got stuck somehow:

    # ps


    PID PPID VSIZE RSS WCHAN PC NAME

    257 181 45780 5292 ffffffff 53030cb4 S com.google.andr

    GTalk's PC is at 53030cb4. Look at the memory map to find out what lib is 0x53......

    # cat /proc/257/maps

    ...

    51000000-5107c000 rwxp 00000000 1f:03 619 /android/lib/libutils.so



    52000000-52013000 rwxp 00000000 1f:03 639 /android/lib/libz.so

    53000000-53039000 rwxp 00000000 1f:03 668 /android/lib/libc.so

    53039000-53042000 rw-p 53039000 00:00 0

    54000000-54002000 rwxp 00000000 1f:03 658 /android/lib/libstdc++.so

    ...

    Disassemble libc to figure out what is going on:



    % prebuilt/Linux/toolchain-eabi-4.2.1/bin/arm-elf-objdump -d out/target/product/sooner/symbols/android/lib/libc.so
    00030ca4 <__futex_wait>:

    30ca4: e1a03002 mov r3, r2

    30ca8: e1a02001 mov r2, r1

    30cac: e3a01000 mov r1, #0 ; 0x0

    30cb0: ef9000f0 swi 0x009000f0

    30cb4: e12fff1e bx lr


    Blocked in a syscall


    In this scenario, the system is blocked in a syscall. To debug using gdb, first tell adb to forward the gdb port:
    % adb forward tcp:5039 tcp:5039

    Start the gdb server and attach to process 257 (as demonstrated in the previous example):

    # gdbserver :5039 --attach 257 &

    Attached; pid = 257

    Listening on port 5039
    % prebuilt/Linux/toolchain-eabi-4.2.1/bin/arm-elf-gdb out/target/product/sooner/system/bin/app_process

    (gdb) set solib-absolute-prefix /work/android/device/out/target/product/sooner/symbols

    (gdb) set solib-search-path /work/android/device/out/target/product/sooner/symbols/android/lib

    (gdb) target remote :5039

    Remote debugging using :5039

    0x53030cb4 in ?? ()

    Current language: auto; currently asm

    Don't let other threads get scheduled while we're debugging. You should "set scheduler-locking off" before issuing a "continue", or else your thread may get stuck on a futex or other spinlock because no other thread can release it.

    (gdb) set scheduler-locking on

    Ignore SIGUSR1 if you're using JamVM. Shouldn't hurt if you're not.

    (gdb) handle SIGUSR1 noprint
    (gdb) where

    #0 __futex_wait () at system/klibc/android/atomics_arm.S:88

    #1 0x53010eb8 in pthread_cond_timedwait (cond=0x12081c, mutex=0x120818, abstime=0xffffffff)

    at system/klibc/android/pthread.c:490

    #2 0x6b01c848 in monitorWait (mon=0x120818, self=0x6b039ba4, ms=0, ns=0) at extlibs/jamvm-1.4.1/src/lock.c:194

    #3 0x6b01d1d8 in objectWait (obj=0x408091c0, ms=0, ns=0) at extlibs/jamvm-1.4.1/src/lock.c:420

    #4 0x6b01d4c8 in jamWait (clazz=0xfffffffc, mb=0x0, ostack=0x2e188) at extlibs/jamvm-1.4.1/src/natives.c:91

    #5 0x6b013b2c in resolveNativeWrapper (clazz=0x408001d0, mb=0x41798, ostack=0x2e188) at extlibs/jamvm-1.4.1/src/dll.c:236

    #6 0x6b015c04 in executeJava () at extlibs/jamvm-1.4.1/src/interp.c:2614

    #7 0x6b01471c in executeMethodVaList (ob=0x0, clazz=0x40808f20, mb=0x12563c, jargs=0xbe9229f4)

    at extlibs/jamvm-1.4.1/src/execute.c:91

    #8 0x6b01bcd0 in Jam_CallStaticVoidMethod (env=0xfffffffc, klass=0x0, methodID=0x12563c)

    at extlibs/jamvm-1.4.1/src/jni.c:1063

    #9 0x58025b2c in android::AndroidRuntime::callStatic (this=0xfffffffc,

    className=0xbe922f0a "android/activity/ActivityThread", methodName=0x57000b7c "main")

    at libs/android_runtime/AndroidRuntime.cpp:215

    #10 0x57000504 in android::app_init (className=0xbe922f0a "android/activity/ActivityThread")

    at servers/app/library/app_init.cpp:20

    #11 0x000089b0 in android::sp::~sp ()

    #12 0x000089b0 in android::sp::~sp ()

    Previous frame identical to this frame (corrupt stack?)
    (gdb) info threads

    7 thread 263 __ioctl () at system/klibc/syscalls/__ioctl.S:12

    6 thread 262 accept () at system/klibc/syscalls/accept.S:12

    5 thread 261 __futex_wait () at system/klibc/android/atomics_arm.S:88

    4 thread 260 __futex_wait () at system/klibc/android/atomics_arm.S:88

    3 thread 259 __futex_wait () at system/klibc/android/atomics_arm.S:88

    2 thread 258 __sigsuspend () at system/klibc/syscalls/__sigsuspend.S:12

    1 thread 257 __futex_wait () at system/klibc/android/atomics_arm.S:88

    (gdb) thread 7

    [Switching to thread 7 (thread 263)]#0 __ioctl () at system/klibc/syscalls/__ioctl.S:12

    12 movs r0, r0

    (gdb) bt


    #0 __ioctl () at system/klibc/syscalls/__ioctl.S:12

    #1 0x53010704 in ioctl (fd=-512, request=-1072143871) at system/klibc/android/ioctl.c:22

    #2 0x51040ac0 in android::IPCThreadState::talkWithDriver (this=0x1207b8, doReceive=true) at RefBase.h:83

    #3 0x510418a0 in android::IPCThreadState::joinThreadPool (this=0x1207b8, isMain=false)

    at libs/utils/IPCThreadState.cpp:343

    #4 0x51046004 in android::PoolThread::threadLoop (this=0xfffffe00) at libs/utils/ProcessState.cpp:52

    #5 0x51036428 in android::Thread::_threadLoop (user=0xfffffe00) at libs/utils/Threads.cpp:1100

    #6 0x58025c68 in android::AndroidRuntime::javaThreadShell (args=0x105ffe28) at libs/android_runtime/AndroidRuntime.cpp:540


    (gdb) thread 6

    [Switching to thread 6 (thread 262)]#0 accept () at system/klibc/syscalls/accept.S:12

    12 movs r0, r0

    (gdb) bt


    #0 accept () at system/klibc/syscalls/accept.S:12

    #1 0x6b0334e4 in jdwpAcceptConnection (state=0xfffffe00) at extlibs/jamvm-1.4.1/jdwp/JdwpNet.c:213

    #2 0x6b032660 in jdwpThreadEntry (self=0x4d020) at extlibs/jamvm-1.4.1/jdwp/JdwpMain.c:37

    #3 0x6b022c2c in shell (args=0x4d960) at extlibs/jamvm-1.4.1/src/thread.c:629


    (gdb) thread 5

    [Switching to thread 5 (thread 261)]#0 __futex_wait () at system/klibc/android/atomics_arm.S:88

    88 bx lr

    (gdb) bt


    #0 __futex_wait () at system/klibc/android/atomics_arm.S:88

    #1 0x53010f48 in pthread_cond_timeout (cond=0x6b039b64, mutex=0x6b039b60, msecs=0) at system/klibc/android/pthread.c:513

    #2 0x6b01c8d0 in monitorWait (mon=0x6b039b60, self=0x4d400, ms=1000, ns=272629312) at extlibs/jamvm-1.4.1/src/lock.c:183

    #3 0x6b022084 in threadSleep (thread=0x4d400, ms=1000, ns=272629312) at extlibs/jamvm-1.4.1/src/thread.c:215

    #4 0x6b00d4fc in asyncGCThreadLoop (self=0x4d400) at extlibs/jamvm-1.4.1/src/alloc.c:1179

    #5 0x6b022c2c in shell (args=0x4d480) at extlibs/jamvm-1.4.1/src/thread.c:629


    (gdb) thread 4

    [Switching to thread 4 (thread 260)]#0 __futex_wait () at system/klibc/android/atomics_arm.S:88

    88 bx lr

    (gdb) bt


    #0 __futex_wait () at system/klibc/android/atomics_arm.S:88

    #1 0x53010eb8 in pthread_cond_timedwait (cond=0x6b039934, mutex=0x6b039930, abstime=0x0)

    at system/klibc/android/pthread.c:490

    #2 0x6b00b3ec in referenceHandlerThreadLoop (self=0x4d360) at extlibs/jamvm-1.4.1/src/alloc.c:1247

    #3 0x6b022c2c in shell (args=0x4d960) at extlibs/jamvm-1.4.1/src/thread.c:629
    (gdb) thread 3

    [Switching to thread 3 (thread 259)]#0 __futex_wait () at system/klibc/android/atomics_arm.S:88

    88 bx lr

    (gdb) bt


    #0 __futex_wait () at system/klibc/android/atomics_arm.S:88

    #1 0x53010eb8 in pthread_cond_timedwait (cond=0x6b03992c, mutex=0x6b039928, abstime=0x0)

    at system/klibc/android/pthread.c:490

    #2 0x6b00b1dc in finalizerThreadLoop (self=0x4d8e0) at extlibs/jamvm-1.4.1/src/alloc.c:1238

    #3 0x6b022c2c in shell (args=0x4d960) at extlibs/jamvm-1.4.1/src/thread.c:629
    (gdb) thread 2

    [Switching to thread 2 (thread 258)]#0 __sigsuspend () at system/klibc/syscalls/__sigsuspend.S:12

    12 movs r0, r0

    (gdb) bt


    #0 __sigsuspend () at system/klibc/syscalls/__sigsuspend.S:12

    #1 0x6b023814 in dumpThreadsLoop (self=0x51b98) at extlibs/jamvm-1.4.1/src/thread.c:1107

    #2 0x6b022c2c in shell (args=0x51b58) at extlibs/jamvm-1.4.1/src/thread.c:629

    Crash in C / C++ code


    If it crashes, connect with aproto and run logcat on the device. You should see output like this:

    I/ActivityManager( 188): Starting activity: Intent { component=com.android.calendar.MonthScreen }

    I/ActivityManager( 188): Starting application com.android.calendar to host activity com.android.calendar.MonthScree

    n

    I/ServiceManager( 417): Executing: /android/bin/app_process (link=/android/bin/app_process, wrapper=/android/bin/app_process)



    I/DEBUG: -- observer of pid 417 starting --

    I/appproc ( 417): App process is starting with pid=417, class=android/activity/ActivityThread.

    I/DEBUG: -- observer of pid 417 exiting --

    I/DEBUG: -- observer of pid 420 starting --

    I/DEBUG: *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

    I/DEBUG: pid: 373, tid: 401 >>> android.content.providers.pim <<<

    I/DEBUG: signal 11 (SIGSEGV), fault addr 00000000

    I/DEBUG: r0 ffffffff r1 00000000 r2 00000454 r3 002136d4

    I/DEBUG: r4 002136c0 r5 40804810 r6 0022dc70 r7 00000010

    I/DEBUG: r8 0020a258 r9 00000014 10 6b039074 fp 109ffcf8

    I/DEBUG: ip 6b039e90 sp 109ffc0c lr 580239f0 pc 6b0156a0

    I/DEBUG: #01 pc 6b0156a0 /android/lib/libjamvm.so

    I/DEBUG: #01 lr 580239f0 /android/lib/libandroid_runtime.so

    I/DEBUG: #02 pc 6b01481c /android/lib/libjamvm.so

    I/DEBUG: #03 pc 6b0148a4 /android/lib/libjamvm.so

    I/DEBUG: #04 pc 6b00ebc0 /android/lib/libjamvm.so

    I/DEBUG: #05 pc 6b02166c /android/lib/libjamvm.so

    I/DEBUG: #06 pc 6b01657c /android/lib/libjamvm.so

    I/DEBUG: #07 pc 6b01481c /android/lib/libjamvm.so

    I/DEBUG: #08 pc 6b0148a4 /android/lib/libjamvm.so

    I/DEBUG: #09 pc 6b0235c0 /android/lib/libjamvm.so

    I/DEBUG: #10 pc 5300fac4 /android/lib/libc.so

    I/DEBUG: #11 pc 5300fc5c /android/lib/libc.so

    I/DEBUG: -- observer of pid 373 exiting --

    I/DEBUG: -- observer of pid 423 starting --

    If debugging output indicates an error in C or C++ code, the addresses aren't particularly useful, but the debugging symbols aren't present on the device. Use the "stack" tool to convert these addresses to files and line numbers, for example:

    pid: 373, tid: 401 >>> android.content.providers.pim <<<
    signal 11 (SIGSEGV), fault addr 00000000

    r0 ffffffff r1 00000000 r2 00000454 r3 002136d4

    r4 002136c0 r5 40804810 r6 0022dc70 r7 00000010

    r8 0020a258 r9 00000014 10 6b039074 fp 109ffcf8

    r8 0020a258 r9 00000014 10 6b039074 fp 109ffcf8
    ADDR FUNCTION FILE:LINE

    6b0156a0 executeJava extlibs/jamvm-1.4.1/src/interp.c:2674

    580239f0 android_util_Parcel_freeBuffer libs/android_runtime/android_util_Binder.cpp:765

    6b01481c executeMethodVaList extlibs/jamvm- 1.4.1/src/execute.c:91

    6b0148a4 executeMethodArgs extlibs/jamvm-1.4.1/src/execute.c:67

    6b00ebc0 initClass extlibs/jamvm-1.4.1/src/class.c:1124

    6b02166c resolveMethod extlibs/jamvm- 1.4.1/src/resolve.c:197

    6b01657c executeJava extlibs/jamvm-1.4.1/src/interp.c:2237

    6b01481c executeMethodVaList extlibs/jamvm-1.4.1/src/execute.c:91

    6b0148a4 executeMethodArgs extlibs/jamvm- 1.4.1/src/execute.c:67

    6b0235c0 threadStart extlibs/jamvm-1.4.1/src/thread.c:355

    5300fac4 __thread_entry system/klibc/android/pthread.c:59

    5300fc5c pthread_create system/klibc/android/pthread.c:182

    Or you can run logcat without any parameters and it will read from stdin. You can then paste output into the terminal or pipe it. Run logcat from the top of the tree in the environment in which you do builds so that the application can determine relative paths to the toolchain to use to decode the object files.


    Debugging with tcpdump and other tools


    Installing tcpdump
    Running tcpdump
    Other network debugging commands

    Installing tcpdump

    Pushing the binary to an existing device


    Download tcpdump from http://www.tcpdump.org/, then execute:

    adb root


    adb remount

    adb push /wherever/you/put/tcpdump /system/xbin/tcpdump

    adb shell chmod 6755 /data/local/tmp/tcpdump

    Including tcpdump in the build image


    If you are running your own build, execute:

    mmm external/tcpdump # install the binary in out/.../system/xbin

    make snod # build a new system.img that includes it

    Flash the device as usual, for example, fastboot flashball.

    If you want to build tcpdump by default, add CUSTOM_TARGETS += tcpdump to your buildspec.mk.

    Running tcpdump


    You need to have root access on your device.

    Batch mode capture


    The typical procedure is to capture packets to a file and then examine the file on the desktop, as illustrated below:

    adb shell tcpdump -i any -p -s 0 -w /sdcard/capture.pcap

    # "-i any": listen on any network interface

    # "-p": disable promiscuous mode (doesn't work anyway)

    # "-s 0": capture the entire packet

    # "-w": write packets to a file (rather than printing to stdout)


    ... do whatever you want to capture, then ^C to stop it ...
    adb pull /sdcard/capture.pcap .

    sudo apt-get install wireshark # or ethereal, if you're still on dapper

    wireshark capture.pcap # or ethereal
    ... look at your packets and be wise ...

    You can run tcpdump in the background from an interactive shell or from Terminal. By default, tcpdump captures all traffic without filtering. If you prefer, add an expression like port 80 to the tcpdump command line.


    Real time packet monitoring


    Execute the following if you would like to watch packets go by rather than capturing them to a file (-n skips DNS lookups. -s 0 captures the entire packet rather than just the header):

    adb shell tcpdump -n -s 0

    Typical tcpdump options apply. For example, if you want to see HTTP traffic:

    adb shell tcpdump -X -n -s 0 port 80

    You can also monitor packets with wireshark or ethereal, as shown below:

    # In one shell, start tcpdump.

    adb shell "tcpdump -n -s 0 -w - | nc -l -p 11233"
    # In a separate shell, forward data and run ethereal.

    adb forward tcp:11233 tcp:11233 && nc 127.0.0.1 11233 | ethereal -k -S -i -

    Note that you can't restart capture via ethereal. If anything goes wrong, you will need to rerun both commands.

    For more immediate output, add -l to the tcpdump command line, but this can cause adb to choke (it helps to use a nonzero argument for -s to limit the amount of data captured per packet; -s 100 is sufficient if you just want to see headers).


    Disabling encryption


    If your service runs over https, tcpdump is of limited use. In this case, you can rewrite some service URLs to use http, for example:

    vendor/google/tools/override-gservices url:calendar_sync_https_proxy \



    https://www.google.com/calendar rewrite http://android.clients.google.com/proxy/calendar

    Other network debugging commands

    On the device:


    • ifconfig interface: note that unlike Linux, you need to give ifconfig an argument

    • netcfg: lists interfaces and IP addresses

    • iftop: like top for network

    • route: examine the routing table

    • netstat: see active network connections

    • nc: netcat connection utility

    On the desktop:


    • curl: fetch URLs directly to emulate device requests

    Download 1,28 Mb.
    1   ...   6   7   8   9   10   11   12   13   ...   95




    Download 1,28 Mb.

    Bosh sahifa
    Aloqalar

        Bosh sahifa



    Android Platform Developer's Guide

    Download 1,28 Mb.