1 /* 2 * Copyright (C) 2017 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 <errno.h> 18 #include <signal.h> 19 #include <stdint.h> 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <string.h> 23 #include <sys/mman.h> 24 #include <sys/ptrace.h> 25 #include <sys/types.h> 26 #include <sys/uio.h> 27 #include <sys/wait.h> 28 #include <unistd.h> 29 30 #include <memory> 31 #include <vector> 32 33 #include <benchmark/benchmark.h> 34 35 #include <backtrace/Backtrace.h> 36 37 #define AT_COMMON_SIZES Arg(1)->Arg(4)->Arg(8)->Arg(16)->Arg(100)->Arg(200)->Arg(500)->Arg(1024) 38 39 static void Attach(pid_t pid) { 40 if (ptrace(PTRACE_ATTACH, pid, 0, 0) == -1) { 41 perror("Failed to attach"); 42 abort(); 43 } 44 45 siginfo_t si; 46 // Wait for up to 5 seconds. 47 for (size_t i = 0; i < 5000; i++) { 48 if (ptrace(PTRACE_GETSIGINFO, pid, 0, &si) == 0) { 49 return; 50 } 51 usleep(1000); 52 } 53 printf("Remote process failed to stop in five seconds.\n"); 54 abort(); 55 } 56 57 class ScopedPidReaper { 58 public: 59 ScopedPidReaper(pid_t pid) : pid_(pid) {} 60 ~ScopedPidReaper() { 61 kill(pid_, SIGKILL); 62 waitpid(pid_, nullptr, 0); 63 } 64 65 private: 66 pid_t pid_; 67 }; 68 69 static size_t ProcessVmRead(pid_t pid, uint64_t remote_src, void* dst, size_t len) { 70 struct iovec dst_iov = { 71 .iov_base = dst, .iov_len = len, 72 }; 73 74 struct iovec src_iov = { 75 .iov_base = reinterpret_cast<void*>(remote_src), .iov_len = len, 76 }; 77 78 ssize_t rc = process_vm_readv(pid, &dst_iov, 1, &src_iov, 1, 0); 79 return rc == -1 ? 0 : rc; 80 } 81 82 static bool PtraceReadLong(pid_t pid, uint64_t addr, long* value) { 83 // ptrace() returns -1 and sets errno when the operation fails. 84 // To disambiguate -1 from a valid result, we clear errno beforehand. 85 errno = 0; 86 *value = ptrace(PTRACE_PEEKTEXT, pid, reinterpret_cast<void*>(addr), nullptr); 87 if (*value == -1 && errno) { 88 return false; 89 } 90 return true; 91 } 92 93 static size_t PtraceRead(pid_t pid, uint64_t addr, void* dst, size_t bytes) { 94 size_t bytes_read = 0; 95 long data; 96 for (size_t i = 0; i < bytes / sizeof(long); i++) { 97 if (!PtraceReadLong(pid, addr, &data)) { 98 return bytes_read; 99 } 100 memcpy(dst, &data, sizeof(long)); 101 dst = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(dst) + sizeof(long)); 102 addr += sizeof(long); 103 bytes_read += sizeof(long); 104 } 105 106 size_t left_over = bytes & (sizeof(long) - 1); 107 if (left_over) { 108 if (!PtraceReadLong(pid, addr, &data)) { 109 return bytes_read; 110 } 111 memcpy(dst, &data, left_over); 112 bytes_read += left_over; 113 } 114 return bytes_read; 115 } 116 117 static void CreateRemoteProcess(size_t size, void** map, pid_t* pid) { 118 *map = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 119 if (*map == MAP_FAILED) { 120 perror("Can't allocate memory"); 121 abort(); 122 } 123 memset(*map, 0xaa, size); 124 125 if ((*pid = fork()) == 0) { 126 for (volatile int i = 0;; i++) 127 ; 128 exit(1); 129 } 130 if (*pid < 0) { 131 perror("Failed to fork"); 132 abort(); 133 } 134 Attach(*pid); 135 // Don't need this map in the current process any more. 136 munmap(*map, size); 137 } 138 139 static void BM_read_with_ptrace(benchmark::State& state) { 140 void* map; 141 pid_t pid; 142 CreateRemoteProcess(state.range(0), &map, &pid); 143 ScopedPidReaper reap(pid); 144 145 std::vector<uint8_t> read_buffer(state.range(0)); 146 uint64_t addr = reinterpret_cast<uint64_t>(map); 147 while (state.KeepRunning()) { 148 if (PtraceRead(pid, addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) { 149 printf("Unexpected bad read.\n"); 150 abort(); 151 } 152 } 153 ptrace(PTRACE_DETACH, pid, 0, 0); 154 } 155 BENCHMARK(BM_read_with_ptrace)->AT_COMMON_SIZES; 156 157 static void BM_read_with_process_vm_read(benchmark::State& state) { 158 void* map; 159 pid_t pid; 160 CreateRemoteProcess(state.range(0), &map, &pid); 161 ScopedPidReaper reap(pid); 162 163 std::vector<uint8_t> read_buffer(state.range(0)); 164 uint64_t addr = reinterpret_cast<uint64_t>(map); 165 while (state.KeepRunning()) { 166 if (ProcessVmRead(pid, addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) { 167 printf("Unexpected bad read.\n"); 168 abort(); 169 } 170 } 171 ptrace(PTRACE_DETACH, pid, 0, 0); 172 } 173 BENCHMARK(BM_read_with_process_vm_read)->AT_COMMON_SIZES; 174 175 static void BM_read_with_backtrace_object(benchmark::State& state) { 176 void* map; 177 pid_t pid; 178 CreateRemoteProcess(state.range(0), &map, &pid); 179 ScopedPidReaper reap(pid); 180 181 std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, BACKTRACE_CURRENT_THREAD)); 182 if (backtrace.get() == nullptr) { 183 printf("Failed to create backtrace.\n"); 184 abort(); 185 } 186 187 uint64_t addr = reinterpret_cast<uint64_t>(map); 188 std::vector<uint8_t> read_buffer(state.range(0)); 189 while (state.KeepRunning()) { 190 if (backtrace->Read(addr, read_buffer.data(), read_buffer.size()) != read_buffer.size()) { 191 printf("Unexpected bad read.\n"); 192 abort(); 193 } 194 } 195 ptrace(PTRACE_DETACH, pid, 0, 0); 196 } 197 BENCHMARK(BM_read_with_backtrace_object)->AT_COMMON_SIZES; 198