/* * 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. */ #ifndef SRC_PROFILING_MEMORY_CLIENT_H_ #define SRC_PROFILING_MEMORY_CLIENT_H_ #include <stddef.h> #include <sys/types.h> #include <atomic> #include <condition_variable> #include <mutex> #include <vector> #include "perfetto/base/unix_socket.h" #include "src/profiling/memory/sampler.h" #include "src/profiling/memory/shared_ring_buffer.h" #include "src/profiling/memory/unhooked_allocator.h" #include "src/profiling/memory/wire_protocol.h" namespace perfetto { namespace profiling { const char* GetThreadStackBase(); constexpr uint32_t kClientSockTimeoutMs = 1000; // Profiling client, used to sample and record the malloc/free family of calls, // and communicate the necessary state to a separate profiling daemon process. // // Created and owned by the malloc hooks. // // Methods of this class are thread-safe unless otherwise stated, in which case // the caller needs to synchronize calls behind a mutex or similar. // // Implementation warning: this class should not use any heap, as otherwise its // destruction would enter the possibly-hooked |free|, which can reference the // Client itself. If avoiding the heap is not possible, then look at using // UnhookedAllocator. class Client { public: // Returns a client that is ready for sampling allocations, using the given // socket (which should already be connected to heapprofd). // // Returns a shared_ptr since that is how the client will ultimately be used, // and to take advantage of std::allocate_shared putting the object & the // control block in one block of memory. static std::shared_ptr<Client> CreateAndHandshake( base::UnixSocketRaw sock, UnhookedAllocator<Client> unhooked_allocator); static base::Optional<base::UnixSocketRaw> ConnectToHeapprofd( const std::string& sock_name); bool RecordMalloc(uint64_t alloc_size, uint64_t total_size, uint64_t alloc_address); // Add address to buffer of deallocations. Flushes the buffer if necessary. bool RecordFree(uint64_t alloc_address); // Returns the number of bytes to assign to an allocation with the given // |alloc_size|, based on the current sampling rate. A return value of zero // means that the allocation should not be recorded. Not idempotent, each // invocation mutates the sampler state. // // Not thread-safe. size_t GetSampleSizeLocked(size_t alloc_size) { return sampler_.SampleSize(alloc_size); } // Public for std::allocate_shared. Use CreateAndHandshake() to create // instances instead. Client(base::UnixSocketRaw sock, ClientConfiguration client_config, SharedRingBuffer shmem, Sampler sampler, pid_t pid_at_creation, const char* main_thread_stack_base); ClientConfiguration client_config_for_testing() { return client_config_; } private: const char* GetStackBase(); // Flush the contents of free_batch_. Must hold free_batch_lock_. bool FlushFreesLocked(); bool SendControlSocketByte(); bool SendWireMessageWithRetriesIfBlocking(const WireMessage&); // This is only valid for non-blocking sockets. This is when // client_config_.block_client is true. bool IsConnected(); ClientConfiguration client_config_; // sampler_ operations are not thread-safe. Sampler sampler_; base::UnixSocketRaw sock_; // Protected by free_batch_lock_. FreeBatch free_batch_; std::timed_mutex free_batch_lock_; const char* main_thread_stack_base_{nullptr}; std::atomic<uint64_t> sequence_number_{0}; SharedRingBuffer shmem_; // Used to detect (during the slow path) the situation where the process has // forked during profiling, and is performing malloc operations in the child. // In this scenario, we want to stop profiling in the child, as otherwise // it'll proceed to write to the same shared buffer & control socket (with // duplicate sequence ids). const pid_t pid_at_creation_; }; } // namespace profiling } // namespace perfetto #endif // SRC_PROFILING_MEMORY_CLIENT_H_