1 /*
2  * Copyright (C) 2012 The Android Open Source Project
3  *
4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
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.ide.eclipse.ndk.internal.launch;
18 
19 import com.android.ddmlib.IDevice;
20 import com.android.ddmlib.IShellOutputReceiver;
21 
22 import java.util.concurrent.CountDownLatch;
23 import java.util.concurrent.atomic.AtomicBoolean;
24 
25 /**
26  * The {@link GdbServerTask} launches gdbserver on the given device and attaches it to
27  * provided pid.
28  */
29 public class GdbServerTask implements Runnable {
30     private IDevice mDevice;
31     private String mRunAs;
32     private String mSocket;
33     private int mPid;
34     private CountDownLatch mAttachLatch;
35 
36     private GdbServerOutputReceiver mOutputReceiver;
37     private Exception mLaunchException;
38 
39     private AtomicBoolean mCancelled = new AtomicBoolean(false);
40     private AtomicBoolean mHasCompleted = new AtomicBoolean(false);
41 
42     /**
43      * Construct a gdbserver task.
44      * @param device device to run gdbserver on
45      * @param runAsPackage name of the package in which gdbserver resides
46      * @param socketName name of the local socket on which the server will listen
47      * @param pid pid of task to attach to
48      * @param attachLatch latch to notify when gdbserver gets attached to the task
49      */
GdbServerTask(IDevice device, String runAsPackage, String socketName, int pid, CountDownLatch attachLatch)50     public GdbServerTask(IDevice device, String runAsPackage, String socketName, int pid,
51             CountDownLatch attachLatch) {
52         mDevice = device;
53         mRunAs = runAsPackage;
54         mSocket = socketName;
55         mPid = pid;
56         mAttachLatch = attachLatch;
57 
58         mOutputReceiver = new GdbServerOutputReceiver();
59     }
60 
61     /**
62      * Runs gdbserver on the device and connects to the given task. If gdbserver manages to
63      * successfully attach itself to the process, then it counts down on its attach latch.
64      */
65     @Override
run()66     public void run() {
67         // Launch gdbserver on the device.
68         String command = String.format("run-as %s lib/gdbserver +%s --attach %d",
69                 mRunAs, mSocket, mPid);
70         try {
71             mDevice.executeShellCommand(command, mOutputReceiver, 0);
72         } catch (Exception e) {
73             mLaunchException = e;
74         }
75     }
76 
77     /** Returns any exceptions that might have occurred while launching gdbserver. */
getLaunchException()78     public Exception getLaunchException() {
79         return mLaunchException;
80     }
81 
82     /** Cancel gdbserver if it is running. */
setCancelled()83     public void setCancelled() {
84         mCancelled.set(true);
85     }
86 
getShellOutput()87     public String getShellOutput() {
88         return mOutputReceiver.getOutput();
89     }
90 
91     private class GdbServerOutputReceiver implements IShellOutputReceiver {
92         private StringBuffer mOutput = new StringBuffer(100);
93 
94         @Override
addOutput(byte[] data, int offset, int length)95         public synchronized void addOutput(byte[] data, int offset, int length) {
96             mOutput.append(new String(data, offset, length));
97 
98             // notify other threads that gdbserver has attached to the task
99             if (mOutput.toString().contains("Attached")) {
100                 mAttachLatch.countDown();
101             }
102         }
103 
104         @Override
flush()105         public void flush() {
106             mHasCompleted.set(true);
107         }
108 
109         @Override
isCancelled()110         public boolean isCancelled() {
111             return mCancelled.get();
112         }
113 
getOutput()114         public synchronized String getOutput() {
115             return mOutput.toString();
116         }
117     }
118 }
119