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