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 
IsEventEnabled(size_t ftrace_event_id)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 
enabled_names()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>
ReadAndAdvance(const uint8_t ** ptr,const uint8_t * end,T * out)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>
ReadIntoVarInt(const uint8_t * start,uint32_t field_id,protozero::Message * out)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>
ReadInode(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)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>
ReadDevId(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)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 
ReadPid(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)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 
ReadCommonPid(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)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>
TranslateBlockDeviceIDToUserspace(T kernel_dev)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