• 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 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