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