• 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 #pragma once
18 
19 #include <sys/types.h>
20 
21 #include <atomic>
22 #include <condition_variable>
23 #include <functional>
24 #include <memory>
25 #include <mutex>
26 #include <thread>
27 
28 #include <android-base/macros.h>
29 #include <android-base/unique_fd.h>
30 
31 #include "event_fd.h"
32 #include "record.h"
33 
34 namespace simpleperf {
35 
36 // RecordBuffer is a circular buffer used to cache records in user-space. It allows one read
37 // thread and one write thread. The record read thread writes records to the buffer, and the main
38 // thread reads records from the buffer.
39 class RecordBuffer {
40  public:
41   RecordBuffer(size_t buffer_size);
size()42   size_t size() const { return buffer_size_; }
43 
44   // Return the size of writable space in the buffer.
45   size_t GetFreeSize() const;
46   // Allocate a writable space for a record. Return nullptr if there isn't enough space.
47   char* AllocWriteSpace(size_t record_size);
48   // Called after writing a record, let the read thread see the record.
49   void FinishWrite();
50 
51   // Get data of the current record. Return nullptr if there is no records in the buffer.
52   char* GetCurrentRecord();
53   // Called after reading a record, the space of the record will be writable.
54   void MoveToNextRecord();
55 
56  private:
57   std::atomic_size_t read_head_;
58   std::atomic_size_t write_head_;
59   size_t cur_write_record_size_ = 0;
60   size_t cur_read_record_size_ = 0;
61   const size_t buffer_size_;
62   std::unique_ptr<char> buffer_;
63 
64   DISALLOW_COPY_AND_ASSIGN(RecordBuffer);
65 };
66 
67 // Parse positions of different fields in record data.
68 class RecordParser {
69  public:
70   RecordParser(const perf_event_attr& attr);
71 
72   // Return pos of the time field in the record. If not available, return 0.
73   size_t GetTimePos(const perf_event_header& header) const;
74   // Return pos of the user stack size field in the sample record. If not available, return 0.
75   size_t GetStackSizePos(const std::function<void(size_t,size_t,void*)>& read_record_fn) const;
76 
77  private:
78   uint64_t sample_type_;
79   uint64_t sample_regs_count_;
80   size_t time_pos_in_sample_records_ = 0;
81   size_t time_rpos_in_non_sample_records_ = 0;
82   size_t callchain_pos_in_sample_records_ = 0;
83 };
84 
85 // Read records from the kernel buffer belong to an event_fd.
86 class KernelRecordReader {
87  public:
88   KernelRecordReader(EventFd* event_fd);
89 
GetEventFd()90   EventFd* GetEventFd() const { return event_fd_; }
91   // Get available data in the kernel buffer. Return true if there is some data.
92   bool GetDataFromKernelBuffer();
93   // Get header of the current record.
RecordHeader()94   const perf_event_header& RecordHeader() { return record_header_; }
95   // Get time of the current record.
RecordTime()96   uint64_t RecordTime() { return record_time_; }
97   // Read data of the current record.
98   void ReadRecord(size_t pos, size_t size, void* dest);
99   // Move to the next record, return false if there is no more records.
100   bool MoveToNextRecord(const RecordParser& parser);
101 
102  private:
103   EventFd* event_fd_;
104   char* buffer_;
105   size_t buffer_mask_;
106   size_t data_pos_ = 0;
107   size_t data_size_ = 0;
108   size_t init_data_size_ = 0;
109   perf_event_header record_header_ = {};
110   uint64_t record_time_ = 0;
111 };
112 
113 // To reduce sample lost rate when recording dwarf based call graph, RecordReadThread uses a
114 // separate high priority (nice -20) thread to read records from kernel buffers to a RecordBuffer.
115 class RecordReadThread {
116  public:
117   RecordReadThread(size_t record_buffer_size, const perf_event_attr& attr, size_t min_mmap_pages,
118                    size_t max_mmap_pages);
119   ~RecordReadThread();
SetBufferLevels(size_t record_buffer_low_level,size_t record_buffer_critical_level)120   void SetBufferLevels(size_t record_buffer_low_level, size_t record_buffer_critical_level) {
121     record_buffer_low_level_ = record_buffer_low_level;
122     record_buffer_critical_level_ = record_buffer_critical_level;
123   }
124 
125   // Below functions are called in the main thread:
126 
127   // When there are records in the RecordBuffer, data_callback will be called in the main thread.
128   bool RegisterDataCallback(IOEventLoop& loop, const std::function<bool()>& data_callback);
129   // Create and read kernel buffers for new event fds.
130   bool AddEventFds(const std::vector<EventFd*>& event_fds);
131   // Destroy kernel buffers of existing event fds.
132   bool RemoveEventFds(const std::vector<EventFd*>& event_fds);
133   // Move all available records in kernel buffers to the RecordBuffer.
134   bool SyncKernelBuffer();
135   // Stop the read thread, no more records will be put into the RecordBuffer.
136   bool StopReadThread();
137 
138   // If available, return the next record in the RecordBuffer, otherwise return nullptr.
139   std::unique_ptr<Record> GetRecord();
GetLostRecords(size_t * lost_samples,size_t * lost_non_samples,size_t * cut_stack_samples)140   void GetLostRecords(size_t* lost_samples, size_t* lost_non_samples, size_t* cut_stack_samples) {
141     *lost_samples = lost_samples_;
142     *lost_non_samples = lost_non_samples_;
143     *cut_stack_samples = cut_stack_samples_;
144   }
145 
146  private:
147   enum Cmd {
148     NO_CMD,
149     CMD_ADD_EVENT_FDS,
150     CMD_REMOVE_EVENT_FDS,
151     CMD_SYNC_KERNEL_BUFFER,
152     CMD_STOP_THREAD,
153   };
154 
155   bool SendCmdToReadThread(Cmd cmd, void* cmd_arg);
156 
157   // Below functions are called in the read thread:
158 
159   void RunReadThread();
160   void IncreaseThreadPriority();
161   Cmd GetCmd();
162   bool HandleCmd(IOEventLoop& loop);
163   bool HandleAddEventFds(IOEventLoop& loop, const std::vector<EventFd*>& event_fds);
164   bool HandleRemoveEventFds(const std::vector<EventFd*>& event_fds);
165   bool ReadRecordsFromKernelBuffer();
166   void PushRecordToRecordBuffer(KernelRecordReader* kernel_record_reader);
167   bool SendDataNotificationToMainThread();
168 
169   RecordBuffer record_buffer_;
170   // When free size in record buffer is below low level, we cut stack data of sample records to 1K.
171   size_t record_buffer_low_level_;
172   // When free size in record buffer is below critical level, we drop sample records to avoid
173   // losing more important records (like mmap or fork records).
174   size_t record_buffer_critical_level_;
175   RecordParser record_parser_;
176   perf_event_attr attr_;
177   size_t stack_size_in_sample_record_ = 0;
178   size_t min_mmap_pages_;
179   size_t max_mmap_pages_;
180 
181   // Used to pass command notification from the main thread to the read thread.
182   android::base::unique_fd write_cmd_fd_;
183   android::base::unique_fd read_cmd_fd_;
184   std::mutex cmd_mutex_;
185   std::condition_variable cmd_finish_cond_;
186   Cmd cmd_;
187   void* cmd_arg_;
188   bool cmd_result_;
189 
190   // Used to send data notification from the read thread to the main thread.
191   android::base::unique_fd write_data_fd_;
192   android::base::unique_fd read_data_fd_;
193   std::atomic_bool has_data_notification_;
194 
195   std::unique_ptr<std::thread> read_thread_;
196   std::vector<KernelRecordReader> kernel_record_readers_;
197 
198   size_t lost_samples_ = 0;
199   size_t lost_non_samples_ = 0;
200   size_t cut_stack_samples_ = 0;
201 };
202 
203 }  // namespace simpleperf
204