• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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