searchusermenu
  • 发布文章
  • 消息中心
点赞
收藏
评论
分享
原创

iorap原理介绍

2023-03-27 01:40:00
393
0

 

概述

在Android 11中,引入了IORap ,这是一个新功能,可大大缩短应用程序的启动时间。

 

IORap 通过预测将需要哪些I / O并提前进行来减少应用程序启动时间。许多应用程序在启动时需要访问I/O.

很多时间会因为阻塞I / O而导致应用程序启动慢。预取数据之后,应用程序几乎可以从pagecache 中立即访问该数据,

从而大大减少了应用程序启动延迟。

框架

代码路径

frameworks/base/startop/iorap

一个java层服务,跟iorapd进行通信;主要负责应用状态的监听工作,状态发生变化或者job 触发时,通知iorapd 进程进行核心处理。

监听包括:ODEX 发生更新,activity launch 进度发生变化,job service 用以触发iorapd compile tracing files。这里重点说一下job service,

android 中job service 为后台普通service,为了完成一些优先级不高的任务,而这里的compile task也是使用job service,而且是在charging

状态下进行,且24小时内只做一次任务。

 

system/iorap/

//iorapd.rc
用于启动/system/bin/iorapd进程
 
//system/iorap/src/perfetto
主要是收集perfetto trace,生成perfetto_trace.pb 
 
//system/iorap/src/compiler
主要是生成预取列表。将perfetto_trace.pb转换成comlplied_trace.pb
 
//system/iorap/src/db
使用sqlite3实现的一个数据库, 数据保存在/data/misc/iorapd
 
//system/iorap/src/inode2filename
将inode转为真正filename的功能
 
//system/iorap/src/prefetcher
主要是实现预读取功能
 
//system/iorap/src/manager
iorapd主要实现逻辑,事件驱动的一个模型,跟java层服务进行互动,并且驱动各个模块进行工作。

 

工作流程

1、收集perfetto  trace。当应用第一次启动时,通过perfetto 产生trace。该trace记录了内核pagecache页面的删除/添加(来自ftrace )。

在应用程序的前几次冷运行中,将进行perfetto跟踪以获取pagecache丢失事件。trace产生后会通知perfetto 的producer 进行记录,

并保存在perfetto_trace.pb 文件中。研究表明,启动时进行perfetto跟踪的开销可以忽略不计。

2、生成预取列表。基于上面的 perfetto trace,IORap 会在设备空闲时, IORap 会根据 perfetto trace 分析 mm_pagemap 事件,并将结果

(inode、偏移量、长度) 转换为 (名称、偏移量、长度),然后将数据存储在预取列表中,预取列表是一个 protobuf 文件。

3、预读取。生成预取列表之后,在新的一次app 冷启动时,app launch的作为触发条件,会通知iorapd 进行预取工作,将之前解析好的

protobuf 传入prefetcherd 进程,并通过系统调用(例如posix_fadvise)进行内核端的预取工作

4、更新预取列表。预取列表不会永久存在,当APP更新后,APP的数据会和之前数据有差异,这会使预取列表过时,过时的预取列表将被删除,

并且开始新一轮的 perfetto trace。

工作流程详解

服务启动

IorapForwardingService服务启动

frameworks/base/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java

IorapForwardingService是由system server中启动的,主要是做了三件事

  • 注册AppLaunchObserver
  • 注册DexOptPackagesUpdated
  • 启动IorapdJobService

服务启动使能

iorap_perfetto_enable和iorap_readahead_enable需要设置为true

protected boolean isIorapEnabled() {
        // These two mendel flags should match those in iorapd native process
        // system/iorapd/src/common/property.h
        boolean isTracingEnabled =
            getMendelFlag("iorap_perfetto_enable""iorapd.perfetto.enable"false);
        boolean isReadAheadEnabled =
            getMendelFlag("iorap_readahead_enable""iorapd.readahead.enable"false);
        // Same as the property in iorapd.rc -- disabling this will mean the 'iorapd' binder process
        // never comes up, so all binder connections will fail indefinitely.
        return IS_ENABLED && (isTracingEnabled || isReadAheadEnabled);
    }

 

onStart

配置相关信息和连接底层的iorap服务

@Override
public void onStart() {
    ...
    retryConnectToRemoteAndConfigure(/*attempts*/0);

retryConnectToRemoteAndConfigure-->connectToRemoteAndConfigure→connectToRemoteAndConfigureLocked

  • 连接底层iorap服务
  • 注册监听AppLaunchObserver、DexOptPackagesUpdated。后面详细介绍这两个
    private boolean connectToRemoteAndConfigureLocked() {
        ...
        // Connect to the native binder service.
        mIorapRemote = provideIorapRemote();
        ...
        invokeRemote(mIorapRemote,
            (IIorap remote) -> remote.setTaskListener(new RemoteTaskListener()) );
        registerInProcessListenersLocked();
        ...
    }
    private void registerInProcessListenersLocked() {
        ...
        ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
                provideLaunchObserverRegistry();
        launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver);
        launchObserverRegistry.registerLaunchObserver(mEventSequenceValidator);
        BackgroundDexOptService.addPackagesUpdatedListener(mDexOptPackagesUpdated);
        ...
    }

 

onBootPhase

主要启动IorapdJobService

@Override
public void onBootPhase(int phase) {
    ...
    mJobService = new IorapdJobService(getContext());
    ...
}      

启动iorapd的jobserver服务,并且设置了启动的条件。原生代码中是设置在charging状态下进行,且24小时内只做一次任务。

public IorapdJobService(Context context) {
    ...
    IORAPD_COMPONENT_NAME = new ComponentName(context, IorapdJobServiceProxy.class);
 
    JobInfo.Builder builder = new JobInfo.Builder(JOB_ID_IORAPD, IORAPD_COMPONENT_NAME);
    builder.setPeriodic(JOB_INTERVAL_MS);
    builder.setPrefetch(true);
    builder.setRequiresCharging(true);
    builder.setRequiresDeviceIdle(true);
   builder.setRequiresStorageNotLow(true);
   IORAPD_JOB_INFO = builder.build();
   JobScheduler js = context.getSystemService(JobScheduler.class);
   js.schedule(IORAPD_JOB_INFO);
   ...
}   

执行jobserver任务:onStartJob→runIdleCompilationAsync

调用iorapd中的onJobScheduledEvent 进complize apk,把perfetto_trace.pb 编译生成complized_trace.pb列表

iorapd服务启动

system/iorap/iorapd.rc

# Start iorapd when either prefetching or tracing is enabled.
on property:persist.device_config.runtime_native_boot.iorap_perfetto_enable=true && property:ro.iorapd.enable=true
     start iorapd
  
on property:persist.device_config.runtime_native_boot.iorap_readahead_enable=true && property:ro.iorapd.enable=true
     start iorapd

需要设置ro.iorapd.enable、persist.device_config.runtime_native_boot.iorap_perfetto_enable或者

persist.device_config.runtime_native_boot.iorap_readahead_enable为true。

收集Trace和预读取

frameworks/base/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java

上面服务启动中,已经介绍,服务启动时注册AppLaunchObserver,监听activity的启动。AppLaunchObserver定义了下面接口,

主要是调用了iorapd服务中对应接口实现功能

onIntentStarted--->iorapd::onAppLaunchEvent(AppLaunchEvent.IntentStarted)
onIntentFailed--->iorapd::onAppLaunchEvent(AppLaunchEvent.IntentFailed)
onActivityLaunched--->iorapd::onAppLaunchEvent(AppLaunchEvent.ActivityLaunched)
onActivityLaunchCancelled--->iorapd::onAppLaunchEvent(AppLaunchEvent.ActivityLaunchCancelled)
onActivityLaunchFinished--->iorapd::onAppLaunchEvent(AppLaunchEvent.ActivityLaunchFinished)

 

system/iorap/src/manager/event_manager.cc

OnAppLaunchEvent
bool EventManager::OnAppLaunchEvent(RequestId request_id,
                                    const AppLaunchEvent& event) {
  return impl_->OnAppLaunchEvent(request_id, event);
}
bool OnAppLaunchEvent(RequestId request_id,
                        const AppLaunchEvent& event) {
    ...
    AppLaunchEvent overwrite_event{};
    AppLaunchEventDefender::Result result =
        app_launch_event_defender_.OnAppLaunchEvent(request_id, event, /*out*/&overwrite_event);
    ...
}
struct AppLaunchEventDefender {
    ...
  Result OnAppLaunchEvent(binder::RequestId request_id,
                          const binder::AppLaunchEvent& event,
                          binder::AppLaunchEvent* overwrite) {
    ...
    switch (last_event_type_) {
      case Type::kUninitialized:
      case Type::kIntentFailed:
      case Type::kActivityLaunchCancelled:
      case Type::kReportFullyDrawn: {
        ...
      }
      case Type::kIntentStarted: {
        ...
      }
      case Type::kActivityLaunched: {
        ...
      }
      case Type::kActivityLaunchFinished: {
        ...
      }
    }
  }
};

具体执行的任务,在OnNewEvent中执行

void OnNewEvent(const AppLaunchEvent& event) {
    ...
    switch (event.type) {
      case Type::kIntentStarted:
        ...
      case Type::kIntentFailed:
        ...
      case Type::kActivityLaunched: {
        ...
        AppComponentName component_name = AppComponentName::FromString(title);
        component_name = component_name.Canonicalize();
        component_name_ = component_name;
       ...
        AppLaunchEvent::Temperature temperature = event.temperature;
        temperature_ = temperature;
        //判断是否是冷启动,冷启动才需要进行预读取
        if (temperature != AppLaunchEvent::Temperature::kCold) {
          LOG(INFO) << "AppLaunchEventState#OnNewEvent don't trace due to non-cold temperature";
        else if (!IsTracing() && !IsReadAhead()) {  // and the temperature is Cold.
          //StartReadAhead进行预读取
          if (allowed_readahead_ && !IsReadAhead()) {
            StartReadAhead(sequence_id_, component_name);
          }
          //进行perfetto trace
          if (allowed_tracing_ && !IsTracing() && !IsReadAhead()) {
            rx_lifetime_ = StartTracing(std::move(component_name));
          }
        else {
         ...
        }
        break;
      }
      case Type::kActivityLaunchFinished:
        ...
        FinishReadAhead();
        break;
      case Type::kActivityLaunchCancelled:
        ...
        AbortTrace();
        AbortReadAhead();
        break;
      case Type::kReportFullyDrawn:
          ...
      default:
 
    }
}

收集Trace

StartTracing

  • 调用perfetto/rx_producer.cc 中的CreateTraceStream 进行trace
  • 创建保存trace文件的目录,目录在/data/misc/iorapd/,比如/data/misc/iorapd/com.ctg.itrdc.file/1000002/dev.dworks.apps.anexplorer.DocumentsActivity/raw_traces
  • 把读取到的trace写入到perfetto_trace.pb文件中
std::optional<rxcpp::composite_subscription> StartTracing(
      AppComponentName component_name) {
    ...
    auto /*observable<PerfettoTraceProto>*/ trace_proto_stream =
        perfetto_factory_->CreateTraceStream(perfetto_commands);
 
          db::PerfettoTraceFileModel file_model =
            db::PerfettoTraceFileModel::CalculateNewestFilePath(versioned_component_name);
 
          std::string file_path = file_model.FilePath();
 
          ScopedFormatTrace atrace_write_to_file(ATRACE_TAG_ACTIVITY_MANAGER,
                                                 "Perfetto Write Trace To File %s",
                                                 file_path.c_str());
            //创建文件夹
          if (!file_model.MkdirWithParents()) {
            LOG(ERROR) << "Cannot save TraceBuffer; failed to mkdirs " << file_path;
            return;
          }
            //把trace文件写入到文件中
          if (!trace_proto.WriteFullyToFile(file_path)) {
            LOG(ERROR) << "Failed to save TraceBuffer to " << file_path;
          else {
            LOG(INFO) << "Perfetto TraceBuffer saved to file: " << file_path;
 
            ScopedFormatTrace atrace_update_raw_traces_table(
                ATRACE_TAG_ACTIVITY_MANAGER,
                "update raw_traces table history_id = %d",
                history_id);
            db::DbHandle db{db::SchemaModel::GetSingleton()};
            std::optional<db::RawTraceModel> raw_trace =
                db::RawTraceModel::Insert(db, history_id, file_path);
                 
          }
        },
         
    is_tracing_ = true;
 
    return lifetime;
  }/

system/iorap/src/perfetto/rx_producer.cc

CreateTraceStream→CreatePerfettoStream

主要调用perfetto/perfetto_consumer.cc中的StartTracing、ReadTrace等接口

static auto/*observable<PerfettoTraceProto>*/
    CreatePerfettoStream(rxcpp::observable<PerfettoStreamCommand> input,
                         std::shared_ptr<PerfettoConsumer> perfetto_consumer,
                         const ::perfetto::protos::TraceConfig& trace_config) {
        // XX: should I also take a scheduler for input here???
 
  auto /*observable<PerfettoStateChange>*/ perfetto_states =
    CreatePerfettoStateStream(trace_config, perfetto_consumer);
    ...
  return input
    .flat_map(
         [](std::tuple<PerfettoStreamCommand, PerfettoStateChange> p) {
           auto& [command, state_change] = p;
           LOG(INFO) << "CreatePerfettoStream#combine("
                        << command << "," << state_change << ")";
           if (command == PerfettoStreamCommand::kShutdown) {
             state_change.GetConsumer()->Destroy(state_change.GetHandle());
           else if (command == PerfettoStreamCommand::kStartTracing
                          && state_change.state == State::kConfigured) {
             state_change.GetConsumer()->StartTracing(state_change.GetHandle());
           else if (command == PerfettoStreamCommand::kStopTracing &&
                          state_change.state == State::kTraceEnded) {
             ::perfetto::consumer::TraceBuffer trace_buffer =
                 state_change.GetConsumer()->ReadTrace(state_change.GetHandle());
             PerfettoTraceProto wire_proto{trace_buffer.begin, trace_buffer.size};
             return rxcpp::observable<>::just(std::move(wire_proto)).as_dynamic();
           }
           return rxcpp::observable<>::empty<PerfettoTraceProto>().as_dynamic();
         }
    );
}

预读取

StartReadAhead

  • 获取compile生成的预取列表。是通过包名和版本号读取,路径比如:/data/misc/iorapd/com.ctg.itrdc.file/1000002/dev.dworks.apps.anexplorer.DocumentsActivity/compiled_traces/compiled_trace.pb
  • 执行预读取。调用/prefetcher/read_ahead.cc中 read_ahead_→BeginTask进行预读取
  void StartReadAhead(size_t id, const AppComponentName& component_name) {
    ...
    std::optional<std::string> file_path = GetCompiledTrace(component_name);
    if (!file_path) {
      LOG(INFO) << "Cannot find a compiled trace.";
      return;
    }
 
    prefetcher::TaskId task{id, *file_path};
    read_ahead_->BeginTask(task);
    // TODO: non-void return signature?
 
    read_ahead_task_ = std::move(task);
  }

生成预读取列表

Job server的启动在服务启动一节,已经介绍,最终调用到manager/event_manager.cc中的OnJobScheduledEvent

  bool OnJobScheduledEvent(RequestId request_id,
                           const JobScheduledEvent& event) {
     ...
    job_scheduled_event_subject_.OnNext(std::move(request_id), event);
    return true;  // No errors.
  }

Job初始化时,初始化函数InitializeRxGraphForJobScheduledEvents,设置了job处理的任务,最终调用StartMaintenance→CompileSingleAppOnDevice

system/iorap/src/maintenance/controller.cc

bool CompileSingleAppOnDevice(const db::DbHandle& db,
                              const ControllerParameters& params,
                              const std::string& package_name) {
  std::vector<db::PackageModel> packages = db::PackageModel::SelectByName(db, package_name.c_str());
  bool ret = true;
  for (db::PackageModel package : packages) {
    if (!CompilePackage(db, package.name, package.version, params)) {
      ret = false;
    }
  }
  return ret;
}

CompilePackage通过app包名和版本号,查找包db所在路径。然后调用CompileActivity compile 所有activity 

// Compiled the perfetto traces for activities in an package.
bool CompilePackage(const db::DbHandle& db,
                    const std::string& package_name,
                    int version,
                    const ControllerParameters& params) {
  ...                      
  std::optional<db::PackageModel> package =
        db::PackageModel::SelectByNameAndVersion(db, package_name.c_str(), version);
   ...     
  std::vector<db::ActivityModel> activities =
      db::ActivityModel::SelectByPackageId(db, package->id);
 
  bool ret = true;
  for (db::ActivityModel activity : activities) {
    if (!CompileActivity(db, package->id, package->name, activity.name, version, params)) {
      ret = false;
    }
  }
  return ret;
}

 

  • 如果complized_trace.pb文件存在,则直接返回。complized_trace.pb路径在/data/misc/iorapd/com.ctg.itrdc.file/1000002/dev.dworks.apps.anexplorer.DocumentsActivity/compiled_traces/compiled_trace.pb
  • 如果complized_trace.pb文件不存在,则通过StartViaFork 启动iorap.cmd.compiler进程。
// Compiled the perfetto traces for an activity.
bool CompileActivity { 
  db::CompiledTraceFileModel output_file =
      CalculateNewestFilePath(package_name, activity_name, version);
  std::string file_path = output_file.FilePath();
  //complized_trace.pb文件如果存在,则直接返回
  if (!params.recompile) {
    if (std::filesystem::exists(file_path)) {
       ...  
      db::VersionedComponentName vcn{package_name, activity_name, version};
      std::optional<db::PrefetchFileModel> prefetch_file =
          db::PrefetchFileModel::SelectByVersionedComponentName(db, vcn);
      if (prefetch_file) {
        return true;
      else {
         ...
      }
    }
  }
 
  std::optional<db::ActivityModel> activity =
      db::ActivityModel::SelectByNameAndPackageId(db, activity_name.c_str(), package_id);
  ...    
  int activity_id = activity->id;
 
  std::vector<db::AppLaunchHistoryModel> histories =
      db::AppLaunchHistoryModel::SelectActivityHistoryForCompile(db, activity_id);
 
  {
    std::vector<compiler::CompilationInput> perfetto_traces =
        GetPerfettoTraceInfo(db, histories);
    ...
    ScopedFormatTrace atrace_compile_fork(ATRACE_TAG_PACKAGE_MANAGER,
                                          "Fork+exec iorap.cmd.compiler",
                                          activity_name.c_str());
    if (!StartViaFork(compiler_params)) {
       ...
    }
  }
  ...

system/iorap/src/compiler

iorap.cmd.compiler进程的main函数,主要是调用PerformCompilation函数

  • ApplyBlacklistToPageCacheEvents中调用iorap.inode2filename中的FindFilenamesFromInodes通过inode 查找文件名。
  • serialize::ProtobufIO::WriteFully把 (inode、偏移量、长度) 转换为 (名称、偏移量、长度),然后将数据存储在预取列表 protobuf 文件中。
bool PerformCompilation(std::vector<CompilationInput> perfetto_traces,
                        std::string output_file_name,
                        bool output_proto,
                        std::optional<std::string> blacklist_filter,
                        inode2filename::InodeResolverDependencies dependencies) {
  auto trace_protos = ReadPerfettoTraceProtos(std::move(perfetto_traces));
  auto resolved_events = ResolvePageCacheEntriesFromProtos(std::move(trace_protos),
                                                           std::move(dependencies));
  auto filtered_events =
      ApplyBlacklistToPageCacheEvents(std::move(resolved_events), blacklist_filter);
  auto compiled_events = CompilePageCacheEvents(std::move(filtered_events));
  ...
   if (output_proto) {
    LOG(INFO) << "compiler: WriteFully to begin into " << output_file_name;
    ::google::protobuf::MessageLite& message = *trace_file_proto.get();
    if (auto res = serialize::ProtobufIO::WriteFully(message, output_file_name); !res) {
      errno = res.error();
      PLOG(ERROR) << "compiler: Failed to write protobuf to file: " << output_file_name;
      return false;
    else {
      LOG(INFO) << "compiler: Wrote protobuf " << output_file_name;
    }
  }

更新预取列表

startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java

服务启动时注册了DexOptPackagesUpdated,DexOptPackagesUpdated会监听应用包的更新

    private class DexOptPackagesUpdated implements BackgroundDexOptService.PackagesUpdatedListener {
        @Override
        public void onPackagesUpdated(ArraySet<String> updatedPackages) {
            String[] updated = updatedPackages.toArray(new String[0]);
            for (String packageName : updated) {
                Log.d(TAG, "onPackagesUpdated: " + packageName);
                invokeRemote(mIorapRemote,
                    (IIorap remote) ->
                        remote.onDexOptEvent(RequestId.nextValueForSequence(),
                                DexOptEvent.createPackageUpdate(packageName))
                );
            }
        }
    }

最终调用到event_manager.ccp中的OnDexOptEvent

  bool OnDexOptEvent(RequestId request_id,
                     const DexOptEvent& event) {
    ...
    return PurgePackage(event.package_name);
  }

PurgePackage调用db中的CleanUpFilesForPackage清除掉过时的db数据

  bool PurgePackage(const std::string& package_name) {
    db::DbHandle db{db::SchemaModel::GetSingleton()};
    db::CleanUpFilesForPackage(db, package_name);
    LOG(INFO) << "PurgePackage: " << package_name;
    return true;
  }

iorap服务监控包更新

system/iorap/src/binder/package_change_observer.cc

android::binder::Status PackageChangeObserver::onPackageChanged(
  const android::content::pm::PackageChangeEvent& event) {
  LOG(INFO) << "Received PackageChangeObserver::onPackageChanged";
  if (event_manager_->OnPackageChanged(event)) {
    return android::binder::Status::ok();
  else {
    return android::binder::Status::fromStatusT(android::BAD_VALUE);
  }
}
 
 
  bool OnPackageChanged(const android::content::pm::PackageChangeEvent& event) {
    LOG(INFO) << "Received " << event;
    if (event.isDeleted) {
      return true;
    }
    // Update the version map.
    if (version_map_->Update(event.packageName, event.version)) {
      return true;
    }
    db::DbHandle db{db::SchemaModel::GetSingleton()};
    db::CleanUpFilesForPackage(db, event.packageName, event.version);
    return true;
  }

 

0条评论
0 / 1000
黄****养
3文章数
0粉丝数
黄****养
3 文章 | 0 粉丝
黄****养
3文章数
0粉丝数
黄****养
3 文章 | 0 粉丝
原创

iorap原理介绍

2023-03-27 01:40:00
393
0

 

概述

在Android 11中,引入了IORap ,这是一个新功能,可大大缩短应用程序的启动时间。

 

IORap 通过预测将需要哪些I / O并提前进行来减少应用程序启动时间。许多应用程序在启动时需要访问I/O.

很多时间会因为阻塞I / O而导致应用程序启动慢。预取数据之后,应用程序几乎可以从pagecache 中立即访问该数据,

从而大大减少了应用程序启动延迟。

框架

代码路径

frameworks/base/startop/iorap

一个java层服务,跟iorapd进行通信;主要负责应用状态的监听工作,状态发生变化或者job 触发时,通知iorapd 进程进行核心处理。

监听包括:ODEX 发生更新,activity launch 进度发生变化,job service 用以触发iorapd compile tracing files。这里重点说一下job service,

android 中job service 为后台普通service,为了完成一些优先级不高的任务,而这里的compile task也是使用job service,而且是在charging

状态下进行,且24小时内只做一次任务。

 

system/iorap/

//iorapd.rc
用于启动/system/bin/iorapd进程
 
//system/iorap/src/perfetto
主要是收集perfetto trace,生成perfetto_trace.pb 
 
//system/iorap/src/compiler
主要是生成预取列表。将perfetto_trace.pb转换成comlplied_trace.pb
 
//system/iorap/src/db
使用sqlite3实现的一个数据库, 数据保存在/data/misc/iorapd
 
//system/iorap/src/inode2filename
将inode转为真正filename的功能
 
//system/iorap/src/prefetcher
主要是实现预读取功能
 
//system/iorap/src/manager
iorapd主要实现逻辑,事件驱动的一个模型,跟java层服务进行互动,并且驱动各个模块进行工作。

 

工作流程

1、收集perfetto  trace。当应用第一次启动时,通过perfetto 产生trace。该trace记录了内核pagecache页面的删除/添加(来自ftrace )。

在应用程序的前几次冷运行中,将进行perfetto跟踪以获取pagecache丢失事件。trace产生后会通知perfetto 的producer 进行记录,

并保存在perfetto_trace.pb 文件中。研究表明,启动时进行perfetto跟踪的开销可以忽略不计。

2、生成预取列表。基于上面的 perfetto trace,IORap 会在设备空闲时, IORap 会根据 perfetto trace 分析 mm_pagemap 事件,并将结果

(inode、偏移量、长度) 转换为 (名称、偏移量、长度),然后将数据存储在预取列表中,预取列表是一个 protobuf 文件。

3、预读取。生成预取列表之后,在新的一次app 冷启动时,app launch的作为触发条件,会通知iorapd 进行预取工作,将之前解析好的

protobuf 传入prefetcherd 进程,并通过系统调用(例如posix_fadvise)进行内核端的预取工作

4、更新预取列表。预取列表不会永久存在,当APP更新后,APP的数据会和之前数据有差异,这会使预取列表过时,过时的预取列表将被删除,

并且开始新一轮的 perfetto trace。

工作流程详解

服务启动

IorapForwardingService服务启动

frameworks/base/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java

IorapForwardingService是由system server中启动的,主要是做了三件事

  • 注册AppLaunchObserver
  • 注册DexOptPackagesUpdated
  • 启动IorapdJobService

服务启动使能

iorap_perfetto_enable和iorap_readahead_enable需要设置为true

protected boolean isIorapEnabled() {
        // These two mendel flags should match those in iorapd native process
        // system/iorapd/src/common/property.h
        boolean isTracingEnabled =
            getMendelFlag("iorap_perfetto_enable""iorapd.perfetto.enable"false);
        boolean isReadAheadEnabled =
            getMendelFlag("iorap_readahead_enable""iorapd.readahead.enable"false);
        // Same as the property in iorapd.rc -- disabling this will mean the 'iorapd' binder process
        // never comes up, so all binder connections will fail indefinitely.
        return IS_ENABLED && (isTracingEnabled || isReadAheadEnabled);
    }

 

onStart

配置相关信息和连接底层的iorap服务

@Override
public void onStart() {
    ...
    retryConnectToRemoteAndConfigure(/*attempts*/0);

retryConnectToRemoteAndConfigure-->connectToRemoteAndConfigure→connectToRemoteAndConfigureLocked

  • 连接底层iorap服务
  • 注册监听AppLaunchObserver、DexOptPackagesUpdated。后面详细介绍这两个
    private boolean connectToRemoteAndConfigureLocked() {
        ...
        // Connect to the native binder service.
        mIorapRemote = provideIorapRemote();
        ...
        invokeRemote(mIorapRemote,
            (IIorap remote) -> remote.setTaskListener(new RemoteTaskListener()) );
        registerInProcessListenersLocked();
        ...
    }
    private void registerInProcessListenersLocked() {
        ...
        ActivityMetricsLaunchObserverRegistry launchObserverRegistry =
                provideLaunchObserverRegistry();
        launchObserverRegistry.registerLaunchObserver(mAppLaunchObserver);
        launchObserverRegistry.registerLaunchObserver(mEventSequenceValidator);
        BackgroundDexOptService.addPackagesUpdatedListener(mDexOptPackagesUpdated);
        ...
    }

 

onBootPhase

主要启动IorapdJobService

@Override
public void onBootPhase(int phase) {
    ...
    mJobService = new IorapdJobService(getContext());
    ...
}      

启动iorapd的jobserver服务,并且设置了启动的条件。原生代码中是设置在charging状态下进行,且24小时内只做一次任务。

public IorapdJobService(Context context) {
    ...
    IORAPD_COMPONENT_NAME = new ComponentName(context, IorapdJobServiceProxy.class);
 
    JobInfo.Builder builder = new JobInfo.Builder(JOB_ID_IORAPD, IORAPD_COMPONENT_NAME);
    builder.setPeriodic(JOB_INTERVAL_MS);
    builder.setPrefetch(true);
    builder.setRequiresCharging(true);
    builder.setRequiresDeviceIdle(true);
   builder.setRequiresStorageNotLow(true);
   IORAPD_JOB_INFO = builder.build();
   JobScheduler js = context.getSystemService(JobScheduler.class);
   js.schedule(IORAPD_JOB_INFO);
   ...
}   

执行jobserver任务:onStartJob→runIdleCompilationAsync

调用iorapd中的onJobScheduledEvent 进complize apk,把perfetto_trace.pb 编译生成complized_trace.pb列表

iorapd服务启动

system/iorap/iorapd.rc

# Start iorapd when either prefetching or tracing is enabled.
on property:persist.device_config.runtime_native_boot.iorap_perfetto_enable=true && property:ro.iorapd.enable=true
     start iorapd
  
on property:persist.device_config.runtime_native_boot.iorap_readahead_enable=true && property:ro.iorapd.enable=true
     start iorapd

需要设置ro.iorapd.enable、persist.device_config.runtime_native_boot.iorap_perfetto_enable或者

persist.device_config.runtime_native_boot.iorap_readahead_enable为true。

收集Trace和预读取

frameworks/base/startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java

上面服务启动中,已经介绍,服务启动时注册AppLaunchObserver,监听activity的启动。AppLaunchObserver定义了下面接口,

主要是调用了iorapd服务中对应接口实现功能

onIntentStarted--->iorapd::onAppLaunchEvent(AppLaunchEvent.IntentStarted)
onIntentFailed--->iorapd::onAppLaunchEvent(AppLaunchEvent.IntentFailed)
onActivityLaunched--->iorapd::onAppLaunchEvent(AppLaunchEvent.ActivityLaunched)
onActivityLaunchCancelled--->iorapd::onAppLaunchEvent(AppLaunchEvent.ActivityLaunchCancelled)
onActivityLaunchFinished--->iorapd::onAppLaunchEvent(AppLaunchEvent.ActivityLaunchFinished)

 

system/iorap/src/manager/event_manager.cc

OnAppLaunchEvent
bool EventManager::OnAppLaunchEvent(RequestId request_id,
                                    const AppLaunchEvent& event) {
  return impl_->OnAppLaunchEvent(request_id, event);
}
bool OnAppLaunchEvent(RequestId request_id,
                        const AppLaunchEvent& event) {
    ...
    AppLaunchEvent overwrite_event{};
    AppLaunchEventDefender::Result result =
        app_launch_event_defender_.OnAppLaunchEvent(request_id, event, /*out*/&overwrite_event);
    ...
}
struct AppLaunchEventDefender {
    ...
  Result OnAppLaunchEvent(binder::RequestId request_id,
                          const binder::AppLaunchEvent& event,
                          binder::AppLaunchEvent* overwrite) {
    ...
    switch (last_event_type_) {
      case Type::kUninitialized:
      case Type::kIntentFailed:
      case Type::kActivityLaunchCancelled:
      case Type::kReportFullyDrawn: {
        ...
      }
      case Type::kIntentStarted: {
        ...
      }
      case Type::kActivityLaunched: {
        ...
      }
      case Type::kActivityLaunchFinished: {
        ...
      }
    }
  }
};

具体执行的任务,在OnNewEvent中执行

void OnNewEvent(const AppLaunchEvent& event) {
    ...
    switch (event.type) {
      case Type::kIntentStarted:
        ...
      case Type::kIntentFailed:
        ...
      case Type::kActivityLaunched: {
        ...
        AppComponentName component_name = AppComponentName::FromString(title);
        component_name = component_name.Canonicalize();
        component_name_ = component_name;
       ...
        AppLaunchEvent::Temperature temperature = event.temperature;
        temperature_ = temperature;
        //判断是否是冷启动,冷启动才需要进行预读取
        if (temperature != AppLaunchEvent::Temperature::kCold) {
          LOG(INFO) << "AppLaunchEventState#OnNewEvent don't trace due to non-cold temperature";
        else if (!IsTracing() && !IsReadAhead()) {  // and the temperature is Cold.
          //StartReadAhead进行预读取
          if (allowed_readahead_ && !IsReadAhead()) {
            StartReadAhead(sequence_id_, component_name);
          }
          //进行perfetto trace
          if (allowed_tracing_ && !IsTracing() && !IsReadAhead()) {
            rx_lifetime_ = StartTracing(std::move(component_name));
          }
        else {
         ...
        }
        break;
      }
      case Type::kActivityLaunchFinished:
        ...
        FinishReadAhead();
        break;
      case Type::kActivityLaunchCancelled:
        ...
        AbortTrace();
        AbortReadAhead();
        break;
      case Type::kReportFullyDrawn:
          ...
      default:
 
    }
}

收集Trace

StartTracing

  • 调用perfetto/rx_producer.cc 中的CreateTraceStream 进行trace
  • 创建保存trace文件的目录,目录在/data/misc/iorapd/,比如/data/misc/iorapd/com.ctg.itrdc.file/1000002/dev.dworks.apps.anexplorer.DocumentsActivity/raw_traces
  • 把读取到的trace写入到perfetto_trace.pb文件中
std::optional<rxcpp::composite_subscription> StartTracing(
      AppComponentName component_name) {
    ...
    auto /*observable<PerfettoTraceProto>*/ trace_proto_stream =
        perfetto_factory_->CreateTraceStream(perfetto_commands);
 
          db::PerfettoTraceFileModel file_model =
            db::PerfettoTraceFileModel::CalculateNewestFilePath(versioned_component_name);
 
          std::string file_path = file_model.FilePath();
 
          ScopedFormatTrace atrace_write_to_file(ATRACE_TAG_ACTIVITY_MANAGER,
                                                 "Perfetto Write Trace To File %s",
                                                 file_path.c_str());
            //创建文件夹
          if (!file_model.MkdirWithParents()) {
            LOG(ERROR) << "Cannot save TraceBuffer; failed to mkdirs " << file_path;
            return;
          }
            //把trace文件写入到文件中
          if (!trace_proto.WriteFullyToFile(file_path)) {
            LOG(ERROR) << "Failed to save TraceBuffer to " << file_path;
          else {
            LOG(INFO) << "Perfetto TraceBuffer saved to file: " << file_path;
 
            ScopedFormatTrace atrace_update_raw_traces_table(
                ATRACE_TAG_ACTIVITY_MANAGER,
                "update raw_traces table history_id = %d",
                history_id);
            db::DbHandle db{db::SchemaModel::GetSingleton()};
            std::optional<db::RawTraceModel> raw_trace =
                db::RawTraceModel::Insert(db, history_id, file_path);
                 
          }
        },
         
    is_tracing_ = true;
 
    return lifetime;
  }/

system/iorap/src/perfetto/rx_producer.cc

CreateTraceStream→CreatePerfettoStream

主要调用perfetto/perfetto_consumer.cc中的StartTracing、ReadTrace等接口

static auto/*observable<PerfettoTraceProto>*/
    CreatePerfettoStream(rxcpp::observable<PerfettoStreamCommand> input,
                         std::shared_ptr<PerfettoConsumer> perfetto_consumer,
                         const ::perfetto::protos::TraceConfig& trace_config) {
        // XX: should I also take a scheduler for input here???
 
  auto /*observable<PerfettoStateChange>*/ perfetto_states =
    CreatePerfettoStateStream(trace_config, perfetto_consumer);
    ...
  return input
    .flat_map(
         [](std::tuple<PerfettoStreamCommand, PerfettoStateChange> p) {
           auto& [command, state_change] = p;
           LOG(INFO) << "CreatePerfettoStream#combine("
                        << command << "," << state_change << ")";
           if (command == PerfettoStreamCommand::kShutdown) {
             state_change.GetConsumer()->Destroy(state_change.GetHandle());
           else if (command == PerfettoStreamCommand::kStartTracing
                          && state_change.state == State::kConfigured) {
             state_change.GetConsumer()->StartTracing(state_change.GetHandle());
           else if (command == PerfettoStreamCommand::kStopTracing &&
                          state_change.state == State::kTraceEnded) {
             ::perfetto::consumer::TraceBuffer trace_buffer =
                 state_change.GetConsumer()->ReadTrace(state_change.GetHandle());
             PerfettoTraceProto wire_proto{trace_buffer.begin, trace_buffer.size};
             return rxcpp::observable<>::just(std::move(wire_proto)).as_dynamic();
           }
           return rxcpp::observable<>::empty<PerfettoTraceProto>().as_dynamic();
         }
    );
}

预读取

StartReadAhead

  • 获取compile生成的预取列表。是通过包名和版本号读取,路径比如:/data/misc/iorapd/com.ctg.itrdc.file/1000002/dev.dworks.apps.anexplorer.DocumentsActivity/compiled_traces/compiled_trace.pb
  • 执行预读取。调用/prefetcher/read_ahead.cc中 read_ahead_→BeginTask进行预读取
  void StartReadAhead(size_t id, const AppComponentName& component_name) {
    ...
    std::optional<std::string> file_path = GetCompiledTrace(component_name);
    if (!file_path) {
      LOG(INFO) << "Cannot find a compiled trace.";
      return;
    }
 
    prefetcher::TaskId task{id, *file_path};
    read_ahead_->BeginTask(task);
    // TODO: non-void return signature?
 
    read_ahead_task_ = std::move(task);
  }

生成预读取列表

Job server的启动在服务启动一节,已经介绍,最终调用到manager/event_manager.cc中的OnJobScheduledEvent

  bool OnJobScheduledEvent(RequestId request_id,
                           const JobScheduledEvent& event) {
     ...
    job_scheduled_event_subject_.OnNext(std::move(request_id), event);
    return true;  // No errors.
  }

Job初始化时,初始化函数InitializeRxGraphForJobScheduledEvents,设置了job处理的任务,最终调用StartMaintenance→CompileSingleAppOnDevice

system/iorap/src/maintenance/controller.cc

bool CompileSingleAppOnDevice(const db::DbHandle& db,
                              const ControllerParameters& params,
                              const std::string& package_name) {
  std::vector<db::PackageModel> packages = db::PackageModel::SelectByName(db, package_name.c_str());
  bool ret = true;
  for (db::PackageModel package : packages) {
    if (!CompilePackage(db, package.name, package.version, params)) {
      ret = false;
    }
  }
  return ret;
}

CompilePackage通过app包名和版本号,查找包db所在路径。然后调用CompileActivity compile 所有activity 

// Compiled the perfetto traces for activities in an package.
bool CompilePackage(const db::DbHandle& db,
                    const std::string& package_name,
                    int version,
                    const ControllerParameters& params) {
  ...                      
  std::optional<db::PackageModel> package =
        db::PackageModel::SelectByNameAndVersion(db, package_name.c_str(), version);
   ...     
  std::vector<db::ActivityModel> activities =
      db::ActivityModel::SelectByPackageId(db, package->id);
 
  bool ret = true;
  for (db::ActivityModel activity : activities) {
    if (!CompileActivity(db, package->id, package->name, activity.name, version, params)) {
      ret = false;
    }
  }
  return ret;
}

 

  • 如果complized_trace.pb文件存在,则直接返回。complized_trace.pb路径在/data/misc/iorapd/com.ctg.itrdc.file/1000002/dev.dworks.apps.anexplorer.DocumentsActivity/compiled_traces/compiled_trace.pb
  • 如果complized_trace.pb文件不存在,则通过StartViaFork 启动iorap.cmd.compiler进程。
// Compiled the perfetto traces for an activity.
bool CompileActivity { 
  db::CompiledTraceFileModel output_file =
      CalculateNewestFilePath(package_name, activity_name, version);
  std::string file_path = output_file.FilePath();
  //complized_trace.pb文件如果存在,则直接返回
  if (!params.recompile) {
    if (std::filesystem::exists(file_path)) {
       ...  
      db::VersionedComponentName vcn{package_name, activity_name, version};
      std::optional<db::PrefetchFileModel> prefetch_file =
          db::PrefetchFileModel::SelectByVersionedComponentName(db, vcn);
      if (prefetch_file) {
        return true;
      else {
         ...
      }
    }
  }
 
  std::optional<db::ActivityModel> activity =
      db::ActivityModel::SelectByNameAndPackageId(db, activity_name.c_str(), package_id);
  ...    
  int activity_id = activity->id;
 
  std::vector<db::AppLaunchHistoryModel> histories =
      db::AppLaunchHistoryModel::SelectActivityHistoryForCompile(db, activity_id);
 
  {
    std::vector<compiler::CompilationInput> perfetto_traces =
        GetPerfettoTraceInfo(db, histories);
    ...
    ScopedFormatTrace atrace_compile_fork(ATRACE_TAG_PACKAGE_MANAGER,
                                          "Fork+exec iorap.cmd.compiler",
                                          activity_name.c_str());
    if (!StartViaFork(compiler_params)) {
       ...
    }
  }
  ...

system/iorap/src/compiler

iorap.cmd.compiler进程的main函数,主要是调用PerformCompilation函数

  • ApplyBlacklistToPageCacheEvents中调用iorap.inode2filename中的FindFilenamesFromInodes通过inode 查找文件名。
  • serialize::ProtobufIO::WriteFully把 (inode、偏移量、长度) 转换为 (名称、偏移量、长度),然后将数据存储在预取列表 protobuf 文件中。
bool PerformCompilation(std::vector<CompilationInput> perfetto_traces,
                        std::string output_file_name,
                        bool output_proto,
                        std::optional<std::string> blacklist_filter,
                        inode2filename::InodeResolverDependencies dependencies) {
  auto trace_protos = ReadPerfettoTraceProtos(std::move(perfetto_traces));
  auto resolved_events = ResolvePageCacheEntriesFromProtos(std::move(trace_protos),
                                                           std::move(dependencies));
  auto filtered_events =
      ApplyBlacklistToPageCacheEvents(std::move(resolved_events), blacklist_filter);
  auto compiled_events = CompilePageCacheEvents(std::move(filtered_events));
  ...
   if (output_proto) {
    LOG(INFO) << "compiler: WriteFully to begin into " << output_file_name;
    ::google::protobuf::MessageLite& message = *trace_file_proto.get();
    if (auto res = serialize::ProtobufIO::WriteFully(message, output_file_name); !res) {
      errno = res.error();
      PLOG(ERROR) << "compiler: Failed to write protobuf to file: " << output_file_name;
      return false;
    else {
      LOG(INFO) << "compiler: Wrote protobuf " << output_file_name;
    }
  }

更新预取列表

startop/iorap/src/com/google/android/startop/iorap/IorapForwardingService.java

服务启动时注册了DexOptPackagesUpdated,DexOptPackagesUpdated会监听应用包的更新

    private class DexOptPackagesUpdated implements BackgroundDexOptService.PackagesUpdatedListener {
        @Override
        public void onPackagesUpdated(ArraySet<String> updatedPackages) {
            String[] updated = updatedPackages.toArray(new String[0]);
            for (String packageName : updated) {
                Log.d(TAG, "onPackagesUpdated: " + packageName);
                invokeRemote(mIorapRemote,
                    (IIorap remote) ->
                        remote.onDexOptEvent(RequestId.nextValueForSequence(),
                                DexOptEvent.createPackageUpdate(packageName))
                );
            }
        }
    }

最终调用到event_manager.ccp中的OnDexOptEvent

  bool OnDexOptEvent(RequestId request_id,
                     const DexOptEvent& event) {
    ...
    return PurgePackage(event.package_name);
  }

PurgePackage调用db中的CleanUpFilesForPackage清除掉过时的db数据

  bool PurgePackage(const std::string& package_name) {
    db::DbHandle db{db::SchemaModel::GetSingleton()};
    db::CleanUpFilesForPackage(db, package_name);
    LOG(INFO) << "PurgePackage: " << package_name;
    return true;
  }

iorap服务监控包更新

system/iorap/src/binder/package_change_observer.cc

android::binder::Status PackageChangeObserver::onPackageChanged(
  const android::content::pm::PackageChangeEvent& event) {
  LOG(INFO) << "Received PackageChangeObserver::onPackageChanged";
  if (event_manager_->OnPackageChanged(event)) {
    return android::binder::Status::ok();
  else {
    return android::binder::Status::fromStatusT(android::BAD_VALUE);
  }
}
 
 
  bool OnPackageChanged(const android::content::pm::PackageChangeEvent& event) {
    LOG(INFO) << "Received " << event;
    if (event.isDeleted) {
      return true;
    }
    // Update the version map.
    if (version_map_->Update(event.packageName, event.version)) {
      return true;
    }
    db::DbHandle db{db::SchemaModel::GetSingleton()};
    db::CleanUpFilesForPackage(db, event.packageName, event.version);
    return true;
  }

 

文章来自个人专栏
文章 | 订阅
0条评论
0 / 1000
请输入你的评论
0
0