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_TRACED_PROBES_FTRACE_CPU_READER_H_ 18 #define SRC_TRACED_PROBES_FTRACE_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 "perfetto/base/gtest_prod_util.h" 30 #include "perfetto/base/paged_memory.h" 31 #include "perfetto/base/pipe.h" 32 #include "perfetto/base/scoped_file.h" 33 #include "perfetto/base/thread_checker.h" 34 #include "perfetto/protozero/message.h" 35 #include "perfetto/protozero/message_handle.h" 36 #include "perfetto/traced/data_source_types.h" 37 #include "src/traced/probes/ftrace/ftrace_config.h" 38 #include "src/traced/probes/ftrace/ftrace_metadata.h" 39 #include "src/traced/probes/ftrace/page_pool.h" 40 #include "src/traced/probes/ftrace/proto_translation_table.h" 41 42 namespace perfetto { 43 44 class FtraceDataSource; 45 struct FtraceThreadSync; 46 class ProtoTranslationTable; 47 48 namespace protos { 49 namespace pbzero { 50 class FtraceEventBundle; 51 } // namespace pbzero 52 } // namespace protos 53 54 55 // Reads raw ftrace data for a cpu and writes that into the perfetto userspace 56 // buffer. 57 class CpuReader { 58 public: 59 using FtraceEventBundle = protos::pbzero::FtraceEventBundle; 60 61 CpuReader(const ProtoTranslationTable*, 62 FtraceThreadSync*, 63 size_t cpu, 64 int generation, 65 base::ScopedFile fd); 66 ~CpuReader(); 67 68 // Drains all available data into the buffer of the passed data sources. 69 void Drain(const std::set<FtraceDataSource*>&); 70 71 void InterruptWorkerThreadWithSignal(); 72 73 template <typename T> ReadAndAdvance(const uint8_t ** ptr,const uint8_t * end,T * out)74 static bool ReadAndAdvance(const uint8_t** ptr, const uint8_t* end, T* out) { 75 if (*ptr > end - sizeof(T)) 76 return false; 77 memcpy(reinterpret_cast<void*>(out), reinterpret_cast<const void*>(*ptr), 78 sizeof(T)); 79 *ptr += sizeof(T); 80 return true; 81 } 82 83 // Caller must do the bounds check: 84 // [start + offset, start + offset + sizeof(T)) 85 // Returns the raw value not the varint. 86 template <typename T> ReadIntoVarInt(const uint8_t * start,uint32_t field_id,protozero::Message * out)87 static T ReadIntoVarInt(const uint8_t* start, 88 uint32_t field_id, 89 protozero::Message* out) { 90 T t; 91 memcpy(&t, reinterpret_cast<const void*>(start), sizeof(T)); 92 out->AppendVarInt<T>(field_id, t); 93 return t; 94 } 95 96 template <typename T> ReadInode(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)97 static void ReadInode(const uint8_t* start, 98 uint32_t field_id, 99 protozero::Message* out, 100 FtraceMetadata* metadata) { 101 T t = ReadIntoVarInt<T>(start, field_id, out); 102 metadata->AddInode(static_cast<Inode>(t)); 103 } 104 105 template <typename T> ReadDevId(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)106 static void ReadDevId(const uint8_t* start, 107 uint32_t field_id, 108 protozero::Message* out, 109 FtraceMetadata* metadata) { 110 T t; 111 memcpy(&t, reinterpret_cast<const void*>(start), sizeof(T)); 112 BlockDeviceID dev_id = TranslateBlockDeviceIDToUserspace<T>(t); 113 out->AppendVarInt<BlockDeviceID>(field_id, dev_id); 114 metadata->AddDevice(dev_id); 115 } 116 ReadPid(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)117 static void ReadPid(const uint8_t* start, 118 uint32_t field_id, 119 protozero::Message* out, 120 FtraceMetadata* metadata) { 121 int32_t pid = ReadIntoVarInt<int32_t>(start, field_id, out); 122 metadata->AddPid(pid); 123 } 124 ReadCommonPid(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)125 static void ReadCommonPid(const uint8_t* start, 126 uint32_t field_id, 127 protozero::Message* out, 128 FtraceMetadata* metadata) { 129 int32_t pid = ReadIntoVarInt<int32_t>(start, field_id, out); 130 metadata->AddCommonPid(pid); 131 } 132 133 // Internally the kernel stores device ids in a different layout to that 134 // exposed to userspace via stat etc. There's no userspace function to convert 135 // between the formats so we have to do it ourselves. 136 template <typename T> TranslateBlockDeviceIDToUserspace(T kernel_dev)137 static BlockDeviceID TranslateBlockDeviceIDToUserspace(T kernel_dev) { 138 // Provided search index s_dev from 139 // https://github.com/torvalds/linux/blob/v4.12/include/linux/fs.h#L404 140 // Convert to user space id using 141 // https://github.com/torvalds/linux/blob/v4.12/include/linux/kdev_t.h#L10 142 // TODO(azappone): see if this is the same on all platforms 143 uint64_t maj = static_cast<uint64_t>(kernel_dev) >> 20; 144 uint64_t min = static_cast<uint64_t>(kernel_dev) & ((1U << 20) - 1); 145 return static_cast<BlockDeviceID>( // From makedev() 146 ((maj & 0xfffff000ULL) << 32) | ((maj & 0xfffULL) << 8) | 147 ((min & 0xffffff00ULL) << 12) | ((min & 0xffULL))); 148 } 149 150 // Parse a raw ftrace page beginning at ptr and write the events a protos 151 // into the provided bundle respecting the given event filter. 152 // |table| contains the mix of compile time (e.g. proto field ids) and 153 // run time (e.g. field offset and size) information necessary to do this. 154 // The table is initialized once at start time by the ftrace controller 155 // which passes it to the CpuReader which passes it here. 156 static size_t ParsePage(const uint8_t* ptr, 157 const EventFilter*, 158 protos::pbzero::FtraceEventBundle*, 159 const ProtoTranslationTable* table, 160 FtraceMetadata*); 161 162 // Parse a single raw ftrace event beginning at |start| and ending at |end| 163 // and write it into the provided bundle as a proto. 164 // |table| contains the mix of compile time (e.g. proto field ids) and 165 // run time (e.g. field offset and size) information necessary to do this. 166 // The table is initialized once at start time by the ftrace controller 167 // which passes it to the CpuReader which passes it to ParsePage which 168 // passes it here. 169 static bool ParseEvent(uint16_t ftrace_event_id, 170 const uint8_t* start, 171 const uint8_t* end, 172 const ProtoTranslationTable* table, 173 protozero::Message* message, 174 FtraceMetadata* metadata); 175 176 static bool ParseField(const Field& field, 177 const uint8_t* start, 178 const uint8_t* end, 179 protozero::Message* message, 180 FtraceMetadata* metadata); 181 182 private: 183 static void RunWorkerThread(size_t cpu, 184 int generation, 185 int trace_fd, 186 PagePool*, 187 FtraceThreadSync*, 188 uint16_t header_size_len); 189 190 CpuReader(const CpuReader&) = delete; 191 CpuReader& operator=(const CpuReader&) = delete; 192 193 const ProtoTranslationTable* const table_; 194 FtraceThreadSync* const thread_sync_; 195 const size_t cpu_; 196 PagePool pool_; 197 base::ScopedFile trace_fd_; 198 std::thread worker_thread_; 199 PERFETTO_THREAD_CHECKER(thread_checker_) 200 }; 201 202 203 } // namespace perfetto 204 205 #endif // SRC_TRACED_PROBES_FTRACE_CPU_READER_H_ 206