1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 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 "native_memory_profiler_sa_service.h"
17 #include "native_memory_profiler_sa_death_recipient.h"
18 #include "iservice_registry.h"
19 #include "ipc_skeleton.h"
20 #include "trace_file_writer.h"
21 #include "socket_context.h"
22 #include "hook_common.h"
23 #include "init_param.h"
24 #include "token_setproc.h"
25 #include "accesstoken_kit.h"
26
27 #include "logging.h"
28
29 namespace OHOS::Developtools::NativeDaemon {
30 namespace {
31 constexpr int32_t TIME_BASE = 1000;
32 constexpr int32_t MAX_TASK_NUM = 4;
33 const std::string FILE_PATH_HEAD = "/data/local/tmp/native_memory_";
34 const std::string FILE_PATH_TAIL = ".htrace";
35 constexpr int32_t DELAYED_SHUTDOWN_TIME = 20;
36 constexpr int FILE_MODE = 0644;
37 }
38
NativeMemoryProfilerSaService()39 NativeMemoryProfilerSaService::NativeMemoryProfilerSaService() : SystemAbility(NATIVE_DAEMON_SYSTEM_ABILITY_ID, true)
40 {
41 serviceName_ = "HookService";
42 serviceEntry_ = std::make_shared<ServiceEntry>();
43 if (!serviceEntry_->StartServer(DEFAULT_UNIX_SOCKET_HOOK_PATH)) {
44 serviceEntry_ = nullptr;
45 PROFILER_LOG_ERROR(LOG_CORE, "Start IPC Service FAIL");
46 return;
47 }
48 serviceEntry_->RegisterService(*this);
49 DelayedShutdown(false);
50 }
51
~NativeMemoryProfilerSaService()52 NativeMemoryProfilerSaService::~NativeMemoryProfilerSaService()
53 {
54 serviceEntry_ = nullptr;
55 }
56
StartServiceAbility()57 bool NativeMemoryProfilerSaService::StartServiceAbility()
58 {
59 sptr<ISystemAbilityManager> serviceManager = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
60 CHECK_NOTNULL(serviceManager, false, "serviceManager is nullptr");
61
62 auto native = new NativeMemoryProfilerSaService();
63 CHECK_NOTNULL(native, false, "native is nullptr");
64 int32_t result = serviceManager->AddSystemAbility(NATIVE_DAEMON_SYSTEM_ABILITY_ID, native);
65 if (result != 0) {
66 PROFILER_LOG_ERROR(LOG_CORE, "Service native memory failed to start");
67 return false;
68 }
69
70 auto abilityObject = serviceManager->AsObject();
71 CHECK_NOTNULL(abilityObject, false, "abilityObject is nullptr");
72
73 bool ret = abilityObject->AddDeathRecipient(new NativeMemoryProfilerSaDeathRecipient());
74 if (ret == false) {
75 PROFILER_LOG_ERROR(LOG_CORE, "AddDeathRecipient failed");
76 return false;
77 }
78 PROFILER_LOG_INFO(LOG_CORE, "Service native memory started successfully");
79 return true;
80 }
81
HasProfilingPermission()82 bool NativeMemoryProfilerSaService::HasProfilingPermission()
83 {
84 uint32_t callingTokenID = IPCSkeleton::GetCallingTokenID();
85 int res = Security::AccessToken::AccessTokenKit::VerifyAccessToken(callingTokenID,
86 "ohos.permission.ENABLE_PROFILER");
87 if (res != Security::AccessToken::PermissionState::PERMISSION_GRANTED) {
88 PROFILER_LOG_ERROR(LOG_CORE, "No profiling permission, please check!");
89 return false;
90 }
91 return true;
92 }
93
Start(std::shared_ptr<NativeMemoryProfilerSaConfig> & config)94 int32_t NativeMemoryProfilerSaService::Start(std::shared_ptr<NativeMemoryProfilerSaConfig>& config)
95 {
96 return StartHook(config);
97 }
98
Stop(uint32_t pid)99 int32_t NativeMemoryProfilerSaService::Stop(uint32_t pid)
100 {
101 StopHook(pid);
102 return RET_OK;
103 }
104
Stop(const std::string & name)105 int32_t NativeMemoryProfilerSaService::Stop(const std::string& name)
106 {
107 StopHook(0, name);
108 return RET_OK;
109 }
110
DumpData(uint32_t fd,std::shared_ptr<NativeMemoryProfilerSaConfig> & config)111 int32_t NativeMemoryProfilerSaService::DumpData(uint32_t fd, std::shared_ptr<NativeMemoryProfilerSaConfig>& config)
112 {
113 if (config->printNmd_) {
114 std::lock_guard<std::mutex> guard(nmdMtx_);
115 nmdPidType_[config->nmdPid_] = std::make_pair(fd, config->nmdType_);
116 return RET_OK;
117 }
118 if (StartHook(config, fd) == RET_ERR) {
119 close(fd);
120 return RET_ERR;
121 }
122 return RET_OK;
123 }
124
StopHook(uint32_t pid,std::string name,bool timeout)125 void NativeMemoryProfilerSaService::StopHook(uint32_t pid, std::string name, bool timeout)
126 {
127 if (!HasProfilingPermission()) {
128 PROFILER_LOG_ERROR(LOG_CORE, "StopHook failed, no profiling permission!");
129 return;
130 }
131 std::lock_guard<std::mutex> guard(mtx_);
132 std::shared_ptr<TaskConfig> config = nullptr;
133 if (pid > 0) {
134 if (auto taskIter = pidCtx_.find(pid); taskIter != pidCtx_.end()) {
135 config = taskIter->second;
136 }
137 } else if (auto taskIter = nameAndFilePathCtx_.find(name); taskIter != nameAndFilePathCtx_.end()) {
138 config = taskIter->second;
139 }
140 if (config == nullptr) {
141 PROFILER_LOG_INFO(LOG_CORE, "NativeMemoryProfilerSaService: hook has stop, pid: %d, process name: %s",
142 pid, name.c_str());
143 return;
144 }
145
146 config->hookMgr->StopPluginSession({});
147 config->hookMgr->DestroyPluginSession({});
148 if (timeout) {
149 scheduleTaskManager_.UnscheduleTaskLockless(config->timerFd);
150 } else {
151 scheduleTaskManager_.UnscheduleTask(config->timerFd);
152 }
153 nameAndFilePathCtx_.erase(config->processName);
154 nameAndFilePathCtx_.erase(config->filePath);
155 pidCtx_.erase(config->pid);
156 if (config->isStartupMode) {
157 hasStartupMode_ = false;
158 }
159 if (config->fd > 0) {
160 close(config->fd);
161 }
162 if (--taskNum_ == 0) {
163 PROFILER_LOG_INFO(LOG_CORE, "StringViewMemoryHold clear");
164 StringViewMemoryHold::GetInstance().Clear();
165 DelayedShutdown(false);
166 }
167 }
168
StartHook(std::shared_ptr<NativeMemoryProfilerSaConfig> & config,uint32_t fd)169 int32_t NativeMemoryProfilerSaService::StartHook(std::shared_ptr<NativeMemoryProfilerSaConfig>& config, uint32_t fd)
170 {
171 if (!HasProfilingPermission()) {
172 PROFILER_LOG_ERROR(LOG_CORE, "StartHook failed, no profiling permission!");
173 return RET_ERR;
174 }
175 if (config == nullptr) {
176 return RET_ERR;
177 }
178 std::lock_guard<std::mutex> guard(mtx_);
179 if (config->filePath_.empty() && fd == 0) {
180 std::string filePathStr = (config->pid_ > 0) ? std::to_string(config->pid_) : config->processName_;
181 config->filePath_ = FILE_PATH_HEAD + filePathStr + FILE_PATH_TAIL;
182 }
183 PROFILER_LOG_INFO(LOG_CORE, "file path: %s", config->filePath_.c_str());
184
185 if (!CheckConfig(config, fd)) {
186 return RET_ERR;
187 }
188
189 if (fd == 0) {
190 int fdTemp = open(config->filePath_.c_str(), O_RDWR | O_CREAT, FILE_MODE);
191 CHECK_TRUE(fdTemp >= 0, RET_ERR, "Failed to open file(%s)", config->filePath_.c_str());
192 fd = static_cast<uint32_t>(fdTemp);
193 }
194
195 std::shared_ptr<TraceFileWriter> writeFile = std::make_shared<TraceFileWriter>(fd);
196 CHECK_NOTNULL(writeFile, RET_ERR, "Failed to create TraceFileWriter");
197 writeFile->SetTimeSource();
198
199 std::shared_ptr<HookManager> hook = std::make_shared<HookManager>();
200 CHECK_NOTNULL(hook, RET_ERR, "Failed to create HookManager");
201 hook->RegisterWriter(writeFile);
202 hook->SetSaMode(true);
203 hook->SetHookConfig(config);
204 hook->SetSaServiceConfig(true, false);
205 if (config->pid_ > 0) {
206 std::lock_guard<std::mutex> nmdGuard(nmdMtx_);
207 if (nmdPidType_.find(config->pid_) != nmdPidType_.end()) {
208 hook->SetNmdInfo(nmdPidType_[config->pid_]);
209 }
210 }
211 if (hook->CreatePluginSession() != RET_OK) {
212 return RET_ERR;
213 }
214 hook->WriteHookConfig();
215 hook->StartPluginSession();
216
217 int32_t timerFd = scheduleTaskManager_.ScheduleTask(
218 [this, config] { this->StopHook(config->pid_, config->processName_, true); },
219 config->duration_ * TIME_BASE,
220 true);
221 if (timerFd == -1) {
222 PROFILER_LOG_ERROR(LOG_CORE, "NativeMemoryProfilerSaService Start Schedule Task failed");
223 return RET_ERR;
224 }
225
226 std::shared_ptr<TaskConfig> configCtx =
227 std::make_shared<TaskConfig>(hook, config->pid_, config->processName_, config->filePath_, timerFd,
228 config->startupMode_, fd);
229 CHECK_NOTNULL(hook, RET_ERR, "Failed to create TaskConfig");
230 if (!hasStartupMode_ && config->startupMode_) {
231 hasStartupMode_ = true;
232 startupModeProcessName_ = config->processName_;
233 }
234
235 if (configCtx->pid > 0) {
236 pidCtx_[configCtx->pid] = configCtx;
237 } else if (!configCtx->processName.empty()) {
238 nameAndFilePathCtx_[configCtx->processName] = configCtx;
239 }
240
241 if (fd == 0) {
242 nameAndFilePathCtx_[configCtx->filePath] = configCtx;
243 }
244 ++taskNum_;
245 DelayedShutdown(true);
246 return RET_OK;
247 }
248
CheckConfig(std::shared_ptr<NativeMemoryProfilerSaConfig> & config,uint32_t fd)249 bool NativeMemoryProfilerSaService::CheckConfig(std::shared_ptr<NativeMemoryProfilerSaConfig>& config, uint32_t fd)
250 {
251 if (taskNum_ + 1 > MAX_TASK_NUM) {
252 PROFILER_LOG_INFO(LOG_CORE, "NativeMemoryProfilerSaService: Support up to 4 tasks at the same time");
253 return false;
254 }
255
256 if (hasStartupMode_ && config->startupMode_) {
257 PROFILER_LOG_INFO(LOG_CORE, "NativeMemoryProfilerSaService: tasks with an existing startup mode, name: %s",
258 startupModeProcessName_.c_str());
259 return false;
260 }
261
262 if (config->pid_ > 0) {
263 config->processName_.clear();
264 if (pidCtx_.find(config->pid_) != pidCtx_.end()) {
265 PROFILER_LOG_INFO(LOG_CORE, "NativeMemoryProfilerSaService: hook has started, pid: %d", config->pid_);
266 return false;
267 }
268 } else if (!config->processName_.empty()) {
269 if (nameAndFilePathCtx_.find(config->processName_) != nameAndFilePathCtx_.end()) {
270 PROFILER_LOG_INFO(LOG_CORE, "NativeMemoryProfilerSaService: hook has started, process name: %s",
271 config->processName_.c_str());
272 return false;
273 }
274 } else {
275 PROFILER_LOG_ERROR(LOG_CORE, "The PID and process name are not configured");
276 return false;
277 }
278
279 if (fd > 0) {
280 return true;
281 }
282
283 if (!config->filePath_.empty()) {
284 if (nameAndFilePathCtx_.find(config->filePath_) != nameAndFilePathCtx_.end()) {
285 PROFILER_LOG_ERROR(LOG_CORE,
286 "NativeMemoryProfilerSaService: File %s is being used.", config->filePath_.c_str());
287 return false;
288 }
289 } else {
290 PROFILER_LOG_ERROR(LOG_CORE, "The file path are not configured");
291 return false;
292 }
293 return true;
294 }
295
FillTaskConfigContext(int32_t pid,const std::string & name)296 void NativeMemoryProfilerSaService::FillTaskConfigContext(int32_t pid, const std::string& name)
297 {
298 std::lock_guard<std::mutex> guard(mtx_);
299 if (auto iter = pidCtx_.find(pid); iter != pidCtx_.end()) {
300 iter->second->processName = name;
301 nameAndFilePathCtx_[name] = iter->second;
302 if (iter->second->isStartupMode) {
303 hasStartupMode_ = false;
304 }
305 } else if (auto it = nameAndFilePathCtx_.find(name); it != nameAndFilePathCtx_.end()) {
306 it->second->pid = pid;
307 pidCtx_[pid] = it->second;
308 if (it->second->isStartupMode) {
309 hasStartupMode_ = false;
310 }
311 } else {
312 PROFILER_LOG_ERROR(LOG_CORE, "NativeMemoryProfilerSaService: fill TaskConfig context failed");
313 }
314 }
315
ProtocolProc(SocketContext & context,uint32_t pnum,const int8_t * buf,const uint32_t size)316 bool NativeMemoryProfilerSaService::ProtocolProc(SocketContext &context, uint32_t pnum, const int8_t *buf,
317 const uint32_t size)
318 {
319 if (size != sizeof(int)) {
320 return false;
321 }
322 int peerConfig = *const_cast<int *>(reinterpret_cast<const int *>(buf));
323 if (peerConfig == -1) {
324 return false;
325 }
326
327 std::string filePath = "/proc/" + std::to_string(peerConfig) + "/cmdline";
328 std::string bundleName;
329 if (!LoadStringFromFile(filePath, bundleName)) {
330 PROFILER_LOG_ERROR(LOG_CORE, "Get process name by pid failed!, pid: %d", peerConfig);
331 return false;
332 }
333 bundleName.resize(strlen(bundleName.c_str()));
334
335 if (bundleName.substr(0, 2) == "./") { // 2: size, Command line programs will be prefixed with "./"
336 bundleName = bundleName.substr(2); // 2: point
337 }
338 FillTaskConfigContext(peerConfig, bundleName); // Save the relevant context for subsequent inspection
339
340 std::lock_guard<std::mutex> guard(mtx_);
341 if (auto iter = pidCtx_.find(peerConfig); iter != pidCtx_.end()) {
342 auto [eventFd, smbFd] = iter->second->hookMgr->GetFds(peerConfig, bundleName);
343 iter->second->hookMgr->SetPid(peerConfig);
344 if (eventFd == smbFd) {
345 PROFILER_LOG_ERROR(LOG_CORE,
346 "Get eventFd and smbFd failed!, name: %s, pid: %d", bundleName.c_str(), peerConfig);
347 return false;
348 }
349 PROFILER_LOG_INFO(LOG_CORE,
350 "ProtocolProc, receive message from hook client, and send hook config to process %d, name: %s",
351 peerConfig, bundleName.c_str());
352 ClientConfig clientConfig;
353 iter->second->hookMgr->GetClientConfig(clientConfig);
354 if (iter->second->hookMgr->GetNoDataQueueFlag()) {
355 clientConfig.freeEventOnlyAddrEnable = true;
356 }
357 context.SendHookConfig(reinterpret_cast<uint8_t *>(&clientConfig), sizeof(clientConfig));
358 context.SendFileDescriptor(smbFd);
359 context.SendFileDescriptor(eventFd);
360 iter->second->hookMgr->ResetStartupParam();
361 } else {
362 PROFILER_LOG_ERROR(LOG_CORE, "ProtocolProc: send config failed");
363 return false;
364 }
365 return true;
366 }
367
DelayedShutdown(bool cancel)368 void NativeMemoryProfilerSaService::DelayedShutdown(bool cancel)
369 {
370 if (cancel) {
371 scheduleTaskManager_.UnscheduleTask(delayedShutdownTimerFd_);
372 delayedShutdownTimerFd_ = -1;
373 } else {
374 int32_t timerFd = scheduleTaskManager_.ScheduleTask(
375 []() {
376 int ret = SystemSetParameter("hiviewdfx.hiprofiler.native_memoryd.start", "0");
377 if (ret < 0) {
378 PROFILER_LOG_ERROR(LOG_CORE, "DelayedShutdown close sa failed");
379 } else {
380 PROFILER_LOG_INFO(LOG_CORE, "DelayedShutdown close sa success");
381 }
382 },
383 DELAYED_SHUTDOWN_TIME * TIME_BASE,
384 true, false);
385 if (timerFd == -1) {
386 PROFILER_LOG_ERROR(LOG_CORE, "NativeMemoryProfilerSaService:DelayedShutdown Schedule Task failed");
387 return;
388 }
389 delayedShutdownTimerFd_ = timerFd;
390 }
391 }
392 } // namespace OHOS::Developtools::NativeDaemon