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