• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef SRC_PROFILING_MEMORY_CLIENT_H_
18 #define SRC_PROFILING_MEMORY_CLIENT_H_
19 
20 #include <stddef.h>
21 #include <sys/types.h>
22 
23 #include <atomic>
24 #include <condition_variable>
25 #include <mutex>
26 #include <vector>
27 
28 #include "perfetto/base/compiler.h"
29 #include "perfetto/ext/base/unix_socket.h"
30 #include "src/profiling/memory/sampler.h"
31 #include "src/profiling/memory/shared_ring_buffer.h"
32 #include "src/profiling/memory/unhooked_allocator.h"
33 #include "src/profiling/memory/wire_protocol.h"
34 
35 namespace perfetto {
36 namespace profiling {
37 
38 struct StackRange {
39   const char* begin;
40   // One past the highest address part of the stack.
41   const char* end;
42 };
43 
44 StackRange GetThreadStackRange();
45 StackRange GetSigAltStackRange();
46 StackRange GetMainThreadStackRange();
47 
48 constexpr uint64_t kInfiniteTries = 0;
49 constexpr uint32_t kClientSockTimeoutMs = 1000;
50 
51 uint64_t GetMaxTries(const ClientConfiguration& client_config);
52 
53 // Profiling client, used to sample and record the malloc/free family of calls,
54 // and communicate the necessary state to a separate profiling daemon process.
55 //
56 // Created and owned by the malloc hooks.
57 //
58 // Methods of this class are thread-safe unless otherwise stated, in which case
59 // the caller needs to synchronize calls behind a mutex or similar.
60 //
61 // Implementation warning: this class should not use any heap, as otherwise its
62 // destruction would enter the possibly-hooked |free|, which can reference the
63 // Client itself. If avoiding the heap is not possible, then look at using
64 // UnhookedAllocator.
65 class Client {
66  public:
67   // Returns a client that is ready for sampling allocations, using the given
68   // socket (which should already be connected to heapprofd).
69   //
70   // Returns a shared_ptr since that is how the client will ultimately be used,
71   // and to take advantage of std::allocate_shared putting the object & the
72   // control block in one block of memory.
73   static std::shared_ptr<Client> CreateAndHandshake(
74       base::UnixSocketRaw sock,
75       UnhookedAllocator<Client> unhooked_allocator);
76 
77   static base::Optional<base::UnixSocketRaw> ConnectToHeapprofd(
78       const std::string& sock_name);
79 
80   bool RecordMalloc(uint32_t heap_id,
81                     uint64_t sample_size,
82                     uint64_t alloc_size,
83                     uint64_t alloc_address) PERFETTO_WARN_UNUSED_RESULT;
84 
85   // Add address to buffer of deallocations. Flushes the buffer if necessary.
86   bool RecordFree(uint32_t heap_id,
87                   uint64_t alloc_address) PERFETTO_WARN_UNUSED_RESULT;
88   bool RecordHeapInfo(uint32_t heap_id,
89                       const char* heap_name,
90                       uint64_t interval);
91 
AddClientSpinlockBlockedUs(size_t n)92   void AddClientSpinlockBlockedUs(size_t n) {
93     shmem_.AddClientSpinlockBlockedUs(n);
94   }
95 
96   // Public for std::allocate_shared. Use CreateAndHandshake() to create
97   // instances instead.
98   Client(base::UnixSocketRaw sock,
99          ClientConfiguration client_config,
100          SharedRingBuffer shmem,
101          pid_t pid_at_creation,
102          StackRange main_thread_stack_range);
103 
104   ~Client();
105 
client_config()106   const ClientConfiguration& client_config() { return client_config_; }
adaptive_sampling_shmem_threshold()107   uint64_t adaptive_sampling_shmem_threshold() {
108     return client_config_.adaptive_sampling_shmem_threshold;
109   }
adaptive_sampling_max_sampling_interval_bytes()110   uint64_t adaptive_sampling_max_sampling_interval_bytes() {
111     return client_config_.adaptive_sampling_max_sampling_interval_bytes;
112   }
write_avail()113   uint64_t write_avail() { return shmem_.write_avail(); }
114 
115   bool IsConnected();
116 
117  private:
118   const char* GetStackEnd(const char* stacktop);
119   bool SendControlSocketByte() PERFETTO_WARN_UNUSED_RESULT;
120   int64_t SendWireMessageWithRetriesIfBlocking(const WireMessage&)
121       PERFETTO_WARN_UNUSED_RESULT;
122 
123   bool IsPostFork();
124 
125 
126   ClientConfiguration client_config_;
127   uint64_t max_shmem_tries_;
128   base::UnixSocketRaw sock_;
129 
130   StackRange main_thread_stack_range_{nullptr, nullptr};
131   std::atomic<uint64_t>
132       sequence_number_[base::ArraySize(ClientConfiguration{}.heaps)] = {};
133   SharedRingBuffer shmem_;
134 
135   // Used to detect (during the slow path) the situation where the process has
136   // forked during profiling, and is performing malloc operations in the child.
137   // In this scenario, we want to stop profiling in the child, as otherwise
138   // it'll proceed to write to the same shared buffer & control socket (with
139   // duplicate sequence ids).
140   const pid_t pid_at_creation_;
141   bool detected_fork_ = false;
142   bool postfork_return_value_ = false;
143 };
144 
145 }  // namespace profiling
146 }  // namespace perfetto
147 
148 #endif  // SRC_PROFILING_MEMORY_CLIENT_H_
149