1/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.ddm;
18
19import org.apache.harmony.dalvik.ddmc.Chunk;
20import org.apache.harmony.dalvik.ddmc.ChunkHandler;
21import org.apache.harmony.dalvik.ddmc.DdmServer;
22import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
23import android.util.Log;
24import java.nio.ByteBuffer;
25
26/**
27 * Handle thread-related traffic.
28 */
29public class DdmHandleThread extends ChunkHandler {
30
31    public static final int CHUNK_THEN = type("THEN");
32    public static final int CHUNK_THCR = type("THCR");
33    public static final int CHUNK_THDE = type("THDE");
34    public static final int CHUNK_THST = type("THST");
35    public static final int CHUNK_STKL = type("STKL");
36
37    private static DdmHandleThread mInstance = new DdmHandleThread();
38
39
40    /* singleton, do not instantiate */
41    private DdmHandleThread() {}
42
43    /**
44     * Register for the messages we're interested in.
45     */
46    public static void register() {
47        DdmServer.registerHandler(CHUNK_THEN, mInstance);
48        DdmServer.registerHandler(CHUNK_THST, mInstance);
49        DdmServer.registerHandler(CHUNK_STKL, mInstance);
50    }
51
52    /**
53     * Called when the DDM server connects.  The handler is allowed to
54     * send messages to the server.
55     */
56    public void connected() {}
57
58    /**
59     * Called when the DDM server disconnects.  Can be used to disable
60     * periodic transmissions or clean up saved state.
61     */
62    public void disconnected() {}
63
64    /**
65     * Handle a chunk of data.
66     */
67    public Chunk handleChunk(Chunk request) {
68        if (false)
69            Log.v("ddm-thread", "Handling " + name(request.type) + " chunk");
70        int type = request.type;
71
72        if (type == CHUNK_THEN) {
73            return handleTHEN(request);
74        } else if (type == CHUNK_THST) {
75            return handleTHST(request);
76        } else if (type == CHUNK_STKL) {
77            return handleSTKL(request);
78        } else {
79            throw new RuntimeException("Unknown packet "
80                + ChunkHandler.name(type));
81        }
82    }
83
84    /*
85     * Handle a "THread notification ENable" request.
86     */
87    private Chunk handleTHEN(Chunk request) {
88        ByteBuffer in = wrapChunk(request);
89
90        boolean enable = (in.get() != 0);
91        //Log.i("ddm-thread", "Thread notify enable: " + enable);
92
93        DdmVmInternal.threadNotify(enable);
94        return null;        // empty response
95    }
96
97    /*
98     * Handle a "THread STatus" request.  This is constructed by the VM.
99     */
100    private Chunk handleTHST(Chunk request) {
101        ByteBuffer in = wrapChunk(request);
102        // currently nothing to read from "in"
103
104        //Log.d("ddm-thread", "Thread status request");
105
106        byte[] status = DdmVmInternal.getThreadStats();
107        if (status != null)
108            return new Chunk(CHUNK_THST, status, 0, status.length);
109        else
110            return createFailChunk(1, "Can't build THST chunk");
111    }
112
113    /*
114     * Handle a STacK List request.
115     *
116     * This is done by threadId, which isn't great since those are
117     * recycled.  We need a thread serial ID.  The Linux tid is an okay
118     * answer as it's unlikely to recycle at the exact wrong moment.
119     * However, we're using the short threadId in THST messages, so we
120     * use them here for consistency.  (One thought is to keep the current
121     * thread ID in the low 16 bits and somehow serialize the top 16 bits.)
122     */
123    private Chunk handleSTKL(Chunk request) {
124        ByteBuffer in = wrapChunk(request);
125        int threadId;
126
127        threadId = in.getInt();
128
129        //Log.d("ddm-thread", "Stack list request " + threadId);
130
131        StackTraceElement[] trace = DdmVmInternal.getStackTraceById(threadId);
132        if (trace == null) {
133            return createFailChunk(1, "Stack trace unavailable");
134        } else {
135            return createStackChunk(trace, threadId);
136        }
137    }
138
139    /*
140     * Serialize a StackTraceElement[] into an STKL chunk.
141     *
142     * We include the threadId in the response so the other side doesn't have
143     * to match up requests and responses as carefully.
144     */
145    private Chunk createStackChunk(StackTraceElement[] trace, int threadId) {
146        int bufferSize = 0;
147
148        bufferSize += 4;            // version, flags, whatever
149        bufferSize += 4;            // thread ID
150        bufferSize += 4;            // frame count
151        for (StackTraceElement elem : trace) {
152            bufferSize += 4 + elem.getClassName().length() * 2;
153            bufferSize += 4 + elem.getMethodName().length() * 2;
154            bufferSize += 4;
155            if (elem.getFileName() != null)
156                bufferSize += elem.getFileName().length() * 2;
157            bufferSize += 4;        // line number
158        }
159
160        ByteBuffer out = ByteBuffer.allocate(bufferSize);
161        out.putInt(0);
162        out.putInt(threadId);
163        out.putInt(trace.length);
164        for (StackTraceElement elem : trace) {
165            out.putInt(elem.getClassName().length());
166            putString(out, elem.getClassName());
167            out.putInt(elem.getMethodName().length());
168            putString(out, elem.getMethodName());
169            if (elem.getFileName() != null) {
170                out.putInt(elem.getFileName().length());
171                putString(out, elem.getFileName());
172            } else {
173                out.putInt(0);
174            }
175            out.putInt(elem.getLineNumber());
176        }
177
178        return new Chunk(CHUNK_STKL, out);
179    }
180}
181
182