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