/* * 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 #include #include #include #include #include #include #include #include #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 { // C++11 std::max is not constexpr. constexpr size_t constexpr_max(size_t x, size_t y) { return x > y ? x : y; } // clang-format makes this unreadable. Turning it off for this block. // clang-format off constexpr size_t kMaxRegisterDataSize = constexpr_max( constexpr_max( constexpr_max( constexpr_max( constexpr_max( constexpr_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(uint32_t) * unwindstack::MIPS_REG_LAST), sizeof(uint64_t) * unwindstack::MIPS64_REG_LAST), sizeof(uint64_t) * unwindstack::RISCV64_REG_MAX ); // clang-format on // 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_