art profile的问题分析
- 1. art profile生成差异的问题
- 2. cts测试内容的分析
- 3. 模拟验证
- 4. snapshot-profile的流程
- 5. prepareAppProfile的流程
- 6. art调试
- 7. profman解析执行
- 8. libprofile保存prof
- 9. 解析prof的demo
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.apkV/NativeDevice: pm install-write 1581118244 CtsDexMetadataSplitApp.dm
/data/local/tmp/CtsDexMetadataSplitApp.dmV/NativeDevice: pm install-write 1581118244
CtsDexMetadataSplitApp.dm.fsv_sig
/data/local/tmp/CtsDexMetadataSplitApp.dm.fsv_sigV/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
可以看到上面二进制文件是有差异的(左边是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);
}
}