1 /*
2 * Copyright (c) Huawei Technologies Co., Ltd. 2021. 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 "runtime_stack_range.h"
17
18 #include <csignal>
19 #include <cstring>
20 #include <map>
21 #include <sys/types.h>
22 #include "c/executor_task.h"
23 #include "get_thread_id.h"
24 #include "utilities.h"
25
26 namespace {
27 constexpr int BASE_MAX = 16;
28
29 struct StandardLibrary {
StandardLibrary__anonc3ddc7af0111::StandardLibrary30 StandardLibrary(uint64_t begin, uint64_t end, const std::string& name)
31 : soBegin_(begin), soEnd_(end), name_(name)
32 {}
33 uint64_t soBegin_;
34 uint64_t soEnd_;
35 std::string name_;
36 };
37
38 static std::map<std::string, StandardLibrary> g_stdLib;
39 static std::map<std::string, StandardLibrary> g_targetLib;
40 static uintptr_t g_stackMainStart = 0;
41 static uintptr_t g_stackMainEnd = 0;
42 } // namespace
43
GetMainStackRange(uintptr_t & stackBottom,uintptr_t & stackTop)44 static bool GetMainStackRange(uintptr_t& stackBottom, uintptr_t& stackTop)
45 {
46 stackBottom = g_stackMainStart;
47 stackTop = g_stackMainEnd;
48 return (stackBottom != 0 && stackTop != 0);
49 }
50
GetSubStackRange(uintptr_t & stackBottom,uintptr_t & stackTop)51 static bool GetSubStackRange(uintptr_t& stackBottom, uintptr_t& stackTop)
52 {
53 bool ret = false;
54 pthread_attr_t tattr;
55 void* base = nullptr;
56 size_t size = 0;
57 if (pthread_getattr_np(pthread_self(), &tattr) != 0) {
58 return ret;
59 }
60 if (pthread_attr_getstack(&tattr, &base, &size) == 0) {
61 stackBottom = reinterpret_cast<uintptr_t>(base);
62 stackTop = reinterpret_cast<uintptr_t>(base) + size;
63 ret = true;
64 }
65 pthread_attr_destroy(&tattr);
66 return ret;
67 }
68
GetSigAltStackRange(uintptr_t & stackBottom,uintptr_t & stackTop)69 static bool GetSigAltStackRange(uintptr_t& stackBottom, uintptr_t& stackTop)
70 {
71 bool ret = false;
72 stack_t altStack;
73 if (sigaltstack(nullptr, &altStack) != -1) {
74 if ((static_cast<uint32_t>(altStack.ss_flags) & SS_ONSTACK) != 0) {
75 stackBottom = reinterpret_cast<uintptr_t>(altStack.ss_sp);
76 stackTop = reinterpret_cast<uintptr_t>(altStack.ss_sp) + altStack.ss_size;
77 ret = true;
78 }
79 }
80 return ret;
81 }
82
GetCoroutineStackRange(uintptr_t & stackBottom,uintptr_t & stackTop)83 static bool GetCoroutineStackRange(uintptr_t& stackBottom, uintptr_t& stackTop)
84 {
85 bool ret = false;
86 void* stackAddr = nullptr;
87 size_t coroutineStackSize = 0;
88 if (ffrt_get_current_coroutine_stack(&stackAddr, &coroutineStackSize)) {
89 stackBottom = reinterpret_cast<uintptr_t>(stackAddr);
90 stackTop = stackBottom + coroutineStackSize;
91 ret = true;
92 }
93 return ret;
94 }
95
IsLegalSoName(const char * fileName)96 bool IsLegalSoName(const char *fileName)
97 {
98 if (fileName == nullptr) {
99 return false;
100 }
101 size_t fileNameLength = strlen(fileName);
102 if (fileNameLength == 0) {
103 return false;
104 }
105 if (fileName[0] == '[' || fileName[strlen(fileName) - 1] == ']' ||
106 std::strncmp(fileName, "/dev/", sizeof("/dev/") - 1) == 0 ||
107 std::strncmp(fileName, "/memfd:", sizeof("/memfd:") - 1) == 0 ||
108 std::strncmp(fileName, "//anon", sizeof("//anon") - 1) == 0) {
109 return false;
110 }
111 return true;
112 }
113
GetStandardLibraryRange(std::string & line,bool targeted=false)114 static void GetStandardLibraryRange(std::string& line, bool targeted = false)
115 {
116 line.resize(strlen(line.c_str()));
117 std::vector<std::string> mapTokens = OHOS::Developtools::NativeDaemon::StringSplit(line, " ");
118 const std::string& soRange = mapTokens.front();
119 std::string& soName = mapTokens.back();
120 if (IsLegalSoName(soName.c_str())) {
121 std::string::size_type concatPos = soRange.find('-');
122 uint64_t soStart = static_cast<uint64_t>(strtoll(soRange.c_str(), nullptr, BASE_MAX));
123 uint64_t soEnd = static_cast<uint64_t>(strtoll(soRange.c_str() + concatPos + 1, nullptr, BASE_MAX));
124 auto iter = g_stdLib.begin();
125 bool isExit = false;
126 if (targeted) {
127 std::tie(iter, isExit) = g_targetLib.try_emplace(soName, StandardLibrary(soStart, soEnd, soName));
128 } else {
129 std::tie(iter, isExit) = g_stdLib.try_emplace(soName, StandardLibrary(soStart, soEnd, soName));
130 }
131 if (!isExit) {
132 if (iter->second.soBegin_ > soStart) {
133 iter->second.soBegin_ = soStart;
134 } else if (iter->second.soEnd_ < soEnd) {
135 iter->second.soEnd_ = soEnd;
136 }
137 }
138 }
139 }
140
ParseTargetedMaps(std::atomic<Range> & targetedRange,std::string targetedLib)141 bool ParseTargetedMaps(std::atomic<Range>& targetedRange, std::string targetedLib)
142 {
143 FILE* fp = fopen("/proc/self/maps", "r");
144 bool ret = false;
145 if (fp == nullptr) {
146 return ret;
147 }
148 char mapInfo[256] = {0}; // 256: map info size
149 int pos = 0;
150 uint64_t begin = 0;
151 uint64_t end = 0;
152 uint64_t offset = 0;
153 char perms[5] = {0}; // 5:rwxp
154 while (fgets(mapInfo, sizeof(mapInfo), fp) != nullptr) {
155 if (strstr(mapInfo, targetedLib.c_str()) != nullptr) {
156 std::string lineStr = mapInfo;
157 GetStandardLibraryRange(lineStr, true);
158 ret = true;
159 }
160 }
161 if (fclose(fp) != 0) {
162 printf("fclose failed.\n");
163 }
164 auto range = targetedRange.load();
165 for (const auto& [soName, stdLibrary]: g_targetLib) {
166 range.start = stdLibrary.soBegin_;
167 range.end = stdLibrary.soEnd_;
168 }
169 targetedRange.store(range);
170 return ret;
171 }
172
GetRuntimeStackRange(const uintptr_t stackPtr,uintptr_t & stackBottom,uintptr_t & stackTop,bool isMainThread)173 bool GetRuntimeStackRange(const uintptr_t stackPtr, uintptr_t& stackBottom, uintptr_t& stackTop, bool isMainThread)
174 {
175 bool ret = false;
176 if (isMainThread) {
177 ret = GetMainStackRange(stackBottom, stackTop);
178 } else {
179 ret = GetSubStackRange(stackBottom, stackTop);
180 if (stackPtr < stackBottom || stackPtr >= stackTop) {
181 ret = GetSigAltStackRange(stackBottom, stackTop);
182 }
183 }
184 if (stackPtr < stackBottom || stackPtr >= stackTop) {
185 ret = GetCoroutineStackRange(stackBottom, stackTop);
186 }
187 return ret && (stackPtr >= stackBottom && stackPtr < stackTop);
188 }
189
ParseSelfMaps(std::vector<std::pair<uint64_t,uint64_t>> & filterStaLibRange)190 bool ParseSelfMaps(std::vector<std::pair<uint64_t, uint64_t>>& filterStaLibRange)
191 {
192 FILE* fp = fopen("/proc/self/maps", "r");
193 bool ret = false;
194 if (fp == nullptr) {
195 return ret;
196 }
197 char mapInfo[256] = {0}; // 256: map info size
198 int pos = 0;
199 uint64_t begin = 0;
200 uint64_t end = 0;
201 uint64_t offset = 0;
202 char perms[5] = {0}; // 5:rwxp
203 while (fgets(mapInfo, sizeof(mapInfo), fp) != nullptr) {
204 if (strstr(mapInfo, "[stack]") != nullptr) {
205 if (sscanf_s(mapInfo, "%" SCNxPTR "-%" SCNxPTR " %4S %" SCNxPTR " %*X:%*X %*d%n", &begin, &end,
206 &perms, sizeof(perms), &offset, &pos) != 4) { // 4:scan size
207 continue;
208 }
209 g_stackMainStart = static_cast<uintptr_t>(begin);
210 g_stackMainEnd = static_cast<uintptr_t>(end);
211 ret = true;
212 } else if (strstr(mapInfo, "ld-musl") != nullptr || strstr(mapInfo, "libc++") != nullptr) {
213 std::string lineStr = mapInfo;
214 GetStandardLibraryRange(lineStr);
215 }
216 }
217 if (fclose(fp) != 0) {
218 printf("fclose failed.\n");
219 }
220 for (const auto& [soName, stdLibrary]: g_stdLib) {
221 filterStaLibRange.emplace_back(stdLibrary.soBegin_, stdLibrary.soEnd_);
222 }
223 return ret;
224 }
225
ParseEvent(const std::string & filePath,std::vector<std::pair<uint64_t,uint64_t>> & filterStaLibRange,const NameData & curRawData)226 void ParseEvent(const std::string& filePath, std::vector<std::pair<uint64_t, uint64_t>>& filterStaLibRange,
227 const NameData& curRawData)
228 {
229 if (curRawData.addr == nullptr) {
230 return;
231 }
232 uint64_t soStart = reinterpret_cast<uint64_t>(curRawData.addr);
233 uint64_t soEnd = soStart + static_cast<uint64_t>(curRawData.mallocSize);
234 auto [iter, success] = g_stdLib.try_emplace(filePath, StandardLibrary(soStart, soEnd, filePath));
235 if (!success) {
236 if (iter->second.soBegin_ > soStart) {
237 iter->second.soBegin_ = soStart;
238 } else if (iter->second.soEnd_ < soEnd) {
239 iter->second.soEnd_ = soEnd;
240 }
241 auto it = filterStaLibRange.rbegin();
242 for (; it != filterStaLibRange.rend(); ++it) {
243 if (it->first == iter->second.soBegin_) {
244 break;
245 }
246 }
247 it->first = iter->second.soBegin_;
248 it->second = iter->second.soEnd_;
249 } else {
250 filterStaLibRange.emplace_back(iter->second.soBegin_, iter->second.soEnd_);
251 }
252 }
253