summaryrefslogtreecommitdiffstats
path: root/runtime/jdwp/jdwp.h
diff options
context:
space:
mode:
Diffstat (limited to 'runtime/jdwp/jdwp.h')
-rw-r--r--runtime/jdwp/jdwp.h432
1 files changed, 432 insertions, 0 deletions
diff --git a/runtime/jdwp/jdwp.h b/runtime/jdwp/jdwp.h
new file mode 100644
index 0000000000..436525c3d0
--- /dev/null
+++ b/runtime/jdwp/jdwp.h
@@ -0,0 +1,432 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_JDWP_JDWP_H_
+#define ART_JDWP_JDWP_H_
+
+#include "base/mutex.h"
+#include "jdwp/jdwp_bits.h"
+#include "jdwp/jdwp_constants.h"
+#include "jdwp/jdwp_expand_buf.h"
+
+#include <pthread.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <string.h>
+
+struct iovec;
+
+namespace art {
+namespace mirror {
+class AbstractMethod;
+} // namespace mirror
+class Thread;
+
+namespace JDWP {
+
+/*
+ * Fundamental types.
+ *
+ * ObjectId and RefTypeId must be the same size.
+ */
+typedef uint32_t FieldId; /* static or instance field */
+typedef uint32_t MethodId; /* any kind of method, including constructors */
+typedef uint64_t ObjectId; /* any object (threadID, stringID, arrayID, etc) */
+typedef uint64_t RefTypeId; /* like ObjectID, but unique for Class objects */
+typedef uint64_t FrameId; /* short-lived stack frame ID */
+
+ObjectId ReadObjectId(const uint8_t** pBuf);
+
+static inline void SetFieldId(uint8_t* buf, FieldId val) { return Set4BE(buf, val); }
+static inline void SetMethodId(uint8_t* buf, MethodId val) { return Set4BE(buf, val); }
+static inline void SetObjectId(uint8_t* buf, ObjectId val) { return Set8BE(buf, val); }
+static inline void SetRefTypeId(uint8_t* buf, RefTypeId val) { return Set8BE(buf, val); }
+static inline void SetFrameId(uint8_t* buf, FrameId val) { return Set8BE(buf, val); }
+static inline void expandBufAddFieldId(ExpandBuf* pReply, FieldId id) { expandBufAdd4BE(pReply, id); }
+static inline void expandBufAddMethodId(ExpandBuf* pReply, MethodId id) { expandBufAdd4BE(pReply, id); }
+static inline void expandBufAddObjectId(ExpandBuf* pReply, ObjectId id) { expandBufAdd8BE(pReply, id); }
+static inline void expandBufAddRefTypeId(ExpandBuf* pReply, RefTypeId id) { expandBufAdd8BE(pReply, id); }
+static inline void expandBufAddFrameId(ExpandBuf* pReply, FrameId id) { expandBufAdd8BE(pReply, id); }
+
+/*
+ * Holds a JDWP "location".
+ */
+struct JdwpLocation {
+ JdwpTypeTag type_tag;
+ RefTypeId class_id;
+ MethodId method_id;
+ uint64_t dex_pc;
+};
+std::ostream& operator<<(std::ostream& os, const JdwpLocation& rhs)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+bool operator==(const JdwpLocation& lhs, const JdwpLocation& rhs);
+bool operator!=(const JdwpLocation& lhs, const JdwpLocation& rhs);
+
+/*
+ * How we talk to the debugger.
+ */
+enum JdwpTransportType {
+ kJdwpTransportUnknown = 0,
+ kJdwpTransportSocket, // transport=dt_socket
+ kJdwpTransportAndroidAdb, // transport=dt_android_adb
+};
+std::ostream& operator<<(std::ostream& os, const JdwpTransportType& rhs);
+
+struct JdwpOptions {
+ JdwpTransportType transport;
+ bool server;
+ bool suspend;
+ std::string host;
+ uint16_t port;
+};
+
+struct JdwpEvent;
+struct JdwpNetStateBase;
+struct ModBasket;
+struct Request;
+
+/*
+ * State for JDWP functions.
+ */
+struct JdwpState {
+ /*
+ * Perform one-time initialization.
+ *
+ * Among other things, this binds to a port to listen for a connection from
+ * the debugger.
+ *
+ * Returns a newly-allocated JdwpState struct on success, or NULL on failure.
+ */
+ static JdwpState* Create(const JdwpOptions* options)
+ LOCKS_EXCLUDED(Locks::mutator_lock_);
+
+ ~JdwpState();
+
+ /*
+ * Returns "true" if a debugger or DDM is connected.
+ */
+ bool IsActive();
+
+ /**
+ * Returns the Thread* for the JDWP daemon thread.
+ */
+ Thread* GetDebugThread();
+
+ /*
+ * Get time, in milliseconds, since the last debugger activity.
+ */
+ int64_t LastDebuggerActivity();
+
+ void ExitAfterReplying(int exit_status);
+
+ /*
+ * When we hit a debugger event that requires suspension, it's important
+ * that we wait for the thread to suspend itself before processing any
+ * additional requests. (Otherwise, if the debugger immediately sends a
+ * "resume thread" command, the resume might arrive before the thread has
+ * suspended itself.)
+ *
+ * The thread should call the "set" function before sending the event to
+ * the debugger. The main JDWP handler loop calls "get" before processing
+ * an event, and will wait for thread suspension if it's set. Once the
+ * thread has suspended itself, the JDWP handler calls "clear" and
+ * continues processing the current event. This works in the suspend-all
+ * case because the event thread doesn't suspend itself until everything
+ * else has suspended.
+ *
+ * It's possible that multiple threads could encounter thread-suspending
+ * events at the same time, so we grab a mutex in the "set" call, and
+ * release it in the "clear" call.
+ */
+ //ObjectId GetWaitForEventThread();
+ void SetWaitForEventThread(ObjectId threadId);
+ void ClearWaitForEventThread();
+
+ /*
+ * These notify the debug code that something interesting has happened. This
+ * could be a thread starting or ending, an exception, or an opportunity
+ * for a breakpoint. These calls do not mean that an event the debugger
+ * is interested has happened, just that something has happened that the
+ * debugger *might* be interested in.
+ *
+ * The item of interest may trigger multiple events, some or all of which
+ * are grouped together in a single response.
+ *
+ * The event may cause the current thread or all threads (except the
+ * JDWP support thread) to be suspended.
+ */
+
+ /*
+ * The VM has finished initializing. Only called when the debugger is
+ * connected at the time initialization completes.
+ */
+ bool PostVMStart() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /*
+ * A location of interest has been reached. This is used for breakpoints,
+ * single-stepping, and method entry/exit. (JDWP requires that these four
+ * events are grouped together in a single response.)
+ *
+ * In some cases "*pLoc" will just have a method and class name, e.g. when
+ * issuing a MethodEntry on a native method.
+ *
+ * "eventFlags" indicates the types of events that have occurred.
+ */
+ bool PostLocationEvent(const JdwpLocation* pLoc, ObjectId thisPtr, int eventFlags)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /*
+ * An exception has been thrown.
+ *
+ * Pass in a zeroed-out "*pCatchLoc" if the exception wasn't caught.
+ */
+ bool PostException(const JdwpLocation* pThrowLoc, ObjectId excepId, RefTypeId excepClassId,
+ const JdwpLocation* pCatchLoc, ObjectId thisPtr)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /*
+ * A thread has started or stopped.
+ */
+ bool PostThreadChange(ObjectId threadId, bool start)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /*
+ * Class has been prepared.
+ */
+ bool PostClassPrepare(JdwpTypeTag tag, RefTypeId refTypeId, const std::string& signature,
+ int status)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /*
+ * The VM is about to stop.
+ */
+ bool PostVMDeath();
+
+ // Called if/when we realize we're talking to DDMS.
+ void NotifyDdmsActive() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /*
+ * Send up a chunk of DDM data.
+ */
+ void DdmSendChunkV(uint32_t type, const iovec* iov, int iov_count)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ bool HandlePacket();
+
+ void SendRequest(ExpandBuf* pReq);
+
+ void ResetState()
+ LOCKS_EXCLUDED(event_list_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /* atomic ops to get next serial number */
+ uint32_t NextRequestSerial();
+ uint32_t NextEventSerial();
+
+ void Run()
+ LOCKS_EXCLUDED(Locks::mutator_lock_,
+ Locks::thread_suspend_count_lock_);
+
+ /*
+ * Register an event by adding it to the event list.
+ *
+ * "*pEvent" must be storage allocated with jdwpEventAlloc(). The caller
+ * may discard its pointer after calling this.
+ */
+ JdwpError RegisterEvent(JdwpEvent* pEvent)
+ LOCKS_EXCLUDED(event_list_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /*
+ * Unregister an event, given the requestId.
+ */
+ void UnregisterEventById(uint32_t requestId)
+ LOCKS_EXCLUDED(event_list_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ /*
+ * Unregister all events.
+ */
+ void UnregisterAll()
+ LOCKS_EXCLUDED(event_list_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ private:
+ explicit JdwpState(const JdwpOptions* options);
+ void ProcessRequest(Request& request, ExpandBuf* pReply);
+ bool InvokeInProgress();
+ bool IsConnected();
+ void SuspendByPolicy(JdwpSuspendPolicy suspend_policy, JDWP::ObjectId thread_self_id)
+ LOCKS_EXCLUDED(Locks::mutator_lock_);
+ void SendRequestAndPossiblySuspend(ExpandBuf* pReq, JdwpSuspendPolicy suspend_policy,
+ ObjectId threadId)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void CleanupMatchList(JdwpEvent** match_list,
+ int match_count)
+ EXCLUSIVE_LOCKS_REQUIRED(event_list_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void EventFinish(ExpandBuf* pReq);
+ void FindMatchingEvents(JdwpEventKind eventKind,
+ ModBasket* basket,
+ JdwpEvent** match_list,
+ int* pMatchCount)
+ EXCLUSIVE_LOCKS_REQUIRED(event_list_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void UnregisterEvent(JdwpEvent* pEvent)
+ EXCLUSIVE_LOCKS_REQUIRED(event_list_lock_)
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+ void SendBufferedRequest(uint32_t type, const iovec* iov, int iov_count);
+
+ public: // TODO: fix privacy
+ const JdwpOptions* options_;
+
+ private:
+ /* wait for creation of the JDWP thread */
+ Mutex thread_start_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ ConditionVariable thread_start_cond_ GUARDED_BY(thread_start_lock_);
+
+ pthread_t pthread_;
+ Thread* thread_;
+
+ volatile int32_t debug_thread_started_ GUARDED_BY(thread_start_lock_);
+ ObjectId debug_thread_id_;
+
+ private:
+ bool run;
+
+ public: // TODO: fix privacy
+ JdwpNetStateBase* netState;
+
+ private:
+ // For wait-for-debugger.
+ Mutex attach_lock_ ACQUIRED_AFTER(thread_start_lock_);
+ ConditionVariable attach_cond_ GUARDED_BY(attach_lock_);
+
+ // Time of last debugger activity, in milliseconds.
+ int64_t last_activity_time_ms_;
+
+ // Global counters and a mutex to protect them.
+ Mutex serial_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ uint32_t request_serial_ GUARDED_BY(serial_lock_);
+ uint32_t event_serial_ GUARDED_BY(serial_lock_);
+
+ // Linked list of events requested by the debugger (breakpoints, class prep, etc).
+ Mutex event_list_lock_;
+ JdwpEvent* event_list_ GUARDED_BY(event_list_lock_);
+ int event_list_size_ GUARDED_BY(event_list_lock_); // Number of elements in event_list_.
+
+ // Used to synchronize suspension of the event thread (to avoid receiving "resume"
+ // events before the thread has finished suspending itself).
+ Mutex event_thread_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
+ ConditionVariable event_thread_cond_ GUARDED_BY(event_thread_lock_);
+ ObjectId event_thread_id_;
+
+ bool ddm_is_active_;
+
+ bool should_exit_;
+ int exit_status_;
+};
+
+std::string DescribeField(const FieldId& field_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+std::string DescribeMethod(const MethodId& method_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+std::string DescribeRefTypeId(const RefTypeId& ref_type_id) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+class Request {
+ public:
+ Request(const uint8_t* bytes, uint32_t available);
+ ~Request();
+
+ std::string ReadUtf8String();
+
+ // Helper function: read a variable-width value from the input buffer.
+ uint64_t ReadValue(size_t width);
+
+ int32_t ReadSigned32(const char* what);
+
+ uint32_t ReadUnsigned32(const char* what);
+
+ FieldId ReadFieldId() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ MethodId ReadMethodId() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ ObjectId ReadObjectId(const char* specific_kind);
+
+ ObjectId ReadArrayId();
+
+ ObjectId ReadObjectId();
+
+ ObjectId ReadThreadId();
+
+ ObjectId ReadThreadGroupId();
+
+ RefTypeId ReadRefTypeId() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ FrameId ReadFrameId();
+
+ template <typename T> T ReadEnum1(const char* specific_kind) {
+ T value = static_cast<T>(Read1());
+ VLOG(jdwp) << " " << specific_kind << " " << value;
+ return value;
+ }
+
+ JdwpTag ReadTag();
+
+ JdwpTypeTag ReadTypeTag();
+
+ JdwpLocation ReadLocation() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ JdwpModKind ReadModKind();
+
+ //
+ // Return values from this JDWP packet's header.
+ //
+ size_t GetLength() { return byte_count_; }
+ uint32_t GetId() { return id_; }
+ uint8_t GetCommandSet() { return command_set_; }
+ uint8_t GetCommand() { return command_; }
+
+ // Returns the number of bytes remaining.
+ size_t size() { return end_ - p_; }
+
+ // Returns a pointer to the next byte.
+ const uint8_t* data() { return p_; }
+
+ void Skip(size_t count) { p_ += count; }
+
+ void CheckConsumed();
+
+ private:
+ uint8_t Read1();
+ uint16_t Read2BE();
+ uint32_t Read4BE();
+ uint64_t Read8BE();
+
+ uint32_t byte_count_;
+ uint32_t id_;
+ uint8_t command_set_;
+ uint8_t command_;
+
+ const uint8_t* p_;
+ const uint8_t* end_;
+
+ DISALLOW_COPY_AND_ASSIGN(Request);
+};
+
+} // namespace JDWP
+
+} // namespace art
+
+#endif // ART_JDWP_JDWP_H_