JdwpMain.cpp revision 4308417beec548c2b2c06ecec4f7f4a965b09fb2
1/*
2 * Copyright (C) 2008 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/*
18 * JDWP initialization.
19 */
20#include "jdwp/JdwpPriv.h"
21#include "Dalvik.h"
22#include "Atomic.h"
23
24#include <stdlib.h>
25#include <unistd.h>
26#include <sys/time.h>
27#include <time.h>
28#include <errno.h>
29
30
31static void* jdwpThreadStart(void* arg);
32
33/*
34 * JdwpNetStateBase class implementation
35 */
36JdwpNetStateBase::JdwpNetStateBase()
37{
38    clientSock = -1;
39    dvmDbgInitMutex(&socketLock);
40}
41
42/*
43 * Write a packet. Grabs a mutex to assure atomicity.
44 */
45ssize_t JdwpNetStateBase::writePacket(ExpandBuf* pReply)
46{
47    dvmDbgLockMutex(&socketLock);
48    ssize_t cc = write(clientSock, expandBufGetBuffer(pReply),
49            expandBufGetLength(pReply));
50    dvmDbgUnlockMutex(&socketLock);
51
52    return cc;
53}
54
55/*
56 * Write a buffered packet. Grabs a mutex to assure atomicity.
57 */
58ssize_t JdwpNetStateBase::writeBufferedPacket(const struct iovec* iov,
59    int iovcnt)
60{
61    dvmDbgLockMutex(&socketLock);
62    ssize_t actual = writev(clientSock, iov, iovcnt);
63    dvmDbgUnlockMutex(&socketLock);
64
65    return actual;
66}
67
68/*
69 * Initialize JDWP.
70 *
71 * Does not return until JDWP thread is running, but may return before
72 * the thread is accepting network connections.
73 */
74JdwpState* dvmJdwpStartup(const JdwpStartupParams* pParams)
75{
76    JdwpState* state = NULL;
77
78    /* comment this out when debugging JDWP itself */
79    android_setMinPriority(LOG_TAG, ANDROID_LOG_DEBUG);
80
81    state = (JdwpState*) calloc(1, sizeof(JdwpState));
82
83    state->params = *pParams;
84
85    state->requestSerial = 0x10000000;
86    state->eventSerial = 0x20000000;
87    dvmDbgInitMutex(&state->threadStartLock);
88    dvmDbgInitMutex(&state->attachLock);
89    dvmDbgInitMutex(&state->serialLock);
90    dvmDbgInitMutex(&state->eventLock);
91    state->eventThreadId = 0;
92    dvmDbgInitMutex(&state->eventThreadLock);
93    dvmDbgInitCond(&state->threadStartCond);
94    dvmDbgInitCond(&state->attachCond);
95    dvmDbgInitCond(&state->eventThreadCond);
96
97    switch (pParams->transport) {
98    case kJdwpTransportSocket:
99        // ALOGD("prepping for JDWP over TCP");
100        state->transport = dvmJdwpSocketTransport();
101        break;
102    case kJdwpTransportAndroidAdb:
103        // ALOGD("prepping for JDWP over ADB");
104        state->transport = dvmJdwpAndroidAdbTransport();
105        /* TODO */
106        break;
107    default:
108        LOGE("Unknown transport %d", pParams->transport);
109        assert(false);
110        goto fail;
111    }
112
113    if (!dvmJdwpNetStartup(state, pParams))
114        goto fail;
115
116    /*
117     * Grab a mutex or two before starting the thread.  This ensures they
118     * won't signal the cond var before we're waiting.
119     */
120    dvmDbgLockMutex(&state->threadStartLock);
121    if (pParams->suspend)
122        dvmDbgLockMutex(&state->attachLock);
123
124    /*
125     * We have bound to a port, or are trying to connect outbound to a
126     * debugger.  Create the JDWP thread and let it continue the mission.
127     */
128    if (!dvmCreateInternalThread(&state->debugThreadHandle, "JDWP",
129            jdwpThreadStart, state))
130    {
131        /* state is getting tossed, but unlock these anyway for cleanliness */
132        dvmDbgUnlockMutex(&state->threadStartLock);
133        if (pParams->suspend)
134            dvmDbgUnlockMutex(&state->attachLock);
135        goto fail;
136    }
137
138    /*
139     * Wait until the thread finishes basic initialization.
140     * TODO: cond vars should be waited upon in a loop
141     */
142    dvmDbgCondWait(&state->threadStartCond, &state->threadStartLock);
143    dvmDbgUnlockMutex(&state->threadStartLock);
144
145
146    /*
147     * For suspend=y, wait for the debugger to connect to us or for us to
148     * connect to the debugger.
149     *
150     * The JDWP thread will signal us when it connects successfully or
151     * times out (for timeout=xxx), so we have to check to see what happened
152     * when we wake up.
153     */
154    if (pParams->suspend) {
155        dvmChangeStatus(NULL, THREAD_VMWAIT);
156        dvmDbgCondWait(&state->attachCond, &state->attachLock);
157        dvmDbgUnlockMutex(&state->attachLock);
158        dvmChangeStatus(NULL, THREAD_RUNNING);
159
160        if (!dvmJdwpIsActive(state)) {
161            LOGE("JDWP connection failed");
162            goto fail;
163        }
164
165        ALOGI("JDWP connected");
166
167        /*
168         * Ordinarily we would pause briefly to allow the debugger to set
169         * breakpoints and so on, but for "suspend=y" the VM init code will
170         * pause the VM when it sends the VM_START message.
171         */
172    }
173
174    return state;
175
176fail:
177    dvmJdwpShutdown(state);     // frees state
178    return NULL;
179}
180
181/*
182 * Reset all session-related state.  There should not be an active connection
183 * to the client at this point.  The rest of the VM still thinks there is
184 * a debugger attached.
185 *
186 * This includes freeing up the debugger event list.
187 */
188void dvmJdwpResetState(JdwpState* state)
189{
190    /* could reset the serial numbers, but no need to */
191
192    dvmJdwpUnregisterAll(state);
193    assert(state->eventList == NULL);
194
195    /*
196     * Should not have one of these in progress.  If the debugger went away
197     * mid-request, though, we could see this.
198     */
199    if (state->eventThreadId != 0) {
200        LOGW("WARNING: resetting state while event in progress");
201        assert(false);
202    }
203}
204
205/*
206 * Tell the JDWP thread to shut down.  Frees "state".
207 */
208void dvmJdwpShutdown(JdwpState* state)
209{
210    void* threadReturn;
211
212    if (state == NULL)
213        return;
214
215    if (dvmJdwpIsTransportDefined(state)) {
216        if (dvmJdwpIsConnected(state))
217            dvmJdwpPostVMDeath(state);
218
219        /*
220         * Close down the network to inspire the thread to halt.
221         */
222        if (gDvm.verboseShutdown)
223            ALOGD("JDWP shutting down net...");
224        dvmJdwpNetShutdown(state);
225
226        if (state->debugThreadStarted) {
227            state->run = false;
228            if (pthread_join(state->debugThreadHandle, &threadReturn) != 0) {
229                LOGW("JDWP thread join failed");
230            }
231        }
232
233        if (gDvm.verboseShutdown)
234            ALOGD("JDWP freeing netstate...");
235        dvmJdwpNetFree(state);
236        state->netState = NULL;
237    }
238    assert(state->netState == NULL);
239
240    dvmJdwpResetState(state);
241    free(state);
242}
243
244/*
245 * Are we talking to a debugger?
246 */
247bool dvmJdwpIsActive(JdwpState* state)
248{
249    return dvmJdwpIsConnected(state);
250}
251
252/*
253 * Entry point for JDWP thread.  The thread was created through the VM
254 * mechanisms, so there is a java/lang/Thread associated with us.
255 */
256static void* jdwpThreadStart(void* arg)
257{
258    JdwpState* state = (JdwpState*) arg;
259
260    ALOGV("JDWP: thread running");
261
262    /*
263     * Finish initializing "state", then notify the creating thread that
264     * we're running.
265     */
266    state->debugThreadHandle = dvmThreadSelf()->handle;
267    state->run = true;
268    android_atomic_release_store(true, &state->debugThreadStarted);
269
270    dvmDbgLockMutex(&state->threadStartLock);
271    dvmDbgCondBroadcast(&state->threadStartCond);
272    dvmDbgUnlockMutex(&state->threadStartLock);
273
274    /* set the thread state to VMWAIT so GCs don't wait for us */
275    dvmDbgThreadWaiting();
276
277    /*
278     * Loop forever if we're in server mode, processing connections.  In
279     * non-server mode, we bail out of the thread when the debugger drops
280     * us.
281     *
282     * We broadcast a notification when a debugger attaches, after we
283     * successfully process the handshake.
284     */
285    while (state->run) {
286        bool first;
287
288        if (state->params.server) {
289            /*
290             * Block forever, waiting for a connection.  To support the
291             * "timeout=xxx" option we'll need to tweak this.
292             */
293            if (!dvmJdwpAcceptConnection(state))
294                break;
295        } else {
296            /*
297             * If we're not acting as a server, we need to connect out to the
298             * debugger.  To support the "timeout=xxx" option we need to
299             * have a timeout if the handshake reply isn't received in a
300             * reasonable amount of time.
301             */
302            if (!dvmJdwpEstablishConnection(state)) {
303                /* wake anybody who was waiting for us to succeed */
304                dvmDbgLockMutex(&state->attachLock);
305                dvmDbgCondBroadcast(&state->attachCond);
306                dvmDbgUnlockMutex(&state->attachLock);
307                break;
308            }
309        }
310
311        /* prep debug code to handle the new connection */
312        dvmDbgConnected();
313
314        /* process requests until the debugger drops */
315        first = true;
316        while (true) {
317            // sanity check -- shouldn't happen?
318            if (dvmThreadSelf()->status != THREAD_VMWAIT) {
319                LOGE("JDWP thread no longer in VMWAIT (now %d); resetting",
320                    dvmThreadSelf()->status);
321                dvmDbgThreadWaiting();
322            }
323
324            if (!dvmJdwpProcessIncoming(state))     /* blocking read */
325                break;
326
327            if (first && !dvmJdwpAwaitingHandshake(state)) {
328                /* handshake worked, tell the interpreter that we're active */
329                first = false;
330
331                /* set thread ID; requires object registry to be active */
332                state->debugThreadId = dvmDbgGetThreadSelfId();
333
334                /* wake anybody who's waiting for us */
335                dvmDbgLockMutex(&state->attachLock);
336                dvmDbgCondBroadcast(&state->attachCond);
337                dvmDbgUnlockMutex(&state->attachLock);
338            }
339        }
340
341        dvmJdwpCloseConnection(state);
342
343        if (state->ddmActive) {
344            state->ddmActive = false;
345
346            /* broadcast the disconnect; must be in RUNNING state */
347            dvmDbgThreadRunning();
348            dvmDbgDdmDisconnected();
349            dvmDbgThreadWaiting();
350        }
351
352        /* release session state, e.g. remove breakpoint instructions */
353        dvmJdwpResetState(state);
354
355        /* tell the interpreter that the debugger is no longer around */
356        dvmDbgDisconnected();
357
358        /* if we had threads suspended, resume them now */
359        dvmUndoDebuggerSuspensions();
360
361        /* if we connected out, this was a one-shot deal */
362        if (!state->params.server)
363            state->run = false;
364    }
365
366    /* back to running, for thread shutdown */
367    dvmDbgThreadRunning();
368
369    ALOGV("JDWP: thread exiting");
370    return NULL;
371}
372
373
374/*
375 * Return the thread handle, or (pthread_t)0 if the debugger isn't running.
376 */
377pthread_t dvmJdwpGetDebugThread(JdwpState* state)
378{
379    if (state == NULL)
380        return 0;
381
382    return state->debugThreadHandle;
383}
384
385
386/*
387 * Support routines for waitForDebugger().
388 *
389 * We can't have a trivial "waitForDebugger" function that returns the
390 * instant the debugger connects, because we run the risk of executing code
391 * before the debugger has had a chance to configure breakpoints or issue
392 * suspend calls.  It would be nice to just sit in the suspended state, but
393 * most debuggers don't expect any threads to be suspended when they attach.
394 *
395 * There's no JDWP event we can post to tell the debugger, "we've stopped,
396 * and we like it that way".  We could send a fake breakpoint, which should
397 * cause the debugger to immediately send a resume, but the debugger might
398 * send the resume immediately or might throw an exception of its own upon
399 * receiving a breakpoint event that it didn't ask for.
400 *
401 * What we really want is a "wait until the debugger is done configuring
402 * stuff" event.  We can approximate this with a "wait until the debugger
403 * has been idle for a brief period".
404 */
405
406/*
407 * Get a notion of the current time, in milliseconds.
408 */
409s8 dvmJdwpGetNowMsec()
410{
411#ifdef HAVE_POSIX_CLOCKS
412    struct timespec now;
413    clock_gettime(CLOCK_MONOTONIC, &now);
414    return now.tv_sec * 1000LL + now.tv_nsec / 1000000LL;
415#else
416    struct timeval now;
417    gettimeofday(&now, NULL);
418    return now.tv_sec * 1000LL + now.tv_usec / 1000LL;
419#endif
420}
421
422/*
423 * Return the time, in milliseconds, since the last debugger activity.
424 *
425 * Returns -1 if no debugger is attached, or 0 if we're in the middle of
426 * processing a debugger request.
427 */
428s8 dvmJdwpLastDebuggerActivity(JdwpState* state)
429{
430    if (!gDvm.debuggerActive) {
431        ALOGD("dvmJdwpLastDebuggerActivity: no active debugger");
432        return -1;
433    }
434
435    s8 last = dvmQuasiAtomicRead64(&state->lastActivityWhen);
436
437    /* initializing or in the middle of something? */
438    if (last == 0) {
439        ALOGV("+++ last=busy");
440        return 0;
441    }
442
443    /* now get the current time */
444    s8 now = dvmJdwpGetNowMsec();
445    assert(now > last);
446
447    ALOGV("+++ debugger interval=%lld", now - last);
448    return now - last;
449}
450