1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021-2023. 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 "hook_manager.h"
17
18 #include <limits>
19 #include <sys/stat.h>
20 #include <unistd.h>
21 #include <cstdlib>
22 #include "command_poller.h"
23 #include "common.h"
24 #include "epoll_event_poller.h"
25 #include "event_notifier.h"
26 #include "hook_common.h"
27 #include "hook_service.h"
28 #include "init_param.h"
29 #include "logging.h"
30 #include "plugin_service_types.pb.h"
31 #include "share_memory_allocator.h"
32 #include "utilities.h"
33 #include "virtual_runtime.h"
34 #include "native_memory_profiler_sa_service.h"
35
36 namespace OHOS::Developtools::NativeDaemon {
37 namespace {
38 constexpr int DEFAULT_EVENT_POLLING_INTERVAL = 5000;
39 constexpr uint32_t PAGE_BYTES = 4096;
40 std::shared_ptr<Writer> g_buffWriter;
41 const std::string STARTUP = "startup:";
42 const std::string PARAM_NAME = "libc.hook_mode";
43 constexpr int SIGNAL_START_HOOK = 36;
44 constexpr int SIGNAL_STOP_HOOK = 37;
45 const std::string VERSION = "1.02";
46 constexpr int32_t RESPONSE_MAX_PID_COUNT = 8;
47 constexpr int32_t MAX_PID_COUNT = 4;
48 constexpr int32_t SIMP_NMD = 3;
49 }
50
~HookManager()51 HookManager::~HookManager()
52 {
53 hookService_ = nullptr;
54 for (const auto& item : hookCtx_) {
55 if (item->eventPoller != nullptr) {
56 item->eventPoller = nullptr;
57 }
58 if (item->shareMemoryBlock != nullptr) {
59 item->shareMemoryBlock = nullptr;
60 }
61 if (item->stackPreprocess != nullptr) {
62 item->stackPreprocess = nullptr;
63 }
64 if (item->stackData != nullptr) {
65 item->stackData = nullptr;
66 }
67 }
68 }
69
CheckProcess()70 bool HookManager::CheckProcess()
71 {
72 if (hookConfig_.pid() > 0) {
73 hookConfig_.add_expand_pids(hookConfig_.pid());
74 }
75 std::set<int32_t> pidCache;
76 for (const auto& pid : hookConfig_.expand_pids()) {
77 if (pid > 0) {
78 struct stat statBuf;
79 std::string pidPath = "/proc/" + std::to_string(pid) + "/status";
80 if (stat(pidPath.c_str(), &statBuf) != 0) {
81 PROFILER_LOG_ERROR(LOG_CORE, "%s: hook process does not exist", __func__);
82 return false;
83 } else {
84 auto [iter, isExist] = pidCache.emplace(pid);
85 if (isExist) {
86 hookCtx_.emplace_back(std::make_shared<HookManagerCtx>(pid));
87 PROFILER_LOG_INFO(LOG_CORE, "hook context: pid: %d", pid);
88 }
89 continue;
90 }
91 }
92 }
93
94 if (!hookConfig_.process_name().empty() && !CheckProcessName()) {
95 return false;
96 }
97
98 if (hookConfig_.response_library_mode()) {
99 if (hookCtx_.size() > RESPONSE_MAX_PID_COUNT) {
100 PROFILER_LOG_ERROR(LOG_CORE, "%s: The maximum allowed is to set %d PIDs.",
101 __func__, RESPONSE_MAX_PID_COUNT);
102 return false;
103 }
104 } else {
105 if (hookCtx_.size() > MAX_PID_COUNT) {
106 PROFILER_LOG_ERROR(LOG_CORE, "%s: The maximum allowed is to set %d PIDs.", __func__, MAX_PID_COUNT);
107 return false;
108 }
109 }
110
111 if (hookCtx_.size() > 1) {
112 isProtobufSerialize_ = true;
113 }
114 return true;
115 }
116
CheckProcessName()117 bool HookManager::CheckProcessName()
118 {
119 int pidValue = -1;
120 const std::string processName = hookConfig_.process_name();
121 bool isExist = COMMON::IsProcessExist(processName, pidValue);
122 if (hookConfig_.startup_mode() || !isExist) {
123 PROFILER_LOG_INFO(LOG_CORE, "Wait process %s start or restart, set param", hookConfig_.process_name().c_str());
124 std::string cmd = STARTUP + hookConfig_.process_name();
125 int ret = SystemSetParameter(PARAM_NAME.c_str(), cmd.c_str());
126 if (ret < 0) {
127 PROFILER_LOG_ERROR(LOG_CORE, "set param failed, please manually set param and start process(%s)",
128 hookConfig_.process_name().c_str());
129 } else {
130 PROFILER_LOG_INFO(LOG_CORE, "set param success, please start process(%s)",
131 hookConfig_.process_name().c_str());
132 hookCtx_.emplace_back(std::make_shared<HookManagerCtx>(hookConfig_.process_name()));
133 hookConfig_.set_startup_mode(true);
134 }
135 } else if (pidValue != -1) {
136 PROFILER_LOG_INFO(LOG_CORE, "Process %s exist, pid = %d", hookConfig_.process_name().c_str(), pidValue);
137 for (const auto& item : hookCtx_) {
138 if (item->pid == pidValue) {
139 return true;
140 }
141 }
142 hookCtx_.emplace_back(std::make_shared<HookManagerCtx>(pidValue));
143 } else {
144 PROFILER_LOG_ERROR(LOG_CORE, "The startup mode parameter is not set, name: %s",
145 hookConfig_.process_name().c_str());
146 return false;
147 }
148 return true;
149 }
150
SetCommandPoller(const std::shared_ptr<CommandPoller> & p)151 void HookManager::SetCommandPoller(const std::shared_ptr<CommandPoller>& p)
152 {
153 commandPoller_ = p;
154 }
155
RegisterAgentPlugin(const std::string & pluginPath)156 bool HookManager::RegisterAgentPlugin(const std::string& pluginPath)
157 {
158 RegisterPluginRequest request;
159 request.set_request_id(commandPoller_->GetRequestId());
160 request.set_path(pluginPath);
161 request.set_sha256("");
162 request.set_name(pluginPath);
163 request.set_buffer_size_hint(0);
164 RegisterPluginResponse response;
165
166 if (commandPoller_->RegisterPlugin(request, response)) {
167 if (response.status() == ResponseStatus::OK) {
168 PROFILER_LOG_DEBUG(LOG_CORE, "response.plugin_id() = %d", response.plugin_id());
169 agentIndex_ = response.plugin_id();
170 PROFILER_LOG_DEBUG(LOG_CORE, "RegisterPlugin OK");
171 } else {
172 PROFILER_LOG_DEBUG(LOG_CORE, "RegisterPlugin FAIL 1");
173 return false;
174 }
175 } else {
176 PROFILER_LOG_DEBUG(LOG_CORE, "RegisterPlugin FAIL 2");
177 return false;
178 }
179
180 return true;
181 }
182
UnregisterAgentPlugin(const std::string & pluginPath)183 bool HookManager::UnregisterAgentPlugin(const std::string& pluginPath)
184 {
185 UnregisterPluginRequest request;
186 request.set_request_id(commandPoller_->GetRequestId());
187 request.set_plugin_id(agentIndex_);
188 UnregisterPluginResponse response;
189 if (commandPoller_->UnregisterPlugin(request, response)) {
190 CHECK_TRUE(response.status() == ResponseStatus::OK, false, "UnregisterPlugin FAIL 1");
191 } else {
192 PROFILER_LOG_DEBUG(LOG_CORE, "UnregisterPlugin FAIL 2");
193 return false;
194 }
195 agentIndex_ = -1;
196
197 return true;
198 }
199
LoadPlugin(const std::string & pluginPath)200 bool HookManager::LoadPlugin(const std::string& pluginPath)
201 {
202 return true;
203 }
204
UnloadPlugin(const std::string & pluginPath)205 bool HookManager::UnloadPlugin(const std::string& pluginPath)
206 {
207 return true;
208 }
209
UnloadPlugin(const uint32_t pluginId)210 bool HookManager::UnloadPlugin(const uint32_t pluginId)
211 {
212 return true;
213 }
214
GetClientConfig(ClientConfig & clientConfig)215 void HookManager::GetClientConfig(ClientConfig& clientConfig)
216 {
217 clientConfig.shareMemorySize = static_cast<uint32_t>(hookConfig_.smb_pages() * PAGE_BYTES);
218 clientConfig.filterSize = static_cast<int32_t>(hookConfig_.filter_size());
219 clientConfig.clockId = COMMON::GetClockId(hookConfig_.clock());
220 clientConfig.maxStackDepth = hookConfig_.max_stack_depth();
221 clientConfig.arktsConfig.maxJsStackDepth = hookConfig_.max_js_stack_depth();
222 clientConfig.mallocDisable = hookConfig_.malloc_disable();
223 clientConfig.mmapDisable = hookConfig_.mmap_disable();
224 clientConfig.freeStackData = hookConfig_.free_stack_report();
225 clientConfig.munmapStackData = hookConfig_.munmap_stack_report();
226 clientConfig.fpunwind = hookConfig_.fp_unwind();
227 clientConfig.arktsConfig.jsFpunwind = hookConfig_.fp_unwind();
228 clientConfig.isBlocked = hookConfig_.blocked();
229 clientConfig.memtraceEnable = hookConfig_.memtrace_enable();
230 clientConfig.statisticsInterval = hookConfig_.statistics_interval();
231 clientConfig.sampleInterval = hookConfig_.sample_interval();
232 clientConfig.responseLibraryMode = hookConfig_.response_library_mode();
233 clientConfig.arktsConfig.jsStackReport = hookConfig_.js_stack_report();
234 clientConfig.printNmd = printMallocNmd_;
235 clientConfig.nmdType = static_cast<int>(nmdParamInfo_.type);
236 clientConfig.largestSize = largestSize_;
237 clientConfig.secondLargestSize = secondLargestSize_;
238 clientConfig.maxGrowthSize = maxGrowthSize_;
239 // -1 is save '\0'
240 int ret = memcpy_s(clientConfig.arktsConfig.filterNapiName, sizeof(clientConfig.arktsConfig.filterNapiName) - 1,
241 hookConfig_.filter_napi_name().c_str(), hookConfig_.filter_napi_name().size());
242 if (ret != EOK) {
243 PROFILER_LOG_ERROR(LOG_CORE, "memcpy_s filter_napi_name fail");
244 }
245 }
246
HandleHookContext(const std::shared_ptr<HookManagerCtx> & ctx)247 bool HookManager::HandleHookContext(const std::shared_ptr<HookManagerCtx>& ctx)
248 {
249 if (ctx == nullptr) {
250 return false;
251 }
252 if (ctx->pid > 0) {
253 ctx->smbName = "hooknativesmb_" + std::to_string(ctx->pid);
254 } else if (!ctx->processName.empty()) {
255 ctx->smbName = "hooknativesmb_" + ctx->processName;
256 } else {
257 PROFILER_LOG_ERROR(LOG_CORE, "HandleHookContext context error, pid: %d, process name: %s",
258 ctx->pid, ctx->processName.c_str());
259 return false;
260 }
261 // create smb and eventNotifier
262 uint32_t bufferSize = static_cast<uint32_t>(hookConfig_.smb_pages()) * PAGE_BYTES; /* bufferConfig.pages() */
263 ctx->shareMemoryBlock = ShareMemoryAllocator::GetInstance().CreateMemoryBlockLocal(ctx->smbName, bufferSize);
264 CHECK_TRUE(ctx->shareMemoryBlock != nullptr, false, "CreateMemoryBlockLocal FAIL %s", ctx->smbName.c_str());
265
266 ctx->eventNotifier = EventNotifier::Create(0, EventNotifier::NONBLOCK);
267 CHECK_NOTNULL(ctx->eventNotifier, false, "create EventNotifier for %s failed!", ctx->smbName.c_str());
268
269 // start event poller task
270 ctx->eventPoller = std::make_unique<EpollEventPoller>(DEFAULT_EVENT_POLLING_INTERVAL);
271 CHECK_NOTNULL(ctx->eventPoller, false, "create event poller FAILED!");
272
273 ctx->eventPoller->Init();
274 ctx->eventPoller->Start();
275
276 PROFILER_LOG_INFO(LOG_CORE, "hookservice smbFd = %d, eventFd = %d\n", ctx->shareMemoryBlock->GetfileDescriptor(),
277 ctx->eventNotifier->GetFd());
278
279 ctx->isRecordAccurately = hookConfig_.record_accurately();
280 PROFILER_LOG_INFO(LOG_CORE, "hookConfig filter size = %d, malloc disable = %d mmap disable = %d",
281 hookConfig_.filter_size(), hookConfig_.malloc_disable(), hookConfig_.mmap_disable());
282 PROFILER_LOG_INFO(LOG_CORE, "hookConfig fp unwind = %d, max stack depth = %d, record_accurately=%d",
283 hookConfig_.fp_unwind(), hookConfig_.max_stack_depth(), ctx->isRecordAccurately);
284 PROFILER_LOG_INFO(LOG_CORE, "hookConfig offline_symbolization = %d", hookConfig_.offline_symbolization());
285 PROFILER_LOG_INFO(LOG_CORE, "hookConfig js_stack_report = %d max_js_stack_depth = %u",
286 hookConfig_.js_stack_report(), hookConfig_.max_js_stack_depth());
287
288 clockid_t pluginDataClockId = COMMON::GetClockId(hookConfig_.clock());
289 if (noDataQueue_) {
290 ctx->stackPreprocess = std::make_shared<StackPreprocess>(nullptr, hookConfig_, pluginDataClockId,
291 fpHookData_, isHookStandalone_, isSaService_, isProtobufSerialize_);
292 ctx->stackPreprocess->SetFlushSize(shareMemorySize_);
293 ctx->stackPreprocess->SetNmdFd(nmdParamInfo_.fd);
294 ctx->eventPoller->AddFileDescriptor(
295 ctx->eventNotifier->GetFd(),
296 std::bind(&StackPreprocess::TakeResultsFromShmem, ctx->stackPreprocess,
297 ctx->eventNotifier, ctx->shareMemoryBlock));
298 } else {
299 ctx->stackData = std::make_shared<StackDataRepeater>(STACK_DATA_SIZE);
300 CHECK_TRUE(ctx->stackData != nullptr, false, "Create StackDataRepeater FAIL");
301 ctx->stackPreprocess = std::make_shared<StackPreprocess>(ctx->stackData, hookConfig_, pluginDataClockId,
302 fpHookData_, isHookStandalone_, isSaService_, isProtobufSerialize_);
303 ctx->stackPreprocess->SetFlushSize(shareMemorySize_);
304 ctx->eventPoller->AddFileDescriptor(
305 ctx->eventNotifier->GetFd(),
306 [this, &ctx] { this->ReadShareMemory(ctx); });
307 }
308 if (isProtobufSerialize_ || isSaService_) {
309 ctx->stackPreprocess->SetWriter(g_buffWriter);
310 } else {
311 ctx->stackPreprocess->SetWriter(const_cast<WriterStructPtr>(writerAdapter_->GetStruct()));
312 }
313 return true;
314 }
315
CheckHapEncryped()316 void HookManager::CheckHapEncryped()
317 {
318 for (const auto& pid : hookConfig_.expand_pids()) {
319 if (pid > 0 && COMMON::CheckApplicationEncryped(pid, "")) {
320 hookConfig_.set_js_stack_report(0);
321 hookConfig_.set_max_js_stack_depth(0);
322 break;
323 }
324 }
325 const std::string processName = hookConfig_.process_name();
326 if (!processName.empty() && COMMON::CheckApplicationEncryped(0, processName)) {
327 PROFILER_LOG_INFO(LOG_CORE, "Encryped Application don't unwind js stack:%s", processName.c_str());
328 hookConfig_.set_js_stack_report(0);
329 hookConfig_.set_max_js_stack_depth(0);
330 }
331 }
332
CreatePluginSession(const std::vector<ProfilerPluginConfig> & config)333 bool HookManager::CreatePluginSession(const std::vector<ProfilerPluginConfig>& config)
334 {
335 PROFILER_LOG_DEBUG(LOG_CORE, "CreatePluginSession");
336 // save config
337 if (!config.empty()) {
338 std::string cfgData = config[0].config_data();
339 if (hookConfig_.ParseFromArray(reinterpret_cast<const uint8_t*>(cfgData.c_str()), cfgData.size()) <= 0) {
340 PROFILER_LOG_ERROR(LOG_CORE, "%s: ParseFromArray failed", __func__);
341 return false;
342 }
343 }
344 if ((!saMode_) && (COMMON::IsUserMode())) {
345 if (!COMMON::CheckApplicationPermission(hookConfig_.pid(), hookConfig_.process_name())) {
346 return false;
347 }
348 }
349 int32_t uShortMax = (std::numeric_limits<unsigned short>::max)();
350 if (hookConfig_.filter_size() > uShortMax) {
351 PROFILER_LOG_WARN(LOG_CORE, "%s: filter size invalid(size exceed 65535), reset to 65535!", __func__);
352 hookConfig_.set_filter_size(uShortMax);
353 }
354 if (!CheckProcess()) { // Check and initialize the context for the target process.
355 return false;
356 }
357 (void)CheckHapEncryped();
358 if (hookConfig_.max_stack_depth() < DLOPEN_MIN_UNWIND_DEPTH) {
359 // set default max depth
360 hookConfig_.set_max_stack_depth(DLOPEN_MIN_UNWIND_DEPTH);
361 }
362 #if defined(__arm__)
363 hookConfig_.set_fp_unwind(false); // if OS is 32-bit,set fp_unwind false.
364 hookConfig_.set_response_library_mode(false);
365 #endif
366 if (hookConfig_.response_library_mode()) {
367 hookConfig_.set_fp_unwind(true);
368 hookConfig_.set_offline_symbolization(true);
369 hookConfig_.set_js_stack_report(0);
370 }
371 // offlinem symbolization, callframe must be compressed
372 if (hookConfig_.offline_symbolization()) {
373 hookConfig_.set_callframe_compress(true);
374 }
375
376 // statistical reporting must be callframe compressed and accurate.
377 if (hookConfig_.statistics_interval() > 0) {
378 hookConfig_.set_callframe_compress(true);
379 hookConfig_.set_record_accurately(true);
380 }
381
382 // malloc and free matching interval reporting must be callframe compressed and accurate.
383 if (hookConfig_.malloc_free_matching_interval() > 0) {
384 hookConfig_.set_callframe_compress(true);
385 hookConfig_.set_record_accurately(true);
386 hookConfig_.set_statistics_interval(0);
387 }
388
389 // callframe compressed, string must be compressed.
390 if (hookConfig_.callframe_compress()) {
391 hookConfig_.set_string_compressed(true);
392 }
393
394 if (hookConfig_.js_stack_report() > 0 && hookConfig_.max_js_stack_depth() == 0 && hookConfig_.fp_unwind()) {
395 hookConfig_.set_max_js_stack_depth(DEFAULT_MAX_JS_STACK_DEPTH);
396 }
397
398 if (hookCtx_.empty()) {
399 PROFILER_LOG_ERROR(LOG_CORE, "HookManager no task");
400 return false;
401 }
402 if (hookConfig_.save_file() && !hookConfig_.file_name().empty()) {
403 auto retFile = COMMON::CheckNotExistsFilePath(hookConfig_.file_name());
404 if (!retFile.first) {
405 PROFILER_LOG_INFO(LOG_CORE, "check file path %s fail", hookConfig_.file_name().c_str());
406 return false;
407 }
408 fpHookData_ = fopen(retFile.second.c_str(), "wb+");
409 if (fpHookData_ == nullptr) {
410 PROFILER_LOG_INFO(LOG_CORE, "fopen file %s fail", hookConfig_.file_name().c_str());
411 return false;
412 }
413 }
414 if (hookConfig_.fp_unwind() && hookConfig_.record_accurately()
415 && hookConfig_.blocked() && hookConfig_.offline_symbolization()
416 && hookConfig_.statistics_interval() > 0
417 && hookConfig_.sample_interval() > 1
418 && !hookConfig_.js_stack_report()) {
419 noDataQueue_ = true;
420 }
421
422 if (!isSaService_) {
423 CreateWriter();
424 }
425
426 for (const auto& item : hookCtx_) {
427 CHECK_TRUE(HandleHookContext(item), false, "handle hook context failed"); // Create the required resources.
428 }
429
430 if (!isSaService_) { // SA mode will start HookService in the service.
431 ClientConfig clientConfig;
432 GetClientConfig(clientConfig);
433 if (noDataQueue_) {
434 clientConfig.freeEventOnlyAddrEnable = true;
435 }
436 std::string clientConfigStr = clientConfig.ToString();
437 PROFILER_LOG_INFO(LOG_CORE, "send hook client config:%s\n", clientConfigStr.c_str());
438 hookService_ = std::make_shared<HookService>(clientConfig, shared_from_this(), (hookCtx_.size() > 1));
439 CHECK_NOTNULL(hookService_, false, "HookService create failed!");
440 }
441
442 return true;
443 }
444
FlushStackArray()445 void HookManager::HookManagerCtx::FlushStackArray()
446 {
447 if (rawDataArray.size() > 0 && stackData != nullptr) {
448 if (!stackData->PutRawStackArray(rawDataArray, rawStackCount)) {
449 PROFILER_LOG_INFO(LOG_CORE, "PutRawStackArray error");
450 }
451 rawStackCount = 0;
452 rawDataArray = {};
453 }
454 }
455
FlushRawStackArray(const std::shared_ptr<HookManagerCtx> & hookCtx,std::shared_ptr<StackDataRepeater::RawStack> & rawStack)456 void HookManager::FlushRawStackArray(const std::shared_ptr<HookManagerCtx>& hookCtx,
457 std::shared_ptr<StackDataRepeater::RawStack>& rawStack)
458 {
459 if (hookCtx == nullptr || rawStack == nullptr) {
460 return;
461 }
462 hookCtx->rawDataArray[hookCtx->rawStackCount] = rawStack;
463 ++hookCtx->rawStackCount;
464 if (hookCtx->rawStackCount == CACHE_ARRAY_SIZE) {
465 hookCtx->FlushStackArray();
466 }
467 }
468
ReadShareMemory(const std::shared_ptr<HookManagerCtx> & hookCtx)469 void HookManager::ReadShareMemory(const std::shared_ptr<HookManagerCtx>& hookCtx)
470 {
471 CHECK_NOTNULL(hookCtx->shareMemoryBlock, NO_RETVAL, "smb is null!");
472 hookCtx->eventNotifier->Take();
473 int rawRealSize = 0;
474 while (true) {
475 auto rawStack = hookCtx->stackData->GetRawStack();
476 bool ret = hookCtx->shareMemoryBlock->TakeData([&](const int8_t data[], uint32_t size) -> bool {
477 if (size == sizeof(void*)) {
478 if (data) {
479 CHECK_TRUE(memcpy_s(&rawStack->freeData, sizeof(rawStack->freeData), data, size) == EOK, false,
480 "memcpy_s freeData failed!");
481 }
482 rawStack->baseStackData = nullptr;
483 return true;
484 }
485 rawStack->freeData = 0;
486 CHECK_TRUE(size >= sizeof(BaseStackRawData), false, "stack data invalid!");
487
488 rawStack->baseStackData = std::make_unique<uint8_t[]>(size);
489 CHECK_TRUE(memcpy_s(rawStack->baseStackData.get(), size, data, size) == EOK, false,
490 "memcpy_s raw data failed!");
491
492 rawStack->stackConext = reinterpret_cast<BaseStackRawData*>(rawStack->baseStackData.get());
493
494 if (rawStack->stackConext->type == NMD_MSG && printMallocNmd_) {
495 rawStack->data = rawStack->baseStackData.get() + sizeof(BaseStackRawData);
496 const char* nmdResult = reinterpret_cast<const char*>(rawStack->data);
497 if (nmdParamInfo_.type == SIMP_NMD) {
498 simplifiedNmd_ = std::string(nmdResult);
499 nmdComplete_ = true;
500 PROFILER_LOG_INFO(LOG_CORE, "receive simplified nmd info, target pid :%d, processName:%s.\n",
501 hookCtx->pid, hookCtx->processName.c_str());
502 } else {
503 lseek(nmdParamInfo_.fd, 0, SEEK_END);
504 (void)write(nmdParamInfo_.fd, nmdResult, strlen(nmdResult));
505 }
506 return true;
507 } else if (rawStack->stackConext->type == END_MSG) {
508 return true;
509 }
510 rawStack->data = rawStack->baseStackData.get() + sizeof(BaseStackRawData);
511 rawStack->reportFlag = true;
512 if (rawStack->stackConext->type == MEMORY_TAG || rawStack->stackConext->type == THREAD_NAME_MSG ||
513 rawStack->stackConext->type == MMAP_FILE_TYPE || rawStack->stackConext->type == PR_SET_VMA_MSG ||
514 rawStack->stackConext->type == JS_STACK_MSG) {
515 return true;
516 }
517 rawStack->reduceStackFlag = false;
518 if (hookConfig_.fp_unwind()) {
519 rawStack->fpDepth = (size - sizeof(BaseStackRawData)) / sizeof(uint64_t);
520 if (rawStack->stackConext->jsChainId > 0) {
521 rawStack->jsStackData = hookCtx->stackPreprocess->GetJsRawStack(rawStack->stackConext->jsChainId);
522 }
523 return true;
524 } else {
525 rawRealSize = sizeof(BaseStackRawData) + MAX_REG_SIZE * sizeof(char);
526 }
527
528 rawStack->stackSize = size - rawRealSize;
529 if (rawStack->stackSize > 0) {
530 rawStack->stackData = rawStack->baseStackData.get() + rawRealSize;
531 }
532 return true;
533 });
534 if (!ret) {
535 break;
536 }
537 if (rawStack->baseStackData == nullptr) {
538 FlushRawStackArray(hookCtx, rawStack);
539 continue;
540 }
541 if (rawStack->stackConext->type == MEMORY_TAG) {
542 std::string tagName = reinterpret_cast<char*>(rawStack->data);
543 hookCtx->stackPreprocess->SaveMemTag(rawStack->stackConext->tagId, tagName);
544 continue;
545 } else if (rawStack->stackConext->type == JS_STACK_MSG) {
546 hookCtx->stackPreprocess->SaveJsRawStack(rawStack->stackConext->jsChainId,
547 reinterpret_cast<char*>(rawStack->data));
548 continue;
549 } else if (rawStack->stackConext->type == END_MSG) {
550 hookCtx->FlushStackArray();
551 if (!hookCtx->stackData->PutRawStack(rawStack, hookCtx->isRecordAccurately)) {
552 break;
553 }
554 if (!hookCtx->stackData->PutRawStack(nullptr, false)) {
555 break;
556 }
557 continue;
558 } else if (rawStack->stackConext->type == NMD_MSG) {
559 continue;
560 }
561 FlushRawStackArray(hookCtx, rawStack);
562 }
563 }
564
DestroyPluginSession(const std::vector<uint32_t> & pluginIds)565 bool HookManager::DestroyPluginSession(const std::vector<uint32_t>& pluginIds)
566 {
567 if ((!saMode_) && (COMMON::IsUserMode())) {
568 if (!COMMON::CheckApplicationPermission(hookConfig_.pid(), hookConfig_.process_name())) {
569 return false;
570 }
571 }
572 for (const auto& item : hookCtx_) {
573 if (item->eventPoller != nullptr) {
574 PROFILER_LOG_ERROR(LOG_CORE, "eventPoller unset!");
575 if (item->eventNotifier != nullptr) {
576 item->eventPoller->RemoveFileDescriptor(item->eventNotifier->GetFd());
577 }
578 item->eventPoller->Stop();
579 item->eventPoller->Finalize();
580 }
581 if (item->shareMemoryBlock != nullptr) {
582 ShareMemoryAllocator::GetInstance().ReleaseMemoryBlockLocal(item->smbName);
583 }
584 if (item->stackData != nullptr) {
585 item->stackData->ClearCache();
586 }
587 }
588 if (fpHookData_) {
589 fclose(fpHookData_);
590 fpHookData_ = nullptr;
591 }
592 return true;
593 }
594
StartPluginSession(const std::vector<uint32_t> & pluginIds,const std::vector<ProfilerPluginConfig> & config,PluginResult & result)595 bool HookManager::StartPluginSession(const std::vector<uint32_t>& pluginIds,
596 const std::vector<ProfilerPluginConfig>& config, PluginResult& result)
597 {
598 UNUSED_PARAMETER(config);
599 if (hookCtx_.empty()) {
600 return false;
601 }
602 if ((!saMode_) && (COMMON::IsUserMode())) {
603 if (!COMMON::CheckApplicationPermission(hookConfig_.pid(), hookConfig_.process_name())) {
604 return false;
605 }
606 }
607 StartPluginSession();
608 return true;
609 }
610
StopPluginSession(const std::vector<uint32_t> & pluginIds)611 bool HookManager::StopPluginSession(const std::vector<uint32_t>& pluginIds)
612 {
613 if (hookCtx_.empty()) {
614 return false;
615 }
616 if ((!saMode_) && (COMMON::IsUserMode())) {
617 if (!COMMON::CheckApplicationPermission(hookConfig_.pid(), hookConfig_.process_name())) {
618 return false;
619 }
620 }
621 for (const auto& item : hookCtx_) {
622 if (item->pid > 0) {
623 PROFILER_LOG_INFO(LOG_CORE, "stop command : send 37 signal to process %d", item->pid);
624 if (kill(item->pid, SIGNAL_STOP_HOOK) == -1) {
625 const int bufSize = 256;
626 char buf[bufSize] = {0};
627 strerror_r(errno, buf, bufSize);
628 PROFILER_LOG_ERROR(LOG_CORE, "send 37 signal to process %d , error = %s", item->pid, buf);
629 }
630 } else {
631 PROFILER_LOG_INFO(LOG_CORE, "StopPluginSession: pid(%d) is less or equal zero.", item->pid);
632 }
633 CHECK_TRUE(item->stackPreprocess != nullptr, false, "stop StackPreprocess FAIL");
634 item->stackPreprocess->StopTakeResults();
635 PROFILER_LOG_INFO(LOG_CORE, "StopTakeResults success");
636 if (hookConfig_.statistics_interval() > 0) {
637 item->stackPreprocess->FlushRecordStatistics();
638 }
639 if (hookConfig_.malloc_free_matching_interval() > 0) {
640 item->stackPreprocess->FlushRecordApplyAndReleaseMatchData();
641 }
642 if (item->stackData != nullptr) {
643 item->stackData->Close();
644 }
645 item->stackPreprocess->FinishTraceFile();
646 }
647 return true;
648 }
649
ResetStartupParam()650 void HookManager::ResetStartupParam()
651 {
652 const std::string resetParam = "startup:disabled";
653 if (hookConfig_.startup_mode()) {
654 int ret = SystemSetParameter(PARAM_NAME.c_str(), resetParam.c_str());
655 if (ret < 0) {
656 PROFILER_LOG_WARN(LOG_CORE, "set param failed, please reset param(%s)", PARAM_NAME.c_str());
657 } else {
658 PROFILER_LOG_INFO(LOG_CORE, "reset param success");
659 }
660 }
661 }
662
ReportPluginBasicData(const std::vector<uint32_t> & pluginIds)663 bool HookManager::ReportPluginBasicData(const std::vector<uint32_t>& pluginIds)
664 {
665 return true;
666 }
667
CreateWriter(std::string pluginName,uint32_t bufferSize,int smbFd,int eventFd,bool isProtobufSerialize)668 bool HookManager::CreateWriter(std::string pluginName, uint32_t bufferSize, int smbFd, int eventFd,
669 bool isProtobufSerialize)
670 {
671 PROFILER_LOG_DEBUG(LOG_CORE, "agentIndex_ %d", agentIndex_);
672 writer_ = std::make_shared<BufferWriter>(pluginName, VERSION, bufferSize, smbFd, eventFd, agentIndex_);
673 isProtobufSerialize_ = isProtobufSerialize;
674 shareMemorySize_ = bufferSize;
675 return true;
676 }
677
CreateWriter()678 void HookManager::CreateWriter()
679 {
680 PROFILER_LOG_INFO(LOG_CORE, "CreateWriter isProtobufSerialize: %d, noDataQueue_: %d",
681 isProtobufSerialize_, noDataQueue_);
682 if (isProtobufSerialize_) {
683 RegisterWriter(writer_);
684 } else {
685 writerAdapter_ = std::make_shared<WriterAdapter>(isProtobufSerialize_);
686 writerAdapter_->SetWriter(writer_);
687 }
688 }
689
ResetWriter(uint32_t pluginId)690 bool HookManager::ResetWriter(uint32_t pluginId)
691 {
692 RegisterWriter(nullptr);
693 return true;
694 }
695
RegisterWriter(const std::shared_ptr<Writer> writer)696 void HookManager::RegisterWriter(const std::shared_ptr<Writer> writer)
697 {
698 g_buffWriter = writer;
699 return;
700 }
701
SetHookConfig(const NativeHookConfig & hookConfig)702 void HookManager::SetHookConfig(const NativeHookConfig& hookConfig)
703 {
704 hookConfig_ = hookConfig;
705 }
706
SethookStandalone(bool HookStandalone)707 void HookManager::SethookStandalone(bool HookStandalone)
708 {
709 isHookStandalone_ = HookStandalone;
710 }
711
SetHookConfig(const std::shared_ptr<NativeMemoryProfilerSaConfig> & config)712 void HookManager::SetHookConfig(const std::shared_ptr<NativeMemoryProfilerSaConfig>& config)
713 {
714 hookConfig_.set_pid(config->pid_);
715 if (!config->processName_.empty()) {
716 hookConfig_.set_process_name(config->processName_);
717 }
718 hookConfig_.set_filter_size(config->filterSize_);
719 hookConfig_.set_smb_pages(config->shareMemorySize_);
720 hookConfig_.set_max_stack_depth(config->maxStackDepth_);
721 hookConfig_.set_malloc_disable(config->mallocDisable_);
722 hookConfig_.set_mmap_disable(config->mmapDisable_);
723 hookConfig_.set_free_stack_report(config->freeStackData_);
724 hookConfig_.set_munmap_stack_report(config->munmapStackData_);
725 hookConfig_.set_malloc_free_matching_interval(config->mallocFreeMatchingInterval_);
726 hookConfig_.set_malloc_free_matching_cnt(config->mallocFreeMatchingCnt_);
727 hookConfig_.set_string_compressed(config->stringCompressed_);
728 hookConfig_.set_fp_unwind(config->fpUnwind_);
729 hookConfig_.set_blocked(config->blocked_);
730 hookConfig_.set_record_accurately(config->recordAccurately_);
731 hookConfig_.set_startup_mode(config->startupMode_);
732 hookConfig_.set_memtrace_enable(config->memtraceEnable_);
733 hookConfig_.set_offline_symbolization(config->offlineSymbolization_);
734 hookConfig_.set_callframe_compress(config->callframeCompress_);
735 hookConfig_.set_statistics_interval(config->statisticsInterval_);
736 hookConfig_.set_clock(COMMON::GetClockStr(config->clockId_));
737 hookConfig_.set_sample_interval(config->sampleInterval_);
738 hookConfig_.set_response_library_mode(config->responseLibraryMode_);
739 hookConfig_.set_js_stack_report(config->jsStackReport_);
740 hookConfig_.set_max_js_stack_depth(config->maxJsStackDepth_);
741 hookConfig_.set_filter_napi_name(config->filterNapiName_);
742 printMallocNmd_ = config->printNmd_;
743 largestSize_ = config->largestSize_;
744 secondLargestSize_ = config->secondLargestSize_;
745 maxGrowthSize_ = config->maxGrowthSize_;
746 }
747
CreatePluginSession()748 int32_t HookManager::CreatePluginSession()
749 {
750 if (CreatePluginSession({})) {
751 return RET_OK;
752 }
753 return RET_ERR;
754 }
755
StartPluginSession()756 void HookManager::StartPluginSession()
757 {
758 for (const auto& item : hookCtx_) {
759 if (item->stackPreprocess == nullptr) {
760 continue;
761 }
762 PROFILER_LOG_ERROR(LOG_CORE, "StartPluginSession name: %s", item->processName.c_str());
763 if (!noDataQueue_) {
764 item->stackPreprocess->StartTakeResults();
765 }
766 item->stackPreprocess->InitStatisticsTime();
767 if (item->pid > 0) {
768 PROFILER_LOG_INFO(LOG_CORE, "start command : send 36 signal to process %d", item->pid);
769 if (kill(item->pid, SIGNAL_START_HOOK) == -1) {
770 const int bufSize = 256;
771 char buf[bufSize] = {0};
772 strerror_r(errno, buf, bufSize);
773 PROFILER_LOG_ERROR(LOG_CORE, "send 36 signal error = %s", buf);
774 }
775 } else {
776 PROFILER_LOG_INFO(LOG_CORE, "StartPluginSession: pid(%d) is less or equal zero.", item->pid);
777 }
778 }
779 if (!saMode_) {
780 int ret = COMMON::PluginWriteToHisysevent("native_hook_plugin", "sh", GetCmdArgs(hookConfig_),
781 COMMON::ErrorType::RET_SUCC, "success");
782 PROFILER_LOG_INFO(LOG_CORE, "hisysevent report native_hook_plugin result:%d", ret);
783 }
784 }
785
GetCmdArgs(NativeHookConfig traceConfig)786 std::string HookManager::GetCmdArgs(NativeHookConfig traceConfig)
787 {
788 std::stringstream args;
789 args << "pid: " << COMMON::GetProcessNameByPid(traceConfig.pid()) << ", ";
790 args << "save_file: " << (traceConfig.save_file() ? "true" : "false") << ", ";
791 args << "filter_size: " << std::to_string(traceConfig.filter_size()) << ", ";
792 args << "smb_pages: " << std::to_string(traceConfig.smb_pages()) << ", ";
793 args << "max_stack_depth: " << std::to_string(traceConfig.max_stack_depth()) << ", ";
794 args << "process_name: " << traceConfig.process_name() << ", ";
795 args << "malloc_disable: " << (traceConfig.malloc_disable() ? "true" : "false") << ", ";
796 args << "mmap_disable: " << (traceConfig.mmap_disable() ? "true" : "false") << ", ";
797 args << "free_stack_report: " << (traceConfig.free_stack_report() ? "true" : "false") << ", ";
798 args << "munmap_stack_report: " << (traceConfig.munmap_stack_report() ? "true" : "false") << ", ";
799 args << "malloc_free_matching_interval: " << std::to_string(traceConfig.malloc_free_matching_interval()) << ", ";
800 args << "malloc_free_matching_cnt: " << std::to_string(traceConfig.malloc_free_matching_cnt()) << ", ";
801 args << "string_compressed: " << (traceConfig.string_compressed() ? "true" : "false") << ", ";
802 args << "fp_unwind: " << (traceConfig.fp_unwind() ? "true" : "false") << ", ";
803 args << "blocked: " << (traceConfig.blocked() ? "true" : "false") << ", ";
804 args << "record_accurately: " << (traceConfig.record_accurately() ? "true" : "false") << ", ";
805 args << "startup_mode: " << (traceConfig.startup_mode() ? "true" : "false") << ", ";
806 args << "memtrace_enable: " << (traceConfig.memtrace_enable() ? "true" : "false") << ", ";
807 args << "offline_symbolization: " << (traceConfig.offline_symbolization() ? "true" : "false") << ", ";
808 args << "callframe_compress: " << (traceConfig.callframe_compress() ? "true" : "false") << ", ";
809 args << "statistics_interval: " << std::to_string(traceConfig.statistics_interval()) << ", ";
810 args << "clock: " << traceConfig.clock() << ", ";
811 args << "sample_interval: " << std::to_string(traceConfig.sample_interval()) << ", ";
812 args << "response_library_mode: " << (traceConfig.response_library_mode() ? "true" : "false") << ", ";
813 args << "js_stack_report: " << std::to_string(traceConfig.js_stack_report()) << ", ";
814 args << "max_js_stack_depth: " << std::to_string(traceConfig.max_js_stack_depth()) << ", ";
815 args << "filter_napi_name: " << traceConfig.filter_napi_name() << ", ";
816 for (const auto& pid : traceConfig.expand_pids()) {
817 args << "expand_pids: " << std::to_string(pid) << ", ";
818 }
819 return args.str();
820 }
821
WriteHookConfig()822 void HookManager::WriteHookConfig()
823 {
824 for (const auto& item : hookCtx_) {
825 if (item == nullptr) {
826 PROFILER_LOG_ERROR(LOG_CORE, "HookManager WriteHookConfig failed");
827 return;
828 }
829 item->stackPreprocess->WriteHookConfig();
830 }
831 }
832
GetFds(int32_t pid,const std::string & name)833 std::pair<int, int> HookManager::GetFds(int32_t pid, const std::string& name)
834 {
835 for (const auto& item : hookCtx_) {
836 if (item->pid == pid || item->processName == name) {
837 if (item->pid == -1) {
838 item->pid = pid;
839 }
840 item->stackPreprocess->SetPid(pid);
841 return {item->eventNotifier->GetFd(), item->shareMemoryBlock->GetfileDescriptor()};
842 }
843 }
844 return {-1, -1};
845 }
846
SetNmdInfo(std::pair<uint32_t,uint32_t> info)847 void HookManager::SetNmdInfo(std::pair<uint32_t, uint32_t> info)
848 {
849 printMallocNmd_ = true;
850 nmdParamInfo_.fd = info.first;
851 nmdParamInfo_.type = info.second;
852 }
853 }