blob: baf6db153832fa40adc4cbed2b4294818596d690 [file] [log] [blame]
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.ddmlib;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
/**
* Handle thread status updates.
*/
final class HandleNativeHeap extends ChunkHandler {
public static final int CHUNK_NHGT = type("NHGT"); //$NON-NLS-1$
public static final int CHUNK_NHSG = type("NHSG"); //$NON-NLS-1$
public static final int CHUNK_NHST = type("NHST"); //$NON-NLS-1$
public static final int CHUNK_NHEN = type("NHEN"); //$NON-NLS-1$
private static final HandleNativeHeap mInst = new HandleNativeHeap();
/**
* Handle getting different sized size_t and pointer reads.
*/
abstract class NativeBuffer {
public NativeBuffer(ByteBuffer buffer) {
mBuffer = buffer;
}
public abstract int getSizeT();
public abstract long getPtr();
protected ByteBuffer mBuffer;
}
/**
* This class treats size_t and pointer values as 32 bit.
*/
final class NativeBuffer32 extends NativeBuffer {
public NativeBuffer32(ByteBuffer buffer) {
super(buffer);
}
@Override
public int getSizeT() {
return mBuffer.getInt();
}
@Override
public long getPtr() {
return (long)mBuffer.getInt() & 0x00000000ffffffffL;
}
}
/**
* This class treats size_t and pointer values as 64 bit.
*/
final class NativeBuffer64 extends NativeBuffer {
public NativeBuffer64(ByteBuffer buffer) {
super(buffer);
}
@Override
public int getSizeT() {
return (int)mBuffer.getLong();
}
@Override
public long getPtr() {
return mBuffer.getLong();
}
}
private HandleNativeHeap() {
}
/**
* Register for the packets we expect to get from the client.
*/
public static void register(MonitorThread mt) {
mt.registerChunkHandler(CHUNK_NHGT, mInst);
mt.registerChunkHandler(CHUNK_NHSG, mInst);
mt.registerChunkHandler(CHUNK_NHST, mInst);
mt.registerChunkHandler(CHUNK_NHEN, mInst);
}
/**
* Client is ready.
*/
@Override
public void clientReady(Client client) throws IOException {}
/**
* Client went away.
*/
@Override
public void clientDisconnected(Client client) {}
/**
* Chunk handler entry point.
*/
@Override
public void handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId) {
Log.d("ddm-nativeheap", "handling " + ChunkHandler.name(type));
if (type == CHUNK_NHGT) {
handleNHGT(client, data);
} else if (type == CHUNK_NHST) {
// start chunk before any NHSG chunk(s)
client.getClientData().getNativeHeapData().clearHeapData();
} else if (type == CHUNK_NHEN) {
// end chunk after NHSG chunk(s)
client.getClientData().getNativeHeapData().sealHeapData();
} else if (type == CHUNK_NHSG) {
handleNHSG(client, data);
} else {
handleUnknownChunk(client, type, data, isReply, msgId);
}
client.update(Client.CHANGE_NATIVE_HEAP_DATA);
}
/**
* Send an NHGT (Native Thread GeT) request to the client.
*/
public static void sendNHGT(Client client) throws IOException {
ByteBuffer rawBuf = allocBuffer(0);
JdwpPacket packet = new JdwpPacket(rawBuf);
ByteBuffer buf = getChunkDataBuf(rawBuf);
// no data in request message
finishChunkPacket(packet, CHUNK_NHGT, buf.position());
Log.d("ddm-nativeheap", "Sending " + name(CHUNK_NHGT));
client.sendAndConsume(packet, mInst);
rawBuf = allocBuffer(2);
packet = new JdwpPacket(rawBuf);
buf = getChunkDataBuf(rawBuf);
buf.put((byte)HandleHeap.WHEN_DISABLE);
buf.put((byte)HandleHeap.WHAT_OBJ);
finishChunkPacket(packet, CHUNK_NHSG, buf.position());
Log.d("ddm-nativeheap", "Sending " + name(CHUNK_NHSG));
client.sendAndConsume(packet, mInst);
}
/*
* Handle our native heap data.
*/
private void handleNHGT(Client client, ByteBuffer data) {
ClientData clientData = client.getClientData();
Log.d("ddm-nativeheap", "NHGT: " + data.limit() + " bytes");
data.order(ByteOrder.LITTLE_ENDIAN);
// There are two supported header formats.
//
// The original version of the header for 32 bit processes:
//
// uint32_t mapSize;
// uint32_t mapSize;
// uint32_t allocSize;
// uint32_t allocInfoSize;
// uint32_t totalMemory;
// uint32_t backtrace_size;
//
// The new header which includes a signature and pointer size:
//
// uint32_t signature; (Which is always 0x812345dd)
// uint16_t version; (Only version 2 of the new format supported)
// uint16_t pointerSize; (Size in bytes of size_t/pointer values)
// size_t mapSize;
// size_t allocSize;
// size_t allocInfoSize;
// size_t totalMemory;
// size_t backtrace_size;
//
// If the signature doesn't match, then the code uses the original
// header format. If the signature matches, then use the new
// header format with variable sizes of size_t and pointers.
int signature = data.getInt(0);
short pointerSize = 4;
if (signature == 0x812345dd) {
// Consume signature value.
int ignore = data.getInt();
short version = data.getShort();
if (version != 2) {
Log.e("ddms", "Unknown header version: " + version);
return;
}
pointerSize = data.getShort();
}
NativeBuffer buffer;
if (pointerSize == 4) {
buffer = new NativeBuffer32(data);
} else if (pointerSize == 8) {
buffer = new NativeBuffer64(data);
} else {
Log.e("ddms", "Unknown pointer size: " + pointerSize);
return;
}
// clear the previous run
clientData.clearNativeAllocationInfo();
int mapSize = buffer.getSizeT();
int allocSize = buffer.getSizeT();
int allocInfoSize = buffer.getSizeT();
int totalMemory = buffer.getSizeT();
int backtraceSize = buffer.getSizeT();
Log.d("ddms", "mapSize: " + mapSize);
Log.d("ddms", "allocSize: " + allocSize);
Log.d("ddms", "allocInfoSize: " + allocInfoSize);
Log.d("ddms", "totalMemory: " + totalMemory);
clientData.setTotalNativeMemory(totalMemory);
// this means that updates aren't turned on.
if (allocInfoSize == 0) {
return;
}
if (mapSize > 0) {
byte[] maps = new byte[mapSize];
data.get(maps, 0, mapSize);
parseMaps(clientData, maps);
}
int iterations = allocSize / allocInfoSize;
for (int i = 0 ; i < iterations ; i++) {
NativeAllocationInfo info = new NativeAllocationInfo(
buffer.getSizeT() /* size */,
buffer.getSizeT() /* allocations */);
for (int j = 0 ; j < backtraceSize ; j++) {
long addr = buffer.getPtr();
if (addr == 0x0) {
// skip past null addresses
continue;
}
info.addStackCallAddress(addr);
}
clientData.addNativeAllocation(info);
}
}
private void handleNHSG(Client client, ByteBuffer data) {
byte dataCopy[] = new byte[data.limit()];
data.rewind();
data.get(dataCopy);
data = ByteBuffer.wrap(dataCopy);
client.getClientData().getNativeHeapData().addHeapData(data);
if (true) {
return;
}
byte[] copy = new byte[data.limit()];
data.get(copy);
ByteBuffer buffer = ByteBuffer.wrap(copy);
buffer.order(ByteOrder.BIG_ENDIAN);
int id = buffer.getInt();
int unitsize = buffer.get();
long startAddress = buffer.getInt() & 0x00000000ffffffffL;
int offset = buffer.getInt();
int allocationUnitCount = buffer.getInt();
// read the usage
while (buffer.position() < buffer.limit()) {
int eState = buffer.get() & 0x000000ff;
int eLen = (buffer.get() & 0x000000ff) + 1;
}
}
private void parseMaps(ClientData clientData, byte[] maps) {
InputStreamReader input = new InputStreamReader(new ByteArrayInputStream(maps));
BufferedReader reader = new BufferedReader(input);
String line;
try {
while ((line = reader.readLine()) != null) {
Log.d("ddms", "line: " + line);
// Expected format:
// 7fe51f2000-7fe5213000 rw-p 00000000 00:00 0 [stack]
int library_start = line.lastIndexOf(' ');
if (library_start == -1) {
continue;
}
// Assume that any string that starts with a / is a
// shared library or executable that we will try to symbolize.
String library = line.substring(library_start+1);
if (!library.startsWith("/")) {
continue;
}
// Parse the start and end address range.
int dashIndex = line.indexOf('-');
int spaceIndex = line.indexOf(' ', dashIndex);
if (dashIndex == -1 || spaceIndex == -1) {
continue;
}
long startAddr = 0;
long endAddr = 0;
try {
startAddr = Long.parseLong(line.substring(0, dashIndex), 16);
endAddr = Long.parseLong(line.substring(dashIndex+1, spaceIndex), 16);
} catch (NumberFormatException e) {
e.printStackTrace();
continue;
}
clientData.addNativeLibraryMapInfo(startAddr, endAddr, library);
Log.d("ddms", library + "(" + Long.toHexString(startAddr) +
" - " + Long.toHexString(endAddr) + ")");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}