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