Merge "Added new -v option to dexlayout to verify output dex file."
diff --git a/dexlayout/Android.bp b/dexlayout/Android.bp
index 9ee9ebd..cf523ec 100644
--- a/dexlayout/Android.bp
+++ b/dexlayout/Android.bp
@@ -19,6 +19,7 @@
"dexlayout.cc",
"dex_ir.cc",
"dex_ir_builder.cc",
+ "dex_verify.cc",
"dex_visualize.cc",
"dex_writer.cc",
],
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 34983cf..4228503 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -56,7 +56,7 @@
entry.end_address_, entry.reg_)));
}
-static uint32_t GetCodeItemSize(const DexFile& dex_file, const DexFile::CodeItem& disk_code_item) {
+static uint32_t GetCodeItemSize(const DexFile::CodeItem& disk_code_item) {
uintptr_t code_item_start = reinterpret_cast<uintptr_t>(&disk_code_item);
uint32_t insns_size = disk_code_item.insns_size_in_code_units_;
uint32_t tries_size = disk_code_item.tries_size_;
@@ -675,7 +675,7 @@
}
}
- uint32_t size = GetCodeItemSize(dex_file, disk_code_item);
+ uint32_t size = GetCodeItemSize(disk_code_item);
CodeItem* code_item = new CodeItem(
registers_size, ins_size, outs_size, debug_info, insns_size, insns, tries, handler_list);
code_item->SetSize(size);
diff --git a/dexlayout/dex_verify.cc b/dexlayout/dex_verify.cc
new file mode 100644
index 0000000..aec1d0c
--- /dev/null
+++ b/dexlayout/dex_verify.cc
@@ -0,0 +1,187 @@
+/*
+ * 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.
+ *
+ * Implementation file of dex ir verifier.
+ *
+ * Compares two dex files at the IR level, allowing differences in layout, but not in data.
+ */
+
+#include "dex_verify.h"
+
+#include "android-base/stringprintf.h"
+
+namespace art {
+
+using android::base::StringPrintf;
+
+bool VerifyOutputDexFile(dex_ir::Header* orig_header,
+ dex_ir::Header* output_header,
+ std::string* error_msg) {
+ dex_ir::Collections& orig = orig_header->GetCollections();
+ dex_ir::Collections& output = output_header->GetCollections();
+
+ // Compare all id sections.
+ if (!VerifyIds(orig.StringIds(), output.StringIds(), "string ids", error_msg) ||
+ !VerifyIds(orig.TypeIds(), output.TypeIds(), "type ids", error_msg) ||
+ !VerifyIds(orig.ProtoIds(), output.ProtoIds(), "proto ids", error_msg) ||
+ !VerifyIds(orig.FieldIds(), output.FieldIds(), "field ids", error_msg) ||
+ !VerifyIds(orig.MethodIds(), output.MethodIds(), "method ids", error_msg)) {
+ return false;
+ }
+ return true;
+}
+
+template<class T> bool VerifyIds(std::vector<std::unique_ptr<T>>& orig,
+ std::vector<std::unique_ptr<T>>& output,
+ const char* section_name,
+ std::string* error_msg) {
+ if (orig.size() != output.size()) {
+ *error_msg = StringPrintf(
+ "Mismatched size for %s section, %zu vs %zu.", section_name, orig.size(), output.size());
+ return false;
+ }
+ for (size_t i = 0; i < orig.size(); ++i) {
+ if (!VerifyId(orig[i].get(), output[i].get(), error_msg)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool VerifyId(dex_ir::StringId* orig, dex_ir::StringId* output, std::string* error_msg) {
+ if (strcmp(orig->Data(), output->Data()) != 0) {
+ *error_msg = StringPrintf("Mismatched string data for string id %u @ orig offset %x, %s vs %s.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Data(),
+ output->Data());
+ return false;
+ }
+ return true;
+}
+
+bool VerifyId(dex_ir::TypeId* orig, dex_ir::TypeId* output, std::string* error_msg) {
+ if (orig->GetStringId()->GetIndex() != output->GetStringId()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched string index for type id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->GetStringId()->GetIndex(),
+ output->GetStringId()->GetIndex());
+ return false;
+ }
+ return true;
+}
+
+bool VerifyId(dex_ir::ProtoId* orig, dex_ir::ProtoId* output, std::string* error_msg) {
+ if (orig->Shorty()->GetIndex() != output->Shorty()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched string index for proto id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Shorty()->GetIndex(),
+ output->Shorty()->GetIndex());
+ return false;
+ }
+ if (orig->ReturnType()->GetIndex() != output->ReturnType()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched type index for proto id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->ReturnType()->GetIndex(),
+ output->ReturnType()->GetIndex());
+ return false;
+ }
+ if (!VerifyTypeList(orig->Parameters(), output->Parameters())) {
+ *error_msg = StringPrintf("Mismatched type list for proto id %u @ orig offset %x.",
+ orig->GetIndex(),
+ orig->GetOffset());
+ }
+ return true;
+}
+
+bool VerifyId(dex_ir::FieldId* orig, dex_ir::FieldId* output, std::string* error_msg) {
+ if (orig->Class()->GetIndex() != output->Class()->GetIndex()) {
+ *error_msg =
+ StringPrintf("Mismatched class type index for field id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Class()->GetIndex(),
+ output->Class()->GetIndex());
+ return false;
+ }
+ if (orig->Type()->GetIndex() != output->Type()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched type index for field id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Class()->GetIndex(),
+ output->Class()->GetIndex());
+ return false;
+ }
+ if (orig->Name()->GetIndex() != output->Name()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched string index for field id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Name()->GetIndex(),
+ output->Name()->GetIndex());
+ return false;
+ }
+ return true;
+}
+
+bool VerifyId(dex_ir::MethodId* orig, dex_ir::MethodId* output, std::string* error_msg) {
+ if (orig->Class()->GetIndex() != output->Class()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched type index for method id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Class()->GetIndex(),
+ output->Class()->GetIndex());
+ return false;
+ }
+ if (orig->Proto()->GetIndex() != output->Proto()->GetIndex()) {
+ *error_msg = StringPrintf("Mismatched proto index for method id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Class()->GetIndex(),
+ output->Class()->GetIndex());
+ return false;
+ }
+ if (orig->Name()->GetIndex() != output->Name()->GetIndex()) {
+ *error_msg =
+ StringPrintf("Mismatched string index for method id %u @ orig offset %x, %u vs %u.",
+ orig->GetIndex(),
+ orig->GetOffset(),
+ orig->Name()->GetIndex(),
+ output->Name()->GetIndex());
+ return false;
+ }
+ return true;
+}
+
+bool VerifyTypeList(const dex_ir::TypeList* orig, const dex_ir::TypeList* output) {
+ if (orig == nullptr || output == nullptr) {
+ return orig == output;
+ }
+ const dex_ir::TypeIdVector* orig_list = orig->GetTypeList();
+ const dex_ir::TypeIdVector* output_list = output->GetTypeList();
+ if (orig_list->size() != output_list->size()) {
+ return false;
+ }
+ for (size_t i = 0; i < orig_list->size(); ++i) {
+ if ((*orig_list)[i]->GetIndex() != (*output_list)[i]->GetIndex()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace art
diff --git a/dexlayout/dex_verify.h b/dexlayout/dex_verify.h
new file mode 100644
index 0000000..a19431c
--- /dev/null
+++ b/dexlayout/dex_verify.h
@@ -0,0 +1,46 @@
+/*
+ * 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.
+ *
+ * Header file of dex ir verifier.
+ *
+ * Compares two dex files at the IR level, allowing differences in layout, but not in data.
+ */
+
+#ifndef ART_DEXLAYOUT_DEX_VERIFY_H_
+#define ART_DEXLAYOUT_DEX_VERIFY_H_
+
+#include "dex_ir.h"
+
+namespace art {
+
+// Check that the output dex file contains the same data as the original.
+// Compares the dex IR of both dex files. Allows the dex files to have different layouts.
+bool VerifyOutputDexFile(dex_ir::Header* orig_header,
+ dex_ir::Header* output_header,
+ std::string* error_msg);
+template<class T> bool VerifyIds(std::vector<std::unique_ptr<T>>& orig,
+ std::vector<std::unique_ptr<T>>& output,
+ const char* section_name,
+ std::string* error_msg);
+bool VerifyId(dex_ir::StringId* orig, dex_ir::StringId* output, std::string* error_msg);
+bool VerifyId(dex_ir::TypeId* orig, dex_ir::TypeId* output, std::string* error_msg);
+bool VerifyId(dex_ir::ProtoId* orig, dex_ir::ProtoId* output, std::string* error_msg);
+bool VerifyId(dex_ir::FieldId* orig, dex_ir::FieldId* output, std::string* error_msg);
+bool VerifyId(dex_ir::MethodId* orig, dex_ir::MethodId* output, std::string* error_msg);
+bool VerifyTypeList(const dex_ir::TypeList* orig, const dex_ir::TypeList* output);
+
+} // namespace art
+
+#endif // ART_DEXLAYOUT_DEX_VERIFY_H_
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index f74fb4e..a310424 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -36,6 +36,7 @@
#include "dex_file-inl.h"
#include "dex_file_verifier.h"
#include "dex_instruction-inl.h"
+#include "dex_verify.h"
#include "dex_visualize.h"
#include "dex_writer.h"
#include "jit/profile_compilation_info.h"
@@ -1692,7 +1693,8 @@
header_->SetFileSize(header_->FileSize() + diff);
}
-void DexLayout::OutputDexFile(const std::string& dex_file_location) {
+void DexLayout::OutputDexFile(const DexFile* dex_file) {
+ const std::string& dex_file_location = dex_file->GetLocation();
std::string error_msg;
std::unique_ptr<File> new_file;
if (!options_.output_to_memmap_) {
@@ -1725,18 +1727,24 @@
if (new_file != nullptr) {
UNUSED(new_file->FlushCloseOrErase());
}
- // Verify the output dex file is ok on debug builds.
+ // Verify the output dex file's structure for debug builds.
if (kIsDebugBuild) {
std::string location = "memory mapped file for " + dex_file_location;
- std::unique_ptr<const DexFile> dex_file(DexFile::Open(mem_map_->Begin(),
- mem_map_->Size(),
- location,
- header_->Checksum(),
- /*oat_dex_file*/ nullptr,
- /*verify*/ true,
- /*verify_checksum*/ false,
- &error_msg));
- DCHECK(dex_file != nullptr) << "Failed to re-open output file:" << error_msg;
+ std::unique_ptr<const DexFile> output_dex_file(DexFile::Open(mem_map_->Begin(),
+ mem_map_->Size(),
+ location,
+ header_->Checksum(),
+ /*oat_dex_file*/ nullptr,
+ /*verify*/ true,
+ /*verify_checksum*/ false,
+ &error_msg));
+ DCHECK(output_dex_file != nullptr) << "Failed to re-open output file:" << error_msg;
+ }
+ // Do IR-level comparison between input and output. This check ignores potential differences
+ // due to layout, so offsets are not checked. Instead, it checks the data contents of each item.
+ if (options_.verify_output_) {
+ std::unique_ptr<dex_ir::Header> orig_header(dex_ir::DexIrBuilder(*dex_file));
+ CHECK(VerifyOutputDexFile(orig_header.get(), header_, &error_msg)) << error_msg;
}
}
@@ -1774,7 +1782,7 @@
if (info_ != nullptr) {
LayoutOutputFile(dex_file);
}
- OutputDexFile(dex_file->GetLocation());
+ OutputDexFile(dex_file);
}
}
diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h
index 74b5253..f26b423 100644
--- a/dexlayout/dexlayout.h
+++ b/dexlayout/dexlayout.h
@@ -58,6 +58,7 @@
bool show_section_headers_ = false;
bool show_section_statistics_ = false;
bool verbose_ = false;
+ bool verify_output_ = false;
bool visualize_pattern_ = false;
OutputFormat output_format_ = kOutputPlain;
const char* output_dex_directory_ = nullptr;
@@ -115,7 +116,7 @@
// Creates a new layout for the dex file based on profile info.
// Currently reorders ClassDefs, ClassDataItems, and CodeItems.
void LayoutOutputFile(const DexFile* dex_file);
- void OutputDexFile(const std::string& dex_file_location);
+ void OutputDexFile(const DexFile* dex_file);
void DumpCFG(const DexFile* dex_file, int idx);
void DumpCFG(const DexFile* dex_file, uint32_t dex_method_idx, const DexFile::CodeItem* code);
diff --git a/dexlayout/dexlayout_main.cc b/dexlayout/dexlayout_main.cc
index 3eac660..38faf96 100644
--- a/dexlayout/dexlayout_main.cc
+++ b/dexlayout/dexlayout_main.cc
@@ -1,4 +1,4 @@
-/*
+ /*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -44,7 +44,7 @@
static void Usage(void) {
fprintf(stderr, "Copyright (C) 2016 The Android Open Source Project\n\n");
fprintf(stderr, "%s: [-a] [-c] [-d] [-e] [-f] [-h] [-i] [-l layout] [-o outfile] [-p profile]"
- " [-s] [-w directory] dexfile...\n\n", kProgramName);
+ " [-s] [-t] [-v] [-w directory] dexfile...\n\n", kProgramName);
fprintf(stderr, " -a : display annotations\n");
fprintf(stderr, " -b : build dex_ir\n");
fprintf(stderr, " -c : verify checksum and exit\n");
@@ -58,6 +58,7 @@
fprintf(stderr, " -p : profile file name (defaults to no profile)\n");
fprintf(stderr, " -s : visualize reference pattern\n");
fprintf(stderr, " -t : display file section sizes\n");
+ fprintf(stderr, " -v : verify output file is canonical to input (IR level comparison)\n");
fprintf(stderr, " -w : output dex directory \n");
}
@@ -76,7 +77,7 @@
// Parse all arguments.
while (1) {
- const int ic = getopt(argc, argv, "abcdefghil:mo:p:stw:");
+ const int ic = getopt(argc, argv, "abcdefghil:mo:p:stvw:");
if (ic < 0) {
break; // done
}
@@ -132,6 +133,9 @@
options.show_section_statistics_ = true;
options.verbose_ = false;
break;
+ case 'v': // verify output
+ options.verify_output_ = true;
+ break;
case 'w': // output dex files directory
options.output_dex_directory_ = optarg;
break;