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