123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203 |
- /*
- * Copyright (C) 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- #pragma once
- #include <sys/types.h>
- #include <atomic>
- #include <condition_variable>
- #include <functional>
- #include <memory>
- #include <mutex>
- #include <thread>
- #include <android-base/macros.h>
- #include <android-base/unique_fd.h>
- #include "event_fd.h"
- #include "record.h"
- namespace simpleperf {
- // RecordBuffer is a circular buffer used to cache records in user-space. It allows one read
- // thread and one write thread. The record read thread writes records to the buffer, and the main
- // thread reads records from the buffer.
- class RecordBuffer {
- public:
- RecordBuffer(size_t buffer_size);
- size_t size() const { return buffer_size_; }
- // Return the size of writable space in the buffer.
- size_t GetFreeSize() const;
- // Allocate a writable space for a record. Return nullptr if there isn't enough space.
- char* AllocWriteSpace(size_t record_size);
- // Called after writing a record, let the read thread see the record.
- void FinishWrite();
- // Get data of the current record. Return nullptr if there is no records in the buffer.
- char* GetCurrentRecord();
- // Called after reading a record, the space of the record will be writable.
- void MoveToNextRecord();
- private:
- std::atomic_size_t read_head_;
- std::atomic_size_t write_head_;
- size_t cur_write_record_size_ = 0;
- size_t cur_read_record_size_ = 0;
- const size_t buffer_size_;
- std::unique_ptr<char> buffer_;
- DISALLOW_COPY_AND_ASSIGN(RecordBuffer);
- };
- // Parse positions of different fields in record data.
- class RecordParser {
- public:
- RecordParser(const perf_event_attr& attr);
- // Return pos of the time field in the record. If not available, return 0.
- size_t GetTimePos(const perf_event_header& header) const;
- // Return pos of the user stack size field in the sample record. If not available, return 0.
- size_t GetStackSizePos(const std::function<void(size_t,size_t,void*)>& read_record_fn) const;
- private:
- uint64_t sample_type_;
- uint64_t sample_regs_count_;
- size_t time_pos_in_sample_records_ = 0;
- size_t time_rpos_in_non_sample_records_ = 0;
- size_t callchain_pos_in_sample_records_ = 0;
- };
- // Read records from the kernel buffer belong to an event_fd.
- class KernelRecordReader {
- public:
- KernelRecordReader(EventFd* event_fd);
- EventFd* GetEventFd() const { return event_fd_; }
- // Get available data in the kernel buffer. Return true if there is some data.
- bool GetDataFromKernelBuffer();
- // Get header of the current record.
- const perf_event_header& RecordHeader() { return record_header_; }
- // Get time of the current record.
- uint64_t RecordTime() { return record_time_; }
- // Read data of the current record.
- void ReadRecord(size_t pos, size_t size, void* dest);
- // Move to the next record, return false if there is no more records.
- bool MoveToNextRecord(const RecordParser& parser);
- private:
- EventFd* event_fd_;
- char* buffer_;
- size_t buffer_mask_;
- size_t data_pos_ = 0;
- size_t data_size_ = 0;
- size_t init_data_size_ = 0;
- perf_event_header record_header_ = {};
- uint64_t record_time_ = 0;
- };
- // To reduce sample lost rate when recording dwarf based call graph, RecordReadThread uses a
- // separate high priority (nice -20) thread to read records from kernel buffers to a RecordBuffer.
- class RecordReadThread {
- public:
- RecordReadThread(size_t record_buffer_size, const perf_event_attr& attr, size_t min_mmap_pages,
- size_t max_mmap_pages);
- ~RecordReadThread();
- void SetBufferLevels(size_t record_buffer_low_level, size_t record_buffer_critical_level) {
- record_buffer_low_level_ = record_buffer_low_level;
- record_buffer_critical_level_ = record_buffer_critical_level;
- }
- // Below functions are called in the main thread:
- // When there are records in the RecordBuffer, data_callback will be called in the main thread.
- bool RegisterDataCallback(IOEventLoop& loop, const std::function<bool()>& data_callback);
- // Create and read kernel buffers for new event fds.
- bool AddEventFds(const std::vector<EventFd*>& event_fds);
- // Destroy kernel buffers of existing event fds.
- bool RemoveEventFds(const std::vector<EventFd*>& event_fds);
- // Move all available records in kernel buffers to the RecordBuffer.
- bool SyncKernelBuffer();
- // Stop the read thread, no more records will be put into the RecordBuffer.
- bool StopReadThread();
- // If available, return the next record in the RecordBuffer, otherwise return nullptr.
- std::unique_ptr<Record> GetRecord();
- void GetLostRecords(size_t* lost_samples, size_t* lost_non_samples, size_t* cut_stack_samples) {
- *lost_samples = lost_samples_;
- *lost_non_samples = lost_non_samples_;
- *cut_stack_samples = cut_stack_samples_;
- }
- private:
- enum Cmd {
- NO_CMD,
- CMD_ADD_EVENT_FDS,
- CMD_REMOVE_EVENT_FDS,
- CMD_SYNC_KERNEL_BUFFER,
- CMD_STOP_THREAD,
- };
- bool SendCmdToReadThread(Cmd cmd, void* cmd_arg);
- // Below functions are called in the read thread:
- void RunReadThread();
- void IncreaseThreadPriority();
- Cmd GetCmd();
- bool HandleCmd(IOEventLoop& loop);
- bool HandleAddEventFds(IOEventLoop& loop, const std::vector<EventFd*>& event_fds);
- bool HandleRemoveEventFds(const std::vector<EventFd*>& event_fds);
- bool ReadRecordsFromKernelBuffer();
- void PushRecordToRecordBuffer(KernelRecordReader* kernel_record_reader);
- bool SendDataNotificationToMainThread();
- RecordBuffer record_buffer_;
- // When free size in record buffer is below low level, we cut stack data of sample records to 1K.
- size_t record_buffer_low_level_;
- // When free size in record buffer is below critical level, we drop sample records to avoid
- // losing more important records (like mmap or fork records).
- size_t record_buffer_critical_level_;
- RecordParser record_parser_;
- perf_event_attr attr_;
- size_t stack_size_in_sample_record_ = 0;
- size_t min_mmap_pages_;
- size_t max_mmap_pages_;
- // Used to pass command notification from the main thread to the read thread.
- android::base::unique_fd write_cmd_fd_;
- android::base::unique_fd read_cmd_fd_;
- std::mutex cmd_mutex_;
- std::condition_variable cmd_finish_cond_;
- Cmd cmd_;
- void* cmd_arg_;
- bool cmd_result_;
- // Used to send data notification from the read thread to the main thread.
- android::base::unique_fd write_data_fd_;
- android::base::unique_fd read_data_fd_;
- std::atomic_bool has_data_notification_;
- std::unique_ptr<std::thread> read_thread_;
- std::vector<KernelRecordReader> kernel_record_readers_;
- size_t lost_samples_ = 0;
- size_t lost_non_samples_ = 0;
- size_t cut_stack_samples_ = 0;
- };
- } // namespace simpleperf
|