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