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 "sys_event_query_wrapper.h"
17
18 #include "doc_query.h"
19 #include "running_status_logger.h"
20 #include "time_util.h"
21
22 namespace OHOS {
23 namespace HiviewDFX {
24 namespace {
25 constexpr char SPACE_CONCAT[] = " ";
26 constexpr int DEFAULT_CONCURRENT_CNT = 0;
27
GetCurTime()28 time_t GetCurTime()
29 {
30 time_t current;
31 (void)time(¤t);
32 return current;
33 }
34
GetSubStrCount(const std::string & content,const std::string & sub)35 int GetSubStrCount(const std::string& content, const std::string& sub)
36 {
37 int cnt = 0;
38 if (content.empty() || sub.empty()) {
39 return cnt;
40 }
41 size_t start = 0;
42 while ((start = content.find(sub, start)) != std::string::npos) {
43 start += sub.size();
44 cnt++;
45 }
46 return cnt;
47 }
48 }
49
50 namespace EventStore {
LogTooManyQueryRules(const std::string sql)51 void QueryStatusLogUtil::LogTooManyQueryRules(const std::string sql)
52 {
53 std::string info { "PLUGIN TOOMANYQUERYCONDITION SQL=" };
54 info.append(sql);
55 Logging(info);
56 }
57
LogTooManyConcurrentQueries(const int limit,bool innerQuery)58 void QueryStatusLogUtil::LogTooManyConcurrentQueries(const int limit, bool innerQuery)
59 {
60 std::string info { innerQuery ? "HIVIEW TOOMANYCOCURRENTQUERIES " : "TOOMANYCOCURRENTQUERIES " };
61 info.append("COUNT > ");
62 info.append(std::to_string(limit));
63 Logging(info);
64 }
65
LogQueryOverTime(time_t costTime,const std::string sql,bool innerQuery)66 void QueryStatusLogUtil::LogQueryOverTime(time_t costTime, const std::string sql, bool innerQuery)
67 {
68 std::string info { innerQuery ? "PLUGIN QUERYOVERTIME " : "QUERYOVERTIME " };
69 info.append(std::to_string(costTime)).append(SPACE_CONCAT).append("SQL=").append(sql);
70 Logging(info);
71 }
72
LogQueryCountOverLimit(const int32_t queryCount,const std::string & sql,bool innerQuery)73 void QueryStatusLogUtil::LogQueryCountOverLimit(const int32_t queryCount, const std::string& sql,
74 bool innerQuery)
75 {
76 std::string info { innerQuery ? "PLUGIN QUERYCOUNTOVERLIMIT " : "QUERYCOUNTOVERLIMIT " };
77 info.append(std::to_string(queryCount)).append(SPACE_CONCAT).append("SQL=").append(sql);
78 Logging(info);
79 }
80
LogQueryTooFrequently(const std::string & sql,const std::string & processName,bool innerQuery)81 void QueryStatusLogUtil::LogQueryTooFrequently(const std::string& sql, const std::string& processName,
82 bool innerQuery)
83 {
84 std::string info { innerQuery ? "HIVIEW QUERYTOOFREQUENTLY " : "QUERYTOOFREQUENTLY " };
85 if (!processName.empty()) {
86 info.append(processName).append(SPACE_CONCAT);
87 }
88 info.append("SQL=").append(sql);
89 Logging(info);
90 }
91
Logging(const std::string & detail)92 void QueryStatusLogUtil::Logging(const std::string& detail)
93 {
94 std::string info = RunningStatusLogger::GetInstance().FormatTimeStamp();
95 info.append(SPACE_CONCAT).append(detail);
96 RunningStatusLogger::GetInstance().Log(info);
97 }
98
99 ConcurrentQueries SysEventQueryWrapper::concurrentQueries_ = { DEFAULT_CONCURRENT_CNT, DEFAULT_CONCURRENT_CNT };
100 LruCache<pid_t, SysEventQueryWrapper::QueryRecord> SysEventQueryWrapper::queryController_;
101 std::mutex SysEventQueryWrapper::lastQueriesMutex_;
102 std::mutex SysEventQueryWrapper::concurrentQueriesMutex_;
Execute(int limit,DbQueryTag tag,QueryProcessInfo callerInfo,DbQueryCallback queryCallback)103 ResultSet SysEventQueryWrapper::Execute(int limit, DbQueryTag tag, QueryProcessInfo callerInfo,
104 DbQueryCallback queryCallback)
105 {
106 ResultSet resultSet;
107 int queryErrorCode = -1;
108 (void)IsConditionCntValid(tag);
109 if (!IsQueryCntLimitValid(tag, limit, queryCallback) ||
110 !IsConcurrentQueryCntValid(tag, queryCallback) ||
111 !IsQueryFrequenceValid(tag, callerInfo, queryCallback)) {
112 resultSet.Set(queryErrorCode, false);
113 return resultSet;
114 }
115 time_t beforeExecute = GetCurTime();
116 IncreaseConcurrentCnt(tag);
117 resultSet = SysEventQuery::Execute(limit, tag, callerInfo, queryCallback);
118 DecreaseConcurrentCnt(tag);
119 time_t afterExecute = GetCurTime();
120 if (!IsQueryCostTimeValid(tag, beforeExecute, afterExecute, queryCallback)) {
121 resultSet.Set(queryErrorCode, false);
122 return resultSet;
123 }
124 if (queryCallback != nullptr) {
125 queryCallback(DbQueryStatus::SUCCEED);
126 }
127 return resultSet;
128 }
129
IsConditionCntValid(const DbQueryTag & tag)130 bool SysEventQueryWrapper::IsConditionCntValid(const DbQueryTag& tag)
131 {
132 const int conditionCntLimit = 7;
133 if (tag.isInnerQuery && GetSubStrCount(this->ToString(), " and ") > conditionCntLimit) {
134 QueryStatusLogUtil::LogTooManyQueryRules(this->ToString());
135 return false;
136 }
137 return true;
138 }
139
IsQueryCntLimitValid(const DbQueryTag & tag,const int limit,const DbQueryCallback & callback)140 bool SysEventQueryWrapper::IsQueryCntLimitValid(const DbQueryTag& tag, const int limit,
141 const DbQueryCallback& callback)
142 {
143 int queryLimit = tag.isInnerQuery ? 50 : 1000;
144 if (limit > queryLimit) {
145 QueryStatusLogUtil::LogQueryCountOverLimit(limit, this->ToString(), tag.isInnerQuery);
146 if (callback != nullptr) {
147 callback(DbQueryStatus::OVER_LIMIT);
148 }
149 return tag.isInnerQuery;
150 }
151 return true;
152 }
153
IsQueryCostTimeValid(const DbQueryTag & tag,const time_t before,const time_t after,const DbQueryCallback & callback)154 bool SysEventQueryWrapper::IsQueryCostTimeValid(const DbQueryTag& tag, const time_t before,
155 const time_t after, const DbQueryCallback& callback)
156 {
157 time_t maxQueryTime = 20;
158 time_t duration = after - before;
159 if (duration < maxQueryTime) {
160 return true;
161 }
162 QueryStatusLogUtil::LogQueryOverTime(duration, this->ToString(), tag.isInnerQuery);
163 if (callback != nullptr) {
164 callback(DbQueryStatus::OVER_TIME);
165 }
166 return tag.isInnerQuery;
167 }
168
IsConcurrentQueryCntValid(const DbQueryTag & tag,const DbQueryCallback & callback)169 bool SysEventQueryWrapper::IsConcurrentQueryCntValid(const DbQueryTag& tag, const DbQueryCallback& callback)
170 {
171 std::lock_guard<std::mutex> lock(concurrentQueriesMutex_);
172 auto& concurrentQueryCnt = tag.isInnerQuery ? concurrentQueries_.first : concurrentQueries_.second;
173 int conCurrentQueryCntLimit = 4;
174 if (concurrentQueryCnt < conCurrentQueryCntLimit) {
175 return true;
176 }
177 QueryStatusLogUtil::LogTooManyConcurrentQueries(conCurrentQueryCntLimit, tag.isInnerQuery);
178 if (callback != nullptr) {
179 callback(DbQueryStatus::CONCURRENT);
180 }
181 return tag.isInnerQuery;
182 }
183
IsQueryFrequenceValid(const DbQueryTag & tag,const QueryProcessInfo & processInfo,const DbQueryCallback & callback)184 bool SysEventQueryWrapper::IsQueryFrequenceValid(const DbQueryTag& tag, const QueryProcessInfo& processInfo,
185 const DbQueryCallback& callback)
186 {
187 std::lock_guard<std::mutex> lock(lastQueriesMutex_);
188 if (!tag.needFrequenceCheck) {
189 return true;
190 }
191 auto queryProcessId = processInfo.first;
192 QueryRecord record = queryController_.Get(queryProcessId);
193 uint64_t cur = TimeUtil::GetMilliseconds();
194 if (!record.IsValid() || (record.begin > cur) || ((cur - record.begin) > QUERY_CONTROL_PERIOD_IN_MILLI_SECONDS)) {
195 record.count = 1; // record the first event querying during one cycle
196 record.begin = cur;
197 queryController_.Put(queryProcessId, record);
198 return true;
199 }
200 record.count++;
201 if (record.count <= QUERY_CONTROL_THRESHOLD) {
202 queryController_.Put(queryProcessId, record);
203 return true;
204 }
205 QueryStatusLogUtil::LogQueryTooFrequently(this->ToString(), processInfo.second, tag.isInnerQuery);
206 if (callback != nullptr) {
207 callback(DbQueryStatus::TOO_FREQENTLY);
208 }
209 return tag.isInnerQuery;
210 }
211
IncreaseConcurrentCnt(const DbQueryTag & tag)212 void SysEventQueryWrapper::IncreaseConcurrentCnt(const DbQueryTag& tag)
213 {
214 std::lock_guard<std::mutex> lock(concurrentQueriesMutex_);
215 auto& concurrentQueryCnt = tag.isInnerQuery ? concurrentQueries_.first : concurrentQueries_.second;
216 concurrentQueryCnt++;
217 }
218
DecreaseConcurrentCnt(const DbQueryTag & tag)219 void SysEventQueryWrapper::DecreaseConcurrentCnt(const DbQueryTag& tag)
220 {
221 std::lock_guard<std::mutex> lock(concurrentQueriesMutex_);
222 auto& concurrentQueryCnt = tag.isInnerQuery ? concurrentQueries_.first : concurrentQueries_.second;
223 concurrentQueryCnt--;
224 }
225 } // namespace EventStore
226 } // namespace HiviewDFX
227 } // namespace OHOS
228