blob: 32b57f841c0925f6a3afd11e13007221084774d9 [file] [log] [blame]
/*
* Copyright (C) 2022 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.
*/
#include "perfetto/public/abi/data_source_abi.h"
#include <bitset>
#include "perfetto/base/thread_annotations.h"
#include "perfetto/tracing/buffer_exhausted_policy.h"
#include "perfetto/tracing/data_source.h"
#include "perfetto/tracing/internal/basic_types.h"
#include "protos/perfetto/common/data_source_descriptor.gen.h"
#include "protos/perfetto/config/data_source_config.gen.h"
#include "src/shared_lib/reset_for_testing.h"
#include "src/shared_lib/stream_writer.h"
namespace {
using ::perfetto::internal::DataSourceInstanceThreadLocalState;
using ::perfetto::internal::DataSourceThreadLocalState;
using ::perfetto::internal::DataSourceType;
thread_local DataSourceThreadLocalState*
g_tls_cache[perfetto::internal::kMaxDataSources];
} // namespace
// Implementation of a shared library data source type (there's one of these per
// type, not per instance).
//
// Returned to the C side when invoking PerfettoDsCreateImpl(). The C side only
// has an opaque pointer to this.
struct PerfettoDsImpl {
// Instance lifecycle callbacks.
PerfettoDsOnSetupCb on_setup_cb = nullptr;
PerfettoDsOnStartCb on_start_cb = nullptr;
PerfettoDsOnStopCb on_stop_cb = nullptr;
PerfettoDsOnDestroyCb on_destroy_cb = nullptr;
PerfettoDsOnFlushCb on_flush_cb = nullptr;
// These are called to create/delete custom thread-local instance state.
PerfettoDsOnCreateCustomState on_create_tls_cb = nullptr;
PerfettoDsOnDeleteCustomState on_delete_tls_cb = nullptr;
// These are called to create/delete custom thread-local instance incremental
// state.
PerfettoDsOnCreateCustomState on_create_incr_cb = nullptr;
PerfettoDsOnDeleteCustomState on_delete_incr_cb = nullptr;
// Passed to all the callbacks as the `user_arg` param.
void* cb_user_arg;
perfetto::BufferExhaustedPolicy buffer_exhausted_policy =
perfetto::BufferExhaustedPolicy::kDrop;
DataSourceType cpp_type;
std::atomic<bool> enabled{false};
std::mutex mu;
std::bitset<perfetto::internal::kMaxDataSourceInstances> enabled_instances
PERFETTO_GUARDED_BY(mu);
bool IsRegistered() {
return cpp_type.static_state()->index !=
perfetto::internal::kMaxDataSources;
}
};
namespace perfetto {
namespace shlib {
// These are only exposed to tests.
void ResetDataSourceTls() {
memset(g_tls_cache, 0, sizeof(g_tls_cache));
}
void DsImplDestroy(struct PerfettoDsImpl* ds_impl) {
delete ds_impl;
}
} // namespace shlib
} // namespace perfetto
namespace {
// Represents a global data source instance (there can be more than one of these
// for a single data source type).
class ShlibDataSource : public perfetto::DataSourceBase {
public:
explicit ShlibDataSource(PerfettoDsImpl* type) : type_(*type) {}
void OnSetup(const SetupArgs& args) override {
if (type_.on_setup_cb) {
std::vector<uint8_t> serialized_config = args.config->SerializeAsArray();
inst_ctx_ = type_.on_setup_cb(
&type_, args.internal_instance_index, serialized_config.data(),
serialized_config.size(), type_.cb_user_arg, nullptr);
}
std::lock_guard<std::mutex> lock(type_.mu);
const bool was_enabled = type_.enabled_instances.any();
type_.enabled_instances.set(args.internal_instance_index);
if (!was_enabled && type_.enabled_instances.any()) {
type_.enabled.store(true, std::memory_order_release);
}
}
void OnStart(const StartArgs& args) override {
if (type_.on_start_cb) {
type_.on_start_cb(&type_, args.internal_instance_index, type_.cb_user_arg,
inst_ctx_, nullptr);
}
}
void OnStop(const StopArgs& args) override {
if (type_.on_stop_cb) {
type_.on_stop_cb(
&type_, args.internal_instance_index, type_.cb_user_arg, inst_ctx_,
const_cast<PerfettoDsOnStopArgs*>(
reinterpret_cast<const PerfettoDsOnStopArgs*>(&args)));
}
std::lock_guard<std::mutex> lock(type_.mu);
type_.enabled_instances.reset(args.internal_instance_index);
if (type_.enabled_instances.none()) {
type_.enabled.store(false, std::memory_order_release);
}
}
~ShlibDataSource() override {
if (type_.on_destroy_cb) {
type_.on_destroy_cb(&type_, type_.cb_user_arg, inst_ctx_);
}
}
void OnFlush(const FlushArgs& args) override {
if (type_.on_flush_cb) {
type_.on_flush_cb(
&type_, args.internal_instance_index, type_.cb_user_arg, inst_ctx_,
const_cast<PerfettoDsOnFlushArgs*>(
reinterpret_cast<const PerfettoDsOnFlushArgs*>(&args)));
}
}
const PerfettoDsImpl& type() const { return type_; }
void* inst_ctx() const { return inst_ctx_; }
private:
PerfettoDsImpl& type_;
void* inst_ctx_ = nullptr;
};
struct DataSourceTraits {
static DataSourceThreadLocalState* GetDataSourceTLS(
perfetto::internal::DataSourceStaticState* static_state,
perfetto::internal::TracingTLS* root_tls) {
auto* ds_tls = &root_tls->data_sources_tls[static_state->index];
// ds_tls->static_state can be:
// * nullptr
// * equal to static_state
// * equal to the static state of a different data source, in tests (when
// ResetForTesting() has been used)
// In any case, there's no need to do anything, the caller will reinitialize
// static_state.
return ds_tls;
}
};
struct TracePointTraits {
using TracePointData = DataSourceType*;
static std::atomic<uint32_t>* GetActiveInstances(TracePointData s) {
return s->valid_instances();
}
};
DataSourceInstanceThreadLocalState::ObjectWithDeleter CreateShlibTls(
DataSourceInstanceThreadLocalState* tls_inst,
uint32_t inst_idx,
void* ctx) {
auto* ds_impl = reinterpret_cast<PerfettoDsImpl*>(ctx);
void* custom_state = ds_impl->on_create_tls_cb(
ds_impl, inst_idx, reinterpret_cast<PerfettoDsTracerImpl*>(tls_inst),
ds_impl->cb_user_arg);
return DataSourceInstanceThreadLocalState::ObjectWithDeleter(
custom_state, ds_impl->on_delete_tls_cb);
}
DataSourceInstanceThreadLocalState::ObjectWithDeleter
CreateShlibIncrementalState(DataSourceInstanceThreadLocalState* tls_inst,
uint32_t inst_idx,
void* ctx) {
auto* ds_impl = reinterpret_cast<PerfettoDsImpl*>(ctx);
void* custom_state = ds_impl->on_create_incr_cb(
ds_impl, inst_idx, reinterpret_cast<PerfettoDsTracerImpl*>(tls_inst),
ds_impl->cb_user_arg);
return DataSourceInstanceThreadLocalState::ObjectWithDeleter(
custom_state, ds_impl->on_delete_incr_cb);
}
} // namespace
// Exposed through data_source_abi.h
std::atomic<bool> perfetto_atomic_false{false};
struct PerfettoDsImpl* PerfettoDsImplCreate() {
return new PerfettoDsImpl();
}
void PerfettoDsSetOnSetupCallback(struct PerfettoDsImpl* ds_impl,
PerfettoDsOnSetupCb cb) {
PERFETTO_CHECK(!ds_impl->IsRegistered());
ds_impl->on_setup_cb = cb;
}
void PerfettoDsSetOnStartCallback(struct PerfettoDsImpl* ds_impl,
PerfettoDsOnStartCb cb) {
PERFETTO_CHECK(!ds_impl->IsRegistered());
ds_impl->on_start_cb = cb;
}
void PerfettoDsSetOnStopCallback(struct PerfettoDsImpl* ds_impl,
PerfettoDsOnStopCb cb) {
PERFETTO_CHECK(!ds_impl->IsRegistered());
ds_impl->on_stop_cb = cb;
}
void PerfettoDsSetOnDestroyCallback(struct PerfettoDsImpl* ds_impl,
PerfettoDsOnDestroyCb cb) {
PERFETTO_CHECK(!ds_impl->IsRegistered());
ds_impl->on_destroy_cb = cb;
}
void PerfettoDsSetOnFlushCallback(struct PerfettoDsImpl* ds_impl,
PerfettoDsOnFlushCb cb) {
PERFETTO_CHECK(!ds_impl->IsRegistered());
ds_impl->on_flush_cb = cb;
}
void PerfettoDsSetOnCreateTls(struct PerfettoDsImpl* ds_impl,
PerfettoDsOnCreateCustomState cb) {
PERFETTO_CHECK(!ds_impl->IsRegistered());
ds_impl->on_create_tls_cb = cb;
}
void PerfettoDsSetOnDeleteTls(struct PerfettoDsImpl* ds_impl,
PerfettoDsOnDeleteCustomState cb) {
PERFETTO_CHECK(!ds_impl->IsRegistered());
ds_impl->on_delete_tls_cb = cb;
}
void PerfettoDsSetOnCreateIncr(struct PerfettoDsImpl* ds_impl,
PerfettoDsOnCreateCustomState cb) {
PERFETTO_CHECK(!ds_impl->IsRegistered());
ds_impl->on_create_incr_cb = cb;
}
void PerfettoDsSetOnDeleteIncr(struct PerfettoDsImpl* ds_impl,
PerfettoDsOnDeleteCustomState cb) {
PERFETTO_CHECK(!ds_impl->IsRegistered());
ds_impl->on_delete_incr_cb = cb;
}
void PerfettoDsSetCbUserArg(struct PerfettoDsImpl* ds_impl, void* user_arg) {
PERFETTO_CHECK(!ds_impl->IsRegistered());
ds_impl->cb_user_arg = user_arg;
}
bool PerfettoDsSetBufferExhaustedPolicy(struct PerfettoDsImpl* ds_impl,
uint32_t policy) {
if (ds_impl->IsRegistered()) {
return false;
}
switch (policy) {
case PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_DROP:
ds_impl->buffer_exhausted_policy = perfetto::BufferExhaustedPolicy::kDrop;
return true;
case PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT:
ds_impl->buffer_exhausted_policy =
perfetto::BufferExhaustedPolicy::kStall;
return true;
}
return false;
}
bool PerfettoDsImplRegister(struct PerfettoDsImpl* ds_impl,
PERFETTO_ATOMIC(bool) * *enabled_ptr,
const void* descriptor,
size_t descriptor_size) {
std::unique_ptr<PerfettoDsImpl> data_source_type(ds_impl);
perfetto::DataSourceDescriptor dsd;
dsd.ParseFromArray(descriptor, descriptor_size);
auto factory = [ds_impl]() {
return std::unique_ptr<perfetto::DataSourceBase>(
new ShlibDataSource(ds_impl));
};
DataSourceType::CreateCustomTlsFn create_custom_tls_fn = nullptr;
DataSourceType::CreateIncrementalStateFn create_incremental_state_fn =
nullptr;
void* cb_ctx = nullptr;
if (data_source_type->on_create_incr_cb &&
data_source_type->on_delete_incr_cb) {
create_incremental_state_fn = CreateShlibIncrementalState;
cb_ctx = data_source_type.get();
}
if (data_source_type->on_create_tls_cb &&
data_source_type->on_delete_tls_cb) {
create_custom_tls_fn = CreateShlibTls;
cb_ctx = data_source_type.get();
}
perfetto::internal::DataSourceParams params;
params.supports_multiple_instances = true;
params.requires_callbacks_under_lock = false;
bool success = data_source_type->cpp_type.Register(
dsd, factory, params, data_source_type->buffer_exhausted_policy,
data_source_type->on_flush_cb == nullptr, create_custom_tls_fn,
create_incremental_state_fn, cb_ctx);
if (!success) {
return false;
}
*enabled_ptr = &data_source_type->enabled;
perfetto::base::ignore_result(data_source_type.release());
return true;
}
void PerfettoDsImplUpdateDescriptor(struct PerfettoDsImpl* ds_impl,
const void* descriptor,
size_t descriptor_size) {
perfetto::DataSourceDescriptor dsd;
dsd.ParseFromArray(descriptor, descriptor_size);
ds_impl->cpp_type.UpdateDescriptor(dsd);
}
PerfettoDsAsyncStopper* PerfettoDsOnStopArgsPostpone(
PerfettoDsOnStopArgs* args) {
auto* cb = new std::function<void()>();
*cb = reinterpret_cast<const ShlibDataSource::StopArgs*>(args)
->HandleStopAsynchronously();
return reinterpret_cast<PerfettoDsAsyncStopper*>(cb);
}
void PerfettoDsStopDone(PerfettoDsAsyncStopper* stopper) {
auto* cb = reinterpret_cast<std::function<void()>*>(stopper);
(*cb)();
delete cb;
}
PerfettoDsAsyncFlusher* PerfettoDsOnFlushArgsPostpone(
PerfettoDsOnFlushArgs* args) {
auto* cb = new std::function<void()>();
*cb = reinterpret_cast<const ShlibDataSource::FlushArgs*>(args)
->HandleFlushAsynchronously();
return reinterpret_cast<PerfettoDsAsyncFlusher*>(cb);
}
void PerfettoDsFlushDone(PerfettoDsAsyncFlusher* stopper) {
auto* cb = reinterpret_cast<std::function<void()>*>(stopper);
(*cb)();
delete cb;
}
void* PerfettoDsImplGetInstanceLocked(struct PerfettoDsImpl* ds_impl,
PerfettoDsInstanceIndex idx) {
auto* internal_state = ds_impl->cpp_type.static_state()->TryGet(idx);
if (!internal_state) {
return nullptr;
}
std::unique_lock<std::recursive_mutex> lock(internal_state->lock);
auto* data_source =
static_cast<ShlibDataSource*>(internal_state->data_source.get());
if (&data_source->type() != ds_impl) {
// The data source instance has been destroyed and recreated as a different
// type while we where tracing.
return nullptr;
}
void* inst_ctx = data_source->inst_ctx();
if (inst_ctx != nullptr) {
lock.release();
}
return inst_ctx;
}
void PerfettoDsImplReleaseInstanceLocked(struct PerfettoDsImpl* ds_impl,
PerfettoDsInstanceIndex idx) {
// The `valid_instances` bitmap might have changed since the lock has been
// taken, but the instance must still be alive (we were holding the lock on
// it).
auto* internal_state = ds_impl->cpp_type.static_state()->GetUnsafe(idx);
internal_state->lock.unlock();
}
void* PerfettoDsImplGetCustomTls(struct PerfettoDsImpl*,
struct PerfettoDsTracerImpl* tracer,
PerfettoDsInstanceIndex) {
auto* tls_inst =
reinterpret_cast<DataSourceInstanceThreadLocalState*>(tracer);
PERFETTO_DCHECK(tls_inst->data_source_custom_tls);
return tls_inst->data_source_custom_tls.get();
}
void* PerfettoDsImplGetIncrementalState(struct PerfettoDsImpl* ds_impl,
struct PerfettoDsTracerImpl* tracer,
PerfettoDsInstanceIndex idx) {
auto* tls_inst =
reinterpret_cast<DataSourceInstanceThreadLocalState*>(tracer);
return ds_impl->cpp_type.GetIncrementalState(tls_inst, idx);
}
struct PerfettoDsImplTracerIterator PerfettoDsImplTraceIterateBegin(
struct PerfettoDsImpl* ds_impl) {
DataSourceThreadLocalState** tls =
&g_tls_cache[ds_impl->cpp_type.static_state()->index];
struct PerfettoDsImplTracerIterator ret = {0, nullptr, nullptr};
uint32_t cached_instances =
ds_impl->cpp_type.valid_instances()->load(std::memory_order_relaxed);
if (!cached_instances) {
return ret;
}
bool res =
ds_impl->cpp_type.TracePrologue<DataSourceTraits, TracePointTraits>(
tls, &cached_instances, &ds_impl->cpp_type);
if (!res) {
return ret;
}
DataSourceType::InstancesIterator it =
ds_impl->cpp_type.BeginIteration<TracePointTraits>(cached_instances, *tls,
&ds_impl->cpp_type);
ret.inst_id = it.i;
(*tls)->root_tls->cached_instances = it.cached_instances;
ret.tracer = reinterpret_cast<struct PerfettoDsTracerImpl*>(it.instance);
if (!ret.tracer) {
ds_impl->cpp_type.TraceEpilogue(*tls);
}
ret.tls = reinterpret_cast<struct PerfettoDsTlsImpl*>(*tls);
return ret;
}
void PerfettoDsImplTraceIterateNext(
struct PerfettoDsImpl* ds_impl,
struct PerfettoDsImplTracerIterator* iterator) {
auto* tls = reinterpret_cast<DataSourceThreadLocalState*>(iterator->tls);
DataSourceType::InstancesIterator it;
it.i = iterator->inst_id;
it.cached_instances = tls->root_tls->cached_instances;
it.instance =
reinterpret_cast<DataSourceInstanceThreadLocalState*>(iterator->tracer);
ds_impl->cpp_type.NextIteration<TracePointTraits>(&it, tls,
&ds_impl->cpp_type);
iterator->inst_id = it.i;
tls->root_tls->cached_instances = it.cached_instances;
iterator->tracer =
reinterpret_cast<struct PerfettoDsTracerImpl*>(it.instance);
if (!iterator->tracer) {
ds_impl->cpp_type.TraceEpilogue(tls);
}
}
void PerfettoDsImplTraceIterateBreak(
struct PerfettoDsImpl* ds_impl,
struct PerfettoDsImplTracerIterator* iterator) {
auto* tls = reinterpret_cast<DataSourceThreadLocalState*>(iterator->tls);
ds_impl->cpp_type.TraceEpilogue(tls);
}
struct PerfettoStreamWriter PerfettoDsTracerImplPacketBegin(
struct PerfettoDsTracerImpl* tracer) {
auto* tls_inst =
reinterpret_cast<DataSourceInstanceThreadLocalState*>(tracer);
auto message_handle = tls_inst->trace_writer->NewTracePacket();
struct PerfettoStreamWriter ret;
protozero::ScatteredStreamWriter* sw = message_handle.TakeStreamWriter();
ret.impl = reinterpret_cast<PerfettoStreamWriterImpl*>(sw);
perfetto::UpdateStreamWriter(*sw, &ret);
return ret;
}
void PerfettoDsTracerImplPacketEnd(struct PerfettoDsTracerImpl* tracer,
struct PerfettoStreamWriter* w) {
auto* tls_inst =
reinterpret_cast<DataSourceInstanceThreadLocalState*>(tracer);
auto* sw = reinterpret_cast<protozero::ScatteredStreamWriter*>(w->impl);
sw->set_write_ptr(w->write_ptr);
tls_inst->trace_writer->FinishTracePacket();
}
void PerfettoDsTracerImplFlush(struct PerfettoDsTracerImpl* tracer,
PerfettoDsTracerOnFlushCb cb,
void* user_arg) {
auto* tls_inst =
reinterpret_cast<DataSourceInstanceThreadLocalState*>(tracer);
std::function<void()> fn;
if (cb != nullptr) {
fn = [user_arg, cb]() { cb(user_arg); };
}
tls_inst->trace_writer->Flush(fn);
}