blob: 9d30bebb5fe8c596599ced916761f14bbd6ca278 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/profiling/memory/wire_protocol.h"
#include "perfetto/base/logging.h"
#include "perfetto/ext/base/unix_socket.h"
#include "perfetto/ext/base/utils.h"
#include "src/profiling/memory/shared_ring_buffer.h"
#include <sys/socket.h>
#include <sys/types.h>
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
#include <bionic/mte.h>
#else
struct ScopedDisableMTE {
// Silence unused variable warnings in non-Android builds.
ScopedDisableMTE() {}
};
#endif
namespace perfetto {
namespace profiling {
namespace {
template <typename T>
bool ViewAndAdvance(char** ptr, T** out, const char* end) {
if (end - sizeof(T) < *ptr)
return false;
*out = reinterpret_cast<T*>(*ptr);
*ptr += sizeof(T);
return true;
}
// We need this to prevent crashes due to FORTIFY_SOURCE.
void UnsafeMemcpy(char* dest, const char* src, size_t n)
__attribute__((no_sanitize("address", "hwaddress"))) {
ScopedDisableMTE m;
for (size_t i = 0; i < n; ++i) {
dest[i] = src[i];
}
}
template <typename F>
int64_t WithBuffer(SharedRingBuffer* shmem, size_t total_size, F fn) {
if (total_size > shmem->size()) {
errno = EMSGSIZE;
return -1;
}
SharedRingBuffer::Buffer buf;
{
ScopedSpinlock lock = shmem->AcquireLock(ScopedSpinlock::Mode::Try);
if (!lock.locked()) {
PERFETTO_DLOG("Failed to acquire spinlock.");
errno = EAGAIN;
return -1;
}
buf = shmem->BeginWrite(lock, total_size);
}
if (!buf) {
PERFETTO_DLOG("Buffer overflow.");
shmem->EndWrite(std::move(buf));
errno = EAGAIN;
return -1;
}
fn(&buf);
auto bytes_free = buf.bytes_free;
shmem->EndWrite(std::move(buf));
return static_cast<int64_t>(bytes_free);
}
} // namespace
int64_t SendWireMessage(SharedRingBuffer* shmem, const WireMessage& msg) {
switch (msg.record_type) {
case RecordType::Malloc: {
size_t total_size = sizeof(msg.record_type) + sizeof(*msg.alloc_header) +
msg.payload_size;
return WithBuffer(
shmem, total_size, [msg](SharedRingBuffer::Buffer* buf) {
memcpy(buf->data, &msg.record_type, sizeof(msg.record_type));
memcpy(buf->data + sizeof(msg.record_type), msg.alloc_header,
sizeof(*msg.alloc_header));
UnsafeMemcpy(reinterpret_cast<char*>(buf->data) +
sizeof(msg.record_type) +
sizeof(*msg.alloc_header),
msg.payload, msg.payload_size);
});
}
case RecordType::Free: {
constexpr size_t total_size =
sizeof(msg.record_type) + sizeof(*msg.free_header);
return WithBuffer(
shmem, total_size, [msg](SharedRingBuffer::Buffer* buf) {
memcpy(buf->data, &msg.record_type, sizeof(msg.record_type));
memcpy(buf->data + sizeof(msg.record_type), msg.free_header,
sizeof(*msg.free_header));
});
}
case RecordType::HeapName: {
constexpr size_t total_size =
sizeof(msg.record_type) + sizeof(*msg.heap_name_header);
return WithBuffer(
shmem, total_size, [msg](SharedRingBuffer::Buffer* buf) {
memcpy(buf->data, &msg.record_type, sizeof(msg.record_type));
memcpy(buf->data + sizeof(msg.record_type), msg.heap_name_header,
sizeof(*msg.heap_name_header));
});
}
}
}
bool ReceiveWireMessage(char* buf, size_t size, WireMessage* out) {
RecordType* record_type;
char* end = buf + size;
if (!ViewAndAdvance<RecordType>(&buf, &record_type, end)) {
PERFETTO_DFATAL_OR_ELOG("Cannot read record type.");
return false;
}
out->payload = nullptr;
out->payload_size = 0;
out->record_type = *record_type;
if (*record_type == RecordType::Malloc) {
if (!ViewAndAdvance<AllocMetadata>(&buf, &out->alloc_header, end)) {
PERFETTO_DFATAL_OR_ELOG("Cannot read alloc header.");
return false;
}
out->payload = buf;
if (buf > end) {
PERFETTO_DFATAL_OR_ELOG("Receive buffer overflowed");
return false;
}
out->payload_size = static_cast<size_t>(end - buf);
} else if (*record_type == RecordType::Free) {
if (!ViewAndAdvance<FreeEntry>(&buf, &out->free_header, end)) {
PERFETTO_DFATAL_OR_ELOG("Cannot read free header.");
return false;
}
} else if (*record_type == RecordType::HeapName) {
if (!ViewAndAdvance<HeapName>(&buf, &out->heap_name_header, end)) {
PERFETTO_DFATAL_OR_ELOG("Cannot read free header.");
return false;
}
} else {
PERFETTO_DFATAL_OR_ELOG("Invalid record type.");
return false;
}
return true;
}
uint64_t GetHeapSamplingInterval(const ClientConfiguration& cli_config,
const char* heap_name) {
for (uint32_t i = 0; i < cli_config.num_heaps; ++i) {
const ClientConfigurationHeap& heap = cli_config.heaps[i];
static_assert(sizeof(heap.name) == HEAPPROFD_HEAP_NAME_SZ,
"correct heap name size");
if (strncmp(&heap.name[0], heap_name, HEAPPROFD_HEAP_NAME_SZ) == 0) {
return heap.interval;
}
}
if (cli_config.all_heaps) {
return cli_config.default_interval;
}
return 0;
}
} // namespace profiling
} // namespace perfetto