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