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