/* * 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 "hitrace_dump.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cJSON.h" #include "parameters.h" #include "hilog/log.h" #include "securec.h" using OHOS::HiviewDFX::HiLog; using OHOS::HiviewDFX::Hitrace::TraceErrorCode; using OHOS::HiviewDFX::Hitrace::TraceRetInfo; using OHOS::HiviewDFX::Hitrace::TraceMode; namespace { struct TagCategory { std::string description; uint64_t tag; int type; std::vector sysFiles; }; struct TraceParams { std::vector tags; std::vector tagGroups; std::string bufferSize; std::string clockType; std::string isOverWrite; std::string outputFile; }; constexpr uint16_t MAGIC_NUMBER = 57161; constexpr uint16_t VERSION_NUMBER = 1; constexpr uint8_t FILE_RAW_TRACE = 0; constexpr int UNIT_TIME = 100000; const int DEFAULT_BUFFER_SIZE = 12 * 1024; const int HIGHER_BUFFER_SIZE = 18 * 1024; const int SAVED_CMDLINES_SIZE = 1024; const std::string DEFAULT_OUTPUT_DIR = "/data/log/hitrace/"; struct TraceFileHeader { uint16_t magicNumber {MAGIC_NUMBER}; uint8_t fileType {FILE_RAW_TRACE}; uint16_t versionNumber {VERSION_NUMBER}; uint32_t reserved {0}; }; enum ContentType : uint8_t { CONTENT_TYPE_DEFAULT = 0, CONTENT_TYPE_EVENTS_FORMAT = 1, CONTENT_TYPE_CMDLINES = 2, CONTENT_TYPE_TGIDS = 3, CONTENT_TYPE_CPU_RAW = 4 }; struct TraceFileContentHeader { uint8_t type = CONTENT_TYPE_DEFAULT; uint32_t length = 0; }; struct LastCpuInfo { std::pair idleAndTotal = {0, 0}; bool isNormal = true; }; struct CpuStat { int8_t cpuId = -1; // 总的为-1 uint64_t user = 0; uint64_t nice = 0; uint64_t system = 0; uint64_t idle = 0; uint64_t iowait = 0; uint64_t irq = 0; uint64_t softirq = 0; uint64_t steal = 0; uint64_t guest = 0; uint64_t guestNice = 0; }; struct PageHeader { uint64_t timestamp = 0; uint64_t size = 0; uint8_t overwrite = 0; uint8_t *startPos = nullptr; uint8_t *endPos = nullptr; }; #ifndef PAGE_SIZE constexpr size_t PAGE_SIZE = 4096; #endif constexpr uint64_t HITRACE_TAG = 0xD002D33; constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, HITRACE_TAG, "HitraceDump"}; std::atomic g_dumpFlag(false); std::atomic g_dumpEnd(true); bool g_monitor = false; // close service monitor for now TraceMode g_traceMode = TraceMode::CLOSE; std::string g_traceRootPath; std::vector> g_traceFilesTable; std::vector g_outputFilesForCmd; TraceParams g_currentTraceParams = {}; std::vector Split(const std::string &str, char delimiter) { std::vector res; size_t startPos = 0; for (size_t i = 0; i < str.size(); i++) { if (str[i] == delimiter) { res.push_back(str.substr(startPos, i - startPos)); startPos = i + 1; } } if (startPos < str.size()) { res.push_back(str.substr(startPos)); } return res; } bool IsTraceMounted() { const std::string debugfsPath = "/sys/kernel/debug/tracing/"; const std::string tracefsPath = "/sys/kernel/tracing/"; if (access((debugfsPath + "trace_marker").c_str(), F_OK) != -1) { g_traceRootPath = debugfsPath; return true; } if (access((tracefsPath + "trace_marker").c_str(), F_OK) != -1) { g_traceRootPath = tracefsPath; return true; } HiLog::Error(LABEL, "IsTraceMounted: Did not find trace folder"); return false; } // Arch is 64bit when reserved = 0; Arch is 32bit when reserved = 1. void GetArchWordSize(TraceFileHeader& header) { if (sizeof(void*) == sizeof(uint64_t)) { header.reserved = 0; } else if (sizeof(void*) == sizeof(uint32_t)) { header.reserved = 1; } HiLog::Info(LABEL, "Kernel bit is %{public}d.", header.reserved); } cJSON* ParseJsonFromFile(const std::string& filePath) { std::ifstream inFile(filePath, std::ios::in); if (!inFile.is_open()) { HiLog::Error(LABEL, "ParseJsonFromFile: %{pubilc}s is not existed.", filePath.c_str()); return nullptr; } std::string fileContent((std::istreambuf_iterator(inFile)), std::istreambuf_iterator()); cJSON* root = cJSON_Parse(fileContent.c_str()); if (root == nullptr) { HiLog::Error(LABEL, "ParseJsonFromFile: %{pubilc}s is not in JSON format.", filePath.c_str()); } inFile.close(); return root; } bool ParseTagCategory(cJSON* tagCategoryNode, std::map& allTags) { cJSON* tags = nullptr; cJSON_ArrayForEach(tags, tagCategoryNode) { TagCategory tagCategory; cJSON* description = cJSON_GetObjectItem(tags, "description"); if (description != nullptr) { tagCategory.description = description->valuestring; } cJSON* tagOffset = cJSON_GetObjectItem(tags, "tag_offset"); if (tagOffset != nullptr) { tagCategory.tag = 1ULL << tagOffset->valueint; } cJSON* type = cJSON_GetObjectItem(tags, "type"); if (type != nullptr) { tagCategory.type = type->valueint; } cJSON* sysFiles = cJSON_GetObjectItem(tags, "sysFiles"); if (sysFiles != nullptr && cJSON_IsArray(sysFiles)) { cJSON* sysFile = nullptr; cJSON_ArrayForEach(sysFile, sysFiles) { if (cJSON_IsString(sysFile)) { tagCategory.sysFiles.push_back(sysFile->valuestring); } } } allTags.insert(std::pair(tags->string, tagCategory)); } return true; } bool ParseTagGroups(cJSON* tagGroupsNode, std::map> &tagGroupTable) { cJSON* tagGroup = nullptr; cJSON_ArrayForEach(tagGroup, tagGroupsNode) { std::string tagGroupName = tagGroup->string; std::vector tagList; cJSON* tag = nullptr; cJSON_ArrayForEach(tag, tagGroup) { tagList.push_back(tag->valuestring); } tagGroupTable.insert(std::pair>(tagGroupName, tagList)); } return true; } bool ParseTagInfo(std::map &allTags, std::map> &tagGroupTable) { std::string traceUtilsPath = "/system/etc/hiview/hitrace_utils.json"; cJSON* root = ParseJsonFromFile(traceUtilsPath); if (root == nullptr) { return false; } cJSON* tagCategory = cJSON_GetObjectItem(root, "tag_category"); if (tagCategory == nullptr) { HiLog::Error(LABEL, "ParseTagInfo: %{pubilc}s is not contain tag_category node.", traceUtilsPath.c_str()); cJSON_Delete(root); return false; } if (!ParseTagCategory(tagCategory, allTags)) { cJSON_Delete(root); return false; } cJSON* tagGroups = cJSON_GetObjectItem(root, "tag_groups"); if (tagGroups == nullptr) { HiLog::Error(LABEL, "ParseTagInfo: %{pubilc}s is not contain tag_groups node.", traceUtilsPath.c_str()); cJSON_Delete(root); return false; } if (!ParseTagGroups(tagGroups, tagGroupTable)) { cJSON_Delete(root); return false; } cJSON_Delete(root); HiLog::Info(LABEL, "ParseTagInfo: parse done."); return true; } bool CheckTags(const std::vector &tags, const std::map &allTags) { for (auto tag : tags) { if (allTags.find(tag) == allTags.end()) { HiLog::Error(LABEL, "CheckTags: %{pubilc}s is not provided.", tag.c_str()); return false; } } return true; } bool CheckTagGroup(const std::vector &tagGroups, const std::map> &tagGroupTable) { for (auto groupName : tagGroups) { if (tagGroupTable.find(groupName) == tagGroupTable.end()) { HiLog::Error(LABEL, "CheckTagGroup: %{pubilc}s is not provided.", groupName.c_str()); return false; } } return true; } bool WriteStrToFile(const std::string& filename, const std::string& str) { std::ofstream out; out.open(g_traceRootPath + filename, std::ios::out); if (out.fail()) { HiLog::Error(LABEL, "WriteStrToFile: %{pubilc}s open failed.", filename.c_str()); return false; } out << str; if (out.bad()) { HiLog::Error(LABEL, "WriteStrToFile: %{pubilc}s write failed.", filename.c_str()); out.close(); return false; } out.flush(); out.close(); return true; } void SetFtraceEnabled(const std::string &path, bool enabled) { WriteStrToFile(path, enabled ? "1" : "0"); } void TruncateFile() { int fd = creat((g_traceRootPath + "trace").c_str(), 0); if (fd == -1) { HiLog::Error(LABEL, "TruncateFile: clear old trace failed."); return; } close(fd); return; } bool SetProperty(const std::string& property, const std::string& value) { bool result = OHOS::system::SetParameter(property, value); if (!result) { HiLog::Error(LABEL, "SetProperty: set %{pubilc}s failed.", value.c_str()); } else { HiLog::Info(LABEL, "SetProperty: set %{pubilc}s success.", value.c_str()); } return result; } void TraceInit(const std::map &allTags) { // close all ftrace events for (auto it = allTags.begin(); it != allTags.end(); it++) { if (it->second.type != 1) { continue; } for (size_t i = 0; i < it->second.sysFiles.size(); i++) { SetFtraceEnabled(it->second.sysFiles[i], false); } } // close all user tags SetProperty("debug.hitrace.tags.enableflags", std::to_string(0)); // set buffer_size_kb 1 WriteStrToFile("buffer_size_kb", "1"); // close tracing_on SetFtraceEnabled("tracing_on", false); } void SetAllTags(const TraceParams &traceParams, const std::map &allTags, const std::map> &tagGroupTable) { std::set readyEnableTagList; for (std::string tagName : traceParams.tags) { readyEnableTagList.insert(tagName); } // if set tagGroup, need to append default group if (traceParams.tagGroups.size() > 0) { auto iter = tagGroupTable.find("default"); if (iter == tagGroupTable.end()) { HiLog::Error(LABEL, "SetAllTags: default group is wrong."); } else { for (auto defaultTag : iter->second) { readyEnableTagList.insert(defaultTag); } } } for (std::string groupName : traceParams.tagGroups) { auto iter = tagGroupTable.find(groupName); if (iter == tagGroupTable.end()) { continue; } for (std::string tag : iter->second) { readyEnableTagList.insert(tag); } } uint64_t enabledUserTags = 0; for (std::string tagName : readyEnableTagList) { auto iter = allTags.find(tagName); if (iter == allTags.end()) { continue; } if (iter->second.type == 0) { enabledUserTags |= iter->second.tag; } if (iter->second.type == 1) { for (const auto& path : iter->second.sysFiles) { SetFtraceEnabled(path, true); HiLog::Info(LABEL, "Ftrace Enabled: set %{pubilc}s success.", path.c_str()); } } } SetProperty("debug.hitrace.tags.enableflags", std::to_string(enabledUserTags)); } std::string CanonicalizeSpecPath(const char* src) { if (src == nullptr || strlen(src) >= PATH_MAX) { HiLog::Error(LABEL, "CanonicalizeSpecPath: %{pubilc}s failed.", src); return ""; } char resolvedPath[PATH_MAX] = { 0 }; #if defined(_WIN32) if (!_fullpath(resolvedPath, src, PATH_MAX)) { return ""; } #else if (access(src, F_OK) == 0) { if (realpath(src, resolvedPath) == nullptr) { HiLog::Error(LABEL, "CanonicalizeSpecPath: realpath %{pubilc}s failed.", src); return ""; } } else { std::string fileName(src); if (fileName.find("..") == std::string::npos) { if (sprintf_s(resolvedPath, PATH_MAX, "%s", src) == -1) { HiLog::Error(LABEL, "CanonicalizeSpecPath: sprintf_s %{pubilc}s failed.", src); return ""; } } else { HiLog::Error(LABEL, "CanonicalizeSpecPath: find .. src failed."); return ""; } } #endif std::string res(resolvedPath); return res; } std::string ReadFile(const std::string& filename) { std::string resolvedPath = CanonicalizeSpecPath((g_traceRootPath + filename).c_str()); std::ifstream fileIn(resolvedPath.c_str()); if (!fileIn.is_open()) { HiLog::Error(LABEL, "ReadFile: %{pubilc}s open failed.", filename.c_str()); return ""; } std::string str((std::istreambuf_iterator(fileIn)), std::istreambuf_iterator()); fileIn.close(); return str; } void SetClock(const std::string& clockType) { const std::string traceClockPath = "trace_clock"; if (clockType.size() == 0) { WriteStrToFile(traceClockPath, "boot"); //set default: boot return; } std::string allClocks = ReadFile(traceClockPath); if (allClocks.find(clockType) == std::string::npos) { HiLog::Error(LABEL, "SetClock: %{pubilc}s is non-existent, set to boot", clockType.c_str()); WriteStrToFile(traceClockPath, "boot"); // set default: boot return; } std::set allClockTypes; size_t curPos = 0; for (size_t i = 0; i < allClocks.size(); i++) { if (allClocks[i] == ' ') { allClockTypes.insert(allClocks.substr(curPos, i - curPos)); curPos = i + 1; } } std::string currentClockType; for (auto i : allClockTypes) { if (clockType.compare(i) == 0) { HiLog::Info(LABEL, "SetClock: set clock %{public}s success.", clockType.c_str()); WriteStrToFile(traceClockPath, clockType); return; } if (i[0] == '[') { currentClockType = i; } } if (currentClockType.size() == 0) { HiLog::Info(LABEL, "SetClock: clockType is boot now."); return; } const int marks = 2; if (clockType.compare(currentClockType.substr(1, currentClockType.size() - marks)) == 0) { HiLog::Info(LABEL, "SetClock: set clock %{public}s success.", clockType.c_str()); return; } HiLog::Info(LABEL, "SetClock: unknown %{public}s, change to default clock_type: boot.", clockType.c_str()); WriteStrToFile(traceClockPath, "boot"); // set default: boot return; } bool SetTraceSetting(const TraceParams &traceParams, const std::map &allTags, const std::map> &tagGroupTable) { TraceInit(allTags); TruncateFile(); SetAllTags(traceParams, allTags, tagGroupTable); WriteStrToFile("current_tracer", "nop"); WriteStrToFile("buffer_size_kb", traceParams.bufferSize); SetClock(traceParams.clockType); if (traceParams.isOverWrite == "1") { WriteStrToFile("options/overwrite", "1"); } else { WriteStrToFile("options/overwrite", "0"); } WriteStrToFile("saved_cmdlines_size", std::to_string(SAVED_CMDLINES_SIZE)); WriteStrToFile("options/record-tgid", "1"); WriteStrToFile("options/record-cmd", "1"); if (traceParams.outputFile.size() > 0) { const mode_t defaultMode = S_IRUSR | S_IWUSR | S_IRGRP; int fd = creat(traceParams.outputFile.c_str(), defaultMode); if (fd == -1) { HiLog::Error(LABEL, "SetTraceSetting: create %{public}s failed.", traceParams.outputFile.c_str()); return false; } else { close(fd); } } return true; } int GetCpuProcessors() { int processors = 0; processors = sysconf(_SC_NPROCESSORS_ONLN); return (processors == 0) ? 1 : processors; } size_t GetFileSize(const std::string &fileName) { if (fileName.empty()) { return 0; } if (access(fileName.c_str(), 0) == -1) { return 0; } struct stat statbuf; stat(fileName.c_str(), &statbuf); return statbuf.st_size; } bool WriteFile(uint8_t contentType, const std::string &src, int outFd) { std::string srcPath = CanonicalizeSpecPath(src.c_str()); int srcFd = open(srcPath.c_str(), O_RDONLY | O_NONBLOCK); if (srcFd < 0) { HiLog::Error(LABEL, "WriteFile: open %{public}s failed.", src.c_str()); return false; } struct TraceFileContentHeader contentHeader; contentHeader.type = contentType; write(outFd, reinterpret_cast(&contentHeader), sizeof(contentHeader)); uint32_t readLen = 0; uint8_t buffer[PAGE_SIZE] = {0}; const int maxReadSize = DEFAULT_BUFFER_SIZE * 1024; const int pageThreshold = PAGE_SIZE / 2; PageHeader *pageHeader = nullptr; int count = 0; const int maxCount = 2; while (readLen < maxReadSize) { ssize_t readBytes = TEMP_FAILURE_RETRY(read(srcFd, buffer, PAGE_SIZE)); if (readBytes <= 0) { break; } write(outFd, buffer, readBytes); readLen += readBytes; // Check raw_trace page size. if (contentType >= CONTENT_TYPE_CPU_RAW) { pageHeader = reinterpret_cast(&buffer); if (pageHeader->size < static_cast(pageThreshold)) { count++; } if (count >= maxCount) { break; } } } contentHeader.length = readLen; uint32_t offset = contentHeader.length + sizeof(contentHeader); off_t pos = lseek(outFd, 0, SEEK_CUR); lseek(outFd, pos - offset, SEEK_SET); write(outFd, reinterpret_cast(&contentHeader), sizeof(contentHeader)); lseek(outFd, pos, SEEK_SET); close(srcFd); return true; } void WriteEventFile(std::string &srcPath, int outFd) { uint8_t buffer[PAGE_SIZE] = {0}; std::string srcSpecPath = CanonicalizeSpecPath(srcPath.c_str()); int srcFd = open(srcSpecPath.c_str(), O_RDONLY); if (srcFd < 0) { HiLog::Error(LABEL, "WriteEventsFormat: open %{public}s failed.", srcPath.c_str()); return; } do { int len = read(srcFd, buffer, PAGE_SIZE); if (len <= 0) { break; } write(outFd, buffer, len); } while (true); close(srcFd); } bool WriteEventsFormat(int outFd) { const std::string savedEventsFormatPath = DEFAULT_OUTPUT_DIR + "saved_events_format"; if (access(savedEventsFormatPath.c_str(), F_OK) != -1) { return WriteFile(CONTENT_TYPE_EVENTS_FORMAT, savedEventsFormatPath, outFd); } std::string filePath = CanonicalizeSpecPath(savedEventsFormatPath.c_str()); int fd = open(filePath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644); if (fd < 0) { HiLog::Error(LABEL, "WriteEventsFormat: open %{public}s failed.", savedEventsFormatPath.c_str()); return false; } const std::vector priorityTracingCategory = { "events/sched/sched_waking/format", "events/sched/sched_wakeup/format", "events/sched/sched_switch/format", "events/sched/sched_blocked_reason/format", "events/power/cpu_frequency/format", "events/power/clock_set_rate/format", "events/power/cpu_frequency_limits/format", "events/f2fs/f2fs_sync_file_enter/format", "events/f2fs/f2fs_sync_file_exit/format", "events/ext4/ext4_da_write_begin/format", "events/ext4/ext4_da_write_end/format", "events/ext4/ext4_sync_file_enter/format", "events/ext4/ext4_sync_file_exit/format", "events/block/block_rq_issue/format", "events/block/block_rq_complete/format", "events/binder/binder_transaction/format", "events/binder/binder_transaction_received/format", "events/ftrace/print/format", }; for (size_t i = 0; i < priorityTracingCategory.size(); i++) { std::string srcPath = g_traceRootPath + priorityTracingCategory[i]; WriteEventFile(srcPath, fd); } close(fd); return WriteFile(CONTENT_TYPE_EVENTS_FORMAT, filePath, outFd); } bool WriteCpuRaw(int outFd) { int cpuNums = GetCpuProcessors(); int ret = true; uint8_t type = CONTENT_TYPE_CPU_RAW; for (int i = 0; i < cpuNums; i++) { std::string src = g_traceRootPath + "per_cpu/cpu" + std::to_string(i) + "/trace_pipe_raw"; if (!WriteFile(static_cast(type + i), src, outFd)) { ret = false; break; } } return ret; } bool WriteCmdlines(int outFd) { std::string cmdlinesPath = g_traceRootPath + "saved_cmdlines"; return WriteFile(CONTENT_TYPE_CMDLINES, cmdlinesPath, outFd); } bool WriteTgids(int outFd) { std::string tgidsPath = g_traceRootPath + "saved_tgids"; return WriteFile(CONTENT_TYPE_TGIDS, tgidsPath, outFd); } bool DumpTraceLoop(const std::string &outputFileName, bool isLimited) { const int sleepTime = 1; const int fileSizeThreshold = 96 * 1024 * 1024; std::string outPath = CanonicalizeSpecPath(outputFileName.c_str()); int outFd = open(outPath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644); if (outFd < 0) { return false; } struct TraceFileHeader header; GetArchWordSize(header); write(outFd, reinterpret_cast(&header), sizeof(header)); WriteEventsFormat(outFd); while (g_dumpFlag) { if (isLimited && GetFileSize(outPath) > fileSizeThreshold) { break; } sleep(sleepTime); WriteCpuRaw(outFd); } WriteCmdlines(outFd); WriteTgids(outFd); close(outFd); return true; } /** * read trace data loop * g_dumpFlag: true = open,false = close * g_dumpEnd: true = end,false = not end * if user has own output file, Output all data to the file specified by the user; * if not, Then place all the result files in/data/local/tmp/and package them once in 96M. */ void ProcessDumpTask() { g_dumpFlag = true; g_dumpEnd = false; g_outputFilesForCmd = {}; if (g_currentTraceParams.outputFile.size() > 0) { if (DumpTraceLoop(g_currentTraceParams.outputFile, false)) { g_outputFilesForCmd.push_back(g_currentTraceParams.outputFile); } g_dumpEnd = true; return; } while (g_dumpFlag) { // Generate file name struct timeval now = {0, 0}; gettimeofday(&now, nullptr); std::string outputFileName = "/data/local/tmp/trace_" + std::to_string(now.tv_sec) + ".sys"; if (DumpTraceLoop(outputFileName, true)) { g_outputFilesForCmd.push_back(outputFileName); } } g_dumpEnd = true; } void SearchFromTable(std::vector &outputFiles, int nowSec) { const int maxInterval = 8; const int agingTime = 30 * 60; for (auto iter = g_traceFilesTable.begin(); iter != g_traceFilesTable.end();) { if (nowSec - iter->second >= agingTime) { // delete outdated trace file if (access(iter->first.c_str(), F_OK) == 0) { remove(iter->first.c_str()); HiLog::Info(LABEL, "delete old %{public}s file success.", iter->first.c_str()); } iter = g_traceFilesTable.erase(iter); continue; } if (nowSec - iter->second <= maxInterval) { outputFiles.push_back(iter->first); } iter++; } } bool ReadRawTrace(std::string &outputFileName) { // read trace data from /per_cpu/cpux/trace_pipe_raw std::string outPath = CanonicalizeSpecPath(outputFileName.c_str()); int outFd = open(outPath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, 0644); if (outFd < 0) { return false; } struct TraceFileHeader header; GetArchWordSize(header); write(outFd, reinterpret_cast(&header), sizeof(header)); if (WriteEventsFormat(outFd) && WriteCpuRaw(outFd) && WriteCmdlines(outFd) && WriteTgids(outFd)) { close(outFd); return true; } HiLog::Error(LABEL, "ReadRawTrace failed."); close(outFd); return false; } std::string GenerateName() { // eg: /data/log/hitrace/trace_localtime@monotime.sys std::string name = DEFAULT_OUTPUT_DIR + "trace_"; // get localtime time_t currentTime; time(¤tTime); struct tm timeInfo = {}; const int bufferSize = 16; char timeStr[bufferSize] = {0}; if (localtime_r(¤tTime, &timeInfo) == nullptr) { HiLog::Error(LABEL, "Get localtime failed."); return ""; } strftime(timeStr, bufferSize, "%Y%m%d%H%M%S", &timeInfo); name += std::string(timeStr); // get monotime struct timespec mts = {0, 0}; clock_gettime(CLOCK_MONOTONIC, &mts); name += "@" + std::to_string(mts.tv_sec) + "-" + std::to_string(mts.tv_nsec) + ".sys"; HiLog::Info(LABEL, "Generate trace name: %{public}s.", name.c_str()); return name; } TraceErrorCode DumpTraceInner(std::vector &outputFiles) { struct timeval now = {0, 0}; gettimeofday(&now, nullptr); int nowSec = now.tv_sec; if (!g_dumpEnd) { const int maxSleepTime = 2 * 1000 * 1000 / UNIT_TIME; // 2s int cur = 0; while (!g_dumpEnd && cur < maxSleepTime) { cur += 1; usleep(UNIT_TIME); } SearchFromTable(outputFiles, nowSec); if (outputFiles.size() == 0) { return TraceErrorCode::WRITE_TRACE_INFO_ERROR; } return TraceErrorCode::SUCCESS; } g_dumpEnd = false; std::string outputFileName = GenerateName(); std::string reOutPath = CanonicalizeSpecPath(outputFileName.c_str()); bool ret = ReadRawTrace(reOutPath); SearchFromTable(outputFiles, nowSec); if (ret) { outputFiles.push_back(outputFileName); g_traceFilesTable.push_back({outputFileName, nowSec}); } else { HiLog::Error(LABEL, "DumpTraceInner: write %{public}s failed.", outputFileName.c_str()); g_dumpEnd = true; return TraceErrorCode::WRITE_TRACE_INFO_ERROR; } g_dumpEnd = true; return TraceErrorCode::SUCCESS; } void AdjustInner(CpuStat &cpuStat, LastCpuInfo &lastCpuInfo, int i) { const int cpuUsageThreshold = 80; const int percentage = 100; uint64_t totalCpuTime = cpuStat.user + cpuStat.nice + cpuStat.system + cpuStat.idle + cpuStat.iowait + cpuStat.irq + cpuStat.softirq; uint64_t cpuUsage = percentage - percentage * (cpuStat.idle - lastCpuInfo.idleAndTotal.first) / (totalCpuTime - lastCpuInfo.idleAndTotal.second); if (cpuUsage >= cpuUsageThreshold && lastCpuInfo.isNormal) { std::string subPath = "per_cpu/cpu" + std::to_string(i) + "/buffer_size_kb"; WriteStrToFile(subPath, std::to_string(HIGHER_BUFFER_SIZE)); lastCpuInfo.isNormal = false; } if (!lastCpuInfo.isNormal && cpuUsage < cpuUsageThreshold) { std::string subPath = "per_cpu/cpu" + std::to_string(i) + "/buffer_size_kb"; WriteStrToFile(subPath, std::to_string(DEFAULT_BUFFER_SIZE)); lastCpuInfo.isNormal = true; } lastCpuInfo.idleAndTotal.first = cpuStat.idle; lastCpuInfo.idleAndTotal.second = totalCpuTime; } bool CpuTraceBufferSizeAdjust(std::vector &lastData, const int cpuNums) { std::ifstream statFile("/proc/stat"); if (!statFile.is_open()) { HiLog::Error(LABEL, "CpuTraceBufferSizeAdjust: open /proc/stat failed."); return false; } std::string data; std::vector cpuStats; const int pos = 3; const int formatNumber = 10; while (std::getline(statFile, data)) { if (data.substr(0, pos) == "cpu" && data[pos] != ' ') { CpuStat cpuStat = {}; int ret = sscanf_s(data.c_str(), "%*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", &cpuStat.user, &cpuStat.nice, &cpuStat.system, &cpuStat.idle, &cpuStat.iowait, &cpuStat.irq, &cpuStat.softirq, &cpuStat.steal, &cpuStat.guest, &cpuStat.guestNice); if (ret != formatNumber) { HiLog::Error(LABEL, "CpuTraceBufferSizeAdjust: format error."); return false; } cpuStats.push_back(cpuStat); } } statFile.close(); if (cpuNums != (int)cpuStats.size()) { HiLog::Error(LABEL, "CpuTraceBufferSizeAdjust: read /proc/stat error."); return false; } for (size_t i = 0; i < cpuStats.size(); i++) { AdjustInner(cpuStats[i], lastData[i], i); } return true; } void MonitorServiceTask() { HiLog::Info(LABEL, "MonitorServiceTask: monitor thread start."); const int maxServiceTimes = 3 * 1000 * 1000 / UNIT_TIME; // 3s int curServiceTimes = 0; const int cpuNums = GetCpuProcessors(); std::vector lastData; for (int i = 0; i < cpuNums; i++) { lastData.push_back({{0, 0}, true}); } while (true) { if (g_traceMode != TraceMode::SERVICE_MODE || !g_monitor) { HiLog::Info(LABEL, "MonitorServiceTask: monitor thread exit because of g_monitor."); break; } if (curServiceTimes >= maxServiceTimes) { // trace ringbuffer dynamic tuning if (!CpuTraceBufferSizeAdjust(lastData, cpuNums)) { HiLog::Info(LABEL, "MonitorServiceTask: monitor thread exit."); break; } curServiceTimes = 0; } else { curServiceTimes++; } usleep(UNIT_TIME); } } void MonitorCmdTask() { int curCmdTimes = 0; const int maxCmdTimes = 5 * 60 * 1000 * 1000 / UNIT_TIME; // 5min exit HiLog::Info(LABEL, "MonitorCmdTask: monitor thread start."); while (true) { if (g_traceMode != TraceMode::CMD_MODE) { HiLog::Info(LABEL, "MonitorCmdTask: monitor thread exit."); return; } if (curCmdTimes >= maxCmdTimes) { HiLog::Error(LABEL, "MonitorCmdTask: CMD_MODE Timeout exit."); g_dumpFlag = false; while (!g_dumpEnd) { usleep(UNIT_TIME); } OHOS::HiviewDFX::Hitrace::CloseTrace(); break; } else { curCmdTimes++; } usleep(UNIT_TIME); } } TraceErrorCode HandleTraceOpen(const TraceParams &traceParams, const std::map &allTags, const std::map> &tagGroupTable) { if (!SetTraceSetting(traceParams, allTags, tagGroupTable)) { return TraceErrorCode::FILE_ERROR; } SetFtraceEnabled("tracing_on", true); g_currentTraceParams = traceParams; return TraceErrorCode::SUCCESS; } TraceErrorCode HandleServiceTraceOpen(const std::vector &tagGroups, const std::map &allTags, const std::map> &tagGroupTable) { TraceParams serviceTraceParams; serviceTraceParams.tagGroups = tagGroups; serviceTraceParams.bufferSize = std::to_string(DEFAULT_BUFFER_SIZE); serviceTraceParams.clockType = "boot"; serviceTraceParams.isOverWrite = "1"; return HandleTraceOpen(serviceTraceParams, allTags, tagGroupTable); } void RemoveUnSpace(std::string str, std::string& args) { int maxCircleTimes = 30; int curTimes = 0; const size_t symbolAndSpaceLen = 2; std::string strSpace = str + " "; while (curTimes < maxCircleTimes) { curTimes++; std::string::size_type index = args.find(strSpace); if (index != std::string::npos) { args.replace(index, symbolAndSpaceLen, str); } else { break; } } } /** * args: tags:tag1,tags2... tagGroups:group1,group2... clockType:boot bufferSize:1024 overwrite:1 output:filename * cmdTraceParams: Save the above parameters */ bool ParseArgs(const std::string &args, TraceParams &cmdTraceParams, const std::map &allTags, const std::map> &tagGroupTable) { std::string userArgs = args; std::string str = ":"; RemoveUnSpace(str, userArgs); str = ","; RemoveUnSpace(str, userArgs); std::vector argList = Split(userArgs, ' '); for (std::string item : argList) { size_t pos = item.find(":"); if (pos == std::string::npos) { HiLog::Error(LABEL, "trace command line without colon appears: %{public}s, continue.", item.c_str()); continue; } std::string itemName = item.substr(0, pos); if (itemName == "tags") { cmdTraceParams.tags = Split(item.substr(pos + 1), ','); } else if (itemName == "tagGroups") { cmdTraceParams.tagGroups = Split(item.substr(pos + 1), ','); } else if (itemName == "clockType") { cmdTraceParams.clockType = item.substr(pos + 1); } else if (itemName == "bufferSize") { cmdTraceParams.bufferSize = item.substr(pos + 1); } else if (itemName == "overwrite") { cmdTraceParams.isOverWrite = item.substr(pos + 1); } else if (itemName == "output") { cmdTraceParams.outputFile = item.substr(pos + 1); } else { HiLog::Error(LABEL, "Extra trace command line options appear when ParseArgs: %{public}s, return false.", itemName.c_str()); return false; } } if (CheckTags(cmdTraceParams.tags, allTags) && CheckTagGroup(cmdTraceParams.tagGroups, tagGroupTable)) { return true; } return false; } /** * When the SERVICE_MODE is started, clear the remaining trace files in the folder. */ void ClearRemainingTrace() { if (access(DEFAULT_OUTPUT_DIR.c_str(), F_OK) != 0) { return; } DIR* dirPtr = opendir(DEFAULT_OUTPUT_DIR.c_str()); if (dirPtr == nullptr) { HiLog::Error(LABEL, "opendir failed."); return; } struct dirent* ptr = nullptr; while ((ptr = readdir(dirPtr)) != nullptr) { if (ptr->d_type == DT_REG) { std::string subFileName = DEFAULT_OUTPUT_DIR + std::string(ptr->d_name); if (remove(subFileName.c_str()) == 0) { HiLog::Info(LABEL, "Delete old trace file: %{public}s success.", subFileName.c_str()); } else { HiLog::Error(LABEL, "Delete old trace file: %{public}s failed.", subFileName.c_str()); } } } closedir(dirPtr); } } // namespace namespace OHOS { namespace HiviewDFX { namespace Hitrace { TraceMode GetTraceMode() { return g_traceMode; } TraceErrorCode OpenTrace(const std::vector &tagGroups) { if (!IsTraceMounted()) { HiLog::Error(LABEL, "OpenTrace: TRACE_NOT_SUPPORTED."); return TRACE_NOT_SUPPORTED; } std::map allTags; std::map> tagGroupTable; if (!ParseTagInfo(allTags, tagGroupTable)) { HiLog::Error(LABEL, "OpenTrace: ParseTagInfo TAG_ERROR."); return TAG_ERROR; } if (tagGroups.size() == 0 || !CheckTagGroup(tagGroups, tagGroupTable)) { HiLog::Error(LABEL, "OpenTrace: TAG_ERROR."); return TAG_ERROR; } if (g_traceMode == CMD_MODE) { HiLog::Error(LABEL, "OpenTrace: TRACE_IS_OCCUPIED."); return TRACE_IS_OCCUPIED; } if (g_traceMode == SERVICE_MODE) { HiLog::Error(LABEL, "OpenTrace: CALL_ERROR."); return CALL_ERROR; } TraceErrorCode ret = HandleServiceTraceOpen(tagGroups, allTags, tagGroupTable); if (ret != SUCCESS) { HiLog::Error(LABEL, "OpenTrace: open fail."); return ret; } g_traceMode = SERVICE_MODE; ClearRemainingTrace(); // open SERVICE_MODE monitor thread std::thread auxiliaryTask(MonitorServiceTask); auxiliaryTask.detach(); HiLog::Info(LABEL, "OpenTrace: SERVICE_MODE open success."); return ret; } TraceErrorCode OpenTrace(const std::string &args) { if (!IsTraceMounted()) { HiLog::Error(LABEL, "Hitrace OpenTrace: TRACE_NOT_SUPPORTED."); return TRACE_NOT_SUPPORTED; } std::map allTags; std::map> tagGroupTable; if (!ParseTagInfo(allTags, tagGroupTable) || allTags.size() == 0 || tagGroupTable.size() == 0) { HiLog::Error(LABEL, "Hitrace OpenTrace: ParseTagInfo TAG_ERROR."); return TAG_ERROR; } // parse args TraceParams cmdTraceParams; if (!ParseArgs(args, cmdTraceParams, allTags, tagGroupTable)) { HiLog::Error(LABEL, "Hitrace OpenTrace: TAG_ERROR."); return TAG_ERROR; } if (g_traceMode != CLOSE) { HiLog::Error(LABEL, "Hitrace OpenTrace: CALL_ERROR."); return CALL_ERROR; } TraceErrorCode ret = HandleTraceOpen(cmdTraceParams, allTags, tagGroupTable); if (ret != SUCCESS) { HiLog::Error(LABEL, "Hitrace OpenTrace: CMD_MODE open failed."); return FILE_ERROR; } g_traceMode = CMD_MODE; // open SERVICE_MODE monitor thread std::thread auxiliaryTask(MonitorCmdTask); auxiliaryTask.detach(); HiLog::Info(LABEL, "Hitrace OpenTrace: CMD_MODE open success."); return ret; } TraceRetInfo DumpTrace() { TraceRetInfo ret; if (g_traceMode != SERVICE_MODE) { HiLog::Error(LABEL, "DumpTrace: CALL_ERROR."); ret.errorCode = CALL_ERROR; return ret; } ret.errorCode = DumpTraceInner(ret.outputFiles); return ret; } TraceErrorCode DumpTraceOn() { // check current trace status if (g_traceMode != CMD_MODE) { HiLog::Error(LABEL, "DumpTraceOn: CALL_ERROR."); return CALL_ERROR; } // start task thread std::thread task(ProcessDumpTask); task.detach(); HiLog::Info(LABEL, "DumpTraceOn: Dumping trace."); return SUCCESS; } TraceRetInfo DumpTraceOff() { TraceRetInfo ret; g_dumpFlag = false; const int waitTime = 10000; while (!g_dumpEnd) { usleep(waitTime); } ret.errorCode = SUCCESS; ret.outputFiles = g_outputFilesForCmd; HiLog::Info(LABEL, "DumpTraceOff: trace files generated success."); return ret; } TraceErrorCode CloseTrace() { if (g_traceMode == CLOSE) { HiLog::Error(LABEL, "CloseTrace: CALL_ERROR."); return CALL_ERROR; } std::map allTags; std::map> tagGroupTable; if (!ParseTagInfo(allTags, tagGroupTable) || allTags.size() == 0 || tagGroupTable.size() == 0) { HiLog::Error(LABEL, "CloseTrace: ParseTagInfo TAG_ERROR."); return TAG_ERROR; } TraceInit(allTags); TruncateFile(); g_traceMode = CLOSE; usleep(UNIT_TIME); return SUCCESS; } std::vector> GetTraceFilesTable() { return g_traceFilesTable; } void SetTraceFilesTable(std::vector>& traceFilesTable) { g_traceFilesTable = traceFilesTable; } } // Hitrace } // HiviewDFX } // OHOS