1 /* 2 * Copyright (C) 2017 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 #ifndef SRC_FTRACE_READER_CPU_READER_H_ 18 #define SRC_FTRACE_READER_CPU_READER_H_ 19 20 #include <stdint.h> 21 #include <string.h> 22 23 #include <array> 24 #include <atomic> 25 #include <memory> 26 #include <set> 27 #include <thread> 28 29 #include "gtest/gtest_prod.h" 30 #include "perfetto/base/build_config.h" 31 #include "perfetto/base/scoped_file.h" 32 #include "perfetto/base/thread_checker.h" 33 #include "perfetto/ftrace_reader/ftrace_controller.h" 34 #include "perfetto/protozero/message.h" 35 #include "perfetto/traced/data_source_types.h" 36 #include "src/ftrace_reader/proto_translation_table.h" 37 38 namespace perfetto { 39 40 class ProtoTranslationTable; 41 42 namespace protos { 43 namespace pbzero { 44 class FtraceEventBundle; 45 } // namespace pbzero 46 } // namespace protos 47 48 // Class for efficient 'is event with id x enabled?' tests. 49 // Mirrors the data in a FtraceConfig but in a format better suited 50 // to be consumed by CpuReader. 51 class EventFilter { 52 public: 53 EventFilter(const ProtoTranslationTable&, std::set<std::string>); 54 ~EventFilter(); 55 IsEventEnabled(size_t ftrace_event_id)56 bool IsEventEnabled(size_t ftrace_event_id) const { 57 if (ftrace_event_id == 0 || ftrace_event_id > enabled_ids_.size()) { 58 return false; 59 } 60 return enabled_ids_[ftrace_event_id]; 61 } 62 enabled_names()63 const std::set<std::string>& enabled_names() const { return enabled_names_; } 64 65 private: 66 EventFilter(const EventFilter&) = delete; 67 EventFilter& operator=(const EventFilter&) = delete; 68 69 const std::vector<bool> enabled_ids_; 70 std::set<std::string> enabled_names_; 71 }; 72 73 // Processes raw ftrace data for a logical CPU core. 74 class CpuReader { 75 public: 76 // |on_data_available| will be called on an arbitrary thread when at least one 77 // page of ftrace data is available for draining on this CPU. 78 CpuReader(const ProtoTranslationTable*, 79 size_t cpu, 80 base::ScopedFile fd, 81 std::function<void()> on_data_available); 82 ~CpuReader(); 83 84 // Drains all available data from the staging pipe into the given sinks. 85 // Should be called in response to the |on_data_available| callback. 86 bool Drain(const std::array<const EventFilter*, kMaxSinks>&, 87 const std::array< 88 protozero::MessageHandle<protos::pbzero::FtraceEventBundle>, 89 kMaxSinks>&, 90 const std::array<FtraceMetadata*, kMaxSinks>& metadatas); 91 92 template <typename T> ReadAndAdvance(const uint8_t ** ptr,const uint8_t * end,T * out)93 static bool ReadAndAdvance(const uint8_t** ptr, const uint8_t* end, T* out) { 94 if (*ptr > end - sizeof(T)) 95 return false; 96 memcpy(reinterpret_cast<void*>(out), reinterpret_cast<const void*>(*ptr), 97 sizeof(T)); 98 *ptr += sizeof(T); 99 return true; 100 } 101 102 // Caller must do the bounds check: 103 // [start + offset, start + offset + sizeof(T)) 104 // Returns the raw value not the varint. 105 template <typename T> ReadIntoVarInt(const uint8_t * start,uint32_t field_id,protozero::Message * out)106 static T ReadIntoVarInt(const uint8_t* start, 107 uint32_t field_id, 108 protozero::Message* out) { 109 T t; 110 memcpy(&t, reinterpret_cast<const void*>(start), sizeof(T)); 111 out->AppendVarInt<T>(field_id, t); 112 return t; 113 } 114 115 template <typename T> ReadInode(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)116 static void ReadInode(const uint8_t* start, 117 uint32_t field_id, 118 protozero::Message* out, 119 FtraceMetadata* metadata) { 120 T t = ReadIntoVarInt<T>(start, field_id, out); 121 metadata->AddInode(static_cast<Inode>(t)); 122 } 123 124 template <typename T> ReadDevId(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)125 static void ReadDevId(const uint8_t* start, 126 uint32_t field_id, 127 protozero::Message* out, 128 FtraceMetadata* metadata) { 129 T t; 130 memcpy(&t, reinterpret_cast<const void*>(start), sizeof(T)); 131 BlockDeviceID dev_id = TranslateBlockDeviceIDToUserspace<T>(t); 132 out->AppendVarInt<BlockDeviceID>(field_id, dev_id); 133 metadata->AddDevice(dev_id); 134 } 135 ReadPid(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)136 static void ReadPid(const uint8_t* start, 137 uint32_t field_id, 138 protozero::Message* out, 139 FtraceMetadata* metadata) { 140 int32_t pid = ReadIntoVarInt<int32_t>(start, field_id, out); 141 metadata->AddPid(pid); 142 } 143 ReadCommonPid(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)144 static void ReadCommonPid(const uint8_t* start, 145 uint32_t field_id, 146 protozero::Message* out, 147 FtraceMetadata* metadata) { 148 int32_t pid = ReadIntoVarInt<int32_t>(start, field_id, out); 149 metadata->AddCommonPid(pid); 150 } 151 152 // Internally the kernel stores device ids in a different layout to that 153 // exposed to userspace via stat etc. There's no userspace function to convert 154 // between the formats so we have to do it ourselves. 155 template <typename T> TranslateBlockDeviceIDToUserspace(T kernel_dev)156 static BlockDeviceID TranslateBlockDeviceIDToUserspace(T kernel_dev) { 157 // Provided search index s_dev from 158 // https://github.com/torvalds/linux/blob/v4.12/include/linux/fs.h#L404 159 // Convert to user space id using 160 // https://github.com/torvalds/linux/blob/v4.12/include/linux/kdev_t.h#L10 161 // TODO(azappone): see if this is the same on all platforms 162 uint64_t maj = static_cast<uint64_t>(kernel_dev) >> 20; 163 uint64_t min = static_cast<uint64_t>(kernel_dev) & ((1U << 20) - 1); 164 return static_cast<BlockDeviceID>( // From makedev() 165 ((maj & 0xfffff000ULL) << 32) | ((maj & 0xfffULL) << 8) | 166 ((min & 0xffffff00ULL) << 12) | ((min & 0xffULL))); 167 } 168 169 // Parse a raw ftrace page beginning at ptr and write the events a protos 170 // into the provided bundle respecting the given event filter. 171 // |table| contains the mix of compile time (e.g. proto field ids) and 172 // run time (e.g. field offset and size) information necessary to do this. 173 // The table is initialized once at start time by the ftrace controller 174 // which passes it to the CpuReader which passes it here. 175 static size_t ParsePage(const uint8_t* ptr, 176 const EventFilter*, 177 protos::pbzero::FtraceEventBundle*, 178 const ProtoTranslationTable* table, 179 FtraceMetadata*); 180 181 // Parse a single raw ftrace event beginning at |start| and ending at |end| 182 // and write it into the provided bundle as a proto. 183 // |table| contains the mix of compile time (e.g. proto field ids) and 184 // run time (e.g. field offset and size) information necessary to do this. 185 // The table is initialized once at start time by the ftrace controller 186 // which passes it to the CpuReader which passes it to ParsePage which 187 // passes it here. 188 static bool ParseEvent(uint16_t ftrace_event_id, 189 const uint8_t* start, 190 const uint8_t* end, 191 const ProtoTranslationTable* table, 192 protozero::Message* message, 193 FtraceMetadata* metadata); 194 195 static bool ParseField(const Field& field, 196 const uint8_t* start, 197 const uint8_t* end, 198 protozero::Message* message, 199 FtraceMetadata* metadata); 200 201 private: 202 static void RunWorkerThread(size_t cpu, 203 int trace_fd, 204 int staging_write_fd, 205 const std::function<void()>& on_data_available, 206 std::atomic<bool>* exiting); 207 208 uint8_t* GetBuffer(); 209 CpuReader(const CpuReader&) = delete; 210 CpuReader& operator=(const CpuReader&) = delete; 211 212 const ProtoTranslationTable* table_; 213 const size_t cpu_; 214 base::ScopedFile trace_fd_; 215 base::ScopedFile staging_read_fd_; 216 base::ScopedFile staging_write_fd_; 217 std::unique_ptr<uint8_t[]> buffer_; 218 std::thread worker_thread_; 219 std::atomic<bool> exiting_{false}; 220 PERFETTO_THREAD_CHECKER(thread_checker_) 221 }; 222 223 } // namespace perfetto 224 225 #endif // SRC_FTRACE_READER_CPU_READER_H_ 226