/* * 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 android.ddm; import org.apache.harmony.dalvik.ddmc.Chunk; import org.apache.harmony.dalvik.ddmc.ChunkHandler; import org.apache.harmony.dalvik.ddmc.DdmServer; import org.apache.harmony.dalvik.ddmc.DdmVmInternal; import android.util.Log; import java.nio.ByteBuffer; /** * Handle thread-related traffic. */ public class DdmHandleThread extends ChunkHandler { public static final int CHUNK_THEN = type("THEN"); public static final int CHUNK_THCR = type("THCR"); public static final int CHUNK_THDE = type("THDE"); public static final int CHUNK_THST = type("THST"); public static final int CHUNK_STKL = type("STKL"); private static DdmHandleThread mInstance = new DdmHandleThread(); /* singleton, do not instantiate */ private DdmHandleThread() {} /** * Register for the messages we're interested in. */ public static void register() { DdmServer.registerHandler(CHUNK_THEN, mInstance); DdmServer.registerHandler(CHUNK_THST, mInstance); DdmServer.registerHandler(CHUNK_STKL, mInstance); } /** * Called when the DDM server connects. The handler is allowed to * send messages to the server. */ public void connected() {} /** * Called when the DDM server disconnects. Can be used to disable * periodic transmissions or clean up saved state. */ public void disconnected() {} /** * Handle a chunk of data. */ public Chunk handleChunk(Chunk request) { if (false) Log.v("ddm-thread", "Handling " + name(request.type) + " chunk"); int type = request.type; if (type == CHUNK_THEN) { return handleTHEN(request); } else if (type == CHUNK_THST) { return handleTHST(request); } else if (type == CHUNK_STKL) { return handleSTKL(request); } else { throw new RuntimeException("Unknown packet " + ChunkHandler.name(type)); } } /* * Handle a "THread notification ENable" request. */ private Chunk handleTHEN(Chunk request) { ByteBuffer in = wrapChunk(request); boolean enable = (in.get() != 0); //Log.i("ddm-thread", "Thread notify enable: " + enable); DdmVmInternal.threadNotify(enable); return null; // empty response } /* * Handle a "THread STatus" request. This is constructed by the VM. */ private Chunk handleTHST(Chunk request) { ByteBuffer in = wrapChunk(request); // currently nothing to read from "in" //Log.d("ddm-thread", "Thread status request"); byte[] status = DdmVmInternal.getThreadStats(); if (status != null) return new Chunk(CHUNK_THST, status, 0, status.length); else return createFailChunk(1, "Can't build THST chunk"); } /* * Handle a STacK List request. * * This is done by threadId, which isn't great since those are * recycled. We need a thread serial ID. The Linux tid is an okay * answer as it's unlikely to recycle at the exact wrong moment. * However, we're using the short threadId in THST messages, so we * use them here for consistency. (One thought is to keep the current * thread ID in the low 16 bits and somehow serialize the top 16 bits.) */ private Chunk handleSTKL(Chunk request) { ByteBuffer in = wrapChunk(request); int threadId; threadId = in.getInt(); //Log.d("ddm-thread", "Stack list request " + threadId); StackTraceElement[] trace = DdmVmInternal.getStackTraceById(threadId); if (trace == null) { return createFailChunk(1, "Stack trace unavailable"); } else { return createStackChunk(trace, threadId); } } /* * Serialize a StackTraceElement[] into an STKL chunk. * * We include the threadId in the response so the other side doesn't have * to match up requests and responses as carefully. */ private Chunk createStackChunk(StackTraceElement[] trace, int threadId) { int bufferSize = 0; bufferSize += 4; // version, flags, whatever bufferSize += 4; // thread ID bufferSize += 4; // frame count for (StackTraceElement elem : trace) { bufferSize += 4 + elem.getClassName().length() * 2; bufferSize += 4 + elem.getMethodName().length() * 2; bufferSize += 4; if (elem.getFileName() != null) bufferSize += elem.getFileName().length() * 2; bufferSize += 4; // line number } ByteBuffer out = ByteBuffer.allocate(bufferSize); out.putInt(0); out.putInt(threadId); out.putInt(trace.length); for (StackTraceElement elem : trace) { out.putInt(elem.getClassName().length()); putString(out, elem.getClassName()); out.putInt(elem.getMethodName().length()); putString(out, elem.getMethodName()); if (elem.getFileName() != null) { out.putInt(elem.getFileName().length()); putString(out, elem.getFileName()); } else { out.putInt(0); } out.putInt(elem.getLineNumber()); } return new Chunk(CHUNK_STKL, out); } }