1 /*
2 * Copyright (C) 2023-2025 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 "trace_storage.h"
16
17 #include <cinttypes>
18
19 #include "cjson_util.h"
20 #include "hiview_logger.h"
21 #include "time_util.h"
22
23 namespace OHOS {
24 namespace HiviewDFX {
25 namespace {
26 DEFINE_LOG_TAG("TraceStorage");
27 constexpr char TABLE_NAME[] = "trace_flow_control";
28 constexpr char COLUMN_SYSTEM_TIME[] = "system_time";
29 constexpr char COLUMN_CALLER_NAME[] = "caller_name";
30 constexpr char COLUMN_USED_SIZE[] = "used_size";
31 constexpr char COLUMN_DYNAMIC_DECREASE[] = "dynamic_decrease";
32 constexpr char DYNAMIC_DECREASE_KEY[] = "DecreaseUnit";
33 constexpr char TRACE_QUOTA_CONFIG_FILE[] = "trace_quota_config.json";
34 const float TEN_PERCENT_LIMIT = 0.1;
35
GetBucket(const TraceFlowRecord & traceFlowRecord)36 NativeRdb::ValuesBucket GetBucket(const TraceFlowRecord& traceFlowRecord)
37 {
38 NativeRdb::ValuesBucket bucket;
39 bucket.PutString(COLUMN_SYSTEM_TIME, traceFlowRecord.systemTime);
40 bucket.PutString(COLUMN_CALLER_NAME, traceFlowRecord.callerName);
41 bucket.PutLong(COLUMN_USED_SIZE, traceFlowRecord.usedSize);
42 bucket.PutLong(COLUMN_DYNAMIC_DECREASE, traceFlowRecord.dynamicDecrease);
43 return bucket;
44 }
45 }
46
TraceStorage(std::shared_ptr<NativeRdb::RdbStore> dbStore,const std::string & caller,const std::string & configPath)47 TraceStorage::TraceStorage(std::shared_ptr<NativeRdb::RdbStore> dbStore, const std::string& caller,
48 const std::string& configPath): caller_(caller), dbStore_(dbStore)
49 {
50 traceQuotaConfig_ = configPath + TRACE_QUOTA_CONFIG_FILE;
51 quota_ = GetTraceQuota(caller);
52 decreaseUnit_ = GetTraceQuota(DYNAMIC_DECREASE_KEY);
53 #ifdef TRACE_MANAGER_UNITTEST
54 testDate_ = TimeUtil::TimestampFormatToDate(std::time(nullptr), "%Y-%m-%d");
55 #endif
56 InitTableRecord();
57 }
58
InitTableRecord()59 void TraceStorage::InitTableRecord()
60 {
61 traceFlowRecord_.callerName = caller_;
62 Query(traceFlowRecord_);
63 HIVIEW_LOGI("systemTime:%{public}s, callerName:%{public}s, usedSize:%{public}" PRId64 " threshold:%{public}" PRId64,
64 traceFlowRecord_.systemTime.c_str(), traceFlowRecord_.callerName.c_str(), traceFlowRecord_.usedSize,
65 traceFlowRecord_.dynamicDecrease);
66 }
67
Store(const TraceFlowRecord & traceFlowRecord)68 void TraceStorage::Store(const TraceFlowRecord& traceFlowRecord)
69 {
70 if (dbStore_ == nullptr) {
71 HIVIEW_LOGE("db store is null,");
72 return;
73 }
74 TraceFlowRecord tmpTraceFlowRecord = {.callerName = traceFlowRecord.callerName};
75 Query(tmpTraceFlowRecord);
76 if (!tmpTraceFlowRecord.systemTime.empty()) { // time not empty means record exist
77 UpdateTable(traceFlowRecord);
78 } else {
79 InsertTable(traceFlowRecord);
80 }
81 }
82
UpdateTable(const TraceFlowRecord & traceFlowRecord)83 void TraceStorage::UpdateTable(const TraceFlowRecord& traceFlowRecord)
84 {
85 NativeRdb::ValuesBucket bucket = GetBucket(traceFlowRecord);
86 NativeRdb::AbsRdbPredicates predicates(TABLE_NAME);
87 predicates.EqualTo(COLUMN_CALLER_NAME, traceFlowRecord.callerName);
88 int changeRows = 0;
89 if (dbStore_->Update(changeRows, bucket, predicates) != NativeRdb::E_OK) {
90 HIVIEW_LOGW("failed to update table");
91 }
92 }
93
InsertTable(const TraceFlowRecord & traceFlowRecord)94 void TraceStorage::InsertTable(const TraceFlowRecord& traceFlowRecord)
95 {
96 NativeRdb::ValuesBucket bucket = GetBucket(traceFlowRecord);
97 int64_t seq = 0;
98 if (dbStore_->Insert(seq, TABLE_NAME, bucket) != NativeRdb::E_OK) {
99 HIVIEW_LOGW("failed to insert table");
100 }
101 }
102
Query(TraceFlowRecord & traceFlowRecord)103 void TraceStorage::Query(TraceFlowRecord& traceFlowRecord)
104 {
105 if (dbStore_ == nullptr) {
106 HIVIEW_LOGE("db store is null");
107 return;
108 }
109 QueryTable(traceFlowRecord);
110 }
111
QueryTable(TraceFlowRecord & traceFlowRecord)112 void TraceStorage::QueryTable(TraceFlowRecord& traceFlowRecord)
113 {
114 NativeRdb::AbsRdbPredicates predicates(TABLE_NAME);
115 predicates.EqualTo(COLUMN_CALLER_NAME, traceFlowRecord.callerName);
116 auto resultSet = dbStore_->Query(predicates, {COLUMN_SYSTEM_TIME, COLUMN_USED_SIZE, COLUMN_DYNAMIC_DECREASE});
117 if (resultSet == nullptr) {
118 HIVIEW_LOGE("failed to query from table %{public}s, db is null", TABLE_NAME);
119 return;
120 }
121
122 if (resultSet->GoToNextRow() == NativeRdb::E_OK) {
123 resultSet->GetString(0, traceFlowRecord.systemTime); // 0 means system_time field
124 resultSet->GetLong(1, traceFlowRecord.usedSize); // 1 means used_size field
125 resultSet->GetLong(2, traceFlowRecord.dynamicDecrease); // 2 means dynamic_threshold field
126 }
127 resultSet->Close();
128 }
129
GetDate()130 std::string TraceStorage::GetDate()
131 {
132 #ifndef TRACE_MANAGER_UNITTEST
133 std::string dateStr = TimeUtil::TimestampFormatToDate(std::time(nullptr), "%Y-%m-%d");
134 return dateStr;
135 #else
136 return testDate_;
137 #endif
138 }
139
140 // remaining trace size contains 10% fluctuation of the quota when quota not completely used up
GetRemainingTraceSize()141 int64_t TraceStorage::GetRemainingTraceSize()
142 {
143 if (quota_ <= 0) {
144 return 0;
145 }
146 if (IsDateChange()) {
147 return quota_ + quota_ * TEN_PERCENT_LIMIT;
148 }
149 if (quota_ <= traceFlowRecord_.usedSize) {
150 return 0;
151 }
152 return (quota_ - traceFlowRecord_.usedSize) + quota_ * TEN_PERCENT_LIMIT;
153 }
154
IsOverLimit()155 bool TraceStorage::IsOverLimit()
156 {
157 if (quota_ <= 0) {
158 return true;
159 }
160 if (IsDateChange()) {
161 return false;
162 }
163 return (quota_ - traceFlowRecord_.dynamicDecrease) < traceFlowRecord_.usedSize;
164 }
165
DecreaseDynamicThreshold()166 void TraceStorage::DecreaseDynamicThreshold()
167 {
168 if (quota_ <= 0) {
169 return;
170 }
171 traceFlowRecord_.dynamicDecrease += decreaseUnit_;
172 Store(traceFlowRecord_);
173 }
174
StoreDb(int64_t traceSize)175 void TraceStorage::StoreDb(int64_t traceSize)
176 {
177 if (quota_ <= 0) {
178 return;
179 }
180 traceFlowRecord_.usedSize += traceSize;
181 HIVIEW_LOGI("systemTime:%{public}s, callerName:%{public}s, usedSize:%{public}" PRId64,
182 traceFlowRecord_.systemTime.c_str(), traceFlowRecord_.callerName.c_str(), traceFlowRecord_.usedSize);
183 Store(traceFlowRecord_);
184 }
185
GetTraceQuota(const std::string & key)186 int64_t TraceStorage::GetTraceQuota(const std::string& key)
187 {
188 auto root = CJsonUtil::ParseJsonRoot(traceQuotaConfig_);
189 if (root == nullptr) {
190 HIVIEW_LOGW("failed to parse config");
191 return -1;
192 }
193 int64_t traceQuota = CJsonUtil::GetIntValue(root, key, 0);
194 if (traceQuota <= 0) {
195 HIVIEW_LOGW("failed to get value for key=%{public}s", key.c_str());
196 }
197 cJSON_Delete(root);
198 return traceQuota;
199 }
200
IsDateChange()201 bool TraceStorage::IsDateChange()
202 {
203 std::string nowDays = GetDate();
204 HIVIEW_LOGI("start to dump, nowDays = %{public}s, systemTime = %{public}s.",
205 nowDays.c_str(), traceFlowRecord_.systemTime.c_str());
206 if (nowDays != traceFlowRecord_.systemTime) {
207 HIVIEW_LOGD("date changes");
208 traceFlowRecord_.systemTime = nowDays;
209 traceFlowRecord_.usedSize = 0;
210 traceFlowRecord_.dynamicDecrease = 0;
211 return true;
212 }
213 return false;
214 }
215 } // namespace HiviewDFX
216 } // namespace OHOS
217