From 4b084525d085b362b6e94976c228d0f4db292418 Mon Sep 17 00:00:00 2001 From: frank-huangran Date: Fri, 1 Aug 2025 15:48:54 +0800 Subject: [PATCH] add smartperf log Signed-off-by: frank-huangran --- .../device_command/utils/src/GetLog.cpp | 258 +++++++++++ .../utils/src/service_plugin.cpp | 61 +++ .../device_command/utils/src/sp_log.cpp | 414 ++++++++++++++++++ 3 files changed, 733 insertions(+) create mode 100644 smartperf_device/device_command/utils/src/GetLog.cpp create mode 100644 smartperf_device/device_command/utils/src/service_plugin.cpp create mode 100644 smartperf_device/device_command/utils/src/sp_log.cpp diff --git a/smartperf_device/device_command/utils/src/GetLog.cpp b/smartperf_device/device_command/utils/src/GetLog.cpp new file mode 100644 index 000000000..ba390f3ce --- /dev/null +++ b/smartperf_device/device_command/utils/src/GetLog.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2021 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/GetLog.h" +#include "include/sp_utils.h" +#include "include/smartperf_command.h" +#include +#include +#include +#include +#include +namespace OHOS { +namespace SmartPerf { +void GetLog::GetHilogInMemory(std::vector &fileList) +{ + // Get current hilog in "hilog" command + std::string hilogTmp = hilogFileDir + "hilogTmp"; + std::string cmd = CMD_COMMAND_MAP.at(CmdCommand::GET_HILOG) + hilogTmp; + std::string cmdResult; + if (!SPUtils::LoadCmd(cmd, cmdResult)) { + WLOGE("Failed to GetHilogCommand files: %s", hilogTmp.c_str()); + } + if (std::filesystem::exists(hilogTmp)) { + currentLogSize += std::filesystem::file_size(hilogTmp); + fileList.push_back(hilogTmp); + } +} + +void GetLog::RemoveLogFile() +{ + // Process before and after send + SPUtils::RemoveDirOrFile(logFilePath); + SPUtils::RemoveDirOrFile(hilogFileDir); + SPUtils::RemoveDirOrFile(daemonLogFileDir); + + currentLogSize = 0; +} + +void GetLog::GenerateDaemonLogFile() +{ + const std::string preLogFileName = "log."; + std::filesystem::path dirPath(LOG_FILE_DIR); // Log file directory + std::vector files; // Log file vector to tar + + SPUtils::CreateDir(daemonLogFileDir); // Create daemonLog directory + + // Save current working directory to restore it later + // Change directory to handle relative paths in tar operations + std::string originPath; + if (std::filesystem::current_path().string().empty()) { + WLOGE("Failed to get current working directory"); + return; + } + originPath = std::filesystem::current_path().string(); + std::filesystem::current_path(LOG_FILE_DIR); + + // Get all log files in LOG_FILE_DIR + for (const auto& entry : std::filesystem::directory_iterator(dirPath)) { + if (std::filesystem::is_regular_file(entry)) { + if (entry.path().filename().string().substr(0, preLogFileName.length()) != preLogFileName) { + continue; // Skip files that don't start with "log." + } + files.push_back(entry.path()); + } + } + + // Sort log files by last write time + std::sort(files.begin(), files.end(), [](const auto& a, const auto& b) { + return std::filesystem::last_write_time(a) > std::filesystem::last_write_time(b); + }); + + // Build tar command with relative paths only, respecting size limit + std::string cpCommand = ""; + for (const auto& file : files) { + uintmax_t fileSize = std::filesystem::file_size(file); + if (currentLogSize + fileSize > logMaxSize) { + break; // Stop if adding this file would exceed the limit + } + currentLogSize += fileSize; + std::string filename = file.filename().string(); + cpCommand += filename + " "; + } + cpCommand += daemonLogFileDir; + SPUtils::CopyFiles(cpCommand); + + std::filesystem::current_path(originPath.c_str()); + WLOGI("Created tar archive of daemonLog files successfully"); +} + +std::time_t to_time_t(const std::filesystem::file_time_type &ftime) +{ + auto systemTime = std::chrono::time_point_cast + (ftime - std::filesystem::file_time_type::clock::now() + std::chrono::system_clock::now()); + return std::chrono::system_clock::to_time_t(systemTime); +} + +void GetLog::GetHilogInData(std::vector &otherFiles, + std::vector &logFiles) +{ + std::filesystem::path dirPath(systemHilogFileDir); + + try { + if (std::filesystem::exists(dirPath)) { + WLOGI("Success read hilog dir"); + } + } catch (const std::filesystem::filesystem_error &e) { + WLOGE("GetHilogFiles error: %s", e.what()); + return; + } + + for (const auto& entry : std::filesystem::directory_iterator(dirPath)) { + if (!std::filesystem::is_regular_file(entry)) { + continue; + } + + std::string extension = entry.path().extension().string(); + if (extension == ".log" || extension == ".zip") { + otherFiles.push_back(entry.path()); + continue; + } + + if (extension != ".gz") { + continue; + } + + // Handle .gz files + auto fileTime = std::filesystem::last_write_time(entry.path()); + auto fileTimeT = to_time_t(fileTime); + auto nowT = to_time_t(std::filesystem::file_time_type::clock::now()); + if (std::localtime(&fileTimeT) == nullptr || std::localtime(&nowT) == nullptr) { + WLOGE("Get local time is null"); + return; + } + std::tm* fileTm = std::localtime(&fileTimeT); + std::tm* nowTm = std::localtime(&nowT); + if (fileTm == nullptr || nowTm == nullptr) { + WLOGE("Get local time ptr is null"); + return; + } + + bool isSameDay = (fileTm->tm_year == nowTm->tm_year) && + (fileTm->tm_mon == nowTm->tm_mon) && + (fileTm->tm_mday == nowTm->tm_mday); + + if (isSameDay) { + logFiles.push_back(entry.path()); + } + } +} + +void GetLog::GenerateHilogFile() +{ + std::vector filesLog; // Log file vector to tar + std::vector filesOther; // Other file vector to tar + + SPUtils::CreateDir(hilogFileDir); + std::string originPath; + if (std::filesystem::current_path().string().empty()) { + WLOGE("Failed to get current working directory"); + return; + } + originPath = std::filesystem::current_path().string(); + GetHilogInMemory(filesLog); + GetHilogInData(filesOther, filesLog); + + if (filesLog.empty() && filesOther.empty()) { + WLOGE("Failed to get hilog files"); + return; + } + + // Sort hilog files by last write time + std::sort(filesLog.begin(), filesLog.end(), [](const auto& a, const auto& b) { + return std::filesystem::last_write_time(a) > std::filesystem::last_write_time(b); + }); + + // cd LOG_FILE_DIR + std::filesystem::current_path(systemHilogFileDir); + // Build tokar command with relative paths only + std::string cpCommand = ""; + for (const auto& file : filesOther) { + uintmax_t fileSize = std::filesystem::file_size(file); + if (currentLogSize + fileSize > logMaxSize) { + break; // Stop if adding this file would exceed the limit + } + currentLogSize += fileSize; + std::string filename = file.filename().string(); + cpCommand += filename + " "; + } + for (const auto& file : filesLog) { + uintmax_t fileSize = std::filesystem::file_size(file); + if (currentLogSize + fileSize > logMaxSize) { + break; // Stop if adding this file would exceed the limit + } + currentLogSize += fileSize; + std::string filename = file.filename().string(); + cpCommand += filename + " "; + } + cpCommand += hilogFileDir; + SPUtils::CopyFiles(cpCommand); + + std::filesystem::current_path(originPath.c_str()); + WLOGI("Created tar archive of hilog files successfully"); +} + +void GetLog::TarLogFile() +{ + GenerateDaemonLogFile(); + GenerateHilogFile(); + + std::string originPath; + if (std::filesystem::current_path().string().empty()) { + WLOGE("Failed to get current working directory"); + return; + } + originPath = std::filesystem::current_path().string(); + + // cd LOG_FILE_DIR + std::filesystem::current_path(LOG_FILE_DIR); + + // Check if directories exist + if (!std::filesystem::exists("daemonLog")) { + WLOGE("One or both directories do not exist"); + std::filesystem::current_path(originPath.c_str()); + return; + } + + // Build tar command with relative paths + std::string tarCommand = logFilePath + " hilog daemonLog"; + SPUtils::TarFiles(tarCommand); + + // Restore original working directory + std::filesystem::current_path(originPath.c_str()); + WLOGI("Created tar archive of log files successfully"); +} + +std::map GetLog::ItemData() +{ + // Remove old log tar file + RemoveLogFile(); + // Create tar archive of log files + TarLogFile(); + // Return empty map to satisfy interface + return std::map(); +} + +} // namespace SmartPerf +} // namespace OHOS \ No newline at end of file diff --git a/smartperf_device/device_command/utils/src/service_plugin.cpp b/smartperf_device/device_command/utils/src/service_plugin.cpp new file mode 100644 index 000000000..2ae5d35c8 --- /dev/null +++ b/smartperf_device/device_command/utils/src/service_plugin.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2025 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 "string" +#include "map" +#include "thread" +#include +#include "include/service_plugin.h" +#include "include/sp_log.h" + +namespace OHOS { + namespace SmartPerf { + ServicePluginHandler::ServicePluginHandler() + { + pluginHandle.resize(PLUGIN_COUNT, nullptr); + } + ServicePluginHandler::~ServicePluginHandler() + { + for (int i = 0; i < PLUGIN_COUNT; i++) { + if (pluginHandle[i] != nullptr) { + dlclose(pluginHandle[i]); + pluginHandle[i] = nullptr; + } + } + } + + std::map ServicePluginHandler::ItemData() + { + return std::map(); + } + + void* ServicePluginHandler::GetSoHandler(enum ServicePluginType type) + { + if (pluginHandle[type] == nullptr) { + char soFilePathChar[PATH_MAX] = {0x00}; + if ((realpath(pluginSoPath[type].c_str(), soFilePathChar) == nullptr)) { + WLOGE("%s is not exist.", pluginSoPath[type].c_str()); + return nullptr; + } + + pluginHandle[type] = dlopen(soFilePathChar, RTLD_LAZY); + if (!pluginHandle[type]) { + WLOGE("open ServicePlugin so file error."); + return nullptr; + } + } + return pluginHandle[type]; + } + } +} \ No newline at end of file diff --git a/smartperf_device/device_command/utils/src/sp_log.cpp b/smartperf_device/device_command/utils/src/sp_log.cpp new file mode 100644 index 000000000..6aea8b54e --- /dev/null +++ b/smartperf_device/device_command/utils/src/sp_log.cpp @@ -0,0 +1,414 @@ +/* + * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved. + * 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 "sp_log.h" +#include "securec.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "include/smartperf_command.h" + +#ifdef HI_LOG_ENABLE +#include "hilog/log.h" +#endif + +namespace OHOS { +namespace SmartPerf { +const int32_t LOG_MAX_LEN = 10000; +const long long MAX_FILE_SIZE = 4 * 1024 * 1024; // 4MB +const double MAX_FILE_KEEP_TIME = 60 * 60 * 24 * 7; // 7days +const int MAX_FILE_COUNT = 256; +const mode_t LOG_FILE_MODE = 0755; +std::mutex g_mtx; +std::string g_currentLogFilePath; +std::string g_currentDate; +bool g_writeEnable = false; + +static void SpLogOut(SpLogLevel logLevel, const char *logBuf) +{ +#ifdef HI_LOG_ENABLE + LogLevel hiLogLevel = LOG_INFO; + switch (logLevel) { + case SP_LOG_DEBUG: + hiLogLevel = LOG_DEBUG; + break; + case SP_LOG_INFO: + hiLogLevel = LOG_INFO; + break; + case SP_LOG_WARN: + hiLogLevel = LOG_WARN; + break; + case SP_LOG_ERROR: + hiLogLevel = LOG_ERROR; + break; + default: + break; + } + (void)HiLogPrint(LOG_CORE, hiLogLevel, LOG_DOMAIN, "SP_daemon", "%{public}s", logBuf); +#else + switch (logLevel) { + case SP_LOG_DEBUG: + printf("[D]%s\n", logBuf); + break; + case SP_LOG_INFO: + printf("[I]%s\n", logBuf); + break; + case SP_LOG_WARN: + printf("[W]%s\n", logBuf); + break; + case SP_LOG_ERROR: + printf("[E]%s\n", logBuf); + break; + default: + break; + } +#endif +} + +static bool EnsureLogDirectoryExists() +{ + if (!std::__fs::filesystem::exists(LOG_FILE_DIR)) { + LOGD("Try create dir: %s", LOG_FILE_DIR.c_str()); + try { + if (mkdir(LOG_FILE_DIR.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) != 0) { + LOGE("Failed to create log directory: %s", strerror(errno)); + return false; + } + + if (!std::__fs::filesystem::exists(LOG_FILE_DIR)) { + LOGE("Failed to create log directory."); + return false; + } + } catch (const std::exception& e) { + LOGE("Exception while creating log directory: %s", e.what()); + return false; + } + } + + return true; +} + +static std::string GetCurrentDate() +{ + auto now = std::chrono::system_clock::now(); + std::time_t time = std::chrono::system_clock::to_time_t(now); + std::tm* tm = std::localtime(&time); + if (tm == nullptr) { + return ""; + } + + std::ostringstream oss; + oss << std::put_time(tm, "%Y%m%d"); + return oss.str(); +} +static std::string GetCurrentLogFilePath(int maxLogNumber) +{ + auto now = std::chrono::system_clock::now(); + std::time_t time = std::chrono::system_clock::to_time_t(now); + std::tm* tm = std::localtime(&time); + if (tm == nullptr) { + return ""; + } + std::ostringstream oss; + oss << LOG_FILE_DIR << "log." << maxLogNumber << "." << + g_currentDate << "-" << std::put_time(tm, "%H%M%S"); + return oss.str(); +} +static bool IsFilePathValid(const std::string& filePath) +{ + char filePathChar[PATH_MAX] = {0x00}; + if ((realpath(filePath.c_str(), filePathChar) == nullptr)) { + LOGE("Log file %s is not exist.", filePath.c_str()); + return false; + } + std::ifstream file(filePathChar, std::ios::binary | std::ios::ate); + if (file.is_open()) { + return file.tellg() <= MAX_FILE_SIZE; + } + return false; +} + +static int CheckFileNameAndGetNumber(const std::string& fileName, const std::string& date, bool* currentDateFlag) +{ + std::regex pattern(R"(^log\.(\d+)\.(\d{8})-\d{6}$)"); + std::smatch matches; + + if (std::regex_match(fileName, matches, pattern)) { + std::string fileDate = matches[2].str(); + if (fileDate == date) { + *currentDateFlag = true; + } + return SPUtilesTye::StringToSometype(matches[1].str()); + } + + return 0; +} + +static bool Chmod(const std::string& sourceFilePath, mode_t mode) +{ + int retCode = chmod(sourceFilePath.c_str(), mode); + if (retCode != 0) { + LOGE("Failed to set %s permission, error code %d.", sourceFilePath.c_str(), retCode); + return false; + } + + return true; +} + +static void TarFile(const std::string& sourceFileName) +{ + std::ostringstream compressedFilePath; + compressedFilePath << LOG_FILE_DIR << sourceFileName << ".tar.gz"; + + std::string tarStr = compressedFilePath.str() + " -C " + LOG_FILE_DIR + " " + sourceFileName; + std::string tarCommand = CMD_COMMAND_MAP.at(CmdCommand::TAR) + tarStr; + std::string cmdResult; + if (!SPUtils::LoadCmd(tarCommand, cmdResult)) { + LOGE("Failed to compress file %s", sourceFileName.c_str()); + return; + } + + Chmod(compressedFilePath.str(), LOG_FILE_MODE); + + tarCommand = CMD_COMMAND_MAP.at(CmdCommand::REMOVE) + LOG_FILE_DIR + sourceFileName; + if (!SPUtils::LoadCmd(tarCommand, cmdResult)) { + LOGE("Failed to delete original file %s", sourceFileName.c_str()); + return; + } + + LOGD("Successfully compressed and deleted file: %s", sourceFileName.c_str()); + return; +} + +static bool GetLogFilePath() +{ + std::string date = GetCurrentDate(); + if (date == "") { + LOGE("Get current date failed"); + return false; + } + if ((date == g_currentDate) && IsFilePathValid(g_currentLogFilePath)) { + return true; + } + LOGE("Current log file path invalid: %s", g_currentLogFilePath.c_str()); + g_currentDate = date; + std::string fileName; + bool currentDateFlag = false; + int fileNumber = 0; + for (const auto& entry : std::__fs::filesystem::directory_iterator(LOG_FILE_DIR)) { + if (entry.is_regular_file()) { + fileName = entry.path().filename().string(); + fileNumber = CheckFileNameAndGetNumber(fileName, date, ¤tDateFlag); + if (fileNumber != 0) { + break; + } + } + } + if (fileNumber == 0) { + g_currentLogFilePath = GetCurrentLogFilePath(1); + if (g_currentLogFilePath == "") { + LOGE("Get current log file data is null"); + return false; + } + return true; + } + std::string filePath = LOG_FILE_DIR + fileName; + if (currentDateFlag && IsFilePathValid(filePath)) { + g_currentLogFilePath = filePath; + return true; + } + TarFile(fileName); + if (fileNumber >= MAX_FILE_COUNT) { + LOGE("Log file full!"); + return false; + } + g_currentLogFilePath = GetCurrentLogFilePath(fileNumber + 1); + return true; +} + +static void WriteMessage(const char* logMessage) +{ + bool chmodFlag = !std::__fs::filesystem::exists(g_currentLogFilePath); + + char logFilePathChar[PATH_MAX] = {0x00}; + if ((realpath(g_currentLogFilePath.c_str(), logFilePathChar) == nullptr)) { + errno_t result = strncpy_s(logFilePathChar, PATH_MAX, + g_currentLogFilePath.c_str(), g_currentLogFilePath.size()); + if (result != 0) { + LOGE("strncpy_s failed with error: %d", result); + return; + } + LOGI("Log file %s is not exist, will create", g_currentLogFilePath.c_str()); + } + + std::ofstream logFile(logFilePathChar, std::ios::app); + if (logFile.is_open()) { + if (chmodFlag) { + if (!Chmod(logFilePathChar, LOG_FILE_MODE)) { + logFile.close(); + return; + } + } + + auto now = std::chrono::system_clock::now(); + std::time_t time = std::chrono::system_clock::to_time_t(now); + std::tm* tm = std::localtime(&time); + if (tm == nullptr) { + LOGE("Write Message get current data is null"); + logFile.close(); + return; + } + + std::ostringstream timeStamp; + timeStamp << std::put_time(tm, "[%H:%M:%S]"); + logFile << timeStamp.str() << logMessage << std::endl; + + logFile.close(); + } else { + LOGE("Unable to open log file for writing: %s", logFilePathChar); + } + + return; +} + +void EnableWriteLogAndDeleteOldLogFiles() +{ + g_writeEnable = true; + std::lock_guard lock(g_mtx); + + if (!EnsureLogDirectoryExists()) { + return; + } + + DIR* dir = opendir(LOG_FILE_DIR.c_str()); + if (dir == nullptr) { + LOGE("Failed to open log directory: %s", LOG_FILE_DIR.c_str()); + return; + } + + struct dirent* entry; + time_t currentTime = time(nullptr); + + while ((entry = readdir(dir)) != nullptr) { + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + std::string filePath = LOG_FILE_DIR + entry->d_name; + char filePathChar[PATH_MAX] = {0x00}; + if ((realpath(filePath.c_str(), filePathChar) == nullptr)) { + LOGE("Log file %s is not exist.", filePath.c_str()); + continue; + } + + struct stat fileStat = {0}; + if (stat(filePathChar, &fileStat) != 0) { + LOGE("Failed to get stats for file: %s", filePathChar); + continue; + } + + double diffSeconds = difftime(currentTime, fileStat.st_mtime); + if (diffSeconds > MAX_FILE_KEEP_TIME) { + if (remove(filePathChar) == 0) { + LOGD("Deleted file: %s, last modified: %.2f seconds ago", filePathChar, diffSeconds); + } else { + LOGE("Failed to delete file: %s", filePathChar); + } + } + } + + closedir(dir); +} + +void EscapeForCSV(std::string str) +{ + std::string escapedStr; + for (char ch : str) { + if (ch == '"') { + escapedStr += "\"\""; + } else if (ch == ',' || ch == '\n' || ch == '\r') { + escapedStr += '"'; + escapedStr += ch; + escapedStr += '"'; + } else { + escapedStr += ch; + } + } + str = escapedStr; +} + +void SpLog(SpLogLevel logLevel, bool isWriteLog, const char *fmt, ...) +{ + if (fmt == nullptr) { + SpLogOut(logLevel, "SP log format string is NULL."); + return; + } + size_t fmtLength = strlen(fmt); + if (fmtLength == 0) { + SpLogOut(logLevel, "SP log format string is empty."); + return; + } + char logBuf[LOG_MAX_LEN] = {0}; + int32_t ret = 0; + va_list arg; + va_start(arg, fmt); + va_list bkArg; + va_copy(bkArg, arg); + ret = vsnprintf_s(logBuf, sizeof(logBuf), sizeof(logBuf) - 1, fmt, bkArg); + va_end(bkArg); + va_end(arg); + if (ret < 0) { + SpLogOut(logLevel, "SP log length error."); + return; + } + if (ret >= static_cast(sizeof(logBuf) - 1)) { + SpLogOut(logLevel, "SP log error: log message truncated."); + return; + } + std::string logStr(logBuf); + EscapeForCSV(logStr); + SpLogOut(logLevel, logStr.c_str()); + + if (!isWriteLog) { + return; + } + + std::lock_guard lock(g_mtx); + + if (!g_writeEnable || !EnsureLogDirectoryExists() || !GetLogFilePath()) { + return; + } + + WriteMessage(logStr.c_str()); + return; +} +} // namespace SmartPerf +} // namespace OHOS \ No newline at end of file -- Gitee