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(&current);
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