/* * 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(); } } }