perfetto源码解析-基础框架(1)
perfetto是google开源的一套系统级别的性能收集与分析工具。
笔者试图在源码的层面分析它的工作原理。
源代码基于AOSP里的external/perfetto版本android-15.0.0_r3。
下图为Android平台上perfetto的主体框架,通过debug log也把主要的producer和data source列在其中
图中,traced是perfetto主要的常驻模块,它提供/dev/socket/traced_producer给其他性能数据生产者注册使用,
提供/dev/socket/traced_consumer供消费者使用,如图主要的消费者就是perfetto命令行,perfetto命令将性能数据记在文件中,由其他的外部工具获取使用
下面看traced启动做了什么 主要入口在 src/traced/service/service.cc
int PERFETTO_EXPORT_ENTRYPOINT ServiceMain(int argc, char** argv) {
enum LongOption {
OPT_VERSION = 1000,
OPT_SET_SOCKET_PERMISSIONS = 1001,
OPT_BACKGROUND,
OPT_ENABLE_RELAY_ENDPOINT
};
首先是命令行的解析,然后是创建service和task_runner, task_runner作为后续task的执行者处理各种异步事件
base::UnixTaskRunner task_runner;
std::unique_ptr<ServiceIPCHost> svc;
TracingService::InitOpts init_opts = {};
#if PERFETTO_BUILDFLAG(PERFETTO_ZLIB)
init_opts.compressor_fn = &ZlibCompressFn;
#endif
if (enable_relay_endpoint)
init_opts.enable_relay_endpoint = true;
svc = ServiceIPCHost::CreateInstance(&task_runner, init_opts);
下面通过进程空间的环境变量拿到producer和cosumer两个socket,这里的socket是在perfetto.rc定义,init里创建再通过环境变量传递给fork出来的service,这是android control socket的机制
const char* env_prod = getenv("ANDROID_SOCKET_traced_producer");
const char* env_cons = getenv("ANDROID_SOCKET_traced_consumer");
PERFETTO_CHECK((!env_prod && !env_cons) || (env_prod && env_cons));
bool started;
if (env_prod) {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
PERFETTO_CHECK(false);
#else
base::ScopedFile producer_fd(atoi(env_prod));
base::ScopedFile consumer_fd(atoi(env_cons));
started = svc->Start(std::move(producer_fd), std::move(consumer_fd));
#endif
然后看Start做了什么,在src/tracing/ipc/service/service_ipc_host_impl.cc
bool ServiceIPCHostImpl::Start(base::ScopedSocketHandle producer_socket_fd,
base::ScopedSocketHandle consumer_socket_fd) {
PERFETTO_CHECK(!svc_); // Check if already started.
// Initialize the IPC transport.
producer_ipc_ports_.emplace_back(
ipc::Host::CreateInstance(std::move(producer_socket_fd), task_runner_));
consumer_ipc_port_ =
ipc::Host::CreateInstance(std::move(consumer_socket_fd), task_runner_);
return DoStart();
}
这里可以看到基于两个socket创建了Host instance, 并且传入了task_runner_
再看host实现 src/ipc/host_impl.cc
std::unique_ptr<Host> Host::CreateInstance(base::ScopedSocketHandle socket_fd,
base::TaskRunner* task_runner) {
std::unique_ptr<HostImpl> host(
new HostImpl(std::move(socket_fd), task_runner));
if (!host->sock() || !host->sock()->is_listening())
return nullptr;
return std::unique_ptr<Host>(std::move(host));
}
继续跟踪HostImpl的构造
HostImpl::HostImpl(base::ScopedSocketHandle socket_fd,
base::TaskRunner* task_runner)
: task_runner_(task_runner), weak_ptr_factory_(this) {
PERFETTO_DCHECK_THREAD(thread_checker_);
sock_ = base::UnixSocket::Listen(std::move(socket_fd), this, task_runner_,
kHostSockFamily, base::SockType::kStream);
}
继续看Listen
std::unique_ptr<UnixSocket> UnixSocket::Listen(ScopedSocketHandle fd,
EventListener* event_listener,
TaskRunner* task_runner,
SockFamily sock_family,
SockType sock_type) {
return std::unique_ptr<UnixSocket>(new UnixSocket(
event_listener, task_runner, std::move(fd), State::kListening,
sock_family, sock_type, SockPeerCredMode::kDefault));
}
然后来到这里 src/base/unix_socket.cc
UnixSocket::UnixSocket(EventListener* event_listener,
TaskRunner* task_runner,
ScopedSocketHandle adopt_fd,
State adopt_state,
SockFamily sock_family,
SockType sock_type,
SockPeerCredMode peer_cred_mode)
: peer_cred_mode_(peer_cred_mode),
event_listener_(event_listener),
task_runner_(task_runner),
weak_ptr_factory_(this) {
state_ = State::kDisconnected;
if (adopt_state == State::kDisconnected) {
PERFETTO_DCHECK(!adopt_fd);
往下看会来到关键点,将socket通过AddFileDescriptorWatch传给task_runner_,并且注册了callback,熟悉poll或者epoll机制的大概能猜到task_runner_的运作机制了
WeakPtr<UnixSocket> weak_ptr = weak_ptr_factory_.GetWeakPtr();
task_runner_->AddFileDescriptorWatch(sock_raw_.watch_handle(), [weak_ptr] {
if (weak_ptr)
weak_ptr->OnEvent();
});
}
再看AddFileDescriptorWatch实现,主要是更新watch_tasks_。这里告一段落,watch_tasks_会在后续UnixTaskRunner Run的时候用起来
void UnixTaskRunner::AddFileDescriptorWatch(PlatformHandle fd,
std::function<void()> task) {
PERFETTO_DCHECK(PlatformHandleChecker::IsValid(fd));
{
std::lock_guard<std::mutex> lock(lock_);
PERFETTO_DCHECK(!watch_tasks_.count(fd));
WatchTask& watch_task = watch_tasks_[fd];
watch_task.callback = std::move(task);
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
watch_task.pending = false;
#else
watch_task.poll_fd_index = SIZE_MAX;
#endif
watch_tasks_changed_ = true;
}
WakeUp();
}
再回到bool ServiceIPCHostImpl::Start那里看DoStart
bool ServiceIPCHostImpl::DoStart() {
// Create and initialize the platform-independent tracing business logic.
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
std::unique_ptr<SharedMemory::Factory> shm_factory(
new SharedMemoryWindows::Factory());
#else
std::unique_ptr<SharedMemory::Factory> shm_factory(
new PosixSharedMemory::Factory());
#endif
svc_ = TracingService::CreateInstance(std::move(shm_factory), task_runner_,
init_opts_);
这里创建了TracingService,和SharedMemory工厂,这里暂且不讨论它们,往下看
下面会通过ExposeService添加producer和cosumer的service到services_里。services_是很有意思的东西,下面会细说
bool HostImpl::ExposeService(std::unique_ptr<Service> service) {
PERFETTO_DCHECK_THREAD(thread_checker_);
const std::string& service_name = service->GetDescriptor().service_name;
if (GetServiceByName(service_name)) {
PERFETTO_DLOG("Duplicate ExposeService(): %s", service_name.c_str());
return false;
}
service->use_shmem_emulation_ =
sock() && !base::SockShmemSupported(sock()->family());
ServiceID sid = ++last_service_id_;
ExposedService exposed_service(sid, service_name, std::move(service));
services_.emplace(sid, std::move(exposed_service));
return true;
}
到这里socket部分的初始化就结束了,返回到ServiceMain
下面看到builtin producers的注册,由注释能看出,这部分poducer是用于动态启动的,例如当需要heapprofd时,通过设置prop来拉起进程,
// Advertise builtin producers only on in-tree builds. These producers serve
// only to dynamically start heapprofd and other services via sysprops, but
// that can only ever happen in in-tree builds.
#if PERFETTO_BUILDFLAG(PERFETTO_ANDROID_BUILD)
BuiltinProducer builtin_producer(&task_runner, /*lazy_stop_delay_ms=*/30000);
builtin_producer.ConnectInProcess(svc->service());
#endif
接着来到 src/traced/service/builtin_producer.cc,这里用与注册的刚好是上面ServiceIPCHostImpl创建的TracingService
void BuiltinProducer::ConnectInProcess(TracingService* svc) {
#if PERFETTO_BUILDFLAG(PERFETTO_OS_WIN)
// TODO(primiano): ConnectProducer should take a base::PlatformProcessId not
// pid_t, as they are different on Windows. But that is a larger refactoring
// and not worth given this is the only use case where it clashes.
const pid_t cur_proc_id = 0;
#else
const pid_t cur_proc_id = base::GetProcessId();
#endif
endpoint_ = svc->ConnectProducer(
this, ClientIdentity(base::GetCurrentUserId(), cur_proc_id), "traced",
/*shared_memory_size_hint_bytes=*/16 * 1024, /*in_process=*/true,
TracingService::ProducerSMBScrapingMode::kDisabled,
/*shared_memory_page_size_hint_bytes=*/4096);
}
void BuiltinProducer::OnConnect() {
DataSourceDescriptor metatrace_dsd;
metatrace_dsd.set_name(MetatraceWriter::kDataSourceName);
metatrace_dsd.set_will_notify_on_stop(true);
endpoint_->RegisterDataSource(metatrace_dsd);
{
DataSourceDescriptor lazy_heapprofd_dsd;
lazy_heapprofd_dsd.set_name(kHeapprofdDataSourceName);
endpoint_->RegisterDataSource(lazy_heapprofd_dsd);
例如这部分代码,设置了traced.lazy.heapprofd,这部分代码是在consumer通过消息请求SetupDataSource时触发。
constexpr char kLazyHeapprofdPropertyName[] = "traced.lazy.heapprofd";
void BuiltinProducer::SetupDataSource(DataSourceInstanceID ds_id,
const DataSourceConfig& ds_config) {
if (ds_config.name() == kHeapprofdDataSourceName ||
ds_config.name() == kJavaHprofDataSourceName) {
SetAndroidProperty(kLazyHeapprofdPropertyName, "1");
lazy_heapprofd_.generation++;
lazy_heapprofd_.instance_ids.emplace(ds_id);
return;
}
而查看了heapprofd.rc,它刚好控制了heapprofd的启动
on property:traced.lazy.heapprofd=1
start heapprofd
接着再回到ServiceMain能看到task_runner的启动和log,
PERFETTO_ILOG("Started traced, listening on %s %s", GetProducerSocket(),
GetConsumerSocket());
task_runner.Run();
至此总结一下前面提到的主要任务包括初始化socket,添加需要设置prop拉起的producer
未完待续