Merge "Speed up MterpShouldSwitchInterpreters check"
diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc
index bb586bf..6f11e62 100644
--- a/compiler/optimizing/constant_folding.cc
+++ b/compiler/optimizing/constant_folding.cc
@@ -113,7 +113,7 @@
void HConstantFoldingVisitor::VisitTypeConversion(HTypeConversion* inst) {
// Constant folding: replace `TypeConversion(a)' with a constant at
// compile time if `a' is a constant.
- HConstant* constant = inst->AsTypeConversion()->TryStaticEvaluation();
+ HConstant* constant = inst->TryStaticEvaluation();
if (constant != nullptr) {
inst->ReplaceWith(constant);
inst->GetBlock()->RemoveInstruction(inst);
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index bd20d28..7fa0c2b 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1168,16 +1168,6 @@
RecordSimplification();
return;
}
- } else if (input->IsIntConstant()) {
- // Try to eliminate type conversion on int constant whose value falls into
- // the range of the result type.
- int32_t value = input->AsIntConstant()->GetValue();
- if (DataType::IsTypeConversionImplicit(value, result_type)) {
- instruction->ReplaceWith(input);
- instruction->GetBlock()->RemoveInstruction(instruction);
- RecordSimplification();
- return;
- }
}
}
diff --git a/compiler/optimizing/instruction_simplifier_mips.h b/compiler/optimizing/instruction_simplifier_mips.h
index 22cc2ef..6cb8aff 100644
--- a/compiler/optimizing/instruction_simplifier_mips.h
+++ b/compiler/optimizing/instruction_simplifier_mips.h
@@ -30,7 +30,7 @@
class InstructionSimplifierMips : public HOptimization {
public:
InstructionSimplifierMips(HGraph* graph, CodeGenerator* codegen, OptimizingCompilerStats* stats)
- : HOptimization(graph, "instruction_simplifier_mips", stats),
+ : HOptimization(graph, kInstructionSimplifierMipsPassName, stats),
codegen_(down_cast<CodeGeneratorMIPS*>(codegen)) {}
static constexpr const char* kInstructionSimplifierMipsPassName = "instruction_simplifier_mips";
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index fa580d9..4a9da7e 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1403,6 +1403,14 @@
if (GetInput()->IsIntConstant()) {
int32_t value = GetInput()->AsIntConstant()->GetValue();
switch (GetResultType()) {
+ case DataType::Type::kInt8:
+ return graph->GetIntConstant(static_cast<int8_t>(value), GetDexPc());
+ case DataType::Type::kUint8:
+ return graph->GetIntConstant(static_cast<uint8_t>(value), GetDexPc());
+ case DataType::Type::kInt16:
+ return graph->GetIntConstant(static_cast<int16_t>(value), GetDexPc());
+ case DataType::Type::kUint16:
+ return graph->GetIntConstant(static_cast<uint16_t>(value), GetDexPc());
case DataType::Type::kInt64:
return graph->GetLongConstant(static_cast<int64_t>(value), GetDexPc());
case DataType::Type::kFloat32:
@@ -1415,6 +1423,14 @@
} else if (GetInput()->IsLongConstant()) {
int64_t value = GetInput()->AsLongConstant()->GetValue();
switch (GetResultType()) {
+ case DataType::Type::kInt8:
+ return graph->GetIntConstant(static_cast<int8_t>(value), GetDexPc());
+ case DataType::Type::kUint8:
+ return graph->GetIntConstant(static_cast<uint8_t>(value), GetDexPc());
+ case DataType::Type::kInt16:
+ return graph->GetIntConstant(static_cast<int16_t>(value), GetDexPc());
+ case DataType::Type::kUint16:
+ return graph->GetIntConstant(static_cast<uint16_t>(value), GetDexPc());
case DataType::Type::kInt32:
return graph->GetIntConstant(static_cast<int32_t>(value), GetDexPc());
case DataType::Type::kFloat32:
diff --git a/openjdkjvmti/events.cc b/openjdkjvmti/events.cc
index be4ebbc..05f9125 100644
--- a/openjdkjvmti/events.cc
+++ b/openjdkjvmti/events.cc
@@ -899,9 +899,9 @@
}
}
-static void SetupTraceListener(JvmtiMethodTraceListener* listener,
- ArtJvmtiEvent event,
- bool enable) {
+void EventHandler::SetupTraceListener(JvmtiMethodTraceListener* listener,
+ ArtJvmtiEvent event,
+ bool enable) {
bool needs_full_deopt = EventNeedsFullDeopt(event);
// Make sure we can deopt.
{
@@ -921,8 +921,21 @@
}
// Add the actual listeners.
- art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative);
uint32_t new_events = GetInstrumentationEventsFor(event);
+ if (new_events == art::instrumentation::Instrumentation::kDexPcMoved) {
+ // Need to skip adding the listeners if the event is breakpoint/single-step since those events
+ // share the same art-instrumentation underlying event. We need to give them their own deopt
+ // request though so the test waits until here.
+ DCHECK(event == ArtJvmtiEvent::kBreakpoint || event == ArtJvmtiEvent::kSingleStep);
+ ArtJvmtiEvent other = event == ArtJvmtiEvent::kBreakpoint ? ArtJvmtiEvent::kSingleStep
+ : ArtJvmtiEvent::kBreakpoint;
+ if (IsEventEnabledAnywhere(other)) {
+ // The event needs to be kept around/is already enabled by the other jvmti event that uses the
+ // same instrumentation event.
+ return;
+ }
+ }
+ art::ScopedThreadStateChange stsc(art::Thread::Current(), art::ThreadState::kNative);
art::instrumentation::Instrumentation* instr = art::Runtime::Current()->GetInstrumentation();
art::gc::ScopedGCCriticalSection gcs(art::Thread::Current(),
art::gc::kGcCauseInstrumentation,
@@ -1002,18 +1015,6 @@
case ArtJvmtiEvent::kGarbageCollectionFinish:
SetupGcPauseTracking(gc_pause_listener_.get(), event, enable);
return;
-
- case ArtJvmtiEvent::kBreakpoint:
- case ArtJvmtiEvent::kSingleStep: {
- ArtJvmtiEvent other = (event == ArtJvmtiEvent::kBreakpoint) ? ArtJvmtiEvent::kSingleStep
- : ArtJvmtiEvent::kBreakpoint;
- // We only need to do anything if there isn't already a listener installed/held-on by the
- // other jvmti event that uses DexPcMoved.
- if (!IsEventEnabledAnywhere(other)) {
- SetupTraceListener(method_trace_listener_.get(), event, enable);
- }
- return;
- }
// FramePop can never be disabled once it's been turned on since we would either need to deal
// with dangling pointers or have missed events.
// TODO We really need to make this not the case anymore.
@@ -1030,6 +1031,8 @@
case ArtJvmtiEvent::kFieldModification:
case ArtJvmtiEvent::kException:
case ArtJvmtiEvent::kExceptionCatch:
+ case ArtJvmtiEvent::kBreakpoint:
+ case ArtJvmtiEvent::kSingleStep:
SetupTraceListener(method_trace_listener_.get(), event, enable);
return;
case ArtJvmtiEvent::kMonitorContendedEnter:
diff --git a/openjdkjvmti/events.h b/openjdkjvmti/events.h
index c73215f..b051366 100644
--- a/openjdkjvmti/events.h
+++ b/openjdkjvmti/events.h
@@ -234,6 +234,8 @@
REQUIRES(!envs_lock_);
private:
+ void SetupTraceListener(JvmtiMethodTraceListener* listener, ArtJvmtiEvent event, bool enable);
+
template <ArtJvmtiEvent kEvent, typename ...Args>
ALWAYS_INLINE
inline std::vector<impl::EventHandlerFunc<kEvent>> CollectEvents(art::Thread* thread,
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index bdbc450..43a5139 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -692,13 +692,15 @@
declaring_class_ = GcRoot<mirror::Class>(const_cast<ArtMethod*>(src)->GetDeclaringClass());
// If the entry point of the method we are copying from is from JIT code, we just
- // put the entry point of the new method to interpreter. We could set the entry point
- // to the JIT code, but this would require taking the JIT code cache lock to notify
- // it, which we do not want at this level.
+ // put the entry point of the new method to interpreter or GenericJNI. We could set
+ // the entry point to the JIT code, but this would require taking the JIT code cache
+ // lock to notify it, which we do not want at this level.
Runtime* runtime = Runtime::Current();
if (runtime->UseJitCompilation()) {
if (runtime->GetJit()->GetCodeCache()->ContainsPc(GetEntryPointFromQuickCompiledCode())) {
- SetEntryPointFromQuickCompiledCodePtrSize(GetQuickToInterpreterBridge(), image_pointer_size);
+ SetEntryPointFromQuickCompiledCodePtrSize(
+ src->IsNative() ? GetQuickGenericJniStub() : GetQuickToInterpreterBridge(),
+ image_pointer_size);
}
}
// Clear the profiling info for the same reasons as the JIT code.
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index ccf4319..e5bb786 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -4515,7 +4515,7 @@
void ClassLinker::CreateProxyConstructor(Handle<mirror::Class> klass, ArtMethod* out) {
// Create constructor for Proxy that must initialize the method.
- CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 23u);
+ CHECK_EQ(GetClassRoot(kJavaLangReflectProxy)->NumDirectMethods(), 21u);
// Find the <init>(InvocationHandler)V method. The exact method offset varies depending
// on which front-end compiler was used to build the libcore DEX files.
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 1dcd935..13029fb 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -963,7 +963,11 @@
}
VariableSizedHandleScope hs(Thread::Current());
std::vector<Handle<mirror::Object>> raw_instances;
- Runtime::Current()->GetHeap()->GetInstances(hs, hs.NewHandle(c), max_count, raw_instances);
+ Runtime::Current()->GetHeap()->GetInstances(hs,
+ hs.NewHandle(c),
+ /* use_is_assignable_from */ false,
+ max_count,
+ raw_instances);
for (size_t i = 0; i < raw_instances.size(); ++i) {
instances->push_back(gRegistry->Add(raw_instances[i].Get()));
}
diff --git a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
index f756312..238ada9 100644
--- a/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_dexcache_entrypoints.cc
@@ -41,6 +41,12 @@
static_assert(sizeof(GcRoot<mirror::String>) == sizeof(GcRoot<mirror::Object>), "Size check.");
DCHECK_NE(bss_offset, IndexBssMappingLookup::npos);
DCHECK_ALIGNED(bss_offset, sizeof(GcRoot<mirror::Object>));
+ if (UNLIKELY(!oat_file->IsExecutable())) {
+ // There are situations where we execute bytecode tied to an oat file opened
+ // as non-executable (i.e. the AOT-compiled code cannot be executed) and we
+ // can JIT that bytecode and get here without the .bss being mmapped.
+ return;
+ }
GcRoot<mirror::Object>* slot = reinterpret_cast<GcRoot<mirror::Object>*>(
const_cast<uint8_t*>(oat_file->BssBegin() + bss_offset));
DCHECK_GE(slot, oat_file->GetBssGcRoots().data());
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 9f62666..f29ae92 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -1796,19 +1796,25 @@
return GetBytesFreedEver() + GetBytesAllocated();
}
+// Check whether the given object is an instance of the given class.
+static bool MatchesClass(mirror::Object* obj,
+ Handle<mirror::Class> h_class,
+ bool use_is_assignable_from) REQUIRES_SHARED(Locks::mutator_lock_) {
+ mirror::Class* instance_class = obj->GetClass();
+ CHECK(instance_class != nullptr);
+ ObjPtr<mirror::Class> klass = h_class.Get();
+ if (use_is_assignable_from) {
+ return klass != nullptr && klass->IsAssignableFrom(instance_class);
+ }
+ return instance_class == klass;
+}
+
void Heap::CountInstances(const std::vector<Handle<mirror::Class>>& classes,
bool use_is_assignable_from,
uint64_t* counts) {
auto instance_counter = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
- mirror::Class* instance_class = obj->GetClass();
- CHECK(instance_class != nullptr);
for (size_t i = 0; i < classes.size(); ++i) {
- ObjPtr<mirror::Class> klass = classes[i].Get();
- if (use_is_assignable_from) {
- if (klass != nullptr && klass->IsAssignableFrom(instance_class)) {
- ++counts[i];
- }
- } else if (instance_class == klass) {
+ if (MatchesClass(obj, classes[i], use_is_assignable_from)) {
++counts[i];
}
}
@@ -1818,11 +1824,12 @@
void Heap::GetInstances(VariableSizedHandleScope& scope,
Handle<mirror::Class> h_class,
+ bool use_is_assignable_from,
int32_t max_count,
std::vector<Handle<mirror::Object>>& instances) {
DCHECK_GE(max_count, 0);
auto instance_collector = [&](mirror::Object* obj) REQUIRES_SHARED(Locks::mutator_lock_) {
- if (obj->GetClass() == h_class.Get()) {
+ if (MatchesClass(obj, h_class, use_is_assignable_from)) {
if (max_count == 0 || instances.size() < static_cast<size_t>(max_count)) {
instances.push_back(scope.NewHandle(obj));
}
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 4d7424c..ac0d82e 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -346,9 +346,10 @@
REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Implements JDWP RT_Instances.
+ // Implements VMDebug.getInstancesOfClasses and JDWP RT_Instances.
void GetInstances(VariableSizedHandleScope& scope,
Handle<mirror::Class> c,
+ bool use_is_assignable_from,
int32_t max_count,
std::vector<Handle<mirror::Object>>& instances)
REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_)
diff --git a/runtime/interpreter/mterp/arm/entry.S b/runtime/interpreter/mterp/arm/entry.S
index de617a9..df4bcc6 100644
--- a/runtime/interpreter/mterp/arm/entry.S
+++ b/runtime/interpreter/mterp/arm/entry.S
@@ -23,7 +23,7 @@
/*
* On entry:
* r0 Thread* self/
- * r1 code_item
+ * r1 insns_
* r2 ShadowFrame
* r3 JValue* result_register
*
@@ -56,6 +56,7 @@
VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame
ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc.
add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode
+ .cfi_register DPC_PSEUDO_REG, rPC
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/arm/header.S b/runtime/interpreter/mterp/arm/header.S
index 51c2ba4..64ab9ef 100644
--- a/runtime/interpreter/mterp/arm/header.S
+++ b/runtime/interpreter/mterp/arm/header.S
@@ -85,6 +85,7 @@
* to expand the macros into assembler assignment statements.
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
diff --git a/runtime/interpreter/mterp/arm64/entry.S b/runtime/interpreter/mterp/arm64/entry.S
index f3d40ff..8d61210 100644
--- a/runtime/interpreter/mterp/arm64/entry.S
+++ b/runtime/interpreter/mterp/arm64/entry.S
@@ -20,7 +20,7 @@
* Interpreter entry point.
* On entry:
* x0 Thread* self/
- * x1 code_item
+ * x1 insns_
* x2 ShadowFrame
* x3 JValue* result_register
*
@@ -46,6 +46,7 @@
add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame
ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc.
add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode
+ .cfi_register DPC_PSEUDO_REG, xPC
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/arm64/header.S b/runtime/interpreter/mterp/arm64/header.S
index 47f12d2..9261b77 100644
--- a/runtime/interpreter/mterp/arm64/header.S
+++ b/runtime/interpreter/mterp/arm64/header.S
@@ -87,6 +87,7 @@
* to expand the macros into assembler assignment statements.
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
diff --git a/runtime/interpreter/mterp/cfi_asm_support.h b/runtime/interpreter/mterp/cfi_asm_support.h
new file mode 100644
index 0000000..a97e153
--- /dev/null
+++ b/runtime/interpreter/mterp/cfi_asm_support.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#ifndef ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_
+#define ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_
+
+/*
+ * To keep track of the Dalvik PC, give assign it a magic register number that
+ * won't be confused with a pysical register. Then, standard .cfi directives
+ * will track the location of it so that it may be extracted during a stack
+ * unwind.
+ *
+ * The Dalvik PC will be in either a physical registor, or the frame.
+ * Encoded from the ASCII string " DEX" -> 0x20 0x44 0x45 0x58
+ */
+#define DPC_PSEUDO_REG 0x20444558
+
+#endif // ART_RUNTIME_INTERPRETER_MTERP_CFI_ASM_SUPPORT_H_
diff --git a/runtime/interpreter/mterp/mips/entry.S b/runtime/interpreter/mterp/mips/entry.S
index 03de985..41b5d56 100644
--- a/runtime/interpreter/mterp/mips/entry.S
+++ b/runtime/interpreter/mterp/mips/entry.S
@@ -32,6 +32,7 @@
*/
ExecuteMterpImpl:
+ .cfi_startproc
.set noreorder
.cpload t9
.set reorder
@@ -53,6 +54,7 @@
EAS2(rREFS, rFP, a0) # point to reference array in shadow frame
lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc
EAS1(rPC, a1, a0) # Create direct pointer to 1st dex opcode
+ .cfi_register DPC_PSEUDO_REG, rPC
EXPORT_PC()
diff --git a/runtime/interpreter/mterp/mips/footer.S b/runtime/interpreter/mterp/mips/footer.S
index 6e1ba1c..1c784ef 100644
--- a/runtime/interpreter/mterp/mips/footer.S
+++ b/runtime/interpreter/mterp/mips/footer.S
@@ -284,4 +284,5 @@
STACK_LOAD_FULL()
jalr zero, ra
+ .cfi_endproc
.end ExecuteMterpImpl
diff --git a/runtime/interpreter/mterp/mips/header.S b/runtime/interpreter/mterp/mips/header.S
index e4552dd..0f7a6f1 100644
--- a/runtime/interpreter/mterp/mips/header.S
+++ b/runtime/interpreter/mterp/mips/header.S
@@ -32,6 +32,7 @@
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
#if (__mips==32) && (__mips_isa_rev>=2)
#define MIPS32REVGE2 /* mips32r2 and greater */
diff --git a/runtime/interpreter/mterp/mips64/entry.S b/runtime/interpreter/mterp/mips64/entry.S
index 436b88d..841a817 100644
--- a/runtime/interpreter/mterp/mips64/entry.S
+++ b/runtime/interpreter/mterp/mips64/entry.S
@@ -73,6 +73,7 @@
dlsa rREFS, v0, rFP, 2
lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2)
dlsa rPC, v0, a1, 1
+ .cfi_register DPC_PSEUDO_REG, rPC
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/mips64/header.S b/runtime/interpreter/mterp/mips64/header.S
index d1acefd..2b550cb 100644
--- a/runtime/interpreter/mterp/mips64/header.S
+++ b/runtime/interpreter/mterp/mips64/header.S
@@ -102,6 +102,7 @@
* to expand the macros into assembler assignment statements.
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
/*
* Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
diff --git a/runtime/interpreter/mterp/out/mterp_arm.S b/runtime/interpreter/mterp/out/mterp_arm.S
index 69d7edb..f3c1124 100644
--- a/runtime/interpreter/mterp/out/mterp_arm.S
+++ b/runtime/interpreter/mterp/out/mterp_arm.S
@@ -92,6 +92,7 @@
* to expand the macros into assembler assignment statements.
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
@@ -341,7 +342,7 @@
/*
* On entry:
* r0 Thread* self/
- * r1 code_item
+ * r1 insns_
* r2 ShadowFrame
* r3 JValue* result_register
*
@@ -374,6 +375,7 @@
VREG_INDEX_TO_ADDR rREFS, r0 @ point to reference array in shadow frame
ldr r0, [r2, #SHADOWFRAME_DEX_PC_OFFSET] @ Get starting dex_pc.
add rPC, r1, r0, lsl #1 @ Create direct pointer to 1st dex opcode
+ .cfi_register DPC_PSEUDO_REG, rPC
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/out/mterp_arm64.S b/runtime/interpreter/mterp/out/mterp_arm64.S
index 82edab4..347d54f 100644
--- a/runtime/interpreter/mterp/out/mterp_arm64.S
+++ b/runtime/interpreter/mterp/out/mterp_arm64.S
@@ -94,6 +94,7 @@
* to expand the macros into assembler assignment statements.
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
#define MTERP_PROFILE_BRANCHES 1
#define MTERP_LOGGING 0
@@ -378,7 +379,7 @@
* Interpreter entry point.
* On entry:
* x0 Thread* self/
- * x1 code_item
+ * x1 insns_
* x2 ShadowFrame
* x3 JValue* result_register
*
@@ -404,6 +405,7 @@
add xREFS, xFP, w0, lsl #2 // point to reference array in shadow frame
ldr w0, [x2, #SHADOWFRAME_DEX_PC_OFFSET] // Get starting dex_pc.
add xPC, x1, w0, lsl #1 // Create direct pointer to 1st dex opcode
+ .cfi_register DPC_PSEUDO_REG, xPC
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/out/mterp_mips.S b/runtime/interpreter/mterp/out/mterp_mips.S
index 8cc1b19..1687afa 100644
--- a/runtime/interpreter/mterp/out/mterp_mips.S
+++ b/runtime/interpreter/mterp/out/mterp_mips.S
@@ -39,6 +39,7 @@
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
#if (__mips==32) && (__mips_isa_rev>=2)
#define MIPS32REVGE2 /* mips32r2 and greater */
@@ -765,6 +766,7 @@
*/
ExecuteMterpImpl:
+ .cfi_startproc
.set noreorder
.cpload t9
.set reorder
@@ -786,6 +788,7 @@
EAS2(rREFS, rFP, a0) # point to reference array in shadow frame
lw a0, SHADOWFRAME_DEX_PC_OFFSET(a2) # Get starting dex_pc
EAS1(rPC, a1, a0) # Create direct pointer to 1st dex opcode
+ .cfi_register DPC_PSEUDO_REG, rPC
EXPORT_PC()
@@ -12842,5 +12845,6 @@
STACK_LOAD_FULL()
jalr zero, ra
+ .cfi_endproc
.end ExecuteMterpImpl
diff --git a/runtime/interpreter/mterp/out/mterp_mips64.S b/runtime/interpreter/mterp/out/mterp_mips64.S
index 139ee25..559c72b 100644
--- a/runtime/interpreter/mterp/out/mterp_mips64.S
+++ b/runtime/interpreter/mterp/out/mterp_mips64.S
@@ -109,6 +109,7 @@
* to expand the macros into assembler assignment statements.
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
/*
* Instead of holding a pointer to the shadow frame, we keep rFP at the base of the vregs. So,
@@ -407,6 +408,7 @@
dlsa rREFS, v0, rFP, 2
lw v0, SHADOWFRAME_DEX_PC_OFFSET(a2)
dlsa rPC, v0, a1, 1
+ .cfi_register DPC_PSEUDO_REG, rPC
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/out/mterp_x86.S b/runtime/interpreter/mterp/out/mterp_x86.S
index cbab61e..0613c9d 100644
--- a/runtime/interpreter/mterp/out/mterp_x86.S
+++ b/runtime/interpreter/mterp/out/mterp_x86.S
@@ -95,6 +95,7 @@
* to expand the macros into assembler assignment statements.
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
/*
* Handle mac compiler specific
@@ -342,7 +343,7 @@
/*
* On entry:
* 0 Thread* self
- * 1 code_item
+ * 1 insns_
* 2 ShadowFrame
* 3 JValue* result_register
*
@@ -379,6 +380,7 @@
leal (rFP, %eax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax
lea (%ecx, %eax, 2), rPC
+ .cfi_register DPC_PSEUDO_REG, rPC
EXPORT_PC
/* Set up for backwards branches & osr profiling */
diff --git a/runtime/interpreter/mterp/out/mterp_x86_64.S b/runtime/interpreter/mterp/out/mterp_x86_64.S
index 83c3e4f..aa91db3 100644
--- a/runtime/interpreter/mterp/out/mterp_x86_64.S
+++ b/runtime/interpreter/mterp/out/mterp_x86_64.S
@@ -91,6 +91,7 @@
* to expand the macros into assembler assignment statements.
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
/*
* Handle mac compiler specific
@@ -328,7 +329,7 @@
/*
* On entry:
* 0 Thread* self
- * 1 code_item
+ * 1 insns_
* 2 ShadowFrame
* 3 JValue* result_register
*
@@ -362,6 +363,7 @@
leaq (rFP, %rax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax
leaq (IN_ARG1, %rax, 2), rPC
+ .cfi_register DPC_PSEUDO_REG, rPC
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/x86/entry.S b/runtime/interpreter/mterp/x86/entry.S
index 055e834..10ca836 100644
--- a/runtime/interpreter/mterp/x86/entry.S
+++ b/runtime/interpreter/mterp/x86/entry.S
@@ -24,7 +24,7 @@
/*
* On entry:
* 0 Thread* self
- * 1 code_item
+ * 1 insns_
* 2 ShadowFrame
* 3 JValue* result_register
*
@@ -61,6 +61,7 @@
leal (rFP, %eax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(%edx), %eax
lea (%ecx, %eax, 2), rPC
+ .cfi_register DPC_PSEUDO_REG, rPC
EXPORT_PC
/* Set up for backwards branches & osr profiling */
diff --git a/runtime/interpreter/mterp/x86/header.S b/runtime/interpreter/mterp/x86/header.S
index 370012f..0e585e8 100644
--- a/runtime/interpreter/mterp/x86/header.S
+++ b/runtime/interpreter/mterp/x86/header.S
@@ -88,6 +88,7 @@
* to expand the macros into assembler assignment statements.
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
/*
* Handle mac compiler specific
diff --git a/runtime/interpreter/mterp/x86_64/entry.S b/runtime/interpreter/mterp/x86_64/entry.S
index 83b845b..d85ef7f 100644
--- a/runtime/interpreter/mterp/x86_64/entry.S
+++ b/runtime/interpreter/mterp/x86_64/entry.S
@@ -24,7 +24,7 @@
/*
* On entry:
* 0 Thread* self
- * 1 code_item
+ * 1 insns_
* 2 ShadowFrame
* 3 JValue* result_register
*
@@ -58,6 +58,7 @@
leaq (rFP, %rax, 4), rREFS
movl SHADOWFRAME_DEX_PC_OFFSET(IN_ARG2), %eax
leaq (IN_ARG1, %rax, 2), rPC
+ .cfi_register DPC_PSEUDO_REG, rPC
EXPORT_PC
/* Starting ibase */
diff --git a/runtime/interpreter/mterp/x86_64/header.S b/runtime/interpreter/mterp/x86_64/header.S
index 9d21f3f..a3ef895 100644
--- a/runtime/interpreter/mterp/x86_64/header.S
+++ b/runtime/interpreter/mterp/x86_64/header.S
@@ -84,6 +84,7 @@
* to expand the macros into assembler assignment statements.
*/
#include "asm_support.h"
+#include "interpreter/mterp/cfi_asm_support.h"
/*
* Handle mac compiler specific
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index cdc55bd..d5520d9 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -1401,26 +1401,38 @@
// Ask the verifier for the dex pcs of all the monitor-enter instructions corresponding to
// the locks held in this stack frame.
- std::vector<uint32_t> monitor_enter_dex_pcs;
+ std::vector<verifier::MethodVerifier::DexLockInfo> monitor_enter_dex_pcs;
verifier::MethodVerifier::FindLocksAtDexPc(m, dex_pc, &monitor_enter_dex_pcs);
- for (uint32_t monitor_dex_pc : monitor_enter_dex_pcs) {
- // The verifier works in terms of the dex pcs of the monitor-enter instructions.
- // We want the registers used by those instructions (so we can read the values out of them).
- const Instruction* monitor_enter_instruction =
- Instruction::At(&code_item->insns_[monitor_dex_pc]);
+ for (verifier::MethodVerifier::DexLockInfo& dex_lock_info : monitor_enter_dex_pcs) {
+ // As a debug check, check that dex PC corresponds to a monitor-enter.
+ if (kIsDebugBuild) {
+ const Instruction* monitor_enter_instruction =
+ Instruction::At(&code_item->insns_[dex_lock_info.dex_pc]);
+ CHECK_EQ(monitor_enter_instruction->Opcode(), Instruction::MONITOR_ENTER)
+ << "expected monitor-enter @" << dex_lock_info.dex_pc << "; was "
+ << reinterpret_cast<const void*>(monitor_enter_instruction);
+ }
- // Quick sanity check.
- CHECK_EQ(monitor_enter_instruction->Opcode(), Instruction::MONITOR_ENTER)
- << "expected monitor-enter @" << monitor_dex_pc << "; was "
- << reinterpret_cast<const void*>(monitor_enter_instruction);
-
- uint16_t monitor_register = monitor_enter_instruction->VRegA();
- uint32_t value;
- bool success = stack_visitor->GetVReg(m, monitor_register, kReferenceVReg, &value);
- CHECK(success) << "Failed to read v" << monitor_register << " of kind "
- << kReferenceVReg << " in method " << m->PrettyMethod();
- mirror::Object* o = reinterpret_cast<mirror::Object*>(value);
- callback(o, callback_context);
+ // Iterate through the set of dex registers, as the compiler may not have held all of them
+ // live.
+ bool success = false;
+ for (uint32_t dex_reg : dex_lock_info.dex_registers) {
+ uint32_t value;
+ success = stack_visitor->GetVReg(m, dex_reg, kReferenceVReg, &value);
+ if (success) {
+ mirror::Object* o = reinterpret_cast<mirror::Object*>(value);
+ callback(o, callback_context);
+ break;
+ }
+ }
+ DCHECK(success) << "Failed to find/read reference for monitor-enter at dex pc "
+ << dex_lock_info.dex_pc
+ << " in method "
+ << m->PrettyMethod();
+ if (!success) {
+ LOG(WARNING) << "Had a lock reported for dex pc " << dex_lock_info.dex_pc
+ << " but was not able to fetch a corresponding object!";
+ }
}
}
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 2235563..c0de374 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -744,6 +744,23 @@
return result;
}
+static jlong DexFile_getStaticSizeOfDexFile(JNIEnv* env, jclass, jobject cookie) {
+ const OatFile* oat_file = nullptr;
+ std::vector<const DexFile*> dex_files;
+ if (!ConvertJavaArrayToDexFiles(env, cookie, /*out */ dex_files, /* out */ oat_file)) {
+ DCHECK(env->ExceptionCheck());
+ return 0;
+ }
+
+ uint64_t file_size = 0;
+ for (auto& dex_file : dex_files) {
+ if (dex_file) {
+ file_size += dex_file->GetHeader().file_size_;
+ }
+ }
+ return static_cast<jlong>(file_size);
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(DexFile, closeDexFile, "(Ljava/lang/Object;)Z"),
NATIVE_METHOD(DexFile,
@@ -779,7 +796,8 @@
NATIVE_METHOD(DexFile, getDexFileStatus,
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"),
NATIVE_METHOD(DexFile, getDexFileOutputPaths,
- "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;")
+ "(Ljava/lang/String;Ljava/lang/String;)[Ljava/lang/String;"),
+ NATIVE_METHOD(DexFile, getStaticSizeOfDexFile, "(Ljava/lang/Object;)J")
};
void register_dalvik_system_DexFile(JNIEnv* env) {
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 2663bea..88a78ab 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -319,6 +319,53 @@
return soa.AddLocalReference<jlongArray>(long_counts);
}
+static jobjectArray VMDebug_getInstancesOfClasses(JNIEnv* env,
+ jclass,
+ jobjectArray javaClasses,
+ jboolean includeAssignable) {
+ ScopedObjectAccess soa(env);
+ StackHandleScope<2> hs(soa.Self());
+ Handle<mirror::ObjectArray<mirror::Class>> classes = hs.NewHandle(
+ soa.Decode<mirror::ObjectArray<mirror::Class>>(javaClasses));
+ if (classes == nullptr) {
+ return nullptr;
+ }
+
+ jclass object_array_class = env->FindClass("[Ljava/lang/Object;");
+ if (env->ExceptionCheck() == JNI_TRUE) {
+ return nullptr;
+ }
+ CHECK(object_array_class != nullptr);
+
+ size_t num_classes = classes->GetLength();
+ jobjectArray result = env->NewObjectArray(num_classes, object_array_class, nullptr);
+ if (env->ExceptionCheck() == JNI_TRUE) {
+ return nullptr;
+ }
+
+ gc::Heap* const heap = Runtime::Current()->GetHeap();
+ MutableHandle<mirror::Class> h_class(hs.NewHandle<mirror::Class>(nullptr));
+ for (size_t i = 0; i < num_classes; ++i) {
+ h_class.Assign(classes->Get(i));
+
+ VariableSizedHandleScope hs2(soa.Self());
+ std::vector<Handle<mirror::Object>> raw_instances;
+ heap->GetInstances(hs2, h_class, includeAssignable, /* max_count */ 0, raw_instances);
+ jobjectArray array = env->NewObjectArray(raw_instances.size(),
+ WellKnownClasses::java_lang_Object,
+ nullptr);
+ if (env->ExceptionCheck() == JNI_TRUE) {
+ return nullptr;
+ }
+
+ for (size_t j = 0; j < raw_instances.size(); ++j) {
+ env->SetObjectArrayElement(array, j, raw_instances[j].ToJObject());
+ }
+ env->SetObjectArrayElement(result, i, array);
+ }
+ return result;
+}
+
// We export the VM internal per-heap-space size/alloc/free metrics
// for the zygote space, alloc space (application heap), and the large
// object space for dumpsys meminfo. The other memory region data such
@@ -534,6 +581,7 @@
NATIVE_METHOD(VMDebug, dumpReferenceTables, "()V"),
NATIVE_METHOD(VMDebug, getAllocCount, "(I)I"),
NATIVE_METHOD(VMDebug, getHeapSpaceStats, "([J)V"),
+ NATIVE_METHOD(VMDebug, getInstancesOfClasses, "([Ljava/lang/Class;Z)[[Ljava/lang/Object;"),
NATIVE_METHOD(VMDebug, getInstructionCount, "([I)V"),
FAST_NATIVE_METHOD(VMDebug, getLoadedClassCount, "()I"),
NATIVE_METHOD(VMDebug, getVmFeatureList, "()[Ljava/lang/String;"),
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index a75157d..c6ba2f7 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -592,8 +592,10 @@
STLDeleteElements(&failure_messages_);
}
-void MethodVerifier::FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc,
- std::vector<uint32_t>* monitor_enter_dex_pcs) {
+void MethodVerifier::FindLocksAtDexPc(
+ ArtMethod* m,
+ uint32_t dex_pc,
+ std::vector<MethodVerifier::DexLockInfo>* monitor_enter_dex_pcs) {
StackHandleScope<2> hs(Thread::Current());
Handle<mirror::DexCache> dex_cache(hs.NewHandle(m->GetDexCache()));
Handle<mirror::ClassLoader> class_loader(hs.NewHandle(m->GetClassLoader()));
@@ -2036,8 +2038,20 @@
// for a thread to be suspended).
if (monitor_enter_dex_pcs_ != nullptr && work_insn_idx_ == interesting_dex_pc_) {
monitor_enter_dex_pcs_->clear(); // The new work line is more accurate than the previous one.
- for (size_t i = 0; i < work_line_->GetMonitorEnterCount(); ++i) {
- monitor_enter_dex_pcs_->push_back(work_line_->GetMonitorEnterDexPc(i));
+
+ std::map<uint32_t, DexLockInfo> depth_to_lock_info;
+ auto collector = [&](uint32_t dex_reg, uint32_t depth) {
+ auto insert_pair = depth_to_lock_info.emplace(depth, DexLockInfo(depth));
+ auto it = insert_pair.first;
+ auto set_insert_pair = it->second.dex_registers.insert(dex_reg);
+ DCHECK(set_insert_pair.second);
+ };
+ work_line_->IterateRegToLockDepths(collector);
+ for (auto& pair : depth_to_lock_info) {
+ monitor_enter_dex_pcs_->push_back(pair.second);
+ // Map depth to dex PC.
+ (*monitor_enter_dex_pcs_)[monitor_enter_dex_pcs_->size() - 1].dex_pc =
+ work_line_->GetMonitorEnterDexPc(pair.second.dex_pc);
}
}
diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h
index 813ce87..c885914 100644
--- a/runtime/verifier/method_verifier.h
+++ b/runtime/verifier/method_verifier.h
@@ -149,10 +149,21 @@
void Dump(std::ostream& os) REQUIRES_SHARED(Locks::mutator_lock_);
void Dump(VariableIndentationOutputStream* vios) REQUIRES_SHARED(Locks::mutator_lock_);
+ // Information structure for a lock held at a certain point in time.
+ struct DexLockInfo {
+ // The registers aliasing the lock.
+ std::set<uint32_t> dex_registers;
+ // The dex PC of the monitor-enter instruction.
+ uint32_t dex_pc;
+
+ explicit DexLockInfo(uint32_t dex_pc_in) {
+ dex_pc = dex_pc_in;
+ }
+ };
// Fills 'monitor_enter_dex_pcs' with the dex pcs of the monitor-enter instructions corresponding
// to the locks held at 'dex_pc' in method 'm'.
static void FindLocksAtDexPc(ArtMethod* m, uint32_t dex_pc,
- std::vector<uint32_t>* monitor_enter_dex_pcs)
+ std::vector<DexLockInfo>* monitor_enter_dex_pcs)
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns the accessed field corresponding to the quick instruction's field
@@ -750,7 +761,7 @@
uint32_t interesting_dex_pc_;
// The container into which FindLocksAtDexPc should write the registers containing held locks,
// null if we're not doing FindLocksAtDexPc.
- std::vector<uint32_t>* monitor_enter_dex_pcs_;
+ std::vector<DexLockInfo>* monitor_enter_dex_pcs_;
// The types of any error that occurs.
std::vector<VerifyError> failures_;
diff --git a/runtime/verifier/register_line.h b/runtime/verifier/register_line.h
index 221aa80..71eb4d6 100644
--- a/runtime/verifier/register_line.h
+++ b/runtime/verifier/register_line.h
@@ -353,6 +353,23 @@
return monitors_[i];
}
+ // We give access to the lock depth map to avoid an expensive poll loop for FindLocksAtDexPC.
+ template <typename T>
+ void IterateRegToLockDepths(T fn) const {
+ for (const auto& pair : reg_to_lock_depths_) {
+ const uint32_t reg = pair.first;
+ uint32_t depths = pair.second;
+ uint32_t depth = 0;
+ while (depths != 0) {
+ if ((depths & 1) != 0) {
+ fn(reg, depth);
+ }
+ depths >>= 1;
+ depth++;
+ }
+ }
+ }
+
private:
void CopyRegToLockDepth(size_t dst, size_t src) {
auto it = reg_to_lock_depths_.find(src);
diff --git a/test/071-dexfile-get-static-size/build b/test/071-dexfile-get-static-size/build
new file mode 100755
index 0000000..0bba66d
--- /dev/null
+++ b/test/071-dexfile-get-static-size/build
@@ -0,0 +1,30 @@
+#!/bin/bash
+#
+# Copyright 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.
+
+./default-build "$@"
+
+# Create and add as resources to the test jar file:
+# 1. test1.dex
+# 2. test2.dex
+# 3. test-jar.jar, containing test1.dex as classes.dex
+# 4. multi-jar.jar, containing test1.dex as classes.dex and test2.dex as classes2.dex
+mkdir test-jar
+cp test1.dex test-jar/classes.dex
+cp test2.dex test-jar/classes2.dex
+zip -j test-jar.jar test-jar/classes.dex
+zip -j multi-jar.jar test-jar/classes.dex test-jar/classes2.dex
+jar uf ${TEST_NAME}.jar test1.dex test2.dex test-jar.jar multi-jar.jar
+
diff --git a/test/071-dexfile-get-static-size/expected.txt b/test/071-dexfile-get-static-size/expected.txt
new file mode 100644
index 0000000..dfb77c3
--- /dev/null
+++ b/test/071-dexfile-get-static-size/expected.txt
@@ -0,0 +1,4 @@
+Size for test1.dex: 1864
+Size for test2.dex: 1264
+Size for test-jar.jar: 1864
+Size for multi-jar.jar: 3128
diff --git a/test/071-dexfile-get-static-size/info.txt b/test/071-dexfile-get-static-size/info.txt
new file mode 100644
index 0000000..5b528e8
--- /dev/null
+++ b/test/071-dexfile-get-static-size/info.txt
@@ -0,0 +1,3 @@
+Test DexFile.getStaticSizeOfDexFile API.
+
+test1.dex and test2.dex are arbitrary valid dex files.
diff --git a/test/071-dexfile-get-static-size/src/Main.java b/test/071-dexfile-get-static-size/src/Main.java
new file mode 100644
index 0000000..4bf4538
--- /dev/null
+++ b/test/071-dexfile-get-static-size/src/Main.java
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.FileOutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+
+public class Main {
+ private static void extractResource(String resource, String filename) throws Exception {
+ ClassLoader loader = Main.class.getClassLoader();
+ InputStream is = loader.getResourceAsStream(resource);
+ OutputStream os = new FileOutputStream(filename);
+ int read;
+ byte[] buf = new byte[4096];
+ while ((read = is.read(buf)) >= 0) {
+ os.write(buf, 0, read);
+ }
+ is.close();
+ os.close();
+ }
+
+ private static long getDexFileSize(String filename) throws Exception {
+ ClassLoader loader = Main.class.getClassLoader();
+ Class<?> DexFile = loader.loadClass("dalvik.system.DexFile");
+ Method DexFile_loadDex = DexFile.getMethod("loadDex",
+ String.class,
+ String.class,
+ Integer.TYPE);
+ Method DexFile_getStaticSizeOfDexFile = DexFile.getMethod("getStaticSizeOfDexFile");
+ Object dexFile = DexFile_loadDex.invoke(null, filename, null, 0);
+ return (Long) DexFile_getStaticSizeOfDexFile.invoke(dexFile);
+ }
+
+ private static void test(String resource) throws Exception {
+ String filename = System.getenv("DEX_LOCATION") + "/" + resource;
+ extractResource(resource, filename);
+ long size = getDexFileSize(filename);
+ System.out.println("Size for " + resource + ": " + size);
+ }
+
+ public static void main(String[] args) throws Exception {
+ test("test1.dex");
+ test("test2.dex");
+ test("test-jar.jar");
+ test("multi-jar.jar");
+ }
+}
diff --git a/test/071-dexfile-get-static-size/test1.dex b/test/071-dexfile-get-static-size/test1.dex
new file mode 100644
index 0000000..84602d0
--- /dev/null
+++ b/test/071-dexfile-get-static-size/test1.dex
Binary files differ
diff --git a/test/071-dexfile-get-static-size/test2.dex b/test/071-dexfile-get-static-size/test2.dex
new file mode 100644
index 0000000..a07c46e
--- /dev/null
+++ b/test/071-dexfile-get-static-size/test2.dex
Binary files differ
diff --git a/test/099-vmdebug/expected.txt b/test/099-vmdebug/expected.txt
index b8d72f6..f7801de 100644
--- a/test/099-vmdebug/expected.txt
+++ b/test/099-vmdebug/expected.txt
@@ -23,3 +23,9 @@
Instances of ClassA assignable 3
Array counts [2, 1, 0]
Array counts assignable [3, 1, 0]
+ClassD got 3, combined mask: 13
+ClassE got 2, combined mask: 18
+null got 0
+ClassD assignable got 5, combined mask: 31
+ClassE assignable got 2, combined mask: 18
+null assignable got 0
diff --git a/test/099-vmdebug/info.txt b/test/099-vmdebug/info.txt
index 7f88086..873429e 100644
--- a/test/099-vmdebug/info.txt
+++ b/test/099-vmdebug/info.txt
@@ -1 +1 @@
-Tests of private dalvik.system.VMDebug support for method tracing.
+Tests of dalvik.system.VMDebug APIs.
diff --git a/test/099-vmdebug/src/Main.java b/test/099-vmdebug/src/Main.java
index 90ad315..e0d829a 100644
--- a/test/099-vmdebug/src/Main.java
+++ b/test/099-vmdebug/src/Main.java
@@ -33,6 +33,7 @@
}
testMethodTracing();
testCountInstances();
+ testGetInstances();
testRuntimeStat();
testRuntimeStats();
}
@@ -249,6 +250,59 @@
System.out.println("Array counts assignable " + Arrays.toString(counts));
}
+ static class ClassD {
+ public int mask;
+
+ public ClassD(int mask) {
+ this.mask = mask;
+ }
+ }
+
+ static class ClassE extends ClassD {
+ public ClassE(int mask) {
+ super(mask);
+ }
+ }
+
+ private static void testGetInstances() throws Exception {
+ ArrayList<Object> l = new ArrayList<Object>();
+ l.add(new ClassD(0x01));
+ l.add(new ClassE(0x02));
+ l.add(new ClassD(0x04));
+ l.add(new ClassD(0x08));
+ l.add(new ClassE(0x10));
+ Runtime.getRuntime().gc();
+ Class<?>[] classes = new Class<?>[] {ClassD.class, ClassE.class, null};
+ Object[][] instances = VMDebug.getInstancesOfClasses(classes, false);
+
+ int mask = 0;
+ for (Object instance : instances[0]) {
+ mask |= ((ClassD)instance).mask;
+ }
+ System.out.println("ClassD got " + instances[0].length + ", combined mask: " + mask);
+
+ mask = 0;
+ for (Object instance : instances[1]) {
+ mask |= ((ClassD)instance).mask;
+ }
+ System.out.println("ClassE got " + instances[1].length + ", combined mask: " + mask);
+ System.out.println("null got " + instances[2].length);
+
+ instances = VMDebug.getInstancesOfClasses(classes, true);
+ mask = 0;
+ for (Object instance : instances[0]) {
+ mask |= ((ClassD)instance).mask;
+ }
+ System.out.println("ClassD assignable got " + instances[0].length + ", combined mask: " + mask);
+
+ mask = 0;
+ for (Object instance : instances[1]) {
+ mask |= ((ClassD)instance).mask;
+ }
+ System.out.println("ClassE assignable got " + instances[1].length + ", combined mask: " + mask);
+ System.out.println("null assignable got " + instances[2].length);
+ }
+
private static class VMDebug {
private static final Method startMethodTracingMethod;
private static final Method stopMethodTracingMethod;
@@ -257,6 +311,7 @@
private static final Method getRuntimeStatsMethod;
private static final Method countInstancesOfClassMethod;
private static final Method countInstancesOfClassesMethod;
+ private static final Method getInstancesOfClassesMethod;
static {
try {
Class<?> c = Class.forName("dalvik.system.VMDebug");
@@ -270,6 +325,8 @@
Class.class, Boolean.TYPE);
countInstancesOfClassesMethod = c.getDeclaredMethod("countInstancesOfClasses",
Class[].class, Boolean.TYPE);
+ getInstancesOfClassesMethod = c.getDeclaredMethod("getInstancesOfClasses",
+ Class[].class, Boolean.TYPE);
} catch (Exception e) {
throw new RuntimeException(e);
}
@@ -300,5 +357,9 @@
return (long[]) countInstancesOfClassesMethod.invoke(
null, new Object[]{classes, assignable});
}
+ public static Object[][] getInstancesOfClasses(Class<?>[] classes, boolean assignable) throws Exception {
+ return (Object[][]) getInstancesOfClassesMethod.invoke(
+ null, new Object[]{classes, assignable});
+ }
}
}
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 58b33be..66270fd 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -104,20 +104,6 @@
}
#endif
-// Currently we have to fall back to our own loader for the boot image when it's compiled PIC
-// because its base is zero. Thus in-process unwinding through it won't work. This is a helper
-// detecting this.
-#if __linux__
-static bool IsPicImage() {
- std::vector<gc::space::ImageSpace*> image_spaces =
- Runtime::Current()->GetHeap()->GetBootImageSpaces();
- CHECK(!image_spaces.empty()); // We should be running with an image.
- const OatFile* oat_file = image_spaces[0]->GetOatFile();
- CHECK(oat_file != nullptr); // We should have an oat file to go with the image.
- return oat_file->IsPic();
-}
-#endif
-
extern "C" JNIEXPORT jboolean JNICALL Java_Main_unwindInProcess(
JNIEnv*,
jobject,
@@ -125,11 +111,6 @@
jint,
jboolean) {
#if __linux__
- if (IsPicImage()) {
- LOG(INFO) << "Image is pic, in-process unwinding check bypassed.";
- return JNI_TRUE;
- }
-
// TODO: What to do on Valgrind?
std::unique_ptr<Backtrace> bt(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, GetTid()));
diff --git a/test/167-visit-locks/expected.txt b/test/167-visit-locks/expected.txt
new file mode 100644
index 0000000..5157c64
--- /dev/null
+++ b/test/167-visit-locks/expected.txt
@@ -0,0 +1,3 @@
+JNI_OnLoad called
+First
+Second
diff --git a/test/167-visit-locks/info.txt b/test/167-visit-locks/info.txt
new file mode 100644
index 0000000..d849bc3
--- /dev/null
+++ b/test/167-visit-locks/info.txt
@@ -0,0 +1 @@
+Regression test for b/68703210
diff --git a/test/167-visit-locks/run b/test/167-visit-locks/run
new file mode 100644
index 0000000..9365411
--- /dev/null
+++ b/test/167-visit-locks/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# 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.
+
+# Use a smaller heap so it's easier to potentially fill up.
+exec ${RUN} $@ --runtime-option -Xmx2m
diff --git a/test/167-visit-locks/smali/TestSync.smali b/test/167-visit-locks/smali/TestSync.smali
new file mode 100644
index 0000000..5e68ad7
--- /dev/null
+++ b/test/167-visit-locks/smali/TestSync.smali
@@ -0,0 +1,119 @@
+.class LTestSync;
+.super Ljava/lang/Object;
+.source "Main.java"
+
+
+# direct methods
+.method constructor <init>()V
+ .registers 1
+
+ .prologue
+ .line 6
+ invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+
+ return-void
+.end method
+
+.method public static run()V
+ # v0-v2 were generated by javac+dx for the original src code, keeping them.
+ # v10..v19 are for tracking, aliasing and manipulating the first lock.
+ # v20..v29 are for tracking, aliasing and manipulating the second lock.
+ .registers 30
+
+ .prologue
+ .line 8
+ const-string v1, "First"
+
+ .line 9
+ const-string v2, "Second"
+
+ move-object v10, v1
+ const v1, 0x1
+
+ .line 10
+ monitor-enter v10
+
+ # Introduce a range of dead copies.
+ move-object v11, v10
+ move-object v12, v10
+ move-object v13, v10
+ move-object v14, v10
+ move-object v15, v10
+ move-object/16 v16, v10
+ move-object/16 v17, v10
+ move-object/16 v18, v10
+
+ # Introduce a copy that we'll use for unlock.
+ move-object/16 v19, v10
+
+ # Clobber the original alias.
+ const v10, 0x3
+
+ move-object/16 v20, v2
+ const v2, 0x2
+
+ .line 11
+ :try_start_b
+ monitor-enter v20
+ :try_end_c
+
+ # Introduce a range of dead copies.
+ move-object/16 v21, v20
+ move-object/16 v22, v20
+ move-object/16 v23, v20
+ move-object/16 v24, v20
+ move-object/16 v25, v20
+ move-object/16 v26, v20
+ move-object/16 v27, v20
+
+ # Introduce another copy that we will hold live.
+ move-object/16 v28, v20
+
+ # Clobber the original alias.
+ const v20, 0x5
+
+ # Introduce another copy that we'll use for unlock.
+ move-object/16 v29, v28
+
+ .catchall {:try_start_b .. :try_end_c} :catchall_15
+
+ .line 12
+ :try_start_c
+ invoke-static/range { v28 }, LMain;->run(Ljava/lang/Object;)V
+
+ .line 13
+ monitor-exit v29
+ :try_end_10
+ .catchall {:try_start_c .. :try_end_10} :catchall_12
+
+ .line 14
+ :try_start_10
+ monitor-exit v19
+ :try_end_11
+ .catchall {:try_start_10 .. :try_end_11} :catchall_15
+
+ .line 15
+ return-void
+
+ .line 13
+ :catchall_12
+ move-exception v0
+
+ :try_start_13
+ monitor-exit v29
+ :try_end_14
+ .catchall {:try_start_13 .. :try_end_14} :catchall_12
+
+ :try_start_14
+ throw v0
+
+ .line 14
+ :catchall_15
+ move-exception v0
+
+ monitor-exit v19
+ :try_end_17
+ .catchall {:try_start_14 .. :try_end_17} :catchall_15
+
+ throw v0
+.end method
diff --git a/test/167-visit-locks/src/Main.java b/test/167-visit-locks/src/Main.java
new file mode 100644
index 0000000..d8da927
--- /dev/null
+++ b/test/167-visit-locks/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[0]);
+
+ Class.forName("TestSync").getMethod("run").invoke(null);
+ }
+
+ public static void run(Object o) {
+ testVisitLocks();
+ }
+
+ public static native void testVisitLocks();
+}
diff --git a/test/167-visit-locks/visit_locks.cc b/test/167-visit-locks/visit_locks.cc
new file mode 100644
index 0000000..e79c880
--- /dev/null
+++ b/test/167-visit-locks/visit_locks.cc
@@ -0,0 +1,74 @@
+/*
+ * 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 "jni.h"
+
+#include <iostream>
+
+#include "android-base/logging.h"
+
+#include "arch/context.h"
+#include "art_method.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "mirror/object-inl.h"
+#include "mirror/string.h"
+#include "monitor.h"
+#include "scoped_thread_state_change-inl.h"
+#include "stack.h"
+#include "thread-current-inl.h"
+
+namespace art {
+
+extern "C" JNIEXPORT void JNICALL Java_Main_testVisitLocks(JNIEnv*, jclass) {
+ ScopedObjectAccess soa(Thread::Current());
+
+ class VisitLocks : public StackVisitor {
+ public:
+ VisitLocks(Thread* thread, Context* context)
+ : StackVisitor(thread, context, StackWalkKind::kIncludeInlinedFrames) {
+ }
+
+ bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
+ ArtMethod* m = GetMethod();
+
+ // Ignore runtime methods.
+ if (m == nullptr || m->IsRuntimeMethod()) {
+ return true;
+ }
+
+ if (m->PrettyMethod() == "void TestSync.run()") {
+ // Interesting frame.
+ Monitor::VisitLocks(this, Callback, nullptr);
+ return false;
+ }
+
+ return true;
+ }
+
+ static void Callback(mirror::Object* obj, void*) REQUIRES_SHARED(Locks::mutator_lock_) {
+ CHECK(obj != nullptr);
+ CHECK(obj->IsString());
+ std::cerr << obj->AsString()->ToModifiedUtf8() << std::endl;
+ }
+ };
+ Context* context = Context::Create();
+ VisitLocks vl(soa.Self(), context);
+ vl.WalkStack();
+ delete context;
+}
+
+} // namespace art
diff --git a/test/667-jit-jni-stub/run b/test/667-jit-jni-stub/run
index 1877be4..f235c6b 100755
--- a/test/667-jit-jni-stub/run
+++ b/test/667-jit-jni-stub/run
@@ -15,4 +15,5 @@
# limitations under the License.
# Disable AOT compilation of JNI stubs.
-${RUN} "${@}" --no-prebuild --no-dex2oat
+# Ensure this test is not subject to unexpected code collection.
+${RUN} "${@}" --no-prebuild --no-dex2oat --runtime-option -Xjitinitialsize:32M
diff --git a/test/711-checker-type-conversion/src/Main.java b/test/711-checker-type-conversion/src/Main.java
index 64ffcd2..2c9c3a1 100644
--- a/test/711-checker-type-conversion/src/Main.java
+++ b/test/711-checker-type-conversion/src/Main.java
@@ -22,20 +22,15 @@
}
}
- /// CHECK-START: byte Main.getByte1() instruction_simplifier (before)
+ /// CHECK-START: byte Main.getByte1() constant_folding (before)
/// CHECK: TypeConversion
/// CHECK: TypeConversion
/// CHECK: Add
/// CHECK: TypeConversion
- /// CHECK-START: byte Main.getByte1() instruction_simplifier (after)
+ /// CHECK-START: byte Main.getByte1() constant_folding (after)
/// CHECK-NOT: TypeConversion
- /// CHECK: Add
- /// CHECK: TypeConversion
-
- /// CHECK-START: byte Main.getByte1() instruction_simplifier$before_codegen (after)
/// CHECK-NOT: Add
- /// CHECK-NOT: TypeConversion
static byte getByte1() {
int i = -2;
@@ -43,20 +38,15 @@
return (byte)((byte)i + (byte)j);
}
- /// CHECK-START: byte Main.getByte2() instruction_simplifier (before)
+ /// CHECK-START: byte Main.getByte2() constant_folding (before)
/// CHECK: TypeConversion
/// CHECK: TypeConversion
/// CHECK: Add
/// CHECK: TypeConversion
- /// CHECK-START: byte Main.getByte2() instruction_simplifier (after)
+ /// CHECK-START: byte Main.getByte2() constant_folding (after)
/// CHECK-NOT: TypeConversion
- /// CHECK: Add
- /// CHECK: TypeConversion
-
- /// CHECK-START: byte Main.getByte2() instruction_simplifier$before_codegen (after)
/// CHECK-NOT: Add
- /// CHECK: TypeConversion
static byte getByte2() {
int i = -100;
@@ -64,8 +54,24 @@
return (byte)((byte)i + (byte)j);
}
+ /// CHECK-START: byte Main.getByte3() constant_folding (before)
+ /// CHECK: TypeConversion
+ /// CHECK: TypeConversion
+ /// CHECK: Add
+ /// CHECK: TypeConversion
+
+ /// CHECK-START: byte Main.getByte2() constant_folding (after)
+ /// CHECK-NOT: TypeConversion
+ /// CHECK-NOT: Add
+
+ static byte getByte3() {
+ long i = 0xabcdabcdabcdL;
+ return (byte)((byte)i + (byte)i);
+ }
+
public static void main(String[] args) {
assertByteEquals(getByte1(), (byte)-5);
assertByteEquals(getByte2(), (byte)(-201));
+ assertByteEquals(getByte3(), (byte)(0xcd + 0xcd));
}
}
diff --git a/test/Android.bp b/test/Android.bp
index 2d526d2..01e424d 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -364,8 +364,9 @@
"141-class-unload/jni_unload.cc",
"148-multithread-gc-annotations/gc_coverage.cc",
"149-suspend-all-stress/suspend_all.cc",
- "203-multi-checkpoint/multi_checkpoint.cc",
"154-gc-loop/heap_interface.cc",
+ "167-visit-locks/visit_locks.cc",
+ "203-multi-checkpoint/multi_checkpoint.cc",
"454-get-vreg/get_vreg_jni.cc",
"457-regs/regs_jni.cc",
"461-get-reference-vreg/get_reference_vreg_jni.cc",
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index e750382..554b8a5 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -874,7 +874,7 @@
global run_all_configs
parser = argparse.ArgumentParser(description="Runs all or a subset of the ART test suite.")
- parser.add_argument('-t', '--test', dest='test', help='name of the test')
+ parser.add_argument('-t', '--test', action='append', dest='tests', help='name(s) of the test(s)')
parser.add_argument('-j', type=int, dest='n_thread')
parser.add_argument('--timeout', default=timeout, type=int, dest='timeout')
for variant in TOTAL_VARIANTS_SET:
@@ -906,10 +906,12 @@
options = setup_env_for_build_target(target_config[options['build_target']],
parser, options)
- test = ''
+ tests = None
env.EXTRA_DISABLED_TESTS.update(set(options['skips']))
- if options['test']:
- test = parse_test_name(options['test'])
+ if options['tests']:
+ tests = set()
+ for test_name in options['tests']:
+ tests |= parse_test_name(test_name)
for variant_type in VARIANT_TYPE_DICT:
for variant in VARIANT_TYPE_DICT[variant_type]:
@@ -935,11 +937,11 @@
if options['run_all']:
run_all_configs = True
- return test
+ return tests
def main():
gather_test_info()
- user_requested_test = parse_option()
+ user_requested_tests = parse_option()
setup_test_env()
if build:
build_targets = ''
@@ -956,8 +958,8 @@
build_command += ' dist'
if subprocess.call(build_command.split()):
sys.exit(1)
- if user_requested_test:
- test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_test,))
+ if user_requested_tests:
+ test_runner_thread = threading.Thread(target=run_tests, args=(user_requested_tests,))
else:
test_runner_thread = threading.Thread(target=run_tests, args=(RUN_TEST_SET,))
test_runner_thread.daemon = True
diff --git a/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java
index d7b1dd7..3bed29b 100644
--- a/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java
+++ b/tools/ahat/src/main/com/android/ahat/heapdump/Parser.java
@@ -853,7 +853,7 @@
}
public long getId() {
- return mBuffer.getInt();
+ return mBuffer.getInt() & 0xFFFFFFFFL;
}
public boolean getBool() {
diff --git a/tools/libjdwp_art_failures.txt b/tools/libjdwp_art_failures.txt
index abcc728..bf1c937 100644
--- a/tools/libjdwp_art_failures.txt
+++ b/tools/libjdwp_art_failures.txt
@@ -64,6 +64,11 @@
"org.apache.harmony.jpda.tests.jdwp.EventModifiers.InstanceOnlyModifierTest#testMethodExit",
"org.apache.harmony.jpda.tests.jdwp.EventModifiers.InstanceOnlyModifierTest#testMethodExitWithReturnValue" ]
},
+{
+ description: "Tests for VMDebug functionality not implemented in the upstream libjdwp",
+ result: EXEC_FAILED,
+ name: "org.apache.harmony.jpda.tests.jdwp.VMDebug.VMDebugTest#testVMDebug"
+},
/* TODO Categorize these failures more. */
{
description: "Tests that fail on both ART and RI. These tests are likely incorrect",