perfetto是google开源的一套系统级别的性能收集与分析工具。

笔者试图在源码的层面分析它的工作原理。

源代码基于AOSP里的external/perfetto版本android-15.0.0_r3。

下图为Android平台上perfetto的主体框架,通过debug log也把主要的producer和data source列在其中 pic1 图中,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

未完待续