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