1 /*
2 * Copyright (c) 2021 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 "feature_analysis.h"
16
17 #include <algorithm>
18 #include <list>
19 #include <regex>
20 #include <string>
21 #include <vector>
22
23 #include "file_util.h"
24 #include "log_util.h"
25 #include "hiview_logger.h"
26 #include "string_util.h"
27
28 using namespace std;
29 namespace OHOS {
30 namespace HiviewDFX {
31 DEFINE_LOG_TAG("FeatureAnalysis");
32
33 const string FeatureAnalysis::COMPOSE_PLUS = "+";
34 const string FeatureAnalysis::COMPOSE_COLON = ":";
35 namespace {
36 const string PARAMETER_REASON = "REASON";
37 }
38
~FeatureAnalysis()39 FeatureAnalysis::~FeatureAnalysis()
40 {
41 HIVIEW_LOGD("<%{public}d> FeatureAnalysis::~FeatureAnalysis.", taskId_);
42 }
43
AnalysisLog()44 bool FeatureAnalysis::AnalysisLog()
45 {
46 HIVIEW_LOGI("<%{public}d> begin analysis <%{public}s>, eventType is <%{public}s>.", taskId_,
47 featureSet_.fullPath.c_str(), eventType_.c_str());
48
49 Extract();
50 if (paramSeekRecord_.empty()) {
51 errorCode_ = EXTRACT_ERROR;
52 return false;
53 }
54 Compose();
55 return true;
56 }
57
Extract()58 void FeatureAnalysis::Extract()
59 {
60 stringstream buffer("");
61 if (!LogUtil::ReadFileBuff(featureSet_.fullPath, buffer) || !buffer.good() || buffer.eof()) {
62 errorCode_ = BUFFER_ERROR;
63 HIVIEW_LOGE("<%{public}d> file is invalid", taskId_);
64 return;
65 }
66
67 // extract info
68 RawInfoPosition(buffer);
69 }
70
RawInfoPosition(stringstream & buffer)71 void FeatureAnalysis::RawInfoPosition(stringstream& buffer)
72 {
73 int skipStep = (featureSet_.skipStep > 0) ? featureSet_.skipStep : MAX_SKIP_LINE;
74 int dismatchCount = 1; // default : countDismatch - 1 >= skipSpace
75 string line;
76 bool segmentStart = false;
77 HIVIEW_LOGI("<%{public}d> skipStep is %{public}d. size:%{public}zu", taskId_, skipStep, featureSet_.rules.size());
78 while (getline(buffer, line)) {
79 if (line.length() > 2048 || // 2048 : max length of line
80 (CheckStartSegment(segmentStart) && (line.empty() || line[0] == ' ' || line[0] == '\t'))) {
81 continue;
82 }
83 GetCursorInfo(buffer, line);
84 for (auto iterCmd = featureSet_.rules.begin(); iterCmd != featureSet_.rules.end();) {
85 // Check the variable symbol and replace it with the parameter value of the variable
86 FeatureRule& featureCmd = *iterCmd;
87
88 if (CheckDepend(featureCmd) || (!CheckVariableParam(featureCmd)) || (!IsSourceMatch(line, featureCmd))) {
89 iterCmd++;
90 continue;
91 }
92 int num = featureCmd.num;
93 bool matchFlag = ParseElementForParam(line, featureCmd);
94 while (--num > 0 && getline(buffer, line)) {
95 GetCursorInfo(buffer, line);
96 ParseElementForParam(line, featureCmd);
97 }
98
99 if (matchFlag && featureCmd.cmdType == L2_RULES) {
100 iterCmd = featureSet_.rules.erase(iterCmd); // erase will iterCmd++, but break avoid out of range
101 } else {
102 iterCmd++;
103 }
104 dismatchCount = 0;
105 break;
106 }
107 dismatchCount++;
108 if (featureSet_.rules.empty() || dismatchCount - 1 >= skipStep) {
109 break;
110 }
111 }
112 }
113
GetCursorInfo(stringstream & buff,const string & line)114 void FeatureAnalysis::GetCursorInfo(stringstream& buff, const string& line)
115 {
116 line_ = line;
117 lineCursor_ = static_cast<int>(buff.tellg()) - static_cast<int>(line.length()) - 1;
118 }
119
CheckStartSegment(bool & segmentStart) const120 bool FeatureAnalysis::CheckStartSegment(bool& segmentStart) const
121 {
122 if (segmentStart) {
123 return segmentStart;
124 }
125 vector<std::pair<std::string, LineFeature>> info = paramSeekRecord_;
126 for (const auto& one : info) {
127 if (one.first.find("LayerTwoCmd") != string::npos ||
128 one.first.find("LayerOneCmd") != string::npos) {
129 segmentStart = true;
130 break;
131 }
132 }
133 return segmentStart;
134 }
135
136 // line match source or not
IsSourceMatch(const string & line,const FeatureRule & rule) const137 bool FeatureAnalysis::IsSourceMatch(const string& line, const FeatureRule& rule) const
138 {
139 string cmdSrc = rule.source;
140 // if startwith "@R@"
141 if (L3_REGULAR_DESCRIPTOR == cmdSrc.substr(0, L3_REGULAR_DESCRIPTOR.length())) {
142 cmdSrc = cmdSrc.substr(L3_REGULAR_DESCRIPTOR.length());
143 return regex_search(line, regex(cmdSrc));
144 }
145
146 // handle OR or AND expression
147 bool isOrExp = (cmdSrc.find(L3_OR_DESCRIPTOR) == string::npos) ? false : true;
148 bool isAndExp = (cmdSrc.find(L3_AND_DESCRIPTOR) == string::npos) ? false : true;
149 if (!isOrExp && !isAndExp) {
150 return line.find(cmdSrc) != string::npos;
151 } else if (isOrExp) {
152 return IsMatchOrExpression(line, cmdSrc);
153 } else if (isAndExp) {
154 return IsMatchAndExpression(line, cmdSrc);
155 }
156 return false;
157 }
158
IsMatchOrExpression(const string & line,const string & src) const159 bool FeatureAnalysis::IsMatchOrExpression(const string& line, const string& src) const
160 {
161 vector<string> srcSplit;
162 StringUtil::SplitStr(src, L3_OR_DESCRIPTOR, srcSplit, false, false);
163 for (auto str : srcSplit) {
164 if (line.find(str) != string::npos) {
165 return true;
166 }
167 }
168 return false;
169 }
170
IsMatchAndExpression(const string & line,const string & src) const171 bool FeatureAnalysis::IsMatchAndExpression(const string& line, const string& src) const
172 {
173 string lineCpy = line;
174 size_t pos;
175 vector<string> srcSplit;
176 StringUtil::SplitStr(src, L3_AND_DESCRIPTOR, srcSplit, false, false);
177 for (auto str : srcSplit) {
178 pos = lineCpy.find(str);
179 if (pos == string::npos) {
180 return false;
181 }
182 lineCpy = lineCpy.substr(pos + str.length());
183 }
184 return true;
185 }
186
ParseElementForParam(const string & src,FeatureRule & rule)187 bool FeatureAnalysis::ParseElementForParam(const string& src, FeatureRule& rule)
188 {
189 if (rule.param.empty()) {
190 return true; // if param is empty, erase the rule
191 }
192
193 bool hasContinue = false;
194 for (auto iter = rule.param.begin(); iter != rule.param.end();) {
195 // subParam.first: parameter name; subParam.second: the expression to match
196 string reg = "";
197 smatch result;
198 int seekType = GetSeekInfo(iter->second, reg);
199 hasContinue = (seekType == LAST_MATCH) ? true : hasContinue;
200 if (reg.find(L3_VARIABLE_TRACE_BLOCK) != string::npos || regex_search(src, result, regex(reg))) {
201 string value = result.str(1).empty() ? "" : string(result.str(1));
202 SetParamRecord(rule.name + "." + iter->first, FormatLineFeature(value, reg), seekType);
203 SetStackRegex(rule.name + "." + iter->first, reg);
204 if (seekType == FIRST_MATCH && rule.cmdType == L2_RULES) {
205 iter = rule.param.erase(iter);
206 } else {
207 iter++;
208 }
209 } else {
210 iter++;
211 }
212 }
213
214 return hasContinue ? false : rule.param.empty();
215 }
216
GetSeekInfo(const string & param,string & value) const217 int FeatureAnalysis::GetSeekInfo(const string& param, string& value) const
218 {
219 if (param.find(L3_SEEK_LAST) != string::npos) {
220 value = StringUtil::GetRightSubstr(param, L3_SEEK_LAST);
221 return LAST_MATCH;
222 }
223 value = param;
224 return FIRST_MATCH;
225 }
226
CheckVariableParam(FeatureRule & rule) const227 bool FeatureAnalysis::CheckVariableParam(FeatureRule& rule) const
228 {
229 // Check whether there is a variable operator &@& in the command
230 string symbol = "";
231 string value = "";
232 bool hasValSymbol = CheckVariable(rule, L3_DESCRIPTOR_LEFT, L3_DESCRIPTOR_RIGHT);
233 if (!hasValSymbol) {
234 return true;
235 }
236 // To replace it if there is one or more variable symbol
237 for (const auto& param : paramSeekRecord_) {
238 symbol = L3_DESCRIPTOR_LEFT + param.first + L3_DESCRIPTOR_RIGHT;
239 value = param.second.value;
240 ReplaceVariable(rule, symbol, value);
241 }
242 return !CheckVariable(rule, L3_DESCRIPTOR_LEFT, L3_DESCRIPTOR_RIGHT); // check var in config
243 }
244
CheckVariable(const FeatureRule & rule,const string & leftTag,const string & rightTag) const245 bool FeatureAnalysis::CheckVariable(const FeatureRule& rule, const string& leftTag, const string& rightTag) const
246 {
247 if ((rule.source.find(leftTag) != string::npos && rule.source.find(rightTag) != string::npos) ||
248 (rule.depend.find(leftTag) != string::npos && rule.depend.find(rightTag) != string::npos)) {
249 return true;
250 }
251 for (auto subParam : rule.param) {
252 if (subParam.second.find(leftTag) != string::npos && subParam.second.find(rightTag) != string::npos) {
253 return true;
254 }
255 }
256
257 return false;
258 }
259
ReplaceVariable(FeatureRule & rule,const string & symbol,const string & value) const260 void FeatureAnalysis::ReplaceVariable(FeatureRule& rule, const string& symbol, const string& value) const
261 {
262 ReplaceVariable(rule.source, symbol, value, rule.source);
263 ReplaceVariable(rule.depend, symbol, value, rule.depend);
264 for (auto subParam : rule.param) {
265 if (ReplaceVariable(subParam.second, symbol, value, subParam.second)) {
266 rule.param[subParam.first] = subParam.second;
267 }
268 }
269 }
270
ReplaceVariable(const string & src,const string & param,const string & value,string & des) const271 bool FeatureAnalysis::ReplaceVariable(const string& src, const string& param,
272 const string& value, string& des) const
273 {
274 des = src;
275 size_t pos = src.find(param);
276 if (pos != string::npos) {
277 des.replace(pos, param.length(), value, 0, value.length());
278 return true;
279 }
280
281 return false;
282 }
283
284 /*
285 * return false if the depend element does not exist
286 * return false if the depend element exists and the feature that the depend element relies on has a value
287 * return true if the depend element exists and the feature that the depend element relies on has no value
288 */
CheckDepend(const FeatureRule & rule) const289 bool FeatureAnalysis::CheckDepend(const FeatureRule& rule) const
290 {
291 bool result = false;
292 if (rule.depend.empty()) {
293 return result;
294 }
295
296 if (paramSeekRecord_.empty()) { // depend exist but no value
297 return true;
298 }
299
300 result = true;
301 for (const auto& one : paramSeekRecord_) {
302 if (one.first.find(rule.depend) != string::npos) {
303 result = false; // depend exist but value exist
304 break;
305 }
306 }
307 return result;
308 }
309
FormatLineFeature(const string & value,const string & regex) const310 LineFeature FeatureAnalysis::FormatLineFeature(const string& value, const string& regex) const
311 {
312 LineFeature paramRecord{};
313 paramRecord.value = value;
314 paramRecord.lineCursor = lineCursor_;
315 return paramRecord;
316 }
317
318 /*
319 * info : eventinfo_ or relatedInfo_ result
320 * params : rule info, for example : key : value
321 * "SUBJECT": "BasicParam.s_subject",
322 * "END_STACK": "MainCallTrace.s_trust_stack",
323 * features : Log feature
324 */
Compose()325 void FeatureAnalysis::Compose()
326 {
327 string result;
328 for (const auto& param : composeRule_) {
329 // keep ordered rule_prio, REASON is specially composed
330 if (eventInfo_.find(param.first) != eventInfo_.end() && param.first != PARAMETER_REASON) {
331 continue;
332 }
333 result = ComposeParam(param.second);
334 if (!result.empty()) {
335 eventInfo_[param.first] = (param.first == PARAMETER_REASON) ?
336 (eventInfo_[param.first] + COMPOSE_COLON + result) : result;
337 }
338 }
339 ProcessReason(eventInfo_);
340 }
341
ComposeTrace(const string & filePath,const string & param,const vector<pair<string,LineFeature>> & lineFeatures,const string & regex) const342 string FeatureAnalysis::ComposeTrace(const string& filePath, const string& param,
343 const vector<pair<string, LineFeature>>& lineFeatures, const string& regex) const
344 {
345 string result;
346 auto iter = find_if(lineFeatures.begin(), lineFeatures.end(),
347 [¶m](const pair<string, LineFeature>& one) {return one.first == param;});
348 if (iter != lineFeatures.end()) {
349 stringstream buffer("");
350 if (LogUtil::ReadFileBuff(filePath, buffer)) {
351 LogUtil::GetTrace(buffer, iter->second.lineCursor, regex, result);
352 }
353 }
354 return result;
355 }
356
ComposeParam(const string & param) const357 string FeatureAnalysis::ComposeParam(const string& param) const
358 {
359 std::vector<std::pair<std::string, LineFeature>> lineFeatures = paramSeekRecord_;
360 vector<string> params = SplitParam(param);
361 vector<string> results;
362 for (const auto& key : params) {
363 auto iter = find_if(lineFeatures.begin(), lineFeatures.end(),
364 [&key](const pair<string, LineFeature>& one) {return one.first == key;});
365 if (iter != lineFeatures.end()) {
366 auto regexIter = stackRegex_.find(key);
367 if (regexIter != stackRegex_.end()) {
368 auto value = ComposeTrace(featureSet_.fullPath, key, lineFeatures, regexIter->second);
369 results.emplace_back(value);
370 } else {
371 results.emplace_back(iter->second.value);
372 }
373 }
374 }
375
376 string tag;
377 if (!results.empty()) {
378 if (param.find(COMPOSE_PLUS) != string::npos) {
379 tag = " ";
380 } else if (param.find(COMPOSE_COLON) != string::npos) {
381 tag = COMPOSE_COLON;
382 } else {
383 tag = "";
384 }
385 }
386 string result = StringUtil::VectorToString(results, false, tag);
387 auto end = result.size() - tag.size();
388 return (end > 0) ? result.substr(0, end) : result;
389 }
390
SplitParam(const string & param) const391 vector<string> FeatureAnalysis::SplitParam(const string& param) const
392 {
393 vector<string> params;
394 if (param.find(COMPOSE_PLUS) != string::npos) {
395 StringUtil::SplitStr(param, COMPOSE_PLUS, params, false, false);
396 } else if (param.find(COMPOSE_COLON) != string::npos) {
397 StringUtil::SplitStr(param, COMPOSE_COLON, params, false, false);
398 } else {
399 params.emplace_back(param);
400 }
401 return params;
402 }
403
ProcessReason(map<string,string> & info)404 void FeatureAnalysis::ProcessReason(map<string, string>& info)
405 {
406 if (info.find(PARAMETER_REASON) != info.end() &&
407 info[PARAMETER_REASON].substr(0, 1) == COMPOSE_COLON) { // 1: first char
408 info[PARAMETER_REASON] = info[PARAMETER_REASON].substr(1);
409 }
410 }
411
SetParamRecord(const std::string & key,const LineFeature & value,const int type)412 void FeatureAnalysis::SetParamRecord(const std::string& key, const LineFeature& value, const int type)
413 {
414 if (type == LAST_MATCH) {
415 for (auto iter = paramSeekRecord_.begin(); iter != paramSeekRecord_.end(); iter++) {
416 if (iter->first == key) {
417 paramSeekRecord_.erase(iter);
418 break;
419 }
420 }
421 }
422 paramSeekRecord_.emplace_back(pair<string, LineFeature>(key, value));
423 }
424
SetStackRegex(const std::string & key,const std::string & regex)425 void FeatureAnalysis::SetStackRegex(const std::string& key, const std::string& regex)
426 {
427 if (regex.find(L3_VARIABLE_TRACE_BLOCK) != string::npos) {
428 stackRegex_.emplace(pair(key, StringUtil::EraseString(regex, L3_VARIABLE_TRACE_BLOCK)));
429 }
430 }
431 } // namespace HiviewDFX
432 } // namespace OHOS
433