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
16 #include "htrace_parser.h"
17 #include <unistd.h>
18 #include "app_start_filter.h"
19 #include "binder_filter.h"
20 #include "common_types.pbreader.h"
21 #include "cpu_filter.h"
22 #include "data_area.h"
23 #include "ftrace_event.pbreader.h"
24 #include "log.h"
25 #include "memory_plugin_result.pbreader.h"
26 #include "stat_filter.h"
27 #include "trace_plugin_result.pbreader.h"
28 #if IS_WASM
29 #include "../rpc/wasm_func.h"
30 #endif
31 namespace SysTuning {
32 namespace TraceStreamer {
HtraceParser(TraceDataCache * dataCache,const TraceStreamerFilters * filters)33 HtraceParser::HtraceParser(TraceDataCache* dataCache, const TraceStreamerFilters* filters)
34 : ParserBase(filters),
35 traceDataCache_(dataCache),
36 htraceCpuDetailParser_(std::make_unique<HtraceCpuDetailParser>(dataCache, filters)),
37 htraceSymbolsDetailParser_(std::make_unique<HtraceSymbolsDetailParser>(dataCache, filters)),
38 htraceMemParser_(std::make_unique<HtraceMemParser>(dataCache, filters)),
39 htraceClockDetailParser_(std::make_unique<HtraceClockDetailParser>(dataCache, filters)),
40 htraceHiLogParser_(std::make_unique<HtraceHiLogParser>(dataCache, filters)),
41 htraceNativeHookParser_(std::make_unique<HtraceNativeHookParser>(dataCache, filters)),
42 htraceHidumpParser_(std::make_unique<HtraceHidumpParser>(dataCache, filters)),
43 cpuUsageParser_(std::make_unique<HtraceCpuDataParser>(dataCache, filters)),
44 networkParser_(std::make_unique<HtraceNetworkParser>(dataCache, filters)),
45 diskIOParser_(std::make_unique<HtraceDiskIOParser>(dataCache, filters)),
46 processParser_(std::make_unique<HtraceProcessParser>(dataCache, filters)),
47 hisyseventParser_(std::make_unique<HtraceHisyseventParser>(dataCache, filters)),
48 jsMemoryParser_(std::make_unique<HtraceJSMemoryParser>(dataCache, filters)),
49 perfDataParser_(std::make_unique<PerfDataParser>(dataCache, filters)),
50 ebpfDataParser_(std::make_unique<EbpfDataParser>(dataCache, filters))
51 {
52 InitPluginNameIndex();
53 if (traceDataCache_->supportThread_) {
54 dataSegArray_ = std::make_unique<HtraceDataSegment[]>(maxSegArraySize);
55 } else {
56 dataSegArray_ = std::make_unique<HtraceDataSegment[]>(1);
57 }
58 }
InitPluginNameIndex()59 void HtraceParser::InitPluginNameIndex()
60 {
61 nativeHookPluginIndex_.insert(traceDataCache_->GetDataIndex("nativehook"));
62 nativeHookPluginIndex_.insert(traceDataCache_->GetDataIndex("hookdaemon"));
63 nativeHookConfigIndex_ = traceDataCache_->GetDataIndex("nativehook_config");
64 hisyseventPluginIndex_ = traceDataCache_->GetDataIndex("hisysevent-plugin");
65 hisyseventPluginConfigIndex_ = traceDataCache_->GetDataIndex("hisysevent-plugin_config");
66 memPluginIndex_ = traceDataCache_->GetDataIndex("memory-plugin");
67 memoryPluginConfigIndex_ = traceDataCache_->GetDataIndex("memory-plugin_config");
68 ftracePluginIndex_.insert(traceDataCache_->GetDataIndex("ftrace-plugin"));
69 ftracePluginIndex_.insert(traceDataCache_->GetDataIndex("/data/local/tmp/libftrace_plugin.z.so"));
70 hilogPluginIndex_.insert(traceDataCache_->GetDataIndex("hilog-plugin"));
71 hilogPluginIndex_.insert(traceDataCache_->GetDataIndex("/data/local/tmp/libhilogplugin.z.so"));
72 hidumpPluginIndex_.insert(traceDataCache_->GetDataIndex("hidump-plugin"));
73 hidumpPluginIndex_.insert(traceDataCache_->GetDataIndex("/data/local/tmp/libhidumpplugin.z.so"));
74 cpuPluginIndex_ = traceDataCache_->GetDataIndex("cpu-plugin");
75 networkPluginIndex_ = traceDataCache_->GetDataIndex("network-plugin");
76 diskioPluginIndex_ = traceDataCache_->GetDataIndex("diskio-plugin");
77 processPluginIndex_ = traceDataCache_->GetDataIndex("process-plugin");
78 arktsPluginIndex_ = traceDataCache_->GetDataIndex("arkts-plugin");
79 arktsPluginConfigIndex_ = traceDataCache_->GetDataIndex("arkts-plugin_config");
80 supportPluginNameIndex_.insert(nativeHookPluginIndex_.begin(), nativeHookPluginIndex_.end());
81 supportPluginNameIndex_.insert(nativeHookConfigIndex_);
82 supportPluginNameIndex_.insert(hisyseventPluginIndex_);
83 supportPluginNameIndex_.insert(hisyseventPluginConfigIndex_);
84 supportPluginNameIndex_.insert(memPluginIndex_);
85 supportPluginNameIndex_.insert(memoryPluginConfigIndex_);
86 supportPluginNameIndex_.insert(ftracePluginIndex_.begin(), ftracePluginIndex_.end());
87 supportPluginNameIndex_.insert(hilogPluginIndex_.begin(), hilogPluginIndex_.end());
88 supportPluginNameIndex_.insert(hidumpPluginIndex_.begin(), hidumpPluginIndex_.end());
89 supportPluginNameIndex_.insert(cpuPluginIndex_);
90 supportPluginNameIndex_.insert(networkPluginIndex_);
91 supportPluginNameIndex_.insert(diskioPluginIndex_);
92 supportPluginNameIndex_.insert(processPluginIndex_);
93 supportPluginNameIndex_.insert(arktsPluginIndex_);
94 supportPluginNameIndex_.insert(arktsPluginConfigIndex_);
95 }
96
ParserFileSO(std::string & directory,const std::vector<std::string> & relativeFilePaths)97 void HtraceParser::ParserFileSO(std::string& directory, const std::vector<std::string>& relativeFilePaths)
98 {
99 for (const auto& filePath : relativeFilePaths) {
100 auto absoluteFilePath = filePath.substr(directory.length());
101 auto symbolsFile =
102 OHOS::Developtools::HiPerf::SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE, absoluteFilePath);
103 symbolsFile->setSymbolsFilePath(directory);
104 symbolsFile->LoadSymbols(absoluteFilePath);
105 symbolsFiles_.emplace_back(std::move(symbolsFile));
106 }
107 }
108
~HtraceParser()109 HtraceParser::~HtraceParser()
110 {
111 TS_LOGI("clockid 2 is for RealTime and 1 is for BootTime");
112 }
113
ReparseSymbolFilesAndResymbolization(std::string & symbolsPath,std::vector<std::string> & symbolsPaths)114 bool HtraceParser::ReparseSymbolFilesAndResymbolization(std::string& symbolsPath,
115 std::vector<std::string>& symbolsPaths)
116 {
117 std::vector<std::string> dir;
118 dir.emplace_back(symbolsPath);
119 auto parseStatus = false;
120 parseStatus = perfDataParser_->PerfReloadSymbolFiles(dir);
121 ParserFileSO(symbolsPath, symbolsPaths);
122 if (traceDataCache_->GetNativeHookFrameData()->Size() > 0) {
123 htraceNativeHookParser_->NativeHookReloadElfSymbolTable(symbolsFiles_);
124 parseStatus = true;
125 }
126 if (traceDataCache_->GetEbpfCallStack()->Size() > 0) {
127 ebpfDataParser_->EBPFReloadElfSymbolTable(symbolsFiles_);
128 parseStatus = true;
129 }
130 symbolsFiles_.clear();
131 return parseStatus;
132 }
133
WaitForParserEnd()134 void HtraceParser::WaitForParserEnd()
135 {
136 if (parseThreadStarted_ || filterThreadStarted_) {
137 toExit_ = true;
138 while (!exited_) {
139 usleep(sleepDur_ * sleepDur_);
140 }
141 }
142 hasGotHeader_ = false;
143 htraceCpuDetailParser_->FilterAllEvents();
144 htraceNativeHookParser_->FinishParseNativeHookData();
145 htraceHiLogParser_->Finish();
146 htraceHidumpParser_->Finish();
147 cpuUsageParser_->Finish();
148 networkParser_->Finish();
149 processParser_->Finish();
150 diskIOParser_->Finish();
151 hisyseventParser_->Finish();
152 jsMemoryParser_->Finish();
153 // keep final upate perf and ebpf data time range
154 ebpfDataParser_->Finish();
155 perfDataParser_->Finish();
156 htraceNativeHookParser_->Finish();
157 htraceMemParser_->Finish();
158 traceDataCache_->GetDataSourceClockIdData()->SetDataSourceClockId(DATA_SOURCE_TYPE_TRACE,
159 dataSourceTypeTraceClockid_);
160 traceDataCache_->GetDataSourceClockIdData()->SetDataSourceClockId(DATA_SOURCE_TYPE_MEM, dataSourceTypeMemClockid_);
161 traceDataCache_->GetDataSourceClockIdData()->SetDataSourceClockId(DATA_SOURCE_TYPE_HILOG,
162 dataSourceTypeHilogClockid_);
163 traceDataCache_->GetDataSourceClockIdData()->SetDataSourceClockId(DATA_SOURCE_TYPE_NATIVEHOOK,
164 dataSourceTypeNativeHookClockid_);
165 traceDataCache_->GetDataSourceClockIdData()->SetDataSourceClockId(DATA_SOURCE_TYPE_FPS, dataSourceTypeFpsClockid_);
166 traceDataCache_->GetDataSourceClockIdData()->SetDataSourceClockId(DATA_SOURCE_TYPE_NETWORK,
167 dataSourceTypeNetworkClockid_);
168 traceDataCache_->GetDataSourceClockIdData()->SetDataSourceClockId(DATA_SOURCE_TYPE_DISKIO,
169 dataSourceTypeDiskioClockid_);
170 traceDataCache_->GetDataSourceClockIdData()->SetDataSourceClockId(DATA_SOURCE_TYPE_CPU, dataSourceTypeCpuClockid_);
171 traceDataCache_->GetDataSourceClockIdData()->SetDataSourceClockId(DATA_SOURCE_TYPE_PROCESS,
172 dataSourceTypeProcessClockid_);
173 traceDataCache_->GetDataSourceClockIdData()->SetDataSourceClockId(DATA_SOURCE_TYPE_HISYSEVENT,
174 dataSourceTypeHisyseventClockid_);
175 traceDataCache_->GetDataSourceClockIdData()->SetDataSourceClockId(DATA_SOURCE_TYPE_JSMEMORY,
176 dataSourceTypeJSMemoryClockid_);
177 traceDataCache_->GetDataSourceClockIdData()->Finish();
178 dataSegArray_.reset();
179 processedDataLen_ = 0;
180 }
181
ParseTraceDataItem(const std::string & buffer)182 void HtraceParser::ParseTraceDataItem(const std::string& buffer)
183 {
184 int32_t head = rawDataHead_;
185 if (!traceDataCache_->supportThread_ || traceDataCache_->isSplitFile_) {
186 dataSegArray_[head].seg = std::make_shared<std::string>(std::move(buffer));
187 dataSegArray_[head].status = TS_PARSE_STATUS_SEPRATED;
188 ParserData(dataSegArray_[head], traceDataCache_->isSplitFile_);
189 return;
190 }
191 while (!toExit_) {
192 if (dataSegArray_[head].status.load() != TS_PARSE_STATUS_INIT) {
193 usleep(sleepDur_);
194 continue;
195 }
196 dataSegArray_[head].seg = std::make_shared<std::string>(std::move(buffer));
197 dataSegArray_[head].status = TS_PARSE_STATUS_SEPRATED;
198 rawDataHead_ = (rawDataHead_ + 1) % maxSegArraySize;
199 break;
200 }
201 if (!parseThreadStarted_) {
202 parseThreadStarted_ = true;
203 int32_t tmp = traceDataCache_->parserThreadNum_;
204 while (tmp--) {
205 parserThreadCount_++;
206 std::thread ParseTypeThread(&HtraceParser::ParseThread, this);
207 ParseTypeThread.detach();
208 TS_LOGI("parser Thread:%d/%d start working ...\n", traceDataCache_->parserThreadNum_ - tmp,
209 traceDataCache_->parserThreadNum_);
210 }
211 }
212 if (!filterThreadStarted_) {
213 filterThreadStarted_ = true;
214 std::thread FilterTypeThread(&HtraceParser::FilterThread, this);
215 TS_LOGI("FilterThread start working ...");
216 FilterTypeThread.detach();
217 }
218 }
219
EnableFileSeparate(bool enabled)220 void HtraceParser::EnableFileSeparate(bool enabled)
221 {
222 jsMemoryParser_->EnableSaveFile(enabled);
223 }
FilterData(HtraceDataSegment & seg,bool isSplitFile)224 void HtraceParser::FilterData(HtraceDataSegment& seg, bool isSplitFile)
225 {
226 bool haveSplitSeg = false;
227 if (seg.dataType == DATA_SOURCE_TYPE_NATIVEHOOK) {
228 htraceNativeHookParser_->Parse(seg, haveSplitSeg);
229 } else if (seg.dataType == DATA_SOURCE_TYPE_NATIVEHOOK_CONFIG) {
230 htraceNativeHookParser_->ParseConfigInfo(seg);
231 } else if (seg.dataType == DATA_SOURCE_TYPE_TRACE) {
232 htraceCpuDetailParser_->FilterAllEventsReader();
233 } else if (seg.dataType == DATA_SOURCE_TYPE_MEM) {
234 htraceMemParser_->Parse(seg, seg.timeStamp, seg.clockId);
235 } else if (seg.dataType == DATA_SOURCE_TYPE_HILOG) {
236 htraceHiLogParser_->Parse(seg.protoData, haveSplitSeg);
237 } else if (seg.dataType == DATA_SOURCE_TYPE_CPU) {
238 cpuUsageParser_->Parse(seg.protoData, seg.timeStamp);
239 } else if (seg.dataType == DATA_SOURCE_TYPE_FPS) {
240 htraceHidumpParser_->Parse(seg.protoData);
241 dataSourceTypeFpsClockid_ = htraceHidumpParser_->ClockId();
242 } else if (seg.dataType == DATA_SOURCE_TYPE_NETWORK) {
243 networkParser_->Parse(seg.protoData, seg.timeStamp);
244 } else if (seg.dataType == DATA_SOURCE_TYPE_PROCESS) {
245 processParser_->Parse(seg.protoData, seg.timeStamp);
246 } else if (seg.dataType == DATA_SOURCE_TYPE_DISKIO) {
247 diskIOParser_->Parse(seg.protoData, seg.timeStamp);
248 } else if (seg.dataType == DATA_SOURCE_TYPE_JSMEMORY) {
249 jsMemoryParser_->Parse(seg.protoData, seg.timeStamp, traceDataCache_->SplitFileMinTime(),
250 traceDataCache_->SplitFileMaxTime(), profilerPluginData_);
251 } else if (seg.dataType == DATA_SOURCE_TYPE_JSMEMORY_CONFIG) {
252 jsMemoryParser_->ParseJSMemoryConfig(seg.protoData);
253 } else if (seg.dataType == DATA_SOURCE_TYPE_HISYSEVENT) {
254 ProtoReader::HisyseventInfo_Reader hisyseventInfo(seg.protoData.data_, seg.protoData.size_);
255 hisyseventParser_->Parse(&hisyseventInfo, seg.timeStamp, haveSplitSeg);
256 } else if (seg.dataType == DATA_SOURCE_TYPE_HISYSEVENT_CONFIG) {
257 ProtoReader::HisyseventConfig_Reader hisyseventConfig(seg.protoData.data_, seg.protoData.size_);
258 hisyseventParser_->Parse(&hisyseventConfig, seg.timeStamp);
259 } else if (seg.dataType == DATA_SOURCE_TYPE_MEM_CONFIG) {
260 htraceMemParser_->ParseMemoryConfig(seg);
261 }
262 if (traceDataCache_->isSplitFile_ && haveSplitSeg) {
263 mTraceDataHtrace_.emplace(splitFileOffset_, nextLength_ + packetSegLength_);
264 }
265 if (traceDataCache_->supportThread_ && !traceDataCache_->isSplitFile_) {
266 filterHead_ = (filterHead_ + 1) % maxSegArraySize;
267 }
268 seg.status = TS_PARSE_STATUS_INIT;
269 }
FilterThread()270 void HtraceParser::FilterThread()
271 {
272 TS_LOGI("filter thread start work!");
273 while (true) {
274 HtraceDataSegment& seg = dataSegArray_[filterHead_];
275 if (seg.status.load() == TS_PARSE_STATUS_INVALID) {
276 seg.status = TS_PARSE_STATUS_INIT;
277 filterHead_ = (filterHead_ + 1) % maxSegArraySize;
278 TS_LOGD("seprateHead_d:\t%d, parseHead_:\t%d, filterHead_:\t%d\n", rawDataHead_, parseHead_, filterHead_);
279 continue;
280 }
281 if (seg.status.load() != TS_PARSE_STATUS_PARSED) {
282 if (toExit_ && !parserThreadCount_) {
283 TS_LOGI("exiting Filter Thread");
284 exited_ = true;
285 filterThreadStarted_ = false;
286 TS_LOGI("seprateHead:\t%d, parseHead_:\t%d, filterHead_:\t%d, status:%d\n", rawDataHead_, parseHead_,
287 filterHead_, seg.status.load());
288 return;
289 }
290 TS_LOGD("seprateHead:\t%d, parseHead_:\t%d, filterHead_:\t%d, status:%d\n", rawDataHead_, parseHead_,
291 filterHead_, seg.status.load());
292 usleep(sleepDur_);
293 continue;
294 }
295 FilterData(seg, false);
296 }
297 }
298
SpliteConfigData(const std::string & pluginName,const HtraceDataSegment & dataSeg)299 bool HtraceParser::SpliteConfigData(const std::string& pluginName, const HtraceDataSegment& dataSeg)
300 {
301 if (EndWith(pluginName, "arkts-plugin_config")) {
302 std::string dataString(dataSeg.seg->c_str(), dataSeg.seg->length());
303 arkTsConfigData_ = lenBuffer_ + dataString;
304 return true;
305 } else if (EndWith(pluginName, "config")) {
306 mTraceDataHtrace_.emplace(splitFileOffset_, nextLength_ + packetSegLength_);
307 return true;
308 }
309 return false;
310 }
311
SpliteDataBySegment(DataIndex pluginNameIndex,HtraceDataSegment & dataSeg)312 bool HtraceParser::SpliteDataBySegment(DataIndex pluginNameIndex, HtraceDataSegment& dataSeg)
313 {
314 if (nativeHookPluginIndex_.count(pluginNameIndex) || ftracePluginIndex_.count(pluginNameIndex) ||
315 hilogPluginIndex_.count(pluginNameIndex) || hisyseventPluginIndex_ == pluginNameIndex) {
316 return false;
317 }
318 // need convert to Primary Time Plugin
319 if (pluginNameIndex == memPluginIndex_) {
320 dataSeg.timeStamp = streamFilters_->clockFilter_->ToPrimaryTraceTime(TS_CLOCK_REALTIME, dataSeg.timeStamp);
321 UpdatePluginTimeRange(TS_CLOCK_BOOTTIME, dataSeg.timeStamp, dataSeg.timeStamp);
322 }
323 if (dataSeg.timeStamp >= traceDataCache_->SplitFileMinTime() &&
324 dataSeg.timeStamp <= traceDataCache_->SplitFileMaxTime()) {
325 mTraceDataHtrace_.emplace(splitFileOffset_, nextLength_ + packetSegLength_);
326 }
327 if (pluginNameIndex == arktsPluginConfigIndex_ || pluginNameIndex == arktsPluginIndex_) {
328 return false;
329 }
330 return true;
331 }
ParseDataByPluginName(HtraceDataSegment & dataSeg,DataIndex pulginNameIndex,const ProtoReader::ProfilerPluginData_Reader & pluginDataZero,bool isSplitFile)332 void HtraceParser::ParseDataByPluginName(HtraceDataSegment& dataSeg,
333 DataIndex pulginNameIndex,
334 const ProtoReader::ProfilerPluginData_Reader& pluginDataZero,
335 bool isSplitFile)
336 {
337 if (nativeHookPluginIndex_.count(pulginNameIndex)) {
338 ParseNativeHook(dataSeg, isSplitFile);
339 } else if (pulginNameIndex == nativeHookConfigIndex_) {
340 ParseNativeHookConfig(dataSeg);
341 } else if (ftracePluginIndex_.count(pulginNameIndex)) { // ok
342 ParseFtrace(dataSeg);
343 } else if (pulginNameIndex == memPluginIndex_) {
344 ParseMemory(pluginDataZero, dataSeg);
345 } else if (hilogPluginIndex_.count(pulginNameIndex)) {
346 ParseHilog(dataSeg);
347 } else if (hidumpPluginIndex_.count(pulginNameIndex)) {
348 ParseFPS(dataSeg);
349 } else if (pulginNameIndex == cpuPluginIndex_) {
350 ParseCpuUsage(dataSeg);
351 } else if (pulginNameIndex == networkPluginIndex_) {
352 ParseNetwork(dataSeg);
353 } else if (pulginNameIndex == diskioPluginIndex_) {
354 ParseDiskIO(dataSeg);
355 } else if (pulginNameIndex == processPluginIndex_) {
356 ParseProcess(dataSeg);
357 } else if (pulginNameIndex == hisyseventPluginIndex_) {
358 ParseHisysevent(dataSeg);
359 } else if (pulginNameIndex == hisyseventPluginConfigIndex_) {
360 ParseHisyseventConfig(dataSeg);
361 } else if (pulginNameIndex == arktsPluginIndex_) {
362 ParseJSMemory(dataSeg, isSplitFile);
363 } else if (pulginNameIndex == arktsPluginConfigIndex_) {
364 ParseJSMemoryConfig(dataSeg);
365 } else if (pulginNameIndex == memoryPluginConfigIndex_) {
366 ParseMemoryConfig(dataSeg, pluginDataZero);
367 }
368 }
369
ParserData(HtraceDataSegment & dataSeg,bool isSplitFile)370 void HtraceParser::ParserData(HtraceDataSegment& dataSeg, bool isSplitFile)
371 {
372 ProtoReader::ProfilerPluginData_Reader pluginDataZero(reinterpret_cast<const uint8_t*>(dataSeg.seg->c_str()),
373 dataSeg.seg->length());
374 if (!pluginDataZero.has_name()) {
375 return;
376 }
377 auto pluginName = pluginDataZero.name().ToStdString();
378 auto pluginNameIndex = traceDataCache_->GetDataIndex(pluginName);
379 if (isSplitFile && SpliteConfigData(pluginName, dataSeg)) {
380 return;
381 }
382 if (pluginDataZero.has_tv_sec() && pluginDataZero.has_tv_nsec()) {
383 dataSeg.timeStamp = pluginDataZero.tv_sec() * SEC_TO_NS + pluginDataZero.tv_nsec();
384 }
385
386 if (isSplitFile && SpliteDataBySegment(pluginNameIndex, dataSeg)) {
387 return;
388 }
389 if (supportPluginNameIndex_.count(pluginNameIndex)) {
390 dataSeg.protoData = pluginDataZero.data();
391 ParseDataByPluginName(dataSeg, pluginNameIndex, pluginDataZero, isSplitFile);
392 } else {
393 #if IS_WASM
394 TraceStreamer_Plugin_Out_Filter(reinterpret_cast<const char*>(pluginDataZero.data().data_),
395 pluginDataZero.data().size_, pluginName);
396 #endif
397 dataSeg.status = TS_PARSE_STATUS_INVALID;
398 streamFilters_->statFilter_->IncreaseStat(TRACE_EVENT_OTHER, STAT_EVENT_DATA_INVALID);
399 return;
400 }
401 if (!traceDataCache_->supportThread_ || traceDataCache_->isSplitFile_) {
402 FilterData(dataSeg, isSplitFile);
403 }
404 }
ParseThread()405 void HtraceParser::ParseThread()
406 {
407 TS_LOGI("parser thread start work!\n");
408 while (true) {
409 int32_t head = GetNextSegment();
410 if (head < 0) {
411 if (head == ERROR_CODE_EXIT) {
412 TS_LOGI("parse thread exit");
413 return;
414 } else if (head == ERROR_CODE_NODATA) {
415 continue;
416 }
417 }
418 HtraceDataSegment& dataSeg = dataSegArray_[head];
419 ParserData(dataSeg, false);
420 }
421 }
422
ParseMemory(const ProtoReader::ProfilerPluginData_Reader & pluginDataZero,HtraceDataSegment & dataSeg)423 void HtraceParser::ParseMemory(const ProtoReader::ProfilerPluginData_Reader& pluginDataZero, HtraceDataSegment& dataSeg)
424 {
425 BuiltinClocks clockId = TS_CLOCK_REALTIME;
426 auto clockIdTemp = pluginDataZero.clock_id();
427 if (clockIdTemp == ProtoReader::ProfilerPluginData_ClockId_CLOCKID_REALTIME) {
428 clockId = TS_CLOCK_REALTIME;
429 }
430 dataSourceTypeMemClockid_ = clockId;
431 dataSeg.dataType = DATA_SOURCE_TYPE_MEM;
432 dataSeg.clockId = clockId;
433 dataSeg.status = TS_PARSE_STATUS_PARSED;
434 }
ParseHilog(HtraceDataSegment & dataSeg)435 void HtraceParser::ParseHilog(HtraceDataSegment& dataSeg)
436 {
437 dataSeg.dataType = DATA_SOURCE_TYPE_HILOG;
438 dataSourceTypeHilogClockid_ = TS_CLOCK_REALTIME;
439 dataSeg.status = TS_PARSE_STATUS_PARSED;
440 }
ParseNativeHookConfig(HtraceDataSegment & dataSeg)441 void HtraceParser::ParseNativeHookConfig(HtraceDataSegment& dataSeg)
442 {
443 dataSeg.dataType = DATA_SOURCE_TYPE_NATIVEHOOK_CONFIG;
444 dataSeg.status = TS_PARSE_STATUS_PARSED;
445 }
ParseNativeHook(HtraceDataSegment & dataSeg,bool isSplitFile)446 void HtraceParser::ParseNativeHook(HtraceDataSegment& dataSeg, bool isSplitFile)
447 {
448 dataSourceTypeNativeHookClockid_ = TS_CLOCK_REALTIME;
449 dataSeg.dataType = DATA_SOURCE_TYPE_NATIVEHOOK;
450 dataSeg.status = TS_PARSE_STATUS_PARSED;
451 if (isSplitFile) {
452 dataSourceType_ = DATA_SOURCE_TYPE_NATIVEHOOK;
453 }
454 }
ParseMemoryConfig(HtraceDataSegment & dataSeg,const ProtoReader::ProfilerPluginData_Reader & pluginDataZero)455 void HtraceParser::ParseMemoryConfig(HtraceDataSegment& dataSeg,
456 const ProtoReader::ProfilerPluginData_Reader& pluginDataZero)
457 {
458 if (pluginDataZero.has_sample_interval()) {
459 uint32_t sampleInterval = pluginDataZero.sample_interval();
460 traceDataCache_->GetTraceConfigData()->AppendNewData("memory_config", "sample_interval",
461 std::to_string(sampleInterval));
462 }
463 dataSeg.dataType = DATA_SOURCE_TYPE_MEM_CONFIG;
464 dataSeg.status = TS_PARSE_STATUS_PARSED;
465 }
466
ParseFtrace(HtraceDataSegment & dataSeg)467 void HtraceParser::ParseFtrace(HtraceDataSegment& dataSeg)
468 {
469 dataSeg.dataType = DATA_SOURCE_TYPE_TRACE;
470 ProtoReader::TracePluginResult_Reader tracePluginResult(dataSeg.protoData);
471 if (tracePluginResult.has_ftrace_cpu_stats()) {
472 auto cpuStats = *tracePluginResult.ftrace_cpu_stats();
473 ProtoReader::FtraceCpuStatsMsg_Reader ftraceCpuStatsMsg(cpuStats.data_, cpuStats.size_);
474 auto s = *ftraceCpuStatsMsg.per_cpu_stats();
475 ProtoReader::PerCpuStatsMsg_Reader perCpuStatsMsg(s.data_, s.size_);
476 TS_LOGD("s.overrun():%lu", perCpuStatsMsg.overrun());
477 TS_LOGD("s.dropped_events():%lu", perCpuStatsMsg.dropped_events());
478 auto clock = ftraceCpuStatsMsg.trace_clock().ToStdString();
479 if (clock == "boot") {
480 clock_ = TS_CLOCK_BOOTTIME;
481 } else if (clock == "mono") {
482 clock_ = TS_MONOTONIC;
483 } else {
484 TS_LOGI("invalid clock:%s", clock.c_str());
485 dataSeg.status = TS_PARSE_STATUS_INVALID;
486 return;
487 }
488 dataSeg.clockId = clock_;
489 dataSourceTypeTraceClockid_ = clock_;
490 dataSeg.status = TS_PARSE_STATUS_PARSED;
491 return;
492 }
493 bool haveSplitSeg = false;
494 dataSeg.clockId = clock_;
495 if (tracePluginResult.has_ftrace_cpu_detail()) {
496 htraceCpuDetailParser_->Parse(dataSeg, tracePluginResult, haveSplitSeg);
497 dataSeg.status = TS_PARSE_STATUS_PARSED;
498 }
499 if (tracePluginResult.has_symbols_detail()) {
500 htraceSymbolsDetailParser_->Parse(dataSeg.protoData); // has Event
501 haveSplitSeg = true;
502 dataSeg.status = TS_PARSE_STATUS_PARSED;
503 }
504 if (tracePluginResult.has_clocks_detail()) {
505 htraceClockDetailParser_->Parse(dataSeg.protoData); // has Event
506 haveSplitSeg = true;
507 dataSeg.status = TS_PARSE_STATUS_PARSED;
508 }
509 if (traceDataCache_->isSplitFile_ && haveSplitSeg) {
510 mTraceDataHtrace_.emplace(splitFileOffset_, nextLength_ + packetSegLength_);
511 }
512 dataSeg.status = TS_PARSE_STATUS_INVALID;
513 }
514
ParseFPS(HtraceDataSegment & dataSeg)515 void HtraceParser::ParseFPS(HtraceDataSegment& dataSeg)
516 {
517 dataSeg.dataType = DATA_SOURCE_TYPE_FPS;
518 dataSeg.status = TS_PARSE_STATUS_PARSED;
519 }
520
ParseCpuUsage(HtraceDataSegment & dataSeg)521 void HtraceParser::ParseCpuUsage(HtraceDataSegment& dataSeg)
522 {
523 dataSourceTypeProcessClockid_ = TS_CLOCK_REALTIME;
524 dataSeg.dataType = DATA_SOURCE_TYPE_CPU;
525 dataSeg.status = TS_PARSE_STATUS_PARSED;
526 }
ParseNetwork(HtraceDataSegment & dataSeg)527 void HtraceParser::ParseNetwork(HtraceDataSegment& dataSeg)
528 {
529 dataSourceTypeProcessClockid_ = TS_CLOCK_REALTIME;
530 dataSeg.dataType = DATA_SOURCE_TYPE_NETWORK;
531 dataSeg.status = TS_PARSE_STATUS_PARSED;
532 }
ParseDiskIO(HtraceDataSegment & dataSeg)533 void HtraceParser::ParseDiskIO(HtraceDataSegment& dataSeg)
534 {
535 dataSourceTypeProcessClockid_ = TS_CLOCK_REALTIME;
536 dataSeg.dataType = DATA_SOURCE_TYPE_DISKIO;
537 dataSeg.status = TS_PARSE_STATUS_PARSED;
538 }
539
ParseProcess(HtraceDataSegment & dataSeg)540 void HtraceParser::ParseProcess(HtraceDataSegment& dataSeg)
541 {
542 dataSourceTypeProcessClockid_ = TS_CLOCK_BOOTTIME;
543 dataSeg.dataType = DATA_SOURCE_TYPE_PROCESS;
544 dataSeg.status = TS_PARSE_STATUS_PARSED;
545 }
546
ParseHisysevent(HtraceDataSegment & dataSeg)547 void HtraceParser::ParseHisysevent(HtraceDataSegment& dataSeg)
548 {
549 dataSourceTypeHisyseventClockid_ = TS_CLOCK_REALTIME;
550 dataSeg.dataType = DATA_SOURCE_TYPE_HISYSEVENT;
551 dataSeg.status = TS_PARSE_STATUS_PARSED;
552 }
ParseHisyseventConfig(HtraceDataSegment & dataSeg)553 void HtraceParser::ParseHisyseventConfig(HtraceDataSegment& dataSeg)
554 {
555 dataSourceTypeHisyseventClockid_ = TS_CLOCK_REALTIME;
556 dataSeg.dataType = DATA_SOURCE_TYPE_HISYSEVENT_CONFIG;
557 dataSeg.status = TS_PARSE_STATUS_PARSED;
558 }
559
ParseJSMemory(HtraceDataSegment & dataSeg,bool isSplitFile)560 void HtraceParser::ParseJSMemory(HtraceDataSegment& dataSeg, bool isSplitFile)
561 {
562 if (isSplitFile) {
563 dataSourceType_ = DATA_SOURCE_TYPE_JSMEMORY;
564 memcpy_s(&profilerPluginData_, sizeof(profilerPluginData_), dataSeg.seg->c_str(), dataSeg.seg->length());
565 }
566 dataSourceTypeJSMemoryClockid_ = TS_CLOCK_REALTIME;
567 dataSeg.dataType = DATA_SOURCE_TYPE_JSMEMORY;
568 dataSeg.status = TS_PARSE_STATUS_PARSED;
569 }
570
ParseJSMemoryConfig(HtraceDataSegment & dataSeg)571 void HtraceParser::ParseJSMemoryConfig(HtraceDataSegment& dataSeg)
572 {
573 dataSourceTypeJSMemoryClockid_ = TS_CLOCK_REALTIME;
574 dataSeg.dataType = DATA_SOURCE_TYPE_JSMEMORY_CONFIG;
575 dataSeg.status = TS_PARSE_STATUS_PARSED;
576 }
577
GetNextSegment()578 int32_t HtraceParser::GetNextSegment()
579 {
580 int32_t head;
581 std::lock_guard<std::mutex> muxLockGuard(htraceDataSegMux_);
582 head = parseHead_;
583 HtraceDataSegment& htraceDataSegmentSeg = dataSegArray_[head];
584 if (htraceDataSegmentSeg.status.load() != TS_PARSE_STATUS_SEPRATED) {
585 if (toExit_) {
586 parserThreadCount_--;
587 TS_LOGI("exiting parser, parserThread Count:%d\n", parserThreadCount_);
588 TS_LOGI("seprateHead_x:\t%d, parseHead_:\t%d, filterHead_:\t%d status:%d\n", rawDataHead_, parseHead_,
589 filterHead_, htraceDataSegmentSeg.status.load());
590 if (!parserThreadCount_ && !filterThreadStarted_) {
591 exited_ = true;
592 }
593 return ERROR_CODE_EXIT;
594 }
595 usleep(sleepDur_);
596 return ERROR_CODE_NODATA;
597 }
598 parseHead_ = (parseHead_ + 1) % maxSegArraySize;
599 htraceDataSegmentSeg.status = TS_PARSE_STATUS_PARSING;
600 return head;
601 }
CalcEbpfCutOffset(std::deque<uint8_t>::iterator & packagesBegin,size_t & currentLength)602 bool HtraceParser::CalcEbpfCutOffset(std::deque<uint8_t>::iterator& packagesBegin, size_t& currentLength)
603 {
604 auto standaloneDataLength = profilerDataLength_ - packetHeaderLength_;
605 if (traceDataCache_->isSplitFile_ && !parsedEbpfOver_) {
606 if (!hasInitEbpfPublicData_) {
607 // Record the offset of Hiperf's 1024-byte header relative to the entire file.
608 ebpfDataParser_->SetEbpfDataOffset(processedDataLen_);
609 ebpfDataParser_->SetSpliteTimeRange(traceDataCache_->SplitFileMinTime(),
610 traceDataCache_->SplitFileMaxTime());
611 parsedFileOffset_ += profilerDataLength_ - packetHeaderLength_;
612 hasInitEbpfPublicData_ = true;
613 }
614 parsedEbpfOver_ = ebpfDataParser_->AddAndSplitEbpfData(packagesBuffer_);
615 if (parsedEbpfOver_) {
616 profilerDataType_ = ProfilerTraceFileHeader::UNKNOW_TYPE;
617 hasGotHeader_ = false;
618 processedDataLen_ += standaloneDataLength;
619 }
620 return false;
621 }
622 if (!traceDataCache_->isSplitFile_ && packagesBuffer_.size() >= standaloneDataLength) {
623 ebpfDataParser_->InitAndParseEbpfData(packagesBuffer_, standaloneDataLength);
624 currentLength -= standaloneDataLength;
625 packagesBegin += standaloneDataLength;
626 profilerDataType_ = ProfilerTraceFileHeader::UNKNOW_TYPE;
627 hasGotHeader_ = false;
628 return true;
629 }
630 return false;
631 }
632
GetHeaderAndUpdateLengthMark(std::deque<uint8_t>::iterator & packagesBegin,size_t & currentLength)633 bool HtraceParser::GetHeaderAndUpdateLengthMark(std::deque<uint8_t>::iterator& packagesBegin, size_t& currentLength)
634 {
635 if (!hasGotHeader_) {
636 if (!InitProfilerTraceFileHeader()) {
637 return false;
638 }
639 packagesBuffer_.erase(packagesBuffer_.begin(), packagesBuffer_.begin() + packetHeaderLength_);
640 processedDataLen_ += packetHeaderLength_;
641 currentLength -= packetHeaderLength_;
642 packagesBegin += packetHeaderLength_;
643 parsedFileOffset_ += packetHeaderLength_;
644 htraceCurentLength_ = profilerDataLength_ - packetHeaderLength_;
645 hasGotHeader_ = true;
646 if (!currentLength) {
647 return false;
648 }
649 }
650 return true;
651 }
652 #if IS_WASM
ParseSDKData()653 bool HtraceParser::ParseSDKData()
654 {
655 if (packagesBuffer_.size() >= profilerDataLength_ - packetHeaderLength_) {
656 auto thirdPartySize = profilerDataLength_ - packetHeaderLength_;
657 auto buffer = std::make_unique<uint8_t[]>(thirdPartySize).get();
658 std::copy(packagesBuffer_.begin(), packagesBuffer_.begin() + thirdPartySize, buffer);
659 TraceStreamer_Plugin_Out_Filter(reinterpret_cast<const char*>(buffer), thirdPartySize, standalonePluginName_);
660 return true;
661 }
662 return false;
663 }
664 #endif
665
ParseSegLengthAndEnsureSegDataEnough(std::deque<uint8_t>::iterator & packagesBegin,size_t & currentLength)666 bool HtraceParser::ParseSegLengthAndEnsureSegDataEnough(std::deque<uint8_t>::iterator& packagesBegin,
667 size_t& currentLength)
668 {
669 std::string bufferLine;
670 if (!hasGotSegLength_) {
671 if (currentLength < packetSegLength_) {
672 return false;
673 }
674 bufferLine.assign(packagesBegin, packagesBegin + packetSegLength_);
675 const uint32_t* len = reinterpret_cast<const uint32_t*>(bufferLine.data());
676 nextLength_ = *len;
677 lenBuffer_ = bufferLine;
678 htraceLength_ += nextLength_ + packetSegLength_;
679 hasGotSegLength_ = true;
680 currentLength -= packetSegLength_;
681 packagesBegin += packetSegLength_;
682 parsedFileOffset_ += packetSegLength_;
683 splitFileOffset_ = profilerDataLength_ - htraceCurentLength_;
684 htraceCurentLength_ -= packetSegLength_;
685 }
686 if (currentLength < nextLength_) {
687 return false;
688 }
689 return true;
690 }
ParseDataRecursively(std::deque<uint8_t>::iterator & packagesBegin,size_t & currentLength)691 bool HtraceParser::ParseDataRecursively(std::deque<uint8_t>::iterator& packagesBegin, size_t& currentLength)
692 {
693 TS_CHECK_TRUE_RET(GetHeaderAndUpdateLengthMark(packagesBegin, currentLength), false);
694 if (profilerDataType_ == ProfilerTraceFileHeader::HIPERF_DATA) {
695 return ParseHiperfData(packagesBegin, currentLength);
696 }
697 if (profilerDataType_ == ProfilerTraceFileHeader::STANDALONE_DATA) {
698 if (EBPF_PLUGIN_NAME.compare(standalonePluginName_) == 0) {
699 return CalcEbpfCutOffset(packagesBegin, currentLength);
700 } else {
701 #if IS_WASM
702 TS_CHECK_TRUE_RET(ParseSDKData(), false); // 三方sdk逻辑待验证。
703 #endif
704 }
705 }
706 std::string bufferLine;
707 while (true) {
708 TS_CHECK_TRUE_RET(ParseSegLengthAndEnsureSegDataEnough(packagesBegin, currentLength), true);
709 bufferLine.assign(packagesBegin, packagesBegin + nextLength_);
710 ParseTraceDataItem(bufferLine);
711 hasGotSegLength_ = false;
712 packagesBegin += nextLength_;
713 currentLength -= nextLength_;
714 parsedFileOffset_ += nextLength_;
715 if (nextLength_ > htraceCurentLength_) {
716 TS_LOGE("fatal error, data length not match nextLength_:%u, htraceCurentLength_:%" PRIu64 "", nextLength_,
717 htraceCurentLength_);
718 }
719 htraceCurentLength_ -= nextLength_;
720 if (htraceCurentLength_ == 0) {
721 hasGotHeader_ = false;
722 processedDataLen_ += packagesBegin - packagesBuffer_.begin();
723 packagesBuffer_.erase(packagesBuffer_.begin(), packagesBegin);
724 profilerDataType_ = ProfilerTraceFileHeader::UNKNOW_TYPE;
725 TS_LOGD("read proto finished!");
726 return ParseDataRecursively(packagesBegin, currentLength);
727 }
728 }
729 return true;
730 }
731
ParseTraceDataSegment(std::unique_ptr<uint8_t[]> bufferStr,size_t size,bool isFinish)732 void HtraceParser::ParseTraceDataSegment(std::unique_ptr<uint8_t[]> bufferStr, size_t size, bool isFinish)
733 {
734 packagesBuffer_.insert(packagesBuffer_.end(), &bufferStr[0], &bufferStr[size]);
735 auto packagesBegin = packagesBuffer_.begin();
736 auto currentLength = packagesBuffer_.size();
737 if (ParseDataRecursively(packagesBegin, currentLength)) {
738 processedDataLen_ += packagesBegin - packagesBuffer_.begin();
739 packagesBuffer_.erase(packagesBuffer_.begin(), packagesBegin);
740 }
741 return;
742 }
ParseHiperfData(std::deque<uint8_t>::iterator & packagesBegin,size_t & currentLength)743 bool HtraceParser::ParseHiperfData(std::deque<uint8_t>::iterator& packagesBegin, size_t& currentLength)
744 {
745 if (!traceDataCache_->isSplitFile_) {
746 if (packagesBuffer_.size() >= profilerDataLength_ - packetHeaderLength_) {
747 auto size = profilerDataLength_ - packetHeaderLength_;
748 (void)perfDataParser_->InitPerfDataAndLoad(packagesBuffer_, size, processedDataLen_, false, true);
749 currentLength -= size;
750 packagesBegin += size;
751 profilerDataType_ = ProfilerTraceFileHeader::UNKNOW_TYPE;
752 hasGotHeader_ = false;
753 return true;
754 }
755 return false;
756 }
757
758 bool isFinish = perfProcessedLen_ + packagesBuffer_.size() >= profilerDataLength_ - packetHeaderLength_;
759 auto size = packagesBuffer_.size();
760 if (isFinish) {
761 size = profilerDataLength_ - packetHeaderLength_ - perfProcessedLen_;
762 }
763 auto ret = perfDataParser_->InitPerfDataAndLoad(packagesBuffer_, size, processedDataLen_, true, isFinish);
764 perfProcessedLen_ += ret;
765 currentLength -= ret;
766 packagesBegin += ret;
767 if (isFinish) {
768 profilerDataType_ = ProfilerTraceFileHeader::UNKNOW_TYPE;
769 hasGotHeader_ = false;
770 }
771 return true;
772 }
StoreTraceDataSegment(std::unique_ptr<uint8_t[]> bufferStr,size_t size,int32_t isFinish)773 void HtraceParser::StoreTraceDataSegment(std::unique_ptr<uint8_t[]> bufferStr, size_t size, int32_t isFinish)
774 {
775 packagesBuffer_.insert(packagesBuffer_.end(), &bufferStr[0], &bufferStr[size]);
776 if (!traceDataCache_->isSplitFile_) {
777 return;
778 }
779
780 uint64_t length = packagesBuffer_.size();
781 auto ret = perfDataParser_->InitPerfDataAndLoad(packagesBuffer_, length, 0, true, isFinish);
782 perfProcessedLen_ += ret;
783 processedDataLen_ += ret;
784 packagesBuffer_.erase(packagesBuffer_.begin(), packagesBuffer_.begin() + ret);
785 return;
786 }
TraceDataSegmentEnd(bool isSplitFile)787 void HtraceParser::TraceDataSegmentEnd(bool isSplitFile)
788 {
789 perfDataParser_->InitPerfDataAndLoad(packagesBuffer_, packagesBuffer_.size(), 0, isSplitFile, true);
790 packagesBuffer_.clear();
791 return;
792 }
793
InitProfilerTraceFileHeader()794 bool HtraceParser::InitProfilerTraceFileHeader()
795 {
796 if (packagesBuffer_.size() < packetHeaderLength_) {
797 TS_LOGI("buffer size less than profiler trace file header");
798 return false;
799 }
800 uint8_t buffer[packetHeaderLength_];
801 (void)memset_s(buffer, sizeof(buffer), 0, sizeof(buffer));
802 int32_t i = 0;
803 for (auto it = packagesBuffer_.begin(); it != packagesBuffer_.begin() + packetHeaderLength_; ++it, ++i) {
804 buffer[i] = *it;
805 }
806 ProfilerTraceFileHeader* pHeader = reinterpret_cast<ProfilerTraceFileHeader*>(buffer);
807 if (pHeader->data.length <= packetHeaderLength_ || pHeader->data.magic != ProfilerTraceFileHeader::HEADER_MAGIC) {
808 TS_LOGE("Profiler Trace data is truncated or invalid magic! len = %" PRIu64 ", maigc = %" PRIx64 "",
809 pHeader->data.length, pHeader->data.magic);
810 return false;
811 }
812 if (pHeader->data.dataType == ProfilerTraceFileHeader::HIPERF_DATA) {
813 perfDataParser_->RecordPerfProfilerHeader(buffer, packetHeaderLength_);
814 } else if (pHeader->data.dataType == ProfilerTraceFileHeader::STANDALONE_DATA &&
815 EBPF_PLUGIN_NAME.compare(pHeader->data.standalonePluginName) == 0) {
816 ebpfDataParser_->RecordEbpfProfilerHeader(buffer, packetHeaderLength_);
817 } else {
818 auto ret = memcpy_s(&profilerTraceFileHeader_, sizeof(profilerTraceFileHeader_), buffer, packetHeaderLength_);
819 if (ret == -1 || profilerTraceFileHeader_.data.magic != ProfilerTraceFileHeader::HEADER_MAGIC) {
820 TS_LOGE("Get profiler trace file header failed! ret = %d, magic = %" PRIx64 "", ret,
821 profilerTraceFileHeader_.data.magic);
822 return false;
823 }
824 }
825 profilerDataLength_ = pHeader->data.length;
826 profilerDataType_ = pHeader->data.dataType;
827 memcpy_s(standalonePluginName_, sizeof(standalonePluginName_), pHeader->data.standalonePluginName,
828 sizeof(standalonePluginName_));
829
830 TS_LOGI("magic = %" PRIx64 ", length = %" PRIu64 ", dataType = %x, boottime = %" PRIu64 "", pHeader->data.magic,
831 pHeader->data.length, pHeader->data.dataType, pHeader->data.boottime);
832 #if IS_WASM
833 const int32_t DATA_TYPE_CLOCK = 100;
834 TraceStreamer_Plugin_Out_SendData(reinterpret_cast<char*>(buffer), packetHeaderLength_, DATA_TYPE_CLOCK);
835 #endif
836 htraceClockDetailParser_->Parse(pHeader);
837 return true;
838 }
839 } // namespace TraceStreamer
840 } // namespace SysTuning
841