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 "get_thread_id.h" 23#include "utilities.h" 24 25namespace { 26constexpr int BASE_MIN = 2; 27constexpr int BASE_CENTRE = 10; 28constexpr int BASE_MAX = 16; 29 30struct StackScope { 31 const char* start; 32 const char* end; 33}; 34struct StandardLibrary { 35 StandardLibrary(uint64_t begin, uint64_t end, const std::string& name) 36 : soBegin_(begin), soEnd_(end), name_(name) 37 {} 38 uint64_t soBegin_; 39 uint64_t soEnd_; 40 std::string name_; 41}; 42 43static StackScope g_mainStack; 44static std::map<std::string, StandardLibrary> g_stdLib; 45} // namespace 46 47static void GetThreadRuntimeStackRange(const char** start, const char** end) 48{ 49 *start = nullptr; 50 *end = nullptr; 51 pthread_t tid = pthread_self(); 52 pthread_attr_t attr; 53 if (pthread_getattr_np(tid, &attr) == 0) { 54 size_t stackSize; 55 if (pthread_attr_getstack(&attr, reinterpret_cast<void**>(const_cast<char**>(start)), &stackSize) == 0) { 56 *end = *start + stackSize; 57 } 58 pthread_attr_destroy(&attr); 59 } 60} 61 62static long long CvtStrToInt(const char* str, int base) 63{ 64 long long result = 0; 65 if (base >= BASE_MIN && base <= BASE_CENTRE) { 66 while (*str) { 67 if (*str >= '0' && *str <= '0' + base - 1) { 68 result = result * base + static_cast<long long>((*str) - '0'); 69 } else { 70 break; 71 } 72 ++str; 73 } 74 } else if (base > BASE_CENTRE && base <= BASE_MAX) { 75 while (*str) { 76 if (*str >= '0' && *str <= '0' + base - 1) { 77 result = result * base + static_cast<long long>(*str) - '0'; 78 } else if (*str >= 'a' && *str <= 'a' + base - 0x0a - 1) { 79 result = result * base + static_cast<long long>(*str) - 'a' + 0x0a; 80 } else if (*str >= 'A' && *str <= 'A' + base - 0x0a - 1) { 81 result = result * base + static_cast<long long>(*str) - 'A' + 0x0a; 82 } else { 83 break; 84 } 85 ++str; 86 } 87 } else { 88 result = 0; 89 } 90 return result; 91} 92 93static int IsEmptyString(const std::string& str) 94{ 95 size_t idx = 0; 96 size_t size = str.size(); 97 while (idx < size) { 98 if (!isspace(static_cast<unsigned char>(str[idx])) && str[idx] != 0) { 99 return 0; 100 } 101 ++idx; 102 } 103 return 1; 104} 105 106static void GetAnUnlimitedLine(FILE* fp, std::string& buf) 107{ 108 if (!fp) { 109 buf.resize(0); 110 return; 111 } 112 char* retLine = nullptr; 113 if (buf.size() == 0) { 114 buf.resize(INIT_LINE_SIZE); 115 } 116 117 int offset = 0; 118 int length = 0; 119 do { 120 if (offset + length >= static_cast<int>(buf.size())) { 121 buf.resize(buf.size() + INC_LINE_SIZE); 122 } 123 retLine = fgets(&buf[0] + offset, buf.size() - offset, fp); 124 if (retLine == nullptr) { 125 break; 126 } 127 length = static_cast<int>(strlen(&buf[0] + offset)); 128 if (offset + length - 1 >= 0 && buf[offset + length - 1] == '\n') { 129 break; 130 } 131 offset += length; 132 } while (1); 133} 134 135static bool IsLegalSoName(const std::string &fileName) 136{ 137 if (fileName.front() == '[' or fileName.back() == ']' or 138 std::strncmp(fileName.c_str(), "/dev/", sizeof("/dev/")) == 0 or 139 std::strncmp(fileName.c_str(), "/memfd:", sizeof("/memfd:")) == 0 or 140 std::strncmp(fileName.c_str(), "//anon", sizeof("//anon")) == 0) { 141 return false; 142 } 143 return true; 144} 145 146static void GetStandardLibraryRange(std::string& line) 147{ 148 line.resize(strlen(line.c_str())); 149 std::vector<std::string> mapTokens = OHOS::Developtools::NativeDaemon::StringSplit(line, " "); 150 const std::string& soRange = mapTokens.front(); 151 std::string& soName = mapTokens.back(); 152 if (IsLegalSoName(soName)) { 153 std::string::size_type concatPos = soRange.find('-'); 154 uint64_t soStart = static_cast<uint64_t>(strtoll(soRange.c_str(), nullptr, BASE_MAX)); 155 uint64_t soEnd = static_cast<uint64_t>(strtoll(soRange.c_str() + concatPos + 1, nullptr, BASE_MAX)); 156 auto [iter, isExit] = g_stdLib.try_emplace(soName, StandardLibrary(soStart, soEnd, soName)); 157 if (!isExit) { 158 if (iter->second.soBegin_ > soStart) { 159 iter->second.soBegin_ = soStart; 160 } else if (iter->second.soEnd_ < soEnd) { 161 iter->second.soEnd_ = soEnd; 162 } 163 } 164 } 165} 166 167void GetMainThreadRuntimeStackRange(std::vector<std::pair<uint64_t, uint64_t>>& filterStaLibRange) 168{ 169 std::string line; 170 FILE* fp = fopen("/proc/self/maps", "re"); 171 if (fp == nullptr) { 172 return; 173 } 174 while (!feof(fp)) { 175 line.clear(); 176 GetAnUnlimitedLine(fp, line); 177 if (IsEmptyString(line)) { 178 continue; 179 } 180 if (line.find("[stack]") != std::string::npos) { 181 std::string::size_type concatPos = line.find('-'); 182 if (concatPos != std::string::npos) { 183 g_mainStack.start = reinterpret_cast<char*>(CvtStrToInt(line.c_str(), BASE_MAX)); 184 g_mainStack.end = reinterpret_cast<char*>(CvtStrToInt(line.c_str() + concatPos + 1, BASE_MAX)); 185 } 186 } else if (line.find("ld-musl") != std::string::npos || line.find("libc++") != std::string::npos) { 187 GetStandardLibraryRange(line); 188 } 189 } 190 if (fclose(fp) != 0) { 191 printf("fclose failed.\n"); 192 } 193 for (const auto& [soName, stdLibrary]: g_stdLib) { 194 filterStaLibRange.emplace_back(stdLibrary.soBegin_, stdLibrary.soEnd_); 195 } 196} 197 198static bool IfContained(const char* start, const char* end, const char* ptr) 199{ 200 bool ret = (ptr >= start && ptr < end); 201 return ret; 202} 203 204static void GetRuntimeSigalAltStackRange(char** start, char** end) 205{ 206 *start = nullptr; 207 *end = nullptr; 208 209 stack_t altStack; 210 211 if (sigaltstack(nullptr, &altStack) != -1) { 212 if ((altStack.ss_flags & SS_ONSTACK) != 0) { 213 *start = reinterpret_cast<char*>(altStack.ss_sp); 214 *end = reinterpret_cast<char*>(altStack.ss_sp) + altStack.ss_size; 215 } 216 } 217} 218 219static bool IfSubThread(pid_t pid, pid_t tid) 220{ 221 return pid != tid; 222} 223 224void GetRuntimeStackEnd(const char* stackptr, const char** end, pid_t pid, pid_t tid) 225{ 226 const char* start = nullptr; 227 *end = nullptr; 228 bool isSubThread = IfSubThread(pid, tid); 229 if (isSubThread) { 230 GetThreadRuntimeStackRange(&start, end); 231 } else { 232 start = g_mainStack.start; 233 *end = g_mainStack.end; 234 } 235 if (!IfContained(start, *end, stackptr)) { 236 char *sigStackStart = nullptr; 237 char *sigStackEnd = nullptr; 238 GetRuntimeSigalAltStackRange(&sigStackStart, &sigStackEnd); 239 if (IfContained(sigStackStart, sigStackEnd, stackptr)) { 240 *end = sigStackEnd; 241 } else if (!(!isSubThread && stackptr < *end)) { 242 *end = nullptr; 243 } 244 } 245} 246