summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/optimizing/register_allocator_linear_scan.cc2
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.h17
-rw-r--r--dexlayout/dexlayout_main.cc6
-rw-r--r--runtime/dexopt_test.cc45
-rw-r--r--runtime/dexopt_test.h2
-rw-r--r--runtime/gc/space/image_space_test.cc2
-rw-r--r--runtime/oat_file_assistant.cc334
-rw-r--r--runtime/oat_file_assistant.h83
-rw-r--r--runtime/oat_file_assistant_test.cc445
-rw-r--r--runtime/oat_file_manager.cc46
-rwxr-xr-xtest/138-duplicate-classes-check2/run19
-rw-r--r--test/677-fsi/expected.txt1
-rw-r--r--test/718-zipfile-finalizer/expected.txt0
-rw-r--r--test/718-zipfile-finalizer/info.txt2
-rw-r--r--test/718-zipfile-finalizer/src/Main.java40
-rw-r--r--tools/dexanalyze/dexanalyze_bytecode.cc24
-rw-r--r--tools/dexanalyze/dexanalyze_strings.cc312
-rw-r--r--tools/dexanalyze/dexanalyze_strings.h6
-rw-r--r--tools/jfuzz/Android.bp29
-rw-r--r--tools/jfuzz/Android.mk25
20 files changed, 539 insertions, 901 deletions
diff --git a/compiler/optimizing/register_allocator_linear_scan.cc b/compiler/optimizing/register_allocator_linear_scan.cc
index 216fb57a96..1e00003701 100644
--- a/compiler/optimizing/register_allocator_linear_scan.cc
+++ b/compiler/optimizing/register_allocator_linear_scan.cc
@@ -312,7 +312,7 @@ void RegisterAllocatorLinearScan::ProcessInstruction(HInstruction* instruction)
for (size_t safepoint_index = safepoints_.size(); safepoint_index > 0; --safepoint_index) {
HInstruction* safepoint = safepoints_[safepoint_index - 1u];
- size_t safepoint_position = safepoint->GetLifetimePosition();
+ size_t safepoint_position = SafepointPosition::ComputePosition(safepoint);
// Test that safepoints are ordered in the optimal way.
DCHECK(safepoint_index == safepoints_.size() ||
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index 97c00c9c1d..92d0b08301 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -230,12 +230,25 @@ class SafepointPosition : public ArenaObject<kArenaAllocSsaLiveness> {
: instruction_(instruction),
next_(nullptr) {}
+ static size_t ComputePosition(HInstruction* instruction) {
+ // We special case instructions emitted at use site, as their
+ // safepoint position needs to be at their use.
+ if (instruction->IsEmittedAtUseSite()) {
+ // Currently only applies to implicit null checks, which are emitted
+ // at the next instruction.
+ DCHECK(instruction->IsNullCheck()) << instruction->DebugName();
+ return instruction->GetLifetimePosition() + 2;
+ } else {
+ return instruction->GetLifetimePosition();
+ }
+ }
+
void SetNext(SafepointPosition* next) {
next_ = next;
}
size_t GetPosition() const {
- return instruction_->GetLifetimePosition();
+ return ComputePosition(instruction_);
}
SafepointPosition* GetNext() const {
@@ -922,7 +935,7 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> {
if (first_safepoint_ == nullptr) {
first_safepoint_ = last_safepoint_ = safepoint;
} else {
- DCHECK_LT(last_safepoint_->GetPosition(), safepoint->GetPosition());
+ DCHECK_LE(last_safepoint_->GetPosition(), safepoint->GetPosition());
last_safepoint_->SetNext(safepoint);
last_safepoint_ = safepoint;
}
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index 71e56d19ea..9f73347354 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -60,6 +60,7 @@ static void Usage(void) {
LOG(ERROR) << " -p : profile file name (defaults to no profile)";
LOG(ERROR) << " -s : visualize reference pattern";
LOG(ERROR) << " -t : display file section sizes";
+ LOG(ERROR) << " -u : update dex checksums";
LOG(ERROR) << " -v : verify output file is canonical to input (IR level comparison)";
LOG(ERROR) << " -w : output dex directory";
LOG(ERROR) << " -x : compact dex generation level, either 'none' or 'fast'";
@@ -85,7 +86,7 @@ int DexlayoutDriver(int argc, char** argv) {
// Parse all arguments.
while (1) {
- const int ic = getopt(argc, argv, "abcdefghil:o:p:stvw:x:");
+ const int ic = getopt(argc, argv, "abcdefghil:o:p:stuvw:x:");
if (ic < 0) {
break; // done
}
@@ -138,6 +139,9 @@ int DexlayoutDriver(int argc, char** argv) {
options.show_section_statistics_ = true;
options.verbose_ = false;
break;
+ case 'u': // update checksum
+ options.update_checksum_ = true;
+ break;
case 'v': // verify output
options.verify_output_ = true;
break;
diff --git a/runtime/dexopt_test.cc b/runtime/dexopt_test.cc
index 9e3159d5e7..127e14e239 100644
--- a/runtime/dexopt_test.cc
+++ b/runtime/dexopt_test.cc
@@ -20,6 +20,8 @@
#include <backtrace/BacktraceMap.h>
#include <gtest/gtest.h>
+#include "android-base/stringprintf.h"
+#include "android-base/strings.h"
#include "base/file_utils.h"
#include "base/mem_map.h"
#include "common_runtime_test.h"
@@ -27,6 +29,7 @@
#include "dex2oat_environment_test.h"
#include "dexopt_test.h"
#include "gc/space/image_space.h"
+#include "hidden_api.h"
namespace art {
void DexoptTest::SetUp() {
@@ -45,6 +48,46 @@ void DexoptTest::PostRuntimeCreate() {
ReserveImageSpace();
}
+static std::string ImageLocation() {
+ Runtime* runtime = Runtime::Current();
+ const std::vector<gc::space::ImageSpace*>& image_spaces =
+ runtime->GetHeap()->GetBootImageSpaces();
+ if (image_spaces.empty()) {
+ return "";
+ }
+ return image_spaces[0]->GetImageLocation();
+}
+
+bool DexoptTest::Dex2Oat(const std::vector<std::string>& args, std::string* error_msg) {
+ Runtime* runtime = Runtime::Current();
+
+ std::vector<std::string> argv;
+ argv.push_back(runtime->GetCompilerExecutable());
+ if (runtime->IsJavaDebuggable()) {
+ argv.push_back("--debuggable");
+ }
+ runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
+
+ if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) {
+ argv.push_back("--runtime-arg");
+ argv.push_back("-Xhidden-api-checks");
+ }
+
+ if (!kIsTargetBuild) {
+ argv.push_back("--host");
+ }
+
+ argv.push_back("--boot-image=" + ImageLocation());
+
+ std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
+ argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
+
+ argv.insert(argv.end(), args.begin(), args.end());
+
+ std::string command_line(android::base::Join(argv, ' '));
+ return Exec(argv, error_msg);
+}
+
void DexoptTest::GenerateOatForTest(const std::string& dex_location,
const std::string& oat_location_in,
CompilerFilter::Filter filter,
@@ -96,7 +139,7 @@ void DexoptTest::GenerateOatForTest(const std::string& dex_location,
}
std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
if (!relocate) {
// Restore the dalvik cache if needed.
diff --git a/runtime/dexopt_test.h b/runtime/dexopt_test.h
index df7181a1e7..5dff379a32 100644
--- a/runtime/dexopt_test.h
+++ b/runtime/dexopt_test.h
@@ -71,6 +71,8 @@ class DexoptTest : public Dex2oatEnvironmentTest {
// Generate a standard oat file in the oat location.
void GenerateOatForTest(const char* dex_location, CompilerFilter::Filter filter);
+ static bool Dex2Oat(const std::vector<std::string>& args, std::string* error_msg);
+
private:
// Pre-Relocate the image to a known non-zero offset so we don't have to
// deal with the runtime randomly relocating the image by 0 and messing up
diff --git a/runtime/gc/space/image_space_test.cc b/runtime/gc/space/image_space_test.cc
index d93385de3a..347af4e1fd 100644
--- a/runtime/gc/space/image_space_test.cc
+++ b/runtime/gc/space/image_space_test.cc
@@ -41,7 +41,7 @@ TEST_F(DexoptTest, ValidateOatFile) {
args.push_back("--dex-file=" + multidex1);
args.push_back("--dex-file=" + dex2);
args.push_back("--oat-file=" + oat_location);
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
std::unique_ptr<OatFile> oat(OatFile::Open(/* zip_fd */ -1,
oat_location.c_str(),
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index f7c74cc23b..e262ff7150 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -36,7 +36,6 @@
#include "exec_utils.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
-#include "hidden_api.h"
#include "image.h"
#include "oat.h"
#include "runtime.h"
@@ -182,30 +181,6 @@ bool OatFileAssistant::IsInBootClassPath() {
return false;
}
-bool OatFileAssistant::Lock(std::string* error_msg) {
- CHECK(error_msg != nullptr);
- CHECK(flock_.get() == nullptr) << "OatFileAssistant::Lock already acquired";
-
- // Note the lock will only succeed for secondary dex files and in test
- // environment.
- //
- // The lock *will fail* for all primary apks in a production environment.
- // The app does not have permissions to create locks next to its dex location
- // (be it system, data or vendor parition). We also cannot use the odex or
- // oat location for the same reasoning.
- //
- // This is best effort and if it fails it's unlikely that we will be able
- // to generate oat files anyway.
- std::string lock_file_name = dex_location_ + "." + GetInstructionSetString(isa_) + ".flock";
-
- flock_ = LockedFile::Open(lock_file_name.c_str(), error_msg);
- if (flock_.get() == nullptr) {
- unlink(lock_file_name.c_str());
- return false;
- }
- return true;
-}
-
int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target,
bool profile_changed,
bool downgrade,
@@ -221,72 +196,10 @@ int OatFileAssistant::GetDexOptNeeded(CompilerFilter::Filter target,
return -dexopt_needed;
}
-// Figure out the currently specified compile filter option in the runtime.
-// Returns true on success, false if the compiler filter is invalid, in which
-// case error_msg describes the problem.
-static bool GetRuntimeCompilerFilterOption(CompilerFilter::Filter* filter,
- std::string* error_msg) {
- CHECK(filter != nullptr);
- CHECK(error_msg != nullptr);
-
- *filter = OatFileAssistant::kDefaultCompilerFilterForDexLoading;
- for (StringPiece option : Runtime::Current()->GetCompilerOptions()) {
- if (option.starts_with("--compiler-filter=")) {
- const char* compiler_filter_string = option.substr(strlen("--compiler-filter=")).data();
- if (!CompilerFilter::ParseCompilerFilter(compiler_filter_string, filter)) {
- *error_msg = std::string("Unknown --compiler-filter value: ")
- + std::string(compiler_filter_string);
- return false;
- }
- }
- }
- return true;
-}
-
bool OatFileAssistant::IsUpToDate() {
return GetBestInfo().Status() == kOatUpToDate;
}
-OatFileAssistant::ResultOfAttemptToUpdate
-OatFileAssistant::MakeUpToDate(bool profile_changed,
- ClassLoaderContext* class_loader_context,
- std::string* error_msg) {
- // The method doesn't use zip_fd_ and directly opens dex files at dex_locations_.
- CHECK_EQ(-1, zip_fd_) << "MakeUpToDate should not be called with zip_fd";
-
- CompilerFilter::Filter target;
- if (!GetRuntimeCompilerFilterOption(&target, error_msg)) {
- return kUpdateNotAttempted;
- }
-
- OatFileInfo& info = GetBestInfo();
- // TODO(calin, jeffhao): the context should really be passed to GetDexOptNeeded: b/62269291.
- // This is actually not trivial in the current logic as it will interact with the collision
- // check:
- // - currently, if the context does not match but we have no collisions we still accept the
- // oat file.
- // - if GetDexOptNeeded would return kDex2OatFromScratch for a context mismatch and we make
- // the oat code up to date the collision check becomes useless.
- // - however, MakeUpToDate will not always succeed (e.g. for primary apks, or for dex files
- // loaded in other processes). So it boils down to how far do we want to complicate
- // the logic in order to enable the use of oat files. Maybe its time to try simplify it.
- switch (info.GetDexOptNeeded(
- target, profile_changed, /*downgrade*/ false, class_loader_context)) {
- case kNoDexOptNeeded:
- return kUpdateSucceeded;
-
- // TODO: For now, don't bother with all the different ways we can call
- // dex2oat to generate the oat file. Always generate the oat file as if it
- // were kDex2OatFromScratch.
- case kDex2OatFromScratch:
- case kDex2OatForBootImage:
- case kDex2OatForRelocation:
- case kDex2OatForFilter:
- return GenerateOatFileNoChecks(info, target, class_loader_context, error_msg);
- }
- UNREACHABLE();
-}
-
std::unique_ptr<OatFile> OatFileAssistant::GetBestOatFile() {
return GetBestInfo().ReleaseFileForUse();
}
@@ -615,243 +528,6 @@ static bool DexLocationToOdexNames(const std::string& location,
return true;
}
-// Prepare a subcomponent of the odex directory.
-// (i.e. create and set the expected permissions on the path `dir`).
-static bool PrepareDirectory(const std::string& dir, std::string* error_msg) {
- struct stat dir_stat;
- if (TEMP_FAILURE_RETRY(stat(dir.c_str(), &dir_stat)) == 0) {
- // The directory exists. Check if it is indeed a directory.
- if (!S_ISDIR(dir_stat.st_mode)) {
- *error_msg = dir + " is not a dir";
- return false;
- } else {
- // The dir is already on disk.
- return true;
- }
- }
-
- // Failed to stat. We need to create the directory.
- if (errno != ENOENT) {
- *error_msg = "Could not stat isa dir " + dir + ":" + strerror(errno);
- return false;
- }
-
- mode_t mode = S_IRWXU | S_IXGRP | S_IXOTH;
- if (mkdir(dir.c_str(), mode) != 0) {
- *error_msg = "Could not create dir " + dir + ":" + strerror(errno);
- return false;
- }
- if (chmod(dir.c_str(), mode) != 0) {
- *error_msg = "Could not create the oat dir " + dir + ":" + strerror(errno);
- return false;
- }
- return true;
-}
-
-// Prepares the odex directory for the given dex location.
-static bool PrepareOdexDirectories(const std::string& dex_location,
- const std::string& expected_odex_location,
- InstructionSet isa,
- std::string* error_msg) {
- std::string actual_odex_location;
- std::string oat_dir;
- std::string isa_dir;
- if (!DexLocationToOdexNames(
- dex_location, isa, &actual_odex_location, &oat_dir, &isa_dir, error_msg)) {
- return false;
- }
- DCHECK_EQ(expected_odex_location, actual_odex_location);
-
- if (!PrepareDirectory(oat_dir, error_msg)) {
- return false;
- }
- if (!PrepareDirectory(isa_dir, error_msg)) {
- return false;
- }
- return true;
-}
-
-class Dex2oatFileWrapper {
- public:
- explicit Dex2oatFileWrapper(File* file)
- : file_(file),
- unlink_file_at_destruction_(true) {
- }
-
- ~Dex2oatFileWrapper() {
- if (unlink_file_at_destruction_ && (file_ != nullptr)) {
- file_->Erase(/*unlink*/ true);
- }
- }
-
- File* GetFile() { return file_.get(); }
-
- void DisableUnlinkAtDestruction() {
- unlink_file_at_destruction_ = false;
- }
-
- private:
- std::unique_ptr<File> file_;
- bool unlink_file_at_destruction_;
-};
-
-OatFileAssistant::ResultOfAttemptToUpdate OatFileAssistant::GenerateOatFileNoChecks(
- OatFileAssistant::OatFileInfo& info,
- CompilerFilter::Filter filter,
- const ClassLoaderContext* class_loader_context,
- std::string* error_msg) {
- CHECK(error_msg != nullptr);
-
- Runtime* runtime = Runtime::Current();
- if (!runtime->IsDex2OatEnabled()) {
- *error_msg = "Generation of oat file for dex location " + dex_location_
- + " not attempted because dex2oat is disabled.";
- return kUpdateNotAttempted;
- }
-
- if (info.Filename() == nullptr) {
- *error_msg = "Generation of oat file for dex location " + dex_location_
- + " not attempted because the oat file name could not be determined.";
- return kUpdateNotAttempted;
- }
- const std::string& oat_file_name = *info.Filename();
- const std::string& vdex_file_name = GetVdexFilename(oat_file_name);
-
- // dex2oat ignores missing dex files and doesn't report an error.
- // Check explicitly here so we can detect the error properly.
- // TODO: Why does dex2oat behave that way?
- struct stat dex_path_stat;
- if (TEMP_FAILURE_RETRY(stat(dex_location_.c_str(), &dex_path_stat)) != 0) {
- *error_msg = "Could not access dex location " + dex_location_ + ":" + strerror(errno);
- return kUpdateNotAttempted;
- }
-
- // If this is the odex location, we need to create the odex file layout (../oat/isa/..)
- if (!info.IsOatLocation()) {
- if (!PrepareOdexDirectories(dex_location_, oat_file_name, isa_, error_msg)) {
- return kUpdateNotAttempted;
- }
- }
-
- // Set the permissions for the oat and the vdex files.
- // The user always gets read and write while the group and others propagate
- // the reading access of the original dex file.
- mode_t file_mode = S_IRUSR | S_IWUSR |
- (dex_path_stat.st_mode & S_IRGRP) |
- (dex_path_stat.st_mode & S_IROTH);
-
- Dex2oatFileWrapper vdex_file_wrapper(OS::CreateEmptyFile(vdex_file_name.c_str()));
- File* vdex_file = vdex_file_wrapper.GetFile();
- if (vdex_file == nullptr) {
- *error_msg = "Generation of oat file " + oat_file_name
- + " not attempted because the vdex file " + vdex_file_name
- + " could not be opened.";
- return kUpdateNotAttempted;
- }
-
- if (fchmod(vdex_file->Fd(), file_mode) != 0) {
- *error_msg = "Generation of oat file " + oat_file_name
- + " not attempted because the vdex file " + vdex_file_name
- + " could not be made world readable.";
- return kUpdateNotAttempted;
- }
-
- Dex2oatFileWrapper oat_file_wrapper(OS::CreateEmptyFile(oat_file_name.c_str()));
- File* oat_file = oat_file_wrapper.GetFile();
- if (oat_file == nullptr) {
- *error_msg = "Generation of oat file " + oat_file_name
- + " not attempted because the oat file could not be created.";
- return kUpdateNotAttempted;
- }
-
- if (fchmod(oat_file->Fd(), file_mode) != 0) {
- *error_msg = "Generation of oat file " + oat_file_name
- + " not attempted because the oat file could not be made world readable.";
- return kUpdateNotAttempted;
- }
-
- std::vector<std::string> args;
- args.push_back("--dex-file=" + dex_location_);
- args.push_back("--output-vdex-fd=" + std::to_string(vdex_file->Fd()));
- args.push_back("--oat-fd=" + std::to_string(oat_file->Fd()));
- args.push_back("--oat-location=" + oat_file_name);
- args.push_back("--compiler-filter=" + CompilerFilter::NameOfFilter(filter));
- const std::string dex2oat_context = class_loader_context == nullptr
- ? OatFile::kSpecialSharedLibrary
- : class_loader_context->EncodeContextForDex2oat(/*base_dir*/ "");
- args.push_back("--class-loader-context=" + dex2oat_context);
-
- if (!Dex2Oat(args, error_msg)) {
- return kUpdateFailed;
- }
-
- if (vdex_file->FlushCloseOrErase() != 0) {
- *error_msg = "Unable to close vdex file " + vdex_file_name;
- return kUpdateFailed;
- }
-
- if (oat_file->FlushCloseOrErase() != 0) {
- *error_msg = "Unable to close oat file " + oat_file_name;
- return kUpdateFailed;
- }
-
- // Mark that the odex file has changed and we should try to reload.
- info.Reset();
- // We have compiled successfully. Disable the auto-unlink.
- vdex_file_wrapper.DisableUnlinkAtDestruction();
- oat_file_wrapper.DisableUnlinkAtDestruction();
-
- return kUpdateSucceeded;
-}
-
-bool OatFileAssistant::Dex2Oat(const std::vector<std::string>& args,
- std::string* error_msg) {
- Runtime* runtime = Runtime::Current();
- std::string image_location = ImageLocation();
- if (image_location.empty()) {
- *error_msg = "No image location found for Dex2Oat.";
- return false;
- }
-
- std::vector<std::string> argv;
- argv.push_back(runtime->GetCompilerExecutable());
- if (runtime->IsJavaDebuggable()) {
- argv.push_back("--debuggable");
- }
- runtime->AddCurrentRuntimeFeaturesAsDex2OatArguments(&argv);
-
- if (!runtime->IsVerificationEnabled()) {
- argv.push_back("--compiler-filter=verify-none");
- }
-
- if (runtime->GetHiddenApiEnforcementPolicy() != hiddenapi::EnforcementPolicy::kNoChecks) {
- argv.push_back("--runtime-arg");
- argv.push_back("-Xhidden-api-checks");
- }
-
- if (runtime->MustRelocateIfPossible()) {
- argv.push_back("--runtime-arg");
- argv.push_back("-Xrelocate");
- } else {
- argv.push_back("--runtime-arg");
- argv.push_back("-Xnorelocate");
- }
-
- if (!kIsTargetBuild) {
- argv.push_back("--host");
- }
-
- argv.push_back("--boot-image=" + image_location);
-
- std::vector<std::string> compiler_options = runtime->GetCompilerOptions();
- argv.insert(argv.end(), compiler_options.begin(), compiler_options.end());
-
- argv.insert(argv.end(), args.begin(), args.end());
-
- std::string command_line(android::base::Join(argv, ' '));
- return Exec(argv, error_msg);
-}
-
bool OatFileAssistant::DexLocationToOdexFilename(const std::string& location,
InstructionSet isa,
std::string* odex_filename,
@@ -885,16 +561,6 @@ bool OatFileAssistant::DexLocationToOatFilename(const std::string& location,
return GetDalvikCacheFilename(location.c_str(), cache_dir.c_str(), oat_filename, error_msg);
}
-std::string OatFileAssistant::ImageLocation() {
- Runtime* runtime = Runtime::Current();
- const std::vector<gc::space::ImageSpace*>& image_spaces =
- runtime->GetHeap()->GetBootImageSpaces();
- if (image_spaces.empty()) {
- return "";
- }
- return image_spaces[0]->GetImageLocation();
-}
-
const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() {
if (!required_dex_checksums_attempted_) {
required_dex_checksums_attempted_ = true;
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index a6d0961835..dbfbdf9fbc 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -48,11 +48,6 @@ class ImageSpace;
// dex location is in the boot class path.
class OatFileAssistant {
public:
- // The default compile filter to use when optimizing dex file at load time if they
- // are out of date.
- static const CompilerFilter::Filter kDefaultCompilerFilterForDexLoading =
- CompilerFilter::kQuicken;
-
enum DexOptNeeded {
// No dexopt should (or can) be done to update the apk/jar.
// Matches Java: dalvik.system.DexFile.NO_DEXOPT_NEEDED = 0
@@ -144,24 +139,6 @@ class OatFileAssistant {
// path.
bool IsInBootClassPath();
- // Obtains a lock on the target oat file.
- // Only one OatFileAssistant object can hold the lock for a target oat file
- // at a time. The Lock is released automatically when the OatFileAssistant
- // object goes out of scope. The Lock() method must not be called if the
- // lock has already been acquired.
- //
- // Returns true on success.
- // Returns false on error, in which case error_msg will contain more
- // information on the error.
- //
- // The 'error_msg' argument must not be null.
- //
- // This is intended to be used to avoid race conditions when multiple
- // processes generate oat files, such as when a foreground Activity and
- // a background Service both use DexClassLoaders pointing to the same dex
- // file.
- bool Lock(std::string* error_msg);
-
// Return what action needs to be taken to produce up-to-date code for this
// dex location. If "downgrade" is set to false, it verifies if the current
// compiler filter is at least as good as an oat file generated with the
@@ -187,33 +164,6 @@ class OatFileAssistant {
// irrespective of the compiler filter of the up-to-date code.
bool IsUpToDate();
- // Return code used when attempting to generate updated code.
- enum ResultOfAttemptToUpdate {
- kUpdateFailed, // We tried making the code up to date, but
- // encountered an unexpected failure.
- kUpdateNotAttempted, // We wanted to update the code, but determined we
- // should not make the attempt.
- kUpdateSucceeded // We successfully made the code up to date
- // (possibly by doing nothing).
- };
-
- // Attempts to generate or relocate the oat file as needed to make it up to
- // date based on the current runtime and compiler options.
- // profile_changed should be true to indicate the profile has recently
- // changed for this dex location.
- //
- // If the dex files need to be made up to date, class_loader_context will be
- // passed to dex2oat.
- //
- // Returns the result of attempting to update the code.
- //
- // If the result is not kUpdateSucceeded, the value of error_msg will be set
- // to a string describing why there was a failure or the update was not
- // attempted. error_msg must not be null.
- ResultOfAttemptToUpdate MakeUpToDate(bool profile_changed,
- ClassLoaderContext* class_loader_context,
- std::string* error_msg);
-
// Returns an oat file that can be used for loading dex files.
// Returns null if no suitable oat file was found.
//
@@ -284,18 +234,6 @@ class OatFileAssistant {
// Returns the status of the oat file for the dex location.
OatStatus OatFileStatus();
- // Executes dex2oat using the current runtime configuration overridden with
- // the given arguments. This does not check to see if dex2oat is enabled in
- // the runtime configuration.
- // Returns true on success.
- //
- // If there is a failure, the value of error_msg will be set to a string
- // describing why there was failure. error_msg must not be null.
- //
- // TODO: The OatFileAssistant probably isn't the right place to have this
- // function.
- static bool Dex2Oat(const std::vector<std::string>& args, std::string* error_msg);
-
// Constructs the odex file name for the given dex location.
// Returns true on success, in which case odex_filename is set to the odex
// file name.
@@ -436,20 +374,6 @@ class OatFileAssistant {
bool file_released_ = false;
};
- // Generate the oat file for the given info from the dex file using the
- // current runtime compiler options, the specified filter and class loader
- // context.
- // This does not check the current status before attempting to generate the
- // oat file.
- //
- // If the result is not kUpdateSucceeded, the value of error_msg will be set
- // to a string describing why there was a failure or the update was not
- // attempted. error_msg must not be null.
- ResultOfAttemptToUpdate GenerateOatFileNoChecks(OatFileInfo& info,
- CompilerFilter::Filter target,
- const ClassLoaderContext* class_loader_context,
- std::string* error_msg);
-
// Return info for the best oat file.
OatFileInfo& GetBestInfo();
@@ -473,13 +397,6 @@ class OatFileAssistant {
// location.
OatStatus GivenOatFileStatus(const OatFile& file);
- // Returns the current image location.
- // Returns an empty string if the image location could not be retrieved.
- //
- // TODO: This method should belong with an image file manager, not
- // the oat file assistant.
- static std::string ImageLocation();
-
// Gets the dex checksums required for an up-to-date oat file.
// Returns cached_required_dex_checksums if the required checksums were
// located. Returns null if the required checksums were not found. The
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 0b3c61d474..5889f8cc3e 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -41,11 +41,6 @@
namespace art {
-static const std::string kSpecialSharedLibrary = "&"; // NOLINT [runtime/string] [4]
-static ClassLoaderContext* kSpecialSharedLibraryContext = nullptr;
-
-static constexpr char kDex2oatCmdLineHiddenApiArg[] = " --runtime-arg -Xhidden-api-checks";
-
class OatFileAssistantTest : public DexoptTest {
public:
void VerifyOptimizationStatus(const std::string& file,
@@ -109,6 +104,97 @@ static bool IsExecutedAsRoot() {
return geteuid() == 0;
}
+// Case: We have a MultiDEX file and up-to-date ODEX file for it with relative
+// encoded dex locations.
+// Expect: The oat file status is kNoDexOptNeeded.
+TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) {
+ std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar";
+ std::string odex_location = GetOdexDir() + "/RelativeEncodedDexLocation.odex";
+
+ // Create the dex file
+ Copy(GetMultiDexSrc1(), dex_location);
+
+ // Create the oat file with relative encoded dex location.
+ std::vector<std::string> args = {
+ "--dex-file=" + dex_location,
+ "--dex-location=" + std::string("RelativeEncodedDexLocation.jar"),
+ "--oat-file=" + odex_location,
+ "--compiler-filter=speed"
+ };
+
+ std::string error_msg;
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+
+ // Verify we can load both dex files.
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
+
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(oat_file.get() != nullptr);
+ EXPECT_TRUE(oat_file->IsExecutable());
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
+ EXPECT_EQ(2u, dex_files.size());
+}
+
+TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ std::string context_location = GetScratchDir() + "/ContextDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+ Copy(GetDexSrc2(), context_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ std::string context_str = "PCL[" + context_location + "]";
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
+ ASSERT_TRUE(context != nullptr);
+ ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
+
+ std::string error_msg;
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + odex_location);
+ args.push_back("--class-loader-context=" + context_str);
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+
+ std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
+ EXPECT_NE(nullptr, oat_file.get());
+ EXPECT_EQ(context->EncodeContextForOatFile(""),
+ oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
+}
+
+TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) {
+ std::string dex_location = GetScratchDir() + "/TestDex.jar";
+ std::string odex_location = GetOdexDir() + "/TestDex.odex";
+ std::string context_location = GetScratchDir() + "/ContextDex.jar";
+ Copy(GetDexSrc1(), dex_location);
+ Copy(GetDexSrc2(), context_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
+
+ std::string context_str = "PCL[" + context_location + "]";
+ std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
+ ASSERT_TRUE(context != nullptr);
+ ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
+
+ std::string error_msg;
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location);
+ args.push_back("--oat-file=" + odex_location);
+ args.push_back("--class-loader-context=" + context_str);
+ ASSERT_TRUE(Dex2Oat(args, &error_msg)) << error_msg;
+
+ // A relative context simulates a dependent split context.
+ std::unique_ptr<ClassLoaderContext> relative_context =
+ ClassLoaderContext::Create("PCL[ContextDex.jar]");
+ EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
+ oat_file_assistant.GetDexOptNeeded(
+ CompilerFilter::kDefaultCompilerFilter,
+ /* downgrade */ false,
+ /* profile_changed */ false,
+ relative_context.get()));
+}
+
// Case: We have a DEX file, but no OAT file for it.
// Expect: The status is kDex2OatNeeded.
TEST_F(OatFileAssistantTest, DexNoOat) {
@@ -145,11 +231,6 @@ TEST_F(OatFileAssistantTest, NoDexNoOat) {
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
- // Trying to make the oat file up to date should not fail or crash.
- std::string error_msg;
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
-
// Trying to get the best oat file should fail, but not crash.
std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
EXPECT_EQ(nullptr, oat_file.get());
@@ -584,37 +665,6 @@ TEST_F(OatFileAssistantTest, StrippedMultiDexNonMainOutOfDate) {
EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
}
-// Case: We have a MultiDEX file and up-to-date ODEX file for it with relative
-// encoded dex locations.
-// Expect: The oat file status is kNoDexOptNeeded.
-TEST_F(OatFileAssistantTest, RelativeEncodedDexLocation) {
- std::string dex_location = GetScratchDir() + "/RelativeEncodedDexLocation.jar";
- std::string odex_location = GetOdexDir() + "/RelativeEncodedDexLocation.odex";
-
- // Create the dex file
- Copy(GetMultiDexSrc1(), dex_location);
-
- // Create the oat file with relative encoded dex location.
- std::vector<std::string> args;
- args.push_back("--dex-file=" + dex_location);
- args.push_back("--dex-location=" + std::string("RelativeEncodedDexLocation.jar"));
- args.push_back("--oat-file=" + odex_location);
- args.push_back("--compiler-filter=speed");
-
- std::string error_msg;
- ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
-
- // Verify we can load both dex files.
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
-
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- ASSERT_TRUE(oat_file.get() != nullptr);
- EXPECT_TRUE(oat_file->IsExecutable());
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- dex_files = oat_file_assistant.LoadDexFiles(*oat_file, dex_location.c_str());
- EXPECT_EQ(2u, dex_files.size());
-}
-
// Case: We have a DEX file and an OAT file out of date with respect to the
// dex checksum.
TEST_F(OatFileAssistantTest, OatDexOutOfDate) {
@@ -872,13 +922,6 @@ TEST_F(OatFileAssistantTest, ResourceOnlyDex) {
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
- // Make the oat file up to date. This should have no effect.
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) <<
- error_msg;
-
EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
@@ -1037,35 +1080,6 @@ TEST_F(OatFileAssistantTest, LoadNoExecOatUpToDate) {
EXPECT_EQ(1u, dex_files.size());
}
-// Case: We don't have a DEX file and can't write the oat file.
-// Expect: We should fail to generate the oat file without crashing.
-TEST_F(OatFileAssistantTest, GenNoDex) {
- if (IsExecutedAsRoot()) {
- // We cannot simulate non writable locations when executed as root: b/38000545.
- LOG(ERROR) << "Test skipped because it's running as root";
- return;
- }
-
- std::string dex_location = GetScratchDir() + "/GenNoDex.jar";
-
- ScopedNonWritable scoped_non_writable(dex_location);
- ASSERT_TRUE(scoped_non_writable.IsSuccessful());
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, true);
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- // We should get kUpdateSucceeded from MakeUpToDate since there's nothing
- // that can be done in this situation.
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
-
- // Verify it didn't create an oat in the default location (dalvik-cache).
- OatFileAssistant ofm(dex_location.c_str(), kRuntimeISA, false);
- EXPECT_EQ(OatFileAssistant::kOatCannotOpen, ofm.OatFileStatus());
- // Verify it didn't create the odex file in the default location (../oat/isa/...odex)
- EXPECT_EQ(OatFileAssistant::kOatCannotOpen, ofm.OdexFileStatus());
-}
-
// Turn an absolute path into a path relative to the current working
// directory.
static std::string MakePathRelative(const std::string& target) {
@@ -1131,13 +1145,6 @@ TEST_F(OatFileAssistantTest, ShortDexLocation) {
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OdexFileStatus());
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
-
- // Trying to make it up to date should have no effect.
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
- EXPECT_TRUE(error_msg.empty());
}
// Case: Non-standard extension for dex file.
@@ -1156,11 +1163,12 @@ TEST_F(OatFileAssistantTest, LongDexExtension) {
EXPECT_EQ(OatFileAssistant::kOatCannotOpen, oat_file_assistant.OatFileStatus());
}
+
// A task to generate a dex location. Used by the RaceToGenerate test.
class RaceGenerateTask : public Task {
public:
- explicit RaceGenerateTask(const std::string& dex_location, const std::string& oat_location)
- : dex_location_(dex_location), oat_location_(oat_location), loaded_oat_file_(nullptr)
+ RaceGenerateTask(const std::string& dex_location, const std::string& oat_location)
+ : dex_location_(dex_location), oat_location_(oat_location), loaded_oat_file_(nullptr)
{}
void Run(Thread* self ATTRIBUTE_UNUSED) {
@@ -1169,6 +1177,15 @@ class RaceGenerateTask : public Task {
std::vector<std::unique_ptr<const DexFile>> dex_files;
std::vector<std::string> error_msgs;
const OatFile* oat_file = nullptr;
+ {
+ // Create the oat file.
+ std::vector<std::string> args;
+ args.push_back("--dex-file=" + dex_location_);
+ args.push_back("--oat-file=" + oat_location_);
+ std::string error_msg;
+ ASSERT_TRUE(DexoptTest::Dex2Oat(args, &error_msg)) << error_msg;
+ }
+
dex_files = Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(
dex_location_.c_str(),
Runtime::Current()->GetSystemClassLoader(),
@@ -1176,8 +1193,9 @@ class RaceGenerateTask : public Task {
&oat_file,
&error_msgs);
CHECK(!dex_files.empty()) << android::base::Join(error_msgs, '\n');
- CHECK(dex_files[0]->GetOatDexFile() != nullptr) << dex_files[0]->GetLocation();
- loaded_oat_file_ = dex_files[0]->GetOatDexFile()->GetOatFile();
+ if (dex_files[0]->GetOatDexFile() != nullptr) {
+ loaded_oat_file_ = dex_files[0]->GetOatDexFile()->GetOatFile();
+ }
CHECK_EQ(loaded_oat_file_, oat_file);
}
@@ -1191,12 +1209,8 @@ class RaceGenerateTask : public Task {
const OatFile* loaded_oat_file_;
};
-// Test the case where multiple processes race to generate an oat file.
-// This simulates multiple processes using multiple threads.
-//
-// We want unique Oat files to be loaded even when there is a race to load.
-// TODO: The test case no longer tests locking the way it was intended since we now get multiple
-// copies of the same Oat files mapped at different locations.
+// Test the case where dex2oat invocations race with multiple processes trying to
+// load the oat file.
TEST_F(OatFileAssistantTest, RaceToGenerate) {
std::string dex_location = GetScratchDir() + "/RaceToGenerate.jar";
std::string oat_location = GetOdexDir() + "/RaceToGenerate.oat";
@@ -1209,24 +1223,26 @@ TEST_F(OatFileAssistantTest, RaceToGenerate) {
// take a while to generate.
Copy(GetLibCoreDexFileNames()[0], dex_location);
- const int kNumThreads = 32;
+ const size_t kNumThreads = 32;
Thread* self = Thread::Current();
ThreadPool thread_pool("Oat file assistant test thread pool", kNumThreads);
std::vector<std::unique_ptr<RaceGenerateTask>> tasks;
- for (int i = 0; i < kNumThreads; i++) {
+ for (size_t i = 0; i < kNumThreads; i++) {
std::unique_ptr<RaceGenerateTask> task(new RaceGenerateTask(dex_location, oat_location));
thread_pool.AddTask(self, task.get());
tasks.push_back(std::move(task));
}
thread_pool.StartWorkers(self);
- thread_pool.Wait(self, true, false);
+ thread_pool.Wait(self, /* do_work */ true, /* may_hold_locks */ false);
- // Verify every task got a unique oat file.
+ // Verify that tasks which got an oat file got a unique one.
std::set<const OatFile*> oat_files;
for (auto& task : tasks) {
const OatFile* oat_file = task->GetLoadedOatFile();
- EXPECT_TRUE(oat_files.find(oat_file) == oat_files.end());
- oat_files.insert(oat_file);
+ if (oat_file != nullptr) {
+ EXPECT_TRUE(oat_files.find(oat_file) == oat_files.end());
+ oat_files.insert(oat_file);
+ }
}
}
@@ -1274,36 +1290,6 @@ TEST_F(OatFileAssistantNoDex2OatTest, LoadMultiDexOdexNoOat) {
EXPECT_EQ(2u, dex_files.size());
}
-TEST_F(OatFileAssistantTest, RuntimeCompilerFilterOptionUsed) {
- std::string dex_location = GetScratchDir() + "/RuntimeCompilerFilterOptionUsed.jar";
- Copy(GetDexSrc1(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-
- std::string error_msg;
- Runtime::Current()->AddCompilerOption("--compiler-filter=quicken");
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) <<
- error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
- EXPECT_EQ(-OatFileAssistant::kDex2OatForFilter,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
- Runtime::Current()->AddCompilerOption("--compiler-filter=speed");
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg))
- << error_msg;
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kQuicken));
- EXPECT_EQ(OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
-
- Runtime::Current()->AddCompilerOption("--compiler-filter=bogus");
- EXPECT_EQ(OatFileAssistant::kUpdateNotAttempted,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg));
-}
-
TEST(OatFileAssistantUtilsTest, DexLocationToOdexFilename) {
std::string error_msg;
std::string odex_file;
@@ -1350,112 +1336,6 @@ TEST_F(OatFileAssistantTest, DexOptStatusValues) {
}
}
-// Verify that when no compiler filter is passed the default one from OatFileAssistant is used.
-TEST_F(OatFileAssistantTest, DefaultMakeUpToDateFilter) {
- std::string dex_location = GetScratchDir() + "/TestDex.jar";
- Copy(GetDexSrc1(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-
- const CompilerFilter::Filter default_filter =
- OatFileAssistant::kDefaultCompilerFilterForDexLoading;
- std::string error_msg;
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded,
- oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg)) <<
- error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(default_filter));
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- EXPECT_NE(nullptr, oat_file.get());
- EXPECT_EQ(default_filter, oat_file->GetCompilerFilter());
-}
-
-TEST_F(OatFileAssistantTest, MakeUpToDateWithSpecialSharedLibrary) {
- std::string dex_location = GetScratchDir() + "/TestDex.jar";
- Copy(GetDexSrc1(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-
- const CompilerFilter::Filter default_filter =
- OatFileAssistant::kDefaultCompilerFilterForDexLoading;
- std::string error_msg;
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(default_filter));
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- EXPECT_NE(nullptr, oat_file.get());
- EXPECT_EQ(kSpecialSharedLibrary,
- oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
-}
-
-TEST_F(OatFileAssistantTest, MakeUpToDateWithContext) {
- std::string dex_location = GetScratchDir() + "/TestDex.jar";
- std::string context_location = GetScratchDir() + "/ContextDex.jar";
- Copy(GetDexSrc1(), dex_location);
- Copy(GetDexSrc2(), context_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-
- const CompilerFilter::Filter default_filter =
- OatFileAssistant::kDefaultCompilerFilterForDexLoading;
- std::string error_msg;
- std::string context_str = "PCL[" + context_location + "]";
- std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
- ASSERT_TRUE(context != nullptr);
- ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
-
- int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(default_filter, false, false, context.get()));
-
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- EXPECT_NE(nullptr, oat_file.get());
- EXPECT_EQ(context->EncodeContextForOatFile(""),
- oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kClassPathKey));
-}
-
-TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiDisabled) {
- hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
- hiddenapi::EnforcementPolicy::kNoChecks);
-
- std::string dex_location = GetScratchDir() + "/TestDexHiddenApiDisabled.jar";
- Copy(GetDexSrc1(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- std::string error_msg;
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
-
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- EXPECT_NE(nullptr, oat_file.get());
-
- const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
- EXPECT_NE(nullptr, cmd_line);
- EXPECT_EQ(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg));
-}
-
-TEST_F(OatFileAssistantTest, MakeUpToDateWithHiddenApiEnabled) {
- hiddenapi::ScopedHiddenApiEnforcementPolicySetting hiddenapi_exemption(
- hiddenapi::EnforcementPolicy::kBlacklistOnly);
-
- std::string dex_location = GetScratchDir() + "/TestDexHiddenApiEnabled.jar";
- Copy(GetDexSrc1(), dex_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- std::string error_msg;
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
-
- std::unique_ptr<OatFile> oat_file = oat_file_assistant.GetBestOatFile();
- EXPECT_NE(nullptr, oat_file.get());
-
- const char* cmd_line = oat_file->GetOatHeader().GetStoreValueByKey(OatHeader::kDex2OatCmdLineKey);
- EXPECT_NE(nullptr, cmd_line);
- EXPECT_NE(nullptr, strstr(cmd_line, kDex2oatCmdLineHiddenApiArg));
-}
-
TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
std::string dex_location = GetScratchDir() + "/TestDex.jar";
std::string context_location = GetScratchDir() + "/ContextDex.jar";
@@ -1464,19 +1344,12 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
- const CompilerFilter::Filter default_filter =
- OatFileAssistant::kDefaultCompilerFilterForDexLoading;
std::string error_msg;
std::string context_str = "PCL[" + context_location + "]";
std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
ASSERT_TRUE(context != nullptr);
ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
- int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(default_filter, false, false, context.get()));
-
// Update the context by overriding the jar file.
Copy(GetMultiDexSrc2(), context_location);
std::unique_ptr<ClassLoaderContext> updated_context = ClassLoaderContext::Create(context_str);
@@ -1484,88 +1357,10 @@ TEST_F(OatFileAssistantTest, GetDexOptNeededWithOutOfDateContext) {
// DexOptNeeded should advise compilation from scratch.
EXPECT_EQ(OatFileAssistant::kDex2OatFromScratch,
oat_file_assistant.GetDexOptNeeded(
- default_filter, false, false, updated_context.get()));
-}
-
-TEST_F(OatFileAssistantTest, GetDexOptNeededWithUpToDateContextRelative) {
- std::string dex_location = GetScratchDir() + "/TestDex.jar";
- std::string context_location = GetScratchDir() + "/ContextDex.jar";
- Copy(GetDexSrc1(), dex_location);
- Copy(GetDexSrc2(), context_location);
-
- OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, false);
-
- const CompilerFilter::Filter default_filter =
- OatFileAssistant::kDefaultCompilerFilterForDexLoading;
- std::string error_msg;
- std::string context_str = "PCL[" + context_location + "]";
- std::unique_ptr<ClassLoaderContext> context = ClassLoaderContext::Create(context_str);
- ASSERT_TRUE(context != nullptr);
- ASSERT_TRUE(context->OpenDexFiles(kRuntimeISA, ""));
-
- int status = oat_file_assistant.MakeUpToDate(false, context.get(), &error_msg);
- EXPECT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
-
- // A relative context simulates a dependent split context.
- std::unique_ptr<ClassLoaderContext> relative_context =
- ClassLoaderContext::Create("PCL[ContextDex.jar]");
- EXPECT_EQ(-OatFileAssistant::kNoDexOptNeeded,
- oat_file_assistant.GetDexOptNeeded(
- default_filter, false, false, relative_context.get()));
-}
-
-TEST_F(OatFileAssistantTest, SystemOdex) {
- std::string dex_location = GetScratchDir() + "/OatUpToDate.jar";
- std::string odex_location = GetScratchDir() + "/OatUpToDate.odex";
- std::string system_location = GetAndroidRoot() + "/OatUpToDate.jar";
-
- std::string error_msg;
-
- Copy(GetDexSrc1(), dex_location);
- EXPECT_FALSE(LocationIsOnSystem(dex_location.c_str()));
-
- {
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- true,
- false);
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
- }
-
- {
- OatFileAssistant oat_file_assistant(dex_location.c_str(),
- kRuntimeISA,
- true,
- true);
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_FALSE(oat_file_assistant.GetBestOatFile()->IsExecutable());
- }
-
- Copy(GetDexSrc1(), system_location);
- EXPECT_TRUE(LocationIsOnSystem(system_location.c_str()));
-
- {
- OatFileAssistant oat_file_assistant(system_location.c_str(),
- kRuntimeISA,
- true,
- false);
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
- }
-
- {
- OatFileAssistant oat_file_assistant(system_location.c_str(),
- kRuntimeISA,
- true,
- true);
- int status = oat_file_assistant.MakeUpToDate(false, kSpecialSharedLibraryContext, &error_msg);
- ASSERT_EQ(OatFileAssistant::kUpdateSucceeded, status) << error_msg;
- EXPECT_TRUE(oat_file_assistant.GetBestOatFile()->IsExecutable());
- }
+ CompilerFilter::kDefaultCompilerFilter,
+ /* downgrade */ false,
+ /* profile_changed */ false,
+ updated_context.get()));
}
// TODO: More Tests:
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 59a1045ba2..bcad4a3428 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -465,57 +465,15 @@ std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
!runtime->IsAotCompiler(),
only_use_system_oat_files_);
- // Lock the target oat location to avoid races generating and loading the
- // oat file.
- std::string error_msg;
- if (!oat_file_assistant.Lock(/*out*/&error_msg)) {
- // Don't worry too much if this fails. If it does fail, it's unlikely we
- // can generate an oat file anyway.
- VLOG(class_linker) << "OatFileAssistant::Lock: " << error_msg;
- }
-
- const OatFile* source_oat_file = nullptr;
-
- if (!oat_file_assistant.IsUpToDate()) {
- // Update the oat file on disk if we can, based on the --compiler-filter
- // option derived from the current runtime options.
- // This may fail, but that's okay. Best effort is all that matters here.
- // TODO(calin): b/64530081 b/66984396. Pass a null context to verify and compile
- // secondary dex files in isolation (and avoid to extract/verify the main apk
- // if it's in the class path). Note this trades correctness for performance
- // since the resulting slow down is unacceptable in some cases until b/64530081
- // is fixed.
- // We still pass the class loader context when the classpath string of the runtime
- // is not empty, which is the situation when ART is invoked standalone.
- ClassLoaderContext* actual_context = Runtime::Current()->GetClassPathString().empty()
- ? nullptr
- : context.get();
- switch (oat_file_assistant.MakeUpToDate(/*profile_changed*/ false,
- actual_context,
- /*out*/ &error_msg)) {
- case OatFileAssistant::kUpdateFailed:
- LOG(WARNING) << error_msg;
- break;
-
- case OatFileAssistant::kUpdateNotAttempted:
- // Avoid spamming the logs if we decided not to attempt making the oat
- // file up to date.
- VLOG(oat) << error_msg;
- break;
-
- case OatFileAssistant::kUpdateSucceeded:
- // Nothing to do.
- break;
- }
- }
-
// Get the oat file on disk.
std::unique_ptr<const OatFile> oat_file(oat_file_assistant.GetBestOatFile().release());
VLOG(oat) << "OatFileAssistant(" << dex_location << ").GetBestOatFile()="
<< reinterpret_cast<uintptr_t>(oat_file.get())
<< " (executable=" << (oat_file != nullptr ? oat_file->IsExecutable() : false) << ")";
+ const OatFile* source_oat_file = nullptr;
CheckCollisionResult check_collision_result = CheckCollisionResult::kPerformedHasCollisions;
+ std::string error_msg;
if ((class_loader != nullptr || dex_elements != nullptr) && oat_file != nullptr) {
// Prevent oat files from being loaded if no class_loader or dex_elements are provided.
// This can happen when the deprecated DexFile.<init>(String) is called directly, and it
diff --git a/test/138-duplicate-classes-check2/run b/test/138-duplicate-classes-check2/run
deleted file mode 100755
index 8494ad9aad..0000000000
--- a/test/138-duplicate-classes-check2/run
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# We want to run as no-dex-file-fallback to confirm that even though the -ex file has a symbolic
-# reference to A, there's no class-def, so we don't detect a collision.
-exec ${RUN} --runtime-option -Xno-dex-file-fallback "${@}"
diff --git a/test/677-fsi/expected.txt b/test/677-fsi/expected.txt
index c7fb8fed77..2b073430b6 100644
--- a/test/677-fsi/expected.txt
+++ b/test/677-fsi/expected.txt
@@ -1,3 +1,2 @@
oat file has dex code, but APK has uncompressed dex code
-oat file has dex code, but APK has uncompressed dex code
Hello World
diff --git a/test/718-zipfile-finalizer/expected.txt b/test/718-zipfile-finalizer/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/718-zipfile-finalizer/expected.txt
diff --git a/test/718-zipfile-finalizer/info.txt b/test/718-zipfile-finalizer/info.txt
new file mode 100644
index 0000000000..c8b827e63c
--- /dev/null
+++ b/test/718-zipfile-finalizer/info.txt
@@ -0,0 +1,2 @@
+Test that ZipFile.finalize doesn't throw exceptions
+in the presence of a not fully constructed instance.
diff --git a/test/718-zipfile-finalizer/src/Main.java b/test/718-zipfile-finalizer/src/Main.java
new file mode 100644
index 0000000000..3eb439b7a6
--- /dev/null
+++ b/test/718-zipfile-finalizer/src/Main.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2018 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.
+ */
+
+import java.util.zip.ZipFile;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ // By throwing an exception when setting up arguments of
+ // the constructor, we end up with a not fully constructed
+ // ZipFile.
+ try {
+ new ZipFile(null, throwException(), null);
+ throw new Error("Expected Exception");
+ } catch (Exception e) {
+ // expected
+ }
+
+ // Run finalizers. The golden file of this test checks
+ // that no exception is thrown from finalizers.
+ System.gc();
+ System.runFinalization();
+ }
+
+ public static int throwException() throws Exception {
+ throw new Exception();
+ }
+}
diff --git a/tools/dexanalyze/dexanalyze_bytecode.cc b/tools/dexanalyze/dexanalyze_bytecode.cc
index e6e58c0ecc..659a940e8b 100644
--- a/tools/dexanalyze/dexanalyze_bytecode.cc
+++ b/tools/dexanalyze/dexanalyze_bytecode.cc
@@ -264,18 +264,18 @@ void NewRegisterInstructions::ProcessCodeItem(const DexFile& dex_file,
uint32_t receiver = inst->VRegB_22c();
uint32_t first_arg_reg = code_item.RegistersSize() - code_item.InsSize();
uint32_t out_reg = inst->VRegA_22c();
- if (Enabled(kExperimentInstanceFieldSelf) &&
- first_arg_reg == receiver &&
- holder_type == current_class_type) {
- if (count_types) {
- ++current_type.fields_.FindOrAdd(dex_field_idx)->second;
- } else {
- uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx);
- ExtendPrefix(&out_reg, &field_idx);
- CHECK(InstNibbles(new_opcode, {out_reg, field_idx}));
- continue;
- }
- } else if (Enabled(kExperimentInstanceField)) {
+ if (Enabled(kExperimentInstanceFieldSelf) &&
+ first_arg_reg == receiver &&
+ holder_type == current_class_type) {
+ if (count_types) {
+ ++current_type.fields_.FindOrAdd(dex_field_idx)->second;
+ } else {
+ uint32_t field_idx = types[holder_type.index_].fields_.Get(dex_field_idx);
+ ExtendPrefix(&out_reg, &field_idx);
+ CHECK(InstNibbles(new_opcode, {out_reg, field_idx}));
+ continue;
+ }
+ } else if (Enabled(kExperimentInstanceField)) {
if (count_types) {
++current_type.types_.FindOrAdd(holder_type.index_)->second;
++types[holder_type.index_].fields_.FindOrAdd(dex_field_idx)->second;
diff --git a/tools/dexanalyze/dexanalyze_strings.cc b/tools/dexanalyze/dexanalyze_strings.cc
index 9f67ff431a..863e4ee4b3 100644
--- a/tools/dexanalyze/dexanalyze_strings.cc
+++ b/tools/dexanalyze/dexanalyze_strings.cc
@@ -19,6 +19,7 @@
#include <algorithm>
#include <iomanip>
#include <iostream>
+#include <queue>
#include "dex/class_accessor-inl.h"
#include "dex/code_item_accessors-inl.h"
@@ -27,8 +28,171 @@
namespace art {
namespace dexanalyze {
+// Tunable parameters.
+static const size_t kMinPrefixLen = 1;
+static const size_t kMaxPrefixLen = 255;
+static const size_t kPrefixConstantCost = 4;
+static const size_t kPrefixIndexCost = 2;
+
+// Node value = (distance from root) * (occurrences - 1).
+class MatchTrie {
+ public:
+ void Add(const std::string& str) {
+ MatchTrie* node = this;
+ size_t depth = 0u;
+ for (uint8_t c : str) {
+ ++depth;
+ if (node->nodes_[c] == nullptr) {
+ MatchTrie* new_node = new MatchTrie();
+ node->nodes_[c].reset(new_node);
+ new_node->parent_ = node;
+ new_node->depth_ = depth;
+ new_node->incoming_ = c;
+ node = new_node;
+ } else {
+ node = node->nodes_[c].get();
+ }
+ ++node->count_;
+ }
+ node->is_end_ = true;
+ }
+
+ // Returns the length of the longest prefix and if it's a leaf node.
+ std::pair<size_t, bool> LongestPrefix(const std::string& str) const {
+ const MatchTrie* node = this;
+ const MatchTrie* best_node = this;
+ size_t depth = 0u;
+ size_t best_depth = 0u;
+ for (uint8_t c : str) {
+ if (node->nodes_[c] == nullptr) {
+ break;
+ }
+ node = node->nodes_[c].get();
+ ++depth;
+ if (node->is_end_) {
+ best_depth = depth;
+ best_node = node;
+ }
+ }
+ bool is_leaf = true;
+ for (const std::unique_ptr<MatchTrie>& cur_node : best_node->nodes_) {
+ if (cur_node != nullptr) {
+ is_leaf = false;
+ }
+ }
+ return {best_depth, is_leaf};
+ }
+
+ int32_t Savings() const {
+ int32_t cost = kPrefixConstantCost;
+ int32_t first_used = 0u;
+ if (chosen_suffix_count_ == 0u) {
+ cost += depth_;
+ }
+ uint32_t extra_savings = 0u;
+ for (MatchTrie* cur = parent_; cur != nullptr; cur = cur->parent_) {
+ if (cur->chosen_) {
+ first_used = cur->depth_;
+ if (cur->chosen_suffix_count_ == 0u) {
+ // First suffix for the chosen parent, remove the cost of the dictionary entry.
+ extra_savings += first_used;
+ }
+ break;
+ }
+ }
+ return count_ * (depth_ - first_used) - cost + extra_savings;
+ }
+
+ template <typename T, typename... Args, template <typename...> class Queue>
+ T PopRealTop(Queue<T, Args...>& queue) {
+ auto pair = queue.top();
+ queue.pop();
+ // Keep updating values until one sticks.
+ while (pair.second->Savings() != pair.first) {
+ pair.first = pair.second->Savings();
+ queue.push(pair);
+ pair = queue.top();
+ queue.pop();
+ }
+ return pair;
+ }
+
+ std::vector<std::string> ExtractPrefixes(size_t max) {
+ std::vector<std::string> ret;
+ // Make priority queue and adaptively update it. Each node value is the savings from picking
+ // it. Insert all of the interesting nodes in the queue (children != 1).
+ std::priority_queue<std::pair<int32_t, MatchTrie*>> queue;
+ // Add all of the nodes to the queue.
+ std::vector<MatchTrie*> work(1, this);
+ while (!work.empty()) {
+ MatchTrie* elem = work.back();
+ work.pop_back();
+ size_t num_childs = 0u;
+ for (const std::unique_ptr<MatchTrie>& child : elem->nodes_) {
+ if (child != nullptr) {
+ work.push_back(child.get());
+ ++num_childs;
+ }
+ }
+ if (num_childs > 1u || elem->is_end_) {
+ queue.emplace(elem->Savings(), elem);
+ }
+ }
+ std::priority_queue<std::pair<int32_t, MatchTrie*>> prefixes;
+ // The savings can only ever go down for a given node, never up.
+ while (max != 0u && !queue.empty()) {
+ std::pair<int32_t, MatchTrie*> pair = PopRealTop(queue);
+ if (pair.second != this && pair.first > 0) {
+ // Pick this node.
+ uint32_t count = pair.second->count_;
+ pair.second->chosen_ = true;
+ for (MatchTrie* cur = pair.second->parent_; cur != this; cur = cur->parent_) {
+ if (cur->chosen_) {
+ break;
+ }
+ cur->count_ -= count;
+ }
+ for (MatchTrie* cur = pair.second->parent_; cur != this; cur = cur->parent_) {
+ ++cur->chosen_suffix_count_;
+ }
+ prefixes.emplace(pair.first, pair.second);
+ --max;
+ } else {
+ // Negative or no EV, just delete the node.
+ }
+ }
+ while (!prefixes.empty()) {
+ std::pair<int32_t, MatchTrie*> pair = PopRealTop(prefixes);
+ if (pair.first <= 0) {
+ continue;
+ }
+ std::vector<uint8_t> chars;
+ for (MatchTrie* cur = pair.second; cur != this; cur = cur->parent_) {
+ chars.push_back(cur->incoming_);
+ }
+ ret.push_back(std::string(chars.rbegin(), chars.rend()));
+ // LOG(INFO) << pair.second->Savings() << " : " << ret.back();
+ }
+ return ret;
+ }
+
+ std::unique_ptr<MatchTrie> nodes_[256];
+ MatchTrie* parent_ = nullptr;
+ uint32_t count_ = 0u;
+ int32_t depth_ = 0u;
+ int32_t savings_ = 0u;
+ uint8_t incoming_ = 0u;
+ // If the current node is the end of a possible prefix.
+ bool is_end_ = false;
+ // If the current node is chosen to be a used prefix.
+ bool chosen_ = false;
+ // If the current node is a prefix of a longer chosen prefix.
+ uint32_t chosen_suffix_count_ = 0u;
+};
+
void AnalyzeStrings::ProcessDexFiles(const std::vector<std::unique_ptr<const DexFile>>& dex_files) {
std::set<std::string> unique_strings;
+ // Accumulate the strings.
for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
for (size_t i = 0; i < dex_file->NumStringIds(); ++i) {
uint32_t length = 0;
@@ -49,18 +213,17 @@ void AnalyzeStrings::ProcessDexFiles(const std::vector<std::unique_ptr<const Dex
}
}
// Unique strings only since we want to exclude savings from multidex duplication.
- std::vector<std::string> strings(unique_strings.begin(), unique_strings.end());
- unique_strings.clear();
-
- // Tunable parameters.
- static const size_t kMinPrefixLen = 1;
- static const size_t kMaxPrefixLen = 255;
- static const size_t kPrefixConstantCost = 4;
- static const size_t kPrefixIndexCost = 2;
+ ProcessStrings(std::vector<std::string>(unique_strings.begin(), unique_strings.end()), 1);
+}
+void AnalyzeStrings::ProcessStrings(const std::vector<std::string>& strings, size_t iterations) {
+ if (iterations == 0u) {
+ return;
+ }
// Calculate total shared prefix.
std::vector<size_t> shared_len;
prefixes_.clear();
+ std::unique_ptr<MatchTrie> prefix_construct(new MatchTrie());
for (size_t i = 0; i < strings.size(); ++i) {
size_t best_len = 0;
if (i > 0) {
@@ -73,62 +236,104 @@ void AnalyzeStrings::ProcessDexFiles(const std::vector<std::unique_ptr<const Dex
std::string prefix;
if (best_len >= kMinPrefixLen) {
prefix = strings[i].substr(0, best_len);
+ prefix_construct->Add(prefix);
++prefixes_[prefix];
+ total_shared_prefix_bytes_ += best_len;
}
total_prefix_index_cost_ += kPrefixIndexCost;
}
- // Optimize the result by moving long prefixes to shorter ones if it causes savings.
- while (true) {
- bool have_savings = false;
- auto it = prefixes_.begin();
- std::vector<std::string> longest;
- for (const auto& pair : prefixes_) {
- longest.push_back(pair.first);
- }
- std::sort(longest.begin(), longest.end(), [](const std::string& a, const std::string& b) {
- return a.length() > b.length();
- });
- // Do longest first since this provides the best results.
- for (const std::string& s : longest) {
- it = prefixes_.find(s);
- CHECK(it != prefixes_.end());
- const std::string& prefix = it->first;
- int64_t best_savings = 0u;
- int64_t best_len = -1;
- for (int64_t len = prefix.length() - 1; len >= 0; --len) {
- auto found = prefixes_.find(prefix.substr(0, len));
- if (len != 0 && found == prefixes_.end()) {
- continue;
+
+ static constexpr size_t kPrefixBits = 15;
+ static constexpr size_t kShortLen = (1u << (15 - kPrefixBits)) - 1;
+ std::unique_ptr<MatchTrie> prefix_trie(new MatchTrie());
+ static constexpr bool kUseGreedyTrie = true;
+ if (kUseGreedyTrie) {
+ std::vector<std::string> prefixes(prefix_construct->ExtractPrefixes(1 << kPrefixBits));
+ for (auto&& str : prefixes) {
+ prefix_trie->Add(str);
+ }
+ } else {
+ // Optimize the result by moving long prefixes to shorter ones if it causes additional savings.
+ while (true) {
+ bool have_savings = false;
+ auto it = prefixes_.begin();
+ std::vector<std::string> longest;
+ for (const auto& pair : prefixes_) {
+ longest.push_back(pair.first);
+ }
+ std::sort(longest.begin(), longest.end(), [](const std::string& a, const std::string& b) {
+ return a.length() > b.length();
+ });
+ // Do longest first since this provides the best results.
+ for (const std::string& s : longest) {
+ it = prefixes_.find(s);
+ CHECK(it != prefixes_.end());
+ const std::string& prefix = it->first;
+ int64_t best_savings = 0u;
+ int64_t best_len = -1;
+ for (int64_t len = prefix.length() - 1; len >= 0; --len) {
+ auto found = prefixes_.find(prefix.substr(0, len));
+ if (len != 0 && found == prefixes_.end()) {
+ continue;
+ }
+ // Calculate savings from downgrading the prefix.
+ int64_t savings = kPrefixConstantCost + prefix.length() -
+ (prefix.length() - len) * it->second;
+ if (savings > best_savings) {
+ best_savings = savings;
+ best_len = len;
+ break;
+ }
}
- // Calculate savings from downgrading the prefix.
- int64_t savings = kPrefixConstantCost + prefix.length() -
- (prefix.length() - len) * it->second;
- if (savings > best_savings) {
- best_savings = savings;
- best_len = len;
- break;
+ if (best_len != -1) {
+ prefixes_[prefix.substr(0, best_len)] += it->second;
+ it = prefixes_.erase(it);
+ optimization_savings_ += best_savings;
+ have_savings = true;
+ } else {
+ ++it;
}
}
- if (best_len != -1) {
- prefixes_[prefix.substr(0, best_len)] += it->second;
- it = prefixes_.erase(it);
- optimization_savings_ += best_savings;
- have_savings = true;
- } else {
- ++it;
+ if (!have_savings) {
+ break;
}
}
- if (!have_savings) {
- break;
+ for (auto&& pair : prefixes_) {
+ prefix_trie->Add(pair.first);
+ }
+ }
+
+ // Count longest prefixes.
+ std::set<std::string> used_prefixes;
+ std::vector<std::string> suffix;
+ for (const std::string& str : strings) {
+ auto pair = prefix_trie->LongestPrefix(str);
+ const size_t len = pair.first;
+ if (len >= kMinPrefixLen) {
+ ++strings_used_prefixed_;
+ total_prefix_savings_ += len;
+ used_prefixes.insert(str.substr(0, len));
+ }
+ suffix.push_back(str.substr(len));
+ if (suffix.back().size() < kShortLen) {
+ ++short_strings_;
+ } else {
+ ++long_strings_;
}
}
- total_num_prefixes_ += prefixes_.size();
- for (const auto& pair : prefixes_) {
+ std::sort(suffix.begin(), suffix.end());
+ for (const std::string& prefix : used_prefixes) {
// 4 bytes for an offset, one for length.
- total_prefix_dict_ += pair.first.length();
+ auto pair = prefix_trie->LongestPrefix(prefix);
+ CHECK_EQ(pair.first, prefix.length());
+ if (pair.second) {
+ // Only need to add to dictionary if it's a leaf, otherwise we can reuse string data of the
+ // other prefix.
+ total_prefix_dict_ += prefix.size();
+ }
total_prefix_table_ += kPrefixConstantCost;
- total_prefix_savings_ += pair.first.length() * pair.second;
}
+ ProcessStrings(suffix, iterations - 1);
}
void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const {
@@ -137,17 +342,20 @@ void AnalyzeStrings::Dump(std::ostream& os, uint64_t total_size) const {
os << "ASCII string data bytes " << Percent(ascii_string_bytes_, total_size) << "\n";
// Prefix based strings.
- os << "Total shared prefix bytes " << Percent(total_prefix_savings_, total_size) << "\n";
+ os << "Total shared prefix bytes " << Percent(total_shared_prefix_bytes_, total_size) << "\n";
os << "Prefix dictionary cost " << Percent(total_prefix_dict_, total_size) << "\n";
os << "Prefix table cost " << Percent(total_prefix_table_, total_size) << "\n";
os << "Prefix index cost " << Percent(total_prefix_index_cost_, total_size) << "\n";
- int64_t net_savings = total_prefix_savings_;
+ int64_t net_savings = total_prefix_savings_ + short_strings_;
net_savings -= total_prefix_dict_;
net_savings -= total_prefix_table_;
net_savings -= total_prefix_index_cost_;
os << "Prefix dictionary elements " << total_num_prefixes_ << "\n";
os << "Optimization savings " << Percent(optimization_savings_, total_size) << "\n";
os << "Prefix net savings " << Percent(net_savings, total_size) << "\n";
+ os << "Strings using prefix "
+ << Percent(strings_used_prefixed_, total_prefix_index_cost_ / kPrefixIndexCost) << "\n";
+ os << "Short strings " << Percent(short_strings_, short_strings_ + long_strings_) << "\n";
if (verbose_level_ >= VerboseLevel::kEverything) {
std::vector<std::pair<std::string, size_t>> pairs(prefixes_.begin(), prefixes_.end());
// Sort lexicographically.
diff --git a/tools/dexanalyze/dexanalyze_strings.h b/tools/dexanalyze/dexanalyze_strings.h
index 3559afaff7..32702a60cb 100644
--- a/tools/dexanalyze/dexanalyze_strings.h
+++ b/tools/dexanalyze/dexanalyze_strings.h
@@ -36,15 +36,21 @@ class AnalyzeStrings : public Experiment {
void Dump(std::ostream& os, uint64_t total_size) const override;
private:
+ void ProcessStrings(const std::vector<std::string>& strings, size_t iterations);
+
int64_t wide_string_bytes_ = 0u;
int64_t ascii_string_bytes_ = 0u;
int64_t string_data_bytes_ = 0u;
+ int64_t total_shared_prefix_bytes_ = 0u;
int64_t total_prefix_savings_ = 0u;
int64_t total_prefix_dict_ = 0u;
int64_t total_prefix_table_ = 0u;
int64_t total_prefix_index_cost_ = 0u;
int64_t total_num_prefixes_ = 0u;
int64_t optimization_savings_ = 0u;
+ int64_t strings_used_prefixed_ = 0u;
+ int64_t short_strings_ = 0u;
+ int64_t long_strings_ = 0u;
std::unordered_map<std::string, size_t> prefixes_;
};
diff --git a/tools/jfuzz/Android.bp b/tools/jfuzz/Android.bp
new file mode 100644
index 0000000000..f0d8b3779d
--- /dev/null
+++ b/tools/jfuzz/Android.bp
@@ -0,0 +1,29 @@
+// Copyright (C) 2016 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.
+
+// Fuzzer tool.
+cc_binary_host {
+ name: "jfuzz",
+ srcs: ["jfuzz.cc"],
+ cflags: [
+ "-O0",
+ "-g",
+ "-Wall",
+ ],
+ target: {
+ windows: {
+ enabled: true,
+ },
+ },
+}
diff --git a/tools/jfuzz/Android.mk b/tools/jfuzz/Android.mk
deleted file mode 100644
index c7002d67ec..0000000000
--- a/tools/jfuzz/Android.mk
+++ /dev/null
@@ -1,25 +0,0 @@
-# Copyright (C) 2016 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.
-
-# Fuzzer tool.
-
-LOCAL_PATH:= $(call my-dir)
-
-include $(CLEAR_VARS)
-LOCAL_CPP_EXTENSION := cc
-LOCAL_SRC_FILES := jfuzz.cc
-LOCAL_CFLAGS += -O0 -g -Wall
-LOCAL_MODULE_HOST_OS := darwin linux windows
-LOCAL_MODULE := jfuzz
-include $(BUILD_HOST_EXECUTABLE)