1 /*
2  * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "stack_preprocess.h"
17 
18 #include <elf.h>
19 #include <unistd.h>
20 
21 #include "common.h"
22 #include "logging.h"
23 #include "plugin_service_types.pb.h"
24 #include "dfx_elf.h"
25 #include "utilities.h"
26 #include "native_hook_result_standard.pb.h"
27 #include "native_hook_config_standard.pb.h"
28 #include "google/protobuf/text_format.h"
29 #include "trace_file_writer.h"
30 
31 
32 constexpr static uint32_t SC_LG_TINY_MIN = 3;
33 constexpr static uint32_t LG_QUANTUM = 4;
34 constexpr static uint32_t SC_NTINY = LG_QUANTUM - SC_LG_TINY_MIN;
35 constexpr static uint32_t SC_LG_TINY_MAXCLASS = (LG_QUANTUM > SC_LG_TINY_MIN ? LG_QUANTUM - 1 : -1);
36 constexpr static uint32_t SC_LG_NGROUP = 2;
37 constexpr static uint32_t LG_SIZE_CLASS_GROUP = 2;
38 constexpr static uint32_t NTBINS = 1;
39 constexpr static uint32_t LG_TINY_MAXCLASS = 3;
40 constexpr static uint32_t MAX_MATCH_CNT = 1000;
41 constexpr static uint32_t MAX_MATCH_INTERVAL = 3600;
42 constexpr static uint32_t LOG_PRINT_TIMES = 10000;
43 constexpr static uint32_t WAIT_STOP_TIME = 5000;
44 constexpr static uint32_t WAIT_TIME_ONCE = 10;
45 constexpr static uint32_t MAX_BATCH_CNT = 40;
46 constexpr static uint32_t RIGHT_MOVE_1 = 1;
47 constexpr static uint32_t RIGHT_MOVE_2 = 2;
48 constexpr static uint32_t RIGHT_MOVE_4 = 4;
49 constexpr static uint32_t RIGHT_MOVE_8 = 8;
50 constexpr static uint32_t RIGHT_MOVE_16 = 16;
51 constexpr static uint64_t SIZE_MASK = 0xFFFFFF0000000000;
52 constexpr static uint64_t JS_OFFLINE_IP_MASK = 0xFFFFFE0000000000;
53 constexpr static uint64_t DWARF_ERROR_ID = 999999;
54 constexpr static uint64_t DWARF_NAPI_CALLBACK = 999999;
55 static std::string JS_CALL_STACK_DEPTH_SEP = ",";   // ',' is js call stack depth separator
56 static std::string JS_SYMBOL_FILEPATH_SEP = "|";    // '|' is js symbol and filepath separator
57 constexpr static int NAPI_CALL_STACK = 2; // just for napi call stack
58 constexpr static uint32_t FRAME_DEPTH = 2; // add two frames
59 #ifdef PERFORMANCE_DEBUG
60 constexpr static uint32_t LONG_TIME_THRESHOLD = 1000000;
61 static std::atomic<uint64_t> timeCost = 0;
62 static std::atomic<uint64_t> unwindTimes = 0;
63 #endif
64 
65 using namespace OHOS::Developtools::NativeDaemon;
66 using namespace OHOS::HiviewDFX;
67 using namespace OHOS::Developtools::Profiler;
68 
StackPreprocess(const StackDataRepeaterPtr& dataRepeater, const NativeHookConfig& hookConfig, clockid_t pluginDataClockId, FILE* fpHookData, bool isHookStandalone, bool isSaService, bool isProtobufSerialize)69 StackPreprocess::StackPreprocess(const StackDataRepeaterPtr& dataRepeater, const NativeHookConfig& hookConfig,
70     clockid_t pluginDataClockId, FILE* fpHookData, bool isHookStandalone, bool isSaService, bool isProtobufSerialize)
71     : dataRepeater_(dataRepeater), hookConfig_(hookConfig), pluginDataClockId_(pluginDataClockId),
72       fpHookData_(fpHookData), isHookStandaloneSerialize_(isHookStandalone), isSaService_(isSaService),
73       isProtobufSerialize_(isProtobufSerialize)
74 {
75     runtime_instance = std::make_shared<VirtualRuntime>(hookConfig_);
76 
77     if (hookConfig_.malloc_free_matching_interval() > MAX_MATCH_INTERVAL) {
78         PROFILER_LOG_INFO(LOG_CORE, "Not support set %d", hookConfig_.malloc_free_matching_interval());
79         hookConfig_.set_malloc_free_matching_interval(MAX_MATCH_INTERVAL);
80     }
81 
82     if (hookConfig_.malloc_free_matching_cnt() > MAX_MATCH_CNT) {
83         PROFILER_LOG_INFO(LOG_CORE, "Not support set %d", hookConfig_.malloc_free_matching_cnt());
84         hookConfig_.set_malloc_free_matching_cnt(MAX_MATCH_CNT);
85     }
86     PROFILER_LOG_INFO(LOG_CORE, "malloc_free_matching_interval = %d malloc_free_matching_cnt = %d\n",
87         hookConfig_.malloc_free_matching_interval(), hookConfig_.malloc_free_matching_cnt());
88 
89     if (hookConfig_.statistics_interval() > 0) {
90         statisticsInterval_ = std::chrono::seconds(hookConfig_.statistics_interval());
91         recordStatisticsMap_.reserve(STATISTICS_MAP_SZIE);
92         statisticsPeriodData_.reserve(STATISTICS_PERIOD_DATA_SIZE);
93         allocAddrMap_.reserve(ALLOC_ADDRMAMP_SIZE);
94     }
95     if (hookConfig_.malloc_free_matching_interval() > 0) {
96         applyAndReleaseMatchInterval_ = std::chrono::seconds(hookConfig_.malloc_free_matching_interval());
97         applyAndReleaseMatchIntervallMap_.reserve(MATCH_ADDRMAMP_SIZE);
98     }
99     PROFILER_LOG_INFO(LOG_CORE, "statistics_interval = %d statisticsInterval_ = %lld \n",
100         hookConfig_.statistics_interval(), statisticsInterval_.count());
101     PROFILER_LOG_INFO(LOG_CORE, "applyAndReleaseMatchInterval_ = %lld", applyAndReleaseMatchInterval_.count());
102     hookDataClockId_ = COMMON::GetClockId(hookConfig_.clock());
103     PROFILER_LOG_INFO(LOG_CORE, "StackPreprocess(): pluginDataClockId = %d hookDataClockId = %d \n",
104         pluginDataClockId_, hookDataClockId_);
105     if (hookConfig_.save_file() && fpHookData_ == nullptr) {
106         PROFILER_LOG_ERROR(LOG_CORE, "If you need to save the file, please set the file_name");
107     }
108     PROFILER_LOG_INFO(LOG_CORE, "isHookStandaloneSerialize_ = %d", isHookStandaloneSerialize_);
109 #if defined(__arm__)
110     u64regs_.resize(PERF_REG_ARM_MAX);
111 #else
112     u64regs_.resize(PERF_REG_ARM64_MAX);
113 #endif
114     callFrames_.reserve(hookConfig_.max_stack_depth() + hookConfig_.max_js_stack_depth());
115     if (hookConfig_.fp_unwind() && hookConfig_.js_stack_report() > 0) {
116         fpJsCallStacks_.reserve(hookConfig_.max_js_stack_depth());
117     }
118 }
119 
~StackPreprocess()120 StackPreprocess::~StackPreprocess()
121 {
122     isStopTakeData_ = true;
123     if (dataRepeater_) {
124         dataRepeater_->Close();
125     }
126     if (thread_.joinable()) {
127         thread_.join();
128     }
129     runtime_instance = nullptr;
130     fpHookData_ = nullptr;
131 }
132 
FinishTraceFile()133 void StackPreprocess::FinishTraceFile()
134 {
135     if (isSaService_) {
136         std::shared_ptr<TraceFileWriter> tfPtr = std::static_pointer_cast<TraceFileWriter>(writer_);
137         tfPtr->SetDurationTime();
138         tfPtr->Finish();
139     }
140 }
141 
SetWriter(const std::shared_ptr<Writer>& writer)142 void StackPreprocess::SetWriter(const std::shared_ptr<Writer>& writer)
143 {
144     writer_ = writer;
145     if (!isSaService_) {
146         stackData_ = BatchNativeHookData();
147     }
148 }
149 
SetWriter(const WriterStructPtr& writer)150 void StackPreprocess::SetWriter(const WriterStructPtr& writer)
151 {
152     resultWriter_ = writer;
153     auto ctx = resultWriter_->startReport(resultWriter_);
154     if (ctx == nullptr) {
155         PROFILER_LOG_ERROR(LOG_CORE, "%s: get RandomWriteCtx FAILED!", __func__);
156         return;
157     }
158     stackData_ = ProtoEncoder::BatchNativeHookData(ctx);
159 }
160 
StartTakeResults()161 bool StackPreprocess::StartTakeResults()
162 {
163     CHECK_NOTNULL(dataRepeater_, false, "data repeater null");
164 
165     std::weak_ptr<StackPreprocess> stackPreprocessPtr(shared_from_this());
166     std::thread demuxer([stackPreprocessPtr] {
167         if (auto ptr = stackPreprocessPtr.lock(); ptr != nullptr) {
168             ptr->TakeResults();
169         }
170     });
171     CHECK_TRUE(demuxer.get_id() != std::thread::id(), false, "demuxer thread invalid");
172 
173     thread_ = std::move(demuxer);
174     isStopTakeData_ = false;
175     return true;
176 }
177 
StopTakeResults()178 bool StackPreprocess::StopTakeResults()
179 {
180     PROFILER_LOG_INFO(LOG_CORE, "start StopTakeResults");
181     int32_t timerFd = scheduleTaskManager_.ScheduleTask(
182         std::bind(&StackPreprocess::ForceStop, this), WAIT_STOP_TIME, true, false);
183     if (timerFd == -1) {
184         PROFILER_LOG_ERROR(LOG_CORE, "StopTakeResults ScheduleTask failed!");
185         return false;
186     }
187     if (!dataRepeater_) {
188         while (!isStopTakeData_) {
189             std::this_thread::sleep_for(std::chrono::milliseconds(WAIT_TIME_ONCE));
190         }
191         return true;
192     }
193     CHECK_NOTNULL(dataRepeater_, false, "data repeater null");
194     CHECK_TRUE(thread_.get_id() != std::thread::id(), false, "thread invalid");
195 
196     PROFILER_LOG_INFO(LOG_CORE, "StopTakeResults Wait thread join");
197 
198     if (thread_.joinable()) {
199         thread_.join();
200     }
201     PROFILER_LOG_INFO(LOG_CORE, "StopTakeResults Wait thread join success");
202     return true;
203 }
204 
IntervalFlushRecordStatistics()205 inline void StackPreprocess::IntervalFlushRecordStatistics()
206 {
207     // interval reporting statistics
208     if (hookConfig_.statistics_interval() > 0) {
209         auto currentTime = std::chrono::steady_clock::now();
210         auto elapsedTime = std::chrono::duration_cast<std::chrono::microseconds>(currentTime - lastStatisticsTime_);
211         if (elapsedTime >= statisticsInterval_) {
212             lastStatisticsTime_ = currentTime;
213             FlushRecordStatistics();
214         }
215     }
216 }
217 
IntervalFlushApplyAndReleaseMatchData()218 inline void StackPreprocess::IntervalFlushApplyAndReleaseMatchData()
219 {
220     // interval reporting apply and release match data
221     if (hookConfig_.malloc_free_matching_interval() > 0) {
222         static auto lastStatisticsTime = std::chrono::steady_clock::now();
223         auto currentTime = std::chrono::steady_clock::now();
224         auto elapsedTime = std::chrono::duration_cast<std::chrono::seconds>(currentTime - lastStatisticsTime);
225         if (elapsedTime >= applyAndReleaseMatchInterval_) {
226             lastStatisticsTime = currentTime;
227             FlushRecordApplyAndReleaseMatchData();
228         }
229     }
230 }
231 
HandleNoStackEvent(RawStackPtr& rawData)232 bool StackPreprocess::HandleNoStackEvent(RawStackPtr& rawData)
233 {
234     if (rawData->stackConext->type == MMAP_FILE_TYPE) {
235         BaseStackRawData* mmapRawData = rawData->stackConext;
236         std::string filePath(reinterpret_cast<char *>(rawData->data));
237         COMMON::AdaptSandboxPath(filePath, rawData->stackConext->pid);
238         PROFILER_LOG_DEBUG(LOG_CORE, "MMAP_FILE_TYPE curMmapAddr=%p, MAP_FIXED=%d, "
239                     "PROT_EXEC=%d, offset=%" PRIu64 ", filePath=%s",
240                     mmapRawData->addr, mmapRawData->mmapArgs.flags & MAP_FIXED,
241                     mmapRawData->mmapArgs.flags & PROT_EXEC, mmapRawData->mmapArgs.offset, filePath.data());
242         std::lock_guard<std::mutex> guard(mtx_);
243         runtime_instance->HandleMapInfo({reinterpret_cast<uint64_t>(mmapRawData->addr),
244             mmapRawData->mallocSize, mmapRawData->mmapArgs.flags, mmapRawData->mmapArgs.offset}, filePath,
245             rawData->stackConext->pid, rawData->stackConext->tid);
246         flushBasicData_ = true;
247     } else if (rawData->stackConext->type == THREAD_NAME_MSG) {
248         std::string threadName = reinterpret_cast<char*>(rawData->data);
249         ReportThreadNameMap(rawData->stackConext->tid, threadName);
250     } else {
251         return false;
252     }
253     return true;
254 }
255 
ForceStop()256 void StackPreprocess::ForceStop()
257 {
258     isStopTakeData_ = true;
259     if (dataRepeater_ != nullptr) {
260         dataRepeater_->Close();
261     }
262 }
263 
TakeResultsFromShmem(const std::shared_ptr<EventNotifier>& eventNotifier, const std::shared_ptr<ShareMemoryBlock>& shareMemoryBlock)264 void StackPreprocess::TakeResultsFromShmem(const std::shared_ptr<EventNotifier>& eventNotifier,
265                                            const std::shared_ptr<ShareMemoryBlock>& shareMemoryBlock)
266 {
267     eventNotifier->Take();
268     StackDataRepeater::RawStack rawStack;
269     RawStackPtr rawData(&rawStack, [](StackDataRepeater::RawStack* del) {});
270     while (!isStopTakeData_) {
271         bool ret = shareMemoryBlock->TakeData(
272             [&](const int8_t data[], uint32_t size) -> bool {
273 #ifdef PERFORMANCE_DEBUG
274             struct timespec start = {};
275             clock_gettime(CLOCK_REALTIME, &start);
276 #endif
277             if (size == sizeof(uint64_t)) {
278                 uint64_t addr = *reinterpret_cast<uint64_t *>(const_cast<int8_t *>(data));
279                 SetFreeStatisticsData(addr);
280 #ifdef PERFORMANCE_DEBUG
281                 struct timespec end = {};
282                 clock_gettime(CLOCK_REALTIME, &end);
283                 uint64_t curTimeCost = (end.tv_sec - start.tv_sec) * MAX_MATCH_CNT * MAX_MATCH_CNT * MAX_MATCH_CNT +
284                     (end.tv_nsec - start.tv_nsec);
285                 timeCost += curTimeCost;
286                 unwindTimes++;
287                 if (unwindTimes % LOG_PRINT_TIMES == 0) {
288                     PROFILER_LOG_ERROR(LOG_CORE,
289                                        "unwindTimes %" PRIu64" cost time = %" PRIu64" mean cost = %" PRIu64"\n",
290                                        unwindTimes.load(), timeCost.load(), timeCost.load() / unwindTimes.load());
291                 }
292 #endif
293                 return true;
294             }
295             CHECK_TRUE(size >= sizeof(BaseStackRawData), false, "stack data invalid!");
296             rawData->stackConext = reinterpret_cast<BaseStackRawData *>(const_cast<int8_t *>(data));
297             rawData->data = reinterpret_cast<uint8_t*>(const_cast<int8_t *>(data)) + sizeof(BaseStackRawData);
298             rawData->fpDepth = (size - sizeof(BaseStackRawData)) / sizeof(uint64_t);
299             if (isStopTakeData_) {
300                 return false;
301             } else if (rawData->stackConext->type == MEMORY_TAG) {
302                 std::string tagName = reinterpret_cast<char*>(rawData->data);
303                 SaveMemTag(rawData->stackConext->tagId, tagName);
304                 return true;
305             } else if (HandleNoStackEvent(rawData)) {
306                 return true;
307             } else if (rawData->stackConext->type == MUNMAP_MSG) {
308                 std::lock_guard<std::mutex> guard(mtx_);
309                 runtime_instance->RemoveMaps(reinterpret_cast<uint64_t>(rawData->stackConext->addr));
310             } else if (rawData->stackConext->type == NMD_MSG) {
311                 const char* nmdResult = reinterpret_cast<const char*>(rawData->data);
312                 lseek(nmdFd_, 0, SEEK_END);
313                 (void)write(nmdFd_, nmdResult, strlen(nmdResult));
314                 return true;
315             }  else if (rawData->stackConext->type == END_MSG) {
316                 isStopTakeData_ = true;
317                 return true;
318             }
319             {
320                 std::lock_guard<std::mutex> guard(mtx_);
321                 runtime_instance->UpdateThread(rawData->stackConext->pid, rawData->stackConext->tid);
322             }
323             ReportOfflineSymbolizationData();
324             std::visit([&](auto& stackData) {
325                 SetHookData(rawData, stackData);
326                 FlushCheck(stackData);
327                 }, stackData_);
328             IntervalFlushRecordStatistics();
329 #ifdef PERFORMANCE_DEBUG
330             struct timespec end = {};
331             clock_gettime(CLOCK_REALTIME, &end);
332             uint64_t curTimeCost = (end.tv_sec - start.tv_sec) * MAX_MATCH_CNT * MAX_MATCH_CNT * MAX_MATCH_CNT +
333                 (end.tv_nsec - start.tv_nsec);
334             if (curTimeCost >= LONG_TIME_THRESHOLD) {
335                 PROFILER_LOG_ERROR(LOG_CORE, "bigTimeCost %" PRIu64 " event=%d fpDepth=%u",
336                             curTimeCost, rawData->stackConext->type, rawData->fpDepth);
337             }
338             timeCost += curTimeCost;
339             unwindTimes++;
340             if (unwindTimes % LOG_PRINT_TIMES == 0) {
341                 PROFILER_LOG_ERROR(LOG_CORE, "unwindTimes %" PRIu64" cost time = %" PRIu64" mean cost = %" PRIu64"\n",
342                     unwindTimes.load(), timeCost.load(), timeCost.load() / unwindTimes.load());
343             }
344 #endif
345             return true;
346         });
347         if (!ret) {
348             break;
349         }
350     }
351 }
352 
TakeResults()353 void StackPreprocess::TakeResults()
354 {
355     if (!dataRepeater_) {
356         return;
357     }
358 
359     size_t minStackDepth = hookConfig_.max_stack_depth() > MIN_STACK_DEPTH
360         ? MIN_STACK_DEPTH : hookConfig_.max_stack_depth();
361     if (hookConfig_.blocked()) {
362         minStackDepth = static_cast<size_t>(hookConfig_.max_stack_depth());
363     }
364     minStackDepth += FILTER_STACK_DEPTH;
365     PROFILER_LOG_INFO(LOG_CORE, "TakeResults thread %d, start!", gettid());
366     while (1) {
367         RawStackPtr batchRawStack[MAX_BATCH_CNT] = {nullptr};
368         if (isStopTakeData_) {
369             break;
370         }
371         uint32_t during = 0;
372         if (hookConfig_.statistics_interval() > 0) {
373             auto currentTime = std::chrono::steady_clock::now();
374             auto timeDiff = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - lastStatisticsTime_);
375             int tempDuring =
376                 std::chrono::duration_cast<std::chrono::milliseconds>(statisticsInterval_).count() - timeDiff.count();
377             during = tempDuring > 0 ? static_cast<uint32_t>(tempDuring) : 0;
378         }
379         bool isTimeOut = false;
380         auto result = dataRepeater_->TakeRawData(during, hookDataClockId_, MAX_BATCH_CNT, batchRawStack,
381                                                  hookConfig_.statistics_interval(), isTimeOut);
382         if (hookConfig_.statistics_interval() > 0 && isTimeOut && result == nullptr) {  // statistics mode
383             IntervalFlushRecordStatistics();
384             continue;
385         }
386         if (!result) {
387             break;
388         }
389         for (unsigned int i = 0; i < MAX_BATCH_CNT; i++) {
390             auto rawData = batchRawStack[i];
391             if (!rawData || isStopTakeData_) {
392                 break;
393             }
394             if (rawData->baseStackData == nullptr) {
395                 if (rawData->freeData) {
396                     SetFreeStatisticsData(rawData->freeData);
397                 }
398                 continue;
399             }
400             if (rawData->stackConext == nullptr) {
401                 PROFILER_LOG_ERROR(LOG_CORE, "StackPreprocess take results rawData->stackConext is nullptr");
402                 continue;
403             }
404             if (rawData->stackConext->type == NMD_MSG) {
405                 continue;
406             } else if (rawData->stackConext->type == END_MSG) {
407                 isStopTakeData_ = true;
408                 break;
409             }
410 #ifdef PERFORMANCE_DEBUG
411             struct timespec start = {};
412             clock_gettime(CLOCK_REALTIME, &start);
413 #endif
414             if (HandleNoStackEvent(rawData)) {
415                 continue;
416             } else if (rawData->stackConext->type == MUNMAP_MSG) {
417                 std::lock_guard<std::mutex> guard(mtx_);
418                 runtime_instance->RemoveMaps(reinterpret_cast<uint64_t>(rawData->stackConext->addr));
419             }
420 
421             if (!rawData->reportFlag) {
422                 ignoreCnts_++;
423                 if (ignoreCnts_ % LOG_PRINT_TIMES == 0) {
424                     PROFILER_LOG_INFO(LOG_CORE, "ignoreCnts_ = %d quene size = %zu\n",
425                                       ignoreCnts_, dataRepeater_->Size());
426                 }
427                 continue;
428             }
429             eventCnts_++;
430             if (eventCnts_ % LOG_PRINT_TIMES == 0) {
431                 PROFILER_LOG_INFO(LOG_CORE, "eventCnts_ = %d quene size = %zu\n", eventCnts_, dataRepeater_->Size());
432             }
433             callFrames_.clear();
434             if (hookConfig_.fp_unwind()) {
435                 FillFpNativeIp(rawData);
436                 if (rawData->stackConext->jsChainId > 0 && rawData->jsStackData && hookConfig_.js_stack_report() > 0) {
437                     FillFpJsData(rawData);
438                 }
439             } else if (rawData->stackConext->type != PR_SET_VMA_MSG) {
440                 if (rawData->stackSize == 0) {
441                     FillDwarfErrorStack();
442                 } else {
443 #if defined(__arm__)
444                 uint32_t *regAddrArm = reinterpret_cast<uint32_t *>(rawData->data);
445                 u64regs_.assign(regAddrArm, regAddrArm + PERF_REG_ARM_MAX);
446 #else
447                 if (memcpy_s(u64regs_.data(), sizeof(uint64_t) * PERF_REG_ARM64_MAX, rawData->data,
448                     sizeof(uint64_t) * PERF_REG_ARM64_MAX) != EOK) {
449                     PROFILER_LOG_ERROR(LOG_CORE, "memcpy_s regs failed");
450                 }
451 #endif
452                 }
453             }
454 #ifdef PERFORMANCE_DEBUG
455             size_t realFrameDepth = callFrames_.size();
456 #endif
457             size_t stackDepth = ((size_t)hookConfig_.max_stack_depth() > MAX_CALL_FRAME_UNWIND_SIZE)
458                         ? MAX_CALL_FRAME_UNWIND_SIZE
459                         : hookConfig_.max_stack_depth() + FILTER_STACK_DEPTH;
460             if (rawData->reduceStackFlag) {
461                 stackDepth = minStackDepth;
462             }
463             if ((hookConfig_.fp_unwind()) || rawData->stackSize > 0) {
464                 std::lock_guard<std::mutex> guard(mtx_);
465                 if (rawData->stackConext->type != PR_SET_VMA_MSG) {
466                     bool ret = runtime_instance->UnwindStack(u64regs_, rawData->stackData, rawData->stackSize,
467                     rawData->stackConext->pid, rawData->stackConext->tid, callFrames_, stackDepth);
468                     if (!ret) {
469                         PROFILER_LOG_ERROR(LOG_CORE, "unwind fatal error");
470                         continue;
471                     }
472                 }
473             }
474             if ((hookConfig_.fp_unwind()) || rawData->stackSize > 0) {
475                 ReportOfflineSymbolizationData();
476             }
477             std::visit([&](auto& stackData) {
478                 if (hookConfig_.save_file() && hookConfig_.file_name() != "" && isHookStandaloneSerialize_) {
479                     SetHookData(rawData, callFrames_, stackData);
480                 } else if (hookConfig_.save_file() && hookConfig_.file_name() != "") {
481                     WriteFrames(rawData, callFrames_);
482                 } else if (!hookConfig_.save_file()) {
483                     if (hookConfig_.malloc_free_matching_interval() > 0) {
484                         SetApplyAndReleaseMatchFrame(rawData, callFrames_, stackData);
485                     } else {
486                         SetHookData(rawData, callFrames_, stackData);
487                     }
488                 }
489                 }, stackData_);
490 
491 #ifdef PERFORMANCE_DEBUG
492             struct timespec end = {};
493             clock_gettime(CLOCK_REALTIME, &end);
494             uint64_t curTimeCost = (end.tv_sec - start.tv_sec) * MAX_MATCH_CNT * MAX_MATCH_CNT * MAX_MATCH_CNT +
495                 (end.tv_nsec - start.tv_nsec);
496             if (curTimeCost >= LONG_TIME_THRESHOLD) {
497                 PROFILER_LOG_ERROR(LOG_CORE, "bigTimeCost %" PRIu64 " event=%d, realFrameDepth=%zu, "
498                     "callFramesDepth=%zu\n",
499                     curTimeCost, rawData->stackConext->type, realFrameDepth, callFrames_.size());
500             }
501             timeCost += curTimeCost;
502             unwindTimes++;
503             if (unwindTimes % LOG_PRINT_TIMES == 0) {
504                 PROFILER_LOG_ERROR(LOG_CORE, "unwindTimes %" PRIu64" cost time = %" PRIu64" mean cost = %" PRIu64"\n",
505                     unwindTimes.load(), timeCost.load(), timeCost.load() / unwindTimes.load());
506             }
507 #endif
508         } // for
509         for (unsigned int i = 0; i < MAX_BATCH_CNT; i++) {
510             if (!batchRawStack[i]) {
511                 break;
512             }
513             dataRepeater_->ReturnRawStack(std::move(batchRawStack[i]));
514         }
515         if (hookConfig_.save_file() && hookConfig_.file_name() != "" && !isHookStandaloneSerialize_) {
516             continue;
517         }
518         if (hookConfig_.statistics_interval() == 0) {
519             std::visit([&](auto& stackData) {
520                 FlushCheck(stackData);
521                 }, stackData_);
522         }
523         IntervalFlushRecordStatistics();
524         IntervalFlushApplyAndReleaseMatchData();
525     } // while
526     PROFILER_LOG_INFO(LOG_CORE, "TakeResults thread %d, exit!", gettid());
527 }
528 
ReportThreadNameMap(uint32_t tid, const std::string& tname)529 inline void StackPreprocess::ReportThreadNameMap(uint32_t tid, const std::string& tname)
530 {
531     std::lock_guard<std::mutex> guard(mtx_);
532     auto it = threadNameMap_.find(tid);
533     if (it == threadNameMap_.end() || it->second != tname) {
534         threadNameMap_[tid] = tname;
535         std::visit([&](auto& stackData) {
536             auto hookData = stackData.add_events();
537             auto thread = hookData->mutable_thread_name_map();
538             thread->set_id(tid);
539             thread->set_name(tname);
540             thread->set_pid(pid_);
541             FlushCheck(stackData);
542             }, stackData_);
543     }
544 }
545 
546 template <typename T>
FillOfflineCallStack(std::vector<CallFrame>& callFrames, size_t idx, T& stackData)547 inline void StackPreprocess::FillOfflineCallStack(std::vector<CallFrame>& callFrames, size_t idx, T& stackData)
548 {
549     for (; idx < callFrames.size(); ++idx) {
550         if (callFrames[idx].isJsFrame_) {
551             ReportFrameMap(callFrames[idx], stackData);
552             callStack_.push_back(callFrames[idx].callFrameId_ | JS_OFFLINE_IP_MASK);
553             continue;
554         }
555         callStack_.push_back(callFrames[idx].ip_);
556     }
557 }
558 
559 template <typename T>
FillCallStack(std::vector<CallFrame>& callFrames, size_t idx, T& stackData)560 inline void StackPreprocess::FillCallStack(std::vector<CallFrame>& callFrames, size_t idx, T& stackData)
561 {
562     for (; idx < callFrames.size(); ++idx) {
563         ReportFrameMap(callFrames[idx], stackData);
564         // for call stack id
565         callStack_.push_back(callFrames[idx].callFrameId_);
566     }
567 }
568 
FindCallStackId(std::vector<uint64_t>& callStack)569 inline uint32_t StackPreprocess::FindCallStackId(std::vector<uint64_t>& callStack)
570 {
571     if (hookConfig_.response_library_mode()) {
572         auto itStack = responseLibraryMap_.find(callStack[0]);
573         if (itStack != responseLibraryMap_.end()) {
574             return itStack->second;
575         }
576     } else {
577         auto itStack = callStackMap_.find(callStack);
578         if (itStack != callStackMap_.end()) {
579             return itStack->second;
580         }
581     }
582     return 0;
583 }
584 
585 /**
586  * @return '0' is invalid stack id, '> 0' is valid stack id
587  */
588 template <typename T>
SetCallStackMap(T& stackData)589 inline uint32_t StackPreprocess::SetCallStackMap(T& stackData)
590 {
591     uint32_t stackId = 0;
592     auto hookData = stackData.add_events();
593     auto stackmap = hookData->mutable_stack_map();
594     if (hookConfig_.response_library_mode()) {
595         stackId = responseLibraryMap_.size() + 1;
596     } else {
597         stackId = callStackMap_.size() + 1;
598     }
599     stackmap->set_id(stackId);
600     // offline symbolization use ip, other use frame_map_id
601     if (hookConfig_.offline_symbolization()) {
602         if constexpr (std::is_same<T, ProtoEncoder::BatchNativeHookData>::value) {
603             stackmap->add_ip(callStack_);
604         } else {
605             for (size_t i = 0; i < callStack_.size(); i++) {
606                 stackmap->add_ip(callStack_[i]);
607             }
608         }
609     } else {
610         if constexpr (std::is_same<T, ProtoEncoder::BatchNativeHookData>::value) {
611             stackmap->add_frame_map_id(callStack_);
612         } else {
613             for (size_t i = 0; i < callStack_.size(); i++) {
614                 stackmap->add_frame_map_id(callStack_[i]);
615             }
616         }
617     }
618     stackmap->set_pid(pid_);
619     if (hookConfig_.response_library_mode()) {
620         responseLibraryMap_[callStack_[0]] = stackId;
621     } else {
622         callStackMap_[callStack_] = stackId;
623     }
624     return stackId;
625 }
626 
627 /**
628  * @return '0' is invalid stack id, '> 0' is valid stack id
629  */
630 template <typename T>
GetCallStackId(const RawStackPtr& rawStack, std::vector<CallFrame>& callFrames, T& stackData)631 inline uint32_t StackPreprocess::GetCallStackId(const RawStackPtr& rawStack, std::vector<CallFrame>& callFrames,
632     T& stackData)
633 {
634     // ignore the first two frame if dwarf unwind
635     size_t idx = hookConfig_.fp_unwind() ? 0 : FILTER_STACK_DEPTH;
636     // if free_stack_report or munmap_stack_report is false, don't need to record.
637     if ((rawStack->stackConext->type == FREE_MSG) && !hookConfig_.free_stack_report()) {
638         return 0;
639     } else if ((rawStack->stackConext->type == MUNMAP_MSG) && !hookConfig_.munmap_stack_report()) {
640         return 0;
641     }
642     callStack_.clear();
643     bool isNapi = false;
644     if (rawStack->stackConext->type == MEMORY_USING_MSG && hookConfig_.js_stack_report() == NAPI_CALL_STACK) {
645         std::string tagName;
646         GetMemTag(rawStack->stackConext->tagId, tagName);
647         if (tagName.find("napi") != std::string::npos) {
648             callStack_.reserve(callFrames.size() + 1);  // 1 : insert a frame
649             callStack_.push_back((DWARF_NAPI_CALLBACK + napiIndex_) | JS_OFFLINE_IP_MASK);
650             isNapi = true;
651         }
652     } else {
653         callStack_.reserve(callFrames.size());
654     }
655     if (!hookConfig_.offline_symbolization()) {
656         FillCallStack(callFrames, idx, stackData);
657     } else {
658         if ((!hookConfig_.fp_unwind()) && rawStack->stackSize == 0) {
659             idx = 0;
660         }
661         FillOfflineCallStack(callFrames, idx, stackData);
662     }
663     if (isNapi) {
664         // insert a frame
665         std::string tagName;
666         GetMemTag(rawStack->stackConext->tagId, tagName);
667         FillNapiStack(tagName, callFrames, napiIndex_);
668         ReportFrameMap(callFrames.back(), stackData);
669         ++napiIndex_;
670     }
671     // return call stack id
672     std::lock_guard<std::mutex> guard(mtx_);
673     uint32_t stackId = FindCallStackId(callStack_);
674     if (stackId > 0) {
675         return stackId;
676     } else {
677         return SetCallStackMap(stackData);
678     }
679 }
680 
681 template <typename T>
SetEventFrame(const ReportEventBaseData& rawStack, T* event, uint32_t stackMapId, const std::string& type)682 void StackPreprocess::SetEventFrame(const ReportEventBaseData& rawStack,
683     T* event, uint32_t stackMapId, const std::string& type)
684 {
685     event->set_pid(pid_);
686     event->set_tid(rawStack.tid);
687     event->set_addr(rawStack.addr);
688     if constexpr (std::is_same<T, ::MmapEvent>::value || std::is_same<T, ProtoEncoder::MmapEvent>::value) {
689         event->set_type(type);
690     }
691 
692     if constexpr (!std::is_same<T, ::FreeEvent>::value && !std::is_same<T, ProtoEncoder::FreeEvent>::value) {
693         auto size = static_cast<uint64_t>(rawStack.mallocSize);
694 #ifdef USE_JEMALLOC
695     if constexpr (std::is_same<T, ::AllocEvent>::value || std::is_same<T, ProtoEncoder::AllocEvent>::value) {
696         size = static_cast<uint64_t>(ComputeAlign(size));
697     }
698 #endif
699         event->set_size(size);
700     }
701     if (hookConfig_.callframe_compress() && stackMapId != 0) {
702         event->set_thread_name_id(rawStack.tid);
703         event->set_stack_id(stackMapId);
704     }
705     event->set_thread_name_id(rawStack.tid);
706 }
707 
708 template <typename T>
SetEventFrame(const RawStackPtr& rawStack, std::vector<CallFrame>& callFrames, T* event, uint32_t stackMapId, const std::string& type)709 void StackPreprocess::SetEventFrame(const RawStackPtr& rawStack, std::vector<CallFrame>& callFrames,
710     T* event, uint32_t stackMapId, const std::string& type)
711 {
712     // ignore the first two frame if dwarf unwind
713     size_t idx = hookConfig_.fp_unwind() ? 0 : FILTER_STACK_DEPTH;
714     event->set_pid(rawStack->stackConext->pid);
715     event->set_tid(rawStack->stackConext->tid);
716     event->set_addr((uint64_t)rawStack->stackConext->addr);
717 
718     if constexpr (std::is_same<T, ::MmapEvent>::value || std::is_same<T, ProtoEncoder::MmapEvent>::value) {
719         event->set_type(type);
720     }
721 
722     if constexpr (!std::is_same<T, ::FreeEvent>::value && !std::is_same<T, ProtoEncoder::FreeEvent>::value) {
723         auto size = static_cast<uint64_t>(rawStack->stackConext->mallocSize);
724 #ifdef USE_JEMALLOC
725     if constexpr (std::is_same<T, ::AllocEvent>::value || std::is_same<T, ProtoEncoder::AllocEvent>::value) {
726         size = static_cast<uint64_t>(ComputeAlign(size));
727     }
728 #endif
729         event->set_size(size);
730     }
731 
732     if (hookConfig_.callframe_compress() && stackMapId != 0) {
733         event->set_thread_name_id(rawStack->stackConext->tid);
734         event->set_stack_id(stackMapId);
735     } else {
736         for (; idx < callFrames.size(); ++idx) {
737             auto frame = event->add_frame_info();
738             SetFrameInfo(*frame, callFrames[idx]);
739         }
740         event->set_thread_name_id(rawStack->stackConext->tid);
741     }
742 }
743 
FillNapiStack(std::string& tagName, std::vector<CallFrame>& callFrames, uint64_t napiIndex)744 void StackPreprocess::FillNapiStack(std::string& tagName, std::vector<CallFrame>& callFrames, uint64_t napiIndex)
745 {
746     #if defined(__aarch64__)
747     uintptr_t pacMask = 0xFFFFFF8000000000;
748 #else
749     uintptr_t pacMask = 0;
750 #endif
751     CallFrame& jsCallFrame = callFrames_.emplace_back(0 & (~pacMask));
752     jsCallFrame.symbolName_ = tagName;
753     jsCallFrame.isJsFrame_ = true;
754     jsCallFrame.needReport_ |= CALL_FRAME_REPORT;
755     jsCallFrame.needReport_ |= SYMBOL_NAME_ID_REPORT;
756     jsCallFrame.needReport_ |= FILE_PATH_ID_REPORT;
757     jsCallFrame.callFrameId_ = DWARF_NAPI_CALLBACK + napiIndex;
758     jsCallFrame.symbolNameId_ = DWARF_NAPI_CALLBACK + napiIndex;
759     jsCallFrame.filePathId_ = DWARF_NAPI_CALLBACK + napiIndex;
760     jsCallFrame.filePath_ = "no-napi-file-path";
761 }
762 
763 template <typename T>
SetAllocStatisticsFrame(const RawStackPtr& rawStack, std::vector<CallFrame>& callFrames, T& stackData)764 void StackPreprocess::SetAllocStatisticsFrame(const RawStackPtr& rawStack, std::vector<CallFrame>& callFrames,
765     T& stackData)
766 {
767     // ignore the first two frame if dwarf unwind
768     size_t idx = hookConfig_.fp_unwind() ? 0 : FILTER_STACK_DEPTH;
769     callStack_.clear();
770     bool isNapi = false;
771     if (hookConfig_.js_stack_report() == NAPI_CALL_STACK) {
772         std::string tagName;
773         GetMemTag(rawStack->stackConext->tagId, tagName);
774         if (tagName.find("napi") != std::string::npos) {
775             callStack_.reserve(callFrames.size() + FRAME_DEPTH);  // insert a frame
776             callStack_.push_back((DWARF_NAPI_CALLBACK + napiIndex_) | JS_OFFLINE_IP_MASK);
777             isNapi = true;
778         }
779     } else {
780         callStack_.reserve(callFrames.size() + 1);
781     }
782     callStack_.push_back(rawStack->stackConext->mallocSize | SIZE_MASK);
783     if (!hookConfig_.offline_symbolization()) {
784         FillCallStack(callFrames, idx, stackData);
785     } else {
786         FillOfflineCallStack(callFrames, idx, stackData);
787     }
788     // insert a frame
789     if (isNapi) {
790         std::string tagName;
791         GetMemTag(rawStack->stackConext->tagId, tagName);
792         FillNapiStack(tagName, callFrames, napiIndex_);
793         ReportFrameMap(callFrames.back(), stackData);
794         ++napiIndex_;
795     }
796     std::lock_guard<std::mutex> guard(mtx_);
797     // by call stack id set alloc statistics data.
798     uint32_t stackId = FindCallStackId(callStack_);
799     if (stackId > 0) {
800         SetAllocStatisticsData(rawStack, stackId, true);
801     } else {
802         stackId = SetCallStackMap(stackData);
803         statisticsModelFlushCallstack_ = true;
804         SetAllocStatisticsData(rawStack, stackId);
805     }
806 }
807 
808 template <typename T>
SetAllocStatisticsFrame(const RawStackPtr& rawStack, T& stackData)809 void StackPreprocess::SetAllocStatisticsFrame(const RawStackPtr& rawStack, T& stackData)
810 {
811     callStack_.resize(rawStack->fpDepth + 1);
812     callStack_[0] = (rawStack->stackConext->mallocSize | SIZE_MASK);
813     if (memcpy_s(callStack_.data() + 1, sizeof(uint64_t) * rawStack->fpDepth,
814                  rawStack->data, sizeof(uint64_t) * rawStack->fpDepth) != EOK) {
815         PROFILER_LOG_ERROR(LOG_CORE, "memcpy_s callStack_ failed");
816         return;
817     }
818     std::lock_guard<std::mutex> guard(mtx_);
819     // by call stack id set alloc statistics data.
820     uint32_t stackId = FindCallStackId(callStack_);
821     if (stackId > 0) {
822         SetAllocStatisticsData(rawStack, stackId, true);
823     } else {
824         stackId = SetCallStackMap(stackData);
825         statisticsModelFlushCallstack_ = true;
826         SetAllocStatisticsData(rawStack, stackId);
827     }
828 }
829 
830 template <typename T>
SetHookData(RawStackPtr rawStack, T& stackData)831 void StackPreprocess::SetHookData(RawStackPtr rawStack, T& stackData)
832 {
833     if (hookConfig_.statistics_interval() > 0) {
834         // statistical reporting must is compressed and accurate.
835         switch (rawStack->stackConext->type) {
836             case FREE_MSG:
837             case MUNMAP_MSG:
838             case MEMORY_UNUSING_MSG: {
839                 SetFreeStatisticsData((uint64_t)rawStack->stackConext->addr);
840                 break;
841             }
842             case MALLOC_MSG:
843                 rawStack->stackConext->mallocSize = ComputeAlign(rawStack->stackConext->mallocSize);
844             case MMAP_MSG:
845             case MMAP_FILE_PAGE_MSG:
846             case MEMORY_USING_MSG: {
847                 SetAllocStatisticsFrame(rawStack, stackData);
848                 break;
849             }
850             case PR_SET_VMA_MSG: {
851                 break;
852             }
853             default: {
854                 PROFILER_LOG_ERROR(LOG_CORE, "statistics event type: error");
855                 break;
856             }
857         }
858         return;
859     }
860 }
861 
ReportOfflineSymbolizationData()862 void StackPreprocess::ReportOfflineSymbolizationData()
863 {
864     if (hookConfig_.offline_symbolization() && flushBasicData_) {
865         SetMapsInfo();
866         flushBasicData_ = false;
867     }
868 }
869 
870 template <typename T>
SetApplyAndReleaseMatchFrame(RawStackPtr rawStack, std::vector<CallFrame>& callFrames, T& stackData)871 void StackPreprocess::SetApplyAndReleaseMatchFrame(RawStackPtr rawStack, std::vector<CallFrame>& callFrames,
872                                                    T& stackData)
873 {
874     uint32_t stackMapId = 0;
875     if (rawStack->stackConext->type != PR_SET_VMA_MSG) {
876         stackMapId = GetCallStackId(rawStack, callFrames, stackData);
877     } else {
878         rawStack->stackConext->tagId = prctlPeriodTags_.size();
879         prctlPeriodTags_.emplace_back(reinterpret_cast<char*>(rawStack->data));
880         applyAndReleaseMatchPeriodListData_.emplace_back(rawStack->stackConext);
881     }
882     if (rawStack->stackConext->type == MALLOC_MSG) {
883         rawStack->stackConext->mallocSize = ComputeAlign(rawStack->stackConext->mallocSize);
884     } else if (rawStack->stackConext->type == PR_SET_VMA_MSG) {
885         return;
886     }
887     uint64_t addr = reinterpret_cast<uint64_t>(rawStack->stackConext->addr);
888     auto iter = applyAndReleaseMatchIntervallMap_.find(addr);
889     if (iter != applyAndReleaseMatchIntervallMap_.end()) {
890         applyAndReleaseMatchPeriodListData_.erase(iter->second);
891         applyAndReleaseMatchIntervallMap_.erase(addr);
892     } else {
893         applyAndReleaseMatchPeriodListData_.emplace_back(rawStack->stackConext, stackMapId);
894         applyAndReleaseMatchIntervallMap_.emplace(addr, std::prev(applyAndReleaseMatchPeriodListData_.end()));
895     }
896 }
897 
898 template <typename T>
SetHookData(RawStackPtr rawStack, std::vector<CallFrame>& callFrames, T& stackData)899 void StackPreprocess::SetHookData(RawStackPtr rawStack, std::vector<CallFrame>& callFrames, T& stackData)
900 {
901     // statistical reporting must is compressed and accurate.
902     if (hookConfig_.statistics_interval() > 0) {
903         switch (rawStack->stackConext->type) {
904             case FREE_MSG:
905             case MUNMAP_MSG:
906             case MEMORY_UNUSING_MSG: {
907                 SetFreeStatisticsData((uint64_t)rawStack->stackConext->addr);
908                 break;
909             }
910             case MALLOC_MSG:
911                 rawStack->stackConext->mallocSize = ComputeAlign(rawStack->stackConext->mallocSize);
912             case MMAP_MSG:
913             case MMAP_FILE_PAGE_MSG:
914             case MEMORY_USING_MSG: {
915                 SetAllocStatisticsFrame(rawStack, callFrames, stackData);
916                 break;
917             }
918             case PR_SET_VMA_MSG: {
919                 break;
920             }
921             default: {
922                 PROFILER_LOG_ERROR(LOG_CORE, "statistics event type:%d error", rawStack->stackConext->type);
923                 break;
924             }
925         }
926         return;
927     }
928 
929     uint32_t stackMapId = 0;
930     if (hookConfig_.callframe_compress() &&
931         !(rawStack->stackConext->type == MEMORY_TAG || rawStack->stackConext->type == PR_SET_VMA_MSG)) {
932         stackMapId = GetCallStackId(rawStack, callFrames, stackData);
933     }
934 
935     if ((!hookConfig_.callframe_compress() || stackMapId == 0) && hookConfig_.string_compressed()) {
936         size_t idx = hookConfig_.fp_unwind() ? 0 : FILTER_STACK_DEPTH;
937         for (; idx < callFrames.size(); ++idx) {
938             ReportSymbolNameMap(callFrames[idx], stackData);
939             ReportFilePathMap(callFrames[idx], stackData);
940         }
941     }
942 
943     auto hookData = stackData.add_events();
944     hookData->set_tv_sec(rawStack->stackConext->ts.tv_sec);
945     hookData->set_tv_nsec(rawStack->stackConext->ts.tv_nsec);
946 
947     if (rawStack->stackConext->type == MALLOC_MSG) {
948         auto allocEvent = hookData->mutable_alloc_event();
949         SetEventFrame(rawStack, callFrames, allocEvent, stackMapId);
950     } else if (rawStack->stackConext->type == FREE_MSG) {
951         auto freeEvent = hookData->mutable_free_event();
952         SetEventFrame(rawStack, callFrames, freeEvent, stackMapId);
953     } else if (rawStack->stackConext->type == MMAP_MSG) {
954         auto mmapEvent = hookData->mutable_mmap_event();
955         SetEventFrame(rawStack, callFrames, mmapEvent, stackMapId);
956     } else if (rawStack->stackConext->type == MMAP_FILE_PAGE_MSG) {
957         auto mmapEvent = hookData->mutable_mmap_event();
958         const std::string prefix = "FilePage:";
959         std::string tagName;
960         if (GetMemTag(rawStack->stackConext->tagId, tagName)) {
961             tagName = prefix + tagName;
962         }
963         SetEventFrame(rawStack, callFrames, mmapEvent, stackMapId, tagName);
964     } else if (rawStack->stackConext->type == MUNMAP_MSG) {
965         auto munmapEvent = hookData->mutable_munmap_event();
966         SetEventFrame(rawStack, callFrames, munmapEvent, stackMapId);
967     } else if (rawStack->stackConext->type == PR_SET_VMA_MSG) {
968         auto tagEvent = hookData->mutable_tag_event();
969         const std::string prefix = "Anonymous:";
970         std::string tagName(reinterpret_cast<char*>(rawStack->data));
971         tagEvent->set_addr((uint64_t)rawStack->stackConext->addr);
972         tagEvent->set_size(rawStack->stackConext->mallocSize);
973         tagEvent->set_tag(prefix + tagName);
974         tagEvent->set_pid(pid_);
975     } else if (rawStack->stackConext->type == MEMORY_USING_MSG) {
976         auto mmapEvent = hookData->mutable_mmap_event();
977         std::string tagName;
978         GetMemTag(rawStack->stackConext->tagId, tagName);
979         SetEventFrame(rawStack, callFrames, mmapEvent, stackMapId, tagName);
980     } else if (rawStack->stackConext->type == MEMORY_UNUSING_MSG) {
981         auto munmapEvent = hookData->mutable_munmap_event();
982         SetEventFrame(rawStack, callFrames, munmapEvent, stackMapId);
983     }
984 }
985 
SetFreeStatisticsData(uint64_t addr)986 inline bool StackPreprocess::SetFreeStatisticsData(uint64_t addr)
987 {
988     // through the addr lookup record
989     auto addrIter = allocAddrMap_.find(addr);
990     if (addrIter != allocAddrMap_.end()) {
991         auto& record = addrIter->second.second;
992         ++record->releaseCount;
993         record->releaseSize += addrIter->second.first;
994         statisticsPeriodData_[record->callstackId] = record;
995         allocAddrMap_.erase(addr);
996         return true;
997     }
998     return false;
999 }
1000 
SetAllocStatisticsData(const RawStackPtr& rawStack, size_t stackId, bool isExists)1001 inline void StackPreprocess::SetAllocStatisticsData(const RawStackPtr& rawStack, size_t stackId, bool isExists)
1002 {
1003     // if the record exists, it is updated.Otherwise Add
1004     if (isExists) {
1005         auto recordIter = recordStatisticsMap_.find(stackId);
1006         if (recordIter != recordStatisticsMap_.end()) {
1007             auto& record = recordIter->second;
1008             ++record.applyCount;
1009             record.applySize += rawStack->stackConext->mallocSize;
1010             allocAddrMap_[(uint64_t)rawStack->stackConext->addr] =
1011                 std::pair(rawStack->stackConext->mallocSize, &recordIter->second);
1012             statisticsPeriodData_[stackId] = &recordIter->second;
1013         }
1014     } else {
1015         RecordStatistic record;
1016         record.pid = rawStack->stackConext->pid;
1017         record.callstackId = stackId;
1018         record.applyCount = 1;
1019         record.applySize = rawStack->stackConext->mallocSize;
1020         switch (rawStack->stackConext->type) {
1021             case MALLOC_MSG: {
1022                 record.type = RecordStatisticsEvent::MALLOC;
1023                 break;
1024             }
1025             case MMAP_MSG: {
1026                 record.type = RecordStatisticsEvent::MMAP;
1027                 break;
1028             }
1029             case MMAP_FILE_PAGE_MSG: {
1030                 record.type = RecordStatisticsEvent::FILE_PAGE_MSG;
1031                 break;
1032             }
1033             case MEMORY_USING_MSG: {
1034                 record.type = RecordStatisticsEvent::MEMORY_USING_MSG;
1035                 record.tagId = rawStack->stackConext->tagId;
1036                 break;
1037             }
1038             default: {
1039                 PROFILER_LOG_ERROR(LOG_CORE, "SetAllocStatisticsData event type error");
1040                 break;
1041             }
1042         }
1043 
1044         auto [recordIter, stat] = recordStatisticsMap_.emplace(stackId, record);
1045         allocAddrMap_[(uint64_t)rawStack->stackConext->addr] =
1046             std::pair(rawStack->stackConext->mallocSize, &recordIter->second);
1047         statisticsPeriodData_[stackId] = &recordIter->second;
1048     }
1049 }
1050 
WriteFrames(RawStackPtr rawStack, const std::vector<CallFrame>& callFrames)1051 void StackPreprocess::WriteFrames(RawStackPtr rawStack, const std::vector<CallFrame>& callFrames)
1052 {
1053     CHECK_TRUE(fpHookData_ != nullptr, NO_RETVAL, "fpHookData_ is nullptr, please check file_name(%s)",
1054         hookConfig_.file_name().c_str());
1055     if (rawStack->stackConext->type == PR_SET_VMA_MSG) {
1056         const std::string prefix = "Anonymous:";
1057         std::string tagName(reinterpret_cast<char*>(rawStack->data));
1058         fprintf(fpHookData_, "prctl;%u;%u;%" PRId64 ";%ld;0x%" PRIx64 ":tag:%s\n",
1059             rawStack->stackConext->pid, rawStack->stackConext->tid,
1060             (int64_t)rawStack->stackConext->ts.tv_sec, rawStack->stackConext->ts.tv_nsec,
1061             (uint64_t)rawStack->stackConext->addr, (prefix + tagName).c_str());
1062         return;
1063     }
1064     std::string tag = "";
1065     switch (rawStack->stackConext->type) {
1066         case FREE_MSG:
1067             tag = "free";
1068             break;
1069         case MALLOC_MSG:
1070             tag = "malloc";
1071             break;
1072         case MMAP_MSG:
1073             tag = "mmap";
1074             break;
1075         case MUNMAP_MSG:
1076             tag = "munmap";
1077             break;
1078         default:
1079             break;
1080     }
1081 
1082     fprintf(fpHookData_, "%s;%u;%u;%" PRId64 ";%ld;0x%" PRIx64 ";%zu\n", tag.c_str(),
1083         rawStack->stackConext->pid, rawStack->stackConext->tid, (int64_t)rawStack->stackConext->ts.tv_sec,
1084         rawStack->stackConext->ts.tv_nsec, (uint64_t)rawStack->stackConext->addr, rawStack->stackConext->mallocSize);
1085     size_t idx = hookConfig_.fp_unwind() ? 0 : FILTER_STACK_DEPTH;
1086     for (; idx < callFrames.size(); ++idx) {
1087         (void)fprintf(fpHookData_, "0x%" PRIx64 ";0x%" PRIx64 ";%s;%s;0x%" PRIx64 ";%" PRIu64 "\n",
1088             callFrames[idx].ip_, callFrames[idx].sp_, std::string(callFrames[idx].symbolName_).c_str(),
1089             std::string(callFrames[idx].filePath_).c_str(), callFrames[idx].offset_, callFrames[idx].symbolOffset_);
1090     }
1091 }
1092 
1093 template <typename T>
SetFrameInfo(T& frame, CallFrame& callFrame)1094 inline void StackPreprocess::SetFrameInfo(T& frame, CallFrame& callFrame)
1095 {
1096     frame.set_ip(callFrame.ip_);
1097     if (hookConfig_.offline_symbolization()) {
1098         // when js mixes offline symbols, the js call stack is reported according to the online symbolization
1099         if (callFrame.isJsFrame_ && callFrame.symbolNameId_ != 0 && callFrame.filePathId_ != 0) {
1100             frame.set_sp(callFrame.sp_);
1101             frame.set_offset(callFrame.offset_);
1102             frame.set_symbol_offset(callFrame.symbolOffset_);
1103             frame.set_symbol_name_id(callFrame.symbolNameId_);
1104             frame.set_file_path_id(callFrame.filePathId_);
1105         }
1106         return;
1107     }
1108     frame.set_sp(callFrame.sp_);
1109     if (!(callFrame.symbolNameId_ != 0 && callFrame.filePathId_ != 0)) {
1110         frame.set_symbol_name(std::string(callFrame.symbolName_));
1111         frame.set_file_path(std::string(callFrame.filePath_));
1112     }
1113     frame.set_offset(callFrame.offset_);
1114     frame.set_symbol_offset(callFrame.symbolOffset_);
1115     if (callFrame.symbolNameId_ != 0 && callFrame.filePathId_ != 0) {
1116         frame.set_symbol_name_id(callFrame.symbolNameId_);
1117         frame.set_file_path_id(callFrame.filePathId_);
1118     }
1119 }
1120 
1121 template <typename T>
ReportSymbolNameMap(CallFrame& callFrame, T& stackData)1122 inline void StackPreprocess::ReportSymbolNameMap(CallFrame& callFrame, T& stackData)
1123 {
1124     if (callFrame.needReport_ & SYMBOL_NAME_ID_REPORT) {
1125         auto hookData = stackData.add_events();
1126         auto symbolMap = hookData->mutable_symbol_name();
1127         symbolMap->set_id(callFrame.symbolNameId_);
1128         symbolMap->set_name(std::string(callFrame.symbolName_));
1129         symbolMap->set_pid(pid_);
1130     }
1131 }
1132 
1133 template <typename T>
ReportFilePathMap(CallFrame& callFrame, T& stackData)1134 inline void StackPreprocess::ReportFilePathMap(CallFrame& callFrame, T& stackData)
1135 {
1136     if (callFrame.needReport_ & FILE_PATH_ID_REPORT) {
1137         auto hookData = stackData.add_events();
1138         auto filePathMap = hookData->mutable_file_path();
1139         filePathMap->set_id(callFrame.filePathId_);
1140         filePathMap->set_name(std::string(callFrame.filePath_));
1141         filePathMap->set_pid(pid_);
1142     }
1143 }
1144 
1145 template <typename T>
ReportFrameMap(CallFrame& callFrame, T& stackData)1146 inline void StackPreprocess::ReportFrameMap(CallFrame& callFrame, T& stackData)
1147 {
1148     if (callFrame.needReport_ & CALL_FRAME_REPORT) {
1149         if ((!hookConfig_.fp_unwind()) && callFrame.callFrameId_ == DWARF_ERROR_ID && !unwindFailReport_) {
1150             return;
1151         } else if ((!hookConfig_.fp_unwind()) && callFrame.callFrameId_ == DWARF_ERROR_ID && unwindFailReport_) {
1152             unwindFailReport_ = false;
1153         }
1154         ReportSymbolNameMap(callFrame, stackData);
1155         ReportFilePathMap(callFrame, stackData);
1156         auto hookData = stackData.add_events();
1157         auto frameMap = hookData->mutable_frame_map();
1158         frameMap->set_id(callFrame.callFrameId_);
1159         auto frame = frameMap->mutable_frame();
1160         SetFrameInfo(*frame, callFrame);
1161         frameMap->set_pid(pid_);
1162     }
1163 }
1164 
SetMapsInfo()1165 void StackPreprocess::SetMapsInfo()
1166 {
1167     std::lock_guard<std::mutex> guard(mtx_);
1168     for (auto& itemSoBegin : runtime_instance->GetOfflineMaps()) {
1169         auto& maps = runtime_instance->GetMapsCache();
1170         auto mapsIter = maps.find(itemSoBegin);
1171         if (mapsIter == maps.end()) {
1172             continue;
1173         }
1174 
1175         ElfSymbolTable symbolInfo;
1176         auto& curMemMaps = mapsIter->second;
1177         GetSymbols(curMemMaps->name_, symbolInfo);
1178         if (symbolInfo.symEntSize == 0) {
1179             continue;
1180         }
1181         std::visit([&](auto& stackData) {
1182             auto hookData = stackData.add_events();
1183             auto filepathMap = hookData->mutable_file_path();
1184             filepathMap->set_id(curMemMaps->filePathId_);
1185             filepathMap->set_name(curMemMaps->name_);
1186             filepathMap->set_pid(pid_);
1187             SetSymbolInfo(curMemMaps->filePathId_, symbolInfo, stackData);
1188 
1189             for (auto& map : curMemMaps->GetMaps()) {
1190                 if (map->prots & PROT_EXEC) {
1191                     auto nativeHookData = stackData.add_events();
1192                     auto mapSerialize = nativeHookData->mutable_maps_info();
1193                     mapSerialize->set_pid(pid_);
1194                     mapSerialize->set_start(map->begin);
1195                     mapSerialize->set_end(map->end);
1196                     mapSerialize->set_offset(map->offset);
1197                     mapSerialize->set_file_path_id(curMemMaps->filePathId_);
1198                 }
1199             }
1200             FlushData(stackData);
1201             }, stackData_);
1202     }
1203     runtime_instance->ClearOfflineMaps();
1204 }
1205 
1206 template <typename T>
SetSymbolInfo(uint32_t filePathId, ElfSymbolTable& symbolInfo, T& batchNativeHookData)1207 void StackPreprocess::SetSymbolInfo(uint32_t filePathId, ElfSymbolTable& symbolInfo, T& batchNativeHookData)
1208 {
1209     if (symbolInfo.symEntSize == 0) {
1210         PROFILER_LOG_ERROR(LOG_CORE, "SetSymbolInfo get symbolInfo failed");
1211         return;
1212     }
1213     auto hookData = batchNativeHookData.add_events();
1214     auto symTable = hookData->mutable_symbol_tab();
1215     symTable->set_file_path_id(filePathId);
1216     symTable->set_text_exec_vaddr(symbolInfo.textVaddr);
1217     symTable->set_text_exec_vaddr_file_offset(symbolInfo.textOffset);
1218     symTable->set_sym_entry_size(symbolInfo.symEntSize);
1219     symTable->set_sym_table(symbolInfo.symTable.data(), symbolInfo.symTable.size());
1220     symTable->set_str_table(symbolInfo.strTable.data(), symbolInfo.strTable.size());
1221     symTable->set_pid(pid_);
1222 }
1223 
1224 template <typename T>
FlushCheck(T& stackData)1225 void StackPreprocess::FlushCheck(T& stackData)
1226 {
1227     if (hookConfig_.statistics_interval() > 0) {
1228         if (!statisticsModelFlushCallstack_) {
1229             return;
1230         }
1231         if constexpr (std::is_same<T, ::BatchNativeHookData>::value) {
1232             FlushData(stackData);
1233         } else {
1234             uint64_t dataLen = static_cast<uint64_t>(stackData.Size());
1235             if (dataLen > flushSize_) {
1236                 FlushData(stackData);
1237             }
1238         }
1239         statisticsModelFlushCallstack_ = false;
1240     } else {
1241         FlushData(stackData);
1242     }
1243 }
1244 
FlushData(BatchNativeHookData& stackData)1245 void StackPreprocess::FlushData(BatchNativeHookData& stackData)
1246 {
1247     if (buffer_ == nullptr) {
1248         return;
1249     }
1250     if (stackData.events().size() > 0) {
1251         size_t length = stackData.ByteSizeLong();
1252         stackData.SerializeToArray(buffer_.get(), length);
1253         if (length < bufferSize_) {
1254             if (isHookStandaloneSerialize_) {
1255                 std::string str;
1256                 ForStandard::BatchNativeHookData StandardStackData;
1257                 StandardStackData.ParseFromArray(buffer_.get(), length);
1258                 google::protobuf::TextFormat::PrintToString(StandardStackData, &str);
1259                 size_t n = fwrite(str.data(), 1, str.size(), fpHookData_);
1260                 fflush(fpHookData_);
1261                 std::get<::BatchNativeHookData>(stackData_).clear_events();
1262                 PROFILER_LOG_DEBUG(LOG_CORE, "Flush Data fwrite n = %zu str.size() = %zu", n, str.size());
1263             } else {
1264                 Flush(buffer_.get(), length);
1265             }
1266         } else {
1267             PROFILER_LOG_ERROR(LOG_CORE, "the data is larger than MAX_BUFFER_SIZE, flush failed");
1268         }
1269     }
1270 }
1271 
FlushData(ProtoEncoder::BatchNativeHookData& stackData)1272 void StackPreprocess::FlushData(ProtoEncoder::BatchNativeHookData& stackData)
1273 {
1274     if (stackData.Size() == 0) {
1275         return;
1276     }
1277 
1278     int messageLen = stackData.Finish();
1279     RandomWriteCtx* ctx = nullptr;
1280     if (!isSaService_) {
1281         resultWriter_->finishReport(resultWriter_, messageLen);
1282         resultWriter_->flush(resultWriter_);
1283         ctx = resultWriter_->startReport(resultWriter_);
1284     } else {
1285         profilerPluginData_.finishAdd_data(messageLen);
1286         FinishReport();
1287         ctx = StartReport();
1288     }
1289 
1290     if (ctx == nullptr) {
1291         PROFILER_LOG_ERROR(LOG_CORE, "%s: get RandomWriteCtx FAILED!", __func__);
1292         return;
1293     }
1294     stackData_ = ProtoEncoder::BatchNativeHookData(ctx);
1295 }
1296 
Flush(const uint8_t* src, size_t size)1297 void StackPreprocess::Flush(const uint8_t* src, size_t size)
1298 {
1299     if (src == nullptr) {
1300         PROFILER_LOG_ERROR(LOG_CORE, "Flush src is nullptr");
1301         return;
1302     }
1303 
1304     if (writer_ == nullptr) {
1305         PROFILER_LOG_ERROR(LOG_CORE, "Flush writer_ is nullptr");
1306         return;
1307     }
1308     writer_->Write(src, size);
1309     writer_->Flush();
1310 
1311     std::get<::BatchNativeHookData>(stackData_).clear_events();
1312 }
1313 
GetSymbols(const std::string& filePath, ElfSymbolTable& symbols)1314 void StackPreprocess::GetSymbols(const std::string& filePath, ElfSymbolTable& symbols)
1315 {
1316     std::shared_ptr<DfxElf> elfPtr = std::make_shared<DfxElf>(filePath);
1317 
1318     symbols.textVaddr = elfPtr->GetStartVaddr();
1319     symbols.textOffset = elfPtr->GetStartOffset();
1320     if (symbols.textVaddr == (std::numeric_limits<uint64_t>::max)()) {
1321         PROFILER_LOG_ERROR(LOG_CORE, "GetSymbols get textVaddr failed");
1322         return;
1323     }
1324 
1325     std::string symSecName;
1326     std::string strSecName;
1327     ShdrInfo shdr;
1328     if (elfPtr->GetSectionInfo(shdr, ".symtab")) {
1329         symSecName = ".symtab";
1330         strSecName = ".strtab";
1331     } else if (elfPtr->GetSectionInfo(shdr, ".dynsym")) {
1332         symSecName = ".dynsym";
1333         strSecName = ".dynstr";
1334     } else {
1335         return;
1336     }
1337     symbols.symEntSize = shdr.entSize;
1338     symbols.symTable.resize(shdr.size);
1339     if (!elfPtr->GetSectionData(symbols.symTable.data(), shdr.size, symSecName)) {
1340         PROFILER_LOG_ERROR(LOG_CORE, "GetSymbols get symbol section data failed");
1341         return;
1342     }
1343     if (!elfPtr->GetSectionInfo(shdr, strSecName)) {
1344         PROFILER_LOG_ERROR(LOG_CORE, "GetSymbols get str section failed");
1345         return;
1346     }
1347     symbols.strTable.resize(shdr.size);
1348     if (!elfPtr->GetSectionData(symbols.strTable.data(), shdr.size, strSecName)) {
1349         PROFILER_LOG_ERROR(LOG_CORE, "GetSymbols get str section failed");
1350         return;
1351     }
1352 }
1353 
FlushRecordStatistics()1354 bool StackPreprocess::FlushRecordStatistics()
1355 {
1356     if (statisticsPeriodData_.empty()) {
1357         return false;
1358     }
1359     std::visit([&](auto& stackData) {
1360         FlushData(stackData);
1361         }, stackData_);
1362     std::visit([&](auto& stackData) {
1363         struct timespec ts;
1364         clock_gettime(hookDataClockId_, &ts);
1365         for (auto [addr, statistics] : statisticsPeriodData_) {
1366             auto hookData = stackData.add_events();
1367             hookData->set_tv_sec(ts.tv_sec);
1368             hookData->set_tv_nsec(ts.tv_nsec);
1369             auto recordEvent = hookData->mutable_statistics_event();
1370             recordEvent->set_pid(statistics->pid);
1371             recordEvent->set_callstack_id(statistics->callstackId);
1372             recordEvent->set_type(statistics->type);
1373             recordEvent->set_apply_count(statistics->applyCount);
1374             recordEvent->set_release_count(statistics->releaseCount);
1375             recordEvent->set_apply_size(statistics->applySize);
1376             recordEvent->set_release_size(statistics->releaseSize);
1377         }
1378         FlushData(stackData);
1379         }, stackData_);
1380     statisticsPeriodData_.clear();
1381     return true;
1382 }
1383 
SaveMemTag(uint32_t tagId, const std::string& tagName)1384 void StackPreprocess::SaveMemTag(uint32_t tagId, const std::string& tagName)
1385 {
1386     std::string temp;
1387     bool res = memTagMap_.Find(tagId, temp);
1388     if (!res) {
1389         memTagMap_.EnsureInsert(tagId, tagName);
1390     }
1391 }
1392 
GetMemTag(uint32_t tagId, std::string& tagName)1393 bool StackPreprocess::GetMemTag(uint32_t tagId, std::string& tagName)
1394 {
1395     return memTagMap_.Find(tagId, tagName);
1396 }
1397 
SaveJsRawStack(uint64_t jsChainId, const char* jsRawStack)1398 void StackPreprocess::SaveJsRawStack(uint64_t jsChainId, const char* jsRawStack)
1399 {
1400     auto iterChainId = jsStackMap_.find(jsChainId);
1401     if (iterChainId == jsStackMap_.end()) {
1402         auto iterRawStack = jsStackSet_.find(jsRawStack);
1403         if (iterRawStack == jsStackSet_.end()) {
1404             auto iter = jsStackSet_.insert(jsRawStack);
1405             jsStackMap_[jsChainId] = iter.first->c_str();
1406         } else {
1407             jsStackMap_[jsChainId] = iterRawStack->c_str();
1408         }
1409     }
1410 }
1411 
GetJsRawStack(uint64_t jsChainId)1412 const char* StackPreprocess::GetJsRawStack(uint64_t jsChainId)
1413 {
1414     auto iter = jsStackMap_.find(jsChainId);
1415     if (iter != jsStackMap_.end()) {
1416         return iter->second;
1417     }
1418     return nullptr;
1419 }
1420 
LgFloor(unsigned long val)1421 unsigned StackPreprocess::LgFloor(unsigned long val)
1422 {
1423     val |= (val >> RIGHT_MOVE_1);
1424     val |= (val >> RIGHT_MOVE_2);
1425     val |= (val >> RIGHT_MOVE_4);
1426     val |= (val >> RIGHT_MOVE_8);
1427     val |= (val >> RIGHT_MOVE_16);
1428     if (sizeof(val) > 4) {              // 4: sizeThreshold
1429         int constant = sizeof(val) * 4; // 4: sizeThreshold
1430         val |= (val >> constant);
1431     }
1432     val++;
1433     if (val == 0) {
1434         return 8 * sizeof(val) - 1; // 8: 8byte
1435     }
1436     return __builtin_ffsl(val) - 2; // 2: adjustment
1437 }
1438 
PowCeil(uint64_t val)1439 uint64_t StackPreprocess::PowCeil(uint64_t val)
1440 {
1441     size_t msbIndex = LgFloor(val - 1);
1442     return 1ULL << (msbIndex + 1);
1443 }
1444 
ComputeAlign(size_t size)1445 size_t StackPreprocess::ComputeAlign(size_t size)
1446 {
1447     if (size == 0) {
1448         return 0;
1449     }
1450     unsigned index = 0;
1451     if (size <= (size_t(1) << SC_LG_TINY_MAXCLASS)) {
1452         unsigned lgTmin = SC_LG_TINY_MAXCLASS - SC_NTINY + 1;
1453         unsigned lgCeil = LgFloor(PowCeil(size));
1454         index = (lgCeil < lgTmin) ? 0 : lgCeil - lgTmin;
1455     } else {
1456         unsigned floor = LgFloor((size << 1) - 1);
1457         unsigned shift = (floor < SC_LG_NGROUP + LG_QUANTUM) ? 0 : floor - (SC_LG_NGROUP + LG_QUANTUM);
1458         unsigned grp = shift << SC_LG_NGROUP;
1459         unsigned lgDelta = (floor < SC_LG_NGROUP + LG_QUANTUM + 1) ? LG_QUANTUM : floor - SC_LG_NGROUP - 1;
1460         size_t deltaInverseMask = size_t(-1) << lgDelta;
1461         unsigned mod = ((((size - 1) & deltaInverseMask) >> lgDelta)) & ((size_t(1) << SC_LG_NGROUP) - 1);
1462         index = SC_NTINY + grp + mod;
1463     }
1464 
1465     if (index < NTBINS) {
1466         return (size_t(1) << (LG_TINY_MAXCLASS - NTBINS + 1 + index));
1467     }
1468     size_t reducedIndex = index - NTBINS;
1469     size_t grpVal = reducedIndex >> LG_SIZE_CLASS_GROUP;
1470     size_t modVal = reducedIndex & ((size_t(1) << LG_SIZE_CLASS_GROUP) - 1);
1471     size_t grpSizeMask = ~((!!grpVal) - 1);
1472     size_t grpSize = ((size_t(1) << (LG_QUANTUM + (LG_SIZE_CLASS_GROUP - 1))) << grpVal) & grpSizeMask;
1473     size_t shiftVal = (grpVal == 0) ? 1 : grpVal;
1474     size_t lgDeltaVal = shiftVal + (LG_QUANTUM - 1);
1475     size_t modSize = (modVal + 1) << lgDeltaVal;
1476     size_t usize = grpSize + modSize;
1477     return usize;
1478 }
1479 
WriteHookConfig()1480 void StackPreprocess::WriteHookConfig()
1481 {
1482     const size_t configSize = hookConfig_.ByteSizeLong();
1483     auto buffer = std::make_unique<uint8_t[]>(configSize);
1484     hookConfig_.SerializeToArray(buffer.get(), configSize);
1485 
1486     writer_->ResetPos();
1487     profilerPluginData_.Reset(writer_->GetCtx());
1488     profilerPluginData_.set_name("nativehook_config");
1489     profilerPluginData_.set_version("1.02");
1490     profilerPluginData_.set_status(0);
1491     profilerPluginData_.set_data(buffer.get(), configSize);
1492 
1493     FinishReport();
1494 
1495     auto ctx = StartReport();
1496     if (ctx == nullptr) {
1497         PROFILER_LOG_ERROR(LOG_CORE, "%s: get RandomWriteCtx FAILED!", __func__);
1498         return;
1499     }
1500     stackData_ = ProtoEncoder::BatchNativeHookData(ctx);
1501 }
1502 
StartReport()1503 RandomWriteCtx* StackPreprocess::StartReport()
1504 {
1505     writer_->ResetPos();
1506     profilerPluginData_.Reset(writer_->GetCtx());
1507     profilerPluginData_.set_name("nativehook");
1508     profilerPluginData_.set_version("1.02");
1509     profilerPluginData_.set_status(0);
1510     return profilerPluginData_.startAdd_data();
1511 }
1512 
FinishReport()1513 void StackPreprocess::FinishReport()
1514 {
1515     struct timespec ts;
1516     clock_gettime(pluginDataClockId_, &ts);
1517     profilerPluginData_.set_clock_id(static_cast<ProfilerPluginData_ClockId>(pluginDataClockId_));
1518     profilerPluginData_.set_tv_sec(ts.tv_sec);
1519     profilerPluginData_.set_tv_nsec(ts.tv_nsec);
1520 
1521     int32_t len = profilerPluginData_.Finish();
1522     if (writer_ == nullptr) {
1523         PROFILER_LOG_ERROR(LOG_CORE, "%s: the writer is nullptr!", __func__);
1524         return;
1525     }
1526     writer_->FinishReport(len);
1527 }
1528 
FillFpNativeIp(RawStackPtr& rawData)1529 void StackPreprocess::FillFpNativeIp(RawStackPtr& rawData)
1530 {
1531 #if defined(__aarch64__)
1532     uintptr_t pacMask = 0xFFFFFF8000000000;
1533 #else
1534     uintptr_t pacMask = 0;
1535 #endif
1536     uint64_t* fpIp = reinterpret_cast<uint64_t *>(rawData->data);
1537     for (uint8_t idx = 0; idx < rawData->fpDepth ; ++idx) {
1538         if (fpIp[idx] == 0) {
1539             break;
1540         }
1541         callFrames_.emplace_back(fpIp[idx] & (~pacMask));
1542     }
1543 }
1544 
FillFpJsData(RawStackPtr& rawData)1545 void StackPreprocess::FillFpJsData(RawStackPtr& rawData)
1546 {
1547     if (hookConfig_.statistics_interval() > 0) {
1548         switch (rawData->stackConext->type) {
1549             case FREE_MSG:
1550             case MUNMAP_MSG:
1551             case MEMORY_UNUSING_MSG:
1552                  return;
1553             default:
1554                 break;
1555         }
1556     }
1557     fpJsCallStacks_.clear();
1558     /**
1559       *     jsStackData:
1560       *              ts_malloc1|entry/src/main/ets/pages/Index.ets:5:5,ts_malloc2|entry/src/main/ets/pages/Index.ets:8:5
1561       *                        |                                      |
1562       *                        JS_SYMBOL_FILEPATH_SEP                 JS_CALL_STACK_DEPTH_SEP
1563       *     jsCallStack:
1564       *                  ts_malloc1|entry/src/main/ets/pages/Index.ets:5:5
1565       *                            / \
1566       *                           |   |
1567       *     jsSymbolFilePathSepPos    |
1568       *                               jsFilePathPos = jsSymbolFilePathSepPos + 1
1569       */
1570     AdvancedSplitString(rawData->jsStackData, JS_CALL_STACK_DEPTH_SEP, fpJsCallStacks_);
1571     for (std::string& jsCallStack: fpJsCallStacks_) {
1572         std::string::size_type jsSymbolFilePathSepPos = jsCallStack.find_first_of(JS_SYMBOL_FILEPATH_SEP);
1573         if (jsSymbolFilePathSepPos == std::string::npos) {
1574             PROFILER_LOG_ERROR(LOG_CORE, "%s: jsCallStack find FAILED!", __func__);
1575             continue;
1576         }
1577         std::string::size_type jsFilePathPos = jsSymbolFilePathSepPos + 1;
1578         jsCallStack[jsSymbolFilePathSepPos] = '\0'; // "ts_malloc1'\0'entry/src/main/ets/pages/Index.ets:5:5"
1579         CallFrame& jsCallFrame = callFrames_.emplace_back(0, 0, true);
1580         jsCallFrame.symbolName_ = StringViewMemoryHold::GetInstance().HoldStringView(jsCallStack.c_str());
1581         jsCallFrame.filePath_ = StringViewMemoryHold::GetInstance().HoldStringView(jsCallStack.c_str() + jsFilePathPos);
1582         if (hookConfig_.offline_symbolization()) {
1583             DfxSymbol symbol;
1584             if (!runtime_instance->ArktsGetSymbolCache(jsCallFrame, symbol)) {
1585                 symbol.filePathId_ = runtime_instance->FillArkTsFilePath(jsCallFrame.filePath_);
1586                 symbol.symbolName_ = jsCallFrame.symbolName_;
1587                 symbol.module_ = jsCallFrame.filePath_;
1588                 symbol.symbolId_ = runtime_instance->GetJsSymbolCacheSize();
1589                 runtime_instance->FillSymbolNameId(jsCallFrame, symbol);
1590                 runtime_instance->FillFileSet(jsCallFrame, symbol);
1591                 jsCallFrame.needReport_ |= CALL_FRAME_REPORT;
1592                 runtime_instance->FillJsSymbolCache(jsCallFrame, symbol);
1593             }
1594             jsCallFrame.callFrameId_ = symbol.symbolId_;
1595             jsCallFrame.symbolNameId_ = symbol.symbolNameId_;
1596             jsCallFrame.filePathId_ = symbol.filePathId_;
1597             jsCallFrame.filePath_ = symbol.module_;
1598             jsCallFrame.symbolName_ = symbol.symbolName_;
1599         }
1600     }
1601 }
1602 
FillDwarfErrorStack()1603 void StackPreprocess::FillDwarfErrorStack()
1604 {
1605 #if defined(__aarch64__)
1606     uintptr_t pacMask = 0xFFFFFF8000000000;
1607 #else
1608     uintptr_t pacMask = 0;
1609 #endif
1610     CallFrame& jsCallFrame = callFrames_.emplace_back(0 & (~pacMask));
1611     jsCallFrame.symbolName_ = "UnwindErrorDwarf";
1612     jsCallFrame.isJsFrame_ = true;
1613     jsCallFrame.needReport_ |= CALL_FRAME_REPORT;
1614     jsCallFrame.needReport_ |= SYMBOL_NAME_ID_REPORT;
1615     jsCallFrame.needReport_ |= FILE_PATH_ID_REPORT;
1616     jsCallFrame.callFrameId_ = DWARF_ERROR_ID;
1617     jsCallFrame.symbolNameId_ = DWARF_ERROR_ID;
1618     jsCallFrame.filePathId_ = DWARF_ERROR_ID;
1619     jsCallFrame.filePath_ = "no-file-path";
1620 }
1621 
FlushRecordApplyAndReleaseMatchData()1622 void StackPreprocess::FlushRecordApplyAndReleaseMatchData()
1623 {
1624     if (applyAndReleaseMatchPeriodListData_.empty()) {
1625         return;
1626     }
1627     std::visit([&](auto& stackData) {
1628         for (const auto& rawStack: applyAndReleaseMatchPeriodListData_) {
1629             auto hookData = stackData.add_events();
1630             hookData->set_tv_sec(rawStack.ts.tv_sec);
1631             hookData->set_tv_nsec(rawStack.ts.tv_nsec);
1632             if (rawStack.type == MALLOC_MSG) {
1633                 auto allocEvent = hookData->mutable_alloc_event();
1634                 SetEventFrame(rawStack, allocEvent, rawStack.stackMapId);
1635             } else if (rawStack.type == FREE_MSG) {
1636                 auto freeEvent = hookData->mutable_free_event();
1637                 SetEventFrame(rawStack, freeEvent, rawStack.stackMapId);
1638             } else if (rawStack.type == MMAP_MSG) {
1639                 auto mmapEvent = hookData->mutable_mmap_event();
1640                 SetEventFrame(rawStack, mmapEvent, rawStack.stackMapId);
1641             } else if (rawStack.type == MMAP_FILE_PAGE_MSG) {
1642                 auto mmapEvent = hookData->mutable_mmap_event();
1643                 const std::string prefix = "FilePage:";
1644                 std::string tagName;
1645                 if (GetMemTag(rawStack.tagId, tagName)) {
1646                     tagName = prefix + tagName;
1647                 }
1648                 SetEventFrame(rawStack, mmapEvent, rawStack.stackMapId, tagName);
1649             } else if (rawStack.type == MUNMAP_MSG) {
1650                 auto munmapEvent = hookData->mutable_munmap_event();
1651                 SetEventFrame(rawStack, munmapEvent, rawStack.stackMapId);
1652             } else if (rawStack.type == PR_SET_VMA_MSG) {
1653                 auto tagEvent = hookData->mutable_tag_event();
1654                 const std::string prefix = "Anonymous:";
1655                 tagEvent->set_addr(rawStack.addr);
1656                 tagEvent->set_size(rawStack.mallocSize);
1657                 tagEvent->set_tag(prefix + prctlPeriodTags_[rawStack.tagId]);
1658                 tagEvent->set_pid(pid_);
1659             } else if (rawStack.type == MEMORY_USING_MSG) {
1660                 auto mmapEvent = hookData->mutable_mmap_event();
1661                 std::string tagName;
1662                 GetMemTag(rawStack.tagId, tagName);
1663                 SetEventFrame(rawStack, mmapEvent, rawStack.stackMapId, tagName);
1664             } else if (rawStack.type == MEMORY_UNUSING_MSG) {
1665                 auto munmapEvent = hookData->mutable_munmap_event();
1666                 SetEventFrame(rawStack, munmapEvent, rawStack.stackMapId);
1667             }
1668         }
1669         FlushData(stackData);
1670         }, stackData_);
1671     applyAndReleaseMatchPeriodListData_.clear();
1672     applyAndReleaseMatchIntervallMap_.clear();
1673     prctlPeriodTags_.clear();
1674 }