• 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 #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