Hdc JS: Add jdwp control support for JS

Signed-off-by: yu_xiaoyan <yu_xiaoyan1@hoperun.com>
This commit is contained in:
yu_xiaoyan 2021-12-02 19:44:46 +08:00
parent aac1c4c2b0
commit d84ccd91c5
11 changed files with 322 additions and 16 deletions

View File

@ -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 = [

View File

@ -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

View File

@ -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,

View File

@ -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<HdcDaemonUnity>(hTaskInfo, TYPE_UNITY, command, payload, payloadSize);
break;
case CMD_SHELL_INIT:

View File

@ -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;
}

View File

@ -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;

View File

@ -13,8 +13,16 @@
* limitations under the License.
*/
#include "jdwp.h"
#include <thread>
#include <sys/eventfd.h>
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<HdcSessionBase *>(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<HdcJdwp *>(args);
std::vector<struct pollfd> 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;
}
}

View File

@ -15,6 +15,8 @@
#ifndef HDC_JDWP_H
#define HDC_JDWP_H
#include "daemon_common.h"
#include <poll.h>
#include <unordered_map>
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<uint32_t, HCtxJdwp> mapCtxJdwp;
uv_rwlock_t lockMapContext;
uv_rwlock_t lockJdwpTrack;
std::unordered_map<int, PollNode> pollNodeMap; // fd, PollNode
std::vector<HTaskInfo> jdwpTrackers;
bool stop;
};
} // namespace Hdc
#endif

View File

@ -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) {

View File

@ -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<uint8_t *>(const_cast<char *>(formatCommand->parameters.c_str())),

View File

@ -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())) {