From eecce02c6be79fd0b2767077d284912850472091 Mon Sep 17 00:00:00 2001 From: duning Date: Tue, 12 Dec 2023 16:03:10 +0800 Subject: [PATCH] =?UTF-8?q?488=20=E6=94=AF=E6=8C=81=E5=91=BD=E4=BB=A4?= =?UTF-8?q?=E8=A1=8C-D=E6=8B=89=E8=B5=B7=E8=B0=83=E8=AF=95=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=20https://gitee.com/openharmony/arkcompiler=5Ftoolcha?= =?UTF-8?q?in/issues/I8J3NS=20Signed-off-by:=20duning=20?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I5bc87933988f2b2c793a165529e629d0f69a7912 --- BUILD.gn | 11 ++ ark/BUILD.gn | 70 +++++++++ ark/main.cpp | 170 ++++++++++++++++++++++ tooling/agent/debugger_impl.cpp | 24 ++- tooling/agent/debugger_impl.h | 4 +- tooling/base/pt_params.cpp | 38 +++++ tooling/base/pt_params.h | 27 ++++ tooling/client/domain/debugger_client.cpp | 23 +++ tooling/client/domain/debugger_client.h | 1 + tooling/client/utils/cli_command.cpp | 29 +++- tooling/client/utils/cli_command.h | 1 + 11 files changed, 392 insertions(+), 6 deletions(-) create mode 100644 ark/BUILD.gn create mode 100644 ark/main.cpp diff --git a/BUILD.gn b/BUILD.gn index d9235f8b..51ef84f9 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -195,6 +195,7 @@ group("ark_toolchain_packages") { deps = [] if (host_os != "mac") { deps += [ + "./ark:ark", "./inspector:ark_debugger", "./inspector:connectserver_debugger", "./tooling:libark_ecma_debugger", @@ -204,6 +205,16 @@ group("ark_toolchain_packages") { } } +group("ark_toolchain_host_linux_packages") { + deps = [] + if (host_os != "mac") { + deps += [ + "./ark:ark(${host_toolchain})", + "./tooling/client/ark_cli:arkdb(${host_toolchain})", + ] + } +} + group("ark_toolchain_unittest") { testonly = true deps = [] diff --git a/ark/BUILD.gn b/ark/BUILD.gn new file mode 100644 index 00000000..d31bd95b --- /dev/null +++ b/ark/BUILD.gn @@ -0,0 +1,70 @@ +#Copyright (c) 2023 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. + +import("../toolchain.gni") + +import("//arkcompiler/ets_runtime/js_runtime_config.gni") + +ohos_executable("ark") { + sources = [ "main.cpp" ] + + configs = [ + "../../ets_runtime:ark_jsruntime_common_config", + "../../ets_runtime:ark_jsruntime_public_config", + "../:ark_toolchain_common_config", + ] + + external_deps = [ "ets_runtime:libark_jsruntime" ] + + deps = [ + "../inspector:ark_debugger", + "../inspector:connectserver_debugger", + "../tooling:libark_ecma_debugger", + "//third_party/libuv:uv", + ] + + if (ark_standalone_build) { + if (run_with_qemu && (is_ohos || is_linux)) { + deps += [ + ":copy_libcxxso_for_qemu", + "$build_root/third_party_gn/bounds_checking_function:libsec_shared_for_qemu", + ] + + if (use_musl) { + deps += + [ "$build_root/third_party_gn/musl:soft_create_linker_for_qemu" ] + } + } + } + + if (!is_arkui_x) { + external_deps += [ + "runtime_core:arkfile_header_deps", + "runtime_core:libarkbase_static", + ] + } else { + external_deps += [ + "$ark_root/libpandabase:libarkbase_static", + "$ark_root/libpandafile:arkfile_header_deps", + ] + } + + # hiviewdfx libraries + external_deps += hiviewdfx_ext_deps + deps += hiviewdfx_deps + + install_enable = false + + part_name = "toolchain" + subsystem_name = "arkcompiler" +} diff --git a/ark/main.cpp b/ark/main.cpp new file mode 100644 index 00000000..ef355876 --- /dev/null +++ b/ark/main.cpp @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2023 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. + */ + +#include +#include +#include +#include +#include // NOLINTNEXTLINE(modernize-deprecated-headers) +#include + +#include "ecmascript/jspandafile/js_pandafile.h" +#include "ecmascript/jspandafile/js_pandafile_manager.h" +#include "ecmascript/base/string_helper.h" +#include "ecmascript/ecma_string.h" +#include "ecmascript/ecma_vm.h" +#include "ecmascript/js_runtime_options.h" +#include "ecmascript/log.h" +#include "ecmascript/mem/mem_controller.h" +#include "ecmascript/mem/clock_scope.h" +#include "ecmascript/napi/include/jsnapi.h" +#include "ecmascript/debugger/js_debugger_manager.h" + +#ifdef OHOS_PLATFORM +#define DEBUGGER_LIBRARY "libark_debugger.z.so" +#else +#define DEBUGGER_LIBRARY "libark_debugger.so" +#endif +using EntityId = panda::panda_file::File::EntityId; + +#ifdef LINUX_PLATFORM +const std::string DEBUGOPTION = "--debug-port"; +#endif +namespace panda::ecmascript { + +void BlockSignals() +{ +#if defined(PANDA_TARGET_UNIX) + sigset_t set; + if (sigemptyset(&set) == -1) { + LOG_ECMA(ERROR) << "sigemptyset failed"; + return; + } +#endif // PANDA_TARGET_UNIX +} + +std::string GetHelper() +{ + std::string str; + str.append(COMMON_HELP_HEAD_MSG); + str.append(HELP_OPTION_MSG); + return str; +} + +bool Debugger(arg_list_t fileNames, EcmaVM *vm, std::string entry) +{ + for (const auto &fileName : fileNames) { + std::cout << "fileName: " << fileName << " entrypoint: " << entry << std::endl; + std::cout << "Please use the parseabc command to parse the abc file" << std::endl; + JSThread *thread = vm->GetJSThread(); + CString FileName = ""; + FileName += fileName; + std::shared_ptr jsPandaFile = + JSPandaFileManager::GetInstance()->LoadJSPandaFile(thread, FileName, entry.c_str(), false); + } +#if defined(LINUX_PLATFORM) + std::string option; + std::cout << "js_runtime_options "; + getline(std::cin, option); + bool ret = false; + if (option.substr(0, DEBUGOPTION.size()) == "--debug-port") { + int port = std::stoi(option.substr(DEBUGOPTION.size(), option.size())); + JSNApi::DebugOption debugOption = {DEBUGGER_LIBRARY, true, port}; + ret = JSNApi::StartDebugger(vm, debugOption); + } +#elif defined(OHOS_PLATFORM) + int port = 9010; // socket port + JSNApi::DebugOption debugOption = {DEBUGGER_LIBRARY, true, port}; + ret = JSNApi::StartDebugger(vm, debugOption); +#endif + for (const auto &fileName : fileNames) { + auto res = JSNApi::Execute(vm, fileName, entry); + if (!res) { + std::cerr << "Cannot execute panda file '" << fileName << "' with entry '" << entry << "'" << std::endl; + return false; + } + } + if (ret) { + JSNApi::StopDebugger(vm); + } + return true; +} + +int Main(const int argc, const char **argv) +{ + auto startTime = + std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()) + .count(); + + BlockSignals(); + + if (argc < 2) { // 2: at least have two arguments + std::cerr << GetHelper(); + return -1; + } + + int newArgc = argc - 1; + std::string files = argv[argc - 1]; + + if (!base::StringHelper::EndsWith(files, ".abc")) { + std::cerr << "The last argument must be abc file" << std::endl; + std::cerr << GetHelper(); + return 1; + } + + JSRuntimeOptions runtimeOptions; + bool retOpt = runtimeOptions.ParseCommand(newArgc, argv); + if (!retOpt) { + std::cerr << GetHelper(); + return 1; + } + + if (runtimeOptions.IsStartupTime()) { + std::cout << "\n" + << "Startup start time: " << startTime << std::endl; + } + bool ret = true; + EcmaVM *vm = JSNApi::CreateEcmaVM(runtimeOptions); + if (vm == nullptr) { + std::cerr << "Cannot Create vm" << std::endl; + return -1; + } + + bool isMergeAbc = runtimeOptions.GetMergeAbc(); + JSNApi::SetBundle(vm, !isMergeAbc); + { + LocalScope scope(vm); + std::string entry = runtimeOptions.GetEntryPoint(); +#if defined(PANDA_TARGET_WINDOWS) + arg_list_t fileNames = base::StringHelper::SplitString(files, ";"); +#else + arg_list_t fileNames = base::StringHelper::SplitString(files, ":"); +#endif + ClockScope execute; + ret = Debugger(fileNames, vm, entry); + auto totalTime = execute.TotalSpentTime(); + if (runtimeOptions.IsEnablePrintExecuteTime()) { + std::cout << "execute pandafile spent time " << totalTime << "ms" << std::endl; + } + } + JSNApi::DestroyJSVM(vm); + return ret ? 0 : -1; +} +} // namespace panda::ecmascript + +int main(int argc, const char **argv) +{ + return panda::ecmascript::Main(argc, argv); +} \ No newline at end of file diff --git a/tooling/agent/debugger_impl.cpp b/tooling/agent/debugger_impl.cpp index f71545c3..a6a5066c 100644 --- a/tooling/agent/debugger_impl.cpp +++ b/tooling/agent/debugger_impl.cpp @@ -67,12 +67,13 @@ DebuggerImpl::~DebuggerImpl() DebuggerApi::DestroyJSDebugger(jsDebugger_); } -bool DebuggerImpl::NotifyScriptParsed(ScriptId scriptId, const std::string &fileName, std::string_view entryPoint) +bool DebuggerImpl::NotifyScriptParsed(ScriptId scriptId, const std::string &fileName, + std::string_view entryPoint, bool ark) { #if !defined(PANDA_TARGET_WINDOWS) && !defined(PANDA_TARGET_MACOS) \ && !defined(PANDA_TARGET_ANDROID) && !defined(PANDA_TARGET_IOS) \ && !defined(PANDA_TARGET_LINUX) - if (fileName.substr(0, DATA_APP_PATH.length()) != DATA_APP_PATH) { + if (fileName.substr(0, DATA_APP_PATH.length()) != DATA_APP_PATH && ark) { LOG_DEBUGGER(DEBUG) << "NotifyScriptParsed: unsupport file: " << fileName; return false; } @@ -94,7 +95,7 @@ bool DebuggerImpl::NotifyScriptParsed(ScriptId scriptId, const std::string &file auto mainMethodIndex = panda_file::File::EntityId(jsPandaFile->GetMainMethodIndex(recordName)); const std::string &source = extractor->GetSourceCode(mainMethodIndex); const std::string &url = extractor->GetSourceFile(mainMethodIndex); - if (source.empty()) { + if (source.empty() && ark) { LOG_DEBUGGER(WARN) << "NotifyScriptParsed: invalid debugger file: " << fileName; return false; } @@ -379,6 +380,7 @@ void DebuggerImpl::DispatcherImpl::Dispatch(const DispatchRequest &request) { "setNativeRange", &DebuggerImpl::DispatcherImpl::SetNativeRange }, { "resetSingleStepper", &DebuggerImpl::DispatcherImpl::ResetSingleStepper }, { "clientDisconnect", &DebuggerImpl::DispatcherImpl::ClientDisconnect }, + { "scriptParsed", &DebuggerImpl::DispatcherImpl::ScriptParsed}, }; const std::string &method = request.GetMethod(); @@ -657,6 +659,13 @@ void DebuggerImpl::DispatcherImpl::DropFrame(const DispatchRequest &request) SendResponse(request, response); } +void DebuggerImpl::DispatcherImpl::ScriptParsed(const DispatchRequest &request) +{ + std::unique_ptr params = ScriptParsedParams::Create(request.GetParams()); + DispatchResponse response = debugger_->ScriptParsed(*params); + SendResponse(request, response); +} + void DebuggerImpl::DispatcherImpl::ClientDisconnect(const DispatchRequest &request) { DispatchResponse response = debugger_->ClientDisconnect(); @@ -1201,6 +1210,15 @@ DispatchResponse DebuggerImpl::DropFrame(const DropFrameParams ¶ms) return DispatchResponse::Ok(); } +DispatchResponse DebuggerImpl::ScriptParsed(const ScriptParsedParams ¶ms) +{ + ScriptId scriptId = params.GetScriptId(); + std::string fileName = params.GetFileName(); + std::string_view entryPoint = params.GetEntryPoint(); + NotifyScriptParsed(scriptId, fileName, entryPoint, false); + return DispatchResponse::Ok(); +} + DispatchResponse DebuggerImpl::ClientDisconnect() { DeviceDisconnectCallback cb = vm_->GetDeviceDisconnectCallback(); diff --git a/tooling/agent/debugger_impl.h b/tooling/agent/debugger_impl.h index aece3b5a..a3ffc6c5 100644 --- a/tooling/agent/debugger_impl.h +++ b/tooling/agent/debugger_impl.h @@ -39,7 +39,7 @@ public: // event bool NotifyScriptParsed(ScriptId scriptId, const std::string &fileName, - std::string_view entryPoint = "func_main_0"); + std::string_view entryPoint = "func_main_0", bool ark = true); bool NotifySingleStep(const JSPtLocation &location); void NotifyPaused(std::optional location, PauseReason reason); bool NotifyNativeOut(); @@ -78,6 +78,7 @@ public: DispatchResponse ReplyNativeCalling(const ReplyNativeCallingParams ¶ms); DispatchResponse DropFrame(const DropFrameParams ¶ms); DispatchResponse ClientDisconnect(); + DispatchResponse ScriptParsed(const ScriptParsedParams ¶ms); /** * @brief: match first script and callback @@ -167,6 +168,7 @@ public: void GetPossibleAndSetBreakpointByUrl(const DispatchRequest &request); void DropFrame(const DispatchRequest &request); void ClientDisconnect(const DispatchRequest &request); + void ScriptParsed(const DispatchRequest &request); private: NO_COPY_SEMANTIC(DispatcherImpl); diff --git a/tooling/base/pt_params.cpp b/tooling/base/pt_params.cpp index ebc8f0e8..73e9fcf4 100644 --- a/tooling/base/pt_params.cpp +++ b/tooling/base/pt_params.cpp @@ -562,6 +562,44 @@ std::unique_ptr DropFrameParams::Create(const PtJson ¶ms) return paramsObject; } +std::unique_ptr ScriptParsedParams::Create(const PtJson ¶ms) +{ + auto paramsObject = std::make_unique(); + std::string error; + Result ret; + + std::string scriptId; + ret = params.GetString("scriptId", &scriptId); + if (ret == Result::SUCCESS) { + paramsObject->scriptId_ = std::stoi(scriptId); + } else if (ret == Result::TYPE_ERROR) { + error += "Wrong type of 'ScriptParsed';"; + } + + std::string fileName; + ret = params.GetString("fileName", &fileName); + if (ret == Result::SUCCESS) { + paramsObject->fileName_ = fileName; + } else if (ret == Result::TYPE_ERROR) { + error += "Wrong type of 'ScriptParsed';"; + } + + std::string entryPoint; + ret = params.GetString("entryPoint", &entryPoint); + if (ret == Result::SUCCESS) { + paramsObject->entryPoint_ = entryPoint; + } else if (ret == Result::TYPE_ERROR) { + error += "Wrong type of 'ScriptParsed';"; + } + + if (!error.empty()) { + LOG_DEBUGGER(ERROR) << "ScriptParsedParams::Create " << error; + return nullptr; + } + + return paramsObject; +} + std::unique_ptr SetNativeRangeParams::Create(const PtJson ¶ms) { auto paramsObject = std::make_unique(); diff --git a/tooling/base/pt_params.h b/tooling/base/pt_params.h index b7af2199..1b7bbd1e 100644 --- a/tooling/base/pt_params.h +++ b/tooling/base/pt_params.h @@ -536,6 +536,33 @@ private: std::vector nativeRange_ {}; }; +class ScriptParsedParams { +public: + ScriptParsedParams() = default; + ~ScriptParsedParams() = default; + static std::unique_ptr Create(const PtJson ¶ms); + + ScriptId GetScriptId() const + { + return scriptId_; + } + + std::string GetFileName() const + { + return fileName_; + } + + std::string GetEntryPoint() const + { + return entryPoint_; + } +private: + ScriptId scriptId_ {0}; + std::string fileName_ {}; + std::string entryPoint_ {}; +}; + + class ResetSingleStepperParams : public PtBaseParams { public: ResetSingleStepperParams() = default; diff --git a/tooling/client/domain/debugger_client.cpp b/tooling/client/domain/debugger_client.cpp index 56b73e8f..9418d3bb 100644 --- a/tooling/client/domain/debugger_client.cpp +++ b/tooling/client/domain/debugger_client.cpp @@ -54,6 +54,7 @@ bool DebuggerClient::DispatcherCmd(const std::string &cmd) { "step-into", std::bind(&DebuggerClient::StepIntoCommand, this)}, { "step-out", std::bind(&DebuggerClient::StepOutCommand, this)}, { "step-over", std::bind(&DebuggerClient::StepOverCommand, this)}, + { "parseabc", std::bind(&DebuggerClient::ParseabcCommand, this)}, }; auto entry = dispatcherTable.find(cmd); @@ -67,6 +68,28 @@ bool DebuggerClient::DispatcherCmd(const std::string &cmd) return false; } +int DebuggerClient::ParseabcCommand() +{ + 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.scriptParsed"); + + std::unique_ptr params = PtJson::CreateObject(); + std::string fileName = breakPointInfoList_.back().url; + params->Add("fileName", fileName.c_str()); + params->Add("entryPoint", "func_main_0"); + request->Add("params", params); + + std::string message = request->Stringify(); + if (session->ClientSendReq(message)) { + session->GetDomainManager().SetDomainById(id, "Debugger"); + } + return 0; +} + int DebuggerClient::BreakCommand() { Session *session = SessionManager::getInstance().GetSessionById(sessionId_); diff --git a/tooling/client/domain/debugger_client.h b/tooling/client/domain/debugger_client.h index ddf03031..9ea4fcbd 100644 --- a/tooling/client/domain/debugger_client.h +++ b/tooling/client/domain/debugger_client.h @@ -59,6 +59,7 @@ public: int StepIntoCommand(); int StepOutCommand(); int StepOverCommand(); + int ParseabcCommand(); void AddBreakPointInfo(const std::string& url, const int& lineNumber, const int& columnNumber = 0); void RecvReply(std::unique_ptr json); diff --git a/tooling/client/utils/cli_command.cpp b/tooling/client/utils/cli_command.cpp index 41cd28d6..ec161777 100644 --- a/tooling/client/utils/cli_command.cpp +++ b/tooling/client/utils/cli_command.cpp @@ -145,6 +145,15 @@ ErrCode CliCommand::ExecCommand() return result; } +bool CliCommand::EndsWith(const std::string &str, const std::string &suffix) +{ + if (str.length() < suffix.length()) { + return false; + } + std::string subStr = str.substr(str.length() - suffix.length(), str.length()); + return subStr == suffix; +} + void CliCommand::CreateCommandMap() { commandMap_ = { @@ -202,6 +211,7 @@ void CliCommand::CreateCommandMap() {std::make_pair("step-into", "si"), std::bind(&CliCommand::DebuggerCommand, this, "step-into")}, {std::make_pair("step-out", "so"), std::bind(&CliCommand::DebuggerCommand, this, "step-out")}, {std::make_pair("step-over", "sov"), std::bind(&CliCommand::DebuggerCommand, this, "step-over")}, + {std::make_pair("parseabc", "pabc"), std::bind(&CliCommand::DebuggerCommand, this, "parseabc")}, {std::make_pair("runtime-disable", "rt-disable"), std::bind(&CliCommand::RuntimeCommand, this, "runtime-disable")}, {std::make_pair("session-new", "session-new"), @@ -303,8 +313,7 @@ ErrCode CliCommand::DebuggerCommand(const std::string &cmd) BreakPointManager &breakpoint = session->GetBreakPointManager(); if (cmd == "delete") { - std::string bnumber = GetArgList()[0]; - int tmpNum = std::stoi(bnumber); + int tmpNum = std::stoi(GetArgList()[0]); if (tmpNum < 0) { LOGE("ardb: the entered sequence number cannot be negativ!"); return ErrCode::ERR_FAIL; @@ -318,6 +327,14 @@ ErrCode CliCommand::DebuggerCommand(const std::string &cmd) } } + if (cmd == "parseabc") { + std::string bnumber = GetArgList()[0]; + if (!EndsWith(bnumber, ".abc")) { + return ErrCode::ERR_FAIL; + } + debuggerCli.AddBreakPointInfo(bnumber, 0); + } + if (cmd == "step-into" || cmd == "step-out" || cmd == "step-over") { RuntimeClient &runtimeClient = session->GetDomainManager().GetRuntimeClient(); runtimeClient.SetIsInitializeTree(true); @@ -329,6 +346,14 @@ ErrCode CliCommand::DebuggerCommand(const std::string &cmd) } if (cmd == "break" && GetArgList().size() == 2) { // 2: two parameters + std::vector breaklist_ = breakpoint.Getbreaklist(); + size_t bsize = breaklist_.size(); + for (size_t i = 0; i < bsize; i++) { + if (breaklist_[i].url == GetArgList()[0] && + std::stoi(breaklist_[i].lineNumber) + 1 == std::stoi(GetArgList()[1])) { + return ErrCode::ERR_FAIL; + } + } debuggerCli.AddBreakPointInfo(GetArgList()[0], std::stoi(GetArgList()[1])); } diff --git a/tooling/client/utils/cli_command.h b/tooling/client/utils/cli_command.h index 5410695e..0330c5e9 100644 --- a/tooling/client/utils/cli_command.h +++ b/tooling/client/utils/cli_command.h @@ -64,6 +64,7 @@ public: ErrCode SessionSwitchCommand(const std::string &cmd); ErrCode TestCommand(const std::string &cmd); ErrCode ExecHelpCommand(); + bool EndsWith(const std::string &str, const std::string &suffix); VecStr GetArgList() { -- Gitee