1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
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_scope_manager.h"
17
18 #ifdef ENABLE_MEMLEAK_DEBUG
19 #include <atomic>
20 #include <cstdio>
21 #include <cstdlib>
22 #include <securec.h>
23 #include <thread>
24 #include <unistd.h>
25 // for libunwind.h empty struct has size 0 in c, size 1 in c++
26 #define UNW_EMPTY_STRUCT uint8_t unused;
27 #include <libunwind.h>
28 #endif
29
30 #include "native_engine/native_value.h"
31 #ifdef ENABLE_MEMLEAK_DEBUG
32 #include "parameters.h"
33 #endif
34 #include <cstddef>
35
36 #include "utils/log.h"
37
38 struct NativeHandle {
39 NativeValue* value = nullptr;
40 NativeHandle* sibling = nullptr;
41 };
42
43 #ifdef ENABLE_MEMLEAK_DEBUG
44 using OHOS::system::GetIntParameter;
45 const int NativeScopeManager::DEBUG_MEMLEAK = OHOS::system::GetIntParameter<int>("persist.napi.memleak.debug", 0);
46 // 100 for the default depth.
47 const int NativeScopeManager::BACKTRACE_DEPTH = OHOS::system::GetIntParameter<int>("persist.napi.memleak.depth", 100);
48 std::atomic<std::vector<struct StructVma>*> NativeScopeManager::vmas(nullptr);
49
TrimAndDupStr(const std::string & source,std::string & str)50 static void TrimAndDupStr(const std::string& source, std::string& str)
51 {
52 auto const firstPos = source.find_first_not_of(' ');
53 if (firstPos == std::string::npos) {
54 return;
55 }
56 auto len = source.find_last_not_of(' ') - firstPos + 1;
57 if (len > NativeScopeManager::MAPINFO_SIZE) {
58 len = NativeScopeManager::MAPINFO_SIZE;
59 }
60 str = source.substr(firstPos, len);
61 }
62
CreateVma(struct StructVma & vma,const std::string & mapInfo)63 static bool CreateVma(struct StructVma& vma, const std::string& mapInfo)
64 {
65 int pos = 0;
66 uint64_t begin = 0;
67 uint64_t end = 0;
68 uint64_t offset = 0;
69 char perms[5] = { 0 }; // 5:rwxp
70
71 if (sscanf_s(mapInfo.c_str(), "%" SCNxPTR "-%" SCNxPTR " %4s %" SCNxPTR " %*x:%*x %*d%n", &begin, &end, &perms,
72 sizeof(perms), &offset, &pos) != 4) { // 4:scan size
73 return false;
74 }
75 vma.begin = begin;
76 vma.end = end;
77 TrimAndDupStr(mapInfo.substr(pos), vma.path);
78 return true;
79 }
80
CreateVmas(int pid,std::vector<struct StructVma> & vmas)81 static void CreateVmas(int pid, std::vector<struct StructVma>& vmas)
82 {
83 if (pid <= 0) {
84 return;
85 }
86 char path[NativeScopeManager::NAME_LEN] = { 0 };
87 if (snprintf_s(path, sizeof(path), sizeof(path) - 1, "/proc/%d/maps", pid) <= 0) {
88 return;
89 }
90 char realPath[PATH_MAX] = { 0 };
91 if (realpath(path, realPath) == nullptr) {
92 return;
93 }
94 FILE* fp = fopen(realPath, "r");
95 if (fp == nullptr) {
96 return;
97 }
98 char mapInfo[NativeScopeManager::MAPINFO_SIZE] = { 0 };
99 while (fgets(mapInfo, sizeof(mapInfo), fp) != nullptr) {
100 struct StructVma vma;
101 std::string info(mapInfo);
102 if (CreateVma(vma, info)) {
103 vmas.push_back(vma);
104 }
105 }
106 (void)fclose(fp);
107 }
108
ResetVmas(std::atomic<std::vector<struct StructVma> * > & vmas)109 static void ResetVmas(std::atomic<std::vector<struct StructVma>*>& vmas)
110 {
111 std::vector<struct StructVma>* new_vmas = new std::vector<struct StructVma>();
112 if (new_vmas == nullptr) {
113 return;
114 }
115 CreateVmas(getpid(), *new_vmas);
116 new_vmas = vmas.exchange(new_vmas);
117 if (new_vmas != nullptr) {
118 delete new_vmas;
119 }
120 }
121
FindMapByAddr(uintptr_t address,const std::vector<struct StructVma> & vmas)122 static const struct StructVma* FindMapByAddr(uintptr_t address, const std::vector<struct StructVma>& vmas)
123 {
124 for (auto iter = vmas.begin(); iter != vmas.end(); iter++) {
125 if (((*iter).begin <= address) && ((*iter).end > address)) {
126 return &(*iter);
127 }
128 }
129 return nullptr;
130 }
131
BackTrace(const std::vector<struct StructVma> & vmas)132 static bool BackTrace(const std::vector<struct StructVma>& vmas)
133 {
134 bool hasUnknowMap = false;
135 int depth = 0;
136 unw_cursor_t cursor;
137 unw_context_t context;
138 unw_getcontext(&context);
139 unw_init_local(&cursor, &context);
140 while (unw_step(&cursor) > 0) {
141 unw_word_t offset, pc;
142 unw_get_reg(&cursor, UNW_REG_IP, &pc);
143 if (pc == 0 || ++depth > NativeScopeManager::BACKTRACE_DEPTH) {
144 break;
145 }
146 char sym[512]; // 512:max length of a symbol.
147 const struct StructVma* vma = FindMapByAddr(pc, vmas);
148 if (vma == nullptr) {
149 hasUnknowMap = true;
150 }
151 int ret = unw_get_proc_name(&cursor, sym, sizeof(sym), &offset);
152 if (ret == 0) {
153 HILOG_ERROR("MEMLEAK: %{public}s +0x%{public}" SCNxPTR ", %{public}s\n", sym, offset,
154 (vma != nullptr) ? vma->path.c_str() : "unknow_path");
155 } else {
156 HILOG_ERROR("MEMLEAK: unknow(%{public}d) pc=0x%{public}" SCNxPTR ", %{public}s\n", ret, pc,
157 (vma != nullptr) ? vma->path.c_str() : "unknow_path");
158 }
159 }
160 return hasUnknowMap;
161 }
162 #endif
163
NativeScopeManager()164 NativeScopeManager::NativeScopeManager()
165 {
166 root_ = NativeScope::CreateNewInstance();
167 current_ = root_;
168 #ifdef ENABLE_MEMLEAK_DEBUG
169 if (NativeScopeManager::DEBUG_MEMLEAK != 0 && NativeScopeManager::vmas == nullptr) {
170 ResetVmas(NativeScopeManager::vmas);
171 }
172 #endif
173 }
174
~NativeScopeManager()175 NativeScopeManager::~NativeScopeManager()
176 {
177 NativeScope* scope = root_;
178 while (scope != nullptr) {
179 NativeScope* tempScope = scope->child;
180 NativeHandle* handle = scope->handlePtr;
181 while (handle != nullptr) {
182 NativeHandle* tempHandle = handle->sibling;
183 nativeChunk_.Delete(handle->value);
184 nativeChunk_.Delete(handle);
185 handle = tempHandle;
186 }
187 delete scope;
188 scope = tempScope;
189 }
190 root_ = nullptr;
191 current_ = nullptr;
192 }
193
Open()194 NativeScope* NativeScopeManager::Open()
195 {
196 if (current_ == nullptr) {
197 HILOG_ERROR("current scope is null when open scope");
198 return nullptr;
199 }
200
201 auto scope = new NativeScope();
202 nativeChunk_.PushChunkStats(current_);
203 if (scope != nullptr) {
204 current_->child = scope;
205 scope->parent = current_;
206 current_ = scope;
207 }
208
209 return scope;
210 }
211
Close(NativeScope * scope,bool needReset)212 void NativeScopeManager::Close(NativeScope* scope, bool needReset)
213 {
214 if ((scope == nullptr) || (scope == root_)) {
215 return;
216 }
217 bool alreadyPop = false;
218 if (scope == current_) {
219 current_ = scope->parent;
220 } else {
221 nativeChunk_.RemoveStats(scope);
222 alreadyPop = true;
223 }
224
225 scope->parent->child = scope->child;
226
227 NativeHandle* handle = scope->handlePtr;
228 while (handle != nullptr) {
229 scope->handlePtr = handle->sibling;
230 nativeChunk_.Delete(handle->value);
231 nativeChunk_.Delete(handle);
232 handle = scope->handlePtr;
233 }
234 if (!alreadyPop) {
235 if (needReset) {
236 nativeChunk_.PopChunkStatsAndReset();
237 } else {
238 nativeChunk_.PopChunkStats();
239 }
240 }
241 delete scope;
242 }
243
OpenEscape()244 NativeScope* NativeScopeManager::OpenEscape()
245 {
246 NativeScope* scope = Open();
247 if (scope != nullptr) {
248 scope->escaped = true;
249 }
250 return scope;
251 }
252
CloseEscape(NativeScope * scope)253 void NativeScopeManager::CloseEscape(NativeScope* scope)
254 {
255 if (scope == nullptr) {
256 return;
257 }
258 Close(scope, false);
259 }
260
Escape(NativeScope * scope,NativeValue * value)261 NativeValue* NativeScopeManager::Escape(NativeScope* scope, NativeValue* value)
262 {
263 NativeValue* result = nullptr;
264
265 if ((scope == nullptr) || (value == nullptr)) {
266 return result;
267 }
268
269 NativeHandle* handle = scope->handlePtr;
270 NativeHandle* temp = nullptr;
271 while (handle != nullptr && scope->escaped) {
272 if (handle->value == value) {
273 if (temp == nullptr) {
274 scope->handlePtr = handle->sibling;
275 } else {
276 temp->sibling = handle->sibling;
277 }
278 if (scope->parent->handlePtr == nullptr) {
279 scope->parent->handlePtr = handle;
280 handle->sibling = nullptr;
281 } else {
282 handle->sibling = scope->parent->handlePtr;
283 scope->parent->handlePtr = handle;
284 }
285 scope->handleCount--;
286 scope->parent->handleCount++;
287 result = scope->parent->handlePtr->value;
288 break;
289 }
290 temp = handle;
291 handle = handle->sibling;
292 }
293 return result;
294 }
295
CreateHandle(NativeValue * value)296 void NativeScopeManager::CreateHandle(NativeValue* value)
297 {
298 if (current_ == nullptr) {
299 HILOG_ERROR("current scope is null when create handle");
300 return;
301 }
302 auto handlePtr = nativeChunk_.New<NativeHandle>();
303 if (handlePtr == nullptr) {
304 HILOG_ERROR("create handle ptr failed");
305 return;
306 }
307 if (current_->handlePtr == nullptr) {
308 current_->handlePtr = handlePtr;
309 current_->handlePtr->value = value;
310 current_->handlePtr->sibling = nullptr;
311 } else {
312 handlePtr->sibling = current_->handlePtr;
313 handlePtr->value = value;
314 current_->handlePtr = handlePtr;
315 }
316 current_->handleCount++;
317 #ifdef ENABLE_MEMLEAK_DEBUG
318 if (NativeScopeManager::DEBUG_MEMLEAK != 0 && current_ == root_) {
319 HILOG_ERROR(
320 "MEMLEAK: size=%{public}" SCNdPTR ", total=%{public}" SCNdPTR, sizeof(*value), current_->handleCount);
321 if (NativeScopeManager::vmas != nullptr && !BackTrace(*NativeScopeManager::vmas)) {
322 return;
323 }
324 ResetVmas(NativeScopeManager::vmas);
325 }
326 #endif
327 }
328