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 }