• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }