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 
17 package android.ddm;
18 
19 import org.apache.harmony.dalvik.ddmc.Chunk;
20 import org.apache.harmony.dalvik.ddmc.ChunkHandler;
21 import org.apache.harmony.dalvik.ddmc.DdmServer;
22 import org.apache.harmony.dalvik.ddmc.DdmVmInternal;
23 import android.util.Log;
24 import java.nio.ByteBuffer;
25 
26 /**
27  * Handle thread-related traffic.
28  */
29 public 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 */
DdmHandleThread()41     private DdmHandleThread() {}
42 
43     /**
44      * Register for the messages we're interested in.
45      */
register()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      */
connected()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      */
disconnected()62     public void disconnected() {}
63 
64     /**
65      * Handle a chunk of data.
66      */
handleChunk(Chunk request)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      */
handleTHEN(Chunk request)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      */
handleTHST(Chunk request)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      */
handleSTKL(Chunk request)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      */
createStackChunk(StackTraceElement[] trace, int threadId)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