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 #include "perf_data_parser.h"
16 #include "clock_filter_ex.h"
17 #include "file.h"
18 #ifdef ENABLE_ADDR2LINE
19 #include "llvm/DebugInfo/Symbolize/Symbolize.h"
20 #endif
21 #include "perf_data_filter.h"
22 #include "perf_file_format.h"
23 #include "stat_filter.h"
24 #include "utilities.h"
25 #include <string>
26
27 namespace SysTuning {
28 namespace TraceStreamer {
PerfDataParser(TraceDataCache * dataCache,const TraceStreamerFilters * ctx)29 PerfDataParser::PerfDataParser(TraceDataCache *dataCache, const TraceStreamerFilters *ctx)
30 : EventParserBase(dataCache, ctx),
31 configNameIndex_(traceDataCache_->dataDict_.GetStringIndex("config_name")),
32 workloaderIndex_(traceDataCache_->dataDict_.GetStringIndex("workload_cmd")),
33 cmdlineIndex_(traceDataCache_->dataDict_.GetStringIndex("cmdline")),
34 runingStateIndex_(traceDataCache_->dataDict_.GetStringIndex("Running")),
35 suspendStatIndex_(traceDataCache_->dataDict_.GetStringIndex("Suspend")),
36 unknownStateIndex_(traceDataCache_->dataDict_.GetStringIndex("-")),
37 pidAndStackHashToCallChainId_(INVALID_UINT32)
38 {
39 SymbolsFile::onRecording_ = false;
40 }
InitPerfDataAndLoad(const std::deque<uint8_t> & dequeBuffer,uint64_t size,uint64_t offset,bool isSplitFile,bool isFinish)41 uint64_t PerfDataParser::InitPerfDataAndLoad(const std::deque<uint8_t> &dequeBuffer,
42 uint64_t size,
43 uint64_t offset,
44 bool isSplitFile,
45 bool isFinish)
46 {
47 if (isSplitFile) {
48 return SplitPerfData(dequeBuffer, size, offset, isFinish);
49 }
50
51 bufferSize_ = size;
52 buffer_ = std::make_unique<uint8_t[]>(size);
53 std::copy(dequeBuffer.begin(), dequeBuffer.begin() + size, buffer_.get());
54 LoadPerfData();
55 buffer_.reset();
56 return size;
57 }
58
DataProcessingLength(const std::deque<uint8_t> & dequeBuffer,uint64_t size,uint64_t offset,bool isFinish)59 uint64_t PerfDataParser::DataProcessingLength(const std::deque<uint8_t> &dequeBuffer,
60 uint64_t size,
61 uint64_t offset,
62 bool isFinish)
63 {
64 using PerfSplitFunc = bool (PerfDataParser::*)(const std::deque<uint8_t> &, uint64_t, uint64_t &, bool &);
65 std::vector<PerfSplitFunc> splitFunc = {&PerfDataParser::SplitPerfStarting,
66 &PerfDataParser::SplitPerfParsingHead,
67 &PerfDataParser::SplitPerfWaitForAttr,
68 &PerfDataParser::SplitPerfParsingAttr,
69 &PerfDataParser::SplitPerfWaitForData,
70 &PerfDataParser::SplitPerfParsingData,
71 &PerfDataParser::SplitPerfParsingFeatureSection,
72 &PerfDataParser::SplitPerfWaitForFinish};
73
74 if (static_cast<size_t>(splitState_) >= splitFunc.size()) {
75 TS_LOGE("Invalid split state %d", static_cast<int>(splitState_));
76 perfSplitError_ = true;
77 SplitDataWithdraw();
78 return size;
79 }
80 uint64_t processedLen = 0;
81 bool ret = true;
82 bool invalid = false;
83 while (ret) {
84 if (isFinish && splitState_ == SplitPerfState::WAIT_FOR_FINISH) {
85 uint64_t currentDataOffset = perfDataOffset_ + processedLength_ + processedLen;
86 HtraceSplitResult offsetData = {.type = (int32_t)SplitDataDataType::SPLIT_FILE_JSON,
87 .originSeg = {.offset = currentDataOffset, .size = size - processedLen}};
88 splitResult_.emplace_back(offsetData);
89 processedLength_ += size;
90 return size;
91 }
92
93 ret = (this->*splitFunc[static_cast<int32_t>(splitState_)])(dequeBuffer, size, processedLen, invalid);
94 if (invalid) {
95 perfSplitError_ = true;
96 SplitDataWithdraw();
97 return size;
98 }
99 }
100
101 if (isFinish) {
102 TS_LOGE("split not finish but data end");
103 perfSplitError_ = true;
104 SplitDataWithdraw();
105 return size;
106 }
107
108 processedLength_ += processedLen;
109 return processedLen;
110 }
111
SplitPerfData(const std::deque<uint8_t> & dequeBuffer,uint64_t size,uint64_t offset,bool isFinish)112 uint64_t PerfDataParser::SplitPerfData(const std::deque<uint8_t> &dequeBuffer,
113 uint64_t size,
114 uint64_t offset,
115 bool isFinish)
116 {
117 if (processedLength_ == 0) {
118 perfDataOffset_ = offset;
119 perfSplitError_ = false;
120 }
121
122 if (perfSplitError_) {
123 return size;
124 }
125
126 uint64_t datalength = DataProcessingLength(dequeBuffer, size, offset, isFinish);
127 return datalength;
128 }
129
SplitPerfStarting(const std::deque<uint8_t> & dequeBuffer,uint64_t size,uint64_t & processedLen,bool & invalid)130 bool PerfDataParser::SplitPerfStarting(const std::deque<uint8_t> &dequeBuffer,
131 uint64_t size,
132 uint64_t &processedLen,
133 bool &invalid)
134 {
135 if (hasProfilerHead_) {
136 HtraceSplitResult htraceHead = {.type = (int32_t)SplitDataDataType::SPLIT_FILE_DATA,
137 .buffer = {.address = reinterpret_cast<uint8_t *>(&profilerHeader_),
138 .size = sizeof(ProfilerTraceFileHeader)}};
139 splitResult_.emplace_back(htraceHead);
140 }
141
142 splitState_ = SplitPerfState::PARSING_HEAD;
143 return true;
144 }
145
SplitPerfParsingHead(const std::deque<uint8_t> & dequeBuffer,uint64_t size,uint64_t & processedLen,bool & invalid)146 bool PerfDataParser::SplitPerfParsingHead(const std::deque<uint8_t> &dequeBuffer,
147 uint64_t size,
148 uint64_t &processedLen,
149 bool &invalid)
150 {
151 processedLen = 0;
152 if (size < sizeof(perf_file_header)) {
153 return false;
154 }
155
156 std::copy_n(dequeBuffer.begin(), sizeof(perf_file_header), reinterpret_cast<char *>(&perfHeader_));
157
158 if (memcmp(perfHeader_.magic, PERF_MAGIC, sizeof(perfHeader_.magic))) {
159 TS_LOGE("invalid magic id");
160 invalid = true;
161 return false;
162 }
163
164 const int fetureMax = 256;
165 const int sizeFetureCount = 8;
166 featureCount_ = 0;
167 for (auto i = 0; i < fetureMax / sizeFetureCount; i++) {
168 std::bitset<sizeFetureCount> features(perfHeader_.features[i]);
169 for (auto j = 0; j < sizeFetureCount; j++) {
170 if (features.test(j)) {
171 featureCount_++;
172 }
173 }
174 }
175
176 HtraceSplitResult perfHead = {
177 .type = (int32_t)SplitDataDataType::SPLIT_FILE_DATA,
178 .buffer = {.address = reinterpret_cast<uint8_t *>(&perfHeader_), .size = sizeof(perf_file_header)}};
179 splitResult_.emplace_back(perfHead);
180 processedLen += sizeof(perf_file_header);
181 splitState_ = SplitPerfState::WAIT_FOR_ATTR;
182 return true;
183 }
184
SplitPerfWaitForAttr(const std::deque<uint8_t> & dequeBuffer,uint64_t size,uint64_t & processedLen,bool & invalid)185 bool PerfDataParser::SplitPerfWaitForAttr(const std::deque<uint8_t> &dequeBuffer,
186 uint64_t size,
187 uint64_t &processedLen,
188 bool &invalid)
189 {
190 if (processedLength_ + processedLen > perfHeader_.attrs.offset) {
191 TS_LOGE("offset of attr is wrong %" PRIu64 "", perfHeader_.attrs.offset);
192 invalid = true;
193 return false;
194 }
195
196 if (processedLength_ + size < perfHeader_.attrs.offset) {
197 processedLen = size;
198 return false;
199 }
200
201 processedLen += perfHeader_.attrs.offset - (processedLength_ + processedLen);
202 splitState_ = SplitPerfState::PARSING_ATTR;
203 return true;
204 }
205
SplitPerfParsingAttr(const std::deque<uint8_t> & dequeBuffer,uint64_t size,uint64_t & processedLen,bool & invalid)206 bool PerfDataParser::SplitPerfParsingAttr(const std::deque<uint8_t> &dequeBuffer,
207 uint64_t size,
208 uint64_t &processedLen,
209 bool &invalid)
210 {
211 int attrCount = perfHeader_.attrs.size / perfHeader_.attrSize;
212 if (attrCount == 0) {
213 TS_LOGE("no attr in file");
214 invalid = true;
215 return false;
216 }
217
218 uint64_t lengthRemain = size - processedLen;
219 if (lengthRemain < perfHeader_.attrs.size) {
220 return false;
221 }
222
223 auto buffer = std::make_unique<uint8_t[]>(perfHeader_.attrs.size);
224 std::copy_n(dequeBuffer.begin() + processedLen, perfHeader_.attrs.size, buffer.get());
225 std::vector<perf_file_attr> vecAttr;
226 for (int index = 0; index < attrCount; ++index) {
227 perf_file_attr *attr = reinterpret_cast<perf_file_attr *>(buffer.get() + perfHeader_.attrSize * index);
228 vecAttr.push_back(*attr);
229 // for Update Clock Type
230 if (index == 0) {
231 useClockId_ = attr->attr.use_clockid;
232 clockId_ = attr->attr.clockid;
233 TS_LOGI("useClockId_ = %u, clockId_ = %u", useClockId_, clockId_);
234 }
235 }
236
237 sampleType_ = vecAttr[0].attr.sample_type;
238 if (!(sampleType_ & PERF_SAMPLE_TIME)) {
239 TS_LOGE("no time in sample data, not support split, sampleType_ = %" PRIx64 "", sampleType_);
240 invalid = true;
241 return false;
242 }
243 sampleTimeOffset_ = (((sampleType_ & PERF_SAMPLE_IDENTIFIER) != 0) + ((sampleType_ & PERF_SAMPLE_IP) != 0) +
244 ((sampleType_ & PERF_SAMPLE_TID) != 0)) *
245 sizeof(uint64_t);
246
247 processedLen += perfHeader_.attrs.size;
248 splitState_ = SplitPerfState::WAIT_FOR_DATA;
249 return true;
250 }
251
SplitPerfWaitForData(const std::deque<uint8_t> & dequeBuffer,uint64_t size,uint64_t & processedLen,bool & invalid)252 bool PerfDataParser::SplitPerfWaitForData(const std::deque<uint8_t> &dequeBuffer,
253 uint64_t size,
254 uint64_t &processedLen,
255 bool &invalid)
256 {
257 if (processedLength_ + processedLen > perfHeader_.data.offset) {
258 TS_LOGE("offset of data is wrong %" PRIu64 "", perfHeader_.data.offset);
259 invalid = true;
260 return false;
261 }
262
263 if (processedLength_ + size < perfHeader_.data.offset) {
264 processedLen = size;
265 return false;
266 }
267
268 HtraceSplitResult offsetData = {.type = (int32_t)SplitDataDataType::SPLIT_FILE_JSON,
269 .originSeg = {.offset = perfDataOffset_ + sizeof(perf_file_header),
270 .size = perfHeader_.data.offset - sizeof(perf_file_header)}};
271 splitResult_.emplace_back(offsetData);
272
273 processedLen += perfHeader_.data.offset - (processedLength_ + processedLen);
274 splitState_ = SplitPerfState::PARSING_DATA;
275 return true;
276 }
277
DataLengthProcessing(const std::deque<uint8_t> & dequeBuffer,perf_event_header & dataHeader,uint64_t size,uint64_t & processedLen,bool & invalid)278 SplitPerfState PerfDataParser::DataLengthProcessing(const std::deque<uint8_t> &dequeBuffer,
279 perf_event_header &dataHeader,
280 uint64_t size,
281 uint64_t &processedLen,
282 bool &invalid)
283 {
284 uint64_t totalDataRemain = perfHeader_.data.offset + perfHeader_.data.size - processedLength_ - processedLen;
285 if (totalDataRemain < sizeof(perf_event_header)) {
286 processedLen += totalDataRemain;
287 splitDataSize_ += totalDataRemain;
288 splitState_ = SplitPerfState::PARSING_FEATURE_SECTION;
289 return SplitPerfState::PARSING_HEAD;
290 }
291
292 uint64_t lengthRemain = size - processedLen;
293 if (lengthRemain < sizeof(perf_event_header)) {
294 return SplitPerfState::STARTING;
295 }
296 std::copy_n(dequeBuffer.begin() + processedLen, sizeof(perf_event_header), reinterpret_cast<char *>(&dataHeader));
297 if (dataHeader.size < sizeof(perf_event_header)) {
298 TS_LOGE("invalid data size %u", dataHeader.size);
299 invalid = true;
300 return SplitPerfState::STARTING;
301 }
302 if (lengthRemain < dataHeader.size) {
303 return SplitPerfState::STARTING;
304 }
305 if (totalDataRemain < sizeof(perf_event_header)) {
306 processedLen += totalDataRemain;
307 splitDataSize_ += totalDataRemain;
308 splitState_ = SplitPerfState::PARSING_FEATURE_SECTION;
309 return SplitPerfState::PARSING_HEAD;
310 }
311 return SplitPerfState::WAIT_FOR_ATTR;
312 }
313
SplitPerfParsingData(const std::deque<uint8_t> & dequeBuffer,uint64_t size,uint64_t & processedLen,bool & invalid)314 bool PerfDataParser::SplitPerfParsingData(const std::deque<uint8_t> &dequeBuffer,
315 uint64_t size,
316 uint64_t &processedLen,
317 bool &invalid)
318 {
319 perf_event_header dataHeader;
320 auto ret = DataLengthProcessing(dequeBuffer, dataHeader, size, processedLen, invalid);
321 if (SplitPerfState::STARTING == ret) {
322 return false;
323 } else if (SplitPerfState::PARSING_HEAD == ret) {
324 return true;
325 }
326 bool needRecord = true;
327 if (splitDataEnd_) {
328 needRecord = false;
329 } else if (dataHeader.type == PERF_RECORD_SAMPLE) {
330 auto buffer = std::make_unique<uint8_t[]>(dataHeader.size);
331 std::copy_n(dequeBuffer.begin() + processedLen + sizeof(perf_event_header),
332 dataHeader.size - sizeof(perf_event_header), buffer.get());
333 uint64_t time = *(reinterpret_cast<uint64_t *>(buffer.get() + sampleTimeOffset_));
334 uint64_t newTimeStamp = 0;
335 if (useClockId_ != 0) {
336 newTimeStamp = streamFilters_->clockFilter_->ToPrimaryTraceTime(perfToTSClockType_.at(clockId_), time);
337 }
338 UpdatePluginTimeRange(perfToTSClockType_.at(clockId_), time, newTimeStamp);
339 if (newTimeStamp < traceDataCache_->SplitFileMinTime()) {
340 needRecord = false;
341 } else if (newTimeStamp > traceDataCache_->SplitFileMaxTime()) {
342 splitDataEnd_ = true;
343 needRecord = false;
344 }
345 }
346
347 if (needRecord) {
348 uint64_t currentDataOffset = perfDataOffset_ + processedLength_ + processedLen;
349 if (splitResult_.rbegin() != splitResult_.rend() &&
350 (splitResult_.rbegin()->originSeg.offset + splitResult_.rbegin()->originSeg.size == currentDataOffset)) {
351 splitResult_.rbegin()->originSeg.size += dataHeader.size;
352 } else {
353 HtraceSplitResult offsetData = {.type = (int32_t)SplitDataDataType::SPLIT_FILE_JSON,
354 .originSeg = {.offset = currentDataOffset, .size = dataHeader.size}};
355 splitResult_.emplace_back(offsetData);
356 }
357 splitDataSize_ += dataHeader.size;
358 }
359 processedLen += dataHeader.size;
360 return true;
361 }
362
SplitPerfParsingFeatureSection(const std::deque<uint8_t> & dequeBuffer,uint64_t size,uint64_t & processedLen,bool & invalid)363 bool PerfDataParser::SplitPerfParsingFeatureSection(const std::deque<uint8_t> &dequeBuffer,
364 uint64_t size,
365 uint64_t &processedLen,
366 bool &invalid)
367 {
368 featureSectioSize_ = featureCount_ * sizeof(perf_file_section);
369 if (featureSectioSize_ == 0) {
370 TS_LOGI("no feature section in file");
371 splitState_ = SplitPerfState::WAIT_FOR_FINISH;
372 return false;
373 }
374
375 uint64_t lengthRemain = size - processedLen;
376 if (lengthRemain < featureSectioSize_) {
377 return false;
378 }
379
380 featureSection_ = std::make_unique<uint8_t[]>(featureSectioSize_);
381 std::copy_n(dequeBuffer.begin() + processedLen, featureSectioSize_, featureSection_.get());
382 uint64_t splitDropSize = perfHeader_.data.size - splitDataSize_;
383 for (auto i = 0; i < featureCount_; ++i) {
384 perf_file_section *featureSections = reinterpret_cast<perf_file_section *>(featureSection_.get());
385 featureSections[i].offset -= splitDropSize;
386 }
387 HtraceSplitResult featureBuff = {.type = (int32_t)SplitDataDataType::SPLIT_FILE_DATA,
388 .buffer = {.address = featureSection_.get(), .size = featureSectioSize_}};
389 splitResult_.emplace_back(featureBuff);
390
391 processedLen += featureSectioSize_;
392 perfHeader_.data.size = splitDataSize_;
393 profilerHeader_.data.length -= splitDropSize;
394 splitState_ = SplitPerfState::WAIT_FOR_FINISH;
395 return true;
396 }
397
SplitPerfWaitForFinish(const std::deque<uint8_t> & dequeBuffer,uint64_t size,uint64_t & processedLen,bool & invalid)398 bool PerfDataParser::SplitPerfWaitForFinish(const std::deque<uint8_t> &dequeBuffer,
399 uint64_t size,
400 uint64_t &processedLen,
401 bool &invalid)
402 {
403 return false;
404 }
405
~PerfDataParser()406 PerfDataParser::~PerfDataParser()
407 {
408 recordDataReader_.reset();
409 if (remove(tmpPerfData_.c_str()) == -1) {
410 TS_LOGE("remove %s err:%s\n", tmpPerfData_.c_str(), strerror(errno));
411 }
412 TS_LOGI("perf data ts MIN:%llu, MAX:%llu", static_cast<unsigned long long>(GetPluginStartTime()),
413 static_cast<unsigned long long>(GetPluginEndTime()));
414 }
415
GetFileIdWithLikelyFilePath(const std::string & inputFilePath)416 std::tuple<uint64_t, DataIndex> PerfDataParser::GetFileIdWithLikelyFilePath(const std::string &inputFilePath)
417 {
418 auto perfFilesData = traceDataCache_->GetConstPerfFilesData();
419 for (auto row = 0; row < perfFilesData.Size(); row++) {
420 auto filePath = traceDataCache_->GetDataFromDict(perfFilesData.FilePaths()[row]);
421 if (EndsWith(filePath, inputFilePath)) {
422 return std::make_tuple(perfFilesData.FileIds()[row], perfFilesData.FilePaths()[row]);
423 }
424 }
425 return std::make_tuple(INVALID_UINT64, INVALID_DATAINDEX);
426 }
427
ReloadPerfFile(const std::unique_ptr<SymbolsFile> & symbolsFile,uint64_t & fileId,DataIndex & filePathIndex)428 bool PerfDataParser::ReloadPerfFile(const std::unique_ptr<SymbolsFile> &symbolsFile,
429 uint64_t &fileId,
430 DataIndex &filePathIndex)
431 {
432 std::tie(fileId, filePathIndex) = GetFileIdWithLikelyFilePath(symbolsFile->filePath_);
433 if (fileId == INVALID_UINT64) {
434 return false;
435 }
436 // clean perf file same fileId data
437 if (!traceDataCache_->GetPerfFilesData()->EraseFileIdSameData(fileId)) {
438 return false;
439 }
440 // add new symbol Data to PerfFile table
441 for (auto dfxSymbol : symbolsFile->GetSymbols()) {
442 auto symbolNameIndex = traceDataCache_->GetDataIndex(dfxSymbol.GetName());
443 traceDataCache_->GetPerfFilesData()->AppendNewPerfFiles(fileId, dfxSymbol.index_, symbolNameIndex,
444 filePathIndex);
445 }
446 return true;
447 }
448
ReloadPerfCallChain(const std::unique_ptr<SymbolsFile> & symbolsFile,uint64_t fileId,DataIndex filePathIndex)449 void PerfDataParser::ReloadPerfCallChain(const std::unique_ptr<SymbolsFile> &symbolsFile,
450 uint64_t fileId,
451 DataIndex filePathIndex)
452 {
453 // Associate perf_callchain with perf_file
454 auto perfCallChainData = traceDataCache_->GetPerfCallChainData();
455
456 for (auto row = 0; row < perfCallChainData->Size(); row++) {
457 if (perfCallChainData->FileIds()[row] == fileId) {
458 // Get the current call stack's pid and tid
459 if (!callChainIdToThreadInfo_.count(perfCallChainData->CallChainIds()[row])) {
460 continue;
461 }
462 pid_t pid;
463 pid_t tid;
464 std::tie(pid, tid) = callChainIdToThreadInfo_.at(perfCallChainData->CallChainIds()[row]);
465 // Get VirtualThread object
466 auto &virtualThread = report_->virtualRuntime_.GetThread(pid, tid);
467 // Get dfxMap object
468 auto dfxMap = virtualThread.FindMapByAddr(perfCallChainData->Ips()[row]);
469 auto vaddr = symbolsFile->GetVaddrInSymbols(perfCallChainData->Ips()[row], dfxMap->begin, dfxMap->offset);
470 auto dfxSymbol = symbolsFile->GetSymbolWithVaddr(vaddr);
471 auto nameIndex = traceDataCache_->GetDataIndex(dfxSymbol.GetName());
472 perfCallChainData->UpdateSymbolRelatedData(row, dfxSymbol.funcVaddr_, dfxSymbol.offsetToVaddr_,
473 dfxSymbol.index_, nameIndex);
474 }
475 }
476 }
477
PerfReloadSymbolFile(const std::unique_ptr<SymbolsFile> & symbolsFile)478 void PerfDataParser::PerfReloadSymbolFile(const std::unique_ptr<SymbolsFile> &symbolsFile)
479 {
480 if (symbolsFile == nullptr) {
481 return;
482 }
483 uint64_t fileId;
484 DataIndex filePathIndex;
485 if (!ReloadPerfFile(symbolsFile, fileId, filePathIndex)) {
486 return;
487 }
488 ReloadPerfCallChain(symbolsFile, fileId, filePathIndex);
489 }
490 #ifdef ENABLE_ADDR2LINE
ParseSourceLocation(const std::string & directory,const std::string & fileName)491 void PerfDataParser::ParseSourceLocation(const std::string &directory, const std::string &fileName)
492 {
493 uint64_t fileId;
494 DataIndex filePathIndex;
495 std::tie(fileId, filePathIndex) = GetFileIdWithLikelyFilePath(fileName);
496 if (fileId == INVALID_UINT64 || filePathIndex == INVALID_UINT64) {
497 return;
498 }
499 llvm::symbolize::LLVMSymbolizer::Options Opts;
500 llvm::symbolize::LLVMSymbolizer symbolizer(Opts);
501
502 // Associate perf_callchain with perf_file
503 auto perfCallChainData = traceDataCache_->GetPerfCallChainData();
504 auto path = directory + "/" + fileName;
505 for (auto row = 0; row < perfCallChainData->Size(); row++) {
506 if (perfCallChainData->FileIds()[row] != fileId) {
507 continue;
508 }
509 uint64_t vaddrInFile = perfCallChainData->VaddrInFiles()[row];
510 uint64_t offsetToVaddr = perfCallChainData->OffsetToVaddrs()[row];
511 if (vaddrInFile == 0 || offsetToVaddr == 0) {
512 continue;
513 }
514 llvm::object::SectionedAddress address = {vaddrInFile + offsetToVaddr,
515 llvm::object::SectionedAddress::UndefSection};
516 auto inlinedContext = symbolizer.symbolizeInlinedCode(path, address);
517 if (inlinedContext && inlinedContext->getNumberOfFrames()) {
518 auto firstFrame = inlinedContext->getFrame(0);
519 auto sourceFileIndex = traceDataCache_->GetDataIndex(firstFrame.FileName);
520 perfCallChainData->SetSourceFileNameAndLineNumber(row, sourceFileIndex,
521 static_cast<uint64_t>(firstFrame.Line));
522 } else {
523 TS_LOGD("symbolizeInlinedCode execute failed!");
524 }
525 }
526 }
527 #endif
LoadPerfData()528 bool PerfDataParser::LoadPerfData()
529 {
530 // try load the perf data
531 int32_t fd(base::OpenFile(tmpPerfData_, O_CREAT | O_RDWR, TS_PERMISSION_RW));
532 if (!fd) {
533 fprintf(stdout, "Failed to create file: %s", tmpPerfData_.c_str());
534 return false;
535 }
536 (void)ftruncate(fd, 0);
537 if (bufferSize_ != (size_t)write(fd, buffer_.get(), bufferSize_)) {
538 close(fd);
539 return false;
540 }
541 close(fd);
542 recordDataReader_ = PerfFileReader::Instance(tmpPerfData_);
543 if (recordDataReader_ == nullptr) {
544 return false;
545 }
546 report_ = std::make_unique<Report>();
547 return Reload();
548 }
Reload()549 bool PerfDataParser::Reload()
550 {
551 pidAndStackHashToCallChainId_.Clear();
552 fileDataDictIdToFileId_.clear();
553 tidToPid_.clear();
554 streamFilters_->perfDataFilter_->BeforeReload();
555 traceDataCache_->GetPerfSampleData()->Clear();
556 traceDataCache_->GetPerfThreadData()->Clear();
557
558 if (!recordDataReader_->ReadFeatureSection()) {
559 printf("record format error.\n");
560 return false;
561 }
562 // update perf report table
563 UpdateEventConfigInfo();
564 UpdateReportWorkloadInfo();
565 UpdateCmdlineInfo();
566 SetHM();
567
568 // update perf Files table
569 UpdateSymbolAndFilesData();
570
571 TS_LOGD("process record");
572 UpdateClockType();
573 recordDataReader_->ReadDataSection(std::bind(&PerfDataParser::RecordCallBack, this, std::placeholders::_1));
574 TS_LOGD("process record completed");
575 TS_LOGI("load perf data done");
576 return true;
577 }
578
UpdateEventConfigInfo()579 void PerfDataParser::UpdateEventConfigInfo()
580 {
581 auto features = recordDataReader_->GetFeatures();
582 cpuOffMode_ = find(features.begin(), features.end(), FEATURE::HIPERF_CPU_OFF) != features.end();
583 if (cpuOffMode_) {
584 TS_LOGD("this is cpuOffMode ");
585 }
586 const PerfFileSection *featureSection = recordDataReader_->GetFeatureSection(FEATURE::EVENT_DESC);
587 if (featureSection) {
588 TS_LOGI("have EVENT_DESC");
589 LoadEventDesc();
590 } else {
591 TS_LOGE("Do not have EVENT_DESC !!!");
592 }
593 }
594
LoadEventDesc()595 void PerfDataParser::LoadEventDesc()
596 {
597 const auto featureSection = recordDataReader_->GetFeatureSection(FEATURE::EVENT_DESC);
598 const auto §ionEventdesc = *static_cast<const PerfFileSectionEventDesc *>(featureSection);
599 TS_LOGI("Event descriptions: %zu", sectionEventdesc.eventDesces_.size());
600 for (size_t i = 0; i < sectionEventdesc.eventDesces_.size(); i++) {
601 const auto &fileAttr = sectionEventdesc.eventDesces_[i];
602 TS_LOGI("event name[%zu]: %s ids: %s", i, fileAttr.name.c_str(), VectorToString(fileAttr.ids).c_str());
603 for (uint64_t id : fileAttr.ids) {
604 report_->configIdIndexMaps_[id] = report_->configs_.size(); // setup index
605 TS_LOGI("add config id map %" PRIu64 " to %zu", id, report_->configs_.size());
606 }
607 // when cpuOffMode_ , don't use count mode , use time mode.
608 auto &config = report_->configs_.emplace_back(fileAttr.name, fileAttr.attr.type, fileAttr.attr.config,
609 cpuOffMode_ ? false : true);
610 config.ids_ = fileAttr.ids;
611 TS_ASSERT(config.ids_.size() > 0);
612
613 auto perfReportData = traceDataCache_->GetPerfReportData();
614 auto configValueIndex = traceDataCache_->dataDict_.GetStringIndex(fileAttr.name.c_str());
615 perfReportData->AppendNewPerfReport(configNameIndex_, configValueIndex);
616 }
617 }
618
UpdateReportWorkloadInfo() const619 void PerfDataParser::UpdateReportWorkloadInfo() const
620 {
621 // workload
622 auto featureSection = recordDataReader_->GetFeatureSection(FEATURE::HIPERF_WORKLOAD_CMD);
623 std::string workloader = "";
624 if (featureSection) {
625 TS_LOGI("found HIPERF_META_WORKLOAD_CMD");
626 auto sectionString = static_cast<const PerfFileSectionString *>(featureSection);
627 workloader = sectionString->ToString();
628 } else {
629 TS_LOGW("NOT found HIPERF_META_WORKLOAD_CMD");
630 }
631 if (workloader.empty()) {
632 TS_LOGW("NOT found HIPERF_META_WORKLOAD_CMD");
633 return;
634 }
635 auto perfReportData = traceDataCache_->GetPerfReportData();
636 auto workloaderValueIndex = traceDataCache_->dataDict_.GetStringIndex(workloader.c_str());
637 perfReportData->AppendNewPerfReport(workloaderIndex_, workloaderValueIndex);
638 }
639
UpdateCmdlineInfo() const640 void PerfDataParser::UpdateCmdlineInfo() const
641 {
642 auto cmdline = recordDataReader_->GetFeatureString(FEATURE::CMDLINE);
643 auto perfReportData = traceDataCache_->GetPerfReportData();
644 auto cmdlineValueIndex = traceDataCache_->dataDict_.GetStringIndex(cmdline.c_str());
645 perfReportData->AppendNewPerfReport(cmdlineIndex_, cmdlineValueIndex);
646 }
647
UpdateSymbolAndFilesData()648 void PerfDataParser::UpdateSymbolAndFilesData()
649 {
650 // we need unwind it (for function name match) even not give us path
651 report_->virtualRuntime_.SetDisableUnwind(false);
652
653 // found symbols in file
654 const auto featureSection = recordDataReader_->GetFeatureSection(FEATURE::HIPERF_FILES_SYMBOL);
655 if (featureSection != nullptr) {
656 const PerfFileSectionSymbolsFiles *sectionSymbolsFiles =
657 static_cast<const PerfFileSectionSymbolsFiles *>(featureSection);
658 report_->virtualRuntime_.UpdateFromPerfData(sectionSymbolsFiles->symbolFileStructs_);
659 }
660 // fileid, symbolIndex, filePathIndex
661 uint64_t fileId = 0;
662 for (auto &symbolsFile : report_->virtualRuntime_.GetSymbolsFiles()) {
663 auto filePathIndex = traceDataCache_->dataDict_.GetStringIndex(symbolsFile->filePath_.c_str());
664 uint32_t serial = 0;
665 for (auto &symbol : symbolsFile->GetSymbols()) {
666 auto symbolIndex = traceDataCache_->dataDict_.GetStringIndex(symbol.GetName());
667 streamFilters_->statFilter_->IncreaseStat(TRACE_PERF, STAT_EVENT_RECEIVED);
668 streamFilters_->perfDataFilter_->AppendPerfFiles(fileId, serial++, symbolIndex, filePathIndex);
669 }
670 if (symbolsFile->GetSymbols().size() == 0) {
671 streamFilters_->perfDataFilter_->AppendPerfFiles(fileId, INVALID_UINT32, INVALID_DATAINDEX, filePathIndex);
672 }
673 fileDataDictIdToFileId_.insert(std::make_pair(filePathIndex, fileId));
674 ++fileId;
675 }
676 }
UpdateClockType()677 void PerfDataParser::UpdateClockType()
678 {
679 const auto &attrIds_ = recordDataReader_->GetAttrSection();
680 if (attrIds_.size() > 0) {
681 useClockId_ = attrIds_[0].attr.use_clockid;
682 clockId_ = attrIds_[0].attr.clockid;
683 TS_LOGI("useClockId_ = %u, clockId_ = %u", useClockId_, clockId_);
684 }
685 }
RecordCallBack(std::unique_ptr<PerfEventRecord> record)686 bool PerfDataParser::RecordCallBack(std::unique_ptr<PerfEventRecord> record)
687 {
688 // tell process tree what happend for rebuild symbols
689 report_->virtualRuntime_.UpdateFromRecord(*record);
690
691 if (record->GetType() == PERF_RECORD_SAMPLE) {
692 std::unique_ptr<PerfRecordSample> sample(static_cast<PerfRecordSample *>(record.release()));
693 uint32_t callChainId = UpdateCallChainUnCompressed(sample);
694 UpdatePerfSampleData(callChainId, sample);
695 } else if (record->GetType() == PERF_RECORD_COMM) {
696 auto recordComm = static_cast<PerfRecordComm *>(record.get());
697 auto range = tidToPid_.equal_range(recordComm->data_.tid);
698 for (auto it = range.first; it != range.second; it++) {
699 if (it->second == recordComm->data_.pid) {
700 return true;
701 }
702 }
703 tidToPid_.insert(std::make_pair(recordComm->data_.tid, recordComm->data_.pid));
704 auto perfThreadData = traceDataCache_->GetPerfThreadData();
705 auto threadNameIndex = traceDataCache_->dataDict_.GetStringIndex(recordComm->data_.comm);
706 perfThreadData->AppendNewPerfThread(recordComm->data_.pid, recordComm->data_.tid, threadNameIndex);
707 }
708 return true;
709 }
710
UpdateCallChainUnCompressed(const std::unique_ptr<PerfRecordSample> & sample)711 uint32_t PerfDataParser::UpdateCallChainUnCompressed(const std::unique_ptr<PerfRecordSample> &sample)
712 {
713 std::string stackStr = "";
714 for (auto &callFrame : sample->callFrames_) {
715 stackStr += "+" + base::number(callFrame.pc, base::INTEGER_RADIX_TYPE_HEX);
716 }
717 auto stackHash = hashFun_(stackStr);
718 auto pid = sample->data_.pid;
719 auto callChainId = pidAndStackHashToCallChainId_.Find(pid, stackHash);
720 if (callChainId != INVALID_UINT32) {
721 return callChainId;
722 }
723 callChainId = ++callChainId_;
724 pidAndStackHashToCallChainId_.Insert(pid, stackHash, callChainId);
725 callChainIdToThreadInfo_.insert({callChainId, std::make_tuple(pid, sample->data_.tid)});
726 uint32_t depth = 0;
727 for (auto frame = sample->callFrames_.rbegin(); frame != sample->callFrames_.rend(); ++frame) {
728 uint64_t fileId = INVALID_UINT64;
729 auto fileDataIndex = traceDataCache_->dataDict_.GetStringIndex(frame->mapName);
730 if (fileDataDictIdToFileId_.count(fileDataIndex) != 0) {
731 fileId = fileDataDictIdToFileId_.at(fileDataIndex);
732 }
733 PerfCallChainRow perfCallChainRow = {callChainId, depth++, frame->pc, frame->funcOffset,
734 frame->mapOffset, fileId, frame->index};
735 traceDataCache_->GetPerfCallChainData()->AppendNewPerfCallChain(perfCallChainRow);
736 }
737 return callChainId;
738 }
739
UpdatePerfSampleData(uint32_t callChainId,std::unique_ptr<PerfRecordSample> & sample)740 void PerfDataParser::UpdatePerfSampleData(uint32_t callChainId, std::unique_ptr<PerfRecordSample> &sample)
741 {
742 auto perfSampleData = traceDataCache_->GetPerfSampleData();
743 uint64_t newTimeStamp = 0;
744 if (useClockId_ == 0) {
745 newTimeStamp = sample->data_.time;
746 } else {
747 newTimeStamp =
748 streamFilters_->clockFilter_->ToPrimaryTraceTime(perfToTSClockType_.at(clockId_), sample->data_.time);
749 }
750 UpdatePluginTimeRange(perfToTSClockType_.at(clockId_), sample->data_.time, newTimeStamp);
751
752 DataIndex threadStatIndex = unknownStateIndex_;
753 auto threadState = report_->GetConfigName(sample->data_.id);
754 if (threadState.compare(wakingEventName_) == 0) {
755 threadStatIndex = runingStateIndex_;
756 } else if (threadState.compare(cpuOffEventName_) == 0) {
757 threadStatIndex = suspendStatIndex_;
758 }
759 auto configIndex = report_->GetConfigIndex(sample->data_.id);
760 PerfSampleRow perfSampleRow = {callChainId, sample->data_.time, sample->data_.tid, sample->data_.period,
761 configIndex, newTimeStamp, sample->data_.cpu, threadStatIndex};
762 perfSampleData->AppendNewPerfSample(perfSampleRow);
763 }
764
Finish()765 void PerfDataParser::Finish()
766 {
767 if (!traceDataCache_->isSplitFile_) {
768 streamFilters_->perfDataFilter_->Finish();
769 }
770 // Update trace_range when there is only perf data in the trace file
771 if (traceDataCache_->traceStartTime_ == INVALID_UINT64 || traceDataCache_->traceEndTime_ == 0) {
772 traceDataCache_->MixTraceTime(GetPluginStartTime(), GetPluginEndTime());
773 } else {
774 TS_LOGI("perfData time is not updated, maybe this trace file has other data");
775 }
776 pidAndStackHashToCallChainId_.Clear();
777 }
778
SetHM()779 void PerfDataParser::SetHM()
780 {
781 std::string os = recordDataReader_->GetFeatureString(FEATURE::OSRELEASE);
782 auto isHM = os.find(HMKERNEL) != std::string::npos;
783 isHM = isHM || os.find("hmkernel") != std::string::npos;
784 isHM = isHM || os.find("HongMeng") != std::string::npos;
785 report_->virtualRuntime_.SetHM(isHM);
786 if (isHM) {
787 pid_t devhost = -1;
788 std::string str = recordDataReader_->GetFeatureString(FEATURE::HIPERF_HM_DEVHOST);
789 if (str != EMPTY_STRING) {
790 devhost = std::stoi(str);
791 }
792 report_->virtualRuntime_.SetDevhostPid(devhost);
793 }
794 }
795 } // namespace TraceStreamer
796 } // namespace SysTuning
797