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