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