• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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