blob: e4f4627616ced21d01539dc3c6cc45b43e835a71 [file] [log] [blame]
/*
* Copyright (C) 2020 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_TP_METATRACE_H_
#define SRC_TRACE_PROCESSOR_TP_METATRACE_H_
#include <array>
#include <functional>
#include <vector>
#include "perfetto/base/time.h"
#include "perfetto/ext/base/metatrace_events.h"
#include "perfetto/ext/base/string_view.h"
#include "perfetto/ext/base/thread_checker.h"
#include "perfetto/trace_processor/metatrace_config.h"
#include "protos/perfetto/trace_processor/metatrace_categories.pbzero.h"
// Trace processor maintains its own base implementation to avoid the
// threading and task runners which are required by base's metatracing.
// Moreover, this metatrace also adds support for args which is missing
// from base's metatracing.
// On the other hand, this implementation is not (currently) thread-safe
// and is likely less performant than base's implementation.
namespace perfetto {
namespace trace_processor {
namespace metatrace {
using Category = protos::pbzero::MetatraceCategories;
// Stores whether meta-tracing is enabled.
extern Category g_enabled_categories;
inline uint64_t TraceTimeNowNs() {
return static_cast<uint64_t>(base::GetBootTimeNs().count());
}
struct Record {
// Timestamp since boot in ns.
uint64_t timestamp_ns;
// Duration of the event.
uint64_t duration_ns;
// The name of the event.
// This is assumed to be a static/long lived string.
const char* event_name;
// Extra context for some types of events.
// This buffer is leaked once per record - every time a record is
// reused, the old memory is released and a new allocation is performed.
char* args_buffer = nullptr;
uint32_t args_buffer_size = 0;
// Adds an arg to the record.
void AddArg(base::StringView key, base::StringView value) {
#if PERFETTO_DCHECK_IS_ON()
// |key| and |value| should not contain any '\0' characters as it
// messes with the |args_buffer| which uses '\0' to deliniate different
// arguments.
for (char c : key) {
PERFETTO_DCHECK(c != '\0');
}
for (char c : value) {
PERFETTO_DCHECK(c != '\0');
}
#endif
size_t new_buffer_size = args_buffer_size + key.size() + value.size() + 2;
args_buffer = static_cast<char*>(realloc(args_buffer, new_buffer_size));
memcpy(&args_buffer[args_buffer_size], key.data(), key.size());
args_buffer[args_buffer_size + key.size()] = '\0';
memcpy(&args_buffer[args_buffer_size + key.size() + 1], value.data(),
value.size());
args_buffer[new_buffer_size - 1] = '\0';
args_buffer_size = static_cast<uint32_t>(new_buffer_size);
}
void AddArg(base::StringView key, const std::string& value) {
AddArg(key, base::StringView(value));
}
void AddArg(base::StringView key, const char* value) {
AddArg(key, base::StringView(value));
}
};
// Implementation of fixed-size ring buffer. The implementation of this
// class is modelled on the RingBuffer in metatrace.h of base but is different
// in a couple of ways:
// 1. This class is *not* thread safe.
// 2. The Record type stored in this class has the capability of storing
// extra, event-specific context. For example, when tracing SQL query
// execution, we store the query string.
// 3. The buffer is designed to be written continuously while meta-tracing
// is enabled and read one-shot at the end of execution.
class RingBuffer {
public:
static constexpr uint32_t kDefaultCapacity = 256 * 1024;
RingBuffer();
~RingBuffer() = default;
std::pair<uint64_t, Record*> AppendRecord(const char* event_name) {
PERFETTO_DCHECK_THREAD(thread_checker_);
PERFETTO_DCHECK(!is_reading_);
uint64_t idx = write_idx_++;
Record* record = At(idx);
record->timestamp_ns = TraceTimeNowNs();
record->duration_ns = 0;
record->event_name = event_name;
record->args_buffer_size = 0;
return std::make_pair(idx, record);
}
Record* At(uint64_t idx) { return &data_[idx % data_.size()]; }
void ReadAll(std::function<void(Record*)>);
static RingBuffer* GetInstance() {
static RingBuffer* rb = new RingBuffer();
return rb;
}
uint64_t IndexOf(Record* record) {
return static_cast<uint64_t>(std::distance(data_.data(), record));
}
// Returns whether the record at the |index| has been overwritten because
// of wraps of the ring buffer.
bool HasOverwritten(uint64_t index) {
return index + data_.size() < write_idx_;
}
// Request the ring buffer to be resized. Clears the existing buffer.
void Resize(size_t requested_capacity);
private:
bool is_reading_ = false;
uint64_t start_idx_ = 0;
uint64_t write_idx_ = 0;
std::vector<Record> data_;
PERFETTO_THREAD_CHECKER(thread_checker_)
};
class ScopedEvent {
public:
ScopedEvent() = default;
~ScopedEvent() {
if (PERFETTO_LIKELY(!record_))
return;
if (RingBuffer::GetInstance()->HasOverwritten(record_idx_))
return;
auto now = TraceTimeNowNs();
record_->duration_ns = now - record_->timestamp_ns;
}
ScopedEvent(ScopedEvent&& value) noexcept {
record_ = value.record_;
record_idx_ = value.record_idx_;
value.record_ = nullptr;
}
template <typename Fn = void(Record*)>
static ScopedEvent Create(
Category category,
const char* event_id,
Fn args_fn = [](Record*) {}) {
if (PERFETTO_LIKELY((category & g_enabled_categories) == 0))
return ScopedEvent();
ScopedEvent event;
std::tie(event.record_idx_, event.record_) =
RingBuffer::GetInstance()->AppendRecord(event_id);
args_fn(event.record_);
return event;
}
private:
ScopedEvent(const ScopedEvent&) = delete;
ScopedEvent& operator=(const ScopedEvent&) = delete;
ScopedEvent& operator=(ScopedEvent&& value) = delete;
Record* record_ = nullptr;
uint64_t record_idx_ = 0;
};
// Enables meta-tracing of trace-processor.
void Enable(MetatraceConfig config = {});
// Disables meta-tracing of trace-processor and reads all records.
void DisableAndReadBuffer(std::function<void(Record*)>);
// Boilerplate to derive a unique variable name for the event.
#define PERFETTO_TP_METATRACE_UID2(a, b) a##b
#define PERFETTO_TP_METATRACE_UID(x) PERFETTO_TP_METATRACE_UID2(metatrace_, x)
#define PERFETTO_TP_TRACE(...) \
auto PERFETTO_TP_METATRACE_UID(__COUNTER__) = \
::perfetto::trace_processor::metatrace::ScopedEvent::Create(__VA_ARGS__)
} // namespace metatrace
} // namespace trace_processor
} // namespace perfetto
#endif // SRC_TRACE_PROCESSOR_TP_METATRACE_H_