1 /*
2 * Copyright (c) 2023 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
16 #include "arkts_plugin.h"
17
18 #include <arpa/inet.h>
19 #include <cstdlib>
20 #include <sys/un.h>
21 #include <unistd.h>
22
23 #include "arkts_plugin_result.pb.h"
24 #include "logging.h"
25 #include "securec.h"
26
27 namespace {
28 const std::string PANDA = "PandaDebugger";
29 const std::string SNAPSHOT_HEAD =
30 R"({"id":1,"method":"HeapProfiler.takeHeapSnapshot","params":{"reportProgress":true,"captureNumericValue":)";
31 const std::string SNAPSHOT_TAIL = R"(,"exposeInternals":false}})";
32 const std::string TIMELINE_HEAD =
33 R"({"id":1,"method":"HeapProfiler.startTrackingHeapObjects","params":{"trackAllocations":)";
34 const std::string TIMELINE_TAIL = "}}";
35 const std::string TIMELINE_STOP =
36 R"({"id":2,"method":"HeapProfiler.stopTrackingHeapObjects","params":{"reportProgress":true}})";
37 const std::string START_CMD_RETURN = R"({"id":1,"result":{}})";
38 const std::string STOP_CMD_RETURN = R"({"id":2,"result":{}})";
39 enum class HeapType : int32_t {
40 SNAPSHOT,
41 TIMELINE,
42 };
43 constexpr char CLIENT_WEBSOCKET_UPGRADE_REQ[] =
44 "GET / HTTP/1.1\r\n"
45 "Connection: Upgrade\r\n"
46 "Pragma: no-cache\r\n"
47 "Cache-Control: no-cache\r\n"
48 "Upgrade: websocket\r\n"
49 "Sec-WebSocket-Version: 13\r\n"
50 "Accept-Encoding: gzip, deflate, br\r\n"
51 "Sec-WebSocket-Key: 64b4B+s5JDlgkdg7NekJ+g==\r\n"
52 "Sec-WebSocket-Extensions: permessage-deflate\r\n";
53 constexpr int32_t CLIENT_WEBSOCKET_UPGRADE_RSP_LEN = 129;
54 constexpr int32_t SOCKET_MASK_LEN = 4;
55 constexpr char MASK_KEY[SOCKET_MASK_LEN + 1] = "abcd";
56 constexpr uint32_t TIME_OUT = 5;
57 constexpr uint32_t MAX_MATCH_CNT = 1000;
58 constexpr int32_t SOCKET_SUCCESS = 0;
59 constexpr int32_t SOCKET_HEADER_LEN = 2;
60 constexpr int32_t PAYLOAD_LEN = 2;
61 constexpr int32_t EXTEND_PAYLOAD_LEN = 8;
62 } // namespace
63
Start(const uint8_t * configData,uint32_t configSize)64 int32_t ArkTSPlugin::Start(const uint8_t* configData, uint32_t configSize)
65 {
66 if (protoConfig_.ParseFromArray(configData, configSize) <= 0) {
67 HILOG_ERROR(LOG_CORE, "%s:parseFromArray failed!", __func__);
68 return -1;
69 }
70 pid_ = protoConfig_.pid();
71 if (pid_ <= 0) {
72 HILOG_ERROR(LOG_CORE, "%s: pid is less than or equal to 0", __func__);
73 return -1;
74 }
75
76 if (!ClientConnectUnixWebSocket(std::to_string(pid_) + PANDA, TIME_OUT)) {
77 return -1;
78 }
79
80 if (!ClientSendWSUpgradeReq()) {
81 return -1;
82 }
83
84 if (!ClientRecvWSUpgradeRsp()) {
85 return -1;
86 }
87
88 if (static_cast<int32_t>(protoConfig_.type()) == static_cast<int32_t>(HeapType::SNAPSHOT)) {
89 snapshotCmd_ = SNAPSHOT_HEAD + (protoConfig_.capture_numeric_value() ? "true" : "false") + SNAPSHOT_TAIL;
90
91 auto interval = ScheduleTaskManager::ms(protoConfig_.interval() * MAX_MATCH_CNT);
92 if (interval.count() == 0) {
93 HILOG_ERROR(LOG_CORE, "%s:scheduleTask interval == 0 error!", __func__);
94 return -1;
95 }
96 auto callback = std::bind(&ArkTSPlugin::Snapshot, this);
97 if (!scheduleTaskManager_.ScheduleTask("snapshot", callback, interval)) {
98 HILOG_ERROR(LOG_CORE, "%s:scheduleTask failed!", __func__);
99 return -1;
100 }
101 } else if (static_cast<int32_t>(protoConfig_.type()) == static_cast<int32_t>(HeapType::TIMELINE)) {
102 timelineCmd_ = TIMELINE_HEAD + (protoConfig_.track_allocations() ? "true" : "false") + TIMELINE_TAIL;
103 if (!ClientSendReq(timelineCmd_.c_str())) {
104 return -1;
105 }
106 FlushData();
107 } else {
108 return -1;
109 }
110 return 0;
111 }
112
Stop()113 int32_t ArkTSPlugin::Stop()
114 {
115 if (static_cast<int32_t>(protoConfig_.type()) == static_cast<int32_t>(HeapType::TIMELINE)) {
116 if (!ClientSendReq(TIMELINE_STOP.c_str())) {
117 Close();
118 return -1;
119 }
120 FlushData();
121 } else if (static_cast<int32_t>(protoConfig_.type()) == static_cast<int32_t>(HeapType::SNAPSHOT)) {
122 scheduleTaskManager_.Shutdown();
123 }
124 Close();
125 return 0;
126 }
127
SetWriter(WriterStruct * writer)128 void ArkTSPlugin::SetWriter(WriterStruct* writer)
129 {
130 resultWriter_ = writer;
131 }
132
Snapshot()133 void ArkTSPlugin::Snapshot()
134 {
135 CHECK_NOTNULL(resultWriter_, NO_RETVAL, "%s: resultWriter_ nullptr", __func__);
136 if (!ClientSendReq(snapshotCmd_.c_str())) {
137 return;
138 }
139 FlushData();
140 }
141
FlushData()142 void ArkTSPlugin::FlushData()
143 {
144 while (true) {
145 std::string recv = Decode();
146 if (recv.empty()) {
147 HILOG_ERROR(LOG_CORE, "%s: recv is empty", __func__);
148 break;
149 }
150 ArkTSResult data;
151 data.set_result(recv.c_str(), recv.size());
152 buffer_.resize(data.ByteSizeLong());
153 data.SerializeToArray(buffer_.data(), buffer_.size());
154 resultWriter_->write(resultWriter_, buffer_.data(), buffer_.size());
155 resultWriter_->flush(resultWriter_);
156 if (recv == START_CMD_RETURN || recv == STOP_CMD_RETURN) {
157 break;
158 }
159 }
160 }
161
ClientConnectUnixWebSocket(const std::string & sockName,uint32_t timeoutLimit)162 bool ArkTSPlugin::ClientConnectUnixWebSocket(const std::string& sockName, uint32_t timeoutLimit)
163 {
164 if (socketState_ != SocketState::UNINITED) {
165 HILOG_ERROR(LOG_CORE, "client has inited");
166 return true;
167 }
168
169 client_ = socket(AF_UNIX, SOCK_STREAM, 0);
170 if (client_ < SOCKET_SUCCESS) {
171 HILOG_ERROR(LOG_CORE, "client socket failed, error = %d, , desc = %s", errno, strerror(errno));
172 return false;
173 }
174
175 // set send and recv timeout limit
176 if (!SetWebSocketTimeOut(client_, timeoutLimit)) {
177 HILOG_ERROR(LOG_CORE, "client SetWebSocketTimeOut failed, error = %d, desc = %s", errno, strerror(errno));
178 close(client_);
179 client_ = -1;
180 return false;
181 }
182
183 struct sockaddr_un serverAddr;
184 if (memset_s(&serverAddr, sizeof(serverAddr), 0, sizeof(serverAddr)) != EOK) {
185 HILOG_ERROR(LOG_CORE, "client memset_s serverAddr failed, error = %d, desc = %s", errno, strerror(errno));
186 close(client_);
187 client_ = -1;
188 return false;
189 }
190 serverAddr.sun_family = AF_UNIX;
191 if (strcpy_s(serverAddr.sun_path + 1, sizeof(serverAddr.sun_path) - 1, sockName.c_str()) != EOK) {
192 HILOG_ERROR(LOG_CORE, "client strcpy_s serverAddr.sun_path failed, error = %d, , desc = %s", errno,
193 strerror(errno));
194 close(client_);
195 client_ = -1;
196 return false;
197 }
198 serverAddr.sun_path[0] = '\0';
199
200 uint32_t len = offsetof(struct sockaddr_un, sun_path) + strlen(sockName.c_str()) + 1;
201 int ret = connect(client_, reinterpret_cast<struct sockaddr*>(&serverAddr), static_cast<int32_t>(len));
202 if (ret != SOCKET_SUCCESS) {
203 HILOG_ERROR(LOG_CORE, "client connect failed, error, error = %d, , desc = %s", errno, strerror(errno));
204 close(client_);
205 client_ = -1;
206 return false;
207 }
208 socketState_ = SocketState::INITED;
209 HILOG_INFO(LOG_CORE, "client connect success...");
210 return true;
211 }
212
ClientSendWSUpgradeReq()213 bool ArkTSPlugin::ClientSendWSUpgradeReq()
214 {
215 if (socketState_ == SocketState::UNINITED) {
216 HILOG_ERROR(LOG_CORE, "client has not inited");
217 return false;
218 }
219 if (socketState_ == SocketState::CONNECTED) {
220 HILOG_ERROR(LOG_CORE, "client has connected");
221 return true;
222 }
223
224 int msgLen = strlen(CLIENT_WEBSOCKET_UPGRADE_REQ);
225 int32_t sendLen = send(client_, CLIENT_WEBSOCKET_UPGRADE_REQ, msgLen, 0);
226 if (sendLen != msgLen) {
227 HILOG_ERROR(LOG_CORE, "client send wsupgrade req failed, error = %d, desc = %s", errno, strerror(errno));
228 socketState_ = SocketState::UNINITED;
229 shutdown(client_, SHUT_RDWR);
230 close(client_);
231 client_ = -1;
232 return false;
233 }
234 HILOG_INFO(LOG_CORE, "client send wsupgrade req success");
235 return true;
236 }
237
ClientRecvWSUpgradeRsp()238 bool ArkTSPlugin::ClientRecvWSUpgradeRsp()
239 {
240 if (socketState_ == SocketState::UNINITED) {
241 HILOG_ERROR(LOG_CORE, "client has not inited");
242 return false;
243 }
244 if (socketState_ == SocketState::CONNECTED) {
245 HILOG_ERROR(LOG_CORE, "ClientRecvWSUpgradeRsp::client has connected");
246 return true;
247 }
248
249 char recvBuf[CLIENT_WEBSOCKET_UPGRADE_RSP_LEN + 1] = {0};
250 int32_t bufLen = recv(client_, recvBuf, CLIENT_WEBSOCKET_UPGRADE_RSP_LEN, 0);
251 if (bufLen != CLIENT_WEBSOCKET_UPGRADE_RSP_LEN) {
252 HILOG_ERROR(LOG_CORE, "client recv wsupgrade rsp failed, error = %d, desc = %s", errno, strerror(errno));
253 socketState_ = SocketState::UNINITED;
254 shutdown(client_, SHUT_RDWR);
255 close(client_);
256 client_ = -1;
257 return false;
258 }
259 socketState_ = SocketState::CONNECTED;
260 HILOG_INFO(LOG_CORE, "client recv wsupgrade rsp success");
261 return true;
262 }
263
ClientSendReq(const std::string & message)264 bool ArkTSPlugin::ClientSendReq(const std::string& message)
265 {
266 if (socketState_ != SocketState::CONNECTED) {
267 HILOG_ERROR(LOG_CORE, "client has not connected");
268 return false;
269 }
270
271 uint32_t msgLen = message.length();
272 std::unique_ptr<char[]> msgBuf = std::make_unique<char[]>(msgLen + 15); // 15: the maximum expand length
273 char* sendBuf = msgBuf.get();
274 uint32_t sendMsgLen = 0;
275 sendBuf[0] = 0x81; // 0x81: the text message sent by the server should start with '0x81'.
276 uint32_t mask = 1;
277 // Depending on the length of the messages, client will use shift operation to get the res
278 // and store them in the buffer.
279 if (msgLen <= 125) { // 125: situation 1 when message's length <= 125
280 sendBuf[1] = msgLen | (mask << 7); // 7: mask need shift left by 7 bits
281 sendMsgLen = 2; // 2: the length of header frame is 2;
282 } else if (msgLen < 65536) { // 65536: message's length
283 sendBuf[1] = 126 | (mask << 7); // 126: payloadLen according to the spec; 7: mask shift left by 7 bits
284 sendBuf[2] = ((msgLen >> 8) & 0xff); // 8: shift right by 8 bits => res * (256^1)
285 sendBuf[3] = (msgLen & 0xff); // 3: store len's data => res * (256^0)
286 sendMsgLen = 4; // 4: the length of header frame is 4
287 } else {
288 sendBuf[1] = 127 | (mask << 7); // 127: payloadLen according to the spec; 7: mask shift left by 7 bits
289 for (int32_t i = 2; i <= 5; i++) { // 2 ~ 5: unused bits
290 sendBuf[i] = 0;
291 }
292 sendBuf[6] = ((msgLen & 0xff000000) >> 24); // 6: shift 24 bits => res * (256^3)
293 sendBuf[7] = ((msgLen & 0x00ff0000) >> 16); // 7: shift 16 bits => res * (256^2)
294 sendBuf[8] = ((msgLen & 0x0000ff00) >> 8); // 8: shift 8 bits => res * (256^1)
295 sendBuf[9] = (msgLen & 0x000000ff); // 9: res * (256^0)
296 sendMsgLen = 10; // 10: the length of header frame is 10
297 }
298
299 if (memcpy_s(sendBuf + sendMsgLen, SOCKET_MASK_LEN, MASK_KEY, SOCKET_MASK_LEN) != EOK) {
300 HILOG_ERROR(LOG_CORE, "client memcpy_s MASK_KEY failed, error = %d, desc = %s", errno, strerror(errno));
301 return false;
302 }
303 sendMsgLen += SOCKET_MASK_LEN;
304
305 std::string maskMessage;
306 for (uint64_t i = 0; i < msgLen; i++) {
307 uint64_t j = i % SOCKET_MASK_LEN;
308 maskMessage.push_back(message[i] ^ MASK_KEY[j]);
309 }
310 if (memcpy_s(sendBuf + sendMsgLen, msgLen, maskMessage.c_str(), msgLen) != EOK) {
311 HILOG_ERROR(LOG_CORE, "client memcpy_s maskMessage failed, error = %d, desc = %s", errno, strerror(errno));
312 return false;
313 }
314 msgBuf[sendMsgLen + msgLen] = '\0';
315
316 if (send(client_, sendBuf, sendMsgLen + msgLen, 0) != static_cast<int>(sendMsgLen + msgLen)) {
317 HILOG_ERROR(LOG_CORE, "client send msg req failed, error = %d, desc = %s", errno, strerror(errno));
318 return false;
319 }
320 HILOG_INFO(LOG_CORE, "ClientRecvWSUpgradeRsp::client send msg req success...");
321 return true;
322 }
323
Close()324 void ArkTSPlugin::Close()
325 {
326 if (socketState_ == SocketState::UNINITED) {
327 HILOG_ERROR(LOG_CORE, "client has not inited");
328 return;
329 }
330 shutdown(client_, SHUT_RDWR);
331 close(client_);
332 client_ = -1;
333 socketState_ = SocketState::UNINITED;
334 }
335
SetWebSocketTimeOut(int32_t fd,uint32_t timeoutLimit)336 bool ArkTSPlugin::SetWebSocketTimeOut(int32_t fd, uint32_t timeoutLimit)
337 {
338 if (timeoutLimit > 0) {
339 struct timeval timeout = {timeoutLimit, 0};
340 if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)) != SOCKET_SUCCESS) {
341 return false;
342 }
343 if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) != SOCKET_SUCCESS) {
344 return false;
345 }
346 }
347 return true;
348 }
349
Decode()350 std::string ArkTSPlugin::Decode()
351 {
352 if (socketState_ != SocketState::CONNECTED) {
353 HILOG_ERROR(LOG_CORE, "client has not connected");
354 return {};
355 }
356 char recvbuf[SOCKET_HEADER_LEN + 1];
357 if (!Recv(client_, recvbuf, SOCKET_HEADER_LEN, 0)) {
358 HILOG_ERROR(LOG_CORE, "Decode failed, client websocket disconnect");
359 socketState_ = SocketState::INITED;
360 shutdown(client_, SHUT_RDWR);
361 close(client_);
362 client_ = -1;
363 return {};
364 }
365 recvbuf[SOCKET_HEADER_LEN] = '\0';
366 WebSocketFrame wsFrame;
367 int32_t index = 0;
368 wsFrame.fin = static_cast<uint8_t>(recvbuf[index] >> 7); // 7: shift right by 7 bits to get the fin
369 wsFrame.opCode = static_cast<uint8_t>(recvbuf[index] & 0xf);
370 if (wsFrame.opCode == 0x1) { // 0x1: 0x1 means a text frame
371 index++;
372 wsFrame.mask = static_cast<uint8_t>((recvbuf[index] >> 7) & 0x1); // 7: to get the mask
373 wsFrame.payloadLen = recvbuf[index] & 0x7f;
374 HandleFrame(wsFrame);
375 return wsFrame.payload.get();
376 }
377 return std::string();
378 }
379
NetToHostLongLong(char * buf,uint32_t len)380 uint64_t ArkTSPlugin::NetToHostLongLong(char* buf, uint32_t len)
381 {
382 uint64_t result = 0;
383 for (uint32_t i = 0; i < len; i++) {
384 result |= static_cast<unsigned char>(buf[i]);
385 if ((i + 1) < len) {
386 result <<= 8; // 8: result need shift left 8 bits in order to big endian convert to int
387 }
388 }
389 return result;
390 }
391
HandleFrame(WebSocketFrame & wsFrame)392 bool ArkTSPlugin::HandleFrame(WebSocketFrame& wsFrame)
393 {
394 if (wsFrame.payloadLen == 126) { // 126: the payloadLen read from frame
395 char recvbuf[PAYLOAD_LEN + 1] = {0};
396 if (!Recv(client_, recvbuf, PAYLOAD_LEN, 0)) {
397 HILOG_ERROR(LOG_CORE, "HandleFrame: Recv payloadLen == 126 failed");
398 return false;
399 }
400 recvbuf[PAYLOAD_LEN] = '\0';
401 uint16_t msgLen = 0;
402 if (memcpy_s(&msgLen, sizeof(recvbuf), recvbuf, sizeof(recvbuf) - 1) != EOK) {
403 return false;
404 }
405 wsFrame.payloadLen = ntohs(msgLen);
406 } else if (wsFrame.payloadLen > 126) { // 126: the payloadLen read from frame
407 char recvbuf[EXTEND_PAYLOAD_LEN + 1] = {0};
408 if (!Recv(client_, recvbuf, EXTEND_PAYLOAD_LEN, 0)) {
409 HILOG_ERROR(LOG_CORE, "HandleFrame: Recv payloadLen > 127 failed");
410 return false;
411 }
412 recvbuf[EXTEND_PAYLOAD_LEN] = '\0';
413 wsFrame.payloadLen = NetToHostLongLong(recvbuf, EXTEND_PAYLOAD_LEN);
414 }
415 return DecodeMessage(wsFrame);
416 }
417
DecodeMessage(WebSocketFrame & wsFrame)418 bool ArkTSPlugin::DecodeMessage(WebSocketFrame& wsFrame)
419 {
420 if (wsFrame.payloadLen == 0 || wsFrame.payloadLen > UINT64_MAX) {
421 return false;
422 }
423 wsFrame.payload = std::make_unique<char[]>(wsFrame.payloadLen + 1);
424 if (wsFrame.mask == 1) {
425 CHECK_TRUE(Recv(client_, wsFrame.maskingKey, SOCKET_MASK_LEN, 0), false,
426 "DecodeMessage: Recv maskingKey failed");
427 wsFrame.maskingKey[SOCKET_MASK_LEN] = '\0';
428
429 char buf[wsFrame.payloadLen + 1];
430 CHECK_TRUE(Recv(client_, buf, wsFrame.payloadLen, 0), false, "DecodeMessage: Recv message with mask failed");
431 buf[wsFrame.payloadLen] = '\0';
432
433 for (uint64_t i = 0; i < wsFrame.payloadLen; i++) {
434 uint64_t j = i % SOCKET_MASK_LEN;
435 wsFrame.payload.get()[i] = buf[i] ^ wsFrame.maskingKey[j];
436 }
437 } else {
438 if (!Recv(client_, wsFrame.payload.get(), wsFrame.payloadLen, 0)) {
439 return false;
440 }
441 }
442 wsFrame.payload.get()[wsFrame.payloadLen] = '\0';
443 return true;
444 }
445
Recv(int32_t client,char * buf,size_t totalLen,int32_t flags) const446 bool ArkTSPlugin::Recv(int32_t client, char* buf, size_t totalLen, int32_t flags) const
447 {
448 size_t recvLen = 0;
449 while (recvLen < totalLen) {
450 ssize_t len = recv(client, buf + recvLen, totalLen - recvLen, flags);
451 CHECK_TRUE(len > 0, false, "Recv payload in while failed, websocket disconnect");
452 recvLen += static_cast<size_t>(len);
453 }
454 buf[totalLen] = '\0';
455 return true;
456 }