blob: 85af6ac67db7b5fdaadc73b6d6c98f0a56cd476f [file] [log] [blame]
/*
* Copyright (C) 2023 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_SORTER_TRACE_TOKEN_BUFFER_H_
#define SRC_TRACE_PROCESSOR_SORTER_TRACE_TOKEN_BUFFER_H_
#include <cstdint>
#include <limits>
#include <optional>
#include <utility>
#include <vector>
#include "perfetto/base/compiler.h"
#include "perfetto/ext/base/circular_queue.h"
#include "perfetto/ext/base/utils.h"
#include "perfetto/trace_processor/trace_blob.h"
#include "perfetto/trace_processor/trace_blob_view.h"
#include "src/trace_processor/importers/common/parser_types.h"
#include "src/trace_processor/util/bump_allocator.h"
namespace perfetto {
namespace trace_processor {
// Helper class which stores tokenized objects while the corresponding events
// are being sorted by TraceSorter.
//
// This class intrusively compresses the tokenized objects as much as possible
// to reduce their memory footprint. This is important to reduce the peak memory
// usage of TraceProcessor which is always hit at some point during sorting.
// The tokenized objects make up the vast majority of this peak so we trade the
// complexity in this class for big reductions in the peak use.
//
// go/perfetto-tp-memory-use gives an overview of trace processor memory usage.
class TraceTokenBuffer {
public:
// Identifier returned when appending items to this buffer. This id can
// later by passed to |Extract| to retrieve the event.
struct Id {
// The allocation id of the object in the buffer.
BumpAllocator::AllocId alloc_id;
};
// Appends an object of type |T| to the token buffer. Returns an id for
// looking up the object later using |Extract|.
template <typename T>
PERFETTO_WARN_UNUSED_RESULT Id Append(T object) {
static_assert(sizeof(T) % 8 == 0, "Size must be a multiple of 8");
static_assert(alignof(T) == 8, "Alignment must be 8");
BumpAllocator::AllocId id = AllocAndResizeInternedVectors(sizeof(T));
new (allocator_.GetPointer(id)) T(std::move(object));
return Id{id};
}
PERFETTO_WARN_UNUSED_RESULT Id Append(TrackEventData);
PERFETTO_WARN_UNUSED_RESULT Id Append(TracePacketData data) {
// While in theory we could add a special case for TracePacketData, the
// judgement call we make is that the code complexity does not justify the
// micro-performance gain you might hope to see by avoiding the few if
// conditions in the |TracePacketData| path.
return Append(TrackEventData(std::move(data)));
}
// Extracts an object of type |T| from the token buffer using an id previously
// returned by |Append|. This type *must* match the type added using Append.
// Mismatching types will caused undefined behaviour.
template <typename T>
PERFETTO_WARN_UNUSED_RESULT T Extract(Id id) {
T* typed_ptr = static_cast<T*>(allocator_.GetPointer(id.alloc_id));
T object(std::move(*typed_ptr));
typed_ptr->~T();
allocator_.Free(id.alloc_id);
return object;
}
// Returns the "past-the-end" id from the underlying allocator.
// The main use of this function is to provide an id which is greater than
// all ids previously returned by |Append|.
//
// This is similar to the |end()| function in standard library vector classes.
BumpAllocator::AllocId PastTheEndAllocId() {
return allocator_.PastTheEndId();
}
// Attempts to free any memory retained by this buffer and the underlying
// allocator. The amount of memory free is implementation defined.
void FreeMemory();
private:
struct BlobWithOffset {
TraceBlob* blob;
size_t offset_in_blob;
};
using InternedIndex = size_t;
using BlobWithOffsets = std::vector<BlobWithOffset>;
using SequenceStates = std::vector<PacketSequenceStateGeneration*>;
// Functions to intern TraceBlob and PacketSequenceStateGeneration: as these
// are often shared between packets, we can significantly reduce memory use
// by only storing them once.
uint32_t InternTraceBlob(InternedIndex, const TraceBlobView&);
uint16_t InternSeqState(InternedIndex, RefPtr<PacketSequenceStateGeneration>);
uint32_t AddTraceBlob(InternedIndex, const TraceBlobView&);
BumpAllocator::AllocId AllocAndResizeInternedVectors(uint32_t size);
InternedIndex GetInternedIndex(BumpAllocator::AllocId);
BumpAllocator allocator_;
base::CircularQueue<BlobWithOffsets> interned_blobs_;
base::CircularQueue<SequenceStates> interned_seqs_;
};
// GCC7 does not like us declaring these inside the class so define these
// out-of-line.
template <>
PERFETTO_WARN_UNUSED_RESULT TrackEventData
TraceTokenBuffer::Extract<TrackEventData>(Id);
template <>
PERFETTO_WARN_UNUSED_RESULT inline TracePacketData
TraceTokenBuffer::Extract<TracePacketData>(Id id) {
// See the comment in Append(TracePacketData) for why we do this.
return Extract<TrackEventData>(id).trace_packet_data;
}
} // namespace trace_processor
} // namespace perfetto
#endif // SRC_TRACE_PROCESSOR_SORTER_TRACE_TOKEN_BUFFER_H_