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