blob: 17ad4de8bc6d0397a73fc5db7e4a996861ced8bc [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.
*/
// The data types used for communication between heapprofd and the client
// embedded in processes that are being profiled.
#ifndef SRC_PROFILING_MEMORY_WIRE_PROTOCOL_H_
#define SRC_PROFILING_MEMORY_WIRE_PROTOCOL_H_
#include <algorithm>
#include <cinttypes>
#include <unwindstack/Elf.h>
#include <unwindstack/MachineArm.h>
#include <unwindstack/MachineArm64.h>
#include <unwindstack/MachineRiscv64.h>
#include <unwindstack/MachineX86.h>
#include <unwindstack/MachineX86_64.h>
#include "perfetto/heap_profile.h"
#include "src/profiling/memory/shared_ring_buffer.h"
#include "src/profiling/memory/util.h"
namespace perfetto {
namespace base {
class UnixSocketRaw;
}
namespace profiling {
constexpr size_t kMaxRegisterDataSize =
std::max({sizeof(uint32_t) * unwindstack::ARM_REG_LAST,
sizeof(uint64_t) * unwindstack::ARM64_REG_LAST,
sizeof(uint32_t) * unwindstack::X86_REG_LAST,
sizeof(uint64_t) * unwindstack::X86_64_REG_LAST,
sizeof(uint64_t) * unwindstack::RISCV64_REG_COUNT});
// Types needed for the wire format used for communication between the client
// and heapprofd. The basic format of a record sent by the client is
// record size (uint64_t) | record type (RecordType = uint64_t) | record
// If record type is Malloc, the record format is AllocMetdata | raw stack.
// If the record type is Free, the record is a FreeEntry.
// If record type is HeapName, the record is a HeapName.
// On connect, heapprofd sends one ClientConfiguration struct over the control
// socket.
// Use uint64_t to make sure the following data is aligned as 64bit is the
// strongest alignment requirement.
struct ClientConfigurationHeap {
char name[HEAPPROFD_HEAP_NAME_SZ];
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) interval;
};
struct ClientConfiguration {
// On average, sample one allocation every interval bytes,
// If interval == 1, sample every allocation.
// Must be >= 1.
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) default_interval;
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) block_client_timeout_us;
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) num_heaps;
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) adaptive_sampling_shmem_threshold;
PERFETTO_CROSS_ABI_ALIGNED(uint64_t)
adaptive_sampling_max_sampling_interval_bytes;
alignas(8) ClientConfigurationHeap heaps[64];
PERFETTO_CROSS_ABI_ALIGNED(bool) block_client;
PERFETTO_CROSS_ABI_ALIGNED(bool) disable_fork_teardown;
PERFETTO_CROSS_ABI_ALIGNED(bool) disable_vfork_detection;
PERFETTO_CROSS_ABI_ALIGNED(bool) all_heaps;
// Just double check that the array sizes are in correct order.
};
enum class RecordType : uint64_t {
Free = 0,
Malloc = 1,
HeapName = 2,
};
// Make the whole struct 8-aligned. This is to make sizeof(AllocMetdata)
// the same on 32 and 64-bit.
struct alignas(8) AllocMetadata {
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) sequence_number;
// Size of the allocation that was made.
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) alloc_size;
// Total number of bytes attributed to this allocation.
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) sample_size;
// Pointer returned by malloc(2) for this allocation.
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) alloc_address;
// Current value of the stack pointer.
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) stack_pointer;
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) clock_monotonic_coarse_timestamp;
// unwindstack::AsmGetRegs assumes this is aligned.
alignas(8) char register_data[kMaxRegisterDataSize];
PERFETTO_CROSS_ABI_ALIGNED(uint32_t) heap_id;
// CPU architecture of the client.
PERFETTO_CROSS_ABI_ALIGNED(unwindstack::ArchEnum) arch;
};
struct FreeEntry {
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) sequence_number;
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) addr;
PERFETTO_CROSS_ABI_ALIGNED(uint32_t) heap_id;
};
struct HeapName {
PERFETTO_CROSS_ABI_ALIGNED(uint64_t) sample_interval;
PERFETTO_CROSS_ABI_ALIGNED(uint32_t) heap_id;
PERFETTO_CROSS_ABI_ALIGNED(char) heap_name[HEAPPROFD_HEAP_NAME_SZ];
};
// Make sure the sizes do not change on different architectures.
static_assert(sizeof(AllocMetadata) == 328,
"AllocMetadata needs to be the same size across ABIs.");
static_assert(sizeof(FreeEntry) == 24,
"FreeEntry needs to be the same size across ABIs.");
static_assert(sizeof(HeapName) == 80,
"HeapName needs to be the same size across ABIs.");
static_assert(sizeof(ClientConfiguration) == 4656,
"ClientConfiguration needs to be the same size across ABIs.");
enum HandshakeFDs : size_t {
kHandshakeMaps = 0,
kHandshakeMem,
kHandshakeSize,
};
struct WireMessage {
RecordType record_type;
AllocMetadata* alloc_header;
FreeEntry* free_header;
HeapName* heap_name_header;
char* payload;
size_t payload_size;
};
int64_t SendWireMessage(SharedRingBuffer* buf, const WireMessage& msg);
// Parse message received over the wire.
// |buf| has to outlive |out|.
// If buf is not a valid message, return false.
bool ReceiveWireMessage(char* buf, size_t size, WireMessage* out);
uint64_t GetHeapSamplingInterval(const ClientConfiguration& cli_config,
const char* heap_name);
constexpr const char* kHeapprofdSocketEnvVar = "ANDROID_SOCKET_heapprofd";
constexpr const char* kHeapprofdSocketFile = "/dev/socket/heapprofd";
} // namespace profiling
} // namespace perfetto
#endif // SRC_PROFILING_MEMORY_WIRE_PROTOCOL_H_