1 /* 2 * Copyright (C) 2018 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_TRACE_PROCESSOR_TIMESTAMPED_TRACE_PIECE_H_ 18 #define SRC_TRACE_PROCESSOR_TIMESTAMPED_TRACE_PIECE_H_ 19 20 #include "perfetto/base/build_config.h" 21 #include "perfetto/trace_processor/basic_types.h" 22 #include "perfetto/trace_processor/ref_counted.h" 23 #include "perfetto/trace_processor/trace_blob_view.h" 24 #include "src/trace_processor/importers/fuchsia/fuchsia_record.h" 25 #include "src/trace_processor/importers/json/json_utils.h" 26 #include "src/trace_processor/importers/proto/packet_sequence_state.h" 27 #include "src/trace_processor/importers/systrace/systrace_line.h" 28 #include "src/trace_processor/storage/trace_storage.h" 29 #include "src/trace_processor/types/trace_processor_context.h" 30 31 // GCC can't figure out the relationship between TimestampedTracePiece's type 32 // and the union, and thus thinks that we may be moving or destroying 33 // uninitialized data in the move constructors / destructors. Disable those 34 // warnings for TimestampedTracePiece and the types it contains. 35 #if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC) 36 #pragma GCC diagnostic push 37 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 38 #endif 39 40 namespace perfetto { 41 namespace trace_processor { 42 43 struct InlineSchedSwitch { 44 int64_t prev_state; 45 int32_t next_pid; 46 int32_t next_prio; 47 StringId next_comm; 48 }; 49 50 struct InlineSchedWaking { 51 int32_t pid; 52 int32_t target_cpu; 53 int32_t prio; 54 StringId comm; 55 }; 56 57 struct TracePacketData { 58 TraceBlobView packet; 59 RefPtr<PacketSequenceStateGeneration> sequence_state; 60 }; 61 62 struct FtraceEventData { 63 TraceBlobView event; 64 RefPtr<PacketSequenceStateGeneration> sequence_state; 65 }; 66 67 struct TrackEventData : public TracePacketData { TrackEventDataTrackEventData68 TrackEventData(TraceBlobView pv, 69 RefPtr<PacketSequenceStateGeneration> generation) 70 : TracePacketData{std::move(pv), std::move(generation)} {} 71 72 static constexpr size_t kMaxNumExtraCounters = 8; 73 74 base::Optional<int64_t> thread_timestamp; 75 base::Optional<int64_t> thread_instruction_count; 76 double counter_value = 0; 77 std::array<double, kMaxNumExtraCounters> extra_counter_values = {}; 78 }; 79 80 // On Windows std::aligned_storage was broken before VS 2017 15.8 and the 81 // compiler (even clang-cl) requires -D_ENABLE_EXTENDED_ALIGNED_STORAGE. Given 82 // the alignment here is purely a performance enhancment with no other 83 // functional requirement, disable it on Win. 84 #if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN) 85 #define PERFETTO_TTS_ALIGNMENT alignas(64) 86 #else 87 #define PERFETTO_TTS_ALIGNMENT 88 #endif 89 90 // A TimestampedTracePiece is (usually a reference to) a piece of a trace that 91 // is sorted by TraceSorter. 92 struct PERFETTO_TTS_ALIGNMENT TimestampedTracePiece { 93 enum class Type { 94 kInvalid = 0, 95 kFtraceEvent, 96 kTracePacket, 97 kInlineSchedSwitch, 98 kInlineSchedWaking, 99 kJsonValue, 100 kFuchsiaRecord, 101 kTrackEvent, 102 kSystraceLine, 103 }; 104 TimestampedTracePieceTimestampedTracePiece105 TimestampedTracePiece(int64_t ts, 106 uint64_t idx, 107 TraceBlobView tbv, 108 RefPtr<PacketSequenceStateGeneration> sequence_state) 109 : packet_data{std::move(tbv), std::move(sequence_state)}, 110 timestamp(ts), 111 packet_idx(idx), 112 type(Type::kTracePacket) {} 113 TimestampedTracePieceTimestampedTracePiece114 TimestampedTracePiece(int64_t ts, uint64_t idx, FtraceEventData fed) 115 : ftrace_event(std::move(fed)), 116 timestamp(ts), 117 packet_idx(idx), 118 type(Type::kFtraceEvent) {} 119 TimestampedTracePieceTimestampedTracePiece120 TimestampedTracePiece(int64_t ts, uint64_t idx, std::string value) 121 : json_value(std::move(value)), 122 timestamp(ts), 123 packet_idx(idx), 124 type(Type::kJsonValue) {} 125 TimestampedTracePieceTimestampedTracePiece126 TimestampedTracePiece(int64_t ts, 127 uint64_t idx, 128 std::unique_ptr<FuchsiaRecord> fr) 129 : fuchsia_record(std::move(fr)), 130 timestamp(ts), 131 packet_idx(idx), 132 type(Type::kFuchsiaRecord) {} 133 TimestampedTracePieceTimestampedTracePiece134 TimestampedTracePiece(int64_t ts, 135 uint64_t idx, 136 std::unique_ptr<TrackEventData> ted) 137 : track_event_data(std::move(ted)), 138 timestamp(ts), 139 packet_idx(idx), 140 type(Type::kTrackEvent) {} 141 TimestampedTracePieceTimestampedTracePiece142 TimestampedTracePiece(int64_t ts, 143 uint64_t idx, 144 std::unique_ptr<SystraceLine> ted) 145 : systrace_line(std::move(ted)), 146 timestamp(ts), 147 packet_idx(idx), 148 type(Type::kSystraceLine) {} 149 TimestampedTracePieceTimestampedTracePiece150 TimestampedTracePiece(int64_t ts, uint64_t idx, InlineSchedSwitch iss) 151 : sched_switch(std::move(iss)), 152 timestamp(ts), 153 packet_idx(idx), 154 type(Type::kInlineSchedSwitch) {} 155 TimestampedTracePieceTimestampedTracePiece156 TimestampedTracePiece(int64_t ts, uint64_t idx, InlineSchedWaking isw) 157 : sched_waking(std::move(isw)), 158 timestamp(ts), 159 packet_idx(idx), 160 type(Type::kInlineSchedWaking) {} 161 TimestampedTracePieceTimestampedTracePiece162 TimestampedTracePiece(TimestampedTracePiece&& ttp) noexcept { 163 // Adopt |ttp|'s data. We have to use placement-new to fill the fields 164 // because their original values may be uninitialized and thus 165 // move-assignment won't work correctly. 166 switch (ttp.type) { 167 case Type::kInvalid: 168 break; 169 case Type::kFtraceEvent: 170 new (&ftrace_event) FtraceEventData(std::move(ttp.ftrace_event)); 171 break; 172 case Type::kTracePacket: 173 new (&packet_data) TracePacketData(std::move(ttp.packet_data)); 174 break; 175 case Type::kInlineSchedSwitch: 176 new (&sched_switch) InlineSchedSwitch(std::move(ttp.sched_switch)); 177 break; 178 case Type::kInlineSchedWaking: 179 new (&sched_waking) InlineSchedWaking(std::move(ttp.sched_waking)); 180 break; 181 case Type::kJsonValue: 182 new (&json_value) std::string(std::move(ttp.json_value)); 183 break; 184 case Type::kFuchsiaRecord: 185 new (&fuchsia_record) 186 std::unique_ptr<FuchsiaRecord>(std::move(ttp.fuchsia_record)); 187 break; 188 case Type::kTrackEvent: 189 new (&track_event_data) 190 std::unique_ptr<TrackEventData>(std::move(ttp.track_event_data)); 191 break; 192 case Type::kSystraceLine: 193 new (&systrace_line) 194 std::unique_ptr<SystraceLine>(std::move(ttp.systrace_line)); 195 } 196 timestamp = ttp.timestamp; 197 packet_idx = ttp.packet_idx; 198 type = ttp.type; 199 200 // Invalidate |ttp|. 201 ttp.type = Type::kInvalid; 202 } 203 204 TimestampedTracePiece& operator=(TimestampedTracePiece&& ttp) { 205 if (this != &ttp) { 206 // First invoke the destructor and then invoke the move constructor 207 // inline via placement-new to implement move-assignment. 208 this->~TimestampedTracePiece(); 209 new (this) TimestampedTracePiece(std::move(ttp)); 210 } 211 return *this; 212 } 213 214 TimestampedTracePiece(const TimestampedTracePiece&) = delete; 215 TimestampedTracePiece& operator=(const TimestampedTracePiece&) = delete; 216 ~TimestampedTracePieceTimestampedTracePiece217 ~TimestampedTracePiece() { 218 switch (type) { 219 case Type::kInvalid: 220 case Type::kInlineSchedSwitch: 221 case Type::kInlineSchedWaking: 222 break; 223 case Type::kFtraceEvent: 224 ftrace_event.~FtraceEventData(); 225 break; 226 case Type::kTracePacket: 227 packet_data.~TracePacketData(); 228 break; 229 case Type::kJsonValue: 230 json_value.~basic_string(); 231 break; 232 case Type::kFuchsiaRecord: 233 fuchsia_record.~unique_ptr(); 234 break; 235 case Type::kTrackEvent: 236 track_event_data.~unique_ptr(); 237 break; 238 case Type::kSystraceLine: 239 systrace_line.~unique_ptr(); 240 break; 241 } 242 } 243 244 // For std::lower_bound(). CompareTimestampedTracePiece245 static inline bool Compare(const TimestampedTracePiece& x, int64_t ts) { 246 return x.timestamp < ts; 247 } 248 249 // For std::sort(). 250 inline bool operator<(const TimestampedTracePiece& o) const { 251 return timestamp < o.timestamp || 252 (timestamp == o.timestamp && packet_idx < o.packet_idx); 253 } 254 255 // For std::sort(). Without this the compiler will fall back on invoking 256 // move operators on temporary objects. swapTimestampedTracePiece257 friend void swap(TimestampedTracePiece& a, TimestampedTracePiece& b) { 258 // We know that TimestampedTracePiece is 64-byte aligned (because of the 259 // alignas(64) in the declaration above). We also know that swapping it is 260 // trivial and we can just swap the memory without invoking move operators. 261 // The cast to aligned_storage below allows the compiler to turn this into 262 // a bunch of movaps with large XMM registers (128/256/512 bit depending on 263 // -mavx). 264 using AS = 265 typename std::aligned_storage<sizeof(TimestampedTracePiece), 266 alignof(TimestampedTracePiece)>::type; 267 using std::swap; 268 swap(reinterpret_cast<AS&>(a), reinterpret_cast<AS&>(b)); 269 } 270 271 // Fields ordered for packing. 272 273 // Data for different types of TimestampedTracePiece. 274 union { 275 FtraceEventData ftrace_event; 276 TracePacketData packet_data; 277 InlineSchedSwitch sched_switch; 278 InlineSchedWaking sched_waking; 279 std::string json_value; 280 std::unique_ptr<FuchsiaRecord> fuchsia_record; 281 std::unique_ptr<TrackEventData> track_event_data; 282 std::unique_ptr<SystraceLine> systrace_line; 283 }; 284 285 int64_t timestamp; 286 uint64_t packet_idx; 287 Type type; 288 }; 289 290 // std::sort<TTS> is an extremely hot path in TraceProcessor (in trace_sorter.h) 291 // When TTS is 512-bit wide, we can leverage SIMD instructions to swap it by 292 // declaring it aligned at its own size, without losing any space in the 293 // CircularQueue due to fragmentation. This makes a 6% difference in the 294 // ingestion time of a large trace. See the comments above in the swap() above. 295 static_assert(sizeof(TimestampedTracePiece) <= 64, 296 "TimestampedTracePiece cannot grow beyond 64 bytes"); 297 298 } // namespace trace_processor 299 } // namespace perfetto 300 301 #if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC) 302 #pragma GCC diagnostic pop 303 #endif 304 305 #endif // SRC_TRACE_PROCESSOR_TIMESTAMPED_TRACE_PIECE_H_ 306