blob: 1b1a0f610397c590b666d4326f6587970acde728 [file] [log] [blame]
/*
* Copyright (C) 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef SRC_TRACE_PROCESSOR_TIMESTAMPED_TRACE_PIECE_H_
#define SRC_TRACE_PROCESSOR_TIMESTAMPED_TRACE_PIECE_H_
#include "perfetto/base/build_config.h"
#include "perfetto/trace_processor/basic_types.h"
#include "perfetto/trace_processor/ref_counted.h"
#include "perfetto/trace_processor/trace_blob_view.h"
#include "src/trace_processor/importers/fuchsia/fuchsia_record.h"
#include "src/trace_processor/importers/json/json_utils.h"
#include "src/trace_processor/importers/proto/packet_sequence_state.h"
#include "src/trace_processor/importers/systrace/systrace_line.h"
#include "src/trace_processor/storage/trace_storage.h"
#include "src/trace_processor/types/trace_processor_context.h"
// GCC can't figure out the relationship between TimestampedTracePiece's type
// and the union, and thus thinks that we may be moving or destroying
// uninitialized data in the move constructors / destructors. Disable those
// warnings for TimestampedTracePiece and the types it contains.
#if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
namespace perfetto {
namespace trace_processor {
struct InlineSchedSwitch {
int64_t prev_state;
int32_t next_pid;
int32_t next_prio;
StringId next_comm;
};
struct InlineSchedWaking {
int32_t pid;
int32_t target_cpu;
int32_t prio;
StringId comm;
};
struct TracePacketData {
TraceBlobView packet;
RefPtr<PacketSequenceStateGeneration> sequence_state;
};
struct FtraceEventData {
TraceBlobView event;
RefPtr<PacketSequenceStateGeneration> sequence_state;
};
struct TrackEventData : public TracePacketData {
TrackEventData(TraceBlobView pv,
RefPtr<PacketSequenceStateGeneration> generation)
: TracePacketData{std::move(pv), std::move(generation)} {}
static constexpr size_t kMaxNumExtraCounters = 8;
base::Optional<int64_t> thread_timestamp;
base::Optional<int64_t> thread_instruction_count;
double counter_value = 0;
std::array<double, kMaxNumExtraCounters> extra_counter_values = {};
};
// On Windows std::aligned_storage was broken before VS 2017 15.8 and the
// compiler (even clang-cl) requires -D_ENABLE_EXTENDED_ALIGNED_STORAGE. Given
// the alignment here is purely a performance enhancment with no other
// functional requirement, disable it on Win.
#if !PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
#define PERFETTO_TTS_ALIGNMENT alignas(64)
#else
#define PERFETTO_TTS_ALIGNMENT
#endif
// A TimestampedTracePiece is (usually a reference to) a piece of a trace that
// is sorted by TraceSorter.
struct PERFETTO_TTS_ALIGNMENT TimestampedTracePiece {
enum class Type {
kInvalid = 0,
kFtraceEvent,
kTracePacket,
kInlineSchedSwitch,
kInlineSchedWaking,
kJsonValue,
kFuchsiaRecord,
kTrackEvent,
kSystraceLine,
};
TimestampedTracePiece(int64_t ts,
uint64_t idx,
TraceBlobView tbv,
RefPtr<PacketSequenceStateGeneration> sequence_state)
: packet_data{std::move(tbv), std::move(sequence_state)},
timestamp(ts),
packet_idx(idx),
type(Type::kTracePacket) {}
TimestampedTracePiece(int64_t ts, uint64_t idx, FtraceEventData fed)
: ftrace_event(std::move(fed)),
timestamp(ts),
packet_idx(idx),
type(Type::kFtraceEvent) {}
TimestampedTracePiece(int64_t ts, uint64_t idx, std::string value)
: json_value(std::move(value)),
timestamp(ts),
packet_idx(idx),
type(Type::kJsonValue) {}
TimestampedTracePiece(int64_t ts,
uint64_t idx,
std::unique_ptr<FuchsiaRecord> fr)
: fuchsia_record(std::move(fr)),
timestamp(ts),
packet_idx(idx),
type(Type::kFuchsiaRecord) {}
TimestampedTracePiece(int64_t ts,
uint64_t idx,
std::unique_ptr<TrackEventData> ted)
: track_event_data(std::move(ted)),
timestamp(ts),
packet_idx(idx),
type(Type::kTrackEvent) {}
TimestampedTracePiece(int64_t ts,
uint64_t idx,
std::unique_ptr<SystraceLine> ted)
: systrace_line(std::move(ted)),
timestamp(ts),
packet_idx(idx),
type(Type::kSystraceLine) {}
TimestampedTracePiece(int64_t ts, uint64_t idx, InlineSchedSwitch iss)
: sched_switch(std::move(iss)),
timestamp(ts),
packet_idx(idx),
type(Type::kInlineSchedSwitch) {}
TimestampedTracePiece(int64_t ts, uint64_t idx, InlineSchedWaking isw)
: sched_waking(std::move(isw)),
timestamp(ts),
packet_idx(idx),
type(Type::kInlineSchedWaking) {}
TimestampedTracePiece(TimestampedTracePiece&& ttp) noexcept {
// Adopt |ttp|'s data. We have to use placement-new to fill the fields
// because their original values may be uninitialized and thus
// move-assignment won't work correctly.
switch (ttp.type) {
case Type::kInvalid:
break;
case Type::kFtraceEvent:
new (&ftrace_event) FtraceEventData(std::move(ttp.ftrace_event));
break;
case Type::kTracePacket:
new (&packet_data) TracePacketData(std::move(ttp.packet_data));
break;
case Type::kInlineSchedSwitch:
new (&sched_switch) InlineSchedSwitch(std::move(ttp.sched_switch));
break;
case Type::kInlineSchedWaking:
new (&sched_waking) InlineSchedWaking(std::move(ttp.sched_waking));
break;
case Type::kJsonValue:
new (&json_value) std::string(std::move(ttp.json_value));
break;
case Type::kFuchsiaRecord:
new (&fuchsia_record)
std::unique_ptr<FuchsiaRecord>(std::move(ttp.fuchsia_record));
break;
case Type::kTrackEvent:
new (&track_event_data)
std::unique_ptr<TrackEventData>(std::move(ttp.track_event_data));
break;
case Type::kSystraceLine:
new (&systrace_line)
std::unique_ptr<SystraceLine>(std::move(ttp.systrace_line));
}
timestamp = ttp.timestamp;
packet_idx = ttp.packet_idx;
type = ttp.type;
// Invalidate |ttp|.
ttp.type = Type::kInvalid;
}
TimestampedTracePiece& operator=(TimestampedTracePiece&& ttp) {
if (this != &ttp) {
// First invoke the destructor and then invoke the move constructor
// inline via placement-new to implement move-assignment.
this->~TimestampedTracePiece();
new (this) TimestampedTracePiece(std::move(ttp));
}
return *this;
}
TimestampedTracePiece(const TimestampedTracePiece&) = delete;
TimestampedTracePiece& operator=(const TimestampedTracePiece&) = delete;
~TimestampedTracePiece() {
switch (type) {
case Type::kInvalid:
case Type::kInlineSchedSwitch:
case Type::kInlineSchedWaking:
break;
case Type::kFtraceEvent:
ftrace_event.~FtraceEventData();
break;
case Type::kTracePacket:
packet_data.~TracePacketData();
break;
case Type::kJsonValue:
json_value.~basic_string();
break;
case Type::kFuchsiaRecord:
fuchsia_record.~unique_ptr();
break;
case Type::kTrackEvent:
track_event_data.~unique_ptr();
break;
case Type::kSystraceLine:
systrace_line.~unique_ptr();
break;
}
}
// For std::lower_bound().
static inline bool Compare(const TimestampedTracePiece& x, int64_t ts) {
return x.timestamp < ts;
}
// For std::sort().
inline bool operator<(const TimestampedTracePiece& o) const {
return timestamp < o.timestamp ||
(timestamp == o.timestamp && packet_idx < o.packet_idx);
}
// For std::sort(). Without this the compiler will fall back on invoking
// move operators on temporary objects.
friend void swap(TimestampedTracePiece& a, TimestampedTracePiece& b) {
// We know that TimestampedTracePiece is 64-byte aligned (because of the
// alignas(64) in the declaration above). We also know that swapping it is
// trivial and we can just swap the memory without invoking move operators.
// The cast to aligned_storage below allows the compiler to turn this into
// a bunch of movaps with large XMM registers (128/256/512 bit depending on
// -mavx).
using AS =
typename std::aligned_storage<sizeof(TimestampedTracePiece),
alignof(TimestampedTracePiece)>::type;
using std::swap;
swap(reinterpret_cast<AS&>(a), reinterpret_cast<AS&>(b));
}
// Fields ordered for packing.
// Data for different types of TimestampedTracePiece.
union {
FtraceEventData ftrace_event;
TracePacketData packet_data;
InlineSchedSwitch sched_switch;
InlineSchedWaking sched_waking;
std::string json_value;
std::unique_ptr<FuchsiaRecord> fuchsia_record;
std::unique_ptr<TrackEventData> track_event_data;
std::unique_ptr<SystraceLine> systrace_line;
};
int64_t timestamp;
uint64_t packet_idx;
Type type;
};
// std::sort<TTS> is an extremely hot path in TraceProcessor (in trace_sorter.h)
// When TTS is 512-bit wide, we can leverage SIMD instructions to swap it by
// declaring it aligned at its own size, without losing any space in the
// CircularQueue due to fragmentation. This makes a 6% difference in the
// ingestion time of a large trace. See the comments above in the swap() above.
static_assert(sizeof(TimestampedTracePiece) <= 64,
"TimestampedTracePiece cannot grow beyond 64 bytes");
} // namespace trace_processor
} // namespace perfetto
#if PERFETTO_BUILDFLAG(PERFETTO_COMPILER_GCC)
#pragma GCC diagnostic pop
#endif
#endif // SRC_TRACE_PROCESSOR_TIMESTAMPED_TRACE_PIECE_H_