upload android base code part3
This commit is contained in:
parent
71b83c22f1
commit
b9e30e05b1
15122 changed files with 2089659 additions and 0 deletions
89
android/art/dexlayout/Android.bp
Normal file
89
android/art/dexlayout/Android.bp
Normal file
|
@ -0,0 +1,89 @@
|
|||
// 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.
|
||||
|
||||
art_cc_defaults {
|
||||
name: "libart-dexlayout-defaults",
|
||||
defaults: ["art_defaults"],
|
||||
host_supported: true,
|
||||
srcs: [
|
||||
"dexlayout.cc",
|
||||
"dex_ir.cc",
|
||||
"dex_ir_builder.cc",
|
||||
"dex_verify.cc",
|
||||
"dex_visualize.cc",
|
||||
"dex_writer.cc",
|
||||
],
|
||||
export_include_dirs: ["."],
|
||||
shared_libs: ["libbase"],
|
||||
static_libs: ["libz"],
|
||||
}
|
||||
|
||||
art_cc_library {
|
||||
name: "libart-dexlayout",
|
||||
defaults: ["libart-dexlayout-defaults"],
|
||||
shared_libs: ["libart"],
|
||||
}
|
||||
|
||||
art_cc_library {
|
||||
name: "libartd-dexlayout",
|
||||
defaults: ["libart-dexlayout-defaults"],
|
||||
shared_libs: ["libartd"],
|
||||
}
|
||||
|
||||
art_cc_binary {
|
||||
name: "dexlayout",
|
||||
defaults: ["art_defaults"],
|
||||
host_supported: true,
|
||||
srcs: ["dexlayout_main.cc"],
|
||||
cflags: ["-Wall"],
|
||||
shared_libs: [
|
||||
"libart",
|
||||
"libart-dexlayout",
|
||||
"libbase",
|
||||
],
|
||||
}
|
||||
|
||||
art_cc_test {
|
||||
name: "art_dexlayout_tests",
|
||||
defaults: ["art_gtest_defaults"],
|
||||
srcs: ["dexlayout_test.cc"],
|
||||
}
|
||||
|
||||
art_cc_binary {
|
||||
name: "dexdiag",
|
||||
defaults: ["art_defaults"],
|
||||
host_supported: true,
|
||||
srcs: ["dexdiag.cc"],
|
||||
cflags: ["-Wall"],
|
||||
shared_libs: [
|
||||
"libart",
|
||||
"libart-dexlayout",
|
||||
],
|
||||
target: {
|
||||
android: {
|
||||
shared_libs: [
|
||||
"libpagemap",
|
||||
]
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
art_cc_test {
|
||||
name: "art_dexdiag_tests",
|
||||
host_supported: true,
|
||||
defaults: [
|
||||
"art_gtest_defaults",
|
||||
],
|
||||
srcs: ["dexdiag_test.cc"],
|
||||
}
|
956
android/art/dexlayout/dex_ir.cc
Normal file
956
android/art/dexlayout/dex_ir.cc
Normal file
|
@ -0,0 +1,956 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Implementation file of the dexlayout utility.
|
||||
*
|
||||
* This is a tool to read dex files into an internal representation,
|
||||
* reorganize the representation, and emit dex files with a better
|
||||
* file layout.
|
||||
*/
|
||||
|
||||
#include "dex_ir.h"
|
||||
#include "dex_instruction-inl.h"
|
||||
#include "dex_ir_builder.h"
|
||||
|
||||
namespace art {
|
||||
namespace dex_ir {
|
||||
|
||||
static uint64_t ReadVarWidth(const uint8_t** data, uint8_t length, bool sign_extend) {
|
||||
uint64_t value = 0;
|
||||
for (uint32_t i = 0; i <= length; i++) {
|
||||
value |= static_cast<uint64_t>(*(*data)++) << (i * 8);
|
||||
}
|
||||
if (sign_extend) {
|
||||
int shift = (7 - length) * 8;
|
||||
return (static_cast<int64_t>(value) << shift) >> shift;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool GetPositionsCb(void* context, const DexFile::PositionInfo& entry) {
|
||||
DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
|
||||
PositionInfoVector& positions = debug_info->GetPositionInfo();
|
||||
positions.push_back(std::unique_ptr<PositionInfo>(new PositionInfo(entry.address_, entry.line_)));
|
||||
return false;
|
||||
}
|
||||
|
||||
static void GetLocalsCb(void* context, const DexFile::LocalInfo& entry) {
|
||||
DebugInfoItem* debug_info = reinterpret_cast<DebugInfoItem*>(context);
|
||||
LocalInfoVector& locals = debug_info->GetLocalInfo();
|
||||
const char* name = entry.name_ != nullptr ? entry.name_ : "(null)";
|
||||
const char* descriptor = entry.descriptor_ != nullptr ? entry.descriptor_ : "";
|
||||
const char* signature = entry.signature_ != nullptr ? entry.signature_ : "";
|
||||
locals.push_back(std::unique_ptr<LocalInfo>(
|
||||
new LocalInfo(name, descriptor, signature, entry.start_address_, entry.end_address_,
|
||||
entry.reg_)));
|
||||
}
|
||||
|
||||
static uint32_t GetDebugInfoStreamSize(const uint8_t* debug_info_stream) {
|
||||
const uint8_t* stream = debug_info_stream;
|
||||
DecodeUnsignedLeb128(&stream); // line_start
|
||||
uint32_t parameters_size = DecodeUnsignedLeb128(&stream);
|
||||
for (uint32_t i = 0; i < parameters_size; ++i) {
|
||||
DecodeUnsignedLeb128P1(&stream); // Parameter name.
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
uint8_t opcode = *stream++;
|
||||
switch (opcode) {
|
||||
case DexFile::DBG_END_SEQUENCE:
|
||||
return stream - debug_info_stream; // end of stream.
|
||||
case DexFile::DBG_ADVANCE_PC:
|
||||
DecodeUnsignedLeb128(&stream); // addr_diff
|
||||
break;
|
||||
case DexFile::DBG_ADVANCE_LINE:
|
||||
DecodeSignedLeb128(&stream); // line_diff
|
||||
break;
|
||||
case DexFile::DBG_START_LOCAL:
|
||||
DecodeUnsignedLeb128(&stream); // register_num
|
||||
DecodeUnsignedLeb128P1(&stream); // name_idx
|
||||
DecodeUnsignedLeb128P1(&stream); // type_idx
|
||||
break;
|
||||
case DexFile::DBG_START_LOCAL_EXTENDED:
|
||||
DecodeUnsignedLeb128(&stream); // register_num
|
||||
DecodeUnsignedLeb128P1(&stream); // name_idx
|
||||
DecodeUnsignedLeb128P1(&stream); // type_idx
|
||||
DecodeUnsignedLeb128P1(&stream); // sig_idx
|
||||
break;
|
||||
case DexFile::DBG_END_LOCAL:
|
||||
case DexFile::DBG_RESTART_LOCAL:
|
||||
DecodeUnsignedLeb128(&stream); // register_num
|
||||
break;
|
||||
case DexFile::DBG_SET_PROLOGUE_END:
|
||||
case DexFile::DBG_SET_EPILOGUE_BEGIN:
|
||||
break;
|
||||
case DexFile::DBG_SET_FILE: {
|
||||
DecodeUnsignedLeb128P1(&stream); // name_idx
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool GetIdFromInstruction(Collections& collections,
|
||||
const Instruction* dec_insn,
|
||||
std::vector<TypeId*>* type_ids,
|
||||
std::vector<StringId*>* string_ids,
|
||||
std::vector<MethodId*>* method_ids,
|
||||
std::vector<FieldId*>* field_ids) {
|
||||
// Determine index and width of the string.
|
||||
uint32_t index = 0;
|
||||
switch (Instruction::FormatOf(dec_insn->Opcode())) {
|
||||
// SOME NOT SUPPORTED:
|
||||
// case Instruction::k20bc:
|
||||
case Instruction::k21c:
|
||||
case Instruction::k35c:
|
||||
// case Instruction::k35ms:
|
||||
case Instruction::k3rc:
|
||||
// case Instruction::k3rms:
|
||||
// case Instruction::k35mi:
|
||||
// case Instruction::k3rmi:
|
||||
case Instruction::k45cc:
|
||||
case Instruction::k4rcc:
|
||||
index = dec_insn->VRegB();
|
||||
break;
|
||||
case Instruction::k31c:
|
||||
index = dec_insn->VRegB();
|
||||
break;
|
||||
case Instruction::k22c:
|
||||
// case Instruction::k22cs:
|
||||
index = dec_insn->VRegC();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
|
||||
// Determine index type, and add reference to the appropriate collection.
|
||||
switch (Instruction::IndexTypeOf(dec_insn->Opcode())) {
|
||||
case Instruction::kIndexTypeRef:
|
||||
if (index < collections.TypeIdsSize()) {
|
||||
type_ids->push_back(collections.GetTypeId(index));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case Instruction::kIndexStringRef:
|
||||
if (index < collections.StringIdsSize()) {
|
||||
string_ids->push_back(collections.GetStringId(index));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case Instruction::kIndexMethodRef:
|
||||
case Instruction::kIndexMethodAndProtoRef:
|
||||
if (index < collections.MethodIdsSize()) {
|
||||
method_ids->push_back(collections.GetMethodId(index));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case Instruction::kIndexFieldRef:
|
||||
if (index < collections.FieldIdsSize()) {
|
||||
field_ids->push_back(collections.GetFieldId(index));
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case Instruction::kIndexUnknown:
|
||||
case Instruction::kIndexNone:
|
||||
case Instruction::kIndexVtableOffset:
|
||||
case Instruction::kIndexFieldOffset:
|
||||
default:
|
||||
break;
|
||||
} // switch
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get all the types, strings, methods, and fields referred to from bytecode.
|
||||
*/
|
||||
static bool GetIdsFromByteCode(Collections& collections,
|
||||
const CodeItem* code,
|
||||
std::vector<TypeId*>* type_ids,
|
||||
std::vector<StringId*>* string_ids,
|
||||
std::vector<MethodId*>* method_ids,
|
||||
std::vector<FieldId*>* field_ids) {
|
||||
bool has_id = false;
|
||||
// Iterate over all instructions.
|
||||
const uint16_t* insns = code->Insns();
|
||||
for (uint32_t insn_idx = 0; insn_idx < code->InsnsSize();) {
|
||||
const Instruction* instruction = Instruction::At(&insns[insn_idx]);
|
||||
const uint32_t insn_width = instruction->SizeInCodeUnits();
|
||||
if (insn_width == 0) {
|
||||
break;
|
||||
}
|
||||
has_id |= GetIdFromInstruction(collections,
|
||||
instruction,
|
||||
type_ids,
|
||||
string_ids,
|
||||
method_ids,
|
||||
field_ids);
|
||||
insn_idx += insn_width;
|
||||
} // for
|
||||
return has_id;
|
||||
}
|
||||
|
||||
EncodedValue* Collections::ReadEncodedValue(const uint8_t** data) {
|
||||
const uint8_t encoded_value = *(*data)++;
|
||||
const uint8_t type = encoded_value & 0x1f;
|
||||
EncodedValue* item = new EncodedValue(type);
|
||||
ReadEncodedValue(data, type, encoded_value >> 5, item);
|
||||
return item;
|
||||
}
|
||||
|
||||
EncodedValue* Collections::ReadEncodedValue(const uint8_t** data, uint8_t type, uint8_t length) {
|
||||
EncodedValue* item = new EncodedValue(type);
|
||||
ReadEncodedValue(data, type, length, item);
|
||||
return item;
|
||||
}
|
||||
|
||||
void Collections::ReadEncodedValue(
|
||||
const uint8_t** data, uint8_t type, uint8_t length, EncodedValue* item) {
|
||||
switch (type) {
|
||||
case DexFile::kDexAnnotationByte:
|
||||
item->SetByte(static_cast<int8_t>(ReadVarWidth(data, length, false)));
|
||||
break;
|
||||
case DexFile::kDexAnnotationShort:
|
||||
item->SetShort(static_cast<int16_t>(ReadVarWidth(data, length, true)));
|
||||
break;
|
||||
case DexFile::kDexAnnotationChar:
|
||||
item->SetChar(static_cast<uint16_t>(ReadVarWidth(data, length, false)));
|
||||
break;
|
||||
case DexFile::kDexAnnotationInt:
|
||||
item->SetInt(static_cast<int32_t>(ReadVarWidth(data, length, true)));
|
||||
break;
|
||||
case DexFile::kDexAnnotationLong:
|
||||
item->SetLong(static_cast<int64_t>(ReadVarWidth(data, length, true)));
|
||||
break;
|
||||
case DexFile::kDexAnnotationFloat: {
|
||||
// Fill on right.
|
||||
union {
|
||||
float f;
|
||||
uint32_t data;
|
||||
} conv;
|
||||
conv.data = static_cast<uint32_t>(ReadVarWidth(data, length, false)) << (3 - length) * 8;
|
||||
item->SetFloat(conv.f);
|
||||
break;
|
||||
}
|
||||
case DexFile::kDexAnnotationDouble: {
|
||||
// Fill on right.
|
||||
union {
|
||||
double d;
|
||||
uint64_t data;
|
||||
} conv;
|
||||
conv.data = ReadVarWidth(data, length, false) << (7 - length) * 8;
|
||||
item->SetDouble(conv.d);
|
||||
break;
|
||||
}
|
||||
case DexFile::kDexAnnotationMethodType: {
|
||||
const uint32_t proto_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
|
||||
item->SetProtoId(GetProtoId(proto_index));
|
||||
break;
|
||||
}
|
||||
case DexFile::kDexAnnotationMethodHandle: {
|
||||
const uint32_t method_handle_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
|
||||
item->SetMethodHandle(GetMethodHandle(method_handle_index));
|
||||
break;
|
||||
}
|
||||
case DexFile::kDexAnnotationString: {
|
||||
const uint32_t string_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
|
||||
item->SetStringId(GetStringId(string_index));
|
||||
break;
|
||||
}
|
||||
case DexFile::kDexAnnotationType: {
|
||||
const uint32_t string_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
|
||||
item->SetTypeId(GetTypeId(string_index));
|
||||
break;
|
||||
}
|
||||
case DexFile::kDexAnnotationField:
|
||||
case DexFile::kDexAnnotationEnum: {
|
||||
const uint32_t field_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
|
||||
item->SetFieldId(GetFieldId(field_index));
|
||||
break;
|
||||
}
|
||||
case DexFile::kDexAnnotationMethod: {
|
||||
const uint32_t method_index = static_cast<uint32_t>(ReadVarWidth(data, length, false));
|
||||
item->SetMethodId(GetMethodId(method_index));
|
||||
break;
|
||||
}
|
||||
case DexFile::kDexAnnotationArray: {
|
||||
EncodedValueVector* values = new EncodedValueVector();
|
||||
const uint32_t size = DecodeUnsignedLeb128(data);
|
||||
// Decode all elements.
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
values->push_back(std::unique_ptr<EncodedValue>(ReadEncodedValue(data)));
|
||||
}
|
||||
item->SetEncodedArray(new EncodedArrayItem(values));
|
||||
break;
|
||||
}
|
||||
case DexFile::kDexAnnotationAnnotation: {
|
||||
AnnotationElementVector* elements = new AnnotationElementVector();
|
||||
const uint32_t type_idx = DecodeUnsignedLeb128(data);
|
||||
const uint32_t size = DecodeUnsignedLeb128(data);
|
||||
// Decode all name=value pairs.
|
||||
for (uint32_t i = 0; i < size; i++) {
|
||||
const uint32_t name_index = DecodeUnsignedLeb128(data);
|
||||
elements->push_back(std::unique_ptr<AnnotationElement>(
|
||||
new AnnotationElement(GetStringId(name_index), ReadEncodedValue(data))));
|
||||
}
|
||||
item->SetEncodedAnnotation(new EncodedAnnotation(GetTypeId(type_idx), elements));
|
||||
break;
|
||||
}
|
||||
case DexFile::kDexAnnotationNull:
|
||||
break;
|
||||
case DexFile::kDexAnnotationBoolean:
|
||||
item->SetBoolean(length != 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Collections::CreateStringId(const DexFile& dex_file, uint32_t i) {
|
||||
const DexFile::StringId& disk_string_id = dex_file.GetStringId(dex::StringIndex(i));
|
||||
StringData* string_data = new StringData(dex_file.GetStringData(disk_string_id));
|
||||
string_datas_.AddItem(string_data, disk_string_id.string_data_off_);
|
||||
|
||||
StringId* string_id = new StringId(string_data);
|
||||
string_ids_.AddIndexedItem(string_id, StringIdsOffset() + i * StringId::ItemSize(), i);
|
||||
}
|
||||
|
||||
void Collections::CreateTypeId(const DexFile& dex_file, uint32_t i) {
|
||||
const DexFile::TypeId& disk_type_id = dex_file.GetTypeId(dex::TypeIndex(i));
|
||||
TypeId* type_id = new TypeId(GetStringId(disk_type_id.descriptor_idx_.index_));
|
||||
type_ids_.AddIndexedItem(type_id, TypeIdsOffset() + i * TypeId::ItemSize(), i);
|
||||
}
|
||||
|
||||
void Collections::CreateProtoId(const DexFile& dex_file, uint32_t i) {
|
||||
const DexFile::ProtoId& disk_proto_id = dex_file.GetProtoId(i);
|
||||
const DexFile::TypeList* type_list = dex_file.GetProtoParameters(disk_proto_id);
|
||||
TypeList* parameter_type_list = CreateTypeList(type_list, disk_proto_id.parameters_off_);
|
||||
|
||||
ProtoId* proto_id = new ProtoId(GetStringId(disk_proto_id.shorty_idx_.index_),
|
||||
GetTypeId(disk_proto_id.return_type_idx_.index_),
|
||||
parameter_type_list);
|
||||
proto_ids_.AddIndexedItem(proto_id, ProtoIdsOffset() + i * ProtoId::ItemSize(), i);
|
||||
}
|
||||
|
||||
void Collections::CreateFieldId(const DexFile& dex_file, uint32_t i) {
|
||||
const DexFile::FieldId& disk_field_id = dex_file.GetFieldId(i);
|
||||
FieldId* field_id = new FieldId(GetTypeId(disk_field_id.class_idx_.index_),
|
||||
GetTypeId(disk_field_id.type_idx_.index_),
|
||||
GetStringId(disk_field_id.name_idx_.index_));
|
||||
field_ids_.AddIndexedItem(field_id, FieldIdsOffset() + i * FieldId::ItemSize(), i);
|
||||
}
|
||||
|
||||
void Collections::CreateMethodId(const DexFile& dex_file, uint32_t i) {
|
||||
const DexFile::MethodId& disk_method_id = dex_file.GetMethodId(i);
|
||||
MethodId* method_id = new MethodId(GetTypeId(disk_method_id.class_idx_.index_),
|
||||
GetProtoId(disk_method_id.proto_idx_),
|
||||
GetStringId(disk_method_id.name_idx_.index_));
|
||||
method_ids_.AddIndexedItem(method_id, MethodIdsOffset() + i * MethodId::ItemSize(), i);
|
||||
}
|
||||
|
||||
void Collections::CreateClassDef(const DexFile& dex_file, uint32_t i) {
|
||||
const DexFile::ClassDef& disk_class_def = dex_file.GetClassDef(i);
|
||||
const TypeId* class_type = GetTypeId(disk_class_def.class_idx_.index_);
|
||||
uint32_t access_flags = disk_class_def.access_flags_;
|
||||
const TypeId* superclass = GetTypeIdOrNullPtr(disk_class_def.superclass_idx_.index_);
|
||||
|
||||
const DexFile::TypeList* type_list = dex_file.GetInterfacesList(disk_class_def);
|
||||
TypeList* interfaces_type_list = CreateTypeList(type_list, disk_class_def.interfaces_off_);
|
||||
|
||||
const StringId* source_file = GetStringIdOrNullPtr(disk_class_def.source_file_idx_.index_);
|
||||
// Annotations.
|
||||
AnnotationsDirectoryItem* annotations = nullptr;
|
||||
const DexFile::AnnotationsDirectoryItem* disk_annotations_directory_item =
|
||||
dex_file.GetAnnotationsDirectory(disk_class_def);
|
||||
if (disk_annotations_directory_item != nullptr) {
|
||||
annotations = CreateAnnotationsDirectoryItem(
|
||||
dex_file, disk_annotations_directory_item, disk_class_def.annotations_off_);
|
||||
}
|
||||
// Static field initializers.
|
||||
const uint8_t* static_data = dex_file.GetEncodedStaticFieldValuesArray(disk_class_def);
|
||||
EncodedArrayItem* static_values =
|
||||
CreateEncodedArrayItem(static_data, disk_class_def.static_values_off_);
|
||||
ClassData* class_data = CreateClassData(
|
||||
dex_file, dex_file.GetClassData(disk_class_def), disk_class_def.class_data_off_);
|
||||
ClassDef* class_def = new ClassDef(class_type, access_flags, superclass, interfaces_type_list,
|
||||
source_file, annotations, static_values, class_data);
|
||||
class_defs_.AddIndexedItem(class_def, ClassDefsOffset() + i * ClassDef::ItemSize(), i);
|
||||
}
|
||||
|
||||
TypeList* Collections::CreateTypeList(const DexFile::TypeList* dex_type_list, uint32_t offset) {
|
||||
if (dex_type_list == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
auto found_type_list = TypeLists().find(offset);
|
||||
if (found_type_list != TypeLists().end()) {
|
||||
return found_type_list->second.get();
|
||||
}
|
||||
TypeIdVector* type_vector = new TypeIdVector();
|
||||
uint32_t size = dex_type_list->Size();
|
||||
for (uint32_t index = 0; index < size; ++index) {
|
||||
type_vector->push_back(GetTypeId(dex_type_list->GetTypeItem(index).type_idx_.index_));
|
||||
}
|
||||
TypeList* new_type_list = new TypeList(type_vector);
|
||||
type_lists_.AddItem(new_type_list, offset);
|
||||
return new_type_list;
|
||||
}
|
||||
|
||||
EncodedArrayItem* Collections::CreateEncodedArrayItem(const uint8_t* static_data, uint32_t offset) {
|
||||
if (static_data == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
auto found_encoded_array_item = EncodedArrayItems().find(offset);
|
||||
if (found_encoded_array_item != EncodedArrayItems().end()) {
|
||||
return found_encoded_array_item->second.get();
|
||||
}
|
||||
uint32_t size = DecodeUnsignedLeb128(&static_data);
|
||||
EncodedValueVector* values = new EncodedValueVector();
|
||||
for (uint32_t i = 0; i < size; ++i) {
|
||||
values->push_back(std::unique_ptr<EncodedValue>(ReadEncodedValue(&static_data)));
|
||||
}
|
||||
// TODO: Calculate the size of the encoded array.
|
||||
EncodedArrayItem* encoded_array_item = new EncodedArrayItem(values);
|
||||
encoded_array_items_.AddItem(encoded_array_item, offset);
|
||||
return encoded_array_item;
|
||||
}
|
||||
|
||||
AnnotationItem* Collections::CreateAnnotationItem(const DexFile::AnnotationItem* annotation,
|
||||
uint32_t offset) {
|
||||
auto found_annotation_item = AnnotationItems().find(offset);
|
||||
if (found_annotation_item != AnnotationItems().end()) {
|
||||
return found_annotation_item->second.get();
|
||||
}
|
||||
uint8_t visibility = annotation->visibility_;
|
||||
const uint8_t* annotation_data = annotation->annotation_;
|
||||
std::unique_ptr<EncodedValue> encoded_value(
|
||||
ReadEncodedValue(&annotation_data, DexFile::kDexAnnotationAnnotation, 0));
|
||||
// TODO: Calculate the size of the annotation.
|
||||
AnnotationItem* annotation_item =
|
||||
new AnnotationItem(visibility, encoded_value->ReleaseEncodedAnnotation());
|
||||
annotation_items_.AddItem(annotation_item, offset);
|
||||
return annotation_item;
|
||||
}
|
||||
|
||||
|
||||
AnnotationSetItem* Collections::CreateAnnotationSetItem(const DexFile& dex_file,
|
||||
const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset) {
|
||||
if (disk_annotations_item == nullptr || (disk_annotations_item->size_ == 0 && offset == 0)) {
|
||||
return nullptr;
|
||||
}
|
||||
auto found_anno_set_item = AnnotationSetItems().find(offset);
|
||||
if (found_anno_set_item != AnnotationSetItems().end()) {
|
||||
return found_anno_set_item->second.get();
|
||||
}
|
||||
std::vector<AnnotationItem*>* items = new std::vector<AnnotationItem*>();
|
||||
for (uint32_t i = 0; i < disk_annotations_item->size_; ++i) {
|
||||
const DexFile::AnnotationItem* annotation =
|
||||
dex_file.GetAnnotationItem(disk_annotations_item, i);
|
||||
if (annotation == nullptr) {
|
||||
continue;
|
||||
}
|
||||
AnnotationItem* annotation_item =
|
||||
CreateAnnotationItem(annotation, disk_annotations_item->entries_[i]);
|
||||
items->push_back(annotation_item);
|
||||
}
|
||||
AnnotationSetItem* annotation_set_item = new AnnotationSetItem(items);
|
||||
annotation_set_items_.AddItem(annotation_set_item, offset);
|
||||
return annotation_set_item;
|
||||
}
|
||||
|
||||
AnnotationsDirectoryItem* Collections::CreateAnnotationsDirectoryItem(const DexFile& dex_file,
|
||||
const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset) {
|
||||
auto found_anno_dir_item = AnnotationsDirectoryItems().find(offset);
|
||||
if (found_anno_dir_item != AnnotationsDirectoryItems().end()) {
|
||||
return found_anno_dir_item->second.get();
|
||||
}
|
||||
const DexFile::AnnotationSetItem* class_set_item =
|
||||
dex_file.GetClassAnnotationSet(disk_annotations_item);
|
||||
AnnotationSetItem* class_annotation = nullptr;
|
||||
if (class_set_item != nullptr) {
|
||||
uint32_t item_offset = disk_annotations_item->class_annotations_off_;
|
||||
class_annotation = CreateAnnotationSetItem(dex_file, class_set_item, item_offset);
|
||||
}
|
||||
const DexFile::FieldAnnotationsItem* fields =
|
||||
dex_file.GetFieldAnnotations(disk_annotations_item);
|
||||
FieldAnnotationVector* field_annotations = nullptr;
|
||||
if (fields != nullptr) {
|
||||
field_annotations = new FieldAnnotationVector();
|
||||
for (uint32_t i = 0; i < disk_annotations_item->fields_size_; ++i) {
|
||||
FieldId* field_id = GetFieldId(fields[i].field_idx_);
|
||||
const DexFile::AnnotationSetItem* field_set_item =
|
||||
dex_file.GetFieldAnnotationSetItem(fields[i]);
|
||||
uint32_t annotation_set_offset = fields[i].annotations_off_;
|
||||
AnnotationSetItem* annotation_set_item =
|
||||
CreateAnnotationSetItem(dex_file, field_set_item, annotation_set_offset);
|
||||
field_annotations->push_back(std::unique_ptr<FieldAnnotation>(
|
||||
new FieldAnnotation(field_id, annotation_set_item)));
|
||||
}
|
||||
}
|
||||
const DexFile::MethodAnnotationsItem* methods =
|
||||
dex_file.GetMethodAnnotations(disk_annotations_item);
|
||||
MethodAnnotationVector* method_annotations = nullptr;
|
||||
if (methods != nullptr) {
|
||||
method_annotations = new MethodAnnotationVector();
|
||||
for (uint32_t i = 0; i < disk_annotations_item->methods_size_; ++i) {
|
||||
MethodId* method_id = GetMethodId(methods[i].method_idx_);
|
||||
const DexFile::AnnotationSetItem* method_set_item =
|
||||
dex_file.GetMethodAnnotationSetItem(methods[i]);
|
||||
uint32_t annotation_set_offset = methods[i].annotations_off_;
|
||||
AnnotationSetItem* annotation_set_item =
|
||||
CreateAnnotationSetItem(dex_file, method_set_item, annotation_set_offset);
|
||||
method_annotations->push_back(std::unique_ptr<MethodAnnotation>(
|
||||
new MethodAnnotation(method_id, annotation_set_item)));
|
||||
}
|
||||
}
|
||||
const DexFile::ParameterAnnotationsItem* parameters =
|
||||
dex_file.GetParameterAnnotations(disk_annotations_item);
|
||||
ParameterAnnotationVector* parameter_annotations = nullptr;
|
||||
if (parameters != nullptr) {
|
||||
parameter_annotations = new ParameterAnnotationVector();
|
||||
for (uint32_t i = 0; i < disk_annotations_item->parameters_size_; ++i) {
|
||||
MethodId* method_id = GetMethodId(parameters[i].method_idx_);
|
||||
const DexFile::AnnotationSetRefList* list =
|
||||
dex_file.GetParameterAnnotationSetRefList(¶meters[i]);
|
||||
parameter_annotations->push_back(std::unique_ptr<ParameterAnnotation>(
|
||||
GenerateParameterAnnotation(dex_file, method_id, list, parameters[i].annotations_off_)));
|
||||
}
|
||||
}
|
||||
// TODO: Calculate the size of the annotations directory.
|
||||
AnnotationsDirectoryItem* annotations_directory_item = new AnnotationsDirectoryItem(
|
||||
class_annotation, field_annotations, method_annotations, parameter_annotations);
|
||||
annotations_directory_items_.AddItem(annotations_directory_item, offset);
|
||||
return annotations_directory_item;
|
||||
}
|
||||
|
||||
ParameterAnnotation* Collections::GenerateParameterAnnotation(
|
||||
const DexFile& dex_file, MethodId* method_id,
|
||||
const DexFile::AnnotationSetRefList* annotation_set_ref_list, uint32_t offset) {
|
||||
AnnotationSetRefList* set_ref_list = nullptr;
|
||||
auto found_set_ref_list = AnnotationSetRefLists().find(offset);
|
||||
if (found_set_ref_list != AnnotationSetRefLists().end()) {
|
||||
set_ref_list = found_set_ref_list->second.get();
|
||||
}
|
||||
if (set_ref_list == nullptr) {
|
||||
std::vector<AnnotationSetItem*>* annotations = new std::vector<AnnotationSetItem*>();
|
||||
for (uint32_t i = 0; i < annotation_set_ref_list->size_; ++i) {
|
||||
const DexFile::AnnotationSetItem* annotation_set_item =
|
||||
dex_file.GetSetRefItemItem(&annotation_set_ref_list->list_[i]);
|
||||
uint32_t set_offset = annotation_set_ref_list->list_[i].annotations_off_;
|
||||
annotations->push_back(CreateAnnotationSetItem(dex_file, annotation_set_item, set_offset));
|
||||
}
|
||||
set_ref_list = new AnnotationSetRefList(annotations);
|
||||
annotation_set_ref_lists_.AddItem(set_ref_list, offset);
|
||||
}
|
||||
return new ParameterAnnotation(method_id, set_ref_list);
|
||||
}
|
||||
|
||||
CodeItem* Collections::CreateCodeItem(const DexFile& dex_file,
|
||||
const DexFile::CodeItem& disk_code_item, uint32_t offset) {
|
||||
uint16_t registers_size = disk_code_item.registers_size_;
|
||||
uint16_t ins_size = disk_code_item.ins_size_;
|
||||
uint16_t outs_size = disk_code_item.outs_size_;
|
||||
uint32_t tries_size = disk_code_item.tries_size_;
|
||||
|
||||
// TODO: Calculate the size of the debug info.
|
||||
const uint8_t* debug_info_stream = dex_file.GetDebugInfoStream(&disk_code_item);
|
||||
DebugInfoItem* debug_info = nullptr;
|
||||
if (debug_info_stream != nullptr) {
|
||||
debug_info = debug_info_items_.GetExistingObject(disk_code_item.debug_info_off_);
|
||||
if (debug_info == nullptr) {
|
||||
uint32_t debug_info_size = GetDebugInfoStreamSize(debug_info_stream);
|
||||
uint8_t* debug_info_buffer = new uint8_t[debug_info_size];
|
||||
memcpy(debug_info_buffer, debug_info_stream, debug_info_size);
|
||||
debug_info = new DebugInfoItem(debug_info_size, debug_info_buffer);
|
||||
debug_info_items_.AddItem(debug_info, disk_code_item.debug_info_off_);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t insns_size = disk_code_item.insns_size_in_code_units_;
|
||||
uint16_t* insns = new uint16_t[insns_size];
|
||||
memcpy(insns, disk_code_item.insns_, insns_size * sizeof(uint16_t));
|
||||
|
||||
TryItemVector* tries = nullptr;
|
||||
CatchHandlerVector* handler_list = nullptr;
|
||||
if (tries_size > 0) {
|
||||
tries = new TryItemVector();
|
||||
handler_list = new CatchHandlerVector();
|
||||
for (uint32_t i = 0; i < tries_size; ++i) {
|
||||
const DexFile::TryItem* disk_try_item = dex_file.GetTryItems(disk_code_item, i);
|
||||
uint32_t start_addr = disk_try_item->start_addr_;
|
||||
uint16_t insn_count = disk_try_item->insn_count_;
|
||||
uint16_t handler_off = disk_try_item->handler_off_;
|
||||
const CatchHandler* handlers = nullptr;
|
||||
for (std::unique_ptr<const CatchHandler>& existing_handlers : *handler_list) {
|
||||
if (handler_off == existing_handlers->GetListOffset()) {
|
||||
handlers = existing_handlers.get();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (handlers == nullptr) {
|
||||
bool catch_all = false;
|
||||
TypeAddrPairVector* addr_pairs = new TypeAddrPairVector();
|
||||
for (CatchHandlerIterator it(disk_code_item, *disk_try_item); it.HasNext(); it.Next()) {
|
||||
const dex::TypeIndex type_index = it.GetHandlerTypeIndex();
|
||||
const TypeId* type_id = GetTypeIdOrNullPtr(type_index.index_);
|
||||
catch_all |= type_id == nullptr;
|
||||
addr_pairs->push_back(std::unique_ptr<const TypeAddrPair>(
|
||||
new TypeAddrPair(type_id, it.GetHandlerAddress())));
|
||||
}
|
||||
handlers = new CatchHandler(catch_all, handler_off, addr_pairs);
|
||||
handler_list->push_back(std::unique_ptr<const CatchHandler>(handlers));
|
||||
}
|
||||
TryItem* try_item = new TryItem(start_addr, insn_count, handlers);
|
||||
tries->push_back(std::unique_ptr<const TryItem>(try_item));
|
||||
}
|
||||
// Manually walk catch handlers list and add any missing handlers unreferenced by try items.
|
||||
const uint8_t* handlers_base = DexFile::GetCatchHandlerData(disk_code_item, 0);
|
||||
const uint8_t* handlers_data = handlers_base;
|
||||
uint32_t handlers_size = DecodeUnsignedLeb128(&handlers_data);
|
||||
while (handlers_size > handler_list->size()) {
|
||||
bool already_added = false;
|
||||
uint16_t handler_off = handlers_data - handlers_base;
|
||||
for (std::unique_ptr<const CatchHandler>& existing_handlers : *handler_list) {
|
||||
if (handler_off == existing_handlers->GetListOffset()) {
|
||||
already_added = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int32_t size = DecodeSignedLeb128(&handlers_data);
|
||||
bool has_catch_all = size <= 0;
|
||||
if (has_catch_all) {
|
||||
size = -size;
|
||||
}
|
||||
if (already_added) {
|
||||
for (int32_t i = 0; i < size; i++) {
|
||||
DecodeUnsignedLeb128(&handlers_data);
|
||||
DecodeUnsignedLeb128(&handlers_data);
|
||||
}
|
||||
if (has_catch_all) {
|
||||
DecodeUnsignedLeb128(&handlers_data);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
TypeAddrPairVector* addr_pairs = new TypeAddrPairVector();
|
||||
for (int32_t i = 0; i < size; i++) {
|
||||
const TypeId* type_id = GetTypeIdOrNullPtr(DecodeUnsignedLeb128(&handlers_data));
|
||||
uint32_t addr = DecodeUnsignedLeb128(&handlers_data);
|
||||
addr_pairs->push_back(
|
||||
std::unique_ptr<const TypeAddrPair>(new TypeAddrPair(type_id, addr)));
|
||||
}
|
||||
if (has_catch_all) {
|
||||
uint32_t addr = DecodeUnsignedLeb128(&handlers_data);
|
||||
addr_pairs->push_back(
|
||||
std::unique_ptr<const TypeAddrPair>(new TypeAddrPair(nullptr, addr)));
|
||||
}
|
||||
const CatchHandler* handler = new CatchHandler(has_catch_all, handler_off, addr_pairs);
|
||||
handler_list->push_back(std::unique_ptr<const CatchHandler>(handler));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t size = DexFile::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);
|
||||
code_items_.AddItem(code_item, offset);
|
||||
// Add "fixup" references to types, strings, methods, and fields.
|
||||
// This is temporary, as we will probably want more detailed parsing of the
|
||||
// instructions here.
|
||||
std::unique_ptr<std::vector<TypeId*>> type_ids(new std::vector<TypeId*>());
|
||||
std::unique_ptr<std::vector<StringId*>> string_ids(new std::vector<StringId*>());
|
||||
std::unique_ptr<std::vector<MethodId*>> method_ids(new std::vector<MethodId*>());
|
||||
std::unique_ptr<std::vector<FieldId*>> field_ids(new std::vector<FieldId*>());
|
||||
if (GetIdsFromByteCode(*this,
|
||||
code_item,
|
||||
type_ids.get(),
|
||||
string_ids.get(),
|
||||
method_ids.get(),
|
||||
field_ids.get())) {
|
||||
CodeFixups* fixups = new CodeFixups(type_ids.release(),
|
||||
string_ids.release(),
|
||||
method_ids.release(),
|
||||
field_ids.release());
|
||||
code_item->SetCodeFixups(fixups);
|
||||
}
|
||||
|
||||
return code_item;
|
||||
}
|
||||
|
||||
MethodItem* Collections::GenerateMethodItem(const DexFile& dex_file, ClassDataItemIterator& cdii) {
|
||||
MethodId* method_id = GetMethodId(cdii.GetMemberIndex());
|
||||
uint32_t access_flags = cdii.GetRawMemberAccessFlags();
|
||||
const DexFile::CodeItem* disk_code_item = cdii.GetMethodCodeItem();
|
||||
CodeItem* code_item = code_items_.GetExistingObject(cdii.GetMethodCodeItemOffset());
|
||||
DebugInfoItem* debug_info = nullptr;
|
||||
if (disk_code_item != nullptr) {
|
||||
if (code_item == nullptr) {
|
||||
code_item = CreateCodeItem(dex_file, *disk_code_item, cdii.GetMethodCodeItemOffset());
|
||||
}
|
||||
debug_info = code_item->DebugInfo();
|
||||
}
|
||||
if (debug_info != nullptr) {
|
||||
bool is_static = (access_flags & kAccStatic) != 0;
|
||||
dex_file.DecodeDebugLocalInfo(
|
||||
disk_code_item, is_static, cdii.GetMemberIndex(), GetLocalsCb, debug_info);
|
||||
dex_file.DecodeDebugPositionInfo(disk_code_item, GetPositionsCb, debug_info);
|
||||
}
|
||||
return new MethodItem(access_flags, method_id, code_item);
|
||||
}
|
||||
|
||||
ClassData* Collections::CreateClassData(
|
||||
const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset) {
|
||||
// Read the fields and methods defined by the class, resolving the circular reference from those
|
||||
// to classes by setting class at the same time.
|
||||
ClassData* class_data = class_datas_.GetExistingObject(offset);
|
||||
if (class_data == nullptr && encoded_data != nullptr) {
|
||||
ClassDataItemIterator cdii(dex_file, encoded_data);
|
||||
// Static fields.
|
||||
FieldItemVector* static_fields = new FieldItemVector();
|
||||
for (; cdii.HasNextStaticField(); cdii.Next()) {
|
||||
FieldId* field_item = GetFieldId(cdii.GetMemberIndex());
|
||||
uint32_t access_flags = cdii.GetRawMemberAccessFlags();
|
||||
static_fields->push_back(std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
|
||||
}
|
||||
// Instance fields.
|
||||
FieldItemVector* instance_fields = new FieldItemVector();
|
||||
for (; cdii.HasNextInstanceField(); cdii.Next()) {
|
||||
FieldId* field_item = GetFieldId(cdii.GetMemberIndex());
|
||||
uint32_t access_flags = cdii.GetRawMemberAccessFlags();
|
||||
instance_fields->push_back(
|
||||
std::unique_ptr<FieldItem>(new FieldItem(access_flags, field_item)));
|
||||
}
|
||||
// Direct methods.
|
||||
MethodItemVector* direct_methods = new MethodItemVector();
|
||||
for (; cdii.HasNextDirectMethod(); cdii.Next()) {
|
||||
direct_methods->push_back(std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
|
||||
}
|
||||
// Virtual methods.
|
||||
MethodItemVector* virtual_methods = new MethodItemVector();
|
||||
for (; cdii.HasNextVirtualMethod(); cdii.Next()) {
|
||||
virtual_methods->push_back(std::unique_ptr<MethodItem>(GenerateMethodItem(dex_file, cdii)));
|
||||
}
|
||||
class_data = new ClassData(static_fields, instance_fields, direct_methods, virtual_methods);
|
||||
class_data->SetSize(cdii.EndDataPointer() - encoded_data);
|
||||
class_datas_.AddItem(class_data, offset);
|
||||
}
|
||||
return class_data;
|
||||
}
|
||||
|
||||
void Collections::CreateCallSitesAndMethodHandles(const DexFile& dex_file) {
|
||||
// Iterate through the map list and set the offset of the CallSiteIds and MethodHandleItems.
|
||||
const DexFile::MapList* map =
|
||||
reinterpret_cast<const DexFile::MapList*>(dex_file.Begin() + MapListOffset());
|
||||
for (uint32_t i = 0; i < map->size_; ++i) {
|
||||
const DexFile::MapItem* item = map->list_ + i;
|
||||
switch (item->type_) {
|
||||
case DexFile::kDexTypeCallSiteIdItem:
|
||||
SetCallSiteIdsOffset(item->offset_);
|
||||
break;
|
||||
case DexFile::kDexTypeMethodHandleItem:
|
||||
SetMethodHandleItemsOffset(item->offset_);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Populate MethodHandleItems first (CallSiteIds may depend on them).
|
||||
for (uint32_t i = 0; i < dex_file.NumMethodHandles(); i++) {
|
||||
CreateMethodHandleItem(dex_file, i);
|
||||
}
|
||||
// Populate CallSiteIds.
|
||||
for (uint32_t i = 0; i < dex_file.NumCallSiteIds(); i++) {
|
||||
CreateCallSiteId(dex_file, i);
|
||||
}
|
||||
}
|
||||
|
||||
void Collections::CreateCallSiteId(const DexFile& dex_file, uint32_t i) {
|
||||
const DexFile::CallSiteIdItem& disk_call_site_id = dex_file.GetCallSiteId(i);
|
||||
const uint8_t* disk_call_item_ptr = dex_file.Begin() + disk_call_site_id.data_off_;
|
||||
EncodedArrayItem* call_site_item =
|
||||
CreateEncodedArrayItem(disk_call_item_ptr, disk_call_site_id.data_off_);
|
||||
|
||||
CallSiteId* call_site_id = new CallSiteId(call_site_item);
|
||||
call_site_ids_.AddIndexedItem(call_site_id, CallSiteIdsOffset() + i * CallSiteId::ItemSize(), i);
|
||||
}
|
||||
|
||||
void Collections::CreateMethodHandleItem(const DexFile& dex_file, uint32_t i) {
|
||||
const DexFile::MethodHandleItem& disk_method_handle = dex_file.GetMethodHandle(i);
|
||||
uint16_t index = disk_method_handle.field_or_method_idx_;
|
||||
DexFile::MethodHandleType type =
|
||||
static_cast<DexFile::MethodHandleType>(disk_method_handle.method_handle_type_);
|
||||
bool is_invoke = type == DexFile::MethodHandleType::kInvokeStatic ||
|
||||
type == DexFile::MethodHandleType::kInvokeInstance ||
|
||||
type == DexFile::MethodHandleType::kInvokeConstructor ||
|
||||
type == DexFile::MethodHandleType::kInvokeDirect ||
|
||||
type == DexFile::MethodHandleType::kInvokeInterface;
|
||||
static_assert(DexFile::MethodHandleType::kLast == DexFile::MethodHandleType::kInvokeInterface,
|
||||
"Unexpected method handle types.");
|
||||
IndexedItem* field_or_method_id;
|
||||
if (is_invoke) {
|
||||
field_or_method_id = GetMethodId(index);
|
||||
} else {
|
||||
field_or_method_id = GetFieldId(index);
|
||||
}
|
||||
MethodHandleItem* method_handle = new MethodHandleItem(type, field_or_method_id);
|
||||
method_handle_items_.AddIndexedItem(
|
||||
method_handle, MethodHandleItemsOffset() + i * MethodHandleItem::ItemSize(), i);
|
||||
}
|
||||
|
||||
static uint32_t HeaderOffset(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint32_t HeaderSize(const dex_ir::Collections& collections ATTRIBUTE_UNUSED) {
|
||||
// Size is in elements, so there is only one header.
|
||||
return 1;
|
||||
}
|
||||
|
||||
// The description of each dex file section type.
|
||||
struct FileSectionDescriptor {
|
||||
public:
|
||||
std::string name;
|
||||
uint16_t type;
|
||||
// A function that when applied to a collection object, gives the size of the section.
|
||||
std::function<uint32_t(const dex_ir::Collections&)> size_fn;
|
||||
// A function that when applied to a collection object, gives the offset of the section.
|
||||
std::function<uint32_t(const dex_ir::Collections&)> offset_fn;
|
||||
};
|
||||
|
||||
static const FileSectionDescriptor kFileSectionDescriptors[] = {
|
||||
{
|
||||
"Header",
|
||||
DexFile::kDexTypeHeaderItem,
|
||||
&HeaderSize,
|
||||
&HeaderOffset,
|
||||
}, {
|
||||
"StringId",
|
||||
DexFile::kDexTypeStringIdItem,
|
||||
&dex_ir::Collections::StringIdsSize,
|
||||
&dex_ir::Collections::StringIdsOffset
|
||||
}, {
|
||||
"TypeId",
|
||||
DexFile::kDexTypeTypeIdItem,
|
||||
&dex_ir::Collections::TypeIdsSize,
|
||||
&dex_ir::Collections::TypeIdsOffset
|
||||
}, {
|
||||
"ProtoId",
|
||||
DexFile::kDexTypeProtoIdItem,
|
||||
&dex_ir::Collections::ProtoIdsSize,
|
||||
&dex_ir::Collections::ProtoIdsOffset
|
||||
}, {
|
||||
"FieldId",
|
||||
DexFile::kDexTypeFieldIdItem,
|
||||
&dex_ir::Collections::FieldIdsSize,
|
||||
&dex_ir::Collections::FieldIdsOffset
|
||||
}, {
|
||||
"MethodId",
|
||||
DexFile::kDexTypeMethodIdItem,
|
||||
&dex_ir::Collections::MethodIdsSize,
|
||||
&dex_ir::Collections::MethodIdsOffset
|
||||
}, {
|
||||
"ClassDef",
|
||||
DexFile::kDexTypeClassDefItem,
|
||||
&dex_ir::Collections::ClassDefsSize,
|
||||
&dex_ir::Collections::ClassDefsOffset
|
||||
}, {
|
||||
"CallSiteId",
|
||||
DexFile::kDexTypeCallSiteIdItem,
|
||||
&dex_ir::Collections::CallSiteIdsSize,
|
||||
&dex_ir::Collections::CallSiteIdsOffset
|
||||
}, {
|
||||
"MethodHandle",
|
||||
DexFile::kDexTypeMethodHandleItem,
|
||||
&dex_ir::Collections::MethodHandleItemsSize,
|
||||
&dex_ir::Collections::MethodHandleItemsOffset
|
||||
}, {
|
||||
"StringData",
|
||||
DexFile::kDexTypeStringDataItem,
|
||||
&dex_ir::Collections::StringDatasSize,
|
||||
&dex_ir::Collections::StringDatasOffset
|
||||
}, {
|
||||
"TypeList",
|
||||
DexFile::kDexTypeTypeList,
|
||||
&dex_ir::Collections::TypeListsSize,
|
||||
&dex_ir::Collections::TypeListsOffset
|
||||
}, {
|
||||
"EncArr",
|
||||
DexFile::kDexTypeEncodedArrayItem,
|
||||
&dex_ir::Collections::EncodedArrayItemsSize,
|
||||
&dex_ir::Collections::EncodedArrayItemsOffset
|
||||
}, {
|
||||
"Annotation",
|
||||
DexFile::kDexTypeAnnotationItem,
|
||||
&dex_ir::Collections::AnnotationItemsSize,
|
||||
&dex_ir::Collections::AnnotationItemsOffset
|
||||
}, {
|
||||
"AnnoSet",
|
||||
DexFile::kDexTypeAnnotationSetItem,
|
||||
&dex_ir::Collections::AnnotationSetItemsSize,
|
||||
&dex_ir::Collections::AnnotationSetItemsOffset
|
||||
}, {
|
||||
"AnnoSetRL",
|
||||
DexFile::kDexTypeAnnotationSetRefList,
|
||||
&dex_ir::Collections::AnnotationSetRefListsSize,
|
||||
&dex_ir::Collections::AnnotationSetRefListsOffset
|
||||
}, {
|
||||
"AnnoDir",
|
||||
DexFile::kDexTypeAnnotationsDirectoryItem,
|
||||
&dex_ir::Collections::AnnotationsDirectoryItemsSize,
|
||||
&dex_ir::Collections::AnnotationsDirectoryItemsOffset
|
||||
}, {
|
||||
"DebugInfo",
|
||||
DexFile::kDexTypeDebugInfoItem,
|
||||
&dex_ir::Collections::DebugInfoItemsSize,
|
||||
&dex_ir::Collections::DebugInfoItemsOffset
|
||||
}, {
|
||||
"CodeItem",
|
||||
DexFile::kDexTypeCodeItem,
|
||||
&dex_ir::Collections::CodeItemsSize,
|
||||
&dex_ir::Collections::CodeItemsOffset
|
||||
}, {
|
||||
"ClassData",
|
||||
DexFile::kDexTypeClassDataItem,
|
||||
&dex_ir::Collections::ClassDatasSize,
|
||||
&dex_ir::Collections::ClassDatasOffset
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<dex_ir::DexFileSection> GetSortedDexFileSections(dex_ir::Header* header,
|
||||
dex_ir::SortDirection direction) {
|
||||
const dex_ir::Collections& collections = header->GetCollections();
|
||||
std::vector<dex_ir::DexFileSection> sorted_sections;
|
||||
// Build the table that will map from offset to color
|
||||
for (const FileSectionDescriptor& s : kFileSectionDescriptors) {
|
||||
sorted_sections.push_back(dex_ir::DexFileSection(s.name,
|
||||
s.type,
|
||||
s.size_fn(collections),
|
||||
s.offset_fn(collections)));
|
||||
}
|
||||
// Sort by offset.
|
||||
std::sort(sorted_sections.begin(),
|
||||
sorted_sections.end(),
|
||||
[=](dex_ir::DexFileSection& a, dex_ir::DexFileSection& b) {
|
||||
if (direction == SortDirection::kSortDescending) {
|
||||
return a.offset > b.offset;
|
||||
} else {
|
||||
return a.offset < b.offset;
|
||||
}
|
||||
});
|
||||
return sorted_sections;
|
||||
}
|
||||
|
||||
} // namespace dex_ir
|
||||
} // namespace art
|
1229
android/art/dexlayout/dex_ir.h
Normal file
1229
android/art/dexlayout/dex_ir.h
Normal file
File diff suppressed because it is too large
Load diff
169
android/art/dexlayout/dex_ir_builder.cc
Normal file
169
android/art/dexlayout/dex_ir_builder.cc
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Header file of an in-memory representation of DEX files.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
#include "dex_ir_builder.h"
|
||||
|
||||
namespace art {
|
||||
namespace dex_ir {
|
||||
|
||||
static void CheckAndSetRemainingOffsets(const DexFile& dex_file, Collections* collections);
|
||||
|
||||
Header* DexIrBuilder(const DexFile& dex_file) {
|
||||
const DexFile::Header& disk_header = dex_file.GetHeader();
|
||||
Header* header = new Header(disk_header.magic_,
|
||||
disk_header.checksum_,
|
||||
disk_header.signature_,
|
||||
disk_header.endian_tag_,
|
||||
disk_header.file_size_,
|
||||
disk_header.header_size_,
|
||||
disk_header.link_size_,
|
||||
disk_header.link_off_,
|
||||
disk_header.data_size_,
|
||||
disk_header.data_off_);
|
||||
Collections& collections = header->GetCollections();
|
||||
// Walk the rest of the header fields.
|
||||
// StringId table.
|
||||
collections.SetStringIdsOffset(disk_header.string_ids_off_);
|
||||
for (uint32_t i = 0; i < dex_file.NumStringIds(); ++i) {
|
||||
collections.CreateStringId(dex_file, i);
|
||||
}
|
||||
// TypeId table.
|
||||
collections.SetTypeIdsOffset(disk_header.type_ids_off_);
|
||||
for (uint32_t i = 0; i < dex_file.NumTypeIds(); ++i) {
|
||||
collections.CreateTypeId(dex_file, i);
|
||||
}
|
||||
// ProtoId table.
|
||||
collections.SetProtoIdsOffset(disk_header.proto_ids_off_);
|
||||
for (uint32_t i = 0; i < dex_file.NumProtoIds(); ++i) {
|
||||
collections.CreateProtoId(dex_file, i);
|
||||
}
|
||||
// FieldId table.
|
||||
collections.SetFieldIdsOffset(disk_header.field_ids_off_);
|
||||
for (uint32_t i = 0; i < dex_file.NumFieldIds(); ++i) {
|
||||
collections.CreateFieldId(dex_file, i);
|
||||
}
|
||||
// MethodId table.
|
||||
collections.SetMethodIdsOffset(disk_header.method_ids_off_);
|
||||
for (uint32_t i = 0; i < dex_file.NumMethodIds(); ++i) {
|
||||
collections.CreateMethodId(dex_file, i);
|
||||
}
|
||||
// ClassDef table.
|
||||
collections.SetClassDefsOffset(disk_header.class_defs_off_);
|
||||
for (uint32_t i = 0; i < dex_file.NumClassDefs(); ++i) {
|
||||
collections.CreateClassDef(dex_file, i);
|
||||
}
|
||||
// MapItem.
|
||||
collections.SetMapListOffset(disk_header.map_off_);
|
||||
// CallSiteIds and MethodHandleItems.
|
||||
collections.CreateCallSitesAndMethodHandles(dex_file);
|
||||
|
||||
CheckAndSetRemainingOffsets(dex_file, &collections);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
static void CheckAndSetRemainingOffsets(const DexFile& dex_file, Collections* collections) {
|
||||
const DexFile::Header& disk_header = dex_file.GetHeader();
|
||||
// Read MapItems and validate/set remaining offsets.
|
||||
const DexFile::MapList* map =
|
||||
reinterpret_cast<const DexFile::MapList*>(dex_file.Begin() + disk_header.map_off_);
|
||||
const uint32_t count = map->size_;
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
const DexFile::MapItem* item = map->list_ + i;
|
||||
switch (item->type_) {
|
||||
case DexFile::kDexTypeHeaderItem:
|
||||
CHECK_EQ(item->size_, 1u);
|
||||
CHECK_EQ(item->offset_, 0u);
|
||||
break;
|
||||
case DexFile::kDexTypeStringIdItem:
|
||||
CHECK_EQ(item->size_, collections->StringIdsSize());
|
||||
CHECK_EQ(item->offset_, collections->StringIdsOffset());
|
||||
break;
|
||||
case DexFile::kDexTypeTypeIdItem:
|
||||
CHECK_EQ(item->size_, collections->TypeIdsSize());
|
||||
CHECK_EQ(item->offset_, collections->TypeIdsOffset());
|
||||
break;
|
||||
case DexFile::kDexTypeProtoIdItem:
|
||||
CHECK_EQ(item->size_, collections->ProtoIdsSize());
|
||||
CHECK_EQ(item->offset_, collections->ProtoIdsOffset());
|
||||
break;
|
||||
case DexFile::kDexTypeFieldIdItem:
|
||||
CHECK_EQ(item->size_, collections->FieldIdsSize());
|
||||
CHECK_EQ(item->offset_, collections->FieldIdsOffset());
|
||||
break;
|
||||
case DexFile::kDexTypeMethodIdItem:
|
||||
CHECK_EQ(item->size_, collections->MethodIdsSize());
|
||||
CHECK_EQ(item->offset_, collections->MethodIdsOffset());
|
||||
break;
|
||||
case DexFile::kDexTypeClassDefItem:
|
||||
CHECK_EQ(item->size_, collections->ClassDefsSize());
|
||||
CHECK_EQ(item->offset_, collections->ClassDefsOffset());
|
||||
break;
|
||||
case DexFile::kDexTypeCallSiteIdItem:
|
||||
CHECK_EQ(item->size_, collections->CallSiteIdsSize());
|
||||
CHECK_EQ(item->offset_, collections->CallSiteIdsOffset());
|
||||
break;
|
||||
case DexFile::kDexTypeMethodHandleItem:
|
||||
CHECK_EQ(item->size_, collections->MethodHandleItemsSize());
|
||||
CHECK_EQ(item->offset_, collections->MethodHandleItemsOffset());
|
||||
break;
|
||||
case DexFile::kDexTypeMapList:
|
||||
CHECK_EQ(item->size_, 1u);
|
||||
CHECK_EQ(item->offset_, disk_header.map_off_);
|
||||
break;
|
||||
case DexFile::kDexTypeTypeList:
|
||||
collections->SetTypeListsOffset(item->offset_);
|
||||
break;
|
||||
case DexFile::kDexTypeAnnotationSetRefList:
|
||||
collections->SetAnnotationSetRefListsOffset(item->offset_);
|
||||
break;
|
||||
case DexFile::kDexTypeAnnotationSetItem:
|
||||
collections->SetAnnotationSetItemsOffset(item->offset_);
|
||||
break;
|
||||
case DexFile::kDexTypeClassDataItem:
|
||||
collections->SetClassDatasOffset(item->offset_);
|
||||
break;
|
||||
case DexFile::kDexTypeCodeItem:
|
||||
collections->SetCodeItemsOffset(item->offset_);
|
||||
break;
|
||||
case DexFile::kDexTypeStringDataItem:
|
||||
collections->SetStringDatasOffset(item->offset_);
|
||||
break;
|
||||
case DexFile::kDexTypeDebugInfoItem:
|
||||
collections->SetDebugInfoItemsOffset(item->offset_);
|
||||
break;
|
||||
case DexFile::kDexTypeAnnotationItem:
|
||||
collections->SetAnnotationItemsOffset(item->offset_);
|
||||
break;
|
||||
case DexFile::kDexTypeEncodedArrayItem:
|
||||
collections->SetEncodedArrayItemsOffset(item->offset_);
|
||||
break;
|
||||
case DexFile::kDexTypeAnnotationsDirectoryItem:
|
||||
collections->SetAnnotationsDirectoryItemsOffset(item->offset_);
|
||||
break;
|
||||
default:
|
||||
LOG(ERROR) << "Unknown map list item type.";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace dex_ir
|
||||
} // namespace art
|
32
android/art/dexlayout/dex_ir_builder.h
Normal file
32
android/art/dexlayout/dex_ir_builder.h
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Header file of an in-memory representation of DEX files.
|
||||
*/
|
||||
|
||||
#ifndef ART_DEXLAYOUT_DEX_IR_BUILDER_H_
|
||||
#define ART_DEXLAYOUT_DEX_IR_BUILDER_H_
|
||||
|
||||
#include "dex_ir.h"
|
||||
|
||||
namespace art {
|
||||
namespace dex_ir {
|
||||
|
||||
dex_ir::Header* DexIrBuilder(const DexFile& dex_file);
|
||||
|
||||
} // namespace dex_ir
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_DEXLAYOUT_DEX_IR_BUILDER_H_
|
1120
android/art/dexlayout/dex_verify.cc
Normal file
1120
android/art/dexlayout/dex_verify.cc
Normal file
File diff suppressed because it is too large
Load diff
125
android/art/dexlayout/dex_verify.h
Normal file
125
android/art/dexlayout/dex_verify.h
Normal file
|
@ -0,0 +1,125 @@
|
|||
/*
|
||||
* 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 VerifyClassDefs(std::vector<std::unique_ptr<dex_ir::ClassDef>>& orig,
|
||||
std::vector<std::unique_ptr<dex_ir::ClassDef>>& output,
|
||||
std::string* error_msg);
|
||||
bool VerifyClassDef(dex_ir::ClassDef* orig, dex_ir::ClassDef* output, std::string* error_msg);
|
||||
|
||||
bool VerifyTypeList(const dex_ir::TypeList* orig, const dex_ir::TypeList* output);
|
||||
|
||||
bool VerifyAnnotationsDirectory(dex_ir::AnnotationsDirectoryItem* orig,
|
||||
dex_ir::AnnotationsDirectoryItem* output,
|
||||
std::string* error_msg);
|
||||
bool VerifyFieldAnnotations(dex_ir::FieldAnnotationVector* orig,
|
||||
dex_ir::FieldAnnotationVector* output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyMethodAnnotations(dex_ir::MethodAnnotationVector* orig,
|
||||
dex_ir::MethodAnnotationVector* output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyParameterAnnotations(dex_ir::ParameterAnnotationVector* orig,
|
||||
dex_ir::ParameterAnnotationVector* output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyAnnotationSetRefList(dex_ir::AnnotationSetRefList* orig,
|
||||
dex_ir::AnnotationSetRefList* output,
|
||||
std::string* error_msg);
|
||||
bool VerifyAnnotationSet(dex_ir::AnnotationSetItem* orig,
|
||||
dex_ir::AnnotationSetItem* output,
|
||||
std::string* error_msg);
|
||||
bool VerifyAnnotation(dex_ir::AnnotationItem* orig,
|
||||
dex_ir::AnnotationItem* output,
|
||||
std::string* error_msg);
|
||||
bool VerifyEncodedAnnotation(dex_ir::EncodedAnnotation* orig,
|
||||
dex_ir::EncodedAnnotation* output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyAnnotationElement(dex_ir::AnnotationElement* orig,
|
||||
dex_ir::AnnotationElement* output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyEncodedValue(dex_ir::EncodedValue* orig,
|
||||
dex_ir::EncodedValue* output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyEncodedArray(dex_ir::EncodedArrayItem* orig,
|
||||
dex_ir::EncodedArrayItem* output,
|
||||
std::string* error_msg);
|
||||
|
||||
bool VerifyClassData(dex_ir::ClassData* orig, dex_ir::ClassData* output, std::string* error_msg);
|
||||
bool VerifyFields(dex_ir::FieldItemVector* orig,
|
||||
dex_ir::FieldItemVector* output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyMethods(dex_ir::MethodItemVector* orig,
|
||||
dex_ir::MethodItemVector* output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyCode(dex_ir::CodeItem* orig, dex_ir::CodeItem* output, std::string* error_msg);
|
||||
bool VerifyDebugInfo(dex_ir::DebugInfoItem* orig,
|
||||
dex_ir::DebugInfoItem* output,
|
||||
std::string* error_msg);
|
||||
bool VerifyPositionInfo(dex_ir::PositionInfoVector& orig,
|
||||
dex_ir::PositionInfoVector& output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyLocalInfo(dex_ir::LocalInfoVector& orig,
|
||||
dex_ir::LocalInfoVector& output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyTries(dex_ir::TryItemVector* orig,
|
||||
dex_ir::TryItemVector* output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyHandlers(dex_ir::CatchHandlerVector* orig,
|
||||
dex_ir::CatchHandlerVector* output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
bool VerifyHandler(const dex_ir::CatchHandler* orig,
|
||||
const dex_ir::CatchHandler* output,
|
||||
uint32_t orig_offset,
|
||||
std::string* error_msg);
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_DEXLAYOUT_DEX_VERIFY_H_
|
347
android/art/dexlayout/dex_visualize.cc
Normal file
347
android/art/dexlayout/dex_visualize.cc
Normal file
|
@ -0,0 +1,347 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Implementation file of the dex layout visualization.
|
||||
*
|
||||
* This is a tool to read dex files into an internal representation,
|
||||
* reorganize the representation, and emit dex files with a better
|
||||
* file layout.
|
||||
*/
|
||||
|
||||
#include "dex_visualize.h"
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "dex_ir.h"
|
||||
#include "dexlayout.h"
|
||||
#include "jit/profile_compilation_info.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
static std::string MultidexName(const std::string& prefix,
|
||||
size_t dex_file_index,
|
||||
const std::string& suffix) {
|
||||
return prefix + ((dex_file_index > 0) ? std::to_string(dex_file_index + 1) : "") + suffix;
|
||||
}
|
||||
|
||||
class Dumper {
|
||||
public:
|
||||
// Colors are based on the type of the section in MapList.
|
||||
explicit Dumper(dex_ir::Header* header)
|
||||
: out_file_(nullptr),
|
||||
sorted_sections_(
|
||||
dex_ir::GetSortedDexFileSections(header, dex_ir::SortDirection::kSortDescending)) { }
|
||||
|
||||
bool OpenAndPrintHeader(size_t dex_index) {
|
||||
// Open the file and emit the gnuplot prologue.
|
||||
out_file_ = fopen(MultidexName("layout", dex_index, ".gnuplot").c_str(), "w");
|
||||
if (out_file_ == nullptr) {
|
||||
return false;
|
||||
}
|
||||
fprintf(out_file_, "set terminal png size 1920,1080\n");
|
||||
fprintf(out_file_, "set output \"%s\"\n", MultidexName("layout", dex_index, ".png").c_str());
|
||||
fprintf(out_file_, "set title \"%s\"\n", MultidexName("classes", dex_index, ".dex").c_str());
|
||||
fprintf(out_file_, "set xlabel \"Page offset into dex\"\n");
|
||||
fprintf(out_file_, "set ylabel \"ClassDef index\"\n");
|
||||
fprintf(out_file_, "set xtics rotate out (");
|
||||
bool printed_one = false;
|
||||
|
||||
for (const dex_ir::DexFileSection& s : sorted_sections_) {
|
||||
if (s.size > 0) {
|
||||
if (printed_one) {
|
||||
fprintf(out_file_, ", ");
|
||||
}
|
||||
fprintf(out_file_, "\"%s\" %d", s.name.c_str(), s.offset / kPageSize);
|
||||
printed_one = true;
|
||||
}
|
||||
}
|
||||
fprintf(out_file_, ")\n");
|
||||
fprintf(out_file_,
|
||||
"plot \"-\" using 1:2:3:4:5 with vector nohead linewidth 1 lc variable notitle\n");
|
||||
return true;
|
||||
}
|
||||
|
||||
int GetColor(uint32_t offset) const {
|
||||
// The dread linear search to find the right section for the reference.
|
||||
uint16_t section = 0;
|
||||
for (const dex_ir::DexFileSection& file_section : sorted_sections_) {
|
||||
if (file_section.offset < offset) {
|
||||
section = file_section.type;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// And a lookup table from type to color.
|
||||
ColorMapType::const_iterator iter = kColorMap.find(section);
|
||||
if (iter != kColorMap.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DumpAddressRange(uint32_t from, uint32_t size, int class_index) {
|
||||
const uint32_t low_page = from / kPageSize;
|
||||
const uint32_t high_page = (size > 0) ? (from + size - 1) / kPageSize : low_page;
|
||||
const uint32_t size_delta = high_page - low_page;
|
||||
fprintf(out_file_, "%d %d %d 0 %d\n", low_page, class_index, size_delta, GetColor(from));
|
||||
}
|
||||
|
||||
void DumpAddressRange(const dex_ir::Item* item, int class_index) {
|
||||
if (item != nullptr) {
|
||||
DumpAddressRange(item->GetOffset(), item->GetSize(), class_index);
|
||||
}
|
||||
}
|
||||
|
||||
void DumpStringData(const dex_ir::StringData* string_data, int class_index) {
|
||||
DumpAddressRange(string_data, class_index);
|
||||
}
|
||||
|
||||
void DumpStringId(const dex_ir::StringId* string_id, int class_index) {
|
||||
DumpAddressRange(string_id, class_index);
|
||||
if (string_id == nullptr) {
|
||||
return;
|
||||
}
|
||||
DumpStringData(string_id->DataItem(), class_index);
|
||||
}
|
||||
|
||||
void DumpTypeId(const dex_ir::TypeId* type_id, int class_index) {
|
||||
DumpAddressRange(type_id, class_index);
|
||||
DumpStringId(type_id->GetStringId(), class_index);
|
||||
}
|
||||
|
||||
void DumpFieldId(const dex_ir::FieldId* field_id, int class_index) {
|
||||
DumpAddressRange(field_id, class_index);
|
||||
if (field_id == nullptr) {
|
||||
return;
|
||||
}
|
||||
DumpTypeId(field_id->Class(), class_index);
|
||||
DumpTypeId(field_id->Type(), class_index);
|
||||
DumpStringId(field_id->Name(), class_index);
|
||||
}
|
||||
|
||||
void DumpFieldItem(const dex_ir::FieldItem* field, int class_index) {
|
||||
DumpAddressRange(field, class_index);
|
||||
if (field == nullptr) {
|
||||
return;
|
||||
}
|
||||
DumpFieldId(field->GetFieldId(), class_index);
|
||||
}
|
||||
|
||||
void DumpProtoId(const dex_ir::ProtoId* proto_id, int class_index) {
|
||||
DumpAddressRange(proto_id, class_index);
|
||||
if (proto_id == nullptr) {
|
||||
return;
|
||||
}
|
||||
DumpStringId(proto_id->Shorty(), class_index);
|
||||
const dex_ir::TypeList* type_list = proto_id->Parameters();
|
||||
if (type_list != nullptr) {
|
||||
for (const dex_ir::TypeId* t : *type_list->GetTypeList()) {
|
||||
DumpTypeId(t, class_index);
|
||||
}
|
||||
}
|
||||
DumpTypeId(proto_id->ReturnType(), class_index);
|
||||
}
|
||||
|
||||
void DumpMethodId(const dex_ir::MethodId* method_id, int class_index) {
|
||||
DumpAddressRange(method_id, class_index);
|
||||
if (method_id == nullptr) {
|
||||
return;
|
||||
}
|
||||
DumpTypeId(method_id->Class(), class_index);
|
||||
DumpProtoId(method_id->Proto(), class_index);
|
||||
DumpStringId(method_id->Name(), class_index);
|
||||
}
|
||||
|
||||
void DumpMethodItem(dex_ir::MethodItem* method,
|
||||
const DexFile* dex_file,
|
||||
int class_index,
|
||||
ProfileCompilationInfo* profile_info) {
|
||||
if (profile_info != nullptr) {
|
||||
uint32_t method_idx = method->GetMethodId()->GetIndex();
|
||||
if (!profile_info->GetMethodHotness(MethodReference(dex_file, method_idx)).IsHot()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
DumpAddressRange(method, class_index);
|
||||
if (method == nullptr) {
|
||||
return;
|
||||
}
|
||||
DumpMethodId(method->GetMethodId(), class_index);
|
||||
const dex_ir::CodeItem* code_item = method->GetCodeItem();
|
||||
if (code_item != nullptr) {
|
||||
DumpAddressRange(code_item, class_index);
|
||||
const dex_ir::CodeFixups* fixups = code_item->GetCodeFixups();
|
||||
if (fixups != nullptr) {
|
||||
std::vector<dex_ir::TypeId*>* type_ids = fixups->TypeIds();
|
||||
for (dex_ir::TypeId* type_id : *type_ids) {
|
||||
DumpTypeId(type_id, class_index);
|
||||
}
|
||||
std::vector<dex_ir::StringId*>* string_ids = fixups->StringIds();
|
||||
for (dex_ir::StringId* string_id : *string_ids) {
|
||||
DumpStringId(string_id, class_index);
|
||||
}
|
||||
std::vector<dex_ir::MethodId*>* method_ids = fixups->MethodIds();
|
||||
for (dex_ir::MethodId* method_id : *method_ids) {
|
||||
DumpMethodId(method_id, class_index);
|
||||
}
|
||||
std::vector<dex_ir::FieldId*>* field_ids = fixups->FieldIds();
|
||||
for (dex_ir::FieldId* field_id : *field_ids) {
|
||||
DumpFieldId(field_id, class_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Dumper() {
|
||||
fclose(out_file_);
|
||||
}
|
||||
|
||||
private:
|
||||
using ColorMapType = std::map<uint16_t, int>;
|
||||
const ColorMapType kColorMap = {
|
||||
{ DexFile::kDexTypeHeaderItem, 1 },
|
||||
{ DexFile::kDexTypeStringIdItem, 2 },
|
||||
{ DexFile::kDexTypeTypeIdItem, 3 },
|
||||
{ DexFile::kDexTypeProtoIdItem, 4 },
|
||||
{ DexFile::kDexTypeFieldIdItem, 5 },
|
||||
{ DexFile::kDexTypeMethodIdItem, 6 },
|
||||
{ DexFile::kDexTypeClassDefItem, 7 },
|
||||
{ DexFile::kDexTypeTypeList, 8 },
|
||||
{ DexFile::kDexTypeAnnotationSetRefList, 9 },
|
||||
{ DexFile::kDexTypeAnnotationSetItem, 10 },
|
||||
{ DexFile::kDexTypeClassDataItem, 11 },
|
||||
{ DexFile::kDexTypeCodeItem, 12 },
|
||||
{ DexFile::kDexTypeStringDataItem, 13 },
|
||||
{ DexFile::kDexTypeDebugInfoItem, 14 },
|
||||
{ DexFile::kDexTypeAnnotationItem, 15 },
|
||||
{ DexFile::kDexTypeEncodedArrayItem, 16 },
|
||||
{ DexFile::kDexTypeAnnotationsDirectoryItem, 16 }
|
||||
};
|
||||
|
||||
FILE* out_file_;
|
||||
std::vector<dex_ir::DexFileSection> sorted_sections_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(Dumper);
|
||||
};
|
||||
|
||||
/*
|
||||
* Dumps a gnuplot data file showing the parts of the dex_file that belong to each class.
|
||||
* If profiling information is present, it dumps only those classes that are marked as hot.
|
||||
*/
|
||||
void VisualizeDexLayout(dex_ir::Header* header,
|
||||
const DexFile* dex_file,
|
||||
size_t dex_file_index,
|
||||
ProfileCompilationInfo* profile_info) {
|
||||
std::unique_ptr<Dumper> dumper(new Dumper(header));
|
||||
if (!dumper->OpenAndPrintHeader(dex_file_index)) {
|
||||
fprintf(stderr, "Could not open output file.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const uint32_t class_defs_size = header->GetCollections().ClassDefsSize();
|
||||
for (uint32_t class_index = 0; class_index < class_defs_size; class_index++) {
|
||||
dex_ir::ClassDef* class_def = header->GetCollections().GetClassDef(class_index);
|
||||
dex::TypeIndex type_idx(class_def->ClassType()->GetIndex());
|
||||
if (profile_info != nullptr && !profile_info->ContainsClass(*dex_file, type_idx)) {
|
||||
continue;
|
||||
}
|
||||
dumper->DumpAddressRange(class_def, class_index);
|
||||
// Type id.
|
||||
dumper->DumpTypeId(class_def->ClassType(), class_index);
|
||||
// Superclass type id.
|
||||
dumper->DumpTypeId(class_def->Superclass(), class_index);
|
||||
// Interfaces.
|
||||
// TODO(jeffhao): get TypeList from class_def to use Item interface.
|
||||
static constexpr uint32_t kInterfaceSizeKludge = 8;
|
||||
dumper->DumpAddressRange(class_def->InterfacesOffset(), kInterfaceSizeKludge, class_index);
|
||||
// Source file info.
|
||||
dumper->DumpStringId(class_def->SourceFile(), class_index);
|
||||
// Annotations.
|
||||
dumper->DumpAddressRange(class_def->Annotations(), class_index);
|
||||
// TODO(sehr): walk the annotations and dump them.
|
||||
// Class data.
|
||||
dex_ir::ClassData* class_data = class_def->GetClassData();
|
||||
if (class_data != nullptr) {
|
||||
dumper->DumpAddressRange(class_data, class_index);
|
||||
if (class_data->StaticFields()) {
|
||||
for (auto& field_item : *class_data->StaticFields()) {
|
||||
dumper->DumpFieldItem(field_item.get(), class_index);
|
||||
}
|
||||
}
|
||||
if (class_data->InstanceFields()) {
|
||||
for (auto& field_item : *class_data->InstanceFields()) {
|
||||
dumper->DumpFieldItem(field_item.get(), class_index);
|
||||
}
|
||||
}
|
||||
if (class_data->DirectMethods()) {
|
||||
for (auto& method_item : *class_data->DirectMethods()) {
|
||||
dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
|
||||
}
|
||||
}
|
||||
if (class_data->VirtualMethods()) {
|
||||
for (auto& method_item : *class_data->VirtualMethods()) {
|
||||
dumper->DumpMethodItem(method_item.get(), dex_file, class_index, profile_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
} // for
|
||||
}
|
||||
|
||||
static uint32_t FindNextByteAfterSection(dex_ir::Header* header,
|
||||
const std::vector<dex_ir::DexFileSection>& sorted_sections,
|
||||
size_t section_index) {
|
||||
for (size_t i = section_index + 1; i < sorted_sections.size(); ++i) {
|
||||
const dex_ir::DexFileSection& section = sorted_sections.at(i);
|
||||
if (section.size != 0) {
|
||||
return section.offset;
|
||||
}
|
||||
}
|
||||
return header->FileSize();
|
||||
}
|
||||
|
||||
/*
|
||||
* Dumps the offset and size of sections within the file.
|
||||
*/
|
||||
void ShowDexSectionStatistics(dex_ir::Header* header, size_t dex_file_index) {
|
||||
// Compute the (multidex) class file name).
|
||||
fprintf(stdout, "%s (%d bytes)\n",
|
||||
MultidexName("classes", dex_file_index, ".dex").c_str(),
|
||||
header->FileSize());
|
||||
fprintf(stdout, "section offset items bytes pages pct\n");
|
||||
std::vector<dex_ir::DexFileSection> sorted_sections =
|
||||
GetSortedDexFileSections(header, dex_ir::SortDirection::kSortAscending);
|
||||
for (size_t i = 0; i < sorted_sections.size(); ++i) {
|
||||
const dex_ir::DexFileSection& file_section = sorted_sections[i];
|
||||
uint32_t bytes = 0;
|
||||
if (file_section.size > 0) {
|
||||
bytes = FindNextByteAfterSection(header, sorted_sections, i) - file_section.offset;
|
||||
}
|
||||
fprintf(stdout,
|
||||
"%-10s %8d %8d %8d %8d %%%02d\n",
|
||||
file_section.name.c_str(),
|
||||
file_section.offset,
|
||||
file_section.size,
|
||||
bytes,
|
||||
RoundUp(bytes, kPageSize) / kPageSize,
|
||||
100 * bytes / header->FileSize());
|
||||
}
|
||||
fprintf(stdout, "\n");
|
||||
}
|
||||
|
||||
} // namespace art
|
45
android/art/dexlayout/dex_visualize.h
Normal file
45
android/art/dexlayout/dex_visualize.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Header file of the dexlayout utility.
|
||||
*
|
||||
* This is a tool to read dex files into an internal representation,
|
||||
* reorganize the representation, and emit dex files with a better
|
||||
* file layout.
|
||||
*/
|
||||
|
||||
#ifndef ART_DEXLAYOUT_DEX_VISUALIZE_H_
|
||||
#define ART_DEXLAYOUT_DEX_VISUALIZE_H_
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
namespace art {
|
||||
|
||||
class DexFile;
|
||||
class ProfileCompilationInfo;
|
||||
namespace dex_ir {
|
||||
class Header;
|
||||
} // namespace dex_ir
|
||||
|
||||
void VisualizeDexLayout(dex_ir::Header* header,
|
||||
const DexFile* dex_file,
|
||||
size_t dex_file_index,
|
||||
ProfileCompilationInfo* profile_info);
|
||||
|
||||
void ShowDexSectionStatistics(dex_ir::Header* header, size_t dex_file_index);
|
||||
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_DEXLAYOUT_DEX_VISUALIZE_H_
|
687
android/art/dexlayout/dex_writer.cc
Normal file
687
android/art/dexlayout/dex_writer.cc
Normal file
|
@ -0,0 +1,687 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Header file of an in-memory representation of DEX files.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "dex_writer.h"
|
||||
#include "utf.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
size_t EncodeIntValue(int32_t value, uint8_t* buffer) {
|
||||
size_t length = 0;
|
||||
if (value >= 0) {
|
||||
while (value > 0x7f) {
|
||||
buffer[length++] = static_cast<uint8_t>(value);
|
||||
value >>= 8;
|
||||
}
|
||||
} else {
|
||||
while (value < -0x80) {
|
||||
buffer[length++] = static_cast<uint8_t>(value);
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
buffer[length++] = static_cast<uint8_t>(value);
|
||||
return length;
|
||||
}
|
||||
|
||||
size_t EncodeUIntValue(uint32_t value, uint8_t* buffer) {
|
||||
size_t length = 0;
|
||||
do {
|
||||
buffer[length++] = static_cast<uint8_t>(value);
|
||||
value >>= 8;
|
||||
} while (value != 0);
|
||||
return length;
|
||||
}
|
||||
|
||||
size_t EncodeLongValue(int64_t value, uint8_t* buffer) {
|
||||
size_t length = 0;
|
||||
if (value >= 0) {
|
||||
while (value > 0x7f) {
|
||||
buffer[length++] = static_cast<uint8_t>(value);
|
||||
value >>= 8;
|
||||
}
|
||||
} else {
|
||||
while (value < -0x80) {
|
||||
buffer[length++] = static_cast<uint8_t>(value);
|
||||
value >>= 8;
|
||||
}
|
||||
}
|
||||
buffer[length++] = static_cast<uint8_t>(value);
|
||||
return length;
|
||||
}
|
||||
|
||||
union FloatUnion {
|
||||
float f_;
|
||||
uint32_t i_;
|
||||
};
|
||||
|
||||
size_t EncodeFloatValue(float value, uint8_t* buffer) {
|
||||
FloatUnion float_union;
|
||||
float_union.f_ = value;
|
||||
uint32_t int_value = float_union.i_;
|
||||
size_t index = 3;
|
||||
do {
|
||||
buffer[index--] = int_value >> 24;
|
||||
int_value <<= 8;
|
||||
} while (int_value != 0);
|
||||
return 3 - index;
|
||||
}
|
||||
|
||||
union DoubleUnion {
|
||||
double d_;
|
||||
uint64_t l_;
|
||||
};
|
||||
|
||||
size_t EncodeDoubleValue(double value, uint8_t* buffer) {
|
||||
DoubleUnion double_union;
|
||||
double_union.d_ = value;
|
||||
uint64_t long_value = double_union.l_;
|
||||
size_t index = 7;
|
||||
do {
|
||||
buffer[index--] = long_value >> 56;
|
||||
long_value <<= 8;
|
||||
} while (long_value != 0);
|
||||
return 7 - index;
|
||||
}
|
||||
|
||||
size_t DexWriter::Write(const void* buffer, size_t length, size_t offset) {
|
||||
DCHECK_LE(offset + length, mem_map_->Size());
|
||||
memcpy(mem_map_->Begin() + offset, buffer, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
size_t DexWriter::WriteSleb128(uint32_t value, size_t offset) {
|
||||
uint8_t buffer[8];
|
||||
EncodeSignedLeb128(buffer, value);
|
||||
return Write(buffer, SignedLeb128Size(value), offset);
|
||||
}
|
||||
|
||||
size_t DexWriter::WriteUleb128(uint32_t value, size_t offset) {
|
||||
uint8_t buffer[8];
|
||||
EncodeUnsignedLeb128(buffer, value);
|
||||
return Write(buffer, UnsignedLeb128Size(value), offset);
|
||||
}
|
||||
|
||||
size_t DexWriter::WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset) {
|
||||
size_t original_offset = offset;
|
||||
size_t start = 0;
|
||||
size_t length;
|
||||
uint8_t buffer[8];
|
||||
int8_t type = encoded_value->Type();
|
||||
switch (type) {
|
||||
case DexFile::kDexAnnotationByte:
|
||||
length = EncodeIntValue(encoded_value->GetByte(), buffer);
|
||||
break;
|
||||
case DexFile::kDexAnnotationShort:
|
||||
length = EncodeIntValue(encoded_value->GetShort(), buffer);
|
||||
break;
|
||||
case DexFile::kDexAnnotationChar:
|
||||
length = EncodeUIntValue(encoded_value->GetChar(), buffer);
|
||||
break;
|
||||
case DexFile::kDexAnnotationInt:
|
||||
length = EncodeIntValue(encoded_value->GetInt(), buffer);
|
||||
break;
|
||||
case DexFile::kDexAnnotationLong:
|
||||
length = EncodeLongValue(encoded_value->GetLong(), buffer);
|
||||
break;
|
||||
case DexFile::kDexAnnotationFloat:
|
||||
length = EncodeFloatValue(encoded_value->GetFloat(), buffer);
|
||||
start = 4 - length;
|
||||
break;
|
||||
case DexFile::kDexAnnotationDouble:
|
||||
length = EncodeDoubleValue(encoded_value->GetDouble(), buffer);
|
||||
start = 8 - length;
|
||||
break;
|
||||
case DexFile::kDexAnnotationMethodType:
|
||||
length = EncodeUIntValue(encoded_value->GetProtoId()->GetIndex(), buffer);
|
||||
break;
|
||||
case DexFile::kDexAnnotationMethodHandle:
|
||||
length = EncodeUIntValue(encoded_value->GetMethodHandle()->GetIndex(), buffer);
|
||||
break;
|
||||
case DexFile::kDexAnnotationString:
|
||||
length = EncodeUIntValue(encoded_value->GetStringId()->GetIndex(), buffer);
|
||||
break;
|
||||
case DexFile::kDexAnnotationType:
|
||||
length = EncodeUIntValue(encoded_value->GetTypeId()->GetIndex(), buffer);
|
||||
break;
|
||||
case DexFile::kDexAnnotationField:
|
||||
case DexFile::kDexAnnotationEnum:
|
||||
length = EncodeUIntValue(encoded_value->GetFieldId()->GetIndex(), buffer);
|
||||
break;
|
||||
case DexFile::kDexAnnotationMethod:
|
||||
length = EncodeUIntValue(encoded_value->GetMethodId()->GetIndex(), buffer);
|
||||
break;
|
||||
case DexFile::kDexAnnotationArray:
|
||||
offset += WriteEncodedValueHeader(type, 0, offset);
|
||||
offset += WriteEncodedArray(encoded_value->GetEncodedArray()->GetEncodedValues(), offset);
|
||||
return offset - original_offset;
|
||||
case DexFile::kDexAnnotationAnnotation:
|
||||
offset += WriteEncodedValueHeader(type, 0, offset);
|
||||
offset += WriteEncodedAnnotation(encoded_value->GetEncodedAnnotation(), offset);
|
||||
return offset - original_offset;
|
||||
case DexFile::kDexAnnotationNull:
|
||||
return WriteEncodedValueHeader(type, 0, offset);
|
||||
case DexFile::kDexAnnotationBoolean:
|
||||
return WriteEncodedValueHeader(type, encoded_value->GetBoolean() ? 1 : 0, offset);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
offset += WriteEncodedValueHeader(type, length - 1, offset);
|
||||
offset += Write(buffer + start, length, offset);
|
||||
return offset - original_offset;
|
||||
}
|
||||
|
||||
size_t DexWriter::WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset) {
|
||||
uint8_t buffer[1] = { static_cast<uint8_t>((value_arg << 5) | value_type) };
|
||||
return Write(buffer, sizeof(uint8_t), offset);
|
||||
}
|
||||
|
||||
size_t DexWriter::WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset) {
|
||||
size_t original_offset = offset;
|
||||
offset += WriteUleb128(values->size(), offset);
|
||||
for (std::unique_ptr<dex_ir::EncodedValue>& value : *values) {
|
||||
offset += WriteEncodedValue(value.get(), offset);
|
||||
}
|
||||
return offset - original_offset;
|
||||
}
|
||||
|
||||
size_t DexWriter::WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset) {
|
||||
size_t original_offset = offset;
|
||||
offset += WriteUleb128(annotation->GetType()->GetIndex(), offset);
|
||||
offset += WriteUleb128(annotation->GetAnnotationElements()->size(), offset);
|
||||
for (std::unique_ptr<dex_ir::AnnotationElement>& annotation_element :
|
||||
*annotation->GetAnnotationElements()) {
|
||||
offset += WriteUleb128(annotation_element->GetName()->GetIndex(), offset);
|
||||
offset += WriteEncodedValue(annotation_element->GetValue(), offset);
|
||||
}
|
||||
return offset - original_offset;
|
||||
}
|
||||
|
||||
size_t DexWriter::WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset) {
|
||||
size_t original_offset = offset;
|
||||
uint32_t prev_index = 0;
|
||||
for (std::unique_ptr<dex_ir::FieldItem>& field : *fields) {
|
||||
uint32_t index = field->GetFieldId()->GetIndex();
|
||||
offset += WriteUleb128(index - prev_index, offset);
|
||||
offset += WriteUleb128(field->GetAccessFlags(), offset);
|
||||
prev_index = index;
|
||||
}
|
||||
return offset - original_offset;
|
||||
}
|
||||
|
||||
size_t DexWriter::WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset) {
|
||||
size_t original_offset = offset;
|
||||
uint32_t prev_index = 0;
|
||||
for (std::unique_ptr<dex_ir::MethodItem>& method : *methods) {
|
||||
uint32_t index = method->GetMethodId()->GetIndex();
|
||||
uint32_t code_off = method->GetCodeItem() == nullptr ? 0 : method->GetCodeItem()->GetOffset();
|
||||
offset += WriteUleb128(index - prev_index, offset);
|
||||
offset += WriteUleb128(method->GetAccessFlags(), offset);
|
||||
offset += WriteUleb128(code_off, offset);
|
||||
prev_index = index;
|
||||
}
|
||||
return offset - original_offset;
|
||||
}
|
||||
|
||||
void DexWriter::WriteStrings() {
|
||||
uint32_t string_data_off[1];
|
||||
for (std::unique_ptr<dex_ir::StringId>& string_id : header_->GetCollections().StringIds()) {
|
||||
string_data_off[0] = string_id->DataItem()->GetOffset();
|
||||
Write(string_data_off, string_id->GetSize(), string_id->GetOffset());
|
||||
}
|
||||
|
||||
for (auto& string_data_pair : header_->GetCollections().StringDatas()) {
|
||||
std::unique_ptr<dex_ir::StringData>& string_data = string_data_pair.second;
|
||||
uint32_t offset = string_data->GetOffset();
|
||||
offset += WriteUleb128(CountModifiedUtf8Chars(string_data->Data()), offset);
|
||||
Write(string_data->Data(), strlen(string_data->Data()), offset);
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteTypes() {
|
||||
uint32_t descriptor_idx[1];
|
||||
for (std::unique_ptr<dex_ir::TypeId>& type_id : header_->GetCollections().TypeIds()) {
|
||||
descriptor_idx[0] = type_id->GetStringId()->GetIndex();
|
||||
Write(descriptor_idx, type_id->GetSize(), type_id->GetOffset());
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteTypeLists() {
|
||||
uint32_t size[1];
|
||||
uint16_t list[1];
|
||||
for (auto& type_list_pair : header_->GetCollections().TypeLists()) {
|
||||
std::unique_ptr<dex_ir::TypeList>& type_list = type_list_pair.second;
|
||||
size[0] = type_list->GetTypeList()->size();
|
||||
uint32_t offset = type_list->GetOffset();
|
||||
offset += Write(size, sizeof(uint32_t), offset);
|
||||
for (const dex_ir::TypeId* type_id : *type_list->GetTypeList()) {
|
||||
list[0] = type_id->GetIndex();
|
||||
offset += Write(list, sizeof(uint16_t), offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteProtos() {
|
||||
uint32_t buffer[3];
|
||||
for (std::unique_ptr<dex_ir::ProtoId>& proto_id : header_->GetCollections().ProtoIds()) {
|
||||
buffer[0] = proto_id->Shorty()->GetIndex();
|
||||
buffer[1] = proto_id->ReturnType()->GetIndex();
|
||||
buffer[2] = proto_id->Parameters() == nullptr ? 0 : proto_id->Parameters()->GetOffset();
|
||||
Write(buffer, proto_id->GetSize(), proto_id->GetOffset());
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteFields() {
|
||||
uint16_t buffer[4];
|
||||
for (std::unique_ptr<dex_ir::FieldId>& field_id : header_->GetCollections().FieldIds()) {
|
||||
buffer[0] = field_id->Class()->GetIndex();
|
||||
buffer[1] = field_id->Type()->GetIndex();
|
||||
buffer[2] = field_id->Name()->GetIndex();
|
||||
buffer[3] = field_id->Name()->GetIndex() >> 16;
|
||||
Write(buffer, field_id->GetSize(), field_id->GetOffset());
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteMethods() {
|
||||
uint16_t buffer[4];
|
||||
for (std::unique_ptr<dex_ir::MethodId>& method_id : header_->GetCollections().MethodIds()) {
|
||||
buffer[0] = method_id->Class()->GetIndex();
|
||||
buffer[1] = method_id->Proto()->GetIndex();
|
||||
buffer[2] = method_id->Name()->GetIndex();
|
||||
buffer[3] = method_id->Name()->GetIndex() >> 16;
|
||||
Write(buffer, method_id->GetSize(), method_id->GetOffset());
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteEncodedArrays() {
|
||||
for (auto& encoded_array_pair : header_->GetCollections().EncodedArrayItems()) {
|
||||
std::unique_ptr<dex_ir::EncodedArrayItem>& encoded_array = encoded_array_pair.second;
|
||||
WriteEncodedArray(encoded_array->GetEncodedValues(), encoded_array->GetOffset());
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteAnnotations() {
|
||||
uint8_t visibility[1];
|
||||
for (auto& annotation_pair : header_->GetCollections().AnnotationItems()) {
|
||||
std::unique_ptr<dex_ir::AnnotationItem>& annotation = annotation_pair.second;
|
||||
visibility[0] = annotation->GetVisibility();
|
||||
size_t offset = annotation->GetOffset();
|
||||
offset += Write(visibility, sizeof(uint8_t), offset);
|
||||
WriteEncodedAnnotation(annotation->GetAnnotation(), offset);
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteAnnotationSets() {
|
||||
uint32_t size[1];
|
||||
uint32_t annotation_off[1];
|
||||
for (auto& annotation_set_pair : header_->GetCollections().AnnotationSetItems()) {
|
||||
std::unique_ptr<dex_ir::AnnotationSetItem>& annotation_set = annotation_set_pair.second;
|
||||
size[0] = annotation_set->GetItems()->size();
|
||||
size_t offset = annotation_set->GetOffset();
|
||||
offset += Write(size, sizeof(uint32_t), offset);
|
||||
for (dex_ir::AnnotationItem* annotation : *annotation_set->GetItems()) {
|
||||
annotation_off[0] = annotation->GetOffset();
|
||||
offset += Write(annotation_off, sizeof(uint32_t), offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteAnnotationSetRefs() {
|
||||
uint32_t size[1];
|
||||
uint32_t annotations_off[1];
|
||||
for (auto& anno_set_ref_pair : header_->GetCollections().AnnotationSetRefLists()) {
|
||||
std::unique_ptr<dex_ir::AnnotationSetRefList>& annotation_set_ref = anno_set_ref_pair.second;
|
||||
size[0] = annotation_set_ref->GetItems()->size();
|
||||
size_t offset = annotation_set_ref->GetOffset();
|
||||
offset += Write(size, sizeof(uint32_t), offset);
|
||||
for (dex_ir::AnnotationSetItem* annotation_set : *annotation_set_ref->GetItems()) {
|
||||
annotations_off[0] = annotation_set == nullptr ? 0 : annotation_set->GetOffset();
|
||||
offset += Write(annotations_off, sizeof(uint32_t), offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteAnnotationsDirectories() {
|
||||
uint32_t directory_buffer[4];
|
||||
uint32_t annotation_buffer[2];
|
||||
for (auto& annotations_directory_pair : header_->GetCollections().AnnotationsDirectoryItems()) {
|
||||
std::unique_ptr<dex_ir::AnnotationsDirectoryItem>& annotations_directory =
|
||||
annotations_directory_pair.second;
|
||||
directory_buffer[0] = annotations_directory->GetClassAnnotation() == nullptr ? 0 :
|
||||
annotations_directory->GetClassAnnotation()->GetOffset();
|
||||
directory_buffer[1] = annotations_directory->GetFieldAnnotations() == nullptr ? 0 :
|
||||
annotations_directory->GetFieldAnnotations()->size();
|
||||
directory_buffer[2] = annotations_directory->GetMethodAnnotations() == nullptr ? 0 :
|
||||
annotations_directory->GetMethodAnnotations()->size();
|
||||
directory_buffer[3] = annotations_directory->GetParameterAnnotations() == nullptr ? 0 :
|
||||
annotations_directory->GetParameterAnnotations()->size();
|
||||
uint32_t offset = annotations_directory->GetOffset();
|
||||
offset += Write(directory_buffer, 4 * sizeof(uint32_t), offset);
|
||||
if (annotations_directory->GetFieldAnnotations() != nullptr) {
|
||||
for (std::unique_ptr<dex_ir::FieldAnnotation>& field :
|
||||
*annotations_directory->GetFieldAnnotations()) {
|
||||
annotation_buffer[0] = field->GetFieldId()->GetIndex();
|
||||
annotation_buffer[1] = field->GetAnnotationSetItem()->GetOffset();
|
||||
offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset);
|
||||
}
|
||||
}
|
||||
if (annotations_directory->GetMethodAnnotations() != nullptr) {
|
||||
for (std::unique_ptr<dex_ir::MethodAnnotation>& method :
|
||||
*annotations_directory->GetMethodAnnotations()) {
|
||||
annotation_buffer[0] = method->GetMethodId()->GetIndex();
|
||||
annotation_buffer[1] = method->GetAnnotationSetItem()->GetOffset();
|
||||
offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset);
|
||||
}
|
||||
}
|
||||
if (annotations_directory->GetParameterAnnotations() != nullptr) {
|
||||
for (std::unique_ptr<dex_ir::ParameterAnnotation>& parameter :
|
||||
*annotations_directory->GetParameterAnnotations()) {
|
||||
annotation_buffer[0] = parameter->GetMethodId()->GetIndex();
|
||||
annotation_buffer[1] = parameter->GetAnnotations()->GetOffset();
|
||||
offset += Write(annotation_buffer, 2 * sizeof(uint32_t), offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteDebugInfoItems() {
|
||||
for (auto& debug_info_pair : header_->GetCollections().DebugInfoItems()) {
|
||||
std::unique_ptr<dex_ir::DebugInfoItem>& debug_info = debug_info_pair.second;
|
||||
Write(debug_info->GetDebugInfo(), debug_info->GetDebugInfoSize(), debug_info->GetOffset());
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteCodeItems() {
|
||||
uint16_t uint16_buffer[4];
|
||||
uint32_t uint32_buffer[2];
|
||||
for (auto& code_item_pair : header_->GetCollections().CodeItems()) {
|
||||
std::unique_ptr<dex_ir::CodeItem>& code_item = code_item_pair.second;
|
||||
uint16_buffer[0] = code_item->RegistersSize();
|
||||
uint16_buffer[1] = code_item->InsSize();
|
||||
uint16_buffer[2] = code_item->OutsSize();
|
||||
uint16_buffer[3] = code_item->TriesSize();
|
||||
uint32_buffer[0] = code_item->DebugInfo() == nullptr ? 0 : code_item->DebugInfo()->GetOffset();
|
||||
uint32_buffer[1] = code_item->InsnsSize();
|
||||
size_t offset = code_item->GetOffset();
|
||||
offset += Write(uint16_buffer, 4 * sizeof(uint16_t), offset);
|
||||
offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset);
|
||||
offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset);
|
||||
if (code_item->TriesSize() != 0) {
|
||||
if (code_item->InsnsSize() % 2 != 0) {
|
||||
uint16_t padding[1] = { 0 };
|
||||
offset += Write(padding, sizeof(uint16_t), offset);
|
||||
}
|
||||
uint32_t start_addr[1];
|
||||
uint16_t insn_count_and_handler_off[2];
|
||||
for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) {
|
||||
start_addr[0] = try_item->StartAddr();
|
||||
insn_count_and_handler_off[0] = try_item->InsnCount();
|
||||
insn_count_and_handler_off[1] = try_item->GetHandlers()->GetListOffset();
|
||||
offset += Write(start_addr, sizeof(uint32_t), offset);
|
||||
offset += Write(insn_count_and_handler_off, 2 * sizeof(uint16_t), offset);
|
||||
}
|
||||
// Leave offset pointing to the end of the try items.
|
||||
WriteUleb128(code_item->Handlers()->size(), offset);
|
||||
for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) {
|
||||
size_t list_offset = offset + handlers->GetListOffset();
|
||||
uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 :
|
||||
handlers->GetHandlers()->size();
|
||||
list_offset += WriteSleb128(size, list_offset);
|
||||
for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) {
|
||||
if (handler->GetTypeId() != nullptr) {
|
||||
list_offset += WriteUleb128(handler->GetTypeId()->GetIndex(), list_offset);
|
||||
}
|
||||
list_offset += WriteUleb128(handler->GetAddress(), list_offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteClasses() {
|
||||
uint32_t class_def_buffer[8];
|
||||
for (std::unique_ptr<dex_ir::ClassDef>& class_def : header_->GetCollections().ClassDefs()) {
|
||||
class_def_buffer[0] = class_def->ClassType()->GetIndex();
|
||||
class_def_buffer[1] = class_def->GetAccessFlags();
|
||||
class_def_buffer[2] = class_def->Superclass() == nullptr ? DexFile::kDexNoIndex :
|
||||
class_def->Superclass()->GetIndex();
|
||||
class_def_buffer[3] = class_def->InterfacesOffset();
|
||||
class_def_buffer[4] = class_def->SourceFile() == nullptr ? DexFile::kDexNoIndex :
|
||||
class_def->SourceFile()->GetIndex();
|
||||
class_def_buffer[5] = class_def->Annotations() == nullptr ? 0 :
|
||||
class_def->Annotations()->GetOffset();
|
||||
class_def_buffer[6] = class_def->GetClassData() == nullptr ? 0 :
|
||||
class_def->GetClassData()->GetOffset();
|
||||
class_def_buffer[7] = class_def->StaticValues() == nullptr ? 0 :
|
||||
class_def->StaticValues()->GetOffset();
|
||||
size_t offset = class_def->GetOffset();
|
||||
Write(class_def_buffer, class_def->GetSize(), offset);
|
||||
}
|
||||
|
||||
for (auto& class_data_pair : header_->GetCollections().ClassDatas()) {
|
||||
std::unique_ptr<dex_ir::ClassData>& class_data = class_data_pair.second;
|
||||
size_t offset = class_data->GetOffset();
|
||||
offset += WriteUleb128(class_data->StaticFields()->size(), offset);
|
||||
offset += WriteUleb128(class_data->InstanceFields()->size(), offset);
|
||||
offset += WriteUleb128(class_data->DirectMethods()->size(), offset);
|
||||
offset += WriteUleb128(class_data->VirtualMethods()->size(), offset);
|
||||
offset += WriteEncodedFields(class_data->StaticFields(), offset);
|
||||
offset += WriteEncodedFields(class_data->InstanceFields(), offset);
|
||||
offset += WriteEncodedMethods(class_data->DirectMethods(), offset);
|
||||
offset += WriteEncodedMethods(class_data->VirtualMethods(), offset);
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteCallSites() {
|
||||
uint32_t call_site_off[1];
|
||||
for (std::unique_ptr<dex_ir::CallSiteId>& call_site_id :
|
||||
header_->GetCollections().CallSiteIds()) {
|
||||
call_site_off[0] = call_site_id->CallSiteItem()->GetOffset();
|
||||
Write(call_site_off, call_site_id->GetSize(), call_site_id->GetOffset());
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteMethodHandles() {
|
||||
uint16_t method_handle_buff[4];
|
||||
for (std::unique_ptr<dex_ir::MethodHandleItem>& method_handle :
|
||||
header_->GetCollections().MethodHandleItems()) {
|
||||
method_handle_buff[0] = static_cast<uint16_t>(method_handle->GetMethodHandleType());
|
||||
method_handle_buff[1] = 0; // unused.
|
||||
method_handle_buff[2] = method_handle->GetFieldOrMethodId()->GetIndex();
|
||||
method_handle_buff[3] = 0; // unused.
|
||||
Write(method_handle_buff, method_handle->GetSize(), method_handle->GetOffset());
|
||||
}
|
||||
}
|
||||
|
||||
struct MapItemContainer {
|
||||
MapItemContainer(uint32_t type, uint32_t size, uint32_t offset)
|
||||
: type_(type), size_(size), offset_(offset) { }
|
||||
|
||||
bool operator<(const MapItemContainer& other) const {
|
||||
return offset_ > other.offset_;
|
||||
}
|
||||
|
||||
uint32_t type_;
|
||||
uint32_t size_;
|
||||
uint32_t offset_;
|
||||
};
|
||||
|
||||
void DexWriter::WriteMapItem() {
|
||||
dex_ir::Collections& collection = header_->GetCollections();
|
||||
std::priority_queue<MapItemContainer> queue;
|
||||
|
||||
// Header and index section.
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeHeaderItem, 1, 0));
|
||||
if (collection.StringIdsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeStringIdItem, collection.StringIdsSize(),
|
||||
collection.StringIdsOffset()));
|
||||
}
|
||||
if (collection.TypeIdsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeTypeIdItem, collection.TypeIdsSize(),
|
||||
collection.TypeIdsOffset()));
|
||||
}
|
||||
if (collection.ProtoIdsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeProtoIdItem, collection.ProtoIdsSize(),
|
||||
collection.ProtoIdsOffset()));
|
||||
}
|
||||
if (collection.FieldIdsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeFieldIdItem, collection.FieldIdsSize(),
|
||||
collection.FieldIdsOffset()));
|
||||
}
|
||||
if (collection.MethodIdsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeMethodIdItem, collection.MethodIdsSize(),
|
||||
collection.MethodIdsOffset()));
|
||||
}
|
||||
if (collection.ClassDefsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeClassDefItem, collection.ClassDefsSize(),
|
||||
collection.ClassDefsOffset()));
|
||||
}
|
||||
if (collection.CallSiteIdsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeCallSiteIdItem, collection.CallSiteIdsSize(),
|
||||
collection.CallSiteIdsOffset()));
|
||||
}
|
||||
if (collection.MethodHandleItemsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeMethodHandleItem,
|
||||
collection.MethodHandleItemsSize(), collection.MethodHandleItemsOffset()));
|
||||
}
|
||||
|
||||
// Data section.
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeMapList, 1, collection.MapListOffset()));
|
||||
if (collection.TypeListsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeTypeList, collection.TypeListsSize(),
|
||||
collection.TypeListsOffset()));
|
||||
}
|
||||
if (collection.AnnotationSetRefListsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeAnnotationSetRefList,
|
||||
collection.AnnotationSetRefListsSize(), collection.AnnotationSetRefListsOffset()));
|
||||
}
|
||||
if (collection.AnnotationSetItemsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeAnnotationSetItem,
|
||||
collection.AnnotationSetItemsSize(), collection.AnnotationSetItemsOffset()));
|
||||
}
|
||||
if (collection.ClassDatasSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeClassDataItem, collection.ClassDatasSize(),
|
||||
collection.ClassDatasOffset()));
|
||||
}
|
||||
if (collection.CodeItemsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeCodeItem, collection.CodeItemsSize(),
|
||||
collection.CodeItemsOffset()));
|
||||
}
|
||||
if (collection.StringDatasSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeStringDataItem, collection.StringDatasSize(),
|
||||
collection.StringDatasOffset()));
|
||||
}
|
||||
if (collection.DebugInfoItemsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeDebugInfoItem, collection.DebugInfoItemsSize(),
|
||||
collection.DebugInfoItemsOffset()));
|
||||
}
|
||||
if (collection.AnnotationItemsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeAnnotationItem, collection.AnnotationItemsSize(),
|
||||
collection.AnnotationItemsOffset()));
|
||||
}
|
||||
if (collection.EncodedArrayItemsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeEncodedArrayItem,
|
||||
collection.EncodedArrayItemsSize(), collection.EncodedArrayItemsOffset()));
|
||||
}
|
||||
if (collection.AnnotationsDirectoryItemsSize() != 0) {
|
||||
queue.push(MapItemContainer(DexFile::kDexTypeAnnotationsDirectoryItem,
|
||||
collection.AnnotationsDirectoryItemsSize(), collection.AnnotationsDirectoryItemsOffset()));
|
||||
}
|
||||
|
||||
uint32_t offset = collection.MapListOffset();
|
||||
uint16_t uint16_buffer[2];
|
||||
uint32_t uint32_buffer[2];
|
||||
uint16_buffer[1] = 0;
|
||||
uint32_buffer[0] = queue.size();
|
||||
offset += Write(uint32_buffer, sizeof(uint32_t), offset);
|
||||
while (!queue.empty()) {
|
||||
const MapItemContainer& map_item = queue.top();
|
||||
uint16_buffer[0] = map_item.type_;
|
||||
uint32_buffer[0] = map_item.size_;
|
||||
uint32_buffer[1] = map_item.offset_;
|
||||
offset += Write(uint16_buffer, 2 * sizeof(uint16_t), offset);
|
||||
offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset);
|
||||
queue.pop();
|
||||
}
|
||||
}
|
||||
|
||||
void DexWriter::WriteHeader() {
|
||||
uint32_t buffer[20];
|
||||
dex_ir::Collections& collections = header_->GetCollections();
|
||||
size_t offset = 0;
|
||||
offset += Write(header_->Magic(), 8 * sizeof(uint8_t), offset);
|
||||
buffer[0] = header_->Checksum();
|
||||
offset += Write(buffer, sizeof(uint32_t), offset);
|
||||
offset += Write(header_->Signature(), 20 * sizeof(uint8_t), offset);
|
||||
uint32_t file_size = header_->FileSize();
|
||||
buffer[0] = file_size;
|
||||
buffer[1] = header_->GetSize();
|
||||
buffer[2] = header_->EndianTag();
|
||||
buffer[3] = header_->LinkSize();
|
||||
buffer[4] = header_->LinkOffset();
|
||||
buffer[5] = collections.MapListOffset();
|
||||
buffer[6] = collections.StringIdsSize();
|
||||
buffer[7] = collections.StringIdsOffset();
|
||||
buffer[8] = collections.TypeIdsSize();
|
||||
buffer[9] = collections.TypeIdsOffset();
|
||||
buffer[10] = collections.ProtoIdsSize();
|
||||
buffer[11] = collections.ProtoIdsOffset();
|
||||
buffer[12] = collections.FieldIdsSize();
|
||||
buffer[13] = collections.FieldIdsOffset();
|
||||
buffer[14] = collections.MethodIdsSize();
|
||||
buffer[15] = collections.MethodIdsOffset();
|
||||
uint32_t class_defs_size = collections.ClassDefsSize();
|
||||
uint32_t class_defs_off = collections.ClassDefsOffset();
|
||||
buffer[16] = class_defs_size;
|
||||
buffer[17] = class_defs_off;
|
||||
buffer[18] = header_->DataSize();
|
||||
buffer[19] = header_->DataOffset();
|
||||
Write(buffer, 20 * sizeof(uint32_t), offset);
|
||||
}
|
||||
|
||||
void DexWriter::WriteMemMap() {
|
||||
WriteStrings();
|
||||
WriteTypes();
|
||||
WriteTypeLists();
|
||||
WriteProtos();
|
||||
WriteFields();
|
||||
WriteMethods();
|
||||
WriteEncodedArrays();
|
||||
WriteAnnotations();
|
||||
WriteAnnotationSets();
|
||||
WriteAnnotationSetRefs();
|
||||
WriteAnnotationsDirectories();
|
||||
WriteDebugInfoItems();
|
||||
WriteCodeItems();
|
||||
WriteClasses();
|
||||
WriteCallSites();
|
||||
WriteMethodHandles();
|
||||
WriteMapItem();
|
||||
WriteHeader();
|
||||
}
|
||||
|
||||
void DexWriter::Output(dex_ir::Header* header, MemMap* mem_map) {
|
||||
DexWriter dex_writer(header, mem_map);
|
||||
dex_writer.WriteMemMap();
|
||||
}
|
||||
|
||||
} // namespace art
|
75
android/art/dexlayout/dex_writer.h
Normal file
75
android/art/dexlayout/dex_writer.h
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Header file of an in-memory representation of DEX files.
|
||||
*/
|
||||
|
||||
#ifndef ART_DEXLAYOUT_DEX_WRITER_H_
|
||||
#define ART_DEXLAYOUT_DEX_WRITER_H_
|
||||
|
||||
#include "base/unix_file/fd_file.h"
|
||||
#include "dex_ir.h"
|
||||
#include "mem_map.h"
|
||||
#include "os.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
class DexWriter {
|
||||
public:
|
||||
DexWriter(dex_ir::Header* header, MemMap* mem_map) : header_(header), mem_map_(mem_map) { }
|
||||
|
||||
static void Output(dex_ir::Header* header, MemMap* mem_map);
|
||||
|
||||
private:
|
||||
void WriteMemMap();
|
||||
|
||||
size_t Write(const void* buffer, size_t length, size_t offset);
|
||||
size_t WriteSleb128(uint32_t value, size_t offset);
|
||||
size_t WriteUleb128(uint32_t value, size_t offset);
|
||||
size_t WriteEncodedValue(dex_ir::EncodedValue* encoded_value, size_t offset);
|
||||
size_t WriteEncodedValueHeader(int8_t value_type, size_t value_arg, size_t offset);
|
||||
size_t WriteEncodedArray(dex_ir::EncodedValueVector* values, size_t offset);
|
||||
size_t WriteEncodedAnnotation(dex_ir::EncodedAnnotation* annotation, size_t offset);
|
||||
size_t WriteEncodedFields(dex_ir::FieldItemVector* fields, size_t offset);
|
||||
size_t WriteEncodedMethods(dex_ir::MethodItemVector* methods, size_t offset);
|
||||
|
||||
void WriteStrings();
|
||||
void WriteTypes();
|
||||
void WriteTypeLists();
|
||||
void WriteProtos();
|
||||
void WriteFields();
|
||||
void WriteMethods();
|
||||
void WriteEncodedArrays();
|
||||
void WriteAnnotations();
|
||||
void WriteAnnotationSets();
|
||||
void WriteAnnotationSetRefs();
|
||||
void WriteAnnotationsDirectories();
|
||||
void WriteDebugInfoItems();
|
||||
void WriteCodeItems();
|
||||
void WriteClasses();
|
||||
void WriteCallSites();
|
||||
void WriteMethodHandles();
|
||||
void WriteMapItem();
|
||||
void WriteHeader();
|
||||
|
||||
dex_ir::Header* const header_;
|
||||
MemMap* const mem_map_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DexWriter);
|
||||
};
|
||||
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_DEXLAYOUT_DEX_WRITER_H_
|
539
android/art/dexlayout/dexdiag.cc
Normal file
539
android/art/dexlayout/dexdiag.cc
Normal file
|
@ -0,0 +1,539 @@
|
|||
/*
|
||||
* 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 <errno.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "android-base/stringprintf.h"
|
||||
|
||||
#include "base/stringpiece.h"
|
||||
|
||||
#include "dex_file.h"
|
||||
#include "dex_ir.h"
|
||||
#include "dex_ir_builder.h"
|
||||
#ifdef ART_TARGET_ANDROID
|
||||
#include "pagemap/pagemap.h"
|
||||
#endif
|
||||
#include "runtime.h"
|
||||
#include "vdex_file.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
using android::base::StringPrintf;
|
||||
|
||||
static bool g_verbose = false;
|
||||
|
||||
// The width needed to print a file page offset (32-bit).
|
||||
static constexpr int kPageCountWidth =
|
||||
static_cast<int>(std::numeric_limits<uint32_t>::digits10);
|
||||
// Display the sections.
|
||||
static constexpr char kSectionHeader[] = "Section name";
|
||||
|
||||
struct DexSectionInfo {
|
||||
public:
|
||||
std::string name;
|
||||
char letter;
|
||||
};
|
||||
|
||||
static const std::map<uint16_t, DexSectionInfo> kDexSectionInfoMap = {
|
||||
{ DexFile::kDexTypeHeaderItem, { "Header", 'H' } },
|
||||
{ DexFile::kDexTypeStringIdItem, { "StringId", 'S' } },
|
||||
{ DexFile::kDexTypeTypeIdItem, { "TypeId", 'T' } },
|
||||
{ DexFile::kDexTypeProtoIdItem, { "ProtoId", 'P' } },
|
||||
{ DexFile::kDexTypeFieldIdItem, { "FieldId", 'F' } },
|
||||
{ DexFile::kDexTypeMethodIdItem, { "MethodId", 'M' } },
|
||||
{ DexFile::kDexTypeClassDefItem, { "ClassDef", 'C' } },
|
||||
{ DexFile::kDexTypeCallSiteIdItem, { "CallSiteId", 'z' } },
|
||||
{ DexFile::kDexTypeMethodHandleItem, { "MethodHandle", 'Z' } },
|
||||
{ DexFile::kDexTypeMapList, { "TypeMap", 'L' } },
|
||||
{ DexFile::kDexTypeTypeList, { "TypeList", 't' } },
|
||||
{ DexFile::kDexTypeAnnotationSetRefList, { "AnnotationSetReferenceItem", '1' } },
|
||||
{ DexFile::kDexTypeAnnotationSetItem, { "AnnotationSetItem", '2' } },
|
||||
{ DexFile::kDexTypeClassDataItem, { "ClassData", 'c' } },
|
||||
{ DexFile::kDexTypeCodeItem, { "CodeItem", 'X' } },
|
||||
{ DexFile::kDexTypeStringDataItem, { "StringData", 's' } },
|
||||
{ DexFile::kDexTypeDebugInfoItem, { "DebugInfo", 'D' } },
|
||||
{ DexFile::kDexTypeAnnotationItem, { "AnnotationItem", '3' } },
|
||||
{ DexFile::kDexTypeEncodedArrayItem, { "EncodedArrayItem", 'E' } },
|
||||
{ DexFile::kDexTypeAnnotationsDirectoryItem, { "AnnotationsDirectoryItem", '4' } }
|
||||
};
|
||||
|
||||
class PageCount {
|
||||
public:
|
||||
PageCount() {
|
||||
for (auto it = kDexSectionInfoMap.begin(); it != kDexSectionInfoMap.end(); ++it) {
|
||||
map_[it->first] = 0;
|
||||
}
|
||||
}
|
||||
void Increment(uint16_t type) {
|
||||
map_[type]++;
|
||||
}
|
||||
size_t Get(uint16_t type) const {
|
||||
return map_.at(type);
|
||||
}
|
||||
private:
|
||||
std::map<uint16_t, size_t> map_;
|
||||
DISALLOW_COPY_AND_ASSIGN(PageCount);
|
||||
};
|
||||
|
||||
class Printer {
|
||||
public:
|
||||
Printer() : section_header_width_(ComputeHeaderWidth()) {
|
||||
}
|
||||
|
||||
void PrintHeader() const {
|
||||
std::cout << StringPrintf("%-*s %*s %*s %% of %% of",
|
||||
section_header_width_,
|
||||
kSectionHeader,
|
||||
kPageCountWidth,
|
||||
"resident",
|
||||
kPageCountWidth,
|
||||
"total"
|
||||
)
|
||||
<< std::endl;
|
||||
std::cout << StringPrintf("%-*s %*s %*s sect. total",
|
||||
section_header_width_,
|
||||
"",
|
||||
kPageCountWidth,
|
||||
"pages",
|
||||
kPageCountWidth,
|
||||
"pages")
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void PrintOne(const char* name,
|
||||
size_t resident,
|
||||
size_t mapped,
|
||||
double percent_of_section,
|
||||
double percent_of_total) const {
|
||||
// 6.2 is sufficient to print 0-100% with two decimal places of accuracy.
|
||||
std::cout << StringPrintf("%-*s %*zd %*zd %6.2f %6.2f",
|
||||
section_header_width_,
|
||||
name,
|
||||
kPageCountWidth,
|
||||
resident,
|
||||
kPageCountWidth,
|
||||
mapped,
|
||||
percent_of_section,
|
||||
percent_of_total)
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
void PrintSkipLine() const { std::cout << std::endl; }
|
||||
|
||||
// Computes the width of the section header column in the table (for fixed formatting).
|
||||
static int ComputeHeaderWidth() {
|
||||
int header_width = 0;
|
||||
for (const auto& pair : kDexSectionInfoMap) {
|
||||
const DexSectionInfo& section_info = pair.second;
|
||||
header_width = std::max(header_width, static_cast<int>(section_info.name.length()));
|
||||
}
|
||||
return header_width;
|
||||
}
|
||||
|
||||
private:
|
||||
const int section_header_width_;
|
||||
};
|
||||
|
||||
static void PrintLetterKey() {
|
||||
std::cout << "L pagetype" << std::endl;
|
||||
for (const auto& pair : kDexSectionInfoMap) {
|
||||
const DexSectionInfo& section_info = pair.second;
|
||||
std::cout << section_info.letter << " " << section_info.name.c_str() << std::endl;
|
||||
}
|
||||
std::cout << "* (Executable page resident)" << std::endl;
|
||||
std::cout << ". (Mapped page not resident)" << std::endl;
|
||||
}
|
||||
|
||||
#ifdef ART_TARGET_ANDROID
|
||||
static char PageTypeChar(uint16_t type) {
|
||||
if (kDexSectionInfoMap.find(type) == kDexSectionInfoMap.end()) {
|
||||
return '-';
|
||||
}
|
||||
return kDexSectionInfoMap.find(type)->second.letter;
|
||||
}
|
||||
|
||||
static uint16_t FindSectionTypeForPage(size_t page,
|
||||
const std::vector<dex_ir::DexFileSection>& sections) {
|
||||
for (const auto& section : sections) {
|
||||
size_t first_page_of_section = section.offset / kPageSize;
|
||||
// Only consider non-empty sections.
|
||||
if (section.size == 0) {
|
||||
continue;
|
||||
}
|
||||
// Attribute the page to the highest-offset section that starts before the page.
|
||||
if (first_page_of_section <= page) {
|
||||
return section.type;
|
||||
}
|
||||
}
|
||||
// If there's no non-zero sized section with an offset below offset we're looking for, it
|
||||
// must be the header.
|
||||
return DexFile::kDexTypeHeaderItem;
|
||||
}
|
||||
|
||||
static void ProcessPageMap(uint64_t* pagemap,
|
||||
size_t start,
|
||||
size_t end,
|
||||
const std::vector<dex_ir::DexFileSection>& sections,
|
||||
PageCount* page_counts) {
|
||||
static constexpr size_t kLineLength = 32;
|
||||
for (size_t page = start; page < end; ++page) {
|
||||
char type_char = '.';
|
||||
if (PM_PAGEMAP_PRESENT(pagemap[page])) {
|
||||
const size_t dex_page_offset = page - start;
|
||||
uint16_t type = FindSectionTypeForPage(dex_page_offset, sections);
|
||||
page_counts->Increment(type);
|
||||
type_char = PageTypeChar(type);
|
||||
}
|
||||
if (g_verbose) {
|
||||
std::cout << type_char;
|
||||
if ((page - start) % kLineLength == kLineLength - 1) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (g_verbose) {
|
||||
if ((end - start) % kLineLength != 0) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void DisplayDexStatistics(size_t start,
|
||||
size_t end,
|
||||
const PageCount& resident_pages,
|
||||
const std::vector<dex_ir::DexFileSection>& sections,
|
||||
Printer* printer) {
|
||||
// Compute the total possible sizes for sections.
|
||||
PageCount mapped_pages;
|
||||
DCHECK_GE(end, start);
|
||||
size_t total_mapped_pages = end - start;
|
||||
if (total_mapped_pages == 0) {
|
||||
return;
|
||||
}
|
||||
for (size_t page = start; page < end; ++page) {
|
||||
const size_t dex_page_offset = page - start;
|
||||
mapped_pages.Increment(FindSectionTypeForPage(dex_page_offset, sections));
|
||||
}
|
||||
size_t total_resident_pages = 0;
|
||||
printer->PrintHeader();
|
||||
for (size_t i = sections.size(); i > 0; --i) {
|
||||
const dex_ir::DexFileSection& section = sections[i - 1];
|
||||
const uint16_t type = section.type;
|
||||
const DexSectionInfo& section_info = kDexSectionInfoMap.find(type)->second;
|
||||
size_t pages_resident = resident_pages.Get(type);
|
||||
double percent_resident = 0;
|
||||
if (mapped_pages.Get(type) > 0) {
|
||||
percent_resident = 100.0 * pages_resident / mapped_pages.Get(type);
|
||||
}
|
||||
printer->PrintOne(section_info.name.c_str(),
|
||||
pages_resident,
|
||||
mapped_pages.Get(type),
|
||||
percent_resident,
|
||||
100.0 * pages_resident / total_mapped_pages);
|
||||
total_resident_pages += pages_resident;
|
||||
}
|
||||
double percent_of_total = 100.0 * total_resident_pages / total_mapped_pages;
|
||||
printer->PrintOne("GRAND TOTAL",
|
||||
total_resident_pages,
|
||||
total_mapped_pages,
|
||||
percent_of_total,
|
||||
percent_of_total);
|
||||
printer->PrintSkipLine();
|
||||
}
|
||||
|
||||
static void ProcessOneDexMapping(uint64_t* pagemap,
|
||||
uint64_t map_start,
|
||||
const DexFile* dex_file,
|
||||
uint64_t vdex_start,
|
||||
Printer* printer) {
|
||||
uint64_t dex_file_start = reinterpret_cast<uint64_t>(dex_file->Begin());
|
||||
size_t dex_file_size = dex_file->Size();
|
||||
if (dex_file_start < vdex_start) {
|
||||
std::cerr << "Dex file start offset for "
|
||||
<< dex_file->GetLocation().c_str()
|
||||
<< " is incorrect: map start "
|
||||
<< StringPrintf("%" PRIx64 " > dex start %" PRIx64 "\n", map_start, dex_file_start)
|
||||
<< std::endl;
|
||||
return;
|
||||
}
|
||||
uint64_t start_page = (dex_file_start - vdex_start) / kPageSize;
|
||||
uint64_t start_address = start_page * kPageSize;
|
||||
uint64_t end_page = RoundUp(start_address + dex_file_size, kPageSize) / kPageSize;
|
||||
std::cout << "DEX "
|
||||
<< dex_file->GetLocation().c_str()
|
||||
<< StringPrintf(": %" PRIx64 "-%" PRIx64,
|
||||
map_start + start_page * kPageSize,
|
||||
map_start + end_page * kPageSize)
|
||||
<< std::endl;
|
||||
// Build a list of the dex file section types, sorted from highest offset to lowest.
|
||||
std::vector<dex_ir::DexFileSection> sections;
|
||||
{
|
||||
std::unique_ptr<dex_ir::Header> header(dex_ir::DexIrBuilder(*dex_file));
|
||||
sections = dex_ir::GetSortedDexFileSections(header.get(),
|
||||
dex_ir::SortDirection::kSortDescending);
|
||||
}
|
||||
PageCount section_resident_pages;
|
||||
ProcessPageMap(pagemap, start_page, end_page, sections, §ion_resident_pages);
|
||||
DisplayDexStatistics(start_page, end_page, section_resident_pages, sections, printer);
|
||||
}
|
||||
|
||||
static bool IsVdexFileMapping(const std::string& mapped_name) {
|
||||
// Confirm that the map is from a vdex file.
|
||||
static const char* suffixes[] = { ".vdex" };
|
||||
for (const char* suffix : suffixes) {
|
||||
size_t match_loc = mapped_name.find(suffix);
|
||||
if (match_loc != std::string::npos && mapped_name.length() == match_loc + strlen(suffix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool DisplayMappingIfFromVdexFile(pm_map_t* map, Printer* printer) {
|
||||
std::string vdex_name = pm_map_name(map);
|
||||
// Extract all the dex files from the vdex file.
|
||||
std::string error_msg;
|
||||
std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_name,
|
||||
false /*writeable*/,
|
||||
false /*low_4gb*/,
|
||||
false /*unquicken */,
|
||||
&error_msg /*out*/));
|
||||
if (vdex == nullptr) {
|
||||
std::cerr << "Could not open vdex file "
|
||||
<< vdex_name
|
||||
<< ": error "
|
||||
<< error_msg
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<const DexFile>> dex_files;
|
||||
if (!vdex->OpenAllDexFiles(&dex_files, &error_msg)) {
|
||||
std::cerr << "Dex files could not be opened for "
|
||||
<< vdex_name
|
||||
<< ": error "
|
||||
<< error_msg
|
||||
<< std::endl;
|
||||
return false;
|
||||
}
|
||||
// Open the page mapping (one uint64_t per page) for the entire vdex mapping.
|
||||
uint64_t* pagemap;
|
||||
size_t len;
|
||||
if (pm_map_pagemap(map, &pagemap, &len) != 0) {
|
||||
std::cerr << "Error creating pagemap." << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Process the dex files.
|
||||
std::cout << "MAPPING "
|
||||
<< pm_map_name(map)
|
||||
<< StringPrintf(": %" PRIx64 "-%" PRIx64, pm_map_start(map), pm_map_end(map))
|
||||
<< std::endl;
|
||||
for (const auto& dex_file : dex_files) {
|
||||
ProcessOneDexMapping(pagemap,
|
||||
pm_map_start(map),
|
||||
dex_file.get(),
|
||||
reinterpret_cast<uint64_t>(vdex->Begin()),
|
||||
printer);
|
||||
}
|
||||
free(pagemap);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ProcessOneOatMapping(uint64_t* pagemap, size_t size, Printer* printer) {
|
||||
static constexpr size_t kLineLength = 32;
|
||||
size_t resident_page_count = 0;
|
||||
for (size_t page = 0; page < size; ++page) {
|
||||
char type_char = '.';
|
||||
if (PM_PAGEMAP_PRESENT(pagemap[page])) {
|
||||
++resident_page_count;
|
||||
type_char = '*';
|
||||
}
|
||||
if (g_verbose) {
|
||||
std::cout << type_char;
|
||||
if (page % kLineLength == kLineLength - 1) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (g_verbose) {
|
||||
if (size % kLineLength != 0) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
double percent_of_total = 100.0 * resident_page_count / size;
|
||||
printer->PrintHeader();
|
||||
printer->PrintOne("EXECUTABLE", resident_page_count, size, percent_of_total, percent_of_total);
|
||||
printer->PrintSkipLine();
|
||||
}
|
||||
|
||||
static bool IsOatFileMapping(const std::string& mapped_name) {
|
||||
// Confirm that the map is from an oat file.
|
||||
static const char* suffixes[] = { ".odex", ".oat" };
|
||||
for (const char* suffix : suffixes) {
|
||||
size_t match_loc = mapped_name.find(suffix);
|
||||
if (match_loc != std::string::npos && mapped_name.length() == match_loc + strlen(suffix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool DisplayMappingIfFromOatFile(pm_map_t* map, Printer* printer) {
|
||||
// Open the page mapping (one uint64_t per page) for the entire vdex mapping.
|
||||
uint64_t* pagemap;
|
||||
size_t len;
|
||||
if (pm_map_pagemap(map, &pagemap, &len) != 0) {
|
||||
std::cerr << "Error creating pagemap." << std::endl;
|
||||
return false;
|
||||
}
|
||||
// Process the dex files.
|
||||
std::cout << "MAPPING "
|
||||
<< pm_map_name(map)
|
||||
<< StringPrintf(": %" PRIx64 "-%" PRIx64, pm_map_start(map), pm_map_end(map))
|
||||
<< std::endl;
|
||||
ProcessOneOatMapping(pagemap, len, printer);
|
||||
free(pagemap);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool FilterByNameContains(const std::string& mapped_file_name,
|
||||
const std::vector<std::string>& name_filters) {
|
||||
// If no filters were set, everything matches.
|
||||
if (name_filters.empty()) {
|
||||
return true;
|
||||
}
|
||||
for (const auto& name_contains : name_filters) {
|
||||
if (mapped_file_name.find(name_contains) != std::string::npos) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void Usage(const char* cmd) {
|
||||
std::cout << "Usage: " << cmd << " [options] pid" << std::endl
|
||||
<< " --contains=<string>: Display sections containing string." << std::endl
|
||||
<< " --help: Shows this message." << std::endl
|
||||
<< " --verbose: Makes displays verbose." << std::endl;
|
||||
PrintLetterKey();
|
||||
}
|
||||
|
||||
static int DexDiagMain(int argc, char* argv[]) {
|
||||
if (argc < 2) {
|
||||
Usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
std::vector<std::string> name_filters;
|
||||
// TODO: add option to track usage by class name, etc.
|
||||
for (int i = 1; i < argc - 1; ++i) {
|
||||
const StringPiece option(argv[i]);
|
||||
if (option == "--help") {
|
||||
Usage(argv[0]);
|
||||
return EXIT_SUCCESS;
|
||||
} else if (option == "--verbose") {
|
||||
g_verbose = true;
|
||||
} else if (option.starts_with("--contains=")) {
|
||||
std::string contains(option.substr(strlen("--contains=")).data());
|
||||
name_filters.push_back(contains);
|
||||
} else {
|
||||
Usage(argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
// Art specific set up.
|
||||
InitLogging(argv, Runtime::Abort);
|
||||
MemMap::Init();
|
||||
|
||||
#ifdef ART_TARGET_ANDROID
|
||||
pid_t pid;
|
||||
char* endptr;
|
||||
pid = (pid_t)strtol(argv[argc - 1], &endptr, 10);
|
||||
if (*endptr != '\0' || kill(pid, 0) != 0) {
|
||||
std::cerr << StringPrintf("Invalid PID \"%s\".\n", argv[argc - 1]) << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// get libpagemap kernel information.
|
||||
pm_kernel_t* ker;
|
||||
if (pm_kernel_create(&ker) != 0) {
|
||||
std::cerr << "Error creating kernel interface -- does this kernel have pagemap?" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// get libpagemap process information.
|
||||
pm_process_t* proc;
|
||||
if (pm_process_create(ker, pid, &proc) != 0) {
|
||||
std::cerr << "Error creating process interface -- does process "
|
||||
<< pid
|
||||
<< " really exist?"
|
||||
<< std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Get the set of mappings by the specified process.
|
||||
pm_map_t** maps;
|
||||
size_t num_maps;
|
||||
if (pm_process_maps(proc, &maps, &num_maps) != 0) {
|
||||
std::cerr << "Error listing maps." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
bool match_found = false;
|
||||
// Process the mappings that are due to vdex or oat files.
|
||||
Printer printer;
|
||||
for (size_t i = 0; i < num_maps; ++i) {
|
||||
std::string mapped_file_name = pm_map_name(maps[i]);
|
||||
// Filter by name contains options (if any).
|
||||
if (!FilterByNameContains(mapped_file_name, name_filters)) {
|
||||
continue;
|
||||
}
|
||||
if (IsVdexFileMapping(mapped_file_name)) {
|
||||
if (!DisplayMappingIfFromVdexFile(maps[i], &printer)) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
match_found = true;
|
||||
} else if (IsOatFileMapping(mapped_file_name)) {
|
||||
if (!DisplayMappingIfFromOatFile(maps[i], &printer)) {
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
match_found = true;
|
||||
}
|
||||
}
|
||||
if (!match_found) {
|
||||
std::cerr << "No relevant memory maps were found." << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
#endif
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
} // namespace art
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
return art::DexDiagMain(argc, argv);
|
||||
}
|
152
android/art/dexlayout/dexdiag_test.cc
Normal file
152
android/art/dexlayout/dexdiag_test.cc
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* 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 <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common_runtime_test.h"
|
||||
|
||||
#include "runtime/exec_utils.h"
|
||||
#include "runtime/oat_file.h"
|
||||
#include "runtime/os.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
static const char* kDexDiagContains = "--contains=core.vdex";
|
||||
static const char* kDexDiagContainsFails = "--contains=anything_other_than_core.vdex";
|
||||
static const char* kDexDiagHelp = "--help";
|
||||
static const char* kDexDiagVerbose = "--verbose";
|
||||
static const char* kDexDiagBinaryName = "dexdiag";
|
||||
|
||||
class DexDiagTest : public CommonRuntimeTest {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
CommonRuntimeTest::SetUp();
|
||||
}
|
||||
|
||||
// Path to the dexdiag(d?)[32|64] binary.
|
||||
std::string GetDexDiagFilePath() {
|
||||
std::string root = GetTestAndroidRoot();
|
||||
|
||||
root += "/bin/";
|
||||
root += kDexDiagBinaryName;
|
||||
|
||||
std::string root32 = root + "32";
|
||||
// If we have both a 32-bit and a 64-bit build, the 32-bit file will have a 32 suffix.
|
||||
if (OS::FileExists(root32.c_str()) && !Is64BitInstructionSet(kRuntimeISA)) {
|
||||
return root32;
|
||||
} else {
|
||||
// This is a 64-bit build or only a single build exists.
|
||||
return root;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<OatFile> OpenOatAndVdexFiles() {
|
||||
// Open the core.oat file.
|
||||
// This is a little convoluted because we have to
|
||||
// get the location of the default core image (.../framework/core.oat),
|
||||
// find it in the right architecture subdirectory (.../framework/arm/core.oat),
|
||||
// Then, opening the oat file has the side-effect of opening the corresponding
|
||||
// vdex file (.../framework/arm/core.vdex).
|
||||
const std::string default_location = GetCoreOatLocation();
|
||||
EXPECT_TRUE(!default_location.empty());
|
||||
std::string oat_location = GetSystemImageFilename(default_location.c_str(), kRuntimeISA);
|
||||
EXPECT_TRUE(!oat_location.empty());
|
||||
std::cout << "==" << oat_location << std::endl;
|
||||
std::string error_msg;
|
||||
std::unique_ptr<OatFile> oat(OatFile::Open(oat_location.c_str(),
|
||||
oat_location.c_str(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
false,
|
||||
/*low_4gb*/false,
|
||||
nullptr,
|
||||
&error_msg));
|
||||
EXPECT_TRUE(oat != nullptr) << error_msg;
|
||||
return oat;
|
||||
}
|
||||
|
||||
// Run dexdiag with a custom boot image location.
|
||||
bool Exec(pid_t this_pid, const std::vector<std::string>& args, std::string* error_msg) {
|
||||
// Invoke 'dexdiag' against the current process.
|
||||
// This should succeed because we have a runtime and so it should
|
||||
// be able to map in the boot.art and do a diff for it.
|
||||
std::vector<std::string> exec_argv;
|
||||
|
||||
// Build the command line "dexdiag <args> this_pid".
|
||||
std::string executable_path = GetDexDiagFilePath();
|
||||
EXPECT_TRUE(OS::FileExists(executable_path.c_str())) << executable_path
|
||||
<< " should be a valid file path";
|
||||
exec_argv.push_back(executable_path);
|
||||
for (const auto& arg : args) {
|
||||
exec_argv.push_back(arg);
|
||||
}
|
||||
exec_argv.push_back(std::to_string(this_pid));
|
||||
|
||||
return ::art::Exec(exec_argv, error_msg);
|
||||
}
|
||||
};
|
||||
|
||||
// We can't run these tests on the host, as they will fail when trying to open
|
||||
// /proc/pid/pagemap.
|
||||
// On the target, we invoke 'dexdiag' against the current process.
|
||||
// This should succeed because we have a runtime and so dexdiag should
|
||||
// be able to find the map for, e.g., boot.vdex and friends.
|
||||
TEST_F(DexDiagTest, DexDiagHelpTest) {
|
||||
// TODO: test the resulting output.
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(Exec(getpid(), { kDexDiagHelp }, &error_msg)) << "Failed to execute -- because: "
|
||||
<< error_msg;
|
||||
}
|
||||
|
||||
#if defined (ART_TARGET)
|
||||
TEST_F(DexDiagTest, DexDiagContainsTest) {
|
||||
#else
|
||||
TEST_F(DexDiagTest, DISABLED_DexDiagContainsTest) {
|
||||
#endif
|
||||
std::unique_ptr<OatFile> oat = OpenOatAndVdexFiles();
|
||||
// TODO: test the resulting output.
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(Exec(getpid(), { kDexDiagContains }, &error_msg)) << "Failed to execute -- because: "
|
||||
<< error_msg;
|
||||
}
|
||||
|
||||
#if defined (ART_TARGET)
|
||||
TEST_F(DexDiagTest, DexDiagContainsFailsTest) {
|
||||
#else
|
||||
TEST_F(DexDiagTest, DISABLED_DexDiagContainsFailsTest) {
|
||||
#endif
|
||||
std::unique_ptr<OatFile> oat = OpenOatAndVdexFiles();
|
||||
// TODO: test the resulting output.
|
||||
std::string error_msg;
|
||||
ASSERT_FALSE(Exec(getpid(), { kDexDiagContainsFails }, &error_msg))
|
||||
<< "Failed to execute -- because: "
|
||||
<< error_msg;
|
||||
}
|
||||
|
||||
#if defined (ART_TARGET)
|
||||
TEST_F(DexDiagTest, DexDiagVerboseTest) {
|
||||
#else
|
||||
TEST_F(DexDiagTest, DISABLED_DexDiagVerboseTest) {
|
||||
#endif
|
||||
// TODO: test the resulting output.
|
||||
std::unique_ptr<OatFile> oat = OpenOatAndVdexFiles();
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(Exec(getpid(), { kDexDiagVerbose }, &error_msg)) << "Failed to execute -- because: "
|
||||
<< error_msg;
|
||||
}
|
||||
|
||||
} // namespace art
|
2021
android/art/dexlayout/dexlayout.cc
Normal file
2021
android/art/dexlayout/dexlayout.cc
Normal file
File diff suppressed because it is too large
Load diff
143
android/art/dexlayout/dexlayout.h
Normal file
143
android/art/dexlayout/dexlayout.h
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Header file of the dexlayout utility.
|
||||
*
|
||||
* This is a tool to read dex files into an internal representation,
|
||||
* reorganize the representation, and emit dex files with a better
|
||||
* file layout.
|
||||
*/
|
||||
|
||||
#ifndef ART_DEXLAYOUT_DEXLAYOUT_H_
|
||||
#define ART_DEXLAYOUT_DEXLAYOUT_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "dex_file_layout.h"
|
||||
#include "dex_ir.h"
|
||||
#include "mem_map.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
class DexFile;
|
||||
class Instruction;
|
||||
class ProfileCompilationInfo;
|
||||
|
||||
/* Supported output formats. */
|
||||
enum OutputFormat {
|
||||
kOutputPlain = 0, // default
|
||||
kOutputXml, // XML-style
|
||||
};
|
||||
|
||||
/* Command-line options. */
|
||||
class Options {
|
||||
public:
|
||||
Options() = default;
|
||||
|
||||
bool dump_ = false;
|
||||
bool build_dex_ir_ = false;
|
||||
bool checksum_only_ = false;
|
||||
bool disassemble_ = false;
|
||||
bool exports_only_ = false;
|
||||
bool ignore_bad_checksum_ = false;
|
||||
bool output_to_memmap_ = false;
|
||||
bool show_annotations_ = false;
|
||||
bool show_file_headers_ = false;
|
||||
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;
|
||||
const char* output_file_name_ = nullptr;
|
||||
const char* profile_file_name_ = nullptr;
|
||||
};
|
||||
|
||||
class DexLayout {
|
||||
public:
|
||||
DexLayout(Options& options,
|
||||
ProfileCompilationInfo* info,
|
||||
FILE* out_file,
|
||||
dex_ir::Header*
|
||||
header = nullptr)
|
||||
: options_(options), info_(info), out_file_(out_file), header_(header) { }
|
||||
|
||||
int ProcessFile(const char* file_name);
|
||||
void ProcessDexFile(const char* file_name, const DexFile* dex_file, size_t dex_file_index);
|
||||
|
||||
dex_ir::Header* GetHeader() const { return header_; }
|
||||
void SetHeader(dex_ir::Header* header) { header_ = header; }
|
||||
|
||||
MemMap* GetAndReleaseMemMap() { return mem_map_.release(); }
|
||||
|
||||
const DexLayoutSections& GetSections() const {
|
||||
return dex_sections_;
|
||||
}
|
||||
|
||||
private:
|
||||
void DumpAnnotationSetItem(dex_ir::AnnotationSetItem* set_item);
|
||||
void DumpBytecodes(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset);
|
||||
void DumpCatches(const dex_ir::CodeItem* code);
|
||||
void DumpClass(int idx, char** last_package);
|
||||
void DumpClassAnnotations(int idx);
|
||||
void DumpClassDef(int idx);
|
||||
void DumpCode(uint32_t idx, const dex_ir::CodeItem* code, uint32_t code_offset);
|
||||
void DumpEncodedAnnotation(dex_ir::EncodedAnnotation* annotation);
|
||||
void DumpEncodedValue(const dex_ir::EncodedValue* data);
|
||||
void DumpFileHeader();
|
||||
void DumpIField(uint32_t idx, uint32_t flags, int i);
|
||||
void DumpInstruction(const dex_ir::CodeItem* code,
|
||||
uint32_t code_offset,
|
||||
uint32_t insn_idx,
|
||||
uint32_t insn_width,
|
||||
const Instruction* dec_insn);
|
||||
void DumpInterface(const dex_ir::TypeId* type_item, int i);
|
||||
void DumpLocalInfo(const dex_ir::CodeItem* code);
|
||||
void DumpMethod(uint32_t idx, uint32_t flags, const dex_ir::CodeItem* code, int i);
|
||||
void DumpPositionInfo(const dex_ir::CodeItem* code);
|
||||
void DumpSField(uint32_t idx, uint32_t flags, int i, dex_ir::EncodedValue* init);
|
||||
void DumpDexFile();
|
||||
|
||||
std::vector<dex_ir::ClassData*> LayoutClassDefsAndClassData(const DexFile* dex_file);
|
||||
int32_t LayoutCodeItems(const DexFile* dex_file,
|
||||
std::vector<dex_ir::ClassData*> new_class_data_order);
|
||||
void LayoutStringData(const DexFile* dex_file);
|
||||
bool IsNextSectionCodeItemAligned(uint32_t offset);
|
||||
template<class T> void FixupSection(std::map<uint32_t, std::unique_ptr<T>>& map, uint32_t diff);
|
||||
void FixupSections(uint32_t offset, uint32_t diff);
|
||||
|
||||
// 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 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);
|
||||
|
||||
Options& options_;
|
||||
ProfileCompilationInfo* info_;
|
||||
FILE* out_file_;
|
||||
dex_ir::Header* header_;
|
||||
std::unique_ptr<MemMap> mem_map_;
|
||||
DexLayoutSections dex_sections_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DexLayout);
|
||||
};
|
||||
|
||||
} // namespace art
|
||||
|
||||
#endif // ART_DEXLAYOUT_DEXLAYOUT_H_
|
208
android/art/dexlayout/dexlayout_main.cc
Normal file
208
android/art/dexlayout/dexlayout_main.cc
Normal file
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* 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.
|
||||
*
|
||||
* Main driver of the dexlayout utility.
|
||||
*
|
||||
* This is a tool to read dex files into an internal representation,
|
||||
* reorganize the representation, and emit dex files with a better
|
||||
* file layout.
|
||||
*/
|
||||
|
||||
#include "dexlayout.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "jit/profile_compilation_info.h"
|
||||
#include "runtime.h"
|
||||
#include "mem_map.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
static const char* kProgramName = "dexlayout";
|
||||
|
||||
/*
|
||||
* Shows usage.
|
||||
*/
|
||||
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] [-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");
|
||||
fprintf(stderr, " -d : disassemble code sections\n");
|
||||
fprintf(stderr, " -e : display exported items only\n");
|
||||
fprintf(stderr, " -f : display summary information from file header\n");
|
||||
fprintf(stderr, " -h : display file header details\n");
|
||||
fprintf(stderr, " -i : ignore checksum failures\n");
|
||||
fprintf(stderr, " -l : output layout, either 'plain' or 'xml'\n");
|
||||
fprintf(stderr, " -o : output file name (defaults to stdout)\n");
|
||||
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");
|
||||
}
|
||||
|
||||
/*
|
||||
* Main driver of the dexlayout utility.
|
||||
*/
|
||||
int DexlayoutDriver(int argc, char** argv) {
|
||||
// Art specific set up.
|
||||
InitLogging(argv, Runtime::Abort);
|
||||
MemMap::Init();
|
||||
|
||||
Options options;
|
||||
options.dump_ = true;
|
||||
options.verbose_ = true;
|
||||
bool want_usage = false;
|
||||
|
||||
// Parse all arguments.
|
||||
while (1) {
|
||||
const int ic = getopt(argc, argv, "abcdefghil:mo:p:stvw:");
|
||||
if (ic < 0) {
|
||||
break; // done
|
||||
}
|
||||
switch (ic) {
|
||||
case 'a': // display annotations
|
||||
options.show_annotations_ = true;
|
||||
break;
|
||||
case 'b': // build dex_ir
|
||||
options.build_dex_ir_ = true;
|
||||
break;
|
||||
case 'c': // verify the checksum then exit
|
||||
options.checksum_only_ = true;
|
||||
break;
|
||||
case 'd': // disassemble Dalvik instructions
|
||||
options.disassemble_ = true;
|
||||
break;
|
||||
case 'e': // exported items only
|
||||
options.exports_only_ = true;
|
||||
break;
|
||||
case 'f': // display outer file header
|
||||
options.show_file_headers_ = true;
|
||||
break;
|
||||
case 'h': // display section headers, i.e. all meta-data
|
||||
options.show_section_headers_ = true;
|
||||
break;
|
||||
case 'i': // continue even if checksum is bad
|
||||
options.ignore_bad_checksum_ = true;
|
||||
break;
|
||||
case 'l': // layout
|
||||
if (strcmp(optarg, "plain") == 0) {
|
||||
options.output_format_ = kOutputPlain;
|
||||
} else if (strcmp(optarg, "xml") == 0) {
|
||||
options.output_format_ = kOutputXml;
|
||||
options.verbose_ = false;
|
||||
} else {
|
||||
want_usage = true;
|
||||
}
|
||||
break;
|
||||
case 'm': // output dex files to a memmap
|
||||
options.output_to_memmap_ = true;
|
||||
break;
|
||||
case 'o': // output file
|
||||
options.output_file_name_ = optarg;
|
||||
break;
|
||||
case 'p': // profile file
|
||||
options.profile_file_name_ = optarg;
|
||||
break;
|
||||
case 's': // visualize access pattern
|
||||
options.visualize_pattern_ = true;
|
||||
options.verbose_ = false;
|
||||
break;
|
||||
case 't': // display section statistics
|
||||
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;
|
||||
default:
|
||||
want_usage = true;
|
||||
break;
|
||||
} // switch
|
||||
} // while
|
||||
|
||||
// Detect early problems.
|
||||
if (optind == argc) {
|
||||
fprintf(stderr, "%s: no file specified\n", kProgramName);
|
||||
want_usage = true;
|
||||
}
|
||||
if (options.checksum_only_ && options.ignore_bad_checksum_) {
|
||||
fprintf(stderr, "Can't specify both -c and -i\n");
|
||||
want_usage = true;
|
||||
}
|
||||
if (want_usage) {
|
||||
Usage();
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Open alternative output file.
|
||||
FILE* out_file = stdout;
|
||||
if (options.output_file_name_) {
|
||||
out_file = fopen(options.output_file_name_, "w");
|
||||
if (!out_file) {
|
||||
fprintf(stderr, "Can't open %s\n", options.output_file_name_);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Open profile file.
|
||||
std::unique_ptr<ProfileCompilationInfo> profile_info;
|
||||
if (options.profile_file_name_) {
|
||||
int profile_fd = open(options.profile_file_name_, O_RDONLY);
|
||||
if (profile_fd < 0) {
|
||||
fprintf(stderr, "Can't open %s\n", options.profile_file_name_);
|
||||
return 1;
|
||||
}
|
||||
profile_info.reset(new ProfileCompilationInfo());
|
||||
if (!profile_info->Load(profile_fd)) {
|
||||
fprintf(stderr, "Can't read profile info from %s\n", options.profile_file_name_);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Create DexLayout instance.
|
||||
DexLayout dex_layout(options, profile_info.get(), out_file);
|
||||
|
||||
// Process all files supplied on command line.
|
||||
int result = 0;
|
||||
while (optind < argc) {
|
||||
result |= dex_layout.ProcessFile(argv[optind++]);
|
||||
} // while
|
||||
|
||||
if (options.output_file_name_) {
|
||||
CHECK(out_file != nullptr && out_file != stdout);
|
||||
fclose(out_file);
|
||||
}
|
||||
|
||||
return result != 0;
|
||||
}
|
||||
|
||||
} // namespace art
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
return art::DexlayoutDriver(argc, argv);
|
||||
}
|
674
android/art/dexlayout/dexlayout_test.cc
Normal file
674
android/art/dexlayout/dexlayout_test.cc
Normal file
|
@ -0,0 +1,674 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/unix_file/fd_file.h"
|
||||
#include "common_runtime_test.h"
|
||||
#include "dex_file-inl.h"
|
||||
#include "exec_utils.h"
|
||||
#include "jit/profile_compilation_info.h"
|
||||
#include "utils.h"
|
||||
|
||||
namespace art {
|
||||
|
||||
static const char kDexFileLayoutInputDex[] =
|
||||
"ZGV4CjAzNQD1KW3+B8NAB0f2A/ZVIBJ0aHrGIqcpVTAUAgAAcAAAAHhWNBIAAAAAAAAAAIwBAAAH"
|
||||
"AAAAcAAAAAQAAACMAAAAAQAAAJwAAAAAAAAAAAAAAAMAAACoAAAAAgAAAMAAAAAUAQAAAAEAADAB"
|
||||
"AAA4AQAAQAEAAEgBAABNAQAAUgEAAGYBAAADAAAABAAAAAUAAAAGAAAABgAAAAMAAAAAAAAAAAAA"
|
||||
"AAAAAAABAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAEAAAAAAAAAdQEAAAAAAAABAAAA"
|
||||
"AAAAAAIAAAAAAAAAAgAAAAAAAAB/AQAAAAAAAAEAAQABAAAAaQEAAAQAAABwEAIAAAAOAAEAAQAB"
|
||||
"AAAAbwEAAAQAAABwEAIAAAAOAAY8aW5pdD4ABkEuamF2YQAGQi5qYXZhAANMQTsAA0xCOwASTGph"
|
||||
"dmEvbGFuZy9PYmplY3Q7AAFWAAQABw48AAQABw48AAAAAQAAgIAEgAIAAAEAAYCABJgCAAAACwAA"
|
||||
"AAAAAAABAAAAAAAAAAEAAAAHAAAAcAAAAAIAAAAEAAAAjAAAAAMAAAABAAAAnAAAAAUAAAADAAAA"
|
||||
"qAAAAAYAAAACAAAAwAAAAAEgAAACAAAAAAEAAAIgAAAHAAAAMAEAAAMgAAACAAAAaQEAAAAgAAAC"
|
||||
"AAAAdQEAAAAQAAABAAAAjAEAAA==";
|
||||
|
||||
// Dex file with catch handler unreferenced by try blocks.
|
||||
// Constructed by building a dex file with try/catch blocks and hex editing.
|
||||
static const char kUnreferencedCatchHandlerInputDex[] =
|
||||
"ZGV4CjAzNQD+exd52Y0f9nY5x5GmInXq5nXrO6Kl2RV4AwAAcAAAAHhWNBIAAAAAAAAAANgCAAAS"
|
||||
"AAAAcAAAAAgAAAC4AAAAAwAAANgAAAABAAAA/AAAAAQAAAAEAQAAAQAAACQBAAA0AgAARAEAANYB"
|
||||
"AADeAQAA5gEAAO4BAAAAAgAADwIAACYCAAA9AgAAUQIAAGUCAAB5AgAAfwIAAIUCAACIAgAAjAIA"
|
||||
"AKECAACnAgAArAIAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAwAAAAOAAAADAAAAAYAAAAAAAAA"
|
||||
"DQAAAAYAAADIAQAADQAAAAYAAADQAQAABQABABAAAAAAAAAAAAAAAAAAAgAPAAAAAQABABEAAAAD"
|
||||
"AAAAAAAAAAAAAAABAAAAAwAAAAAAAAADAAAAAAAAAMgCAAAAAAAAAQABAAEAAAC1AgAABAAAAHAQ"
|
||||
"AwAAAA4AAwABAAIAAgC6AgAAIQAAAGIAAAAaAQoAbiACABAAYgAAABoBCwBuIAIAEAAOAA0AYgAA"
|
||||
"ABoBAQBuIAIAEAAo8A0AYgAAABoBAgBuIAIAEAAo7gAAAAAAAAcAAQAHAAAABwABAAIBAg8BAhgA"
|
||||
"AQAAAAQAAAABAAAABwAGPGluaXQ+AAZDYXRjaDEABkNhdGNoMgAQSGFuZGxlclRlc3QuamF2YQAN"
|
||||
"TEhhbmRsZXJUZXN0OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABVMamF2YS9sYW5nL0V4Y2VwdGlv"
|
||||
"bjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9sYW5nL1N5"
|
||||
"c3RlbTsABFRyeTEABFRyeTIAAVYAAlZMABNbTGphdmEvbGFuZy9TdHJpbmc7AARtYWluAANvdXQA"
|
||||
"B3ByaW50bG4AAQAHDgAEAQAHDn17AncdHoseAAAAAgAAgYAExAIBCdwCAAANAAAAAAAAAAEAAAAA"
|
||||
"AAAAAQAAABIAAABwAAAAAgAAAAgAAAC4AAAAAwAAAAMAAADYAAAABAAAAAEAAAD8AAAABQAAAAQA"
|
||||
"AAAEAQAABgAAAAEAAAAkAQAAASAAAAIAAABEAQAAARAAAAIAAADIAQAAAiAAABIAAADWAQAAAyAA"
|
||||
"AAIAAAC1AgAAACAAAAEAAADIAgAAABAAAAEAAADYAgAA";
|
||||
|
||||
// Dex file with 0-size (catch all only) catch handler unreferenced by try blocks.
|
||||
// Constructed by building a dex file with try/catch blocks and hex editing.
|
||||
static const char kUnreferenced0SizeCatchHandlerInputDex[] =
|
||||
"ZGV4CjAzNQCEbEEvMstSNpQpjPdfMEfUBS48cis2QRJoAwAAcAAAAHhWNBIAAAAAAAAAAMgCAAAR"
|
||||
"AAAAcAAAAAcAAAC0AAAAAwAAANAAAAABAAAA9AAAAAQAAAD8AAAAAQAAABwBAAAsAgAAPAEAAOoB"
|
||||
"AADyAQAABAIAABMCAAAqAgAAPgIAAFICAABmAgAAaQIAAG0CAACCAgAAhgIAAIoCAACQAgAAlQIA"
|
||||
"AJ4CAACiAgAAAgAAAAMAAAAEAAAABQAAAAYAAAAHAAAACQAAAAcAAAAFAAAAAAAAAAgAAAAFAAAA"
|
||||
"3AEAAAgAAAAFAAAA5AEAAAQAAQANAAAAAAAAAAAAAAAAAAIADAAAAAEAAQAOAAAAAgAAAAAAAAAA"
|
||||
"AAAAAQAAAAIAAAAAAAAAAQAAAAAAAAC5AgAAAAAAAAEAAQABAAAApgIAAAQAAABwEAMAAAAOAAQA"
|
||||
"AQACAAIAqwIAAC8AAABiAAAAGgEPAG4gAgAQAGIAAAAaAQoAbiACABAAYgAAABoBEABuIAIAEABi"
|
||||
"AAAAGgELAG4gAgAQAA4ADQBiAQAAGgIKAG4gAgAhACcADQBiAQAAGgILAG4gAgAhACcAAAAAAAAA"
|
||||
"BwABAA4AAAAHAAEAAgAdACYAAAABAAAAAwAAAAEAAAAGAAY8aW5pdD4AEEhhbmRsZXJUZXN0Lmph"
|
||||
"dmEADUxIYW5kbGVyVGVzdDsAFUxqYXZhL2lvL1ByaW50U3RyZWFtOwASTGphdmEvbGFuZy9PYmpl"
|
||||
"Y3Q7ABJMamF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwABVgACVkwAE1tMamF2"
|
||||
"YS9sYW5nL1N0cmluZzsAAmYxAAJmMgAEbWFpbgADb3V0AAdwcmludGxuAAJ0MQACdDIAAQAHDgAE"
|
||||
"AQAHDnl7eXkCeB2bAAAAAgAAgYAEvAIBCdQCAA0AAAAAAAAAAQAAAAAAAAABAAAAEQAAAHAAAAAC"
|
||||
"AAAABwAAALQAAAADAAAAAwAAANAAAAAEAAAAAQAAAPQAAAAFAAAABAAAAPwAAAAGAAAAAQAAABwB"
|
||||
"AAABIAAAAgAAADwBAAABEAAAAgAAANwBAAACIAAAEQAAAOoBAAADIAAAAgAAAKYCAAAAIAAAAQAA"
|
||||
"ALkCAAAAEAAAAQAAAMgCAAA=";
|
||||
|
||||
// Dex file with an unreferenced catch handler at end of code item.
|
||||
// Constructed by building a dex file with try/catch blocks and hex editing.
|
||||
static const char kUnreferencedEndingCatchHandlerInputDex[] =
|
||||
"ZGV4CjAzNQCEflufI6xGTDDRmLpbfYi6ujPrDLIwvYcEBAAAcAAAAHhWNBIAAAAAAAAAAGQDAAAT"
|
||||
"AAAAcAAAAAgAAAC8AAAAAwAAANwAAAABAAAAAAEAAAUAAAAIAQAAAQAAADABAAC0AgAAUAEAAE4C"
|
||||
"AABWAgAAXgIAAGYCAAB4AgAAhwIAAJ4CAAC1AgAAyQIAAN0CAADxAgAA9wIAAP0CAAAAAwAABAMA"
|
||||
"ABkDAAAcAwAAIgMAACcDAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAADgAAAAwAAAAGAAAA"
|
||||
"AAAAAA0AAAAGAAAAQAIAAA0AAAAGAAAASAIAAAUAAQARAAAAAAAAAAAAAAAAAAAADwAAAAAAAgAQ"
|
||||
"AAAAAQABABIAAAADAAAAAAAAAAAAAAABAAAAAwAAAAAAAAADAAAAAAAAAFADAAAAAAAAAQABAAEA"
|
||||
"AAAwAwAABAAAAHAQBAAAAA4AAgAAAAIAAgA1AwAAIQAAAGIAAAAaAQoAbiADABAAYgAAABoBCwBu"
|
||||
"IAMAEAAOAA0AYgAAABoBAQBuIAMAEAAo8A0AYgAAABoBAgBuIAMAEAAo7gAAAAAAAAcAAQAHAAAA"
|
||||
"BwABAAIBAg8BAhgAAwABAAIAAgBCAwAAIQAAAGIAAAAaAQoAbiADABAAYgAAABoBCwBuIAMAEAAO"
|
||||
"AA0AYgAAABoBAQBuIAMAEAAo8A0AYgAAABoBAgBuIAMAEAAo7gAAAAAAAAcAAQAHAAAABwABAAIB"
|
||||
"Ag8BAhgAAQAAAAQAAAABAAAABwAGPGluaXQ+AAZDYXRjaDEABkNhdGNoMgAQSGFuZGxlclRlc3Qu"
|
||||
"amF2YQANTEhhbmRsZXJUZXN0OwAVTGphdmEvaW8vUHJpbnRTdHJlYW07ABVMamF2YS9sYW5nL0V4"
|
||||
"Y2VwdGlvbjsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEvbGFuZy9TdHJpbmc7ABJMamF2YS9s"
|
||||
"YW5nL1N5c3RlbTsABFRyeTEABFRyeTIAAVYAAlZMABNbTGphdmEvbGFuZy9TdHJpbmc7AAFhAARt"
|
||||
"YWluAANvdXQAB3ByaW50bG4AAQAHDgAEAAcOfHsCeB0eih4AEQEABw59ewJ3HR6LHgAAAAMAAIGA"
|
||||
"BNACAQnoAgEJ1AMAAA0AAAAAAAAAAQAAAAAAAAABAAAAEwAAAHAAAAACAAAACAAAALwAAAADAAAA"
|
||||
"AwAAANwAAAAEAAAAAQAAAAABAAAFAAAABQAAAAgBAAAGAAAAAQAAADABAAABIAAAAwAAAFABAAAB"
|
||||
"EAAAAgAAAEACAAACIAAAEwAAAE4CAAADIAAAAwAAADADAAAAIAAAAQAAAFADAAAAEAAAAQAAAGQD"
|
||||
"AAA=";
|
||||
|
||||
// Dex file with multiple code items that have the same debug_info_off_. Constructed by a modified
|
||||
// dexlayout on XandY.
|
||||
static const char kDexFileDuplicateOffset[] =
|
||||
"ZGV4CjAzNwAQfXfPCB8qCxo7MqdFhmHZQwCv8+udHD8MBAAAcAAAAHhWNBIAAAAAAAAAAFQDAAAT"
|
||||
"AAAAcAAAAAgAAAC8AAAAAQAAANwAAAABAAAA6AAAAAUAAADwAAAAAwAAABgBAACUAgAAeAEAABQC"
|
||||
"AAAeAgAAJgIAACsCAAAyAgAANwIAAFsCAAB7AgAAngIAALICAAC1AgAAvQIAAMUCAADIAgAA1QIA"
|
||||
"AOkCAADvAgAA9QIAAPwCAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAACQAAAAkAAAAHAAAA"
|
||||
"AAAAAAIAAQASAAAAAAAAAAEAAAABAAAAAQAAAAIAAAAAAAAAAgAAAAEAAAAGAAAAAQAAAAAAAAAA"
|
||||
"AAAABgAAAAAAAAAKAAAAAAAAACsDAAAAAAAAAQAAAAAAAAAGAAAAAAAAAAsAAAD0AQAANQMAAAAA"
|
||||
"AAACAAAAAAAAAAAAAAAAAAAACwAAAAQCAAA/AwAAAAAAAAIAAAAUAwAAGgMAAAEAAAAjAwAAAQAB"
|
||||
"AAEAAAAFAAAABAAAAHAQBAAAAA4AAQABAAEAAAAFAAAABAAAAHAQBAAAAA4AAQAAAAEAAAAFAAAA"
|
||||
"CAAAACIAAQBwEAEAAABpAAAADgABAAEAAQAAAAUAAAAEAAAAcBAAAAAADgB4AQAAAAAAAAAAAAAA"
|
||||
"AAAAhAEAAAAAAAAAAAAAAAAAAAg8Y2xpbml0PgAGPGluaXQ+AANMWDsABUxZJFo7AANMWTsAIkxk"
|
||||
"YWx2aWsvYW5ub3RhdGlvbi9FbmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5l"
|
||||
"ckNsYXNzOwAhTGRhbHZpay9hbm5vdGF0aW9uL01lbWJlckNsYXNzZXM7ABJMamF2YS9sYW5nL09i"
|
||||
"amVjdDsAAVYABlguamF2YQAGWS5qYXZhAAFaAAthY2Nlc3NGbGFncwASZW1pdHRlcjogamFjay00"
|
||||
"LjI1AARuYW1lAAR0aGlzAAV2YWx1ZQABegARAAcOABMABw4AEgAHDnYAEQAHDgACAwERGAICBAIN"
|
||||
"BAgPFwwCBQERHAEYAQAAAQAAgIAEjAMAAAEAAYCABKQDAQACAAAIAoiABLwDAYCABNwDAAAADwAA"
|
||||
"AAAAAAABAAAAAAAAAAEAAAATAAAAcAAAAAIAAAAIAAAAvAAAAAMAAAABAAAA3AAAAAQAAAABAAAA"
|
||||
"6AAAAAUAAAAFAAAA8AAAAAYAAAADAAAAGAEAAAMQAAACAAAAeAEAAAEgAAAEAAAAjAEAAAYgAAAC"
|
||||
"AAAA9AEAAAIgAAATAAAAFAIAAAMgAAAEAAAA/wIAAAQgAAADAAAAFAMAAAAgAAADAAAAKwMAAAAQ"
|
||||
"AAABAAAAVAMAAA==";
|
||||
|
||||
// Dex file with null value for annotations_off in the annotation_set_ref_list.
|
||||
// Constructed by building a dex file with annotations and hex editing.
|
||||
static const char kNullSetRefListElementInputDex[] =
|
||||
"ZGV4CjAzNQB1iA+7ZwgkF+7E6ZesYFc2lRAR3qnRAanwAwAAcAAAAHhWNBIAAAAAAAAAACADAAAS"
|
||||
"AAAAcAAAAAgAAAC4AAAAAwAAANgAAAABAAAA/AAAAAQAAAAEAQAAAgAAACQBAACMAgAAZAEAAOgB"
|
||||
"AADwAQAAAAIAAAMCAAAQAgAAIAIAADQCAABIAgAAawIAAI0CAAC1AgAAyAIAANECAADUAgAA2QIA"
|
||||
"ANwCAADjAgAA6QIAAAMAAAAEAAAABQAAAAYAAAAHAAAACAAAAAkAAAAMAAAAAgAAAAMAAAAAAAAA"
|
||||
"DAAAAAcAAAAAAAAADQAAAAcAAADgAQAABgAGAAsAAAAAAAEAAAAAAAAAAgAOAAAAAQAAABAAAAAC"
|
||||
"AAEAAAAAAAAAAAAAAAAAAgAAAAAAAAABAAAAsAEAAAgDAAAAAAAAAQAAAAEmAAACAAAA2AEAAAoA"
|
||||
"AADIAQAAFgMAAAAAAAACAAAAAAAAAHwBAAABAAAA/AIAAAAAAAABAAAAAgMAAAEAAQABAAAA8AIA"
|
||||
"AAQAAABwEAMAAAAOAAIAAgAAAAAA9QIAAAEAAAAOAAAAAAAAAAAAAAAAAAAAAQAAAAEAAABkAQAA"
|
||||
"cAEAAAAAAAAAAAAAAAAAAAEAAAAEAAAAAgAAAAMAAwAGPGluaXQ+AA5Bbm5vQ2xhc3MuamF2YQAB"
|
||||
"TAALTEFubm9DbGFzczsADkxNeUFubm90YXRpb247ABJMamF2YS9sYW5nL09iamVjdDsAEkxqYXZh"
|
||||
"L2xhbmcvU3RyaW5nOwAhTGphdmEvbGFuZy9hbm5vdGF0aW9uL0Fubm90YXRpb247ACBMamF2YS9s"
|
||||
"YW5nL2Fubm90YXRpb24vUmV0ZW50aW9uOwAmTGphdmEvbGFuZy9hbm5vdGF0aW9uL1JldGVudGlv"
|
||||
"blBvbGljeTsAEU15QW5ub3RhdGlvbi5qYXZhAAdSVU5USU1FAAFWAANWTEwAAWEABWFOYW1lAARu"
|
||||
"YW1lAAV2YWx1ZQABAAcOAAICAAAHDgABBQERGwABAQEQFw8AAAIAAICABIQDAQmcAwAAAAECgQgA"
|
||||
"AAARAAAAAAAAAAEAAAAAAAAAAQAAABIAAABwAAAAAgAAAAgAAAC4AAAAAwAAAAMAAADYAAAABAAA"
|
||||
"AAEAAAD8AAAABQAAAAQAAAAEAQAABgAAAAIAAAAkAQAAAhAAAAEAAABkAQAAAxAAAAMAAABwAQAA"
|
||||
"ASAAAAIAAACEAQAABiAAAAIAAACwAQAAARAAAAIAAADYAQAAAiAAABIAAADoAQAAAyAAAAIAAADw"
|
||||
"AgAABCAAAAIAAAD8AgAAACAAAAIAAAAIAwAAABAAAAEAAAAgAwAA";
|
||||
|
||||
// Dex file with shared empty class data item for multiple class defs.
|
||||
// Constructing by building a dex file with multiple classes and hex editing.
|
||||
static const char kMultiClassDataInputDex[] =
|
||||
"ZGV4CjAzNQALJgF9TtnLq748xVe/+wyxETrT9lTEiW6YAQAAcAAAAHhWNBIAAAAAAAAAADQBAAAI"
|
||||
"AAAAcAAAAAQAAACQAAAAAAAAAAAAAAACAAAAoAAAAAAAAAAAAAAAAgAAALAAAACoAAAA8AAAAPAA"
|
||||
"AAD4AAAAAAEAAAMBAAAIAQAADQEAACEBAAAkAQAAAgAAAAMAAAAEAAAABQAAAAEAAAAGAAAAAgAA"
|
||||
"AAcAAAABAAAAAQYAAAMAAAAAAAAAAAAAAAAAAAAnAQAAAAAAAAIAAAABBgAAAwAAAAAAAAABAAAA"
|
||||
"AAAAACcBAAAAAAAABkEuamF2YQAGQi5qYXZhAAFJAANMQTsAA0xCOwASTGphdmEvbGFuZy9PYmpl"
|
||||
"Y3Q7AAFhAAFiAAAAAAABAAAAARkAAAAIAAAAAAAAAAEAAAAAAAAAAQAAAAgAAABwAAAAAgAAAAQA"
|
||||
"AACQAAAABAAAAAIAAACgAAAABgAAAAIAAACwAAAAAiAAAAgAAADwAAAAACAAAAIAAAAnAQAAABAA"
|
||||
"AAEAAAA0AQAA";
|
||||
|
||||
// Dex file with code info followed by non 4-byte aligned section.
|
||||
// Constructed a dex file with code info followed by string data and hex edited.
|
||||
static const char kUnalignedCodeInfoInputDex[] =
|
||||
"ZGV4CjAzNQDXJzXNb4iWn2SLhmLydW/8h1K9moERIw7UAQAAcAAAAHhWNBIAAAAAAAAAAEwBAAAG"
|
||||
"AAAAcAAAAAMAAACIAAAAAQAAAJQAAAAAAAAAAAAAAAMAAACgAAAAAQAAALgAAAD8AAAA2AAAAAIB"
|
||||
"AAAKAQAAEgEAABcBAAArAQAALgEAAAIAAAADAAAABAAAAAQAAAACAAAAAAAAAAAAAAAAAAAAAAAA"
|
||||
"AAUAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAABAAAAAAAAADsBAAAAAAAAAQABAAEAAAAxAQAA"
|
||||
"BAAAAHAQAgAAAA4AAQABAAAAAAA2AQAAAQAAAA4ABjxpbml0PgAGQS5qYXZhAANMQTsAEkxqYXZh"
|
||||
"L2xhbmcvT2JqZWN0OwABVgABYQABAAcOAAMABw4AAAABAQCBgATYAQEB8AEAAAALAAAAAAAAAAEA"
|
||||
"AAAAAAAAAQAAAAYAAABwAAAAAgAAAAMAAACIAAAAAwAAAAEAAACUAAAABQAAAAMAAACgAAAABgAA"
|
||||
"AAEAAAC4AAAAASAAAAIAAADYAAAAAiAAAAYAAAACAQAAAyAAAAIAAAAxAQAAACAAAAEAAAA7AQAA"
|
||||
"ABAAAAEAAABMAQAA";
|
||||
|
||||
// Dex file with class data section preceding code items.
|
||||
// Constructed by passing dex file through dexmerger tool and hex editing.
|
||||
static const char kClassDataBeforeCodeInputDex[] =
|
||||
"ZGV4CjAzNQCZKmCu3XXn4zvxCh5VH0gZNNobEAcsc49EAgAAcAAAAHhWNBIAAAAAAAAAAAQBAAAJ"
|
||||
"AAAAcAAAAAQAAACUAAAAAgAAAKQAAAAAAAAAAAAAAAUAAAC8AAAAAQAAAOQAAABAAQAABAEAAPgB"
|
||||
"AAAAAgAACAIAAAsCAAAQAgAAJAIAACcCAAAqAgAALQIAAAIAAAADAAAABAAAAAUAAAACAAAAAAAA"
|
||||
"AAAAAAAFAAAAAwAAAAAAAAABAAEAAAAAAAEAAAAGAAAAAQAAAAcAAAABAAAACAAAAAIAAQAAAAAA"
|
||||
"AQAAAAEAAAACAAAAAAAAAAEAAAAAAAAAjAEAAAAAAAALAAAAAAAAAAEAAAAAAAAAAQAAAAkAAABw"
|
||||
"AAAAAgAAAAQAAACUAAAAAwAAAAIAAACkAAAABQAAAAUAAAC8AAAABgAAAAEAAADkAAAAABAAAAEA"
|
||||
"AAAEAQAAACAAAAEAAACMAQAAASAAAAQAAACkAQAAAiAAAAkAAAD4AQAAAyAAAAQAAAAwAgAAAAAB"
|
||||
"AwCBgASkAwEBvAMBAdADAQHkAwAAAQABAAEAAAAwAgAABAAAAHAQBAAAAA4AAgABAAAAAAA1AgAA"
|
||||
"AgAAABIQDwACAAEAAAAAADoCAAACAAAAEiAPAAIAAQAAAAAAPwIAAAIAAAASMA8ABjxpbml0PgAG"
|
||||
"QS5qYXZhAAFJAANMQTsAEkxqYXZhL2xhbmcvT2JqZWN0OwABVgABYQABYgABYwABAAcOAAMABw4A"
|
||||
"BgAHDgAJAAcOAA==";
|
||||
|
||||
// Dex file with local info containing a null type descriptor.
|
||||
// Constructed a dex file with debug info sequence containing DBG_RESTART_LOCAL without any
|
||||
// DBG_START_LOCAL to give it a declared type.
|
||||
static const char kUnknownTypeDebugInfoInputDex[] =
|
||||
"ZGV4CjAzNQBtKqZfzjHLNSNwW2A6Bz9FuCEX0sL+FF38AQAAcAAAAHhWNBIAAAAAAAAAAHQBAAAI"
|
||||
"AAAAcAAAAAQAAACQAAAAAgAAAKAAAAAAAAAAAAAAAAMAAAC4AAAAAQAAANAAAAAMAQAA8AAAABwB"
|
||||
"AAAkAQAALAEAAC8BAAA0AQAASAEAAEsBAABOAQAAAgAAAAMAAAAEAAAABQAAAAIAAAAAAAAAAAAA"
|
||||
"AAUAAAADAAAAAAAAAAEAAQAAAAAAAQAAAAYAAAACAAEAAAAAAAEAAAABAAAAAgAAAAAAAAABAAAA"
|
||||
"AAAAAGMBAAAAAAAAAQABAAEAAABUAQAABAAAAHAQAgAAAA4AAgABAAAAAABZAQAAAgAAABIQDwAG"
|
||||
"PGluaXQ+AAZBLmphdmEAAUkAA0xBOwASTGphdmEvbGFuZy9PYmplY3Q7AAFWAAFhAAR0aGlzAAEA"
|
||||
"Bw4AAwAHDh4GAAYAAAAAAQEAgYAE8AEBAYgCAAAACwAAAAAAAAABAAAAAAAAAAEAAAAIAAAAcAAA"
|
||||
"AAIAAAAEAAAAkAAAAAMAAAACAAAAoAAAAAUAAAADAAAAuAAAAAYAAAABAAAA0AAAAAEgAAACAAAA"
|
||||
"8AAAAAIgAAAIAAAAHAEAAAMgAAACAAAAVAEAAAAgAAABAAAAYwEAAAAQAAABAAAAdAEAAA==";
|
||||
|
||||
// Dex file with multiple class data items pointing to the same code item.
|
||||
// Constructed by hex editing.
|
||||
static const char kDuplicateCodeItemInputDex[] =
|
||||
"ZGV4CjAzNQCwKtVglQOmLWuHwldN5jkBOInC7mTMhJMAAgAAcAAAAHhWNBIAAAAAAAAAAHgBAAAH"
|
||||
"AAAAcAAAAAMAAACMAAAAAQAAAJgAAAAAAAAAAAAAAAQAAACkAAAAAQAAAMQAAAAcAQAA5AAAACQB"
|
||||
"AAAsAQAANAEAADkBAABNAQAAUAEAAFMBAAACAAAAAwAAAAQAAAAEAAAAAgAAAAAAAAAAAAAAAAAA"
|
||||
"AAAAAAAFAAAAAAAAAAYAAAABAAAAAAAAAAAAAAABAAAAAQAAAAAAAAABAAAAAAAAAGUBAAAAAAAA"
|
||||
"AQABAAEAAABWAQAABAAAAHAQAwAAAA4AAQABAAAAAABbAQAAAQAAAA4AAAABAAEAAAAAAGABAAAB"
|
||||
"AAAADgAAAAY8aW5pdD4ABkEuamF2YQADTEE7ABJMamF2YS9sYW5nL09iamVjdDsAAVYAAWEAAWIA"
|
||||
"AQAHDgADAAcOAAUABw4AAAABAgCBgATkAQEA/AEBAPwBAAsAAAAAAAAAAQAAAAAAAAABAAAABwAA"
|
||||
"AHAAAAACAAAAAwAAAIwAAAADAAAAAQAAAJgAAAAFAAAABAAAAKQAAAAGAAAAAQAAAMQAAAABIAAA"
|
||||
"AwAAAOQAAAACIAAABwAAACQBAAADIAAAAwAAAFYBAAAAIAAAAQAAAGUBAAAAEAAAAQAAAHgBAAA=";
|
||||
|
||||
static void WriteBase64ToFile(const char* base64, File* file) {
|
||||
// Decode base64.
|
||||
CHECK(base64 != nullptr);
|
||||
size_t length;
|
||||
std::unique_ptr<uint8_t[]> bytes(DecodeBase64(base64, &length));
|
||||
CHECK(bytes != nullptr);
|
||||
if (!file->WriteFully(bytes.get(), length)) {
|
||||
PLOG(FATAL) << "Failed to write base64 as file";
|
||||
}
|
||||
}
|
||||
|
||||
static void WriteFileBase64(const char* base64, const char* location) {
|
||||
// Write to provided file.
|
||||
std::unique_ptr<File> file(OS::CreateEmptyFile(location));
|
||||
CHECK(file != nullptr);
|
||||
WriteBase64ToFile(base64, file.get());
|
||||
if (file->FlushCloseOrErase() != 0) {
|
||||
PLOG(FATAL) << "Could not flush and close test file.";
|
||||
}
|
||||
}
|
||||
|
||||
class DexLayoutTest : public CommonRuntimeTest {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
CommonRuntimeTest::SetUp();
|
||||
}
|
||||
|
||||
// Runs FullPlainOutput test.
|
||||
bool FullPlainOutputExec(std::string* error_msg) {
|
||||
// TODO: dexdump2 -> dexdump ?
|
||||
ScratchFile dexdump_output;
|
||||
const std::string& dexdump_filename = dexdump_output.GetFilename();
|
||||
std::string dexdump = GetTestAndroidRoot() + "/bin/dexdump2";
|
||||
EXPECT_TRUE(OS::FileExists(dexdump.c_str())) << dexdump << " should be a valid file path";
|
||||
|
||||
ScratchFile dexlayout_output;
|
||||
const std::string& dexlayout_filename = dexlayout_output.GetFilename();
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
|
||||
for (const std::string &dex_file : GetLibCoreDexFileNames()) {
|
||||
std::vector<std::string> dexdump_exec_argv =
|
||||
{ dexdump, "-d", "-f", "-h", "-l", "plain", "-o", dexdump_filename, dex_file };
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-d", "-f", "-h", "-l", "plain", "-o", dexlayout_filename, dex_file };
|
||||
if (!::art::Exec(dexdump_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
if (!::art::Exec(dexlayout_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> diff_exec_argv =
|
||||
{ "/usr/bin/diff", dexdump_filename, dexlayout_filename };
|
||||
if (!::art::Exec(diff_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Runs DexFileOutput test.
|
||||
bool DexFileOutputExec(std::string* error_msg) {
|
||||
ScratchFile tmp_file;
|
||||
const std::string& tmp_name = tmp_file.GetFilename();
|
||||
size_t tmp_last_slash = tmp_name.rfind('/');
|
||||
std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
|
||||
for (const std::string &dex_file : GetLibCoreDexFileNames()) {
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-w", tmp_dir, "-o", tmp_name, dex_file };
|
||||
if (!::art::Exec(dexlayout_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
size_t dex_file_last_slash = dex_file.rfind("/");
|
||||
std::string dex_file_name = dex_file.substr(dex_file_last_slash + 1);
|
||||
std::vector<std::string> unzip_exec_argv =
|
||||
{ "/usr/bin/unzip", dex_file, "classes.dex", "-d", tmp_dir};
|
||||
if (!::art::Exec(unzip_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> diff_exec_argv =
|
||||
{ "/usr/bin/diff", tmp_dir + "classes.dex" , tmp_dir + dex_file_name };
|
||||
if (!::art::Exec(diff_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> rm_zip_exec_argv = { "/bin/rm", tmp_dir + "classes.dex" };
|
||||
if (!::art::Exec(rm_zip_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
std::vector<std::string> rm_out_exec_argv = { "/bin/rm", tmp_dir + dex_file_name };
|
||||
if (!::art::Exec(rm_out_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Create a profile with some subset of methods and classes.
|
||||
void CreateProfile(const std::string& input_dex,
|
||||
const std::string& out_profile,
|
||||
const std::string& dex_location) {
|
||||
std::vector<std::unique_ptr<const DexFile>> dex_files;
|
||||
std::string error_msg;
|
||||
bool result = DexFile::Open(input_dex.c_str(),
|
||||
input_dex,
|
||||
false,
|
||||
&error_msg,
|
||||
&dex_files);
|
||||
|
||||
ASSERT_TRUE(result) << error_msg;
|
||||
ASSERT_GE(dex_files.size(), 1u);
|
||||
|
||||
size_t profile_methods = 0;
|
||||
size_t profile_classes = 0;
|
||||
ProfileCompilationInfo pfi;
|
||||
std::set<DexCacheResolvedClasses> classes;
|
||||
for (const std::unique_ptr<const DexFile>& dex_file : dex_files) {
|
||||
for (uint32_t i = 0; i < dex_file->NumMethodIds(); i += 2) {
|
||||
uint8_t flags = 0u;
|
||||
|
||||
if ((i & 3) != 0) {
|
||||
flags |= ProfileCompilationInfo::MethodHotness::kFlagHot;
|
||||
++profile_methods;
|
||||
} else if ((i & 2) != 0) {
|
||||
flags |= ProfileCompilationInfo::MethodHotness::kFlagStartup;
|
||||
++profile_methods;
|
||||
}
|
||||
pfi.AddMethodIndex(static_cast<ProfileCompilationInfo::MethodHotness::Flag>(flags),
|
||||
dex_location,
|
||||
dex_file->GetLocationChecksum(),
|
||||
/*dex_method_idx*/i,
|
||||
dex_file->NumMethodIds());
|
||||
}
|
||||
DexCacheResolvedClasses cur_classes(dex_location,
|
||||
dex_location,
|
||||
dex_file->GetLocationChecksum(),
|
||||
dex_file->NumMethodIds());
|
||||
// Add every even class too.
|
||||
for (uint32_t i = 0; i < dex_file->NumClassDefs(); i += 1) {
|
||||
if ((i & 2) == 0) {
|
||||
cur_classes.AddClass(dex_file->GetClassDef(i).class_idx_);
|
||||
++profile_classes;
|
||||
}
|
||||
}
|
||||
classes.insert(cur_classes);
|
||||
}
|
||||
pfi.AddClasses(classes);
|
||||
// Write to provided file.
|
||||
std::unique_ptr<File> file(OS::CreateEmptyFile(out_profile.c_str()));
|
||||
ASSERT_TRUE(file != nullptr);
|
||||
pfi.Save(file->Fd());
|
||||
if (file->FlushCloseOrErase() != 0) {
|
||||
PLOG(FATAL) << "Could not flush and close test file.";
|
||||
}
|
||||
EXPECT_GE(profile_methods, 0u);
|
||||
EXPECT_GE(profile_classes, 0u);
|
||||
}
|
||||
|
||||
// Runs DexFileLayout test.
|
||||
bool DexFileLayoutExec(std::string* error_msg) {
|
||||
ScratchFile tmp_file;
|
||||
std::string tmp_name = tmp_file.GetFilename();
|
||||
size_t tmp_last_slash = tmp_name.rfind("/");
|
||||
std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
|
||||
|
||||
// Write inputs and expected outputs.
|
||||
std::string dex_file = tmp_dir + "classes.dex";
|
||||
WriteFileBase64(kDexFileLayoutInputDex, dex_file.c_str());
|
||||
std::string profile_file = tmp_dir + "primary.prof";
|
||||
CreateProfile(dex_file, profile_file, dex_file);
|
||||
// WriteFileBase64(kDexFileLayoutInputProfile, profile_file.c_str());
|
||||
std::string output_dex = tmp_dir + "classes.dex.new";
|
||||
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file };
|
||||
if (!::art::Exec(dexlayout_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// -v makes sure that the layout did not corrupt the dex file.
|
||||
|
||||
std::vector<std::string> rm_exec_argv =
|
||||
{ "/bin/rm", dex_file, profile_file, output_dex };
|
||||
if (!::art::Exec(rm_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Runs DexFileLayout test twice (second time is run on output of first time)
|
||||
// for behavior consistency.
|
||||
bool DexFileLayoutFixedPointExec(std::string* error_msg) {
|
||||
ScratchFile tmp_file;
|
||||
std::string tmp_name = tmp_file.GetFilename();
|
||||
size_t tmp_last_slash = tmp_name.rfind("/");
|
||||
std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
|
||||
|
||||
// Unzip the test dex file to the classes.dex destination. It is required to unzip since
|
||||
// opening from jar recalculates the dex location checksum.
|
||||
std::string dex_file = tmp_dir + "classes.dex";
|
||||
|
||||
std::vector<std::string> unzip_args = {
|
||||
"/usr/bin/unzip",
|
||||
GetTestDexFileName("ManyMethods"),
|
||||
"classes.dex",
|
||||
"-d",
|
||||
tmp_dir,
|
||||
};
|
||||
if (!art::Exec(unzip_args, error_msg)) {
|
||||
LOG(ERROR) << "Failed to unzip dex";
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string profile_file = tmp_dir + "primary.prof";
|
||||
CreateProfile(dex_file, profile_file, dex_file);
|
||||
std::string output_dex = tmp_dir + "classes.dex.new";
|
||||
std::string second_output_dex = tmp_dir + "classes.dex.new.new";
|
||||
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
|
||||
// -v makes sure that the layout did not corrupt the dex file.
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, dex_file };
|
||||
if (!::art::Exec(dexlayout_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Recreate the profile with the new dex location. This is required so that the profile dex
|
||||
// location matches.
|
||||
CreateProfile(dex_file, profile_file, output_dex);
|
||||
|
||||
// -v makes sure that the layout did not corrupt the dex file.
|
||||
// -i since the checksum won't match from the first layout.
|
||||
std::vector<std::string> second_dexlayout_exec_argv =
|
||||
{ dexlayout, "-i", "-v", "-w", tmp_dir, "-o", tmp_name, "-p", profile_file, output_dex };
|
||||
if (!::art::Exec(second_dexlayout_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool diff_result = true;
|
||||
std::vector<std::string> diff_exec_argv =
|
||||
{ "/usr/bin/diff", output_dex, second_output_dex };
|
||||
if (!::art::Exec(diff_exec_argv, error_msg)) {
|
||||
diff_result = false;
|
||||
}
|
||||
|
||||
std::vector<std::string> rm_exec_argv =
|
||||
{ "/bin/rm", dex_file, profile_file, output_dex, second_output_dex };
|
||||
if (!::art::Exec(rm_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return diff_result;
|
||||
}
|
||||
|
||||
// Runs UnreferencedCatchHandlerTest & Unreferenced0SizeCatchHandlerTest.
|
||||
bool UnreferencedCatchHandlerExec(std::string* error_msg, const char* filename) {
|
||||
ScratchFile tmp_file;
|
||||
std::string tmp_name = tmp_file.GetFilename();
|
||||
size_t tmp_last_slash = tmp_name.rfind("/");
|
||||
std::string tmp_dir = tmp_name.substr(0, tmp_last_slash + 1);
|
||||
|
||||
// Write inputs and expected outputs.
|
||||
std::string input_dex = tmp_dir + "classes.dex";
|
||||
WriteFileBase64(filename, input_dex.c_str());
|
||||
std::string output_dex = tmp_dir + "classes.dex.new";
|
||||
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-w", tmp_dir, "-o", "/dev/null", input_dex };
|
||||
if (!::art::Exec(dexlayout_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Diff input and output. They should be the same.
|
||||
std::vector<std::string> diff_exec_argv = { "/usr/bin/diff", input_dex, output_dex };
|
||||
if (!::art::Exec(diff_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> rm_exec_argv = { "/bin/rm", input_dex, output_dex };
|
||||
if (!::art::Exec(rm_exec_argv, error_msg)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DexLayoutExec(ScratchFile* dex_file,
|
||||
const char* dex_filename,
|
||||
ScratchFile* profile_file,
|
||||
std::vector<std::string>& dexlayout_exec_argv) {
|
||||
WriteBase64ToFile(dex_filename, dex_file->GetFile());
|
||||
EXPECT_EQ(dex_file->GetFile()->Flush(), 0);
|
||||
if (profile_file != nullptr) {
|
||||
CreateProfile(dex_file->GetFilename(), profile_file->GetFilename(), dex_file->GetFilename());
|
||||
}
|
||||
std::string error_msg;
|
||||
const bool result = ::art::Exec(dexlayout_exec_argv, &error_msg);
|
||||
if (!result) {
|
||||
LOG(ERROR) << "Error: " << error_msg;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
TEST_F(DexLayoutTest, FullPlainOutput) {
|
||||
// Disable test on target.
|
||||
TEST_DISABLED_FOR_TARGET();
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(FullPlainOutputExec(&error_msg)) << error_msg;
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, DexFileOutput) {
|
||||
// Disable test on target.
|
||||
TEST_DISABLED_FOR_TARGET();
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(DexFileOutputExec(&error_msg)) << error_msg;
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, DexFileLayout) {
|
||||
// Disable test on target.
|
||||
TEST_DISABLED_FOR_TARGET();
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(DexFileLayoutExec(&error_msg)) << error_msg;
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, DexFileLayoutFixedPoint) {
|
||||
// Disable test on target.
|
||||
TEST_DISABLED_FOR_TARGET();
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(DexFileLayoutFixedPointExec(&error_msg)) << error_msg;
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, UnreferencedCatchHandler) {
|
||||
// Disable test on target.
|
||||
TEST_DISABLED_FOR_TARGET();
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(UnreferencedCatchHandlerExec(&error_msg,
|
||||
kUnreferencedCatchHandlerInputDex)) << error_msg;
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, Unreferenced0SizeCatchHandler) {
|
||||
// Disable test on target.
|
||||
TEST_DISABLED_FOR_TARGET();
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(UnreferencedCatchHandlerExec(&error_msg,
|
||||
kUnreferenced0SizeCatchHandlerInputDex)) << error_msg;
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, UnreferencedEndingCatchHandler) {
|
||||
// Disable test on target.
|
||||
TEST_DISABLED_FOR_TARGET();
|
||||
std::string error_msg;
|
||||
ASSERT_TRUE(UnreferencedCatchHandlerExec(&error_msg,
|
||||
kUnreferencedEndingCatchHandlerInputDex)) << error_msg;
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, DuplicateOffset) {
|
||||
ScratchFile temp_dex;
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-a", "-i", "-o", "/dev/null", temp_dex.GetFilename() };
|
||||
ASSERT_TRUE(DexLayoutExec(&temp_dex,
|
||||
kDexFileDuplicateOffset,
|
||||
nullptr /* profile_file */,
|
||||
dexlayout_exec_argv));
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, NullSetRefListElement) {
|
||||
ScratchFile temp_dex;
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-o", "/dev/null", temp_dex.GetFilename() };
|
||||
ASSERT_TRUE(DexLayoutExec(&temp_dex,
|
||||
kNullSetRefListElementInputDex,
|
||||
nullptr /* profile_file */,
|
||||
dexlayout_exec_argv));
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, MultiClassData) {
|
||||
ScratchFile temp_dex;
|
||||
ScratchFile temp_profile;
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() };
|
||||
ASSERT_TRUE(DexLayoutExec(&temp_dex,
|
||||
kMultiClassDataInputDex,
|
||||
&temp_profile,
|
||||
dexlayout_exec_argv));
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, UnalignedCodeInfo) {
|
||||
ScratchFile temp_dex;
|
||||
ScratchFile temp_profile;
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() };
|
||||
ASSERT_TRUE(DexLayoutExec(&temp_dex,
|
||||
kUnalignedCodeInfoInputDex,
|
||||
&temp_profile,
|
||||
dexlayout_exec_argv));
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, ClassDataBeforeCode) {
|
||||
ScratchFile temp_dex;
|
||||
ScratchFile temp_profile;
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-p", temp_profile.GetFilename(), "-o", "/dev/null", temp_dex.GetFilename() };
|
||||
ASSERT_TRUE(DexLayoutExec(&temp_dex,
|
||||
kClassDataBeforeCodeInputDex,
|
||||
&temp_profile,
|
||||
dexlayout_exec_argv));
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, UnknownTypeDebugInfo) {
|
||||
ScratchFile temp_dex;
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-o", "/dev/null", temp_dex.GetFilename() };
|
||||
ASSERT_TRUE(DexLayoutExec(&temp_dex,
|
||||
kUnknownTypeDebugInfoInputDex,
|
||||
nullptr /* profile_file */,
|
||||
dexlayout_exec_argv));
|
||||
}
|
||||
|
||||
TEST_F(DexLayoutTest, DuplicateCodeItem) {
|
||||
ScratchFile temp_dex;
|
||||
std::string dexlayout = GetTestAndroidRoot() + "/bin/dexlayout";
|
||||
EXPECT_TRUE(OS::FileExists(dexlayout.c_str())) << dexlayout << " should be a valid file path";
|
||||
std::vector<std::string> dexlayout_exec_argv =
|
||||
{ dexlayout, "-o", "/dev/null", temp_dex.GetFilename() };
|
||||
ASSERT_TRUE(DexLayoutExec(&temp_dex,
|
||||
kDuplicateCodeItemInputDex,
|
||||
nullptr /* profile_file */,
|
||||
dexlayout_exec_argv));
|
||||
}
|
||||
|
||||
} // namespace art
|
Loading…
Add table
Add a link
Reference in a new issue