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” .
然后你可以找到具体的代码位置
public void testProfileSnapshotAfterInstall() throws Exception {
assumeProfilesAreEnabled();
boolean useProfileForS = ApiLevelUtil.isAtLeast(getDevice(), "S");
File dmBaseFile = useProfileForS ? mDmBaseFileForS : mDmBaseFile;
File dmBaseFsvSigFile = useProfileForS ? mDmBaseFsvSigFileForS : mDmBaseFsvSigFile;
String dmName = mDmBaseFile.getName();
new InstallMultiple()
.addApk(mApkBaseFile).addDm(dmBaseFile, dmBaseFsvSigFile, dmName).run();
String snapshotCmd = "cmd package snapshot-profile " + INSTALL_PACKAGE;
String result = getDevice().executeShellCommand(snapshotCmd);
assertTrue(result.trim().isEmpty());
byte[] rawDeviceProfile = extractProfileSnapshotFromDevice();
byte[] rawMetadataProfile = extractProfileFromDexMetadata(dmBaseFile);
if (useProfileForS) {
ProfileReaderV15 snapshotReader = new ProfileReaderV15(rawDeviceProfile);
ProfileReaderV15 expectedReader = new ProfileReaderV15(rawMetadataProfile);
assertArrayEquals(expectedReader.dexFilesData, snapshotReader.dexFilesData);
assertArrayEquals(expectedReader.extraDescriptorsData,
snapshotReader.extraDescriptorsData);
assertArrayEquals(expectedReader.classesData, snapshotReader.classesData);
assertArrayEquals(expectedReader.methodsData, snapshotReader.methodsData);
} else {
byte[] snapshotProfileBytes = new ProfileReaderV10(rawDeviceProfile).data;
byte[] expectedProfileBytes = new ProfileReaderV10(rawMetadataProfile).data;
assertArrayEquals(expectedProfileBytes, snapshotProfileBytes);
}
}
这个问题出错是在methodsData的内容匹配出现差异。
由于cts调试需要的环境比较多,查看cts的代码,看一下如何本地模拟
private void run(boolean expectingSuccess) throws DeviceNotAvailableException {
final ITestDevice device = mDevice;
final StringBuilder cmd = new StringBuilder();
cmd.append("pm install-create");
for (String arg : mArgs) {
cmd.append(' ').append(arg);
}
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));
}
} catch (NumberFormatException e) {
throw new IllegalStateException("Failed to parse install session: " + result);
}
if (sessionId == -1) {
throw new IllegalStateException("Failed to create install session: " + result);
}
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;
if (!device.pushFile(file, remotePath)) {
throw new IllegalStateException("Failed to push " + file);
}
cmd.setLength(0);
cmd.append("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"));
}
cmd.setLength(0);
cmd.append("pm install-commit");
cmd.append(' ').append(sessionId);
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
可以看到上面二进制文件是有差异的(左边是snapshot prof,右边是dm的prof)。
本地可以复现,调试起来就方便很多
4. snapshot-profile的流程
=> 从adb shell cmd package snapshot-profile com.android.cts.dexmetadata.splitappk 开始调查
case "snapshot-profile":
return runSnapshotProfile();
mInterface.getArtManager().snapshotRuntimeProfile(profileType, packageName,
codePath, callback, callingPackage);
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);
}
}
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();
}
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,
true,
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,
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) {
const char* profman_bin = select_execution_binary(
kProfmanPath, kProfmanDebugPath, 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");
}
uint32_t min_new_classes_percent_change = ::android::base::GetUintProperty<uint32_t>(
"dalvik.vm.bgdexopt.new-classes-percent",
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));
}
uint32_t min_new_methods_percent_change = ::android::base::GetUintProperty<uint32_t>(
"dalvik.vm.bgdexopt.new-methods-percent",
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));
}
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 ) != PackageInstaller.STATUS_SUCCESS) {
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也注释掉
# 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();
}
}
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) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
OpenApkFilesFromLocations(&dex_files);
if (!profile.UpdateProfileKeys(dex_files)) {
return kErrorFailedToUpdateProfile;
}
bool result = use_fds
? profile.Save(reference_profile_file_fd_)
: profile.Save(reference_profile_file_, nullptr);
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测试中的实际值
bool ProfileCompilationInfo::Save(int fd) {
uint64_t start = NanoTime();
ScopedTrace trace(__PRETTY_FUNCTION__);
DCHECK_GE(fd, 0);
uint64_t extra_descriptors_section_size = 0u;
if (!extra_descriptors_.empty()) {
extra_descriptors_section_size += sizeof(uint16_t);
for (const std::string& descriptor : extra_descriptors_) {
extra_descriptors_section_size += sizeof(uint16_t) + descriptor.size();
}
}
uint64_t dex_files_section_size = sizeof(ProfileIndexType);
uint64_t classes_section_size = 0u;
uint64_t methods_section_size = 0u;
DCHECK_LE(info_.size(), MaxProfileIndex());
for (const std::unique_ptr<DexFileData>& dex_data : info_) {
if (dex_data->profile_key.size() > kMaxDexFileKeyLength) {
LOG(WARNING) << "DexFileKey exceeds allocated limit";
return false;
}
dex_files_section_size +=
3 * sizeof(uint32_t) +
sizeof(uint16_t) + dex_data->profile_key.size();
classes_section_size += dex_data->ClassesDataSize();
methods_section_size += dex_data->MethodsDataSize();
}
const uint32_t file_section_count =
1u +
(extra_descriptors_section_size != 0u ? 1u : 0u) +
(classes_section_size != 0u ? 1u : 0u) +
(methods_section_size != 0u ? 1u : 0u);
uint64_t header_and_infos_size =
sizeof(FileHeader) + file_section_count * sizeof(FileSectionInfo);
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.";
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;
}
DCHECK_EQ(lseek(fd, 0, SEEK_CUR), 0);
constexpr uint32_t kMaxNumberOfSections = enum_cast<uint32_t>(FileSectionType::kNumberOfSections);
constexpr uint64_t kMaxHeaderAndInfosSize =
sizeof(FileHeader) + kMaxNumberOfSections * sizeof(FileSectionInfo);
DCHECK_LE(header_and_infos_size, kMaxHeaderAndInfosSize);
std::array<uint8_t, kMaxHeaderAndInfosSize> placeholder;
memset(placeholder.data(), 0, header_and_infos_size);
if (!WriteBuffer(fd, placeholder.data(), header_and_infos_size)) {
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;
size_t section_index = 0u;
uint32_t file_offset = header_and_infos_size;
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;
section_index += 1u;
};
{
SafeBuffer buffer(dex_files_section_size);
buffer.WriteUintAndAdvance(dchecked_integral_cast<ProfileIndexType>(info_.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);
buffer.WriteUintAndAdvance(dex_data->num_type_ids);
buffer.WriteUintAndAdvance(dex_data->num_method_ids);
buffer.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(dex_data->profile_key.size()));
buffer.WriteAndAdvance(dex_data->profile_key.c_str(), dex_data->profile_key.size());
}
DCHECK_EQ(buffer.GetAvailableBytes(), 0u);
if (!WriteBuffer(fd, buffer.Get(), dex_files_section_size)) {
return false;
}
add_section_info(FileSectionType::kDexFiles, dex_files_section_size, 0u);
}
整个dex files section写入prof的数据如下,请对应代码注释和图来理解
8.3 写入extra descriptors section
由于extra_descriptors_section_size这问题里面是0,这里只是粗略描述一下里面的大概内容
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未压缩前的实际数据
这里先解释原始写入的数据(未压缩前的实际数据)
if (classes_section_size != 0u) {
SafeBuffer buffer(classes_section_size);
for (const std::unique_ptr<DexFileData>& dex_data : info_) {
dex_data->WriteClasses(buffer);
}
写入的内容如下
//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压缩后的实际数据
if (!buffer.Deflate()) {
return false;
}
if (!WriteBuffer(fd, buffer.Get(), buffer.Size())) {
return false;
}
add_section_info(FileSectionType::kClasses, buffer.Size(), classes_section_size);
}
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出问题的地方,我们就将每个步骤的数据都打出来看一下
if (methods_section_size != 0u) {
SafeBuffer buffer(methods_section_size);
for (const std::unique_ptr<DexFileData>& dex_data : info_) {
dex_data->WriteMethods(buffer);
}
if (!buffer.Deflate()) {
return false;
}
if (!WriteBuffer(fd, buffer.Get(), buffer.Size())) {
return false;
}
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]),
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;
uint32_t methods_data_size = MethodsDataSize(&method_flags, &saved_bitmap_bit_size);
2、写入2个字节的profile_index,此处是0x00 00
if (methods_data_size == 0u) {
return;
}
DCHECK_GE(buffer.GetAvailableBytes(), methods_data_size);
uint32_t expected_available_bytes_at_end = buffer.GetAvailableBytes() - methods_data_size;
buffer.WriteUintAndAdvance(profile_index);
3、写入following_data_size=21,一共4个字节,0x15 00 00 00;还有method_flags=7,2个字节,0x07 00
uint32_t following_data_size = methods_data_size - sizeof(ProfileIndexType) - sizeof(uint32_t);
buffer.WriteUintAndAdvance(following_data_size);
buffer.WriteUintAndAdvance(method_flags);
4、由于saved_bitmap_bit_size是116,一共14.5个字节,BitsToBytesRoundUp之后是15个字节,按照字节存储的话需要15个字节,故saved_bitmap_byte_size=15
size_t saved_bitmap_byte_size = BitsToBytesRoundUp(saved_bitmap_bit_size);
DCHECK_LE(saved_bitmap_byte_size, buffer.GetAvailableBytes());
5、从apk里面读取方法的位图数据,写入SafeBuffer中去,注意,最后一位之只入了4个位,半个字节. SafeBuffer buffer写的指针前移15位,认为这些都是已经写入过的数据位。
BitMemoryRegion saved_bitmap(buffer.GetCurrentPtr(), 0, saved_bitmap_bit_size);
size_t saved_bitmap_index = 0u;
ForMethodBitmapHotnessFlags([&](MethodHotness::Flag flag) {
if ((method_flags & flag) != 0u) {
size_t index = FlagBitmapIndex(static_cast<MethodHotness::Flag>(flag));
BitMemoryRegion src = method_bitmap.Subregion(index * num_method_ids, num_method_ids);
saved_bitmap.StoreBits(saved_bitmap_index * num_method_ids, src, num_method_ids);
++saved_bitmap_index;
}
});
DCHECK_EQ(saved_bitmap_index * num_method_ids, saved_bitmap_bit_size);
buffer.Advance(saved_bitmap_byte_size);
=> 到这里,仔细的同学们可以看出来,有半个字节字节没有写入,那问题是否出在这里,我们将此时的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) {
uint16_t method_index = method_entry.first;
const InlineCacheMap& inline_cache_map = method_entry.second;
DCHECK_GE(method_index, last_method_index);
uint16_t diff_with_last_method_index = method_index - last_method_index;
last_method_index = method_index;
buffer.WriteUintAndAdvance(diff_with_last_method_index);
buffer.WriteUintAndAdvance(dchecked_integral_cast<uint16_t>(inline_cache_map.size()));
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;
buffer.WriteUintAndAdvance(dex_pc);
if (dex_pc_data.is_missing_types) {
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";
buffer.WriteUintAndAdvance(dchecked_integral_cast<uint8_t>(classes.size()));
WriteClassSet(buffer, classes);
}
}
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里面写入
if (methods_section_size != 0u) {
if (!buffer.Deflate()) {
return false;
}
if (!WriteBuffer(fd, buffer.Get(), buffer.Size())) {
return false;
}
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是段的信息
if (file_offset > GetSizeWarningThresholdBytes()) {
LOG(WARNING) << "Profile data size exceeds "
<< GetSizeWarningThresholdBytes()
<< " It has " << file_offset << " bytes";
}
if (lseek64(fd, sizeof(FileHeader), SEEK_SET) != sizeof(FileHeader)) {
return false;
}
SafeBuffer section_infos_buffer(section_index * 4u * sizeof(uint32_t));
for (size_t i = 0; i != section_index; ++i) {
const FileSectionInfo& info = section_infos[i];
section_infos_buffer.WriteUintAndAdvance(enum_cast<uint32_t>(info.GetType()));
section_infos_buffer.WriteUintAndAdvance(info.GetFileOffset());
section_infos_buffer.WriteUintAndAdvance(info.GetFileSize());
section_infos_buffer.WriteUintAndAdvance(info.GetInflatedSize());
}
DCHECK_EQ(section_infos_buffer.GetAvailableBytes(), 0u);
if (!WriteBuffer(fd, section_infos_buffer.Get(), section_infos_buffer.Size())) {
return false;
}
如下面是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_段个数
FileHeader header(version_, section_index);
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;
}
如下面是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);
}
}
});
}
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);
bb.order(ByteOrder.LITTLE_ENDIAN);
int getResult = bb.getInt();
Log.e("yunhen6", "yunhen6 ProfileReaderV15 1 getInt = 0x" + Integer.toHexString(getResult));
getResult = bb.getInt();
Log.e("yunhen6", "yunhen6 ProfileReaderV15 2 getInt = 0x" + Integer.toHexString(getResult));
int section_count = bb.getInt();
Log.e("yunhen6", "yunhen6 ProfileReaderV15 3 section_count = 0x" + Integer.toHexString(section_count));
getResult = 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));
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:
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:
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:
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:
skipSection(bb);
break;
}
}
}
private byte[] readSection(ByteBuffer bb) throws Exception {
int fileOffset = bb.getInt();
int fileSize = bb.getInt();
int inflatedSize = bb.getInt();
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) {
byte[] data = new byte[inflatedSize];
Inflater inflater = new Inflater();
inflater.setInput(bb.array(), fileOffset, fileSize);
int getResult = 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 {
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();
Log.e("yunhen6", "yunhen6 skipSection 1 getInt = 0x" + Integer.toHexString(getResult));
getResult = bb.getInt();
Log.e("yunhen6", "yunhen6 skipSection 2 getInt = 0x" + Integer.toHexString(getResult));
getResult = bb.getInt();
Log.e("yunhen6", "yunhen6 skipSection 3 getInt = 0x" + Integer.toHexString(getResult));
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
|