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