/* * Copyright (c) 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 "hidebug_native_interface.h" #include #include #include #include #include #include "dump_usage.h" #include "hidebug_app_thread_cpu.h" #include "hidebug_util.h" #include "hitrace_meter.h" #include "hilog/log.h" #include "client/cpu_collector_client.h" #include "client/memory_collector_client.h" #include "utility/memory_collector.h" namespace OHOS { namespace HiviewDFX { #undef LOG_DOMAIN #define LOG_DOMAIN 0xD002D0A #undef LOG_TAG #define LOG_TAG "HiDebug_Native_Interface" namespace { constexpr int64_t SECOND_TO_NANOSECOND = 1000 * 1000 * 1000; int GetNativeMemInfo(NativeMemInfo& nativeMemInfo) { std::shared_ptr collector = UCollectUtil::MemoryCollector::Create(); if (!collector) { HILOG_ERROR(LOG_CORE, "GetAppNativeMemInfo Failed"); return NATIVE_FAIL; } int pid = getprocpid(); auto collectResult = collector->CollectProcessMemory(pid); if (collectResult.retCode != UCollect::UcError::SUCCESS) { HILOG_ERROR(LOG_CORE, "CollectProcessMemory Failed,retCode = %{public}d", static_cast(collectResult.retCode)); return NATIVE_FAIL; } auto collectVss = collector->CollectProcessVss(pid); if (collectVss.retCode != UCollect::UcError::SUCCESS) { HILOG_ERROR(LOG_CORE, "CollectProcessVss Failed,retCode = %{public}d", static_cast(collectVss.retCode)); return NATIVE_FAIL; } nativeMemInfo.pss = static_cast(collectResult.data.pss) + static_cast(collectResult.data.swapPss); nativeMemInfo.rss = static_cast(collectResult.data.rss); nativeMemInfo.sharedDirty = static_cast(collectResult.data.sharedDirty); nativeMemInfo.privateDirty = static_cast(collectResult.data.privateDirty); nativeMemInfo.sharedClean = static_cast(collectResult.data.sharedClean); nativeMemInfo.privateClean = static_cast(collectResult.data.privateClean); nativeMemInfo.vss = static_cast(collectVss.data); return NATIVE_SUCCESS; } int GetGraphicMemoryInfo(int32_t& graphicMemoryInfo) { auto collector = UCollectClient::MemoryCollector::Create(); if (!collector) { HILOG_ERROR(LOG_CORE, "GetGraphicUsage Failed"); return NATIVE_FAIL; } auto collectResult = collector->GetGraphicUsage(); if (collectResult.retCode != UCollect::UcError::SUCCESS || collectResult.data < 0) { HILOG_ERROR(LOG_CORE, "GetGraphicUsage Failed,retCode = %{public}d", static_cast(collectResult.retCode)); return NATIVE_FAIL; } graphicMemoryInfo = collectResult.data; return NATIVE_SUCCESS; } int GetSystemCpuUsageInfo(double& cpuUsage) { std::shared_ptr collector = UCollectClient::CpuCollector::Create(); if (!collector) { HILOG_ERROR(LOG_CORE, "GetSystemCpuUsage Failed"); return NATIVE_FAIL; } auto collectResult = collector->GetSysCpuUsage(); if (collectResult.retCode != UCollect::UcError::SUCCESS) { HILOG_ERROR(LOG_CORE, "GetSystemCpuUsage Failed, retCode: %{public}d", static_cast(collectResult.retCode)); return NATIVE_FAIL; } cpuUsage = collectResult.data; return NATIVE_SUCCESS; } } class HidebugNativeInterfaceImpl : public HidebugNativeInterface { public: HidebugNativeInterfaceImpl() = default; HidebugNativeInterfaceImpl(const HidebugNativeInterfaceImpl&) = delete; HidebugNativeInterfaceImpl& operator =(const HidebugNativeInterfaceImpl&) = delete; double GetCpuUsage() override; std::map GetAppThreadCpuUsage() override; TraceErrorCode StartAppTraceCapture(uint64_t tags, uint32_t flag, uint32_t limitsize, std::string &file) override; TraceErrorCode StopAppTraceCapture() override; int GetMemoryLeakResource(const std::string& type, int32_t value, bool enabledDebugLog) override; std::optional GetSystemCpuUsage() override; std::optional GetAppMemoryLimit() override; std::optional GetVss() override; std::optional GetAppNativeMemInfo(bool withCache) override; std::optional GetSystemMemInfo() override; bool IsDebuggerConnected() override; std::optional GetGraphicsMemory() override; private: static inline HidebugAppThreadCpu threadCpu_; // It should be initialized at startup. }; HidebugNativeInterface& HidebugNativeInterface::GetInstance() { static HidebugNativeInterfaceImpl instance; return instance; } double HidebugNativeInterfaceImpl::GetCpuUsage() { std::unique_ptr dumpUsage = std::make_unique(); pid_t pid = getprocpid(); return dumpUsage->GetCpuUsage(pid); } std::map HidebugNativeInterfaceImpl::GetAppThreadCpuUsage() { auto collectResult = threadCpu_.CollectThreadStatInfos(); if (collectResult.retCode != UCollect::UcError::SUCCESS) { HILOG_ERROR(LOG_CORE, "GetAppThreadCpuUsage fail, ret: %{public}d", static_cast(collectResult.retCode)); return {}; } std::map threadMap; for (const auto &threadCpuStatInfo : collectResult.data) { threadMap[threadCpuStatInfo.tid] = threadCpuStatInfo.cpuUsage; } return threadMap; } TraceErrorCode HidebugNativeInterfaceImpl::StartAppTraceCapture(uint64_t tags, uint32_t flag, uint32_t limitsize, std::string &file) { if (flag != TraceFlag::FLAG_MAIN_THREAD && flag != TraceFlag::FLAG_ALL_THREAD) { return TRACE_INVALID_ARGUMENT; } auto ret = StartCaptureAppTrace(static_cast(flag), tags, limitsize, file); if (ret == RET_SUCC) { return TRACE_SUCCESS; } if (ret == RET_FAIL_INVALID_ARGS) { return TRACE_INVALID_ARGUMENT; } if (ret == RET_STARTED) { return TRACE_CAPTURED_ALREADY; } if (ret == RET_FAIL_MKDIR || ret == RET_FAIL_SETACL || ret == RET_FAIL_EACCES || ret == RET_FAIL_ENOENT) { return TRACE_NO_PERMISSION; } return TRACE_ABNORMAL; } TraceErrorCode HidebugNativeInterfaceImpl::StopAppTraceCapture() { auto ret = StopCaptureAppTrace(); if (ret == RET_SUCC) { return TRACE_SUCCESS; } if (ret == RET_STOPPED) { return NO_TRACE_RUNNING; } return TRACE_ABNORMAL; } std::optional HidebugNativeInterfaceImpl::GetSystemCpuUsage() { static CachedValue cachedCpuUsage; HILOG_INFO(LOG_CORE, "GetSystemCpuUsage"); constexpr int64_t effectiveTime = 2 * SECOND_TO_NANOSECOND; auto ret = cachedCpuUsage.GetOrUpdateCachedValue(effectiveTime, GetSystemCpuUsageInfo); if (ret.first == NATIVE_SUCCESS) { return ret.second; } return {}; } std::optional HidebugNativeInterfaceImpl::GetAppMemoryLimit() { auto collector = UCollectUtil::MemoryCollector::Create(); if (!collector) { HILOG_ERROR(LOG_CORE, "GetAppMemoryLimit Failed"); return {}; } auto collectResult = collector->CollectMemoryLimit(); if (collectResult.retCode != UCollect::UcError::SUCCESS) { HILOG_ERROR(LOG_CORE, "GetAppMemoryLimit Failed, retCode: %{public}d", static_cast(collectResult.retCode)); return {}; } MemoryLimitInfo memoryLimit; memoryLimit.vssLimit = collectResult.data.vssLimit; memoryLimit.rssLimit = collectResult.data.rssLimit; return memoryLimit; } std::optional HidebugNativeInterfaceImpl::GetAppNativeMemInfo(bool withCache) { static CachedValue cachedNativeMemInfo; constexpr int64_t effectiveTime = 5 * 60 * SECOND_TO_NANOSECOND; auto ret = cachedNativeMemInfo.GetOrUpdateCachedValue(withCache ? effectiveTime : 0, GetNativeMemInfo); if (ret.first == NATIVE_SUCCESS) { return ret.second; } return {}; } std::optional HidebugNativeInterfaceImpl::GetVss() { std::shared_ptr collector = UCollectUtil::MemoryCollector::Create(); if (!collector) { HILOG_ERROR(LOG_CORE, "GetVssInfo Failed"); return {}; } int pid = getprocpid(); auto collectVss = collector->CollectProcessVss(pid); if (collectVss.retCode != UCollect::UcError::SUCCESS) { HILOG_ERROR(LOG_CORE, "CollectProcessVss Failed,retCode = %{public}d", static_cast(collectVss.retCode)); return {}; } return collectVss.data; } std::optional HidebugNativeInterfaceImpl::GetSystemMemInfo() { std::shared_ptr collector = UCollectUtil::MemoryCollector::Create(); if (!collector) { HILOG_ERROR(LOG_CORE, "GetSystemMemInfo Failed"); return {}; } auto collectResult = collector->CollectSysMemory(); if (collectResult.retCode != UCollect::UcError::SUCCESS) { HILOG_ERROR(LOG_CORE, "GetSystemMemInfo Failed,retCode = %{public}d", static_cast(collectResult.retCode)); return {}; } SystemMemoryInfo systemMemoryInfo{}; systemMemoryInfo.totalMem = static_cast(collectResult.data.memTotal); systemMemoryInfo.freeMem = static_cast(collectResult.data.memFree); systemMemoryInfo.availableMem = static_cast(collectResult.data.memAvailable); return systemMemoryInfo; } int HidebugNativeInterfaceImpl::GetMemoryLeakResource(const std::string& type, int32_t value, bool enabledDebugLog) { HILOG_DEBUG(LOG_CORE, "GetMemoryLeakResource"); auto memoryCollect = UCollectClient::MemoryCollector::Create(); if (!memoryCollect) { HILOG_ERROR(LOG_CORE, "GetMemoryLeakResource Failed, return result"); return NATIVE_FAIL; } UCollectClient::MemoryCaller memoryCaller; memoryCaller.pid = getprocpid(); memoryCaller.resourceType = type; memoryCaller.limitValue = value; memoryCaller.enabledDebugLog = enabledDebugLog; auto result = memoryCollect->SetAppResourceLimit(memoryCaller); if (result.retCode != UCollect::UcError::SUCCESS) { HILOG_ERROR(LOG_CORE, "GetMemoryLeakResource Failed, retCode: %{public}d, return the last result", static_cast(result.retCode)); return NATIVE_FAIL; } HILOG_DEBUG(LOG_CORE, "GetMemoryLeakResource Success, retCode: %{public}d", static_cast(result.retCode)); return NATIVE_SUCCESS; } bool HidebugNativeInterfaceImpl::IsDebuggerConnected() { HILOG_DEBUG(LOG_CORE, "IsDebuggerConnected"); std::ifstream file("/proc/self/status"); if (!file.is_open()) { HILOG_ERROR(LOG_CORE, "IsDebuggerConnected:: open status file failed!"); return false; } std::string line; while (std::getline(file, line)) { if (line.find("TracerPid:") != std::string::npos) { std::string pidStr = line.substr(line.find(":") + 1); return std::atoi(pidStr.c_str()) != 0; } } HILOG_ERROR(LOG_CORE, "IsDebuggerConnected:: no find the TracerPid:"); return false; } std::optional HidebugNativeInterfaceImpl::GetGraphicsMemory() { static CachedValue cachedGraphicUsage; constexpr int64_t effectiveTime = 2 * SECOND_TO_NANOSECOND; // 2s auto ret = cachedGraphicUsage.GetOrUpdateCachedValue(effectiveTime, GetGraphicMemoryInfo); if (ret.first == NATIVE_SUCCESS) { return ret.second; } return {}; } } }