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 "htrace_native_hook_parser.h"
16 #include "clock_filter.h"
17 #include "process_filter.h"
18 #include "stat_filter.h"
19 namespace SysTuning {
20 namespace TraceStreamer {
HtraceNativeHookParser(TraceDataCache * dataCache,const TraceStreamerFilters * ctx)21 HtraceNativeHookParser::HtraceNativeHookParser(TraceDataCache* dataCache, const TraceStreamerFilters* ctx)
22 : HtracePluginTimeParser(dataCache, ctx),
23 addrToAllocEventRow_(INVALID_UINT64),
24 addrToMmapEventRow_(INVALID_UINT64),
25 frameToFrameId_(INVALID_UINT64)
26 {
27 invalidLibPathIndexs_.insert(traceDataCache_->dataDict_.GetStringIndex("/system/lib/libc++.so"));
28 invalidLibPathIndexs_.insert(traceDataCache_->dataDict_.GetStringIndex("/system/lib64/libc++.so"));
29 invalidLibPathIndexs_.insert(traceDataCache_->dataDict_.GetStringIndex("/system/lib/ld-musl-aarch64.so.1"));
30 invalidLibPathIndexs_.insert(traceDataCache_->dataDict_.GetStringIndex("/system/lib/ld-musl-arm.so.1"));
31 }
32
~HtraceNativeHookParser()33 HtraceNativeHookParser::~HtraceNativeHookParser()
34 {
35 TS_LOGI("native hook data ts MIN:%llu, MAX:%llu", static_cast<unsigned long long>(GetPluginStartTime()),
36 static_cast<unsigned long long>(GetPluginEndTime()));
37 TS_LOGI("native real ts MIN:%llu, MAX:%llu", static_cast<unsigned long long>(MinTs()),
38 static_cast<unsigned long long>(MaxTs()));
39 }
40 // In order to improve the accuracy of data, it is necessary to sort the original data.
41 // Data sorting will be reduced by 5% to 10% Speed of parsing data.
SortNativeHookData(BatchNativeHookData & tracePacket)42 void HtraceNativeHookParser::SortNativeHookData(BatchNativeHookData& tracePacket)
43 {
44 for (auto i = 0; i < tracePacket.events_size(); i++) {
45 auto nativeHookData = std::make_unique<NativeHookData>(*tracePacket.mutable_events(i));
46 auto timeStamp = nativeHookData->tv_nsec() + nativeHookData->tv_sec() * SEC_TO_NS;
47 tsNativeHookQueue_.insert(std::make_pair(timeStamp, std::move(nativeHookData)));
48 MaybeParseNativeHookData();
49 }
50 return;
51 }
52 template <class T1, class T2>
UpdateMap(std::unordered_map<T1,T2> & sourceMap,T1 key,T2 value)53 void HtraceNativeHookParser::UpdateMap(std::unordered_map<T1, T2>& sourceMap, T1 key, T2 value)
54 {
55 auto itor = sourceMap.find(key);
56 if (itor != sourceMap.end()) {
57 itor->second = value;
58 } else {
59 sourceMap.insert(std::make_pair(key, value));
60 }
61 }
MaybeParseNativeHookData()62 void HtraceNativeHookParser::MaybeParseNativeHookData()
63 {
64 if (tsNativeHookQueue_.size() > MAX_CACHE_SIZE) {
65 ParseNativeHookData(tsNativeHookQueue_.begin()->first, tsNativeHookQueue_.begin()->second.get());
66 tsNativeHookQueue_.erase(tsNativeHookQueue_.begin());
67 }
68 }
FinishParseNativeHookData()69 void HtraceNativeHookParser::FinishParseNativeHookData()
70 {
71 for (auto it = tsNativeHookQueue_.begin(); it != tsNativeHookQueue_.end(); it++) {
72 ParseNativeHookData(it->first, it->second.get());
73 }
74 if (traceDataCache_->GetNativeHookData()->Size() == 0) {
75 return;
76 }
77 traceDataCache_->GetNativeHookData()->UpdateMemMapSubType();
78 traceDataCache_->GetNativeHookFrameData()->UpdateSymbolId();
79 traceDataCache_->GetNativeHookFrameData()->UpdateFileId(filePathIdToFilePathName_);
80 std::map<uint64_t, uint64_t> callIdToLastCallerPathIndex;
81 traceDataCache_->GetNativeHookFrameData()->GetCallIdToLastLibId(invalidLibPathIndexs_,
82 callIdToLastCallerPathIndex);
83 if (callIdToLastCallerPathIndex.size()) {
84 traceDataCache_->GetNativeHookData()->UpdateLastCallerPathIndexs(callIdToLastCallerPathIndex);
85 }
86
87 UpdateThreadNameWithNativeHookData();
88 tsNativeHookQueue_.clear();
89 threadNameIdToThreadName_.clear();
90 itidToThreadNameId_.clear();
91 }
ParseAllocEvent(uint64_t newTimeStamp,const NativeHookData * nativeHookData)92 void HtraceNativeHookParser::ParseAllocEvent(uint64_t newTimeStamp, const NativeHookData* nativeHookData)
93 {
94 auto allocEvent = nativeHookData->alloc_event();
95 auto itid = streamFilters_->processFilter_->GetOrCreateThreadWithPid(allocEvent.tid(), allocEvent.pid());
96 auto ipid = streamFilters_->processFilter_->GetInternalPid(allocEvent.pid());
97 if (allocEvent.thread_name_id() != 0) {
98 UpdateMap(itidToThreadNameId_, itid, allocEvent.thread_name_id());
99 }
100 auto callChainId = ParseNativeHookFrame(allocEvent.frame_info());
101 auto row = traceDataCache_->GetNativeHookData()->AppendNewNativeHookData(
102 callChainId, ipid, itid, allocEvent.GetTypeName(), INVALID_UINT64, newTimeStamp, 0, 0, allocEvent.addr(),
103 allocEvent.size(), allocEvent.size());
104 addrToAllocEventRow_.Insert(ipid, allocEvent.addr(), static_cast<uint64_t>(row));
105 MaybeUpdateCurrentSizeDur(row, newTimeStamp, true);
106 }
ParseFreeEvent(uint64_t newTimeStamp,const NativeHookData * nativeHookData)107 void HtraceNativeHookParser::ParseFreeEvent(uint64_t newTimeStamp, const NativeHookData* nativeHookData)
108 {
109 auto freeEvent = nativeHookData->free_event();
110 auto itid = streamFilters_->processFilter_->GetOrCreateThreadWithPid(freeEvent.tid(), freeEvent.pid());
111 auto ipid = streamFilters_->processFilter_->GetInternalPid(freeEvent.pid());
112 if (freeEvent.thread_name_id() != 0) {
113 UpdateMap(itidToThreadNameId_, itid, freeEvent.thread_name_id());
114 }
115 int64_t freeHeapSize = 0;
116 auto row = addrToAllocEventRow_.Find(ipid, freeEvent.addr());
117 if (row != INVALID_UINT64 && newTimeStamp > traceDataCache_->GetNativeHookData()->TimeStamData()[row]) {
118 addrToAllocEventRow_.Erase(ipid, freeEvent.addr());
119 traceDataCache_->GetNativeHookData()->UpdateHeapDuration(row, newTimeStamp);
120 freeHeapSize = traceDataCache_->GetNativeHookData()->MemSizes()[row];
121 } else if (row == INVALID_UINT64) {
122 TS_LOGW("func addr:%lu is empty", freeEvent.addr());
123 streamFilters_->statFilter_->IncreaseStat(TRACE_NATIVE_HOOK_FREE, STAT_EVENT_DATA_INVALID);
124 return;
125 }
126 auto callChainId = ParseNativeHookFrame(freeEvent.frame_info());
127 row = traceDataCache_->GetNativeHookData()->AppendNewNativeHookData(
128 callChainId, ipid, itid, freeEvent.GetTypeName(), INVALID_UINT64, newTimeStamp, 0, 0, freeEvent.addr(),
129 freeHeapSize, (-1) * freeHeapSize);
130 if (freeHeapSize != 0) {
131 MaybeUpdateCurrentSizeDur(row, newTimeStamp, true);
132 }
133 }
ParseMmapEvent(uint64_t newTimeStamp,const NativeHookData * nativeHookData)134 void HtraceNativeHookParser::ParseMmapEvent(uint64_t newTimeStamp, const NativeHookData* nativeHookData)
135 {
136 auto mMapEvent = nativeHookData->mmap_event();
137 auto itid = streamFilters_->processFilter_->GetOrCreateThreadWithPid(mMapEvent.tid(), mMapEvent.pid());
138 auto ipid = streamFilters_->processFilter_->GetInternalPid(mMapEvent.pid());
139 if (mMapEvent.thread_name_id() != 0) {
140 UpdateMap(itidToThreadNameId_, itid, mMapEvent.thread_name_id());
141 }
142 DataIndex subType = INVALID_UINT64;
143 if (!mMapEvent.type().empty()) {
144 subType = traceDataCache_->dataDict_.GetStringIndex(mMapEvent.type());
145 traceDataCache_->GetNativeHookData()->UpdateAddrToMemMapSubType(mMapEvent.addr(), mMapEvent.size(), subType);
146 }
147 auto callChainId = ParseNativeHookFrame(mMapEvent.frame_info());
148 auto row = traceDataCache_->GetNativeHookData()->AppendNewNativeHookData(
149 callChainId, ipid, itid, mMapEvent.GetTypeName(), subType, newTimeStamp, 0, 0, mMapEvent.addr(),
150 mMapEvent.size(), mMapEvent.size());
151 addrToMmapEventRow_.Insert(ipid, mMapEvent.addr(), static_cast<uint64_t>(row));
152 MaybeUpdateCurrentSizeDur(row, newTimeStamp, false);
153 }
ParseMunmapEvent(uint64_t newTimeStamp,const NativeHookData * nativeHookData)154 void HtraceNativeHookParser::ParseMunmapEvent(uint64_t newTimeStamp, const NativeHookData* nativeHookData)
155 {
156 auto mUnMapEvent = nativeHookData->munmap_event();
157 auto itid = streamFilters_->processFilter_->GetOrCreateThreadWithPid(mUnMapEvent.tid(), mUnMapEvent.pid());
158 auto ipid = streamFilters_->processFilter_->GetInternalPid(mUnMapEvent.pid());
159 if (mUnMapEvent.thread_name_id() != 0) {
160 UpdateMap(itidToThreadNameId_, itid, mUnMapEvent.thread_name_id());
161 }
162 auto row = addrToMmapEventRow_.Find(ipid, mUnMapEvent.addr());
163 int64_t effectiveMUnMapSize = 0;
164 if (row != INVALID_UINT64 && newTimeStamp > traceDataCache_->GetNativeHookData()->TimeStamData()[row]) {
165 addrToMmapEventRow_.Erase(ipid, mUnMapEvent.addr());
166 traceDataCache_->GetNativeHookData()->UpdateHeapDuration(row, newTimeStamp);
167 effectiveMUnMapSize = static_cast<int64_t>(mUnMapEvent.size());
168 } else if (row == INVALID_UINT64) {
169 TS_LOGW("func addr:%lu is empty", mUnMapEvent.addr());
170 streamFilters_->statFilter_->IncreaseStat(TRACE_NATIVE_HOOK_MUNMAP, STAT_EVENT_DATA_INVALID);
171 return;
172 }
173 auto callChainId = ParseNativeHookFrame(mUnMapEvent.frame_info());
174 row = traceDataCache_->GetNativeHookData()->AppendNewNativeHookData(
175 callChainId, ipid, itid, mUnMapEvent.GetTypeName(), INVALID_UINT64, newTimeStamp, 0, 0, mUnMapEvent.addr(),
176 mUnMapEvent.size(), (-1) * effectiveMUnMapSize);
177 if (effectiveMUnMapSize != 0) {
178 MaybeUpdateCurrentSizeDur(row, newTimeStamp, false);
179 }
180 }
ParseTagEvent(const NativeHookData * nativeHookData)181 void HtraceNativeHookParser::ParseTagEvent(const NativeHookData* nativeHookData)
182 {
183 auto memMapTagEvent = nativeHookData->tag_event();
184 auto addr = memMapTagEvent.addr();
185 auto size = memMapTagEvent.size();
186 auto tagIndex = traceDataCache_->dataDict_.GetStringIndex(memMapTagEvent.tag());
187 traceDataCache_->GetNativeHookData()->UpdateAddrToMemMapSubType(addr, static_cast<int64_t>(size), tagIndex);
188 }
ParseFileEvent(const NativeHookData * nativeHookData)189 void HtraceNativeHookParser::ParseFileEvent(const NativeHookData* nativeHookData)
190 {
191 auto filePathMapMessage = nativeHookData->file_path();
192 auto id = filePathMapMessage.id();
193 auto nameIndex = traceDataCache_->dataDict_.GetStringIndex(filePathMapMessage.name());
194 filePathIdToFilePathName_.insert(std::make_pair(id, nameIndex));
195 }
ParseSymbolEvent(const NativeHookData * nativeHookData)196 void HtraceNativeHookParser::ParseSymbolEvent(const NativeHookData* nativeHookData)
197 {
198 auto symbolMapMessage = nativeHookData->symbol_name();
199 auto id = symbolMapMessage.id();
200 auto nameIndex = traceDataCache_->dataDict_.GetStringIndex(symbolMapMessage.name());
201 traceDataCache_->GetNativeHookFrameData()->UpdateSymbolIdToNameMap(id, nameIndex);
202 }
ParseThreadEvent(const NativeHookData * nativeHookData)203 void HtraceNativeHookParser::ParseThreadEvent(const NativeHookData* nativeHookData)
204 {
205 auto threadNameMapMessage = nativeHookData->thread_name_map();
206 auto id = threadNameMapMessage.id();
207 auto nameIndex = traceDataCache_->dataDict_.GetStringIndex(threadNameMapMessage.name());
208 UpdateMap(threadNameIdToThreadName_, id, nameIndex);
209 }
210
ParseNativeHookData(const uint64_t timeStamp,const NativeHookData * nativeHookData)211 void HtraceNativeHookParser::ParseNativeHookData(const uint64_t timeStamp, const NativeHookData* nativeHookData)
212 {
213 auto eventCase = nativeHookData->event_case();
214 uint64_t newTimeStamp = 0;
215 if (eventCase >= NativeHookData::kAllocEvent && eventCase <= NativeHookData::kMunmapEvent) {
216 newTimeStamp = streamFilters_->clockFilter_->ToPrimaryTraceTime(TS_CLOCK_REALTIME, timeStamp);
217 UpdatePluginTimeRange(TS_CLOCK_REALTIME, timeStamp, newTimeStamp);
218 switch (eventCase) {
219 case NativeHookData::kAllocEvent:
220 streamFilters_->statFilter_->IncreaseStat(TRACE_NATIVE_HOOK_MALLOC, STAT_EVENT_RECEIVED);
221 if (newTimeStamp == timeStamp) {
222 streamFilters_->statFilter_->IncreaseStat(TRACE_NATIVE_HOOK_MALLOC, STAT_EVENT_DATA_INVALID);
223 }
224 ParseAllocEvent(newTimeStamp, nativeHookData);
225 break;
226 case NativeHookData::kFreeEvent:
227 streamFilters_->statFilter_->IncreaseStat(TRACE_NATIVE_HOOK_FREE, STAT_EVENT_RECEIVED);
228 if (newTimeStamp == timeStamp) {
229 streamFilters_->statFilter_->IncreaseStat(TRACE_NATIVE_HOOK_FREE, STAT_EVENT_DATA_INVALID);
230 }
231 ParseFreeEvent(newTimeStamp, nativeHookData);
232 break;
233 case NativeHookData::kMmapEvent:
234 streamFilters_->statFilter_->IncreaseStat(TRACE_NATIVE_HOOK_MMAP, STAT_EVENT_RECEIVED);
235 if (newTimeStamp == timeStamp) {
236 streamFilters_->statFilter_->IncreaseStat(TRACE_NATIVE_HOOK_MMAP, STAT_EVENT_DATA_INVALID);
237 }
238 ParseMmapEvent(newTimeStamp, nativeHookData);
239 break;
240 case NativeHookData::kMunmapEvent:
241 streamFilters_->statFilter_->IncreaseStat(TRACE_NATIVE_HOOK_MUNMAP, STAT_EVENT_RECEIVED);
242 if (newTimeStamp == timeStamp) {
243 streamFilters_->statFilter_->IncreaseStat(TRACE_NATIVE_HOOK_MUNMAP, STAT_EVENT_DATA_INVALID);
244 }
245 ParseMunmapEvent(newTimeStamp, nativeHookData);
246 break;
247 default:
248 TS_LOGE("An unknown type of data was received!");
249 break;
250 }
251 } else {
252 switch (eventCase) {
253 case NativeHookData::kTagEvent:
254 ParseTagEvent(nativeHookData);
255 break;
256 case NativeHookData::kFilePath:
257 ParseFileEvent(nativeHookData);
258 break;
259 case NativeHookData::kSymbolName:
260 ParseSymbolEvent(nativeHookData);
261 break;
262 case NativeHookData::kThreadNameMap:
263 ParseThreadEvent(nativeHookData);
264 break;
265 default:
266 TS_LOGE("An unknown type of data was received!");
267 break;
268 }
269 }
270 }
MaybeUpdateCurrentSizeDur(uint64_t row,uint64_t timeStamp,bool isMalloc)271 void HtraceNativeHookParser::MaybeUpdateCurrentSizeDur(uint64_t row, uint64_t timeStamp, bool isMalloc)
272 {
273 auto& lastAnyEventRaw = isMalloc ? lastMallocEventRaw_ : lastMmapEventRaw_;
274 if (lastAnyEventRaw != INVALID_UINT64) {
275 traceDataCache_->GetNativeHookData()->UpdateCurrentSizeDur(lastAnyEventRaw, timeStamp);
276 }
277 lastAnyEventRaw = row;
278 }
ParseNativeHookFrame(const RepeatedPtrField<::Frame> & repeatedFrame)279 uint64_t HtraceNativeHookParser::ParseNativeHookFrame(const RepeatedPtrField<::Frame>& repeatedFrame)
280 {
281 // the callstack from nativehook of sourcedata is reverse order
282 // we need to show the last frame firstly
283 std::vector<std::unique_ptr<NativeHookFrameTemp>> callStackTemp = {};
284 auto depth = 0;
285 for (auto i = repeatedFrame.size() - 1; i >= 0; i--) {
286 auto frame = repeatedFrame.Get(i);
287 DataIndex symbolNameIndex = INVALID_UINT64;
288 DataIndex filePathIndex = INVALID_UINT64;
289 if (!frame.symbol_name().empty()) {
290 symbolNameIndex = traceDataCache_->dataDict_.GetStringIndex(frame.symbol_name().c_str());
291 traceDataCache_->GetNativeHookFrameData()->UpdateSymbolIdToNameMap(symbolNameIndex, symbolNameIndex);
292 } else if (frame.symbol_name_id()) {
293 symbolNameIndex = frame.symbol_name_id();
294 }
295 if (!frame.file_path().empty()) {
296 filePathIndex = traceDataCache_->dataDict_.GetStringIndex(frame.file_path().c_str());
297 filePathIdToFilePathName_.insert(std::make_pair(filePathIndex, filePathIndex));
298 } else if (frame.file_path_id()) {
299 filePathIndex = frame.file_path_id();
300 }
301 callStackTemp.emplace_back(std::move(std::make_unique<NativeHookFrameTemp>(
302 filePathIndex, symbolNameIndex, depth, frame.offset(), frame.symbol_offset())));
303 depth++;
304 }
305
306 // Determine whether to write callstack data to cache
307 auto callChainId = INVALID_UINT64;
308 bool callStackNotExist = false;
309 auto size = callStackTemp.size();
310 for (auto itor = callStackTemp.begin(); itor != callStackTemp.end(); itor++) {
311 auto callstack = itor->get();
312 auto ret = frameToFrameId_.Find(callstack->fileId_, callstack->symbolId_, callstack->depth_, size);
313 if (ret != INVALID_UINT64) { // find it
314 if (callChainId == INVALID_UINT64) {
315 callChainId = ret;
316 } else if (callChainId != ret) {
317 callStackNotExist = true;
318 break;
319 }
320 } else { // not find it
321 callStackNotExist = true;
322 break;
323 }
324 }
325 // write callstack data to cache
326 if (callStackNotExist) {
327 callChainId = ++callChainId_;
328 for (auto itor = callStackTemp.begin(); itor != callStackTemp.end(); itor++) {
329 auto callstack = itor->get();
330 frameToFrameId_.Insert(callstack->fileId_, callstack->symbolId_, callstack->depth_, callStackTemp.size(),
331 callChainId);
332 traceDataCache_->GetNativeHookFrameData()->AppendNewNativeHookFrame(
333 callChainId, callstack->depth_, callstack->ip_, callstack->sp_,
334 callstack->symbolId_, callstack->fileId_, callstack->offset_, callstack->symbolOffset_);
335 }
336 }
337 callStackTemp.clear();
338 return callChainId;
339 }
UpdateThreadNameWithNativeHookData() const340 void HtraceNativeHookParser::UpdateThreadNameWithNativeHookData() const
341 {
342 if (itidToThreadNameId_.empty() || threadNameIdToThreadName_.empty()) {
343 return;
344 }
345 for (auto itor = itidToThreadNameId_.begin(); itor != itidToThreadNameId_.end(); ++itor) {
346 auto thread = traceDataCache_->GetThreadData(itor->first);
347 if (thread->nameIndex_ == 0) {
348 auto threadNameMapItor = threadNameIdToThreadName_.find(itor->second);
349 if (threadNameMapItor != threadNameIdToThreadName_.end()) {
350 thread->nameIndex_ = threadNameMapItor->second;
351 }
352 }
353 }
354 }
Finish()355 void HtraceNativeHookParser::Finish()
356 {
357 traceDataCache_->MixTraceTime(GetPluginStartTime(), GetPluginEndTime());
358 }
359 } // namespace TraceStreamer
360 } // namespace SysTuning
361