1 /*
2  * Copyright (c) 2023-2024 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 #include <charconv>
16 #include <chrono>
17 #include <cinttypes>
18 #include <ctime>
19 #include <sys/stat.h>
20 #include <unordered_map>
21 #include <vector>
22 
23 #include "app_caller_event.h"
24 #include "app_event_task_storage.h"
25 #include "file_util.h"
26 #include "hiview_logger.h"
27 #include "parameter_ex.h"
28 #include "string_util.h"
29 #include "time_util.h"
30 #include "trace_flow_controller.h"
31 #include "trace_utils.h"
32 
33 using namespace OHOS::HiviewDFX;
34 
35 namespace OHOS {
36 namespace HiviewDFX {
37 namespace {
38 DEFINE_LOG_TAG("UCollectUtil-TraceCollector");
39 const std::string UNIFIED_SHARE_PATH = "/data/log/hiview/unified_collection/trace/share/";
40 const std::string UNIFIED_SPECIAL_PATH = "/data/log/hiview/unified_collection/trace/special/";
41 const std::string DB_PATH = "/data/log/hiview/unified_collection/trace/";
42 const int64_t XPERF_SIZE = 1750 * 1024 * 1024;
43 const int64_t XPOWER_SIZE = 700 * 1024 * 1024;
44 const int64_t RELIABILITY_SIZE = 750 * 1024 * 1024;
45 const int64_t HIVIEW_SIZE = 350 * 1024 * 1024;
46 const int64_t FOUNDATION_SIZE = 150 * 1024 * 1024;
47 const float TEN_PERCENT_LIMIT = 0.1;
48 
GetActualReliabilitySize()49 int64_t GetActualReliabilitySize()
50 {
51     return  Parameter::IsLaboratoryMode() ? RELIABILITY_SIZE * 5 : RELIABILITY_SIZE; // 5 : laboratory largen 5 times
52 }
53 
54 const std::unordered_map<UCollect::TraceCaller, std::pair<std::string, int64_t>> TRACE_QUOTA = {
55     {UCollect::TraceCaller::XPERF, {"xperf", XPERF_SIZE}},
56     {UCollect::TraceCaller::XPOWER, {"xpower", XPOWER_SIZE}},
57     {UCollect::TraceCaller::RELIABILITY, {"reliability", GetActualReliabilitySize()}},
58     {UCollect::TraceCaller::HIVIEW, {"hiview", HIVIEW_SIZE}},
59     {UCollect::TraceCaller::FOUNDATION, {"foundation", FOUNDATION_SIZE}},
60 };
61 }
62 
CreateTracePath(const std::string & filePath)63 void CreateTracePath(const std::string &filePath)
64 {
65     if (FileUtil::FileExists(filePath)) {
66         return;
67     }
68     if (!CreateMultiDirectory(filePath)) {
69         HIVIEW_LOGE("failed to create multidirectory %{public}s.", filePath.c_str());
70         return;
71     }
72 }
73 
InitTraceDb()74 void TraceFlowController::InitTraceDb()
75 {
76     traceFlowRecord_ = QueryDb();
77     HIVIEW_LOGI("systemTime:%{public}s, callerName:%{public}s, usedSize:%{public}" PRId64,
78         traceFlowRecord_.systemTime.c_str(), traceFlowRecord_.callerName.c_str(),
79         traceFlowRecord_.usedSize);
80 }
81 
InitTraceStorage()82 void TraceFlowController::InitTraceStorage()
83 {
84     CreateTracePath(UNIFIED_SHARE_PATH);
85     CreateTracePath(UNIFIED_SPECIAL_PATH);
86 
87     traceStorage_ = std::make_shared<TraceStorage>(DB_PATH);
88 }
89 
TraceFlowController(UCollect::TraceCaller caller)90 TraceFlowController::TraceFlowController(UCollect::TraceCaller caller) : caller_(caller)
91 {
92     InitTraceStorage();
93     InitTraceDb();
94 }
95 
NeedDump()96 bool TraceFlowController::NeedDump()
97 {
98     std::string nowDays = GetDate();
99     HIVIEW_LOGI("start to dump, nowDays = %{public}s, systemTime = %{public}s.",
100         nowDays.c_str(), traceFlowRecord_.systemTime.c_str());
101     if (nowDays != traceFlowRecord_.systemTime) {
102         // date changes
103         traceFlowRecord_.systemTime = nowDays;
104         traceFlowRecord_.usedSize = 0;
105         return true;
106     }
107     return TRACE_QUOTA.find(caller_) != TRACE_QUOTA.end() ?
108         traceFlowRecord_.usedSize < TRACE_QUOTA.at(caller_).second : true;
109 }
110 
NeedUpload(TraceRetInfo ret)111 bool TraceFlowController::NeedUpload(TraceRetInfo ret)
112 {
113     int64_t traceSize = GetTraceSize(ret);
114     HIVIEW_LOGI("start to upload , traceSize = %{public}" PRId64 ".", traceSize);
115     if (TRACE_QUOTA.find(caller_) == TRACE_QUOTA.end()) {
116         return true;
117     }
118     if (IsLowerLimit(traceFlowRecord_.usedSize, traceSize, TRACE_QUOTA.at(caller_).second)) {
119         traceFlowRecord_.usedSize += traceSize;
120         return true;
121     }
122     return false;
123 }
124 
IsLowerLimit(int64_t nowSize,int64_t traceSize,int64_t limitSize)125 bool TraceFlowController::IsLowerLimit(int64_t nowSize, int64_t traceSize, int64_t limitSize)
126 {
127     if (limitSize == 0) {
128         HIVIEW_LOGE("error, limit size is zero.");
129         return false;
130     }
131 
132     int64_t totalSize = nowSize + traceSize;
133     if (totalSize < limitSize) {
134         return true;
135     }
136 
137     float limit = static_cast<float>(totalSize - limitSize) / limitSize;
138     if (limit > TEN_PERCENT_LIMIT) {
139         return false;
140     }
141     return true;
142 }
143 
StoreDb()144 void TraceFlowController::StoreDb()
145 {
146     if (TRACE_QUOTA.find(caller_) == TRACE_QUOTA.end()) {
147         HIVIEW_LOGI("caller %{public}d not need store", caller_);
148         return;
149     }
150     HIVIEW_LOGI("systemTime:%{public}s, callerName:%{public}s, usedSize:%{public}" PRId64,
151         traceFlowRecord_.systemTime.c_str(), traceFlowRecord_.callerName.c_str(),
152         traceFlowRecord_.usedSize);
153     traceStorage_->Store(traceFlowRecord_);
154 }
155 
GetTraceSize(TraceRetInfo ret)156 int64_t TraceFlowController::GetTraceSize(TraceRetInfo ret)
157 {
158     struct stat fileInfo;
159     int64_t traceSize = 0;
160     for (const auto &tracePath : ret.outputFiles) {
161         int ret = stat(tracePath.c_str(), &fileInfo);
162         if (ret != 0) {
163             HIVIEW_LOGE("%{public}s is not exists, ret = %{public}d.", tracePath.c_str(), ret);
164             continue;
165         }
166         traceSize += fileInfo.st_size;
167     }
168     return traceSize;
169 }
170 
GetDate()171 std::string TraceFlowController::GetDate()
172 {
173     std::string dateStr = TimeUtil::TimestampFormatToDate(std::time(nullptr), "%Y-%m-%d");
174     return dateStr;
175 }
176 
QueryDb()177 TraceFlowRecord TraceFlowController::QueryDb()
178 {
179     struct TraceFlowRecord tmpTraceFlowRecord;
180     if (TRACE_QUOTA.find(caller_) == TRACE_QUOTA.end()) {
181         return tmpTraceFlowRecord;
182     }
183     tmpTraceFlowRecord.callerName = TRACE_QUOTA.at(caller_).first;
184     traceStorage_->Query(tmpTraceFlowRecord);
185     return tmpTraceFlowRecord;
186 }
187 
HasCallOnceToday(int32_t uid,uint64_t happenTime)188 bool TraceFlowController::HasCallOnceToday(int32_t uid, uint64_t happenTime)
189 {
190     uint64_t happenTimeInSecond = happenTime / TimeUtil::SEC_TO_MILLISEC;
191     std::string date = TimeUtil::TimestampFormatToDate(happenTimeInSecond, "%Y%m%d");
192 
193     AppEventTask appEventTask;
194     appEventTask.id_ = 0;
195     int32_t dateNum = 0;
196     auto result = std::from_chars(date.c_str(), date.c_str() + date.size(), dateNum);
197     if (result.ec != std::errc()) {
198         HIVIEW_LOGW("convert error, dateStr: %{public}s", date.c_str());
199         return false;
200     }
201     traceStorage_->QueryAppEventTask(uid, dateNum, appEventTask);
202     return appEventTask.id_ > 0;
203 }
204 
RecordCaller(std::shared_ptr<AppCallerEvent> appEvent)205 bool TraceFlowController::RecordCaller(std::shared_ptr<AppCallerEvent> appEvent)
206 {
207     uint64_t happenTimeInSecond = appEvent->happenTime_ / TimeUtil::SEC_TO_MILLISEC;
208     std::string date = TimeUtil::TimestampFormatToDate(happenTimeInSecond, "%Y%m%d");
209     int64_t dateNum = 0;
210     auto result = std::from_chars(date.c_str(), date.c_str() + date.size(), dateNum);
211     if (result.ec != std::errc()) {
212         HIVIEW_LOGW("convert error, dateStr: %{public}s", date.c_str());
213         return false;
214     }
215     AppEventTask appEventTask;
216     appEventTask.taskDate_ = dateNum;
217     appEventTask.taskType_ = APP_EVENT_TASK_TYPE_JANK_EVENT;
218     appEventTask.uid_ = appEvent->uid_;
219     appEventTask.pid_ = appEvent->pid_;
220     appEventTask.bundleName_ = appEvent->bundleName_;
221     appEventTask.bundleVersion_ = appEvent->bundleVersion_;
222     appEventTask.startTime_ = appEvent->taskBeginTime_;
223     appEventTask.finishTime_ = appEvent->taskEndTime_;
224     appEventTask.resourePath_ = appEvent->externalLog_;
225     appEventTask.resourceSize_ = static_cast<int32_t>(FileUtil::GetFileSize(appEventTask.resourePath_));
226     appEventTask.state_ = APP_EVENT_TASK_STATE_FINISH;
227     return traceStorage_->StoreAppEventTask(appEventTask);
228 }
229 
CleanOldAppTrace()230 void TraceFlowController::CleanOldAppTrace()
231 {
232     UCollect::TraceCaller caller = UCollect::TraceCaller::APP;
233     FileRemove(caller);
234 
235     uint64_t timeNow = TimeUtil::GetMilliseconds() / TimeUtil::SEC_TO_MILLISEC;
236     uint32_t secondsOfThreeDays = 3 * TimeUtil::SECONDS_PER_DAY; // 3 : clean data three days ago
237     if (timeNow < secondsOfThreeDays) {
238         HIVIEW_LOGW("time is invalid");
239         return;
240     }
241     uint64_t timeThreeDaysAgo = timeNow - secondsOfThreeDays;
242     std::string dateThreeDaysAgo = TimeUtil::TimestampFormatToDate(timeThreeDaysAgo, "%Y%m%d");
243     int32_t dateNum = 0;
244     auto result = std::from_chars(dateThreeDaysAgo.c_str(),
245         dateThreeDaysAgo.c_str() + dateThreeDaysAgo.size(), dateNum);
246     if (result.ec != std::errc()) {
247         HIVIEW_LOGW("convert error, dateStr: %{public}s", dateThreeDaysAgo.c_str());
248         return;
249     }
250     traceStorage_->RemoveOldAppEventTask(dateNum);
251 }
252 } // HiViewDFX
253 } // OHOS
254