Merge "Support compiling run-tests with jack"
diff --git a/Android.mk b/Android.mk
index a050c58..c01464a 100644
--- a/Android.mk
+++ b/Android.mk
@@ -79,6 +79,7 @@
include $(art_path)/runtime/Android.mk
include $(art_path)/compiler/Android.mk
include $(art_path)/dexdump/Android.mk
+include $(art_path)/dexlist/Android.mk
include $(art_path)/dex2oat/Android.mk
include $(art_path)/disassembler/Android.mk
include $(art_path)/oatdump/Android.mk
@@ -237,10 +238,10 @@
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
endif
-# Dexdump regression test.
+# Dexdump/list regression test.
.PHONY: test-art-host-dexdump
-test-art-host-dexdump: dexdump2
- art/test/dexdump/run-all-tests
+test-art-host-dexdump: $(addprefix $(HOST_OUT_EXECUTABLES)/, dexdump2 dexlist2)
+ ANDROID_HOST_OUT=$(realpath $(HOST_OUT)) art/test/dexdump/run-all-tests
# Valgrind. Currently only 32b gtests.
.PHONY: valgrind-test-art-host
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 273ae0e..0958c64 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -104,6 +104,17 @@
$(TARGET_CORE_IMAGE_default_no-pic_32) \
dexdump2
+# The dexlist test requires an image and the dexlist utility.
+# TODO: rename into dexlist when migration completes
+ART_GTEST_dexlist_test_HOST_DEPS := \
+ $(HOST_CORE_IMAGE_default_no-pic_64) \
+ $(HOST_CORE_IMAGE_default_no-pic_32) \
+ $(HOST_OUT_EXECUTABLES)/dexlist2
+ART_GTEST_dexlist_test_TARGET_DEPS := \
+ $(TARGET_CORE_IMAGE_default_no-pic_64) \
+ $(TARGET_CORE_IMAGE_default_no-pic_32) \
+ dexlist2
+
# The imgdiag test has dependencies on core.oat since it needs to load it during the test.
# For the host, also add the installed tool (in the base size, that should suffice). For the
# target, just the module is fine, the sync will happen late enough.
@@ -132,6 +143,7 @@
RUNTIME_GTEST_COMMON_SRC_FILES := \
cmdline/cmdline_parser_test.cc \
dexdump/dexdump_test.cc \
+ dexlist/dexlist_test.cc \
imgdiag/imgdiag_test.cc \
oatdump/oatdump_test.cc \
runtime/arch/arch_test.cc \
diff --git a/compiler/dex/quick/x86/int_x86.cc b/compiler/dex/quick/x86/int_x86.cc
index d993d93..d1fe167 100755
--- a/compiler/dex/quick/x86/int_x86.cc
+++ b/compiler/dex/quick/x86/int_x86.cc
@@ -1336,9 +1336,24 @@
}
OpRegReg(kOpRev, rl_result.reg.GetLow(), rl_i.reg.GetHigh());
OpRegReg(kOpRev, rl_result.reg.GetHigh(), r_i_low);
+ // Free up at least one input register if it was a temp. Otherwise we may be in the bad
+ // situation of not having a temp available for SwapBits. Make sure it's not overlapping
+ // with the output, though.
if (rl_i.reg.GetLowReg() == rl_result.reg.GetLowReg()) {
+ // There's definitely a free temp after this.
FreeTemp(r_i_low);
+ } else {
+ // We opportunistically release both here. That saves duplication of the register state
+ // lookup (to see if it's actually a temp).
+ if (rl_i.reg.GetLowReg() != rl_result.reg.GetHighReg()) {
+ FreeTemp(rl_i.reg.GetLow());
+ }
+ if (rl_i.reg.GetHighReg() != rl_result.reg.GetLowReg() &&
+ rl_i.reg.GetHighReg() != rl_result.reg.GetHighReg()) {
+ FreeTemp(rl_i.reg.GetHigh());
+ }
}
+
SwapBits(rl_result.reg.GetLow(), 1, 0x55555555);
SwapBits(rl_result.reg.GetLow(), 2, 0x33333333);
SwapBits(rl_result.reg.GetLow(), 4, 0x0f0f0f0f);
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 8551382..bb1b40a 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -259,14 +259,20 @@
return false;
}
-bool HGraphBuilder::IsBlockInPcRange(HBasicBlock* block,
- uint32_t dex_pc_start,
- uint32_t dex_pc_end) {
- uint32_t dex_pc = block->GetDexPc();
- return block != entry_block_
- && block != exit_block_
- && dex_pc >= dex_pc_start
- && dex_pc < dex_pc_end;
+static const DexFile::TryItem* GetTryItem(HBasicBlock* block,
+ const DexFile::CodeItem& code_item,
+ const ArenaBitVector& can_block_throw) {
+ DCHECK(!block->IsSingleTryBoundary());
+
+ // Block does not contain throwing instructions. Even if it is covered by
+ // a TryItem, we will consider it not in a try block.
+ if (!can_block_throw.IsBitSet(block->GetBlockId())) {
+ return nullptr;
+ }
+
+ // Instructions in the block may throw. Find a TryItem covering this block.
+ int32_t try_item_idx = DexFile::FindTryItem(code_item, block->GetDexPc());
+ return (try_item_idx == -1) ? nullptr : DexFile::GetTryItems(code_item, try_item_idx);
}
void HGraphBuilder::CreateBlocksForTryCatch(const DexFile::CodeItem& code_item) {
@@ -327,30 +333,37 @@
return;
}
+ const size_t num_blocks = graph_->GetBlocks().Size();
+ ArenaBitVector can_block_throw(arena_, num_blocks, false);
+
+ // Scan blocks and mark those which contain throwing instructions.
+ for (size_t block_id = 0; block_id < num_blocks; ++block_id) {
+ HBasicBlock* block = graph_->GetBlocks().Get(block_id);
+ for (HInstructionIterator insn(block->GetInstructions()); !insn.Done(); insn.Advance()) {
+ if (insn.Current()->CanThrow()) {
+ can_block_throw.SetBit(block_id);
+ break;
+ }
+ }
+ }
+
// Iterate over all blocks, find those covered by some TryItem and:
// (a) split edges which enter/exit the try range,
// (b) create TryBoundary instructions in the new blocks,
// (c) link the new blocks to corresponding exception handlers.
// We cannot iterate only over blocks in `branch_targets_` because switch-case
// blocks share the same dex_pc.
- for (size_t block_id = 1, e = graph_->GetBlocks().Size(); block_id < e; ++block_id) {
+ for (size_t block_id = 0; block_id < num_blocks; ++block_id) {
HBasicBlock* try_block = graph_->GetBlocks().Get(block_id);
- // Iteration starts from 1 to skip the entry block.
- DCHECK_NE(try_block, entry_block_);
- // Exit block has not yet been added to the graph at this point.
- DCHECK_NE(try_block, exit_block_);
// TryBoundary blocks are added at the end of the list and not iterated over.
DCHECK(!try_block->IsSingleTryBoundary());
// Find the TryItem for this block.
- int32_t try_item_idx = DexFile::FindTryItem(code_item, try_block->GetDexPc());
- if (try_item_idx == -1) {
+ const DexFile::TryItem* try_item = GetTryItem(try_block, code_item, can_block_throw);
+ if (try_item == nullptr) {
continue;
}
- const DexFile::TryItem& try_item = *DexFile::GetTryItems(code_item, try_item_idx);
- uint32_t try_start = try_item.start_addr_;
- uint32_t try_end = try_start + try_item.insn_count_;
if (try_block->IsCatchBlock()) {
// Catch blocks are always considered an entry point into the TryItem in
@@ -373,7 +386,7 @@
DCHECK(split_position != nullptr);
HBasicBlock* catch_block = try_block;
try_block = catch_block->SplitBefore(split_position);
- SplitTryBoundaryEdge(catch_block, try_block, HTryBoundary::kEntry, code_item, try_item);
+ SplitTryBoundaryEdge(catch_block, try_block, HTryBoundary::kEntry, code_item, *try_item);
} else {
// For non-catch blocks, find predecessors which are not covered by the
// same TryItem range. Such edges enter the try block and will have
@@ -385,12 +398,14 @@
// TryItem. We split it again and insert an entry point.
if (kIsDebugBuild) {
HTryBoundary* last_insn = predecessor->GetLastInstruction()->AsTryBoundary();
+ const DexFile::TryItem* predecessor_try_item =
+ GetTryItem(predecessor->GetSinglePredecessor(), code_item, can_block_throw);
DCHECK(!last_insn->IsEntry());
DCHECK_EQ(last_insn->GetNormalFlowSuccessor(), try_block);
DCHECK(try_block->IsFirstIndexOfPredecessor(predecessor, i));
- DCHECK(!IsBlockInPcRange(predecessor->GetSinglePredecessor(), try_start, try_end));
+ DCHECK_NE(try_item, predecessor_try_item);
}
- } else if (!IsBlockInPcRange(predecessor, try_start, try_end)) {
+ } else if (GetTryItem(predecessor, code_item, can_block_throw) != try_item) {
// This is an entry point into the TryItem and the edge has not been
// split yet. That means that `predecessor` is not in a TryItem, or
// it is in a different TryItem and we happened to iterate over this
@@ -399,7 +414,7 @@
// Not an edge on the boundary of the try block.
continue;
}
- SplitTryBoundaryEdge(predecessor, try_block, HTryBoundary::kEntry, code_item, try_item);
+ SplitTryBoundaryEdge(predecessor, try_block, HTryBoundary::kEntry, code_item, *try_item);
}
}
@@ -416,11 +431,13 @@
// TryItem. We split it again and insert an exit.
if (kIsDebugBuild) {
HTryBoundary* last_insn = successor->GetLastInstruction()->AsTryBoundary();
+ const DexFile::TryItem* successor_try_item =
+ GetTryItem(last_insn->GetNormalFlowSuccessor(), code_item, can_block_throw);
DCHECK_EQ(try_block, successor->GetSinglePredecessor());
DCHECK(last_insn->IsEntry());
- DCHECK(!IsBlockInPcRange(last_insn->GetNormalFlowSuccessor(), try_start, try_end));
+ DCHECK_NE(try_item, successor_try_item);
}
- } else if (!IsBlockInPcRange(successor, try_start, try_end)) {
+ } else if (GetTryItem(successor, code_item, can_block_throw) != try_item) {
// This is an exit out of the TryItem and the edge has not been split
// yet. That means that either `successor` is not in a TryItem, or it
// is in a different TryItem and we happened to iterate over this
@@ -437,7 +454,7 @@
// Not an edge on the boundary of the try block.
continue;
}
- SplitTryBoundaryEdge(try_block, successor, HTryBoundary::kExit, code_item, try_item);
+ SplitTryBoundaryEdge(try_block, successor, HTryBoundary::kExit, code_item, *try_item);
}
}
}
@@ -496,14 +513,14 @@
// Add the suspend check to the entry block.
entry_block_->AddInstruction(new (arena_) HSuspendCheck(0));
entry_block_->AddInstruction(new (arena_) HGoto());
+ // Add the exit block at the end.
+ graph_->AddBlock(exit_block_);
// Iterate over blocks covered by TryItems and insert TryBoundaries at entry
// and exit points. This requires all control-flow instructions and
// non-exceptional edges to have been created.
InsertTryBoundaryBlocks(code_item);
- // Add the exit block at the end to give it the highest id.
- graph_->AddBlock(exit_block_);
return true;
}
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index e487255..7098eb8 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -98,9 +98,6 @@
HBasicBlock* FindBlockStartingAt(int32_t dex_pc) const;
HBasicBlock* FindOrCreateBlockStartingAt(int32_t dex_pc);
- // Returns whether the dex_pc of `block` lies within the given range.
- bool IsBlockInPcRange(HBasicBlock* block, uint32_t dex_pc_start, uint32_t dex_pc_end);
-
// Adds new blocks to `branch_targets_` starting at the limits of TryItems and
// their exception handlers.
void CreateBlocksForTryCatch(const DexFile::CodeItem& code_item);
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index 504c141..37c060c 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -357,6 +357,10 @@
StartAttributeStream("kind") << barrier->GetBarrierKind();
}
+ void VisitMonitorOperation(HMonitorOperation* monitor) OVERRIDE {
+ StartAttributeStream("kind") << (monitor->IsEnter() ? "enter" : "exit");
+ }
+
void VisitLoadClass(HLoadClass* load_class) OVERRIDE {
StartAttributeStream("gen_clinit_check") << std::boolalpha
<< load_class->MustGenerateClinitCheck() << std::noboolalpha;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index e4a7aa6..59255d1 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -4131,13 +4131,19 @@
};
HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::None()), kind_(kind), dex_pc_(dex_pc) {
+ : HTemplateInstruction(SideEffects::ChangesSomething()), kind_(kind), dex_pc_(dex_pc) {
SetRawInputAt(0, object);
}
// Instruction may throw a Java exception, so we need an environment.
- bool NeedsEnvironment() const OVERRIDE { return true; }
- bool CanThrow() const OVERRIDE { return true; }
+ bool NeedsEnvironment() const OVERRIDE { return CanThrow(); }
+
+ bool CanThrow() const OVERRIDE {
+ // Verifier guarantees that monitor-exit cannot throw.
+ // This is important because it allows the HGraphBuilder to remove
+ // a dead throw-catch loop generated for `synchronized` blocks/methods.
+ return IsEnter();
+ }
uint32_t GetDexPc() const OVERRIDE { return dex_pc_; }
diff --git a/dexlist/Android.mk b/dexlist/Android.mk
new file mode 100755
index 0000000..988fe03
--- /dev/null
+++ b/dexlist/Android.mk
@@ -0,0 +1,54 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# TODO(ajcbik): Art-i-fy this makefile
+
+# TODO(ajcbik): rename dexlist2 into dexlist when Dalvik version is removed
+
+LOCAL_PATH:= $(call my-dir)
+
+dexlist_src_files := dexlist.cc
+dexlist_c_includes := art/runtime
+dexlist_libraries := libart
+
+##
+## Build the device command line tool dexlist.
+##
+
+ifneq ($(SDK_ONLY),true) # SDK_only doesn't need device version
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := cc
+LOCAL_SRC_FILES := $(dexlist_src_files)
+LOCAL_C_INCLUDES := $(dexlist_c_includes)
+LOCAL_CFLAGS += -Wall
+LOCAL_SHARED_LIBRARIES += $(dexlist_libraries)
+LOCAL_MODULE := dexlist2
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+include $(BUILD_EXECUTABLE)
+endif # !SDK_ONLY
+
+##
+## Build the host command line tool dexlist.
+##
+
+include $(CLEAR_VARS)
+LOCAL_CPP_EXTENSION := cc
+LOCAL_SRC_FILES := $(dexlist_src_files)
+LOCAL_C_INCLUDES := $(dexlist_c_includes)
+LOCAL_CFLAGS += -Wall
+LOCAL_SHARED_LIBRARIES += $(dexlist_libraries)
+LOCAL_MODULE := dexlist2
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
new file mode 100644
index 0000000..d7c0e4c
--- /dev/null
+++ b/dexlist/dexlist.cc
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Implementation file of the dexlist utility.
+ *
+ * This is a re-implementation of the original dexlist utility that was
+ * based on Dalvik functions in libdex into a new dexlist that is now
+ * based on Art functions in libart instead. The output is identical to
+ * the original for correct DEX files. Error messages may differ, however.
+ *
+ * List all methods in all concrete classes in one or more DEX files.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "dex_file-inl.h"
+#include "mem_map.h"
+#include "runtime.h"
+
+namespace art {
+
+static const char* gProgName = "dexlist";
+
+/* Command-line options. */
+static struct {
+ char* argCopy;
+ const char* classToFind;
+ const char* methodToFind;
+ const char* outputFileName;
+} gOptions;
+
+/*
+ * Output file. Defaults to stdout.
+ */
+static FILE* gOutFile = stdout;
+
+/*
+ * Data types that match the definitions in the VM specification.
+ */
+typedef uint8_t u1;
+typedef uint16_t u2;
+typedef uint32_t u4;
+typedef uint64_t u8;
+typedef int32_t s4;
+typedef int64_t s8;
+
+/*
+ * Returns a newly-allocated string for the "dot version" of the class
+ * name for the given type descriptor. That is, The initial "L" and
+ * final ";" (if any) have been removed and all occurrences of '/'
+ * have been changed to '.'.
+ */
+static char* descriptorToDot(const char* str) {
+ size_t at = strlen(str);
+ if (str[0] == 'L') {
+ at -= 2; // Two fewer chars to copy.
+ str++;
+ }
+ char* newStr = reinterpret_cast<char*>(malloc(at + 1));
+ newStr[at] = '\0';
+ while (at > 0) {
+ at--;
+ newStr[at] = (str[at] == '/') ? '.' : str[at];
+ }
+ return newStr;
+}
+
+/*
+ * Positions table callback; we just want to catch the number of the
+ * first line in the method, which *should* correspond to the first
+ * entry from the table. (Could also use "min" here.)
+ */
+static bool positionsCb(void* context, u4 /*address*/, u4 lineNum) {
+ int* pFirstLine = reinterpret_cast<int *>(context);
+ if (*pFirstLine == -1) {
+ *pFirstLine = lineNum;
+ }
+ return 0;
+}
+
+/*
+ * Dumps a method.
+ */
+static void dumpMethod(const DexFile* pDexFile,
+ const char* fileName, u4 idx, u4 flags,
+ const DexFile::CodeItem* pCode, u4 codeOffset) {
+ // Abstract and native methods don't get listed.
+ if (pCode == nullptr || codeOffset == 0) {
+ return;
+ }
+
+ // Method information.
+ const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx);
+ const char* methodName = pDexFile->StringDataByIdx(pMethodId.name_idx_);
+ const char* classDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
+ char* className = descriptorToDot(classDescriptor);
+ const u4 insnsOff = codeOffset + 0x10;
+
+ // Don't list methods that do not match a particular query.
+ if (gOptions.methodToFind != nullptr &&
+ (strcmp(gOptions.classToFind, className) != 0 ||
+ strcmp(gOptions.methodToFind, methodName) != 0)) {
+ free(className);
+ return;
+ }
+
+ // If the filename is empty, then set it to something printable.
+ if (fileName == nullptr || fileName[0] == 0) {
+ fileName = "(none)";
+ }
+
+ // Find the first line.
+ int firstLine = -1;
+ bool is_static = (flags & kAccStatic) != 0;
+ pDexFile->DecodeDebugInfo(
+ pCode, is_static, idx, positionsCb, nullptr, &firstLine);
+
+ // Method signature.
+ const Signature signature = pDexFile->GetMethodSignature(pMethodId);
+ char* typeDesc = strdup(signature.ToString().c_str());
+
+ // Dump actual method information.
+ fprintf(gOutFile, "0x%08x %d %s %s %s %s %d\n",
+ insnsOff, pCode->insns_size_in_code_units_ * 2,
+ className, methodName, typeDesc, fileName, firstLine);
+
+ free(typeDesc);
+ free(className);
+}
+
+/*
+ * Runs through all direct and virtual methods in the class.
+ */
+void dumpClass(const DexFile* pDexFile, u4 idx) {
+ const DexFile::ClassDef& pClassDef = pDexFile->GetClassDef(idx);
+
+ const char* fileName;
+ if (pClassDef.source_file_idx_ == DexFile::kDexNoIndex) {
+ fileName = nullptr;
+ } else {
+ fileName = pDexFile->StringDataByIdx(pClassDef.source_file_idx_);
+ }
+
+ const u1* pEncodedData = pDexFile->GetClassData(pClassDef);
+ if (pEncodedData != nullptr) {
+ ClassDataItemIterator pClassData(*pDexFile, pEncodedData);
+ // Skip the fields.
+ for (; pClassData.HasNextStaticField(); pClassData.Next()) {}
+ for (; pClassData.HasNextInstanceField(); pClassData.Next()) {}
+ // Direct methods.
+ for (; pClassData.HasNextDirectMethod(); pClassData.Next()) {
+ dumpMethod(pDexFile, fileName,
+ pClassData.GetMemberIndex(),
+ pClassData.GetRawMemberAccessFlags(),
+ pClassData.GetMethodCodeItem(),
+ pClassData.GetMethodCodeItemOffset());
+ }
+ // Virtual methods.
+ for (; pClassData.HasNextVirtualMethod(); pClassData.Next()) {
+ dumpMethod(pDexFile, fileName,
+ pClassData.GetMemberIndex(),
+ pClassData.GetRawMemberAccessFlags(),
+ pClassData.GetMethodCodeItem(),
+ pClassData.GetMethodCodeItemOffset());
+ }
+ }
+}
+
+/*
+ * Processes a single file (either direct .dex or indirect .zip/.jar/.apk).
+ */
+static int processFile(const char* fileName) {
+ // If the file is not a .dex file, the function tries .zip/.jar/.apk files,
+ // all of which are Zip archives with "classes.dex" inside.
+ std::string error_msg;
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ if (!DexFile::Open(fileName, fileName, &error_msg, &dex_files)) {
+ fputs(error_msg.c_str(), stderr);
+ fputc('\n', stderr);
+ return -1;
+ }
+
+ // Determine if opening file yielded a single dex file.
+ //
+ // TODO(ajcbik): this restriction is not really needed, but kept
+ // for now to stay close to original dexlist; we can
+ // later relax this!
+ //
+ if (dex_files.size() != 1) {
+ fprintf(stderr, "ERROR: DEX parse failed\n");
+ return -1;
+ }
+ const DexFile* pDexFile = dex_files[0].get();
+
+ // Success. Iterate over all classes.
+ fprintf(gOutFile, "#%s\n", fileName);
+ const u4 classDefsSize = pDexFile->GetHeader().class_defs_size_;
+ for (u4 idx = 0; idx < classDefsSize; idx++) {
+ dumpClass(pDexFile, idx);
+ }
+ return 0;
+}
+
+/*
+ * Shows usage.
+ */
+static void usage(void) {
+ fprintf(stderr, "Copyright (C) 2007 The Android Open Source Project\n\n");
+ fprintf(stderr, "%s: [-m p.c.m] [-o outfile] dexfile...\n", gProgName);
+ fprintf(stderr, "\n");
+}
+
+/*
+ * Main driver of the dexlist utility.
+ */
+int dexlistDriver(int argc, char** argv) {
+ // Art specific set up.
+ InitLogging(argv);
+ MemMap::Init();
+
+ // Reset options.
+ bool wantUsage = false;
+ memset(&gOptions, 0, sizeof(gOptions));
+
+ // Parse all arguments.
+ while (1) {
+ const int ic = getopt(argc, argv, "o:m:");
+ if (ic < 0) {
+ break; // done
+ }
+ switch (ic) {
+ case 'o': // output file
+ gOptions.outputFileName = optarg;
+ break;
+ case 'm':
+ // If -m X.Y.Z is given, then find all instances of the
+ // fully-qualified method name. This isn't really what
+ // dexlist is for, but it's easy to do it here.
+ {
+ gOptions.argCopy = strdup(optarg);
+ char* meth = strrchr(gOptions.argCopy, '.');
+ if (meth == nullptr) {
+ fprintf(stderr, "Expected: package.Class.method\n");
+ wantUsage = true;
+ } else {
+ *meth = '\0';
+ gOptions.classToFind = gOptions.argCopy;
+ gOptions.methodToFind = meth + 1;
+ }
+ }
+ break;
+ default:
+ wantUsage = true;
+ break;
+ } // switch
+ } // while
+
+ // Detect early problems.
+ if (optind == argc) {
+ fprintf(stderr, "%s: no file specified\n", gProgName);
+ wantUsage = true;
+ }
+ if (wantUsage) {
+ usage();
+ free(gOptions.argCopy);
+ return 2;
+ }
+
+ // Open alternative output file.
+ if (gOptions.outputFileName) {
+ gOutFile = fopen(gOptions.outputFileName, "w");
+ if (!gOutFile) {
+ fprintf(stderr, "Can't open %s\n", gOptions.outputFileName);
+ free(gOptions.argCopy);
+ return 1;
+ }
+ }
+
+ // Process all files supplied on command line. If one of them fails we
+ // continue on, only returning a failure at the end.
+ int result = 0;
+ while (optind < argc) {
+ result |= processFile(argv[optind++]);
+ } // while
+
+ free(gOptions.argCopy);
+ return result != 0;
+}
+
+} // namespace art
+
+int main(int argc, char** argv) {
+ return art::dexlistDriver(argc, argv);
+}
+
diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc
new file mode 100644
index 0000000..7b1b63d
--- /dev/null
+++ b/dexlist/dexlist_test.cc
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+#include <vector>
+#include <sstream>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/stringprintf.h"
+#include "common_runtime_test.h"
+#include "runtime/arch/instruction_set.h"
+#include "runtime/gc/heap.h"
+#include "runtime/gc/space/image_space.h"
+#include "runtime/os.h"
+#include "runtime/utils.h"
+#include "utils.h"
+
+namespace art {
+
+class DexListTest : public CommonRuntimeTest {
+ protected:
+ virtual void SetUp() {
+ CommonRuntimeTest::SetUp();
+ // Dogfood our own lib core dex file.
+ dex_file_ = GetLibCoreDexFileName();
+ }
+
+ // Runs test with given arguments.
+ bool Exec(const std::vector<std::string>& args, std::string* error_msg) {
+ // TODO(ajcbik): dexlist2 -> dexlist
+ std::string file_path = GetTestAndroidRoot();
+ if (IsHost()) {
+ file_path += "/bin/dexlist2";
+ } else {
+ file_path += "/xbin/dexlist2";
+ }
+ EXPECT_TRUE(OS::FileExists(file_path.c_str())) << file_path << " should be a valid file path";
+ std::vector<std::string> exec_argv = { file_path };
+ exec_argv.insert(exec_argv.end(), args.begin(), args.end());
+ return ::art::Exec(exec_argv, error_msg);
+ }
+
+ std::string dex_file_;
+};
+
+
+TEST_F(DexListTest, NoInputFileGiven) {
+ std::string error_msg;
+ ASSERT_FALSE(Exec({}, &error_msg)) << error_msg;
+}
+
+TEST_F(DexListTest, CantOpenOutput) {
+ std::string error_msg;
+ ASSERT_FALSE(Exec({"-o", "/joho", dex_file_}, &error_msg)) << error_msg;
+}
+
+TEST_F(DexListTest, IllFormedMethod) {
+ std::string error_msg;
+ ASSERT_FALSE(Exec({"-m", "joho", dex_file_}, &error_msg)) << error_msg;
+}
+
+TEST_F(DexListTest, FullOutput) {
+ std::string error_msg;
+ ASSERT_TRUE(Exec({"-o", "/dev/null", dex_file_}, &error_msg)) << error_msg;
+}
+
+TEST_F(DexListTest, MethodOutput) {
+ std::string error_msg;
+ ASSERT_TRUE(Exec({"-o", "/dev/null", "-m", "java.lang.Object.toString",
+ dex_file_}, &error_msg)) << error_msg;
+}
+
+} // namespace art
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index cf4f822..8dde547 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -1618,7 +1618,8 @@
stats_.alignment_bytes += bitmap_section.Offset() - image_header_.GetImageSize();
stats_.bitmap_bytes += bitmap_section.Size();
stats_.art_field_bytes += field_section.Size();
- stats_.art_method_bytes += method_section.Size();
+ // RoundUp to 8 bytes to match the intern table alignment expectation.
+ stats_.art_method_bytes += RoundUp(method_section.Size(), sizeof(uint64_t));
stats_.interned_strings_bytes += intern_section.Size();
stats_.Dump(os);
os << "\n";
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index de46b35..97d170e 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -4836,6 +4836,9 @@
CHECK(!Runtime::Current()->GetHeap()->IsAllocTrackingEnabled());
records = &dummy;
}
+ // We don't need to wait on the condition variable records->new_record_condition_, because this
+ // function only reads the class objects, which are already marked so it doesn't change their
+ // reachability.
//
// Part 1: generate string tables.
@@ -4850,7 +4853,7 @@
count > 0 && it != end; count--, it++) {
const gc::AllocRecord* record = it->second;
std::string temp;
- class_names.Add(record->GetClass()->GetDescriptor(&temp));
+ class_names.Add(record->GetClassDescriptor(&temp));
for (size_t i = 0, depth = record->GetDepth(); i < depth; i++) {
ArtMethod* m = record->StackElement(i).GetMethod();
class_names.Add(m->GetDeclaringClassDescriptor());
@@ -4902,7 +4905,7 @@
const gc::AllocRecord* record = it->second;
size_t stack_depth = record->GetDepth();
size_t allocated_object_class_name_index =
- class_names.IndexOf(record->GetClass()->GetDescriptor(&temp));
+ class_names.IndexOf(record->GetClassDescriptor(&temp));
JDWP::Append4BE(bytes, record->ByteCount());
JDWP::Append2BE(bytes, static_cast<uint16_t>(record->GetTid()));
JDWP::Append2BE(bytes, allocated_object_class_name_index);
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index 11921f4..6537ed2 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -32,6 +32,15 @@
return method_->GetLineNumFromDexPC(dex_pc_);
}
+const char* AllocRecord::GetClassDescriptor(std::string* storage) const {
+ // klass_ could contain null only if we implement class unloading.
+ if (UNLIKELY(klass_.IsNull())) {
+ return "null";
+ } else {
+ return klass_.Read()->GetDescriptor(storage);
+ }
+}
+
void AllocRecordObjectMap::SetProperties() {
#ifdef HAVE_ANDROID_OS
// Check whether there's a system property overriding the max number of records.
@@ -97,7 +106,7 @@
// Only visit the last recent_record_max_ number of allocation records in entries_ and mark the
// klass_ fields as strong roots.
for (auto it = entries_.rbegin(), end = entries_.rend(); count > 0 && it != end; count--, ++it) {
- buffered_visitor.VisitRoot(it->second->GetClassGcRoot());
+ buffered_visitor.VisitRootIfNonNull(it->second->GetClassGcRoot());
}
}
@@ -107,6 +116,8 @@
GcRoot<mirror::Class>& klass = record->GetClassGcRoot();
// This does not need a read barrier because this is called by GC.
mirror::Object* old_object = klass.Read<kWithoutReadBarrier>();
+ // The class object can become null if we implement class unloading.
+ // In that case we might still want to keep the class name string (not implemented).
mirror::Object* new_object = UNLIKELY(old_object == nullptr) ?
nullptr : callback(old_object, arg);
if (UNLIKELY(old_object != new_object)) {
@@ -163,11 +174,6 @@
allow_new_record_ = false;
}
-void AllocRecordObjectMap::EnsureNewAllocationRecordsDisallowed() {
- CHECK(!allow_new_record_);
-}
-
-
struct AllocRecordStackVisitor : public StackVisitor {
AllocRecordStackVisitor(Thread* thread, AllocRecordStackTrace* trace_in, size_t max)
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h
index f567153..06721c8 100644
--- a/runtime/gc/allocation_record.h
+++ b/runtime/gc/allocation_record.h
@@ -188,7 +188,10 @@
return klass_.Read();
}
- GcRoot<mirror::Class>& GetClassGcRoot() {
+ const char* GetClassDescriptor(std::string* storage) const
+ SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
+
+ GcRoot<mirror::Class>& GetClassGcRoot() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
return klass_;
}
@@ -262,15 +265,18 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
+ // Allocation tracking could be enabled by user in between DisallowNewAllocationRecords() and
+ // AllowNewAllocationRecords(), in which case new allocation records can be added although they
+ // should be disallowed. However, this is GC-safe because new objects are not processed in this GC
+ // cycle. The only downside of not handling this case is that such new allocation records can be
+ // swept from the list. But missing the first few records is acceptable for using the button to
+ // enable allocation tracking.
void DisallowNewAllocationRecords()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
void AllowNewAllocationRecords()
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
- void EnsureNewAllocationRecordsDisallowed()
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- EXCLUSIVE_LOCKS_REQUIRED(Locks::alloc_tracker_lock_);
// TODO: Is there a better way to hide the entries_'s type?
EntryList::iterator Begin()
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index c7d2e9f..5e69b79 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -334,6 +334,8 @@
}
}
}
+ // TODO: Other garbage collectors uses Runtime::VisitConcurrentRoots(), refactor this part
+ // to also use the same function.
{
TimingLogger::ScopedTiming split2("VisitConstantRoots", GetTimings());
Runtime::Current()->VisitConstantRoots(this);
@@ -351,6 +353,7 @@
TimingLogger::ScopedTiming split5("VisitNonThreadRoots", GetTimings());
Runtime::Current()->VisitNonThreadRoots(this);
}
+ Runtime::Current()->GetHeap()->VisitAllocationRecords(this);
// Immune spaces.
for (auto& space : heap_->GetContinuousSpaces()) {
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 1b45ea1..a782fc8 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3734,17 +3734,6 @@
}
}
-void Heap::EnsureNewAllocationRecordsDisallowed() const {
- if (IsAllocTrackingEnabled()) {
- // Lock and unlock once to ensure that no threads are still in the
- // middle of adding new allocation records.
- MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
- if (IsAllocTrackingEnabled()) {
- GetAllocationRecords()->EnsureNewAllocationRecordsDisallowed();
- }
- }
-}
-
// Based on debug malloc logic from libc/bionic/debug_stacktrace.cpp.
class StackCrawlState {
public:
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 1c75bd0..cfb6e06 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -721,10 +721,6 @@
SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
- void EnsureNewAllocationRecordsDisallowed() const
- SHARED_LOCKS_REQUIRED(Locks::mutator_lock_)
- LOCKS_EXCLUDED(Locks::alloc_tracker_lock_);
-
private:
class ConcurrentGCTask;
class CollectorTransitionTask;
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 20e4149..5067b0d 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1510,7 +1510,6 @@
monitor_list_->EnsureNewMonitorsDisallowed();
intern_table_->EnsureNewInternsDisallowed();
java_vm_->EnsureNewWeakGlobalsDisallowed();
- heap_->EnsureNewAllocationRecordsDisallowed();
}
void Runtime::SetInstructionSet(InstructionSet instruction_set) {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index 92c9eb8..09db7cd 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -4133,7 +4133,9 @@
<< " to be compatible with type '" << insn_type
<< "' but found type '" << *field_type
<< "' in get-object";
- work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
+ if (error != VERIFY_ERROR_BAD_CLASS_HARD) {
+ work_line_->SetRegisterType(this, vregA, reg_types_.Conflict());
+ }
return;
}
}
diff --git a/runtime/verifier/register_line-inl.h b/runtime/verifier/register_line-inl.h
index 244deed..9cd2bdf 100644
--- a/runtime/verifier/register_line-inl.h
+++ b/runtime/verifier/register_line-inl.h
@@ -38,10 +38,9 @@
verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "Expected category1 register type not '"
<< new_type << "'";
return false;
- } else if (new_type.IsConflict()) { // should only be set during a merge
- verifier->Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "Set register to unknown type " << new_type;
- return false;
} else {
+ // Note: previously we failed when asked to set a conflict. However, conflicts are OK as long
+ // as they are not accessed, and our backends can handle this nowadays.
line_[vdst] = new_type.GetId();
}
// Clear the monitor entry bits for this register.
@@ -93,8 +92,9 @@
if (!SetRegisterType(verifier, vdst, type)) {
return;
}
- if ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
- (cat == kTypeCategoryRef && !type.IsReferenceTypes())) {
+ if (!type.IsConflict() && // Allow conflicts to be copied around.
+ ((cat == kTypeCategory1nr && !type.IsCategory1Types()) ||
+ (cat == kTypeCategoryRef && !type.IsReferenceTypes()))) {
verifier->Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "copy1 v" << vdst << "<-v" << vsrc << " type=" << type
<< " cat=" << static_cast<int>(cat);
} else if (cat == kTypeCategoryRef) {
diff --git a/test/082-inline-execute/src/Main.java b/test/082-inline-execute/src/Main.java
index 4dfa73c..177c5a4 100644
--- a/test/082-inline-execute/src/Main.java
+++ b/test/082-inline-execute/src/Main.java
@@ -1000,6 +1000,45 @@
Assert.assertEquals(Long.reverse(0x8765432187654321L), 0x84c2a6e184c2a6e1L);
Assert.assertEquals(Long.reverse(Long.MAX_VALUE), 0xfffffffffffffffeL);
Assert.assertEquals(Long.reverse(Long.MIN_VALUE), 1L);
+
+ Assert.assertEquals(test_Long_reverse_b22324327(0xaaaaaaaaaaaaaaaaL, 0x5555555555555555L),
+ 157472205507277347L);
+ }
+
+ // A bit more complicated than the above. Use local variables to stress register allocation.
+ private static long test_Long_reverse_b22324327(long l1, long l2) {
+ // A couple of local integers. Use them in a loop, so they get promoted.
+ int i1 = 0, i2 = 1, i3 = 2, i4 = 3, i5 = 4, i6 = 5, i7 = 6, i8 = 7;
+ for (int k = 0; k < 10; k++) {
+ i1 += 1;
+ i2 += 2;
+ i3 += 3;
+ i4 += 4;
+ i5 += 5;
+ i6 += 6;
+ i7 += 7;
+ i8 += 8;
+ }
+
+ // Do the Long.reverse() calls, save the results.
+ long r1 = Long.reverse(l1);
+ long r2 = Long.reverse(l2);
+
+ // Some more looping with the ints.
+ for (int k = 0; k < 10; k++) {
+ i1 += 1;
+ i2 += 2;
+ i3 += 3;
+ i4 += 4;
+ i5 += 5;
+ i6 += 6;
+ i7 += 7;
+ i8 += 8;
+ }
+
+ // Include everything in the result, so things are kept live. Try to be a little bit clever to
+ // avoid things being folded somewhere.
+ return (r1 / i1) + (r2 / i2) + i3 + i4 + i5 + i6 + i7 + i8;
}
static Object runtime;
diff --git a/test/510-checker-try-catch/smali/Builder.smali b/test/510-checker-try-catch/smali/Builder.smali
index 4ea7b61..87e4064 100644
--- a/test/510-checker-try-catch/smali/Builder.smali
+++ b/test/510-checker-try-catch/smali/Builder.smali
@@ -713,20 +713,20 @@
## CHECK-START: int Builder.testSwitchTryExit(int, int, int, int) builder (after)
## CHECK: name "B0"
-## CHECK: successors "<<BEnterTry:B\d+>>"
+## CHECK: successors "<<BEnterTry1:B\d+>>"
## CHECK: name "<<BPSwitch0:B\d+>>"
-## CHECK: predecessors "<<BEnterTry>>"
-## CHECK: successors "<<BTry2:B\d+>>" "<<BPSwitch1:B\d+>>"
+## CHECK: predecessors "<<BEnterTry1>>"
+## CHECK: successors "<<BTry2:B\d+>>" "<<BExitTry1:B\d+>>"
## CHECK: If
-## CHECK: name "<<BPSwitch1>>"
-## CHECK: predecessors "<<BPSwitch0>>"
-## CHECK: successors "<<BExitTry1:B\d+>>" "<<BTry1:B\d+>>"
+## CHECK: name "<<BPSwitch1:B\d+>>"
+## CHECK: predecessors "<<BExitTry1>>"
+## CHECK: successors "<<BOutside:B\d+>>" "<<BEnterTry2:B\d+>>"
## CHECK: If
-## CHECK: name "<<BTry1>>"
-## CHECK: predecessors "<<BPSwitch1>>"
+## CHECK: name "<<BTry1:B\d+>>"
+## CHECK: predecessors "<<BEnterTry2>>"
## CHECK: successors "<<BTry2>>"
## CHECK: Div
@@ -735,28 +735,34 @@
## CHECK: successors "<<BExitTry2:B\d+>>"
## CHECK: Div
-## CHECK: name "<<BOutside:B\d+>>"
-## CHECK: predecessors "<<BExitTry1>>" "<<BExitTry2>>"
+## CHECK: name "<<BOutside>>"
+## CHECK: predecessors "<<BPSwitch1>>" "<<BExitTry2>>"
## CHECK: successors "<<BCatchReturn:B\d+>>"
## CHECK: Div
## CHECK: name "<<BCatchReturn>>"
-## CHECK: predecessors "<<BOutside>>" "<<BEnterTry>>" "<<BExitTry1>>" "<<BExitTry2>>"
+## CHECK: predecessors "<<BOutside>>" "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2>>" "<<BExitTry2>>"
## CHECK: flags "catch_block"
## CHECK: Return
-## CHECK: name "<<BEnterTry>>"
+## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "B0"
## CHECK: successors "<<BPSwitch0>>"
## CHECK: xhandlers "<<BCatchReturn>>"
## CHECK: TryBoundary kind:entry
## CHECK: name "<<BExitTry1>>"
-## CHECK: predecessors "<<BPSwitch1>>"
-## CHECK: successors "<<BOutside>>"
+## CHECK: predecessors "<<BPSwitch0>>"
+## CHECK: successors "<<BPSwitch1>>"
## CHECK: xhandlers "<<BCatchReturn>>"
## CHECK: TryBoundary kind:exit
+## CHECK: name "<<BEnterTry2>>"
+## CHECK: predecessors "<<BPSwitch1>>"
+## CHECK: successors "<<BTry1>>"
+## CHECK: xhandlers "<<BCatchReturn>>"
+## CHECK: TryBoundary kind:entry
+
## CHECK: name "<<BExitTry2>>"
## CHECK: predecessors "<<BTry2>>"
## CHECK: successors "<<BOutside>>"
@@ -767,6 +773,7 @@
.registers 4
:try_start
+ div-int/2addr p0, p1
packed-switch p0, :pswitch_data
div-int/2addr p0, p1
@@ -809,6 +816,10 @@
## CHECK: flags "catch_block"
## CHECK: StoreLocal [v0,<<Minus1>>]
+## CHECK: name "<<BExit>>"
+## CHECK: predecessors "<<BExitTry>>" "<<BCatch>>"
+## CHECK: Exit
+
## CHECK: name "<<BEnterTry>>"
## CHECK: predecessors "B0"
## CHECK: successors "<<BTry>>"
@@ -821,10 +832,6 @@
## CHECK: xhandlers "<<BCatch>>"
## CHECK: TryBoundary kind:exit
-## CHECK: name "<<BExit>>"
-## CHECK: predecessors "<<BExitTry>>" "<<BCatch>>"
-## CHECK: Exit
-
.method public static testThrow(Ljava/lang/Exception;)I
.registers 2
@@ -852,6 +859,9 @@
## CHECK: name "<<BReturn:B\d+>>"
## CHECK: predecessors "<<BExitTry>>"
+## CHECK: successors "<<BExit:B\d+>>"
+
+## CHECK: name "<<BExit>>"
## CHECK: name "<<BTry:B\d+>>"
## CHECK: predecessors "<<BEnterTry>>"
@@ -956,48 +966,51 @@
## CHECK: successors "<<BCatch1:B\d+>>"
## CHECK: name "<<BCatch1>>"
-## CHECK: predecessors "B0" "<<BEnter2:B\d+>>" "<<BExit2:B\d+>>"
-## CHECK: successors "<<BEnter1:B\d+>>"
+## CHECK: predecessors "B0" "<<BEnterTry2:B\d+>>" "<<BExitTry2:B\d+>>"
+## CHECK: successors "<<BEnterTry1:B\d+>>"
## CHECK: flags "catch_block"
## CHECK: name "<<BCatch2:B\d+>>"
-## CHECK: predecessors "<<BExit1:B\d+>>" "<<BEnter1>>" "<<BExit1>>"
-## CHECK: successors "<<BEnter2>>"
+## CHECK: predecessors "<<BExitTry1:B\d+>>" "<<BEnterTry1>>" "<<BExitTry1>>"
+## CHECK: successors "<<BEnterTry2>>"
## CHECK: flags "catch_block"
## CHECK: name "<<BReturn:B\d+>>"
-## CHECK: predecessors "<<BExit2>>"
+## CHECK: predecessors "<<BExitTry2>>"
+## CHECK: successors "<<BExit:B\d+>>"
## CHECK: Return
+## CHECK: name "<<BExit>>"
+
## CHECK: name "<<BTry1:B\d+>>"
-## CHECK: predecessors "<<BEnter1>>"
-## CHECK: successors "<<BExit1>>"
+## CHECK: predecessors "<<BEnterTry1>>"
+## CHECK: successors "<<BExitTry1>>"
## CHECK: Div
-## CHECK: name "<<BEnter1>>"
+## CHECK: name "<<BEnterTry1>>"
## CHECK: predecessors "<<BCatch1>>"
## CHECK: successors "<<BTry1>>"
## CHECK: xhandlers "<<BCatch2>>"
## CHECK: TryBoundary kind:entry
-## CHECK: name "<<BExit1>>"
+## CHECK: name "<<BExitTry1>>"
## CHECK: predecessors "<<BTry1>>"
## CHECK: successors "<<BCatch2>>"
## CHECK: xhandlers "<<BCatch2>>"
## CHECK: TryBoundary kind:exit
## CHECK: name "<<BTry2:B\d+>>"
-## CHECK: predecessors "<<BEnter2>>"
-## CHECK: successors "<<BExit2>>"
+## CHECK: predecessors "<<BEnterTry2>>"
+## CHECK: successors "<<BExitTry2>>"
## CHECK: Div
-## CHECK: name "<<BEnter2>>"
+## CHECK: name "<<BEnterTry2>>"
## CHECK: predecessors "<<BCatch2>>"
## CHECK: successors "<<BTry2>>"
## CHECK: xhandlers "<<BCatch1>>"
## CHECK: TryBoundary kind:entry
-## CHECK: name "<<BExit2>>"
+## CHECK: name "<<BExitTry2>>"
## CHECK: predecessors "<<BTry2>>"
## CHECK: successors "<<BReturn>>"
## CHECK: xhandlers "<<BCatch1>>"
@@ -1131,3 +1144,29 @@
:try_end
.catchall {:try_start .. :try_end} :catch_all
.end method
+
+## CHECK-START: int Builder.testSynchronized(java.lang.Object) builder (after)
+## CHECK: flags "catch_block"
+## CHECK-NOT: end_block
+## CHECK: MonitorOperation kind:exit
+
+.method public static testSynchronized(Ljava/lang/Object;)I
+ .registers 2
+
+ monitor-enter p0
+
+ :try_start_9
+ invoke-virtual {p0}, Ljava/lang/Object;->hashCode()I
+ move-result v0
+
+ monitor-exit p0
+ return v0
+
+ :catchall_11
+ move-exception v0
+ monitor-exit p0
+ :try_end_15
+ .catchall {:try_start_9 .. :try_end_15} :catchall_11
+
+ throw v0
+.end method
diff --git a/test/800-smali/expected.txt b/test/800-smali/expected.txt
index fe68c5b..6ee7a5b 100644
--- a/test/800-smali/expected.txt
+++ b/test/800-smali/expected.txt
@@ -28,4 +28,5 @@
b/22080519
b/21645819
b/22244733
+b/22331663
Done!
diff --git a/test/800-smali/smali/b_22331663.smali b/test/800-smali/smali/b_22331663.smali
new file mode 100644
index 0000000..af99152
--- /dev/null
+++ b/test/800-smali/smali/b_22331663.smali
@@ -0,0 +1,35 @@
+.class public LB22331663;
+.super Ljava/lang/Object;
+
+
+.method public static run(Z)V
+.registers 6
+ if-eqz v5, :Label2
+
+:Label1
+ # Construct a java.lang.Object completely, and throw a new exception.
+ new-instance v4, Ljava/lang/Object;
+ invoke-direct {v4}, Ljava/lang/Object;-><init>()V
+
+ new-instance v3, Ljava/lang/RuntimeException;
+ invoke-direct {v3}, Ljava/lang/RuntimeException;-><init>()V
+ throw v3
+
+:Label2
+ # Allocate a java.lang.Object (do not initialize), and throw a new exception.
+ new-instance v4, Ljava/lang/Object;
+
+ new-instance v3, Ljava/lang/RuntimeException;
+ invoke-direct {v3}, Ljava/lang/RuntimeException;-><init>()V
+ throw v3
+
+:Label3
+ # Catch handler. Here we had to merge the uninitialized with the initialized reference,
+ # which creates a conflict. Copy the conflict, and then return. This should not make the
+ # verifier fail the method.
+ move-object v0, v4
+
+ return-void
+
+.catchall {:Label1 .. :Label3} :Label3
+.end method
diff --git a/test/800-smali/src/Main.java b/test/800-smali/src/Main.java
index 61f0d7b..3dbba8d 100644
--- a/test/800-smali/src/Main.java
+++ b/test/800-smali/src/Main.java
@@ -103,6 +103,8 @@
null, null));
testCases.add(new TestCase("b/22244733", "B22244733", "run", new Object[] { "abc" },
null, "abc"));
+ testCases.add(new TestCase("b/22331663", "B22331663", "run", new Object[] { false },
+ null, null));
}
public void runTests() {
diff --git a/test/dexdump/bytecodes.lst b/test/dexdump/bytecodes.lst
new file mode 100644
index 0000000..aeda7b4
--- /dev/null
+++ b/test/dexdump/bytecodes.lst
@@ -0,0 +1,20 @@
+#bytecodes.dex
+0x000009a0 8 com.google.android.test.BuildConfig <init> ()V BuildConfig.java 4
+0x000009b8 8 com.google.android.test.R$attr <init> ()V R.java 11
+0x000009d0 8 com.google.android.test.R$drawable <init> ()V R.java 13
+0x000009e8 8 com.google.android.test.R <init> ()V R.java 10
+0x00000a00 148 com.google.android.test.Test <clinit> ()V Test.java 7
+0x00000aa4 468 com.google.android.test.Test <init> ()V Test.java 43
+0x00000ca4 478 com.google.android.test.Test add (Ljava/lang/Object;)Ljava/lang/Object; Test.java 179
+0x00000ea0 236 com.google.android.test.Test adds (Ljava/lang/Object;)Ljava/lang/Object; Test.java 201
+0x00000f9c 342 com.google.android.test.Test copies ()V Test.java 216
+0x00001104 156 com.google.android.test.Test doit (I)V Test.java 98
+0x000011b0 146 com.google.android.test.Test geta ()Z Test.java 72
+0x00001254 38 com.google.android.test.Test p (I)V Test.java 120
+0x0000128c 636 com.google.android.test.Test params (BCSIJFDLjava/lang/Object;[I)J Test.java 232
+0x00001518 170 com.google.android.test.Test q (II)V Test.java 127
+0x000015d4 186 com.google.android.test.Test r (II)I Test.java 139
+0x000016a0 388 com.google.android.test.Test s (JJ)J Test.java 159
+0x00001834 96 com.google.android.test.Test seta ()V Test.java 60
+0x000018a4 14 com.google.android.test.Test onStart ()V Test.java 86
+0x000018c4 18 com.google.android.test.Test run ()V Test.java 92
diff --git a/test/dexdump/checkers.lst b/test/dexdump/checkers.lst
new file mode 100644
index 0000000..daef138
--- /dev/null
+++ b/test/dexdump/checkers.lst
@@ -0,0 +1,82 @@
+#checkers.dex
+0x0000149c 8 com.google.android.checkers.Checkers <init> ()V (none) -1
+0x000014b4 66 com.google.android.checkers.Checkers a (Z)V (none) -1
+0x00001508 8 com.google.android.checkers.Checkers onConfigurationChanged (Landroid/content/res/Configuration;)V (none) -1
+0x00001520 118 com.google.android.checkers.Checkers onCreate (Landroid/os/Bundle;)V (none) -1
+0x000015a8 432 com.google.android.checkers.Checkers onCreateOptionsMenu (Landroid/view/Menu;)Z (none) -1
+0x00001768 116 com.google.android.checkers.Checkers onKeyDown (ILandroid/view/KeyEvent;)Z (none) -1
+0x000017ec 316 com.google.android.checkers.Checkers onOptionsItemSelected (Landroid/view/MenuItem;)Z (none) -1
+0x00001938 42 com.google.android.checkers.Checkers onPause ()V (none) -1
+0x00001974 16 com.google.android.checkers.Checkers onStop ()V (none) -1
+0x00001994 88 com.google.android.checkers.Checkers onTrackballEvent (Landroid/view/MotionEvent;)Z (none) -1
+0x000019fc 1324 com.google.android.checkers.CheckersView <init> (Landroid/content/Context;Landroid/content/SharedPreferences;)V (none) -1
+0x00001f48 62 com.google.android.checkers.CheckersView a (Landroid/content/SharedPreferences$Editor;Ljava/lang/String;[I)V (none) -1
+0x00001f98 66 com.google.android.checkers.CheckersView a (Landroid/content/SharedPreferences;Ljava/lang/String;[I)V (none) -1
+0x00001fec 126 com.google.android.checkers.CheckersView a (Landroid/graphics/Canvas;IIII)V (none) -1
+0x0000207c 162 com.google.android.checkers.CheckersView a (Landroid/graphics/Canvas;IIIILandroid/graphics/Paint;Landroid/graphics/Paint;Z)V (none) -1
+0x00002130 8 com.google.android.checkers.CheckersView a (Lcom/google/android/checkers/CheckersView;I)V (none) -1
+0x00002148 588 com.google.android.checkers.CheckersView a (Landroid/content/SharedPreferences;)Z (none) -1
+0x000023d0 22 com.google.android.checkers.CheckersView a (Lcom/google/android/checkers/CheckersView;)Z (none) -1
+0x000023f8 1290 com.google.android.checkers.CheckersView a (ZIIII)Z (none) -1
+0x00002930 204 com.google.android.checkers.CheckersView b (FF)I (none) -1
+0x00002a20 36 com.google.android.checkers.CheckersView b (I)V (none) -1
+0x00002a60 198 com.google.android.checkers.CheckersView b (Landroid/graphics/Canvas;IIII)V (none) -1
+0x00002b38 524 com.google.android.checkers.CheckersView c (I)V (none) -1
+0x00002d54 176 com.google.android.checkers.CheckersView d ()V (none) -1
+0x00002e14 20 com.google.android.checkers.CheckersView e ()Z (none) -1
+0x00002e38 128 com.google.android.checkers.CheckersView a ()V (none) -1
+0x00002ec8 226 com.google.android.checkers.CheckersView a (FF)V (none) -1
+0x00002fd8 32 com.google.android.checkers.CheckersView a (IIII)V (none) -1
+0x00003008 340 com.google.android.checkers.CheckersView a (Landroid/content/SharedPreferences$Editor;)V (none) -1
+0x00003178 34 com.google.android.checkers.CheckersView a (I)Z (none) -1
+0x000031ac 44 com.google.android.checkers.CheckersView a (Z)Z (none) -1
+0x000031f4 60 com.google.android.checkers.CheckersView b ()V (none) -1
+0x0000324c 138 com.google.android.checkers.CheckersView b (Z)Z (none) -1
+0x000032f4 16 com.google.android.checkers.CheckersView c ()I (none) -1
+0x00003320 68 com.google.android.checkers.CheckersView c (Z)Z (none) -1
+0x00003380 38 com.google.android.checkers.CheckersView d (Z)Z (none) -1
+0x000033c4 2528 com.google.android.checkers.CheckersView draw (Landroid/graphics/Canvas;)V (none) -1
+0x00003dd0 38 com.google.android.checkers.CheckersView e (Z)Z (none) -1
+0x00003e14 104 com.google.android.checkers.CheckersView onSizeChanged (IIII)V (none) -1
+0x00003e98 82 com.google.android.checkers.CheckersView onTouchEvent (Landroid/view/MotionEvent;)Z (none) -1
+0x00003efc 128 com.google.android.checkers.CheckersView setLevel (I)V (none) -1
+0x00003f98 2780 com.google.android.checkers.a <clinit> ()V (none) -1
+0x00004a84 188 com.google.android.checkers.a <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x00004b5c 28 com.google.android.checkers.a a (II)I (none) -1
+0x00004b88 2592 com.google.android.checkers.a a (IIIIIZ)I (none) -1
+0x000055b8 110 com.google.android.checkers.a a (IZ)I (none) -1
+0x00005638 196 com.google.android.checkers.a a (Z)I (none) -1
+0x0000570c 112 com.google.android.checkers.a a (ZII)I (none) -1
+0x0000578c 88 com.google.android.checkers.a a (ZIIIZ)I (none) -1
+0x000057f4 68 com.google.android.checkers.a a (ZIIZ)I (none) -1
+0x00005848 152 com.google.android.checkers.a a (IIII)V (none) -1
+0x000058f0 78 com.google.android.checkers.a a (IIIII)V (none) -1
+0x00005950 198 com.google.android.checkers.a a (IIIIIIII)V (none) -1
+0x00005a28 1750 com.google.android.checkers.a a (IZI)Z (none) -1
+0x00006110 92 com.google.android.checkers.a b (ZIIIZ)I (none) -1
+0x0000617c 112 com.google.android.checkers.a b (ZIIZ)I (none) -1
+0x000061fc 38 com.google.android.checkers.a b ()V (none) -1
+0x0000624c 736 com.google.android.checkers.a b (I)V (none) -1
+0x0000653c 198 com.google.android.checkers.a b (IIIIIIII)V (none) -1
+0x00006614 922 com.google.android.checkers.a b (IZI)Z (none) -1
+0x000069c0 108 com.google.android.checkers.a c (ZIIZ)I (none) -1
+0x00006a3c 16 com.google.android.checkers.a c ()V (none) -1
+0x00006a68 406 com.google.android.checkers.a c (IIIIIIII)V (none) -1
+0x00006c10 112 com.google.android.checkers.a d (ZIIZ)I (none) -1
+0x00006c90 16 com.google.android.checkers.a a (ZZ)I (none) -1
+0x00006cb0 90 com.google.android.checkers.a a ()V (none) -1
+0x00006d1c 8 com.google.android.checkers.a a (I)V (none) -1
+0x00006d34 74 com.google.android.checkers.a a (IIIIZ)V (none) -1
+0x00006d90 32 com.google.android.checkers.a b (ZZ)V (none) -1
+0x00006dcc 1052 com.google.android.checkers.a run ()V (none) -1
+0x000071f8 12 com.google.android.checkers.b <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x00007214 28 com.google.android.checkers.b onClick (Landroid/content/DialogInterface;I)V (none) -1
+0x00007240 12 com.google.android.checkers.c <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x0000725c 2 com.google.android.checkers.c onClick (Landroid/content/DialogInterface;I)V (none) -1
+0x00007270 12 com.google.android.checkers.d <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x0000728c 2 com.google.android.checkers.d onClick (Landroid/content/DialogInterface;I)V (none) -1
+0x000072a0 12 com.google.android.checkers.e <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x000072bc 14 com.google.android.checkers.e onClick (Landroid/content/DialogInterface;I)V (none) -1
+0x000072dc 12 com.google.android.checkers.f <init> (Lcom/google/android/checkers/CheckersView;)V (none) -1
+0x000072f8 12 com.google.android.checkers.f onClick (Landroid/content/DialogInterface;I)V (none) -1
+0x00007314 58 com.google.android.checkers.g a ([B)Z (none) -1
diff --git a/test/dexdump/run-all-tests b/test/dexdump/run-all-tests
index f5d8cd6..d9f1e96 100755
--- a/test/dexdump/run-all-tests
+++ b/test/dexdump/run-all-tests
@@ -37,10 +37,14 @@
tmpdir=/tmp/test-$$
mkdir ${tmpdir}
-# Set up binary and flags to test.
+# Set up dexdump binary and flags to test.
DEXD="${ANDROID_HOST_OUT}/bin/dexdump2"
-FLAGS1="-dfh"
-FLAGS2="-l xml"
+DEXDFLAGS1="-dfh"
+DEXDFLAGS2="-l xml"
+
+# Set up dexlist binary and flags to test.
+DEXL="${ANDROID_HOST_OUT}/bin/dexlist2"
+DEXLFLAGS=""
# Run the tests.
passed=0
@@ -50,18 +54,28 @@
basenm=`basename "${i}" .dex`
txtfile=${basenm}.txt
xmlfile=${basenm}.xml
- genfile1=${tmpdir}/${txtfile}
- genfile2=${tmpdir}/${xmlfile}
- ${DEXD} ${FLAGS1} ${i} > ${genfile1}
- cmp ${txtfile} ${genfile1}
+ lstfile=${basenm}.lst
+ gentxtfile=${tmpdir}/${txtfile}
+ genxmlfile=${tmpdir}/${xmlfile}
+ genlstfile=${tmpdir}/${lstfile}
+ ${DEXD} ${DEXDFLAGS1} ${i} > ${gentxtfile}
+ cmp ${txtfile} ${gentxtfile}
if [ "$?" = "0" ]; then
((passed += 1))
else
((failed += 1))
echo failed: ${i}
fi
- ${DEXD} ${FLAGS2} ${i} > ${genfile2}
- cmp ${xmlfile} ${genfile2}
+ ${DEXD} ${DEXDFLAGS2} ${i} > ${genxmlfile}
+ cmp ${xmlfile} ${genxmlfile}
+ if [ "$?" = "0" ]; then
+ ((passed += 1))
+ else
+ ((failed += 1))
+ echo failed: ${i}
+ fi
+ ${DEXL} ${DEXLFLAGS} ${i} > ${genlstfile}
+ cmp ${lstfile} ${genlstfile}
if [ "$?" = "0" ]; then
((passed += 1))
else
@@ -77,7 +91,7 @@
echo
# Clean up, cd back to original dir.
-#rm -rf ${tmpdir}
+rm -rf ${tmpdir}
cd ${oldwd}
# Return status.
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index cf6be83..842d87e 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -458,7 +458,9 @@
# When running under gdb, we cannot do piping and grepping...
$cmdline "$@"
else
- $cmdline "$@" 2>&1
+ trap 'kill -INT -$pid' INT
+ $cmdline "$@" 2>&1 & pid=$!
+ wait $pid
# Add extra detail if time out is enabled.
if [ ${PIPESTATUS[0]} = 124 ] && [ "$TIME_OUT" = "y" ]; then
echo -e "\e[91mTEST TIMED OUT!\e[0m" >&2