1 /*
2 * Copyright (C) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #include "jdwp.h"
16 #include <sys/eventfd.h>
17 #include <thread>
18
19 namespace Hdc {
HdcJdwp(uv_loop_t * loopIn)20 HdcJdwp::HdcJdwp(uv_loop_t *loopIn)
21 {
22 listenPipe.data = this;
23 loop = loopIn;
24 refCount = 0;
25 stop = false;
26 awakenPollFd = -1;
27 uv_rwlock_init(&lockMapContext);
28 uv_rwlock_init(&lockJdwpTrack);
29 awakenPollFd = -1;
30 stop = false;
31 }
32
~HdcJdwp()33 HdcJdwp::~HdcJdwp()
34 {
35 if (awakenPollFd >= 0) {
36 close(awakenPollFd);
37 awakenPollFd = -1;
38 }
39 uv_rwlock_destroy(&lockMapContext);
40 uv_rwlock_destroy(&lockJdwpTrack);
41 }
42
ReadyForRelease()43 bool HdcJdwp::ReadyForRelease()
44 {
45 return refCount == 0;
46 }
47
Stop()48 void HdcJdwp::Stop()
49 {
50 stop = true;
51 WakePollThread();
52 auto funcListenPipeClose = [](uv_handle_t *handle) -> void {
53 HdcJdwp *thisClass = (HdcJdwp *)handle->data;
54 --thisClass->refCount;
55 };
56 Base::TryCloseHandle((const uv_handle_t *)&listenPipe, funcListenPipeClose);
57 freeContextMutex.lock();
58 for (auto &&obj : mapCtxJdwp) {
59 HCtxJdwp v = obj.second;
60 FreeContext(v);
61 }
62 AdminContext(OP_CLEAR, 0, nullptr);
63 freeContextMutex.unlock();
64 }
65
MallocContext()66 void *HdcJdwp::MallocContext()
67 {
68 HCtxJdwp ctx = nullptr;
69 if ((ctx = new ContextJdwp()) == nullptr) {
70 return nullptr;
71 }
72 ctx->thisClass = this;
73 ctx->pipe.data = ctx;
74 ++refCount;
75 return ctx;
76 }
77
78 // Single thread, two parameters can be used
FreeContext(HCtxJdwp ctx)79 void HdcJdwp::FreeContext(HCtxJdwp ctx)
80 {
81 if (ctx->finish) {
82 return;
83 }
84 ctx->finish = true;
85 WRITE_LOG(LOG_INFO, "FreeContext for targetPID :%d", ctx->pid);
86 Base::TryCloseHandle((const uv_handle_t *)&ctx->pipe);
87 AdminContext(OP_REMOVE, ctx->pid, nullptr);
88 auto funcReqClose = [](uv_idle_t *handle) -> void {
89 HCtxJdwp ctx = (HCtxJdwp)handle->data;
90 --ctx->thisClass->refCount;
91 Base::TryCloseHandle((uv_handle_t *)handle, Base::CloseIdleCallback);
92 delete ctx;
93 };
94 Base::IdleUvTask(loop, ctx, funcReqClose);
95 }
96
RemoveFdFromPollList(uint32_t pid)97 void HdcJdwp::RemoveFdFromPollList(uint32_t pid)
98 {
99 for (auto &&pair : pollNodeMap) {
100 if (pair.second.ppid == pid) {
101 WRITE_LOG(LOG_INFO, "RemoveFdFromPollList for pid:%d.", pid);
102 pollNodeMap.erase(pair.second.pollfd.fd);
103 break;
104 }
105 }
106 }
107
ReadStream(uv_stream_t * pipe,ssize_t nread,const uv_buf_t * buf)108 void HdcJdwp::ReadStream(uv_stream_t *pipe, ssize_t nread, const uv_buf_t *buf)
109 {
110 bool ret = true;
111 if (nread == UV_ENOBUFS) { // It is definite enough, usually only 4 bytes
112 ret = false;
113 WRITE_LOG(LOG_DEBUG, "HdcJdwp::ReadStream IOBuf max");
114 } else if (nread == 0) {
115 return;
116 #ifdef JS_JDWP_CONNECT
117 } else if (nread < JS_PKG_MIN_SIZE || nread > JS_PKG_MX_SIZE) { // valid Js package size
118 #else
119 } else if (nread < 0 || nread != 4) { // 4 : 4 bytes
120 #endif // JS_JDWP_CONNECT
121 ret = false;
122 WRITE_LOG(LOG_DEBUG, "HdcJdwp::ReadStream invalid package nread:%d.", nread);
123 }
124
125 HCtxJdwp ctxJdwp = static_cast<HCtxJdwp>(pipe->data);
126 HdcJdwp *thisClass = static_cast<HdcJdwp *>(ctxJdwp->thisClass);
127 if (ret) {
128 uint32_t pid = 0;
129 char *p = ctxJdwp->buf;
130 if (nread == sizeof(uint32_t)) { // Java: pid
131 pid = atoi(p);
132 } else { // JS:pid PkgName
133 #ifdef JS_JDWP_CONNECT
134 struct JsMsgHeader *jsMsg = reinterpret_cast<struct JsMsgHeader *>(p);
135 if (jsMsg->msgLen == nread) {
136 pid = jsMsg->pid;
137 string pkgName = string((char *)p + sizeof(JsMsgHeader), jsMsg->msgLen - sizeof(JsMsgHeader));
138 ctxJdwp->pkgName = pkgName;
139 } else {
140 ret = false;
141 WRITE_LOG(LOG_DEBUG, "HdcJdwp::ReadStream invalid js package size %d:%d.", jsMsg->msgLen, nread);
142 }
143 #endif // JS_JDWP_CONNECT
144 }
145 if (pid > 0) {
146 ctxJdwp->pid = pid;
147 #ifdef JS_JDWP_CONNECT
148 WRITE_LOG(LOG_DEBUG, "JDWP accept pid:%d-pkg:%s", pid, ctxJdwp->pkgName.c_str());
149 #else
150 WRITE_LOG(LOG_DEBUG, "JDWP accept pid:%d", pid);
151 #endif // JS_JDWP_CONNECT
152 thisClass->AdminContext(OP_ADD, pid, ctxJdwp);
153 ret = true;
154 int fd = -1;
155 if (uv_fileno(reinterpret_cast<uv_handle_t *>(&(ctxJdwp->pipe)), &fd) < 0) {
156 WRITE_LOG(LOG_DEBUG, "HdcJdwp::ReadStream uv_fileno fail.");
157 } else {
158 thisClass->freeContextMutex.lock();
159 thisClass->pollNodeMap.emplace(fd, PollNode(fd, pid));
160 thisClass->freeContextMutex.unlock();
161 thisClass->WakePollThread();
162 }
163 }
164 }
165 Base::ZeroArray(ctxJdwp->buf);
166 if (!ret) {
167 WRITE_LOG(LOG_INFO, "ReadStream proc:%d err, free it.", ctxJdwp->pid);
168 thisClass->freeContextMutex.lock();
169 thisClass->FreeContext(ctxJdwp);
170 thisClass->freeContextMutex.unlock();
171 }
172 }
173
174 #ifdef JS_JDWP_CONNECT
GetProcessListExtendPkgName()175 string HdcJdwp::GetProcessListExtendPkgName()
176 {
177 string ret;
178 uv_rwlock_rdlock(&lockMapContext);
179 for (auto &&v : mapCtxJdwp) {
180 HCtxJdwp hj = v.second;
181 ret += std::to_string(v.first) + " " + hj->pkgName + "\n";
182 }
183 uv_rwlock_rdunlock(&lockMapContext);
184 return ret;
185 }
186 #endif // JS_JDWP_CONNECT
187
AcceptClient(uv_stream_t * server,int status)188 void HdcJdwp::AcceptClient(uv_stream_t *server, int status)
189 {
190 uv_pipe_t *listenPipe = (uv_pipe_t *)server;
191 HdcJdwp *thisClass = (HdcJdwp *)listenPipe->data;
192 HCtxJdwp ctxJdwp = (HCtxJdwp)thisClass->MallocContext();
193 if (!ctxJdwp) {
194 return;
195 }
196 uv_pipe_init(thisClass->loop, &ctxJdwp->pipe, 1);
197 if (uv_accept(server, (uv_stream_t *)&ctxJdwp->pipe) < 0) {
198 WRITE_LOG(LOG_DEBUG, "uv_accept failed");
199 thisClass->freeContextMutex.lock();
200 thisClass->FreeContext(ctxJdwp);
201 thisClass->freeContextMutex.unlock();
202 return;
203 }
204 auto funAlloc = [](uv_handle_t *handle, size_t sizeSuggested, uv_buf_t *buf) -> void {
205 HCtxJdwp ctxJdwp = (HCtxJdwp)handle->data;
206 buf->base = (char *)ctxJdwp->buf;
207 buf->len = sizeof(ctxJdwp->buf);
208 };
209 uv_read_start((uv_stream_t *)&ctxJdwp->pipe, funAlloc, ReadStream);
210 }
211
212 // Test bash connnet(UNIX-domain sockets):nc -U path/jdwp-control < hexpid.file
213 // Test uv connect(pipe): 'uv_pipe_connect'
JdwpListen()214 bool HdcJdwp::JdwpListen()
215 {
216 #ifdef HDC_PCDEBUG
217 // if test, canbe enable
218 return true;
219 const char jdwpCtrlName[] = { 'j', 'd', 'w', 'p', '-', 'c', 'o', 'n', 't', 'r', 'o', 'l', 0 };
220 unlink(jdwpCtrlName);
221 #else
222 const char jdwpCtrlName[] = { '\0', 'j', 'd', 'w', 'p', '-', 'c', 'o', 'n', 't', 'r', 'o', 'l', 0 };
223 #endif
224 const int DEFAULT_BACKLOG = 4;
225 bool ret = false;
226 while (true) {
227 uv_pipe_init(loop, &listenPipe, 0);
228 listenPipe.data = this;
229 if ((uv_pipe_bind(&listenPipe, jdwpCtrlName))) {
230 constexpr int bufSize = 1024;
231 char buf[bufSize] = { 0 };
232 strerror_r(errno, buf, bufSize);
233 WRITE_LOG(LOG_WARN, "Bind error : %d: %s", errno, buf);
234 return 1;
235 }
236 if (uv_listen((uv_stream_t *)&listenPipe, DEFAULT_BACKLOG, AcceptClient)) {
237 break;
238 }
239 ++refCount;
240 ret = true;
241 break;
242 }
243 // listenPipe close by stop
244 return ret;
245 }
246
247 // Working in the main thread, but will be accessed by each session thread, so we need to set thread lock
AdminContext(const uint8_t op,const uint32_t pid,HCtxJdwp ctxJdwp)248 void *HdcJdwp::AdminContext(const uint8_t op, const uint32_t pid, HCtxJdwp ctxJdwp)
249 {
250 HCtxJdwp hRet = nullptr;
251 switch (op) {
252 case OP_ADD: {
253 uv_rwlock_wrlock(&lockMapContext);
254 mapCtxJdwp[pid] = ctxJdwp;
255 uv_rwlock_wrunlock(&lockMapContext);
256 break;
257 }
258 case OP_REMOVE:
259 uv_rwlock_wrlock(&lockMapContext);
260 mapCtxJdwp.erase(pid);
261 RemoveFdFromPollList(pid);
262 uv_rwlock_wrunlock(&lockMapContext);
263 break;
264 case OP_QUERY: {
265 uv_rwlock_rdlock(&lockMapContext);
266 if (mapCtxJdwp.count(pid)) {
267 hRet = mapCtxJdwp[pid];
268 }
269 uv_rwlock_rdunlock(&lockMapContext);
270 break;
271 }
272 case OP_CLEAR: {
273 uv_rwlock_wrlock(&lockMapContext);
274 mapCtxJdwp.clear();
275 pollNodeMap.clear();
276 uv_rwlock_wrunlock(&lockMapContext);
277 break;
278 }
279 default:
280 break;
281 }
282 if (op == OP_ADD || op == OP_REMOVE || op == OP_CLEAR) {
283 uv_rwlock_wrlock(&lockJdwpTrack);
284 ProcessListUpdated();
285 uv_rwlock_wrunlock(&lockJdwpTrack);
286 }
287 return hRet;
288 }
289
290 // work on main thread
SendCallbackJdwpNewFD(uv_write_t * req,int status)291 void HdcJdwp::SendCallbackJdwpNewFD(uv_write_t *req, int status)
292 {
293 // It usually works successful, not notify session work
294 HCtxJdwp ctx = (HCtxJdwp)req->data;
295 if (status >= 0) {
296 WRITE_LOG(LOG_DEBUG, "SendCallbackJdwpNewFD successful %d, active jdwp forward", ctx->pid);
297 } else {
298 WRITE_LOG(LOG_WARN, "SendCallbackJdwpNewFD failed %d", ctx->pid);
299 }
300 // close my process's fd
301 Base::TryCloseHandle((const uv_handle_t *)&ctx->jvmTCP);
302 delete req;
303 --ctx->thisClass->refCount;
304 }
305
306 // Each session calls the interface through the main thread message queue, which cannot be called directly across
307 // threads
308 // work on main thread
SendJdwpNewFD(uint32_t targetPID,int fd)309 bool HdcJdwp::SendJdwpNewFD(uint32_t targetPID, int fd)
310 {
311 bool ret = false;
312 while (true) {
313 HCtxJdwp ctx = (HCtxJdwp)AdminContext(OP_QUERY, targetPID, nullptr);
314 if (!ctx) {
315 break;
316 }
317 ctx->dummy = (uint8_t)'!';
318 if (uv_tcp_init(loop, &ctx->jvmTCP)) {
319 break;
320 }
321 if (uv_tcp_open(&ctx->jvmTCP, fd)) {
322 break;
323 }
324 // transfer fd to jvm
325 // clang-format off
326 if (Base::SendToStreamEx((uv_stream_t *)&ctx->pipe, (uint8_t *)&ctx->dummy, 1, (uv_stream_t *)&ctx->jvmTCP,
327 (void *)SendCallbackJdwpNewFD, (const void *)ctx) < 0) {
328 break;
329 }
330 // clang-format on
331 ++refCount;
332 ret = true;
333 WRITE_LOG(LOG_DEBUG, "SendJdwpNewFD successful targetPID:%d fd%d", targetPID, fd);
334 break;
335 }
336 return ret;
337 }
338
339 // cross thread call begin
CheckPIDExist(uint32_t targetPID)340 bool HdcJdwp::CheckPIDExist(uint32_t targetPID)
341 {
342 HCtxJdwp ctx = (HCtxJdwp)AdminContext(OP_QUERY, targetPID, nullptr);
343 return ctx != nullptr;
344 }
345
GetProcessList()346 string HdcJdwp::GetProcessList()
347 {
348 string ret;
349 uv_rwlock_rdlock(&lockMapContext);
350 for (auto &&v : mapCtxJdwp) {
351 ret += std::to_string(v.first) + "\n";
352 }
353 uv_rwlock_rdunlock(&lockMapContext);
354 return ret;
355 }
356 // cross thread call finish
357
JdwpProcessListMsg(char * buffer,size_t bufferlen)358 size_t HdcJdwp::JdwpProcessListMsg(char *buffer, size_t bufferlen)
359 {
360 // Message is length-prefixed with 4 hex digits in ASCII.
361 static constexpr size_t headerLen = 5;
362 char head[headerLen + 2];
363 #ifdef JS_JDWP_CONNECT
364 string result = GetProcessListExtendPkgName();
365 #else
366 string result = GetProcessList();
367 #endif // JS_JDWP_CONNECT
368
369 size_t len = result.length();
370 if (bufferlen < (len + headerLen)) {
371 WRITE_LOG(LOG_WARN, "truncating JDWP process list (max len = %zu) ", bufferlen);
372 len = bufferlen;
373 }
374 if (snprintf_s(head, sizeof head, sizeof head - 1, "%04zx\n", len) < 0) {
375 WRITE_LOG(LOG_WARN, " JdwpProcessListMsg head fail.");
376 return 0;
377 }
378 if (memcpy_s(buffer, bufferlen, head, headerLen) != EOK) {
379 WRITE_LOG(LOG_WARN, " JdwpProcessListMsg get head fail.");
380 return 0;
381 }
382 if (memcpy_s(buffer + headerLen, (bufferlen - headerLen), result.c_str(), len) != EOK) {
383 WRITE_LOG(LOG_WARN, " JdwpProcessListMsg get data fail.");
384 return 0;
385 }
386 return len + headerLen;
387 }
388
SendProcessList(HTaskInfo t,string data)389 void HdcJdwp::SendProcessList(HTaskInfo t, string data)
390 {
391 if (t == nullptr || data.size() == 0) {
392 WRITE_LOG(LOG_WARN, " SendProcessList, Nothing needs to be sent.");
393 return;
394 }
395 void *clsSession = t->ownerSessionClass;
396 HdcSessionBase *sessionBase = static_cast<HdcSessionBase *>(clsSession);
397 sessionBase->LogMsg(t->sessionId, t->channelId, MSG_OK, data.c_str());
398 }
399
ProcessListUpdated(HTaskInfo task)400 void HdcJdwp::ProcessListUpdated(HTaskInfo task)
401 {
402 if (jdwpTrackers.size() <= 0) {
403 WRITE_LOG(LOG_DEBUG, "None jdwpTrackers.");
404 return;
405 }
406 #ifdef JS_JDWP_CONNECT
407 static constexpr uint32_t jpidTrackListSize = 1024 * 4;
408 #else
409 static constexpr uint32_t jpidTrackListSize = 1024;
410 #endif // JS_JDWP_CONNECT
411 std::string data;
412 data.resize(jpidTrackListSize);
413 size_t len = JdwpProcessListMsg(&data[0], data.size());
414 if (len <= 0) {
415 return;
416 }
417 data.resize(len);
418 if (task != nullptr) {
419 SendProcessList(task, data);
420 } else {
421 for (auto &t : jdwpTrackers) {
422 if (t == nullptr) {
423 continue;
424 }
425 if (t->taskStop || t->taskFree || !t->taskClass) { // The channel for the track-jpid has been stopped.
426 jdwpTrackers.erase(remove(jdwpTrackers.begin(), jdwpTrackers.end(), t), jdwpTrackers.end());
427 if (jdwpTrackers.size() <= 0) {
428 return;
429 }
430 } else {
431 SendProcessList(t, data);
432 }
433 }
434 }
435 }
436
CreateJdwpTracker(HTaskInfo taskInfo)437 bool HdcJdwp::CreateJdwpTracker(HTaskInfo taskInfo)
438 {
439 if (taskInfo == nullptr) {
440 return false;
441 }
442 uv_rwlock_wrlock(&lockJdwpTrack);
443 auto it = std::find(jdwpTrackers.begin(), jdwpTrackers.end(), taskInfo);
444 if (it == jdwpTrackers.end()) {
445 jdwpTrackers.push_back(taskInfo);
446 }
447 ProcessListUpdated(taskInfo);
448 uv_rwlock_wrunlock(&lockJdwpTrack);
449 return true;
450 }
451
RemoveJdwpTracker(HTaskInfo taskInfo)452 void HdcJdwp::RemoveJdwpTracker(HTaskInfo taskInfo)
453 {
454 if (taskInfo == nullptr) {
455 return;
456 }
457 uv_rwlock_wrlock(&lockJdwpTrack);
458 auto it = std::find(jdwpTrackers.begin(), jdwpTrackers.end(), taskInfo);
459 if (it != jdwpTrackers.end()) {
460 WRITE_LOG(LOG_DEBUG, "RemoveJdwpTracker channelId:%d, taskType:%d.", taskInfo->channelId, taskInfo->taskType);
461 jdwpTrackers.erase(remove(jdwpTrackers.begin(), jdwpTrackers.end(), *it), jdwpTrackers.end());
462 }
463 uv_rwlock_wrunlock(&lockJdwpTrack);
464 }
465
DrainAwakenPollThread() const466 void HdcJdwp::DrainAwakenPollThread() const
467 {
468 uint64_t value = 0;
469 ssize_t retVal = read(awakenPollFd, &value, sizeof(value));
470 if (retVal < 0) {
471 WRITE_LOG(LOG_FATAL, "DrainAwakenPollThread: Failed to read data from awaken pipe %d", retVal);
472 }
473 }
474
WakePollThread()475 void HdcJdwp::WakePollThread()
476 {
477 if (awakenPollFd < 0) {
478 WRITE_LOG(LOG_DEBUG, "awakenPollFd: MUST initialized before notifying");
479 return;
480 }
481 static const uint64_t increment = 1;
482 ssize_t retVal = write(awakenPollFd, &increment, sizeof(increment));
483 if (retVal < 0) {
484 WRITE_LOG(LOG_FATAL, "WakePollThread: Failed to write data into awaken pipe %d", retVal);
485 }
486 }
487
FdEventPollThread(void * args)488 void *HdcJdwp::FdEventPollThread(void *args)
489 {
490 auto thisClass = static_cast<HdcJdwp *>(args);
491 std::vector<struct pollfd> pollfds;
492 size_t size = 0;
493 while (!thisClass->stop) {
494 thisClass->freeContextMutex.lock();
495 if (size != thisClass->pollNodeMap.size() || thisClass->pollNodeMap.size() == 0) {
496 pollfds.clear();
497 struct pollfd pollFd;
498 for (const auto &pair : thisClass->pollNodeMap) {
499 pollFd.fd = pair.second.pollfd.fd;
500 pollFd.events = pair.second.pollfd.events;
501 pollFd.revents = pair.second.pollfd.revents;
502 pollfds.push_back(pollFd);
503 }
504 pollFd.fd = thisClass->awakenPollFd;
505 pollFd.events = POLLIN;
506 pollFd.revents = 0;
507 pollfds.push_back(pollFd);
508 size = pollfds.size();
509 }
510 thisClass->freeContextMutex.unlock();
511 poll(&pollfds[0], size, -1);
512 for (const auto &pollfdsing : pollfds) {
513 if (pollfdsing.revents & (POLLNVAL | POLLRDHUP | POLLHUP | POLLERR)) { // POLLNVAL:fd not open
514 thisClass->freeContextMutex.lock();
515 auto it = thisClass->pollNodeMap.find(pollfdsing.fd);
516 if (it != thisClass->pollNodeMap.end()) {
517 uint32_t targetPID = it->second.ppid;
518 HCtxJdwp ctx = static_cast<HCtxJdwp>(thisClass->AdminContext(OP_QUERY, targetPID, nullptr));
519 if (ctx != nullptr) {
520 thisClass->AdminContext(OP_REMOVE, targetPID, nullptr);
521 }
522 }
523 thisClass->freeContextMutex.unlock();
524 } else if (pollfdsing.revents & POLLIN) {
525 if (pollfdsing.fd == thisClass->awakenPollFd) {
526 thisClass->DrainAwakenPollThread();
527 }
528 }
529 }
530 }
531 return nullptr;
532 }
533
CreateFdEventPoll()534 int HdcJdwp::CreateFdEventPoll()
535 {
536 pthread_t tid;
537 if (awakenPollFd >= 0) {
538 close(awakenPollFd);
539 awakenPollFd = -1;
540 }
541 awakenPollFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
542 if (awakenPollFd < 0) {
543 WRITE_LOG(LOG_FATAL, "CreateFdEventPoll : Failed to create awakenPollFd");
544 return ERR_GENERIC;
545 }
546 int tret = pthread_create(&tid, nullptr, FdEventPollThread, this);
547 if (tret != 0) {
548 WRITE_LOG(LOG_INFO, "FdEventPollThread create fail.");
549 return tret;
550 }
551 return RET_SUCCESS;
552 }
553
554 // jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8000
Initial()555 int HdcJdwp::Initial()
556 {
557 freeContextMutex.lock();
558 pollNodeMap.clear();
559 freeContextMutex.unlock();
560 if (!JdwpListen()) {
561 return ERR_MODULE_JDWP_FAILED;
562 }
563 if (CreateFdEventPoll() < 0) {
564 return ERR_MODULE_JDWP_FAILED;
565 }
566 return RET_SUCCESS;
567 }
568 }
569