/* * Copyright (C) 2017 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. */ #ifndef SRC_TRACED_PROBES_FTRACE_CPU_READER_H_ #define SRC_TRACED_PROBES_FTRACE_CPU_READER_H_ #include #include #include #include #include #include #include #include "perfetto/ext/base/optional.h" #include "perfetto/ext/base/paged_memory.h" #include "perfetto/ext/base/pipe.h" #include "perfetto/ext/base/scoped_file.h" #include "perfetto/ext/base/thread_checker.h" #include "perfetto/ext/traced/data_source_types.h" #include "perfetto/ext/tracing/core/trace_writer.h" #include "perfetto/protozero/message.h" #include "perfetto/protozero/message_handle.h" #include "src/traced/probes/ftrace/compact_sched.h" #include "src/traced/probes/ftrace/ftrace_metadata.h" #include "src/traced/probes/ftrace/proto_translation_table.h" namespace perfetto { class FtraceDataSource; class LazyKernelSymbolizer; class ProtoTranslationTable; struct FtraceDataSourceConfig; namespace protos { namespace pbzero { class FtraceEventBundle; } // namespace pbzero } // namespace protos // Reads raw ftrace data for a cpu, parses it, and writes it into the perfetto // tracing buffers. class CpuReader { public: using FtraceEventBundle = protos::pbzero::FtraceEventBundle; struct PageHeader { uint64_t timestamp; uint64_t size; bool lost_events; }; CpuReader(size_t cpu, const ProtoTranslationTable* table, LazyKernelSymbolizer* symbolizer, base::ScopedFile trace_fd); ~CpuReader(); // Reads and parses all ftrace data for this cpu (in batches), until we catch // up to the writer, or hit |max_pages|. Returns number of pages read. size_t ReadCycle(uint8_t* parsing_buf, size_t parsing_buf_size_pages, size_t max_pages, const std::set& started_data_sources); template static bool ReadAndAdvance(const uint8_t** ptr, const uint8_t* end, T* out) { if (*ptr > end - sizeof(T)) return false; memcpy(reinterpret_cast(out), reinterpret_cast(*ptr), sizeof(T)); *ptr += sizeof(T); return true; } // Caller must do the bounds check: // [start + offset, start + offset + sizeof(T)) // Returns the raw value not the varint. template static T ReadIntoVarInt(const uint8_t* start, uint32_t field_id, protozero::Message* out) { T t; memcpy(&t, reinterpret_cast(start), sizeof(T)); out->AppendVarInt(field_id, t); return t; } template static void ReadInode(const uint8_t* start, uint32_t field_id, protozero::Message* out, FtraceMetadata* metadata) { T t = ReadIntoVarInt(start, field_id, out); metadata->AddInode(static_cast(t)); } template static void ReadDevId(const uint8_t* start, uint32_t field_id, protozero::Message* out, FtraceMetadata* metadata) { T t; memcpy(&t, reinterpret_cast(start), sizeof(T)); BlockDeviceID dev_id = TranslateBlockDeviceIDToUserspace(t); out->AppendVarInt(field_id, dev_id); metadata->AddDevice(dev_id); } template static void ReadSymbolAddr(const uint8_t* start, uint32_t field_id, protozero::Message* out, FtraceMetadata* metadata) { // ReadSymbolAddr is a bit special. In order to not disclose KASLR layout // via traces, we put in the trace only a mangled address (which really is // the insertion order into metadata.kernel_addrs). We don't care about the // actual symbol addesses. We just need to match that against the symbol // name in the names in the FtraceEventBundle.KernelSymbols. T full_addr; memcpy(&full_addr, reinterpret_cast(start), sizeof(T)); uint32_t interned_index = metadata->AddSymbolAddr(full_addr); out->AppendVarInt(field_id, interned_index); } static void ReadPid(const uint8_t* start, uint32_t field_id, protozero::Message* out, FtraceMetadata* metadata) { int32_t pid = ReadIntoVarInt(start, field_id, out); metadata->AddPid(pid); } static void ReadCommonPid(const uint8_t* start, uint32_t field_id, protozero::Message* out, FtraceMetadata* metadata) { int32_t pid = ReadIntoVarInt(start, field_id, out); metadata->AddCommonPid(pid); } // Internally the kernel stores device ids in a different layout to that // exposed to userspace via stat etc. There's no userspace function to convert // between the formats so we have to do it ourselves. template static BlockDeviceID TranslateBlockDeviceIDToUserspace(T kernel_dev) { // Provided search index s_dev from // https://github.com/torvalds/linux/blob/v4.12/include/linux/fs.h#L404 // Convert to user space id using // https://github.com/torvalds/linux/blob/v4.12/include/linux/kdev_t.h#L10 // TODO(azappone): see if this is the same on all platforms uint64_t maj = static_cast(kernel_dev) >> 20; uint64_t min = static_cast(kernel_dev) & ((1U << 20) - 1); return static_cast( // From makedev() ((maj & 0xfffff000ULL) << 32) | ((maj & 0xfffULL) << 8) | ((min & 0xffffff00ULL) << 12) | ((min & 0xffULL))); } // Returns a parsed representation of the given raw ftrace page's header. static base::Optional ParsePageHeader( const uint8_t** ptr, uint16_t page_header_size_len); // Parse the payload of a raw ftrace page, and write the events as protos // into the provided bundle (and/or compact buffer). // |table| contains the mix of compile time (e.g. proto field ids) and // run time (e.g. field offset and size) information necessary to do this. // The table is initialized once at start time by the ftrace controller // which passes it to the CpuReader which passes it here. // The caller is responsible for validating that the page_header->size stays // within the current page. static size_t ParsePagePayload(const uint8_t* start_of_payload, const PageHeader* page_header, const ProtoTranslationTable* table, const FtraceDataSourceConfig* ds_config, CompactSchedBuffer* compact_sched_buffer, FtraceEventBundle* bundle, FtraceMetadata* metadata); // Parse a single raw ftrace event beginning at |start| and ending at |end| // and write it into the provided bundle as a proto. // |table| contains the mix of compile time (e.g. proto field ids) and // run time (e.g. field offset and size) information necessary to do this. // The table is initialized once at start time by the ftrace controller // which passes it to the CpuReader which passes it to ParsePage which // passes it here. static bool ParseEvent(uint16_t ftrace_event_id, const uint8_t* start, const uint8_t* end, const ProtoTranslationTable* table, protozero::Message* message, FtraceMetadata* metadata); static bool ParseField(const Field& field, const uint8_t* start, const uint8_t* end, const ProtoTranslationTable* table, protozero::Message* message, FtraceMetadata* metadata); // Parse a sched_switch event according to pre-validated format, and buffer // the individual fields in the given compact encoding batch. static void ParseSchedSwitchCompact(const uint8_t* start, uint64_t timestamp, const CompactSchedSwitchFormat* format, CompactSchedBuffer* compact_buf, FtraceMetadata* metadata); // Parse a sched_waking event according to pre-validated format, and buffer // the individual fields in the given compact encoding batch. static void ParseSchedWakingCompact(const uint8_t* start, uint64_t timestamp, const CompactSchedWakingFormat* format, CompactSchedBuffer* compact_buf, FtraceMetadata* metadata); // Parses & encodes the given range of contiguous tracing pages. Called by // |ReadAndProcessBatch| for each active data source. // // public and static for testing static bool ProcessPagesForDataSource(TraceWriter* trace_writer, FtraceMetadata* metadata, size_t cpu, const FtraceDataSourceConfig* ds_config, const uint8_t* parsing_buf, const size_t pages_read, const ProtoTranslationTable* table, LazyKernelSymbolizer* symbolizer); private: CpuReader(const CpuReader&) = delete; CpuReader& operator=(const CpuReader&) = delete; // Reads at most |max_pages| of ftrace data, parses it, and writes it // into |started_data_sources|. Returns number of pages read. // See comment on ftrace_controller.cc:kMaxParsingWorkingSetPages for // rationale behind the batching. size_t ReadAndProcessBatch( uint8_t* parsing_buf, size_t max_pages, bool first_batch_in_cycle, const std::set& started_data_sources); const size_t cpu_; const ProtoTranslationTable* const table_; LazyKernelSymbolizer* const symbolizer_; base::ScopedFile trace_fd_; }; } // namespace perfetto #endif // SRC_TRACED_PROBES_FTRACE_CPU_READER_H_