diff --git a/BUILD.gn b/BUILD.gn index 0fd54743..5470ce02 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -13,6 +13,9 @@ import("//build/ohos.gni") HDC_PATH = "//developtools/hdc_standard" +declare_args() { + js_jdwp_connect = true +} hdc_common_sources = [ "${HDC_PATH}/src/common/async_cmd.cpp", @@ -51,6 +54,9 @@ ohos_executable("hdcd") { defines = [ "HARMONY_PROJECT" ] + if (js_jdwp_connect) { + defines += [ "JS_JDWP_CONNECT" ] + } configs = [ ":hdc_config" ] deps = [ diff --git a/src/common/define.h b/src/common/define.h index 46b5ca3d..4696d121 100644 --- a/src/common/define.h +++ b/src/common/define.h @@ -89,6 +89,7 @@ const string CMDSTR_APP_INSTALL = "install"; const string CMDSTR_APP_UNINSTALL = "uninstall"; const string CMDSTR_APP_SIDELOAD = "sideload"; const string CMDSTR_LIST_JDWP = "jpid"; +const string CMDSTR_TRACK_JDWP = "track-jpid"; const string CMDSTR_INNER_ENABLE_KEEPALIVE = "alive"; } // namespace Hdc #endif // HDC_DEFINE_H diff --git a/src/common/define_plus.h b/src/common/define_plus.h index d2bc938d..2309682a 100644 --- a/src/common/define_plus.h +++ b/src/common/define_plus.h @@ -126,6 +126,7 @@ enum HdcCommand { CMD_UNITY_BUGREPORT_INIT, CMD_UNITY_BUGREPORT_DATA, CMD_UNITY_JPID, + CMD_TRACK_JPID, // Shell commands types CMD_SHELL_INIT = 2000, CMD_SHELL_DATA, diff --git a/src/daemon/daemon.cpp b/src/daemon/daemon.cpp index b3a28a44..f22fc1e8 100644 --- a/src/daemon/daemon.cpp +++ b/src/daemon/daemon.cpp @@ -107,6 +107,7 @@ bool HdcDaemon::RedirectToTask(HTaskInfo hTaskInfo, HSession hSession, const uin case CMD_UNITY_TERMINATE: case CMD_UNITY_BUGREPORT_INIT: case CMD_UNITY_JPID: + case CMD_TRACK_JPID: ret = TaskCommandDispatch(hTaskInfo, TYPE_UNITY, command, payload, payloadSize); break; case CMD_SHELL_INIT: diff --git a/src/daemon/daemon_unity.cpp b/src/daemon/daemon_unity.cpp index ed2d0b8b..98566d43 100644 --- a/src/daemon/daemon_unity.cpp +++ b/src/daemon/daemon_unity.cpp @@ -238,6 +238,17 @@ inline bool HdcDaemonUnity::ListJdwpProcess(void *daemonIn) return true; } +inline bool HdcDaemonUnity::TrackJdwpProcess(void *daemonIn) +{ + HdcDaemon *daemon = (HdcDaemon *)daemonIn; + if (!(((HdcJdwp *)daemon->clsJdwp)->CreateJdwpTracker(taskInfo))) { + string result = MESSAGE_FAIL; + LogMsg(MSG_OK, result.c_str()); + return false; + } + return true; +} + bool HdcDaemonUnity::CommandDispatch(const uint16_t command, uint8_t *payload, const int payloadSize) { bool ret = true; @@ -292,6 +303,12 @@ bool HdcDaemonUnity::CommandDispatch(const uint16_t command, uint8_t *payload, c ListJdwpProcess(daemon); break; } + case CMD_TRACK_JPID: { + if (!TrackJdwpProcess(daemon)) { + ret = false; + } + break; + } default: break; } diff --git a/src/daemon/daemon_unity.h b/src/daemon/daemon_unity.h index c816487f..01b9d48b 100644 --- a/src/daemon/daemon_unity.h +++ b/src/daemon/daemon_unity.h @@ -36,6 +36,7 @@ private: bool GetHiLog(const char *cmd); bool ListJdwpProcess(void *daemonIn); bool AsyncCmdOut(bool finish, int64_t exitStatus, const string result); + bool TrackJdwpProcess(void *daemonIn); const string rebootProperty = "sys.powerctl"; AsyncCmd asyncCommand; diff --git a/src/daemon/jdwp.cpp b/src/daemon/jdwp.cpp index 7a3b1526..8e8d0089 100644 --- a/src/daemon/jdwp.cpp +++ b/src/daemon/jdwp.cpp @@ -13,8 +13,16 @@ * limitations under the License. */ #include "jdwp.h" +#include +#include namespace Hdc { +#ifdef JS_JDWP_CONNECT +static constexpr uint32_t JPID_TRACK_LIST_SIZE = 1024 * 4; +#else +static constexpr uint32_t JPID_TRACK_LIST_SIZE = 1024; +#endif // JS_JDWP_CONNECT + HdcJdwp::HdcJdwp(uv_loop_t *loopIn) { Base::ZeroStruct(listenPipe); @@ -22,11 +30,17 @@ HdcJdwp::HdcJdwp(uv_loop_t *loopIn) loop = loopIn; refCount = 0; uv_rwlock_init(&lockMapContext); + uv_rwlock_init(&lockJdwpTrack); } HdcJdwp::~HdcJdwp() { + if (awakenPollFd >= 0) { + close(awakenPollFd); + awakenPollFd = -1; + } uv_rwlock_destroy(&lockMapContext); + uv_rwlock_destroy(&lockJdwpTrack); } bool HdcJdwp::ReadyForRelease() @@ -36,6 +50,8 @@ bool HdcJdwp::ReadyForRelease() void HdcJdwp::Stop() { + stop = true; + WakePollThread(); auto funcListenPipeClose = [](uv_handle_t *handle) -> void { HdcJdwp *thisClass = (HdcJdwp *)handle->data; --thisClass->refCount; @@ -81,27 +97,58 @@ void HdcJdwp::FreeContext(HCtxJdwp ctx) void HdcJdwp::ReadStream(uv_stream_t *pipe, ssize_t nread, const uv_buf_t *buf) { bool ret = true; - HCtxJdwp ctxJdwp = (HCtxJdwp)pipe->data; - HdcJdwp *thisClass = (HdcJdwp *)ctxJdwp->thisClass; - char *p = ctxJdwp->buf; - uint32_t pid = 0; - - if (nread == UV_ENOBUFS) { // It is definite enough, usually only 4 bytes + if (nread == UV_ENOBUFS) { // It is definite enough, usually only 4 bytes ret = false; WRITE_LOG(LOG_DEBUG, "HdcJdwp::ReadStream IOBuf max"); } else if (nread == 0) { return; - } else if (nread < 0 || nread != 4) { // 4 : 4 bytes +#ifdef JS_JDWP_CONNECT + } else if (nread < 0 || nread < JS_PKG_MIN_SIZE) { +#else + } else if (nread < 0 || nread != 4) { // 4 : 4 bytes +#endif // JS_JDWP_CONNECT ret = false; - WRITE_LOG(LOG_DEBUG, "HdcJdwp::ReadStream program exit pid:%d", ctxJdwp->pid); + WRITE_LOG(LOG_DEBUG, "HdcJdwp::ReadStream invalid package nread:%d.", nread); } + + HCtxJdwp ctxJdwp = (HCtxJdwp)pipe->data; + HdcJdwp *thisClass = (HdcJdwp *)ctxJdwp->thisClass; if (ret) { - pid = atoi(p); + uint32_t pid = 0; + char *p = ctxJdwp->buf; + if (nread == sizeof(uint32_t)) { // Java: pid + pid = atoi(p); + } else { // JS:pid PkgName +#ifdef JS_JDWP_CONNECT + struct JsMsgHeader *jsMsg = (struct JsMsgHeader *)p; + if (jsMsg->msgLen == nread) { + pid = jsMsg->pid; + string pkgName = + string((char *)p + sizeof(JsMsgHeader), jsMsg->msgLen - sizeof(JsMsgHeader)); + ctxJdwp->pkgName = pkgName; + } else { + ret = false; + WRITE_LOG(LOG_DEBUG, "HdcJdwp::ReadStream invalid js package size %d:%d.", + jsMsg->msgLen, nread); + } +#endif // JS_JDWP_CONNECT + } if (pid > 0) { - WRITE_LOG(LOG_DEBUG, "JDWP accept pid:%d", pid); ctxJdwp->pid = pid; +#ifdef JS_JDWP_CONNECT + WRITE_LOG(LOG_DEBUG, "JDWP accept pid:%d-pkg:%s", pid, ctxJdwp->pkgName.c_str()); +#else + WRITE_LOG(LOG_DEBUG, "JDWP accept pid:%d", pid); +#endif // JS_JDWP_CONNECT thisClass->AdminContext(OP_ADD, pid, ctxJdwp); ret = true; + int fd = -1; + if (uv_fileno((uv_handle_t *)&(ctxJdwp->pipe), &fd) < 0) { + WRITE_LOG(LOG_DEBUG, "HdcJdwp::ReadStream uv_fileno fail."); + } else { + thisClass->pollNodeMap.emplace(fd, PollNode(fd, pid)); + thisClass->WakePollThread(); + } } } Base::ZeroArray(ctxJdwp->buf); @@ -110,6 +157,20 @@ void HdcJdwp::ReadStream(uv_stream_t *pipe, ssize_t nread, const uv_buf_t *buf) } } +#ifdef JS_JDWP_CONNECT +string HdcJdwp::GetProcessListExtendPkgName() +{ + string ret; + uv_rwlock_rdlock(&lockMapContext); + for (auto &&v : mapCtxJdwp) { + HCtxJdwp hj = v.second; + ret += std::to_string(v.first) + " " + hj->pkgName + "\n"; + } + uv_rwlock_rdunlock(&lockMapContext); + return ret; +} +#endif // JS_JDWP_CONNECT + void HdcJdwp::AcceptClient(uv_stream_t *server, int status) { uv_pipe_t *listenPipe = (uv_pipe_t *)server; @@ -191,12 +252,18 @@ void *HdcJdwp::AdminContext(const uint8_t op, const uint32_t pid, HCtxJdwp ctxJd case OP_CLEAR: { uv_rwlock_wrlock(&lockMapContext); mapCtxJdwp.clear(); + pollNodeMap.clear(); uv_rwlock_wrunlock(&lockMapContext); break; } default: break; } + if (op == OP_ADD || op == OP_REMOVE || op == OP_CLEAR) { + uv_rwlock_rdlock(&lockJdwpTrack); + ProcessListUpdated(); + uv_rwlock_rdunlock(&lockJdwpTrack); + } return hRet; } @@ -268,12 +335,173 @@ string HdcJdwp::GetProcessList() } // cross thread call finish -// jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8000 -int HdcJdwp::Initial() +size_t HdcJdwp::JdwpProcessListMsg(char *buffer, size_t bufferlen) { - if (!JdwpListen()) { - return ERR_MODULE_JDWP_FAILED; + // Message is length-prefixed with 4 hex digits in ASCII. + static constexpr size_t headerLen = 5; + + char head[headerLen + 2]; +#ifdef JS_JDWP_CONNECT + string result = GetProcessListExtendPkgName(); +#else + string result = GetProcessList(); +#endif // JS_JDWP_CONNECT + + size_t len = result.length(); + if (bufferlen < (len + headerLen)) { + WRITE_LOG(LOG_WARN, "truncating JDWP process list (max len = %zu) ", bufferlen); + len = bufferlen; + } + if (snprintf_s(head, sizeof head, sizeof head - 1, "%04zx\n", len) < 0) { + WRITE_LOG(LOG_WARN, " JdwpProcessListMsg head fail."); + return 0; + } + if (memcpy_s(buffer, bufferlen, head, headerLen) != 0) { + WRITE_LOG(LOG_WARN, " JdwpProcessListMsg get head fail."); + return 0; + } + if (memcpy_s(buffer + headerLen, (bufferlen - headerLen), result.c_str(), len) != 0) { + WRITE_LOG(LOG_WARN, " JdwpProcessListMsg get data fail."); + return 0; + } + return len + headerLen; +} + +void HdcJdwp::ProcessListUpdated(void) +{ + if (jdwpTrackers.size() <= 0) { + return; + } + std::string data; + data.resize(JPID_TRACK_LIST_SIZE); + size_t len = JdwpProcessListMsg(&data[0], data.size()); + if (len <= 0) { + return; + } + data.resize(len); + for (auto &t : jdwpTrackers) { + if (t->taskStop || t->taskFree || !t->taskClass) { + jdwpTrackers.erase(remove(jdwpTrackers.begin(), jdwpTrackers.end(), t), + jdwpTrackers.end()); + } else { + void *clsSession = t->ownerSessionClass; + HdcSessionBase *sessionBase = reinterpret_cast(clsSession); + sessionBase->LogMsg(t->sessionId, t->channelId, MSG_OK, data.c_str()); + } + } +} + +bool HdcJdwp::CreateJdwpTracker(HTaskInfo hTaskInfo) +{ + if (hTaskInfo == nullptr) { + return false; + } + jdwpTrackers.push_back(hTaskInfo); + ProcessListUpdated(); + return true; +} + +void HdcJdwp::DrainAwakenPollThread() const +{ + uint64_t value = 0; + ssize_t retVal = read(awakenPollFd, &value, sizeof(value)); + if (retVal < 0) { + WRITE_LOG(LOG_FATAL, "DrainAwakenPollThread: Failed to read data from awaken pipe %d", + retVal); + } +} + +void HdcJdwp::WakePollThread() +{ + if (awakenPollFd < 0) { + WRITE_LOG(LOG_DEBUG, "awakenPollFd: MUST initialized before notifying"); + return; + } + static const uint64_t increment = 1; + ssize_t retVal = write(awakenPollFd, &increment, sizeof(increment)); + if (retVal < 0) { + WRITE_LOG(LOG_FATAL, "WakePollThread: Failed to write data into awaken pipe %d", retVal); + } +} + +void *HdcJdwp::FdEventPollThread(void *args) +{ + auto thisClass = static_cast(args); + std::vector pollfds; + size_t size = 0; + while (!thisClass->stop) { + if (size != thisClass->pollNodeMap.size() || thisClass->pollNodeMap.size() == 0) { + pollfds.clear(); + struct pollfd pollFd; + for (const auto &pair : thisClass->pollNodeMap) { + pollFd.fd = pair.second.pollfd.fd; + pollFd.events = pair.second.pollfd.events; + pollFd.revents = pair.second.pollfd.revents; + pollfds.push_back(pollFd); + } + pollFd.fd = thisClass->awakenPollFd; + pollFd.events = POLLIN; + pollFd.revents = 0; + pollfds.push_back(pollFd); + size = pollfds.size(); + } + poll(&pollfds[0], size, -1); + for (const auto &pollfdsing : pollfds) { + if (pollfdsing.revents & + (POLLNVAL | POLLRDHUP | POLLHUP | POLLERR)) { // POLLNVAL:fd not open + auto it = thisClass->pollNodeMap.find(pollfdsing.fd); + if (it != thisClass->pollNodeMap.end()) { + uint32_t targetPID = it->second.ppid; + HCtxJdwp ctx = + (HCtxJdwp)(thisClass->AdminContext(OP_QUERY, targetPID, nullptr)); + if (ctx != nullptr) { + WRITE_LOG(LOG_INFO, "FreeContext for targetPID :%d", targetPID); + uv_read_stop((uv_stream_t *)&ctx->pipe); + thisClass->FreeContext(ctx); + } + } + thisClass->pollNodeMap.erase(it); + } else if (pollfdsing.revents & POLLIN) { + if (pollfdsing.fd == thisClass->awakenPollFd) { + thisClass->DrainAwakenPollThread(); + } + } + } + } + return nullptr; +} + +int HdcJdwp::CreateFdEventPoll() +{ + pthread_t tid; + if (awakenPollFd >= 0) { + close(awakenPollFd); + awakenPollFd = -1; + } + awakenPollFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); + if (awakenPollFd < 0) { + WRITE_LOG(LOG_FATAL, "CreateFdEventPoll : Failed to create awakenPollFd"); + return ERR_GENERIC; + } + int tret = pthread_create(&tid, nullptr, FdEventPollThread, this); + if (tret != 0) { + WRITE_LOG(LOG_INFO, "FdEventPollThread create fail."); + return tret; } return RET_SUCCESS; } + +// jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8000 +int HdcJdwp::Initial() +{ + pollNodeMap.clear(); + if (!JdwpListen()) { + return ERR_MODULE_JDWP_FAILED; + } + if (CreateFdEventPoll() < 0) { + return ERR_MODULE_JDWP_FAILED; + } + stop = false; + return RET_SUCCESS; +} } \ No newline at end of file diff --git a/src/daemon/jdwp.h b/src/daemon/jdwp.h index 93b3b902..3b9adea2 100644 --- a/src/daemon/jdwp.h +++ b/src/daemon/jdwp.h @@ -15,6 +15,8 @@ #ifndef HDC_JDWP_H #define HDC_JDWP_H #include "daemon_common.h" +#include +#include namespace Hdc { class HdcJdwp { @@ -23,19 +25,50 @@ public: virtual ~HdcJdwp(); int Initial(); void Stop(); + bool CreateJdwpTracker(HTaskInfo hTaskInfo) ; bool ReadyForRelease(); - string GetProcessList(); bool SendJdwpNewFD(uint32_t targetPID, int fd); bool CheckPIDExist(uint32_t targetPID); - +#ifdef JS_JDWP_CONNECT + static constexpr uint8_t JS_PKG_MIN_SIZE = 15; // JsMsgHeader + "pkgName:"uint8_t[7~127] + static constexpr uint8_t JS_PKG_MX_SIZE = 135; +#endif // JS_JDWP_CONNECT private: + struct _PollFd { + int fd; + short events; + short revents; + }; + struct PollNode { + _PollFd pollfd; + uint32_t ppid; + PollNode(int fd, uint32_t pid) + { + Base::ZeroStruct(pollfd); + pollfd.fd = fd; + pollfd.events = POLLNVAL | POLLRDHUP | POLLHUP | POLLERR; + pollfd.revents = 0; + ppid = pid; + } + }; +#ifdef JS_JDWP_CONNECT + struct JsMsgHeader { + uint32_t msgLen; + uint32_t pid; + }; +#endif // JS_JDWP_CONNECT struct ContextJdwp { uint32_t pid; uv_pipe_t pipe; HdcJdwp *thisClass; bool finish; +#ifdef JS_JDWP_CONNECT + char buf[JS_PKG_MX_SIZE]; + string pkgName; +#else char buf[sizeof(uint32_t)]; +#endif // JS_JDWP_CONNECT uint8_t dummy; uv_tcp_t jvmTCP; }; @@ -45,15 +78,29 @@ private: static void AcceptClient(uv_stream_t *server, int status); static void ReadStream(uv_stream_t *pipe, ssize_t nread, const uv_buf_t *buf); static void SendCallbackJdwpNewFD(uv_write_t *req, int status); + static void *FdEventPollThread(void *args); + size_t JdwpProcessListMsg(char *buffer, size_t bufferlen); +#ifdef JS_JDWP_CONNECT + string GetProcessListExtendPkgName(); +#endif // JS_JDWP_CONNECT void *MallocContext(); void FreeContext(HCtxJdwp ctx); void *AdminContext(const uint8_t op, const uint32_t pid, HCtxJdwp ctxJdwp); + int CreateFdEventPoll(); + void ProcessListUpdated(void) ; + void DrainAwakenPollThread() const; + void WakePollThread(); uv_loop_t *loop; uv_pipe_t listenPipe; uint32_t refCount; + int32_t awakenPollFd; map mapCtxJdwp; uv_rwlock_t lockMapContext; + uv_rwlock_t lockJdwpTrack; + std::unordered_map pollNodeMap; // fd, PollNode + std::vector jdwpTrackers; + bool stop; }; } // namespace Hdc #endif \ No newline at end of file diff --git a/src/host/main.cpp b/src/host/main.cpp index 513f1e7f..be28e580 100644 --- a/src/host/main.cpp +++ b/src/host/main.cpp @@ -66,6 +66,7 @@ int IsRegisterCommand(string &outCommand, const char *cmd, const char *cmdnext) registerCommand.push_back(CMDSTR_APP_SIDELOAD); registerCommand.push_back(CMDSTR_TARGET_REBOOT); registerCommand.push_back(CMDSTR_LIST_JDWP); + registerCommand.push_back(CMDSTR_TRACK_JDWP); for (string v : registerCommand) { if (doubleCommand == v) { diff --git a/src/host/server_for_client.cpp b/src/host/server_for_client.cpp index 18abf088..5b4e6835 100644 --- a/src/host/server_for_client.cpp +++ b/src/host/server_for_client.cpp @@ -457,6 +457,7 @@ bool HdcServerForClient::DoCommandRemote(HChannel hChannel, void *formatCommandI case CMD_UNITY_RUNMODE: case CMD_UNITY_HILOG: case CMD_UNITY_ROOTRUN: + case CMD_TRACK_JPID: case CMD_UNITY_JPID: { if (!SendToDaemon(hChannel, formatCommand->cmdFlag, reinterpret_cast(const_cast(formatCommand->parameters.c_str())), diff --git a/src/host/translate.cpp b/src/host/translate.cpp index b335f74a..d0b76f87 100644 --- a/src/host/translate.cpp +++ b/src/host/translate.cpp @@ -238,6 +238,8 @@ namespace TranslateCommand { outCmd->cmdFlag = CMD_UNITY_REMOUNT; } else if (!strcmp(input.c_str(), CMDSTR_LIST_JDWP.c_str())) { outCmd->cmdFlag = CMD_UNITY_JPID; + } else if (!strcmp(input.c_str(), CMDSTR_TRACK_JDWP.c_str())) { + outCmd->cmdFlag = CMD_TRACK_JPID; } else if (!strncmp(input.c_str(), CMDSTR_TARGET_REBOOT.c_str(), CMDSTR_TARGET_REBOOT.size())) { TargetReboot(input.c_str(), outCmd); } else if (!strncmp(input.c_str(), CMDSTR_TARGET_MODE.c_str(), CMDSTR_TARGET_MODE.size())) {