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 "dfx_log_dump.h"
17 #include <fstream>
18 #include <unistd.h>
19 #include <malloc.h>
20 #include <sys/time.h>
21 #include "securec.h"
22 
23 namespace {
24 constexpr int32_t FILE_MAX = 100;
25 constexpr int32_t FILE_LINE_MAX = 50000;
26 }
27 namespace OHOS {
28 namespace Media {
GetInstance()29 DfxLogDump &DfxLogDump::GetInstance()
30 {
31     static DfxLogDump dfxLogDump;
32     return dfxLogDump;
33 }
34 
DfxLogDump()35 DfxLogDump::DfxLogDump()
36 {
37     thread_ = std::make_unique<std::thread>([this] () -> void { this->TaskProcessor(); });
38 }
39 
~DfxLogDump()40 DfxLogDump::~DfxLogDump()
41 {
42     {
43         std::unique_lock<std::mutex> lock(mutex_);
44         isExit_ = true;
45         cond_.notify_all();
46     }
47     if (thread_ != nullptr && thread_->joinable()) {
48         thread_->join();
49     }
50 }
51 
AddNewLog(const char * level,const OHOS::HiviewDFX::HiLogLabel & label,std::string & logStr)52 static void AddNewLog(const char *level, const OHOS::HiviewDFX::HiLogLabel &label, std::string &logStr)
53 {
54     struct timeval time = {};
55     (void)gettimeofday(&time, nullptr);
56     int64_t second = time.tv_sec % 60;
57     int64_t allMinute = time.tv_sec / 60;
58     int64_t minute = allMinute % 60;
59     int64_t hour = allMinute / 60 % 24;
60     int64_t mSecond = time.tv_usec / 1000;
61 
62     logStr += std::to_string(hour);
63     logStr += ":";
64     logStr += std::to_string(minute);
65     logStr += ":";
66     logStr += std::to_string(second);
67     logStr += ":";
68     logStr += std::to_string(mSecond);
69     logStr += " ";
70     logStr += level;
71     logStr += " pid:";
72     logStr += std::to_string(getpid());
73     logStr += " tid:";
74     logStr += std::to_string(gettid());
75     logStr += " ";
76     logStr += label.tag;
77     logStr += ":";
78 }
79 
SaveLog(const char * level,const OHOS::HiviewDFX::HiLogLabel & label,const char * fmt,...)80 void DfxLogDump::SaveLog(const char *level, const OHOS::HiviewDFX::HiLogLabel &label, const char *fmt, ...)
81 {
82     std::unique_lock<std::mutex> lock(mutex_);
83     if (!isEnable_) {
84         return;
85     }
86     std::string temp = "";
87     std::string fmtStr = fmt;
88     int32_t srcPos = 0;
89     auto dtsPos = fmtStr.find("{public}", srcPos);
90     const int32_t pubLen = 8;
91     while (dtsPos != std::string::npos) {
92         temp += fmtStr.substr(srcPos, dtsPos - srcPos);
93         srcPos = static_cast<int32_t>(dtsPos) + pubLen;
94         dtsPos = fmtStr.find("{public}", srcPos);
95     }
96     temp += fmtStr.substr(srcPos);
97 
98     va_list ap;
99     va_start(ap, fmt);
100     constexpr uint32_t maxLogLen = 1024;
101     char logBuf[maxLogLen];
102     auto ret = vsnprintf_s(logBuf, maxLogLen, maxLogLen - 1, temp.c_str(), ap);
103     va_end(ap);
104 
105     AddNewLog(level, label, logString_);
106     if (ret < 0) {
107         logString_ += "dump log error";
108     } else {
109         logString_ += logBuf;
110     }
111     logString_ += "\n";
112     lineCount_++;
113     if (lineCount_ >= FILE_LINE_MAX) {
114         cond_.notify_all();
115     }
116 }
117 
UpdateCheckEnable()118 void DfxLogDump::UpdateCheckEnable()
119 {
120     std::string file = "/data/test/log/check.config";
121     std::ofstream ofStream(file);
122     if (!ofStream.is_open()) {
123         isEnable_ = false;
124         return;
125     }
126     ofStream.close();
127     isEnable_ = true;
128 }
129 
TaskProcessor()130 void DfxLogDump::TaskProcessor()
131 {
132     pthread_setname_np(pthread_self(), "DfxLogTask");
133     (void)mallopt(M_SET_THREAD_CACHE, M_THREAD_CACHE_DISABLE);
134     (void)mallopt(M_DELAYED_FREE, M_DELAYED_FREE_DISABLE);
135     while (true) {
136         std::string temp;
137         int32_t lineCount = 0;
138         {
139             std::unique_lock<std::mutex> lock(mutex_);
140             if (isExit_) {
141                 return;
142             }
143             static constexpr int32_t timeout = 60; // every 1 minute have a log
144             cond_.wait_for(lock, std::chrono::seconds(timeout),
145                 [this] {
146                     UpdateCheckEnable();
147                     return isExit_ || isDump_ || lineCount_ >= FILE_LINE_MAX || !logString_.empty();
148             });
149             isDump_ = false;
150             lineCount = lineCount_;
151             lineCount_ = lineCount_ >= FILE_LINE_MAX ? 0 : lineCount_;
152             swap(logString_, temp);
153         }
154 
155         std::string file = "/data/test/log/";
156         file += std::to_string(getpid());
157         file += "_hilog_media.log";
158         file += std::to_string(fileCount_);
159         std::ofstream ofStream;
160         if (isNewFile_) {
161             ofStream.open(file, std::ios::out | std::ios::trunc);
162         } else {
163             ofStream.open(file, std::ios::out | std::ios::app);
164         }
165         if (!ofStream.is_open()) {
166             continue;
167         }
168         isNewFile_ = false;
169         if (lineCount >= FILE_LINE_MAX) {
170             isNewFile_ = true;
171             fileCount_++;
172             fileCount_ = fileCount_ > FILE_MAX ? 0 : fileCount_;
173         }
174         ofStream.write(temp.c_str(), temp.size());
175         ofStream.close();
176     }
177 }
178 } // namespace Media
179 } // namespace OHOS
180