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