blob: 15f71e8fa16c29eb78aeb18bfad3af30a4513185 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include <string>
#include "arch/instruction_set.h"
#include "base/compiler_filter.h"
#include "dexopt_test.h"
#include "dexoptanalyzer.h"
namespace art {
namespace dexoptanalyzer {
class DexoptAnalyzerTest : public DexoptTest {
protected:
std::string GetDexoptAnalyzerCmd() {
std::string file_path = GetArtBinDir() + "/dexoptanalyzer";
if (kIsDebugBuild) {
file_path += 'd';
}
EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
return file_path;
}
int Analyze(const std::string& dex_file,
CompilerFilter::Filter compiler_filter,
ProfileAnalysisResult profile_analysis_result,
const char* class_loader_context,
bool downgrade = false) {
std::string dexoptanalyzer_cmd = GetDexoptAnalyzerCmd();
std::vector<std::string> argv_str;
argv_str.push_back(dexoptanalyzer_cmd);
argv_str.push_back("--dex-file=" + dex_file);
argv_str.push_back("--isa=" + std::string(GetInstructionSetString(kRuntimeISA)));
argv_str.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(compiler_filter));
argv_str.push_back("--profile-analysis-result=" +
std::to_string(static_cast<int>(profile_analysis_result)));
if (downgrade) {
argv_str.push_back("--downgrade");
}
argv_str.push_back("--runtime-arg");
argv_str.push_back(GetClassPathOption("-Xbootclasspath:", GetLibCoreDexFileNames()));
argv_str.push_back("--runtime-arg");
argv_str.push_back(GetClassPathOption("-Xbootclasspath-locations:", GetLibCoreDexLocations()));
argv_str.push_back("--image=" + GetImageLocation());
argv_str.push_back("--android-data=" + android_data_);
if (class_loader_context != nullptr) {
argv_str.push_back("--class-loader-context=" + std::string(class_loader_context));
}
std::string error;
return ExecAndReturnCode(argv_str, &error);
}
int DexoptanalyzerToOatFileAssistant(int dexoptanalyzerResult) {
switch (dexoptanalyzerResult) {
case 0: return OatFileAssistant::kNoDexOptNeeded;
case 1: return OatFileAssistant::kDex2OatFromScratch;
case 2: return OatFileAssistant::kDex2OatForBootImage;
case 3: return OatFileAssistant::kDex2OatForFilter;
case 4: return -OatFileAssistant::kDex2OatForBootImage;
case 5: return -OatFileAssistant::kDex2OatForFilter;
default: return dexoptanalyzerResult;
}
}
// Verify that the output of dexoptanalyzer for the given arguments is the same
// as the output of OatFileAssistant::GetDexOptNeeded.
void Verify(const std::string& dex_file,
CompilerFilter::Filter compiler_filter,
ProfileAnalysisResult profile_analysis_result =
ProfileAnalysisResult::kDontOptimizeSmallDelta,
bool downgrade = false,
const char* class_loader_context = "PCL[]") {
std::unique_ptr<ClassLoaderContext> context = class_loader_context == nullptr
? nullptr
: ClassLoaderContext::Create(class_loader_context);
if (context != nullptr) {
std::vector<int> context_fds;
ASSERT_TRUE(context->OpenDexFiles("", context_fds, /*only_read_checksums*/ true));
}
int dexoptanalyzerResult = Analyze(
dex_file, compiler_filter, profile_analysis_result, class_loader_context, downgrade);
dexoptanalyzerResult = DexoptanalyzerToOatFileAssistant(dexoptanalyzerResult);
OatFileAssistant oat_file_assistant(dex_file.c_str(),
kRuntimeISA,
context.get(),
/*load_executable=*/ false);
bool assume_profile_changed = profile_analysis_result == ProfileAnalysisResult::kOptimize;
int assistantResult = oat_file_assistant.GetDexOptNeeded(
compiler_filter, assume_profile_changed, downgrade);
EXPECT_EQ(assistantResult, dexoptanalyzerResult);
}
};
// The tests below exercise the same test case from oat_file_assistant_test.cc.
// Case: We have a DEX file, but no ODEX file for it.
TEST_F(DexoptAnalyzerTest, DexNoOat) {
std::string dex_location = GetScratchDir() + "/DexNoOat.jar";
Copy(GetDexSrc1(), dex_location);
Verify(dex_location, CompilerFilter::kSpeed);
Verify(dex_location, CompilerFilter::kVerify);
Verify(dex_location, CompilerFilter::kSpeedProfile);
Verify(dex_location, CompilerFilter::kSpeed,
ProfileAnalysisResult::kDontOptimizeSmallDelta, false, nullptr);
}
// Case: We have a DEX file and up-to-date ODEX file for it.
TEST_F(DexoptAnalyzerTest, OatUpToDate) {
std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
std::string odex_location = GetOdexDir() + "/OatUpToDate.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
Verify(dex_location, CompilerFilter::kSpeed);
Verify(dex_location, CompilerFilter::kVerify);
Verify(dex_location, CompilerFilter::kEverything);
Verify(dex_location, CompilerFilter::kSpeed,
ProfileAnalysisResult::kDontOptimizeSmallDelta, false, nullptr);
}
// Case: We have a DEX file and speed-profile ODEX file for it.
TEST_F(DexoptAnalyzerTest, ProfileOatUpToDate) {
std::string dex_location = GetScratchDir() + "/ProfileOatUpToDate.jar";
std::string odex_location = GetOdexDir() + "/ProfileOatUpToDate.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeedProfile);
Verify(dex_location, CompilerFilter::kSpeedProfile,
ProfileAnalysisResult::kDontOptimizeSmallDelta);
Verify(dex_location, CompilerFilter::kVerify, ProfileAnalysisResult::kDontOptimizeSmallDelta);
Verify(dex_location, CompilerFilter::kSpeedProfile, ProfileAnalysisResult::kOptimize);
Verify(dex_location, CompilerFilter::kVerify, ProfileAnalysisResult::kOptimize);
}
// Case: We have a DEX file, verify odex file for it, and we ask if it's up to date
// when the profiles are empty or full.
TEST_F(DexoptAnalyzerTest, VerifyAndEmptyProfiles) {
std::string dex_location = GetScratchDir() + "/VerifyAndEmptyProfiles.jar";
std::string odex_location = GetOdexDir() + "/VerifyAndEmptyProfiles.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerify);
// If we want to speed-profile something that was verified, do it even if
// the profile analysis returns kDontOptimizeSmallDelta (it means that we do have profile data,
// so a transition verify -> speed-profile is still worth).
ASSERT_EQ(
static_cast<int>(ReturnCode::kDex2OatForFilterOdex),
Analyze(dex_location, CompilerFilter::kSpeedProfile,
ProfileAnalysisResult::kDontOptimizeSmallDelta, "PCL[]"));
// If we want to speed-profile something that was verified but the profiles are empty,
// don't do it - there will be no gain.
ASSERT_EQ(
static_cast<int>(ReturnCode::kNoDexOptNeeded),
Analyze(dex_location, CompilerFilter::kSpeedProfile,
ProfileAnalysisResult::kDontOptimizeEmptyProfiles, "PCL[]"));
// Standard case where we need to re-compile a speed-profile because of sufficient new
// information in the profile.
ASSERT_EQ(
static_cast<int>(ReturnCode::kDex2OatForFilterOdex),
Analyze(dex_location, CompilerFilter::kSpeedProfile,
ProfileAnalysisResult::kOptimize, "PCL[]"));
}
TEST_F(DexoptAnalyzerTest, Downgrade) {
std::string dex_location = GetScratchDir() + "/Downgrade.jar";
std::string odex_location = GetOdexDir() + "/Downgrade.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kVerify);
Verify(dex_location, CompilerFilter::kSpeedProfile,
ProfileAnalysisResult::kDontOptimizeSmallDelta, true);
Verify(dex_location, CompilerFilter::kVerify,
ProfileAnalysisResult::kDontOptimizeSmallDelta, true);
}
// Case: We have a MultiDEX file and up-to-date ODEX file for it.
TEST_F(DexoptAnalyzerTest, MultiDexOatUpToDate) {
std::string dex_location = GetScratchDir() + "/MultiDexOatUpToDate.jar";
std::string odex_location = GetOdexDir() + "/MultiDexOatUpToDate.odex";
Copy(GetMultiDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
Verify(dex_location, CompilerFilter::kSpeed, ProfileAnalysisResult::kDontOptimizeSmallDelta);
}
// Case: We have a MultiDEX file where the secondary dex file is out of date.
TEST_F(DexoptAnalyzerTest, MultiDexSecondaryOutOfDate) {
std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar";
std::string odex_location = GetOdexDir() + "/MultiDexSecondaryOutOfDate.odex";
// Compile code for GetMultiDexSrc1.
Copy(GetMultiDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
// Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum
// is out of date.
Copy(GetMultiDexSrc2(), dex_location);
Verify(dex_location, CompilerFilter::kSpeed, ProfileAnalysisResult::kDontOptimizeSmallDelta);
}
// Case: We have a DEX file and an ODEX file out of date with respect to the
// dex checksum.
TEST_F(DexoptAnalyzerTest, OatDexOutOfDate) {
std::string dex_location = GetScratchDir() + "/OatDexOutOfDate.jar";
std::string odex_location = GetOdexDir() + "/OatDexOutOfDate.odex";
// We create a dex, generate an oat for it, then overwrite the dex with a
// different dex to make the oat out of date.
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
Copy(GetDexSrc2(), dex_location);
Verify(dex_location, CompilerFilter::kSpeed);
}
// Case: We have a DEX file and an ODEX file out of date with respect to the
// boot image.
TEST_F(DexoptAnalyzerTest, OatImageOutOfDate) {
std::string dex_location = GetScratchDir() + "/OatImageOutOfDate.jar";
std::string odex_location = GetOdexDir() + "/OatImageOutOfDate.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOatForTest(dex_location,
odex_location,
CompilerFilter::kSpeed,
/*with_alternate_image=*/true);
Verify(dex_location, CompilerFilter::kVerify);
Verify(dex_location, CompilerFilter::kSpeed);
}
// Case: We have a DEX file and an ODEX file, but no OAT file.
TEST_F(DexoptAnalyzerTest, DexOdexNoOat) {
std::string dex_location = GetScratchDir() + "/DexOdexNoOat.jar";
std::string odex_location = GetOdexDir() + "/DexOdexNoOat.odex";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
Verify(dex_location, CompilerFilter::kSpeed);
Verify(dex_location, CompilerFilter::kEverything);
}
// Case: We have a stripped (or resource-only) DEX file, no ODEX file and no
// OAT file. Expect: The status is kNoDexOptNeeded.
TEST_F(DexoptAnalyzerTest, ResourceOnlyDex) {
std::string dex_location = GetScratchDir() + "/ResourceOnlyDex.jar";
Copy(GetResourceOnlySrc1(), dex_location);
Verify(dex_location, CompilerFilter::kSpeed);
Verify(dex_location, CompilerFilter::kVerify);
}
// Case: We have a DEX file, an ODEX file and an OAT file.
TEST_F(DexoptAnalyzerTest, OdexOatOverlap) {
std::string dex_location = GetScratchDir() + "/OdexOatOverlap.jar";
std::string odex_location = GetOdexDir() + "/OdexOatOverlap.odex";
std::string oat_location = GetOdexDir() + "/OdexOatOverlap.oat";
Copy(GetDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kSpeed);
// Create the oat file by copying the odex so they are located in the same
// place in memory.
Copy(odex_location, oat_location);
Verify(dex_location, CompilerFilter::kSpeed);
}
// Case: Non-standard extension for dex file.
TEST_F(DexoptAnalyzerTest, LongDexExtension) {
std::string dex_location = GetScratchDir() + "/LongDexExtension.jarx";
Copy(GetDexSrc1(), dex_location);
Verify(dex_location, CompilerFilter::kSpeed);
}
// Case: Very short, non-existent Dex location.
TEST_F(DexoptAnalyzerTest, ShortDexLocation) {
std::string dex_location = "/xx";
Verify(dex_location, CompilerFilter::kSpeed);
}
// Case: We have a DEX file and up-to-date OAT file for it, and we check with
// a class loader context.
TEST_F(DexoptAnalyzerTest, ClassLoaderContext) {
std::string dex_location1 = GetScratchDir() + "/DexToAnalyze.jar";
std::string odex_location1 = GetOdexDir() + "/DexToAnalyze.odex";
std::string dex_location2 = GetScratchDir() + "/DexInContext.jar";
Copy(GetDexSrc1(), dex_location1);
Copy(GetDexSrc2(), dex_location2);
std::string class_loader_context = "PCL[" + dex_location2 + "]";
std::string class_loader_context_option = "--class-loader-context=PCL[" + dex_location2 + "]";
// Generate the odex to get the class loader context also open the dex files.
GenerateOdexForTest(dex_location1, odex_location1, CompilerFilter::kSpeed, /* compilation_reason= */ nullptr, /* extra_args= */ { class_loader_context_option });
Verify(dex_location1, CompilerFilter::kSpeed, ProfileAnalysisResult::kDontOptimizeSmallDelta,
false, class_loader_context.c_str());
}
} // namespace dexoptanalyzer
} // namespace art