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 "stack_preprocess.h"
16
17 #include <elf.h>
18 #include <unistd.h>
19
20 #include "common.h"
21 #include "logging.h"
22 #include "plugin_service_types.pb.h"
23 #include "elf_parser.h"
24 #include "utilities.h"
25 #include "native_hook_result_standard.pb.h"
26 #include "native_hook_config_standard.pb.h"
27 #include "google/protobuf/text_format.h"
28 static std::atomic<uint64_t> timeCost = 0;
29 static std::atomic<uint64_t> unwindTimes = 0;
30
31 constexpr static uint32_t MAX_BUFFER_SIZE = 10 * 1024 * 1024;
32 constexpr static uint32_t MAX_MATCH_CNT = 1000;
33 constexpr static uint32_t MAX_MATCH_INTERVAL = 2000;
34 constexpr static uint32_t LOG_PRINT_TIMES = 10000;
35 constexpr static uint32_t FUNCTION_MAP_LOG_PRINT = 100;
36 constexpr static uint32_t FILE_MAP_LOG_PRINT = 10;
37 constexpr static uint32_t MAX_BATCH_CNT = 5;
38 constexpr static uint32_t LONG_TIME_THRESHOLD = 1000000;
39
40 using namespace OHOS::Developtools::NativeDaemon;
41
StackPreprocess(const StackDataRepeaterPtr & dataRepeater,const NativeHookConfig & hookConfig,clockid_t pluginDataClockId,bool isHookStandalone)42 StackPreprocess::StackPreprocess(const StackDataRepeaterPtr& dataRepeater,
43 const NativeHookConfig& hookConfig,
44 clockid_t pluginDataClockId,
45 bool isHookStandalone) : dataRepeater_(dataRepeater), buffer_(new (std::nothrow) uint8_t[MAX_BUFFER_SIZE]),
46 hookConfig_(hookConfig), fpHookData_(nullptr, nullptr),
47 pluginDataClockId_(pluginDataClockId), isHookStandaloneSerialize_(isHookStandalone)
48 {
49 runtime_instance = std::make_shared<VirtualRuntime>(hookConfig_);
50
51 if (hookConfig_.malloc_free_matching_interval() > MAX_MATCH_INTERVAL) {
52 HILOG_INFO(LOG_CORE, "Not support set %d", hookConfig_.malloc_free_matching_interval());
53 hookConfig_.set_malloc_free_matching_interval(MAX_MATCH_INTERVAL);
54 }
55
56 if (hookConfig_.malloc_free_matching_cnt() > MAX_MATCH_CNT) {
57 HILOG_INFO(LOG_CORE, "Not support set %d", hookConfig_.malloc_free_matching_cnt());
58 hookConfig_.set_malloc_free_matching_cnt(MAX_MATCH_CNT);
59 }
60 HILOG_INFO(LOG_CORE, "malloc_free_matching_interval = %d malloc_free_matching_cnt = %d\n",
61 hookConfig_.malloc_free_matching_interval(), hookConfig_.malloc_free_matching_cnt());
62
63 if (hookConfig_.statistics_interval() > 0) {
64 statisticsInterval_ = std::chrono::seconds(hookConfig_.statistics_interval());
65 }
66 HILOG_INFO(LOG_CORE, "statistics_interval = %d statisticsInterval_ = %lld \n",
67 hookConfig_.statistics_interval(), statisticsInterval_.count());
68 hookDataClockId_ = COMMON::GetClockId(hookConfig_.clock());
69 HILOG_INFO(LOG_CORE, "StackPreprocess(): pluginDataClockId = %d hookDataClockId = %d \n",
70 pluginDataClockId_, hookDataClockId_);
71 // create file
72 if (hookConfig_.save_file()) {
73 if (hookConfig_.file_name() != "") {
74 HILOG_DEBUG(LOG_CORE, "save file name = %s", hookConfig_.file_name().c_str());
75 FILE *fp = fopen(hookConfig_.file_name().c_str(), "wb+");
76 if (fp) {
77 fpHookData_.reset();
78 fpHookData_ = std::unique_ptr<FILE, decltype(&fclose)>(fp, fclose);
79 if (isHookStandaloneSerialize_) {
80 std::string str;
81 size_t length = hookConfig_.ByteSizeLong();
82 hookConfig_.SerializeToArray(buffer_.get(), length);
83 ForStandard::NativeHookConfig StandardNativeHookConfig;
84 StandardNativeHookConfig.ParseFromArray(buffer_.get(), length);
85 google::protobuf::TextFormat::PrintToString(StandardNativeHookConfig, &str);
86 size_t n = fwrite(str.data(), 1, str.size(), fp);
87 HILOG_DEBUG(LOG_CORE, "Flush hookConfig_ fwrite n = %zu strbuf.size() = %zu", n, str.size());
88 }
89 } else {
90 fpHookData_.reset();
91 }
92 } else {
93 HILOG_WARN(LOG_CORE, "If you need to save the file, please set the file_name");
94 }
95 }
96 HILOG_INFO(LOG_CORE, "isHookStandaloneSerialize_ = %d\n", isHookStandaloneSerialize_);
97 #if defined(__arm__)
98 u64regs_.resize(PERF_REG_ARM_MAX);
99 #else
100 u64regs_.resize(PERF_REG_ARM64_MAX);
101 #endif
102 callFrames_.reserve(hookConfig_.max_stack_depth());
103 }
104
StackPreprocess(bool fpUnwind)105 StackPreprocess::StackPreprocess(bool fpUnwind): fpHookData_(nullptr, nullptr) {}
106
~StackPreprocess()107 StackPreprocess::~StackPreprocess()
108 {
109 isStopTakeData_ = true;
110 if (dataRepeater_) {
111 dataRepeater_->Close();
112 }
113 if (thread_.joinable()) {
114 thread_.join();
115 }
116 runtime_instance = nullptr;
117 fpHookData_ = nullptr;
118 }
119
SetWriter(const std::shared_ptr<BufferWriter> & writer)120 void StackPreprocess::SetWriter(const std::shared_ptr<BufferWriter>& writer)
121 {
122 writer_ = writer;
123 }
124
StartTakeResults()125 bool StackPreprocess::StartTakeResults()
126 {
127 CHECK_NOTNULL(dataRepeater_, false, "data repeater null");
128
129 std::thread demuxer(&StackPreprocess::TakeResults, this);
130 CHECK_TRUE(demuxer.get_id() != std::thread::id(), false, "demuxer thread invalid");
131
132 thread_ = std::move(demuxer);
133 isStopTakeData_ = false;
134 return true;
135 }
136
StopTakeResults()137 bool StackPreprocess::StopTakeResults()
138 {
139 HILOG_INFO(LOG_CORE, "start StopTakeResults");
140 CHECK_NOTNULL(dataRepeater_, false, "data repeater null");
141 CHECK_TRUE(thread_.get_id() != std::thread::id(), false, "thread invalid");
142
143 isStopTakeData_ = true;
144 dataRepeater_->PutRawStack(nullptr, false);
145 HILOG_INFO(LOG_CORE, "StopTakeResults Wait thread join");
146
147 if (thread_.joinable()) {
148 thread_.join();
149 }
150 HILOG_INFO(LOG_CORE, "StopTakeResults Wait thread join success");
151 return true;
152 }
153
TakeResults()154 void StackPreprocess::TakeResults()
155 {
156 if (!dataRepeater_) {
157 return;
158 }
159
160 size_t minStackDepth = hookConfig_.max_stack_depth() > MIN_STACK_DEPTH
161 ? MIN_STACK_DEPTH : hookConfig_.max_stack_depth();
162 if (hookConfig_.blocked()) {
163 minStackDepth = static_cast<size_t>(hookConfig_.max_stack_depth());
164 }
165 minStackDepth += FILTER_STACK_DEPTH;
166 HILOG_INFO(LOG_CORE, "TakeResults thread %d, start!", gettid());
167 while (1) {
168 BatchNativeHookData stackData;
169 RawStackPtr batchRawStack[MAX_BATCH_CNT] = {nullptr};
170 auto result = dataRepeater_->TakeRawData(hookConfig_.malloc_free_matching_interval(), hookDataClockId_,
171 MAX_BATCH_CNT, batchRawStack);
172 if (!result || isStopTakeData_) {
173 break;
174 }
175 for (unsigned int i = 0; i < MAX_BATCH_CNT; i++) {
176 auto rawData = batchRawStack[i];
177 if (!rawData || isStopTakeData_) {
178 break;
179 }
180 if (rawData->stackConext->type == MMAP_FILE_TYPE) {
181 BaseStackRawData* mmapRawData = rawData->stackConext;
182 std::string filePath(reinterpret_cast<char *>(rawData->data));
183 COMMON::AdaptSandboxPath(filePath, rawData->stackConext->pid);
184 HILOG_DEBUG(LOG_CORE, "MMAP_FILE_TYPE curMmapAddr=%p, MAP_FIXED=%d, "
185 "PROT_EXEC=%d, offset=%" PRIu64 ", filePath=%s",
186 mmapRawData->addr, mmapRawData->mmapArgs.flags & MAP_FIXED,
187 mmapRawData->mmapArgs.flags & PROT_EXEC, mmapRawData->mmapArgs.offset, filePath.data());
188 runtime_instance->HandleMapInfo(reinterpret_cast<uint64_t>(mmapRawData->addr), mmapRawData->mallocSize,
189 mmapRawData->mmapArgs.flags, mmapRawData->mmapArgs.offset, filePath);
190 flushBasicData_ = true;
191 continue;
192 } else if (rawData->stackConext->type == MUNMAP_MSG) {
193 runtime_instance->RemoveMaps(reinterpret_cast<uint64_t>(rawData->stackConext->addr));
194 }
195 if (rawData->stackConext->type == THREAD_NAME_MSG) {
196 std::string threadName = reinterpret_cast<char*>(rawData->data);
197 ReportThreadNameMap(rawData->stackConext->tid, threadName, stackData);
198 continue;
199 }
200
201 if (!rawData->reportFlag) {
202 ignoreCnts_++;
203 if (ignoreCnts_ % LOG_PRINT_TIMES == 0) {
204 HILOG_INFO(LOG_CORE, "ignoreCnts_ = %d quene size = %zu\n", ignoreCnts_, dataRepeater_->Size());
205 }
206 continue;
207 }
208 eventCnts_++;
209 if (eventCnts_ % LOG_PRINT_TIMES == 0) {
210 HILOG_INFO(LOG_CORE, "eventCnts_ = %d quene size = %zu\n", eventCnts_, dataRepeater_->Size());
211 }
212 callFrames_.clear();
213 if (hookConfig_.fp_unwind()) {
214 uint64_t* fpIp = reinterpret_cast<uint64_t *>(rawData->data);
215 for (uint8_t idx = 0; idx < rawData->fpDepth ; ++idx) {
216 if (fpIp[idx] == 0) {
217 break;
218 }
219 callFrames_.emplace_back(fpIp[idx]);
220 }
221 } else {
222 #if defined(__arm__)
223 uint32_t *regAddrArm = reinterpret_cast<uint32_t *>(rawData->data);
224 u64regs_.assign(regAddrArm, regAddrArm + PERF_REG_ARM_MAX);
225 #else
226 if (memcpy_s(u64regs_.data(), sizeof(uint64_t) * PERF_REG_ARM64_MAX, rawData->data,
227 sizeof(uint64_t) * PERF_REG_ARM64_MAX) != EOK) {
228 HILOG_ERROR(LOG_CORE, "memcpy_s regs failed");
229 }
230 #endif
231 }
232 #ifdef PERFORMANCE_DEBUG
233 struct timespec start = {};
234 clock_gettime(CLOCK_REALTIME, &start);
235 size_t realFrameDepth = callFrames_.size();
236 #endif
237 size_t stackDepth = ((size_t)hookConfig_.max_stack_depth() > MAX_CALL_FRAME_UNWIND_SIZE)
238 ? MAX_CALL_FRAME_UNWIND_SIZE
239 : hookConfig_.max_stack_depth() + FILTER_STACK_DEPTH;
240 if (rawData->reduceStackFlag) {
241 stackDepth = minStackDepth;
242 }
243 bool ret = runtime_instance->UnwindStack(u64regs_, rawData->stackData, rawData->stackSize,
244 rawData->stackConext->pid, rawData->stackConext->tid, callFrames_, stackDepth);
245 if (!ret) {
246 HILOG_ERROR(LOG_CORE, "unwind fatal error");
247 continue;
248 }
249 if (hookConfig_.save_file() && hookConfig_.file_name() != "" && isHookStandaloneSerialize_) {
250 SetHookData(rawData, callFrames_, stackData);
251 } else if (hookConfig_.save_file() && hookConfig_.file_name() != "") {
252 WriteFrames(rawData, callFrames_);
253 } else if (!hookConfig_.save_file()) {
254 SetHookData(rawData, callFrames_, stackData);
255 }
256 #ifdef PERFORMANCE_DEBUG
257 struct timespec end = {};
258 clock_gettime(CLOCK_REALTIME, &end);
259 uint64_t curTimeCost = (end.tv_sec - start.tv_sec) * MAX_MATCH_CNT * MAX_MATCH_CNT * MAX_MATCH_CNT +
260 (end.tv_nsec - start.tv_nsec);
261 if (curTimeCost >= LONG_TIME_THRESHOLD) {
262 HILOG_ERROR(LOG_CORE, "bigTimeCost %" PRIu64 " event=%d, realFrameDepth=%zu, "
263 "callFramesDepth=%zu\n",
264 curTimeCost, rawData->stackConext->type, realFrameDepth, callFrames_.size());
265 }
266 timeCost += curTimeCost;
267 unwindTimes++;
268 if (unwindTimes % LOG_PRINT_TIMES == 0) {
269 HILOG_ERROR(LOG_CORE, "unwindTimes %" PRIu64" cost time = %" PRIu64" mean cost = %" PRIu64"\n",
270 unwindTimes.load(), timeCost.load(), timeCost.load() / unwindTimes.load());
271 }
272 #endif
273 }
274 if (hookConfig_.save_file() && hookConfig_.file_name() != "" && !isHookStandaloneSerialize_) {
275 continue;
276 }
277 FlushData(stackData);
278
279 // interval reporting statistics
280 if (hookConfig_.statistics_interval() > 0) {
281 auto currentTime = std::chrono::steady_clock::now();
282 auto elapsedTime = std::chrono::duration_cast<std::chrono::seconds>(currentTime - lastStatisticsTime_);
283 if (elapsedTime >= statisticsInterval_) {
284 lastStatisticsTime_ = currentTime;
285 FlushRecordStatistics();
286 }
287 }
288 }
289 HILOG_INFO(LOG_CORE, "TakeResults thread %d, exit!", gettid());
290 }
291
ReportThreadNameMap(uint32_t tid,const std::string & tname,BatchNativeHookData & batchNativeHookData)292 inline void StackPreprocess::ReportThreadNameMap(uint32_t tid, const std::string& tname,
293 BatchNativeHookData& batchNativeHookData)
294 {
295 auto it = threadNameMap_.find(tid);
296 if (it == threadNameMap_.end() || it->second != tname) {
297 threadNameMap_[tid] = tname;
298 auto hookData = batchNativeHookData.add_events();
299 auto* thread = hookData->mutable_thread_name_map();
300 thread->set_id(tid);
301 thread->set_name(tname);
302 }
303 }
304
FillOfflineCallStack(std::vector<CallFrame> & callFrames,size_t idx)305 inline void StackPreprocess::FillOfflineCallStack(std::vector<CallFrame>& callFrames, size_t idx)
306 {
307 for (; idx < callFrames.size(); ++idx) {
308 callStack_.push_back(callFrames[idx].ip_);
309 }
310 }
311
FillCallStack(std::vector<CallFrame> & callFrames,BatchNativeHookData & batchNativeHookData,size_t idx)312 inline void StackPreprocess::FillCallStack(std::vector<CallFrame>& callFrames,
313 BatchNativeHookData& batchNativeHookData, size_t idx)
314 {
315 for (; idx < callFrames.size(); ++idx) {
316 ReportFrameMap(callFrames[idx], batchNativeHookData);
317 // for call stack id
318 callStack_.push_back(callFrames[idx].callFrameId_);
319 }
320 }
321
322 /**
323 * @return '0' is invalid stack id, '> 0' is valid stack id
324 */
SetCallStackMap(BatchNativeHookData & batchNativeHookData)325 inline uint32_t StackPreprocess::SetCallStackMap(BatchNativeHookData& batchNativeHookData)
326 {
327 auto hookData = batchNativeHookData.add_events();
328 StackMap* stackmap = hookData->mutable_stack_map();
329 uint32_t stackId = callStackMap_.size() + 1;
330 stackmap->set_id(stackId);
331 // offline symbolization use ip, other use frame_map_id
332 if (hookConfig_.offline_symbolization()) {
333 for (size_t i = 0; i < callStack_.size(); i++) {
334 stackmap->add_ip(callStack_[i]);
335 }
336 } else {
337 for (size_t i = 0; i < callStack_.size(); i++) {
338 stackmap->add_frame_map_id(callStack_[i]);
339 }
340 }
341 callStackMap_[callStack_] = stackId;
342 return stackId;
343 }
344
345 /**
346 * @return '0' is invalid stack id, '> 0' is valid stack id
347 */
GetCallStackId(const RawStackPtr & rawStack,std::vector<CallFrame> & callFrames,BatchNativeHookData & batchNativeHookData)348 inline uint32_t StackPreprocess::GetCallStackId(const RawStackPtr& rawStack,
349 std::vector<CallFrame>& callFrames,
350 BatchNativeHookData& batchNativeHookData)
351 {
352 // ignore the first two frame if dwarf unwind
353 size_t idx = hookConfig_.fp_unwind() ? 0 : FILTER_STACK_DEPTH;
354 // if free_stack_report or munmap_stack_report is false, don't need to record.
355 if ((rawStack->stackConext->type == FREE_MSG) && !hookConfig_.free_stack_report()) {
356 return 0;
357 } else if ((rawStack->stackConext->type == MUNMAP_MSG) && !hookConfig_.munmap_stack_report()) {
358 return 0;
359 }
360 callStack_.clear();
361 callStack_.reserve(callFrames.size());
362 if (!hookConfig_.offline_symbolization()) {
363 FillCallStack(callFrames, batchNativeHookData, idx);
364 } else {
365 FillOfflineCallStack(callFrames, idx);
366 }
367 // return call stack id
368 auto itStack = callStackMap_.find(callStack_);
369 if (itStack != callStackMap_.end()) {
370 return itStack->second;
371 } else {
372 return SetCallStackMap(batchNativeHookData);
373 }
374 }
375
376 template <typename T>
SetEventFrame(const RawStackPtr & rawStack,std::vector<CallFrame> & callFrames,BatchNativeHookData & batchNativeHookData,T * event,uint32_t stackMapId)377 void StackPreprocess::SetEventFrame(const RawStackPtr& rawStack,
378 std::vector<CallFrame>& callFrames,
379 BatchNativeHookData& batchNativeHookData,
380 T* event, uint32_t stackMapId)
381 {
382 // ignore the first two frame if dwarf unwind
383 size_t idx = hookConfig_.fp_unwind() ? 0 : FILTER_STACK_DEPTH;
384 event->set_pid(rawStack->stackConext->pid);
385 event->set_tid(rawStack->stackConext->tid);
386 event->set_addr((uint64_t)rawStack->stackConext->addr);
387
388 if (hookConfig_.callframe_compress() && stackMapId != 0) {
389 event->set_stack_id(stackMapId);
390 } else if (hookConfig_.string_compressed()) {
391 for (; idx < callFrames.size(); ++idx) {
392 ReportSymbolNameMap(callFrames[idx], batchNativeHookData);
393 ReportFilePathMap(callFrames[idx], batchNativeHookData);
394 Frame* frame = event->add_frame_info();
395 SetFrameInfo(*frame, callFrames[idx]);
396 }
397 } else {
398 for (; idx < callFrames.size(); ++idx) {
399 Frame* frame = event->add_frame_info();
400 SetFrameInfo(*frame, callFrames[idx]);
401 }
402 }
403 }
404
SetAllocStatisticsFrame(const RawStackPtr & rawStack,std::vector<CallFrame> & callFrames,BatchNativeHookData & batchNativeHookData)405 void StackPreprocess::SetAllocStatisticsFrame(const RawStackPtr& rawStack,
406 std::vector<CallFrame>& callFrames,
407 BatchNativeHookData& batchNativeHookData)
408 {
409 // ignore the first two frame if dwarf unwind
410 size_t idx = hookConfig_.fp_unwind() ? 0 : FILTER_STACK_DEPTH;
411 callStack_.clear();
412 callStack_.reserve(callFrames.size());
413 if (!hookConfig_.offline_symbolization()) {
414 FillCallStack(callFrames, batchNativeHookData, idx);
415 } else {
416 FillOfflineCallStack(callFrames, idx);
417 }
418 // by call stack id set alloc statistics data.
419 auto itStack = callStackMap_.find(callStack_);
420 if (itStack != callStackMap_.end()) {
421 SetAllocStatisticsData(rawStack, itStack->second, true);
422 } else {
423 auto stackId = SetCallStackMap(batchNativeHookData);
424 SetAllocStatisticsData(rawStack, stackId);
425 }
426 }
427
SetHookData(RawStackPtr rawStack,std::vector<CallFrame> & callFrames,BatchNativeHookData & batchNativeHookData)428 void StackPreprocess::SetHookData(RawStackPtr rawStack,
429 std::vector<CallFrame>& callFrames, BatchNativeHookData& batchNativeHookData)
430 {
431 if (hookConfig_.offline_symbolization() && flushBasicData_) {
432 SetMapsInfo(rawStack->stackConext->pid);
433 flushBasicData_ = false;
434 }
435
436 // statistical reporting must is compressed and accurate.
437 if (hookConfig_.statistics_interval() > 0) {
438 switch (rawStack->stackConext->type) {
439 case FREE_MSG:
440 case MUNMAP_MSG:
441 case MEMORY_UNUSING_MSG: {
442 SetFreeStatisticsData((uint64_t)rawStack->stackConext->addr);
443 break;
444 }
445 case MALLOC_MSG:
446 case MMAP_MSG:
447 case MMAP_FILE_PAGE_MSG:
448 case MEMORY_USING_MSG: {
449 SetAllocStatisticsFrame(rawStack, callFrames, batchNativeHookData);
450 break;
451 }
452 default: {
453 HILOG_ERROR(LOG_CORE, "statistics event type:%d error", rawStack->stackConext->type);
454 break;
455 }
456 }
457 return;
458 }
459
460 uint32_t stackMapId = 0;
461 if (hookConfig_.callframe_compress() &&
462 !(rawStack->stackConext->type == MEMORY_TAG || rawStack->stackConext->type == PR_SET_VMA_MSG)) {
463 stackMapId = GetCallStackId(rawStack, callFrames, batchNativeHookData);
464 }
465
466 NativeHookData* hookData = batchNativeHookData.add_events();
467 hookData->set_tv_sec(rawStack->stackConext->ts.tv_sec);
468 hookData->set_tv_nsec(rawStack->stackConext->ts.tv_nsec);
469
470 if (rawStack->stackConext->type == MALLOC_MSG) {
471 AllocEvent* allocEvent = hookData->mutable_alloc_event();
472 allocEvent->set_size(static_cast<uint64_t>(rawStack->stackConext->mallocSize));
473 allocEvent->set_thread_name_id(rawStack->stackConext->tid);
474 SetEventFrame(rawStack, callFrames, batchNativeHookData, allocEvent, stackMapId);
475 } else if (rawStack->stackConext->type == FREE_MSG) {
476 FreeEvent* freeEvent = hookData->mutable_free_event();
477 freeEvent->set_thread_name_id(rawStack->stackConext->tid);
478 SetEventFrame(rawStack, callFrames, batchNativeHookData, freeEvent, stackMapId);
479 } else if (rawStack->stackConext->type == MMAP_MSG) {
480 MmapEvent* mmapEvent = hookData->mutable_mmap_event();
481 mmapEvent->set_size(static_cast<uint64_t>(rawStack->stackConext->mallocSize));
482 mmapEvent->set_thread_name_id(rawStack->stackConext->tid);
483 SetEventFrame(rawStack, callFrames, batchNativeHookData, mmapEvent, stackMapId);
484 } else if (rawStack->stackConext->type == MMAP_FILE_PAGE_MSG) {
485 MmapEvent* mmapEvent = hookData->mutable_mmap_event();
486 mmapEvent->set_size(static_cast<uint64_t>(rawStack->stackConext->mallocSize));
487 mmapEvent->set_thread_name_id(rawStack->stackConext->tid);
488 const std::string prefix = "FilePage:";
489 std::string tagName;
490 if (GetMemTag(rawStack->stackConext->tagId, tagName)) {
491 mmapEvent->set_type(prefix + tagName);
492 }
493 SetEventFrame(rawStack, callFrames, batchNativeHookData, mmapEvent, stackMapId);
494 } else if (rawStack->stackConext->type == MUNMAP_MSG) {
495 MunmapEvent* munmapEvent = hookData->mutable_munmap_event();
496 munmapEvent->set_size(static_cast<uint64_t>(rawStack->stackConext->mallocSize));
497 munmapEvent->set_thread_name_id(rawStack->stackConext->tid);
498 SetEventFrame(rawStack, callFrames, batchNativeHookData, munmapEvent, stackMapId);
499 } else if (rawStack->stackConext->type == PR_SET_VMA_MSG) {
500 MemTagEvent* tagEvent = hookData->mutable_tag_event();
501 const std::string prefix = "Anonymous:";
502 std::string tagName(reinterpret_cast<char*>(rawStack->data));
503 tagEvent->set_tag(prefix + tagName);
504 tagEvent->set_size(rawStack->stackConext->mallocSize);
505 tagEvent->set_addr((uint64_t)rawStack->stackConext->addr);
506 } else if (rawStack->stackConext->type == MEMORY_USING_MSG) {
507 MmapEvent* mmapEvent = hookData->mutable_mmap_event();
508 mmapEvent->set_size(static_cast<uint64_t>(rawStack->stackConext->mallocSize));
509 mmapEvent->set_thread_name_id(rawStack->stackConext->tid);
510 std::string tagName;
511 if (GetMemTag(rawStack->stackConext->tagId, tagName)) {
512 mmapEvent->set_type(tagName);
513 }
514 SetEventFrame(rawStack, callFrames, batchNativeHookData, mmapEvent, stackMapId);
515 } else if (rawStack->stackConext->type == MEMORY_UNUSING_MSG) {
516 MunmapEvent* munmapEvent = hookData->mutable_munmap_event();
517 munmapEvent->set_size(static_cast<uint64_t>(rawStack->stackConext->mallocSize));
518 munmapEvent->set_thread_name_id(rawStack->stackConext->tid);
519 SetEventFrame(rawStack, callFrames, batchNativeHookData, munmapEvent, stackMapId);
520 }
521 }
522
SetFreeStatisticsData(uint64_t addr)523 inline bool StackPreprocess::SetFreeStatisticsData(uint64_t addr)
524 {
525 // through the addr lookup record
526 auto addrIter = allocAddrMap_.find(addr);
527 if (addrIter != allocAddrMap_.end()) {
528 auto& record = addrIter->second.second;
529 ++record->releaseCount;
530 record->releaseSize += addrIter->second.first;
531 statisticsPeriodData_[record->callstackId] = record;
532 allocAddrMap_.erase(addr);
533 return true;
534 }
535 return false;
536 }
537
SetAllocStatisticsData(const RawStackPtr & rawStack,size_t stackId,bool isExists)538 inline void StackPreprocess::SetAllocStatisticsData(const RawStackPtr& rawStack, size_t stackId, bool isExists)
539 {
540 // if the record exists, it is updated.Otherwise Add
541 if (isExists) {
542 auto recordIter = recordStatisticsMap_.find(stackId);
543 if (recordIter != recordStatisticsMap_.end()) {
544 auto& record = recordIter->second;
545 ++record.applyCount;
546 record.applySize += rawStack->stackConext->mallocSize;
547 allocAddrMap_[(uint64_t)rawStack->stackConext->addr] =
548 std::pair(rawStack->stackConext->mallocSize, &recordIter->second);
549 statisticsPeriodData_[stackId] = &recordIter->second;
550 }
551 } else {
552 RecordStatistic record;
553 record.pid = rawStack->stackConext->pid;
554 record.callstackId = stackId;
555 record.applyCount = 1;
556 record.applySize = rawStack->stackConext->mallocSize;
557 switch (rawStack->stackConext->type) {
558 case MALLOC_MSG: {
559 record.type = RecordStatisticsEvent::MALLOC;
560 break;
561 }
562 case MMAP_MSG: {
563 record.type = RecordStatisticsEvent::MMAP;
564 break;
565 }
566 case MMAP_FILE_PAGE_MSG: {
567 record.type = RecordStatisticsEvent::FILE_PAGE_MSG;
568 break;
569 }
570 case MEMORY_USING_MSG: {
571 record.type = RecordStatisticsEvent::MEMORY_USING_MSG;
572 record.tagId = rawStack->stackConext->tagId;
573 break;
574 }
575 default: {
576 HILOG_ERROR(LOG_CORE, "SetAllocStatisticsData event type error");
577 break;
578 }
579 }
580
581 auto [recordIter, stat] = recordStatisticsMap_.emplace(stackId, record);
582 allocAddrMap_[(uint64_t)rawStack->stackConext->addr] =
583 std::pair(rawStack->stackConext->mallocSize, &recordIter->second);
584 statisticsPeriodData_[stackId] = &recordIter->second;
585 }
586 }
587
WriteFrames(RawStackPtr rawStack,const std::vector<CallFrame> & callFrames)588 void StackPreprocess::WriteFrames(RawStackPtr rawStack, const std::vector<CallFrame>& callFrames)
589 {
590 CHECK_TRUE(fpHookData_.get() != nullptr, NO_RETVAL, "fpHookData_ is nullptr, please check file_name(%s)",
591 hookConfig_.file_name().c_str());
592 if (rawStack->stackConext->type == PR_SET_VMA_MSG) {
593 const std::string prefix = "Anonymous:";
594 std::string tagName;
595 GetMemTag(rawStack->stackConext->tagId, tagName);
596 fprintf(fpHookData_.get(), "prctl;%u;%u;%" PRId64 ";%ld;0x%" PRIx64 ":tag:%s\n",
597 rawStack->stackConext->pid, rawStack->stackConext->tid,
598 (int64_t)rawStack->stackConext->ts.tv_sec, rawStack->stackConext->ts.tv_nsec,
599 (uint64_t)rawStack->stackConext->addr, (prefix + tagName).c_str());
600 return;
601 }
602 std::string tag = "";
603 switch (rawStack->stackConext->type) {
604 case FREE_MSG:
605 tag = "free";
606 break;
607 case MALLOC_MSG:
608 tag = "malloc";
609 break;
610 case MMAP_MSG:
611 tag = "mmap";
612 break;
613 case MUNMAP_MSG:
614 tag = "munmap";
615 break;
616 default:
617 break;
618 }
619
620 fprintf(fpHookData_.get(), "%s;%u;%u;%" PRId64 ";%ld;0x%" PRIx64 ";%zu\n", tag.c_str(),
621 rawStack->stackConext->pid, rawStack->stackConext->tid, (int64_t)rawStack->stackConext->ts.tv_sec,
622 rawStack->stackConext->ts.tv_nsec, (uint64_t)rawStack->stackConext->addr, rawStack->stackConext->mallocSize);
623 size_t idx = hookConfig_.fp_unwind() ? 0 : FILTER_STACK_DEPTH;
624 for (; idx < callFrames.size(); ++idx) {
625 (void)fprintf(fpHookData_.get(), "0x%" PRIx64 ";0x%" PRIx64 ";%s;%s;0x%" PRIx64 ";%" PRIu64 "\n",
626 callFrames[idx].ip_, callFrames[idx].sp_, std::string(callFrames[idx].symbolName_).c_str(),
627 std::string(callFrames[idx].filePath_).c_str(), callFrames[idx].offset_, callFrames[idx].symbolOffset_);
628 }
629 }
630
SetFrameInfo(Frame & frame,CallFrame & callFrame)631 inline void StackPreprocess::SetFrameInfo(Frame& frame, CallFrame& callFrame)
632 {
633 frame.set_ip(callFrame.ip_);
634 if (hookConfig_.offline_symbolization()) {
635 return;
636 }
637 frame.set_sp(callFrame.sp_);
638 frame.set_offset(callFrame.offset_);
639 frame.set_symbol_offset(callFrame.symbolOffset_);
640
641 if (callFrame.symbolNameId_ != 0 && callFrame.filePathId_ != 0) {
642 frame.set_symbol_name_id(callFrame.symbolNameId_);
643 frame.set_file_path_id(callFrame.filePathId_);
644 } else {
645 frame.set_symbol_name(std::string(callFrame.symbolName_));
646 frame.set_file_path(std::string(callFrame.filePath_));
647 }
648 }
649
ReportSymbolNameMap(CallFrame & callFrame,BatchNativeHookData & batchNativeHookData)650 inline void StackPreprocess::ReportSymbolNameMap(CallFrame& callFrame, BatchNativeHookData& batchNativeHookData)
651 {
652 if (callFrame.needReport_ & SYMBOL_NAME_ID_REPORT) {
653 auto hookData = batchNativeHookData.add_events();
654 SymbolMap* symbolMap = hookData->mutable_symbol_name();
655 symbolMap->set_id(callFrame.symbolNameId_);
656 symbolMap->set_name(std::string(callFrame.symbolName_));
657 }
658 }
659
ReportFilePathMap(CallFrame & callFrame,BatchNativeHookData & batchNativeHookData)660 inline void StackPreprocess::ReportFilePathMap(CallFrame& callFrame, BatchNativeHookData& batchNativeHookData)
661 {
662 if (callFrame.needReport_ & FILE_PATH_ID_REPORT) {
663 auto hookData = batchNativeHookData.add_events();
664 FilePathMap* filePathMap = hookData->mutable_file_path();
665 filePathMap->set_id(callFrame.filePathId_);
666 filePathMap->set_name(std::string(callFrame.filePath_));
667 }
668 }
669
ReportFrameMap(CallFrame & callFrame,BatchNativeHookData & batchNativeHookData)670 inline void StackPreprocess::ReportFrameMap(CallFrame& callFrame, BatchNativeHookData& batchNativeHookData)
671 {
672 if (callFrame.needReport_ & CALL_FRAME_REPORT) {
673 ReportSymbolNameMap(callFrame, batchNativeHookData);
674 ReportFilePathMap(callFrame, batchNativeHookData);
675 auto hookData = batchNativeHookData.add_events();
676 FrameMap* frameMap = hookData->mutable_frame_map();
677 Frame* frame = frameMap->mutable_frame();
678 SetFrameInfo(*frame, callFrame);
679 frameMap->set_id(callFrame.callFrameId_);
680 }
681 }
682
SetMapsInfo(pid_t pid)683 void StackPreprocess::SetMapsInfo(pid_t pid)
684 {
685 for (auto& itemSoBegin : runtime_instance->GetOfflineMaps()) {
686 auto& maps = runtime_instance->GetMapsCache();
687 auto mapsIter = maps.find(itemSoBegin);
688 if (mapsIter == maps.end()) {
689 continue;
690 }
691
692 ElfSymbolTable symbolInfo;
693 auto& curMemMaps = mapsIter->second;
694 GetSymbols(curMemMaps.name_, symbolInfo);
695 if (symbolInfo.symEntSize == 0) {
696 continue;
697 }
698 BatchNativeHookData stackData;
699 NativeHookData* hookData = stackData.add_events();
700 FilePathMap* filepathMap = hookData->mutable_file_path();
701 filepathMap->set_id(curMemMaps.filePathId_);
702 filepathMap->set_name(curMemMaps.name_);
703 SetSymbolInfo(curMemMaps.filePathId_, symbolInfo, stackData);
704
705 for (auto& map : curMemMaps.maps_) {
706 if (map.type_ & PROT_EXEC) {
707 NativeHookData* nativeHookData = stackData.add_events();
708 MapsInfo* mapSerialize = nativeHookData->mutable_maps_info();
709 mapSerialize->set_pid(pid);
710 mapSerialize->set_start(map.begin_);
711 mapSerialize->set_end(map.end_);
712 mapSerialize->set_offset(map.pageoffset_);
713 mapSerialize->set_file_path_id(curMemMaps.filePathId_);
714 }
715 }
716 FlushData(stackData);
717 }
718 runtime_instance->ClearOfflineMaps();
719 }
720
SetSymbolInfo(uint32_t filePathId,ElfSymbolTable & symbolInfo,BatchNativeHookData & batchNativeHookData)721 void StackPreprocess::SetSymbolInfo(uint32_t filePathId, ElfSymbolTable& symbolInfo,
722 BatchNativeHookData& batchNativeHookData)
723 {
724 if (symbolInfo.symEntSize == 0) {
725 HILOG_ERROR(LOG_CORE, "SetSymbolInfo get symbolInfo failed");
726 return;
727 }
728 NativeHookData* hookData = batchNativeHookData.add_events();
729 SymbolTable* symTable = hookData->mutable_symbol_tab();
730 symTable->set_file_path_id(filePathId);
731 symTable->set_text_exec_vaddr(symbolInfo.textVaddr);
732 symTable->set_text_exec_vaddr_file_offset(symbolInfo.textOffset);
733 symTable->set_sym_entry_size(symbolInfo.symEntSize);
734 symTable->set_sym_table(symbolInfo.symTable.data(), symbolInfo.symTable.size());
735 symTable->set_str_table(symbolInfo.strTable.data(), symbolInfo.strTable.size());
736 }
737
FlushData(BatchNativeHookData & stackData)738 void StackPreprocess::FlushData(BatchNativeHookData& stackData)
739 {
740 if (stackData.events().size() > 0) {
741 size_t length = stackData.ByteSizeLong();
742 stackData.SerializeToArray(buffer_.get(), length);
743 if (length < MAX_BUFFER_SIZE) {
744 if (isHookStandaloneSerialize_) {
745 std::string str;
746 ForStandard::BatchNativeHookData StandardStackData;
747 StandardStackData.ParseFromArray(buffer_.get(), length);
748 google::protobuf::TextFormat::PrintToString(StandardStackData, &str);
749 size_t n = fwrite(str.data(), 1, str.size(), fpHookData_.get());
750 fflush(fpHookData_.get());
751 HILOG_DEBUG(LOG_CORE, "Flush Data fwrite n = %zu str.size() = %zu", n, str.size());
752 } else {
753 Flush(buffer_.get(), length);
754 }
755 } else {
756 HILOG_ERROR(LOG_CORE, "the data is larger than MAX_BUFFER_SIZE, flush failed");
757 }
758 }
759 }
760
Flush(const uint8_t * src,size_t size)761 void StackPreprocess::Flush(const uint8_t* src, size_t size)
762 {
763 if (src == nullptr) {
764 HILOG_ERROR(LOG_CORE, "Flush src is nullptr");
765 return;
766 }
767 ProfilerPluginData pluginData;
768 pluginData.set_name("nativehook");
769 pluginData.set_version("1.02");
770 pluginData.set_status(0);
771 pluginData.set_data(src, size);
772 struct timespec ts;
773 clock_gettime(pluginDataClockId_, &ts);
774 pluginData.set_clock_id(static_cast<ProfilerPluginData_ClockId>(pluginDataClockId_));
775 pluginData.set_tv_sec(ts.tv_sec);
776 pluginData.set_tv_nsec(ts.tv_nsec);
777 writer_->WriteMessage(pluginData, "nativehook");
778 writer_->Flush();
779 }
780
GetSymbols(const std::string & filePath,ElfSymbolTable & symbols)781 void StackPreprocess::GetSymbols(const std::string& filePath, ElfSymbolTable& symbols)
782 {
783 using OHOS::Developtools::NativeDaemon::ELF::ElfFile;
784 std::unique_ptr<ElfFile> elfPtr = ElfFile::MakeUnique(filePath);
785 if (elfPtr == nullptr) {
786 HILOG_ERROR(LOG_CORE, "GetSymbols elfPtr is nullptr");
787 return;
788 }
789
790 symbols.textVaddr = (std::numeric_limits<uint64_t>::max)();
791 for (auto &item : elfPtr->phdrs_) {
792 if ((item->type_ == PT_LOAD) && (item->flags_ & PF_X)) {
793 // find the min addr
794 if (symbols.textVaddr != (std::min)(symbols.textVaddr, item->vaddr_)) {
795 symbols.textVaddr = (std::min)(symbols.textVaddr, item->vaddr_);
796 symbols.textOffset = item->offset_;
797 }
798 }
799 }
800 if (symbols.textVaddr == (std::numeric_limits<uint64_t>::max)()) {
801 HILOG_ERROR(LOG_CORE, "GetSymbols get textVaddr failed");
802 return;
803 }
804
805 std::string symSecName;
806 std::string strSecName;
807 if (elfPtr->shdrs_.find(".symtab") != elfPtr->shdrs_.end()) {
808 symSecName = ".symtab";
809 strSecName = ".strtab";
810 } else if (elfPtr->shdrs_.find(".dynsym") != elfPtr->shdrs_.end()) {
811 symSecName = ".dynsym";
812 strSecName = ".dynstr";
813 } else {
814 return;
815 }
816 const auto &sym = elfPtr->shdrs_[static_cast<const std::string>(symSecName)];
817 const uint8_t* symData = elfPtr->GetSectionData(sym->secIndex_);
818 const auto &str = elfPtr->shdrs_[static_cast<const std::string>(strSecName)];
819 const uint8_t* strData = elfPtr->GetSectionData(str->secIndex_);
820
821 if (sym->secSize_ == 0 || str->secSize_ == 0) {
822 HILOG_ERROR(LOG_CORE, "GetSymbols get section size failed, \
823 sym size: %" PRIu64 ", str size: %" PRIu64 "", sym->secSize_, str->secSize_);
824 return;
825 }
826 symbols.symEntSize = sym->secEntrySize_;
827 symbols.symTable.resize(sym->secSize_);
828 std::copy(symData, symData + sym->secSize_, symbols.symTable.data());
829 symbols.strTable.resize(str->secSize_);
830 std::copy(strData, strData + str->secSize_, symbols.strTable.data());
831 }
832
FlushRecordStatistics()833 bool StackPreprocess::FlushRecordStatistics()
834 {
835 if (statisticsPeriodData_.empty()) {
836 return false;
837 }
838 struct timespec ts;
839 clock_gettime(hookDataClockId_, &ts);
840 BatchNativeHookData statisticsData;
841 for (auto [addr, statistics] : statisticsPeriodData_) {
842 NativeHookData* hookData = statisticsData.add_events();
843 hookData->set_tv_sec(ts.tv_sec);
844 hookData->set_tv_nsec(ts.tv_nsec);
845 RecordStatisticsEvent* recordEvent = hookData->mutable_statistics_event();
846 recordEvent->set_pid(statistics->pid);
847 recordEvent->set_callstack_id(statistics->callstackId);
848 recordEvent->set_type(statistics->type);
849 recordEvent->set_apply_count(statistics->applyCount);
850 recordEvent->set_release_count(statistics->releaseCount);
851 recordEvent->set_apply_size(statistics->applySize);
852 recordEvent->set_release_size(statistics->releaseSize);
853
854 std::string tagName;
855 if (statistics->type == RecordStatisticsEvent::MEMORY_USING_MSG && GetMemTag(statistics->tagId, tagName)) {
856 recordEvent->set_tag_name(tagName);
857 }
858 }
859 FlushData(statisticsData);
860 statisticsPeriodData_.clear();
861
862 return true;
863 }
864
SaveMemTag(uint32_t tagId,const std::string & tagName)865 void StackPreprocess::SaveMemTag(uint32_t tagId, const std::string& tagName)
866 {
867 std::string temp;
868 bool res = memTagMap_.Find(tagId, temp);
869 if (!res) {
870 memTagMap_[tagId] = tagName;
871 }
872 }
873
GetMemTag(uint32_t tagId,std::string & tagName)874 bool StackPreprocess::GetMemTag(uint32_t tagId, std::string& tagName)
875 {
876 return memTagMap_.Find(tagId, tagName);
877 }
878