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