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__anonc0be73cd0111::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 uintptr_t g_stackMainStart = 0;
40 static uintptr_t g_stackMainEnd = 0;
41 } // namespace
42
GetMainStackRange(uintptr_t & stackBottom,uintptr_t & stackTop)43 static bool GetMainStackRange(uintptr_t& stackBottom, uintptr_t& stackTop)
44 {
45 stackBottom = g_stackMainStart;
46 stackTop = g_stackMainEnd;
47 return (stackBottom != 0 && stackTop != 0);
48 }
49
GetSubStackRange(uintptr_t & stackBottom,uintptr_t & stackTop)50 static bool GetSubStackRange(uintptr_t& stackBottom, uintptr_t& stackTop)
51 {
52 bool ret = false;
53 pthread_attr_t tattr;
54 void* base = nullptr;
55 size_t size = 0;
56 if (pthread_getattr_np(pthread_self(), &tattr) != 0) {
57 return ret;
58 }
59 if (pthread_attr_getstack(&tattr, &base, &size) == 0) {
60 stackBottom = reinterpret_cast<uintptr_t>(base);
61 stackTop = reinterpret_cast<uintptr_t>(base) + size;
62 ret = true;
63 }
64 pthread_attr_destroy(&tattr);
65 return ret;
66 }
67
GetSigAltStackRange(uintptr_t & stackBottom,uintptr_t & stackTop)68 static bool GetSigAltStackRange(uintptr_t& stackBottom, uintptr_t& stackTop)
69 {
70 bool ret = false;
71 stack_t altStack;
72 if (sigaltstack(nullptr, &altStack) != -1) {
73 if ((static_cast<uint32_t>(altStack.ss_flags) & SS_ONSTACK) != 0) {
74 stackBottom = reinterpret_cast<uintptr_t>(altStack.ss_sp);
75 stackTop = reinterpret_cast<uintptr_t>(altStack.ss_sp) + altStack.ss_size;
76 ret = true;
77 }
78 }
79 return ret;
80 }
81
GetCoroutineStackRange(uintptr_t & stackBottom,uintptr_t & stackTop)82 static bool GetCoroutineStackRange(uintptr_t& stackBottom, uintptr_t& stackTop)
83 {
84 bool ret = false;
85 void* stackAddr = nullptr;
86 size_t coroutineStackSize = 0;
87 if (ffrt_get_current_coroutine_stack(&stackAddr, &coroutineStackSize)) {
88 stackBottom = reinterpret_cast<uintptr_t>(stackAddr);
89 stackTop = stackBottom + coroutineStackSize;
90 ret = true;
91 }
92 return ret;
93 }
94
IsLegalSoName(const std::string & fileName)95 bool IsLegalSoName(const std::string &fileName)
96 {
97 if (fileName.front() == '[' || fileName.back() == ']' ||
98 std::strncmp(fileName.c_str(), "/dev/", sizeof("/dev/")) == 0 ||
99 std::strncmp(fileName.c_str(), "/memfd:", sizeof("/memfd:")) == 0 ||
100 std::strncmp(fileName.c_str(), "//anon", sizeof("//anon")) == 0) {
101 return false;
102 }
103 return true;
104 }
105
GetStandardLibraryRange(std::string & line)106 static void GetStandardLibraryRange(std::string& line)
107 {
108 line.resize(strlen(line.c_str()));
109 std::vector<std::string> mapTokens = OHOS::Developtools::NativeDaemon::StringSplit(line, " ");
110 const std::string& soRange = mapTokens.front();
111 std::string& soName = mapTokens.back();
112 if (IsLegalSoName(soName)) {
113 std::string::size_type concatPos = soRange.find('-');
114 uint64_t soStart = static_cast<uint64_t>(strtoll(soRange.c_str(), nullptr, BASE_MAX));
115 uint64_t soEnd = static_cast<uint64_t>(strtoll(soRange.c_str() + concatPos + 1, nullptr, BASE_MAX));
116 auto [iter, isExit] = g_stdLib.try_emplace(soName, StandardLibrary(soStart, soEnd, soName));
117 if (!isExit) {
118 if (iter->second.soBegin_ > soStart) {
119 iter->second.soBegin_ = soStart;
120 } else if (iter->second.soEnd_ < soEnd) {
121 iter->second.soEnd_ = soEnd;
122 }
123 }
124 }
125 }
126
GetRuntimeStackRange(const uintptr_t stackPtr,uintptr_t & stackBottom,uintptr_t & stackTop,bool isMainThread)127 bool GetRuntimeStackRange(const uintptr_t stackPtr, uintptr_t& stackBottom, uintptr_t& stackTop, bool isMainThread)
128 {
129 bool ret = false;
130 if (isMainThread) {
131 ret = GetMainStackRange(stackBottom, stackTop);
132 } else {
133 ret = GetSubStackRange(stackBottom, stackTop);
134 if (stackPtr < stackBottom || stackPtr >= stackTop) {
135 ret = GetSigAltStackRange(stackBottom, stackTop);
136 }
137 }
138 if (stackPtr < stackBottom || stackPtr >= stackTop) {
139 ret = GetCoroutineStackRange(stackBottom, stackTop);
140 }
141 return ret && (stackPtr >= stackBottom && stackPtr < stackTop);
142 }
143
ParseSelfMaps(std::vector<std::pair<uint64_t,uint64_t>> & filterStaLibRange)144 bool ParseSelfMaps(std::vector<std::pair<uint64_t, uint64_t>>& filterStaLibRange)
145 {
146 FILE* fp = fopen("/proc/self/maps", "r");
147 bool ret = false;
148 if (fp == nullptr) {
149 return ret;
150 }
151 char mapInfo[256] = {0}; // 256: map info size
152 int pos = 0;
153 uint64_t begin = 0;
154 uint64_t end = 0;
155 uint64_t offset = 0;
156 char perms[5] = {0}; // 5:rwxp
157 while (fgets(mapInfo, sizeof(mapInfo), fp) != nullptr) {
158 if (strstr(mapInfo, "[stack]") != nullptr) {
159 if (sscanf_s(mapInfo, "%" SCNxPTR "-%" SCNxPTR " %4S %" SCNxPTR " %*X:%*X %*d%n", &begin, &end,
160 &perms, sizeof(perms), &offset, &pos) != 4) { // 4:scan size
161 continue;
162 }
163 g_stackMainStart = static_cast<uintptr_t>(begin);
164 g_stackMainEnd = static_cast<uintptr_t>(end);
165 ret = true;
166 } else if (strstr(mapInfo, "ld-musl") != nullptr || strstr(mapInfo, "libc++") != nullptr) {
167 std::string lineStr = mapInfo;
168 GetStandardLibraryRange(lineStr);
169 }
170 }
171 if (fclose(fp) != 0) {
172 printf("fclose failed.\n");
173 }
174 for (const auto& [soName, stdLibrary]: g_stdLib) {
175 filterStaLibRange.emplace_back(stdLibrary.soBegin_, stdLibrary.soEnd_);
176 }
177 return ret;
178 }
179
ParseEvent(const std::string & filePath,std::vector<std::pair<uint64_t,uint64_t>> & filterStaLibRange,const NameData & curRawData)180 void ParseEvent(const std::string& filePath, std::vector<std::pair<uint64_t, uint64_t>>& filterStaLibRange,
181 const NameData& curRawData)
182 {
183 if (curRawData.addr == nullptr) {
184 return;
185 }
186 uint64_t soStart = reinterpret_cast<uint64_t>(curRawData.addr);
187 uint64_t soEnd = soStart + static_cast<uint64_t>(curRawData.mallocSize);
188 auto [iter, success] = g_stdLib.try_emplace(filePath, StandardLibrary(soStart, soEnd, filePath));
189 if (!success) {
190 if (iter->second.soBegin_ > soStart) {
191 iter->second.soBegin_ = soStart;
192 } else if (iter->second.soEnd_ < soEnd) {
193 iter->second.soEnd_ = soEnd;
194 }
195 auto it = filterStaLibRange.rbegin();
196 for (; it != filterStaLibRange.rend(); ++it) {
197 if (it->first == iter->second.soBegin_) {
198 break;
199 }
200 }
201 it->first = iter->second.soBegin_;
202 it->second = iter->second.soEnd_;
203 } else {
204 filterStaLibRange.emplace_back(iter->second.soBegin_, iter->second.soEnd_);
205 }
206 }
207