Methods Summary |
---|
private void | addRequestId(int id, ChunkHandler handler)
synchronized (mOutstandingReqs) {
if (Log.Config.LOGV) Log.v("ddms",
"Adding req 0x" + Integer.toHexString(id) +" to set");
mOutstandingReqs.put(id, handler);
}
|
void | close(boolean notify)Close the client socket channel. If there is a debugger associated
with us, close that too.
Closing a channel automatically unregisters it from the selector.
However, we have to iterate through the selector loop before it
actually lets them go and allows the file descriptors to close.
The caller is expected to manage that.
Log.i("ddms", "Closing " + this.toString());
mOutstandingReqs.clear();
try {
if (mChan != null) {
mChan.close();
mChan = null;
}
if (mDebugger != null) {
mDebugger.close();
mDebugger = null;
}
}
catch (IOException ioe) {
Log.w("ddms", "failed to close " + this);
// swallow it -- not much else to do
}
mDevice.removeClient(this, notify);
|
synchronized boolean | ddmSeen()The MonitorThread calls this when it sees a DDM request or reply.
If we haven't seen a DDM packet before, we advance the state to
ST_READY and return "false". Otherwise, just return true.
The idea is to let the MonitorThread know when we first see a DDM
packet, so we can send a broadcast to the handlers when a client
connection is made. This method is synchronized so that we only
send the broadcast once.
if (mConnState == ST_NEED_DDM_PKT) {
mConnState = ST_READY;
return false;
} else if (mConnState != ST_READY) {
Log.w("ddms", "WEIRD: in ddmSeen with state=" + mConnState);
}
return true;
|
public void | enableAllocationTracker(boolean enable)Enables or disables the Allocation tracker for this client.
If enabled, the VM will start tracking allocation informations. A call to
{@link #requestAllocationDetails()} will make the VM sends the information about all the
allocations that happened between the enabling and the request.
try {
HandleHeap.sendREAE(this, enable);
} catch (IOException e) {
Log.e("ddmlib", e);
}
|
public void | executeGarbageCollector()Forces the client to execute its garbage collector.
try {
HandleHeap.sendHPGC(this);
} catch (IOException ioe) {
Log.w("ddms", "Send of HPGC message failed");
// ignore
}
|
void | forwardPacketToDebugger(JdwpPacket packet)Forward the packet to the debugger (if still connected to one).
Consumes the packet.
Debugger dbg = mDebugger;
if (dbg == null) {
Log.i("ddms", "Discarding packet");
packet.consume();
} else {
dbg.sendAndConsume(packet);
}
|
public ClientData | getClientData()Returns the {@link ClientData} object containing this client information.
return mClientData;
|
Debugger | getDebugger()Return the Debugger object associated with this client.
return mDebugger;
|
public int | getDebuggerListenPort()Returns the debugger port for this client.
return mDebuggerListenPort;
|
public Device | getDevice()Returns the {@link Device} on which this Client is running.
return mDevice;
|
JdwpPacket | getJdwpPacket()Return information for the first full JDWP packet in the buffer.
If we don't yet have a full packet, return null.
If we haven't yet received the JDWP handshake, we watch for it here
and consume it without admitting to have done so. Upon receipt
we send out the "HELO" message, which is why this can throw an
IOException.
/*
* On entry, the data starts at offset 0 and ends at "position".
* "limit" is set to the buffer capacity.
*/
if (mConnState == ST_AWAIT_SHAKE) {
/*
* The first thing we get from the client is a response to our
* handshake. It doesn't look like a packet, so we have to
* handle it specially.
*/
int result;
result = JdwpPacket.findHandshake(mReadBuffer);
//Log.v("ddms", "findHand: " + result);
switch (result) {
case JdwpPacket.HANDSHAKE_GOOD:
Log.i("ddms",
"Good handshake from client, sending HELO to " + mClientData.getPid());
JdwpPacket.consumeHandshake(mReadBuffer);
mConnState = ST_NEED_DDM_PKT;
HandleHello.sendHELO(this, SERVER_PROTOCOL_VERSION);
// see if we have another packet in the buffer
return getJdwpPacket();
case JdwpPacket.HANDSHAKE_BAD:
Log.i("ddms", "Bad handshake from client");
if (MonitorThread.getInstance().getRetryOnBadHandshake()) {
// we should drop the client, but also attempt to reopen it.
// This is done by the DeviceMonitor.
mDevice.getMonitor().addClientToDropAndReopen(this,
IDebugPortProvider.NO_STATIC_PORT);
} else {
// mark it as bad, close the socket, and don't retry
mConnState = ST_NOT_JDWP;
close(true /* notify */);
}
break;
case JdwpPacket.HANDSHAKE_NOTYET:
Log.i("ddms", "No handshake from client yet.");
break;
default:
Log.e("ddms", "Unknown packet while waiting for client handshake");
}
return null;
} else if (mConnState == ST_NEED_DDM_PKT ||
mConnState == ST_NOT_DDM ||
mConnState == ST_READY) {
/*
* Normal packet traffic.
*/
if (mReadBuffer.position() != 0) {
if (Log.Config.LOGV) Log.v("ddms",
"Checking " + mReadBuffer.position() + " bytes");
}
return JdwpPacket.findPacket(mReadBuffer);
} else {
/*
* Not expecting data when in this state.
*/
Log.e("ddms", "Receiving data in state = " + mConnState);
}
return null;
|
public boolean | isDdmAware()Returns true if the client VM is DDM-aware.
Calling here is only allowed after the connection has been
established.
switch (mConnState) {
case ST_INIT:
case ST_NOT_JDWP:
case ST_AWAIT_SHAKE:
case ST_NEED_DDM_PKT:
case ST_NOT_DDM:
case ST_ERROR:
case ST_DISCONNECTED:
return false;
case ST_READY:
return true;
default:
assert false;
return false;
}
|
public boolean | isDebuggerAttached()Returns true if a debugger is currently attached to the client.
return mDebugger.isDebuggerAttached();
|
public boolean | isHeapUpdateEnabled()Returns whether the heap update is enabled.
return mHeapUpdateEnabled;
|
ChunkHandler | isResponseToUs(int id)Determine whether this is a response to a request we sent earlier.
If so, return the ChunkHandler responsible.
synchronized (mOutstandingReqs) {
ChunkHandler handler = mOutstandingReqs.get(id);
if (handler != null) {
if (Log.Config.LOGV) Log.v("ddms",
"Found 0x" + Integer.toHexString(id)
+ " in request set - " + handler);
return handler;
}
}
return null;
|
public boolean | isSelectedClient()Returns whether this client is the current selected client, accepting debugger connection
on the "selected debugger port".
MonitorThread monitorThread = MonitorThread.getInstance();
if (monitorThread != null) {
return monitorThread.getSelectedClient() == this;
}
return false;
|
public boolean | isThreadUpdateEnabled()Returns whether the thread update is enabled.
return mThreadUpdateEnabled;
|
public boolean | isValid()Returns whether this {@link Client} has a valid connection to the application VM.
return mChan != null;
|
public void | kill()Sends a kill message to the VM.
try {
HandleExit.sendEXIT(this, 1);
} catch (IOException ioe) {
Log.w("ddms", "Send of EXIT message failed");
// ignore
}
|
void | listenForDebugger(int listenPort)Tell the client to open a server socket channel and listen for
connections on the specified port.
mDebuggerListenPort = listenPort;
mDebugger = new Debugger(this, listenPort);
|
void | packetFailed(JdwpPacket reply)An earlier request resulted in a failure. This is the expected
response to a HELO message when talking to a non-DDM client.
if (mConnState == ST_NEED_DDM_PKT) {
Log.i("ddms", "Marking " + this + " as non-DDM client");
mConnState = ST_NOT_DDM;
} else if (mConnState != ST_NOT_DDM) {
Log.w("ddms", "WEIRD: got JDWP failure packet on DDM req");
}
|
void | read()Read data from our channel.
This is called when data is known to be available, and we don't yet
have a full packet in the buffer. If the buffer is at capacity,
expand it.
int count;
if (mReadBuffer.position() == mReadBuffer.capacity()) {
if (mReadBuffer.capacity() * 2 > MAX_BUF_SIZE) {
Log.e("ddms", "Exceeded MAX_BUF_SIZE!");
throw new BufferOverflowException();
}
Log.d("ddms", "Expanding read buffer to "
+ mReadBuffer.capacity() * 2);
ByteBuffer newBuffer = ByteBuffer.allocate(mReadBuffer.capacity() * 2);
// copy entire buffer to new buffer
mReadBuffer.position(0);
newBuffer.put(mReadBuffer); // leaves "position" at end of copied
mReadBuffer = newBuffer;
}
count = mChan.read(mReadBuffer);
if (count < 0)
throw new IOException("read failed");
if (Log.Config.LOGV) Log.v("ddms", "Read " + count + " bytes from " + this);
//Log.hexDump("ddms", Log.DEBUG, mReadBuffer.array(),
// mReadBuffer.arrayOffset(), mReadBuffer.position());
|
void | register(java.nio.channels.Selector sel)Registers the client with a Selector.
if (mChan != null) {
mChan.register(sel, SelectionKey.OP_READ, this);
}
|
void | removeRequestId(int id)
synchronized (mOutstandingReqs) {
if (Log.Config.LOGV) Log.v("ddms",
"Removing req 0x" + Integer.toHexString(id) + " from set");
mOutstandingReqs.remove(id);
}
//Log.w("ddms", "Request " + Integer.toHexString(id)
// + " could not be removed from " + this);
|
public void | requestAllocationDetails()Sends a request to the VM to send the information about all the allocations that have
happened since the call to {@link #enableAllocationTracker(boolean)} with enable
set to null . This is asynchronous.
The allocation information can be accessed by {@link ClientData#getAllocations()}.
The notification that the new data is available will be received through
{@link IClientChangeListener#clientChanged(Client, int)} with a changeMask
containing the mask {@link #CHANGE_HEAP_ALLOCATIONS}.
try {
HandleHeap.sendREAL(this);
} catch (IOException e) {
Log.e("ddmlib", e);
}
|
public void | requestAllocationStatus()Sends a request to the VM to send the enable status of the allocation tracking.
This is asynchronous.
The allocation status can be accessed by {@link ClientData#getAllocationStatus()}.
The notification that the new status is available will be received through
{@link IClientChangeListener#clientChanged(Client, int)} with a changeMask
containing the mask {@link #CHANGE_HEAP_ALLOCATION_STATUS}.
try {
HandleHeap.sendREAQ(this);
} catch (IOException e) {
Log.e("ddmlib", e);
}
|
public boolean | requestNativeHeapInformation()Sends a native heap update request. this is asynchronous.
The native heap info can be accessed by {@link ClientData#getNativeAllocationList()}.
The notification that the new data is available will be received through
{@link IClientChangeListener#clientChanged(Client, int)} with a changeMask
containing the mask {@link #CHANGE_NATIVE_HEAP_DATA}.
try {
HandleNativeHeap.sendNHGT(this);
return true;
} catch (IOException e) {
Log.e("ddmlib", e);
}
return false;
|
public void | requestThreadStackTrace(int threadId)Sends a thread stack trace update request. This is asynchronous.
The thread info can be accessed by {@link ClientData#getThreads()} and
{@link ThreadInfo#getStackTrace()}.
The notification that the new data is available
will be received through {@link IClientChangeListener#clientChanged(Client, int)}
with a changeMask containing the mask {@link #CHANGE_THREAD_STACKTRACE}.
HandleThread.requestThreadStackCallRefresh(this, threadId);
|
public void | requestThreadUpdate()Sends a thread update request. This is asynchronous.
The thread info can be accessed by {@link ClientData#getThreads()}. The notification
that the new data is available will be received through
{@link IClientChangeListener#clientChanged(Client, int)} with a changeMask
containing the mask {@link #CHANGE_THREAD_DATA}.
HandleThread.requestThreadUpdate(this);
|
void | sendAndConsume(JdwpPacket packet)Send a non-DDM packet to the client.
Equivalent to sendAndConsume(packet, null).
sendAndConsume(packet, null);
|
void | sendAndConsume(JdwpPacket packet, ChunkHandler replyHandler)Send a DDM packet to the client.
Ideally, we can do this with a single channel write. If that doesn't
happen, we have to prevent anybody else from writing to the channel
until this packet completes, so we synchronize on the channel.
Another goal is to avoid unnecessary buffer copies, so we write
directly out of the JdwpPacket's ByteBuffer.
if (mChan == null) {
// can happen for e.g. THST packets
Log.v("ddms", "Not sending packet -- client is closed");
return;
}
if (replyHandler != null) {
/*
* Add the ID to the list of outstanding requests. We have to do
* this before sending the packet, in case the response comes back
* before our thread returns from the packet-send function.
*/
addRequestId(packet.getId(), replyHandler);
}
synchronized (mChan) {
try {
packet.writeAndConsume(mChan);
}
catch (IOException ioe) {
removeRequestId(packet.getId());
throw ioe;
}
}
|
boolean | sendHandshake()Initiate the JDWP handshake.
On failure, closes the socket and returns false.
assert mWriteBuffer.position() == 0;
try {
// assume write buffer can hold 14 bytes
JdwpPacket.putHandshake(mWriteBuffer);
int expectedLen = mWriteBuffer.position();
mWriteBuffer.flip();
if (mChan.write(mWriteBuffer) != expectedLen)
throw new IOException("partial handshake write");
}
catch (IOException ioe) {
Log.e("ddms-client", "IO error during handshake: " + ioe.getMessage());
mConnState = ST_ERROR;
close(true /* notify */);
return false;
}
finally {
mWriteBuffer.clear();
}
mConnState = ST_AWAIT_SHAKE;
return true;
|
public void | setAsSelectedClient()Sets the client to accept debugger connection on the "selected debugger port".
MonitorThread monitorThread = MonitorThread.getInstance();
if (monitorThread != null) {
monitorThread.setSelectedClient(this);
}
|
public void | setHeapUpdateEnabled(boolean enabled)Enables or disables the heap update.
If true , any GC will cause the client to send its heap information.
The heap information can be accessed by {@link ClientData#getVmHeapData()}.
The notification that the new data is available
will be received through {@link IClientChangeListener#clientChanged(Client, int)}
with a changeMask containing the value {@link #CHANGE_HEAP_DATA}.
mHeapUpdateEnabled = enabled;
try {
HandleHeap.sendHPIF(this,
enabled ? HandleHeap.HPIF_WHEN_EVERY_GC : HandleHeap.HPIF_WHEN_NEVER);
HandleHeap.sendHPSG(this,
enabled ? HandleHeap.WHEN_GC : HandleHeap.WHEN_DISABLE,
HandleHeap.WHAT_MERGE);
} catch (IOException ioe) {
// ignore it here; client will clean up shortly
}
update(CHANGE_HEAP_MODE);
|
public void | setThreadUpdateEnabled(boolean enabled)Enables or disables the thread update.
If true the VM will be able to send thread information. Thread information
must be requested with {@link #requestThreadUpdate()}.
mThreadUpdateEnabled = enabled;
if (enabled == false) {
mClientData.clearThreads();
}
try {
HandleThread.sendTHEN(this, enabled);
} catch (IOException ioe) {
// ignore it here; client will clean up shortly
ioe.printStackTrace();
}
update(CHANGE_THREAD_MODE);
|
public java.lang.String | toString()Returns a string representation of the {@link Client} object.
return "[Client pid: " + mClientData.getPid() + "]";
|
void | update(int changeMask)
mDevice.update(this, changeMask);
|