1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to deal
6 * in the Software without restriction, including without limitation the rights
7 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 * copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 * THE SOFTWARE.
21 */
22
23 #include "debugger.h"
24
25 #include <arpa/inet.h>
26 #include <pthread.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/socket.h>
31 #include <unistd.h>
32
33 #include "message_server.h"
34
35 static int g_connectFlag = 0;
36 static int g_client = 0;
DBG_SetConnectFlag(int flag,int client)37 void DBG_SetConnectFlag(int flag, int client)
38 {
39 g_connectFlag = flag;
40 g_client = client;
41 }
42
DBG_IsConnect()43 static int DBG_IsConnect()
44 {
45 return g_connectFlag;
46 }
47
DBG_GetClient()48 static int DBG_GetClient()
49 {
50 return g_client;
51 }
52
DBG_ConnectToIde()53 static int DBG_ConnectToIde()
54 {
55 const char *hostAddr = JS_DEBUGGER_HOST_ADDRESS;
56 const int portNum = JS_DEBUGGER_PORT_NUM;
57 struct sockaddr_in ideAddr;
58 if (memset_s(&ideAddr, sizeof(ideAddr), 0, sizeof(ideAddr)) != 0) {
59 DEBUGGER_LOGE("DBG_ConnectToIde memset_s fail");
60 return FAIL_CAUSE_SOCKET_NO_CLIENT;
61 }
62 ideAddr.sin_port = htons(portNum);
63 ideAddr.sin_family = AF_INET;
64 ideAddr.sin_addr.s_addr = inet_addr(hostAddr);
65 int client = TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_STREAM, 0));
66 if (client <= 0) {
67 DEBUGGER_LOGI("DBG_ConnectToIde client=%d", client);
68 return FAIL_CAUSE_SOCKET_NO_CLIENT;
69 }
70 if (TEMP_FAILURE_RETRY(connect(client,
71 (const struct sockaddr *)&ideAddr, sizeof(ideAddr))) == -1) {
72 DEBUGGER_LOGE("DBG_ConnectToIde fail connectResult fail");
73 return FAIL_CAUSE_SOCKET_COMMON_FAIL;
74 }
75
76 return client;
77 }
78
DBG_SocketWrite(int client,const char * buf,int len)79 static int DBG_SocketWrite(int client, const char *buf, int len)
80 {
81 if (client <= 0 || len == 0 || buf == NULL) {
82 DEBUGGER_LOGE("DBG_SocketWrite fail client=%d, len=%d, buf=%s", client, len, buf);
83 return FAIL_CAUSE_SOCKET_COMMON_FAIL;
84 }
85
86 int loc = 0;
87 while (loc < len) {
88 int ret = TEMP_FAILURE_RETRY(write(client, (const void *)(buf + loc), len - loc));
89 if (ret <= 0 || ret > (len - loc)) {
90 DEBUGGER_LOGE("DBG_SocketWrite fail ret=%d", ret);
91 return FAIL_CAUSE_READ_MSG_FAIL;
92 }
93 loc = loc + ret;
94 }
95
96 return JS_SOCKET_SUCCESS;
97 }
98
DBG_ReadMsg(DebuggerInfo * debuggerInfo)99 static JSValue DBG_ReadMsg(DebuggerInfo *debuggerInfo)
100 {
101 if (debuggerInfo == NULL) {
102 DEBUGGER_LOGE("DBG_ReadMsg fail debuggerInfo=NULL");
103 return JS_UNDEFINED;
104 }
105 while (QueueIsEmpty()) {
106 usleep(DBG_WAITING_TIME);
107 }
108
109 const char *message = QueueFront();
110 DEBUGGER_LOGI("DBG_ReadMsg %s", message);
111 JSValue msg = JS_ParseJSON(debuggerInfo->cx, message, strlen(message), "<debugger>");
112 QueuePop();
113
114 return msg;
115 }
116
DBG_SendMsg(DebuggerInfo * debuggerInfo,JSValue msgBody)117 static void DBG_SendMsg(DebuggerInfo *debuggerInfo, JSValue msgBody)
118 {
119 if (debuggerInfo == NULL) {
120 return;
121 }
122 JSContext *cx = debuggerInfo->cx;
123 if (cx == NULL) {
124 return;
125 }
126 JSValueConst args[] = {msgBody, JS_UNDEFINED, JS_UNDEFINED};
127 JSValue jsValMsg = JS_JsonStringify(cx, JS_UNDEFINED, 1, args);
128 size_t len = 0;
129 const char *jsonStrMsg = JS_ToCStringLen(cx, &len, jsValMsg);
130 if (jsonStrMsg == NULL) {
131 DEBUGGER_LOGE("DBG_SendMsg fail jsonStrMsg=%s", jsonStrMsg);
132 return;
133 }
134 if (len == 0) {
135 DEBUGGER_LOGE("DBG_SendMsg fail len=%zu", len);
136 JS_FreeValue(cx, jsValMsg);
137 return;
138 }
139
140 DEBUGGER_LOGI("DBG_SendMsg: %s", jsonStrMsg);
141 char msglen[WRITE_MSG_LEN] = {0};
142 if (sprintf_s(msglen, sizeof(msglen), "%08x\n", (int)len + 1) < 0) {
143 JS_FreeCString(cx, jsonStrMsg);
144 JS_FreeValue(cx, jsValMsg);
145 return;
146 }
147 int writeMsgLenRet = DBG_SocketWrite(debuggerInfo->client, msglen, WRITE_MSG_LEN - 1);
148 if (!writeMsgLenRet) {
149 JS_FreeCString(cx, jsonStrMsg);
150 JS_FreeValue(cx, jsValMsg);
151 return;
152 }
153 int writeMsgRet = DBG_SocketWrite(debuggerInfo->client, jsonStrMsg, len);
154 if (!writeMsgRet) {
155 JS_FreeCString(cx, jsonStrMsg);
156 JS_FreeValue(cx, jsValMsg);
157 return;
158 }
159 char addLine[WRITE_MSG_ADD_NEW_LINE] = { '\n', '\0' };
160 int writeNewLineRet = DBG_SocketWrite(debuggerInfo->client, addLine, 1);
161 if (!writeNewLineRet) {
162 JS_FreeCString(cx, jsonStrMsg);
163 JS_FreeValue(cx, jsValMsg);
164 return;
165 }
166 JS_FreeCString(cx, jsonStrMsg);
167 JS_FreeValue(cx, jsValMsg);
168 }
DBG_SendStopMsg(DebuggerInfo * debuggerInfo,const char * stopReason)169 static void DBG_SendStopMsg(DebuggerInfo *debuggerInfo, const char *stopReason)
170 {
171 if (debuggerInfo == NULL) {
172 DEBUGGER_LOGE("DBG_SendStopMsg fail debuggerInfo=NULL");
173 return;
174 }
175 JSContext *cx = debuggerInfo->cx;
176 if (cx == NULL) {
177 DEBUGGER_LOGE("DBG_SendStopMsg fail cx=NULL");
178 return;
179 }
180 JSValue stopEvent = JS_NewObject(cx);
181 JS_SetPropertyStr(cx, stopEvent, "reason", JS_NewString(cx, stopReason));
182 JS_SetPropertyStr(cx, stopEvent, "type", JS_NewString(cx, "StoppedEvent"));
183 JS_SetPropertyStr(cx, stopEvent, "thread", JS_NewInt64(cx, (int64_t)debuggerInfo->client));
184 JSValue msgBody = JS_NewObject(cx);
185 JS_SetPropertyStr(cx, msgBody, "type", JS_NewString(cx, "event"));
186 JS_SetPropertyStr(cx, msgBody, "event", stopEvent);
187 DBG_SendMsg(debuggerInfo, msgBody);
188 JS_FreeValue(cx, msgBody);
189
190 return;
191 }
192
DBG_SendResponseMsg(DebuggerInfo * debuggerInfo,JSValue request,JSValue body)193 static void DBG_SendResponseMsg(DebuggerInfo *debuggerInfo, JSValue request, JSValue body)
194 {
195 if (debuggerInfo == NULL) {
196 DEBUGGER_LOGE("DBG_SendResponseMsg fail debuggerInfo=NULL");
197 return;
198 }
199 JSContext *cx = debuggerInfo->cx;
200 if (cx == NULL) {
201 DEBUGGER_LOGE("DBG_SendResponseMsg fail cx=NULL");
202 return;
203 }
204 JSValue msgBody = JS_NewObject(cx);
205 JS_SetPropertyStr(cx, msgBody, "type", JS_NewString(cx, "response"));
206 JS_SetPropertyStr(cx, msgBody, "request_seq", JS_GetPropertyStr(cx, request, "request_seq"));
207 JS_SetPropertyStr(cx, msgBody, "body", body);
208 DBG_SendMsg(debuggerInfo, msgBody);
209 JS_FreeValue(cx, msgBody);
210
211 return;
212 }
213
214
DBG_SetBreakpoints(DebuggerInfo * debuggerInfo,JSValue breakpoints)215 static void DBG_SetBreakpoints(DebuggerInfo *debuggerInfo, JSValue breakpoints)
216 {
217 if (debuggerInfo == NULL) {
218 DEBUGGER_LOGE("DBG_SetBreakpoints fail debuggerInfo=NULL");
219 return;
220 }
221 JSValue filename = JS_GetPropertyStr(debuggerInfo->cx, breakpoints, "path");
222 const char *path = JS_ToCString(debuggerInfo->cx, filename);
223 if (path == NULL) {
224 DEBUGGER_LOGE("DBG_SetBreakpoints fail path=%s", path);
225 JS_FreeValue(debuggerInfo->cx, filename);
226 return;
227 }
228 JSValue pathData = JS_GetPropertyStr(debuggerInfo->cx, debuggerInfo->breakpoints, path);
229 if (!JS_IsUndefined(pathData)) {
230 DEBUGGER_LOGE("DBG_SetBreakpoints fail pathData=JS_Undefined");
231 JS_FreeValue(debuggerInfo->cx, pathData);
232 }
233
234 pathData = JS_NewObject(debuggerInfo->cx);
235 JS_SetPropertyStr(debuggerInfo->cx, debuggerInfo->breakpoints, path, pathData);
236 JS_FreeCString(debuggerInfo->cx, path);
237 JS_FreeValue(debuggerInfo->cx, filename);
238
239 JSValue jsBreakpoints = JS_GetPropertyStr(debuggerInfo->cx, breakpoints, "breakpoints");
240 JS_SetPropertyStr(debuggerInfo->cx, pathData, "breakpoints", jsBreakpoints);
241 JS_FreeValue(debuggerInfo->cx, jsBreakpoints);
242
243 return;
244 }
245
DBG_CotinueProcess(DebuggerInfo * debuggerInfo,JSValue msg,const uint8_t * pc)246 static void DBG_CotinueProcess(DebuggerInfo *debuggerInfo, JSValue msg, const uint8_t *pc)
247 {
248 if (debuggerInfo == NULL) {
249 DEBUGGER_LOGE("DBG_CotinueProcess fail debuggerInfo=NULL");
250 return;
251 }
252 JSContext *cx = debuggerInfo->cx;
253 if (cx == NULL) {
254 return;
255 }
256 debuggerInfo->depth = JS_GetStackDepth(cx);
257 LocInfo loc = JS_GetCurrentLocation(cx, pc);
258 debuggerInfo->stepOperation = STEP_CONTINUE;
259 debuggerInfo->loc = loc;
260 DBG_SendResponseMsg(debuggerInfo, msg, JS_UNDEFINED);
261
262 return;
263 }
264
DBG_StackTraceProcess(DebuggerInfo * debuggerInfo,JSValue msg,const uint8_t * curPc)265 static void DBG_StackTraceProcess(DebuggerInfo *debuggerInfo, JSValue msg, const uint8_t *curPc)
266 {
267 if (debuggerInfo == NULL) {
268 DEBUGGER_LOGE("DBG_StackTraceProcess fail debuggerInfo=NULL");
269 return;
270 }
271 JSValue stackTrace = JS_BuildStackTrace(debuggerInfo->cx, curPc);
272 DBG_SendResponseMsg(debuggerInfo, msg, stackTrace);
273
274 return;
275 }
276
277
DBG_SetScopes(JSContext * cx,JSValue scopes,int scopeCount,int scopeType,int frameId)278 static void DBG_SetScopes(JSContext *cx, JSValue scopes, int scopeCount, int scopeType, int frameId)
279 {
280 if (cx == NULL) {
281 DEBUGGER_LOGE("DBG_StackTraceProcess fail cx=NULL");
282 return;
283 }
284 JSValue scopeObj = JS_NewObject(cx);
285 JSValue expensiveFlag = JS_FALSE;
286 switch (scopeType) {
287 case GLOBAL:
288 expensiveFlag = JS_TRUE;
289 JS_SetPropertyStr(cx, scopeObj, "name", JS_NewString(cx, "Global"));
290 break;
291 case LOCAL:
292 JS_SetPropertyStr(cx, scopeObj, "name", JS_NewString(cx, "Locals"));
293 break;
294 case CLOSURE:
295 JS_SetPropertyStr(cx, scopeObj, "name", JS_NewString(cx, "Closure"));
296 break;
297 default:
298 return;
299 }
300 JS_SetPropertyStr(cx, scopeObj, "reference",
301 JS_NewInt32(cx, (frameId << FRAME_MOVE_TWO_STEP) + scopeType));
302 JS_SetPropertyStr(cx, scopeObj, "expensive", expensiveFlag);
303 JS_SetPropertyUint32(cx, scopes, scopeCount, scopeObj);
304
305 return;
306 }
307
DBG_GetValueAsUint32Type(JSContext * cx,JSValue obj,const char * property)308 uint32_t DBG_GetValueAsUint32Type(JSContext *cx, JSValue obj, const char *property)
309 {
310 if (cx == NULL) {
311 DEBUGGER_LOGE("DBG_GetValueAsUint32Type fail cx=NULL");
312 return -1;
313 }
314 JSValue prop = JS_GetPropertyStr(cx, obj, property);
315 uint32_t ret;
316 JS_ToUint32(cx, &ret, prop);
317 JS_FreeValue(cx, prop);
318
319 return ret;
320 }
321
322
DBG_ScopesProcess(DebuggerInfo * debuggerInfo,JSValue msg)323 static void DBG_ScopesProcess(DebuggerInfo *debuggerInfo, JSValue msg)
324 {
325 if (debuggerInfo == NULL) {
326 DEBUGGER_LOGE("DBG_ScopesProcess fail debuggerInfo=NULL");
327 return;
328 }
329 JSContext *cx = debuggerInfo->cx;
330 if (cx == NULL) {
331 DEBUGGER_LOGE("DBG_ScopesProcess fail cx=NULL");
332 return;
333 }
334 JSValue argsValue = JS_GetPropertyStr(cx, msg, "args");
335 int frameId = DBG_GetValueAsUint32Type(cx, argsValue, "frameId");
336 if (frameId == -1) {
337 DEBUGGER_LOGE("DBG_ScopesProcess fail frameId=%d", frameId);
338 return;
339 }
340 JS_FreeValue(cx, argsValue);
341 JSValue scopes = JS_NewArray(cx);
342 int scopeCount = 0;
343 DBG_SetScopes(cx, scopes, scopeCount++, LOCAL, frameId);
344 DBG_SetScopes(cx, scopes, scopeCount++, CLOSURE, frameId);
345 DBG_SetScopes(cx, scopes, scopeCount++, GLOBAL, frameId);
346 DBG_SendResponseMsg(debuggerInfo, msg, scopes);
347 JS_FreeValue(cx, scopes);
348
349 return;
350 }
351
DBG_SetStepOverToDebugger(DebuggerInfo * debuggerInfo,JSValue msg,const uint8_t * pc)352 static void DBG_SetStepOverToDebugger(DebuggerInfo *debuggerInfo, JSValue msg, const uint8_t *pc)
353 {
354 if (debuggerInfo == NULL) {
355 DEBUGGER_LOGE("DBG_SetStepOverToDebugger fail debuggerInfo=NULL");
356 return;
357 }
358 JSContext *cx = debuggerInfo->cx;
359 if (cx == NULL) {
360 DEBUGGER_LOGE("DBG_SetStepOverToDebugger fail cx=NULL");
361 return;
362 }
363 debuggerInfo->depth = JS_GetStackDepth(cx);
364 LocInfo loc = JS_GetCurrentLocation(cx, pc);
365 debuggerInfo->stepOperation = STEP_NEXT;
366 debuggerInfo->loc = loc;
367 DBG_SendResponseMsg(debuggerInfo, msg, JS_UNDEFINED);
368
369 return;
370 }
371
DBG_SetStepOutToDebugger(DebuggerInfo * debuggerInfo,JSValue msg,const uint8_t * pc)372 static void DBG_SetStepOutToDebugger(DebuggerInfo *debuggerInfo, JSValue msg, const uint8_t *pc)
373 {
374 if (debuggerInfo == NULL) {
375 DEBUGGER_LOGE("DBG_SetStepOutToDebugger fail debuggerInfo=NULL");
376 return;
377 }
378 JSContext *cx = debuggerInfo->cx;
379 if (cx == NULL) {
380 DEBUGGER_LOGE("DBG_SetStepOutToDebugger fail cx=NULL");
381 return;
382 }
383 debuggerInfo->depth = JS_GetStackDepth(cx);
384 LocInfo loc = JS_GetCurrentLocation(cx, pc);
385 debuggerInfo->stepOperation = STEP_OUT;
386 debuggerInfo->loc = loc;
387 DBG_SendResponseMsg(debuggerInfo, msg, JS_UNDEFINED);
388
389 return;
390 }
391
DBG_SetStepInToDebugger(DebuggerInfo * debuggerInfo,JSValue msg,const uint8_t * pc)392 static void DBG_SetStepInToDebugger(DebuggerInfo *debuggerInfo, JSValue msg, const uint8_t *pc)
393 {
394 if (debuggerInfo == NULL) {
395 DEBUGGER_LOGE("DBG_SetStepInToDebugger fail debuggerInfo=NULL");
396 return;
397 }
398 JSContext *cx = debuggerInfo->cx;
399 if (cx == NULL) {
400 DEBUGGER_LOGE("DBG_SetStepInToDebugger fail cx=NULL");
401 return;
402 }
403 debuggerInfo->depth = JS_GetStackDepth(cx);
404 LocInfo loc = JS_GetCurrentLocation(cx, pc);
405 debuggerInfo->stepOperation = STEP_IN;
406 debuggerInfo->loc = loc;
407 DBG_SendResponseMsg(debuggerInfo, msg, JS_UNDEFINED);
408
409 return;
410 }
411
DBG_FreePropEnum(JSContext * cx,JSPropertyEnum * tab,uint32_t len)412 static void DBG_FreePropEnum(JSContext *cx, JSPropertyEnum *tab, uint32_t len)
413 {
414 if (cx == NULL || tab == NULL) {
415 DEBUGGER_LOGE("DBG_FreePropEnum fail cx=NULL or tab=NULL");
416 return;
417 }
418 for (uint32_t i = 0; i < len; i++) {
419 JS_FreeAtom(cx, tab[i].atom);
420 }
421 js_free(cx, tab);
422
423 return;
424 }
425
DBG_GetObjectVariableReference(JSContext * cx,struct DebuggerVariableState * state,JSValue var,JSValue varVal)426 static uint32_t DBG_GetObjectVariableReference(JSContext *cx,
427 struct DebuggerVariableState *state,
428 JSValue var,
429 JSValue varVal)
430 {
431 if (cx == NULL || state == NULL) {
432 DEBUGGER_LOGE("DBG_GetObjectVariableReference fail cx=NULL || state=NULL");
433 return 0;
434 }
435 uint32_t reference = 0;
436 JSObject *pObj = JS_VALUE_GET_OBJ(varVal);
437 if (pObj == NULL) {
438 return reference;
439 }
440 uint32_t pVal = (uint32_t)pObj;
441 JSValue found = JS_GetPropertyUint32(cx, state->variablePointers, pVal);
442 if (JS_IsUndefined(found)) {
443 reference = state->variableReferenceCount++;
444 JS_SetPropertyUint32(cx, state->variableReferences, reference, JS_DupValue(cx, varVal));
445 JS_SetPropertyUint32(cx, state->variablePointers, pVal, JS_NewInt32(cx, reference));
446 } else {
447 JS_ToUint32(cx, &reference, found);
448 }
449 JS_FreeValue(cx, found);
450
451 return reference;
452 }
453
DBG_SetVariableType(JSContext * cx,struct DebuggerVariableState * state,JSValue var,JSValue varVal)454 static void DBG_SetVariableType(JSContext *cx,
455 struct DebuggerVariableState *state,
456 JSValue var,
457 JSValue varVal)
458 {
459 if (cx == NULL || state == NULL) {
460 DEBUGGER_LOGE("DBG_SetVariableType fail cx=NULL || state=NULL");
461 return;
462 }
463 uint32_t tag = JS_VALUE_GET_TAG(varVal);
464 uint32_t reference = 0;
465 switch (tag) {
466 case JS_TAG_INT: // Same processing as JS_TAG_BIG_INT
467 case JS_TAG_BIG_INT:
468 JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "integer"));
469 break;
470 case JS_TAG_FLOAT64: // Same processing as JS_TAG_BIG_FLOAT
471 case JS_TAG_BIG_FLOAT:
472 JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "float"));
473 break;
474 case JS_TAG_BOOL:
475 JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "bool"));
476 break;
477 case JS_TAG_STRING:
478 JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "string"));
479 break;
480 case JS_TAG_NULL:
481 JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "null"));
482 break;
483 case JS_TAG_EXCEPTION:
484 JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "exception"));
485 break;
486 case JS_TAG_UNDEFINED:
487 JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "undefined"));
488 break;
489 case JS_TAG_OBJECT:
490 JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "object"));
491 reference = DBG_GetObjectVariableReference(cx, state, var, varVal);
492 break;
493 default:
494 break;
495 }
496 JS_SetPropertyStr(cx, var, "variablesReference", JS_NewInt32(cx, reference));
497
498 return;
499 }
500
DBG_SetVariableProperty(JSContext * cx,JSValue varVal,JSValue var,const char * valueProperty)501 static void DBG_SetVariableProperty(JSContext *cx,
502 JSValue varVal,
503 JSValue var,
504 const char *valueProperty)
505 {
506 if (cx == NULL || valueProperty == NULL) {
507 DEBUGGER_LOGE("DBG_SetVariableProperty fail cx=NULL || valueProperty=%s", valueProperty);
508 return;
509 }
510 if (JS_IsArray(cx, varVal)) {
511 uint32_t len = DBG_GetValueAsUint32Type(cx, varVal, "length");
512 char lenBuf[STR_BUF_SIZE] = {0};
513 if (sprintf_s(lenBuf, sizeof(lenBuf), "Array (%d)", len) < 0) {
514 return;
515 }
516 JS_SetPropertyStr(cx, var, valueProperty, JS_NewString(cx, lenBuf));
517 JS_SetPropertyStr(cx, var, "indexedVariables", JS_NewInt32(cx, len));
518 } else {
519 JS_SetPropertyStr(cx, var, valueProperty, JS_ToString(cx, varVal));
520 }
521
522 return;
523 }
524
DBG_GetVariableObj(JSContext * cx,struct DebuggerVariableState * state,JSValue varName,JSValue varVal)525 static JSValue DBG_GetVariableObj(JSContext *cx,
526 struct DebuggerVariableState *state,
527 JSValue varName,
528 JSValue varVal)
529 {
530 if (cx == NULL || state == NULL) {
531 DEBUGGER_LOGE("DBG_GetVariableObj fail cx=NULL || state=NULL");
532 return JS_NULL;
533 }
534 JSValue var = JS_NewObject(cx);
535 JS_SetPropertyStr(cx, var, "name", varName);
536 DBG_SetVariableProperty(cx, varVal, var, "value");
537 DBG_SetVariableType(cx, state, var, varVal);
538
539 return var;
540 }
541
DBG_ProcessUndefinedVariable(JSContext * cx,struct DebuggerVariableState * state,uint32_t reference,JSValue variable)542 static JSValue DBG_ProcessUndefinedVariable(JSContext *cx, struct DebuggerVariableState *state,
543 uint32_t reference, JSValue variable)
544 {
545 if (cx == NULL || state == NULL) {
546 DEBUGGER_LOGE("DBG_ProcessUndefinedVariable fail cx=NULL || state=NULL");
547 return JS_NULL;
548 }
549 int frame = reference >> FRAME_MOVE_TWO_STEP;
550 int scope = reference % TYPE_OF_SCOPE_NUM;
551 if (frame >= JS_GetStackDepth(cx)) {
552 return variable;
553 }
554 switch (scope) {
555 case GLOBAL:
556 variable = JS_GetGlobalObject(cx);
557 break;
558 case LOCAL:
559 variable = JS_GetLocalVariables(cx, frame);
560 break;
561 case CLOSURE:
562 variable = JS_GetClosureVariables(cx, frame);
563 break;
564 default:
565 break;
566 }
567 JS_SetPropertyUint32(cx, state->variableReferences, reference, JS_DupValue(cx, variable));
568 return variable;
569 }
570
DBG_VariablesUnFilteredProcess(JSContext * cx,JSValue variable,JSValue properties,struct DebuggerVariableState * state)571 static JSValue DBG_VariablesUnFilteredProcess(JSContext *cx,
572 JSValue variable,
573 JSValue properties,
574 struct DebuggerVariableState *state)
575 {
576 if (cx == NULL || state == NULL) {
577 DEBUGGER_LOGE("DBG_VariablesUnFilteredProcess fail cx=NULL || state=NULL");
578 return JS_NULL;
579 }
580 JSPropertyEnum *tabAtom = NULL;
581 uint32_t tabAtomCount = 0;
582 if (JS_GetOwnPropertyNames(cx, &tabAtom, &tabAtomCount, variable,
583 JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) {
584 return properties;
585 }
586
587 for (uint32_t idx = 0; idx < tabAtomCount; idx++) {
588 JSValue value = JS_GetProperty(cx, variable, tabAtom[idx].atom);
589 JSValue variableJson = DBG_GetVariableObj(cx, state,
590 JS_AtomToString(cx, tabAtom[idx].atom), value);
591 JS_FreeValue(cx, value);
592 JS_SetPropertyUint32(cx, properties, idx, variableJson);
593 }
594 DBG_FreePropEnum(cx, tabAtom, tabAtomCount);
595
596 return properties;
597 }
598
DBG_VariablesFilteredProcess(JSContext * cx,JSValue args,JSValue variable,JSValue properties,struct DebuggerVariableState * state)599 static JSValue DBG_VariablesFilteredProcess(JSContext *cx,
600 JSValue args,
601 JSValue variable,
602 JSValue properties,
603 struct DebuggerVariableState *state)
604 {
605 if (cx == NULL || state == NULL) {
606 DEBUGGER_LOGE("DBG_VariablesFilteredProcess fail cx=NULL || state=NULL");
607 return JS_NULL;
608 }
609 const char *filterStr = JS_ToCString(cx, JS_GetPropertyStr(cx, args, "filter"));
610 if (filterStr == NULL) {
611 DEBUGGER_LOGE("DBG_VariablesFilteredProcess fail filterStr=%s", filterStr);
612 return JS_NULL;
613 }
614 int indexed = strcmp(filterStr, "indexed");
615 JS_FreeCString(cx, filterStr);
616
617 if (indexed != 0) {
618 return DBG_VariablesUnFilteredProcess(cx, variable, properties, state);
619 }
620
621 uint32_t start = DBG_GetValueAsUint32Type(cx, args, "start");
622 if (start < 0) {
623 return JS_NULL;
624 }
625 uint32_t count = DBG_GetValueAsUint32Type(cx, args, "count");
626 if (count < 0) {
627 return JS_NULL;
628 }
629 char nameBuf[STR_BUF_SIZE] = {0};
630 for (uint32_t i = 0; i < count; i++) {
631 JSValue value = JS_GetPropertyUint32(cx, variable, start + i);
632 if (sprintf_s(nameBuf, sizeof(nameBuf), "%d", i) < 0) {
633 continue;
634 }
635 JSValue variableJson = DBG_GetVariableObj(cx, state, JS_NewString(cx, nameBuf), value);
636 JS_FreeValue(cx, value);
637 JS_SetPropertyUint32(cx, properties, i, variableJson);
638 }
639
640 return properties;
641 }
642
DBG_VariablesProcess(DebuggerInfo * debuggerInfo,JSValue request,struct DebuggerVariableState * state)643 static void DBG_VariablesProcess(DebuggerInfo *debuggerInfo,
644 JSValue request,
645 struct DebuggerVariableState *state)
646 {
647 if (debuggerInfo == NULL || state == NULL) {
648 DEBUGGER_LOGE("DBG_VariablesProcess fail debuggerInfo=NULL || state=NULL");
649 return;
650 }
651 JSContext *cx = debuggerInfo->cx;
652 if (cx == NULL) {
653 DEBUGGER_LOGE("DBG_VariablesProcess fail cx=NULL");
654 return;
655 }
656 JSValue args = JS_GetPropertyStr(cx, request, "args");
657 uint32_t reference = DBG_GetValueAsUint32Type(cx, args, "variablesReference");
658 JSValue properties = JS_NewArray(cx);
659 JSValue variable = JS_GetPropertyUint32(cx, state->variableReferences, reference);
660 if (JS_IsUndefined(variable)) {
661 variable = DBG_ProcessUndefinedVariable(cx, state, reference, variable);
662 }
663 JSValue filter = JS_GetPropertyStr(cx, args, "filter");
664 if (!JS_IsUndefined(filter)) {
665 properties = DBG_VariablesFilteredProcess(cx, args, variable, properties, state);
666 } else {
667 properties = DBG_VariablesUnFilteredProcess(cx, variable, properties, state);
668 }
669 JS_FreeValue(cx, variable);
670 JS_FreeValue(cx, args);
671 DBG_SendResponseMsg(debuggerInfo, request, properties);
672 JS_FreeValue(cx, properties);
673
674 return;
675 }
676
DBG_EvaluateProcess(DebuggerInfo * debuggerInfo,JSValue msg,struct DebuggerVariableState * state)677 static void DBG_EvaluateProcess(DebuggerInfo *debuggerInfo,
678 JSValue msg,
679 struct DebuggerVariableState *state)
680 {
681 if (debuggerInfo == NULL || state == NULL) {
682 DEBUGGER_LOGE("DBG_EvaluateProcess fail debuggerInfo=NULL || state=NULL");
683 return;
684 }
685 JSContext *cx = debuggerInfo->cx;
686 if (cx == NULL) {
687 DEBUGGER_LOGE("DBG_EvaluateProcess fail cx=NULL");
688 return;
689 }
690 JSValue argsJsVal = JS_GetPropertyStr(cx, msg, "args");
691 JSValue expressionJsVal = JS_GetPropertyStr(cx, argsJsVal, "expression");
692 JS_FreeValue(cx, argsJsVal);
693 int frameId;
694 JSValue frameJsVal = JS_GetPropertyStr(cx, argsJsVal, "frameId");
695 JS_ToInt32(cx, &frameId, frameJsVal);
696 JS_FreeValue(cx, frameJsVal);
697 JSValue ret = JS_DebuggerEvaluate(cx, frameId, expressionJsVal);
698 if (JS_IsException(ret)) {
699 DEBUGGER_LOGE("DBG_EvaluateProcess fail ret=JS_Exception");
700 JS_FreeValue(cx, ret);
701 ret = JS_GetException(cx);
702 }
703 JS_FreeValue(cx, expressionJsVal);
704
705 JSValue body = JS_NewObject(cx);
706 DBG_SetVariableProperty(cx, ret, body, "result");
707 DBG_SetVariableType(cx, state, body, ret);
708 DBG_SendResponseMsg(debuggerInfo, msg, body);
709 JS_FreeValue(cx, body);
710
711 return;
712 }
713
DBG_PauseProcess(DebuggerInfo * debuggerInfo,JSValue msg)714 static void DBG_PauseProcess(DebuggerInfo *debuggerInfo, JSValue msg)
715 {
716 if (debuggerInfo == NULL) {
717 DEBUGGER_LOGE("DBG_PauseProcess fail debuggerInfo=NULL");
718 return;
719 }
720 DBG_SendResponseMsg(debuggerInfo, msg, JS_UNDEFINED);
721 DBG_SendStopMsg(debuggerInfo, "pause");
722
723 return;
724 }
725
DBG_RequestProcess(DebuggerInfo * debuggerInfo,JSValue msg,struct DebuggerVariableState * state)726 static int DBG_RequestProcess(DebuggerInfo *debuggerInfo,
727 JSValue msg,
728 struct DebuggerVariableState *state)
729 {
730 if (debuggerInfo == NULL || state == NULL) {
731 return DBG_NEED_READ_MSG;
732 }
733 JSContext *cx = debuggerInfo->cx;
734 if (cx == NULL) {
735 return DBG_NEED_READ_MSG;
736 }
737 const uint8_t *pc = state->curPc;
738 int isNeedReadMsg = 0;
739 JSValue jsRequestMsg = JS_GetPropertyStr(cx, msg, "request");
740 JSValue jsCommand = JS_GetPropertyStr(cx, jsRequestMsg, "command");
741 const char *command = JS_ToCString(cx, jsCommand);
742 if (command == NULL) {
743 DEBUGGER_LOGE("DBG_RequestProcess fail command=%s", command);
744 return DBG_NEED_READ_MSG;
745 }
746 if (strcmp(command, "continue") == 0) {
747 DBG_CotinueProcess(debuggerInfo, jsRequestMsg, pc);
748 } else if (strcmp(command, "stackTrace") == 0) {
749 DBG_StackTraceProcess(debuggerInfo, jsRequestMsg, pc);
750 isNeedReadMsg = DBG_NEED_READ_MSG;
751 } else if (strcmp(command, "scopes") == 0) {
752 DBG_ScopesProcess(debuggerInfo, jsRequestMsg);
753 isNeedReadMsg = DBG_NEED_READ_MSG;
754 } else if (strcmp(command, "stepIn") == 0) {
755 DBG_SetStepInToDebugger(debuggerInfo, jsRequestMsg, pc);
756 } else if (strcmp(command, "stepOut") == 0) {
757 DBG_SetStepOutToDebugger(debuggerInfo, jsRequestMsg, pc);
758 } else if (strcmp(command, "next") == 0) {
759 DBG_SetStepOverToDebugger(debuggerInfo, jsRequestMsg, pc);
760 } else if (strcmp(command, "variables") == 0) {
761 DBG_VariablesProcess(debuggerInfo, jsRequestMsg, state);
762 isNeedReadMsg = DBG_NEED_READ_MSG;
763 } else if (strcmp(command, "evaluate") == 0) {
764 DBG_EvaluateProcess(debuggerInfo, jsRequestMsg, state);
765 isNeedReadMsg = DBG_NEED_READ_MSG;
766 } else if (strcmp(command, "pause") == 0) {
767 DBG_PauseProcess(debuggerInfo, jsRequestMsg);
768 isNeedReadMsg = DBG_NEED_READ_MSG;
769 }
770 JS_FreeCString(cx, command);
771 JS_FreeValue(cx, jsRequestMsg);
772
773 return isNeedReadMsg;
774 }
775
DBG_ProcessMsgByType(DebuggerInfo * debuggerInfo,const char * type,JSValue msg,struct DebuggerVariableState * state)776 static int DBG_ProcessMsgByType(DebuggerInfo *debuggerInfo, const char *type,
777 JSValue msg, struct DebuggerVariableState *state)
778 {
779 if (debuggerInfo == NULL || type == NULL) {
780 DEBUGGER_LOGE("DBG_ProcessMsgByType fail debuggerInfo=NULL || type=%s", type);
781 return DBG_NEED_READ_MSG;
782 }
783 if (strcmp(type, "request") == 0) {
784 return DBG_RequestProcess(debuggerInfo, msg, state);
785 }
786 if (strcmp(type, "breakpoints") == 0) {
787 JSValue jsBreakpointsVal = JS_GetPropertyStr(debuggerInfo->cx, msg, "breakpoints");
788 DBG_SetBreakpoints(debuggerInfo, jsBreakpointsVal);
789 JS_FreeValue(debuggerInfo->cx, jsBreakpointsVal);
790 return DBG_NEED_READ_MSG;
791 }
792 if (strcmp(type, "continue") == 0) {
793 return DBG_NO_NEED_READ_MSG;
794 }
795 return DBG_NEED_READ_MSG;
796 }
797
DBG_GetVariableState(DebuggerInfo * debuggerInfo,const uint8_t * pc)798 static DebuggerVariableState DBG_GetVariableState(DebuggerInfo *debuggerInfo, const uint8_t *pc)
799 {
800 struct DebuggerVariableState state = {0};
801 state.variableReferenceCount = JS_GetStackDepth(debuggerInfo->cx) << STACK_DEPTH_MOVE_TWO_STEP;
802 state.variablePointers = JS_NewObject(debuggerInfo->cx);
803 state.variableReferences = JS_NewObject(debuggerInfo->cx);
804 state.curPc = pc;
805
806 return state;
807 }
808
DBG_ProcessMsg(DebuggerInfo * debuggerInfo,const uint8_t * pc,int runningBreakpoint)809 static void DBG_ProcessMsg(DebuggerInfo *debuggerInfo, const uint8_t *pc, int runningBreakpoint)
810 {
811 if (debuggerInfo == NULL) {
812 DEBUGGER_LOGE("DBG_ProcessMsg fail debuggerInfo=NULL");
813 return;
814 }
815 int isNeedReadMsg = DBG_NEED_READ_MSG;
816 struct DebuggerVariableState state = DBG_GetVariableState(debuggerInfo, pc);
817 while (isNeedReadMsg) {
818 fflush(stdout);
819 fflush(stderr);
820 JSValue msg = DBG_ReadMsg(debuggerInfo);
821 if (JS_IsUndefined(msg)) {
822 DEBUGGER_LOGE("DBG_ProcessMsg fail msg=JS_Undefined");
823 return;
824 }
825 JSValue jsType = JS_GetPropertyStr(debuggerInfo->cx, msg, "type");
826 const char *type = JS_ToCString(debuggerInfo->cx, jsType);
827 JS_FreeValue(debuggerInfo->cx, jsType);
828 if (type == NULL) {
829 DEBUGGER_LOGE("DBG_ProcessMsg fail type is NULL");
830 return;
831 }
832 DEBUGGER_LOGI("DBG_ProcessMsg type=%s", type);
833 isNeedReadMsg = DBG_ProcessMsgByType(debuggerInfo, type, msg, &state);
834 if (runningBreakpoint) {
835 isNeedReadMsg = DBG_NO_NEED_READ_MSG;
836 }
837 JS_FreeCString(debuggerInfo->cx, type);
838 }
839 }
840
DBG_Entry(JSContext * cx,int client)841 static void DBG_Entry(JSContext *cx, int client)
842 {
843 if (cx == NULL) {
844 DEBUGGER_LOGE("DBG_Entry fail cx=NULL");
845 return;
846 }
847 struct DebuggerInfo *debuggerInfo = JS_GetDebuggerInfo(cx);
848 if (debuggerInfo == NULL) {
849 DEBUGGER_LOGE("DBG_Entry fail debuggerInfo=NULL");
850 return;
851 }
852 debuggerInfo->cx = cx;
853 debuggerInfo->client = client;
854 debuggerInfo->breakpoints = JS_NewObject(cx);
855 debuggerInfo->isConnected = 1;
856 DBG_SendStopMsg(debuggerInfo, "entry");
857 }
858
DBG_IsLocEqual(DebuggerInfo * debuggerInfo,uint32_t depth,LocInfo loc)859 static int DBG_IsLocEqual(DebuggerInfo *debuggerInfo, uint32_t depth, LocInfo loc)
860 {
861 if (debuggerInfo == NULL) {
862 DEBUGGER_LOGE("DBG_IsLocEqual fail debuggerInfo=NULL");
863 return DBG_LOC_ISEQUAL;
864 }
865 if (loc.line == 0 || loc.line == -1) {
866 return DBG_LOC_ISEQUAL;
867 }
868 DEBUGGER_LOGI("DBG_IsLocEqual last depth %d, this depth %d, last line %d, this line %d", debuggerInfo->depth,
869 depth, debuggerInfo->loc.line, loc.line);
870 if ((debuggerInfo->depth == depth) && (debuggerInfo->loc.line == loc.line) &&
871 (debuggerInfo->loc.filename == loc.filename)) {
872 return DBG_LOC_ISEQUAL;
873 }
874
875 return DBG_LOC_ISNOTEQUAL;
876 }
877
DBG_ProcessStepOperation(JSContext * cx,DebuggerInfo * debuggerInfo,const uint8_t * pc)878 static int DBG_ProcessStepOperation(JSContext *cx, DebuggerInfo *debuggerInfo, const uint8_t *pc)
879 {
880 if (cx == NULL || debuggerInfo == NULL) {
881 DEBUGGER_LOGE("DBG_ProcessStepOperation fail cx=NULL || debuggerInfo==NULL");
882 return 1;
883 }
884 int isNeedReadMsg = 0;
885 uint32_t depth = JS_GetStackDepth(cx);
886 LocInfo loc = JS_GetCurrentLocation(cx, pc);
887 if (debuggerInfo->stepOperation == STEP_CONTINUE) {
888 if (depth == debuggerInfo->depth) {
889 // Depth is equal indicates that current opcode's line is not the same as last one.
890 // Clear stepOperation so that it's possible to break on the same line inside loops.
891 debuggerInfo->stepOperation = NO_STEP_OPERATION;
892 }
893 isNeedReadMsg = 0;
894 return isNeedReadMsg;
895 }
896 if (debuggerInfo->stepOperation == STEP_NEXT) {
897 if (depth > debuggerInfo->depth || (loc.line == debuggerInfo->loc.line &&
898 loc.filename == debuggerInfo->loc.filename)) {
899 isNeedReadMsg = 0;
900 return isNeedReadMsg;
901 }
902 DBG_SendStopMsg(debuggerInfo, "step");
903 debuggerInfo->stepOperation = NO_STEP_OPERATION;
904 isNeedReadMsg = 1;
905 return isNeedReadMsg;
906 }
907 if (debuggerInfo->stepOperation == STEP_IN) {
908 if ((loc.line == 0) || (loc.line == debuggerInfo->loc.line &&
909 loc.filename == debuggerInfo->loc.filename)) {
910 isNeedReadMsg = 0;
911 return isNeedReadMsg;
912 }
913 DBG_SendStopMsg(debuggerInfo, "stepIn");
914 debuggerInfo->stepOperation = NO_STEP_OPERATION;
915 isNeedReadMsg = 1;
916 return isNeedReadMsg;
917 }
918 if (debuggerInfo->stepOperation == STEP_OUT) {
919 if (depth >= debuggerInfo->depth) {
920 isNeedReadMsg = 0;
921 return isNeedReadMsg;
922 }
923 DBG_SendStopMsg(debuggerInfo, "stepOut");
924 debuggerInfo->stepOperation = NO_STEP_OPERATION;
925 isNeedReadMsg = 1;
926 return isNeedReadMsg;
927 }
928 debuggerInfo->stepOperation = NO_STEP_OPERATION;
929
930 return isNeedReadMsg;
931 }
932
DBG_FreeSources(JSContext * cx,DebuggerInfo * debuggerInfo)933 void DBG_FreeSources(JSContext *cx, DebuggerInfo *debuggerInfo)
934 {
935 if (cx == NULL) {
936 DEBUGGER_LOGE("DBG_FreeSources fail cx=NULL");
937 return;
938 }
939 if (debuggerInfo->client <= 0) {
940 return;
941 }
942 close(debuggerInfo->client);
943 JS_FreeValue(cx, debuggerInfo->breakpoints);
944 }
945
DBG_CallDebugger(JSContext * cx,const uint8_t * pc)946 void DBG_CallDebugger(JSContext *cx, const uint8_t *pc)
947 {
948 if (cx == NULL) {
949 DEBUGGER_LOGE("DBG_CallDebugger fail cx=NULL");
950 return;
951 }
952 struct DebuggerInfo *debuggerInfo = JS_GetDebuggerInfo(cx);
953 if (debuggerInfo == NULL) {
954 DEBUGGER_LOGE("DBG_CallDebugger fail debuggerInfo=NULL");
955 return;
956 }
957 if (debuggerInfo->isConnected == 0) {
958 pthread_t tid;
959 if (pthread_create(&tid, NULL, &DBG_StartAgent, NULL) != 0) {
960 DEBUGGER_LOGE("pthread_create fail!");
961 return;
962 }
963 while (DBG_IsConnect() == 0) {
964 usleep(DBG_WAITING_TIME);
965 }
966 DBG_Entry(cx, DBG_GetClient());
967 // read msg when first time must be breakpoints msg, and set breakpoints to debugger
968 DBG_ProcessMsg(debuggerInfo, pc, 0);
969 }
970
971 if (QueueIsEmpty() == 0) {
972 DBG_ProcessMsg(debuggerInfo, pc, 1);
973 }
974
975 // when last time the debugger have step operaton, check the location is changed
976 if (debuggerInfo->stepOperation != NO_STEP_OPERATION) {
977 // check loction
978 uint32_t depth = JS_GetStackDepth(cx);
979 LocInfo loc = JS_GetCurrentLocation(cx, pc);
980 if (DBG_IsLocEqual(debuggerInfo, depth, loc) == DBG_LOC_ISEQUAL) {
981 return;
982 }
983 }
984 // must check breakpotint first, then process step operation
985 if (JS_HitBreakpoint(cx, pc) && JS_JudgeConditionBreakPoint(cx, pc)) {
986 LocInfo loc = JS_GetCurrentLocation(cx, pc);
987 DEBUGGER_LOGI("DBG_CallDebugger hit breakpoint at line %d", loc.line);
988
989 debuggerInfo->stepOperation = NO_STEP_OPERATION;
990 debuggerInfo->depth = JS_GetStackDepth(cx);
991 DBG_SendStopMsg(debuggerInfo, "breakpoint");
992 DBG_ProcessMsg(debuggerInfo, pc, 0);
993 return;
994 }
995 // process step operation
996 if (debuggerInfo->stepOperation != NO_STEP_OPERATION) {
997 // process step operation
998 int isNeedReadMsg = DBG_ProcessStepOperation(cx, debuggerInfo, pc);
999 if (!isNeedReadMsg) {
1000 return;
1001 }
1002 // continue process msg
1003 DBG_ProcessMsg(debuggerInfo, pc, 0);
1004 }
1005
1006 return;
1007 }
1008