blob: a4791f8e731c0c5e53367d033023ffbc0c32f6ed [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#ifndef SRC_PROFILING_MEMORY_CLIENT_H_
18#define SRC_PROFILING_MEMORY_CLIENT_H_
19
Florian Mayer88665be2018-10-03 13:15:38 +010020#include <pthread.h>
Florian Mayer824274d2018-09-17 11:33:45 +010021#include <stddef.h>
Florian Mayeraf266022018-09-17 15:18:15 +010022
Florian Mayer824274d2018-09-17 11:33:45 +010023#include <mutex>
24#include <vector>
25
26#include "perfetto/base/scoped_file.h"
Florian Mayerb85a9382018-09-27 13:59:01 +010027#include "src/profiling/memory/wire_protocol.h"
Florian Mayer824274d2018-09-17 11:33:45 +010028
29namespace perfetto {
30
Florian Mayerb85a9382018-09-27 13:59:01 +010031class BorrowedSocket;
Florian Mayer824274d2018-09-17 11:33:45 +010032
33class SocketPool {
34 public:
35 friend class BorrowedSocket;
36 SocketPool(std::vector<base::ScopedFile> sockets);
37
38 BorrowedSocket Borrow();
39
40 private:
41 void Return(base::ScopedFile fd);
Florian Mayerb85a9382018-09-27 13:59:01 +010042 std::mutex mutex_;
Florian Mayer824274d2018-09-17 11:33:45 +010043 std::condition_variable cv_;
44 std::vector<base::ScopedFile> sockets_;
45 size_t available_sockets_;
Florian Mayeraf266022018-09-17 15:18:15 +010046 size_t dead_sockets_ = 0;
Florian Mayer824274d2018-09-17 11:33:45 +010047};
48
Florian Mayerb85a9382018-09-27 13:59:01 +010049// Socket borrowed from a SocketPool. Gets returned once it goes out of scope.
50class BorrowedSocket {
51 public:
52 BorrowedSocket(const BorrowedSocket&) = delete;
53 BorrowedSocket& operator=(const BorrowedSocket&) = delete;
54 BorrowedSocket(BorrowedSocket&& other) noexcept {
55 fd_ = std::move(other.fd_);
56 socket_pool_ = other.socket_pool_;
57 other.socket_pool_ = nullptr;
58 }
59
60 BorrowedSocket(base::ScopedFile fd, SocketPool* socket_pool)
61 : fd_(std::move(fd)), socket_pool_(socket_pool) {}
62
63 ~BorrowedSocket() {
64 if (socket_pool_ != nullptr)
65 socket_pool_->Return(std::move(fd_));
66 }
67
68 int operator*() { return get(); }
69
70 int get() { return *fd_; }
71
72 void Close() { fd_.reset(); }
73
Florian Mayerf1510c42018-10-17 11:40:52 +010074 operator bool() const { return !!fd_; }
75
Florian Mayerb85a9382018-09-27 13:59:01 +010076 private:
77 base::ScopedFile fd_;
78 SocketPool* socket_pool_ = nullptr;
79};
80
81// Cache for frees that have been observed. It is infeasible to send every
82// free separately, so we batch and send the whole buffer once it is full.
83class FreePage {
84 public:
85 // Add address to buffer. Flush if necessary using a socket borrowed from
86 // pool.
87 // Can be called from any thread. Must not hold mutex_.`
88 void Add(const uint64_t addr, uint64_t sequence_number, SocketPool* pool);
89
90 private:
91 // Needs to be called holding mutex_.
92 void FlushLocked(SocketPool* pool);
93
94 FreeMetadata free_page_;
95 std::mutex mutex_;
96 size_t offset_ = 0;
97};
98
99const char* GetThreadStackBase();
100
Florian Mayer88665be2018-10-03 13:15:38 +0100101// RAII wrapper around pthread_key_t. This is different from a ScopedResource
102// because it needs a separate boolean indicating validity.
103class PThreadKey {
104 public:
105 PThreadKey(const PThreadKey&) = delete;
106 PThreadKey& operator=(const PThreadKey&) = delete;
107
108 PThreadKey(void (*destructor)(void*)) noexcept
109 : valid_(pthread_key_create(&key_, destructor) == 0) {}
110 ~PThreadKey() noexcept {
111 if (valid_)
112 pthread_key_delete(key_);
113 }
114 bool valid() const { return valid_; }
115 pthread_key_t get() const {
116 PERFETTO_DCHECK(valid_);
117 return key_;
118 }
119
120 private:
121 pthread_key_t key_;
122 bool valid_;
123};
124
Florian Mayerb85a9382018-09-27 13:59:01 +0100125// This is created and owned by the malloc hooks.
126class Client {
127 public:
128 Client(std::vector<base::ScopedFile> sockets);
129 Client(const std::string& sock_name, size_t conns);
Florian Mayer9a7fb2b2018-10-17 18:17:40 +0100130 void RecordMalloc(uint64_t alloc_size,
131 uint64_t total_size,
132 uint64_t alloc_address);
Florian Mayerb85a9382018-09-27 13:59:01 +0100133 void RecordFree(uint64_t alloc_address);
Florian Mayer9a7fb2b2018-10-17 18:17:40 +0100134 void MaybeSampleAlloc(uint64_t alloc_size,
135 uint64_t alloc_address,
136 void* (*unhooked_malloc)(size_t),
137 void (*unhooked_free)(void*));
Florian Mayerb85a9382018-09-27 13:59:01 +0100138
Florian Mayer1fe77732018-10-02 17:25:53 +0100139 ClientConfiguration client_config_for_testing() { return client_config_; }
140
Florian Mayerb85a9382018-09-27 13:59:01 +0100141 private:
Florian Mayer9a7fb2b2018-10-17 18:17:40 +0100142 size_t ShouldSampleAlloc(uint64_t alloc_size,
143 void* (*unhooked_malloc)(size_t),
144 void (*unhooked_free)(void*));
Florian Mayerb85a9382018-09-27 13:59:01 +0100145 const char* GetStackBase();
146
Florian Mayerf1510c42018-10-17 11:40:52 +0100147 bool inited_ = false;
Florian Mayer1fe77732018-10-02 17:25:53 +0100148 ClientConfiguration client_config_;
Florian Mayer88665be2018-10-03 13:15:38 +0100149 PThreadKey pthread_key_;
Florian Mayerb85a9382018-09-27 13:59:01 +0100150 SocketPool socket_pool_;
151 FreePage free_page_;
152 const char* main_thread_stack_base_ = nullptr;
153 std::atomic<uint64_t> sequence_number_{0};
154};
155
Florian Mayer824274d2018-09-17 11:33:45 +0100156} // namespace perfetto
157
158#endif // SRC_PROFILING_MEMORY_CLIENT_H_