/* * 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 "src/profiling/memory/shared_ring_buffer.h" namespace perfetto { namespace base { class UnixSocketRaw; } namespace profiling { struct ClientConfiguration { // On average, sample one allocation every interval bytes, // If interval == 1, sample every allocation. // Must be >= 1. uint64_t interval; bool block_client; }; // Types needed for the wire format used for communication between the client // and heapprofd. The basic format of a record 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 sequence of FreeBatchEntry. // Use uint64_t to make sure the following data is aligned as 64bit is the // strongest alignment requirement. // 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( sizeof(unwindstack::arm_user_regs), sizeof(unwindstack::arm64_user_regs)), sizeof(unwindstack::x86_user_regs)), sizeof(unwindstack::x86_64_user_regs)), sizeof(unwindstack::mips_user_regs)), sizeof(unwindstack::mips64_user_regs) ); // clang-format on constexpr size_t kFreeBatchSize = 1024; enum class RecordType : uint64_t { Free = 0, Malloc = 1, }; struct AllocMetadata { uint64_t sequence_number; // Size of the allocation that was made. uint64_t alloc_size; // Total number of bytes attributed to this allocation. uint64_t total_size; // Pointer returned by malloc(2) for this allocation. uint64_t alloc_address; // Current value of the stack pointer. uint64_t stack_pointer; // Offset of the data at stack_pointer from the start of this record. uint64_t stack_pointer_offset; uint64_t clock_monotonic_coarse_timestamp; alignas(uint64_t) char register_data[kMaxRegisterDataSize]; // CPU architecture of the client. This determines the size of the // register data that follows this struct. unwindstack::ArchEnum arch; }; struct FreeBatchEntry { uint64_t sequence_number; uint64_t addr; }; struct FreeBatch { uint64_t num_entries; uint64_t clock_monotonic_coarse_timestamp; FreeBatchEntry entries[kFreeBatchSize]; FreeBatch() { num_entries = 0; } }; enum HandshakeFDs : size_t { kHandshakeMaps = 0, kHandshakeMem = 1, kHandshakeSize = 2, }; struct WireMessage { RecordType record_type; AllocMetadata* alloc_header; FreeBatch* free_header; char* payload; size_t payload_size; }; bool 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); 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_