• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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             if (CheckDepend(featureCmd)) {
88                 // The depend element exists but the dependent feature has no value
89                 iterCmd++;
90                 continue;
91             }
92 
93             if (CheckVariableParam(featureCmd) && IsSourceMatch(line, featureCmd)) {
94                 int num = featureCmd.num;
95                 bool matchFlag = ParseElementForParam(line, featureCmd);
96                 while (--num > 0 && getline(buffer, line)) {
97                     GetCursorInfo(buffer, line);
98                     ParseElementForParam(line, featureCmd);
99                 }
100 
101                 if (matchFlag && featureCmd.cmdType == L2_RULES) {
102                     iterCmd = featureSet_.rules.erase(iterCmd); // erase will iterCmd++, but break avoid out of range
103                 } else {
104                     iterCmd++;
105                 }
106                 dismatchCount = 0;
107                 break;
108             } else {
109                 iterCmd++;
110             }
111         }
112         dismatchCount++;
113         if (featureSet_.rules.empty() || dismatchCount - 1 >= skipStep) {
114             break;
115         }
116     }
117 }
118 
GetCursorInfo(stringstream & buff,const string & line)119 void FeatureAnalysis::GetCursorInfo(stringstream& buff, const string& line)
120 {
121     line_ = line;
122     lineCursor_ = static_cast<int>(buff.tellg()) - static_cast<int>(line.length()) - 1;
123 }
124 
CheckStartSegment(bool & segmentStart) const125 bool FeatureAnalysis::CheckStartSegment(bool& segmentStart) const
126 {
127     if (segmentStart) {
128         return segmentStart;
129     }
130     vector<std::pair<std::string, LineFeature>> info = paramSeekRecord_;
131     for (const auto& one : info) {
132         if (one.first.find("LayerTwoCmd") != string::npos ||
133             one.first.find("LayerOneCmd") != string::npos) {
134             segmentStart = true;
135             break;
136         }
137     }
138     return segmentStart;
139 }
140 
141 // line match source or not
IsSourceMatch(const string & line,const FeatureRule & rule) const142 bool FeatureAnalysis::IsSourceMatch(const string& line, const FeatureRule& rule) const
143 {
144     string cmdSrc = rule.source;
145     // if startwith "@R@"
146     if (L3_REGULAR_DESCRIPTOR == cmdSrc.substr(0, L3_REGULAR_DESCRIPTOR.length())) {
147         cmdSrc = cmdSrc.substr(L3_REGULAR_DESCRIPTOR.length());
148         return regex_search(line, regex(cmdSrc));
149     }
150 
151     // handle OR or AND expression
152     bool isOrExp = (cmdSrc.find(L3_OR_DESCRIPTOR) == string::npos) ? false : true;
153     bool isAndExp = (cmdSrc.find(L3_AND_DESCRIPTOR) == string::npos) ? false : true;
154     if (!isOrExp && !isAndExp) {
155         return line.find(cmdSrc) != string::npos;
156     } else if (isOrExp) {
157         return IsMatchOrExpression(line, cmdSrc);
158     } else if (isAndExp) {
159         return IsMatchAndExpression(line, cmdSrc);
160     }
161     return false;
162 }
163 
IsMatchOrExpression(const string & line,const string & src) const164 bool FeatureAnalysis::IsMatchOrExpression(const string& line, const string& src) const
165 {
166     vector<string> srcSplit;
167     StringUtil::SplitStr(src, L3_OR_DESCRIPTOR, srcSplit, false, false);
168     for (auto str : srcSplit) {
169         if (line.find(str) != string::npos) {
170             return true;
171         }
172     }
173     return false;
174 }
175 
IsMatchAndExpression(const string & line,const string & src) const176 bool FeatureAnalysis::IsMatchAndExpression(const string& line, const string& src) const
177 {
178     string lineCpy = line;
179     size_t pos;
180     vector<string> srcSplit;
181     StringUtil::SplitStr(src, L3_AND_DESCRIPTOR, srcSplit, false, false);
182     for (auto str : srcSplit) {
183         pos = lineCpy.find(str);
184         if (pos == string::npos) {
185             return false;
186         }
187         lineCpy = lineCpy.substr(pos + str.length());
188     }
189     return true;
190 }
191 
ParseElementForParam(const string & src,FeatureRule & rule)192 bool FeatureAnalysis::ParseElementForParam(const string& src, FeatureRule& rule)
193 {
194     if (rule.param.empty()) {
195         return true; // if param is empty, erase the rule
196     }
197 
198     bool hasContinue = false;
199     for (auto iter = rule.param.begin(); iter != rule.param.end();) {
200         // subParam.first: parameter name; subParam.second: the expression to match
201         string reg = "";
202         smatch result;
203         int seekType = GetSeekInfo(iter->second, reg);
204         hasContinue = (seekType == LAST_MATCH) ? true : hasContinue;
205         if (reg.find(L3_VARIABLE_TRACE_BLOCK) != string::npos || regex_search(src, result, regex(reg))) {
206             string value = result.str(1).empty() ? "" : string(result.str(1));
207             SetParamRecord(rule.name + "." + iter->first, FormatLineFeature(value, reg), seekType);
208             SetStackRegex(rule.name + "." + iter->first, reg);
209             if (seekType == FIRST_MATCH && rule.cmdType == L2_RULES) {
210                 iter = rule.param.erase(iter);
211             } else {
212                 iter++;
213             }
214         } else {
215             iter++;
216         }
217     }
218 
219     return hasContinue ? false : rule.param.empty();
220 }
221 
GetSeekInfo(const string & param,string & value) const222 int FeatureAnalysis::GetSeekInfo(const string& param, string& value) const
223 {
224     if (param.find(L3_SEEK_LAST) != string::npos) {
225         value = StringUtil::GetRightSubstr(param, L3_SEEK_LAST);
226         return LAST_MATCH;
227     }
228     value = param;
229     return FIRST_MATCH;
230 }
231 
CheckVariableParam(FeatureRule & rule) const232 bool FeatureAnalysis::CheckVariableParam(FeatureRule& rule) const
233 {
234     // Check whether there is a variable operator &@& in the command
235     string symbol = "";
236     string value = "";
237     bool hasValSymbol = CheckVariable(rule, L3_DESCRIPTOR_LEFT, L3_DESCRIPTOR_RIGHT);
238     if (!hasValSymbol) {
239         return true;
240     }
241     // To replace it if there is one or more variable symbol
242     for (const auto& param : paramSeekRecord_) {
243         symbol = L3_DESCRIPTOR_LEFT + param.first + L3_DESCRIPTOR_RIGHT;
244         value = param.second.value;
245         ReplaceVariable(rule, symbol, value);
246     }
247     return !CheckVariable(rule, L3_DESCRIPTOR_LEFT, L3_DESCRIPTOR_RIGHT); // check var in config
248 }
249 
CheckVariable(const FeatureRule & rule,const string & leftTag,const string & rightTag) const250 bool FeatureAnalysis::CheckVariable(const FeatureRule& rule, const string& leftTag, const string& rightTag) const
251 {
252     if ((rule.source.find(leftTag) != string::npos && rule.source.find(rightTag) != string::npos) ||
253         (rule.depend.find(leftTag) != string::npos && rule.depend.find(rightTag) != string::npos)) {
254         return true;
255     }
256     for (auto subParam : rule.param) {
257         if (subParam.second.find(leftTag) != string::npos && subParam.second.find(rightTag) != string::npos) {
258             return true;
259         }
260     }
261 
262     return false;
263 }
264 
ReplaceVariable(FeatureRule & rule,const string & symbol,const string & value) const265 void FeatureAnalysis::ReplaceVariable(FeatureRule& rule, const string& symbol, const string& value) const
266 {
267     ReplaceVariable(rule.source, symbol, value, rule.source);
268     ReplaceVariable(rule.depend, symbol, value, rule.depend);
269     for (auto subParam : rule.param) {
270         if (ReplaceVariable(subParam.second, symbol, value, subParam.second)) {
271             rule.param[subParam.first] = subParam.second;
272         }
273     }
274 }
275 
ReplaceVariable(const string & src,const string & param,const string & value,string & des) const276 bool FeatureAnalysis::ReplaceVariable(const string& src, const string& param,
277     const string& value, string& des) const
278 {
279     des = src;
280     size_t pos = src.find(param);
281     if (pos != string::npos) {
282         des.replace(pos, param.length(), value, 0, value.length());
283         return true;
284     }
285 
286     return false;
287 }
288 
289 /*
290  * return false if the depend element does not exist
291  * return false if the depend element exists and the feature that the depend element relies on has a value
292  * return true if the depend element exists and the feature that the depend element relies on has no value
293  */
CheckDepend(const FeatureRule & rule) const294 bool FeatureAnalysis::CheckDepend(const FeatureRule& rule) const
295 {
296     bool result = false;
297     if (rule.depend.empty()) {
298         return result;
299     }
300 
301     if (paramSeekRecord_.empty()) {  // depend exist but no value
302         return true;
303     }
304 
305     result = true;
306     for (const auto& one : paramSeekRecord_) {
307         if (one.first.find(rule.depend) != string::npos) {
308             result = false; // depend exist but value exist
309             break;
310         }
311     }
312     return result;
313 }
314 
FormatLineFeature(const string & value,const string & regex) const315 LineFeature FeatureAnalysis::FormatLineFeature(const string& value, const string& regex) const
316 {
317     LineFeature paramRecord{};
318     paramRecord.value = value;
319     paramRecord.lineCursor = lineCursor_;
320     return paramRecord;
321 }
322 
323 /*
324  * info : eventinfo_ or relatedInfo_ result
325  * params : rule info, for example : key : value
326  *  "SUBJECT": "BasicParam.s_subject",
327  *  "END_STACK": "MainCallTrace.s_trust_stack",
328  * features : Log feature
329  */
Compose()330 void FeatureAnalysis::Compose()
331 {
332     string result;
333     for (const auto& param : composeRule_) {
334         // keep ordered rule_prio, REASON is specially composed
335         if (eventInfo_.find(param.first) != eventInfo_.end() && param.first != PARAMETER_REASON) {
336             continue;
337         }
338         result = ComposeParam(param.second);
339         if (!result.empty()) {
340             eventInfo_[param.first] = (param.first == PARAMETER_REASON) ?
341                 (eventInfo_[param.first] + COMPOSE_COLON + result) : result;
342         }
343     }
344     ProcessReason(eventInfo_);
345 }
346 
ComposeTrace(const string & filePath,const string & param,const vector<pair<string,LineFeature>> & lineFeatures,const string & regex) const347 string FeatureAnalysis::ComposeTrace(const string& filePath, const string& param,
348     const vector<pair<string, LineFeature>>& lineFeatures, const string& regex) const
349 {
350     string result;
351     auto iter = find_if(lineFeatures.begin(), lineFeatures.end(),
352         [&param](const pair<string, LineFeature>& one) {return one.first == param;});
353     if (iter != lineFeatures.end()) {
354         stringstream buffer("");
355         if (LogUtil::ReadFileBuff(filePath, buffer)) {
356             LogUtil::GetTrace(buffer, iter->second.lineCursor, regex, result);
357         }
358     }
359     return result;
360 }
361 
ComposeParam(const string & param) const362 string FeatureAnalysis::ComposeParam(const string& param) const
363 {
364     std::vector<std::pair<std::string, LineFeature>> lineFeatures = paramSeekRecord_;
365     vector<string> params = SplitParam(param);
366     vector<string> results;
367     for (const auto& key : params) {
368         auto iter = find_if(lineFeatures.begin(), lineFeatures.end(),
369             [&key](const pair<string, LineFeature>& one) {return one.first == key;});
370         if (iter != lineFeatures.end()) {
371             auto regexIter = stackRegex_.find(key);
372             if (regexIter != stackRegex_.end()) {
373                 auto value = ComposeTrace(featureSet_.fullPath, key, lineFeatures, regexIter->second);
374                 results.emplace_back(value);
375             } else {
376                 results.emplace_back(iter->second.value);
377             }
378         }
379     }
380 
381     string tag;
382     if (!results.empty()) {
383         if (param.find(COMPOSE_PLUS) != string::npos) {
384             tag = " ";
385         } else if (param.find(COMPOSE_COLON) != string::npos) {
386             tag = COMPOSE_COLON;
387         } else {
388             tag = "";
389         }
390     }
391     string result = StringUtil::VectorToString(results, false, tag);
392     auto end = result.size() - tag.size();
393     return (end > 0) ? result.substr(0, end) : result;
394 }
395 
SplitParam(const string & param) const396 vector<string> FeatureAnalysis::SplitParam(const string& param) const
397 {
398     vector<string> params;
399     if (param.find(COMPOSE_PLUS) != string::npos) {
400         StringUtil::SplitStr(param, COMPOSE_PLUS, params, false, false);
401     } else if (param.find(COMPOSE_COLON) != string::npos) {
402         StringUtil::SplitStr(param, COMPOSE_COLON, params, false, false);
403     } else {
404         params.emplace_back(param);
405     }
406     return params;
407 }
408 
ProcessReason(map<string,string> & info)409 void FeatureAnalysis::ProcessReason(map<string, string>& info)
410 {
411     if (info.find(PARAMETER_REASON) != info.end() &&
412         info[PARAMETER_REASON].substr(0, 1) == COMPOSE_COLON) { // 1: first char
413         info[PARAMETER_REASON] = info[PARAMETER_REASON].substr(1);
414     }
415 }
416 
SetParamRecord(const std::string & key,const LineFeature & value,const int type)417 void FeatureAnalysis::SetParamRecord(const std::string& key, const LineFeature& value, const int type)
418 {
419     if (type == LAST_MATCH) {
420         for (auto iter = paramSeekRecord_.begin(); iter != paramSeekRecord_.end(); iter++) {
421             if (iter->first == key) {
422                 paramSeekRecord_.erase(iter);
423                 break;
424             }
425         }
426     }
427     paramSeekRecord_.emplace_back(pair<string, LineFeature>(key, value));
428 }
429 
SetStackRegex(const std::string & key,const std::string & regex)430 void FeatureAnalysis::SetStackRegex(const std::string& key, const std::string& regex)
431 {
432     if (regex.find(L3_VARIABLE_TRACE_BLOCK) != string::npos) {
433         stackRegex_.emplace(pair(key, StringUtil::EraseString(regex, L3_VARIABLE_TRACE_BLOCK)));
434     }
435 }
436 } // namespace HiviewDFX
437 } // namespace OHOS
438