blob: 61f0fb5a6886756b0c78c964d042ba01bd1a309d [file] [log] [blame]
Primiano Tuccide82dae2018-06-04 16:17:49 +02001/*
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 "gtest/gtest_prod.h"
Primiano Tuccide82dae2018-06-04 16:17:49 +020030#include "perfetto/base/page_allocator.h"
31#include "perfetto/base/scoped_file.h"
32#include "perfetto/base/thread_checker.h"
33#include "perfetto/protozero/message.h"
Primiano Tuccifd8240d2018-08-01 09:34:54 +010034#include "perfetto/protozero/message_handle.h"
Primiano Tuccide82dae2018-06-04 16:17:49 +020035#include "perfetto/traced/data_source_types.h"
Primiano Tuccifd8240d2018-08-01 09:34:54 +010036#include "src/traced/probes/ftrace/ftrace_config.h"
37#include "src/traced/probes/ftrace/ftrace_metadata.h"
Primiano Tuccide82dae2018-06-04 16:17:49 +020038#include "src/traced/probes/ftrace/proto_translation_table.h"
39
40namespace perfetto {
41
Primiano Tuccifd8240d2018-08-01 09:34:54 +010042class FtraceDataSource;
Primiano Tuccide82dae2018-06-04 16:17:49 +020043class ProtoTranslationTable;
44
45namespace protos {
46namespace pbzero {
47class FtraceEventBundle;
48} // namespace pbzero
49} // namespace protos
50
Primiano Tuccifd8240d2018-08-01 09:34:54 +010051class EventFilter; // Declared down below.
Primiano Tuccide82dae2018-06-04 16:17:49 +020052
Primiano Tuccifd8240d2018-08-01 09:34:54 +010053// Reads raw ftrace data for a cpu and writes that into the perfetto userspace
54// buffer.
Primiano Tuccide82dae2018-06-04 16:17:49 +020055class CpuReader {
56 public:
Primiano Tuccifd8240d2018-08-01 09:34:54 +010057 using FtraceEventBundle = protos::pbzero::FtraceEventBundle;
58
Primiano Tuccide82dae2018-06-04 16:17:49 +020059 // |on_data_available| will be called on an arbitrary thread when at least one
60 // page of ftrace data is available for draining on this CPU.
61 CpuReader(const ProtoTranslationTable*,
62 size_t cpu,
63 base::ScopedFile fd,
64 std::function<void()> on_data_available);
65 ~CpuReader();
66
Primiano Tuccifd8240d2018-08-01 09:34:54 +010067 // Drains all available data from the staging pipe into the buffer of the
68 // passed data sources.
Primiano Tuccide82dae2018-06-04 16:17:49 +020069 // Should be called in response to the |on_data_available| callback.
Primiano Tuccifd8240d2018-08-01 09:34:54 +010070 bool Drain(const std::set<FtraceDataSource*>&);
Primiano Tuccide82dae2018-06-04 16:17:49 +020071
72 template <typename T>
73 static bool ReadAndAdvance(const uint8_t** ptr, const uint8_t* end, T* out) {
74 if (*ptr > end - sizeof(T))
75 return false;
76 memcpy(reinterpret_cast<void*>(out), reinterpret_cast<const void*>(*ptr),
77 sizeof(T));
78 *ptr += sizeof(T);
79 return true;
80 }
81
82 // Caller must do the bounds check:
83 // [start + offset, start + offset + sizeof(T))
84 // Returns the raw value not the varint.
85 template <typename T>
86 static T ReadIntoVarInt(const uint8_t* start,
87 uint32_t field_id,
88 protozero::Message* out) {
89 T t;
90 memcpy(&t, reinterpret_cast<const void*>(start), sizeof(T));
91 out->AppendVarInt<T>(field_id, t);
92 return t;
93 }
94
95 template <typename T>
96 static void ReadInode(const uint8_t* start,
97 uint32_t field_id,
98 protozero::Message* out,
99 FtraceMetadata* metadata) {
100 T t = ReadIntoVarInt<T>(start, field_id, out);
101 metadata->AddInode(static_cast<Inode>(t));
102 }
103
104 template <typename T>
105 static void ReadDevId(const uint8_t* start,
106 uint32_t field_id,
107 protozero::Message* out,
108 FtraceMetadata* metadata) {
109 T t;
110 memcpy(&t, reinterpret_cast<const void*>(start), sizeof(T));
111 BlockDeviceID dev_id = TranslateBlockDeviceIDToUserspace<T>(t);
112 out->AppendVarInt<BlockDeviceID>(field_id, dev_id);
113 metadata->AddDevice(dev_id);
114 }
115
116 static void ReadPid(const uint8_t* start,
117 uint32_t field_id,
118 protozero::Message* out,
119 FtraceMetadata* metadata) {
120 int32_t pid = ReadIntoVarInt<int32_t>(start, field_id, out);
121 metadata->AddPid(pid);
122 }
123
124 static void ReadCommonPid(const uint8_t* start,
125 uint32_t field_id,
126 protozero::Message* out,
127 FtraceMetadata* metadata) {
128 int32_t pid = ReadIntoVarInt<int32_t>(start, field_id, out);
129 metadata->AddCommonPid(pid);
130 }
131
132 // Internally the kernel stores device ids in a different layout to that
133 // exposed to userspace via stat etc. There's no userspace function to convert
134 // between the formats so we have to do it ourselves.
135 template <typename T>
136 static BlockDeviceID TranslateBlockDeviceIDToUserspace(T kernel_dev) {
137 // Provided search index s_dev from
138 // https://github.com/torvalds/linux/blob/v4.12/include/linux/fs.h#L404
139 // Convert to user space id using
140 // https://github.com/torvalds/linux/blob/v4.12/include/linux/kdev_t.h#L10
141 // TODO(azappone): see if this is the same on all platforms
142 uint64_t maj = static_cast<uint64_t>(kernel_dev) >> 20;
143 uint64_t min = static_cast<uint64_t>(kernel_dev) & ((1U << 20) - 1);
144 return static_cast<BlockDeviceID>( // From makedev()
145 ((maj & 0xfffff000ULL) << 32) | ((maj & 0xfffULL) << 8) |
146 ((min & 0xffffff00ULL) << 12) | ((min & 0xffULL)));
147 }
148
149 // Parse a raw ftrace page beginning at ptr and write the events a protos
150 // into the provided bundle respecting the given event filter.
151 // |table| contains the mix of compile time (e.g. proto field ids) and
152 // run time (e.g. field offset and size) information necessary to do this.
153 // The table is initialized once at start time by the ftrace controller
154 // which passes it to the CpuReader which passes it here.
155 static size_t ParsePage(const uint8_t* ptr,
156 const EventFilter*,
157 protos::pbzero::FtraceEventBundle*,
158 const ProtoTranslationTable* table,
159 FtraceMetadata*);
160
161 // Parse a single raw ftrace event beginning at |start| and ending at |end|
162 // and write it into the provided bundle as a proto.
163 // |table| contains the mix of compile time (e.g. proto field ids) and
164 // run time (e.g. field offset and size) information necessary to do this.
165 // The table is initialized once at start time by the ftrace controller
166 // which passes it to the CpuReader which passes it to ParsePage which
167 // passes it here.
168 static bool ParseEvent(uint16_t ftrace_event_id,
169 const uint8_t* start,
170 const uint8_t* end,
171 const ProtoTranslationTable* table,
172 protozero::Message* message,
173 FtraceMetadata* metadata);
174
175 static bool ParseField(const Field& field,
176 const uint8_t* start,
177 const uint8_t* end,
178 protozero::Message* message,
179 FtraceMetadata* metadata);
180
181 private:
Primiano Tuccib6de48b2018-07-26 16:00:44 +0100182 enum ThreadCtl : uint32_t { kRun = 0, kExit };
Primiano Tuccide82dae2018-06-04 16:17:49 +0200183 static void RunWorkerThread(size_t cpu,
184 int trace_fd,
185 int staging_write_fd,
186 const std::function<void()>& on_data_available,
Primiano Tuccib6de48b2018-07-26 16:00:44 +0100187 std::atomic<ThreadCtl>* cmd_atomic);
Primiano Tuccide82dae2018-06-04 16:17:49 +0200188
189 uint8_t* GetBuffer();
190 CpuReader(const CpuReader&) = delete;
191 CpuReader& operator=(const CpuReader&) = delete;
192
Primiano Tuccifd8240d2018-08-01 09:34:54 +0100193 const ProtoTranslationTable* const table_;
Primiano Tuccide82dae2018-06-04 16:17:49 +0200194 const size_t cpu_;
195 base::ScopedFile trace_fd_;
196 base::ScopedFile staging_read_fd_;
197 base::ScopedFile staging_write_fd_;
198 base::PageAllocator::UniquePtr buffer_;
199 std::thread worker_thread_;
Primiano Tuccib6de48b2018-07-26 16:00:44 +0100200 std::atomic<ThreadCtl> cmd_{kRun};
Primiano Tuccide82dae2018-06-04 16:17:49 +0200201 PERFETTO_THREAD_CHECKER(thread_checker_)
202};
203
Primiano Tuccifd8240d2018-08-01 09:34:54 +0100204// Class for efficient 'is event with id x enabled?' tests.
205// Mirrors the data in a FtraceConfig but in a format better suited
206// to be consumed by CpuReader.
207class EventFilter {
208 public:
209 EventFilter(const ProtoTranslationTable&, std::set<std::string>);
210 ~EventFilter();
211
212 bool IsEventEnabled(size_t ftrace_event_id) const {
213 if (ftrace_event_id == 0 || ftrace_event_id > enabled_ids_.size()) {
214 return false;
215 }
216 return enabled_ids_[ftrace_event_id];
217 }
218
219 const std::set<std::string>& enabled_names() const { return enabled_names_; }
220
221 private:
222 EventFilter(const EventFilter&) = delete;
223 EventFilter& operator=(const EventFilter&) = delete;
224
225 const std::vector<bool> enabled_ids_;
226 std::set<std::string> enabled_names_;
227};
228
Primiano Tuccide82dae2018-06-04 16:17:49 +0200229} // namespace perfetto
230
231#endif // SRC_TRACED_PROBES_FTRACE_CPU_READER_H_