xref: /aosp_15_r20/external/perfetto/src/traced/probes/ftrace/compact_sched.h (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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     bool switch_requested,
92     const CompactSchedEventFormat& compact_format);
93 
94 CompactSchedConfig EnabledCompactSchedConfigForTesting();
95 CompactSchedConfig DisabledCompactSchedConfigForTesting();
96 
97 // Collects fields of sched_switch events, allowing them to be written out
98 // in a compact encoding.
99 class CompactSchedSwitchBuffer {
100  public:
timestamp()101   protozero::PackedVarInt& timestamp() { return timestamp_; }
prev_state()102   protozero::PackedVarInt& prev_state() { return prev_state_; }
next_pid()103   protozero::PackedVarInt& next_pid() { return next_pid_; }
next_prio()104   protozero::PackedVarInt& next_prio() { return next_prio_; }
next_comm_index()105   protozero::PackedVarInt& next_comm_index() { return next_comm_index_; }
106 
size()107   size_t size() const {
108     // Caller should fill all per-field buffers at the same rate.
109     return timestamp_.size();
110   }
111 
AppendTimestamp(uint64_t timestamp)112   inline void AppendTimestamp(uint64_t timestamp) {
113     timestamp_.Append(timestamp - last_timestamp_);
114     last_timestamp_ = timestamp;
115   }
116 
117   void Write(
118       protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const;
119   void Reset();
120 
121  private:
122   // First timestamp in a bundle is absolute. The rest are all delta-encoded,
123   // each relative to the preceding sched_switch timestamp.
124   uint64_t last_timestamp_ = 0;
125 
126   protozero::PackedVarInt timestamp_;
127   protozero::PackedVarInt prev_state_;
128   protozero::PackedVarInt next_pid_;
129   protozero::PackedVarInt next_prio_;
130   // Interning indices of the next_comm values. See |CommInterner|.
131   protozero::PackedVarInt next_comm_index_;
132 };
133 
134 // As |CompactSchedSwitchBuffer|, but for sched_waking events.
135 class CompactSchedWakingBuffer {
136  public:
pid()137   protozero::PackedVarInt& pid() { return pid_; }
target_cpu()138   protozero::PackedVarInt& target_cpu() { return target_cpu_; }
prio()139   protozero::PackedVarInt& prio() { return prio_; }
comm_index()140   protozero::PackedVarInt& comm_index() { return comm_index_; }
common_flags()141   protozero::PackedVarInt& common_flags() { return common_flags_; }
142 
size()143   size_t size() const {
144     // Caller should fill all per-field buffers at the same rate.
145     return timestamp_.size();
146   }
147 
AppendTimestamp(uint64_t timestamp)148   inline void AppendTimestamp(uint64_t timestamp) {
149     timestamp_.Append(timestamp - last_timestamp_);
150     last_timestamp_ = timestamp;
151   }
152 
153   void Write(
154       protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const;
155   void Reset();
156 
157  private:
158   uint64_t last_timestamp_ = 0;
159 
160   protozero::PackedVarInt timestamp_;
161   protozero::PackedVarInt pid_;
162   protozero::PackedVarInt target_cpu_;
163   protozero::PackedVarInt prio_;
164   // Interning indices of the comm values. See |CommInterner|.
165   protozero::PackedVarInt comm_index_;
166   protozero::PackedVarInt common_flags_;
167 };
168 
169 class CommInterner {
170  public:
171   static constexpr size_t kExpectedCommLength = 16;
172 
InternComm(const char * ptr)173   size_t InternComm(const char* ptr) {
174     // Linearly scan existing string views, ftrace reader will
175     // make sure this set doesn't grow too large.
176     base::StringView transient_view(ptr);
177     for (size_t i = 0; i < interned_comms_size_; i++) {
178       if (transient_view == interned_comms_[i]) {
179         return i;
180       }
181     }
182 
183     // Unique comm, intern it. Null byte is not copied over.
184     char* start = intern_buf_ + intern_buf_write_pos_;
185     size_t size = transient_view.size();
186     memcpy(start, ptr, size);
187     intern_buf_write_pos_ += size;
188 
189     size_t idx = interned_comms_size_;
190     base::StringView safe_view(start, size);
191     interned_comms_[interned_comms_size_++] = safe_view;
192 
193     PERFETTO_DCHECK(intern_buf_write_pos_ <= sizeof(intern_buf_));
194     PERFETTO_DCHECK(interned_comms_size_ < kMaxElements);
195     return idx;
196   }
197 
interned_comms_size()198   size_t interned_comms_size() const { return interned_comms_size_; }
199 
200   void Write(
201       protos::pbzero::FtraceEventBundle::CompactSched* compact_out) const;
202   void Reset();
203 
204  private:
205   // TODO(rsavitski): Consider making the storage dynamically-expandable instead
206   // to not rely on sizing the buffer for the worst case.
207   static constexpr size_t kMaxElements = 4096;
208 
209   char intern_buf_[kMaxElements * (kExpectedCommLength - 1)];
210   size_t intern_buf_write_pos_ = 0;
211 
212   // Views into unique interned comm strings. Even if every event carries a
213   // unique comm, the ftrace reader is expected to flush the compact buffer way
214   // before this reaches capacity. This is since the cost of processing each
215   // event grows with every unique interned comm (as the interning needs to
216   // search all existing internings).
217   std::array<base::StringView, kMaxElements> interned_comms_;
218   uint32_t interned_comms_size_ = 0;
219 };
220 
221 // Mutable state for buffering parts of scheduling events, that can later be
222 // written out in a compact format with |WriteAndReset|. Used by the ftrace
223 // reader.
224 class CompactSchedBuffer {
225  public:
sched_switch()226   CompactSchedSwitchBuffer& sched_switch() { return switch_; }
sched_waking()227   CompactSchedWakingBuffer& sched_waking() { return waking_; }
interner()228   CommInterner& interner() { return interner_; }
229 
230   // Writes out the currently buffered events, and starts the next batch
231   // internally.
232   void WriteAndReset(protos::pbzero::FtraceEventBundle* bundle);
233 
234   // Not normally needed: reinitialise the buffer from an unknown state.
235   void Reset();
236 
237  private:
238   CommInterner interner_;
239   CompactSchedSwitchBuffer switch_;
240   CompactSchedWakingBuffer waking_;
241 };
242 
243 }  // namespace perfetto
244 
245 #endif  // SRC_TRACED_PROBES_FTRACE_COMPACT_SCHED_H_
246