1 /*
2  * Copyright (C) 2021 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 <stdint.h>
19 #include <stdio.h>
20 #include <sys/ptrace.h>
21 #include <unistd.h>
22 
23 #include <memory>
24 
25 #include <benchmark/benchmark.h>
26 
27 #include <unwindstack/Maps.h>
28 #include <unwindstack/Memory.h>
29 #include <unwindstack/Regs.h>
30 #include <unwindstack/Unwinder.h>
31 
32 #include "MemoryRemote.h"
33 #include "tests/TestUtils.h"
34 
WaitForRemote(pid_t pid,volatile bool * ready_ptr)35 static bool WaitForRemote(pid_t pid, volatile bool* ready_ptr) {
36   usleep(1000);
37   for (size_t i = 0; i < 1000; i++) {
38     if (ptrace(PTRACE_ATTACH, pid, 0, 0) == 0) {
39       unwindstack::TestQuiescePid(pid);
40 
41       unwindstack::MemoryRemote memory(pid);
42       bool ready;
43       uint64_t ready_addr = reinterpret_cast<uint64_t>(ready_ptr);
44       if (memory.ReadFully(ready_addr, &ready, sizeof(ready)) && ready) {
45         return true;
46       }
47     } else if (errno != ESRCH) {
48       // Attach failed with unknown error.
49       perror("Ptrace failed:");
50       return false;
51     }
52     usleep(5000);
53   }
54   printf("Pid %d did not quiesce in a timely fashion.\n", pid);
55   return false;
56 }
57 
RemoteCall6(volatile bool * ready)58 size_t RemoteCall6(volatile bool* ready) {
59   *ready = true;
60   while (true)
61     ;
62 }
63 
RemoteCall5(volatile bool * ready)64 size_t RemoteCall5(volatile bool* ready) {
65   return RemoteCall6(ready);
66 }
67 
RemoteCall4(volatile bool * ready)68 size_t RemoteCall4(volatile bool* ready) {
69   return RemoteCall5(ready);
70 }
71 
RemoteCall3(volatile bool * ready)72 size_t RemoteCall3(volatile bool* ready) {
73   return RemoteCall4(ready);
74 }
75 
RemoteCall2(volatile bool * ready)76 size_t RemoteCall2(volatile bool* ready) {
77   return RemoteCall3(ready);
78 }
79 
RemoteCall1(volatile bool * ready)80 size_t RemoteCall1(volatile bool* ready) {
81   return RemoteCall2(ready);
82 }
83 
StartRemoteRun()84 static pid_t StartRemoteRun() {
85   static volatile bool ready = false;
86 
87   pid_t pid;
88   if ((pid = fork()) == 0) {
89     RemoteCall1(&ready);
90     exit(0);
91   }
92   if (pid == -1) {
93     return -1;
94   }
95 
96   if (!WaitForRemote(pid, &ready)) {
97     kill(pid, SIGKILL);
98     waitpid(pid, nullptr, 0);
99     return -1;
100   }
101 
102   return pid;
103 }
104 
RemoteUnwind(benchmark::State & state,bool cached)105 static void RemoteUnwind(benchmark::State& state, bool cached) {
106   pid_t pid = StartRemoteRun();
107   if (pid == -1) {
108     state.SkipWithError("Failed to start remote process.");
109   }
110   unwindstack::TestScopedPidReaper reap(pid);
111 
112   std::shared_ptr<unwindstack::Memory> process_memory;
113   if (cached) {
114     process_memory = unwindstack::Memory::CreateProcessMemoryCached(pid);
115   } else {
116     process_memory = unwindstack::Memory::CreateProcessMemory(pid);
117   }
118   unwindstack::RemoteMaps maps(pid);
119   if (!maps.Parse()) {
120     state.SkipWithError("Failed to parse maps.");
121   }
122 
123   for (auto _ : state) {
124     std::unique_ptr<unwindstack::Regs> regs(unwindstack::Regs::RemoteGet(pid));
125     unwindstack::Unwinder unwinder(32, &maps, regs.get(), process_memory);
126     unwinder.Unwind();
127     if (unwinder.NumFrames() < 5) {
128       state.SkipWithError("Failed to unwind properly.");
129     }
130   }
131 
132   ptrace(PTRACE_DETACH, pid, 0, 0);
133 }
134 
BM_remote_unwind_uncached(benchmark::State & state)135 static void BM_remote_unwind_uncached(benchmark::State& state) {
136   RemoteUnwind(state, false);
137 }
138 BENCHMARK(BM_remote_unwind_uncached);
139 
BM_remote_unwind_cached(benchmark::State & state)140 static void BM_remote_unwind_cached(benchmark::State& state) {
141   RemoteUnwind(state, true);
142 }
143 BENCHMARK(BM_remote_unwind_cached);
144