• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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_COMPACT_SCHED_H_
18 #define SRC_TRACED_PROBES_FTRACE_COMPACT_SCHED_H_
19 
20 #include <stdint.h>
21 
22 #include "perfetto/ext/base/string_view.h"
23 #include "perfetto/protozero/packed_repeated_fields.h"
24 #include "protos/perfetto/trace/ftrace/ftrace_event_bundle.pbzero.h"
25 #include "src/traced/probes/ftrace/event_info_constants.h"
26 #include "src/traced/probes/ftrace/ftrace_config_utils.h"
27 
28 namespace perfetto {
29 
30 // The subset of the sched_switch event's format that is used when parsing and
31 // encoding into the compact format.
32 struct CompactSchedSwitchFormat {
33   uint32_t event_id;
34   uint16_t size;
35 
36   uint16_t next_pid_offset;
37   FtraceFieldType next_pid_type;
38   uint16_t next_prio_offset;
39   FtraceFieldType next_prio_type;
40   uint16_t prev_state_offset;
41   FtraceFieldType prev_state_type;
42   uint16_t next_comm_offset;
43 };
44 
45 // The subset of the sched_waking event's format that is used when parsing and
46 // encoding into the compact format.
47 struct CompactSchedWakingFormat {
48   uint32_t event_id;
49   uint16_t size;
50 
51   uint16_t pid_offset;
52   FtraceFieldType pid_type;
53   uint16_t target_cpu_offset;
54   FtraceFieldType target_cpu_type;
55   uint16_t prio_offset;
56   FtraceFieldType prio_type;
57   uint16_t comm_offset;
58   uint16_t common_flags_offset;
59   FtraceFieldType common_flags_type;
60 };
61 
62 // Pre-parsed format of a subset of scheduling events, for use during ftrace
63 // parsing if compact encoding is enabled. Holds a flag, |format_valid| to
64 // state whether the compile-time assumptions about the format held at runtime.
65 // If they didn't, we cannot use the compact encoding.
66 struct CompactSchedEventFormat {
67   // If false, the rest of the struct is considered invalid.
68   const bool format_valid;
69 
70   const CompactSchedSwitchFormat sched_switch;
71   const CompactSchedWakingFormat sched_waking;
72 };
73 
74 CompactSchedEventFormat ValidateFormatForCompactSched(
75     const std::vector<Event>& events,
76     const std::vector<Field>& common_fields);
77 
78 CompactSchedEventFormat InvalidCompactSchedEventFormatForTesting();
79 
80 // Compact encoding configuration used at ftrace reading & parsing time.
81 struct CompactSchedConfig {
CompactSchedConfigCompactSchedConfig82   CompactSchedConfig(bool _enabled) : enabled(_enabled) {}
83 
84   // If true, and sched_switch and/or sched_waking events are enabled, encode
85   // them in a compact format instead of the normal form.
86   const bool enabled = false;
87 };
88 
89 CompactSchedConfig CreateCompactSchedConfig(
90     const FtraceConfig& request,
91     const CompactSchedEventFormat& compact_format);
92 
93 CompactSchedConfig EnabledCompactSchedConfigForTesting();
94 CompactSchedConfig DisabledCompactSchedConfigForTesting();
95 
96 // Collects fields of sched_switch events, allowing them to be written out
97 // in a compact encoding.
98 class CompactSchedSwitchBuffer {
99  public:
timestamp()100   protozero::PackedVarInt& timestamp() { return timestamp_; }
prev_state()101   protozero::PackedVarInt& prev_state() { return prev_state_; }
next_pid()102   protozero::PackedVarInt& next_pid() { return next_pid_; }
next_prio()103   protozero::PackedVarInt& next_prio() { return next_prio_; }
next_comm_index()104   protozero::PackedVarInt& next_comm_index() { return next_comm_index_; }
105 
size()106   size_t size() const {
107     // Caller should fill all per-field buffers at the same rate.
108     return timestamp_.size();
109   }
110 
AppendTimestamp(uint64_t timestamp)111   inline void AppendTimestamp(uint64_t timestamp) {
112     timestamp_.Append(timestamp - last_timestamp_);
113     last_timestamp_ = timestamp;
114   }
115 
116   void Write(
117       protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const;
118   void Reset();
119 
120  private:
121   // First timestamp in a bundle is absolute. The rest are all delta-encoded,
122   // each relative to the preceding sched_switch timestamp.
123   uint64_t last_timestamp_ = 0;
124 
125   protozero::PackedVarInt timestamp_;
126   protozero::PackedVarInt prev_state_;
127   protozero::PackedVarInt next_pid_;
128   protozero::PackedVarInt next_prio_;
129   // Interning indices of the next_comm values. See |CommInterner|.
130   protozero::PackedVarInt next_comm_index_;
131 };
132 
133 // As |CompactSchedSwitchBuffer|, but for sched_waking events.
134 class CompactSchedWakingBuffer {
135  public:
pid()136   protozero::PackedVarInt& pid() { return pid_; }
target_cpu()137   protozero::PackedVarInt& target_cpu() { return target_cpu_; }
prio()138   protozero::PackedVarInt& prio() { return prio_; }
comm_index()139   protozero::PackedVarInt& comm_index() { return comm_index_; }
common_flags()140   protozero::PackedVarInt& common_flags() { return common_flags_; }
141 
size()142   size_t size() const {
143     // Caller should fill all per-field buffers at the same rate.
144     return timestamp_.size();
145   }
146 
AppendTimestamp(uint64_t timestamp)147   inline void AppendTimestamp(uint64_t timestamp) {
148     timestamp_.Append(timestamp - last_timestamp_);
149     last_timestamp_ = timestamp;
150   }
151 
152   void Write(
153       protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const;
154   void Reset();
155 
156  private:
157   uint64_t last_timestamp_ = 0;
158 
159   protozero::PackedVarInt timestamp_;
160   protozero::PackedVarInt pid_;
161   protozero::PackedVarInt target_cpu_;
162   protozero::PackedVarInt prio_;
163   // Interning indices of the comm values. See |CommInterner|.
164   protozero::PackedVarInt comm_index_;
165   protozero::PackedVarInt common_flags_;
166 };
167 
168 class CommInterner {
169  public:
170   static constexpr size_t kExpectedCommLength = 16;
171 
InternComm(const char * ptr)172   size_t InternComm(const char* ptr) {
173     // Linearly scan existing string views, ftrace reader will
174     // make sure this set doesn't grow too large.
175     base::StringView transient_view(ptr);
176     for (size_t i = 0; i < interned_comms_size_; i++) {
177       if (transient_view == interned_comms_[i]) {
178         return i;
179       }
180     }
181 
182     // Unique comm, intern it. Null byte is not copied over.
183     char* start = intern_buf_ + intern_buf_write_pos_;
184     size_t size = transient_view.size();
185     memcpy(start, ptr, size);
186     intern_buf_write_pos_ += size;
187 
188     size_t idx = interned_comms_size_;
189     base::StringView safe_view(start, size);
190     interned_comms_[interned_comms_size_++] = safe_view;
191 
192     PERFETTO_DCHECK(intern_buf_write_pos_ <= sizeof(intern_buf_));
193     PERFETTO_DCHECK(interned_comms_size_ < kMaxElements);
194     return idx;
195   }
196 
interned_comms_size()197   size_t interned_comms_size() const { return interned_comms_size_; }
198 
199   void Write(
200       protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const;
201   void Reset();
202 
203  private:
204   // TODO(rsavitski): Consider making the storage dynamically-expandable instead
205   // to not rely on sizing the buffer for the worst case.
206   static constexpr size_t kMaxElements = 4096;
207 
208   char intern_buf_[kMaxElements * (kExpectedCommLength - 1)];
209   size_t intern_buf_write_pos_ = 0;
210 
211   // Views into unique interned comm strings. Even if every event carries a
212   // unique comm, the ftrace reader is expected to flush the compact buffer way
213   // before this reaches capacity. This is since the cost of processing each
214   // event grows with every unique interned comm (as the interning needs to
215   // search all existing internings).
216   std::array<base::StringView, kMaxElements> interned_comms_;
217   uint32_t interned_comms_size_ = 0;
218 };
219 
220 // Mutable state for buffering parts of scheduling events, that can later be
221 // written out in a compact format with |WriteAndReset|. Used by the ftrace
222 // reader.
223 class CompactSchedBuffer {
224  public:
sched_switch()225   CompactSchedSwitchBuffer& sched_switch() { return switch_; }
sched_waking()226   CompactSchedWakingBuffer& sched_waking() { return waking_; }
interner()227   CommInterner& interner() { return interner_; }
228 
229   // Writes out the currently buffered events, and starts the next batch
230   // internally.
231   void WriteAndReset(protos::pbzero::FtraceEventBundle* bundle);
232 
233  private:
234   CommInterner interner_;
235   CompactSchedSwitchBuffer switch_;
236   CompactSchedWakingBuffer waking_;
237 };
238 
239 }  // namespace perfetto
240 
241 #endif  // SRC_TRACED_PROBES_FTRACE_COMPACT_SCHED_H_
242