art profile生成差异的问题分析

1. art profile生成差异的问题

最近Android S遇到一个问题,art升级以后,cts测试失败了,报错日志

I/ModuleListener: [1/1]
com.android.compatibility.common.tradefed.testtype.JarHostTest
com.android.cts.dexmetadata.InstallDexMetadataHostTest#testProfileSnapshotAfterInstall
FAILURE: arrays first differed at element [22]; expected:<7> but
was:<119> at
org.junit.internal.ComparisonCriteria.arrayEquals(ComparisonCriteria.java:78)
at
org.junit.internal.ComparisonCriteria.arrayEquals(ComparisonCriteria.java:28)
at org.junit.Assert.internalArrayEquals(Assert.java:534) at
org.junit.Assert.assertArrayEquals(Assert.java:343) at
org.junit.Assert.assertArrayEquals(Assert.java:354) at
com.android.cts.dexmetadata.InstallDexMetadataHostTest.testProfileSnapshotAfterInstall(InstallDexMetadataHostTest.java:356)

大概意思是com.android.cts.dexmetadata.InstallDexMetadataHostTest.testProfileSnapshotAfterInstall(InstallDexMetadataHostTest.java:356)测试报错,里面元素 [22],期望值是7,但实际是119。

本身对art这块了解得不多,遇到这个问题,很多内容需要从头看起,这里就将分析过程记录下来

2. cts测试内容的分析

找到cts的代码目录

cts$ grep -rn “testProfileSnapshotAfterInstall” .

然后你可以找到具体的代码位置

//cts/hostsidetests/dexmetadata/host/src/com/android/cts/dexmetadata/InstallDexMetadataHostTest.java
    public void testProfileSnapshotAfterInstall() throws Exception {//报错的测试case
        assumeProfilesAreEnabled();

        // Determine which profile to use.
        boolean useProfileForS = ApiLevelUtil.isAtLeast(getDevice(), "S");//这个是true

        // Install the app.
        File dmBaseFile = useProfileForS ? mDmBaseFileForS : mDmBaseFile;//.apk的文件,实际的apk
        File dmBaseFsvSigFile = useProfileForS ? mDmBaseFsvSigFileForS : mDmBaseFsvSigFile;//.fsv_sig文件,Fs Verity的签名校验文件
        String dmName = mDmBaseFile.getName();  // APK name with ".apk" replaced by ".dm".就是".dm"文件,全程dex metadata
        new InstallMultiple()
                .addApk(mApkBaseFile).addDm(dmBaseFile, dmBaseFsvSigFile, dmName).run();//后面有说明,这里指的安装apk

        // Take a snapshot of the installed profile.
        String snapshotCmd = "cmd package snapshot-profile " + INSTALL_PACKAGE;
        String result = getDevice().executeShellCommand(snapshotCmd);//执行adb指令
        assertTrue(result.trim().isEmpty());

        // Extract the profile bytes from the dex metadata and from the profile snapshot.
        byte[] rawDeviceProfile = extractProfileSnapshotFromDevice();//取出生成的snapshot prof文件(/data/misc/profman/com.android.cts.dexmetadata.splitapp.prof)
        byte[] rawMetadataProfile = extractProfileFromDexMetadata(dmBaseFile);//取出dm文件里面的prof文件
        if (useProfileForS) {
            ProfileReaderV15 snapshotReader = new ProfileReaderV15(rawDeviceProfile);//生成ProfileReaderV15的解析器,解析生成的snapshot prof文件
            ProfileReaderV15 expectedReader = new ProfileReaderV15(rawMetadataProfile);//解析".dm"里面的prof文件

            assertArrayEquals(expectedReader.dexFilesData, snapshotReader.dexFilesData);//匹配dexFilesData
            assertArrayEquals(expectedReader.extraDescriptorsData,
                              snapshotReader.extraDescriptorsData);//匹配extraDescriptorsData
            assertArrayEquals(expectedReader.classesData, snapshotReader.classesData);//匹配classesData
            assertArrayEquals(expectedReader.methodsData, snapshotReader.methodsData);//匹配methodsData,这里也是这个问题出错的地方
         } else {
            byte[] snapshotProfileBytes = new ProfileReaderV10(rawDeviceProfile).data;
            byte[] expectedProfileBytes = new ProfileReaderV10(rawMetadataProfile).data;

            assertArrayEquals(expectedProfileBytes, snapshotProfileBytes);
         }
    }

这个问题出错是在methodsData的内容匹配出现差异。

由于cts调试需要的环境比较多,查看cts的代码,看一下如何本地模拟

//安装的apk的方法
//cts/hostsidetests/dexmetadata/host/src/com/android/cts/dexmetadata/BaseInstallMultiple.java
   private void run(boolean expectingSuccess) throws DeviceNotAvailableException {
        final ITestDevice device = mDevice;

        // Create an install session
        final StringBuilder cmd = new StringBuilder();
        cmd.append("pm install-create");
        for (String arg : mArgs) {
            cmd.append(' ').append(arg);//第一个指令是pm install-create -g
        }

        String result = device.executeShellCommand(cmd.toString());
        TestCase.assertTrue(result, result.startsWith("Success"));

        final int start = result.lastIndexOf("[");
        final int end = result.lastIndexOf("]");
        int sessionId = -1;
        try {
            if (start != -1 && end != -1 && start < end) {
                sessionId = Integer.parseInt(result.substring(start + 1, end));//获取pm install-create -g返回的session id
            }
        } catch (NumberFormatException e) {
            throw new IllegalStateException("Failed to parse install session: " + result);
        }
        if (sessionId == -1) {
            throw new IllegalStateException("Failed to create install session: " + result);
        }

        // Push our files into session. Ideally we'd use stdin streaming,
        // but ddmlib doesn't support it yet.
        assert(mFilesToInstall.size() == mInstallNames.size());
        int numPushedFiles = 0;
        boolean success = false;
        try {
            for (int i = 0, size = mFilesToInstall.size(); i != size; ++i) {
                File file = mFilesToInstall.get(i);
                String name = mInstallNames.get(i);
                final String remotePath = "/data/local/tmp/" + name;//安装的文件都在/data/local/tmp/里面
                if (!device.pushFile(file, remotePath)) {
                    throw new IllegalStateException("Failed to push " + file);
                }

                cmd.setLength(0);
                cmd.append("pm install-write");//分别将之前的mFilesToInstall,如'.apk', '.dm', '.fsv_sig'用pm install-write写入
                cmd.append(' ').append(sessionId);
                cmd.append(' ').append(name);
                cmd.append(' ').append(remotePath);

                result = device.executeShellCommand(cmd.toString());
                TestCase.assertTrue(result, result.startsWith("Success"));
            }

            // Everything staged; let's pull trigger
            cmd.setLength(0);
            cmd.append("pm install-commit");
            cmd.append(' ').append(sessionId);//最后用pm install-commit结束安装指令

            result = device.executeShellCommand(cmd.toString());
            //...
    }

结合CTS的报错日志,测试过程看起来就是将"/data/local/tmp/"里面的apk安装进入系统,并执行cmd package snapshot-profile生成snapshot prof

V/NativeDevice: pm install-create -g on 83cf23d4 returned Success:
created install session [1581118244]

V/NativeDevice: pm install-write 1581118244 CtsDexMetadataSplitApp.apk
/data/local/tmp/CtsDexMetadataSplitApp.apk

V/NativeDevice: pm install-write 1581118244 CtsDexMetadataSplitApp.dm
/data/local/tmp/CtsDexMetadataSplitApp.dm

V/NativeDevice: pm install-write 1581118244
CtsDexMetadataSplitApp.dm.fsv_sig
/data/local/tmp/CtsDexMetadataSplitApp.dm.fsv_sig

V/NativeDevice: pm install-commit 1581118244

V/NativeDevice: cmd package snapshot-profile
com.android.cts.dexmetadata.splitapp

3. 模拟验证

首先将cts测试过程中的"/data/local/tmp/"文件pull出来,并放入需要验证的手机里面

adb root; adb push CtsDexMetadataSplitApp.apk /data/local/tmp/ ; adb push CtsDexMetadataSplitApp.dm /data/local/tmp/ ; adb push CtsDexMetadataSplitApp.dm.fsv_sig /data/local/tmp/ ;

安装apk

adb shell pm install-create -g
=>Success: created install session [1581118244]

adb shell pm install-write 1581118244 CtsDexMetadataSplitApp.apk /data/local/tmp/CtsDexMetadataSplitApp.apk
adb shell pm install-write 1581118244 CtsDexMetadataSplitApp.dm /data/local/tmp/CtsDexMetadataSplitApp.dm
adb shell pm install-write 1581118244 CtsDexMetadataSplitApp.dm.fsv_sig /data/local/tmp/CtsDexMetadataSplitApp.dm.fsv_sig
adb shell pm install-commit 1581118244

生成snapshot prof文件

adb shell cmd package snapshot-profile com.android.cts.dexmetadata.splitapp

=>二进制对比profman的prof和dm里面的primary.prof
adb pull /data/misc/profman/com.android.cts.dexmetadata.splitapp.prof .
解压CtsDexMetadataSplitApp.dm里面的primary.prof

prof二进制对比
可以看到上面二进制文件是有差异的(左边是snapshot prof,右边是dm的prof)。

本地可以复现,调试起来就方便很多

4. snapshot-profile的流程

=> 从adb shell cmd package snapshot-profile com.android.cts.dexmetadata.splitappk开始调查

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
                  case "snapshot-profile":
                      return runSnapshotProfile();

        mInterface.getArtManager().snapshotRuntimeProfile(profileType, packageName,
                codePath, callback, callingPackage);



//Installer.java
    public boolean createProfileSnapshot(int appId, String packageName, String profileName,
            String classpath) throws InstallerException {
        if (!checkBeforeRemote()) return false;
        try {
            return mInstalld.createProfileSnapshot(appId, packageName, profileName, classpath);
        } catch (Exception e) {
            throw InstallerException.from(e);
        }
    }

//frameworks/native/cmds/installd/InstalldNativeService.cpp
  binder::Status InstalldNativeService::createProfileSnapshot(int32_t appId,
          const std::string& packageName, const std::string& profileName,
          const std::string& classpath, bool* _aidl_return) {
      ENFORCE_UID(AID_SYSTEM);
      CHECK_ARGUMENT_PACKAGE_NAME(packageName);
      std::lock_guard<std::recursive_mutex> lock(mLock);
  
      *_aidl_return = create_profile_snapshot(appId, packageName, profileName, classpath);
      return ok();
  }

//frameworks/native/cmds/installd/dexopt.cpp
  bool create_profile_snapshot(int32_t app_id, const std::string& package_name,
          const std::string& profile_name, const std::string& classpath) {
      if (app_id == -1) {
          return create_boot_image_profile_snapshot(package_name, profile_name, classpath);
      } else {
          return create_app_profile_snapshot(app_id, package_name, profile_name, classpath);
      }
  }
static bool create_app_profile_snapshot(int32_t app_id,
                                        const std::string& package_name,
                                        const std::string& profile_name,
                                        const std::string& classpath) {
                
    args.SetupMerge(profiles_fd,
            snapshot_fd,
            apk_fds,
            dex_locations,
            /* for_snapshot= */ true,
            /* for_boot_image= */ false);


    void SetupMerge(const std::vector<unique_fd>& profiles_fd,
                    const unique_fd& reference_profile_fd,
                    const std::vector<unique_fd>& apk_fds = std::vector<unique_fd>(),
                    const std::vector<std::string>& dex_locations = std::vector<std::string>(),
                    bool for_snapshot = false,
                    bool for_boot_image = false) {
        SetupArgs(profiles_fd,
                  reference_profile_fd,
                  apk_fds,
                  dex_locations,
                  /*copy_and_update=*/ false,
                  for_snapshot,
                  for_boot_image);
    }

  void SetupArgs(const std::vector<unique_fd>& profile_fds,
                  const unique_fd& reference_profile_fd,
                  const std::vector<unique_fd>& apk_fds,
                  const std::vector<std::string>& dex_locations,
                  bool copy_and_update,
                  bool for_snapshot,
                  bool for_boot_image) {

        // TODO(calin): Assume for now we run in the bg compile job (which is in
        // most of the invocation). With the current data flow, is not very easy or
        // clean to discover this in RunProfman (it will require quite a messy refactoring).
        const char* profman_bin = select_execution_binary(
            kProfmanPath, kProfmanDebugPath, /*background_job_compile=*/ true);

        if (copy_and_update) {
            CHECK_EQ(1u, profile_fds.size());
            CHECK_EQ(1u, apk_fds.size());
        }
        if (reference_profile_fd != -1) {
            AddArg("--reference-profile-file-fd=" + std::to_string(reference_profile_fd.get()));
        }

        for (const unique_fd& fd : profile_fds) {
            AddArg("--profile-file-fd=" + std::to_string(fd.get()));
        }

        for (const unique_fd& fd : apk_fds) {
            AddArg("--apk-fd=" + std::to_string(fd.get()));
        }

        for (const std::string& dex_location : dex_locations) {
            AddArg("--dex-location=" + dex_location);
        }

        if (copy_and_update) {
            AddArg("--copy-and-update-profile-key");
        }

        if (for_snapshot) {
            AddArg("--force-merge");
        }

        if (for_boot_image) {
            AddArg("--boot-image-merge");
        }

        // The percent won't exceed 100, otherwise, don't set it and use the
        // default one set in profman.
        uint32_t min_new_classes_percent_change = ::android::base::GetUintProperty<uint32_t>(
            "dalvik.vm.bgdexopt.new-classes-percent",
            /*default*/std::numeric_limits<uint32_t>::max());
        if (min_new_classes_percent_change <= 100) {
          AddArg("--min-new-classes-percent-change=" +
                 std::to_string(min_new_classes_percent_change));
        }

        // The percent won't exceed 100, otherwise, don't set it and use the
        // default one set in profman.
        uint32_t min_new_methods_percent_change = ::android::base::GetUintProperty<uint32_t>(
            "dalvik.vm.bgdexopt.new-methods-percent",
            /*default*/std::numeric_limits<uint32_t>::max());
        if (min_new_methods_percent_change <= 100) {
          AddArg("--min-new-methods-percent-change=" +
                 std::to_string(min_new_methods_percent_change));
        }

        // Do not add after dex2oat_flags, they should override others for debugging.
        PrepareArgs(profman_bin);
    }               
  

运行的是installd里面的create_app_profile_snapshot,代码已经贴出,这里内容讲解就不进行了

主要发现来自添加的日志,首先执行者是profman,传入的内容是/data/misc/profiles/ref/com.android.cts.dexmetadata.splitapp/primary.prof.snapshot、/data/misc/profiles/cur/0/com.android.cts.dexmetadata.splitapp/primary.prof、/data/misc/profiles/ref/com.android.cts.dexmetadata.splitapp/primary.prof

09-22 22:59:06.752 1621 2672 E installd: SetupArgs 10 profman_bin = /apex/com.android.art/bin/profman
09-22 22:59:06.752 1621 2672 E installd: PrepareArgs start bin = /apex/com.android.art/bin/profman,
09-22 22:59:06.752 1621 2672 E installd: PrepareArgs arg = /apex/com.android.art/bin/profman,
09-22 22:59:06.752 1621 2672 E installd: PrepareArgs arg = --reference-profile-file-fd=7, ///data/misc/profiles/ref/com.android.cts.dexmetadata.splitapp/primary.prof.snapshot
09-22 22:59:06.752 1621 2672 E installd: PrepareArgs arg = --profile-file-fd=9, ///data/misc/profiles/cur/0/com.android.cts.dexmetadata.splitapp/primary.prof
09-22 22:59:06.752 1621 2672 E installd: PrepareArgs arg = --profile-file-fd=8, ///data/misc/profiles/ref/com.android.cts.dexmetadata.splitapp/primary.prof
09-22 22:59:06.752 1621 2672 E installd: PrepareArgs arg = --apk-fd=10, ///data/app/~~5qOnzKwynL6hst3jji4kDA==/com.android.cts.dexmetadata.splitapp-07MstG0U2dWCr5GnVAf87Q==/base.apk
09-22 22:59:06.752 1621 2672 E installd: PrepareArgs arg = --dex-location=/data/app/~~5qOnzKwynL6hst3jji4kDA==/com.android.cts.dexmetadata.splitapp-07MstG0U2dWCr5GnVAf87Q==/base.apk,
09-22 22:59:06.753 1621 2672 E installd: PrepareArgs arg = --force-merge,
09-22 22:59:06.811 1403 6775 W PackageManagerShellCommand: runSnapshotProfile 12 outputProfilePath = /data/misc/profman/com.android.cts.dexmetadata.splitapp.prof

=> 在上述目录找一下这些文件,最终发现,snapshot prof(/data/misc/profman/com.android.cts.dexmetadata.splitapp.prof),和安装生成的/data/misc/profiles/ref/com.android.cts.dexmetadata.splitapp/primary.prof是一样的,那么问题在安装apk的时候就出现了。

掉头,重新来过,在安装里面找原因

5. prepareAppProfile的流程

单步调试在执行pm install-commit 1581118244的时候, prof文件就生成了,断点调试加上部分安装流程分析,最终发现第二次调用mInstaller.prepareAppProfile,prof就生成了

    private int runInstallCommit() throws RemoteException {
        final PrintWriter pw = getOutPrintWriter();
        String opt;
        
        if (doCommitSession(sessionId, false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {//执行这个之后就出来prof了

    private void installPackagesLI(List<InstallRequest> requests) {
        final Map<String, ScanResult> preparedScans = new ArrayMap<>(requests.size());
        final Map<String, InstallArgs> installArgs = new ArrayMap<>(requests.size());
        final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
        final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
        final Map<String, VersionInfo> versionInfos = new ArrayMap<>(requests.size());
        final Map<String, PackageSetting> lastStaticSharedLibSettings =
                new ArrayMap<>(requests.size());
        final Map<String, Boolean> createdAppId = new ArrayMap<>(requests.size());

            executePostCommitSteps(commitRequest);
        } finally {
            if (success) {

    public void prepareAppProfiles(
        //...
        boolean result = mInstaller.prepareAppProfile(pkg.getPackageName(), user, appId,
                profileName, codePath, dexMetadataPath);
        if (!result) {
            Slog.e(TAG, "Failed to prepare profile for " +
                    pkg.getPackageName() + ":" + codePath);
        }


  binder::Status InstalldNativeService::prepareAppProfile(const std::string& packageName,
          int32_t userId, int32_t appId, const std::string& profileName, const std::string& codePath,
          const std::optional<std::string>& dexMetadata, bool* _aidl_return) {
      ENFORCE_UID(AID_SYSTEM);
      CHECK_ARGUMENT_PACKAGE_NAME(packageName);
      CHECK_ARGUMENT_PATH(codePath);
      std::lock_guard<std::recursive_mutex> lock(mLock);
  
      *_aidl_return = prepare_app_profile(packageName, userId, appId, profileName, codePath,
          dexMetadata);
      return ok();
  }

实际还是installd中调用art的profman去生成prof

09-30 12:46:31.231 1597 2039 E installd: PrepareArgs start bin = /apex/com.android.art/bin/profman,
09-30 12:46:31.231 1597 2039 E installd: PrepareArgs arg = /apex/com.android.art/bin/profman,
09-30 12:46:31.232 1597 2039 E installd: PrepareArgs arg = --reference-profile-file-fd=7, ///data/misc/profiles/ref/com.android.cts.dexmetadata.splitapp/primary.prof
09-30 12:46:31.232 1597 2039 E installd: PrepareArgs arg = --profile-file-fd=8, ///data/app/~~gYDDfuWgf-fzY_Gy4LLY8Q==/com.android.cts.dexmetadata.splitapp-Ork7uUmdrBzPyNLU1Rw3cA==/base.dm
09-30 12:46:31.232 1597 2039 E installd: PrepareArgs arg = --apk-fd=9, ///data/app/~~gYDDfuWgf-fzY_Gy4LLY8Q==/com.android.cts.dexmetadata.splitapp-Ork7uUmdrBzPyNLU1Rw3cA==/base.apk
09-30 12:46:31.232 1597 2039 E installd: PrepareArgs arg = --dex-location=/data/app/~~gYDDfuWgf-fzY_Gy4LLY8Q==/com.android.cts.dexmetadata.splitapp-Ork7uUmdrBzPyNLU1Rw3cA==/base.apk,
09-30 12:46:31.232 1597 2039 E installd: PrepareArgs arg = --copy-and-update-profile-key,
09-30 12:46:31.232 1597 2039 E installd: End

上面传入的参数有/data/misc/profiles/ref/com.android.cts.dexmetadata.splitapp/primary.prof(这里是空的,在art中会写入数据到里面)、base.dm、base.apk,执行的方法是--copy-and-update-profile-key

6. art调试

在art中修改代码发现日志都没打印出来,android S上使用的art是vendor/partner_modules/ArtPrebuilt,这个是gms里面的art,给mainline了,源码修改无效,需要将这个bp屏蔽掉。

同时将mainline_modules_s.mk里面的art也注释掉

//vendor/partner_modules/build/mainline_modules_s.mk
# Art
#MAINLINE_COMPRESS_APEX_ART ?= true
#ifeq ($(MAINLINE_COMPRESS_APEX_ART),true)
#PRODUCT_PACKAGES += \
#    com.google.android.art_compressed
#else
#PRODUCT_PACKAGES += \
#    com.google.android.art
#endif

然后就可以进行art的调试了

7. profman解析执行

profman传入参数并解析

static int profman(int argc, char** argv) {
  ProfMan profman;

  profman.ParseArgs(argc, argv);//解析参数
  //...
  if (profman.ShouldCopyAndUpdateProfileKey()) {
    return profman.CopyAndUpdateProfileKey();//--copy-and-update-profile-key选项将执行CopyAndUpdateProfileKey方法
  }
  //...
}

  void ParseArgs(int argc, char **argv) {
      //...
      } else if (option == "--copy-and-update-profile-key") {
        copy_and_update_profile_key_ = true;
      }
      //...
  }

  bool ShouldCopyAndUpdateProfileKey() const {
    return copy_and_update_profile_key_;
  }
  int32_t CopyAndUpdateProfileKey() {

    //...
    if (load_ok) {
      // Open the dex files to look up classes and methods.
      std::vector<std::unique_ptr<const DexFile>> dex_files;
      OpenApkFilesFromLocations(&dex_files);//加载.apk

      if (!profile.UpdateProfileKeys(dex_files)) {
        return kErrorFailedToUpdateProfile;
      }
      bool result = use_fds
          ? profile.Save(reference_profile_file_fd_)
          : profile.Save(reference_profile_file_, /*bytes_written=*/ nullptr);//use_fds是true,reference_profile_file_fd_就是--reference-profile-file-fd传入的,这里是保存prof的路径/data/misc/profiles/ref/com.android.cts.dexmetadata.splitapp/primary.prof
      return result ? 0 : kErrorFailedToSaveProfile;
    } else {
      return kErrorFailedToLoadProfile;
    }
  }

ps:
/apex/com.android.art/bin/profman一个工具,实际执行还在libprofile里面

8. libprofile保存prof

这里面的内容是重点,会稍微讲的仔细一点

8.1 初始化header and infos到prof中

代码里面有解释每一行代码的意思,还有此次cts测试中的实际值

//art/libprofile/profile/profile_compilation_info.cc
bool ProfileCompilationInfo::Save(int fd) {//fd是保存prof的句柄
  uint64_t start = NanoTime();
  ScopedTrace trace(__PRETTY_FUNCTION__);
  DCHECK_GE(fd, 0);

  // Collect uncompressed section sizes.
  // Use `uint64_t` and assume this cannot overflow as we would have run out of memory.
  uint64_t extra_descriptors_section_size = 0u;
  if (!extra_descriptors_.empty()) {//extra_descriptors_针对这个cts prof的apk是空的,也就是extra_descriptors_section_size=0
    extra_descriptors_section_size += sizeof(uint16_t);  // Number of descriptors.
    for (const std::string& descriptor : extra_descriptors_) {
      // Length-prefixed string, the length is `uint16_t`.
      extra_descriptors_section_size += sizeof(uint16_t) + descriptor.size();
    }
  }
  uint64_t dex_files_section_size = sizeof(ProfileIndexType);  // ProfileIndexType是uint16_t,2个字节,dex_files_section_size是2
  uint64_t classes_section_size = 0u;//apk class类区域大小
  uint64_t methods_section_size = 0u;//apk method方法区域的大小
  DCHECK_LE(info_.size(), MaxProfileIndex());
  //从info_里面获取DexFileData,这里apk目前只有一个.apk
  for (const std::unique_ptr<DexFileData>& dex_data : info_) {
    if (dex_data->profile_key.size() > kMaxDexFileKeyLength) {//dex_data->profile_key这里是base.apk,他的size不能大于kMaxDexFileKeyLength = 1024
      LOG(WARNING) << "DexFileKey exceeds allocated limit";
      return false;
    }
    //也就是dex_files_section_size = 2 + 12 + 2 + 8 = 24
    dex_files_section_size +=
        3 * sizeof(uint32_t) +  // Checksum, num_type_ids, num_method_ids. 这里包含Checksum、num_type_ids, num_method_ids 3*4= 12
        // Length-prefixed string, the length is `uint16_t`.
        sizeof(uint16_t) + dex_data->profile_key.size();//存储profile_key的长度、还有profile_key字符"base.apk"本身 2 + 8
    classes_section_size += dex_data->ClassesDataSize();//classes_section_size=106,是apk class类区域大小
    methods_section_size += dex_data->MethodsDataSize();//methods_section_size=27,是apk method方法区域的大小
  }

  //file_section_count=1+0+1+1=3, dex files apk文件肯定是有的,extra descriptors这里目前是0,没有加入
  //classes、methods都是有的
  const uint32_t file_section_count =
      /* dex files */ 1u +
      /* extra descriptors */ (extra_descriptors_section_size != 0u ? 1u : 0u) +
      /* classes */ (classes_section_size != 0u ? 1u : 0u) +
      /* methods */ (methods_section_size != 0u ? 1u : 0u);

/*
class ProfileCompilationInfo::FileHeader {
 /...
 private:
  // The upper bound for file section count is used to ensure that there
  // shall be no arithmetic overflow when calculating size of the header
  // with section information.
  static const uint32_t kMaxFileSectionCount;

  uint8_t magic_[4] = {0, 0, 0, 0};//4个字节,这个是"pro\0", 默认是kProfileMagic[] = { 'p', 'r', 'o', '\0' };
  uint8_t version_[4] = {0, 0, 0, 0};//4个字节,这个版本号默认是"015\0",如果是ForBootImage,则是"016\0"
  uint32_t file_section_count_ = 0u;//4个字节,这个是3
};

class ProfileCompilationInfo::FileSectionInfo {
 /...
 private:
  FileSectionType type_;//4个字节,uint32_t
  uint32_t file_offset_;//4个字节
  uint32_t file_size_;//4个字节
  uint32_t inflated_size_;  //4个字节// If 0, do not inflate and use data from file directly.
};
*/
  //FileHeader=12(这里有3个变量,一共12个字节,静态常量不在里面),file_section_count=3,sizeof(FileSectionInfo) = 16
  uint64_t header_and_infos_size =
      sizeof(FileHeader) + file_section_count * sizeof(FileSectionInfo);//header_and_infos_size = 12 + (3*16) =  60

  // Check size limit. Allow large profiles for non target builds for the case
  // where we are merging many profiles to generate a boot image profile.
  //header_and_infos_size = 60,dex_files_section_size = 24,extra_descriptors_section_size = 0,classes_section_size = 106,methods_section_size = 27
  //total_uncompressed_size = 60+24+0+106+27 = 217
  uint64_t total_uncompressed_size =
      header_and_infos_size +
      dex_files_section_size +
      extra_descriptors_section_size +
      classes_section_size +
      methods_section_size;

  VLOG(profiler) << "Required capacity: " << total_uncompressed_size << " bytes.";
  //总大小不能超过1500000U(如果是ForBootImage则是100000000U),这个case不会进去的
  if (total_uncompressed_size > GetSizeErrorThresholdBytes()) {
    LOG(ERROR) << "Profile data size exceeds "
               << GetSizeErrorThresholdBytes()
               << " bytes. Profile will not be written to disk."
               << " It requires " << total_uncompressed_size << " bytes.";
    return false;
  }

  // Start with an invalid file header and section infos.
  DCHECK_EQ(lseek(fd, 0, SEEK_CUR), 0);
  constexpr uint32_t kMaxNumberOfSections = enum_cast<uint32_t>(FileSectionType::kNumberOfSections);//kMaxNumberOfSections=kNumberOfSections=4
  //kMaxHeaderAndInfosSize = (FileHeader)12 + (kMaxNumberOfSections)4*(sizeof(FileSectionInfo))16 = 76
  constexpr uint64_t kMaxHeaderAndInfosSize =
      sizeof(FileHeader) + kMaxNumberOfSections * sizeof(FileSectionInfo);
  DCHECK_LE(header_and_infos_size, kMaxHeaderAndInfosSize);//header_and_infos_size需要小于kMaxHeaderAndInfosSize
  std::array<uint8_t, kMaxHeaderAndInfosSize> placeholder;//新建placeholder
  memset(placeholder.data(), 0, header_and_infos_size);//将placeholder里面0-(header_and_infos_size)60清0

  if (!WriteBuffer(fd, placeholder.data(), header_and_infos_size)) {//先把header_and_infos_size=60的数据清0, 这里存放的是prof的头部数据
    return false;
  }

WriteBuffer已经开始写入prof内容了,具体写入位置如下,只是里面的值全部写入0
在这里插入图片描述

8.2 写入dex files section

8.2.1 写入info_.size()

从info_里面获取DexFileData,这里apk目前只有一个.apk,故info_.size() = 1

  std::array<FileSectionInfo, kMaxNumberOfSections> section_infos;//段的info信息,包含类型、压缩数据大小,实际数据大小
  size_t section_index = 0u;
  uint32_t file_offset = header_and_infos_size;//header_and_infos_size=60
  //定义add_section_info函数,type是文件段的类型,目前有dex_files/classes/methods 3个段,file_size是各个段压缩写入prof的大小
  //inflated_size是没有压缩前,各个段的实际数据大小
  //如实际数据大小:dex_files_section_size = 24,extra_descriptors_section_size = 0,classes_section_size = 106,methods_section_size = 27
  auto add_section_info = [&](FileSectionType type, uint32_t file_size, uint32_t inflated_size) {
    DCHECK_LT(section_index, section_infos.size());
    section_infos[section_index] = FileSectionInfo(type, file_offset, file_size, inflated_size);
    file_offset += file_size;//file_size=24, file_offset = 84
    section_index += 1u;//section_index每进来一次+1
  };



  // Write the dex files section. 开始写入第一个dex文件的段落
  {
    SafeBuffer buffer(dex_files_section_size);
    //                          0100(60) f3c2
    //f426 4900 0000 3a00 0000 0800 6261 7365  .&I...:.....base 80 -1
    //2e61 706b 7801 6360 3066 e065 60a4 0304  .apkx.c`0f.e`... 96 -1
    buffer.WriteUintAndAdvance(dchecked_integral_cast<ProfileIndexType>(info_.size()));//2个字节,info_.size() = 1,存放01 00(小端存储方式)
    
/*
  template <typename T>
  void WriteUintAndAdvance(T value) {//写入SafeBuffer的方法
    static_assert(std::is_integral_v<T>);
    WriteAndAdvance(&value, sizeof(value));//按照字节写入
  }

  void WriteAndAdvance(const void* data, size_t data_size) {
    DCHECK_LE(data_size, GetAvailableBytes());
    memcpy(ptr_current_, data, data_size);//字节拷贝
    ptr_current_ += data_size;
  }
*/

写入2个字节,[60],[61],使用的是小端存储方式,info_.size() = 0x0001 , 低地址存放在低字节,如[60]存放01;高地址存放高字节,如[61]存放00
在这里插入图片描述
ps:
这里需要注意一下写入的位置是SafeBuffer,还没写入prof的文件句柄fd中,属于临时存储位置。特别注意一下:WriteAndAdvance属于字节写入

8.2.2 写入checksum/num_type_ids/num_method_ids/profile_key

    for (const std::unique_ptr<DexFileData>& dex_data : info_) {
      buffer.WriteUintAndAdvance(dex_data->checksum);//checksum = 653574899 (0x26F4C2F3), 4个字节小端存储从低地址到高地址存放方式f3 c2 f4 26
      buffer.WriteUintAndAdvance(dex_data->num_type_ids);// uint32_t num_type_ids = 73(49)
      buffer.WriteUintAndAdvance(dex_data->num_method_ids);// uint32_t num_method_ids = 58(3A)
      buffer.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(dex_data->profile_key.size()));// uint16_t profile_key是"base.apk"的长度size()是8
      buffer.WriteAndAdvance(dex_data->profile_key.c_str(), dex_data->profile_key.size());//base.apk按照:'b'(ASCII码是98=0x62)存放在低地址的方式存放,'k'是最高地址
    }
    DCHECK_EQ(buffer.GetAvailableBytes(), 0u);
    // Write the dex files section uncompressed.写入dex files段落的时候是不压缩的
    if (!WriteBuffer(fd, buffer.Get(), dex_files_section_size)) {
      return false;
    }
/*
enum class ProfileCompilationInfo::FileSectionType : uint32_t {
  // The values of section enumerators and data format for individual sections
  // must not be changed without changing the profile file version. New sections
  // can be added at the end and they shall be ignored by old versions of ART.

  // The list of the dex files included in the profile.
  // There must be exactly one dex file section and it must be first.
  kDexFiles = 0,

  // Extra descriptors for referencing classes that do not have a `dex::TypeId`
  // in the referencing dex file, such as classes from a different dex file
  // (even outside of the dex files in the profile) or array classes that were
  // used from other dex files or created through reflection.
  kExtraDescriptors = 1,

  // Classes included in the profile.
  kClasses = 2,

  // Methods included in the profile, their hotness flags and inline caches.
  kMethods = 3,

  // The number of known sections.
  kNumberOfSections = 4
};
*/
    //add_section_info添加已经写入的段落信息,如type是kDexFiles=0,dex_files_section_size = 24, inflated_size = 0的意思是不压缩
    add_section_info(FileSectionType::kDexFiles, dex_files_section_size, /*inflated_size=*/ 0u);
  }

整个dex files section写入prof的数据如下,请对应代码注释和图来理解
在这里插入图片描述

8.3 写入extra descriptors section

由于extra_descriptors_section_size这问题里面是0,这里只是粗略描述一下里面的大概内容

  // Write the extra descriptors section.//写入额外描述段落
  if (extra_descriptors_section_size != 0u) {
    SafeBuffer buffer(extra_descriptors_section_size);
    buffer.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(extra_descriptors_.size()));//写入有多少个额外描述
    for (const std::string& descriptor : extra_descriptors_) {
      buffer.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(descriptor.size()));//每个描述的大小
      buffer.WriteAndAdvance(descriptor.c_str(), descriptor.size());//描述的内容是什么

    }
    if (!buffer.Deflate()) {//压缩
      return false;
    }
    if (!WriteBuffer(fd, buffer.Get(), buffer.Size())) {//写入压缩后的数据
      return false;
    }
    add_section_info(
        FileSectionType::kExtraDescriptors, buffer.Size(), extra_descriptors_section_size);
  }

8.4 写入classes section

8.4.1 classes section未压缩前的实际数据

这里先解释原始写入的数据(未压缩前的实际数据)

  // Write the classes section.写入class段落
  if (classes_section_size != 0u) {
    SafeBuffer buffer(classes_section_size);
    for (const std::unique_ptr<DexFileData>& dex_data : info_) {
      dex_data->WriteClasses(buffer);//写入Classes段落的地方
    }

/*
//WriteClasses方法
void ProfileCompilationInfo::DexFileData::WriteClasses(SafeBuffer& buffer) const {
  if (class_set.empty()) {
    return;
  }
  buffer.WriteUintAndAdvance(profile_index);//这里只有一个DexFileData, 这个case里面profile_index=0,2个字节
  buffer.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(class_set.size()));//class_set.size()=51=0x33,2个字节

  WriteClassSet(buffer, class_set);
}

void ProfileCompilationInfo::DexFileData::WriteClassSet(
    SafeBuffer& buffer,
    const ArenaSet<dex::TypeIndex>& class_set) {
  // Store the difference between the type indexes for better compression.
  uint16_t last_type_index = 0u;
  for (const dex::TypeIndex& type_index : class_set) {//遍历大小是51的class_set,总共写入51*2=102个字节,上面写入的profile_index和class_set.size()一共106
    DCHECK_GE(type_index.index_, last_type_index);
    uint16_t diff_with_last_type_index = type_index.index_ - last_type_index;//diff_with_last_type_index第一个存储的是class的TypeIndex的下标index_, 其他都是针对上一个的差值
    last_type_index = type_index.index_;//last_type_index第一次是0, index_是TypeIndex下标,这里index_第一个是13到63,每个都是递增1
    buffer.WriteUintAndAdvance(diff_with_last_type_index);//这里第一个存储的是diff_with_last_type_index,避免存储数据过大,一次2个字节
  }
}
*/

写入的内容如下

//class段落实际写入的数据
09-30 21:58:15.068 9483 9483 E : ProfileReaderV15 4_2 data[0] = 0x0
09-30 21:58:15.068 9483 9483 E : ProfileReaderV15 4_2 data[1] = 0x0
09-30 21:58:15.068 9483 9483 E : ProfileReaderV15 4_2 data[2] = 0x33
09-30 21:58:15.068 9483 9483 E : ProfileReaderV15 4_2 data[3] = 0x0
09-30 21:58:15.068 9483 9483 E : ProfileReaderV15 4_2 data[4] = 0xd
09-30 21:58:15.068 9483 9483 E : ProfileReaderV15 4_2 data[5] = 0x0
09-30 21:58:15.068 9483 9483 E : ProfileReaderV15 4_2 data[6] = 0x1
09-30 21:58:15.069 9483 9483 E : ProfileReaderV15 4_2 data[7] = 0x0
09-30 21:58:15.069 9483 9483 E : ProfileReaderV15 4_2 data[8] = 0x1
//…
09-30 21:58:15.079 9483 9483 E : ProfileReaderV15 4_2 data[104] = 0x1
09-30 21:58:15.080 9483 9483 E : ProfileReaderV15 4_2 data[105] = 0x0

8.4.2 classes压缩后的实际数据

    //对数据进行压缩,调用的是zip里面的deflate
    if (!buffer.Deflate()) {
      return false;
    }
    //写入prof的数据是压缩厚的buffer数据
    if (!WriteBuffer(fd, buffer.Get(), buffer.Size())) {
      return false;
    }
    //添加kClasses=2段落的信息,一会会写在prof头部
    add_section_info(FileSectionType::kClasses, buffer.Size(), classes_section_size);
  }

/*
  // Deflate a filled buffer. Replaces the internal buffer with a new one, also filled.
  //DEFLATE是同时使用了LZ77算法与哈夫曼编码(Huffman Coding)的一个无损数据压缩算法, 该压缩算法在zip格式中使用的
  bool Deflate() {
    DCHECK_EQ(GetAvailableBytes(), 0u);
    DCHECK_NE(Size(), 0u);
    ArrayRef<const uint8_t> in_buffer(Get(), Size());//in_buffer是未压缩的数据
    uint32_t output_size = 0;
    std::unique_ptr<uint8_t[]> compressed_buffer = DeflateBuffer(in_buffer, &output_size);//output_size是压缩后的大小
    if (compressed_buffer == nullptr) {
      return false;
    }
    storage_ = std::move(compressed_buffer);//重新将压缩后的compressed_buffer赋值给storage_
    ptr_current_ = storage_.get() + output_size;//指针偏移,指向压缩数据末尾
    ptr_end_ = ptr_current_;//代表已经写完了
    return true;
  }

// Deflate the input buffer `in_buffer`. It returns a buffer of
// compressed data for the input buffer of `*compressed_data_size` size.
std::unique_ptr<uint8_t[]> DeflateBuffer(ArrayRef<const uint8_t> in_buffer,
                                          uint32_t* compressed_data_size) {
  z_stream strm;
  strm.zalloc = Z_NULL;
  strm.zfree = Z_NULL;
  strm.opaque = Z_NULL;
  int init_ret = deflateInit(&strm, 1);//1是压缩类型,#define Z_BEST_SPEED             1,此处代表的是最快的速度
  if (init_ret != Z_OK) {
    return nullptr;
  }

  uint32_t out_size = dchecked_integral_cast<uint32_t>(deflateBound(&strm, in_buffer.size()));

  std::unique_ptr<uint8_t[]> compressed_buffer(new uint8_t[out_size]);
  strm.avail_in = in_buffer.size();
  strm.next_in = const_cast<uint8_t*>(in_buffer.data());
  strm.avail_out = out_size;
  strm.next_out = &compressed_buffer[0];
  int ret = deflate(&strm, Z_FINISH);//deflate是具体的压缩实现
  if (ret == Z_STREAM_ERROR) {
    return nullptr;
  }
  *compressed_data_size = out_size - strm.avail_out;

  int end_ret = deflateEnd(&strm);
  if (end_ret != Z_OK) {
    return nullptr;
  }

  return compressed_buffer;
}
*/

buffer.Deflate()通过zip压缩算法转换后的值如下,一共有17个

09-30 21:58:15.080 9483 9483 E : ProfileReaderV15 4_3 dataOrg[0] = 0x78
09-30 21:58:15.080 9483 9483 E : ProfileReaderV15 4_3 dataOrg[1] = 0x1
09-30 21:58:15.080 9483 9483 E : ProfileReaderV15 4_3 dataOrg[2] = 0x63
09-30 21:58:15.080 9483 9483 E : ProfileReaderV15 4_3 dataOrg[3] = 0x60
09-30 21:58:15.080 9483 9483 E : ProfileReaderV15 4_3 dataOrg[4] = 0x30
09-30 21:58:15.081 9483 9483 E : ProfileReaderV15 4_3 dataOrg[5] = 0x66
09-30 21:58:15.081 9483 9483 E : ProfileReaderV15 4_3 dataOrg[6] = 0xe0
09-30 21:58:15.081 9483 9483 E : ProfileReaderV15 4_3 dataOrg[7] = 0x65
09-30 21:58:15.081 9483 9483 E : ProfileReaderV15 4_3 dataOrg[8] = 0x60
09-30 21:58:15.081 9483 9483 E : ProfileReaderV15 4_3 dataOrg[9] = 0xa4
09-30 21:58:15.081 9483 9483 E : ProfileReaderV15 4_3 dataOrg[10] = 0x3
09-30 21:58:15.081 9483 9483 E : ProfileReaderV15 4_3 dataOrg[11] = 0x4
09-30 21:58:15.081 9483 9483 E : ProfileReaderV15 4_3 dataOrg[12] = 0x0
09-30 21:58:15.081 9483 9483 E : ProfileReaderV15 4_3 dataOrg[13] = 0x24
09-30 21:58:15.081 9483 9483 E : ProfileReaderV15 4_3 dataOrg[14] = 0x46
09-30 21:58:15.082 9483 9483 E : ProfileReaderV15 4_3 dataOrg[15] = 0x0
09-30 21:58:15.082 9483 9483 E : ProfileReaderV15 4_3 dataOrg[16] = 0x73

WriteBuffer写入prof的位置如下
在这里插入图片描述

使用'profman --profile-file=primary.prof --dump-only'可以查看数据,如class数据是13到63和WriteClassSet写入的内容是一样的

1|unknown:/data/misc/profiles/ref/com.android.cts.dexmetadata.splitapp # profman --profile-file=primary.prof --dump-only
=== Dex files ===
=== profile ===
ProfileInfo [015]
base.apk [index=0] [checksum=26f4c2f3] [num_type_ids=73] [num_method_ids=58]
hot methods: 5[],
startup methods: 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
post startup methods: 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
classes: 13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,

8.5 写入methods section

写入方法段落

8.5.1 SafeBuffer buffer初始化

这里面就是cts出问题的地方,我们就将每个步骤的数据都打出来看一下

  // Write the methods section.写入方法段落的内容
  if (methods_section_size != 0u) {
    SafeBuffer buffer(methods_section_size);//构建SafeBuffer,methods_section_size是27

    for (const std::unique_ptr<DexFileData>& dex_data : info_) {
      dex_data->WriteMethods(buffer);//往SafeBuffer写入Methods
    }

    if (!buffer.Deflate()) {//压缩SafeBuffer的数据
      return false;
    }

    //向prof里面写入信息
    if (!WriteBuffer(fd, buffer.Get(), buffer.Size())) {
      return false;
    }
    //添加kMethods=3段落的信息,一会会写在prof头部
    add_section_info(FileSectionType::kMethods, buffer.Size(), methods_section_size);
  }

SafeBuffer buffer(methods_section_size)初始化的时候是有内容的

10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 0, PrintBuffer = 0, char =
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 1, PrintBuffer = 0, char =
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 2, PrintBuffer = 21, char =
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 3, PrintBuffer = 0, char =
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 4, PrintBuffer = 0, char =
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 5, PrintBuffer = 0, char =
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 6, PrintBuffer = 7, char =
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 7, PrintBuffer = 0, char =
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 8, PrintBuffer = 224, char =
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 9, PrintBuffer = 255, char = �
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 10, PrintBuffer = 255, char = �
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 11, PrintBuffer = 255, char = �
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 12, PrintBuffer = 255, char = �
10-08 15:41:48.352 19955 19955 I profman : PrintBuffer 1 ij = 13, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 14, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 15, PrintBuffer = 1, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 16, PrintBuffer = 98, char = b
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 17, PrintBuffer = 97, char = a
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 18, PrintBuffer = 115, char = s
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 19, PrintBuffer = 101, char = e
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 20, PrintBuffer = 46, char = .
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 21, PrintBuffer = 97, char = a
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 22, PrintBuffer = 112, char = p //初始化0x70
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 23, PrintBuffer = 107, char = k
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 24, PrintBuffer = 14, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 25, PrintBuffer = 13, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 26, PrintBuffer = 126, char = ~

SafeBuffer 的初始化过程,针对内容的只是一个new uint8_t[size],上面写入class段落时也是一样的,暂时看不出问题,继续往下看

class ProfileCompilationInfo::SafeBuffer {
 public:
  SafeBuffer()
      : storage_(nullptr),
        ptr_current_(nullptr),
        ptr_end_(nullptr) {}

  explicit SafeBuffer(size_t size)
      : storage_(new uint8_t[size]), //这里去new的对象
        ptr_current_(storage_.get()),
        ptr_end_(ptr_current_ + size) {}

8.5.2 WriteMethods往SafeBuffer写入Methods

往SafeBuffer写入Methodsdex_data->WriteMethods(buffer)

1、MethodsDataSize获取热点函数类型method_flags包括kFlagHot、kFlagStartup、kFlagPostStartup,占用的位数saved_bitmap_bit_size = 116

void ProfileCompilationInfo::DexFileData::WriteMethods(SafeBuffer& buffer) const {
  uint16_t method_flags;
  size_t saved_bitmap_bit_size;
  //获取包含的热点函数类型method_flags = 7, 占用的位数saved_bitmap_bit_size = 116
  uint32_t methods_data_size = MethodsDataSize(&method_flags, &saved_bitmap_bit_size);

/*
uint32_t ProfileCompilationInfo::DexFileData::MethodsDataSize(
     uint16_t* method_flags,
     size_t* saved_bitmap_bit_size) const {
  uint16_t local_method_flags = GetUsedBitmapFlags();//GetUsedBitmapFlags() = 6,包括热点函数类型kFlagStartup = 2, kFlagPostStartup = 4
  //num_method_ids是58,POPCOUNT是返回2进制里面有多少个1,此处是2个,也就是58*2=116
  size_t local_saved_bitmap_bit_size = POPCOUNT(local_method_flags) * num_method_ids;

  if (!method_map.empty()) {
    local_method_flags |= enum_cast<uint16_t>(MethodHotness::kFlagHot);//包括kFlagHot=1的类型
  }

  size_t size = 0u;
  if (local_method_flags != 0u) {
    size_t num_hot_methods = method_map.size();
    size_t num_dex_pc_entries = 0u;
    size_t num_class_entries = 0u;
    for (const auto& method_entry : method_map) {
      const InlineCacheMap& inline_cache_map = method_entry.second;
      num_dex_pc_entries += inline_cache_map.size();
      for (const auto& inline_cache_entry : inline_cache_map) {
        const DexPcData& dex_pc_data = inline_cache_entry.second;
        num_class_entries += dex_pc_data.classes.size();
      }
    }

    constexpr size_t kPerHotMethodSize =  //4
        sizeof(uint16_t) +  // Method index diff.
        sizeof(uint16_t);   // Inline cache size. 
    constexpr size_t kPerDexPcEntrySize =  //3
        sizeof(uint16_t) +  // Dex PC.
        sizeof(uint8_t);    // Number of inline cache classes.
    constexpr size_t kPerClassEntrySize =  //2
        sizeof(uint16_t);   // Type index diff.

// using ProfileIndexType = uint16_t;

    size = sizeof(ProfileIndexType) +                 // Which dex file.  //2
           sizeof(uint32_t) +                         // Total size of following data.  //4
           sizeof(uint16_t) +                         // Method flags.  //2
           saved_bitmap_byte_size +                   // Bitmap data.  //15
           num_hot_methods * kPerHotMethodSize +      // Data for hot methods.  //4
           num_dex_pc_entries * kPerDexPcEntrySize +  // Data for dex pc entries.  //0
           num_class_entries * kPerClassEntrySize;    // Data for inline cache class entries.  //0

//10-08 15:41:48.348 19955 19955 I profman : DexFileData::MethodsDataSize 3 size = 27, num_hot_methods = 1, num_dex_pc_entries = 0, num_class_entries = 0, kPerHotMethodSize = 4, kPerDexPcEntrySize = 3, kPerClassEntrySize = 2, saved_bitmap_byte_size = 15

  if (method_flags != nullptr) {
    *method_flags = local_method_flags;
  }
  if (saved_bitmap_bit_size != nullptr) {
    *saved_bitmap_bit_size = local_saved_bitmap_bit_size;
  }
  return size;
}

  //热点函数类型
  class MethodHotness {
   public:
    enum Flag {
      // Marker flag used to simplify iterations.
      kFlagFirst = 1 << 0,
      // The method is profile-hot (this is implementation specific, e.g. equivalent to JIT-warm)
      kFlagHot = 1 << 0,
      // Executed during the app startup as determined by the runtime.
      kFlagStartup = 1 << 1,
      // Executed after app startup as determined by the runtime.
      kFlagPostStartup = 1 << 2,
      // Marker flag used to simplify iterations.
      kFlagLastRegular = 1 << 2,
      // Executed by a 32bit process.
      kFlag32bit = 1 << 3,
      // Executed by a 64bit process.
      kFlag64bit = 1 << 4,
      // Executed on sensitive thread (e.g. UI).
      kFlagSensitiveThread = 1 << 5,
      // Executed during the app startup as determined by the framework (equivalent to am start).
      kFlagAmStartup = 1 << 6,
      // Executed after the app startup as determined by the framework (equivalent to am start).
      kFlagAmPostStartup = 1 << 7,
      // Executed during system boot.
      kFlagBoot = 1 << 8,
      // Executed after the system has booted.
      kFlagPostBoot = 1 << 9,

      // The startup bins captured the relative order of when a method become hot. There are 6
      // total bins supported and each hot method will have at least one bit set. If the profile was
      // merged multiple times more than one bit may be set as a given method may become hot at
      // various times during subsequent executions.
      // The granularity of the bins is unspecified (i.e. the runtime is free to change the
      // values it uses - this may be 100ms, 200ms etc...).
      kFlagStartupBin = 1 << 10,
      kFlagStartupMaxBin = 1 << 15,
      // Marker flag used to simplify iterations.
      kFlagLastBoot = 1 << 15,
    };

*/

2、写入2个字节的profile_index,此处是0x00 00

  if (methods_data_size == 0u) {//这里不为0
    return;  // No data to write.
  }
  DCHECK_GE(buffer.GetAvailableBytes(), methods_data_size);
  //构建SafeBuffer的时候就是用的methods_data_size=27,于是开始的时候buffer.GetAvailableBytes()=27,也就是expected_available_bytes_at_end=0
  uint32_t expected_available_bytes_at_end = buffer.GetAvailableBytes() - methods_data_size;

  // Write the profile index.
  buffer.WriteUintAndAdvance(profile_index);//写入一个uint16_t 2个字节, profile_index是0

3、写入following_data_size=21,一共4个字节,0x15 00 00 00;还有method_flags=7,2个字节,0x07 00

  // Write the total size of the following methods data (without the profile index
  // and the total size itself) for easy skipping when the dex file is filtered out.
  uint32_t following_data_size = methods_data_size - sizeof(ProfileIndexType) - sizeof(uint32_t);//methods_data_size = 27 - 2 - 4
  buffer.WriteUintAndAdvance(following_data_size);//写入uint32_t 4个字节,Little-Endian方式,following_data_size = 21,存放在低地址的一个字节处,其他为0
  // Write the used method flags.
  buffer.WriteUintAndAdvance(method_flags);//写入一个uint16_t 2个字节, method_flags = 7

4、由于saved_bitmap_bit_size是116,一共14.5个字节,BitsToBytesRoundUp之后是15个字节,按照字节存储的话需要15个字节,故saved_bitmap_byte_size=15

  // Write the bitmap data.
  size_t saved_bitmap_byte_size = BitsToBytesRoundUp(saved_bitmap_bit_size);//saved_bitmap_bit_size = 116/8 = 14.5 RoundUp = 15
  DCHECK_LE(saved_bitmap_byte_size, buffer.GetAvailableBytes());

/*
//Round的相关方法
  template<typename T>
  constexpr T RoundDown(T x, typename Identity<T>::type n) {
    DCHECK(IsPowerOfTwo(n));
    return (x & -n);
  }

  template<typename T>
  constexpr T RoundUp(T x, typename std::remove_reference<T>::type n) {
    return RoundDown(x + n - 1, n);
  }

  template<typename T>
  inline T* AlignDown(T* x, uintptr_t n) {
    return reinterpret_cast<T*>(RoundDown(reinterpret_cast<uintptr_t>(x), n));
  }

  inline static constexpr size_t BitsToBytesRoundUp(size_t num_bits) {//116, kBitsPerByte = 8
    return RoundUp(num_bits, kBitsPerByte) / kBitsPerByte;
  }
*/

5、从apk里面读取方法的位图数据,写入SafeBuffer中去,注意,最后一位之只入了4个位,半个字节.
SafeBuffer buffer写的指针前移15位,认为这些都是已经写入过的数据位。

  //写入部分是:0到(saved_bitmap_bit_size)116,写入是从SafeBuffer的当前指针ptr_current_开始
  BitMemoryRegion saved_bitmap(buffer.GetCurrentPtr(), /*bit_start=*/ 0, saved_bitmap_bit_size);
  size_t saved_bitmap_index = 0u;
  ForMethodBitmapHotnessFlags([&](MethodHotness::Flag flag) {
    if ((method_flags & flag) != 0u) {//method_flags = 7 kFlagHot, GetUsedBitmapFlags() = 6
      size_t index = FlagBitmapIndex(static_cast<MethodHotness::Flag>(flag));//flag是记录热点函数类型,此处遍历的是kFlagStartup = 2, kFlagPostStartup = 4
      BitMemoryRegion src = method_bitmap.Subregion(index * num_method_ids, num_method_ids);//num_method_ids = 58,每次记录58个,index是记录多少个58, method_bitmap是apk里面的内容构建出来的,可以认为是数据源
      saved_bitmap.StoreBits(saved_bitmap_index * num_method_ids, src, num_method_ids);//saved_bitmap_index是0和1
      ++saved_bitmap_index;
    }
  });
  DCHECK_EQ(saved_bitmap_index * num_method_ids, saved_bitmap_bit_size);//判断是否写入大小正确
  buffer.Advance(saved_bitmap_byte_size);//写的指针前移15位, saved_bitmap_byte_size = 15

/*
  //BitMemoryRegion构造函数data=SafeBuffer的当前指针ptr_current_,bit_start=0,bit_size=116
  ALWAYS_INLINE BitMemoryRegion(uint8_t* data, ssize_t bit_start, size_t bit_size) {
    // Normalize规范化 the data pointer. Note that bit_start may be negative.
    data_ = AlignDown(data + (bit_start >> kBitsPerByteLog2), kPageSize);
    bit_start_ = bit_start + kBitsPerByte * (data - data_);
    bit_size_ = bit_size;//116
  }

  //重点关注StoreBits的实现
  // Store bits from other bit region.
  ALWAYS_INLINE void StoreBits(size_t bit_offset, const BitMemoryRegion& src, size_t bit_length) {//第一次bit_offset = 0, bit_length = 58
    DCHECK_LE(bit_offset, bit_size_);
    DCHECK_LE(bit_length, bit_size_ - bit_offset);
    size_t bit = 0;
    constexpr size_t kNumBits = BitSizeOf<uint32_t>();// kNumBits = 32bit, 也就是4个字节,4*8
    for (; bit + kNumBits <= bit_length; bit += kNumBits) {
      StoreBits(bit_offset + bit, src.LoadBits(bit, kNumBits), kNumBits);//第一次是StoreBits(0, src.LoadBits(bit, kNumBits), )
    }
    size_t num_bits = bit_length - bit;
    StoreBits(bit_offset + bit, src.LoadBits(bit, num_bits), num_bits);
  }


  // 这个是最关键的地方,我们用简单的例子来描述
  // 假设我们写入value只有4位,如二进制0111, 假设bit_offset=0, 假设bit_length=4,bit_start_也假设为0
  // The least significant bit is stored in the smallest memory offset.
  ALWAYS_INLINE void StoreBits(size_t bit_offset, uint32_t value, size_t bit_length) {
    if (bit_length == 0) {
      return;
    }
    // Write data byte by byte to avoid races with other threads
    // on bytes that do not overlap with this region.
    //假设掩码mask 11111111 11111111 11111111 11111111 >> (32-4) = 00001111 11111111 11111111 11111111
    //同时假设BitSizeOf<uint32_t>() 变成BitSizeOf<uint8_t>(),也就是掩码mask将变成00001111
    uint32_t mask = std::numeric_limits<uint32_t>::max() >> (BitSizeOf<uint32_t>() - bit_length);
    size_t index = (bit_start_ + bit_offset) / kBitsPerByte; //(0 + 0) / 8 = 0
    size_t shift = (bit_start_ + bit_offset) % kBitsPerByte; //(0 + 0) % 8 = 0


    //清零操作,11110000(前面几位是没有清零的),之清除了需要设置的bit数,此处是4
    data_[index] &= ~(mask << shift);  // Clear bits.
    //将value赋值给data_[0],注意此处只有4位赋值了,也就是data_[0]= ???? 0111, ????代表原来是什么就是什么
    data_[index] |= (value << shift);  // Set bits. value是设置的内容(data_[0]=第一次0111 1000)
    size_t finished_bits = kBitsPerByte - shift;//finished_bits=8(第一次目前已经设置了8bit)
    for (int i = 1; finished_bits < bit_length; i++, finished_bits += kBitsPerByte) {//这个例子里面不涉及for里面的内容,里面也是设置位数
      data_[index + i] &= ~(mask >> finished_bits);  // Clear bits.
      data_[index + i] |= (value >> finished_bits);  // Set bits.
    }
    DCHECK_EQ(value, LoadBits(bit_offset, bit_length));
  }

*/

=> 到这里,仔细的同学们可以看出来,有半个字节字节没有写入,那问题是否出在这里,我们将此时的SafeBuffer打印出来

10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 0, PrintBuffer = 0, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 1, PrintBuffer = 0, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 2, PrintBuffer = 21, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 3, PrintBuffer = 0, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 4, PrintBuffer = 0, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 5, PrintBuffer = 0, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 6, PrintBuffer = 7, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 7, PrintBuffer = 0, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 8, PrintBuffer = 224, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 9, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 10, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 11, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 12, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 13, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 14, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 15, PrintBuffer = 129, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 16, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 17, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 18, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 19, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 20, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 21, PrintBuffer = 255, char = �
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 22, PrintBuffer = 119, char = w //not ok 0x77
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 23, PrintBuffer = 107, char = k
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 24, PrintBuffer = 14, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 25, PrintBuffer = 13, char =
10-08 15:41:48.353 19955 19955 I profman : PrintBuffer 1 ij = 26, PrintBuffer = 126, char = ~

还记得之前出错的日志吗?是不是就是这个119了呢?

FAILURE: arrays first differed at element [22]; expected:<7> but
was:<119> at

顺便将expected的methodsData和snapshot的methodsData都打出来,发现确实就是上面数据

expected methodsData:[0, 0, 21, 0, 0, 0, 7, 0, -32, -1, -1, -1, -1, -1, -1, -127, -1, -1, -1, -1, -1, -1, 7, 5, 0, 0, 0],
snapshot methodsData:[0, 0, 21, 0, 0, 0, 7, 0, -32, -1, -1, -1, -1, -1, -1, -127, -1, -1, -1, -1, -1, -1, 119, 5, 0, 0, 0]

那么问题已经很清晰了。就是new uint8_t[size]初始化未请0,后面写入prof的时候也没有清0导致的问题,这个不影响prof功能正常使用,但是会导致prof生成文件出现差异。

ps:
问题定位到这里,发现malloc内存分配器初始化的时候没有清零,而ok的机器使用的是scudo默认带有清零选项。 这个可以配置malloc的选项,也可以改成scudo的分配器即可修复这个问题

6、写入diff_with_last_method_index = 5(2个bit)、写入inline_cache_map.size() = 0(2个bit)

  uint16_t last_method_index = 0;
  for (const auto& method_entry : method_map) {//method_map是apk里面的源数据,这个case里面只有一个method_entry
    uint16_t method_index = method_entry.first;
    const InlineCacheMap& inline_cache_map = method_entry.second;

    // Store the difference between the method indices for better compression.
    // The SafeMap is ordered by method_id, so the difference will always be non negative.
    DCHECK_GE(method_index, last_method_index);
    uint16_t diff_with_last_method_index = method_index - last_method_index;//此处method_index = 5,last_method_index = 0
    last_method_index = method_index;//last_method_index=5
    buffer.WriteUintAndAdvance(diff_with_last_method_index);//写入2个bit, diff_with_last_method_index = 5
    // Add inline cache map size.
    buffer.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(inline_cache_map.size()));//写入2个bit, inline_cache_map.size() = 0

    //此处inline_cache_map是0,这里不进去写内容
    // Add inline cache entries.
    for (const auto& inline_cache_entry : inline_cache_map) {
      uint16_t dex_pc = inline_cache_entry.first;
      const DexPcData& dex_pc_data = inline_cache_entry.second;
      const ArenaSet<dex::TypeIndex>& classes = dex_pc_data.classes;

      // Add the dex pc.
      buffer.WriteUintAndAdvance(dex_pc);

      // Add the megamorphic/missing_types encoding if needed and continue.
      // In either cases we don't add any classes to the profiles and so there's
      // no point to continue.
      // TODO: in case we miss types there is still value to add the rest of the
      // classes. (This requires changing profile version or using a new section type.)
      if (dex_pc_data.is_missing_types) {
        // At this point the megamorphic flag should not be set.
        DCHECK(!dex_pc_data.is_megamorphic);
        DCHECK_EQ(classes.size(), 0u);
        buffer.WriteUintAndAdvance(kIsMissingTypesEncoding);
        continue;
      } else if (dex_pc_data.is_megamorphic) {
        DCHECK_EQ(classes.size(), 0u);
        buffer.WriteUintAndAdvance(kIsMegamorphicEncoding);
        continue;
      }

      DCHECK_LT(classes.size(), ProfileCompilationInfo::kIndividualInlineCacheSize);
      DCHECK_NE(classes.size(), 0u) << "InlineCache contains a dex_pc with 0 classes";

      // Add the number of classes for the dex PC.
      buffer.WriteUintAndAdvance(dchecked_integral_cast<uint8_t>(classes.size()));

      // Store the class set.
      WriteClassSet(buffer, classes);
    }
  }

  // Check if we've written the right number of bytes.
  DCHECK_EQ(buffer.GetAvailableBytes(), expected_available_bytes_at_end);
}

=> 到这里就完整的向SafeBuffer写完了Methods段落的数据

09-30 21:58:15.083 9483 9483 E : ProfileReaderV15 4_2 data[0] = 0x0
09-30 21:58:15.083 9483 9483 E : ProfileReaderV15 4_2 data[1] = 0x0
09-30 21:58:15.083 9483 9483 E : ProfileReaderV15 4_2 data[2] = 0x15
09-30 21:58:15.083 9483 9483 E : ProfileReaderV15 4_2 data[3] = 0x0
09-30 21:58:15.083 9483 9483 E : ProfileReaderV15 4_2 data[4] = 0x0
09-30 21:58:15.083 9483 9483 E : ProfileReaderV15 4_2 data[5] = 0x0
09-30 21:58:15.083 9483 9483 E : ProfileReaderV15 4_2 data[6] = 0x7
09-30 21:58:15.084 9483 9483 E : ProfileReaderV15 4_2 data[7] = 0x0
09-30 21:58:15.084 9483 9483 E : ProfileReaderV15 4_2 data[8] = 0xe0
09-30 21:58:15.084 9483 9483 E : ProfileReaderV15 4_2 data[9] = 0xff
09-30 21:58:15.084 9483 9483 E : ProfileReaderV15 4_2 data[10] = 0xff
09-30 21:58:15.084 9483 9483 E : ProfileReaderV15 4_2 data[11] = 0xff
09-30 21:58:15.084 9483 9483 E : ProfileReaderV15 4_2 data[12] = 0xff
09-30 21:58:15.084 9483 9483 E : ProfileReaderV15 4_2 data[13] = 0xff
09-30 21:58:15.084 9483 9483 E : ProfileReaderV15 4_2 data[14] = 0xff
09-30 21:58:15.084 9483 9483 E : ProfileReaderV15 4_2 data[15] = 0x81
09-30 21:58:15.084 9483 9483 E : ProfileReaderV15 4_2 data[16] = 0xff
09-30 21:58:15.085 9483 9483 E : ProfileReaderV15 4_2 data[17] = 0xff
09-30 21:58:15.085 9483 9483 E : ProfileReaderV15 4_2 data[18] = 0xff
09-30 21:58:15.085 9483 9483 E : ProfileReaderV15 4_2 data[19] = 0xff
09-30 21:58:15.085 9483 9483 E : ProfileReaderV15 4_2 data[20] = 0xff
09-30 21:58:15.085 9483 9483 E : ProfileReaderV15 4_2 data[21] = 0xff
09-30 21:58:15.085 9483 9483 E : ProfileReaderV15 4_2 data[22] = 0x77 //not ok
09-30 21:58:15.085 9483 9483 E : ProfileReaderV15 4_2 data[23] = 0x5
09-30 21:58:15.085 9483 9483 E : ProfileReaderV15 4_2 data[24] = 0x0
09-30 21:58:15.085 9483 9483 E : ProfileReaderV15 4_2 data[25] = 0x0
09-30 21:58:15.086 9483 9483 E : ProfileReaderV15 4_2 data[26] = 0x0

8.5.3 Deflate压缩数据和向prof里面写入

  // Write the methods section.写入方法段落的内容
  if (methods_section_size != 0u) {
    //...
    if (!buffer.Deflate()) {//压缩SafeBuffer的数据
      return false;
    }

    //向prof里面写入信息
    if (!WriteBuffer(fd, buffer.Get(), buffer.Size())) {
      return false;
    }
    //添加kMethods=3段落的信息,一会会写在prof头部
    add_section_info(FileSectionType::kMethods, buffer.Size(), methods_section_size);
  }

压缩后的数据如下,一共27个字节

09-30 21:58:15.086 9483 9483 E : ProfileReaderV15 4_3 dataOrg[0] = 0x78
09-30 21:58:15.086 9483 9483 E : ProfileReaderV15 4_3 dataOrg[1] = 0x1
09-30 21:58:15.086 9483 9483 E : ProfileReaderV15 4_3 dataOrg[2] = 0x63
09-30 21:58:15.086 9483 9483 E : ProfileReaderV15 4_3 dataOrg[3] = 0x60
09-30 21:58:15.086 9483 9483 E : ProfileReaderV15 4_3 dataOrg[4] = 0x10
09-30 21:58:15.086 9483 9483 E : ProfileReaderV15 4_3 dataOrg[5] = 0x65
09-30 21:58:15.086 9483 9483 E : ProfileReaderV15 4_3 dataOrg[6] = 0x60
09-30 21:58:15.086 9483 9483 E : ProfileReaderV15 4_3 dataOrg[7] = 0x60
09-30 21:58:15.087 9483 9483 E : ProfileReaderV15 4_3 dataOrg[8] = 0x60
09-30 21:58:15.087 9483 9483 E : ProfileReaderV15 4_3 dataOrg[9] = 0x67
09-30 21:58:15.087 9483 9483 E : ProfileReaderV15 4_3 dataOrg[10] = 0x78
09-30 21:58:15.087 9483 9483 E : ProfileReaderV15 4_3 dataOrg[11] = 0xf0
09-30 21:58:15.087 9483 9483 E : ProfileReaderV15 4_3 dataOrg[12] = 0x1f
09-30 21:58:15.087 9483 9483 E : ProfileReaderV15 4_3 dataOrg[13] = 0xc
09-30 21:58:15.087 9483 9483 E : ProfileReaderV15 4_3 dataOrg[14] = 0x1a
09-30 21:58:15.087 9483 9483 E : ProfileReaderV15 4_3 dataOrg[15] = 0x21
09-30 21:58:15.087 9483 9483 E : ProfileReaderV15 4_3 dataOrg[16] = 0x54
09-30 21:58:15.088 9483 9483 E : ProfileReaderV15 4_3 dataOrg[17] = 0x39
09-30 21:58:15.088 9483 9483 E : ProfileReaderV15 4_3 dataOrg[18] = 0x2b
09-30 21:58:15.088 9483 9483 E : ProfileReaderV15 4_3 dataOrg[19] = 0x3
09-30 21:58:15.088 9483 9483 E : ProfileReaderV15 4_3 dataOrg[20] = 0x3
09-30 21:58:15.088 9483 9483 E : ProfileReaderV15 4_3 dataOrg[21] = 0x3
09-30 21:58:15.088 9483 9483 E : ProfileReaderV15 4_3 dataOrg[22] = 0x0
09-30 21:58:15.088 9483 9483 E : ProfileReaderV15 4_3 dataOrg[23] = 0xab
09-30 21:58:15.088 9483 9483 E : ProfileReaderV15 4_3 dataOrg[24] = 0x3e
09-30 21:58:15.088 9483 9483 E : ProfileReaderV15 4_3 dataOrg[25] = 0xd
09-30 21:58:15.089 9483 9483 E : ProfileReaderV15 4_3 dataOrg[26] = 0xee

实际写入的prof如下图,是可以一一对应上的
在这里插入图片描述

8.6 写入section infos

section infos是段的信息

  //再次检查file_offset是否大于500000U(ForBootImage的话是25000000U)
  if (file_offset > GetSizeWarningThresholdBytes()) {
    LOG(WARNING) << "Profile data size exceeds "
        << GetSizeWarningThresholdBytes()
        << " It has " << file_offset << " bytes";
  }

  // FileHeader的内容会在最后写入,先写入section_infos_buffer,故将prof fd的游标(打开后的偏移量offset)重新置为sizeof(FileHeader)=12的位置
  // Write section infos.
  if (lseek64(fd, sizeof(FileHeader), SEEK_SET) != sizeof(FileHeader)) {
    return false;
  }

  //section_index是add_section_info中增加的,一共增加了3个,分别是kDexFiles=0/kClasses=2/kMethods=3
  //section_index * 4u * sizeof(uint32_t) = 3*4*4 = 48
  SafeBuffer section_infos_buffer(section_index * 4u * sizeof(uint32_t));
  for (size_t i = 0; i != section_index; ++i) {//遍历每一个段的DexFiles/Classes/Methods的info信息
    const FileSectionInfo& info = section_infos[i];
    section_infos_buffer.WriteUintAndAdvance(enum_cast<uint32_t>(info.GetType()));//分别是kDexFiles=0/kClasses=2/kMethods=3
    //内容开始位置GetFileOffset分别是60,84,101
    section_infos_buffer.WriteUintAndAdvance(info.GetFileOffset());
    //段落压缩后大小是24/17/27
    section_infos_buffer.WriteUintAndAdvance(info.GetFileSize());
    //段落原始数据大小是0(0的意思是未压缩,大小是上面GetFileSize的内容24)/106/27
    section_infos_buffer.WriteUintAndAdvance(info.GetInflatedSize());
  }
  DCHECK_EQ(section_infos_buffer.GetAvailableBytes(), 0u);

  //写入段落信息section_infos_buffer,上面都是按照字节写入,一共写入48个字节
  if (!WriteBuffer(fd, section_infos_buffer.Get(), section_infos_buffer.Size())) {
    return false;
  }
  
/*
class ProfileCompilationInfo::FileSectionInfo {
 /...
 private:
  FileSectionType type_;//4个字节,uint32_t
  uint32_t file_offset_;//4个字节
  uint32_t file_size_;//4个字节
  uint32_t inflated_size_;  //4个字节// If 0, do not inflate and use data from file directly.
};
*/

如下面是2进制的解释

7072 6f00 3031 3500 0300 0000 0000(section infos start) 0000 pro.015…
=> 0000 0000 : kDexFiles = 0x0 //都是section_infos_buffer写入的,一共48个字节
3c00 0000 1800 0000 0000 0000 0200 0000 <… 32 -1
=> 3c00 0000 : fileOffset = 0x3c (dexFilesData) //从第60个开始取
=> 1800 0000 : fileSize = 0x18 (dexFilesData) //大小是24,到第83个
=> 0000 0000 : inflatedSize = 0x0 (dexFilesData) //压缩大小是0
=> 0200 0000 : sectionType = 0x2 (kClasses) 下一个是class的数据
5400 0000 1100 0000 6a00 0000 0300 0000 T…j…
=> 5400 0000 : fileOffset = 0x54 (kClasses) //从84个开始取
=> 1100 0000 : fileSize = 0x11 (kClasses) //大小是17,到第100个
=> 6a00 0000 : inflatedSize = 0x6a (kClasses) //是压缩数据,解压后数据是106个
=> 0300 0000 : sectionType = 0x3 (kMethods) //下一个是kMethods的数据
6500 0000 1b00 0000 1b00 0000(section infos end) 0100(60) f3c2 //e…
=> 6500 0000 : fileOffset = 0x65 (kMethods) //从101个开始取
=> 1b00 0000 : fileSize = 0x1b (kMethods) //大小是27,到第127个
=> 1b00 0000 : inflatedSize = 0x1b (kMethods) //是压缩数据,解压后数据是27个

=> 写入prof的section infos如下图的内容
在这里插入图片描述

8.7 写入FileHeader

FileHeader是文件头的描述包含magic_、版本version_、file_section_count_段个数


  // Write header.
  //version_版本号默认是"015\0",如果是ForBootImage,则是"016\0"; magic_是"pro\0"; file_section_count_段个数是3
  FileHeader header(version_, section_index);//version_版本号是015, section_index = 3

  // FileHeader的内容会在最后写入,将prof fd的游标(打开后的偏移量offset)重新置为0的位置,从头部写入
  if (lseek(fd, 0, SEEK_SET) != 0) {
    return false;
  }

  //写入头的信息
  if (!WriteBuffer(fd, &header, sizeof(FileHeader))) {
    return false;
  }

  uint64_t total_time = NanoTime() - start;
  LOG(INFO) << " Compressed from "
                 << std::to_string(total_uncompressed_size)
                 << " to "
                 << std::to_string(file_offset);
  LOG(INFO) << " Time to save profile: " << std::to_string(total_time);
  return true;
}
/*
class ProfileCompilationInfo::FileHeader {
 /...
 private:
  // The upper bound for file section count is used to ensure that there
  // shall be no arithmetic overflow when calculating size of the header
  // with section information.
  static const uint32_t kMaxFileSectionCount;

  uint8_t magic_[4] = {0, 0, 0, 0};//4个字节,这个是"pro\0", 默认是kProfileMagic[] = { 'p', 'r', 'o', '\0' };
  uint8_t version_[4] = {0, 0, 0, 0};//4个字节,这个版本号默认是"015\0",如果是ForBootImage,则是"016\0"
  uint32_t file_section_count_ = 0u;//4个字节,这个是3
};
*/

如下面是2进制的解释

7072 6f00 3031 3500 0300 0000 0000(section_infos_buffer) 0000 pro.015…
=> 7072 6f00 : “pro\0”
=> 3031 3500 : “015\0”
=> 0300 0000 : section_count = 0x3 //都是FileHeader写入的

=> 写入prof的FileHeader如下图的内容
在这里插入图片描述

9. 解析prof的demo

参考cts代码,这个提供一个简单的java demo解析prof的内容,希望有助于大家学习

package com.example.myapplication;

import android.os.Bundle;

import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.snackbar.Snackbar;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

import android.util.ArrayMap;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.zip.Inflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;


public class MainActivity extends AppCompatActivity {

    public static ArrayMap<String, Long> sLastMusicPackage = new ArrayMap<>();

    public static ArrayList<String> sLastMusic = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);



        FloatingActionButton fab = findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
                try {
                    byte[] profileData =  extractProfileFromDexMetadata();
                    for (int i = 0; i < profileData.length; i++) {
                        Log.e("yunhen6", "yunhen6 onClick 1_1 profileData[" + i + "] 0x" + String.format("%x", profileData[i]));
                    }
                    Log.e("yunhen6", "yunhen6 onClick 1 profileData = " + Arrays.toString(profileData));
                    ProfileReaderV15 dataReader = new ProfileReaderV15(profileData);

                } catch (Exception e) {
                    Log.e("yunhen6", "yunhen6 onClick 2 Exception e = " + e);
                }

            }
        });
    }


    /** Extracts the profile bytes from the dex metadata profile. */
    private byte[] extractProfileFromDexMetadata() {
        try {
            InputStream input = getAssets().open("splitapp_ok.prof");
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            final byte[] buffer = new byte[128];
            for (int count; (count = input.read(buffer)) != -1; ) {
                bos.write(buffer, 0, count);
            }
            return bos.toByteArray();


        } catch (Exception e) {
            Log.e("yunhen6", "yunhen6 extractProfileFromDexMetadata e = " + e);
        }
        return null;
    }

    static class ProfileReaderV15 {
        byte[] dexFilesData;
        byte[] extraDescriptorsData;
        byte[] classesData;
        byte[] methodsData;

        ProfileReaderV15(byte[] bytes) throws Exception {
            ByteBuffer bb = ByteBuffer.wrap(bytes);

            // Read header.
            bb.order(ByteOrder.LITTLE_ENDIAN);//小端,一个int 4个bit
            int getResult = bb.getInt();//assertEquals(0x006f7270 /* LE "pro\0" */, bb.getInt());
            Log.e("yunhen6", "yunhen6 ProfileReaderV15 1 getInt = 0x" + Integer.toHexString(getResult));
            getResult = bb.getInt();//assertEquals(0x00353130 /* LE "015\0" */, bb.getInt());
            Log.e("yunhen6", "yunhen6 ProfileReaderV15 2 getInt = 0x" + Integer.toHexString(getResult));
            int section_count = bb.getInt();// 00 00 00 03
            Log.e("yunhen6", "yunhen6 ProfileReaderV15 3 section_count = 0x" + Integer.toHexString(section_count));


            // Mandatory dex files section.
            getResult = bb.getInt();//assertEquals(/*kDexFiles*/ 0, bb.getInt());
            Log.e("yunhen6", "yunhen6 ProfileReaderV15 4 kDexFiles = 0x" + Integer.toHexString(getResult));
            dexFilesData = readSection(bb);
            Log.e("yunhen6", "yunhen6 ProfileReaderV15 4_1 dexFilesData = " + Arrays.toString(dexFilesData));

            // Read optional sections. Assume no more than one occurrence of each known section.
            for (int i = 1; i != section_count; ++i) {
                int sectionType = bb.getInt();
                Log.e("yunhen6", "yunhen6 ProfileReaderV15 4_2 sectionType = 0x" + Integer.toHexString(sectionType));
                switch (sectionType) {
                    case 1:  // kExtraDescriptors
                        //assertTrue(extraDescriptorsData == null);
                        Log.e("yunhen6", "yunhen6 ProfileReaderV15 5 extraDescriptorsData = " + Arrays.toString(extraDescriptorsData));
                        extraDescriptorsData = readSection(bb);
                        Log.e("yunhen6", "yunhen6 ProfileReaderV15 6 extraDescriptorsData = " + Arrays.toString(extraDescriptorsData));

                        break;
                    case 2:  // kClasses
                        Log.e("yunhen6", "yunhen6 ProfileReaderV15 7 classesData = " + Arrays.toString(classesData));
                        classesData = readSection(bb);
                        Log.e("yunhen6", "yunhen6 ProfileReaderV15 8 classesData = " + Arrays.toString(classesData));

                        break;
                    case 3:  // kMethods
                        Log.e("yunhen6", "yunhen6 ProfileReaderV15 9 methodsData = " + Arrays.toString(methodsData));
                        methodsData = readSection(bb);
                        Log.e("yunhen6", "yunhen6 ProfileReaderV15 10 methodsData = " + Arrays.toString(methodsData));

                        break;
                    default:
                        // Unknown section. Skip it. New versions of ART are allowed
                        // to add sections that shall be ignored by old versions.
                        skipSection(bb);
                        break;
                }
            }
        }
//
        private byte[] readSection(ByteBuffer bb) throws Exception {
            int fileOffset = bb.getInt();//0x00 00 00 3c == 60
            int fileSize = bb.getInt();//0x00 00 00 18 == 24
            int inflatedSize = bb.getInt();//0x00 00 00 00

            Log.e("yunhen6", "yunhen6 ProfileReaderV15 1 fileOffset = 0x" + Integer.toHexString(fileOffset));
            Log.e("yunhen6", "yunhen6 ProfileReaderV15 2 fileSize = 0x" + Integer.toHexString(fileSize));
            Log.e("yunhen6", "yunhen6 ProfileReaderV15 3 inflatedSize = 0x" + Integer.toHexString(inflatedSize));

            if (inflatedSize != 0) {
                // Decompress section.
                byte[] data = new byte[inflatedSize];
                Inflater inflater = new Inflater();
                inflater.setInput(bb.array(), fileOffset, fileSize);
                int getResult = inflater.inflate(data);//assertEquals(inflatedSize, inflater.inflate(data));
                Log.e("yunhen6", "yunhen6 ProfileReaderV15 4 inflate = 0x" + Integer.toHexString(getResult));
                for (int j = 0; j < data.length; j++) {
                    Log.e("yunhen6", "yunhen6 ProfileReaderV15 4_2 data[" + j + "] = 0x" + String.format("%x", data[j]));
                }
                byte[] dataOrg = new byte[fileSize];
                System.arraycopy(bb.array(), fileOffset, dataOrg, 0, fileSize);
                for (int j = 0; j < dataOrg.length; j++) {
                    Log.e("yunhen6", "yunhen6 ProfileReaderV15 4_3 dataOrg[" + j + "] = 0x" + String.format("%x", dataOrg[j]));
                }
                return data;
            } else {
                // Copy uncompressed data.
                byte[] data = new byte[fileSize];
                System.arraycopy(bb.array(), fileOffset, data, 0, fileSize);
                Log.e("yunhen6", "yunhen6 ProfileReaderV15 5 data = " + data);
                for (int j = 0; j < data.length; j++) {
                    Log.e("yunhen6", "yunhen6 ProfileReaderV15 5_2 data[" + j + "] = 0x" + String.format("%x", data[j]));
                }
                return data;
            }
        }

        private void skipSection(ByteBuffer bb) {
            int getResult = bb.getInt();  // fileOffset
            Log.e("yunhen6", "yunhen6 skipSection 1 getInt = 0x" + Integer.toHexString(getResult));
            getResult = bb.getInt();  // fileSize
            Log.e("yunhen6", "yunhen6 skipSection 2 getInt = 0x" + Integer.toHexString(getResult));
            getResult = bb.getInt();  // inflatedSize
            Log.e("yunhen6", "yunhen6 skipSection 3 getInt = 0x" + Integer.toHexString(getResult));


        }
    }


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值