Addr2Linepublic 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 | sProcessCacheLoaded 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[] | sCrLfbyte array representing a carriage return. Used to push addresses in the
process pipes. | private String | mLibraryPath to the library | private Process | mProcessthe command line process | private BufferedReader | mResultReaderbuffer to read the result of the command line process from | private BufferedOutputStream | mAddressWriteroutput 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.
mLibrary = library;
|
Methods Summary |
---|
public NativeStackCallInfo | getAddress(long addr)Looks up an address and returns method name, source file name, and line
number.
// 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.Addr2Line | getProcess(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.
// 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 boolean | start()Starts the command line process.
// 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 void | stop()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 void | stopAll()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();
}
}
|
|