blob: 8065733633be8b271e1ceb633949d44456d2af27 [file] [log] [blame]
Florian Mayer824274d2018-09-17 11:33:45 +01001/*
2 * Copyright (C) 2018 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 "src/profiling/memory/client.h"
18
19#include <inttypes.h>
Ryan Savitski03693fb2019-01-18 17:07:06 +000020#include <sys/prctl.h>
Florian Mayerb85a9382018-09-27 13:59:01 +010021#include <sys/syscall.h>
Ryan Savitski9eb2d5b2019-04-25 15:02:31 +010022#include <sys/types.h>
Florian Mayerb85a9382018-09-27 13:59:01 +010023#include <unistd.h>
Florian Mayerb85a9382018-09-27 13:59:01 +010024#include <unwindstack/MachineArm.h>
25#include <unwindstack/MachineArm64.h>
26#include <unwindstack/MachineMips.h>
27#include <unwindstack/MachineMips64.h>
28#include <unwindstack/MachineX86.h>
29#include <unwindstack/MachineX86_64.h>
30#include <unwindstack/Regs.h>
31#include <unwindstack/RegsGetLocal.h>
Florian Mayer824274d2018-09-17 11:33:45 +010032
Ryan Savitski9eb2d5b2019-04-25 15:02:31 +010033#include <atomic>
34#include <new>
35
Florian Mayer824274d2018-09-17 11:33:45 +010036#include "perfetto/base/logging.h"
Eric Seckler83dcc8c2019-08-21 12:18:43 +010037#include "perfetto/base/time.h"
Primiano Tucci2c5488f2019-06-01 03:27:28 +010038#include "perfetto/ext/base/scoped_file.h"
39#include "perfetto/ext/base/thread_utils.h"
Primiano Tucci2c5488f2019-06-01 03:27:28 +010040#include "perfetto/ext/base/unix_socket.h"
41#include "perfetto/ext/base/utils.h"
Florian Mayer88665be2018-10-03 13:15:38 +010042#include "src/profiling/memory/sampler.h"
Ryan Savitskia502bde2019-02-26 21:34:03 +000043#include "src/profiling/memory/scoped_spinlock.h"
Florian Mayerb85a9382018-09-27 13:59:01 +010044#include "src/profiling/memory/wire_protocol.h"
Florian Mayer824274d2018-09-17 11:33:45 +010045
46namespace perfetto {
Florian Mayer8c0d0482018-10-22 14:23:40 +010047namespace profiling {
Florian Mayeraf266022018-09-17 15:18:15 +010048namespace {
49
Florian Mayer51950592019-03-06 20:05:15 +000050const char kSingleByte[1] = {'x'};
Florian Mayer65081572018-12-18 13:41:08 +000051constexpr std::chrono::seconds kLockTimeout{1};
Florian Mayerd6bdb6f2019-05-03 17:53:58 +010052constexpr auto kResendBackoffUs = 100;
Florian Mayer15e71f82018-10-17 15:11:49 +010053
Florian Mayerb85a9382018-09-27 13:59:01 +010054inline bool IsMainThread() {
Primiano Tucci5e6bc992018-12-12 15:04:36 +000055 return getpid() == base::GetThreadId();
Florian Mayerb85a9382018-09-27 13:59:01 +010056}
Florian Mayeraf266022018-09-17 15:18:15 +010057
Florian Mayer88665be2018-10-03 13:15:38 +010058// TODO(b/117203899): Remove this after making bionic implementation safe to
59// use.
60char* FindMainThreadStack() {
61 base::ScopedFstream maps(fopen("/proc/self/maps", "r"));
62 if (!maps) {
63 return nullptr;
64 }
65 while (!feof(*maps)) {
66 char line[1024];
67 char* data = fgets(line, sizeof(line), *maps);
68 if (data != nullptr && strstr(data, "[stack]")) {
69 char* sep = strstr(data, "-");
70 if (sep == nullptr)
71 continue;
72 sep++;
73 return reinterpret_cast<char*>(strtoll(sep, nullptr, 16));
74 }
75 }
76 return nullptr;
77}
78
Ryan Savitski03693fb2019-01-18 17:07:06 +000079int UnsetDumpable(int) {
80 prctl(PR_SET_DUMPABLE, 0);
81 return 0;
82}
83
Florian Mayeraf266022018-09-17 15:18:15 +010084} // namespace
85
Florian Mayerb85a9382018-09-27 13:59:01 +010086const char* GetThreadStackBase() {
87 pthread_attr_t attr;
88 if (pthread_getattr_np(pthread_self(), &attr) != 0)
89 return nullptr;
90 base::ScopedResource<pthread_attr_t*, pthread_attr_destroy, nullptr> cleanup(
91 &attr);
92
93 char* stackaddr;
94 size_t stacksize;
95 if (pthread_attr_getstack(&attr, reinterpret_cast<void**>(&stackaddr),
96 &stacksize) != 0)
97 return nullptr;
98 return stackaddr + stacksize;
99}
100
Ryan Savitski4a783172019-03-21 19:27:12 +0000101// static
102base::Optional<base::UnixSocketRaw> Client::ConnectToHeapprofd(
103 const std::string& sock_name) {
Primiano Tuccid4be9662019-10-22 17:45:13 -0400104 auto sock = base::UnixSocketRaw::CreateMayFail(base::SockFamily::kUnix,
105 base::SockType::kStream);
Ryan Savitski4a783172019-03-21 19:27:12 +0000106 if (!sock || !sock.Connect(sock_name)) {
107 PERFETTO_PLOG("Failed to connect to %s", sock_name.c_str());
108 return base::nullopt;
Florian Mayer51950592019-03-06 20:05:15 +0000109 }
Ryan Savitski4a783172019-03-21 19:27:12 +0000110 if (!sock.SetTxTimeout(kClientSockTimeoutMs)) {
111 PERFETTO_PLOG("Failed to set send timeout for %s", sock_name.c_str());
112 return base::nullopt;
113 }
114 if (!sock.SetRxTimeout(kClientSockTimeoutMs)) {
115 PERFETTO_PLOG("Failed to set receive timeout for %s", sock_name.c_str());
116 return base::nullopt;
117 }
118 return std::move(sock);
119}
120
121// static
Ryan Savitski646d3662019-04-08 12:53:29 +0100122std::shared_ptr<Client> Client::CreateAndHandshake(
123 base::UnixSocketRaw sock,
124 UnhookedAllocator<Client> unhooked_allocator) {
Ryan Savitski4a783172019-03-21 19:27:12 +0000125 if (!sock) {
Florian Mayer33d8ea22019-05-09 19:57:07 +0100126 PERFETTO_DFATAL_OR_ELOG("Socket not connected.");
Ryan Savitski4a783172019-03-21 19:27:12 +0000127 return nullptr;
128 }
Florian Mayer51950592019-03-06 20:05:15 +0000129
Florian Mayer754b57f2019-04-15 13:17:55 +0100130 PERFETTO_DCHECK(sock.IsBlocking());
131
Ryan Savitski03693fb2019-01-18 17:07:06 +0000132 // We might be running in a process that is not dumpable (such as app
133 // processes on user builds), in which case the /proc/self/mem will be chown'd
134 // to root:root, and will not be accessible even to the process itself (see
135 // man 5 proc). In such situations, temporarily mark the process dumpable to
136 // be able to open the files, unsetting dumpability immediately afterwards.
137 int orig_dumpable = prctl(PR_GET_DUMPABLE);
138
139 enum { kNop, kDoUnset };
140 base::ScopedResource<int, UnsetDumpable, kNop, false> unset_dumpable(kNop);
141 if (orig_dumpable == 0) {
142 unset_dumpable.reset(kDoUnset);
143 prctl(PR_SET_DUMPABLE, 1);
144 }
145
Florian Mayer1b489ad2019-06-26 22:18:46 -0700146 size_t num_send_fds = kHandshakeSize;
147
Florian Mayerb03fd282018-10-03 16:05:16 +0100148 base::ScopedFile maps(base::OpenFile("/proc/self/maps", O_RDONLY));
Ryan Savitski03693fb2019-01-18 17:07:06 +0000149 if (!maps) {
Florian Mayer33d8ea22019-05-09 19:57:07 +0100150 PERFETTO_DFATAL_OR_ELOG("Failed to open /proc/self/maps");
Ryan Savitski4a783172019-03-21 19:27:12 +0000151 return nullptr;
Florian Mayer0ff382c2018-11-13 16:19:16 +0000152 }
Ryan Savitski03693fb2019-01-18 17:07:06 +0000153 base::ScopedFile mem(base::OpenFile("/proc/self/mem", O_RDONLY));
154 if (!mem) {
Florian Mayer33d8ea22019-05-09 19:57:07 +0100155 PERFETTO_DFATAL_OR_ELOG("Failed to open /proc/self/mem");
Ryan Savitski4a783172019-03-21 19:27:12 +0000156 return nullptr;
Ryan Savitski03693fb2019-01-18 17:07:06 +0000157 }
Florian Mayer7142c7c2019-05-20 18:11:41 +0100158
Florian Mayer1b489ad2019-06-26 22:18:46 -0700159 base::ScopedFile page_idle(base::OpenFile("/proc/self/page_idle", O_RDWR));
160 if (!page_idle) {
161 PERFETTO_LOG("Failed to open /proc/self/page_idle. Continuing.");
162 num_send_fds = kHandshakeSize - 1;
163 }
164
Ryan Savitski03693fb2019-01-18 17:07:06 +0000165 // Restore original dumpability value if we overrode it.
166 unset_dumpable.reset();
167
Florian Mayer51950592019-03-06 20:05:15 +0000168 int fds[kHandshakeSize];
169 fds[kHandshakeMaps] = *maps;
170 fds[kHandshakeMem] = *mem;
Florian Mayer1b489ad2019-06-26 22:18:46 -0700171 fds[kHandshakePageIdle] = *page_idle;
Florian Mayer51950592019-03-06 20:05:15 +0000172
Florian Mayer1fe77732018-10-02 17:25:53 +0100173 // Send an empty record to transfer fds for /proc/self/maps and
174 // /proc/self/mem.
Florian Mayer1b489ad2019-06-26 22:18:46 -0700175 if (sock.Send(kSingleByte, sizeof(kSingleByte), fds, num_send_fds) !=
Florian Mayer51950592019-03-06 20:05:15 +0000176 sizeof(kSingleByte)) {
Florian Mayer33d8ea22019-05-09 19:57:07 +0100177 PERFETTO_DFATAL_OR_ELOG("Failed to send file descriptors.");
Ryan Savitski4a783172019-03-21 19:27:12 +0000178 return nullptr;
Florian Mayerf1510c42018-10-17 11:40:52 +0100179 }
Florian Mayer51950592019-03-06 20:05:15 +0000180
Ryan Savitski4a783172019-03-21 19:27:12 +0000181 ClientConfiguration client_config;
Florian Mayer51950592019-03-06 20:05:15 +0000182 base::ScopedFile shmem_fd;
Florian Mayer0e26d712019-03-26 14:18:14 +0000183 size_t recv = 0;
184 while (recv < sizeof(client_config)) {
185 size_t num_fds = 0;
186 base::ScopedFile* fd = nullptr;
187 if (!shmem_fd) {
188 num_fds = 1;
189 fd = &shmem_fd;
190 }
191 ssize_t rd = sock.Receive(reinterpret_cast<char*>(&client_config) + recv,
192 sizeof(client_config) - recv, fd, num_fds);
193 if (rd == -1) {
194 PERFETTO_PLOG("Failed to receive ClientConfiguration.");
195 return nullptr;
196 }
197 if (rd == 0) {
198 PERFETTO_LOG("Server disconnected while sending ClientConfiguration.");
199 return nullptr;
200 }
201 recv += static_cast<size_t>(rd);
202 }
203
204 if (!shmem_fd) {
Florian Mayer33d8ea22019-05-09 19:57:07 +0100205 PERFETTO_DFATAL_OR_ELOG("Did not receive shmem fd.");
Ryan Savitski4a783172019-03-21 19:27:12 +0000206 return nullptr;
Florian Mayerf1510c42018-10-17 11:40:52 +0100207 }
Florian Mayer51950592019-03-06 20:05:15 +0000208
209 auto shmem = SharedRingBuffer::Attach(std::move(shmem_fd));
210 if (!shmem || !shmem->is_valid()) {
Florian Mayer33d8ea22019-05-09 19:57:07 +0100211 PERFETTO_DFATAL_OR_ELOG("Failed to attach to shmem.");
Ryan Savitski4a783172019-03-21 19:27:12 +0000212 return nullptr;
Florian Mayer51950592019-03-06 20:05:15 +0000213 }
Florian Mayer51950592019-03-06 20:05:15 +0000214
Ryan Savitski4a783172019-03-21 19:27:12 +0000215 PERFETTO_DCHECK(client_config.interval >= 1);
Florian Mayerd6bdb6f2019-05-03 17:53:58 +0100216 // TODO(fmayer): Always make this nonblocking.
217 // This is so that without block_client, we get the old behaviour that rate
218 // limits using the blocking socket. We do not want to change that for Q.
219 sock.SetBlocking(!client_config.block_client);
Ryan Savitski4a783172019-03-21 19:27:12 +0000220 Sampler sampler{client_config.interval};
Ryan Savitski646d3662019-04-08 12:53:29 +0100221 // note: the shared_ptr will retain a copy of the unhooked_allocator
Ryan Savitski9eb2d5b2019-04-25 15:02:31 +0100222 return std::allocate_shared<Client>(unhooked_allocator, std::move(sock),
223 client_config, std::move(shmem.value()),
224 std::move(sampler), getpid(),
225 FindMainThreadStack());
Florian Mayerb85a9382018-09-27 13:59:01 +0100226}
227
Ryan Savitski4a783172019-03-21 19:27:12 +0000228Client::Client(base::UnixSocketRaw sock,
229 ClientConfiguration client_config,
230 SharedRingBuffer shmem,
231 Sampler sampler,
Ryan Savitski9eb2d5b2019-04-25 15:02:31 +0100232 pid_t pid_at_creation,
Ryan Savitski4a783172019-03-21 19:27:12 +0000233 const char* main_thread_stack_base)
234 : client_config_(client_config),
235 sampler_(std::move(sampler)),
236 sock_(std::move(sock)),
237 main_thread_stack_base_(main_thread_stack_base),
Ryan Savitski9eb2d5b2019-04-25 15:02:31 +0100238 shmem_(std::move(shmem)),
239 pid_at_creation_(pid_at_creation) {}
Florian Mayerb85a9382018-09-27 13:59:01 +0100240
241const char* Client::GetStackBase() {
242 if (IsMainThread()) {
243 if (!main_thread_stack_base_)
244 // Because pthread_attr_getstack reads and parses /proc/self/maps and
245 // /proc/self/stat, we have to cache the result here.
246 main_thread_stack_base_ = GetThreadStackBase();
247 return main_thread_stack_base_;
248 }
249 return GetThreadStackBase();
250}
251
252// The stack grows towards numerically smaller addresses, so the stack layout
253// of main calling malloc is as follows.
254//
255// +------------+
256// |SendWireMsg |
257// stacktop +--> +------------+ 0x1000
258// |RecordMalloc| +
259// +------------+ |
260// | malloc | |
261// +------------+ |
262// | main | v
263// stackbase +-> +------------+ 0xffff
Florian Mayer6eb891b2019-06-25 13:57:59 -0700264bool Client::RecordMalloc(uint64_t sample_size,
265 uint64_t alloc_size,
Florian Mayer9a7fb2b2018-10-17 18:17:40 +0100266 uint64_t alloc_address) {
Florian Mayer8525eb42019-04-30 13:09:33 +0100267 if (PERFETTO_UNLIKELY(getpid() != pid_at_creation_)) {
Ryan Savitski9eb2d5b2019-04-25 15:02:31 +0100268 PERFETTO_LOG("Detected post-fork child situation, stopping profiling.");
269 return false;
270 }
271
Florian Mayerb85a9382018-09-27 13:59:01 +0100272 AllocMetadata metadata;
273 const char* stackbase = GetStackBase();
274 const char* stacktop = reinterpret_cast<char*>(__builtin_frame_address(0));
275 unwindstack::AsmGetRegs(metadata.register_data);
276
Florian Mayer33d8ea22019-05-09 19:57:07 +0100277 if (PERFETTO_UNLIKELY(stackbase < stacktop)) {
278 PERFETTO_DFATAL_OR_ELOG("Stackbase >= stacktop.");
Florian Mayer20c4b3c2019-01-24 18:25:35 +0000279 return false;
Florian Mayerb85a9382018-09-27 13:59:01 +0100280 }
281
282 uint64_t stack_size = static_cast<uint64_t>(stackbase - stacktop);
Florian Mayer6eb891b2019-06-25 13:57:59 -0700283 metadata.sample_size = sample_size;
Florian Mayerb85a9382018-09-27 13:59:01 +0100284 metadata.alloc_size = alloc_size;
285 metadata.alloc_address = alloc_address;
286 metadata.stack_pointer = reinterpret_cast<uint64_t>(stacktop);
287 metadata.stack_pointer_offset = sizeof(AllocMetadata);
288 metadata.arch = unwindstack::Regs::CurrentArch();
Florian Mayerc40573e2018-11-16 14:27:28 +0000289 metadata.sequence_number =
290 1 + sequence_number_.fetch_add(1, std::memory_order_acq_rel);
Florian Mayerb85a9382018-09-27 13:59:01 +0100291
Florian Mayer5bd65f42019-05-02 16:20:38 +0100292 struct timespec ts;
293 if (clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == 0) {
294 metadata.clock_monotonic_coarse_timestamp =
295 static_cast<uint64_t>(base::FromPosixTimespec(ts).count());
296 } else {
297 metadata.clock_monotonic_coarse_timestamp = 0;
298 }
299
Florian Mayerb85a9382018-09-27 13:59:01 +0100300 WireMessage msg{};
Florian Mayer88665be2018-10-03 13:15:38 +0100301 msg.record_type = RecordType::Malloc;
Florian Mayerb85a9382018-09-27 13:59:01 +0100302 msg.alloc_header = &metadata;
303 msg.payload = const_cast<char*>(stacktop);
304 msg.payload_size = static_cast<size_t>(stack_size);
305
Florian Mayerd6bdb6f2019-05-03 17:53:58 +0100306 if (!SendWireMessageWithRetriesIfBlocking(msg))
307 return false;
308
309 return SendControlSocketByte();
310}
311
312bool Client::SendWireMessageWithRetriesIfBlocking(const WireMessage& msg) {
313 for (;;) {
314 if (PERFETTO_LIKELY(SendWireMessage(&shmem_, msg)))
315 return true;
316 // retry if in blocking mode and still connected
317 if (client_config_.block_client && base::IsAgain(errno) && IsConnected()) {
318 usleep(kResendBackoffUs);
319 continue;
320 }
321 PERFETTO_PLOG("Failed to write to shared ring buffer. Disconnecting.");
Florian Mayer51950592019-03-06 20:05:15 +0000322 return false;
323 }
Florian Mayerb85a9382018-09-27 13:59:01 +0100324}
325
Ryan Savitski549753e2019-03-15 15:52:37 +0000326bool Client::RecordFree(const uint64_t alloc_address) {
Ryan Savitski4a783172019-03-21 19:27:12 +0000327 uint64_t sequence_number =
328 1 + sequence_number_.fetch_add(1, std::memory_order_acq_rel);
Florian Mayer9a7fb2b2018-10-17 18:17:40 +0100329
Ryan Savitski549753e2019-03-15 15:52:37 +0000330 std::unique_lock<std::timed_mutex> l(free_batch_lock_, kLockTimeout);
331 if (!l.owns_lock())
332 return false;
333 if (free_batch_.num_entries == kFreeBatchSize) {
334 if (!FlushFreesLocked())
335 return false;
336 // Flushed the contents of the buffer, reset it for reuse.
337 free_batch_.num_entries = 0;
338 }
339 FreeBatchEntry& current_entry =
340 free_batch_.entries[free_batch_.num_entries++];
341 current_entry.sequence_number = sequence_number;
Ryan Savitski4a783172019-03-21 19:27:12 +0000342 current_entry.addr = alloc_address;
Ryan Savitski549753e2019-03-15 15:52:37 +0000343 return true;
344}
345
346bool Client::FlushFreesLocked() {
Florian Mayer8525eb42019-04-30 13:09:33 +0100347 if (PERFETTO_UNLIKELY(getpid() != pid_at_creation_)) {
Ryan Savitski9eb2d5b2019-04-25 15:02:31 +0100348 PERFETTO_LOG("Detected post-fork child situation, stopping profiling.");
349 return false;
350 }
351
Ryan Savitski549753e2019-03-15 15:52:37 +0000352 WireMessage msg = {};
353 msg.record_type = RecordType::Free;
354 msg.free_header = &free_batch_;
Florian Mayer5bd65f42019-05-02 16:20:38 +0100355 struct timespec ts;
356 if (clock_gettime(CLOCK_MONOTONIC_COARSE, &ts) == 0) {
357 free_batch_.clock_monotonic_coarse_timestamp =
358 static_cast<uint64_t>(base::FromPosixTimespec(ts).count());
359 } else {
360 free_batch_.clock_monotonic_coarse_timestamp = 0;
361 }
362
Florian Mayerd6bdb6f2019-05-03 17:53:58 +0100363 if (!SendWireMessageWithRetriesIfBlocking(msg))
Ryan Savitski549753e2019-03-15 15:52:37 +0000364 return false;
Florian Mayer754b57f2019-04-15 13:17:55 +0100365 return SendControlSocketByte();
366}
367
Florian Mayerd6bdb6f2019-05-03 17:53:58 +0100368bool Client::IsConnected() {
369 PERFETTO_DCHECK(!sock_.IsBlocking());
370 char buf[1];
371 ssize_t recv_bytes = sock_.Receive(buf, sizeof(buf), nullptr, 0);
372 if (recv_bytes == 0)
373 return false;
374 // This is not supposed to happen because currently heapprofd does not send
375 // data to the client. Here for generality's sake.
376 if (recv_bytes > 0)
377 return true;
378 return base::IsAgain(errno);
379}
380
Florian Mayer754b57f2019-04-15 13:17:55 +0100381bool Client::SendControlSocketByte() {
Florian Mayerd6bdb6f2019-05-03 17:53:58 +0100382 // TODO(fmayer): Fix the special casing that only block_client uses a
383 // nonblocking socket.
384 if (sock_.Send(kSingleByte, sizeof(kSingleByte)) == -1 &&
385 (!client_config_.block_client || !base::IsAgain(errno))) {
Ryan Savitski4a783172019-03-21 19:27:12 +0000386 PERFETTO_PLOG("Failed to send control socket byte.");
Ryan Savitski549753e2019-03-15 15:52:37 +0000387 return false;
388 }
389 return true;
390}
391
Florian Mayer8c0d0482018-10-22 14:23:40 +0100392} // namespace profiling
Florian Mayer824274d2018-09-17 11:33:45 +0100393} // namespace perfetto