/* * 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(); 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_GC); 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 cd = client.getClientData(); Log.d("ddm-nativeheap", "NHGT: " + data.limit() + " bytes"); // TODO - process incoming data and save in "cd" byte[] copy = new byte[data.limit()]; data.get(copy); // clear the previous run cd.clearNativeAllocationInfo(); ByteBuffer buffer = ByteBuffer.wrap(copy); buffer.order(ByteOrder.LITTLE_ENDIAN); // read the header // typedef struct Header { // uint32_t mapSize; // uint32_t allocSize; // uint32_t allocInfoSize; // uint32_t totalMemory; // uint32_t backtraceSize; // }; int mapSize = buffer.getInt(); int allocSize = buffer.getInt(); int allocInfoSize = buffer.getInt(); int totalMemory = buffer.getInt(); int backtraceSize = buffer.getInt(); Log.d("ddms", "mapSize: " + mapSize); Log.d("ddms", "allocSize: " + allocSize); Log.d("ddms", "allocInfoSize: " + allocInfoSize); Log.d("ddms", "totalMemory: " + totalMemory); cd.setTotalNativeMemory(totalMemory); // this means that updates aren't turned on. if (allocInfoSize == 0) return; if (mapSize > 0) { byte[] maps = new byte[mapSize]; buffer.get(maps, 0, mapSize); parseMaps(cd, maps); } int iterations = allocSize / allocInfoSize; for (int i = 0 ; i < iterations ; i++) { NativeAllocationInfo info = new NativeAllocationInfo( buffer.getInt() /* size */, buffer.getInt() /* allocations */); for (int j = 0 ; j < backtraceSize ; j++) { long addr = ((long)buffer.getInt()) & 0x00000000ffffffffL; info.addStackCallAddress(addr);; } cd.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; } // WORK IN PROGRESS // Log.e("ddm-nativeheap", "NHSG: ----------------------------------"); // Log.e("ddm-nativeheap", "NHSG: " + data.limit() + " bytes"); 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 = (int) buffer.get(); long startAddress = (long) buffer.getInt() & 0x00000000ffffffffL; int offset = buffer.getInt(); int allocationUnitCount = buffer.getInt(); // Log.e("ddm-nativeheap", "id: " + id); // Log.e("ddm-nativeheap", "unitsize: " + unitsize); // Log.e("ddm-nativeheap", "startAddress: 0x" + Long.toHexString(startAddress)); // Log.e("ddm-nativeheap", "offset: " + offset); // Log.e("ddm-nativeheap", "allocationUnitCount: " + allocationUnitCount); // Log.e("ddm-nativeheap", "end: 0x" + // Long.toHexString(startAddress + unitsize * allocationUnitCount)); // read the usage while (buffer.position() < buffer.limit()) { int eState = (int)buffer.get() & 0x000000ff; int eLen = ((int)buffer.get() & 0x000000ff) + 1; //Log.e("ddm-nativeheap", "solidity: " + (eState & 0x7) + " - kind: " // + ((eState >> 3) & 0x7) + " - len: " + eLen); } // count += unitsize * allocationUnitCount; // Log.e("ddm-nativeheap", "count = " + count); } private void parseMaps(ClientData cd, byte[] maps) { InputStreamReader input = new InputStreamReader(new ByteArrayInputStream(maps)); BufferedReader reader = new BufferedReader(input); String line; try { // most libraries are defined on several lines, so we need to make sure we parse // all the library lines and only add the library at the end long startAddr = 0; long endAddr = 0; String library = null; while ((line = reader.readLine()) != null) { Log.d("ddms", "line: " + line); if (line.length() < 16) { continue; } try { long tmpStart = Long.parseLong(line.substring(0, 8), 16); long tmpEnd = Long.parseLong(line.substring(9, 17), 16); /* * only check for library addresses as defined in * //device/config/prelink-linux-arm.map */ if (tmpStart >= 0x0000000080000000L && tmpStart <= 0x00000000BFFFFFFFL) { int index = line.indexOf('/'); if (index == -1) continue; String tmpLib = line.substring(index); if (library == null || (library != null && tmpLib.equals(library) == false)) { if (library != null) { cd.addNativeLibraryMapInfo(startAddr, endAddr, library); Log.d("ddms", library + "(" + Long.toHexString(startAddr) + " - " + Long.toHexString(endAddr) + ")"); } // now init the new library library = tmpLib; startAddr = tmpStart; endAddr = tmpEnd; } else { // add the new end endAddr = tmpEnd; } } } catch (NumberFormatException e) { e.printStackTrace(); } } if (library != null) { cd.addNativeLibraryMapInfo(startAddr, endAddr, library); Log.d("ddms", library + "(" + Long.toHexString(startAddr) + " - " + Long.toHexString(endAddr) + ")"); } } catch (IOException e) { e.printStackTrace(); } } }