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