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