1 /*
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/wire_protocol.h"
18
19 #include "perfetto/base/logging.h"
20 #include "perfetto/ext/base/unix_socket.h"
21 #include "perfetto/ext/base/utils.h"
22 #include "src/profiling/memory/shared_ring_buffer.h"
23
24 #include <sys/socket.h>
25 #include <sys/types.h>
26
27 #if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
28 #include <bionic/mte.h>
29 #else
30 struct ScopedDisableMTE {
31 // Silence unused variable warnings in non-Android builds.
ScopedDisableMTEScopedDisableMTE32 ScopedDisableMTE() {}
33 };
34 #endif
35
36 namespace perfetto {
37 namespace profiling {
38
39 namespace {
40
41 template <typename T>
ViewAndAdvance(char ** ptr,T ** out,const char * end)42 bool ViewAndAdvance(char** ptr, T** out, const char* end) {
43 if (end - sizeof(T) < *ptr)
44 return false;
45 *out = reinterpret_cast<T*>(*ptr);
46 *ptr += sizeof(T);
47 return true;
48 }
49
50 // We need this to prevent crashes due to FORTIFY_SOURCE.
UnsafeMemcpy(char * dest,const char * src,size_t n)51 void UnsafeMemcpy(char* dest, const char* src, size_t n)
52 __attribute__((no_sanitize("address", "hwaddress"))) {
53 ScopedDisableMTE m;
54 for (size_t i = 0; i < n; ++i) {
55 dest[i] = src[i];
56 }
57 }
58
59 template <typename F>
WithBuffer(SharedRingBuffer * shmem,size_t total_size,F fn)60 int64_t WithBuffer(SharedRingBuffer* shmem, size_t total_size, F fn) {
61 SharedRingBuffer::Buffer buf;
62 {
63 ScopedSpinlock lock = shmem->AcquireLock(ScopedSpinlock::Mode::Try);
64 if (!lock.locked()) {
65 PERFETTO_DLOG("Failed to acquire spinlock.");
66 errno = EAGAIN;
67 return -1;
68 }
69 buf = shmem->BeginWrite(lock, total_size);
70 }
71 if (!buf) {
72 PERFETTO_DLOG("Buffer overflow.");
73 shmem->EndWrite(std::move(buf));
74 errno = EAGAIN;
75 return -1;
76 }
77
78 fn(&buf);
79
80 auto bytes_free = buf.bytes_free;
81 shmem->EndWrite(std::move(buf));
82 return static_cast<int64_t>(bytes_free);
83 }
84
85 } // namespace
86
SendWireMessage(SharedRingBuffer * shmem,const WireMessage & msg)87 int64_t SendWireMessage(SharedRingBuffer* shmem, const WireMessage& msg) {
88 switch (msg.record_type) {
89 case RecordType::Malloc: {
90 size_t total_size = sizeof(msg.record_type) + sizeof(*msg.alloc_header) +
91 msg.payload_size;
92 return WithBuffer(
93 shmem, total_size, [msg](SharedRingBuffer::Buffer* buf) {
94 memcpy(buf->data, &msg.record_type, sizeof(msg.record_type));
95 memcpy(buf->data + sizeof(msg.record_type), msg.alloc_header,
96 sizeof(*msg.alloc_header));
97 UnsafeMemcpy(reinterpret_cast<char*>(buf->data) +
98 sizeof(msg.record_type) +
99 sizeof(*msg.alloc_header),
100 msg.payload, msg.payload_size);
101 });
102 }
103 case RecordType::Free: {
104 constexpr size_t total_size =
105 sizeof(msg.record_type) + sizeof(*msg.free_header);
106 return WithBuffer(
107 shmem, total_size, [msg](SharedRingBuffer::Buffer* buf) {
108 memcpy(buf->data, &msg.record_type, sizeof(msg.record_type));
109 memcpy(buf->data + sizeof(msg.record_type), msg.free_header,
110 sizeof(*msg.free_header));
111 });
112 }
113 case RecordType::HeapName: {
114 constexpr size_t total_size =
115 sizeof(msg.record_type) + sizeof(*msg.heap_name_header);
116 return WithBuffer(
117 shmem, total_size, [msg](SharedRingBuffer::Buffer* buf) {
118 memcpy(buf->data, &msg.record_type, sizeof(msg.record_type));
119 memcpy(buf->data + sizeof(msg.record_type), msg.heap_name_header,
120 sizeof(*msg.heap_name_header));
121 });
122 }
123 }
124 }
125
ReceiveWireMessage(char * buf,size_t size,WireMessage * out)126 bool ReceiveWireMessage(char* buf, size_t size, WireMessage* out) {
127 RecordType* record_type;
128 char* end = buf + size;
129 if (!ViewAndAdvance<RecordType>(&buf, &record_type, end)) {
130 PERFETTO_DFATAL_OR_ELOG("Cannot read record type.");
131 return false;
132 }
133
134 out->payload = nullptr;
135 out->payload_size = 0;
136 out->record_type = *record_type;
137
138 if (*record_type == RecordType::Malloc) {
139 if (!ViewAndAdvance<AllocMetadata>(&buf, &out->alloc_header, end)) {
140 PERFETTO_DFATAL_OR_ELOG("Cannot read alloc header.");
141 return false;
142 }
143 out->payload = buf;
144 if (buf > end) {
145 PERFETTO_DFATAL_OR_ELOG("Receive buffer overflowed");
146 return false;
147 }
148 out->payload_size = static_cast<size_t>(end - buf);
149 } else if (*record_type == RecordType::Free) {
150 if (!ViewAndAdvance<FreeEntry>(&buf, &out->free_header, end)) {
151 PERFETTO_DFATAL_OR_ELOG("Cannot read free header.");
152 return false;
153 }
154 } else if (*record_type == RecordType::HeapName) {
155 if (!ViewAndAdvance<HeapName>(&buf, &out->heap_name_header, end)) {
156 PERFETTO_DFATAL_OR_ELOG("Cannot read free header.");
157 return false;
158 }
159 } else {
160 PERFETTO_DFATAL_OR_ELOG("Invalid record type.");
161 return false;
162 }
163 return true;
164 }
165
GetHeapSamplingInterval(const ClientConfiguration & cli_config,const char * heap_name)166 uint64_t GetHeapSamplingInterval(const ClientConfiguration& cli_config,
167 const char* heap_name) {
168 for (uint32_t i = 0; i < cli_config.num_heaps; ++i) {
169 const ClientConfigurationHeap& heap = cli_config.heaps[i];
170 static_assert(sizeof(heap.name) == HEAPPROFD_HEAP_NAME_SZ,
171 "correct heap name size");
172 if (strncmp(&heap.name[0], heap_name, HEAPPROFD_HEAP_NAME_SZ) == 0) {
173 return heap.interval;
174 }
175 }
176 if (cli_config.all_heaps) {
177 return cli_config.default_interval;
178 }
179 return 0;
180 }
181
182 } // namespace profiling
183 } // namespace perfetto
184