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