1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/openharmony-third_party_quickjs

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Клонировать/Скачать
debugger.c 34 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
wupengyong Отправлено 23.12.2021 04:56 66eac83
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010
/*
* Copyright (c) 2021 Huawei Device Co., Ltd.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "debugger.h"
#include <arpa/inet.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "message_server.h"
static __thread bool g_isServerStarted = 0;
static __thread bool g_isBreakPointSet = 0;
static __thread bool g_isAttachMode = 0;
void DBG_SetDebugMode(bool isAttachMode)
{
g_isAttachMode = isAttachMode;
}
static int DBG_ConnectToIde()
{
const char *hostAddr = JS_DEBUGGER_HOST_ADDRESS;
const int portNum = JS_DEBUGGER_PORT_NUM;
struct sockaddr_in ideAddr;
if (memset_s(&ideAddr, sizeof(ideAddr), 0, sizeof(ideAddr)) != 0) {
DEBUGGER_LOGE("DBG_ConnectToIde memset_s fail");
return FAIL_CAUSE_SOCKET_NO_CLIENT;
}
ideAddr.sin_port = htons(portNum);
ideAddr.sin_family = AF_INET;
ideAddr.sin_addr.s_addr = inet_addr(hostAddr);
int client = TEMP_FAILURE_RETRY(socket(AF_INET, SOCK_STREAM, 0));
if (client <= 0) {
DEBUGGER_LOGI("DBG_ConnectToIde client=%d", client);
return FAIL_CAUSE_SOCKET_NO_CLIENT;
}
if (TEMP_FAILURE_RETRY(connect(client,
(const struct sockaddr *)&ideAddr, sizeof(ideAddr))) == -1) {
DEBUGGER_LOGE("DBG_ConnectToIde fail connectResult fail");
return FAIL_CAUSE_SOCKET_COMMON_FAIL;
}
return client;
}
static int DBG_SocketWrite(int client, const char *buf, int len)
{
if (client <= 0 || len == 0 || buf == NULL) {
DEBUGGER_LOGE("DBG_SocketWrite fail client=%d, len=%d, buf=%s", client, len, buf);
return FAIL_CAUSE_SOCKET_COMMON_FAIL;
}
int loc = 0;
while (loc < len) {
int ret = TEMP_FAILURE_RETRY(write(client, (const void *)(buf + loc), len - loc));
if (ret <= 0 || ret > (len - loc)) {
DEBUGGER_LOGE("DBG_SocketWrite fail ret=%d", ret);
return FAIL_CAUSE_READ_MSG_FAIL;
}
loc = loc + ret;
}
return JS_SOCKET_SUCCESS;
}
static JSValue DBG_ReadMsg(DebuggerInfo *debuggerInfo)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_ReadMsg fail debuggerInfo=NULL");
return JS_UNDEFINED;
}
while (QueueIsEmpty(debuggerInfo->client)) {
usleep(DBG_WAITING_TIME);
}
const char *message = QueueFront(debuggerInfo->client);
DEBUGGER_LOGI("DBG_ReadMsg %s", message);
JSValue msg = JS_ParseJSON(debuggerInfo->cx, message, strlen(message), "<debugger>");
QueuePop(debuggerInfo->client);
return msg;
}
static void DBG_SendMsg(DebuggerInfo *debuggerInfo, JSValue msgBody)
{
if (debuggerInfo == NULL) {
return;
}
JSContext *cx = debuggerInfo->cx;
if (cx == NULL) {
return;
}
JSValueConst args[] = {msgBody, JS_UNDEFINED, JS_UNDEFINED};
JSValue jsValMsg = JS_JsonStringify(cx, JS_UNDEFINED, 1, args);
size_t len = 0;
const char *jsonStrMsg = JS_ToCStringLen(cx, &len, jsValMsg);
if (jsonStrMsg == NULL) {
DEBUGGER_LOGE("DBG_SendMsg fail jsonStrMsg=%s", jsonStrMsg);
return;
}
if (len == 0) {
DEBUGGER_LOGE("DBG_SendMsg fail len=%zu", len);
JS_FreeValue(cx, jsValMsg);
return;
}
DEBUGGER_LOGI("DBG_SendMsg: %s", jsonStrMsg);
char msglen[WRITE_MSG_LEN] = {0};
if (sprintf_s(msglen, sizeof(msglen), "%08x\n", (int)len + 1) < 0) {
JS_FreeCString(cx, jsonStrMsg);
JS_FreeValue(cx, jsValMsg);
return;
}
int writeMsgLenRet = DBG_SocketWrite(debuggerInfo->client, msglen, WRITE_MSG_LEN - 1);
if (!writeMsgLenRet) {
JS_FreeCString(cx, jsonStrMsg);
JS_FreeValue(cx, jsValMsg);
return;
}
int writeMsgRet = DBG_SocketWrite(debuggerInfo->client, jsonStrMsg, len);
if (!writeMsgRet) {
JS_FreeCString(cx, jsonStrMsg);
JS_FreeValue(cx, jsValMsg);
return;
}
char addLine[WRITE_MSG_ADD_NEW_LINE] = { '\n', '\0' };
int writeNewLineRet = DBG_SocketWrite(debuggerInfo->client, addLine, 1);
if (!writeNewLineRet) {
JS_FreeCString(cx, jsonStrMsg);
JS_FreeValue(cx, jsValMsg);
return;
}
JS_FreeCString(cx, jsonStrMsg);
JS_FreeValue(cx, jsValMsg);
}
static void DBG_SendStopMsg(DebuggerInfo *debuggerInfo, const char *stopReason)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_SendStopMsg fail debuggerInfo=NULL");
return;
}
JSContext *cx = debuggerInfo->cx;
if (cx == NULL) {
DEBUGGER_LOGE("DBG_SendStopMsg fail cx=NULL");
return;
}
JSValue stopEvent = JS_NewObject(cx);
JS_SetPropertyStr(cx, stopEvent, "reason", JS_NewString(cx, stopReason));
JS_SetPropertyStr(cx, stopEvent, "type", JS_NewString(cx, "StoppedEvent"));
JS_SetPropertyStr(cx, stopEvent, "thread", JS_NewInt64(cx, (int64_t)debuggerInfo->client));
JSValue msgBody = JS_NewObject(cx);
JS_SetPropertyStr(cx, msgBody, "type", JS_NewString(cx, "event"));
JS_SetPropertyStr(cx, msgBody, "event", stopEvent);
DBG_SendMsg(debuggerInfo, msgBody);
JS_FreeValue(cx, msgBody);
return;
}
static void DBG_SendResponseMsg(DebuggerInfo *debuggerInfo, JSValue request, JSValue body)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_SendResponseMsg fail debuggerInfo=NULL");
return;
}
JSContext *cx = debuggerInfo->cx;
if (cx == NULL) {
DEBUGGER_LOGE("DBG_SendResponseMsg fail cx=NULL");
return;
}
JSValue msgBody = JS_NewObject(cx);
JS_SetPropertyStr(cx, msgBody, "type", JS_NewString(cx, "response"));
JS_SetPropertyStr(cx, msgBody, "request_seq", JS_GetPropertyStr(cx, request, "request_seq"));
JS_SetPropertyStr(cx, msgBody, "body", body);
DBG_SendMsg(debuggerInfo, msgBody);
JS_FreeValue(cx, msgBody);
return;
}
static void DBG_SetBreakpoints(DebuggerInfo *debuggerInfo, JSValue breakpoints)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_SetBreakpoints fail debuggerInfo=NULL");
return;
}
JSValue filename = JS_GetPropertyStr(debuggerInfo->cx, breakpoints, "path");
const char *path = JS_ToCString(debuggerInfo->cx, filename);
if (path == NULL) {
DEBUGGER_LOGE("DBG_SetBreakpoints fail path=%s", path);
JS_FreeValue(debuggerInfo->cx, filename);
return;
}
JSValue pathData = JS_GetPropertyStr(debuggerInfo->cx, debuggerInfo->breakpoints, path);
if (!JS_IsUndefined(pathData)) {
DEBUGGER_LOGE("DBG_SetBreakpoints fail pathData=JS_Undefined");
JS_FreeValue(debuggerInfo->cx, pathData);
}
pathData = JS_NewObject(debuggerInfo->cx);
JS_SetPropertyStr(debuggerInfo->cx, debuggerInfo->breakpoints, path, pathData);
JS_FreeCString(debuggerInfo->cx, path);
JS_FreeValue(debuggerInfo->cx, filename);
JSValue jsBreakpoints = JS_GetPropertyStr(debuggerInfo->cx, breakpoints, "breakpoints");
JS_SetPropertyStr(debuggerInfo->cx, pathData, "breakpoints", jsBreakpoints);
JS_FreeValue(debuggerInfo->cx, jsBreakpoints);
return;
}
static void DBG_CotinueProcess(DebuggerInfo *debuggerInfo, JSValue msg, const uint8_t *pc)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_CotinueProcess fail debuggerInfo=NULL");
return;
}
JSContext *cx = debuggerInfo->cx;
if (cx == NULL) {
return;
}
debuggerInfo->depth = JS_GetStackDepth(cx);
LocInfo loc = JS_GetCurrentLocation(cx, pc);
debuggerInfo->stepOperation = STEP_CONTINUE;
debuggerInfo->loc = loc;
DBG_SendResponseMsg(debuggerInfo, msg, JS_UNDEFINED);
return;
}
static void DBG_StackTraceProcess(DebuggerInfo *debuggerInfo, JSValue msg, const uint8_t *curPc)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_StackTraceProcess fail debuggerInfo=NULL");
return;
}
JSValue stackTrace = JS_BuildStackTrace(debuggerInfo->cx, curPc);
DBG_SendResponseMsg(debuggerInfo, msg, stackTrace);
return;
}
static void DBG_SetScopes(JSContext *cx, JSValue scopes, int scopeCount, int scopeType, int frameId)
{
if (cx == NULL) {
DEBUGGER_LOGE("DBG_StackTraceProcess fail cx=NULL");
return;
}
JSValue scopeObj = JS_NewObject(cx);
JSValue expensiveFlag = JS_FALSE;
switch (scopeType) {
case GLOBAL:
expensiveFlag = JS_TRUE;
JS_SetPropertyStr(cx, scopeObj, "name", JS_NewString(cx, "Global"));
break;
case LOCAL:
JS_SetPropertyStr(cx, scopeObj, "name", JS_NewString(cx, "Locals"));
break;
case CLOSURE:
JS_SetPropertyStr(cx, scopeObj, "name", JS_NewString(cx, "Closure"));
break;
default:
return;
}
JS_SetPropertyStr(cx, scopeObj, "reference",
JS_NewInt32(cx, (frameId << FRAME_MOVE_TWO_STEP) + scopeType));
JS_SetPropertyStr(cx, scopeObj, "expensive", expensiveFlag);
JS_SetPropertyUint32(cx, scopes, scopeCount, scopeObj);
return;
}
uint32_t DBG_GetValueAsUint32Type(JSContext *cx, JSValue obj, const char *property)
{
if (cx == NULL) {
DEBUGGER_LOGE("DBG_GetValueAsUint32Type fail cx=NULL");
return -1;
}
JSValue prop = JS_GetPropertyStr(cx, obj, property);
uint32_t ret;
JS_ToUint32(cx, &ret, prop);
JS_FreeValue(cx, prop);
return ret;
}
static void DBG_ScopesProcess(DebuggerInfo *debuggerInfo, JSValue msg)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_ScopesProcess fail debuggerInfo=NULL");
return;
}
JSContext *cx = debuggerInfo->cx;
if (cx == NULL) {
DEBUGGER_LOGE("DBG_ScopesProcess fail cx=NULL");
return;
}
JSValue argsValue = JS_GetPropertyStr(cx, msg, "args");
int frameId = DBG_GetValueAsUint32Type(cx, argsValue, "frameId");
if (frameId == -1) {
DEBUGGER_LOGE("DBG_ScopesProcess fail frameId=%d", frameId);
return;
}
JS_FreeValue(cx, argsValue);
JSValue scopes = JS_NewArray(cx);
int scopeCount = 0;
DBG_SetScopes(cx, scopes, scopeCount++, LOCAL, frameId);
DBG_SetScopes(cx, scopes, scopeCount++, CLOSURE, frameId);
DBG_SetScopes(cx, scopes, scopeCount++, GLOBAL, frameId);
DBG_SendResponseMsg(debuggerInfo, msg, scopes);
JS_FreeValue(cx, scopes);
return;
}
static void DBG_SetStepOverToDebugger(DebuggerInfo *debuggerInfo, JSValue msg, const uint8_t *pc)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_SetStepOverToDebugger fail debuggerInfo=NULL");
return;
}
JSContext *cx = debuggerInfo->cx;
if (cx == NULL) {
DEBUGGER_LOGE("DBG_SetStepOverToDebugger fail cx=NULL");
return;
}
debuggerInfo->depth = JS_GetStackDepth(cx);
LocInfo loc = JS_GetCurrentLocation(cx, pc);
debuggerInfo->stepOperation = STEP_NEXT;
debuggerInfo->loc = loc;
DBG_SendResponseMsg(debuggerInfo, msg, JS_UNDEFINED);
return;
}
static void DBG_SetStepOutToDebugger(DebuggerInfo *debuggerInfo, JSValue msg, const uint8_t *pc)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_SetStepOutToDebugger fail debuggerInfo=NULL");
return;
}
JSContext *cx = debuggerInfo->cx;
if (cx == NULL) {
DEBUGGER_LOGE("DBG_SetStepOutToDebugger fail cx=NULL");
return;
}
debuggerInfo->depth = JS_GetStackDepth(cx);
LocInfo loc = JS_GetCurrentLocation(cx, pc);
debuggerInfo->stepOperation = STEP_OUT;
debuggerInfo->loc = loc;
DBG_SendResponseMsg(debuggerInfo, msg, JS_UNDEFINED);
return;
}
static void DBG_SetStepInToDebugger(DebuggerInfo *debuggerInfo, JSValue msg, const uint8_t *pc)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_SetStepInToDebugger fail debuggerInfo=NULL");
return;
}
JSContext *cx = debuggerInfo->cx;
if (cx == NULL) {
DEBUGGER_LOGE("DBG_SetStepInToDebugger fail cx=NULL");
return;
}
debuggerInfo->depth = JS_GetStackDepth(cx);
LocInfo loc = JS_GetCurrentLocation(cx, pc);
debuggerInfo->stepOperation = STEP_IN;
debuggerInfo->loc = loc;
DBG_SendResponseMsg(debuggerInfo, msg, JS_UNDEFINED);
return;
}
static void DBG_FreePropEnum(JSContext *cx, JSPropertyEnum *tab, uint32_t len)
{
if (cx == NULL || tab == NULL) {
DEBUGGER_LOGE("DBG_FreePropEnum fail cx=NULL or tab=NULL");
return;
}
for (uint32_t i = 0; i < len; i++) {
JS_FreeAtom(cx, tab[i].atom);
}
js_free(cx, tab);
return;
}
static uint32_t DBG_GetObjectVariableReference(JSContext *cx,
struct DebuggerVariableState *state,
JSValue var,
JSValue varVal)
{
if (cx == NULL || state == NULL) {
DEBUGGER_LOGE("DBG_GetObjectVariableReference fail cx=NULL || state=NULL");
return 0;
}
uint32_t reference = 0;
JSObject *pObj = JS_VALUE_GET_OBJ(varVal);
if (pObj == NULL) {
return reference;
}
uint32_t pVal = (uint32_t)pObj;
JSValue found = JS_GetPropertyUint32(cx, state->variablePointers, pVal);
if (JS_IsUndefined(found)) {
reference = state->variableReferenceCount++;
JS_SetPropertyUint32(cx, state->variableReferences, reference, JS_DupValue(cx, varVal));
JS_SetPropertyUint32(cx, state->variablePointers, pVal, JS_NewInt32(cx, reference));
} else {
JS_ToUint32(cx, &reference, found);
}
JS_FreeValue(cx, found);
return reference;
}
static void DBG_SetVariableType(JSContext *cx,
struct DebuggerVariableState *state,
JSValue var,
JSValue varVal)
{
if (cx == NULL || state == NULL) {
DEBUGGER_LOGE("DBG_SetVariableType fail cx=NULL || state=NULL");
return;
}
uint32_t tag = JS_VALUE_GET_TAG(varVal);
uint32_t reference = 0;
switch (tag) {
case JS_TAG_INT: // Same processing as JS_TAG_BIG_INT
case JS_TAG_BIG_INT:
JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "integer"));
break;
case JS_TAG_FLOAT64: // Same processing as JS_TAG_BIG_FLOAT
case JS_TAG_BIG_FLOAT:
JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "float"));
break;
case JS_TAG_BOOL:
JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "bool"));
break;
case JS_TAG_STRING:
JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "string"));
break;
case JS_TAG_NULL:
JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "null"));
break;
case JS_TAG_EXCEPTION:
JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "exception"));
break;
case JS_TAG_UNDEFINED:
JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "undefined"));
break;
case JS_TAG_OBJECT:
JS_SetPropertyStr(cx, var, "type", JS_NewString(cx, "object"));
reference = DBG_GetObjectVariableReference(cx, state, var, varVal);
break;
default:
break;
}
JS_SetPropertyStr(cx, var, "variablesReference", JS_NewInt32(cx, reference));
return;
}
static void DBG_SetVariableProperty(JSContext *cx,
JSValue varVal,
JSValue var,
const char *valueProperty)
{
if (cx == NULL || valueProperty == NULL) {
DEBUGGER_LOGE("DBG_SetVariableProperty fail cx=NULL || valueProperty=%s", valueProperty);
return;
}
if (JS_IsArray(cx, varVal)) {
uint32_t len = DBG_GetValueAsUint32Type(cx, varVal, "length");
char lenBuf[STR_BUF_SIZE] = {0};
if (sprintf_s(lenBuf, sizeof(lenBuf), "Array (%d)", len) < 0) {
return;
}
JS_SetPropertyStr(cx, var, valueProperty, JS_NewString(cx, lenBuf));
JS_SetPropertyStr(cx, var, "indexedVariables", JS_NewInt32(cx, len));
} else {
JS_SetPropertyStr(cx, var, valueProperty, JS_ToString(cx, varVal));
}
return;
}
static JSValue DBG_GetVariableObj(JSContext *cx,
struct DebuggerVariableState *state,
JSValue varName,
JSValue varVal)
{
if (cx == NULL || state == NULL) {
DEBUGGER_LOGE("DBG_GetVariableObj fail cx=NULL || state=NULL");
return JS_NULL;
}
JSValue var = JS_NewObject(cx);
JS_SetPropertyStr(cx, var, "name", varName);
DBG_SetVariableProperty(cx, varVal, var, "value");
DBG_SetVariableType(cx, state, var, varVal);
return var;
}
static JSValue DBG_ProcessUndefinedVariable(JSContext *cx, struct DebuggerVariableState *state,
uint32_t reference, JSValue variable)
{
if (cx == NULL || state == NULL) {
DEBUGGER_LOGE("DBG_ProcessUndefinedVariable fail cx=NULL || state=NULL");
return JS_NULL;
}
int frame = reference >> FRAME_MOVE_TWO_STEP;
int scope = reference % TYPE_OF_SCOPE_NUM;
if (frame >= JS_GetStackDepth(cx)) {
return variable;
}
switch (scope) {
case GLOBAL:
variable = JS_GetGlobalObject(cx);
break;
case LOCAL:
variable = JS_GetLocalVariables(cx, frame);
break;
case CLOSURE:
variable = JS_GetClosureVariables(cx, frame);
break;
default:
break;
}
JS_SetPropertyUint32(cx, state->variableReferences, reference, JS_DupValue(cx, variable));
return variable;
}
static JSValue DBG_VariablesUnFilteredProcess(JSContext *cx,
JSValue variable,
JSValue properties,
struct DebuggerVariableState *state)
{
if (cx == NULL || state == NULL) {
DEBUGGER_LOGE("DBG_VariablesUnFilteredProcess fail cx=NULL || state=NULL");
return JS_NULL;
}
JSPropertyEnum *tabAtom = NULL;
uint32_t tabAtomCount = 0;
if (JS_GetOwnPropertyNames(cx, &tabAtom, &tabAtomCount, variable,
JS_GPN_STRING_MASK | JS_GPN_SYMBOL_MASK)) {
return properties;
}
for (uint32_t idx = 0; idx < tabAtomCount; idx++) {
JSValue value = JS_GetProperty(cx, variable, tabAtom[idx].atom);
JSValue variableJson = DBG_GetVariableObj(cx, state,
JS_AtomToString(cx, tabAtom[idx].atom), value);
JS_FreeValue(cx, value);
JS_SetPropertyUint32(cx, properties, idx, variableJson);
}
DBG_FreePropEnum(cx, tabAtom, tabAtomCount);
return properties;
}
static JSValue DBG_VariablesFilteredProcess(JSContext *cx,
JSValue args,
JSValue variable,
JSValue properties,
struct DebuggerVariableState *state)
{
if (cx == NULL || state == NULL) {
DEBUGGER_LOGE("DBG_VariablesFilteredProcess fail cx=NULL || state=NULL");
return JS_NULL;
}
const char *filterStr = JS_ToCString(cx, JS_GetPropertyStr(cx, args, "filter"));
if (filterStr == NULL) {
DEBUGGER_LOGE("DBG_VariablesFilteredProcess fail filterStr=%s", filterStr);
return JS_NULL;
}
int indexed = strcmp(filterStr, "indexed");
JS_FreeCString(cx, filterStr);
if (indexed != 0) {
return DBG_VariablesUnFilteredProcess(cx, variable, properties, state);
}
uint32_t start = DBG_GetValueAsUint32Type(cx, args, "start");
if (start < 0) {
return JS_NULL;
}
uint32_t count = DBG_GetValueAsUint32Type(cx, args, "count");
if (count < 0) {
return JS_NULL;
}
char nameBuf[STR_BUF_SIZE] = {0};
for (uint32_t i = 0; i < count; i++) {
JSValue value = JS_GetPropertyUint32(cx, variable, start + i);
if (sprintf_s(nameBuf, sizeof(nameBuf), "%d", i) < 0) {
continue;
}
JSValue variableJson = DBG_GetVariableObj(cx, state, JS_NewString(cx, nameBuf), value);
JS_FreeValue(cx, value);
JS_SetPropertyUint32(cx, properties, i, variableJson);
}
return properties;
}
static void DBG_VariablesProcess(DebuggerInfo *debuggerInfo,
JSValue request,
struct DebuggerVariableState *state)
{
if (debuggerInfo == NULL || state == NULL) {
DEBUGGER_LOGE("DBG_VariablesProcess fail debuggerInfo=NULL || state=NULL");
return;
}
JSContext *cx = debuggerInfo->cx;
if (cx == NULL) {
DEBUGGER_LOGE("DBG_VariablesProcess fail cx=NULL");
return;
}
JSValue args = JS_GetPropertyStr(cx, request, "args");
uint32_t reference = DBG_GetValueAsUint32Type(cx, args, "variablesReference");
JSValue properties = JS_NewArray(cx);
JSValue variable = JS_GetPropertyUint32(cx, state->variableReferences, reference);
if (JS_IsUndefined(variable)) {
variable = DBG_ProcessUndefinedVariable(cx, state, reference, variable);
}
JSValue filter = JS_GetPropertyStr(cx, args, "filter");
if (!JS_IsUndefined(filter)) {
properties = DBG_VariablesFilteredProcess(cx, args, variable, properties, state);
} else {
properties = DBG_VariablesUnFilteredProcess(cx, variable, properties, state);
}
JS_FreeValue(cx, variable);
JS_FreeValue(cx, args);
DBG_SendResponseMsg(debuggerInfo, request, properties);
JS_FreeValue(cx, properties);
return;
}
static void DBG_EvaluateProcess(DebuggerInfo *debuggerInfo,
JSValue msg,
struct DebuggerVariableState *state)
{
if (debuggerInfo == NULL || state == NULL) {
DEBUGGER_LOGE("DBG_EvaluateProcess fail debuggerInfo=NULL || state=NULL");
return;
}
JSContext *cx = debuggerInfo->cx;
if (cx == NULL) {
DEBUGGER_LOGE("DBG_EvaluateProcess fail cx=NULL");
return;
}
JSValue argsJsVal = JS_GetPropertyStr(cx, msg, "args");
JSValue expressionJsVal = JS_GetPropertyStr(cx, argsJsVal, "expression");
JS_FreeValue(cx, argsJsVal);
int frameId;
JSValue frameJsVal = JS_GetPropertyStr(cx, argsJsVal, "frameId");
JS_ToInt32(cx, &frameId, frameJsVal);
JS_FreeValue(cx, frameJsVal);
JSValue ret = JS_DebuggerEvaluate(cx, frameId, expressionJsVal);
if (JS_IsException(ret)) {
DEBUGGER_LOGE("DBG_EvaluateProcess fail ret=JS_Exception");
JS_FreeValue(cx, ret);
ret = JS_GetException(cx);
}
JS_FreeValue(cx, expressionJsVal);
JSValue body = JS_NewObject(cx);
DBG_SetVariableProperty(cx, ret, body, "result");
DBG_SetVariableType(cx, state, body, ret);
DBG_SendResponseMsg(debuggerInfo, msg, body);
JS_FreeValue(cx, body);
return;
}
static void DBG_PauseProcess(DebuggerInfo *debuggerInfo, JSValue msg)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_PauseProcess fail debuggerInfo=NULL");
return;
}
DBG_SendResponseMsg(debuggerInfo, msg, JS_UNDEFINED);
DBG_SendStopMsg(debuggerInfo, "pause");
return;
}
static int DBG_RequestProcess(DebuggerInfo *debuggerInfo,
JSValue msg,
struct DebuggerVariableState *state)
{
if (debuggerInfo == NULL || state == NULL) {
return DBG_NEED_READ_MSG;
}
JSContext *cx = debuggerInfo->cx;
if (cx == NULL) {
return DBG_NEED_READ_MSG;
}
const uint8_t *pc = state->curPc;
int isNeedReadMsg = 0;
JSValue jsRequestMsg = JS_GetPropertyStr(cx, msg, "request");
JSValue jsCommand = JS_GetPropertyStr(cx, jsRequestMsg, "command");
const char *command = JS_ToCString(cx, jsCommand);
if (command == NULL) {
DEBUGGER_LOGE("DBG_RequestProcess fail command=%s", command);
return DBG_NEED_READ_MSG;
}
if (strcmp(command, "continue") == 0) {
DBG_CotinueProcess(debuggerInfo, jsRequestMsg, pc);
} else if (strcmp(command, "stackTrace") == 0) {
DBG_StackTraceProcess(debuggerInfo, jsRequestMsg, pc);
isNeedReadMsg = DBG_NEED_READ_MSG;
} else if (strcmp(command, "scopes") == 0) {
DBG_ScopesProcess(debuggerInfo, jsRequestMsg);
isNeedReadMsg = DBG_NEED_READ_MSG;
} else if (strcmp(command, "stepIn") == 0) {
DBG_SetStepInToDebugger(debuggerInfo, jsRequestMsg, pc);
} else if (strcmp(command, "stepOut") == 0) {
DBG_SetStepOutToDebugger(debuggerInfo, jsRequestMsg, pc);
} else if (strcmp(command, "next") == 0) {
DBG_SetStepOverToDebugger(debuggerInfo, jsRequestMsg, pc);
} else if (strcmp(command, "variables") == 0) {
DBG_VariablesProcess(debuggerInfo, jsRequestMsg, state);
isNeedReadMsg = DBG_NEED_READ_MSG;
} else if (strcmp(command, "evaluate") == 0) {
DBG_EvaluateProcess(debuggerInfo, jsRequestMsg, state);
isNeedReadMsg = DBG_NEED_READ_MSG;
} else if (strcmp(command, "pause") == 0) {
DBG_PauseProcess(debuggerInfo, jsRequestMsg);
isNeedReadMsg = DBG_NEED_READ_MSG;
}
JS_FreeCString(cx, command);
JS_FreeValue(cx, jsRequestMsg);
return isNeedReadMsg;
}
static int DBG_ProcessMsgByType(DebuggerInfo *debuggerInfo, const char *type,
JSValue msg, struct DebuggerVariableState *state)
{
if (debuggerInfo == NULL || type == NULL) {
DEBUGGER_LOGE("DBG_ProcessMsgByType fail debuggerInfo=NULL || type=%s", type);
return DBG_NEED_READ_MSG;
}
if (strcmp(type, "request") == 0) {
return DBG_RequestProcess(debuggerInfo, msg, state);
}
if (strcmp(type, "breakpoints") == 0) {
JSValue jsBreakpointsVal = JS_GetPropertyStr(debuggerInfo->cx, msg, "breakpoints");
DBG_SetBreakpoints(debuggerInfo, jsBreakpointsVal);
JS_FreeValue(debuggerInfo->cx, jsBreakpointsVal);
return DBG_NEED_READ_MSG;
}
if (strcmp(type, "continue") == 0) {
return DBG_NO_NEED_READ_MSG;
}
return DBG_NEED_READ_MSG;
}
static DebuggerVariableState DBG_GetVariableState(DebuggerInfo *debuggerInfo, const uint8_t *pc)
{
struct DebuggerVariableState state = {0};
state.variableReferenceCount = JS_GetStackDepth(debuggerInfo->cx) << STACK_DEPTH_MOVE_TWO_STEP;
state.variablePointers = JS_NewObject(debuggerInfo->cx);
state.variableReferences = JS_NewObject(debuggerInfo->cx);
state.curPc = pc;
return state;
}
static void DBG_ProcessMsg(DebuggerInfo *debuggerInfo, const uint8_t *pc, int runningBreakpoint)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_ProcessMsg fail debuggerInfo=NULL");
return;
}
int isNeedReadMsg = DBG_NEED_READ_MSG;
struct DebuggerVariableState state = DBG_GetVariableState(debuggerInfo, pc);
while (isNeedReadMsg) {
fflush(stdout);
fflush(stderr);
JSValue msg = DBG_ReadMsg(debuggerInfo);
if (JS_IsUndefined(msg)) {
DEBUGGER_LOGE("DBG_ProcessMsg fail msg=JS_Undefined");
return;
}
JSValue jsType = JS_GetPropertyStr(debuggerInfo->cx, msg, "type");
const char *type = JS_ToCString(debuggerInfo->cx, jsType);
JS_FreeValue(debuggerInfo->cx, jsType);
if (type == NULL) {
DEBUGGER_LOGE("DBG_ProcessMsg fail type is NULL");
return;
}
DEBUGGER_LOGI("DBG_ProcessMsg type=%s", type);
isNeedReadMsg = DBG_ProcessMsgByType(debuggerInfo, type, msg, &state);
if (runningBreakpoint) {
isNeedReadMsg = DBG_NO_NEED_READ_MSG;
}
JS_FreeCString(debuggerInfo->cx, type);
}
}
static void DBG_Entry(JSContext *cx, int client)
{
if (cx == NULL) {
DEBUGGER_LOGE("DBG_Entry fail cx=NULL");
return;
}
struct DebuggerInfo *debuggerInfo = JS_GetDebuggerInfo(cx);
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_Entry fail debuggerInfo=NULL");
return;
}
debuggerInfo->cx = cx;
// debuggerInfo->client = client;
debuggerInfo->breakpoints = JS_NewObject(cx);
// debuggerInfo->isConnected = 1;
DBG_SendStopMsg(debuggerInfo, "entry");
}
static int DBG_IsLocEqual(DebuggerInfo *debuggerInfo, uint32_t depth, LocInfo loc)
{
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_IsLocEqual fail debuggerInfo=NULL");
return DBG_LOC_ISEQUAL;
}
if (loc.line == 0 || loc.line == -1) {
return DBG_LOC_ISEQUAL;
}
DEBUGGER_LOGI("DBG_IsLocEqual last depth %d, this depth %d, last line %d, this line %d", debuggerInfo->depth,
depth, debuggerInfo->loc.line, loc.line);
if ((debuggerInfo->depth == depth) && (debuggerInfo->loc.line == loc.line) &&
(debuggerInfo->loc.filename == loc.filename)) {
return DBG_LOC_ISEQUAL;
}
return DBG_LOC_ISNOTEQUAL;
}
static int DBG_ProcessStepOperation(JSContext *cx, DebuggerInfo *debuggerInfo, const uint8_t *pc)
{
if (cx == NULL || debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_ProcessStepOperation fail cx=NULL || debuggerInfo==NULL");
return 1;
}
int isNeedReadMsg = 0;
uint32_t depth = JS_GetStackDepth(cx);
LocInfo loc = JS_GetCurrentLocation(cx, pc);
if (debuggerInfo->stepOperation == STEP_CONTINUE) {
if (depth == debuggerInfo->depth) {
// Depth is equal indicates that current opcode's line is not the same as last one.
// Clear stepOperation so that it's possible to break on the same line inside loops.
debuggerInfo->stepOperation = NO_STEP_OPERATION;
}
isNeedReadMsg = 0;
return isNeedReadMsg;
}
if (debuggerInfo->stepOperation == STEP_NEXT) {
if (depth > debuggerInfo->depth || (loc.line == debuggerInfo->loc.line &&
loc.filename == debuggerInfo->loc.filename)) {
isNeedReadMsg = 0;
return isNeedReadMsg;
}
DBG_SendStopMsg(debuggerInfo, "step");
debuggerInfo->stepOperation = NO_STEP_OPERATION;
isNeedReadMsg = 1;
return isNeedReadMsg;
}
if (debuggerInfo->stepOperation == STEP_IN) {
if ((loc.line == 0) || (loc.line == debuggerInfo->loc.line &&
loc.filename == debuggerInfo->loc.filename)) {
isNeedReadMsg = 0;
return isNeedReadMsg;
}
DBG_SendStopMsg(debuggerInfo, "stepIn");
debuggerInfo->stepOperation = NO_STEP_OPERATION;
isNeedReadMsg = 1;
return isNeedReadMsg;
}
if (debuggerInfo->stepOperation == STEP_OUT) {
if (depth >= debuggerInfo->depth) {
isNeedReadMsg = 0;
return isNeedReadMsg;
}
DBG_SendStopMsg(debuggerInfo, "stepOut");
debuggerInfo->stepOperation = NO_STEP_OPERATION;
isNeedReadMsg = 1;
return isNeedReadMsg;
}
debuggerInfo->stepOperation = NO_STEP_OPERATION;
return isNeedReadMsg;
}
void DBG_FreeSources(JSContext *cx, DebuggerInfo *debuggerInfo)
{
if (cx == NULL) {
DEBUGGER_LOGE("DBG_FreeSources fail cx=NULL");
return;
}
if (debuggerInfo->client <= 0) {
return;
}
close(debuggerInfo->client);
JS_FreeValue(cx, debuggerInfo->breakpoints);
}
void DBG_CallDebugger(JSContext *cx, const uint8_t *pc)
{
if (cx == NULL) {
DEBUGGER_LOGE("DBG_CallDebugger fail cx=NULL");
return;
}
struct DebuggerInfo *debuggerInfo = JS_GetDebuggerInfo(cx);
if (debuggerInfo == NULL) {
DEBUGGER_LOGE("DBG_CallDebugger fail debuggerInfo=NULL");
return;
}
if (!g_isServerStarted) {
pthread_t tid;
if (pthread_create(&tid, NULL, &DBG_StartAgent, (void*)debuggerInfo) != 0) {
DEBUGGER_LOGE("pthread_create fail!");
return;
}
g_isServerStarted = true;
if (!g_isAttachMode) {
while (debuggerInfo->isConnected != 1) {
usleep(DBG_WAITING_TIME);
}
DBG_Entry(cx, debuggerInfo->client);
// read msg when first time must be breakpoints msg, and set breakpoints to debugger
DBG_ProcessMsg(debuggerInfo, pc, 0);
g_isBreakPointSet = true;
}
}
if (g_isAttachMode && (debuggerInfo->isConnected == 1 && !g_isBreakPointSet)) {
// ide attached, accept breakpoints msg
DBG_Entry(cx, debuggerInfo->client);
DBG_ProcessMsg(debuggerInfo, pc, 0);
g_isBreakPointSet = true;
}
if (QueueIsEmpty(debuggerInfo->client) == 0) {
DBG_ProcessMsg(debuggerInfo, pc, 1);
}
// when last time the debugger have step operaton, check the location is changed
if (debuggerInfo->stepOperation != NO_STEP_OPERATION) {
// check loction
uint32_t depth = JS_GetStackDepth(cx);
LocInfo loc = JS_GetCurrentLocation(cx, pc);
if (DBG_IsLocEqual(debuggerInfo, depth, loc) == DBG_LOC_ISEQUAL) {
return;
}
}
// must check breakpotint first, then process step operation
if (g_isBreakPointSet && JS_HitBreakpoint(cx, pc) && JS_JudgeConditionBreakPoint(cx, pc)) {
LocInfo loc = JS_GetCurrentLocation(cx, pc);
DEBUGGER_LOGI("DBG_CallDebugger hit breakpoint at line %d", loc.line);
debuggerInfo->stepOperation = NO_STEP_OPERATION;
debuggerInfo->depth = JS_GetStackDepth(cx);
DBG_SendStopMsg(debuggerInfo, "breakpoint");
DBG_ProcessMsg(debuggerInfo, pc, 0);
return;
}
// process step operation
if (debuggerInfo->stepOperation != NO_STEP_OPERATION) {
// process step operation
int isNeedReadMsg = DBG_ProcessStepOperation(cx, debuggerInfo, pc);
if (!isNeedReadMsg) {
return;
}
// continue process msg
DBG_ProcessMsg(debuggerInfo, pc, 0);
}
return;
}

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/openharmony-third_party_quickjs.git
git@api.gitlife.ru:oschina-mirror/openharmony-third_party_quickjs.git
oschina-mirror
openharmony-third_party_quickjs
openharmony-third_party_quickjs
master