• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/base/gtest_prod_util.h"
30 #include "perfetto/base/paged_memory.h"
31 #include "perfetto/base/pipe.h"
32 #include "perfetto/base/scoped_file.h"
33 #include "perfetto/base/thread_checker.h"
34 #include "perfetto/protozero/message.h"
35 #include "perfetto/protozero/message_handle.h"
36 #include "perfetto/traced/data_source_types.h"
37 #include "src/traced/probes/ftrace/ftrace_config.h"
38 #include "src/traced/probes/ftrace/ftrace_metadata.h"
39 #include "src/traced/probes/ftrace/page_pool.h"
40 #include "src/traced/probes/ftrace/proto_translation_table.h"
41 
42 namespace perfetto {
43 
44 class FtraceDataSource;
45 struct FtraceThreadSync;
46 class ProtoTranslationTable;
47 
48 namespace protos {
49 namespace pbzero {
50 class FtraceEventBundle;
51 }  // namespace pbzero
52 }  // namespace protos
53 
54 
55 // Reads raw ftrace data for a cpu and writes that into the perfetto userspace
56 // buffer.
57 class CpuReader {
58  public:
59   using FtraceEventBundle = protos::pbzero::FtraceEventBundle;
60 
61   CpuReader(const ProtoTranslationTable*,
62             FtraceThreadSync*,
63             size_t cpu,
64             int generation,
65             base::ScopedFile fd);
66   ~CpuReader();
67 
68   // Drains all available data into the buffer of the passed data sources.
69   void Drain(const std::set<FtraceDataSource*>&);
70 
71   void InterruptWorkerThreadWithSignal();
72 
73   template <typename T>
ReadAndAdvance(const uint8_t ** ptr,const uint8_t * end,T * out)74   static bool ReadAndAdvance(const uint8_t** ptr, const uint8_t* end, T* out) {
75     if (*ptr > end - sizeof(T))
76       return false;
77     memcpy(reinterpret_cast<void*>(out), reinterpret_cast<const void*>(*ptr),
78            sizeof(T));
79     *ptr += sizeof(T);
80     return true;
81   }
82 
83   // Caller must do the bounds check:
84   // [start + offset, start + offset + sizeof(T))
85   // Returns the raw value not the varint.
86   template <typename T>
ReadIntoVarInt(const uint8_t * start,uint32_t field_id,protozero::Message * out)87   static T ReadIntoVarInt(const uint8_t* start,
88                           uint32_t field_id,
89                           protozero::Message* out) {
90     T t;
91     memcpy(&t, reinterpret_cast<const void*>(start), sizeof(T));
92     out->AppendVarInt<T>(field_id, t);
93     return t;
94   }
95 
96   template <typename T>
ReadInode(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)97   static void ReadInode(const uint8_t* start,
98                         uint32_t field_id,
99                         protozero::Message* out,
100                         FtraceMetadata* metadata) {
101     T t = ReadIntoVarInt<T>(start, field_id, out);
102     metadata->AddInode(static_cast<Inode>(t));
103   }
104 
105   template <typename T>
ReadDevId(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)106   static void ReadDevId(const uint8_t* start,
107                         uint32_t field_id,
108                         protozero::Message* out,
109                         FtraceMetadata* metadata) {
110     T t;
111     memcpy(&t, reinterpret_cast<const void*>(start), sizeof(T));
112     BlockDeviceID dev_id = TranslateBlockDeviceIDToUserspace<T>(t);
113     out->AppendVarInt<BlockDeviceID>(field_id, dev_id);
114     metadata->AddDevice(dev_id);
115   }
116 
ReadPid(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)117   static void ReadPid(const uint8_t* start,
118                       uint32_t field_id,
119                       protozero::Message* out,
120                       FtraceMetadata* metadata) {
121     int32_t pid = ReadIntoVarInt<int32_t>(start, field_id, out);
122     metadata->AddPid(pid);
123   }
124 
ReadCommonPid(const uint8_t * start,uint32_t field_id,protozero::Message * out,FtraceMetadata * metadata)125   static void ReadCommonPid(const uint8_t* start,
126                             uint32_t field_id,
127                             protozero::Message* out,
128                             FtraceMetadata* metadata) {
129     int32_t pid = ReadIntoVarInt<int32_t>(start, field_id, out);
130     metadata->AddCommonPid(pid);
131   }
132 
133   // Internally the kernel stores device ids in a different layout to that
134   // exposed to userspace via stat etc. There's no userspace function to convert
135   // between the formats so we have to do it ourselves.
136   template <typename T>
TranslateBlockDeviceIDToUserspace(T kernel_dev)137   static BlockDeviceID TranslateBlockDeviceIDToUserspace(T kernel_dev) {
138     // Provided search index s_dev from
139     // https://github.com/torvalds/linux/blob/v4.12/include/linux/fs.h#L404
140     // Convert to user space id using
141     // https://github.com/torvalds/linux/blob/v4.12/include/linux/kdev_t.h#L10
142     // TODO(azappone): see if this is the same on all platforms
143     uint64_t maj = static_cast<uint64_t>(kernel_dev) >> 20;
144     uint64_t min = static_cast<uint64_t>(kernel_dev) & ((1U << 20) - 1);
145     return static_cast<BlockDeviceID>(  // From makedev()
146         ((maj & 0xfffff000ULL) << 32) | ((maj & 0xfffULL) << 8) |
147         ((min & 0xffffff00ULL) << 12) | ((min & 0xffULL)));
148   }
149 
150   // Parse a raw ftrace page beginning at ptr and write the events a protos
151   // into the provided bundle respecting the given event filter.
152   // |table| contains the mix of compile time (e.g. proto field ids) and
153   // run time (e.g. field offset and size) information necessary to do this.
154   // The table is initialized once at start time by the ftrace controller
155   // which passes it to the CpuReader which passes it here.
156   static size_t ParsePage(const uint8_t* ptr,
157                           const EventFilter*,
158                           protos::pbzero::FtraceEventBundle*,
159                           const ProtoTranslationTable* table,
160                           FtraceMetadata*);
161 
162   // Parse a single raw ftrace event beginning at |start| and ending at |end|
163   // and write it into the provided bundle as a proto.
164   // |table| contains the mix of compile time (e.g. proto field ids) and
165   // run time (e.g. field offset and size) information necessary to do this.
166   // The table is initialized once at start time by the ftrace controller
167   // which passes it to the CpuReader which passes it to ParsePage which
168   // passes it here.
169   static bool ParseEvent(uint16_t ftrace_event_id,
170                          const uint8_t* start,
171                          const uint8_t* end,
172                          const ProtoTranslationTable* table,
173                          protozero::Message* message,
174                          FtraceMetadata* metadata);
175 
176   static bool ParseField(const Field& field,
177                          const uint8_t* start,
178                          const uint8_t* end,
179                          protozero::Message* message,
180                          FtraceMetadata* metadata);
181 
182  private:
183   static void RunWorkerThread(size_t cpu,
184                               int generation,
185                               int trace_fd,
186                               PagePool*,
187                               FtraceThreadSync*,
188                               uint16_t header_size_len);
189 
190   CpuReader(const CpuReader&) = delete;
191   CpuReader& operator=(const CpuReader&) = delete;
192 
193   const ProtoTranslationTable* const table_;
194   FtraceThreadSync* const thread_sync_;
195   const size_t cpu_;
196   PagePool pool_;
197   base::ScopedFile trace_fd_;
198   std::thread worker_thread_;
199   PERFETTO_THREAD_CHECKER(thread_checker_)
200 };
201 
202 
203 }  // namespace perfetto
204 
205 #endif  // SRC_TRACED_PROBES_FTRACE_CPU_READER_H_
206