• 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_hitrace.h"
35 #include "thermal_log.h"
36 #include "thermal_zone_manager.h"
37 #include "zlib.h"
38 
39 namespace OHOS {
40 namespace HDI {
41 namespace Thermal {
42 namespace V1_1 {
43 namespace {
44 constexpr uint8_t LOG_INDEX_LEN = 4;
45 constexpr int32_t MAX_FILE_NUM = 10;
46 constexpr long int MAX_FILE_SIZE = 10 * 1024 * 1024;
47 constexpr int32_t MAX_TIME_LEN = 20;
48 constexpr int32_t TIME_FORMAT_1 = 1;
49 constexpr int32_t TIME_FORMAT_2 = 2;
50 constexpr int32_t COMPRESS_READ_BUF_SIZE = 4096;
51 constexpr int32_t DEFAULT_WIDTH = 20;
52 constexpr int32_t DEFAULT_INTERVAL = 5000;
53 constexpr int32_t MIN_INTERVAL = 100;
54 const std::string TIMESTAMP_TITLE = "timestamp";
55 const std::string THERMAL_LOG_ENABLE = "persist.thermal.log.enable";
56 const std::string THERMAL_LOG_WIDTH = "persist.thermal.log.width";
57 const std::string THERMAL_LOG_INTERVAL = "persist.thermal.log.interval";
58 uint32_t g_currentLogIndex = 0;
59 bool g_firstCreate = true;
60 std::deque<std::string> g_saveLogFile;
61 std::string g_outPath = "";
62 std::string g_logTime = "";
63 }
64 
65 std::shared_ptr<ThermalDfx> ThermalDfx::instance_ = nullptr;
66 std::mutex ThermalDfx::mutexInstance_;
67 
GetInstance()68 ThermalDfx& 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 
DestroyInstance()77 void ThermalDfx::DestroyInstance()
78 {
79     std::lock_guard<std::mutex> lock(mutexInstance_);
80     instance_ = nullptr;
81 }
82 
GetCurrentTime(const int32_t format)83 static 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 
ThermalDfx()115 ThermalDfx::ThermalDfx() :
116     width_(static_cast<uint8_t>(DEFAULT_WIDTH)), interval_(static_cast<uint32_t>(DEFAULT_INTERVAL)), enable_(true)
117 {
118 }
119 
~ThermalDfx()120 ThermalDfx::~ThermalDfx()
121 {
122     enable_ = false;
123 }
124 
GetFileNameIndex(const uint32_t index)125 std::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 
CanonicalizeSpecPath(const char * src)133 std::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 
Compress(const std::string & dataFile,const std::string & destFile)162 bool 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 
CompressFile()201 void ThermalDfx::CompressFile()
202 {
203     ThermalHitrace trace("ThermalDfx_CompressFile");
204     THERMAL_HILOGD(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     long int size = ftell(fp);
220     if (size < MAX_FILE_SIZE) {
221         if (fclose(fp) < 0) {
222             THERMAL_HILOGW(COMP_HDI, "fclose() failed");
223         }
224         THERMAL_HILOGD(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_HILOGD(COMP_HDI, "CompressFile done");
252 }
253 
PrepareWriteDfxLog()254 bool 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_HILOGD(COMP_HDI, "param does not start recording");
262         return false;
263     }
264 
265     return true;
266 }
267 
CreateLogFile()268 void ThermalDfx::CreateLogFile()
269 {
270     ThermalHitrace trace("ThermalDfx_CreateLogFile");
271     THERMAL_HILOGD(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_HILOGD(COMP_HDI, "CreateLogFile done");
302 }
303 
ProcessLogInfo(std::string & logFile,bool isEmpty)304 void 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 
WriteToEmptyFile(std::ofstream & wStream,std::string & currentTime)319 void 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 
WriteToFile(std::ofstream & wStream,std::string & currentTime)341 void 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 
InfoChangedCallback(const char * key,const char * value,void * context)364 void 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 
WidthWatchCallback(const std::string & value)385 void 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 
IntervalWatchCallback(const std::string & value)391 void 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 
EnableWatchCallback(const std::string & value)397 void ThermalDfx::EnableWatchCallback(const std::string& value)
398 {
399     enable_ = (value == "true");
400 }
401 
GetIntParameter(const std::string & key,const int32_t def,const int32_t minValue)402 int32_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 
GetBoolParameter(const std::string & key,const bool def)408 bool 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 
GetInterval()417 uint32_t ThermalDfx::GetInterval()
418 {
419     return interval_;
420 }
421 
DoWork()422 void ThermalDfx::DoWork()
423 {
424     if (enable_) {
425         CreateLogFile();
426         CompressFile();
427     }
428 }
429 
Init()430 void 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