1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2023. All rights reserved.
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 "ptreader_parser.h"
17 #include <cmath>
18 #include <sstream>
19 #include <unistd.h>
20 #include "app_start_filter.h"
21 #include "binder_filter.h"
22 #include "cpu_filter.h"
23 #ifdef ENABLE_HISYSEVENT
24 #include "hi_sysevent_measure_filter.h"
25 #endif
26 #include "parting_string.h"
27 #include "stat_filter.h"
28 #include "system_event_measure_filter.h"
29 namespace SysTuning {
30 namespace TraceStreamer {
PtreaderParser(TraceDataCache * dataCache,const TraceStreamerFilters * filters,TraceFileType fileType)31 PtreaderParser::PtreaderParser(TraceDataCache *dataCache, const TraceStreamerFilters *filters, TraceFileType fileType)
32 : ParserBase(filters),
33 traceDataCache_(dataCache),
34 #ifdef ENABLE_BYTRACE
35 bytraceEventParser_(std::make_unique<BytraceEventParser>(dataCache, filters)),
36 #endif
37 #ifdef ENABLE_HILOG
38 hilogParser_(std::make_unique<PtreaderHilogParser>(dataCache, filters)),
39 #endif
40 #ifdef ENABLE_HISYSEVENT
41 hiSysEventParser_(std::make_unique<PtreaderHiSysEventParser>(dataCache, filters)),
42 #endif
43 fileType_(fileType)
44 {
45 if (traceDataCache_->supportThread_) {
46 dataSegArray_ = std::make_unique<DataSegment[]>(maxSegArraySize);
47 } else {
48 dataSegArray_ = std::make_unique<DataSegment[]>(1);
49 }
50 }
51
52 PtreaderParser::~PtreaderParser() = default;
53
WaitForParserEnd()54 void PtreaderParser::WaitForParserEnd()
55 {
56 if (parseThreadStarted_ || filterThreadStarted_) {
57 toExit_ = true;
58 while (!exited_) {
59 usleep(sleepDur_ * sleepDur_);
60 }
61 }
62 #ifdef ENABLE_BYTRACE
63 bytraceEventParser_->FilterAllEvents();
64 bytraceEventParser_->Clear();
65 #endif
66 #ifdef ENABLE_HILOG
67 hilogParser_->FilterAllHilogData();
68 #endif
69 #ifdef ENABLE_HISYSEVENT
70 hiSysEventParser_->Finish();
71 #endif
72 traceDataCache_->MixTraceTime(traceDataCache_->traceStartTime_, traceDataCache_->traceEndTime_);
73 dataSegArray_.reset();
74 ClearPtreaderSplitData();
75 }
UpdateSplitPos()76 bool PtreaderParser::UpdateSplitPos()
77 {
78 maxSplitPos_ = mPtreaderSplitData_.size() - 1;
79 TS_CHECK_TRUE_RET(minSplitPos_ == INVALID_INT32, true);
80 minSplitPos_ = mPtreaderSplitData_.size() - 1;
81 TS_LOGI("minSplitPos_=%d", minSplitPos_);
82 return true;
83 }
84
85 template <typename Iterator>
WhileDetermine(Iterator & packagesLine,Iterator & packagesBegin,bool & isParsingOver_,bool isFinish)86 int32_t PtreaderParser::WhileDetermine(Iterator &packagesLine,
87 Iterator &packagesBegin,
88 bool &isParsingOver_,
89 bool isFinish)
90 {
91 // While loop break and continue
92 if (packagesLine == packagesBuffer_.end()) {
93 if (isFinish) {
94 isParsingOver_ = true;
95 } else {
96 return 1;
97 }
98 }
99 if (packagesLine == packagesBuffer_.begin()) {
100 packagesLine++;
101 curFileOffset_ += std::distance(packagesBegin, packagesLine);
102 packagesBegin = packagesLine;
103 return DETERMINE_CONTINUE;
104 }
105 return DETERMINE_RETURN;
106 }
107
GotoDetermine(std::string & bufferLine,bool & haveSplitSeg)108 int32_t PtreaderParser::GotoDetermine(std::string &bufferLine, bool &haveSplitSeg)
109 {
110 if (traceDataCache_->isSplitFile_) {
111 mPtreaderSplitData_.emplace_back(curFileOffset_, curDataSize_);
112 }
113 if (isFirstLine_) {
114 isFirstLine_ = false;
115 #ifdef ENABLE_BYTRACE
116 if (IsHtmlTrace(bufferLine)) {
117 isHtmlTrace_ = true;
118 return 1;
119 }
120 #endif
121 }
122 #ifdef ENABLE_BYTRACE
123 if (isHtmlTrace_) {
124 if (!isHtmlTraceContent_) {
125 if (IsHtmlTraceBegin(bufferLine)) {
126 isHtmlTraceContent_ = true;
127 }
128 return 1;
129 }
130 auto pos = bufferLine.find(script_.c_str());
131 if (pos != std::string::npos) {
132 isHtmlTraceContent_ = false;
133 bufferLine = bufferLine.substr(0, pos);
134 if (std::all_of(bufferLine.begin(), bufferLine.end(), isspace)) {
135 return 1;
136 }
137 }
138 }
139 if (IsTraceComment(bufferLine)) {
140 traceCommentLines_++;
141 mPtreaderSplitData_.clear();
142 return 1;
143 }
144 #endif
145 if (bufferLine.empty()) {
146 parsedTraceInvalidLines_++;
147 return 1;
148 }
149 if (fileType_ == TRACE_FILETYPE_HI_SYSEVENT) {
150 #ifdef ENABLE_HISYSEVENT
151 hiSysEventParser_->ParseHiSysEventDataItem(bufferLine, seq_, haveSplitSeg);
152 #endif
153 }
154 #ifdef ENABLE_BYTRACE
155 else if (isBytrace_) {
156 if (!traceBegan_) {
157 traceBegan_ = true;
158 }
159 ParseTraceDataItem(bufferLine);
160 }
161 #endif
162 #ifdef ENABLE_HILOG
163 else if (fileType_ == TRACE_FILETYPE_HILOG) {
164 hilogParser_->ParseHilogDataItem(bufferLine, seq_, haveSplitSeg);
165 }
166 #endif
167 return DETERMINE_RETURN;
168 }
169
ParseTraceDataSegment(std::unique_ptr<uint8_t[]> bufferStr,size_t size,bool isFinish)170 void PtreaderParser::ParseTraceDataSegment(std::unique_ptr<uint8_t[]> bufferStr, size_t size, bool isFinish)
171 {
172 if (isParsingOver_) {
173 return;
174 }
175 packagesBuffer_.insert(packagesBuffer_.end(), &bufferStr[0], &bufferStr[size]);
176 auto packagesBegin = packagesBuffer_.begin();
177 while (true) {
178 auto packagesLine = std::find(packagesBegin, packagesBuffer_.end(), '\n');
179 int32_t determine = WhileDetermine(packagesLine, packagesBegin, isParsingOver_, isFinish);
180 if (1 == determine) {
181 break;
182 } else if (DETERMINE_CONTINUE == determine) {
183 continue;
184 }
185 // Support parsing windows file format(ff=dos)
186 auto extra = 0;
187 if (packagesLine != packagesBuffer_.end() && *(packagesLine - 1) == '\r') {
188 extra = 1;
189 }
190 bool haveSplitSeg = false;
191 std::string bufferLine(packagesBegin, packagesLine - extra);
192 curDataSize_ = std::distance(packagesBegin, packagesLine) + 1;
193 int32_t op = GotoDetermine(bufferLine, haveSplitSeg);
194 if (1 == op) {
195 goto NEXT_LINE;
196 }
197 if (haveSplitSeg) {
198 UpdateSplitPos();
199 }
200 NEXT_LINE:
201 if (isParsingOver_) {
202 break;
203 }
204 curFileOffset_ += curDataSize_;
205 packagesBegin = packagesLine + 1;
206 seq_++;
207 continue;
208 }
209 if (isParsingOver_) {
210 packagesBuffer_.clear();
211 } else {
212 packagesBuffer_.erase(packagesBuffer_.begin(), packagesBegin);
213 }
214 return;
215 }
216
ParseTraceDataItem(const std::string & buffer)217 void PtreaderParser::ParseTraceDataItem(const std::string &buffer)
218 {
219 #ifdef ENABLE_BYTRACE
220 if (!traceDataCache_->supportThread_ || traceDataCache_->isSplitFile_) {
221 dataSegArray_[rawDataHead_].seg = std::move(buffer);
222 ParserData(dataSegArray_[rawDataHead_]);
223 return;
224 }
225 int32_t head = rawDataHead_;
226 while (!toExit_) {
227 if (dataSegArray_[head].status.load() != TS_PARSE_STATUS_INIT) {
228 TS_LOGD("rawDataHead_:\t%d, parseHead_:\t%d, filterHead_:\t%d status:\t%d\n", rawDataHead_, parseHead_,
229 filterHead_, dataSegArray_[head].status.load());
230 usleep(sleepDur_);
231 continue;
232 }
233 dataSegArray_[head].seg = std::move(buffer);
234 dataSegArray_[head].status = TS_PARSE_STATUS_SEPRATED;
235 rawDataHead_ = (rawDataHead_ + 1) % maxSegArraySize;
236 break;
237 }
238 if (!parseThreadStarted_) {
239 parseThreadStarted_ = true;
240 int32_t tmp = traceDataCache_->parserThreadNum_;
241 while (tmp--) {
242 parserThreadCount_++;
243 std::thread MatchLineThread(&PtreaderParser::ParseThread, this);
244 MatchLineThread.detach();
245 TS_LOGI("parser Thread:%d/%d start working ...\n", traceDataCache_->parserThreadNum_ - tmp,
246 traceDataCache_->parserThreadNum_);
247 }
248 }
249 if (!filterThreadStarted_) {
250 filterThreadStarted_ = true;
251 std::thread ParserThread(&PtreaderParser::FilterThread, this);
252 ParserThread.detach();
253 }
254 #endif
255 }
256 #ifdef ENABLE_BYTRACE
GetNextSegment()257 int32_t PtreaderParser::GetNextSegment()
258 {
259 int32_t head;
260 std::lock_guard<std::mutex> muxLockGuard(dataSegMux_);
261 head = parseHead_;
262 DataSegment &seg = dataSegArray_[head];
263 if (seg.status.load() != TS_PARSE_STATUS_SEPRATED) {
264 if (toExit_) {
265 parserThreadCount_--;
266 TS_LOGI("exiting parser, parserThread Count:%d\n", parserThreadCount_);
267 if (!parserThreadCount_ && !filterThreadStarted_) {
268 exited_ = true;
269 }
270 return ERROR_CODE_EXIT;
271 }
272 TS_LOGD("ParseThread watting:\t%d, parseHead_:\t%d, filterHead_:\t%d status:\t%d\n", rawDataHead_, parseHead_,
273 filterHead_, seg.status.load());
274 usleep(sleepDur_);
275 return ERROR_CODE_NODATA;
276 }
277 parseHead_ = (parseHead_ + 1) % maxSegArraySize;
278 seg.status = TS_PARSE_STATUS_PARSING;
279 return head;
280 }
281
GetDataSegAttr(DataSegment & seg,const std::smatch & matcheLine) const282 void PtreaderParser::GetDataSegAttr(DataSegment &seg, const std::smatch &matcheLine) const
283 {
284 size_t index = 0;
285 std::optional<uint32_t> optionalPid = base::StrToInt<uint32_t>(matcheLine[++index].str());
286 if (!optionalPid.has_value()) {
287 TS_LOGD("Illegal pid!");
288 seg.status = TS_PARSE_STATUS_INVALID;
289 return;
290 }
291 seg.bufLine.tgid = base::StrToInt<uint32_t>(matcheLine[++index].str()).value_or(0);
292 std::optional<uint32_t> optionalCpu = base::StrToInt<uint32_t>(matcheLine[++index].str());
293 if (!optionalCpu.has_value()) {
294 TS_LOGD("Illegal cpu!");
295 seg.status = TS_PARSE_STATUS_INVALID;
296 return;
297 }
298 // Directly parsing double may result in accuracy loss issues
299 std::optional<double> optionalTime = base::StrToDouble(matcheLine[++index].str());
300 if (!optionalTime.has_value()) {
301 TS_LOGE("Illegal ts");
302 seg.status = TS_PARSE_STATUS_INVALID;
303 return;
304 }
305 seg.bufLine.eventName = matcheLine[++index].str();
306 seg.bufLine.task = matcheLine.prefix();
307 StrTrim(seg.bufLine.task);
308 if (seg.bufLine.task == "<...>") {
309 seg.bufLine.task = "";
310 }
311 seg.bufLine.argsStr = matcheLine.suffix();
312 StrTrim(seg.bufLine.argsStr);
313 seg.bufLine.pid = optionalPid.value();
314 seg.bufLine.cpu = optionalCpu.value();
315 seg.bufLine.ts = optionalTime.value() * 1e9;
316 seg.status = TS_PARSE_STATUS_PARSED;
317 }
ParseThread()318 void PtreaderParser::ParseThread()
319 {
320 while (true) {
321 int32_t head = GetNextSegment();
322 if (head < 0) {
323 if (head == ERROR_CODE_NODATA) {
324 continue;
325 }
326 if (!filterThreadStarted_) {
327 exited_ = true;
328 }
329 return;
330 }
331 DataSegment &seg = dataSegArray_[head];
332 ParserData(seg);
333 }
334 }
335
ParserData(DataSegment & seg)336 void PtreaderParser::ParserData(DataSegment &seg)
337 {
338 std::smatch matcheLine;
339 if (!std::regex_search(seg.seg, matcheLine, bytraceMatcher_)) {
340 TS_LOGD("Not support this event (line: %s)", seg.seg.c_str());
341 streamFilters_->statFilter_->IncreaseStat(TRACE_EVENT_OTHER, STAT_EVENT_DATA_INVALID);
342 seg.status = TS_PARSE_STATUS_INVALID;
343 parsedTraceInvalidLines_++;
344 return;
345 } else {
346 parsedTraceValidLines_++;
347 }
348 GetDataSegAttr(seg, matcheLine);
349 if (traceDataCache_->isSplitFile_) {
350 if (seg.bufLine.ts >= traceDataCache_->SplitFileMinTime() &&
351 seg.bufLine.ts <= traceDataCache_->SplitFileMaxTime()) {
352 UpdateSplitPos();
353 }
354 return;
355 }
356
357 if (!traceDataCache_->supportThread_) {
358 FilterData(seg);
359 return;
360 }
361 }
FilterThread()362 void PtreaderParser::FilterThread()
363 {
364 while (true) {
365 DataSegment &seg = dataSegArray_[filterHead_];
366 if (!FilterData(seg)) {
367 return;
368 }
369 }
370 }
FilterData(DataSegment & seg)371 bool PtreaderParser::FilterData(DataSegment &seg)
372 {
373 if (!traceDataCache_->supportThread_ || traceDataCache_->isSplitFile_) {
374 if (seg.status.load() != TS_PARSE_STATUS_INVALID) {
375 bytraceEventParser_->ParseDataItem(seg.bufLine);
376 seg.status = TS_PARSE_STATUS_INIT;
377 return true;
378 }
379 streamFilters_->statFilter_->IncreaseStat(TRACE_EVENT_OTHER, STAT_EVENT_DATA_INVALID);
380 return false;
381 }
382 if (seg.status.load() == TS_PARSE_STATUS_INVALID) {
383 filterHead_ = (filterHead_ + 1) % maxSegArraySize;
384 streamFilters_->statFilter_->IncreaseStat(TRACE_EVENT_OTHER, STAT_EVENT_DATA_INVALID);
385 seg.status = TS_PARSE_STATUS_INIT;
386 return true;
387 }
388 if (seg.status.load() != TS_PARSE_STATUS_PARSED) {
389 if (toExit_ && !parserThreadCount_) {
390 TS_LOGI("exiting FilterThread Thread\n");
391 exited_ = true;
392 filterThreadStarted_ = false;
393 return false;
394 }
395 usleep(sleepDur_);
396 return true;
397 }
398 bytraceEventParser_->ParseDataItem(seg.bufLine);
399 filterHead_ = (filterHead_ + 1) % maxSegArraySize;
400 seg.status = TS_PARSE_STATUS_INIT;
401 return true;
402 }
403 #endif
404 } // namespace TraceStreamer
405 } // namespace SysTuning
406