blob: 4b1faf84e5fba99e8238df1fc48a17603a8095d3 [file]
/*
* Copyright 2025 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.
*/
#define LOG_TAG "ProtoLog"
#include "DataSource.h"
#include "ProtoLogConfig.h"
#include "ProtoLogTypes.h"
#include "perfetto/public/protos/trace/trace.pzc.h"
#include "protos/trace/android/protolog.pzc.h"
#include "protos/trace/profiling/profile_common.pzc.h"
#include <config/android/protolog_config.pzc.h>
#include <log/log.h>
#include <perfetto/public/abi/pb_decoder_abi.h>
#include <perfetto/public/data_source.h>
#include <perfetto/public/pb_utils.h>
#include <perfetto/public/producer.h>
#include <perfetto/public/protos/config/data_source_config.pzc.h>
#include <perfetto/public/protos/trace/interned_data/interned_data.pzc.h>
#include <perfetto/public/protos/trace/trace_packet.pzc.h>
#include <string.h>
#include <utils/CallStack.h>
#include <utils/String8.h>
#include <condition_variable>
#include <functional>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <thread>
#include <tuple>
#include <unordered_map>
#include <vector>
namespace {
constexpr uint64_t FNV_OFFSET_BASIS = 0xcbf29ce484222325;
constexpr uint64_t FNV_PRIME = 0x100000001b3;
uint64_t fnv1a_64(std::string_view str, uint64_t hash = FNV_OFFSET_BASIS) {
for (char c : str) {
hash ^= static_cast<uint8_t>(c);
hash *= FNV_PRIME;
}
return hash;
}
} // namespace
namespace android {
namespace protolog {
namespace datasource {
struct PerfettoDs gDataSource = PERFETTO_DS_INIT();
std::once_flag gInitializeFlag;
std::shared_mutex g_config_mutex;
std::unordered_map<PerfettoDsInstanceIndex, std::shared_ptr<const ProtoLogConfig>>
g_instance_configs;
static void* OnSetupCb(struct PerfettoDsImpl* ds, PerfettoDsInstanceIndex inst_id, void* ds_config,
size_t ds_config_size, void* user_arg, struct PerfettoDsOnSetupArgs* args) {
std::shared_ptr<ProtoLogConfig> instance_config = std::make_shared<ProtoLogConfig>();
if (!instance_config->parse(ds_config, ds_config_size)) {
ALOGE("Failed to parse config for instance %u", inst_id);
}
InstanceContext* context = new InstanceContext();
context->inst_id = inst_id;
{
std::lock_guard<std::shared_mutex> lock(g_config_mutex);
g_instance_configs[inst_id] = std::move(instance_config);
}
return context;
}
static void OnDestroyCb(struct PerfettoDsImpl*, void* user_arg, void* instance_state) {
if (!instance_state) return;
InstanceContext* context = static_cast<InstanceContext*>(instance_state);
PerfettoDsInstanceIndex inst_id = context->inst_id;
std::shared_ptr<const ProtoLogConfig> config_ptr = nullptr;
{
std::lock_guard<std::shared_mutex> lock(g_config_mutex);
auto it = g_instance_configs.find(inst_id);
if (it != g_instance_configs.end()) {
config_ptr = it->second;
g_instance_configs.erase(it);
} else {
ALOGE("Config not found for instance %u in on_destroy_cb!", inst_id);
}
}
delete context;
}
static void* OnCreateTlsCb(struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id,
struct PerfettoDsTracerImpl* tracer, void* user_arg) {
ProtoLogTlsState* tls_state = new ProtoLogTlsState();
std::shared_ptr<const ProtoLogConfig> config_ptr = nullptr;
{
std::shared_lock lock(g_config_mutex);
auto it = g_instance_configs.find(inst_id);
if (it != g_instance_configs.end()) {
config_ptr = it->second;
}
}
if (config_ptr != nullptr) {
tls_state->config = config_ptr;
} else {
ALOGE("Config not found for instance %u during on_create_tls_cb!", inst_id);
}
return tls_state;
}
static void OnDeleteTlsCb(void* ptr) {
delete static_cast<ProtoLogTlsState*>(ptr);
}
static void* OnCreateIncrCb(struct PerfettoDsImpl*, PerfettoDsInstanceIndex,
struct PerfettoDsTracerImpl*, void*) {
return new IncrementalState();
}
static void OnDeleteIncrCb(void* ptr) {
delete static_cast<IncrementalState*>(ptr);
}
void InitDataSource(uint32_t backends) {
ALOGD("Initializing ProtoLog DataSource");
std::call_once(gInitializeFlag, [backends] {
struct PerfettoProducerInitArgs args = PERFETTO_PRODUCER_INIT_ARGS_INIT();
args.backends = backends;
PerfettoProducerInit(args);
struct PerfettoDsParams params = PerfettoDsParamsDefault();
params.user_arg = nullptr;
params.on_setup_cb = OnSetupCb;
params.on_destroy_cb = OnDestroyCb;
params.on_create_tls_cb = OnCreateTlsCb;
params.on_delete_tls_cb = OnDeleteTlsCb;
params.on_create_incr_cb = OnCreateIncrCb;
params.on_delete_incr_cb = OnDeleteIncrCb;
PerfettoDsRegister(&gDataSource, NAME.cbegin(), params);
});
}
InternResult IncrementalState::internGroup(std::string_view group) {
uint64_t hash = fnv1a_64(group); // Use the same FNV-1a helper
auto [id, is_new] = group_intern_table.getOrEmplace(group, hash);
return {id, is_new};
}
InternResult IncrementalState::internMessage(ProtoLogLevel level, std::string_view group,
std::string_view format) {
uint64_t hash = fnv1a_64(group);
hash = fnv1a_64(format, hash);
hash ^= static_cast<uint8_t>(level);
hash *= FNV_PRIME;
auto key = std::make_tuple(level, group, format);
auto [id, is_new] = message_intern_table.getOrEmplace(key, hash);
return {id, is_new};
}
InternResult IncrementalState::internString(std::string_view str) {
auto [id, is_new] = string_intern_table.getOrEmplace(str, next_string_id);
if (is_new) {
next_string_id++;
}
return {id, is_new};
}
} // namespace datasource
} // namespace protolog
} // namespace android