• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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