| /* | 
 |  * 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 <cinttypes> | 
 |  | 
 | #include <unwindstack/Elf.h> | 
 | #include <unwindstack/MachineArm.h> | 
 | #include <unwindstack/MachineArm64.h> | 
 | #include <unwindstack/MachineMips.h> | 
 | #include <unwindstack/MachineMips64.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 { | 
 |  | 
 | // 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_ |