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