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 <dirent.h>
21 #include <fcntl.h>
22 #include <sys/socket.h>
23 #include <sys/stat.h>
24 #include <sys/statfs.h>
25 #include <unistd.h>
26 #include <hdf_log.h>
27 #include <hdf_base.h>
28
29 #include "directory_ex.h"
30 #include <datetime_ex.h>
31 #ifdef DATA_SIZE_HISYSEVENT_ENABLE
32 #include "hisysevent.h"
33 #endif
34 #include "param_wrapper.h"
35 #include "parameter.h"
36 #include "securec.h"
37 #include "string_ex.h"
38 #include "sysparam_errno.h"
39 #include "thermal_hdf_utils.h"
40 #ifdef THERMAL_HITRACE_ENABLE
41 #include "hitrace_meter.h"
42 #endif
43 #include "thermal_log.h"
44 #include "thermal_zone_manager.h"
45 #include "zlib.h"
46
47 namespace OHOS {
48 namespace HDI {
49 namespace Thermal {
50 namespace V1_1 {
51 namespace {
52 constexpr int32_t MAX_FILE_NUM = 10;
53 constexpr long int MAX_FILE_SIZE = 10 * 1024 * 1024;
54 constexpr int32_t MAX_TIME_LEN = 20;
55 constexpr int32_t TIME_FORMAT_1 = 1;
56 constexpr int32_t TIME_FORMAT_2 = 2;
57 constexpr int32_t COMPRESS_READ_BUF_SIZE = 4096;
58 constexpr int32_t DEFAULT_WIDTH = 20;
59 constexpr int32_t DEFAULT_INTERVAL = 5000;
60 constexpr int32_t MIN_INTERVAL = 100;
61 constexpr int32_t LOG_COMPARE_START_INDEX = 13;
62 constexpr int32_t LOG_COMPARE_SUB_LEN = 15;
63 constexpr int64_t TWENTY_FOUR_HOURS = 60 * 60 * 24 * 1000;
64 const std::string TIMESTAMP_TITLE = "timestamp";
65 const std::string THERMAL_LOG_ENABLE = "persist.thermal.log.enable";
66 const std::string THERMAL_LOG_WIDTH = "persist.thermal.log.width";
67 const std::string THERMAL_LOG_INTERVAL = "persist.thermal.log.interval";
68 const std::string DATA_FILE_PATH = "/data";
69 bool g_firstReport = true;
70 bool g_firstCreate = true;
71 std::deque<std::string> g_saveLogFile;
72 std::string g_outPath = "";
73 std::string g_logTime = "";
74 }
75
76 std::shared_ptr<ThermalDfx> ThermalDfx::instance_ = nullptr;
77 std::mutex ThermalDfx::mutexInstance_;
78
GetInstance()79 ThermalDfx& ThermalDfx::GetInstance()
80 {
81 std::lock_guard<std::mutex> lock(mutexInstance_);
82 if (instance_ == nullptr) {
83 instance_ = std::make_shared<ThermalDfx>();
84 }
85 return *(instance_.get());
86 }
87
DestroyInstance()88 void ThermalDfx::DestroyInstance()
89 {
90 std::lock_guard<std::mutex> lock(mutexInstance_);
91 instance_ = nullptr;
92 }
93
GetCurrentTime(const int32_t format)94 static std::string GetCurrentTime(const int32_t format)
95 {
96 struct tm* pTime;
97 char strTime[MAX_TIME_LEN] = {0};
98 time_t t;
99 if (time(&t) == -1) {
100 THERMAL_HILOGW(COMP_HDI, "call time failed");
101 return "";
102 }
103
104 pTime = localtime(&t);
105 if (pTime == nullptr) {
106 THERMAL_HILOGW(COMP_HDI, "pTime Get localtime failed");
107 return "";
108 }
109 if (format == TIME_FORMAT_1) {
110 if (strftime(strTime, sizeof(strTime), "%Y%m%d-%H%M%S", pTime) == 0U) {
111 THERMAL_HILOGW(COMP_HDI, "call strfime failed");
112 return "";
113 }
114 } else if (format == TIME_FORMAT_2) {
115 if (strftime(strTime, sizeof(strTime), "%Y-%m-%d %H:%M:%S", pTime) == 0U) {
116 THERMAL_HILOGW(COMP_HDI, "call strfime failed");
117 return "";
118 }
119 } else {
120 THERMAL_HILOGW(COMP_HDI, "invalid format value");
121 return "";
122 }
123 return strTime;
124 }
125
ThermalDfx()126 ThermalDfx::ThermalDfx() :
127 width_(static_cast<uint8_t>(DEFAULT_WIDTH)), interval_(static_cast<uint32_t>(DEFAULT_INTERVAL)), enable_(true)
128 {
129 }
130
~ThermalDfx()131 ThermalDfx::~ThermalDfx()
132 {
133 enable_ = false;
134 }
135
CanonicalizeSpecPath(const char * src)136 std::string ThermalDfx::CanonicalizeSpecPath(const char* src)
137 {
138 if (src == nullptr || strlen(src) >= PATH_MAX) {
139 fprintf(stderr, "Error: CanonicalizeSpecPath %s failed", src);
140 return "";
141 }
142 char resolvedPath[PATH_MAX] = { 0 };
143 if (access(src, F_OK) == 0) {
144 if (realpath(src, resolvedPath) == nullptr) {
145 fprintf(stderr, "Error: realpath %s failed", src);
146 return "";
147 }
148 } else {
149 std::string fileName(src);
150 if (fileName.find("..") == std::string::npos) {
151 if (snprintf_s(resolvedPath, PATH_MAX, sizeof(resolvedPath) - 1, src) == -1) {
152 fprintf(stderr, "Error: sprintf_s %s failed", src);
153 return "";
154 }
155 } else {
156 fprintf(stderr, "Error: find .. %s failed", src);
157 return "";
158 }
159 }
160
161 std::string res(resolvedPath);
162 return res;
163 }
164
Compress(const std::string & dataFile,const std::string & destFile)165 bool ThermalDfx::Compress(const std::string& dataFile, const std::string& destFile)
166 {
167 std::string resolvedPath = CanonicalizeSpecPath(dataFile.c_str());
168 FILE* fp = fopen(resolvedPath.c_str(), "rb");
169 if (fp == nullptr) {
170 THERMAL_HILOGE(COMP_HDI, "Fail to open data file %{public}s", dataFile.c_str());
171 perror("Fail to fopen(rb)");
172 return false;
173 }
174
175 std::unique_ptr<gzFile_s, decltype(&gzclose)> fgz(gzopen(destFile.c_str(), "wb"), gzclose);
176 if (fgz == nullptr) {
177 THERMAL_HILOGE(COMP_HDI, "Fail to call gzopen(%{public}s)", destFile.c_str());
178 fclose(fp);
179 return false;
180 }
181
182 std::vector<char> buf(COMPRESS_READ_BUF_SIZE);
183 size_t len = 0;
184 while ((len = fread(buf.data(), sizeof(uint8_t), buf.size(), fp))) {
185 if (gzwrite(fgz.get(), buf.data(), len) == 0) {
186 THERMAL_HILOGE(COMP_HDI, "Fail to call gzwrite for %{public}zu bytes", len);
187 fclose(fp);
188 return false;
189 }
190 }
191 if (!feof(fp)) {
192 if (ferror(fp) != 0) {
193 THERMAL_HILOGE(COMP_HDI, "ferror return err");
194 fclose(fp);
195 return false;
196 }
197 }
198 if (fclose(fp) < 0) {
199 return false;
200 }
201 return true;
202 }
203
CompressFile()204 void ThermalDfx::CompressFile()
205 {
206 #ifdef THERMAL_HITRACE_ENABLE
207 HitraceScopedEx trace(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_POWER, "ThermalDfx_CompressFile");
208 #endif
209 THERMAL_HILOGD(COMP_HDI, "CompressFile start");
210 std::string unCompressFile = g_outPath + "/thermal.000." + g_logTime;
211
212 FILE* fp = fopen(unCompressFile.c_str(), "rb");
213 if (fp == nullptr) {
214 THERMAL_HILOGE(COMP_HDI, "open uncompressfile failed");
215 return;
216 }
217
218 if (fseek(fp, SEEK_SET, SEEK_END) != 0) {
219 THERMAL_HILOGE(COMP_HDI, "fseek() failed");
220 fclose(fp);
221 return;
222 }
223
224 long int size = ftell(fp);
225 if (size < MAX_FILE_SIZE) {
226 if (fclose(fp) < 0) {
227 THERMAL_HILOGW(COMP_HDI, "fclose() failed");
228 }
229 THERMAL_HILOGD(COMP_HDI, "file is not enough for compress");
230 return;
231 }
232 if (fclose(fp) < 0) {
233 THERMAL_HILOGW(COMP_HDI, "fclose() failed");
234 }
235
236 std::string compressFile = g_outPath + "/thermal.000." + g_logTime + ".gz";
237 if (!Compress(unCompressFile, compressFile)) {
238 THERMAL_HILOGE(COMP_HDI, "CompressFile fail");
239 return;
240 }
241
242 if (remove(unCompressFile.c_str()) != 0) {
243 THERMAL_HILOGW(COMP_HDI, "failed to remove file %{public}s", unCompressFile.c_str());
244 }
245
246 if (g_saveLogFile.size() >= MAX_FILE_NUM) {
247 if (remove(g_saveLogFile.front().c_str()) != 0) {
248 THERMAL_HILOGW(COMP_HDI, "failed to remove file %{public}s", compressFile.c_str());
249 }
250 g_saveLogFile.pop_front();
251 }
252 g_saveLogFile.push_back(compressFile);
253 g_logTime = GetCurrentTime(TIME_FORMAT_1);
254 THERMAL_HILOGD(COMP_HDI, "CompressFile done");
255 }
256
RemoveLogFile()257 void ThermalDfx::RemoveLogFile()
258 {
259 DIR* dir = opendir(g_outPath.c_str());
260 if (dir == nullptr) {
261 THERMAL_HILOGE(COMP_HDI, "Failed to open directory: %{public}s, errno = %{public}d",
262 g_outPath.c_str(), errno);
263 return;
264 }
265
266 struct dirent* entry;
267 g_saveLogFile.clear();
268 while ((entry = readdir(dir)) != nullptr) {
269 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
270 continue;
271 }
272 g_saveLogFile.push_back(g_outPath + "/" + entry->d_name);
273 }
274 std::sort(g_saveLogFile.begin(), g_saveLogFile.end(), [](const std::string &a, const std::string &b) {
275 return a.substr(g_outPath.length() + LOG_COMPARE_START_INDEX, LOG_COMPARE_SUB_LEN) <
276 b.substr(g_outPath.length() + LOG_COMPARE_START_INDEX, LOG_COMPARE_SUB_LEN);
277 });
278 while (static_cast<int32_t>(g_saveLogFile.size()) > MAX_FILE_NUM) {
279 if (remove(g_saveLogFile.front().c_str()) != 0) {
280 THERMAL_HILOGW(COMP_HDI, "failed to remove file %{public}s", g_saveLogFile.front().c_str());
281 }
282 g_saveLogFile.pop_front();
283 }
284
285 closedir(dir);
286 }
287
CompressAllFile()288 void ThermalDfx::CompressAllFile()
289 {
290 std::string compressExtension = ".gz";
291 std::deque<std::string> compressFileList(g_saveLogFile);
292 g_saveLogFile.clear();
293 for (auto& originFile : compressFileList) {
294 if (originFile.rfind(compressExtension) != std::string::npos) {
295 g_saveLogFile.emplace_back(originFile);
296 continue;
297 }
298 std::string compressFile = originFile + compressExtension;
299 if (!Compress(originFile, compressFile)) {
300 THERMAL_HILOGE(COMP_HDI, "CompressFile fail");
301 continue;
302 }
303 if (remove(originFile.c_str()) != 0) {
304 THERMAL_HILOGW(COMP_HDI, "failed to remove file %{public}s", originFile.c_str());
305 }
306 g_saveLogFile.emplace_back(compressFile);
307 }
308 }
309
PrepareWriteDfxLog()310 bool ThermalDfx::PrepareWriteDfxLog()
311 {
312 if (g_outPath == "") {
313 THERMAL_HILOGW(COMP_HDI, "parse thermal_hdi_config.xml outpath fail");
314 return false;
315 }
316 if (!enable_) {
317 THERMAL_HILOGD(COMP_HDI, "param does not start recording");
318 return false;
319 }
320
321 return true;
322 }
323
CreateLogFile()324 void ThermalDfx::CreateLogFile()
325 {
326 #ifdef THERMAL_HITRACE_ENABLE
327 HitraceScopedEx trace(HITRACE_LEVEL_COMMERCIAL, HITRACE_TAG_POWER, "ThermalDfx_CreateLogFile");
328 #endif
329 THERMAL_HILOGD(COMP_HDI, "CreateLogFile start");
330 if (!PrepareWriteDfxLog()) {
331 THERMAL_HILOGD(COMP_HDI, "prepare write dfx log failed");
332 return;
333 }
334 if (g_firstCreate) {
335 g_logTime = GetCurrentTime(TIME_FORMAT_1);
336 RemoveLogFile();
337 CompressAllFile();
338 g_firstCreate = false;
339 }
340 std::string logFile = g_outPath + "/thermal.000." + g_logTime;
341 if (access(g_outPath.c_str(), 0) == -1) {
342 auto ret = ForceCreateDirectory(g_outPath.c_str());
343 if (!ret) {
344 THERMAL_HILOGE(COMP_HDI, "create output dir failed");
345 return;
346 }
347 }
348
349 bool isEmpty = false;
350 std::ifstream fin(logFile);
351 std::fstream file;
352 file.open(logFile, std::ios::in);
353 if (file.eof() || !fin) {
354 isEmpty = true;
355 }
356 file.close();
357
358 ProcessLogInfo(logFile, isEmpty);
359 THERMAL_HILOGD(COMP_HDI, "CreateLogFile done");
360 }
361
ProcessLogInfo(std::string & logFile,bool isEmpty)362 void ThermalDfx::ProcessLogInfo(std::string& logFile, bool isEmpty)
363 {
364 std::string currentTime = GetCurrentTime(TIME_FORMAT_2);
365 std::ofstream wStream(logFile, std::ios::app);
366 if (wStream.is_open()) {
367 if (isEmpty) {
368 WriteToEmptyFile(wStream, currentTime);
369 return;
370 }
371
372 WriteToFile(wStream, currentTime);
373 wStream.close();
374 }
375 }
376
WriteToEmptyFile(std::ofstream & wStream,std::string & currentTime)377 void ThermalDfx::WriteToEmptyFile(std::ofstream& wStream, std::string& currentTime)
378 {
379 wStream << TIMESTAMP_TITLE;
380 for (uint8_t i = 0; i < width_; ++i) {
381 wStream << " ";
382 }
383 std::vector<DfxTraceInfo> logInfo = ThermalHdfConfig::GetInstance().GetTracingInfo();
384 for (const auto& info : logInfo) {
385 wStream << info.title;
386 if (info.valuePath == logInfo.back().valuePath && info.title == logInfo.back().title) {
387 break;
388 }
389 for (uint8_t i = 0; i < width_ - info.title.length(); ++i) {
390 wStream << " ";
391 }
392 }
393 wStream << "\n";
394
395 WriteToFile(wStream, currentTime);
396 wStream.close();
397 }
398
WriteToFile(std::ofstream & wStream,std::string & currentTime)399 void ThermalDfx::WriteToFile(std::ofstream& wStream, std::string& currentTime)
400 {
401 wStream << currentTime;
402 for (uint8_t i = 0; i < width_ + TIMESTAMP_TITLE.length() - currentTime.length(); ++i) {
403 wStream << " ";
404 }
405 std::vector<DfxTraceInfo>& logInfo = ThermalHdfConfig::GetInstance().GetTracingInfo();
406 std::string value;
407 for (const auto& info : logInfo) {
408 if (!ThermalHdfUtils::ReadNode(info.valuePath, value)) {
409 THERMAL_HILOGW(COMP_HDI, "Read node failed, title = %{public}s", info.title.c_str());
410 }
411 wStream << value;
412 if (info.valuePath == logInfo.back().valuePath && info.title == logInfo.back().title) {
413 break;
414 }
415 for (uint8_t i = 0; i < width_ - value.length(); ++i) {
416 wStream << " ";
417 }
418 }
419 wStream << "\n";
420 }
421
InfoChangedCallback(const char * key,const char * value,void * context)422 void ThermalDfx::InfoChangedCallback(const char* key, const char* value, void* context)
423 {
424 if (key == nullptr || value == nullptr) {
425 return;
426 }
427 std::string keyStr(key);
428 std::string valueStr(value);
429 THERMAL_HILOGI(COMP_HDI, "thermal log param change, key = %{public}s, value = %{public}s", keyStr.c_str(),
430 valueStr.c_str());
431 auto& thermalDfx = ThermalDfx::GetInstance();
432 if (keyStr == THERMAL_LOG_ENABLE) {
433 thermalDfx.EnableWatchCallback(valueStr);
434 }
435 if (keyStr == THERMAL_LOG_WIDTH) {
436 thermalDfx.WidthWatchCallback(valueStr);
437 }
438 if (keyStr == THERMAL_LOG_INTERVAL) {
439 thermalDfx.IntervalWatchCallback(valueStr);
440 }
441 }
442
WidthWatchCallback(const std::string & value)443 void ThermalDfx::WidthWatchCallback(const std::string& value)
444 {
445 int32_t width = OHOS::StrToInt(value, width) ? width : DEFAULT_WIDTH;
446 width_ = static_cast<uint8_t>((width < DEFAULT_WIDTH) ? DEFAULT_WIDTH : width);
447 }
448
IntervalWatchCallback(const std::string & value)449 void ThermalDfx::IntervalWatchCallback(const std::string& value)
450 {
451 int32_t interval = OHOS::StrToInt(value, interval) ? interval : DEFAULT_INTERVAL;
452 interval_ = static_cast<uint32_t>((interval < MIN_INTERVAL) ? MIN_INTERVAL : interval);
453 }
454
EnableWatchCallback(const std::string & value)455 void ThermalDfx::EnableWatchCallback(const std::string& value)
456 {
457 enable_ = (value == "true");
458 }
459
GetIntParameter(const std::string & key,const int32_t def,const int32_t minValue)460 int32_t ThermalDfx::GetIntParameter(const std::string& key, const int32_t def, const int32_t minValue)
461 {
462 int32_t value = OHOS::system::GetIntParameter(key, def);
463 return (value < minValue) ? def : value;
464 }
465
GetBoolParameter(const std::string & key,const bool def)466 bool ThermalDfx::GetBoolParameter(const std::string& key, const bool def)
467 {
468 std::string value;
469 if (OHOS::system::GetStringParameter(THERMAL_LOG_ENABLE, value) != 0) {
470 return def;
471 }
472 return (value == "true");
473 }
474
GetInterval()475 uint32_t ThermalDfx::GetInterval()
476 {
477 return interval_;
478 }
479
GetDeviceValidSize(const std::string & path)480 double ThermalDfx::GetDeviceValidSize(const std::string& path)
481 {
482 struct statfs stat;
483 if (statfs(path.c_str(), &stat) != 0) {
484 return 0;
485 }
486 constexpr double units = 1024.0;
487 return (static_cast<double>(stat.f_bfree) / units) * (static_cast<double>(stat.f_bsize) / units);
488 }
489
GetDirectorySize(const std::string & directoryPath)490 uint64_t ThermalDfx::GetDirectorySize(const std::string& directoryPath)
491 {
492 uint64_t totalSize = 0;
493 DIR* dir = opendir(directoryPath.c_str());
494 if (dir == nullptr) {
495 THERMAL_HILOGE(COMP_HDI, "Failed to open directory: %{public}s, errno = %{public}d",
496 directoryPath.c_str(), errno);
497 return totalSize;
498 }
499
500 struct dirent* entry;
501 while ((entry = readdir(dir)) != nullptr) {
502 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
503 continue;
504 }
505
506 std::string filePath = directoryPath + "/" + entry->d_name;
507 struct stat fileStat;
508 if (stat(filePath.c_str(), &fileStat) == 0) {
509 totalSize += static_cast<uint64_t>(fileStat.st_size);
510 }
511 }
512
513 closedir(dir);
514 return totalSize;
515 }
516
WriteDataHisysevent()517 void ThermalDfx::WriteDataHisysevent()
518 {
519 THERMAL_HILOGD(COMP_HDI, "Report data hisysevent, g_outPath: %{public}s", g_outPath.c_str());
520 #ifdef DATA_SIZE_HISYSEVENT_ENABLE
521 uint64_t remainSize = static_cast<uint64_t>(GetDeviceValidSize(DATA_FILE_PATH));
522 uint64_t fileSize = GetDirectorySize(g_outPath);
523 HiSysEventWrite(HiviewDFX::HiSysEvent::Domain::FILEMANAGEMENT, "USER_DATA_SIZE",
524 HiviewDFX::HiSysEvent::EventType::STATISTIC,
525 "COMPONENT_NAME", "drivers_peripheral_thermal",
526 "PARTITION_NAME", DATA_FILE_PATH,
527 "REMAIN_PARTITION_SIZE", remainSize,
528 "FILE_OR_FOLDER_PATH", g_outPath,
529 "FILE_OR_FOLDER_SIZE", fileSize);
530 #endif
531 }
532
ReportDataHisysevent()533 void ThermalDfx::ReportDataHisysevent()
534 {
535 if (g_firstReport) {
536 WriteDataHisysevent();
537 beginTimeMs_ = GetTickCount();
538 g_firstReport = false;
539 return;
540 }
541 int64_t endTimeMs = GetTickCount();
542 if (endTimeMs - beginTimeMs_ >= TWENTY_FOUR_HOURS) {
543 WriteDataHisysevent();
544 beginTimeMs_ = endTimeMs;
545 }
546 }
547
DoWork()548 void ThermalDfx::DoWork()
549 {
550 if (enable_) {
551 CreateLogFile();
552 CompressFile();
553 #ifdef DATA_SIZE_HISYSEVENT_ENABLE
554 ReportDataHisysevent();
555 #endif
556 }
557 }
558
Init()559 void ThermalDfx::Init()
560 {
561 beginTimeMs_ = GetTickCount();
562 interval_ = static_cast<uint32_t>(GetIntParameter(THERMAL_LOG_INTERVAL, DEFAULT_INTERVAL, MIN_INTERVAL));
563 width_ = static_cast<uint8_t>(GetIntParameter(THERMAL_LOG_WIDTH, DEFAULT_WIDTH, DEFAULT_WIDTH));
564 enable_ = GetBoolParameter(THERMAL_LOG_ENABLE, true);
565 THERMAL_HILOGI(COMP_HDI,
566 "The thermal log param is init, interval_ = %{public}d, width = %{public}d, enable = %{public}d",
567 interval_.load(), width_.load(), enable_.load());
568
569 WatchParameter(THERMAL_LOG_ENABLE.c_str(), InfoChangedCallback, nullptr);
570 WatchParameter(THERMAL_LOG_WIDTH.c_str(), InfoChangedCallback, nullptr);
571 int32_t code = WatchParameter(THERMAL_LOG_INTERVAL.c_str(), InfoChangedCallback, nullptr);
572 if (code != OHOSStartUpSysParamErrorCode::EC_SUCCESS) {
573 THERMAL_HILOGW(COMP_HDI, "thermal log watch parameters failed. error = %{public}d", code);
574 }
575
576 XmlTraceConfig& config = ThermalHdfConfig::GetInstance().GetXmlTraceConfig();
577 g_outPath = config.outPath;
578 }
579 } // V1_1
580 } // Thermal
581 } // HDI
582 } // OHOS
583