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..c639bd1f5ab853a8660dfc000ad1b173cf8e8951 --- /dev/null +++ b/tooling/client/BUILD.gn @@ -0,0 +1,101 @@ +# 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", + "domain/profiler_client.cpp", + "domain/debugger_client.cpp", + "domain/runtime_client.cpp", + "manager/domain_manager.cpp", + "manager/variable_manager.cpp" + ] + + deps += [ + "$toolchain_root/websocket:websocket", + "$toolchain_root/tooling:libark_ecma_debugger_set", + "$ark_third_party_root/openssl:libcrypto_shared", + "//third_party/libuv:uv", + sdk_libc_secshared_dep, + ] + + external_deps += [ + "ets_runtime:libark_jsruntime", + ] + + 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..642109d1514318e2457feadd8cb76655d39b05b7 --- /dev/null +++ b/tooling/client/domain/debugger_client.cpp @@ -0,0 +1,221 @@ +/* + * 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 DebuggerClient::DispatcherCmd(int id, const std::string &cmd, std::string* reqStr) +{ + std::map> dispatcherTable { + { "break", std::bind(&DebuggerClient::BreakCommand, this, id)}, + { "backtrack", std::bind(&DebuggerClient::BacktrackCommand, this, id)}, + { "continue", std::bind(&DebuggerClient::ContinueCommand, this, id)}, + { "delete", std::bind(&DebuggerClient::DeleteCommand, this, id)}, + { "jump", std::bind(&DebuggerClient::JumpCommand, this, id)}, + { "disable", std::bind(&DebuggerClient::DisableCommand, this, id)}, + { "display", std::bind(&DebuggerClient::DisplayCommand, this, id)}, + { "enable", std::bind(&DebuggerClient::EnableCommand, this, id)}, + { "finish", std::bind(&DebuggerClient::FinishCommand, this, id)}, + { "frame", std::bind(&DebuggerClient::FrameCommand, this, id)}, + { "ignore", std::bind(&DebuggerClient::IgnoreCommand, this, id)}, + { "infobreakpoints", std::bind(&DebuggerClient::InfobreakpointsCommand, this, id)}, + { "infosource", std::bind(&DebuggerClient::InfosourceCommand, this, id)}, + { "list", std::bind(&DebuggerClient::NextCommand, this, id)}, + { "next", std::bind(&DebuggerClient::ListCommand, this, id)}, + { "print", std::bind(&DebuggerClient::PrintCommand, this, id)}, + { "ptype", std::bind(&DebuggerClient::PtypeCommand, this, id)}, + { "run", std::bind(&DebuggerClient::RunCommand, this, id)}, + { "setvar", std::bind(&DebuggerClient::SetvarCommand, this, id)}, + { "step", std::bind(&DebuggerClient::StepCommand, this, id)}, + { "undisplay", std::bind(&DebuggerClient::UndisplayCommand, this, id)}, + { "watch", std::bind(&DebuggerClient::WatchCommand, this, id)}, + { "resume", std::bind(&DebuggerClient::ResumeCommand, this, id)} + }; + + auto entry = dispatcherTable.find(cmd); + if (entry != dispatcherTable.end()) { + *reqStr = entry->second(); + LOGE("DebuggerClient DispatcherCmd reqStr1: %{public}s", reqStr->c_str()); + return true; + } else { + *reqStr = "Unknown commond: " + cmd; + LOGE("DebuggerClient DispatcherCmd reqStr2: %{public}s", reqStr->c_str()); + return false; + } +} + +std::string DebuggerClient::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", std::stoi(breakPointInfoList_.back().lineNumber)); + params->Add("url", breakPointInfoList_.back().url.c_str()); + request->Add("params", params); + return request->Stringify(); +} + +std::string DebuggerClient::BacktrackCommand([[maybe_unused]] int id) +{ + return "backtrack"; +} + +std::string DebuggerClient::ContinueCommand([[maybe_unused]] int id) +{ + return "continue"; +} + +std::string DebuggerClient::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 DebuggerClient::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 DebuggerClient::DisplayCommand([[maybe_unused]] int id) +{ + return "display"; +} + +std::string DebuggerClient::EnableCommand(int id) +{ + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Debugger.enable"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string DebuggerClient::FinishCommand([[maybe_unused]] int id) +{ + return "finish"; +} + +std::string DebuggerClient::FrameCommand([[maybe_unused]] int id) +{ + return "frame"; +} + +std::string DebuggerClient::IgnoreCommand([[maybe_unused]] int id) +{ + return "ignore"; +} + +std::string DebuggerClient::InfobreakpointsCommand([[maybe_unused]] int id) +{ + return "infobreakpoint"; +} + +std::string DebuggerClient::InfosourceCommand([[maybe_unused]] int id) +{ + return "infosource"; +} + +std::string DebuggerClient::JumpCommand([[maybe_unused]] int id) +{ + return "jump"; +} + +std::string DebuggerClient::NextCommand([[maybe_unused]] int id) +{ + return "next"; +} + +std::string DebuggerClient::ListCommand([[maybe_unused]] int id) +{ + return "list"; +} + +std::string DebuggerClient::PrintCommand([[maybe_unused]] int id) +{ + return "print"; +} + +std::string DebuggerClient::PtypeCommand([[maybe_unused]] int id) +{ + return "ptype"; +} + +std::string DebuggerClient::RunCommand([[maybe_unused]] int id) +{ + return "run"; +} + +std::string DebuggerClient::SetvarCommand([[maybe_unused]] int id) +{ + return "Debugger.setVariableValue"; +} + +std::string DebuggerClient::StepCommand([[maybe_unused]] int id) +{ + return "step"; +} + +std::string DebuggerClient::UndisplayCommand([[maybe_unused]] int id) +{ + return "undisplay"; +} + +std::string DebuggerClient::WatchCommand([[maybe_unused]] int id) +{ + return "Debugger.evaluateOnCallFrame"; +} + +std::string DebuggerClient::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 DebuggerClient::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..2ef0c3a116a6c790a649476ce7a69ee6dbda1408 --- /dev/null +++ b/tooling/client/domain/debugger_client.h @@ -0,0 +1,63 @@ +/* + * 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 + +namespace OHOS::ArkCompiler::Toolchain { +struct BreakPointInfo { + std::string lineNumber; + std::string url; +}; +class DebuggerClient final { +public: + DebuggerClient() = default; + ~DebuggerClient() = 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..68308b90f36f30601bcc517288bfa287209aa66b --- /dev/null +++ b/tooling/client/domain/heapprofiler_client.cpp @@ -0,0 +1,252 @@ +/* + * 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 "log_wrapper.h" + +#include +#include +#include + +using Result = panda::ecmascript::tooling::Result; +namespace OHOS::ArkCompiler::Toolchain { +static constexpr int32_t SAMPLING_INTERVAL = 16384; +bool HeapProfilerClient::DispatcherCmd(int id, const std::string &cmd, const std::string &arg, std::string* reqStr) +{ + if (reqStr == nullptr) { + return false; + } + path_ = arg; + + 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) +{ + idEventMap_.emplace(id, HEAPDUMP); + 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) +{ + idEventMap_.emplace(id, ALLOCATION); + 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) +{ + idEventMap_.emplace(id, ALLOCATION_STOP); + 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) +{ + idEventMap_.emplace(id, ENABLE); + 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) +{ + idEventMap_.emplace(id, DISABLE); + 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(int id) +{ + idEventMap_.emplace(id, SAMPLING); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "HeapProfiler.startSampling"); + + std::unique_ptr params = PtJson::CreateObject(); + params->Add("samplingInterval", SAMPLING_INTERVAL); + request->Add("params", params); + return request->Stringify(); +} + +std::string HeapProfilerClient::SampingStop(int id) +{ + idEventMap_.emplace(id, SAMPLING_STOP); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "HeapProfiler.stopSampling"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string HeapProfilerClient::CollectGarbage(int id) +{ + idEventMap_.emplace(id, COLLECT_GARBAGE); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "HeapProfiler.collectGarbage"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +void HeapProfilerClient::RecvReply(std::unique_ptr json) +{ + if (json == nullptr) { + LOGE("json parse error"); + return; + } + + if (!json->IsObject()) { + LOGE("json parse format error"); + json->ReleaseRoot(); + return; + } + + Result ret; + std::unique_ptr params; + ret = json->GetObject("params", ¶ms); + if (ret != Result::SUCCESS) { + LOGE("find params error"); + return; + } + + 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; + } + method = wholeMethod.substr(indexPoint + 1, length); + if (method == "lastSeenObjectId") { + isAllocationMsg_ = true; + } + } + + std::string chunk; + ret = params->GetString("chunk", &chunk); + if (ret != Result::SUCCESS) { + LOGE("find chunk error"); + return; + } + + std::string head = "{\"snapshot\":\n"; + if (!strncmp(chunk.c_str(), head.c_str(), head.length())) { + time_t timep; + time(&timep); + char tmp1[16]; + char tmp2[16]; + size_t result = 0; + result = strftime(tmp1, sizeof(tmp1), "%Y%m%d", localtime(&timep)); + if (result == 0) { + LOGE("get time failed"); + return; + } + result = strftime(tmp2, sizeof(tmp2), "%H%M%S", localtime(&timep)); + if (result == 0) { + LOGE("get time failed"); + return; + } + if (isAllocationMsg_) { + fileName_ = "Heap-" + std::string(tmp1) + "T" + std::string(tmp2) + ".heaptimeline"; + } else { + fileName_ = "Heap-" + std::string(tmp1) + "T" + std::string(tmp2) + ".heapsnapshot"; + } + std::cout << "file name is " << fileName_ << std::endl; + std::cout << ">>> "; + fflush(stdout); + } + + std::string tail = "]\n}\n"; + std::string subStr = chunk.substr(chunk.length() - tail.length(), chunk.length()); + if (!strncmp(subStr.c_str(), tail.c_str(), tail.length())) { + isAllocationMsg_ = false; + } + WriteHeapProfilerForFile(fileName_, chunk); +} + +bool HeapProfilerClient::WriteHeapProfilerForFile(std::string fileName, std::string data) +{ + std::ofstream ofs; + std::string pathname = path_ + fileName; + ofs.open(pathname.c_str(), std::ios::app); + if (!ofs.is_open()) { + LOGE("toolchain_client: file open error!"); + return false; + } + int strSize = data.size(); + ofs.write(data.c_str(), strSize); + ofs.close(); + ofs.clear(); + return true; +} +} // 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..f0777d49b8ea5d5ca8afe54dd308218a5974e006 --- /dev/null +++ b/tooling/client/domain/heapprofiler_client.h @@ -0,0 +1,63 @@ +/* + * 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 +#include +#include + +#include "pt_json.h" + +using PtJson = panda::ecmascript::tooling::PtJson; + +namespace OHOS::ArkCompiler::Toolchain { +enum HeapProfilerEvent { + DAFAULT_VALUE = 0, + ALLOCATION = 1, + ALLOCATION_STOP, + HEAPDUMP, + ENABLE, + DISABLE, + SAMPLING, + SAMPLING_STOP, + COLLECT_GARBAGE +}; +class HeapProfilerClient final { +public: + HeapProfilerClient() = default; + ~HeapProfilerClient() = default; + + bool DispatcherCmd(int id, const std::string &cmd, const std::string &arg, 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); + void RecvReply(std::unique_ptr json); + bool WriteHeapProfilerForFile(std::string fileName, std::string data); + +private: + std::string fileName_; + std::map idEventMap_; + std::string path_; + bool isAllocationMsg_ {false}; +}; +} // 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..1d9cba95423a763681ef68610c6b68b98da4604c --- /dev/null +++ b/tooling/client/domain/profiler_client.cpp @@ -0,0 +1,182 @@ +/* + * 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/profiler_client.h" +#include "pt_types.h" +#include "log_wrapper.h" + +#include +#include +#include +#include + +using Result = panda::ecmascript::tooling::Result; +using Profile = panda::ecmascript::tooling::Profile; +namespace OHOS::ArkCompiler::Toolchain { +bool ProfilerClient::DispatcherCmd(int id, const std::string &cmd, std::string* reqStr) +{ + std::map> dispatcherTable { + { "cpuprofile", std::bind(&ProfilerClient::CpuprofileCommand, this, id)}, + { "cpuprofile-stop", std::bind(&ProfilerClient::CpuprofileStopCommand, this, id)}, + { "cpuprofile-setSamplingInterval", std::bind(&ProfilerClient::SetSamplingIntervalCommand, this, id)}, + { "cpuprofile-enable", std::bind(&ProfilerClient::CpuprofileEnableCommand, this, id)}, + { "cpuprofile-disable", std::bind(&ProfilerClient::CpuprofileDisableCommand, this, id)}, + }; + + auto entry = dispatcherTable.find(cmd); + if (entry == dispatcherTable.end()) { + *reqStr = "Unknown commond: " + cmd; + LOGE("DispatcherCmd reqStr2: %{public}s", reqStr->c_str()); + return false; + } + *reqStr = entry->second(); + LOGE("DispatcherCmd reqStr1: %{public}s", reqStr->c_str()); + return true; +} + +std::string ProfilerClient::CpuprofileEnableCommand(int id) +{ + idEventMap_.emplace(id, "cpuprofileenable"); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Profiler.enable"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string ProfilerClient::CpuprofileDisableCommand(int id) +{ + idEventMap_.emplace(id, "cpuprofiledisable"); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Profiler.disable"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string ProfilerClient::CpuprofileCommand(int id) +{ + idEventMap_.emplace(id, "cpuprofile"); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Profiler.start"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string ProfilerClient::CpuprofileStopCommand(int id) +{ + idEventMap_.emplace(id, "cpuprofilestop"); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Profiler.stop"); + + std::unique_ptr params = PtJson::CreateObject(); + request->Add("params", params); + return request->Stringify(); +} + +std::string ProfilerClient::SetSamplingIntervalCommand(int id) +{ + idEventMap_.emplace(id, "setsamplinginterval"); + std::unique_ptr request = PtJson::CreateObject(); + request->Add("id", id); + request->Add("method", "Profiler.setSamplingInterval"); + + std::unique_ptr params = PtJson::CreateObject(); + params->Add("interval", interval_); + request->Add("params", params); + return request->Stringify(); +} + +void ProfilerClient::RecvProfilerResult(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 profile; + ret = result->GetObject("profile", &profile); + if (ret != Result::SUCCESS) { + LOGE("toolchain_client: the cmd is not cp-stop!"); + return; + } + ProfilerSingleton& pro = ProfilerSingleton::getInstance(); + time_t timep; + time(&timep); + char tmp1[16]; + char tmp2[16]; + size_t timeResult = 0; + timeResult = strftime(tmp1, sizeof(tmp1), "%Y%m%d", localtime(&timep)); + if (timeResult == 0) { + LOGE("get time failed"); + return; + } + timeResult = strftime(tmp2, sizeof(tmp2), "%H%M%S", localtime(&timep)); + if (timeResult == 0) { + LOGE("get time failed"); + return; + } + std::string fileName = "CPU-" + std::string(tmp1) + "T" + std::string(tmp2) + ".cpuprofile"; + std::string cpufile = pro.GetAddress() + fileName; + std::cout << "toolchain_client: cpuprofile file name is " << cpufile << std::endl; + std::cout << ">>> "; + fflush(stdout); + WriteCpuProfileForFile(cpufile, profile->Stringify()); + pro.SaveCpuName(fileName); +} + +bool ProfilerClient::WriteCpuProfileForFile(const std::string &fileName, const std::string &data) +{ + std::ofstream ofs; + ofs.open(fileName.c_str(), std::ios::out); + if (!ofs.is_open()) { + LOGE("toolchain_client: file open error!"); + return false; + } + int strSize = data.size(); + ofs.write(data.c_str(), strSize); + ofs.close(); + ofs.clear(); + ProfilerSingleton& pro = ProfilerSingleton::getInstance(); + pro.SetAddress(""); + return true; +} + +void ProfilerClient::SetSamplingInterval(int interval) +{ + this->interval_ = interval; +} +} //OHOS::ArkCompiler::Toolchain diff --git a/tooling/client/domain/profiler_client.h b/tooling/client/domain/profiler_client.h new file mode 100644 index 0000000000000000000000000000000000000000..fe7c9ff540f2fd4ff9adceac0f6d20df22a255b9 --- /dev/null +++ b/tooling/client/domain/profiler_client.h @@ -0,0 +1,91 @@ +/* + * 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_PROFILER_CLIENT_H +#define ECMASCRIPT_TOOLING_CLIENT_DOMAIN_PROFILER_CLIENT_H + +#include +#include + +#include + +#include "pt_json.h" + +namespace OHOS::ArkCompiler::Toolchain { +using PtJson = panda::ecmascript::tooling::PtJson; + +class ProfilerSingleton { +public: + ProfilerSingleton(const ProfilerSingleton&) = delete; + ProfilerSingleton& operator=(const ProfilerSingleton&) = delete; + + static ProfilerSingleton& getInstance() + { + static ProfilerSingleton instance; + return instance; + } + + std::vector SaveCpuName(const std::string &data) + { + cpulist_.emplace_back(data); + return cpulist_; + } + + void ShowCpuFile() + { + size_t size = cpulist_.size(); + for (size_t i = 0; i < size; i++) { + std::cout << cpulist_[i] << std::endl; + } + } + + void SetAddress(std::string address) + { + address_ = address; + } + + std::string GetAddress() + { + return address_; + } + +private: + std::vector cpulist_; + std::string address_ = ""; + ProfilerSingleton() {} + ~ProfilerSingleton() {} +}; + +class ProfilerClient final { +public: + ProfilerClient() = default; + ~ProfilerClient() = default; + + bool DispatcherCmd(int id, const std::string &cmd, std::string* reqStr); + std::string CpuprofileCommand(int id); + std::string CpuprofileStopCommand(int id); + std::string SetSamplingIntervalCommand(int id); + std::string CpuprofileEnableCommand(int id); + std::string CpuprofileDisableCommand(int id); + bool WriteCpuProfileForFile(const std::string &fileName, const std::string &data); + void RecvProfilerResult(std::unique_ptr json); + void SetSamplingInterval(int interval); + +private: + int32_t interval_ = 0; + std::map idEventMap_ {}; +}; +} // OHOS::ArkCompiler::Toolchain +#endif diff --git a/tooling/client/domain/runtime_client.cpp b/tooling/client/domain/runtime_client.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b29e090e3aa9ed69c24dbde93f9ca8507065df73 --- /dev/null +++ b/tooling/client/domain/runtime_client.cpp @@ -0,0 +1,137 @@ +/* + * 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()) { + *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..d9da400d4d57e59ecde99b27b6f4bf62e9586b87 --- /dev/null +++ b/tooling/client/domain/runtime_client.h @@ -0,0 +1,61 @@ +/* + * 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/domain_manager.cpp b/tooling/client/manager/domain_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..24c3f7de9b3e01dd710d07c81b58dcd49724129a --- /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 "log_wrapper.h" +#include "pt_json.h" +#include "manager/variable_manager.h" +#include "domain/runtime_client.h" +#include "domain_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; + 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") { + profilerClient_.RecvProfilerResult(std::move(json)); + } else if (domain == "Runtime") { + RuntimeClient &runtimeClient = RuntimeClient::getInstance(); + if (id == static_cast(runtimeClient.GetIdByMethod("getProperties"))) { + VariableManager &variableManager = VariableManager::getInstance(); + variableManager.HandleMessage(std::move(json)); + variableManager.ShowVariableInfos(); + } else { + LOGI("Runtime 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..c29e2816cb8b2346abcd7d7b1543acac2420dcc8 --- /dev/null +++ b/tooling/client/manager/domain_manager.h @@ -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. + */ + +#ifndef ECMASCRIPT_TOOLING_CLIENT_MANAGER_DOMAIN_MANAGER_H +#define ECMASCRIPT_TOOLING_CLIENT_MANAGER_DOMAIN_MANAGER_H + +#include + +#include "domain/heapprofiler_client.h" +#include "domain/profiler_client.h" + +namespace OHOS::ArkCompiler::Toolchain { +class DomainManager { +public: + DomainManager() = default; + ~DomainManager() = default; + + void DispatcherReply(char* msg); + + 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); + } + } + + HeapProfilerClient* GetHeapProfilerClient() + { + return &heapProfilerClient_; + } + + ProfilerClient* GetProfilerClient() + { + return &profilerClient_; + } + +private: + HeapProfilerClient heapProfilerClient_ {}; + ProfilerClient profilerClient_ {}; + std::map idDomainMap_ {}; +}; +} // OHOS::ArkCompiler::Toolchain +#endif \ No newline at end of file diff --git a/tooling/client/manager/variable_manager.cpp b/tooling/client/manager/variable_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f43cf41789836a7b7499e5702eb2e2f1231f68a6 --- /dev/null +++ b/tooling/client/manager/variable_manager.cpp @@ -0,0 +1,81 @@ +/* + * 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 "log_wrapper.h" +#include "manager/variable_manager.h" + +using PtJson = panda::ecmascript::tooling::PtJson; +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 + 5, std::move(variableInfo)); // 5: index start at 5 + } +} + +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. CLOSURE" << std::endl; + std::cout << "3. MODULE" << std::endl; + std::cout << "3. GLOBAL" << std::endl; +} + +std::string VariableManager::FindObjectIdByIndex(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..a912c71278e09c3b5d37c6f88f8fb132db5befae --- /dev/null +++ b/tooling/client/manager/variable_manager.h @@ -0,0 +1,50 @@ +/* + * 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" +namespace OHOS::ArkCompiler::Toolchain { +using PtJson = panda::ecmascript::tooling::PtJson; +using Result = panda::ecmascript::tooling::Result; +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(const std::unique_ptr json); + + void ShowVariableInfos(); + + std::string FindObjectIdByIndex(const int32_t index); + +private: + VariableManager() = default; + ~VariableManager() = default; + 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..9a42fa561a63219ce02d14a796f4335af4d3739a --- /dev/null +++ b/tooling/client/toolchain_cli/BUILD.gn @@ -0,0 +1,113 @@ +# 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" ] + + if (is_linux) { + defines += [ + "PANDA_TARGET_UNIX", + "PANDA_TARGET_LINUX", + "PANDA_USE_FUTEX", + ] + } else if (is_mingw) { + cflags_cc += [ + "-std=c++17", + "-Wno-ignored-attributes", + ] + defines += [ + "PANDA_TARGET_WINDOWS", + "_CRTBLD", + "__LIBMSVCRT__", + ] + } else if (is_mac) { + defines += [ + "PANDA_TARGET_UNIX", + "PANDA_TARGET_MACOS", + ] + } else if (target_os == "android") { + defines += [ + "PANDA_TARGET_ANDROID", + "PANDA_TARGET_UNIX", + "PANDA_USE_FUTEX", + ] + if (!ark_standalone_build) { + defines += [ "ENABLE_ANLOG" ] + } + } else if (target_os == "ios") { + defines += [ + "PANDA_TARGET_UNIX", + "PANDA_TARGET_IOS", + ] + } else { + defines += [ + "PANDA_TARGET_UNIX", + "PANDA_USE_FUTEX", + ] + if (!is_standard_system && (current_cpu != "arm" || is_wearable_product)) { + defines += [ "PANDA_TARGET_MOBILE" ] + } + } +} + +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/base", + "$toolchain_root/tooling/client", + "$toolchain_root/tooling/client/toolchain_cli", + "//third_party/cJSON", + "//third_party/libuv/include", + ] + + deps = [ + "$toolchain_root/tooling/client:libark_toolchain_client_set", + ] + + external_deps = [ + "ets_runtime:libark_jsruntime", + "hitrace:hitrace_meter", + ] + + 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..c5b6aafb84960c4b3c5914544aae852212eb6d98 --- /dev/null +++ b/tooling/client/toolchain_cli/cli_command.cpp @@ -0,0 +1,313 @@ +/* + * 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 "log_wrapper.h" +#include "manager/domain_manager.h" +#include "domain/debugger_client.h" +#include "manager/variable_manager.h" +#include "domain/runtime_client.h" +#include "cli_command.h" + +namespace OHOS::ArkCompiler::Toolchain { +DomainManager g_domainManager; +WebsocketClient g_cliSocket; +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" + " cpuprofile-show(cp-show) cpuprofile show\n" + " cpuprofile-setSamplingInterval(cp-ssi) cpuprofile setSamplingInterval\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", + "cpuprofile-show", + "cpuprofile-setSamplingInterval", + "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("cpuprofile-show", "cp-show"), + std::bind(&CliCommand::CpuProfileCommand, this, "cpuprofile-show")}, + {std::make_pair("cpuprofile-setSamplingInterval", "cp-ssi"), + std::bind(&CliCommand::CpuProfileCommand, this, "cpuprofile-setSamplingInterval")}, + {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::DebuggerCommand, 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; + HeapProfilerClient* heapProfilerClient = g_domainManager.GetHeapProfilerClient(); + VecStr argList = GetArgList(); + if (argList.empty()) { + argList.push_back("/data/"); + } + result = heapProfilerClient->DispatcherCmd(id_, cmd, argList[0], &request); + if (result) { + g_cliSocket.ClientSendReq(request); + if (g_domainManager.GetDomainById(id_).empty()) { + 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; + std::string request; + bool result = false; + ProfilerClient* profilerClient = g_domainManager.GetProfilerClient(); + ProfilerSingleton& pro = ProfilerSingleton::getInstance(); + if (cmd == "cpuprofile-show") { + pro.ShowCpuFile(); + return ERR_OK; + } + if (cmd == "cpuprofile-setSamplingInterval") { + profilerClient->SetSamplingInterval(std::atoi(GetArgList()[0].c_str())); + } + if (cmd == "cpuprofile-stop" && GetArgList().size() == 1) { + pro.SetAddress(GetArgList()[0]); + } + result = profilerClient->DispatcherCmd(id_, cmd, &request); + if (result) { + g_cliSocket.ClientSendReq(request); + if (g_domainManager.GetDomainById(id_) == "") { + g_domainManager.SetDomainById(id_, "Profiler"); + } + } else { + return ERR_FAIL; + } + 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_); + DebuggerClient debuggerCli; + if (GetArgList().size() == 2) { // 2: two parameters + 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(std::stoi(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..00d5111408d656945a30dbf4f4e6d8f863bf0968 --- /dev/null +++ b/tooling/client/toolchain_cli/cli_command.h @@ -0,0 +1,86 @@ +/* + * 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/profiler_client.h" +#include "manager/domain_manager.h" + +namespace OHOS::ArkCompiler::Toolchain { +using ErrCode = int; +using StrPair = std::pair; +using VecStr = std::vector; +extern DomainManager g_domainManager; +extern WebsocketClient g_cliSocket; + +enum ReturnCode { + 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() + { + return argList_; + } + +private: + std::string cmd_ ; + VecStr argList_ {}; + std::map> commandMap_; + std::string resultReceiver_ = ""; + HeapProfilerClient heapProfilerCli_; + 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..8f3d253d8c6ccde553e829848ebc7e5d0c6eb376 --- /dev/null +++ b/tooling/client/toolchain_cli/main.cpp @@ -0,0 +1,232 @@ +/* + * 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 + +#include "cli_command.h" + +namespace OHOS::ArkCompiler::Toolchain { +uint32_t g_messageId = 0; +uv_async_t* g_socketSignal; +uv_async_t* g_inputSignal; +uv_async_t* g_releaseHandle; +uv_loop_t* g_loop; + + +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; +} + +void ReleaseHandle(uv_async_t *handle) +{ + uv_close(reinterpret_cast(g_inputSignal), [](uv_handle_t* handle) { + if (handle != nullptr) { + delete reinterpret_cast(handle); + handle = nullptr; + } + }); + + uv_close(reinterpret_cast(g_socketSignal), [](uv_handle_t* handle) { + if (handle != nullptr) { + delete reinterpret_cast(handle); + handle = nullptr; + } + }); + + uv_close(reinterpret_cast(g_releaseHandle), [](uv_handle_t* handle) { + if (handle != nullptr) { + delete reinterpret_cast(handle); + handle = nullptr; + } + }); + + if (g_loop != nullptr) { + uv_stop(g_loop); + } +} + +void InputOnMessage(uv_async_t *handle) +{ + LOGE("InputOnMessage: beign"); + char* msg = static_cast(handle->data); + std::string inputStr = std::string(msg); + std::vector cliCmdStr = SplitString(inputStr, " "); + g_messageId += 1; + CliCommand cmd(cliCmdStr, g_messageId); + if (ERR_FAIL == cmd.ExecCommand()) { + g_messageId -= 1; + } + LOGE("InputOnMessage: cmd"); + std::cout << ">>> "; + fflush(stdout); + if (msg != nullptr) { + free(msg); + } + LOGE("InputOnMessage: end"); +} + +void GetInputCommand(void *arg) +{ + std::cout << ">>> "; + std::string inputStr; + while (getline(std::cin, inputStr)) { + if (inputStr.empty()) { + std::cout << ">>> "; + continue; + } + if ((!strcmp(inputStr.c_str(), "quit"))||(!strcmp(inputStr.c_str(), "q"))) { + LOGE("toolchain_cli: quit"); + g_cliSocket.Close(); + if (uv_is_active(reinterpret_cast(g_releaseHandle))) { + uv_async_send(g_releaseHandle); + } + break; + } + if (uv_is_active(reinterpret_cast(g_inputSignal))) { + uint32_t len = inputStr.length(); + LOGE("GetInputCommand: len is %{public}d", len); + char* msg = (char*)malloc(len + 1); + if ((msg != nullptr) && uv_is_active(reinterpret_cast(g_inputSignal))) { + strncpy_s(msg, len + 1, inputStr.c_str(), len); + LOGE("GetInputCommand: msg is %{public}s", msg); + g_inputSignal->data = std::move(msg); + uv_async_send(g_inputSignal); + LOGE("GetInputCommand: end"); + } + } + } +} + +void SocketOnMessage(uv_async_t *handle) +{ + char* msg = static_cast(handle->data); + g_domainManager.DispatcherReply(msg); + if (msg != nullptr) { + free(msg); + } +} + +void GetSocketMessage(void *arg) +{ + while (g_cliSocket.IsConnected()) { + std::string decMessage = g_cliSocket.Decode(); + uint32_t len = decMessage.length(); + if (len == 0) { + continue; + } + char* msg = (char*)malloc(len + 1); + if ((msg != nullptr) && uv_is_active(reinterpret_cast(g_socketSignal))) { + strncpy_s(msg, len + 1, decMessage.c_str(), len); + g_socketSignal->data = std::move(msg); + uv_async_send(g_socketSignal); + } + } +} + +int Main(const int argc, const char** argv) +{ + uint32_t port = 0; + + if (argc < 2) { // 2: two parameters + 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)) { // 65535: max port + LOGE("toolchain_cli:InitToolchainWebSocketForPort the port = %{public}d is wrong.", port); + return -1; + } + if (!g_cliSocket.InitToolchainWebSocketForPort(port, 5)) { // 5: five times + LOGE("toolchain_cli:InitToolchainWebSocketForPort failed"); + return -1; + } + } else { + if (!g_cliSocket.InitToolchainWebSocketForSockName(argv[1])) { + LOGE("toolchain_cli:InitToolchainWebSocketForSockName failed"); + return -1; + } + } + + if (!g_cliSocket.ClientSendWSUpgradeReq()) { + LOGE("toolchain_cli:ClientSendWSUpgradeReq failed"); + return -1; + } + if (!g_cliSocket.ClientRecvWSUpgradeRsp()) { + LOGE("toolchain_cli:ClientRecvWSUpgradeRsp failed"); + return -1; + } + + g_loop = uv_default_loop(); + + g_inputSignal = new uv_async_t; + uv_async_init(g_loop, g_inputSignal, reinterpret_cast(InputOnMessage)); + + g_socketSignal = new uv_async_t; + uv_async_init(g_loop, g_socketSignal, reinterpret_cast(SocketOnMessage)); + + g_releaseHandle = new uv_async_t; + uv_async_init(g_loop, g_releaseHandle, reinterpret_cast(ReleaseHandle)); + + uv_thread_t inputTid; + uv_thread_create(&inputTid, GetInputCommand, nullptr); + + uv_thread_t socketTid; + uv_thread_create(&socketTid, GetSocketMessage, nullptr); + + uv_run(g_loop, UV_RUN_DEFAULT); + } + 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..de9da7f908e203be8437e371fbad0318dc93754e --- /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 WebsocketClient::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 WebsocketClient::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 WebsocketClient::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 WebsocketClient::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 WebsocketClient::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 WebsocketClient::Decode() +{ + if (socketState_ != ToolchainSocketState::CONNECTED) { + LOGE("WebsocketClient:Decode failed, websocket not connected!"); + return ""; + } + char recvbuf[SOCKET_HEADER_LEN + 1]; + if (!Recv(client_, recvbuf, SOCKET_HEADER_LEN, 0)) { + LOGE("WebsocketClient: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("WebsocketClient Decode: Send pong frame failed"); + return ""; + } + } + return ""; +} + +bool WebsocketClient::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("WebsocketClient HandleFrame: Recv payloadLen == 126 failed"); + return false; + } + + uint16_t msgLen = 0; + if (memcpy_s(&msgLen, sizeof(recvbuf), recvbuf, sizeof(recvbuf) - 1) != EOK) { + LOGE("WebsocketClient 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("WebsocketClient HandleFrame: Recv payloadLen > 127 failed"); + return false; + } + wsFrame.payloadLen = NetToHostLongLong(recvbuf, EXTEND_PAYLOAD_LEN); + } + return DecodeMessage(wsFrame); +} + +bool WebsocketClient::DecodeMessage(ToolchainWebSocketFrame& wsFrame) +{ + if (wsFrame.payloadLen == 0 || wsFrame.payloadLen > UINT64_MAX) { + LOGE("WebsocketClient: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("WebsocketClient DecodeMessage: Recv maskingkey failed"); + return false; + } + + if (!Recv(client_, buf, msgLen, 0)) { + LOGE("WebsocketClient 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("WebsocketClient DecodeMessage: Recv message without mask failed"); + return false; + } + + if (memcpy_s(wsFrame.payload.get(), msgLen, buf, msgLen) != EOK) { + LOGE("WebsocketClient DecodeMessage: memcpy_s failed"); + return false; + } + } + wsFrame.payload.get()[msgLen] = '\0'; + return true; +} + +uint64_t WebsocketClient::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 WebsocketClient::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("WebsocketClient Send Message in while failed, WebsocketClient disconnect"); + return false; + } + sendLen += static_cast(len); + } + return true; +} + +bool WebsocketClient::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("WebsocketClient Recv payload in while failed, WebsocketClient disconnect"); + return false; + } + recvLen += static_cast(len); + } + buf[totalLen] = '\0'; + return true; +} + +void WebsocketClient::Close() +{ + if (socketState_ == ToolchainSocketState::UNINITED) { + return; + } + socketState_ = ToolchainSocketState::UNINITED; + close(client_); + client_ = -1; +} + +bool WebsocketClient::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("WebsocketClient:SetWebSocketTimeOut setsockopt SO_SNDTIMEO failed"); + return false; + } + if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, + reinterpret_cast(&timeout), sizeof(timeout)) != SOCKET_SUCCESS) { + LOGE("WebsocketClient:SetWebSocketTimeOut setsockopt SO_RCVTIMEO failed"); + return false; + } + } + return true; +} + +bool WebsocketClient::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..1795b7badc3f2af8826d04100924c62d9d1d0af0 --- /dev/null +++ b/tooling/client/websocket/websocket_client.h @@ -0,0 +1,82 @@ +/* + * 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 + +#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 WebsocketClient : public WebSocket { +public: + enum ToolchainSocketState : uint8_t { + UNINITED, + INITED, + CONNECTED, + }; + WebsocketClient() = default; + ~WebsocketClient() = 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