Home | History | Annotate | Download | only in ftrace_reader
      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 
     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 
     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>
     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>
    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>
    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>
    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 
    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 
    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>
    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