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 "perf_data_parser.h"
16 #include "perf_data_filter.h"
17 #include "stat_filter.h"
18
19 namespace SysTuning {
20 namespace TraceStreamer {
PerfDataParser(TraceDataCache * dataCache,const TraceStreamerFilters * ctx)21 PerfDataParser::PerfDataParser(TraceDataCache* dataCache, const TraceStreamerFilters* ctx)
22 : HtracePluginTimeParser(dataCache, ctx), frameToCallChainId_(INVALID_UINT64)
23 {
24 configNameIndex_ = traceDataCache_->dataDict_.GetStringIndex("config_name");
25 workloaderIndex_ = traceDataCache_->dataDict_.GetStringIndex("workload_cmd");
26 cmdlineIndex_ = traceDataCache_->dataDict_.GetStringIndex("cmdline");
27 runingStateIndex_ = traceDataCache_->dataDict_.GetStringIndex("Running");
28 suspendStatIndex_ = traceDataCache_->dataDict_.GetStringIndex("Suspend");
29 unkonwnStateIndex_ = traceDataCache_->dataDict_.GetStringIndex("-");
30 }
InitPerfDataAndLoad(const std::deque<uint8_t> dequeBuffer,uint64_t size)31 void PerfDataParser::InitPerfDataAndLoad(const std::deque<uint8_t> dequeBuffer, uint64_t size)
32 {
33 bufferSize_ = size;
34 buffer_ = std::make_unique<uint8_t[]>(size);
35 std::copy(dequeBuffer.begin(), dequeBuffer.begin() + size, buffer_.get());
36 LoadPerfData();
37 }
~PerfDataParser()38 PerfDataParser::~PerfDataParser()
39 {
40 TS_LOGI("perf data ts MIN:%llu, MAX:%llu", static_cast<unsigned long long>(GetPluginStartTime()),
41 static_cast<unsigned long long>(GetPluginEndTime()));
42 }
43
LoadPerfData()44 bool PerfDataParser::LoadPerfData()
45 {
46 TS_LOGI("enter");
47 // try load the perf data
48 recordDataReader_ = PerfFileReader::Instance(buffer_.get(), bufferSize_);
49 buffer_.release();
50 if (recordDataReader_ == nullptr) {
51 return false;
52 }
53
54 if (!recordDataReader_->ReadFeatureSection()) {
55 printf("record format error.\n");
56 return false;
57 }
58 // update perf report table
59 UpdateEventConfigInfo();
60 UpdateReportWorkloadInfo();
61 UpdateCmdlineInfo();
62
63 // update perf Files table
64 UpdateSymbolAndFilesData();
65
66 TS_LOGD("process record");
67 UpdateClockType();
68 recordDataReader_->ReadDataSection(std::bind(&PerfDataParser::RecordCallBack, this, std::placeholders::_1));
69 TS_LOGD("process record completed");
70 TS_LOGI("load perf data done");
71 return true;
72 }
73
UpdateEventConfigInfo()74 void PerfDataParser::UpdateEventConfigInfo()
75 {
76 auto features = recordDataReader_->GetFeatures();
77 cpuOffMode_ = find(features.begin(), features.end(), FEATURE::HIPERF_CPU_OFF) != features.end();
78 if (cpuOffMode_) {
79 TS_LOGD("this is cpuOffMode ");
80 }
81 const PerfFileSection* featureSection = recordDataReader_->GetFeatureSection(FEATURE::EVENT_DESC);
82 if (featureSection) {
83 TS_LOGI("have EVENT_DESC");
84 LoadEventDesc();
85 } else {
86 TS_LOGE("Do not have EVENT_DESC !!!");
87 }
88 }
89
LoadEventDesc()90 void PerfDataParser::LoadEventDesc()
91 {
92 const auto featureSection = recordDataReader_->GetFeatureSection(FEATURE::EVENT_DESC);
93 const auto& sectionEventdesc = *static_cast<const PerfFileSectionEventDesc*>(featureSection);
94 TS_LOGI("Event descriptions: %zu", sectionEventdesc.eventDesces_.size());
95 for (size_t i = 0; i < sectionEventdesc.eventDesces_.size(); i++) {
96 const auto& fileAttr = sectionEventdesc.eventDesces_[i];
97 TS_LOGI("event name[%zu]: %s ids: %s", i, fileAttr.name.c_str(), VectorToString(fileAttr.ids).c_str());
98 for (uint64_t id : fileAttr.ids) {
99 report_.configIdIndexMaps_[id] = report_.configs_.size(); // setup index
100 TS_LOGI("add config id map %" PRIu64 " to %zu", id, report_.configs_.size());
101 }
102 // when cpuOffMode_ , don't use count mode , use time mode.
103 auto& config = report_.configs_.emplace_back(fileAttr.name, fileAttr.attr.type, fileAttr.attr.config,
104 cpuOffMode_ ? false : true);
105 config.ids_ = fileAttr.ids;
106 TS_ASSERT(config.ids_.size() > 0);
107
108 auto perfReportData = traceDataCache_->GetPerfReportData();
109 auto configValueIndex = traceDataCache_->dataDict_.GetStringIndex(fileAttr.name.c_str());
110 perfReportData->AppendNewPerfReport(configNameIndex_, configValueIndex);
111 }
112 }
113
UpdateReportWorkloadInfo() const114 void PerfDataParser::UpdateReportWorkloadInfo() const
115 {
116 // workload
117 auto featureSection = recordDataReader_->GetFeatureSection(FEATURE::HIPERF_WORKLOAD_CMD);
118 std::string workloader = "";
119 if (featureSection) {
120 TS_LOGI("found HIPERF_META_WORKLOAD_CMD");
121 auto sectionString = static_cast<const PerfFileSectionString*>(featureSection);
122 workloader = sectionString->toString();
123 } else {
124 TS_LOGW("NOT found HIPERF_META_WORKLOAD_CMD");
125 }
126 if (workloader.empty()) {
127 TS_LOGW("NOT found HIPERF_META_WORKLOAD_CMD");
128 return;
129 }
130 auto perfReportData = traceDataCache_->GetPerfReportData();
131 auto workloaderValueIndex = traceDataCache_->dataDict_.GetStringIndex(workloader.c_str());
132 perfReportData->AppendNewPerfReport(workloaderIndex_, workloaderValueIndex);
133 }
134
UpdateCmdlineInfo() const135 void PerfDataParser::UpdateCmdlineInfo() const
136 {
137 auto cmdline = recordDataReader_->GetFeatureString(FEATURE::CMDLINE);
138 auto perfReportData = traceDataCache_->GetPerfReportData();
139 auto cmdlineValueIndex = traceDataCache_->dataDict_.GetStringIndex(cmdline.c_str());
140 perfReportData->AppendNewPerfReport(cmdlineIndex_, cmdlineValueIndex);
141 }
142
UpdateSymbolAndFilesData()143 void PerfDataParser::UpdateSymbolAndFilesData()
144 {
145 // we need unwind it (for function name match) even not give us path
146 report_.virtualRuntime_.SetDisableUnwind(false);
147
148 // found symbols in file
149 const auto featureSection = recordDataReader_->GetFeatureSection(FEATURE::HIPERF_FILES_SYMBOL);
150 if (featureSection != nullptr) {
151 const PerfFileSectionSymbolsFiles* sectionSymbolsFiles =
152 static_cast<const PerfFileSectionSymbolsFiles*>(featureSection);
153 report_.virtualRuntime_.UpdateFromPerfData(sectionSymbolsFiles->symbolFileStructs_);
154 }
155 uint64_t fileId = 0;
156 for (auto& symbolsFile : report_.virtualRuntime_.GetSymbolsFiles()) {
157 auto filePathIndex = traceDataCache_->dataDict_.GetStringIndex(symbolsFile->filePath_.c_str());
158 uint32_t serial = 0;
159 for (auto& symbol : symbolsFile->GetSymbols()) {
160 auto symbolIndex = traceDataCache_->dataDict_.GetStringIndex(symbol.Name().data());
161 streamFilters_->statFilter_->IncreaseStat(TRACE_PERF, STAT_EVENT_RECEIVED);
162 streamFilters_->perfDataFilter_->AppendPerfFiles(fileId, serial++, symbolIndex, filePathIndex);
163 }
164 fileDataDictIdToFileId_.insert(std::make_pair(filePathIndex, fileId));
165 ++fileId;
166 }
167 }
UpdateClockType()168 void PerfDataParser::UpdateClockType()
169 {
170 const auto& attrIds_ = recordDataReader_->GetAttrSection();
171 if (attrIds_.size() > 0) {
172 useClockId_ = attrIds_[0].attr.use_clockid;
173 clockId_ = attrIds_[0].attr.clockid;
174 TS_LOGE("useClockId_ = %u, clockId_ = %u", useClockId_, clockId_);
175 }
176 }
RecordCallBack(std::unique_ptr<PerfEventRecord> record)177 bool PerfDataParser::RecordCallBack(std::unique_ptr<PerfEventRecord> record)
178 {
179 // tell process tree what happend for rebuild symbols
180 report_.virtualRuntime_.UpdateFromRecord(*record);
181
182 if (record->GetType() == PERF_RECORD_SAMPLE) {
183 std::unique_ptr<PerfRecordSample> sample(static_cast<PerfRecordSample*>(record.release()));
184 auto callChainId = UpdatePerfCallChainData(sample);
185 UpdatePerfSampleData(callChainId, sample);
186 } else if (record->GetType() == PERF_RECORD_COMM) {
187 auto recordComm = static_cast<PerfRecordComm*>(record.get());
188 auto range = tidToPid_.equal_range(recordComm->data_.tid);
189 for (auto it = range.first; it != range.second; it++) {
190 if (it->second == recordComm->data_.pid) {
191 return true;
192 }
193 }
194 tidToPid_.insert(std::make_pair(recordComm->data_.tid, recordComm->data_.pid));
195 auto perfThreadData = traceDataCache_->GetPerfThreadData();
196 auto threadNameIndex = traceDataCache_->dataDict_.GetStringIndex(recordComm->data_.comm);
197 perfThreadData->AppendNewPerfThread(recordComm->data_.pid, recordComm->data_.tid, threadNameIndex);
198 }
199 return true;
200 }
201
UpdatePerfCallChainData(std::unique_ptr<PerfRecordSample> & sample)202 uint64_t PerfDataParser::UpdatePerfCallChainData(std::unique_ptr<PerfRecordSample>& sample)
203 {
204 uint64_t depth = 0;
205 bool callStackNotExist = false;
206 uint64_t callChainId = INVALID_UINT64;
207 std::vector<std::unique_ptr<CallStackTemp>> callStackTemp = {};
208 // Filter callstack unuse data
209 for (auto frame = sample->callFrames_.rbegin(); frame != sample->callFrames_.rend(); ++frame) {
210 auto symbolId = frame->symbolIndex_;
211 if (symbolId == -1 && frame->vaddrInFile_ == 0) {
212 continue;
213 }
214 auto fileDataIndex = traceDataCache_->dataDict_.GetStringIndex(frame->filePath_);
215 auto itor = fileDataDictIdToFileId_.find(fileDataIndex);
216 if (itor == fileDataDictIdToFileId_.end()) {
217 continue;
218 }
219 auto fileId = itor->second;
220 callStackTemp.emplace_back(
221 std::move(std::make_unique<CallStackTemp>(depth, frame->vaddrInFile_, fileId, symbolId)));
222 depth++;
223 }
224 // Determine whether to write callstack data to cache
225 auto size = callStackTemp.size();
226 for (auto itor = callStackTemp.begin(); itor != callStackTemp.end(); itor++) {
227 auto callstack = itor->get();
228 auto ret = frameToCallChainId_.Find(callstack->fileId_, callstack->symbolId_, callstack->depth_, size);
229 if (ret != INVALID_UINT64) { // find it
230 if (callChainId == INVALID_UINT64) {
231 callChainId = ret;
232 } else if (callChainId != ret) {
233 callStackNotExist = true;
234 break;
235 }
236 } else { // not find it
237 callStackNotExist = true;
238 break;
239 }
240 }
241 // write callstack data to cache
242 if (callStackNotExist) {
243 callChainId = ++callChainId_;
244 for (auto itor = callStackTemp.begin(); itor != callStackTemp.end(); itor++) {
245 auto callstack = itor->get();
246 frameToCallChainId_.Insert(callstack->fileId_, callstack->symbolId_, callstack->depth_,
247 callStackTemp.size(), callChainId);
248 streamFilters_->perfDataFilter_->AppendPerfCallChain(
249 callChainId, callstack->depth_, callstack->vaddrInFile_, callstack->fileId_, callstack->symbolId_);
250 }
251 }
252 callStackTemp.clear();
253 return callChainId;
254 }
255
UpdatePerfSampleData(uint64_t callChainId,std::unique_ptr<PerfRecordSample> & sample)256 void PerfDataParser::UpdatePerfSampleData(uint64_t callChainId, std::unique_ptr<PerfRecordSample>& sample)
257 {
258 auto perfSampleData = traceDataCache_->GetPerfSampleData();
259 uint64_t newTimeStamp = 0;
260 if (useClockId_ == 0) {
261 newTimeStamp = sample->data_.time;
262 } else {
263 newTimeStamp =
264 streamFilters_->clockFilter_->ToPrimaryTraceTime(perfToTSClockType_.at(clockId_), sample->data_.time);
265 }
266 UpdatePluginTimeRange(perfToTSClockType_.at(clockId_), sample->data_.time, newTimeStamp);
267
268 DataIndex threadStatIndex = unkonwnStateIndex_;
269 auto threadState = report_.GetConfigName(sample->data_.id);
270 if (threadState.compare(wakingEventName_) == 0) {
271 threadStatIndex = runingStateIndex_;
272 } else if (threadState.compare(cpuOffEventName_) == 0) {
273 threadStatIndex = suspendStatIndex_;
274 }
275 auto configIndex = report_.GetConfigIndex(sample->data_.id);
276 perfSampleData->AppendNewPerfSample(callChainId, sample->data_.time, sample->data_.tid, sample->data_.period,
277 configIndex, newTimeStamp, sample->data_.cpu, threadStatIndex);
278 }
279
Finish()280 void PerfDataParser::Finish()
281 {
282 streamFilters_->perfDataFilter_->Finish();
283 // Update trace_range when there is only perf data in the trace file
284 if (traceDataCache_->traceStartTime_ == INVALID_UINT64 || traceDataCache_->traceEndTime_ == 0) {
285 traceDataCache_->MixTraceTime(GetPluginStartTime(), GetPluginEndTime());
286 }
287 frameToCallChainId_.Clear();
288 }
289 } // namespace TraceStreamer
290 } // namespace SysTuning
291