Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2015 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include "environment.h" |
| 18 | |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 19 | #include <inttypes.h> |
| 20 | #include <stdio.h> |
Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 21 | #include <stdlib.h> |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 22 | #include <sys/utsname.h> |
Yabin Cui | c848560 | 2015-08-20 15:04:39 -0700 | [diff] [blame] | 23 | |
| 24 | #include <limits> |
Yabin Cui | cb4c17e | 2015-10-26 16:15:29 -0700 | [diff] [blame] | 25 | #include <set> |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 26 | #include <unordered_map> |
Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 27 | #include <vector> |
| 28 | |
Elliott Hughes | 66dd09e | 2015-12-04 14:00:57 -0800 | [diff] [blame] | 29 | #include <android-base/file.h> |
| 30 | #include <android-base/logging.h> |
Yabin Cui | 569f64a | 2016-02-05 17:32:08 -0800 | [diff] [blame] | 31 | #include <android-base/parseint.h> |
Elliott Hughes | 66dd09e | 2015-12-04 14:00:57 -0800 | [diff] [blame] | 32 | #include <android-base/strings.h> |
| 33 | #include <android-base/stringprintf.h> |
Yabin Cui | 7e5aa13 | 2016-11-16 22:47:53 +0000 | [diff] [blame] | 34 | #include <procinfo/process.h> |
Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 35 | |
Yabin Cui | ebf79f3 | 2016-06-01 15:39:39 -0700 | [diff] [blame] | 36 | #if defined(__ANDROID__) |
| 37 | #include <sys/system_properties.h> |
| 38 | #endif |
| 39 | |
Yabin Cui | 8f62251 | 2015-05-05 19:58:07 -0700 | [diff] [blame] | 40 | #include "read_elf.h" |
Yabin Cui | 003b245 | 2016-09-29 15:32:45 -0700 | [diff] [blame] | 41 | #include "thread_tree.h" |
Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 42 | #include "utils.h" |
| 43 | |
Yabin Cui | ffaa912 | 2016-01-15 15:25:48 -0800 | [diff] [blame] | 44 | class LineReader { |
| 45 | public: |
Chih-Hung Hsieh | 4dd14a3 | 2016-04-25 14:08:08 -0700 | [diff] [blame] | 46 | explicit LineReader(FILE* fp) : fp_(fp), buf_(nullptr), bufsize_(0) { |
Yabin Cui | ffaa912 | 2016-01-15 15:25:48 -0800 | [diff] [blame] | 47 | } |
| 48 | |
| 49 | ~LineReader() { |
| 50 | free(buf_); |
| 51 | fclose(fp_); |
| 52 | } |
| 53 | |
| 54 | char* ReadLine() { |
| 55 | if (getline(&buf_, &bufsize_, fp_) != -1) { |
| 56 | return buf_; |
| 57 | } |
| 58 | return nullptr; |
| 59 | } |
| 60 | |
| 61 | size_t MaxLineSize() { |
| 62 | return bufsize_; |
| 63 | } |
| 64 | |
| 65 | private: |
| 66 | FILE* fp_; |
| 67 | char* buf_; |
| 68 | size_t bufsize_; |
| 69 | }; |
| 70 | |
Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 71 | std::vector<int> GetOnlineCpus() { |
| 72 | std::vector<int> result; |
| 73 | FILE* fp = fopen("/sys/devices/system/cpu/online", "re"); |
| 74 | if (fp == nullptr) { |
| 75 | PLOG(ERROR) << "can't open online cpu information"; |
| 76 | return result; |
| 77 | } |
| 78 | |
| 79 | LineReader reader(fp); |
| 80 | char* line; |
| 81 | if ((line = reader.ReadLine()) != nullptr) { |
Yabin Cui | cb4c17e | 2015-10-26 16:15:29 -0700 | [diff] [blame] | 82 | result = GetCpusFromString(line); |
Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 83 | } |
| 84 | CHECK(!result.empty()) << "can't get online cpu information"; |
| 85 | return result; |
| 86 | } |
| 87 | |
Yabin Cui | cb4c17e | 2015-10-26 16:15:29 -0700 | [diff] [blame] | 88 | std::vector<int> GetCpusFromString(const std::string& s) { |
| 89 | std::set<int> cpu_set; |
Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 90 | bool have_dash = false; |
| 91 | const char* p = s.c_str(); |
| 92 | char* endp; |
Yabin Cui | cb4c17e | 2015-10-26 16:15:29 -0700 | [diff] [blame] | 93 | int last_cpu; |
Chih-Hung Hsieh | 7d6c8ab | 2016-04-15 16:12:03 -0700 | [diff] [blame] | 94 | int cpu; |
Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 95 | // Parse line like: 0,1-3, 5, 7-8 |
Chih-Hung Hsieh | 7d6c8ab | 2016-04-15 16:12:03 -0700 | [diff] [blame] | 96 | while ((cpu = static_cast<int>(strtol(p, &endp, 10))) != 0 || endp != p) { |
Yabin Cui | cb4c17e | 2015-10-26 16:15:29 -0700 | [diff] [blame] | 97 | if (have_dash && !cpu_set.empty()) { |
| 98 | for (int t = last_cpu + 1; t < cpu; ++t) { |
| 99 | cpu_set.insert(t); |
Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 100 | } |
| 101 | } |
| 102 | have_dash = false; |
Yabin Cui | cb4c17e | 2015-10-26 16:15:29 -0700 | [diff] [blame] | 103 | cpu_set.insert(cpu); |
| 104 | last_cpu = cpu; |
Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 105 | p = endp; |
| 106 | while (!isdigit(*p) && *p != '\0') { |
| 107 | if (*p == '-') { |
| 108 | have_dash = true; |
| 109 | } |
| 110 | ++p; |
| 111 | } |
| 112 | } |
Yabin Cui | cb4c17e | 2015-10-26 16:15:29 -0700 | [diff] [blame] | 113 | return std::vector<int>(cpu_set.begin(), cpu_set.end()); |
Yabin Cui | 323e945 | 2015-04-20 18:07:17 -0700 | [diff] [blame] | 114 | } |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 115 | |
Yabin Cui | 7134f38 | 2016-03-25 17:43:43 -0700 | [diff] [blame] | 116 | static std::vector<KernelMmap> GetLoadedModules() { |
| 117 | std::vector<KernelMmap> result; |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 118 | FILE* fp = fopen("/proc/modules", "re"); |
| 119 | if (fp == nullptr) { |
| 120 | // There is no /proc/modules on Android devices, so we don't print error if failed to open it. |
| 121 | PLOG(DEBUG) << "failed to open file /proc/modules"; |
| 122 | return result; |
| 123 | } |
| 124 | LineReader reader(fp); |
| 125 | char* line; |
| 126 | while ((line = reader.ReadLine()) != nullptr) { |
| 127 | // Parse line like: nf_defrag_ipv6 34768 1 nf_conntrack_ipv6, Live 0xffffffffa0fe5000 |
| 128 | char name[reader.MaxLineSize()]; |
| 129 | uint64_t addr; |
| 130 | if (sscanf(line, "%s%*lu%*u%*s%*s 0x%" PRIx64, name, &addr) == 2) { |
Yabin Cui | 7134f38 | 2016-03-25 17:43:43 -0700 | [diff] [blame] | 131 | KernelMmap map; |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 132 | map.name = name; |
| 133 | map.start_addr = addr; |
| 134 | result.push_back(map); |
| 135 | } |
| 136 | } |
Yabin Cui | b421297 | 2016-05-25 14:08:05 -0700 | [diff] [blame] | 137 | bool all_zero = true; |
| 138 | for (const auto& map : result) { |
| 139 | if (map.start_addr != 0) { |
| 140 | all_zero = false; |
| 141 | } |
| 142 | } |
| 143 | if (all_zero) { |
| 144 | LOG(DEBUG) << "addresses in /proc/modules are all zero, so ignore kernel modules"; |
| 145 | return std::vector<KernelMmap>(); |
| 146 | } |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 147 | return result; |
| 148 | } |
| 149 | |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 150 | static void GetAllModuleFiles(const std::string& path, |
| 151 | std::unordered_map<std::string, std::string>* module_file_map) { |
Yabin Cui | 0786586 | 2016-08-22 13:39:19 -0700 | [diff] [blame] | 152 | for (const auto& name : GetEntriesInDir(path)) { |
| 153 | std::string entry_path = path + "/" + name; |
| 154 | if (IsRegularFile(entry_path) && android::base::EndsWith(name, ".ko")) { |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 155 | std::string module_name = name.substr(0, name.size() - 3); |
| 156 | std::replace(module_name.begin(), module_name.end(), '-', '_'); |
Yabin Cui | 0786586 | 2016-08-22 13:39:19 -0700 | [diff] [blame] | 157 | module_file_map->insert(std::make_pair(module_name, entry_path)); |
| 158 | } else if (IsDir(entry_path)) { |
| 159 | GetAllModuleFiles(entry_path, module_file_map); |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 160 | } |
| 161 | } |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 162 | } |
| 163 | |
Yabin Cui | 7134f38 | 2016-03-25 17:43:43 -0700 | [diff] [blame] | 164 | static std::vector<KernelMmap> GetModulesInUse() { |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 165 | utsname uname_buf; |
| 166 | if (TEMP_FAILURE_RETRY(uname(&uname_buf)) != 0) { |
| 167 | PLOG(ERROR) << "uname() failed"; |
| 168 | return std::vector<KernelMmap>(); |
| 169 | } |
| 170 | std::string linux_version = uname_buf.release; |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 171 | std::string module_dirpath = "/lib/modules/" + linux_version + "/kernel"; |
| 172 | std::unordered_map<std::string, std::string> module_file_map; |
| 173 | GetAllModuleFiles(module_dirpath, &module_file_map); |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 174 | // TODO: There is no /proc/modules or /lib/modules on Android, find methods work on it. |
| 175 | std::vector<KernelMmap> module_mmaps = GetLoadedModules(); |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 176 | for (auto& module : module_mmaps) { |
| 177 | auto it = module_file_map.find(module.name); |
| 178 | if (it != module_file_map.end()) { |
| 179 | module.filepath = it->second; |
| 180 | } |
| 181 | } |
| 182 | return module_mmaps; |
| 183 | } |
| 184 | |
Yabin Cui | 7134f38 | 2016-03-25 17:43:43 -0700 | [diff] [blame] | 185 | void GetKernelAndModuleMmaps(KernelMmap* kernel_mmap, std::vector<KernelMmap>* module_mmaps) { |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 186 | kernel_mmap->name = DEFAULT_KERNEL_MMAP_NAME; |
Yabin Cui | 7134f38 | 2016-03-25 17:43:43 -0700 | [diff] [blame] | 187 | kernel_mmap->start_addr = 0; |
| 188 | kernel_mmap->filepath = kernel_mmap->name; |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 189 | *module_mmaps = GetModulesInUse(); |
Yabin Cui | 7134f38 | 2016-03-25 17:43:43 -0700 | [diff] [blame] | 190 | for (auto& map : *module_mmaps) { |
| 191 | if (map.filepath.empty()) { |
| 192 | map.filepath = "[" + map.name + "]"; |
| 193 | } |
| 194 | } |
| 195 | |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 196 | if (module_mmaps->size() == 0) { |
Chih-Hung Hsieh | 7d6c8ab | 2016-04-15 16:12:03 -0700 | [diff] [blame] | 197 | kernel_mmap->len = std::numeric_limits<uint64_t>::max() - kernel_mmap->start_addr; |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 198 | } else { |
| 199 | std::sort( |
| 200 | module_mmaps->begin(), module_mmaps->end(), |
Yabin Cui | 7134f38 | 2016-03-25 17:43:43 -0700 | [diff] [blame] | 201 | [](const KernelMmap& m1, const KernelMmap& m2) { return m1.start_addr < m2.start_addr; }); |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 202 | // When not having enough privilege, all addresses are read as 0. |
| 203 | if (kernel_mmap->start_addr == (*module_mmaps)[0].start_addr) { |
| 204 | kernel_mmap->len = 0; |
| 205 | } else { |
| 206 | kernel_mmap->len = (*module_mmaps)[0].start_addr - kernel_mmap->start_addr - 1; |
| 207 | } |
| 208 | for (size_t i = 0; i + 1 < module_mmaps->size(); ++i) { |
| 209 | if ((*module_mmaps)[i].start_addr == (*module_mmaps)[i + 1].start_addr) { |
| 210 | (*module_mmaps)[i].len = 0; |
| 211 | } else { |
| 212 | (*module_mmaps)[i].len = |
| 213 | (*module_mmaps)[i + 1].start_addr - (*module_mmaps)[i].start_addr - 1; |
| 214 | } |
| 215 | } |
Yabin Cui | c848560 | 2015-08-20 15:04:39 -0700 | [diff] [blame] | 216 | module_mmaps->back().len = |
Chih-Hung Hsieh | 7d6c8ab | 2016-04-15 16:12:03 -0700 | [diff] [blame] | 217 | std::numeric_limits<uint64_t>::max() - module_mmaps->back().start_addr; |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 218 | } |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 219 | } |
| 220 | |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 221 | static bool ReadThreadNameAndPid(pid_t tid, std::string* comm, pid_t* pid) { |
Yabin Cui | 7e5aa13 | 2016-11-16 22:47:53 +0000 | [diff] [blame] | 222 | android::procinfo::ProcessInfo procinfo; |
| 223 | if (!android::procinfo::GetProcessInfo(tid, &procinfo)) { |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 224 | return false; |
| 225 | } |
Yabin Cui | 7e5aa13 | 2016-11-16 22:47:53 +0000 | [diff] [blame] | 226 | if (comm != nullptr) { |
| 227 | *comm = procinfo.name; |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 228 | } |
Yabin Cui | 7e5aa13 | 2016-11-16 22:47:53 +0000 | [diff] [blame] | 229 | if (pid != nullptr) { |
| 230 | *pid = procinfo.pid; |
| 231 | } |
| 232 | return true; |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 233 | } |
| 234 | |
Yabin Cui | bc2a102 | 2016-08-29 12:33:17 -0700 | [diff] [blame] | 235 | std::vector<pid_t> GetThreadsInProcess(pid_t pid) { |
Yabin Cui | b032de7 | 2015-06-17 21:15:09 -0700 | [diff] [blame] | 236 | std::vector<pid_t> result; |
Yabin Cui | 7e5aa13 | 2016-11-16 22:47:53 +0000 | [diff] [blame] | 237 | android::procinfo::GetProcessTids(pid, &result); |
Yabin Cui | b032de7 | 2015-06-17 21:15:09 -0700 | [diff] [blame] | 238 | return result; |
| 239 | } |
| 240 | |
Yabin Cui | 5f43fc4 | 2016-12-13 13:47:49 -0800 | [diff] [blame] | 241 | bool IsThreadAlive(pid_t tid) { |
| 242 | return IsDir(android::base::StringPrintf("/proc/%d", tid)); |
| 243 | } |
| 244 | |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 245 | bool GetProcessForThread(pid_t tid, pid_t* pid) { |
| 246 | return ReadThreadNameAndPid(tid, nullptr, pid); |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 247 | } |
| 248 | |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 249 | bool GetThreadName(pid_t tid, std::string* name) { |
| 250 | return ReadThreadNameAndPid(tid, name, nullptr); |
| 251 | } |
| 252 | |
| 253 | std::vector<pid_t> GetAllProcesses() { |
| 254 | std::vector<pid_t> result; |
| 255 | std::vector<std::string> entries = GetEntriesInDir("/proc"); |
| 256 | for (const auto& entry : entries) { |
| 257 | pid_t pid; |
| 258 | if (!android::base::ParseInt(entry.c_str(), &pid, 0)) { |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 259 | continue; |
| 260 | } |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 261 | result.push_back(pid); |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 262 | } |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 263 | return result; |
Yabin Cui | 7d59bb4 | 2015-05-04 20:27:57 -0700 | [diff] [blame] | 264 | } |
| 265 | |
| 266 | bool GetThreadMmapsInProcess(pid_t pid, std::vector<ThreadMmap>* thread_mmaps) { |
| 267 | std::string map_file = android::base::StringPrintf("/proc/%d/maps", pid); |
| 268 | FILE* fp = fopen(map_file.c_str(), "re"); |
| 269 | if (fp == nullptr) { |
| 270 | PLOG(DEBUG) << "can't open file " << map_file; |
| 271 | return false; |
| 272 | } |
| 273 | thread_mmaps->clear(); |
| 274 | LineReader reader(fp); |
| 275 | char* line; |
| 276 | while ((line = reader.ReadLine()) != nullptr) { |
| 277 | // Parse line like: 00400000-00409000 r-xp 00000000 fc:00 426998 /usr/lib/gvfs/gvfsd-http |
| 278 | uint64_t start_addr, end_addr, pgoff; |
| 279 | char type[reader.MaxLineSize()]; |
| 280 | char execname[reader.MaxLineSize()]; |
| 281 | strcpy(execname, ""); |
| 282 | if (sscanf(line, "%" PRIx64 "-%" PRIx64 " %s %" PRIx64 " %*x:%*x %*u %s\n", &start_addr, |
| 283 | &end_addr, type, &pgoff, execname) < 4) { |
| 284 | continue; |
| 285 | } |
| 286 | if (strcmp(execname, "") == 0) { |
| 287 | strcpy(execname, DEFAULT_EXECNAME_FOR_THREAD_MMAP); |
| 288 | } |
| 289 | ThreadMmap thread; |
| 290 | thread.start_addr = start_addr; |
| 291 | thread.len = end_addr - start_addr; |
| 292 | thread.pgoff = pgoff; |
| 293 | thread.name = execname; |
| 294 | thread.executable = (type[2] == 'x'); |
| 295 | thread_mmaps->push_back(thread); |
| 296 | } |
| 297 | return true; |
| 298 | } |
Yabin Cui | 8f62251 | 2015-05-05 19:58:07 -0700 | [diff] [blame] | 299 | |
| 300 | bool GetKernelBuildId(BuildId* build_id) { |
Yabin Cui | dec43c1 | 2016-07-29 16:40:40 -0700 | [diff] [blame] | 301 | ElfStatus result = GetBuildIdFromNoteFile("/sys/kernel/notes", build_id); |
| 302 | if (result != ElfStatus::NO_ERROR) { |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 303 | LOG(DEBUG) << "failed to read /sys/kernel/notes: " << result; |
Yabin Cui | dec43c1 | 2016-07-29 16:40:40 -0700 | [diff] [blame] | 304 | } |
| 305 | return result == ElfStatus::NO_ERROR; |
Yabin Cui | 8f62251 | 2015-05-05 19:58:07 -0700 | [diff] [blame] | 306 | } |
| 307 | |
| 308 | bool GetModuleBuildId(const std::string& module_name, BuildId* build_id) { |
| 309 | std::string notefile = "/sys/module/" + module_name + "/notes/.note.gnu.build-id"; |
| 310 | return GetBuildIdFromNoteFile(notefile, build_id); |
| 311 | } |
Yabin Cui | b032de7 | 2015-06-17 21:15:09 -0700 | [diff] [blame] | 312 | |
Yabin Cui | b032de7 | 2015-06-17 21:15:09 -0700 | [diff] [blame] | 313 | bool GetValidThreadsFromThreadString(const std::string& tid_str, std::set<pid_t>* tid_set) { |
| 314 | std::vector<std::string> strs = android::base::Split(tid_str, ","); |
Yabin Cui | 569f64a | 2016-02-05 17:32:08 -0800 | [diff] [blame] | 315 | for (const auto& s : strs) { |
Yabin Cui | 38e573e | 2015-08-06 11:25:09 -0700 | [diff] [blame] | 316 | int tid; |
Yabin Cui | 569f64a | 2016-02-05 17:32:08 -0800 | [diff] [blame] | 317 | if (!android::base::ParseInt(s.c_str(), &tid, 0)) { |
Yabin Cui | b032de7 | 2015-06-17 21:15:09 -0700 | [diff] [blame] | 318 | LOG(ERROR) << "Invalid tid '" << s << "'"; |
| 319 | return false; |
| 320 | } |
| 321 | if (!IsDir(android::base::StringPrintf("/proc/%d", tid))) { |
| 322 | LOG(ERROR) << "Non existing thread '" << tid << "'"; |
| 323 | return false; |
| 324 | } |
| 325 | tid_set->insert(tid); |
| 326 | } |
| 327 | return true; |
| 328 | } |
Yabin Cui | d713f95 | 2015-06-23 18:50:36 -0700 | [diff] [blame] | 329 | |
Yabin Cui | ebf79f3 | 2016-06-01 15:39:39 -0700 | [diff] [blame] | 330 | /* |
| 331 | * perf event paranoia level: |
| 332 | * -1 - not paranoid at all |
| 333 | * 0 - disallow raw tracepoint access for unpriv |
| 334 | * 1 - disallow cpu events for unpriv |
| 335 | * 2 - disallow kernel profiling for unpriv |
| 336 | * 3 - disallow user profiling for unpriv |
| 337 | */ |
| 338 | static bool ReadPerfEventParanoid(int* value) { |
| 339 | std::string s; |
| 340 | if (!android::base::ReadFileToString("/proc/sys/kernel/perf_event_paranoid", &s)) { |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 341 | PLOG(DEBUG) << "failed to read /proc/sys/kernel/perf_event_paranoid"; |
Yabin Cui | ebf79f3 | 2016-06-01 15:39:39 -0700 | [diff] [blame] | 342 | return false; |
| 343 | } |
| 344 | s = android::base::Trim(s); |
| 345 | if (!android::base::ParseInt(s.c_str(), value)) { |
| 346 | PLOG(ERROR) << "failed to parse /proc/sys/kernel/perf_event_paranoid: " << s; |
| 347 | return false; |
| 348 | } |
| 349 | return true; |
| 350 | } |
| 351 | |
| 352 | static const char* GetLimitLevelDescription(int limit_level) { |
| 353 | switch (limit_level) { |
| 354 | case -1: return "unlimited"; |
| 355 | case 0: return "disallowing raw tracepoint access for unpriv"; |
| 356 | case 1: return "disallowing cpu events for unpriv"; |
| 357 | case 2: return "disallowing kernel profiling for unpriv"; |
| 358 | case 3: return "disallowing user profiling for unpriv"; |
| 359 | default: return "unknown level"; |
| 360 | } |
| 361 | } |
| 362 | |
| 363 | bool CheckPerfEventLimit() { |
Yabin Cui | 1137a8c | 2017-06-01 13:23:29 -0700 | [diff] [blame] | 364 | // Root is not limited by /proc/sys/kernel/perf_event_paranoid. However, the monitored threads |
| 365 | // may create child processes not running as root. To make sure the child processes have |
| 366 | // enough permission to create inherited tracepoint events, write -1 to perf_event_paranoid. |
| 367 | // See http://b/62230699. |
| 368 | if (IsRoot() && android::base::WriteStringToFile("-1", "/proc/sys/kernel/perf_event_paranoid")) { |
Yabin Cui | 4f41df6 | 2016-06-01 17:29:06 -0700 | [diff] [blame] | 369 | return true; |
| 370 | } |
Yabin Cui | ebf79f3 | 2016-06-01 15:39:39 -0700 | [diff] [blame] | 371 | int limit_level; |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 372 | bool can_read_paranoid = ReadPerfEventParanoid(&limit_level); |
| 373 | if (can_read_paranoid && limit_level <= 1) { |
Yabin Cui | ebf79f3 | 2016-06-01 15:39:39 -0700 | [diff] [blame] | 374 | return true; |
| 375 | } |
| 376 | #if defined(__ANDROID__) |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 377 | const char* prop_name = "security.perf_harden"; |
| 378 | char prop_value[PROP_VALUE_MAX]; |
| 379 | if (__system_property_get(prop_name, prop_value) <= 0) { |
| 380 | // can't do anything if there is no such property. |
| 381 | return true; |
| 382 | } |
| 383 | if (strcmp(prop_value, "0") == 0) { |
| 384 | return true; |
| 385 | } |
Yabin Cui | ebf79f3 | 2016-06-01 15:39:39 -0700 | [diff] [blame] | 386 | // Try to enable perf_event_paranoid by setprop security.perf_harden=0. |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 387 | if (__system_property_set(prop_name, "0") == 0) { |
Yabin Cui | ebf79f3 | 2016-06-01 15:39:39 -0700 | [diff] [blame] | 388 | sleep(1); |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 389 | if (can_read_paranoid && ReadPerfEventParanoid(&limit_level) && limit_level <= 1) { |
| 390 | return true; |
| 391 | } |
| 392 | if (__system_property_get(prop_name, prop_value) > 0 && strcmp(prop_value, "0") == 0) { |
Yabin Cui | ebf79f3 | 2016-06-01 15:39:39 -0700 | [diff] [blame] | 393 | return true; |
| 394 | } |
| 395 | } |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 396 | if (can_read_paranoid) { |
| 397 | LOG(WARNING) << "/proc/sys/kernel/perf_event_paranoid is " << limit_level |
| 398 | << ", " << GetLimitLevelDescription(limit_level) << "."; |
| 399 | } |
Yabin Cui | ebf79f3 | 2016-06-01 15:39:39 -0700 | [diff] [blame] | 400 | LOG(WARNING) << "Try using `adb shell setprop security.perf_harden 0` to allow profiling."; |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 401 | return false; |
Yabin Cui | ebf79f3 | 2016-06-01 15:39:39 -0700 | [diff] [blame] | 402 | #else |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 403 | if (can_read_paranoid) { |
| 404 | LOG(WARNING) << "/proc/sys/kernel/perf_event_paranoid is " << limit_level |
| 405 | << ", " << GetLimitLevelDescription(limit_level) << "."; |
| 406 | return false; |
| 407 | } |
Yabin Cui | ebf79f3 | 2016-06-01 15:39:39 -0700 | [diff] [blame] | 408 | #endif |
| 409 | return true; |
| 410 | } |
Yabin Cui | b891549 | 2016-06-22 12:43:09 -0700 | [diff] [blame] | 411 | |
Yabin Cui | 0720d48 | 2017-03-06 17:05:50 -0800 | [diff] [blame] | 412 | bool GetMaxSampleFrequency(uint64_t* max_sample_freq) { |
| 413 | std::string s; |
| 414 | if (!android::base::ReadFileToString("/proc/sys/kernel/perf_event_max_sample_rate", &s)) { |
| 415 | PLOG(DEBUG) << "failed to read /proc/sys/kernel/perf_event_max_sample_rate"; |
| 416 | return false; |
| 417 | } |
| 418 | s = android::base::Trim(s); |
| 419 | if (!android::base::ParseUint(s.c_str(), max_sample_freq)) { |
| 420 | LOG(ERROR) << "failed to parse /proc/sys/kernel/perf_event_max_sample_rate: " << s; |
| 421 | return false; |
| 422 | } |
| 423 | return true; |
| 424 | } |
| 425 | |
Yabin Cui | b891549 | 2016-06-22 12:43:09 -0700 | [diff] [blame] | 426 | bool CheckSampleFrequency(uint64_t sample_freq) { |
| 427 | if (sample_freq == 0) { |
| 428 | LOG(ERROR) << "Sample frequency can't be zero."; |
| 429 | return false; |
| 430 | } |
Yabin Cui | b891549 | 2016-06-22 12:43:09 -0700 | [diff] [blame] | 431 | uint64_t max_sample_freq; |
Yabin Cui | 0720d48 | 2017-03-06 17:05:50 -0800 | [diff] [blame] | 432 | if (!GetMaxSampleFrequency(&max_sample_freq)) { |
| 433 | // Omit the check if can't read perf_event_max_sample_rate. |
| 434 | return true; |
Yabin Cui | b891549 | 2016-06-22 12:43:09 -0700 | [diff] [blame] | 435 | } |
| 436 | if (sample_freq > max_sample_freq) { |
| 437 | LOG(ERROR) << "Sample frequency " << sample_freq << " is out of range [1, " |
| 438 | << max_sample_freq << "]"; |
| 439 | return false; |
| 440 | } |
| 441 | return true; |
| 442 | } |
Yabin Cui | af69895 | 2016-07-08 11:27:48 -0700 | [diff] [blame] | 443 | |
| 444 | bool CheckKernelSymbolAddresses() { |
| 445 | const std::string kptr_restrict_file = "/proc/sys/kernel/kptr_restrict"; |
| 446 | std::string s; |
| 447 | if (!android::base::ReadFileToString(kptr_restrict_file, &s)) { |
Yabin Cui | 0a7a06d | 2016-10-28 13:27:41 -0700 | [diff] [blame] | 448 | PLOG(DEBUG) << "failed to read " << kptr_restrict_file; |
Yabin Cui | af69895 | 2016-07-08 11:27:48 -0700 | [diff] [blame] | 449 | return false; |
| 450 | } |
| 451 | s = android::base::Trim(s); |
| 452 | int value; |
| 453 | if (!android::base::ParseInt(s.c_str(), &value)) { |
| 454 | LOG(ERROR) << "failed to parse " << kptr_restrict_file << ": " << s; |
| 455 | return false; |
| 456 | } |
Yabin Cui | a90503d | 2017-05-19 12:02:15 -0700 | [diff] [blame] | 457 | // Accessible to everyone? |
Yabin Cui | af69895 | 2016-07-08 11:27:48 -0700 | [diff] [blame] | 458 | if (value == 0) { |
| 459 | return true; |
| 460 | } |
Yabin Cui | a90503d | 2017-05-19 12:02:15 -0700 | [diff] [blame] | 461 | // Accessible to root? |
Yabin Cui | af69895 | 2016-07-08 11:27:48 -0700 | [diff] [blame] | 462 | if (value == 1 && IsRoot()) { |
| 463 | return true; |
| 464 | } |
Yabin Cui | a90503d | 2017-05-19 12:02:15 -0700 | [diff] [blame] | 465 | // Can we make it accessible to us? |
| 466 | if (IsRoot() && android::base::WriteStringToFile("1", kptr_restrict_file)) { |
| 467 | return true; |
| 468 | } |
Yabin Cui | af69895 | 2016-07-08 11:27:48 -0700 | [diff] [blame] | 469 | LOG(WARNING) << "Access to kernel symbol addresses is restricted. If " |
| 470 | << "possible, please do `echo 0 >/proc/sys/kernel/kptr_restrict` " |
| 471 | << "to fix this."; |
| 472 | return false; |
| 473 | } |
Yabin Cui | 417df29 | 2016-11-16 12:26:13 -0800 | [diff] [blame] | 474 | |
| 475 | ArchType GetMachineArch() { |
| 476 | utsname uname_buf; |
| 477 | if (TEMP_FAILURE_RETRY(uname(&uname_buf)) != 0) { |
| 478 | PLOG(WARNING) << "uname() failed"; |
| 479 | return GetBuildArch(); |
| 480 | } |
| 481 | ArchType arch = GetArchType(uname_buf.machine); |
| 482 | if (arch != ARCH_UNSUPPORTED) { |
| 483 | return arch; |
| 484 | } |
| 485 | return GetBuildArch(); |
| 486 | } |
Yabin Cui | 63a1c3d | 2017-05-19 12:57:44 -0700 | [diff] [blame] | 487 | |
| 488 | void PrepareVdsoFile() { |
| 489 | // vdso is an elf file in memory loaded in each process's user space by the kernel. To read |
| 490 | // symbols from it and unwind through it, we need to dump it into a file in storage. |
| 491 | // It doesn't affect much when failed to prepare vdso file, so there is no need to return values. |
| 492 | std::vector<ThreadMmap> thread_mmaps; |
| 493 | if (!GetThreadMmapsInProcess(getpid(), &thread_mmaps)) { |
| 494 | return; |
| 495 | } |
| 496 | const ThreadMmap* vdso_map = nullptr; |
| 497 | for (const auto& map : thread_mmaps) { |
| 498 | if (map.name == "[vdso]") { |
| 499 | vdso_map = ↦ |
| 500 | break; |
| 501 | } |
| 502 | } |
| 503 | if (vdso_map == nullptr) { |
| 504 | return; |
| 505 | } |
| 506 | std::string s(vdso_map->len, '\0'); |
| 507 | memcpy(&s[0], reinterpret_cast<void*>(static_cast<uintptr_t>(vdso_map->start_addr)), |
| 508 | vdso_map->len); |
| 509 | std::unique_ptr<TemporaryFile> tmpfile(new TemporaryFile); |
| 510 | if (!android::base::WriteStringToFile(s, tmpfile->path)) { |
| 511 | return; |
| 512 | } |
| 513 | Dso::SetVdsoFile(std::move(tmpfile), sizeof(size_t) == sizeof(uint64_t)); |
| 514 | } |