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 "runtime_stack_range.h"
17
18 #include <cassert>
19 #include <csignal>
20 #include <cstdio>
21 #include <cstring>
22 #include <iostream>
23 #include <pthread.h>
24 #include <string>
25 #include <sys/types.h>
26 #include <unistd.h>
27 #include "get_thread_id.h"
28
29 namespace {
30 constexpr int BASE_MIN = 2;
31 constexpr int BASE_CENTRE = 10;
32 constexpr int BASE_MAX = 16;
33
34 struct StackScope {
35 const char* start;
36 const char* end;
37 };
38 static StackScope g_mainStack;
39 } // namespace
40
GetThreadRuntimeStackRange(const char ** start,const char ** end)41 static void GetThreadRuntimeStackRange(const char** start, const char** end)
42 {
43 *start = nullptr;
44 *end = nullptr;
45 pthread_t tid = pthread_self();
46 pthread_attr_t attr;
47 if (pthread_getattr_np(tid, &attr) == 0) {
48 char* stackAddr = nullptr;
49 size_t stackSize;
50 if (pthread_attr_getstack(&attr, reinterpret_cast<void**>(const_cast<char**>(start)), &stackSize) == 0) {
51 *end = *start + stackSize;
52 }
53 pthread_attr_destroy(&attr);
54 }
55 }
56
CvtStrToInt(const char * str,int base)57 static long long CvtStrToInt(const char* str, int base)
58 {
59 long long result = 0;
60 if (base >= BASE_MIN && base <= BASE_CENTRE) {
61 while (*str) {
62 if (*str >= '0' && *str <= '0' + base - 1) {
63 result = result * base + static_cast<long long>((*str) - '0');
64 } else {
65 break;
66 }
67 ++str;
68 }
69 } else if (base > BASE_CENTRE && base <= BASE_MAX) {
70 while (*str) {
71 if (*str >= '0' && *str <= '0' + base - 1) {
72 result = result * base + static_cast<long long>(*str) - '0';
73 } else if (*str >= 'a' && *str <= 'a' + base - 0x0a - 1) {
74 result = result * base + static_cast<long long>(*str) - 'a' + 0x0a;
75 } else if (*str >= 'A' && *str <= 'A' + base - 0x0a - 1) {
76 result = result * base + static_cast<long long>(*str) - 'A' + 0x0a;
77 } else {
78 break;
79 }
80 ++str;
81 }
82 } else {
83 assert(0);
84 result = 0;
85 }
86 return result;
87 }
88
IsEmptyString(const std::string & str)89 static int IsEmptyString(const std::string& str)
90 {
91 size_t idx = 0;
92 size_t size = str.size();
93 while (idx < size) {
94 if (!isspace(static_cast<unsigned char>(str[idx])) && str[idx] != 0) {
95 return 0;
96 }
97 ++idx;
98 }
99 return 1;
100 }
101
GetAnUnlimitedLine(FILE * fp,std::string & buf)102 static void GetAnUnlimitedLine(FILE* fp, std::string& buf)
103 {
104 if (!fp) {
105 buf.resize(0);
106 return;
107 }
108 char* retLine = nullptr;
109 if (buf.size() == 0) {
110 buf.resize(INIT_LINE_SIZE);
111 }
112
113 int offset = 0;
114 int length = 0;
115 do {
116 if (offset + length >= static_cast<int>(buf.size())) {
117 buf.resize(buf.size() + INC_LINE_SIZE);
118 }
119 retLine = fgets(&buf[0] + offset, buf.size() - offset, fp);
120 if (retLine == nullptr) {
121 break;
122 }
123 length = static_cast<int>(strlen(&buf[0] + offset));
124 if (offset + length - 1 >= 0 && buf[offset + length - 1] == '\n') {
125 break;
126 }
127 offset += length;
128 } while (1);
129 }
130
GetMainThreadRuntimeStackRange()131 void GetMainThreadRuntimeStackRange()
132 {
133 std::string line;
134 FILE* fp = fopen("/proc/self/maps", "re");
135 if (fp == nullptr) {
136 return;
137 }
138 while (!feof(fp)) {
139 GetAnUnlimitedLine(fp, line);
140 if (IsEmptyString(line)) {
141 continue;
142 }
143 std::string::size_type pos = line.find("[stack]");
144 if (pos != static_cast<std::string::size_type>(-1)) {
145 std::string::size_type concatPos = line.find('-');
146 if (concatPos == static_cast<std::string::size_type>(-1)) {
147 continue;
148 }
149 g_mainStack.start = reinterpret_cast<char*>(CvtStrToInt(line.c_str(), BASE_MAX));
150 g_mainStack.end = reinterpret_cast<char*>(CvtStrToInt(line.c_str() + concatPos + 1, BASE_MAX));
151
152 break;
153 }
154 }
155 if (fclose(fp) != 0) {
156 printf("fclose failed.\n");
157 }
158 }
159
IfContained(const char * start,const char * end,const char * ptr)160 static bool IfContained(const char* start, const char* end, const char* ptr)
161 {
162 bool ret = (ptr >= start && ptr < end);
163 return ret;
164 }
165
GetRuntimeSigalAltStackRange(char ** start,char ** end)166 static void GetRuntimeSigalAltStackRange(char** start, char** end)
167 {
168 *start = nullptr;
169 *end = nullptr;
170
171 stack_t altStack;
172
173 if (sigaltstack(nullptr, &altStack) != -1) {
174 if ((altStack.ss_flags & SS_ONSTACK) != 0) {
175 *start = static_cast<char*>(altStack.ss_sp);
176 *end = static_cast<char*>(altStack.ss_sp) + altStack.ss_size;
177 }
178 }
179 }
180
IfSubThread(pid_t pid,pid_t tid)181 static bool IfSubThread(pid_t pid, pid_t tid)
182 {
183 return pid != tid;
184 }
185
GetRuntimeStackEnd(const char * stackptr,const char ** end,pid_t pid,pid_t tid)186 void GetRuntimeStackEnd(const char* stackptr, const char** end, pid_t pid, pid_t tid)
187 {
188 const char* start = nullptr;
189 *end = nullptr;
190 bool isSubThread = IfSubThread(pid, tid);
191 if (isSubThread) {
192 GetThreadRuntimeStackRange(&start, end);
193 } else {
194 start = g_mainStack.start;
195 *end = g_mainStack.end;
196 }
197 if (!IfContained(start, *end, stackptr)) {
198 char *sigStackStart = nullptr;
199 char *sigStackEnd = nullptr;
200 GetRuntimeSigalAltStackRange(&sigStackStart, &sigStackEnd);
201 if (IfContained(sigStackStart, sigStackEnd, stackptr)) {
202 *end = sigStackEnd;
203 } else if (!(!isSubThread && stackptr < *end)) {
204 *end = nullptr;
205 }
206 }
207 }
208