diff --git a/BUILD.gn b/BUILD.gn index bfa153b70b1a8c1fa953b771e236f25596b916a1..89bdfb0a71ec57e5679d589cdff14ec495cf11cb 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -133,6 +133,8 @@ group("ark_toolchain_packages") { "$toolchain_root/inspector:ark_debugger", "$toolchain_root/inspector:connectserver_debugger", "$toolchain_root/tooling:libark_ecma_debugger", + "$toolchain_root/tooling/client:libark_toolchain_client", + "$toolchain_root/tooling/client/toolchain_cli:toolchain_cli" ] } } diff --git a/tooling/client/BUILD.gn b/tooling/client/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..bf9360f7e0b126c0a44550897e0b048f629ace10 --- /dev/null +++ b/tooling/client/BUILD.gn @@ -0,0 +1,96 @@ +# 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("//arkcompiler/toolchain/toolchain.gni") + +config("hiviewdfx_config") { + defines = [] + if (enable_dump_in_faultlog) { + defines += [ "ENABLE_DUMP_IN_FAULTLOG" ] + } + if (enable_bytrace) { + defines += [ "ENABLE_BYTRACE" ] + cflags_cc = [ "-Wno-gnu-zero-variadic-macro-arguments" ] + } + if (enable_hitrace) { + defines += [ "ENABLE_HITRACE" ] + } + if (enable_hilog) { + defines += [ "ENABLE_HILOG" ] + } + + include_dirs = [ "$hilog_root/include" ] +} + +ohos_source_set("libark_toolchain_client_set") { + stack_protector_ret = false + defines = [] + deps = [] + configs = [ + sdk_libc_secshared_config, + ] + + # hiviewdfx libraries + external_deps = hiviewdfx_ext_deps + deps += hiviewdfx_deps + + include_dirs = [ + "$toolchain_root", + "$toolchain_root/inspector", + "$toolchain_root/websocket", + "$toolchain_root/tooling/base", + "$toolchain_root/tooling/client", + "//third_party/cJSON", + ] + + sources = [ + "websocket/websocket_client.cpp", + "domain/heapprofiler_client.cpp", + ] + + deps += [ + "$toolchain_root/websocket:websocket", + "$toolchain_root/tooling:libark_ecma_debugger_set", + "$ark_third_party_root/openssl:libcrypto_shared", + sdk_libc_secshared_dep, + ] + + external_deps += [ + "ets_runtime:libark_jsruntime", + "libuv:uv", + ] + + configs += [ + ":hiviewdfx_config", + ] + + cflags_cc = [ + "-Wno-error=vla-extension", + ] + + subsystem_name = "arkcompiler" + part_name = "toolchain" +} + +ohos_shared_library("libark_toolchain_client") { + stack_protector_ret = false + deps = [ ":libark_toolchain_client_set" ] + + if (!is_mingw && !is_mac) { + output_extension = "so" + } + + subsystem_name = "arkcompiler" + part_name = "toolchain" +} + diff --git a/tooling/client/domain/debugger_client.cpp b/tooling/client/domain/debugger_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..60ca2696e57ba1b804b926dae88a763d76af3a17 --- /dev/null +++ b/tooling/client/domain/debugger_client.cpp @@ -0,0 +1,230 @@ +/* + * 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 "domain/debugger_client.h" +#include "pt_json.h" +#include "log_wrapper.h" + +#include +#include +#include + +using PtJson = panda::ecmascript::tooling::PtJson; +namespace OHOS::ArkCompiler::Toolchain { +bool DebugerClient::DispatcherCmd(int id, const std::string cmd, std::string* reqStr) +{ + std::map> dispatcherTable { + { "break", std::bind(&DebugerClient::BreakCommand, this, id)}, + { "backtrack", std::bind(&DebugerClient::BacktrackCommand, this, id)}, + { "continue", std::bind(&DebugerClient::ContinueCommand, this, id)}, + { "delete", std::bind(&DebugerClient::DeleteCommand, this, id)}, + { "jump", std::bind(&DebugerClient::JumpCommand, this, id)}, + { "disable", std::bind(&DebugerClient::DisableCommand, this, id)}, + { "display", std::bind(&DebugerClient::DisplayCommand, this, id)}, + { "enable", std::bind(&DebugerClient::EnableCommand, this, id)}, + { "finish", std::bind(&DebugerClient::FinishCommand, this, id)}, + { "frame", std::bind(&DebugerClient::FrameCommand, this, id)}, + { "ignore", std::bind(&DebugerClient::IgnoreCommand, this, id)}, + { "infobreakpoints", std::bind(&DebugerClient::InfobreakpointsCommand, this, id)}, + { "infosource", std::bind(&DebugerClient::InfosourceCommand, this, id)}, + { "list", std::bind(&DebugerClient::NextCommand, this, id)}, + { "next", std::bind(&DebugerClient::ListCommand, this, id)}, + { "print", std::bind(&DebugerClient::PrintCommand, this, id)}, + { "ptype", std::bind(&DebugerClient::PtypeCommand, this, id)}, + { "run", std::bind(&DebugerClient::RunCommand, this, id)}, + { "setvar", std::bind(&DebugerClient::SetvarCommand, this, id)}, + { "step", std::bind(&DebugerClient::StepCommand, this, id)}, + { "undisplay", std::bind(&DebugerClient::UndisplayCommand, this, id)}, + { "watch", std::bind(&DebugerClient::WatchCommand, this, id)}, + { "resume", std::bind(&DebugerClient::ResumeCommand, this, id)} + }; + + auto entry = dispatcherTable.find(cmd); + + if (entry != dispatcherTable.end() && entry->second != nullptr) { + *reqStr = entry->second(); + LOGE("DebugerClient DispatcherCmd reqStr1: %{public}s", reqStr->c_str()); + return true; + } else { + *reqStr = "Unknown commond: " + cmd; + LOGE("DebugerClient DispatcherCmd reqStr2: %{public}s", reqStr->c_str()); + return false; + } +} + +std::string DebugerClient::BreakCommand(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Debugger.setBreakpointByUrl"); + + std::unique_ptr params = PtJson::CreateObject(); + params->Add("columnNumber", 0); + params->Add("lineNumber", 248); + params->Add("url", "entry/src/main/ets/pages/Index.ets"); + request->Add("params", params); + return request->Stringify(); +} + +std::string DebugerClient::BacktrackCommand(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "HeapProfiler.startTrackingHeapObjects"); + return request->Stringify(); +} + +std::string DebugerClient::ContinueCommand(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "HeapProfiler.stopTrackingHeapObjects"); + return request->Stringify(); +} + +std::string DebugerClient::DeleteCommand(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Debugger.removeBreakpoint"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string DebugerClient::DisableCommand(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Debugger.disable"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string DebugerClient::DisplayCommand([[maybe_unused]] int id) +{ + return "display"; +} + +std::string DebugerClient::EnableCommand([[maybe_unused]] int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Debugger.enable"); + + std::unique_ptr params = PtJson::CreateObject(); + // params->Add("maxScriptsCacheSize", 1.0E7); + request->Add("params", params); + return request->Stringify(); +} + +std::string DebugerClient::FinishCommand([[maybe_unused]] int id) +{ + return "finish"; +} + +std::string DebugerClient::FrameCommand([[maybe_unused]] int id) +{ + return "frame"; +} + +std::string DebugerClient::IgnoreCommand([[maybe_unused]] int id) +{ + return "ignore"; +} + +std::string DebugerClient::InfobreakpointsCommand([[maybe_unused]] int id) +{ + return "infobreakpoint"; +} + +std::string DebugerClient::InfosourceCommand([[maybe_unused]] int id) +{ + return "infosource"; +} + +std::string DebugerClient::JumpCommand([[maybe_unused]] int id) +{ + return "jump"; +} + +std::string DebugerClient::NextCommand([[maybe_unused]] int id) +{ + return "next"; +} + +std::string DebugerClient::ListCommand([[maybe_unused]] int id) +{ + return "list"; +} + +std::string DebugerClient::PrintCommand([[maybe_unused]] int id) +{ + return "print"; +} + +std::string DebugerClient::PtypeCommand([[maybe_unused]] int id) +{ + return "ptype"; +} + +std::string DebugerClient::RunCommand([[maybe_unused]] int id) +{ + return "run"; +} + +std::string DebugerClient::SetvarCommand([[maybe_unused]] int id) +{ + return "Debugger.setVariableValue"; +} + +std::string DebugerClient::StepCommand([[maybe_unused]] int id) +{ + return "step"; +} + +std::string DebugerClient::UndisplayCommand([[maybe_unused]] int id) +{ + return "undisplay"; +} + +std::string DebugerClient::WatchCommand([[maybe_unused]] int id) +{ + return "Debugger.evaluateOnCallFrame"; +} + +std::string DebugerClient::ResumeCommand(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Debugger.resume"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +void DebugerClient::AddBreakPointInfo(std::string url, std::string lineNumber) +{ + BreakPointInfo breakPointInfo; + breakPointInfo.url = url; + breakPointInfo.lineNumber = lineNumber; + breakPointInfoList_.emplace_back(breakPointInfo); +} + +} //OHOS::ArkCompiler::Toolchain \ No newline at end of file diff --git a/tooling/client/domain/debugger_client.h b/tooling/client/domain/debugger_client.h new file mode 100644 index 0000000000000000000000000000000000000000..a427a22972985e87de5d3c5a36aaf5c54776a396 --- /dev/null +++ b/tooling/client/domain/debugger_client.h @@ -0,0 +1,64 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_CLIENT_DOMAIN_DEBUGGER_CLIENT_H +#define ECMASCRIPT_TOOLING_CLIENT_DOMAIN_DEBUGGER_CLIENT_H + +#include +#include +#include "toolchain_cli/cli_command.h" + +namespace OHOS::ArkCompiler::Toolchain { +struct BreakPointInfo { + std::string lineNumber; + std::string url; +}; +class DebugerClient final { +public: + DebugerClient() = default; + ~DebugerClient() = default; + + bool DispatcherCmd(int id, const std::string cmd, std::string* reqStr); + std::string BreakCommand(int id); + std::string BacktrackCommand(int id); + std::string ContinueCommand(int id); + std::string DeleteCommand(int id); + std::string DisableCommand(int id); + std::string DisplayCommand(int id); + std::string EnableCommand(int id); + std::string FinishCommand(int id); + std::string FrameCommand(int id); + std::string IgnoreCommand(int id); + std::string InfobreakpointsCommand(int id); + std::string InfosourceCommand(int id); + std::string JumpCommand(int id); + std::string NextCommand(int id); + std::string ListCommand(int id); + std::string PrintCommand(int id); + std::string PtypeCommand(int id); + std::string RunCommand(int id); + std::string SetvarCommand(int id); + std::string StepCommand(int id); + std::string UndisplayCommand(int id); + std::string WatchCommand(int id); + std::string ResumeCommand(int id); + + void AddBreakPointInfo(std::string url, std::string lineNumber); + +private: + std::vector breakPointInfoList_; +}; +} //OHOS::ArkCompiler::Toolchain +#endif \ No newline at end of file diff --git a/tooling/client/domain/heapprofiler_client.cpp b/tooling/client/domain/heapprofiler_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..93448e3b467a0552ce86acc5663113536f745784 --- /dev/null +++ b/tooling/client/domain/heapprofiler_client.cpp @@ -0,0 +1,126 @@ +/* + * 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 "domain/heapprofiler_client.h" +#include "pt_json.h" +#include "log_wrapper.h" + +#include +#include +#include + +using PtJson = panda::ecmascript::tooling::PtJson; +namespace OHOS::ArkCompiler::Toolchain { +bool HeapProfilerClient::DispatcherCmd(int id, const std::string cmd, std::string* reqStr) +{ + std::map> dispatcherTable { + { "allocationtrack", std::bind(&HeapProfilerClient::AllocationTrackCommand, this, id)}, + { "allocationtrack-stop", std::bind(&HeapProfilerClient::AllocationTrackStopCommand, this, id)}, + { "heapdump", std::bind(&HeapProfilerClient::HeapDumpCommand, this, id)}, + { "heapprofiler-enable", std::bind(&HeapProfilerClient::Enable, this, id)}, + { "heapprofiler-disable", std::bind(&HeapProfilerClient::Disable, this, id)}, + { "sampling", std::bind(&HeapProfilerClient::Samping, this, id)}, + { "sampling-stop", std::bind(&HeapProfilerClient::SampingStop, this, id)}, + { "collectgarbage", std::bind(&HeapProfilerClient::CollectGarbage, this, id)} + }; + + auto entry = dispatcherTable.find(cmd); + + if (entry != dispatcherTable.end() && entry->second != nullptr) { + *reqStr = entry->second(); + LOGE("DispatcherCmd reqStr1: %{public}s", reqStr->c_str()); + return true; + } else { + *reqStr = "Unknown commond: " + cmd; + LOGE("DispatcherCmd reqStr2: %{public}s", reqStr->c_str()); + return false; + } +} + +std::string HeapProfilerClient::HeapDumpCommand(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "HeapProfiler.takeHeapSnapshot"); + + std::unique_ptr params = PtJson::CreateObject(); + params->Add("reportProgress", true); + params->Add("captureNumericValue", true); + params->Add("exposeInternals", false); + request->Add("params", params); + return request->Stringify(); +} + +std::string HeapProfilerClient::AllocationTrackCommand(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "HeapProfiler.startTrackingHeapObjects"); + + std::unique_ptr params = PtJson::CreateObject(); + params->Add("trackAllocations", true); + request->Add("params", params); + return request->Stringify(); +} + +std::string HeapProfilerClient::AllocationTrackStopCommand(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "HeapProfiler.stopTrackingHeapObjects"); + + std::unique_ptr params = PtJson::CreateObject(); + params->Add("reportProgress", true); + request->Add("params", params); + return request->Stringify(); +} + +std::string HeapProfilerClient::Enable(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "HeapProfiler.enable"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string HeapProfilerClient::Disable(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "HeapProfiler.disable"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string HeapProfilerClient::Samping([[maybe_unused]] int id) +{ + return "Samping"; +} + +std::string HeapProfilerClient::SampingStop([[maybe_unused]] int id) +{ + return "SampingStop"; +} + +std::string HeapProfilerClient::CollectGarbage([[maybe_unused]] int id) +{ + return "CollectGarbage"; +} +} //OHOS::ArkCompiler::Toolchain \ No newline at end of file diff --git a/tooling/client/domain/heapprofiler_client.h b/tooling/client/domain/heapprofiler_client.h new file mode 100644 index 0000000000000000000000000000000000000000..496cd237d6da5c94854a102bd7fadb2d8403727f --- /dev/null +++ b/tooling/client/domain/heapprofiler_client.h @@ -0,0 +1,40 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_CLIENT_DOMAIN_HEAPPROFILER_CLIENT_H +#define ECMASCRIPT_TOOLING_CLIENT_DOMAIN_HEAPPROFILER_CLIENT_H + +#include + +namespace OHOS::ArkCompiler::Toolchain { +class HeapProfilerClient final { +public: + HeapProfilerClient() = default; + ~HeapProfilerClient() = default; + + bool DispatcherCmd(int id, const std::string cmd, std::string* reqStr); + std::string HeapDumpCommand(int id); + std::string AllocationTrackCommand(int id); + std::string AllocationTrackStopCommand(int id); + std::string Enable(int id); + std::string Disable(int id); + std::string Samping(int id); + std::string SampingStop(int id); + std::string CollectGarbage(int id); +private: + +}; +} //OHOS::ArkCompiler::Toolchain +#endif \ No newline at end of file diff --git a/tooling/client/domain/profiler_client.cpp b/tooling/client/domain/profiler_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9703852db3a45abbb5ce2f92cb3ea4b27ae7aed3 --- /dev/null +++ b/tooling/client/domain/profiler_client.cpp @@ -0,0 +1,14 @@ +/* + * 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. + */ diff --git a/tooling/client/domain/profiler_client.h b/tooling/client/domain/profiler_client.h new file mode 100644 index 0000000000000000000000000000000000000000..9703852db3a45abbb5ce2f92cb3ea4b27ae7aed3 --- /dev/null +++ b/tooling/client/domain/profiler_client.h @@ -0,0 +1,14 @@ +/* + * 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. + */ diff --git a/tooling/client/domain/runtime_client.cpp b/tooling/client/domain/runtime_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4e351d085a80d01d12f59979300b2a5389941f76 --- /dev/null +++ b/tooling/client/domain/runtime_client.cpp @@ -0,0 +1,138 @@ +/* + * 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 "domain/runtime_client.h" +#include "pt_json.h" +#include "log_wrapper.h" + +#include +#include +#include + +using PtJson = panda::ecmascript::tooling::PtJson; +namespace OHOS::ArkCompiler::Toolchain { +bool RuntimeClient::DispatcherCmd(int id, const std::string cmd, std::string* reqStr ) +{ + std::map> dispatcherTable { + { "heapusage", std::bind(&RuntimeClient::HeapusageCommand, this, id)}, + { "runtime-enable", std::bind(&RuntimeClient::RuntimeEnableCommand, this, id)}, + { "runtime-disable", std::bind(&RuntimeClient::RuntimeDisableCommand, this, id)}, + { "print", std::bind(&RuntimeClient::GetPropertiesCommand, this, id)}, + { "print2", std::bind(&RuntimeClient::GetPropertiesCommand2, this, id)}, + { "run", std::bind(&RuntimeClient::RunIfWaitingForDebuggerCommand, this, id)}, + }; + + auto entry = dispatcherTable.find(cmd); + + if (entry != dispatcherTable.end() && entry->second != nullptr) { + *reqStr = entry->second(); + LOGE("RuntimeClient DispatcherCmd reqStr1: %{public}s", reqStr->c_str()); + return true; + } else { + *reqStr = "Unknown commond: " + cmd; + LOGE("RuntimeClient DispatcherCmd reqStr2: %{public}s", reqStr->c_str()); + return false; + } +} + +std::string RuntimeClient::HeapusageCommand(int id) +{ + idMethodMap_.emplace("getHeapUsage", id); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Runtime.getHeapUsage"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string RuntimeClient::RuntimeEnableCommand(int id) +{ + idMethodMap_.emplace("enable", id); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Runtime.enable"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string RuntimeClient::RuntimeDisableCommand(int id) +{ + idMethodMap_.emplace("disable", id); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Runtime.disable"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string RuntimeClient::RunIfWaitingForDebuggerCommand(int id) +{ + idMethodMap_.emplace("runIfWaitingForDebugger", id); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Runtime.runIfWaitingForDebugger"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string RuntimeClient::GetPropertiesCommand(int id) +{ + idMethodMap_.emplace("getProperties", id); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Runtime.getProperties"); + + std::unique_ptr params = PtJson::CreateObject(); + params->Add("accessorPropertiesOnly", false); + params->Add("generatePreview", true); + params->Add("objectId", objectId_.c_str()); + params->Add("ownProperties", true); + request->Add("params", params); + return request->Stringify(); +} + +std::string RuntimeClient::GetPropertiesCommand2(int id) +{ + idMethodMap_.emplace("getProperties", id); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Runtime.getProperties"); + + std::unique_ptr params = PtJson::CreateObject(); + params->Add("accessorPropertiesOnly", true); + params->Add("generatePreview", true); + params->Add("objectId", "0"); + params->Add("ownProperties", false); + request->Add("params", params); + return request->Stringify(); +} + +int RuntimeClient::GetIdByMethod(const std::string method) +{ + auto it = idMethodMap_.find(method); + if (it != idMethodMap_.end()) { + return it->second; + } + return 0; +} +} //OHOS::ArkCompiler::Toolchain \ No newline at end of file diff --git a/tooling/client/domain/runtime_client.h b/tooling/client/domain/runtime_client.h new file mode 100644 index 0000000000000000000000000000000000000000..69459eb8079338aa205f50ab06da8250ce6d259a --- /dev/null +++ b/tooling/client/domain/runtime_client.h @@ -0,0 +1,59 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_CLIENT_DOMAIN_RUNTIME_CLIENT_H +#define ECMASCRIPT_TOOLING_CLIENT_DOMAIN_RUNTIME_CLIENT_H + +#include +#include + +namespace OHOS::ArkCompiler::Toolchain { +class RuntimeClient final { +public: + RuntimeClient(const RuntimeClient&) = delete; + RuntimeClient& operator=(const RuntimeClient&) = delete; + + static RuntimeClient& getInstance() { + static RuntimeClient instance; + return instance; + } + + bool DispatcherCmd(int id, const std::string cmd, std::string* reqStr); + std::string HeapusageCommand(int id); + std::string RuntimeEnableCommand(int id); + std::string RuntimeDisableCommand(int id); + std::string RunIfWaitingForDebuggerCommand(int id); + std::string GetPropertiesCommand(int id); + std::string GetPropertiesCommand2(int id); + + std::map GetIdMethodMap() const { + return idMethodMap_; + } + + void SetObjectId(const std::string objectId) { + objectId_ = objectId; + } + + int GetIdByMethod(const std::string method); + +private: + RuntimeClient() = default; + ~RuntimeClient() = default; + std::map idMethodMap_; + std::string objectId_ {"0"}; + +}; +} //OHOS::ArkCompiler::Toolchain +#endif \ No newline at end of file diff --git a/tooling/client/manager/breakpoint_manager.cpp b/tooling/client/manager/breakpoint_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9703852db3a45abbb5ce2f92cb3ea4b27ae7aed3 --- /dev/null +++ b/tooling/client/manager/breakpoint_manager.cpp @@ -0,0 +1,14 @@ +/* + * 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. + */ diff --git a/tooling/client/manager/breakpoint_manager.h b/tooling/client/manager/breakpoint_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..9703852db3a45abbb5ce2f92cb3ea4b27ae7aed3 --- /dev/null +++ b/tooling/client/manager/breakpoint_manager.h @@ -0,0 +1,14 @@ +/* + * 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. + */ diff --git a/tooling/client/manager/domain_manager.cpp b/tooling/client/manager/domain_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7636506911f1c82d3747ae8be66802db499decd4 --- /dev/null +++ b/tooling/client/manager/domain_manager.cpp @@ -0,0 +1,80 @@ +/* + * 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 "manager/domain_manager.h" +#include "log_wrapper.h" +#include "pt_json.h" +#include "manager/variable_manager.h" + +using PtJson = panda::ecmascript::tooling::PtJson; +using Result = panda::ecmascript::tooling::Result; +namespace OHOS::ArkCompiler::Toolchain { +void DomainManager::DispatcherReply(char* msg) +{ + std::string decMessage = std::string(msg); + std::unique_ptr json = PtJson::Parse(decMessage); + if (json == nullptr) { + LOGE("json parse error"); + return; + } + + if (!json->IsObject()) { + LOGE("json parse format error"); + json->ReleaseRoot(); + return; + } + + std::string domain; + Result ret; + int32_t id; + ret = json->GetInt("id", &id); + if (ret == Result::SUCCESS) { + domain = GetDomainById(id); + RemoveDomainById(id); + } + + std::string wholeMethod; + std::string method; + ret = json->GetString("method", &wholeMethod); + if (ret == Result::SUCCESS) { + std::string::size_type length = wholeMethod.length(); + std::string::size_type indexPoint; + indexPoint = wholeMethod.find_first_of('.', 0); + if (indexPoint == std::string::npos || indexPoint == 0 || indexPoint == length - 1) { + return; + } + domain = wholeMethod.substr(0, indexPoint); + } + + if (domain == "HeapProfiler") { + heapProfilerClient_.RecvReply(std::move(json)); + } else if (domain == "Profiler") { + LOGI("Profiler replay message is %{public}s", json->Stringify().c_str()); + } else if (domain == "Runtime") { + RuntimeClient &runtimeClient = RuntimeClient::getInstance(); + if (static_cast(id) == runtimeClient.GetIdByMethod("getproperties")) { + VariableManager &variableManager = VariableManager::getInstance(); + variableManager.HandleMessage(std::move(json)); + variableManager.ShowVariableInfos(); + } else { + LOGI("Debugger replay message is %{public}s", json->Stringify().c_str()); + } + } else if (domain == "Debugger") { + LOGI("Debugger replay message is %{public}s", json->Stringify().c_str()); + } +} +} \ No newline at end of file diff --git a/tooling/client/manager/domain_manager.h b/tooling/client/manager/domain_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..f606f311abcfdc6771b7f370f0c952fcfc210601 --- /dev/null +++ b/tooling/client/manager/domain_manager.h @@ -0,0 +1,58 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_CLIENT_MANAGER_DOMAIN_MANAGER_H +#define ECMASCRIPT_TOOLING_CLIENT_MANAGER_DOMAIN_MANAGER_H + +#include + +#include "toolchain_cli/cli_command.h" + +namespace OHOS::ArkCompiler::Toolchain { +class DomainManager{ +public: + DomainManager() = default; + ~DomainManager() = default; + + void DispatcherReply(char* decMessage); + + std::string GetDomainById(uint32_t id) + { + auto iter = idDomainMap_.find(id); + if (iter == idDomainMap_.end()) { + return ""; + } + return iter->second; + } + + void SetDomainById(uint32_t id, std::string domain) + { + idDomainMap_.emplace(id, domain); + } + + void RemoveDomainById(uint32_t id) + { + auto it = idDomainMap_.find(id); + if (it != idDomainMap_.end()) { + idDomainMap_.erase(it); + } + } + +private: + HeapProfilerClient heapProfilerClient_; + std::map idDomainMap_; +}; +} //OHOS::ArkCompiler::Toolchain +#endif \ No newline at end of file diff --git a/tooling/client/manager/stack_manager.cpp b/tooling/client/manager/stack_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9703852db3a45abbb5ce2f92cb3ea4b27ae7aed3 --- /dev/null +++ b/tooling/client/manager/stack_manager.cpp @@ -0,0 +1,14 @@ +/* + * 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. + */ diff --git a/tooling/client/manager/stack_manager.h b/tooling/client/manager/stack_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..9703852db3a45abbb5ce2f92cb3ea4b27ae7aed3 --- /dev/null +++ b/tooling/client/manager/stack_manager.h @@ -0,0 +1,14 @@ +/* + * 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. + */ diff --git a/tooling/client/manager/step_manager.cpp b/tooling/client/manager/step_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9703852db3a45abbb5ce2f92cb3ea4b27ae7aed3 --- /dev/null +++ b/tooling/client/manager/step_manager.cpp @@ -0,0 +1,14 @@ +/* + * 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. + */ diff --git a/tooling/client/manager/step_manager.h b/tooling/client/manager/step_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..9703852db3a45abbb5ce2f92cb3ea4b27ae7aed3 --- /dev/null +++ b/tooling/client/manager/step_manager.h @@ -0,0 +1,14 @@ +/* + * 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. + */ diff --git a/tooling/client/manager/variable_manager.cpp b/tooling/client/manager/variable_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..545279cab4743fbdbf150e49aae68da3fb6901af --- /dev/null +++ b/tooling/client/manager/variable_manager.cpp @@ -0,0 +1,83 @@ +/* + * 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 "log_wrapper.h" +#include "manager/variable_manager.h" + +using PtJson = panda::ecmascript::tooling::PtJson; +using Result = panda::ecmascript::tooling::Result; +namespace OHOS::ArkCompiler::Toolchain { +void VariableManager::HandleMessage(const std::unique_ptr json) +{ + if (json == nullptr) { + LOGE("toolchain_client: json parse error"); + return; + } + + if (!json->IsObject()) { + LOGE("toolchain_client: json parse format error"); + json->ReleaseRoot(); + return; + } + + std::unique_ptr result; + Result ret = json->GetObject("result", &result); + if (ret != Result::SUCCESS) { + LOGE("toolchain_client: find result error"); + return; + } + + std::unique_ptr innerResult; + ret = result->GetArray("result", &innerResult); + if (ret != Result::SUCCESS) { + LOGE("toolchain_client: find innerResult error"); + return; + } + + for (int32_t i = 0; i < innerResult->GetSize(); i++) { + std::unique_ptr variableInfo = PropertyDescriptor::Create(*(innerResult->Get(i))); + variableInfos_.emplace(i + 4, std::move(variableInfo)); + } +} + +void VariableManager::ShowVariableInfos() +{ + std::cout << std::endl; + std::cout << "1. LOCAL" << std::endl; + for (const auto& info : variableInfos_) { + if (info.second->GetValue()->HasDescription()) { + std::cout << " " << info.first << ". " << info.second->GetName() << " = " << + info.second->GetValue()->GetDescription() << std::endl; + } else { + std::cout << " " << info.first << ". " << info.second->GetName() << " = " << + info.second->GetValue()->GetType() << std::endl; + } + } + std::cout << "2. MODULE" << std::endl; + std::cout << "3. GLOBAL" << std::endl; +} + +std::string VariableManager::FindObjectIdByIndex(const int32_t index) +{ + auto it = variableInfos_.find(index); + if (it != variableInfos_.end()) { + return std::to_string(it->second->GetValue()->GetObjectId()); + } + LOGE("index not found"); + return ""; +} +} \ No newline at end of file diff --git a/tooling/client/manager/variable_manager.h b/tooling/client/manager/variable_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..9d978acbdad30eb4406082e5b80be641f2e2bede --- /dev/null +++ b/tooling/client/manager/variable_manager.h @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_CLIENT_MANAGER_VARIABLE_MANAGER_H +#define ECMASCRIPT_TOOLING_CLIENT_MANAGER_VARIABLE_MANAGER_H + +#include +#include +#include "pt_json.h" +#include "pt_types.h" + +using PtJson = panda::ecmascript::tooling::PtJson; +namespace OHOS::ArkCompiler::Toolchain { +using panda::ecmascript::tooling::PropertyDescriptor; +class VariableManager final { +public: + VariableManager(const VariableManager&) = delete; + VariableManager& operator=(const VariableManager&) = delete; + + static VariableManager& getInstance() { + static VariableManager instance; + return instance; + } + + void HandleMessage(std::unique_ptr json); + + void ShowVariableInfos(); + + std::string FindObjectIdByIndex(const int32_t index); + + std::multimap> &GetVariableInfos() { + return variableInfos_; + } + +private: + VariableManager() {} + ~VariableManager() {} + std::multimap> variableInfos_; +}; +} //OHOS::ArkCompiler::Toolchain +#endif \ No newline at end of file diff --git a/tooling/client/toolchain_cli/BUILD.gn b/tooling/client/toolchain_cli/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..6340ce325ef7124c73a27dbab6242e7c60d857be --- /dev/null +++ b/tooling/client/toolchain_cli/BUILD.gn @@ -0,0 +1,60 @@ +# 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("//arkcompiler/toolchain/toolchain.gni") + +config("hiviewdfx_config") { + defines = [] + if (enable_dump_in_faultlog) { + defines += [ "ENABLE_DUMP_IN_FAULTLOG" ] + } + if (enable_bytrace) { + defines += [ "ENABLE_BYTRACE" ] + cflags_cc = [ "-Wno-gnu-zero-variadic-macro-arguments" ] + } + if (enable_hitrace) { + defines += [ "ENABLE_HITRACE" ] + } + if (enable_hilog) { + defines += [ "ENABLE_HILOG" ] + } + + include_dirs = [ "$hilog_root/include" ] +} + +ohos_executable("toolchain_cli") { + sources = [ + "main.cpp", + "cli_command.cpp", + ] + + include_dirs = [ + "$toolchain_root", + "$toolchain_root/tooling", + "$toolchain_root/inspector", + "$toolchain_root/websocket", + "$toolchain_root/tooling/client", + "$toolchain_root/tooling/client/toolchain_cli", + ] + + deps = [ + "$toolchain_root/tooling/client:libark_toolchain_client_set", + ] + + configs = [ ":hiviewdfx_config" ] + + install_enable = false + + part_name = "toolchain" + subsystem_name = "arkcompiler" +} diff --git a/tooling/client/toolchain_cli/cli_command.cpp b/tooling/client/toolchain_cli/cli_command.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ee622f6dc4c847afd64255b22595e8240131194a --- /dev/null +++ b/tooling/client/toolchain_cli/cli_command.cpp @@ -0,0 +1,278 @@ +/* + * 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 + +#include "cli_command.h" +#include "log_wrapper.h" +#include "manager/domain_manager.h" +#include "domain/debugger_client.h" +#include "manager/variable_manager.h" +#include "domain/runtime_client.h" + +namespace OHOS::ArkCompiler::Toolchain{ +extern ToolchainWebsocket g_cliSocket; +extern DomainManager g_domainManager; +const std::string HELP_MSG = "usage: \n" + " These are common commands list:\n" + " allocationtrack(at) allocation-track-start with options\n" + " allocationtrack-stop(at-stop) allocation-track-stop\n" + " heapdump(hd) heapdump with options\n" + " heapprofiler-enable(hp-enable) heapdump enable\n" + " heapprofiler-disable(hp-disable) heapdump disable\n" + " sampling(sampling) heapprofiler sampling\n" + " sampling-stop(sampling-stop) heapprofiler sampling stop\n" + " collectgarbage(gc) heapprofiler collectgarbage\n" + " cpuprofile(cp) cpuprofile start\n" + " cpuprofile-stop(cp-stop) cpuprofile stop\n" + " cpuprofile-enable(cp-enable) cpuprofile enable\n" + " cpuprofile-disable(cp-disable) cpuprofile disable\n" + " runtime-enable(rt-enable) runtime enable\n" + " heapusage(hu) runtime getHeapUsage\n" + " break(b) break with options\n" + " backtrack(bt) backtrace\n" + " continue(c) continue\n" + " delete(d) delete with options\n" + " disable disable\n" + " display display\n" + " enable enable\n" + " finish(fin) finish\n" + " frame(f) frame\n" + " help(h) list available commands\n" + " ignore(ig) ignore\n" + " infobreakpoints(infob) info-breakpoints\n" + " infosource(infos) info-source\n" + " jump(j) jump\n" + " list(l) list\n" + " next(n) next\n" + " print(p) print with options\n" + " ptype ptype\n" + " quit(q) quit\n" + " run(r) run\n" + " setvar(sv) set value with options\n" + " step(s) step\n" + " undisplay undisplay\n" + " watch(wa) watch\n"; + +const std::vector cmdList = { + "allocationtrack", + "allocationtrack-stop", + "heapdump", + "heapprofiler-enable", + "heapprofiler-disable", + "sampling", + "sampling-stop", + "collectgarbage", + "cpuprofile", + "cpuprofile-stop", + "cpuprofile-enable", + "cpuprofile-disable", + "runtime-enable", + "heapusage", + "break", + "backtrack", + "continue", + "delete", + "disable", + "display", + "enable", + "finish", + "frame", + "help", + "ignore", + "infobreakpoints", + "infosource", + "jump", + "list", + "next", + "print", + "ptype", + "run", + "setvar", + "step", + "undisplay", + "watch" +}; +ErrCode CliCommand::ExecCommand() +{ + CreateCommandMap(); + + int result = OnCommand(); + return result; +} + +void CliCommand::CreateCommandMap() +{ + commandMap_ = { + {std::make_pair("allocationtrack","at"), std::bind(&CliCommand::HeapProfilerCommand, this, "allocationtrack")}, + {std::make_pair("allocationtrack-stop","at-stop"), + std::bind(&CliCommand::HeapProfilerCommand, this, "allocationtrack-stop")}, + {std::make_pair("heapdump","hd"), std::bind(&CliCommand::HeapProfilerCommand, this, "heapdump")}, + {std::make_pair("heapprofiler-enable","hp-enable"), + std::bind(&CliCommand::HeapProfilerCommand, this, "heapprofiler-enable")}, + {std::make_pair("heapprofiler-disable","hp-disable"), + std::bind(&CliCommand::HeapProfilerCommand, this, "heapprofiler-disable")}, + {std::make_pair("sampling","sampling"), std::bind(&CliCommand::HeapProfilerCommand, this, "sampling")}, + {std::make_pair("sampling-stop","sampling-stop"), + std::bind(&CliCommand::HeapProfilerCommand, this, "sampling-stop")}, + {std::make_pair("collectgarbage","gc"), std::bind(&CliCommand::HeapProfilerCommand, this, "collectgarbage")}, + {std::make_pair("cpuprofile","cp"), std::bind(&CliCommand::CpuProfileCommand, this, "cpuprofile")}, + {std::make_pair("cpuprofile-stop","cp-stop"), + std::bind(&CliCommand::CpuProfileCommand, this, "cpuprofile-stop")}, + {std::make_pair("cpuprofile-enable","cp-enable"), + std::bind(&CliCommand::CpuProfileCommand, this, "cpuprofile-enable")}, + {std::make_pair("cpuprofile-disable","cp-disable"), + std::bind(&CliCommand::CpuProfileCommand, this, "cpuprofile-disable")}, + {std::make_pair("runtime-enable","rt-enable"), std::bind(&CliCommand::RuntimeCommand, this, "runtime-enable")}, + {std::make_pair("heapusage","hu"), std::bind(&CliCommand::RuntimeCommand, this, "heapusage")}, + {std::make_pair("break","b"), std::bind(&CliCommand::DebuggerCommand, this, "break")}, + {std::make_pair("backtrack","bt"), std::bind(&CliCommand::DebuggerCommand, this, "backtrack")}, + {std::make_pair("continue","c"), std::bind(&CliCommand::DebuggerCommand, this, "continue")}, + {std::make_pair("delete","d"), std::bind(&CliCommand::DebuggerCommand, this, "delete")}, + {std::make_pair("disable","disable"), std::bind(&CliCommand::DebuggerCommand, this, "disable")}, + {std::make_pair("display","display"), std::bind(&CliCommand::DebuggerCommand, this, "display")}, + {std::make_pair("enable","enable"), std::bind(&CliCommand::DebuggerCommand, this, "enable")}, + {std::make_pair("finish","fin"), std::bind(&CliCommand::DebuggerCommand, this, "finish")}, + {std::make_pair("frame","f"), std::bind(&CliCommand::DebuggerCommand, this, "frame")}, + {std::make_pair("help","h"), std::bind(&CliCommand::ExecHelpCommand, this)}, + {std::make_pair("ignore","ig"), std::bind(&CliCommand::DebuggerCommand, this, "ignore")}, + {std::make_pair("infobreakpoints","infob"), std::bind(&CliCommand::DebuggerCommand, this, "infobreakpoints")}, + {std::make_pair("infosource","infos"), std::bind(&CliCommand::DebuggerCommand, this, "infosource")}, + {std::make_pair("jump","j"), std::bind(&CliCommand::DebuggerCommand, this, "jump")}, + {std::make_pair("list","l"), std::bind(&CliCommand::DebuggerCommand, this, "list")}, + {std::make_pair("next","n"), std::bind(&CliCommand::DebuggerCommand, this, "next")}, + {std::make_pair("print","p"), std::bind(&CliCommand::RuntimeCommand, this, "print")}, + {std::make_pair("print2","p2"), std::bind(&CliCommand::RuntimeCommand, this, "print2")}, + {std::make_pair("ptype","ptype"), std::bind(&CliCommand::DebuggerCommand, this, "ptype")}, + {std::make_pair("run","r"), std::bind(&CliCommand::RuntimeCommand, this, "run")}, + {std::make_pair("setvar","sv"), std::bind(&CliCommand::DebuggerCommand, this, "setvar")}, + {std::make_pair("step","s"), std::bind(&CliCommand::DebuggerCommand, this, "step")}, + {std::make_pair("undisplay","undisplay"), std::bind(&CliCommand::DebuggerCommand, this, "undisplay")}, + {std::make_pair("watch","wa"), std::bind(&CliCommand::DebuggerCommand, this, "watch")}, + {std::make_pair("resume","resume"), std::bind(&CliCommand::DebuggerCommand, this, "resume")}, + }; +} + +ErrCode CliCommand::HeapProfilerCommand(const std::string cmd) +{ + std::cout << "exe success, cmd is " << cmd << std::endl; + std::string request; + bool result = false; + LOGE("HeapProfilerCommand: %{public}d", id_); + result = heapProfilerCli_.DispatcherCmd(id_, cmd, &request); + if (result) { + g_cliSocket.ClientSendReq(request); + if (g_domainManager.GetDomainById(id_) == "") { + g_domainManager.SetDomainById(id_, "heapprofiler"); + } + } else { + return ERR_FAIL; + } + return ERR_OK; +} + +ErrCode CliCommand::CpuProfileCommand(const std::string cmd) +{ + std::cout << "exe success, cmd is " << cmd << std::endl; + return ERR_OK; +} + +ErrCode CliCommand::DebuggerCommand(const std::string cmd) +{ + std::cout << "exe success, cmd is " << cmd << std::endl; + std::string request; + bool result = false; + LOGE("DebuggerCommand: %{public}d", id_); + DebugerClient debuggerCli; + if (GetArgList().size() == 2) { + debuggerCli.AddBreakPointInfo(GetArgList()[0], GetArgList()[1]); + } + result = debuggerCli.DispatcherCmd(id_, cmd, &request); + if (result) { + g_cliSocket.ClientSendReq(request); + if (g_domainManager.GetDomainById(id_) == "") { + g_domainManager.SetDomainById(id_, "Debugger"); + } + } else { + return ERR_FAIL; + } + return ERR_OK; +} + +ErrCode CliCommand::RuntimeCommand(const std::string cmd) +{ + std::cout << "exe success, cmd is " << cmd << std::endl; + std::string request; + bool result = false; + LOGE("RuntimeCommand: %{public}d", id_); + RuntimeClient runtimeClient = RuntimeClient::getInstance(); + VariableManager variableManager = VariableManager::getInstance(); + if (cmd == "print" && GetArgList().size() == 1) { + std::string objectId = variableManager.FindObjectIdByIndex(static_cast(GetArgList()[0])); + runtimeClient.SetObjectId(objectId); + } + result = runtimeClient.DispatcherCmd(id_, cmd, &request); + if (result) { + g_cliSocket.ClientSendReq(request); + if (g_domainManager.GetDomainById(id_) == "") { + g_domainManager.SetDomainById(id_, "Runtime"); + } + } else { + return ERR_FAIL; + } + return ERR_OK; +} + +ErrCode CliCommand::ExecHelpCommand() +{ + std::cout << HELP_MSG; + return ERR_OK; +} + +ErrCode CliCommand::OnCommand() +{ + std::map>::iterator it; + StrPair cmdPair; + bool haveCmdFlag = false; + + for (it = commandMap_.begin(); it != commandMap_.end(); it++) { + cmdPair = it->first; + if (!strcmp(cmdPair.first.c_str(), cmd_.c_str()) + ||!strcmp(cmdPair.second.c_str(), cmd_.c_str())) { + auto respond = it->second; + return respond(); + } + } + + for(unsigned int i = 0; i < cmdList.size(); i++) { + if (!strncmp(cmdList[i].c_str(), cmd_.c_str(), std::strlen(cmd_.c_str()))) { + haveCmdFlag = true; + std::cout << cmdList[i] << " "; + } + } + + if (haveCmdFlag) { + std::cout << std::endl; + } else { + ExecHelpCommand(); + } + + return ERR_FAIL; +} +} // namespace OHOS::ArkCompiler::Toolchain diff --git a/tooling/client/toolchain_cli/cli_command.h b/tooling/client/toolchain_cli/cli_command.h new file mode 100644 index 0000000000000000000000000000000000000000..df645446e5cdba260144ffbabcc6c443a36d2664 --- /dev/null +++ b/tooling/client/toolchain_cli/cli_command.h @@ -0,0 +1,84 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_CLIENT_TOOLCHAIN_CLI_COMMAND_H +#define ECMASCRIPT_TOOLING_CLIENT_TOOLCHAIN_CLI_COMMAND_H + +#include +#include +#include +#include + +#include "log_wrapper.h" +#include "websocket/websocket_client.h" +#include "domain/heapprofiler_client.h" +#include "domain/runtime_client.h" + + +namespace OHOS::ArkCompiler::Toolchain{ +using ErrCode = int; +using StrPair = std::pair; +using VecStr = std::vector; +enum { + ERR_OK = 0, + ERR_FAIL = 1 +}; + +class CliCommand { +public: + CliCommand(std::vector cliCmdStr, int cmdId) + { + id_ = cmdId; + cmd_ = cliCmdStr[0]; + for (unsigned int i = 1; i < cliCmdStr.size(); i++) { + argList_.push_back(cliCmdStr[i]); + } + } + + ~CliCommand() + { + } + + ErrCode OnCommand(); + ErrCode ExecCommand(); + void CreateCommandMap(); + ErrCode HeapProfilerCommand(const std::string cmd); + ErrCode DebuggerCommand(const std::string cmd); + ErrCode CpuProfileCommand(const std::string cmd); + ErrCode RuntimeCommand(const std::string cmd); + ErrCode ExecHelpCommand(); + + uint32_t GetId() const + { + return id_; + } + + VecStr GetArgList() const + { + return argList_; + } + +private: + std::string cmd_; + VecStr argList_; + std::map> commandMap_; + std::string resultReceiver_ = ""; + HeapProfilerClient heapProfilerCli_; + // RuntimeClient runtimeCli_; + uint32_t id_ = 0; +}; +} // namespace OHOS::ArkCompiler::Toolchain + +#endif //ECMASCRIPT_TOOLING_CLIENT_TOOLCHAIN_CLI_COMMAND_H \ No newline at end of file diff --git a/tooling/client/toolchain_cli/main.cpp b/tooling/client/toolchain_cli/main.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc4d42aad7e0c4605937818c3b32462303479a43 --- /dev/null +++ b/tooling/client/toolchain_cli/main.cpp @@ -0,0 +1,114 @@ +/* + * 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 "cli_command.h" + +namespace OHOS::ArkCompiler::Toolchain{ +bool StrToUInt(const char *content, uint32_t *result) +{ + const int DEC = 10; + char *endPtr = nullptr; + *result = std::strtoul(content, &endPtr, DEC); + if (endPtr == content || *endPtr != '\0') { + return false; + } + return true; +} + +std::vector SplitString(const std::string &str, const std::string &delimiter) +{ + std::size_t strIndex = 0; + std::vector value; + std::size_t pos = str.find_first_of(delimiter, strIndex); + while ((pos < str.size()) && (pos > strIndex)) { + std::string subStr = str.substr(strIndex, pos - strIndex); + value.push_back(std::move(subStr)); + strIndex = pos; + strIndex = str.find_first_not_of(delimiter, strIndex); + pos = str.find_first_of(delimiter, strIndex); + } + if (pos > strIndex) { + std::string subStr = str.substr(strIndex, pos - strIndex); + if (!subStr.empty()) { + value.push_back(std::move(subStr)); + } + } + return value; +} + +int Main(const int argc, const char** argv) +{ + uint32_t port = 0; + OHOS::ArkCompiler::Toolchain::ToolchainWebsocket cliSocket; + if(argc < 2) { + LOGE("toolchain_cli is missing a parameter"); + return -1; + } + if(strstr(argv[0], "toolchain_cli") != nullptr) { + if(StrToUInt(argv[1], &port)) { + if((port <= 0) || (port >= 65535)) { + LOGE("toolchain_cli:InitToolchainWebSocketForPort the port = %{public}d is wrong.", port); + return -1; + } + if(!cliSocket.InitToolchainWebSocketForPort(port, 5)) { + LOGE("toolchain_cli:InitToolchainWebSocketForPort failed"); + return -1; + } + } else { + if(!cliSocket.InitToolchainWebSocketForSockName(argv[1])) { + LOGE("toolchain_cli:InitToolchainWebSocketForSockName failed"); + return -1; + } + } + + if (!cliSocket.ClientSendWSUpgradeReq()) { + LOGE("toolchain_cli:ClientSendWSUpgradeReq failed"); + return -1; + } + if (!cliSocket.ClientRecvWSUpgradeRsp()) { + LOGE("toolchain_cli:ClientRecvWSUpgradeRsp failed"); + return -1; + } + + std::cout << ">>> "; + std::string inputStr; + int cmdId = 0; + while(getline(std::cin, inputStr)) { + if((!strcmp(inputStr.c_str(), "quit"))||(!strcmp(inputStr.c_str(), "q"))) { + LOGE("toolchain_cli: quit"); + cliSocket.Close(); + break; + } + std::vector cliCmdStr = SplitString(inputStr, " "); + cmdId += 1; + OHOS::ArkCompiler::Toolchain::CliCommand cmd(&cliSocket, cliCmdStr, cmdId); + if(ERR_FAIL == cmd.ExecCommand()) { + cmdId -= 1; + } + std::cout << ">>> "; + } + } + return 0; +} +} //OHOS::ArkCompiler::Toolchain + +int main(int argc, const char **argv) +{ + return OHOS::ArkCompiler::Toolchain::Main(argc, argv); +} diff --git a/tooling/client/websocket/websocket_client.cpp b/tooling/client/websocket/websocket_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cfc3c564f76baed265340463e3bd5a9bc26c4f10 --- /dev/null +++ b/tooling/client/websocket/websocket_client.cpp @@ -0,0 +1,428 @@ +/* + * 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 +#include + +#include "log_wrapper.h" +#include "websocket_client.h" + + +namespace OHOS::ArkCompiler::Toolchain { +bool ToolchainWebsocket::InitToolchainWebSocketForPort (int port, uint32_t timeoutLimit) +{ + if (socketState_ != ToolchainSocketState::UNINITED) { + LOGE("InitToolchainWebSocketForPort::client has inited."); + return true; + } + + client_ = socket(AF_INET, SOCK_STREAM, 0); + if (client_ < SOCKET_SUCCESS) { + LOGE("InitToolchainWebSocketForPort::client socket failed, error = %{public}d , desc = %{public}s", + errno, strerror(errno)); + return false; + } + + // set send and recv timeout limit + if (!SetWebSocketTimeOut(client_, timeoutLimit)) { + LOGE("InitToolchainWebSocketForPort::client SetWebSocketTimeOut failed, error = %{public}d , desc = %{public}s", + errno, strerror(errno)); + close(client_); + client_ = -1; + return false; + } + + sockaddr_in clientAddr; + if (memset_s(&clientAddr, sizeof(clientAddr), 0, sizeof(clientAddr)) != EOK) { + LOGE("InitToolchainWebSocketForPort::client memset_s clientAddr failed, error = %{public}d, desc = %{public}s", + errno, strerror(errno)); + close(client_); + client_ = -1; + return false; + } + clientAddr.sin_family = AF_INET; + clientAddr.sin_port = htons(port); + if (int ret = inet_pton(AF_INET, "127.0.0.1", &clientAddr.sin_addr) < NET_SUCCESS) { + LOGE("InitToolchainWebSocketForPort::client inet_pton failed, error = %{public}d, desc = %{public}s", + errno, strerror(errno)); + close(client_); + client_ = -1; + return false; + } + + int ret = connect(client_, reinterpret_cast(&clientAddr), sizeof(clientAddr)); + if (ret != SOCKET_SUCCESS) { + LOGE("InitToolchainWebSocketForPort::client connect failed, error = %{public}d, desc = %{public}s", + errno, strerror(errno)); + close(client_); + client_ = -1; + return false; + } + socketState_ = ToolchainSocketState::INITED; + LOGE("InitToolchainWebSocketForPort::client connect success."); + return true; +} + +bool ToolchainWebsocket::InitToolchainWebSocketForSockName(const std::string &sockName, uint32_t timeoutLimit) +{ + if (socketState_ != ToolchainSocketState::UNINITED) { + LOGE("InitToolchainWebSocketForSockName::client has inited."); + return true; + } + + client_ = socket(AF_UNIX, SOCK_STREAM, 0); + if (client_ < SOCKET_SUCCESS) { + LOGE("InitToolchainWebSocketForSockName::client socket failed, error = %{public}d , desc = %{public}s", + errno, strerror(errno)); + return false; + } + + // set send and recv timeout limit + if (!SetWebSocketTimeOut(client_, timeoutLimit)) { + LOGE("InitToolchainWebSocketForSockName::client SetWebSocketTimeOut failed, error = %{public}d ,\ + desc = %{public}s", errno, strerror(errno)); + close(client_); + client_ = -1; + return false; + } + + struct sockaddr_un serverAddr; + if (memset_s(&serverAddr, sizeof(serverAddr), 0, sizeof(serverAddr)) != EOK) { + LOGE("InitToolchainWebSocketForSockName::client memset_s clientAddr failed, error = %{public}d,\ + desc = %{public}s", errno, strerror(errno)); + close(client_); + client_ = -1; + return false; + } + serverAddr.sun_family = AF_UNIX; + if (strcpy_s(serverAddr.sun_path + 1, sizeof(serverAddr.sun_path) - 1, sockName.c_str()) != EOK) { + LOGE("InitToolchainWebSocketForSockName::client strcpy_s serverAddr.sun_path failed, error = %{public}d,\ + desc = %{public}s", errno, strerror(errno)); + close(client_); + client_ = -1; + return false; + } + serverAddr.sun_path[0] = '\0'; + + uint32_t len = offsetof(struct sockaddr_un, sun_path) + strlen(sockName.c_str()) + 1; + int ret = connect(client_, reinterpret_cast(&serverAddr), static_cast(len)); + if (ret != SOCKET_SUCCESS) { + LOGE("InitToolchainWebSocketForSockName::client connect failed, error = %{public}d, desc = %{public}s", + errno, strerror(errno)); + close(client_); + client_ = -1; + return false; + } + socketState_ = ToolchainSocketState::INITED; + LOGE("InitToolchainWebSocketForSockName::client connect success."); + return true; +} + +bool ToolchainWebsocket::ClientSendWSUpgradeReq() +{ + if (socketState_ == ToolchainSocketState::UNINITED) { + LOGE("ClientSendWSUpgradeReq::client has not inited."); + return false; + } + if (socketState_ == ToolchainSocketState::CONNECTED) { + LOGE("ClientSendWSUpgradeReq::client has connected."); + return true; + } + + int msgLen = strlen(CLIENT_WEBSOCKET_UPGRADE_REQ); + int32_t sendLen = send(client_, CLIENT_WEBSOCKET_UPGRADE_REQ, msgLen, 0); + if (sendLen != msgLen) { + LOGE("ClientSendWSUpgradeReq::client send wsupgrade req failed, error = %{public}d, desc = %{public}sn", + errno, strerror(errno)); + socketState_ = ToolchainSocketState::UNINITED; + close(client_); + client_ = -1; + return false; + } + LOGE("ClientSendWSUpgradeReq::client send wsupgrade req success."); + return true; +} + +bool ToolchainWebsocket::ClientRecvWSUpgradeRsp() +{ + if (socketState_ == ToolchainSocketState::UNINITED) { + LOGE("ClientRecvWSUpgradeRsp::client has not inited."); + return false; + } + if (socketState_ == ToolchainSocketState::CONNECTED) { + LOGE("ClientRecvWSUpgradeRsp::client has connected."); + return true; + } + + char recvBuf[CLIENT_WEBSOCKET_UPGRADE_RSP_LEN + 1] = {0}; + int32_t bufLen = recv(client_, recvBuf, CLIENT_WEBSOCKET_UPGRADE_RSP_LEN, 0); + if (bufLen != CLIENT_WEBSOCKET_UPGRADE_RSP_LEN) { + LOGE("ClientRecvWSUpgradeRsp::client recv wsupgrade rsp failed, error = %{public}d, desc = %{public}sn", + errno, strerror(errno)); + socketState_ = ToolchainSocketState::UNINITED; + close(client_); + client_ = -1; + return false; + } + socketState_ = ToolchainSocketState::CONNECTED; + LOGE("ClientRecvWSUpgradeRsp::client recv wsupgrade rsp success."); + return true; +} + +bool ToolchainWebsocket::ClientSendReq(const std::string &message) +{ + if (socketState_ != ToolchainSocketState::CONNECTED) { + LOGE("ClientSendReq::client has not connected."); + return false; + } + + uint32_t msgLen = message.length(); + std::unique_ptr msgBuf = std::make_unique(msgLen + 15); // 15: the maximum expand length + char *sendBuf = msgBuf.get(); + uint32_t sendMsgLen = 0; + sendBuf[0] = 0x81; // 0x81: the text message sent by the server should start with '0x81'. + uint32_t mask = 1; + // Depending on the length of the messages, client will use shift operation to get the res + // and store them in the buffer. + if (msgLen <= 125) { // 125: situation 1 when message's length <= 125 + sendBuf[1] = msgLen | (mask << 7); // 7: mask need shift left by 7 bits + sendMsgLen = 2; // 2: the length of header frame is 2; + } else if (msgLen < 65536) { // 65536: message's length + sendBuf[1] = 126 | (mask << 7); // 126: payloadLen according to the spec; 7: mask shift left by 7 bits + sendBuf[2] = ((msgLen >> 8) & 0xff); // 8: shift right by 8 bits => res * (256^1) + sendBuf[3] = (msgLen & 0xff); // 3: store len's data => res * (256^0) + sendMsgLen = 4; // 4: the length of header frame is 4 + } else { + sendBuf[1] = 127 | (mask << 7); // 127: payloadLen according to the spec; 7: mask shift left by 7 bits + for (int32_t i = 2; i <= 5; i++) { // 2 ~ 5: unused bits + sendBuf[i] = 0; + } + sendBuf[6] = ((msgLen & 0xff000000) >> 24); // 6: shift 24 bits => res * (256^3) + sendBuf[7] = ((msgLen & 0x00ff0000) >> 16); // 7: shift 16 bits => res * (256^2) + sendBuf[8] = ((msgLen & 0x0000ff00) >> 8); // 8: shift 8 bits => res * (256^1) + sendBuf[9] = (msgLen & 0x000000ff); // 9: res * (256^0) + sendMsgLen = 10; // 10: the length of header frame is 10 + } + + if (memcpy_s(sendBuf + sendMsgLen, SOCKET_MASK_LEN, MASK_KEY, SOCKET_MASK_LEN) != EOK) { + LOGE("ClientSendReq::client memcpy_s MASK_KEY failed, error = %{public}d, desc = %{public}s", + errno, strerror(errno)); + return false; + } + sendMsgLen += SOCKET_MASK_LEN; + + std::string maskMessage; + for (uint64_t i = 0; i < msgLen; i++) { + uint64_t j = i % SOCKET_MASK_LEN; + maskMessage.push_back(message[i] ^ MASK_KEY[j]); + } + if (memcpy_s(sendBuf + sendMsgLen, msgLen, maskMessage.c_str(), msgLen) != EOK) { + LOGE("ClientSendReq::client memcpy_s maskMessage failed, error = %{public}d, desc = %{public}s", + errno, strerror(errno)); + return false; + } + msgBuf[sendMsgLen + msgLen] = '\0'; + + if (send(client_, sendBuf, sendMsgLen + msgLen, 0) != static_cast(sendMsgLen + msgLen)) { + LOGE("ClientSendReq::client send msg req failed, error = %{public}d, desc = %{public}s", + errno, strerror(errno)); + return false; + } + LOGE("ClientRecvWSUpgradeRsp::client send msg req success."); + return true; +} + +std::string ToolchainWebsocket::Decode() +{ + if (socketState_ != ToolchainSocketState::CONNECTED) { + LOGE("ToolchainWebsocket:Decode failed, websocket not connected!"); + return ""; + } + char recvbuf[SOCKET_HEADER_LEN + 1]; + if (!Recv(client_, recvbuf, SOCKET_HEADER_LEN, 0)) { + LOGE("ToolchainWebsocket:Decode failed, client websocket disconnect"); + socketState_ = ToolchainSocketState::INITED; + close(client_); + client_ = -1; + return ""; + } + ToolchainWebSocketFrame wsFrame; + int32_t index = 0; + wsFrame.fin = static_cast(recvbuf[index] >> 7); // 7: shift right by 7 bits to get the fin + wsFrame.opcode = static_cast(recvbuf[index] & 0xf); + if (wsFrame.opcode == 0x1) { // 0x1: 0x1 means a text frame + index++; + wsFrame.mask = static_cast((recvbuf[index] >> 7) & 0x1); // 7: to get the mask + wsFrame.payloadLen = recvbuf[index] & 0x7f; + if (HandleFrame(wsFrame)) { + return wsFrame.payload.get(); + } + return ""; + } else if (wsFrame.opcode == 0x9) { // 0x9: 0x9 means a ping frame + // send pong frame + char pongFrame[SOCKET_HEADER_LEN] = {0}; + pongFrame[0] = 0x8a; // 0x8a: 0x8a means a pong frame + pongFrame[1] = 0x0; + if (!Send(client_, pongFrame, SOCKET_HEADER_LEN, 0)) { + LOGE("ToolchainWebsocket Decode: Send pong frame failed"); + return ""; + } + } + return ""; +} + +bool ToolchainWebsocket::HandleFrame(ToolchainWebSocketFrame& wsFrame) +{ + if (wsFrame.payloadLen == 126) { // 126: the payloadLen read from frame + char recvbuf[PAYLOAD_LEN + 1] = {0}; + if (!Recv(client_, recvbuf, PAYLOAD_LEN, 0)) { + LOGE("ToolchainWebsocket HandleFrame: Recv payloadLen == 126 failed"); + return false; + } + + uint16_t msgLen = 0; + if (memcpy_s(&msgLen, sizeof(recvbuf), recvbuf, sizeof(recvbuf) - 1) != EOK) { + LOGE("ToolchainWebsocket HandleFrame: memcpy_s failed"); + return false; + } + wsFrame.payloadLen = ntohs(msgLen); + } else if (wsFrame.payloadLen > 126) { // 126: the payloadLen read from frame + char recvbuf[EXTEND_PAYLOAD_LEN + 1] = {0}; + if (!Recv(client_, recvbuf, EXTEND_PAYLOAD_LEN, 0)) { + LOGE("ToolchainWebsocket HandleFrame: Recv payloadLen > 127 failed"); + return false; + } + wsFrame.payloadLen = NetToHostLongLong(recvbuf, EXTEND_PAYLOAD_LEN); + } + return DecodeMessage(wsFrame); +} + +bool ToolchainWebsocket::DecodeMessage(ToolchainWebSocketFrame& wsFrame) +{ + if (wsFrame.payloadLen == 0 || wsFrame.payloadLen > UINT64_MAX) { + LOGE("ToolchainWebsocket:ReadMsg length error, expected greater than zero and less than UINT64_MAX"); + return false; + } + uint64_t msgLen = wsFrame.payloadLen; + wsFrame.payload = std::make_unique(msgLen + 1); + if (wsFrame.mask == 1) { + char buf[msgLen + 1]; + if (!Recv(client_, wsFrame.maskingkey, SOCKET_MASK_LEN, 0)) { + LOGE("ToolchainWebsocket DecodeMessage: Recv maskingkey failed"); + return false; + } + + if (!Recv(client_, buf, msgLen, 0)) { + LOGE("ToolchainWebsocket DecodeMessage: Recv message with mask failed"); + return false; + } + + for (uint64_t i = 0; i < msgLen; i++) { + uint64_t j = i % SOCKET_MASK_LEN; + wsFrame.payload.get()[i] = buf[i] ^ wsFrame.maskingkey[j]; + } + } else { + char buf[msgLen + 1]; + if (!Recv(client_, buf, msgLen, 0)) { + LOGE("ToolchainWebsocket DecodeMessage: Recv message without mask failed"); + return false; + } + + if (memcpy_s(wsFrame.payload.get(), msgLen, buf, msgLen) != EOK) { + LOGE("ToolchainWebsocket DecodeMessage: memcpy_s failed"); + return false; + } + } + wsFrame.payload.get()[msgLen] = '\0'; + return true; +} + +uint64_t ToolchainWebsocket::NetToHostLongLong(char* buf, uint32_t len) +{ + uint64_t result = 0; + for (uint32_t i = 0; i < len; i++) { + result |= static_cast(buf[i]); + if ((i + 1) < len) { + result <<= 8; // 8: result need shift left 8 bits in order to big endian convert to int + } + } + return result; +} + +bool ToolchainWebsocket::Send(int32_t fd, const char* buf, size_t totalLen, int32_t flags) const +{ + size_t sendLen = 0; + while (sendLen < totalLen) { + ssize_t len = send(fd, buf + sendLen, totalLen - sendLen, flags); + if (len <= 0) { + LOGE("ToolchainWebsocket Send Message in while failed, ToolchainWebsocket disconnect"); + return false; + } + sendLen += static_cast(len); + } + return true; +} + +bool ToolchainWebsocket::Recv(int32_t fd, char* buf, size_t totalLen, int32_t flags) const +{ + size_t recvLen = 0; + while (recvLen < totalLen) { + ssize_t len = recv(fd, buf + recvLen, totalLen - recvLen, flags); + if (len <= 0) { + LOGE("ToolchainWebsocket Recv payload in while failed, ToolchainWebsocket disconnect"); + return false; + } + recvLen += static_cast(len); + } + buf[totalLen] = '\0'; + return true; +} + +void ToolchainWebsocket::Close() +{ + if (socketState_ == ToolchainSocketState::UNINITED) { + return; + } + socketState_ = ToolchainSocketState::UNINITED; + close(client_); + client_ = -1; +} + +bool ToolchainWebsocket::SetWebSocketTimeOut(int32_t fd, uint32_t timeoutLimit) +{ + if (timeoutLimit > 0) { + struct timeval timeout = {timeoutLimit, 0}; + if (setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, + reinterpret_cast(&timeout), sizeof(timeout)) != SOCKET_SUCCESS) { + LOGE("ToolchainWebsocket:SetWebSocketTimeOut setsockopt SO_SNDTIMEO failed"); + return false; + } + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&timeout), sizeof(timeout)) != SOCKET_SUCCESS) { + LOGE("ToolchainWebsocket:SetWebSocketTimeOut setsockopt SO_RCVTIMEO failed"); + return false; + } + } + return true; +} + +bool ToolchainWebsocket::IsConnected() +{ + return socketState_ == ToolchainSocketState::CONNECTED; +} +} // namespace OHOS::ArkCompiler::Toolchain \ No newline at end of file diff --git a/tooling/client/websocket/websocket_client.h b/tooling/client/websocket/websocket_client.h new file mode 100644 index 0000000000000000000000000000000000000000..5c8840099d9b25fc70e919342a94d6d5993f7e6b --- /dev/null +++ b/tooling/client/websocket/websocket_client.h @@ -0,0 +1,80 @@ +/* + * 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. + */ + +#ifndef ECMASCRIPT_TOOLING_CLIENT_WEBSOCKET_CLIENT_H +#define ECMASCRIPT_TOOLING_CLIENT_WEBSOCKET_CLIENT_H + +#include +#include +#include + +#include "websocket.h" + +namespace OHOS::ArkCompiler::Toolchain { +struct ToolchainWebSocketFrame { + uint8_t fin = 0; + uint8_t opcode = 0; + uint8_t mask = 0; + uint64_t payloadLen = 0; + char maskingkey[5] = {0}; + std::unique_ptr payload = nullptr; +}; +class ToolchainWebsocket : public WebSocket { +public: + enum ToolchainSocketState : uint8_t { + UNINITED, + INITED, + CONNECTED, + }; + ToolchainWebsocket() = default; + ~ToolchainWebsocket() = default; + bool InitToolchainWebSocketForPort(int port, uint32_t timeoutLimit = 5); + bool InitToolchainWebSocketForSockName(const std::string &sockName, uint32_t timeoutLimit = 0); + bool ClientSendWSUpgradeReq(); + bool ClientRecvWSUpgradeRsp(); + bool ClientSendReq(const std::string &message); + std::string Decode(); + bool HandleFrame(ToolchainWebSocketFrame& wsFrame); + bool DecodeMessage(ToolchainWebSocketFrame& wsFrame); + uint64_t NetToHostLongLong(char* buf, uint32_t len); + bool Recv(int32_t fd, char* buf, size_t totalLen, int32_t flags) const; + bool Send(int32_t fd, const char* buf, size_t totalLen, int32_t flags) const; + void Close(); + bool SetWebSocketTimeOut(int32_t fd, uint32_t timeoutLimit); + bool IsConnected(); +private: + int32_t client_ {-1}; + std::atomic socketState_ {ToolchainSocketState::UNINITED}; + static constexpr int32_t CLIENT_WEBSOCKET_UPGRADE_RSP_LEN = 129; + static constexpr char CLIENT_WEBSOCKET_UPGRADE_REQ[] = "GET / HTTP/1.1\r\n" + "Connection: Upgrade\r\n" + "Pragma: no-cache\r\n" + "Cache-Control: no-cache\r\n" + "Upgrade: websocket\r\n" + "Sec-WebSocket-Version: 13\r\n" + "Accept-Encoding: gzip, deflate, br\r\n" + "Sec-WebSocket-Key: 64b4B+s5JDlgkdg7NekJ+g==\r\n" + "Sec-WebSocket-Extensions: permessage-deflate\r\n"; + static constexpr int32_t SOCKET_SUCCESS = 0; + static constexpr int NET_SUCCESS = 1; + static constexpr int32_t SOCKET_MASK_LEN = 4; + static constexpr int32_t SOCKET_HEADER_LEN = 2; + static constexpr int32_t PAYLOAD_LEN = 2; + static constexpr int32_t EXTEND_PAYLOAD_LEN = 8; + static constexpr char MASK_KEY[SOCKET_MASK_LEN + 1] = "abcd"; +}; +} // namespace OHOS::ArkCompiler::Toolchain + +#endif \ No newline at end of file