1/* 2 * Copyright (c) 2022-2023 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16#include "thermal_dfx.h" 17#include <cerrno> 18#include <cstdio> 19#include <deque> 20#include <fcntl.h> 21#include <sys/socket.h> 22#include <sys/stat.h> 23#include <unistd.h> 24#include <hdf_log.h> 25#include <hdf_base.h> 26 27#include "directory_ex.h" 28#include "param_wrapper.h" 29#include "parameter.h" 30#include "securec.h" 31#include "string_ex.h" 32#include "sysparam_errno.h" 33#include "thermal_hdf_utils.h" 34#include "thermal_hitrace.h" 35#include "thermal_log.h" 36#include "thermal_zone_manager.h" 37#include "zlib.h" 38 39namespace OHOS { 40namespace HDI { 41namespace Thermal { 42namespace V1_1 { 43namespace { 44constexpr uint8_t LOG_INDEX_LEN = 4; 45constexpr int32_t MAX_FILE_NUM = 10; 46constexpr int32_t MAX_FILE_SIZE = 10 * 1024 * 1024; 47constexpr int32_t MAX_TIME_LEN = 20; 48constexpr int32_t TIME_FORMAT_1 = 1; 49constexpr int32_t TIME_FORMAT_2 = 2; 50constexpr int32_t COMPRESS_READ_BUF_SIZE = 4096; 51constexpr int32_t DEFAULT_WIDTH = 20; 52constexpr int32_t DEFAULT_INTERVAL = 5000; 53constexpr int32_t MIN_INTERVAL = 100; 54const std::string TIMESTAMP_TITLE = "timestamp"; 55const std::string THERMAL_LOG_ENABLE = "persist.thermal.log.enable"; 56const std::string THERMAL_LOG_WIDTH = "persist.thermal.log.width"; 57const std::string THERMAL_LOG_INTERVAL = "persist.thermal.log.interval"; 58uint32_t g_currentLogIndex = 0; 59bool g_firstCreate = true; 60std::deque<std::string> g_saveLogFile; 61std::string g_outPath = ""; 62std::string g_logTime = ""; 63} 64 65std::shared_ptr<ThermalDfx> ThermalDfx::instance_ = nullptr; 66std::mutex ThermalDfx::mutexInstance_; 67 68ThermalDfx& ThermalDfx::GetInstance() 69{ 70 std::lock_guard<std::mutex> lock(mutexInstance_); 71 if (instance_ == nullptr) { 72 instance_ = std::make_shared<ThermalDfx>(); 73 } 74 return *(instance_.get()); 75} 76 77void ThermalDfx::DestroyInstance() 78{ 79 std::lock_guard<std::mutex> lock(mutexInstance_); 80 instance_ = nullptr; 81} 82 83static std::string GetCurrentTime(const int32_t format) 84{ 85 struct tm* pTime; 86 char strTime[MAX_TIME_LEN] = {0}; 87 time_t t; 88 if (time(&t) == -1) { 89 THERMAL_HILOGW(COMP_HDI, "call time failed"); 90 return ""; 91 } 92 93 pTime = localtime(&t); 94 if (pTime == nullptr) { 95 THERMAL_HILOGW(COMP_HDI, "pTime Get localtime failed"); 96 return ""; 97 } 98 if (format == TIME_FORMAT_1) { 99 if (strftime(strTime, sizeof(strTime), "%Y%m%d-%H%M%S", pTime) == 0U) { 100 THERMAL_HILOGW(COMP_HDI, "call strfime failed"); 101 return ""; 102 } 103 } else if (format == TIME_FORMAT_2) { 104 if (strftime(strTime, sizeof(strTime), "%Y-%m-%d %H:%M:%S", pTime) == 0U) { 105 THERMAL_HILOGW(COMP_HDI, "call strfime failed"); 106 return ""; 107 } 108 } else { 109 THERMAL_HILOGW(COMP_HDI, "invalid format value"); 110 return ""; 111 } 112 return strTime; 113} 114 115ThermalDfx::ThermalDfx() : 116 width_(static_cast<uint8_t>(DEFAULT_WIDTH)), interval_(static_cast<uint32_t>(DEFAULT_INTERVAL)), enable_(true) 117{ 118} 119 120ThermalDfx::~ThermalDfx() 121{ 122 enable_ = false; 123} 124 125std::string ThermalDfx::GetFileNameIndex(const uint32_t index) 126{ 127 char res[LOG_INDEX_LEN]; 128 (void)snprintf_s(res, sizeof(res), sizeof(res) - 1, "%03d", index % MAX_FILE_NUM); 129 std::string fileNameIndex(res); 130 return fileNameIndex; 131} 132 133std::string ThermalDfx::CanonicalizeSpecPath(const char* src) 134{ 135 if (src == nullptr || strlen(src) >= PATH_MAX) { 136 fprintf(stderr, "Error: CanonicalizeSpecPath %s failed", src); 137 return ""; 138 } 139 char resolvedPath[PATH_MAX] = { 0 }; 140 if (access(src, F_OK) == 0) { 141 if (realpath(src, resolvedPath) == nullptr) { 142 fprintf(stderr, "Error: realpath %s failed", src); 143 return ""; 144 } 145 } else { 146 std::string fileName(src); 147 if (fileName.find("..") == std::string::npos) { 148 if (snprintf_s(resolvedPath, PATH_MAX, sizeof(resolvedPath) - 1, src) == -1) { 149 fprintf(stderr, "Error: sprintf_s %s failed", src); 150 return ""; 151 } 152 } else { 153 fprintf(stderr, "Error: find .. %s failed", src); 154 return ""; 155 } 156 } 157 158 std::string res(resolvedPath); 159 return res; 160} 161 162bool ThermalDfx::Compress(const std::string& dataFile, const std::string& destFile) 163{ 164 std::string resolvedPath = CanonicalizeSpecPath(dataFile.c_str()); 165 FILE* fp = fopen(resolvedPath.c_str(), "rb"); 166 if (fp == nullptr) { 167 THERMAL_HILOGE(COMP_HDI, "Fail to open data file %{public}s", dataFile.c_str()); 168 perror("Fail to fopen(rb)"); 169 return false; 170 } 171 172 std::unique_ptr<gzFile_s, decltype(&gzclose)> fgz(gzopen(destFile.c_str(), "wb"), gzclose); 173 if (fgz == nullptr) { 174 THERMAL_HILOGE(COMP_HDI, "Fail to call gzopen(%{public}s)", destFile.c_str()); 175 fclose(fp); 176 return false; 177 } 178 179 std::vector<char> buf(COMPRESS_READ_BUF_SIZE); 180 size_t len = 0; 181 while ((len = fread(buf.data(), sizeof(uint8_t), buf.size(), fp))) { 182 if (gzwrite(fgz.get(), buf.data(), len) == 0) { 183 THERMAL_HILOGE(COMP_HDI, "Fail to call gzwrite for %{public}zu bytes", len); 184 fclose(fp); 185 return false; 186 } 187 } 188 if (!feof(fp)) { 189 if (ferror(fp) != 0) { 190 THERMAL_HILOGE(COMP_HDI, "ferror return err"); 191 fclose(fp); 192 return false; 193 } 194 } 195 if (fclose(fp) < 0) { 196 return false; 197 } 198 return true; 199} 200 201void ThermalDfx::CompressFile() 202{ 203 ThermalHitrace trace("ThermalDfx_CompressFile"); 204 THERMAL_HILOGI(COMP_HDI, "CompressFile start"); 205 std::string unCompressFile = g_outPath + "/" + "thermal." + GetFileNameIndex(g_currentLogIndex) + "." + g_logTime; 206 207 FILE* fp = fopen(unCompressFile.c_str(), "rb"); 208 if (fp == nullptr) { 209 THERMAL_HILOGE(COMP_HDI, "open uncompressfile failed"); 210 return; 211 } 212 213 if (fseek(fp, SEEK_SET, SEEK_END) != 0) { 214 THERMAL_HILOGE(COMP_HDI, "fseek() failed"); 215 fclose(fp); 216 return; 217 } 218 219 unsigned long size = ftell(fp); 220 if (size < MAX_FILE_SIZE) { 221 if (fclose(fp) < 0) { 222 THERMAL_HILOGW(COMP_HDI, "fclose() failed"); 223 } 224 THERMAL_HILOGI(COMP_HDI, "file is not enough for compress"); 225 return; 226 } 227 if (fclose(fp) < 0) { 228 THERMAL_HILOGW(COMP_HDI, "fclose() failed"); 229 } 230 231 std::string compressFile = 232 g_outPath + "/" + "thermal." + GetFileNameIndex(g_currentLogIndex) + "." + g_logTime + ".gz"; 233 if (!Compress(unCompressFile, compressFile)) { 234 THERMAL_HILOGE(COMP_HDI, "CompressFile fail"); 235 return; 236 } 237 238 if (remove(unCompressFile.c_str()) != 0) { 239 THERMAL_HILOGW(COMP_HDI, "failed to remove file %{public}s", unCompressFile.c_str()); 240 } 241 242 if (g_saveLogFile.size() >= MAX_FILE_NUM) { 243 if (remove(g_saveLogFile.front().c_str()) != 0) { 244 THERMAL_HILOGW(COMP_HDI, "failed to remove file %{public}s", compressFile.c_str()); 245 } 246 g_saveLogFile.pop_front(); 247 } 248 g_saveLogFile.push_back(compressFile); 249 g_currentLogIndex++; 250 g_logTime = GetCurrentTime(TIME_FORMAT_1); 251 THERMAL_HILOGI(COMP_HDI, "CompressFile done"); 252} 253 254bool ThermalDfx::PrepareWriteDfxLog() 255{ 256 if (g_outPath == "") { 257 THERMAL_HILOGW(COMP_HDI, "parse thermal_hdi_config.xml outpath fail"); 258 return false; 259 } 260 if (!enable_) { 261 THERMAL_HILOGI(COMP_HDI, "param does not start recording"); 262 return false; 263 } 264 265 return true; 266} 267 268void ThermalDfx::CreateLogFile() 269{ 270 ThermalHitrace trace("ThermalDfx_CreateLogFile"); 271 THERMAL_HILOGI(COMP_HDI, "CreateLogFile start"); 272 if (!PrepareWriteDfxLog()) { 273 THERMAL_HILOGD(COMP_HDI, "prepare write dfx log failed"); 274 return; 275 } 276 if (g_firstCreate) { 277 g_currentLogIndex = 0; 278 g_logTime = GetCurrentTime(TIME_FORMAT_1); 279 g_firstCreate = false; 280 } 281 std::string logFile = g_outPath + "/" + "thermal." + GetFileNameIndex(g_currentLogIndex) + 282 "." + g_logTime; 283 if (access(g_outPath.c_str(), 0) == -1) { 284 auto ret = ForceCreateDirectory(g_outPath.c_str()); 285 if (!ret) { 286 THERMAL_HILOGE(COMP_HDI, "create output dir failed"); 287 return; 288 } 289 } 290 291 bool isEmpty = false; 292 std::ifstream fin(logFile); 293 std::fstream file; 294 file.open(logFile, std::ios::in); 295 if (file.eof() || !fin) { 296 isEmpty = true; 297 } 298 file.close(); 299 300 ProcessLogInfo(logFile, isEmpty); 301 THERMAL_HILOGI(COMP_HDI, "CreateLogFile done"); 302} 303 304void ThermalDfx::ProcessLogInfo(std::string& logFile, bool isEmpty) 305{ 306 std::string currentTime = GetCurrentTime(TIME_FORMAT_2); 307 std::ofstream wStream(logFile, std::ios::app); 308 if (wStream.is_open()) { 309 if (isEmpty) { 310 WriteToEmptyFile(wStream, currentTime); 311 return; 312 } 313 314 WriteToFile(wStream, currentTime); 315 wStream.close(); 316 } 317} 318 319void ThermalDfx::WriteToEmptyFile(std::ofstream& wStream, std::string& currentTime) 320{ 321 wStream << TIMESTAMP_TITLE; 322 for (uint8_t i = 0; i < width_; ++i) { 323 wStream << " "; 324 } 325 std::vector<DfxTraceInfo> logInfo = ThermalHdfConfig::GetInstance().GetTracingInfo(); 326 for (const auto& info : logInfo) { 327 wStream << info.title; 328 if (info.valuePath == logInfo.back().valuePath && info.title == logInfo.back().title) { 329 break; 330 } 331 for (uint8_t i = 0; i < width_ - info.title.length(); ++i) { 332 wStream << " "; 333 } 334 } 335 wStream << "\n"; 336 337 WriteToFile(wStream, currentTime); 338 wStream.close(); 339} 340 341void ThermalDfx::WriteToFile(std::ofstream& wStream, std::string& currentTime) 342{ 343 wStream << currentTime; 344 for (uint8_t i = 0; i < width_ + TIMESTAMP_TITLE.length() - currentTime.length(); ++i) { 345 wStream << " "; 346 } 347 std::vector<DfxTraceInfo>& logInfo = ThermalHdfConfig::GetInstance().GetTracingInfo(); 348 std::string value; 349 for (const auto& info : logInfo) { 350 if (!ThermalHdfUtils::ReadNode(info.valuePath, value)) { 351 THERMAL_HILOGW(COMP_HDI, "Read node failed, title = %{public}s", info.title.c_str()); 352 } 353 wStream << value; 354 if (info.valuePath == logInfo.back().valuePath && info.title == logInfo.back().title) { 355 break; 356 } 357 for (uint8_t i = 0; i < width_ - value.length(); ++i) { 358 wStream << " "; 359 } 360 } 361 wStream << "\n"; 362} 363 364void ThermalDfx::InfoChangedCallback(const char* key, const char* value, void* context) 365{ 366 if (key == nullptr || value == nullptr) { 367 return; 368 } 369 std::string keyStr(key); 370 std::string valueStr(value); 371 THERMAL_HILOGI(COMP_HDI, "thermal log param change, key = %{public}s, value = %{public}s", keyStr.c_str(), 372 valueStr.c_str()); 373 auto& thermalDfx = ThermalDfx::GetInstance(); 374 if (keyStr == THERMAL_LOG_ENABLE) { 375 thermalDfx.EnableWatchCallback(valueStr); 376 } 377 if (keyStr == THERMAL_LOG_WIDTH) { 378 thermalDfx.WidthWatchCallback(valueStr); 379 } 380 if (keyStr == THERMAL_LOG_INTERVAL) { 381 thermalDfx.IntervalWatchCallback(valueStr); 382 } 383} 384 385void ThermalDfx::WidthWatchCallback(const std::string& value) 386{ 387 int32_t width = OHOS::StrToInt(value, width) ? width : DEFAULT_WIDTH; 388 width_ = static_cast<uint8_t>((width < DEFAULT_WIDTH) ? DEFAULT_WIDTH : width); 389} 390 391void ThermalDfx::IntervalWatchCallback(const std::string& value) 392{ 393 int32_t interval = OHOS::StrToInt(value, interval) ? interval : DEFAULT_INTERVAL; 394 interval_ = static_cast<uint32_t>((interval < MIN_INTERVAL) ? MIN_INTERVAL : interval); 395} 396 397void ThermalDfx::EnableWatchCallback(const std::string& value) 398{ 399 enable_ = (value == "true"); 400} 401 402int32_t ThermalDfx::GetIntParameter(const std::string& key, const int32_t def, const int32_t minValue) 403{ 404 int32_t value = OHOS::system::GetIntParameter(key, def); 405 return (value < minValue) ? def : value; 406} 407 408bool ThermalDfx::GetBoolParameter(const std::string& key, const bool def) 409{ 410 std::string value; 411 if (OHOS::system::GetStringParameter(THERMAL_LOG_ENABLE, value) != 0) { 412 return def; 413 } 414 return (value == "true"); 415} 416 417uint32_t ThermalDfx::GetInterval() 418{ 419 return interval_; 420} 421 422void ThermalDfx::DoWork() 423{ 424 if (enable_) { 425 CreateLogFile(); 426 CompressFile(); 427 } 428} 429 430void ThermalDfx::Init() 431{ 432 interval_ = static_cast<uint32_t>(GetIntParameter(THERMAL_LOG_INTERVAL, DEFAULT_INTERVAL, MIN_INTERVAL)); 433 width_ = static_cast<uint8_t>(GetIntParameter(THERMAL_LOG_WIDTH, DEFAULT_WIDTH, DEFAULT_WIDTH)); 434 enable_ = GetBoolParameter(THERMAL_LOG_ENABLE, true); 435 THERMAL_HILOGI(COMP_HDI, 436 "The thermal log param is init, interval_ = %{public}d, width = %{public}d, enable = %{public}d", 437 interval_.load(), width_.load(), enable_.load()); 438 439 WatchParameter(THERMAL_LOG_ENABLE.c_str(), InfoChangedCallback, nullptr); 440 WatchParameter(THERMAL_LOG_WIDTH.c_str(), InfoChangedCallback, nullptr); 441 int32_t code = WatchParameter(THERMAL_LOG_INTERVAL.c_str(), InfoChangedCallback, nullptr); 442 if (code != OHOSStartUpSysParamErrorCode::EC_SUCCESS) { 443 THERMAL_HILOGW(COMP_HDI, "thermal log watch parameters failed. error = %{public}d", code); 444 } 445 446 XmlTraceConfig& config = ThermalHdfConfig::GetInstance().GetXmlTraceConfig(); 447 g_outPath = config.outPath; 448} 449} // V1_1 450} // Thermal 451} // HDI 452} // OHOS 453