修改fork和ipc调用的时序

Signed-off-by: yuanye <yuanye64@huawei.com>
This commit is contained in:
yuanye 2024-11-01 15:01:44 +08:00
parent 1d4e4ba6da
commit d146b4f2ed
12 changed files with 218 additions and 119 deletions

View File

@ -115,24 +115,25 @@ config("hiperf_inner_config") {
}
sources_platform_common = [
"./src/perf_file_format.cpp",
"./src/command.cpp",
"./src/subcommand.cpp",
"./src/option.cpp",
"./src/utilities.cpp",
"./src/symbols_file.cpp",
"./src/virtual_runtime.cpp",
"./src/virtual_thread.cpp",
"./src/perf_file_reader.cpp",
"./src/perf_event_record.cpp",
"./src/dwarf_encoding.cpp",
"./src/subcommand_help.cpp",
"./src/subcommand_dump.cpp",
"./src/subcommand_report.cpp",
"./src/ipc_utilities.cpp",
"./src/option.cpp",
"./src/perf_event_record.cpp",
"./src/perf_file_format.cpp",
"./src/perf_file_reader.cpp",
"./src/register.cpp",
"./src/report.cpp",
"./src/report_json_file.cpp",
"./src/register.cpp",
"./src/subcommand.cpp",
"./src/subcommand_dump.cpp",
"./src/subcommand_help.cpp",
"./src/subcommand_report.cpp",
"./src/symbols_file.cpp",
"./src/unique_stack_table.cpp",
"./src/utilities.cpp",
"./src/virtual_runtime.cpp",
"./src/virtual_thread.cpp",
]
if (is_ohos) {

View File

@ -77,7 +77,7 @@ int main()
}
auto it = []() {
TestCodeThread(0);
}
};
std::thread workload(it);
sleep(waitTime);
// try for each thread times

29
include/ipc_utilities.h Normal file
View File

@ -0,0 +1,29 @@
/*
* Copyright (c) 2024-2024 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 HIPERF_IPC_UTILITIES_H_
#define HIPERF_IPC_UTILITIES_H_
#include <string>
namespace OHOS::Developtools::HiPerf {
bool IsDebugableApp(const std::string& bundleName);
bool IsApplicationEncryped(const int pid);
void CheckIpcBeforeFork();
} // namespace OHOS::Developtools::HiPerf
#endif // HIPERF_IPC_UTILITIES_H_

View File

@ -271,7 +271,8 @@ private:
std::thread clientCommandHanle_;
bool clientExit_ = false;
void ClientCommandHandle();
bool ClientCommandResponse(bool OK);
bool ClientCommandResponse(bool ok);
bool ClientCommandResponse(const std::string& str);
bool IsSamplingRunning();
// for cmdline client
std::string controlCmd_ = {};
@ -283,7 +284,10 @@ private:
bool CreateFifoServer();
bool SendFifoAndWaitReply(const std::string &cmd, const std::chrono::milliseconds &timeOut);
bool WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut);
void WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut, std::string& reply);
void CloseClientThread();
bool allowIpc_ = true;
std::string HandleAppInfo();
bool PreparePerfEvent();
bool PrepareSysKernel();

View File

@ -335,9 +335,8 @@ pid_t GetAppPackagePid(const std::string &appPackage, const pid_t oldPid, const
bool IsRestarted(const std::string &appPackage);
void CollectPidsByAppname(std::set<pid_t> &pids, const std::string &appPackage);
bool CheckAppIsRunning (std::vector<pid_t> &selectPids, const std::string &appPackage, int checkAppMs);
bool IsExistDebugByApp(const std::string& bundleName);
bool IsExistDebugByPid(const std::vector<pid_t> &pids);
bool IsDebugableApp(const std::string& bundleName);
bool IsExistDebugByApp(const std::string& bundleName, std::string& err);
bool IsExistDebugByPid(const std::vector<pid_t> &pids, std::string& err);
bool IsSupportNonDebuggableApp();
const std::string GetUserType();
bool GetDeveloperMode();
@ -345,7 +344,6 @@ bool IsArkJsFile(const std::string& filepath);
std::string GetProcessName(int pid);
bool NeedAdaptSandboxPath(char *filename, int pid, u16 &headerSize);
bool NeedAdaptHMBundlePath(std::string& filename, const std::string& threadname);
bool IsApplicationEncryped(const int pid);
template <typename Func>
class ScopeGuard {

90
src/ipc_utilities.cpp Normal file
View File

@ -0,0 +1,90 @@
/*
* Copyright (c) 2024-2024 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 "ipc_utilities.h"
#include <atomic>
#include "debug_logger.h"
#include "hiperf_hilog.h"
#include "utilities.h"
#if defined(is_ohos) && is_ohos && defined(BUNDLE_FRAMEWORK_ENABLE)
#include "application_info.h"
#include "bundle_mgr_proxy.h"
#endif
#if defined(is_ohos) && is_ohos
#include "iservice_registry.h"
#include "system_ability_definition.h"
#endif
namespace OHOS::Developtools::HiPerf {
static std::atomic<bool> haveIpc = false;
bool IsDebugableApp(const std::string& bundleName)
{
#if defined(is_ohos) && is_ohos && defined(BUNDLE_FRAMEWORK_ENABLE)
CHECK_TRUE(bundleName.empty(), false, LOG_TYPE_PRINTF, "bundleName is empty!\n");
sptr<ISystemAbilityManager> sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
CHECK_TRUE(sam == nullptr, false, LOG_TYPE_PRINTF, "GetSystemAbilityManager failed!\n");
sptr<IRemoteObject> remoteObject = sam->GetSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID);
CHECK_TRUE(remoteObject == nullptr, false, LOG_TYPE_PRINTF, "Get BundleMgr SA failed!\n");
sptr<AppExecFwk::BundleMgrProxy> proxy = iface_cast<AppExecFwk::BundleMgrProxy>(remoteObject);
CHECK_TRUE(proxy == nullptr, false, LOG_TYPE_PRINTF, "iface_cast failed!\n");
AppExecFwk::ApplicationInfo appInfo;
bool ret = proxy->GetApplicationInfo(bundleName, AppExecFwk::GET_APPLICATION_INFO_WITH_DISABLE,
AppExecFwk::Constants::ANY_USERID, appInfo);
CHECK_TRUE(!ret, false, 1, "%s GetApplicationInfo failed!", bundleName.c_str());
HLOGD("bundleName is %s,appProvisionType: %s", bundleName.c_str(), appInfo.appProvisionType.c_str());
return appInfo.appProvisionType == AppExecFwk::Constants::APP_PROVISION_TYPE_DEBUG;
#else
return false;
#endif
}
bool IsApplicationEncryped(const int pid)
{
#if defined(is_ohos) && is_ohos && defined(BUNDLE_FRAMEWORK_ENABLE)
CHECK_TRUE(pid <= 0, false, LOG_TYPE_PRINTF, "Invalid -p value '%d', the pid should be larger than 0\n", pid);
std::string bundleName = GetProcessName(pid);
CHECK_TRUE(bundleName.empty(), false, 1, "bundleName is empty,pid is %d", pid);
sptr<ISystemAbilityManager> sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
CHECK_TRUE(sam == nullptr, false, LOG_TYPE_PRINTF, "GetSystemAbilityManager failed!\n");
sptr<IRemoteObject> remoteObject = sam->GetSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID);
CHECK_TRUE(remoteObject == nullptr, false, LOG_TYPE_PRINTF, "Get BundleMgr SA failed!\n");
sptr<AppExecFwk::BundleMgrProxy> proxy = iface_cast<AppExecFwk::BundleMgrProxy>(remoteObject);
CHECK_TRUE(proxy == nullptr, false, LOG_TYPE_PRINTF, "iface_cast failed!\n");
AppExecFwk::ApplicationInfo appInfo;
bool ret = proxy->GetApplicationInfo(bundleName, AppExecFwk::ApplicationFlag::GET_BASIC_APPLICATION_INFO,
AppExecFwk::Constants::ANY_USERID, appInfo);
CHECK_TRUE(!ret, false, 1, "%s:%s GetApplicationInfo failed!", __func__, bundleName.c_str());
bool isEncrypted = (appInfo.applicationReservedFlag &
static_cast<uint32_t>(AppExecFwk::ApplicationReservedFlag::ENCRYPTED_APPLICATION)) != 0;
HLOGD("check application encryped.%d : %s, pid:%d", isEncrypted, bundleName.c_str(), pid);
return isEncrypted;
#else
return false;
#endif
}
void CheckIpcBeforeFork()
{
if (haveIpc.load()) {
HIPERF_HILOGW(MODULE_DEFAULT, "fork after ipc!");
}
}
} // namespace OHOS::Developtools::HiPerf

View File

@ -112,6 +112,5 @@ int main(const int argc, const char *argv[])
#endif
HLOGD("normal exit.");
fflush(stdout);
_exit(0);
return 0;
}

View File

@ -32,6 +32,7 @@
#include "command.h"
#include "debug_logger.h"
#include "hiperf_client.h"
#include "ipc_utilities.h"
#if defined(is_ohos) && is_ohos
#include "hiperf_hilog.h"
#endif
@ -212,6 +213,10 @@ bool SubCommandRecord::GetOptions(std::vector<std::string> &args)
if (!Option::GetOptionValue(args, "--exclude-hiperf", excludeHiperf_)) {
return false;
}
if (!Option::GetOptionValue(args, "--control", controlCmd_)) {
return false;
}
allowIpc_ = controlCmd_ != CONTROL_CMD_PREPARE;
if (!Option::GetOptionValue(args, "-z", compressData_)) {
return false;
}
@ -257,7 +262,8 @@ bool SubCommandRecord::GetOptions(std::vector<std::string> &args)
if (!Option::GetOptionValue(args, "--app", appPackage_)) {
return false;
}
if (!IsExistDebugByApp(appPackage_)) {
std::string err = "";
if (allowIpc_ && !IsExistDebugByApp(appPackage_, err)) {
return false;
}
if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) {
@ -272,7 +278,7 @@ bool SubCommandRecord::GetOptions(std::vector<std::string> &args)
if (!Option::GetOptionValue(args, "-p", selectPids_)) {
return false;
}
if (!IsExistDebugByPid(selectPids_)) {
if (allowIpc_ && !IsExistDebugByPid(selectPids_, err)) {
return false;
}
if (!Option::GetOptionValue(args, "-t", selectTids_)) {
@ -330,9 +336,6 @@ bool SubCommandRecord::GetOptions(std::vector<std::string> &args)
if (!Option::GetOptionValue(args, "--pipe_output", clientPipeOutput_)) {
return false;
}
if (!Option::GetOptionValue(args, "--control", controlCmd_)) {
return false;
}
if (!Option::GetOptionValue(args, "--dedup_stack", dedupStack_)) {
return false;
}
@ -950,30 +953,22 @@ void SubCommandRecord::WriteCommEventBeforeSampling()
}
}
bool SubCommandRecord::ClientCommandResponse(bool OK)
bool SubCommandRecord::ClientCommandResponse(bool ok)
{
using namespace HiperfClient;
if (OK) {
size_t size = write(clientPipeOutput_, ReplyOK.c_str(), ReplyOK.size());
if (size != ReplyOK.size()) {
char errInfo[ERRINFOLEN] = { 0 };
strerror_r(errno, errInfo, ERRINFOLEN);
HLOGD("Server:%s -> %d : %zd %d:%s", ReplyOK.c_str(), clientPipeOutput_, size, errno,
errInfo);
return false;
}
return true;
} else {
size_t size = write(clientPipeOutput_, ReplyFAIL.c_str(), ReplyFAIL.size());
if (size != ReplyFAIL.size()) {
char errInfo[ERRINFOLEN] = { 0 };
strerror_r(errno, errInfo, ERRINFOLEN);
HLOGD("Server:%s -> %d : %zd %d:%s", ReplyFAIL.c_str(), clientPipeOutput_, size, errno,
errInfo);
return false;
}
return true;
return ClientCommandResponse(ok ? HiperfClient::ReplyOK : HiperfClient::ReplyFAIL);
}
bool SubCommandRecord::ClientCommandResponse(const std::string& str)
{
size_t size = write(clientPipeOutput_, str.c_str(), str.size());
if (size != str.size()) {
char errInfo[ERRINFOLEN] = { 0 };
strerror_r(errno, errInfo, ERRINFOLEN);
HLOGD("Server:%s -> %d : %zd %d:%s", str.c_str(), clientPipeOutput_, size, errno,
errInfo);
return false;
}
return true;
}
bool SubCommandRecord::IsSamplingRunning()
@ -1103,7 +1098,10 @@ bool SubCommandRecord::CreateFifoServer()
return false;
}
CheckIpcBeforeFork();
pid_t pid = fork();
allowIpc_ = true;
if (pid == -1) {
strerror_r(errno, errInfo, ERRINFOLEN);
HLOGE("fork failed. %d:%s", errno, errInfo);
@ -1118,12 +1116,24 @@ bool SubCommandRecord::CreateFifoServer()
HLOGE("open fifo file(%s) failed. %d:%s", CONTROL_FIFO_FILE_S2C.c_str(), errno, errInfo);
return false;
}
std::string err = HandleAppInfo();
if (!err.empty()) {
ClientCommandResponse(err);
return false;
}
nullFd_ = open("/dev/null", O_WRONLY);
(void)dup2(nullFd_, STDOUT_FILENO); // redirect stdout to /dev/null
} else { // parent process
isFifoClient_ = true;
int fd = open(CONTROL_FIFO_FILE_S2C.c_str(), O_RDONLY | O_NONBLOCK);
if (fd == -1 or !WaitFifoReply(fd, CONTROL_WAITREPY_TOMEOUT)) {
std::string reply = "";
if (fd != -1) {
WaitFifoReply(fd, CONTROL_WAITREPY_TOMEOUT, reply);
}
if (fd == -1 or reply != HiperfClient::ReplyOK) {
if (reply != HiperfClient::ReplyOK) {
printf("%s\n", reply.c_str());
}
close(fd);
kill(pid, SIGKILL);
remove(CONTROL_FIFO_FILE_C2S.c_str());
@ -1168,12 +1178,19 @@ bool SubCommandRecord::SendFifoAndWaitReply(const std::string &cmd, const std::c
}
bool SubCommandRecord::WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut)
{
std::string reply;
WaitFifoReply(fd, timeOut, reply);
return reply == HiperfClient::ReplyOK;
}
void SubCommandRecord::WaitFifoReply(int fd, const std::chrono::milliseconds &timeOut, std::string& reply)
{
struct pollfd pollFd {
fd, POLLIN, 0
};
int polled = poll(&pollFd, 1, timeOut.count());
std::string reply;
reply.clear();
if (polled > 0) {
while (true) {
char c;
@ -1192,11 +1209,6 @@ bool SubCommandRecord::WaitFifoReply(int fd, const std::chrono::milliseconds &ti
} else {
HLOGD("wait fifo file(%s) failed", CONTROL_FIFO_FILE_S2C.c_str());
}
if (reply == HiperfClient::ReplyOK) {
return true;
}
return false;
}
bool SubCommandRecord::OnSubCommand(std::vector<std::string> &args)
@ -1907,6 +1919,18 @@ bool SubCommandRecord::OnlineReportData()
__FUNCTION__, ret ? "success" : "fail");
return ret;
}
std::string SubCommandRecord::HandleAppInfo()
{
std::string err = "";
if (!IsExistDebugByApp(appPackage_, err)) {
return err;
}
if (!IsExistDebugByPid(selectPids_, err)) {
return err;
}
return err;
}
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS

View File

@ -99,7 +99,8 @@ bool SubCommandStat::ParseOption(std::vector<std::string> &args)
HLOGD("get option --app failed");
return false;
}
if (!IsExistDebugByApp(appPackage_)) {
std::string err = "";
if (!IsExistDebugByApp(appPackage_, err)) {
return false;
}
if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) {
@ -109,7 +110,7 @@ bool SubCommandStat::ParseOption(std::vector<std::string> &args)
HLOGD("get option -p failed");
return false;
}
if (!IsExistDebugByPid(selectPids_)) {
if (!IsExistDebugByPid(selectPids_, err)) {
return false;
}
if (!Option::GetOptionValue(args, "-t", selectTids_)) {

View File

@ -41,6 +41,7 @@
#include "hiperf_hilog.h"
#include "unwinder_config.h"
#include "utilities.h"
#include "ipc_utilities.h"
using namespace OHOS::HiviewDFX;
using namespace std::chrono;

View File

@ -19,6 +19,7 @@
#include <sys/wait.h>
#include <unistd.h>
#include "debug_logger.h"
#include "ipc_utilities.h"
namespace OHOS {
namespace Developtools {
@ -78,6 +79,7 @@ bool TrackedCommand::CreateChildProcess()
if (!InitSignalPipes(startFd, ackFd)) {
return false;
}
CheckIpcBeforeFork();
pid_t pid = fork();
if (pid == -1) {
HLOGE("fork() failed in TrackedCommand::CreateChildProcess()");

View File

@ -25,16 +25,7 @@
#endif
#include "hiperf_hilog.h"
#if defined(is_ohos) && is_ohos && defined(BUNDLE_FRAMEWORK_ENABLE)
#include "application_info.h"
#include "bundle_mgr_proxy.h"
#endif
#if defined(is_ohos) && is_ohos
#include "iservice_registry.h"
#include "system_ability_definition.h"
using namespace OHOS;
using namespace OHOS::AppExecFwk;
#endif
#include "ipc_utilities.h"
using namespace std::chrono;
namespace OHOS {
@ -661,7 +652,7 @@ bool CheckAppIsRunning (std::vector<pid_t> &selectPids, const std::string &appPa
return true;
}
bool IsExistDebugByApp(const std::string& bundleName)
bool IsExistDebugByApp(const std::string& bundleName, std::string& err)
{
std::string bundleNameTmp = bundleName;
auto pos = bundleNameTmp.find(":");
@ -670,17 +661,22 @@ bool IsExistDebugByApp(const std::string& bundleName)
}
if (!IsSupportNonDebuggableApp() && !bundleNameTmp.empty() && !IsDebugableApp(bundleNameTmp)) {
HLOGE("--app option only support debug application.");
printf("--app option only support debug application\n");
err = "--app option only support debug application";
printf("%s\n", err.c_str());
return false;
}
return true;
}
bool IsExistDebugByPid(const std::vector<pid_t> &pids)
bool IsExistDebugByPid(const std::vector<pid_t> &pids, std::string& err)
{
CHECK_TRUE(pids.empty(), true, 1, "IsExistDebugByPid: pids is empty.");
for (auto pid : pids) {
CHECK_TRUE(pid <= 0, false, LOG_TYPE_PRINTF, "Invalid -p value '%d', the pid should be larger than 0\n", pid);
if (pid <= 0) {
err = "Invalid -p value '" + std::to_string(pid) + "', the pid should be larger than 0";
printf("%s\n", err.c_str());
return false;
}
std::string bundleName = GetProcessName(pid);
auto pos = bundleName.find(":");
if (pos != std::string::npos) {
@ -688,34 +684,14 @@ bool IsExistDebugByPid(const std::vector<pid_t> &pids)
}
if (!IsSupportNonDebuggableApp() && !IsDebugableApp(bundleName)) {
HLOGE("-p option only support debug application for %s", bundleName.c_str());
printf("-p option only support debug application\n");
err = "-p option only support debug application";
printf("%s\n", err.c_str());
return false;
}
}
return true;
}
bool IsDebugableApp(const std::string& bundleName)
{
#if defined(is_ohos) && is_ohos && defined(BUNDLE_FRAMEWORK_ENABLE)
CHECK_TRUE(bundleName.empty(), false, LOG_TYPE_PRINTF, "bundleName is empty!\n");
sptr<ISystemAbilityManager> sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
CHECK_TRUE(sam == nullptr, false, LOG_TYPE_PRINTF, "GetSystemAbilityManager failed!\n");
sptr<IRemoteObject> remoteObject = sam->GetSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID);
CHECK_TRUE(remoteObject == nullptr, false, LOG_TYPE_PRINTF, "Get BundleMgr SA failed!\n");
sptr<BundleMgrProxy> proxy = iface_cast<BundleMgrProxy>(remoteObject);
CHECK_TRUE(proxy == nullptr, false, LOG_TYPE_PRINTF, "iface_cast failed!\n");
AppExecFwk::ApplicationInfo appInfo;
bool ret = proxy->GetApplicationInfo(bundleName, AppExecFwk::GET_APPLICATION_INFO_WITH_DISABLE,
AppExecFwk::Constants::ANY_USERID, appInfo);
CHECK_TRUE(!ret, false, 1, "%s GetApplicationInfo failed!", bundleName.c_str());
HLOGD("bundleName is %s,appProvisionType: %s", bundleName.c_str(), appInfo.appProvisionType.c_str());
return appInfo.appProvisionType == Constants::APP_PROVISION_TYPE_DEBUG;
#else
return false;
#endif
}
bool IsSupportNonDebuggableApp()
{
// root first
@ -866,32 +842,6 @@ bool IsHiviewCall()
return false;
#endif
}
bool IsApplicationEncryped(const int pid)
{
#if defined(is_ohos) && is_ohos && defined(BUNDLE_FRAMEWORK_ENABLE)
CHECK_TRUE(pid <= 0, false, LOG_TYPE_PRINTF, "Invalid -p value '%d', the pid should be larger than 0\n", pid);
std::string bundleName = GetProcessName(pid);
CHECK_TRUE(bundleName.empty(), false, 1, "bundleName is empty,pid is %d", pid);
sptr<ISystemAbilityManager> sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
CHECK_TRUE(sam == nullptr, false, LOG_TYPE_PRINTF, "GetSystemAbilityManager failed!\n");
sptr<IRemoteObject> remoteObject = sam->GetSystemAbility(BUNDLE_MGR_SERVICE_SYS_ABILITY_ID);
CHECK_TRUE(remoteObject == nullptr, false, LOG_TYPE_PRINTF, "Get BundleMgr SA failed!\n");
sptr<BundleMgrProxy> proxy = iface_cast<BundleMgrProxy>(remoteObject);
CHECK_TRUE(proxy == nullptr, false, LOG_TYPE_PRINTF, "iface_cast failed!\n");
AppExecFwk::ApplicationInfo appInfo;
bool ret = proxy->GetApplicationInfo(bundleName, AppExecFwk::ApplicationFlag::GET_BASIC_APPLICATION_INFO,
AppExecFwk::Constants::ANY_USERID, appInfo);
CHECK_TRUE(!ret, false, 1, "%s:%s GetApplicationInfo failed!", __func__, bundleName.c_str());
bool isEncrypted = (appInfo.applicationReservedFlag &
static_cast<uint32_t>(AppExecFwk::ApplicationReservedFlag::ENCRYPTED_APPLICATION)) != 0;
HLOGD("check application encryped.%d : %s, pid:%d", isEncrypted, bundleName.c_str(), pid);
return isEncrypted;
#else
return false;
#endif
}
} // namespace HiPerf
} // namespace Developtools
} // namespace OHOS