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