• 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   if (total_size > shmem->size()) {
62     errno = EMSGSIZE;
63     return -1;
64   }
65   SharedRingBuffer::Buffer buf;
66   {
67     ScopedSpinlock lock = shmem->AcquireLock(ScopedSpinlock::Mode::Try);
68     if (!lock.locked()) {
69       PERFETTO_DLOG("Failed to acquire spinlock.");
70       errno = EAGAIN;
71       return -1;
72     }
73     buf = shmem->BeginWrite(lock, total_size);
74   }
75   if (!buf) {
76     PERFETTO_DLOG("Buffer overflow.");
77     shmem->EndWrite(std::move(buf));
78     errno = EAGAIN;
79     return -1;
80   }
81 
82   fn(&buf);
83 
84   auto bytes_free = buf.bytes_free;
85   shmem->EndWrite(std::move(buf));
86   return static_cast<int64_t>(bytes_free);
87 }
88 
89 }  // namespace
90 
SendWireMessage(SharedRingBuffer * shmem,const WireMessage & msg)91 int64_t SendWireMessage(SharedRingBuffer* shmem, const WireMessage& msg) {
92   switch (msg.record_type) {
93     case RecordType::Malloc: {
94       size_t total_size = sizeof(msg.record_type) + sizeof(*msg.alloc_header) +
95                           msg.payload_size;
96       return WithBuffer(
97           shmem, total_size, [msg](SharedRingBuffer::Buffer* buf) {
98             memcpy(buf->data, &msg.record_type, sizeof(msg.record_type));
99             memcpy(buf->data + sizeof(msg.record_type), msg.alloc_header,
100                    sizeof(*msg.alloc_header));
101             UnsafeMemcpy(reinterpret_cast<char*>(buf->data) +
102                              sizeof(msg.record_type) +
103                              sizeof(*msg.alloc_header),
104                          msg.payload, msg.payload_size);
105           });
106     }
107     case RecordType::Free: {
108       constexpr size_t total_size =
109           sizeof(msg.record_type) + sizeof(*msg.free_header);
110       return WithBuffer(
111           shmem, total_size, [msg](SharedRingBuffer::Buffer* buf) {
112             memcpy(buf->data, &msg.record_type, sizeof(msg.record_type));
113             memcpy(buf->data + sizeof(msg.record_type), msg.free_header,
114                    sizeof(*msg.free_header));
115           });
116     }
117     case RecordType::HeapName: {
118       constexpr size_t total_size =
119           sizeof(msg.record_type) + sizeof(*msg.heap_name_header);
120       return WithBuffer(
121           shmem, total_size, [msg](SharedRingBuffer::Buffer* buf) {
122             memcpy(buf->data, &msg.record_type, sizeof(msg.record_type));
123             memcpy(buf->data + sizeof(msg.record_type), msg.heap_name_header,
124                    sizeof(*msg.heap_name_header));
125           });
126     }
127   }
128 }
129 
ReceiveWireMessage(char * buf,size_t size,WireMessage * out)130 bool ReceiveWireMessage(char* buf, size_t size, WireMessage* out) {
131   RecordType* record_type;
132   char* end = buf + size;
133   if (!ViewAndAdvance<RecordType>(&buf, &record_type, end)) {
134     PERFETTO_DFATAL_OR_ELOG("Cannot read record type.");
135     return false;
136   }
137 
138   out->payload = nullptr;
139   out->payload_size = 0;
140   out->record_type = *record_type;
141 
142   if (*record_type == RecordType::Malloc) {
143     if (!ViewAndAdvance<AllocMetadata>(&buf, &out->alloc_header, end)) {
144       PERFETTO_DFATAL_OR_ELOG("Cannot read alloc header.");
145       return false;
146     }
147     out->payload = buf;
148     if (buf > end) {
149       PERFETTO_DFATAL_OR_ELOG("Receive buffer overflowed");
150       return false;
151     }
152     out->payload_size = static_cast<size_t>(end - buf);
153   } else if (*record_type == RecordType::Free) {
154     if (!ViewAndAdvance<FreeEntry>(&buf, &out->free_header, end)) {
155       PERFETTO_DFATAL_OR_ELOG("Cannot read free header.");
156       return false;
157     }
158   } else if (*record_type == RecordType::HeapName) {
159     if (!ViewAndAdvance<HeapName>(&buf, &out->heap_name_header, end)) {
160       PERFETTO_DFATAL_OR_ELOG("Cannot read free header.");
161       return false;
162     }
163   } else {
164     PERFETTO_DFATAL_OR_ELOG("Invalid record type.");
165     return false;
166   }
167   return true;
168 }
169 
GetHeapSamplingInterval(const ClientConfiguration & cli_config,const char * heap_name)170 uint64_t GetHeapSamplingInterval(const ClientConfiguration& cli_config,
171                                  const char* heap_name) {
172   for (uint32_t i = 0; i < cli_config.num_heaps; ++i) {
173     const ClientConfigurationHeap& heap = cli_config.heaps[i];
174     static_assert(sizeof(heap.name) == HEAPPROFD_HEAP_NAME_SZ,
175                   "correct heap name size");
176     if (strncmp(&heap.name[0], heap_name, HEAPPROFD_HEAP_NAME_SZ) == 0) {
177       return heap.interval;
178     }
179   }
180   if (cli_config.all_heaps) {
181     return cli_config.default_interval;
182   }
183   return 0;
184 }
185 
186 }  // namespace profiling
187 }  // namespace perfetto
188