FileDocCategorySizeDatePackage
Addr2Line.javaAPI DocAndroid 1.5 API9834Wed May 06 22:41:08 BST 2009com.android.ddmuilib

Addr2Line

public class Addr2Line extends Object
Represents an addr2line process to get filename/method information from a memory address.
Each process can only handle one library, which should be provided when creating a new process.

The processes take some time to load as they need to parse the library files. For this reason, processes cannot be manually started. Instead the class keeps an internal list of processes and one asks for a process for a specific library, using getProcess(String library).

Internally, the processes are started in pipe mode to be able to query them with multiple addresses.

Fields Summary
private static final HashMap
sProcessCache
Loaded processes list. This is also used as a locking object for any methods dealing with starting/stopping/creating processes/querying for method.
private static final byte[]
sCrLf
byte array representing a carriage return. Used to push addresses in the process pipes.
private String
mLibrary
Path to the library
private Process
mProcess
the command line process
private BufferedReader
mResultReader
buffer to read the result of the command line process from
private BufferedOutputStream
mAddressWriter
output stream to provide new addresses to decode to the command line process
Constructors Summary
private Addr2Line(String library)
Construct the object with a library name.
The library should be in a format that makes
$ANDROID_PRODUCT_OUT + "/symbols" + library a valid file.

param
library the library in which to look for address.

        mLibrary = library;
    
Methods Summary
public NativeStackCallInfogetAddress(long addr)
Looks up an address and returns method name, source file name, and line number.

param
addr the address to look up
return
a BacktraceInfo object containing the method/filename/linenumber or null if the process we stopped before the query could be processed, or if an IO exception happened.

        // even though we don't access the hashmap object, we need to
        // synchronized on it to prevent
        // another thread from stopping the process we're going to query.
        synchronized (sProcessCache) {
            // check the process is still alive/allocated
            if (mProcess != null) {
                // prepare to the write the address to the output buffer.

                // first, conversion to a string containing the hex value.
                String tmp = Long.toString(addr, 16);

                try {
                    // write the address to the buffer
                    mAddressWriter.write(tmp.getBytes());

                    // add CR-LF
                    mAddressWriter.write(sCrLf);

                    // flush it all.
                    mAddressWriter.flush();

                    // read the result. We need to read 2 lines
                    String method = mResultReader.readLine();
                    String source = mResultReader.readLine();

                    // make the backtrace object and return it
                    if (method != null && source != null) {
                        return new NativeStackCallInfo(mLibrary, method, source);
                    }
                } catch (IOException e) {
                    // log the error
                    Log.e("ddms",
                            "Error while trying to get information for addr: "
                                    + tmp + " in library: " + mLibrary);
                    // we'll return null later
                }
            }
        }
        return null;
    
public static com.android.ddmuilib.Addr2LinegetProcess(java.lang.String library)
Returns the instance of a Addr2Line process for the specified library.
The library should be in a format that makes
$ANDROID_PRODUCT_OUT + "/symbols" + library a valid file.

param
library the library in which to look for addresses.
return
a new Addr2Line object representing a started process, ready to be queried for addresses. If any error happened when launching a new process, null will be returned.


                                                                                           
          
        // synchronize around the hashmap object
        if (library != null) {
            synchronized (sProcessCache) {
                // look for an existing process
                Addr2Line process = sProcessCache.get(library);

                // if we don't find one, we create it
                if (process == null) {
                    process = new Addr2Line(library);

                    // then we start it
                    boolean status = process.start();

                    if (status) {
                        // if starting the process worked, then we add it to the
                        // list.
                        sProcessCache.put(library, process);
                    } else {
                        // otherwise we just drop the object, to return null
                        process = null;
                    }
                }
                // return the process
                return process;
            }
        }
        return null;
    
private booleanstart()
Starts the command line process.

return
true if the process was started, false if it failed to start, or if there was any other errors.

        // because this is only called from getProcess() we know we don't need
        // to synchronize this code.

        // get the output directory.
        String symbols = DdmUiPreferences.getSymbolDirectory();

        // build the command line
        String[] command = new String[5];
        command[0] = DdmUiPreferences.getAddr2Line();
        command[1] = "-C";
        command[2] = "-f";
        command[3] = "-e";
        command[4] = symbols + mLibrary.replaceAll("libc\\.so", "libc_debug\\.so");

        try {
            // attempt to start the process
            mProcess = Runtime.getRuntime().exec(command);

            if (mProcess != null) {
                // get the result reader
                InputStreamReader is = new InputStreamReader(mProcess
                        .getInputStream());
                mResultReader = new BufferedReader(is);

                // get the outstream to write the addresses
                mAddressWriter = new BufferedOutputStream(mProcess
                        .getOutputStream());

                // check our streams are here
                if (mResultReader == null || mAddressWriter == null) {
                    // not here? stop the process and return false;
                    mProcess.destroy();
                    mProcess = null;
                    return false;
                }

                // return a success
                return true;
            }

        } catch (IOException e) {
            // log the error
            String msg = String.format(
                    "Error while trying to start %1$s process for library %2$s",
                    DdmUiPreferences.getAddr2Line(), mLibrary);
            Log.e("ddm-Addr2Line", msg);

            // drop the process just in case
            if (mProcess != null) {
                mProcess.destroy();
                mProcess = null;
            }
        }

        // we can be here either cause the allocation of mProcess failed, or we
        // caught an exception
        return false;
    
public voidstop()
Stops the command line process.

        synchronized (sProcessCache) {
            if (mProcess != null) {
                // remove the process from the list
                sProcessCache.remove(mLibrary);

                // then stops the process
                mProcess.destroy();

                // set the reference to null.
                // this allows to make sure another thread calling getAddress()
                // will not query a stopped thread
                mProcess = null;
            }
        }
    
public static voidstopAll()
Stops all current running processes.

        // because of concurrent access (and our use of HashMap.values()), we
        // can't rely on the synchronized inside stop(). We need to put one
        // around the whole loop.
        synchronized (sProcessCache) {
            // just a basic loop on all the values in the hashmap and call to
            // stop();
            Collection<Addr2Line> col = sProcessCache.values();
            for (Addr2Line a2l : col) {
                a2l.stop();
            }
        }