• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.ddmlib;
18 
19 import java.io.IOException;
20 import java.nio.ByteBuffer;
21 
22 /**
23  * Handle thread status updates.
24  */
25 final class HandleThread extends ChunkHandler {
26 
27     public static final int CHUNK_THEN = type("THEN");
28     public static final int CHUNK_THCR = type("THCR");
29     public static final int CHUNK_THDE = type("THDE");
30     public static final int CHUNK_THST = type("THST");
31     public static final int CHUNK_THNM = type("THNM");
32     public static final int CHUNK_STKL = type("STKL");
33 
34     private static final HandleThread mInst = new HandleThread();
35 
36     // only read/written by requestThreadUpdates()
37     private static volatile boolean mThreadStatusReqRunning = false;
38     private static volatile boolean mThreadStackTraceReqRunning = false;
39 
HandleThread()40     private HandleThread() {}
41 
42 
43     /**
44      * Register for the packets we expect to get from the client.
45      */
register(MonitorThread mt)46     public static void register(MonitorThread mt) {
47         mt.registerChunkHandler(CHUNK_THCR, mInst);
48         mt.registerChunkHandler(CHUNK_THDE, mInst);
49         mt.registerChunkHandler(CHUNK_THST, mInst);
50         mt.registerChunkHandler(CHUNK_THNM, mInst);
51         mt.registerChunkHandler(CHUNK_STKL, mInst);
52     }
53 
54     /**
55      * Client is ready.
56      */
57     @Override
clientReady(Client client)58     public void clientReady(Client client) throws IOException {
59         Log.d("ddm-thread", "Now ready: " + client);
60         if (client.isThreadUpdateEnabled())
61             sendTHEN(client, true);
62     }
63 
64     /**
65      * Client went away.
66      */
67     @Override
clientDisconnected(Client client)68     public void clientDisconnected(Client client) {}
69 
70     /**
71      * Chunk handler entry point.
72      */
73     @Override
handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId)74     public void handleChunk(Client client, int type, ByteBuffer data, boolean isReply, int msgId) {
75 
76         Log.d("ddm-thread", "handling " + ChunkHandler.name(type));
77 
78         if (type == CHUNK_THCR) {
79             handleTHCR(client, data);
80         } else if (type == CHUNK_THDE) {
81             handleTHDE(client, data);
82         } else if (type == CHUNK_THST) {
83             handleTHST(client, data);
84         } else if (type == CHUNK_THNM) {
85             handleTHNM(client, data);
86         } else if (type == CHUNK_STKL) {
87             handleSTKL(client, data);
88         } else {
89             handleUnknownChunk(client, type, data, isReply, msgId);
90         }
91     }
92 
93     /*
94      * Handle a thread creation message.
95      *
96      * We should be tolerant of receiving a duplicate create message.  (It
97      * shouldn't happen with the current implementation.)
98      */
handleTHCR(Client client, ByteBuffer data)99     private void handleTHCR(Client client, ByteBuffer data) {
100         int threadId, nameLen;
101         String name;
102 
103         threadId = data.getInt();
104         nameLen = data.getInt();
105         name = getString(data, nameLen);
106 
107         Log.v("ddm-thread", "THCR: " + threadId + " '" + name + "'");
108 
109         client.getClientData().addThread(threadId, name);
110         client.update(Client.CHANGE_THREAD_DATA);
111     }
112 
113     /*
114      * Handle a thread death message.
115      */
handleTHDE(Client client, ByteBuffer data)116     private void handleTHDE(Client client, ByteBuffer data) {
117         int threadId;
118 
119         threadId = data.getInt();
120         Log.v("ddm-thread", "THDE: " + threadId);
121 
122         client.getClientData().removeThread(threadId);
123         client.update(Client.CHANGE_THREAD_DATA);
124     }
125 
126     /*
127      * Handle a thread status update message.
128      *
129      * Response has:
130      *  (1b) header len
131      *  (1b) bytes per entry
132      *  (2b) thread count
133      * Then, for each thread:
134      *  (4b) threadId (matches value from THCR)
135      *  (1b) thread status
136      *  (4b) tid
137      *  (4b) utime
138      *  (4b) stime
139      */
handleTHST(Client client, ByteBuffer data)140     private void handleTHST(Client client, ByteBuffer data) {
141         int headerLen, bytesPerEntry, extraPerEntry;
142         int threadCount;
143 
144         headerLen = (data.get() & 0xff);
145         bytesPerEntry = (data.get() & 0xff);
146         threadCount = data.getShort();
147 
148         headerLen -= 4;     // we've read 4 bytes
149         while (headerLen-- > 0)
150             data.get();
151 
152         extraPerEntry = bytesPerEntry - 18;     // we want 18 bytes
153 
154         Log.v("ddm-thread", "THST: threadCount=" + threadCount);
155 
156         /*
157          * For each thread, extract the data, find the appropriate
158          * client, and add it to the ClientData.
159          */
160         for (int i = 0; i < threadCount; i++) {
161             int threadId, status, tid, utime, stime;
162             boolean isDaemon = false;
163 
164             threadId = data.getInt();
165             status = data.get();
166             tid = data.getInt();
167             utime = data.getInt();
168             stime = data.getInt();
169             if (bytesPerEntry >= 18)
170                 isDaemon = (data.get() != 0);
171 
172             Log.v("ddm-thread", "  id=" + threadId
173                 + ", status=" + status + ", tid=" + tid
174                 + ", utime=" + utime + ", stime=" + stime);
175 
176             ClientData cd = client.getClientData();
177             ThreadInfo threadInfo = cd.getThread(threadId);
178             if (threadInfo != null)
179                 threadInfo.updateThread(status, tid, utime, stime, isDaemon);
180             else
181                 Log.d("ddms", "Thread with id=" + threadId + " not found");
182 
183             // slurp up any extra
184             for (int slurp = extraPerEntry; slurp > 0; slurp--)
185                 data.get();
186         }
187 
188         client.update(Client.CHANGE_THREAD_DATA);
189     }
190 
191     /*
192      * Handle a THNM (THread NaMe) message.  We get one of these after
193      * somebody calls Thread.setName() on a running thread.
194      */
handleTHNM(Client client, ByteBuffer data)195     private void handleTHNM(Client client, ByteBuffer data) {
196         int threadId, nameLen;
197         String name;
198 
199         threadId = data.getInt();
200         nameLen = data.getInt();
201         name = getString(data, nameLen);
202 
203         Log.v("ddm-thread", "THNM: " + threadId + " '" + name + "'");
204 
205         ThreadInfo threadInfo = client.getClientData().getThread(threadId);
206         if (threadInfo != null) {
207             threadInfo.setThreadName(name);
208             client.update(Client.CHANGE_THREAD_DATA);
209         } else {
210             Log.d("ddms", "Thread with id=" + threadId + " not found");
211         }
212     }
213 
214 
215     /**
216      * Parse an incoming STKL.
217      */
handleSTKL(Client client, ByteBuffer data)218     private void handleSTKL(Client client, ByteBuffer data) {
219         StackTraceElement[] trace;
220         int i, threadId, stackDepth;
221         @SuppressWarnings("unused")
222         int future;
223 
224         future = data.getInt();
225         threadId = data.getInt();
226 
227         Log.v("ddms", "STKL: " + threadId);
228 
229         /* un-serialize the StackTraceElement[] */
230         stackDepth = data.getInt();
231         trace = new StackTraceElement[stackDepth];
232         for (i = 0; i < stackDepth; i++) {
233             String className, methodName, fileName;
234             int len, lineNumber;
235 
236             len = data.getInt();
237             className = getString(data, len);
238             len = data.getInt();
239             methodName = getString(data, len);
240             len = data.getInt();
241             if (len == 0) {
242                 fileName = null;
243             } else {
244                 fileName = getString(data, len);
245             }
246             lineNumber = data.getInt();
247 
248             trace[i] = new StackTraceElement(className, methodName, fileName,
249                         lineNumber);
250         }
251 
252         ThreadInfo threadInfo = client.getClientData().getThread(threadId);
253         if (threadInfo != null) {
254             threadInfo.setStackCall(trace);
255             client.update(Client.CHANGE_THREAD_STACKTRACE);
256         } else {
257             Log.d("STKL", String.format(
258                     "Got stackcall for thread %1$d, which does not exists (anymore?).", //$NON-NLS-1$
259                     threadId));
260         }
261     }
262 
263 
264     /**
265      * Send a THEN (THread notification ENable) request to the client.
266      */
sendTHEN(Client client, boolean enable)267     public static void sendTHEN(Client client, boolean enable)
268         throws IOException {
269 
270         ByteBuffer rawBuf = allocBuffer(1);
271         JdwpPacket packet = new JdwpPacket(rawBuf);
272         ByteBuffer buf = getChunkDataBuf(rawBuf);
273 
274         if (enable)
275             buf.put((byte)1);
276         else
277             buf.put((byte)0);
278 
279         finishChunkPacket(packet, CHUNK_THEN, buf.position());
280         Log.d("ddm-thread", "Sending " + name(CHUNK_THEN) + ": " + enable);
281         client.sendAndConsume(packet, mInst);
282     }
283 
284 
285     /**
286      * Send a STKL (STacK List) request to the client.  The VM will suspend
287      * the target thread, obtain its stack, and return it.  If the thread
288      * is no longer running, a failure result will be returned.
289      */
sendSTKL(Client client, int threadId)290     public static void sendSTKL(Client client, int threadId)
291         throws IOException {
292 
293         if (false) {
294             Log.d("ddm-thread", "would send STKL " + threadId);
295             return;
296         }
297 
298         ByteBuffer rawBuf = allocBuffer(4);
299         JdwpPacket packet = new JdwpPacket(rawBuf);
300         ByteBuffer buf = getChunkDataBuf(rawBuf);
301 
302         buf.putInt(threadId);
303 
304         finishChunkPacket(packet, CHUNK_STKL, buf.position());
305         Log.d("ddm-thread", "Sending " + name(CHUNK_STKL) + ": " + threadId);
306         client.sendAndConsume(packet, mInst);
307     }
308 
309 
310     /**
311      * This is called periodically from the UI thread.  To avoid locking
312      * the UI while we request the updates, we create a new thread.
313      *
314      */
requestThreadUpdate(final Client client)315     static void requestThreadUpdate(final Client client) {
316         if (client.isDdmAware() && client.isThreadUpdateEnabled()) {
317             if (mThreadStatusReqRunning) {
318                 Log.w("ddms", "Waiting for previous thread update req to finish");
319                 return;
320             }
321 
322             new Thread("Thread Status Req") {
323                 @Override
324                 public void run() {
325                     mThreadStatusReqRunning = true;
326                     try {
327                         sendTHST(client);
328                     } catch (IOException ioe) {
329                         Log.d("ddms", "Unable to request thread updates from "
330                                 + client + ": " + ioe.getMessage());
331                     } finally {
332                         mThreadStatusReqRunning = false;
333                     }
334                 }
335             }.start();
336         }
337     }
338 
requestThreadStackCallRefresh(final Client client, final int threadId)339     static void requestThreadStackCallRefresh(final Client client, final int threadId) {
340         if (client.isDdmAware() && client.isThreadUpdateEnabled()) {
341             if (mThreadStackTraceReqRunning ) {
342                 Log.w("ddms", "Waiting for previous thread stack call req to finish");
343                 return;
344             }
345 
346             new Thread("Thread Status Req") {
347                 @Override
348                 public void run() {
349                     mThreadStackTraceReqRunning = true;
350                     try {
351                         sendSTKL(client, threadId);
352                     } catch (IOException ioe) {
353                         Log.d("ddms", "Unable to request thread stack call updates from "
354                                 + client + ": " + ioe.getMessage());
355                     } finally {
356                         mThreadStackTraceReqRunning = false;
357                     }
358                 }
359             }.start();
360         }
361 
362     }
363 
364     /*
365      * Send a THST request to the specified client.
366      */
sendTHST(Client client)367     private static void sendTHST(Client client) throws IOException {
368         ByteBuffer rawBuf = allocBuffer(0);
369         JdwpPacket packet = new JdwpPacket(rawBuf);
370         ByteBuffer buf = getChunkDataBuf(rawBuf);
371 
372         // nothing much to say
373 
374         finishChunkPacket(packet, CHUNK_THST, buf.position());
375         Log.d("ddm-thread", "Sending " + name(CHUNK_THST));
376         client.sendAndConsume(packet, mInst);
377     }
378 }
379 
380