From 7530e1903572379cfeffe0c3be1cc8df4a4bbd1b Mon Sep 17 00:00:00 2001 From: dengwenjun Date: Wed, 7 May 2025 14:51:10 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90ArkTs=E3=80=91=E3=80=90=E8=B0=83?= =?UTF-8?q?=E8=AF=95=E3=80=91=E8=B0=83=E8=AF=95=E6=94=AF=E6=8C=81=E7=AC=A6?= =?UTF-8?q?=E5=8F=B7=E6=96=AD=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.Debugger新增setSymbolicBreakpoints和removeSymbolicBreakpoints协议; 2.通过缓存开发者设置的函数名断点,在MethodEntry阶段对对函数名进行校验,若设置符号断点,则在函数实现第一行停住; 3.若因符号断点停住,向IDE发送paused消息,消息中reason字段内容为SYMBOL; Issue:https://gitee.com/openharmony/arkcompiler_toolchain/issues/IC28KS?from=project-issue Signed-off-by: dengwenjun --- tooling/agent/debugger_impl.cpp | 72 ++++++- tooling/agent/debugger_impl.h | 8 + tooling/backend/js_pt_hooks.cpp | 15 ++ tooling/backend/js_pt_hooks.h | 2 + tooling/base/pt_events.h | 3 + tooling/base/pt_params.cpp | 75 ++++++++ tooling/base/pt_params.h | 55 ++++++ tooling/client/domain/debugger_client.cpp | 61 ++++++ tooling/client/domain/debugger_client.h | 9 + tooling/client/utils/cli_command.cpp | 54 ++++++ tooling/client/utils/cli_command.h | 2 + tooling/dispatcher.h | 3 +- tooling/test/client_utils/test_list.cpp | 2 + tooling/test/debugger_impl_test.cpp | 60 ++++++ tooling/test/debugger_params_test.cpp | 25 +++ .../test/testcases/js_symbolbreakpoint_test.h | 179 ++++++++++++++++++ tooling/test/utils/test_hooks.h | 2 + 17 files changed, 625 insertions(+), 2 deletions(-) create mode 100644 tooling/test/testcases/js_symbolbreakpoint_test.h diff --git a/tooling/agent/debugger_impl.cpp b/tooling/agent/debugger_impl.cpp index 54716095..a8a80762 100755 --- a/tooling/agent/debugger_impl.cpp +++ b/tooling/agent/debugger_impl.cpp @@ -469,7 +469,9 @@ void DebuggerImpl::InitializeExtendedProtocolsList() "resetSingleStepper", "callFunctionOn", "smartStepInto", - "saveAllPossibleBreakpoints" + "saveAllPossibleBreakpoints", + "setSymbolicBreakpoints", + "removeSymbolicBreakpoints" }; debuggerExtendedProtocols_ = std::move(debuggerProtocolList); } @@ -585,6 +587,12 @@ void DebuggerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) case Method::SAVE_ALL_POSSIBLE_BREAKPOINTS: SaveAllPossibleBreakpoints(request); break; + case Method::SET_SYMBOLIC_BREAKPOINTS: + SetSymbolicBreakpoints(request); + break; + case Method::REMOVE_SYMBOLIC_BREAKPOINTS: + RemoveSymbolicBreakpoints(request); + break; default: SendResponse(request, DispatchResponse::Fail("Unknown method: " + request.GetMethod())); break; @@ -651,6 +659,10 @@ DebuggerImpl::DispatcherImpl::Method DebuggerImpl::DispatcherImpl::GetMethodEnum return Method::CALL_FUNCTION_ON; } else if (method == "saveAllPossibleBreakpoints") { return Method::SAVE_ALL_POSSIBLE_BREAKPOINTS; + } else if (method == "setSymbolicBreakpoints") { + return Method::SET_SYMBOLIC_BREAKPOINTS; + } else if (method == "removeSymbolicBreakpoints") { + return Method::REMOVE_SYMBOLIC_BREAKPOINTS; } else { return Method::UNKNOWN; } @@ -833,6 +845,30 @@ void DebuggerImpl::DispatcherImpl::SaveAllPossibleBreakpoints(const DispatchRequ SendResponse(request, response); } +void DebuggerImpl::DispatcherImpl::SetSymbolicBreakpoints(const DispatchRequest &request) +{ + std::unique_ptr params = + SetSymbolicBreakpointsParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = debugger_->SetSymbolicBreakpoints(*params); + SendResponse(request, response); +} + +void DebuggerImpl::DispatcherImpl::RemoveSymbolicBreakpoints(const DispatchRequest &request) +{ + std::unique_ptr params = + RemoveSymbolicBreakpointsParams::Create(request.GetParams()); + if (params == nullptr) { + SendResponse(request, DispatchResponse::Fail("wrong params")); + return; + } + DispatchResponse response = debugger_->RemoveSymbolicBreakpoints(*params); + SendResponse(request, response); +} + void DebuggerImpl::DispatcherImpl::SetPauseOnExceptions(const DispatchRequest &request) { std::unique_ptr params = SetPauseOnExceptionsParams::Create(request.GetParams()); @@ -1454,6 +1490,35 @@ DispatchResponse DebuggerImpl::SaveAllPossibleBreakpoints(const SaveAllPossibleB return DispatchResponse::Ok(); } +DispatchResponse DebuggerImpl::SetSymbolicBreakpoints(const SetSymbolicBreakpointsParams ¶ms) +{ + if (!vm_->GetJsDebuggerManager()->IsDebugMode()) { + return DispatchResponse::Fail("SetSymbolicBreakpoints: debugger agent is not enabled"); + } + if (!params.HasSymbolicBreakpoints()) { + return DispatchResponse::Fail("SetSymbolicBreakpoints: no symbolicBreakpoints exists"); + } + // Symbolic breakpoints support only function names + DebuggerApi::SetSymbolicBreakpoint(jsDebugger_, *(params.GetFunctionNamesSet())); + + return DispatchResponse::Ok(); +} + +DispatchResponse DebuggerImpl::RemoveSymbolicBreakpoints(const RemoveSymbolicBreakpointsParams ¶ms) +{ + if (!vm_->GetJsDebuggerManager()->IsDebugMode()) { + return DispatchResponse::Fail("RemoveSymbolicBreakpoints: debugger agent is not enabled"); + } + if (!params.HasSymbolicBreakpoints()) { + return DispatchResponse::Fail("RemoveSymbolicBreakpoints: no symbolBreakpoints removed"); + } + // Symbolic breakpoints support only function names + for (const auto& symbolicBreakpoint : *(params.GetFunctionNamesSet())) { + DebuggerApi::RemoveSymbolicBreakpoint(jsDebugger_, symbolicBreakpoint); + } + return DispatchResponse::Ok(); +} + void DebuggerImpl::SavePendingBreakpoints(const SaveAllPossibleBreakpointsParams ¶ms) { for (const auto &entry : *(params.GetBreakpointsMap())) { @@ -1890,6 +1955,11 @@ void DebuggerImpl::GenerateScopeChains(bool getScope, } } +void DebuggerImpl::SetPauseOnNextByteCode(bool pauseOnNextByteCode) +{ + pauseOnNextByteCode_ = pauseOnNextByteCode; +} + std::unique_ptr DebuggerImpl::GetLocalScopeChain(const FrameHandler *frameHandler, std::unique_ptr *thisObj) { diff --git a/tooling/agent/debugger_impl.h b/tooling/agent/debugger_impl.h index c3a0ddad..9ee191c5 100644 --- a/tooling/agent/debugger_impl.h +++ b/tooling/agent/debugger_impl.h @@ -60,6 +60,7 @@ public: void SetNativeOutPause(bool nativeOutPause); void AddBreakpointDetail(const std::string &url, int32_t lineNumber, std::string *outId, std::vector> *outLocations); + void SetPauseOnNextByteCode(bool pauseOnNextByteCode); DispatchResponse ContinueToLocation(const ContinueToLocationParams ¶ms); DispatchResponse Enable(const EnableParams ¶ms, UniqueDebuggerId *id); @@ -98,6 +99,9 @@ public: std::unique_ptr *outRemoteObject, std::optional> *outExceptionDetails); DispatchResponse SaveAllPossibleBreakpoints(const SaveAllPossibleBreakpointsParams ¶ms); + DispatchResponse SetSymbolicBreakpoints(const SetSymbolicBreakpointsParams ¶ms); + DispatchResponse RemoveSymbolicBreakpoints(const RemoveSymbolicBreakpointsParams ¶ms); + /** * @brief: match first script and callback * @@ -181,6 +185,8 @@ public: void ClientDisconnect(const DispatchRequest &request); void CallFunctionOn(const DispatchRequest &request); void SaveAllPossibleBreakpoints(const DispatchRequest &request); + void SetSymbolicBreakpoints(const DispatchRequest &request); + void RemoveSymbolicBreakpoints(const DispatchRequest &request); enum class Method { CONTINUE_TO_LOCATION, @@ -212,6 +218,8 @@ public: CLIENT_DISCONNECT, CALL_FUNCTION_ON, SAVE_ALL_POSSIBLE_BREAKPOINTS, + SET_SYMBOLIC_BREAKPOINTS, + REMOVE_SYMBOLIC_BREAKPOINTS, UNKNOWN }; Method GetMethodEnum(const std::string& method); diff --git a/tooling/backend/js_pt_hooks.cpp b/tooling/backend/js_pt_hooks.cpp index 4c484ed4..c7386b14 100644 --- a/tooling/backend/js_pt_hooks.cpp +++ b/tooling/backend/js_pt_hooks.cpp @@ -56,6 +56,12 @@ bool JSPtHooks::SingleStep(const JSPtLocation &location) // pause or step complete if (debugger_->NotifySingleStep(location)) { + // pause for symbol breakpoint + if (breakOnSymbol_) { + debugger_->NotifyPaused({}, SYMBOL); + breakOnSymbol_ = false; + return true; + } debugger_->NotifyPaused({}, OTHER); return true; } @@ -123,4 +129,13 @@ void JSPtHooks::DisableFirstTimeFlag() { firstTime_ = false; } + +void JSPtHooks::HitSymbolicBreakpoint() +{ + LOG_DEBUGGER(VERBOSE) << "JSPtHooks: HitSymbolicBreakpoint"; + + breakOnSymbol_ = true; + + debugger_->SetPauseOnNextByteCode(true); +} } // namespace panda::ecmascript::tooling diff --git a/tooling/backend/js_pt_hooks.h b/tooling/backend/js_pt_hooks.h index 58c76e4f..26ad9648 100644 --- a/tooling/backend/js_pt_hooks.h +++ b/tooling/backend/js_pt_hooks.h @@ -42,6 +42,7 @@ public: void VmDeath() override {} void SendableMethodEntry(JSHandle method) override; void DisableFirstTimeFlag() override; + void HitSymbolicBreakpoint() override; private: NO_COPY_SEMANTIC(JSPtHooks); @@ -49,6 +50,7 @@ private: DebuggerImpl *debugger_ {nullptr}; bool firstTime_ {true}; + bool breakOnSymbol_ {false}; }; } // namespace panda::ecmascript::tooling #endif // ECMASCRIPT_TOOLING_BACKEND_JS_PT_HOOKS_H \ No newline at end of file diff --git a/tooling/base/pt_events.h b/tooling/base/pt_events.h index 9d1bea7e..67cc8a22 100644 --- a/tooling/base/pt_events.h +++ b/tooling/base/pt_events.h @@ -160,6 +160,9 @@ public: case NATIVE_OUT: { return "Native out"; } + case SYMBOL: { + return "Symbol"; + } default: { LOG_DEBUGGER(ERROR) << "Unknown paused reason: " << reason; } diff --git a/tooling/base/pt_params.cpp b/tooling/base/pt_params.cpp index 3f5934db..78c2acfd 100644 --- a/tooling/base/pt_params.cpp +++ b/tooling/base/pt_params.cpp @@ -1362,4 +1362,79 @@ std::unique_ptr SaveAllPossibleBreakpointsPara return paramsObject; } + +std::unique_ptr SetSymbolicBreakpointsParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::unordered_set functionNamesSet {}; + std::string error; + Result ret; + + std::unique_ptr symbolicBreakpoints; + ret = params.GetArray("symbolicBreakpoints", &symbolicBreakpoints); + if (ret != Result::SUCCESS) { + LOG_DEBUGGER(ERROR) << "SetSymbolicBreakpointsParams::Get SymbolicBreakpoints: " << error; + return nullptr; + } + + int32_t length = symbolicBreakpoints->GetSize(); + for (int32_t i = 0; i < length; i++) { + auto json = symbolicBreakpoints->Get(i); + std::string functionName; + ret = json->GetString("functionName", &functionName); + if (ret == Result::SUCCESS) { + functionNamesSet.insert(std::move(functionName)); + } else if (ret == Result::TYPE_ERROR) { + error += "Wrong type of 'functionName';"; + } else if (ret == Result::NOT_EXIST) { + error += "'functionName' no exists;"; + } + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "SetSymbolicBreakpointsParams::Create " << error; + return nullptr; + } + paramsObject->functionNamesSet_ = functionNamesSet; + + return paramsObject; +} + +std::unique_ptr RemoveSymbolicBreakpointsParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::unordered_set functionNamesSet {}; + std::string error; + Result ret; + + std::unique_ptr symbolicBreakpoints; + ret = params.GetArray("symbolicBreakpoints", &symbolicBreakpoints); + if (ret != Result::SUCCESS) { + LOG_DEBUGGER(ERROR) << "RemoveSymbolicBreakpointsParams::Get SymbolicBreakpoints: " << error; + return nullptr; + } + + int32_t length = symbolicBreakpoints->GetSize(); + for (int32_t i = 0; i < length; i++) { + auto json = symbolicBreakpoints->Get(i); + std::string functionName; + ret = json->GetString("functionName", &functionName); + if (ret == Result::SUCCESS) { + functionNamesSet.insert(std::move(functionName)); + } else if (ret == Result::TYPE_ERROR) { + error += "Wrong type of 'functionName';"; + } else if (ret == Result::NOT_EXIST) { + error += "'functionName' no exists;"; + } + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "RemoveSymbolicBreakpointsParams::Create " << error; + return nullptr; + } + paramsObject->functionNamesSet_ = functionNamesSet; + + return paramsObject; +} + } // namespace panda::ecmascript::tooling diff --git a/tooling/base/pt_params.h b/tooling/base/pt_params.h index 404616ef..880addf9 100644 --- a/tooling/base/pt_params.h +++ b/tooling/base/pt_params.h @@ -1410,5 +1410,60 @@ private: std::optional>>> breakpointsMap_ {}; }; + +class SetSymbolicBreakpointsParams : public PtBaseParams { +public: + SetSymbolicBreakpointsParams() = default; + ~SetSymbolicBreakpointsParams() = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + const std::unordered_set *GetFunctionNamesSet() const + { + if (!HasSymbolicBreakpoints()) { + return nullptr; + } + return &(functionNamesSet_.value()); + } + + bool HasSymbolicBreakpoints() const + { + return functionNamesSet_.has_value(); + } + +private: + NO_COPY_SEMANTIC(SetSymbolicBreakpointsParams); + NO_MOVE_SEMANTIC(SetSymbolicBreakpointsParams); + + std::optional> functionNamesSet_ {}; +}; + +class RemoveSymbolicBreakpointsParams : public PtBaseParams { +public: + RemoveSymbolicBreakpointsParams() = default; + ~RemoveSymbolicBreakpointsParams() = default; + + static std::unique_ptr Create(const PtJson ¶ms); + + const std::unordered_set *GetFunctionNamesSet() const + { + if (!HasSymbolicBreakpoints()) { + return nullptr; + } + return &(functionNamesSet_.value()); + } + + bool HasSymbolicBreakpoints() const + { + return functionNamesSet_.has_value(); + } + +private: + NO_COPY_SEMANTIC(RemoveSymbolicBreakpointsParams); + NO_MOVE_SEMANTIC(RemoveSymbolicBreakpointsParams); + + std::optional> functionNamesSet_ {}; +}; + } // namespace panda::ecmascript::tooling #endif \ No newline at end of file diff --git a/tooling/client/domain/debugger_client.cpp b/tooling/client/domain/debugger_client.cpp index 0cf24c78..7baa6ca2 100644 --- a/tooling/client/domain/debugger_client.cpp +++ b/tooling/client/domain/debugger_client.cpp @@ -49,6 +49,8 @@ bool DebuggerClient::DispatcherCmd(const std::string &cmd) { "step-over", std::bind(&DebuggerClient::StepOverCommand, this)}, { "enable-launch-accelerate", std::bind(&DebuggerClient::EnableLaunchAccelerateCommand, this)}, { "saveAllPossibleBreakpoints", std::bind(&DebuggerClient::SaveAllPossibleBreakpointsCommand, this)}, + { "setSymbolicBreakpoints", std::bind(&DebuggerClient::SetSymbolicBreakpointsCommand, this)}, + { "removeSymbolicBreakpoints", std::bind(&DebuggerClient::RemoveSymbolicBreakpointsCommand, this)}, }; auto entry = dispatcherTable.find(cmd); @@ -308,6 +310,13 @@ void DebuggerClient::AddBreakPointInfo(const std::string& url, const int& lineNu breakPointInfoList_.emplace_back(breakPointInfo); } +void DebuggerClient::AddSymbolicBreakpointInfo(const std::string& functionName) +{ + SymbolicBreakpointInfo symbolicBreakpointInfo; + symbolicBreakpointInfo.functionName = functionName; + symbolicBreakpointInfoList_.emplace_back(symbolicBreakpointInfo); +} + void DebuggerClient::RecvReply(std::unique_ptr json) { if (json == nullptr) { @@ -458,6 +467,58 @@ int DebuggerClient::SaveAllPossibleBreakpointsCommand() return 0; } +int DebuggerClient::SetSymbolicBreakpointsCommand() +{ + Session *session = SessionManager::getInstance().GetSessionById(sessionId_); + uint32_t id = session->GetMessageId(); + + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Debugger.setSymbolicBreakpoints"); + + std::unique_ptr params = PtJson::CreateObject(); + std::unique_ptr symbolicBreakpoints = PtJson::CreateArray(); + + std::unique_ptr symbolicBreakpoint = PtJson::CreateObject(); + symbolicBreakpoint->Add("functionName", symbolicBreakpointInfoList_.back().functionName.c_str()); + symbolicBreakpoints->Push(symbolicBreakpoint); + params->Add("symbolicBreakpoints", symbolicBreakpoints); + request->Add("params", params); + + std::string message = request->Stringify(); + if (session->ClientSendReq(message)) { + session->GetDomainManager().SetDomainById(id, "Debugger"); + } + + return 0; +} + +int DebuggerClient::RemoveSymbolicBreakpointsCommand() +{ + Session *session = SessionManager::getInstance().GetSessionById(sessionId_); + uint32_t id = session->GetMessageId(); + + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Debugger.removeSymbolicBreakpoints"); + + std::unique_ptr params = PtJson::CreateObject(); + std::unique_ptr symbolicBreakpoints = PtJson::CreateArray(); + + std::unique_ptr symbolicBreakpoint = PtJson::CreateObject(); + symbolicBreakpoint->Add("functionName", symbolicBreakpointInfoList_.back().functionName.c_str()); + symbolicBreakpoints->Push(symbolicBreakpoint); + params->Add("symbolicBreakpoints", symbolicBreakpoints); + request->Add("params", params); + + std::string message = request->Stringify(); + if (session->ClientSendReq(message)) { + session->GetDomainManager().SetDomainById(id, "Debugger"); + } + + return 0; +} + int DebuggerClient::EnableLaunchAccelerateCommand() { Session *session = SessionManager::getInstance().GetSessionById(sessionId_); diff --git a/tooling/client/domain/debugger_client.h b/tooling/client/domain/debugger_client.h index aaeffd40..24b5aa54 100644 --- a/tooling/client/domain/debugger_client.h +++ b/tooling/client/domain/debugger_client.h @@ -29,6 +29,11 @@ struct BreakPointInfo { int columnNumber; std::string url; }; + +struct SymbolicBreakpointInfo { + std::string functionName; +}; + class DebuggerClient final { public: DebuggerClient(int32_t sessionId) : sessionId_(sessionId) {} @@ -61,14 +66,18 @@ public: int StepOverCommand(); int EnableLaunchAccelerateCommand(); int SaveAllPossibleBreakpointsCommand(); + int SetSymbolicBreakpointsCommand(); + int RemoveSymbolicBreakpointsCommand(); void AddBreakPointInfo(const std::string& url, const int& lineNumber, const int& columnNumber = 0); + void AddSymbolicBreakpointInfo(const std::string& functionName); void RecvReply(std::unique_ptr json); void PausedReply(const std::unique_ptr json); void handleResponse(std::unique_ptr json); private: std::vector breakPointInfoList_ {}; + std::vector symbolicBreakpointInfoList_ {}; int32_t sessionId_; }; } // OHOS::ArkCompiler::Toolchain diff --git a/tooling/client/utils/cli_command.cpp b/tooling/client/utils/cli_command.cpp index 8747800b..c98cdae3 100644 --- a/tooling/client/utils/cli_command.cpp +++ b/tooling/client/utils/cli_command.cpp @@ -35,6 +35,8 @@ const std::string HELP_MSG = "usage: \n" " runtime-enable(rt-enable) runtime enable\n" " heapusage(hu) runtime getHeapUsage\n" " break(b) break with options\n" + " setSymbolicBreakpoints setSymbolicBreakpoints\n" + " removeSymbolicBreakpoints removeSymbolicBreakpoints\n" " backtrack(bt) backtrace\n" " continue(c) continue\n" " delete(d) delete with options\n" @@ -90,6 +92,8 @@ const std::vector cmdList = { "runtime-enable", "heapusage", "break", + "setSymbolicBreakpoints", + "removeSymbolicBreakpoints", "backtrack", "continue", "delete", @@ -174,6 +178,10 @@ void CliCommand::CreateCommandMap() std::bind(&CliCommand::DebuggerCommand, this, "enable-launch-accelerate")}, {std::make_pair("saveAllPossibleBreakpoints", "b-new"), std::bind(&CliCommand::SaveAllPossibleBreakpointsCommand, this, "saveAllPossibleBreakpoints")}, + {std::make_pair("setSymbolicBreakpoints", "setSymbolicBreakpoints"), + std::bind(&CliCommand::SetSymbolicBreakpointsCommand, this, "setSymbolicBreakpoints")}, + {std::make_pair("removeSymbolicBreakpoints", "removeSymbolicBreakpoints"), + std::bind(&CliCommand::RemoveSymbolicBreakpointsCommand, this, "removeSymbolicBreakpoints")}, }; CreateOtherCommandMap(); } @@ -363,6 +371,52 @@ ErrCode CliCommand::BreakCommand(const std::string &cmd) return result ? ErrCode::ERR_OK : ErrCode::ERR_FAIL; } +ErrCode CliCommand::SetSymbolicBreakpointsCommand(const std::string &cmd) +{ + bool result = false; + Session *session = SessionManager::getInstance().GetSessionById(sessionId_); + DebuggerClient &debuggerCli = session->GetDomainManager().GetDebuggerClient(); + BreakPointManager &breakpointManager = session->GetBreakPointManager(); + std::vector breaklist_ = breakpointManager.Getbreaklist(); + if (GetArgList().size() == 1) { //1: one arguments + if (Utils::IsNumber(GetArgList()[0])) { + OutputCommand(cmd, false); + return ErrCode::ERR_FAIL; + } + debuggerCli.AddSymbolicBreakpointInfo(GetArgList()[0]); + } else { + OutputCommand(cmd, false); + return ErrCode::ERR_FAIL; + } + + result = debuggerCli.DispatcherCmd(cmd); + OutputCommand(cmd, true); + return result ? ErrCode::ERR_OK : ErrCode::ERR_FAIL; +} + +ErrCode CliCommand::RemoveSymbolicBreakpointsCommand(const std::string &cmd) +{ + bool result = false; + Session *session = SessionManager::getInstance().GetSessionById(sessionId_); + DebuggerClient &debuggerCli = session->GetDomainManager().GetDebuggerClient(); + BreakPointManager &breakpointManager = session->GetBreakPointManager(); + std::vector breaklist_ = breakpointManager.Getbreaklist(); + if (GetArgList().size() == 1) { //1: one arguments + if (Utils::IsNumber(GetArgList()[0])) { + OutputCommand(cmd, false); + return ErrCode::ERR_FAIL; + } + debuggerCli.AddSymbolicBreakpointInfo(GetArgList()[0]); + } else { + OutputCommand(cmd, false); + return ErrCode::ERR_FAIL; + } + + result = debuggerCli.DispatcherCmd(cmd); + OutputCommand(cmd, true); + return result ? ErrCode::ERR_OK : ErrCode::ERR_FAIL; +} + ErrCode CliCommand::DeleteCommand(const std::string &cmd) { bool result = false; diff --git a/tooling/client/utils/cli_command.h b/tooling/client/utils/cli_command.h index 88cafd29..b23667b7 100644 --- a/tooling/client/utils/cli_command.h +++ b/tooling/client/utils/cli_command.h @@ -73,6 +73,8 @@ public: ErrCode TestCommand(const std::string &cmd); ErrCode ExecHelpCommand(); ErrCode SaveAllPossibleBreakpointsCommand(const std::string &cmd); + ErrCode SetSymbolicBreakpointsCommand(const std::string &cmd); + ErrCode RemoveSymbolicBreakpointsCommand(const std::string &cmd); void OutputCommand(const std::string &cmd, bool flag); VecStr GetArgList() diff --git a/tooling/dispatcher.h b/tooling/dispatcher.h index 92c1f1e5..7066f86e 100644 --- a/tooling/dispatcher.h +++ b/tooling/dispatcher.h @@ -1,4 +1,4 @@ -/* + /* * Copyright (c) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ #include #include +#include #include "tooling/base/pt_returns.h" diff --git a/tooling/test/client_utils/test_list.cpp b/tooling/test/client_utils/test_list.cpp index 24e7883d..baacc718 100644 --- a/tooling/test/client_utils/test_list.cpp +++ b/tooling/test/client_utils/test_list.cpp @@ -72,6 +72,7 @@ #include "tooling/test/testcases/js_watch_set_type_test.h" #include "tooling/test/testcases/js_watch_test.h" #include "tooling/test/testcases/js_watch_variable_test.h" +#include "tooling/test/testcases/js_symbolbreakpoint_test.h" namespace panda::ecmascript::tooling::test { static std::string g_currentTestName = ""; @@ -133,6 +134,7 @@ static void RegisterTests() TestUtil::RegisterTest("JsHeapusageRecursionTest", GetJsHeapusageRecursionTest()); TestUtil::RegisterTest("JsSmartStepoutTest", GetJsSmartStepoutTest()); TestUtil::RegisterTest("JsAccelerateLaunchTest", GetJsAccelerateLaunchTest()); + TestUtil::RegisterTest("JsSymbolicBreakpointTest", GetJsSymbolicBreakpointTest()); } std::vector GetTestList() diff --git a/tooling/test/debugger_impl_test.cpp b/tooling/test/debugger_impl_test.cpp index 7d0e3654..7e598763 100644 --- a/tooling/test/debugger_impl_test.cpp +++ b/tooling/test/debugger_impl_test.cpp @@ -648,6 +648,66 @@ HWTEST_F_L0(DebuggerImplTest, Dispatcher_Dispatch_RemoveBreakpointsByUrl__002) } } +HWTEST_F_L0(DebuggerImplTest, Dispatcher_Dispatch_SetSymbolBreakpoints_001) +{ + std::string outStrForCallbackCheck = ""; + std::function callback = + [&outStrForCallbackCheck]([[maybe_unused]] const void *ptr, const std::string &inStrOfReply) { + outStrForCallbackCheck = inStrOfReply;}; + ProtocolChannel *protocolChannel = new ProtocolHandler(callback, ecmaVm); + auto runtimeImpl = std::make_unique(ecmaVm, protocolChannel); + auto debuggerImpl = std::make_unique(ecmaVm, protocolChannel, runtimeImpl.get()); + auto dispatcherImpl = std::make_unique(protocolChannel, std::move(debuggerImpl)); + + std::string msg = std::string() + + R"({ + "id":0, + "method":"Debugger.setSymbolicBreakpoints", + "params":{} + })"; + DispatchRequest request(msg); + + dispatcherImpl->Dispatch(request); + EXPECT_STREQ(outStrForCallbackCheck.c_str(), R"({"id":0,"result":{"code":1,"message":"wrong params"}})"); + if (protocolChannel) { + delete protocolChannel; + protocolChannel = nullptr; + } +} + +HWTEST_F_L0(DebuggerImplTest, Dispatcher_Dispatch_SetSymbolBreakpoints_002) +{ + std::string outStrForCallbackCheck = ""; + std::function callback = + [&outStrForCallbackCheck]([[maybe_unused]] const void *ptr, const std::string &inStrOfReply) { + outStrForCallbackCheck = inStrOfReply;}; + ProtocolChannel *protocolChannel = new ProtocolHandler(callback, ecmaVm); + auto runtimeImpl = std::make_unique(ecmaVm, protocolChannel); + auto debuggerImpl = std::make_unique(ecmaVm, protocolChannel, runtimeImpl.get()); + auto dispatcherImpl = std::make_unique(protocolChannel, std::move(debuggerImpl)); + + std::string msg = std::string() + + R"({ + "id":0, + "method":"Debugger.setSymbolicBreakpoints", + "params":{ + "symbolicBreakpoints":[ + { + "condition":"testDebug" + } + ] + } + })"; + DispatchRequest request(msg); + + dispatcherImpl->Dispatch(request); + EXPECT_STREQ(outStrForCallbackCheck.c_str(), R"({"id":0,"result":{"code":1,"message":"wrong params"}})"); + if (protocolChannel) { + delete protocolChannel; + protocolChannel = nullptr; + } +} + HWTEST_F_L0(DebuggerImplTest, Dispatcher_Dispatch_Resume__001) { std::string outStrForCallbackCheck = ""; diff --git a/tooling/test/debugger_params_test.cpp b/tooling/test/debugger_params_test.cpp index f01dcc53..af06a80e 100644 --- a/tooling/test/debugger_params_test.cpp +++ b/tooling/test/debugger_params_test.cpp @@ -263,6 +263,31 @@ HWTEST_F_L0(DebuggerParamsTest, SaveAllPossibleBreakpointsParamsCreateTest) EXPECT_EQ(params->GetBreakpointsMap()->size(), 1); } +HWTEST_F_L0(DebuggerParamsTest, SetSymbolicBreakpointsParamsCreateTest) +{ + std::string msg; + std::unique_ptr params; + + // abnormal + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{}})"; + params = SetSymbolicBreakpointsParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_EQ(params, nullptr); + + // abnormal + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"symbolicBreakpoints":[ + {"condition":"testDebug"}]}})"; + params = SetSymbolicBreakpointsParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_EQ(params, nullptr); + + // normal + msg = std::string() + R"({"id":0,"method":"Debugger.Test","params":{"symbolicBreakpoints":[ + {"functionName":"testDebug1"}, {"functionName":"testDebug2"}]}})"; + params = SetSymbolicBreakpointsParams::Create(DispatchRequest(msg).GetParams()); + ASSERT_NE(params, nullptr); + EXPECT_TRUE(params->HasSymbolicBreakpoints()); + EXPECT_EQ(params->GetFunctionNamesSet()->size(), 2); +} + HWTEST_F_L0(DebuggerParamsTest, StartSamplingParamsCreateTest) { std::string msg; diff --git a/tooling/test/testcases/js_symbolbreakpoint_test.h b/tooling/test/testcases/js_symbolbreakpoint_test.h new file mode 100644 index 00000000..57cc74e0 --- /dev/null +++ b/tooling/test/testcases/js_symbolbreakpoint_test.h @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ECMASCRIPT_TOOLING_TEST_TESTCASES_JS_SYMBOLICBREAKPOINT_LOOP_TEST_H +#define ECMASCRIPT_TOOLING_TEST_TESTCASES_JS_SYMBOLICBREAKPOINT_LOOP_TEST_H + +#include "tooling/test/client_utils/test_util.h" + +namespace panda::ecmascript::tooling::test { +class JsSymbolicBreakpointTest : public TestActions { +public: + JsSymbolicBreakpointTest() + { + testAction = { + {SocketAction::SEND, "enable"}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + {SocketAction::SEND, "runtime-enable"}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + {SocketAction::SEND, "run"}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + // load common_func.js + {SocketAction::RECV, "Debugger.scriptParsed", ActionRule::STRING_CONTAIN}, + // break on start + {SocketAction::RECV, "Debugger.paused", ActionRule::STRING_CONTAIN}, + // set symbol breakpoints + {SocketAction::SEND, "setSymbolicBreakpoints " "common_func"}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + {SocketAction::SEND, "setSymbolicBreakpoints " "for_loop"}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + + // hit the common_func function + {SocketAction::SEND, "resume"}, + {SocketAction::RECV, "Debugger.resumed", ActionRule::STRING_CONTAIN}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + {SocketAction::RECV, "Debugger.paused", ActionRule::CUSTOM_RULE, + [this](auto recv, auto, auto) -> bool { return RecvBreakInfo(recv, "common_func", 16); }}, + + // set symbol breakpoints + {SocketAction::SEND, "setSymbolicBreakpoints " "while_loop"}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + + // hit the for_loop function + {SocketAction::SEND, "resume"}, + {SocketAction::RECV, "Debugger.resumed", ActionRule::STRING_CONTAIN}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + {SocketAction::RECV, "Debugger.paused", ActionRule::CUSTOM_RULE, + [this](auto recv, auto, auto) -> bool { return RecvBreakInfo(recv, "for_loop", 26); }}, + + // set symbol breakpoints + {SocketAction::SEND, "setSymbolicBreakpoints " "while_loop"}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + + // hit the while_loop function + {SocketAction::SEND, "resume"}, + {SocketAction::RECV, "Debugger.resumed", ActionRule::STRING_CONTAIN}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + {SocketAction::RECV, "Debugger.paused", ActionRule::CUSTOM_RULE, + [this](auto recv, auto, auto) -> bool { return RecvBreakInfo(recv, "while_loop", 34); }}, + + // set symbol breakpoints + {SocketAction::SEND, "setSymbolicBreakpoints " "loop_switch"}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + + // hit the loop_switch function + {SocketAction::SEND, "resume"}, + {SocketAction::RECV, "Debugger.resumed", ActionRule::STRING_CONTAIN}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + {SocketAction::RECV, "Debugger.paused", ActionRule::STRING_CONTAIN}, + + // set symbol breakpoints + {SocketAction::SEND, "setSymbolicBreakpoints " "factorial"}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + + // firstly hit the factorial function + {SocketAction::SEND, "resume"}, + {SocketAction::RECV, "Debugger.resumed", ActionRule::STRING_CONTAIN}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + {SocketAction::RECV, "Debugger.paused", ActionRule::CUSTOM_RULE, + [this](auto recv, auto, auto) -> bool { return RecvBreakInfo(recv, "factorial", 57); }}, + + // remove symbol breakpoints + {SocketAction::SEND, "removeSymbolicBreakpoints " "loop_switch"}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + + // secondly hit the factorial function + {SocketAction::SEND, "resume"}, + {SocketAction::RECV, "Debugger.resumed", ActionRule::STRING_CONTAIN}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + {SocketAction::RECV, "Debugger.paused", ActionRule::CUSTOM_RULE, + [this](auto recv, auto, auto) -> bool { return RecvBreakInfo(recv, "factorial", 57); }}, + + // remove symbol breakpoints + {SocketAction::SEND, "removeSymbolicBreakpoints " "factorial"}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + + // reply success and run + {SocketAction::SEND, "success"}, + {SocketAction::SEND, "resume"}, + {SocketAction::RECV, "Debugger.resumed", ActionRule::STRING_CONTAIN}, + {SocketAction::RECV, "", ActionRule::CUSTOM_RULE, MatchRule::replySuccess}, + }; + } + + bool RecvBreakInfo(std::string recv, std::string funcName, int lineNumber) + { + std::unique_ptr json = PtJson::Parse(recv); + Result ret; + std::string method = ""; + ret = json->GetString("method", &method); + if (ret != Result::SUCCESS || method != "Debugger.paused") { + return false; + } + + std::unique_ptr params = nullptr; + ret = json->GetObject("params", ¶ms); + if (ret != Result::SUCCESS) { + return false; + } + + std::unique_ptr callFrames = nullptr; + ret = params->GetArray("callFrames", &callFrames); + if (ret != Result::SUCCESS) { + return false; + } + + std::string functionName = ""; + ret = callFrames->Get(0)->GetString("functionName", &functionName); + if (ret != Result::SUCCESS || functionName != funcName) { + return false; + } + + std::unique_ptr location = nullptr; + ret = callFrames->Get(0)->GetObject("location", &location); + if (ret != Result::SUCCESS) { + return false; + } + + int lineNum = 0; + ret = location->GetInt("lineNumber", &lineNum); + if (ret != Result::SUCCESS || lineNum != lineNumber) { + return false; + } + + DebuggerClient debuggerClient(0); + debuggerClient.PausedReply(std::move(json)); + return true; + } + + std::pair GetEntryPoint() override + { + return {pandaFile_, entryPoint_}; + } + ~JsSymbolicBreakpointTest() = default; + +private: + std::string pandaFile_ = DEBUGGER_ABC_DIR "common_func.abc"; + std::string sourceFile_ = DEBUGGER_JS_DIR "common_func.js"; + std::string entryPoint_ = "common_func"; +}; + +std::unique_ptr GetJsSymbolicBreakpointTest() +{ + return std::make_unique(); +} +} // namespace panda::ecmascript::tooling::test + +#endif // ECMASCRIPT_TOOLING_TEST_TESTCASES_JS_SYMBOLICBREAKPOINT_LOOP_TEST_H \ No newline at end of file diff --git a/tooling/test/utils/test_hooks.h b/tooling/test/utils/test_hooks.h index 1fd7e2bd..6941fcc7 100644 --- a/tooling/test/utils/test_hooks.h +++ b/tooling/test/utils/test_hooks.h @@ -120,6 +120,8 @@ public: void DisableFirstTimeFlag() override {} + void HitSymbolicBreakpoint() override {} + void TerminateTest() { debugInterface_->UnregisterHooks(); -- Gitee