• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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