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/ext/base/optional.h" 30 #include "perfetto/ext/base/paged_memory.h" 31 #include "perfetto/ext/base/pipe.h" 32 #include "perfetto/ext/base/scoped_file.h" 33 #include "perfetto/ext/base/thread_checker.h" 34 #include "perfetto/ext/traced/data_source_types.h" 35 #include "perfetto/ext/tracing/core/trace_writer.h" 36 #include "perfetto/protozero/message.h" 37 #include "perfetto/protozero/message_handle.h" 38 #include "src/traced/probes/ftrace/compact_sched.h" 39 #include "src/traced/probes/ftrace/ftrace_metadata.h" 40 #include "src/traced/probes/ftrace/proto_translation_table.h" 41 42 namespace perfetto { 43 44 class FtraceDataSource; 45 class LazyKernelSymbolizer; 46 class ProtoTranslationTable; 47 struct FtraceClockSnapshot; 48 struct FtraceDataSourceConfig; 49 50 namespace protos { 51 namespace pbzero { 52 class FtraceEventBundle; 53 enum FtraceClock : int32_t; 54 } // namespace pbzero 55 } // namespace protos 56 57 // Reads raw ftrace data for a cpu, parses it, and writes it into the perfetto 58 // tracing buffers. 59 class CpuReader { 60 public: 61 using FtraceEventBundle = protos::pbzero::FtraceEventBundle; 62 63 struct PageHeader { 64 uint64_t timestamp; 65 uint64_t size; 66 bool lost_events; 67 }; 68 69 CpuReader(size_t cpu, 70 const ProtoTranslationTable* table, 71 LazyKernelSymbolizer* symbolizer, 72 const FtraceClockSnapshot*, 73 base::ScopedFile trace_fd); 74 ~CpuReader(); 75 76 // Reads and parses all ftrace data for this cpu (in batches), until we catch 77 // up to the writer, or hit |max_pages|. Returns number of pages read. 78 size_t ReadCycle(uint8_t* parsing_buf, 79 size_t parsing_buf_size_pages, 80 size_t max_pages, 81 const std::set<FtraceDataSource*>& started_data_sources); 82 83 template <typename T> ReadAndAdvance(const uint8_t ** ptr,const uint8_t * end,T * out)84 static bool ReadAndAdvance(const uint8_t** ptr, const uint8_t* end, T* out) { 85 if (*ptr > end - sizeof(T)) 86 return false; 87 memcpy(reinterpret_cast<void*>(out), reinterpret_cast<const void*>(*ptr), 88 sizeof(T)); 89 *ptr += sizeof(T); 90 return true; 91 } 92 93 // Caller must do the bounds check: 94 // [start + offset, start + offset + sizeof(T)) 95 // Returns the raw value not the varint. 96 template <typename T> ReadIntoVarInt(const uint8_t * start,uint32_t field_id,protozero::Message * out)97 static T ReadIntoVarInt(const uint8_t* start, 98 uint32_t field_id, 99 protozero::Message* out) { 100 T t; 101 memcpy(&t, reinterpret_cast<const void*>(start), sizeof(T)); 102 out->AppendVarInt<T>(field_id, t); 103 return t; 104 } 105 106 template <typename T> ReadInode(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)107 static void ReadInode(const uint8_t* start, 108 uint32_t field_id, 109 protozero::Message* out, 110 FtraceMetadata* metadata) { 111 T t = ReadIntoVarInt<T>(start, field_id, out); 112 metadata->AddInode(static_cast<Inode>(t)); 113 } 114 115 template <typename T> ReadDevId(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)116 static void ReadDevId(const uint8_t* start, 117 uint32_t field_id, 118 protozero::Message* out, 119 FtraceMetadata* metadata) { 120 T t; 121 memcpy(&t, reinterpret_cast<const void*>(start), sizeof(T)); 122 BlockDeviceID dev_id = TranslateBlockDeviceIDToUserspace<T>(t); 123 out->AppendVarInt<BlockDeviceID>(field_id, dev_id); 124 metadata->AddDevice(dev_id); 125 } 126 127 template <typename T> ReadSymbolAddr(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)128 static void ReadSymbolAddr(const uint8_t* start, 129 uint32_t field_id, 130 protozero::Message* out, 131 FtraceMetadata* metadata) { 132 // ReadSymbolAddr is a bit special. In order to not disclose KASLR layout 133 // via traces, we put in the trace only a mangled address (which really is 134 // the insertion order into metadata.kernel_addrs). We don't care about the 135 // actual symbol addesses. We just need to match that against the symbol 136 // name in the names in the FtraceEventBundle.KernelSymbols. 137 T full_addr; 138 memcpy(&full_addr, reinterpret_cast<const void*>(start), sizeof(T)); 139 uint32_t interned_index = metadata->AddSymbolAddr(full_addr); 140 out->AppendVarInt(field_id, interned_index); 141 } 142 ReadPid(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)143 static void ReadPid(const uint8_t* start, 144 uint32_t field_id, 145 protozero::Message* out, 146 FtraceMetadata* metadata) { 147 int32_t pid = ReadIntoVarInt<int32_t>(start, field_id, out); 148 metadata->AddPid(pid); 149 } 150 ReadCommonPid(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)151 static void ReadCommonPid(const uint8_t* start, 152 uint32_t field_id, 153 protozero::Message* out, 154 FtraceMetadata* metadata) { 155 int32_t pid = ReadIntoVarInt<int32_t>(start, field_id, out); 156 metadata->AddCommonPid(pid); 157 } 158 159 // Internally the kernel stores device ids in a different layout to that 160 // exposed to userspace via stat etc. There's no userspace function to convert 161 // between the formats so we have to do it ourselves. 162 template <typename T> TranslateBlockDeviceIDToUserspace(T kernel_dev)163 static BlockDeviceID TranslateBlockDeviceIDToUserspace(T kernel_dev) { 164 // Provided search index s_dev from 165 // https://github.com/torvalds/linux/blob/v4.12/include/linux/fs.h#L404 166 // Convert to user space id using 167 // https://github.com/torvalds/linux/blob/v4.12/include/linux/kdev_t.h#L10 168 // TODO(azappone): see if this is the same on all platforms 169 uint64_t maj = static_cast<uint64_t>(kernel_dev) >> 20; 170 uint64_t min = static_cast<uint64_t>(kernel_dev) & ((1U << 20) - 1); 171 return static_cast<BlockDeviceID>( // From makedev() 172 ((maj & 0xfffff000ULL) << 32) | ((maj & 0xfffULL) << 8) | 173 ((min & 0xffffff00ULL) << 12) | ((min & 0xffULL))); 174 } 175 176 // Returns a parsed representation of the given raw ftrace page's header. 177 static base::Optional<CpuReader::PageHeader> ParsePageHeader( 178 const uint8_t** ptr, 179 uint16_t page_header_size_len); 180 181 // Parse the payload of a raw ftrace page, and write the events as protos 182 // into the provided bundle (and/or compact buffer). 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 here. 187 // The caller is responsible for validating that the page_header->size stays 188 // within the current page. 189 static size_t ParsePagePayload(const uint8_t* start_of_payload, 190 const PageHeader* page_header, 191 const ProtoTranslationTable* table, 192 const FtraceDataSourceConfig* ds_config, 193 CompactSchedBuffer* compact_sched_buffer, 194 FtraceEventBundle* bundle, 195 FtraceMetadata* metadata); 196 197 // Parse a single raw ftrace event beginning at |start| and ending at |end| 198 // and write it into the provided bundle as a proto. 199 // |table| contains the mix of compile time (e.g. proto field ids) and 200 // run time (e.g. field offset and size) information necessary to do this. 201 // The table is initialized once at start time by the ftrace controller 202 // which passes it to the CpuReader which passes it to ParsePage which 203 // passes it here. 204 static bool ParseEvent(uint16_t ftrace_event_id, 205 const uint8_t* start, 206 const uint8_t* end, 207 const ProtoTranslationTable* table, 208 protozero::Message* message, 209 FtraceMetadata* metadata); 210 211 static bool ParseField(const Field& field, 212 const uint8_t* start, 213 const uint8_t* end, 214 const ProtoTranslationTable* table, 215 protozero::Message* message, 216 FtraceMetadata* metadata); 217 218 // Parse a sched_switch event according to pre-validated format, and buffer 219 // the individual fields in the given compact encoding batch. 220 static void ParseSchedSwitchCompact(const uint8_t* start, 221 uint64_t timestamp, 222 const CompactSchedSwitchFormat* format, 223 CompactSchedBuffer* compact_buf, 224 FtraceMetadata* metadata); 225 226 // Parse a sched_waking event according to pre-validated format, and buffer 227 // the individual fields in the given compact encoding batch. 228 static void ParseSchedWakingCompact(const uint8_t* start, 229 uint64_t timestamp, 230 const CompactSchedWakingFormat* format, 231 CompactSchedBuffer* compact_buf, 232 FtraceMetadata* metadata); 233 234 // Parses & encodes the given range of contiguous tracing pages. Called by 235 // |ReadAndProcessBatch| for each active data source. 236 // 237 // Returns the number of correctly processed pages. If the return value is 238 // equal to |pages_read|, there was no error. Otherwise, the return value 239 // points to the first page that contains an error. 240 // 241 // public and static for testing 242 static size_t ProcessPagesForDataSource( 243 TraceWriter* trace_writer, 244 FtraceMetadata* metadata, 245 size_t cpu, 246 const FtraceDataSourceConfig* ds_config, 247 const uint8_t* parsing_buf, 248 const size_t pages_read, 249 const ProtoTranslationTable* table, 250 LazyKernelSymbolizer* symbolizer, 251 const FtraceClockSnapshot*, 252 protos::pbzero::FtraceClock); 253 set_ftrace_clock(protos::pbzero::FtraceClock clock)254 void set_ftrace_clock(protos::pbzero::FtraceClock clock) { 255 ftrace_clock_ = clock; 256 } 257 258 private: 259 CpuReader(const CpuReader&) = delete; 260 CpuReader& operator=(const CpuReader&) = delete; 261 262 // Reads at most |max_pages| of ftrace data, parses it, and writes it 263 // into |started_data_sources|. Returns number of pages read. 264 // See comment on ftrace_controller.cc:kMaxParsingWorkingSetPages for 265 // rationale behind the batching. 266 size_t ReadAndProcessBatch( 267 uint8_t* parsing_buf, 268 size_t max_pages, 269 bool first_batch_in_cycle, 270 const std::set<FtraceDataSource*>& started_data_sources); 271 272 const size_t cpu_; 273 const ProtoTranslationTable* const table_; 274 LazyKernelSymbolizer* const symbolizer_; 275 const FtraceClockSnapshot* const ftrace_clock_snapshot_; 276 base::ScopedFile trace_fd_; 277 protos::pbzero::FtraceClock ftrace_clock_{}; 278 }; 279 280 } // namespace perfetto 281 282 #endif // SRC_TRACED_PROBES_FTRACE_CPU_READER_H_ 283