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