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