Merge "Move registration into android package"
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e2f8d92..414a43c 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -472,7 +472,8 @@
? cls->FindDeclaredDirectMethod(method_name, signature, image_size)
: cls->FindDeclaredVirtualMethod(method_name, signature, image_size);
if (method == nullptr) {
- LOG(FATAL) << "Could not find method of intrinsic " << class_name << method_name << signature;
+ LOG(FATAL) << "Could not find method of intrinsic "
+ << class_name << " " << method_name << " " << signature;
}
DCHECK_EQ(method->GetInvokeType(), invoke_type);
method->SetIntrinsic(static_cast<uint32_t>(intrinsic));
@@ -497,11 +498,13 @@
// those compilations will pick up a boot image that have the ArtMethod already
// set with the intrinsics flag.
ScopedObjectAccess soa(Thread::Current());
-#define OPTIMIZING_INTRINSICS(Name, InvokeType, NeedsEnvironmentOrCache, SideEffects, Exceptions, ClassName, MethodName, Signature) \
+#define SETUP_INTRINSICS(Name, InvokeType, NeedsEnvironmentOrCache, SideEffects, Exceptions, \
+ ClassName, MethodName, Signature) \
SetupIntrinsic(soa.Self(), Intrinsics::k##Name, InvokeType, ClassName, MethodName, Signature);
#include "intrinsics_list.h"
-INTRINSICS_LIST(OPTIMIZING_INTRINSICS)
+INTRINSICS_LIST(SETUP_INTRINSICS)
#undef INTRINSICS_LIST
+#undef SETUP_INTRINSICS
}
// Compile:
// 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 8ae04a1..13c73dc 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1389,7 +1389,7 @@
void ImageWriter::CalculateNewObjectOffsets() {
Thread* const self = Thread::Current();
- StackHandleScopeCollection handles(self);
+ VariableSizedHandleScope handles(self);
std::vector<Handle<ObjectArray<Object>>> image_roots;
for (size_t i = 0, size = oat_filenames_.size(); i != size; ++i) {
image_roots.push_back(handles.NewHandle(CreateImageRoots(i)));
diff --git a/compiler/intrinsics_list.h b/compiler/intrinsics_list.h
index 5877f57..b617387 100644
--- a/compiler/intrinsics_list.h
+++ b/compiler/intrinsics_list.h
@@ -1,7 +1,7 @@
/*
- * Copyright (C, "", "", "") 2015 The Android Open Source Project
+ * Copyright (C) 2015 The Android Open Source Project
*
- * Licensed under the Apache License, Version 2.0 (the "License", "", "", "");
+ * 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
*
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc
index 0cf5f18..afb8fce 100644
--- a/compiler/jni/jni_compiler_test.cc
+++ b/compiler/jni/jni_compiler_test.cc
@@ -530,7 +530,7 @@
<< "invocations have finished (as before they were invoked).";
}
- HandleScope* const handle_scope_;
+ BaseHandleScope* const handle_scope_;
};
static void expectNumStackReferences(size_t val1, size_t val2) {
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 7e58d78..bfb342f 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -322,7 +322,7 @@
ThreadOffset<kPointerSize> jni_start =
is_synchronized
? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStartSynchronized)
- : (is_fast_native
+ : ((is_fast_native && !reference_return) // TODO: support @FastNative returning obj
? QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodFastStart)
: QUICK_ENTRYPOINT_OFFSET(kPointerSize, pJniMethodStart));
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 580ef72..f896f11 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -43,7 +43,7 @@
OptimizingCompilerStats* compiler_stats,
const uint8_t* interpreter_metadata,
Handle<mirror::DexCache> dex_cache,
- StackHandleScopeCollection* handles)
+ VariableSizedHandleScope* handles)
: graph_(graph),
dex_file_(dex_file),
code_item_(code_item),
@@ -68,7 +68,7 @@
// Only for unit testing.
HGraphBuilder(HGraph* graph,
const DexFile::CodeItem& code_item,
- StackHandleScopeCollection* handles,
+ VariableSizedHandleScope* handles,
Primitive::Type return_type = Primitive::kPrimInt)
: graph_(graph),
dex_file_(nullptr),
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index c501ccf..55fcb12 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -87,11 +87,12 @@
: HOptimization(graph, kInductionPassName),
global_depth_(0),
stack_(graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)),
- scc_(graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)),
map_(std::less<HInstruction*>(),
graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)),
+ scc_(graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)),
cycle_(std::less<HInstruction*>(),
graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)),
+ type_(Primitive::kPrimVoid),
induction_(std::less<HLoopInformation*>(),
graph->GetArena()->Adapter(kArenaAllocInductionVarAnalysis)) {
}
@@ -103,7 +104,6 @@
for (HReversePostOrderIterator it_graph(*graph_); !it_graph.Done(); it_graph.Advance()) {
HBasicBlock* graph_block = it_graph.Current();
// Don't analyze irreducible loops.
- // TODO(ajcbik): could/should we remove this restriction?
if (graph_block->IsLoopHeader() && !graph_block->GetLoopInformation()->IsIrreducible()) {
VisitLoop(graph_block->GetLoopInformation());
}
@@ -121,7 +121,7 @@
HBasicBlock* loop_block = it_loop.Current();
DCHECK(loop_block->IsInLoop());
if (loop_block->GetLoopInformation() != loop) {
- continue; // Inner loops already visited.
+ continue; // Inner loops visited later.
}
// Visit phi-operations and instructions.
for (HInstructionIterator it(loop_block->GetPhis()); !it.Done(); it.Advance()) {
@@ -285,6 +285,9 @@
} else if (instruction->IsSub()) {
update = SolveAddSub(
loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true);
+ } else if (instruction->IsXor()) {
+ update = SolveXor(
+ loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), true);
} else if (instruction->IsTypeConversion()) {
update = SolveCnv(instruction->AsTypeConversion());
}
@@ -553,6 +556,27 @@
return nullptr;
}
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveXor(HLoopInformation* loop,
+ HInstruction* entry_phi,
+ HInstruction* instruction,
+ HInstruction* x,
+ HInstruction* y,
+ bool is_first_call) {
+ InductionInfo* b = LookupInfo(loop, y);
+ // Solve within a tight cycle on x = x ^ c.
+ if (b != nullptr && b->induction_class == kInvariant) {
+ if (x == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
+ InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
+ return CreateInduction(kPeriodic, CreateInvariantOp(kXor, initial, b), initial, type_);
+ }
+ }
+ // Try the other way around if considered for first time.
+ if (is_first_call) {
+ return SolveXor(loop, entry_phi, instruction, y, x, false);
+ }
+ return nullptr;
+}
+
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) {
Primitive::Type from = conversion->GetInputType();
Primitive::Type to = conversion->GetResultType();
@@ -850,8 +874,8 @@
int64_t value = -1;
if (IsExact(a, &value)) {
if (value == 0) {
- // Simplify 0 + b = b, 0 * b = 0.
- if (op == kAdd) {
+ // Simplify 0 + b = b, 0 ^ b = b, 0 * b = 0.
+ if (op == kAdd || op == kXor) {
return b;
} else if (op == kMul) {
return a;
@@ -867,8 +891,8 @@
}
if (IsExact(b, &value)) {
if (value == 0) {
- // Simplify a + 0 = a, a - 0 = a, a * 0 = 0, -0 = 0.
- if (op == kAdd || op == kSub) {
+ // Simplify a + 0 = a, a - 0 = a, a ^ 0 = a, a * 0 = 0, -0 = 0.
+ if (op == kAdd || op == kSub || op == kXor) {
return a;
} else if (op == kMul || op == kNeg) {
return b;
@@ -939,6 +963,7 @@
case kNeg: inv += " - "; break;
case kMul: inv += " * "; break;
case kDiv: inv += " / "; break;
+ case kXor: inv += " ^ "; break;
case kLT: inv += " < "; break;
case kLE: inv += " <= "; break;
case kGT: inv += " > "; break;
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index cd4c830..06aee31 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -64,6 +64,7 @@
kNeg,
kMul,
kDiv,
+ kXor,
kFetch,
// Trip-counts.
kTripCountInLoop, // valid in full loop; loop is finite
@@ -171,7 +172,13 @@
HInstruction* x,
HInstruction* y,
InductionOp op,
- bool is_first_call);
+ bool is_first_call); // possibly swaps x and y to try again
+ InductionInfo* SolveXor(HLoopInformation* loop,
+ HInstruction* entry_phi,
+ HInstruction* instruction,
+ HInstruction* x,
+ HInstruction* y,
+ bool is_first_call); // possibly swaps x and y to try again
InductionInfo* SolveCnv(HTypeConversion* conversion);
// Trip count information.
@@ -219,8 +226,8 @@
// Temporary book-keeping during the analysis.
uint32_t global_depth_;
ArenaVector<HInstruction*> stack_;
- ArenaVector<HInstruction*> scc_;
ArenaSafeMap<HInstruction*, NodeInfo> map_;
+ ArenaVector<HInstruction*> scc_;
ArenaSafeMap<HInstruction*, InductionInfo*> cycle_;
Primitive::Type type_;
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 292bc4e..7c467f6 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -107,7 +107,7 @@
}
// Builds if-statement at depth d.
- HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock **ifF) {
+ HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock** ifF) {
HBasicBlock* cond = new (&allocator_) HBasicBlock(graph_);
HBasicBlock* ifTrue = new (&allocator_) HBasicBlock(graph_);
HBasicBlock* ifFalse = new (&allocator_) HBasicBlock(graph_);
@@ -259,15 +259,15 @@
// k = - i;
// }
BuildLoopNest(1);
- HInstruction *add = InsertInstruction(
+ HInstruction* add = InsertInstruction(
new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0);
- HInstruction *sub = InsertInstruction(
+ HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
- HInstruction *mul = InsertInstruction(
+ HInstruction* mul = InsertInstruction(
new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0);
- HInstruction *shl = InsertInstruction(
+ HInstruction* shl = InsertInstruction(
new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0);
- HInstruction *neg = InsertInstruction(
+ HInstruction* neg = InsertInstruction(
new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0);
PerformInductionVarAnalysis();
@@ -291,10 +291,10 @@
HPhi* k = InsertLoopPhi(0, 0);
k->AddInput(constant0_);
- HInstruction *add = InsertInstruction(
+ HInstruction* add = InsertInstruction(
new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
HInstruction* store1 = InsertArrayStore(add, 0);
- HInstruction *sub = InsertInstruction(
+ HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0);
HInstruction* store2 = InsertArrayStore(sub, 0);
k->AddInput(sub);
@@ -381,7 +381,7 @@
k->AddInput(constant0_);
HInstruction* store = InsertArrayStore(k, 0);
- HInstruction *sub = InsertInstruction(
+ HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0);
k->AddInput(sub);
PerformInductionVarAnalysis();
@@ -407,7 +407,7 @@
HInstruction* store = InsertArrayStore(k, 0);
k->AddInput(t);
- HInstruction *sub = InsertInstruction(
+ HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0);
t->AddInput(sub);
PerformInductionVarAnalysis();
@@ -431,15 +431,15 @@
HPhi* k = InsertLoopPhi(0, 0);
k->AddInput(constant0_);
- HInstruction *add = InsertInstruction(
+ HInstruction* add = InsertInstruction(
new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0);
- HInstruction *sub = InsertInstruction(
+ HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0);
- HInstruction *mul = InsertInstruction(
+ HInstruction* mul = InsertInstruction(
new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0);
- HInstruction *shl = InsertInstruction(
+ HInstruction* shl = InsertInstruction(
new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0);
- HInstruction *neg = InsertInstruction(
+ HInstruction* neg = InsertInstruction(
new (&allocator_) HNeg(Primitive::kPrimInt, k), 0);
k->AddInput(
InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0));
@@ -497,7 +497,7 @@
k->AddInput(constant0_);
HInstruction* store = InsertArrayStore(k, 0);
- HInstruction *sub = InsertInstruction(
+ HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0);
k->AddInput(sub);
PerformInductionVarAnalysis();
@@ -506,6 +506,45 @@
EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str());
}
+TEST_F(InductionVarAnalysisTest, FindXorPeriodicInduction) {
+ // Setup:
+ // k = 0;
+ // for (int i = 0; i < 100; i++) {
+ // a[k] = 0;
+ // k = k ^ 1;
+ // }
+ BuildLoopNest(1);
+ HPhi* k = InsertLoopPhi(0, 0);
+ k->AddInput(constant0_);
+
+ HInstruction* store = InsertArrayStore(k, 0);
+ HInstruction* x = InsertInstruction(
+ new (&allocator_) HXor(Primitive::kPrimInt, k, constant1_), 0);
+ k->AddInput(x);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str());
+ EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(x, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindXor100PeriodicInduction) {
+ // Setup:
+ // k = 100;
+ // for (int i = 0; i < 100; i++) {
+ // k = k ^ 100;
+ // }
+ BuildLoopNest(1);
+ HPhi* k = InsertLoopPhi(0, 0);
+ k->AddInput(constant100_);
+
+ HInstruction* x = InsertInstruction(
+ new (&allocator_) HXor(Primitive::kPrimInt, k, constant100_), 0);
+ k->AddInput(x);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("periodic(((100) ^ (100)), (100)):PrimInt", GetInductionInfo(x, 0).c_str());
+}
+
TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) {
// Setup:
// k = 0;
@@ -526,15 +565,15 @@
k_header->AddInput(k_body);
// Derived expressions.
- HInstruction *add = InsertInstruction(
+ HInstruction* add = InsertInstruction(
new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0);
- HInstruction *sub = InsertInstruction(
+ HInstruction* sub = InsertInstruction(
new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0);
- HInstruction *mul = InsertInstruction(
+ HInstruction* mul = InsertInstruction(
new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0);
- HInstruction *shl = InsertInstruction(
+ HInstruction* shl = InsertInstruction(
new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0);
- HInstruction *neg = InsertInstruction(
+ HInstruction* neg = InsertInstruction(
new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0);
PerformInductionVarAnalysis();
@@ -563,7 +602,7 @@
k[d] = InsertLoopPhi(0, d);
}
- HInstruction *inc = InsertInstruction(
+ HInstruction* inc = InsertInstruction(
new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9);
HInstruction* store = InsertArrayStore(inc, 9);
@@ -597,7 +636,7 @@
// a[i] = 0;
// }
BuildLoopNest(1);
- HInstruction *conv = InsertInstruction(
+ HInstruction* conv = InsertInstruction(
new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
HInstruction* store1 = InsertArrayStore(conv, 0);
HInstruction* store2 = InsertArrayStore(basic_[0], 0);
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index cd8b7c7..140c7f0 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -525,6 +525,8 @@
return GetMul(info->op_a, info->op_b, trip, in_body, is_min);
case HInductionVarAnalysis::kDiv:
return GetDiv(info->op_a, info->op_b, trip, in_body, is_min);
+ case HInductionVarAnalysis::kXor:
+ return GetXor(info->op_a, info->op_b);
case HInductionVarAnalysis::kFetch:
return GetFetch(info->fetch, trip, in_body, is_min);
case HInductionVarAnalysis::kTripCountInLoop:
@@ -626,6 +628,21 @@
return Value();
}
+InductionVarRange::Value InductionVarRange::GetXor(
+ HInductionVarAnalysis::InductionInfo* info1,
+ HInductionVarAnalysis::InductionInfo* info2) const {
+ int64_t v1 = 0;
+ int64_t v2 = 0;
+ // Only accept exact values.
+ if (IsConstant(info1, kExact, &v1) && IsConstant(info2, kExact, &v2)) {
+ int64_t value = v1 ^ v2;
+ if (CanLongValueFitIntoInt(value)) {
+ return Value(static_cast<int32_t>(value));
+ }
+ }
+ return Value();
+}
+
InductionVarRange::Value InductionVarRange::MulRangeAndConstant(
int64_t value,
HInductionVarAnalysis::InductionInfo* info,
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index df31e81..8951300 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -193,6 +193,8 @@
HInductionVarAnalysis::InductionInfo* trip,
bool in_body,
bool is_min) const;
+ Value GetXor(HInductionVarAnalysis::InductionInfo* info1,
+ HInductionVarAnalysis::InductionInfo* info2) const;
Value MulRangeAndConstant(int64_t value,
HInductionVarAnalysis::InductionInfo* info,
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index 486626b..a1dcd58 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -38,7 +38,7 @@
const DexCompilationUnit& outer_compilation_unit,
const DexCompilationUnit& caller_compilation_unit,
CompilerDriver* compiler_driver,
- StackHandleScopeCollection* handles,
+ VariableSizedHandleScope* handles,
OptimizingCompilerStats* stats,
size_t total_number_of_dex_registers,
size_t depth)
@@ -197,7 +197,7 @@
const size_t total_number_of_dex_registers_;
const size_t depth_;
size_t number_of_inlined_instructions_;
- StackHandleScopeCollection* const handles_;
+ VariableSizedHandleScope* const handles_;
DISALLOW_COPY_AND_ASSIGN(HInliner);
};
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 874c1ed..1e69966 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -35,7 +35,7 @@
// double).
static constexpr bool kEnableFloatingPointStaticEvaluation = (FLT_EVAL_METHOD == 0);
-void HGraph::InitializeInexactObjectRTI(StackHandleScopeCollection* handles) {
+void HGraph::InitializeInexactObjectRTI(VariableSizedHandleScope* handles) {
ScopedObjectAccess soa(Thread::Current());
// Create the inexact Object reference type and store it in the HGraph.
ClassLinker* linker = Runtime::Current()->GetClassLinker();
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 348f99d..daefc3c 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -336,7 +336,7 @@
}
// Acquires and stores RTI of inexact Object to be used when creating HNullConstant.
- void InitializeInexactObjectRTI(StackHandleScopeCollection* handles);
+ void InitializeInexactObjectRTI(VariableSizedHandleScope* handles);
ArenaAllocator* GetArena() const { return arena_; }
const ArenaVector<HBasicBlock*>& GetBlocks() const { return blocks_; }
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index d6f8307..4370a84 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -319,7 +319,7 @@
CompilerDriver* driver,
const DexCompilationUnit& dex_compilation_unit,
PassObserver* pass_observer,
- StackHandleScopeCollection* handles) const;
+ VariableSizedHandleScope* handles) const;
void RunOptimizations(HOptimization* optimizations[],
size_t length,
@@ -358,7 +358,7 @@
CompilerDriver* driver,
const DexCompilationUnit& dex_compilation_unit,
PassObserver* pass_observer,
- StackHandleScopeCollection* handles) const;
+ VariableSizedHandleScope* handles) const;
void RunArchOptimizations(InstructionSet instruction_set,
HGraph* graph,
@@ -442,7 +442,7 @@
CodeGenerator* codegen,
CompilerDriver* driver,
const DexCompilationUnit& dex_compilation_unit,
- StackHandleScopeCollection* handles,
+ VariableSizedHandleScope* handles,
SideEffectsAnalysis* most_recent_side_effects,
HInductionVarAnalysis* most_recent_induction) {
std::string opt_name = ConvertPassNameToOptimizationName(pass_name);
@@ -524,7 +524,7 @@
CodeGenerator* codegen,
CompilerDriver* driver,
const DexCompilationUnit& dex_compilation_unit,
- StackHandleScopeCollection* handles) {
+ VariableSizedHandleScope* handles) {
// Few HOptimizations constructors require SideEffectsAnalysis or HInductionVarAnalysis
// instances. This method assumes that each of them expects the nearest instance preceeding it
// in the pass name list.
@@ -570,7 +570,7 @@
CompilerDriver* driver,
const DexCompilationUnit& dex_compilation_unit,
PassObserver* pass_observer,
- StackHandleScopeCollection* handles) const {
+ VariableSizedHandleScope* handles) const {
OptimizingCompilerStats* stats = compilation_stats_.get();
const CompilerOptions& compiler_options = driver->GetCompilerOptions();
bool should_inline = (compiler_options.GetInlineDepthLimit() > 0)
@@ -707,7 +707,7 @@
CompilerDriver* driver,
const DexCompilationUnit& dex_compilation_unit,
PassObserver* pass_observer,
- StackHandleScopeCollection* handles) const {
+ VariableSizedHandleScope* handles) const {
OptimizingCompilerStats* stats = compilation_stats_.get();
ArenaAllocator* arena = graph->GetArena();
if (driver->GetCompilerOptions().GetPassesToRun() != nullptr) {
@@ -949,7 +949,7 @@
{
ScopedObjectAccess soa(Thread::Current());
- StackHandleScopeCollection handles(soa.Self());
+ VariableSizedHandleScope handles(soa.Self());
// Do not hold `mutator_lock_` between optimizations.
ScopedThreadSuspension sts(soa.Self(), kNative);
diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h
index 2a23c92..58d9017 100644
--- a/compiler/optimizing/optimizing_unit_test.h
+++ b/compiler/optimizing/optimizing_unit_test.h
@@ -90,7 +90,7 @@
{
ScopedObjectAccess soa(Thread::Current());
- StackHandleScopeCollection handles(soa.Self());
+ VariableSizedHandleScope handles(soa.Self());
HGraphBuilder builder(graph, *item, &handles, return_type);
bool graph_built = (builder.BuildGraph() == kAnalysisSuccess);
return graph_built ? graph : nullptr;
diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc
index 45a3ce4..83698ad 100644
--- a/compiler/optimizing/reference_type_propagation.cc
+++ b/compiler/optimizing/reference_type_propagation.cc
@@ -35,7 +35,7 @@
}
}
-static inline ReferenceTypeInfo::TypeHandle GetRootHandle(StackHandleScopeCollection* handles,
+static inline ReferenceTypeInfo::TypeHandle GetRootHandle(VariableSizedHandleScope* handles,
ClassLinker::ClassRoot class_root,
ReferenceTypeInfo::TypeHandle* cache) {
if (!ReferenceTypeInfo::IsValidHandle(*cache)) {
@@ -109,7 +109,7 @@
ReferenceTypePropagation::ReferenceTypePropagation(HGraph* graph,
Handle<mirror::DexCache> hint_dex_cache,
- StackHandleScopeCollection* handles,
+ VariableSizedHandleScope* handles,
bool is_first_run,
const char* name)
: HOptimization(graph, name),
diff --git a/compiler/optimizing/reference_type_propagation.h b/compiler/optimizing/reference_type_propagation.h
index 61428b2..4663471 100644
--- a/compiler/optimizing/reference_type_propagation.h
+++ b/compiler/optimizing/reference_type_propagation.h
@@ -34,7 +34,7 @@
public:
ReferenceTypePropagation(HGraph* graph,
Handle<mirror::DexCache> hint_dex_cache,
- StackHandleScopeCollection* handles,
+ VariableSizedHandleScope* handles,
bool is_first_run,
const char* name = kReferenceTypePropagationPassName);
@@ -56,7 +56,7 @@
private:
class HandleCache {
public:
- explicit HandleCache(StackHandleScopeCollection* handles) : handles_(handles) { }
+ explicit HandleCache(VariableSizedHandleScope* handles) : handles_(handles) { }
template <typename T>
MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_) {
@@ -74,7 +74,7 @@
ReferenceTypeInfo::TypeHandle GetThrowableClassHandle();
private:
- StackHandleScopeCollection* handles_;
+ VariableSizedHandleScope* handles_;
ReferenceTypeInfo::TypeHandle object_class_handle_;
ReferenceTypeInfo::TypeHandle class_class_handle_;
diff --git a/compiler/optimizing/reference_type_propagation_test.cc b/compiler/optimizing/reference_type_propagation_test.cc
index 75a4eac..b061c87 100644
--- a/compiler/optimizing/reference_type_propagation_test.cc
+++ b/compiler/optimizing/reference_type_propagation_test.cc
@@ -35,7 +35,7 @@
~ReferenceTypePropagationTest() { }
- void SetupPropagation(StackHandleScopeCollection* handles) {
+ void SetupPropagation(VariableSizedHandleScope* handles) {
graph_->InitializeInexactObjectRTI(handles);
propagation_ = new (&allocator_) ReferenceTypePropagation(graph_,
Handle<mirror::DexCache>(),
@@ -79,7 +79,7 @@
TEST_F(ReferenceTypePropagationTest, ProperSetup) {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScopeCollection handles(soa.Self());
+ VariableSizedHandleScope handles(soa.Self());
SetupPropagation(&handles);
EXPECT_TRUE(propagation_ != nullptr);
@@ -88,7 +88,7 @@
TEST_F(ReferenceTypePropagationTest, MergeInvalidTypes) {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScopeCollection handles(soa.Self());
+ VariableSizedHandleScope handles(soa.Self());
SetupPropagation(&handles);
// Two invalid types.
@@ -120,7 +120,7 @@
TEST_F(ReferenceTypePropagationTest, MergeValidTypes) {
ScopedObjectAccess soa(Thread::Current());
- StackHandleScopeCollection handles(soa.Self());
+ VariableSizedHandleScope handles(soa.Self());
SetupPropagation(&handles);
// Same types.
diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h
index d7360ad..45dac54 100644
--- a/compiler/optimizing/ssa_builder.h
+++ b/compiler/optimizing/ssa_builder.h
@@ -49,7 +49,7 @@
public:
SsaBuilder(HGraph* graph,
Handle<mirror::DexCache> dex_cache,
- StackHandleScopeCollection* handles)
+ VariableSizedHandleScope* handles)
: graph_(graph),
dex_cache_(dex_cache),
handles_(handles),
@@ -116,7 +116,7 @@
HGraph* graph_;
Handle<mirror::DexCache> dex_cache_;
- StackHandleScopeCollection* const handles_;
+ VariableSizedHandleScope* const handles_;
// True if types of ambiguous ArrayGets have been resolved.
bool agets_fixed_;
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 8a51dc2..5ec9898 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -274,7 +274,7 @@
AbortF("field operation on NULL object: %p", java_object);
return false;
}
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o)) {
+ if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(o.Ptr())) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
AbortF("field operation on invalid %s: %p",
ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
@@ -782,7 +782,7 @@
}
}
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj)) {
+ if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(obj.Ptr())) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
AbortF("%s is an invalid %s: %p (%p)",
what, ToStr<IndirectRefKind>(GetIndirectRefKind(java_object)).c_str(),
@@ -939,7 +939,7 @@
ObjPtr<mirror::Class> c = soa.Decode<mirror::Class>(jc);
if (c == nullptr) {
*msg += "NULL";
- } else if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c)) {
+ } else if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(c.Ptr())) {
StringAppendF(msg, "INVALID POINTER:%p", jc);
} else if (!c->IsClass()) {
*msg += "INVALID NON-CLASS OBJECT OF TYPE:" + PrettyTypeOf(c);
@@ -1108,7 +1108,7 @@
}
ObjPtr<mirror::Array> a = soa.Decode<mirror::Array>(java_array);
- if (UNLIKELY(!Runtime::Current()->GetHeap()->IsValidObjectAddress(a))) {
+ if (UNLIKELY(!Runtime::Current()->GetHeap()->IsValidObjectAddress(a.Ptr()))) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
AbortF("jarray is an invalid %s: %p (%p)",
ToStr<IndirectRefKind>(GetIndirectRefKind(java_array)).c_str(),
@@ -1145,7 +1145,7 @@
}
ArtField* f = soa.DecodeField(fid);
// TODO: Better check here.
- if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f->GetDeclaringClass())) {
+ if (!Runtime::Current()->GetHeap()->IsValidObjectAddress(f->GetDeclaringClass().Ptr())) {
Runtime::Current()->GetHeap()->DumpSpaces(LOG_STREAM(ERROR));
AbortF("invalid jfieldID: %p", fid);
return nullptr;
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 7006f70..502ce4b 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -892,15 +892,16 @@
std::vector<uint64_t>* counts) {
gc::Heap* heap = Runtime::Current()->GetHeap();
heap->CollectGarbage(false);
- std::vector<mirror::Class*> classes;
+ VariableSizedHandleScope hs(Thread::Current());
+ std::vector<Handle<mirror::Class>> classes;
counts->clear();
for (size_t i = 0; i < class_ids.size(); ++i) {
JDWP::JdwpError error;
- mirror::Class* c = DecodeClass(class_ids[i], &error);
+ ObjPtr<mirror::Class> c = DecodeClass(class_ids[i], &error);
if (c == nullptr) {
return error;
}
- classes.push_back(c);
+ classes.push_back(hs.NewHandle(c));
counts->push_back(0);
}
heap->CountInstances(classes, false, &(*counts)[0]);
@@ -913,12 +914,13 @@
// We only want reachable instances, so do a GC.
heap->CollectGarbage(false);
JDWP::JdwpError error;
- mirror::Class* c = DecodeClass(class_id, &error);
+ ObjPtr<mirror::Class> c = DecodeClass(class_id, &error);
if (c == nullptr) {
return error;
}
- std::vector<mirror::Object*> raw_instances;
- Runtime::Current()->GetHeap()->GetInstances(c, max_count, raw_instances);
+ std::vector<ObjPtr<mirror::Object>> raw_instances;
+ StackHandleScope<1> hs(Thread::Current());
+ Runtime::Current()->GetHeap()->GetInstances(hs.NewHandle(c), max_count, raw_instances);
for (size_t i = 0; i < raw_instances.size(); ++i) {
instances->push_back(gRegistry->Add(raw_instances[i]));
}
@@ -930,11 +932,11 @@
gc::Heap* heap = Runtime::Current()->GetHeap();
heap->CollectGarbage(false);
JDWP::JdwpError error;
- mirror::Object* o = gRegistry->Get<mirror::Object*>(object_id, &error);
+ ObjPtr<mirror::Object> o = gRegistry->Get<mirror::Object*>(object_id, &error);
if (o == nullptr) {
return JDWP::ERR_INVALID_OBJECT;
}
- std::vector<mirror::Object*> raw_instances;
+ std::vector<ObjPtr<mirror::Object>> raw_instances;
heap->GetReferringObjects(o, max_count, raw_instances);
for (size_t i = 0; i < raw_instances.size(); ++i) {
referring_objects->push_back(gRegistry->Add(raw_instances[i]));
diff --git a/runtime/entrypoints/quick/quick_jni_entrypoints.cc b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
index 7c7e2da..20fa0d8 100644
--- a/runtime/entrypoints/quick/quick_jni_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_jni_entrypoints.cc
@@ -144,7 +144,7 @@
HandleWrapperObjPtr<mirror::Object> h_obj(hs.NewHandleWrapper(&o));
CheckReferenceResult(h_obj, self);
}
- VerifyObject(o.Ptr());
+ VerifyObject(o);
return o.Ptr();
}
diff --git a/runtime/gc/allocation_listener.h b/runtime/gc/allocation_listener.h
index 6fb74d3..f60bc0c 100644
--- a/runtime/gc/allocation_listener.h
+++ b/runtime/gc/allocation_listener.h
@@ -22,6 +22,7 @@
#include "base/macros.h"
#include "base/mutex.h"
+#include "obj_ptr.h"
#include "object_callbacks.h"
#include "gc_root.h"
@@ -39,7 +40,7 @@
public:
virtual ~AllocationListener() {}
- virtual void ObjectAllocated(Thread* self, mirror::Object** obj, size_t byte_count)
+ virtual void ObjectAllocated(Thread* self, ObjPtr<mirror::Object>* obj, size_t byte_count)
REQUIRES_SHARED(Locks::mutator_lock_) = 0;
};
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index 13ebb27..d921900 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -19,6 +19,7 @@
#include "art_method-inl.h"
#include "base/enums.h"
#include "base/stl_util.h"
+#include "obj_ptr-inl.h"
#include "stack.h"
#ifdef ART_TARGET_ANDROID
@@ -263,7 +264,7 @@
}
void AllocRecordObjectMap::RecordAllocation(Thread* self,
- mirror::Object** obj,
+ ObjPtr<mirror::Object>* obj,
size_t byte_count) {
// Get stack trace outside of lock in case there are allocations during the stack walk.
// b/27858645.
@@ -305,7 +306,7 @@
trace.SetTid(self->GetTid());
// Add the record.
- Put(*obj, AllocRecord(byte_count, (*obj)->GetClass(), std::move(trace)));
+ Put(obj->Ptr(), AllocRecord(byte_count, (*obj)->GetClass(), std::move(trace)));
DCHECK_LE(Size(), alloc_record_max_);
}
diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h
index f1f013b..c8b2b89 100644
--- a/runtime/gc/allocation_record.h
+++ b/runtime/gc/allocation_record.h
@@ -21,6 +21,7 @@
#include <memory>
#include "base/mutex.h"
+#include "obj_ptr.h"
#include "object_callbacks.h"
#include "gc_root.h"
@@ -210,7 +211,7 @@
// Caller needs to check that it is enabled before calling since we read the stack trace before
// checking the enabled boolean.
void RecordAllocation(Thread* self,
- mirror::Object** obj,
+ ObjPtr<mirror::Object>* obj,
size_t byte_count)
REQUIRES(!Locks::alloc_tracker_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/gc/collector/semi_space.cc b/runtime/gc/collector/semi_space.cc
index 2e97172..76a478e 100644
--- a/runtime/gc/collector/semi_space.cc
+++ b/runtime/gc/collector/semi_space.cc
@@ -296,7 +296,6 @@
REQUIRES_SHARED(Locks::mutator_lock_) ALWAYS_INLINE {
mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
if (from_space_->HasAddress(ref)) {
- Runtime::Current()->GetHeap()->DumpObject(LOG_STREAM(INFO), obj);
LOG(FATAL) << ref << " found in from space";
}
}
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index 00adefb..05ce9c7 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -41,7 +41,7 @@
template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor>
inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
- mirror::Class* klass,
+ ObjPtr<mirror::Class> klass,
size_t byte_count,
AllocatorType allocator,
const PreFenceVisitor& pre_fence_visitor) {
@@ -52,16 +52,19 @@
CHECK_EQ(self->GetState(), kRunnable);
self->AssertThreadSuspensionIsAllowable();
self->AssertNoPendingException();
+ // Make sure to preserve klass.
+ StackHandleScope<1> hs(self);
+ HandleWrapperObjPtr<mirror::Class> h = hs.NewHandleWrapper(&klass);
self->PoisonObjectPointers();
}
// Need to check that we arent the large object allocator since the large object allocation code
// path this function. If we didn't check we would have an infinite loop.
- mirror::Object* obj;
+ ObjPtr<mirror::Object> obj;
if (kCheckLargeObject && UNLIKELY(ShouldAllocLargeObject(klass, byte_count))) {
obj = AllocLargeObject<kInstrumented, PreFenceVisitor>(self, &klass, byte_count,
pre_fence_visitor);
if (obj != nullptr) {
- return obj;
+ return obj.Ptr();
} else {
// There should be an OOM exception, since we are retrying, clear it.
self->ClearException();
@@ -85,7 +88,7 @@
obj->SetClass(klass);
if (kUseBakerOrBrooksReadBarrier) {
if (kUseBrooksReadBarrier) {
- obj->SetReadBarrierPointer(obj);
+ obj->SetReadBarrierPointer(obj.Ptr());
}
obj->AssertReadBarrierPointer();
}
@@ -93,14 +96,15 @@
usable_size = bytes_allocated;
pre_fence_visitor(obj, usable_size);
QuasiAtomic::ThreadFenceForConstructor();
- } else if (!kInstrumented && allocator == kAllocatorTypeRosAlloc &&
- (obj = rosalloc_space_->AllocThreadLocal(self, byte_count, &bytes_allocated)) &&
- LIKELY(obj != nullptr)) {
+ } else if (
+ !kInstrumented && allocator == kAllocatorTypeRosAlloc &&
+ (obj = rosalloc_space_->AllocThreadLocal(self, byte_count, &bytes_allocated)) != nullptr &&
+ LIKELY(obj != nullptr)) {
DCHECK(!is_running_on_memory_tool_);
obj->SetClass(klass);
if (kUseBakerOrBrooksReadBarrier) {
if (kUseBrooksReadBarrier) {
- obj->SetReadBarrierPointer(obj);
+ obj->SetReadBarrierPointer(obj.Ptr());
}
obj->AssertReadBarrierPointer();
}
@@ -141,7 +145,7 @@
obj->SetClass(klass);
if (kUseBakerOrBrooksReadBarrier) {
if (kUseBrooksReadBarrier) {
- obj->SetReadBarrierPointer(obj);
+ obj->SetReadBarrierPointer(obj.Ptr());
}
obj->AssertReadBarrierPointer();
}
@@ -213,25 +217,25 @@
}
VerifyObject(obj);
self->VerifyStack();
- return obj;
+ return obj.Ptr();
}
// The size of a thread-local allocation stack in the number of references.
static constexpr size_t kThreadLocalAllocationStackSize = 128;
-inline void Heap::PushOnAllocationStack(Thread* self, mirror::Object** obj) {
+inline void Heap::PushOnAllocationStack(Thread* self, ObjPtr<mirror::Object>* obj) {
if (kUseThreadLocalAllocationStack) {
- if (UNLIKELY(!self->PushOnThreadLocalAllocationStack(*obj))) {
+ if (UNLIKELY(!self->PushOnThreadLocalAllocationStack(obj->Ptr()))) {
PushOnThreadLocalAllocationStackWithInternalGC(self, obj);
}
- } else if (UNLIKELY(!allocation_stack_->AtomicPushBack(*obj))) {
+ } else if (UNLIKELY(!allocation_stack_->AtomicPushBack(obj->Ptr()))) {
PushOnAllocationStackWithInternalGC(self, obj);
}
}
template <bool kInstrumented, typename PreFenceVisitor>
inline mirror::Object* Heap::AllocLargeObject(Thread* self,
- mirror::Class** klass,
+ ObjPtr<mirror::Class>* klass,
size_t byte_count,
const PreFenceVisitor& pre_fence_visitor) {
// Save and restore the class in case it moves.
@@ -405,7 +409,7 @@
return ret;
}
-inline bool Heap::ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const {
+inline bool Heap::ShouldAllocLargeObject(ObjPtr<mirror::Class> c, size_t byte_count) const {
// We need to have a zygote space or else our newly allocated large object can end up in the
// Zygote resulting in it being prematurely freed.
// We can only do this for primitive objects since large objects will not be within the card table
@@ -435,7 +439,7 @@
inline void Heap::CheckConcurrentGC(Thread* self,
size_t new_num_bytes_allocated,
- mirror::Object** obj) {
+ ObjPtr<mirror::Object>* obj) {
if (UNLIKELY(new_num_bytes_allocated >= concurrent_start_bytes_)) {
RequestConcurrentGCAndSaveObject(self, false, obj);
}
@@ -447,6 +451,16 @@
card_table_->MarkCard(dst.Ptr());
}
+inline void Heap::WriteBarrierArray(ObjPtr<mirror::Object> dst,
+ int start_offset ATTRIBUTE_UNUSED,
+ size_t length ATTRIBUTE_UNUSED) {
+ card_table_->MarkCard(dst.Ptr());
+}
+
+inline void Heap::WriteBarrierEveryFieldOf(ObjPtr<mirror::Object> obj) {
+ card_table_->MarkCard(obj.Ptr());
+}
+
} // namespace gc
} // namespace art
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 33f849a..770a55d 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -47,6 +47,7 @@
#include "gc/collector/semi_space.h"
#include "gc/collector/sticky_mark_sweep.h"
#include "gc/reference_processor.h"
+#include "gc/scoped_gc_critical_section.h"
#include "gc/space/bump_pointer_space.h"
#include "gc/space/dlmalloc_space-inl.h"
#include "gc/space/image_space.h"
@@ -760,83 +761,6 @@
}
}
-std::string Heap::SafeGetClassDescriptor(mirror::Class* klass) {
- if (!IsValidContinuousSpaceObjectAddress(klass)) {
- return StringPrintf("<non heap address klass %p>", klass);
- }
- mirror::Class* component_type = klass->GetComponentType<kVerifyNone>();
- if (IsValidContinuousSpaceObjectAddress(component_type) && klass->IsArrayClass<kVerifyNone>()) {
- std::string result("[");
- result += SafeGetClassDescriptor(component_type);
- return result;
- } else if (UNLIKELY(klass->IsPrimitive<kVerifyNone>())) {
- return Primitive::Descriptor(klass->GetPrimitiveType<kVerifyNone>());
- } else if (UNLIKELY(klass->IsProxyClass<kVerifyNone>())) {
- return Runtime::Current()->GetClassLinker()->GetDescriptorForProxy(klass);
- } else {
- mirror::DexCache* dex_cache = klass->GetDexCache<kVerifyNone>();
- if (!IsValidContinuousSpaceObjectAddress(dex_cache)) {
- return StringPrintf("<non heap address dex_cache %p>", dex_cache);
- }
- const DexFile* dex_file = dex_cache->GetDexFile();
- uint16_t class_def_idx = klass->GetDexClassDefIndex();
- if (class_def_idx == DexFile::kDexNoIndex16) {
- return "<class def not found>";
- }
- const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx);
- const DexFile::TypeId& type_id = dex_file->GetTypeId(class_def.class_idx_);
- return dex_file->GetTypeDescriptor(type_id);
- }
-}
-
-std::string Heap::SafePrettyTypeOf(mirror::Object* obj) {
- if (obj == nullptr) {
- return "null";
- }
- mirror::Class* klass = obj->GetClass<kVerifyNone>();
- if (klass == nullptr) {
- return "(class=null)";
- }
- std::string result(SafeGetClassDescriptor(klass));
- if (obj->IsClass()) {
- result += "<" + SafeGetClassDescriptor(obj->AsClass<kVerifyNone>()) + ">";
- }
- return result;
-}
-
-void Heap::DumpObject(std::ostream& stream, mirror::Object* obj) {
- if (obj == nullptr) {
- stream << "(obj=null)";
- return;
- }
- if (IsAligned<kObjectAlignment>(obj)) {
- space::Space* space = nullptr;
- // Don't use find space since it only finds spaces which actually contain objects instead of
- // spaces which may contain objects (e.g. cleared bump pointer spaces).
- for (const auto& cur_space : continuous_spaces_) {
- if (cur_space->HasAddress(obj)) {
- space = cur_space;
- break;
- }
- }
- // Unprotect all the spaces.
- for (const auto& con_space : continuous_spaces_) {
- mprotect(con_space->Begin(), con_space->Capacity(), PROT_READ | PROT_WRITE);
- }
- stream << "Object " << obj;
- if (space != nullptr) {
- stream << " in space " << *space;
- }
- mirror::Class* klass = obj->GetClass<kVerifyNone>();
- stream << "\nclass=" << klass;
- if (klass != nullptr) {
- stream << " type= " << SafePrettyTypeOf(obj);
- }
- // Re-protect the address we faulted on.
- mprotect(AlignDown(obj, kPageSize), kPageSize, PROT_NONE);
- }
-}
-
bool Heap::IsCompilingBoot() const {
if (!Runtime::Current()->IsAotCompiler()) {
return false;
@@ -1325,33 +1249,42 @@
VLOG(heap) << "Finished ~Heap()";
}
-space::ContinuousSpace* Heap::FindContinuousSpaceFromObject(const mirror::Object* obj,
- bool fail_ok) const {
+
+space::ContinuousSpace* Heap::FindContinuousSpaceFromAddress(const mirror::Object* addr) const {
for (const auto& space : continuous_spaces_) {
- if (space->Contains(obj)) {
+ if (space->Contains(addr)) {
return space;
}
}
- if (!fail_ok) {
- LOG(FATAL) << "object " << reinterpret_cast<const void*>(obj) << " not inside any spaces!";
- }
return nullptr;
}
-space::DiscontinuousSpace* Heap::FindDiscontinuousSpaceFromObject(const mirror::Object* obj,
+space::ContinuousSpace* Heap::FindContinuousSpaceFromObject(ObjPtr<mirror::Object> obj,
+ bool fail_ok) const {
+ space::ContinuousSpace* space = FindContinuousSpaceFromAddress(obj.Ptr());
+ if (space != nullptr) {
+ return space;
+ }
+ if (!fail_ok) {
+ LOG(FATAL) << "object " << obj << " not inside any spaces!";
+ }
+ return nullptr;
+}
+
+space::DiscontinuousSpace* Heap::FindDiscontinuousSpaceFromObject(ObjPtr<mirror::Object> obj,
bool fail_ok) const {
for (const auto& space : discontinuous_spaces_) {
- if (space->Contains(obj)) {
+ if (space->Contains(obj.Ptr())) {
return space;
}
}
if (!fail_ok) {
- LOG(FATAL) << "object " << reinterpret_cast<const void*>(obj) << " not inside any spaces!";
+ LOG(FATAL) << "object " << obj << " not inside any spaces!";
}
return nullptr;
}
-space::Space* Heap::FindSpaceFromObject(const mirror::Object* obj, bool fail_ok) const {
+space::Space* Heap::FindSpaceFromObject(ObjPtr<mirror::Object> obj, bool fail_ok) const {
space::Space* result = FindContinuousSpaceFromObject(obj, true);
if (result != nullptr) {
return result;
@@ -1359,6 +1292,21 @@
return FindDiscontinuousSpaceFromObject(obj, fail_ok);
}
+space::Space* Heap::FindSpaceFromAddress(const void* addr) const {
+ for (const auto& space : continuous_spaces_) {
+ if (space->Contains(reinterpret_cast<const mirror::Object*>(addr))) {
+ return space;
+ }
+ }
+ for (const auto& space : discontinuous_spaces_) {
+ if (space->Contains(reinterpret_cast<const mirror::Object*>(addr))) {
+ return space;
+ }
+ }
+ return nullptr;
+}
+
+
void Heap::ThrowOutOfMemoryError(Thread* self, size_t byte_count, AllocatorType allocator_type) {
// If we're in a stack overflow, do not create a new exception. It would require running the
// constructor, which will of course still be in a stack overflow.
@@ -1423,6 +1371,8 @@
// Deflate the monitors, this can cause a pause but shouldn't matter since we don't care
// about pauses.
ScopedTrace trace("Deflating monitors");
+ // Avoid race conditions on the lock word for CC.
+ ScopedGCCriticalSection gcs(self, kGcCauseTrim, kCollectorTypeHeapTrim);
ScopedSuspendAll ssa(__FUNCTION__);
uint64_t start_time = NanoTime();
size_t count = runtime->GetMonitorList()->DeflateMonitors();
@@ -1523,62 +1473,49 @@
<< static_cast<int>(100 * managed_utilization) << "%.";
}
-bool Heap::IsValidObjectAddress(ObjPtr<mirror::Object> obj) const {
- // Note: we deliberately don't take the lock here, and mustn't test anything that would require
- // taking the lock.
- if (obj == nullptr) {
+bool Heap::IsValidObjectAddress(const void* addr) const {
+ if (addr == nullptr) {
return true;
}
- return IsAligned<kObjectAlignment>(obj.Ptr()) &&
- FindSpaceFromObject(obj.Ptr(), true) != nullptr;
+ return IsAligned<kObjectAlignment>(addr) && FindSpaceFromAddress(addr) != nullptr;
}
-bool Heap::IsNonDiscontinuousSpaceHeapAddress(const mirror::Object* obj) const {
- return FindContinuousSpaceFromObject(obj, true) != nullptr;
+bool Heap::IsNonDiscontinuousSpaceHeapAddress(const void* addr) const {
+ return FindContinuousSpaceFromAddress(reinterpret_cast<const mirror::Object*>(addr)) != nullptr;
}
-bool Heap::IsValidContinuousSpaceObjectAddress(const mirror::Object* obj) const {
- if (obj == nullptr || !IsAligned<kObjectAlignment>(obj)) {
+bool Heap::IsLiveObjectLocked(ObjPtr<mirror::Object> obj,
+ bool search_allocation_stack,
+ bool search_live_stack,
+ bool sorted) {
+ if (UNLIKELY(!IsAligned<kObjectAlignment>(obj.Ptr()))) {
return false;
}
- for (const auto& space : continuous_spaces_) {
- if (space->HasAddress(obj)) {
- return true;
- }
- }
- return false;
-}
-
-bool Heap::IsLiveObjectLocked(mirror::Object* obj, bool search_allocation_stack,
- bool search_live_stack, bool sorted) {
- if (UNLIKELY(!IsAligned<kObjectAlignment>(obj))) {
- return false;
- }
- if (bump_pointer_space_ != nullptr && bump_pointer_space_->HasAddress(obj)) {
+ if (bump_pointer_space_ != nullptr && bump_pointer_space_->HasAddress(obj.Ptr())) {
mirror::Class* klass = obj->GetClass<kVerifyNone>();
if (obj == klass) {
// This case happens for java.lang.Class.
return true;
}
return VerifyClassClass(klass) && IsLiveObjectLocked(klass);
- } else if (temp_space_ != nullptr && temp_space_->HasAddress(obj)) {
+ } else if (temp_space_ != nullptr && temp_space_->HasAddress(obj.Ptr())) {
// If we are in the allocated region of the temp space, then we are probably live (e.g. during
// a GC). When a GC isn't running End() - Begin() is 0 which means no objects are contained.
- return temp_space_->Contains(obj);
+ return temp_space_->Contains(obj.Ptr());
}
- if (region_space_ != nullptr && region_space_->HasAddress(obj)) {
+ if (region_space_ != nullptr && region_space_->HasAddress(obj.Ptr())) {
return true;
}
space::ContinuousSpace* c_space = FindContinuousSpaceFromObject(obj, true);
space::DiscontinuousSpace* d_space = nullptr;
if (c_space != nullptr) {
- if (c_space->GetLiveBitmap()->Test(obj)) {
+ if (c_space->GetLiveBitmap()->Test(obj.Ptr())) {
return true;
}
} else {
d_space = FindDiscontinuousSpaceFromObject(obj, true);
if (d_space != nullptr) {
- if (d_space->GetLiveBitmap()->Test(obj)) {
+ if (d_space->GetLiveBitmap()->Test(obj.Ptr())) {
return true;
}
}
@@ -1590,20 +1527,20 @@
}
if (search_allocation_stack) {
if (sorted) {
- if (allocation_stack_->ContainsSorted(obj)) {
+ if (allocation_stack_->ContainsSorted(obj.Ptr())) {
return true;
}
- } else if (allocation_stack_->Contains(obj)) {
+ } else if (allocation_stack_->Contains(obj.Ptr())) {
return true;
}
}
if (search_live_stack) {
if (sorted) {
- if (live_stack_->ContainsSorted(obj)) {
+ if (live_stack_->ContainsSorted(obj.Ptr())) {
return true;
}
- } else if (live_stack_->Contains(obj)) {
+ } else if (live_stack_->Contains(obj.Ptr())) {
return true;
}
}
@@ -1611,12 +1548,12 @@
// We need to check the bitmaps again since there is a race where we mark something as live and
// then clear the stack containing it.
if (c_space != nullptr) {
- if (c_space->GetLiveBitmap()->Test(obj)) {
+ if (c_space->GetLiveBitmap()->Test(obj.Ptr())) {
return true;
}
} else {
d_space = FindDiscontinuousSpaceFromObject(obj, true);
- if (d_space != nullptr && d_space->GetLiveBitmap()->Test(obj)) {
+ if (d_space != nullptr && d_space->GetLiveBitmap()->Test(obj.Ptr())) {
return true;
}
}
@@ -1646,7 +1583,7 @@
}
}
-void Heap::VerifyObjectBody(mirror::Object* obj) {
+void Heap::VerifyObjectBody(ObjPtr<mirror::Object> obj) {
if (verify_object_mode_ == kVerifyObjectModeDisabled) {
return;
}
@@ -1655,7 +1592,7 @@
if (UNLIKELY(static_cast<size_t>(num_bytes_allocated_.LoadRelaxed()) < 10 * KB)) {
return;
}
- CHECK_ALIGNED(obj, kObjectAlignment) << "Object isn't aligned";
+ CHECK_ALIGNED(obj.Ptr(), kObjectAlignment) << "Object isn't aligned";
mirror::Class* c = obj->GetFieldObject<mirror::Class, kVerifyNone>(mirror::Object::ClassOffset());
CHECK(c != nullptr) << "Null class in object " << obj;
CHECK_ALIGNED(c, kObjectAlignment) << "Class " << c << " not aligned in object " << obj;
@@ -1734,14 +1671,13 @@
size_t* bytes_allocated,
size_t* usable_size,
size_t* bytes_tl_bulk_allocated,
- mirror::Class** klass) {
+ ObjPtr<mirror::Class>* klass) {
bool was_default_allocator = allocator == GetCurrentAllocator();
// Make sure there is no pending exception since we may need to throw an OOME.
self->AssertNoPendingException();
DCHECK(klass != nullptr);
StackHandleScope<1> hs(self);
- HandleWrapper<mirror::Class> h(hs.NewHandleWrapper(klass));
- klass = nullptr; // Invalidate for safety.
+ HandleWrapperObjPtr<mirror::Class> h(hs.NewHandleWrapper(klass));
// The allocation failed. If the GC is running, block until it completes, and then retry the
// allocation.
collector::GcType last_gc = WaitForGcToComplete(kGcCauseForAlloc, self);
@@ -1944,7 +1880,7 @@
class InstanceCounter {
public:
- InstanceCounter(const std::vector<mirror::Class*>& classes,
+ InstanceCounter(const std::vector<Handle<mirror::Class>>& classes,
bool use_is_assignable_from,
uint64_t* counts)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -1956,7 +1892,7 @@
mirror::Class* instance_class = obj->GetClass();
CHECK(instance_class != nullptr);
for (size_t i = 0; i < instance_counter->classes_.size(); ++i) {
- mirror::Class* klass = instance_counter->classes_[i];
+ ObjPtr<mirror::Class> klass = instance_counter->classes_[i].Get();
if (instance_counter->use_is_assignable_from_) {
if (klass != nullptr && klass->IsAssignableFrom(instance_class)) {
++instance_counter->counts_[i];
@@ -1968,13 +1904,14 @@
}
private:
- const std::vector<mirror::Class*>& classes_;
+ const std::vector<Handle<mirror::Class>>& classes_;
bool use_is_assignable_from_;
uint64_t* const counts_;
DISALLOW_COPY_AND_ASSIGN(InstanceCounter);
};
-void Heap::CountInstances(const std::vector<mirror::Class*>& classes, bool use_is_assignable_from,
+void Heap::CountInstances(const std::vector<Handle<mirror::Class>>& classes,
+ bool use_is_assignable_from,
uint64_t* counts) {
InstanceCounter counter(classes, use_is_assignable_from, counts);
VisitObjects(InstanceCounter::Callback, &counter);
@@ -1982,15 +1919,17 @@
class InstanceCollector {
public:
- InstanceCollector(mirror::Class* c, int32_t max_count, std::vector<mirror::Object*>& instances)
+ InstanceCollector(Handle<mirror::Class> c,
+ int32_t max_count,
+ std::vector<ObjPtr<mirror::Object>>& instances)
REQUIRES_SHARED(Locks::mutator_lock_)
- : class_(c), max_count_(max_count), instances_(instances) {
- }
+ : class_(c), max_count_(max_count), instances_(instances) {}
+
static void Callback(mirror::Object* obj, void* arg)
REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
DCHECK(arg != nullptr);
InstanceCollector* instance_collector = reinterpret_cast<InstanceCollector*>(arg);
- if (obj->GetClass() == instance_collector->class_) {
+ if (obj->GetClass() == instance_collector->class_.Get()) {
if (instance_collector->max_count_ == 0 ||
instance_collector->instances_.size() < instance_collector->max_count_) {
instance_collector->instances_.push_back(obj);
@@ -1999,27 +1938,28 @@
}
private:
- const mirror::Class* const class_;
+ Handle<mirror::Class> const class_;
const uint32_t max_count_;
- std::vector<mirror::Object*>& instances_;
+ std::vector<ObjPtr<mirror::Object>>& instances_;
DISALLOW_COPY_AND_ASSIGN(InstanceCollector);
};
-void Heap::GetInstances(mirror::Class* c,
+void Heap::GetInstances(Handle<mirror::Class> c,
int32_t max_count,
- std::vector<mirror::Object*>& instances) {
+ std::vector<ObjPtr<mirror::Object>>& instances) {
InstanceCollector collector(c, max_count, instances);
VisitObjects(&InstanceCollector::Callback, &collector);
}
class ReferringObjectsFinder {
public:
- ReferringObjectsFinder(mirror::Object* object,
+ ReferringObjectsFinder(ObjPtr<mirror::Object> object,
int32_t max_count,
- std::vector<mirror::Object*>& referring_objects)
+ std::vector<ObjPtr<mirror::Object>>& referring_objects)
REQUIRES_SHARED(Locks::mutator_lock_)
- : object_(object), max_count_(max_count), referring_objects_(referring_objects) {
- }
+ : object_(object),
+ max_count_(max_count),
+ referring_objects_(referring_objects) {}
static void Callback(mirror::Object* obj, void* arg)
REQUIRES_SHARED(Locks::mutator_lock_, Locks::heap_bitmap_lock_) {
@@ -2029,12 +1969,14 @@
// For bitmap Visit.
// TODO: Fix lock analysis to not use NO_THREAD_SAFETY_ANALYSIS, requires support for
// annotalysis on visitors.
- void operator()(mirror::Object* o) const NO_THREAD_SAFETY_ANALYSIS {
+ void operator()(ObjPtr<mirror::Object> o) const NO_THREAD_SAFETY_ANALYSIS {
o->VisitReferences(*this, VoidFunctor());
}
// For Object::VisitReferences.
- void operator()(mirror::Object* obj, MemberOffset offset, bool is_static ATTRIBUTE_UNUSED) const
+ void operator()(ObjPtr<mirror::Object> obj,
+ MemberOffset offset,
+ bool is_static ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
mirror::Object* ref = obj->GetFieldObject<mirror::Object>(offset);
if (ref == object_ && (max_count_ == 0 || referring_objects_.size() < max_count_)) {
@@ -2047,14 +1989,15 @@
void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
private:
- const mirror::Object* const object_;
+ ObjPtr<mirror::Object> const object_;
const uint32_t max_count_;
- std::vector<mirror::Object*>& referring_objects_;
+ std::vector<ObjPtr<mirror::Object>>& referring_objects_;
DISALLOW_COPY_AND_ASSIGN(ReferringObjectsFinder);
};
-void Heap::GetReferringObjects(mirror::Object* o, int32_t max_count,
- std::vector<mirror::Object*>& referring_objects) {
+void Heap::GetReferringObjects(ObjPtr<mirror::Object> o,
+ int32_t max_count,
+ std::vector<ObjPtr<mirror::Object>>& referring_objects) {
ReferringObjectsFinder finder(o, max_count, referring_objects);
VisitObjects(&ReferringObjectsFinder::Callback, &finder);
}
@@ -3113,41 +3056,42 @@
const bool verify_referent_;
};
-void Heap::PushOnAllocationStackWithInternalGC(Thread* self, mirror::Object** obj) {
+void Heap::PushOnAllocationStackWithInternalGC(Thread* self, ObjPtr<mirror::Object>* obj) {
// Slow path, the allocation stack push back must have already failed.
- DCHECK(!allocation_stack_->AtomicPushBack(*obj));
+ DCHECK(!allocation_stack_->AtomicPushBack(obj->Ptr()));
do {
// TODO: Add handle VerifyObject.
StackHandleScope<1> hs(self);
- HandleWrapper<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
+ HandleWrapperObjPtr<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
// Push our object into the reserve region of the allocaiton stack. This is only required due
// to heap verification requiring that roots are live (either in the live bitmap or in the
// allocation stack).
- CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(*obj));
+ CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(obj->Ptr()));
CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false);
- } while (!allocation_stack_->AtomicPushBack(*obj));
+ } while (!allocation_stack_->AtomicPushBack(obj->Ptr()));
}
-void Heap::PushOnThreadLocalAllocationStackWithInternalGC(Thread* self, mirror::Object** obj) {
+void Heap::PushOnThreadLocalAllocationStackWithInternalGC(Thread* self,
+ ObjPtr<mirror::Object>* obj) {
// Slow path, the allocation stack push back must have already failed.
- DCHECK(!self->PushOnThreadLocalAllocationStack(*obj));
+ DCHECK(!self->PushOnThreadLocalAllocationStack(obj->Ptr()));
StackReference<mirror::Object>* start_address;
StackReference<mirror::Object>* end_address;
while (!allocation_stack_->AtomicBumpBack(kThreadLocalAllocationStackSize, &start_address,
&end_address)) {
// TODO: Add handle VerifyObject.
StackHandleScope<1> hs(self);
- HandleWrapper<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
+ HandleWrapperObjPtr<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
// Push our object into the reserve region of the allocaiton stack. This is only required due
// to heap verification requiring that roots are live (either in the live bitmap or in the
// allocation stack).
- CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(*obj));
+ CHECK(allocation_stack_->AtomicPushBackIgnoreGrowthLimit(obj->Ptr()));
// Push into the reserve allocation stack.
CollectGarbageInternal(collector::kGcTypeSticky, kGcCauseForAlloc, false);
}
self->SetThreadLocalAllocationStack(start_address, end_address);
// Retry on the new thread-local allocation stack.
- CHECK(self->PushOnThreadLocalAllocationStack(*obj)); // Must succeed.
+ CHECK(self->PushOnThreadLocalAllocationStack(obj->Ptr())); // Must succeed.
}
// Must do this with mutators suspended since we are directly accessing the allocation stacks.
@@ -3737,7 +3681,7 @@
}
}
-void Heap::AddFinalizerReference(Thread* self, mirror::Object** object) {
+void Heap::AddFinalizerReference(Thread* self, ObjPtr<mirror::Object>* object) {
ScopedObjectAccess soa(self);
ScopedLocalRef<jobject> arg(self->GetJniEnv(), soa.AddLocalReference<jobject>(*object));
jvalue args[1];
@@ -3747,9 +3691,11 @@
*object = soa.Decode<mirror::Object>(arg.get()).Ptr();
}
-void Heap::RequestConcurrentGCAndSaveObject(Thread* self, bool force_full, mirror::Object** obj) {
+void Heap::RequestConcurrentGCAndSaveObject(Thread* self,
+ bool force_full,
+ ObjPtr<mirror::Object>* obj) {
StackHandleScope<1> hs(self);
- HandleWrapper<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
+ HandleWrapperObjPtr<mirror::Object> wrapper(hs.NewHandleWrapper(obj));
RequestConcurrentGC(self, force_full);
}
@@ -4026,7 +3972,7 @@
mod_union_tables_.Put(mod_union_table->GetSpace(), mod_union_table);
}
-void Heap::CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count) {
+void Heap::CheckPreconditionsForAllocObject(ObjPtr<mirror::Class> c, size_t byte_count) {
CHECK(c == nullptr || (c->IsClassClass() && byte_count >= sizeof(mirror::Class)) ||
(c->IsVariableSize() || c->GetObjectSize() == byte_count)) << c->GetClassFlags();
CHECK_GE(byte_count, sizeof(mirror::Object));
@@ -4152,7 +4098,7 @@
return state.GetFrameCount();
}
-void Heap::CheckGcStressMode(Thread* self, mirror::Object** obj) {
+void Heap::CheckGcStressMode(Thread* self, ObjPtr<mirror::Object>* obj) {
auto* const runtime = Runtime::Current();
if (gc_stress_mode_ && runtime->GetClassLinker()->IsInitialized() &&
!runtime->IsActiveTransaction() && mirror::Class::HasJavaLangClass()) {
@@ -4191,9 +4137,9 @@
gc_disabled_for_shutdown_ = true;
}
-bool Heap::ObjectIsInBootImageSpace(mirror::Object* obj) const {
+bool Heap::ObjectIsInBootImageSpace(ObjPtr<mirror::Object> obj) const {
for (gc::space::ImageSpace* space : boot_image_spaces_) {
- if (space->HasAddress(obj)) {
+ if (space->HasAddress(obj.Ptr())) {
return true;
}
}
@@ -4250,7 +4196,7 @@
AllocationListener* old = GetAndOverwriteAllocationListener(&alloc_listener_, nullptr);
if (old != nullptr) {
- Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
+ Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
}
}
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index 5e17a52..95db4dd 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -34,6 +34,7 @@
#include "gc/collector_type.h"
#include "gc/space/large_object_space.h"
#include "globals.h"
+#include "handle.h"
#include "obj_ptr.h"
#include "object_callbacks.h"
#include "offsets.h"
@@ -194,36 +195,48 @@
// Allocates and initializes storage for an object instance.
template <bool kInstrumented, typename PreFenceVisitor>
mirror::Object* AllocObject(Thread* self,
- mirror::Class* klass,
+ ObjPtr<mirror::Class> klass,
size_t num_bytes,
const PreFenceVisitor& pre_fence_visitor)
REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_,
+ REQUIRES(!*gc_complete_lock_,
+ !*pending_task_lock_,
+ !*backtrace_lock_,
!Roles::uninterruptible_) {
- return AllocObjectWithAllocator<kInstrumented, true>(
- self, klass, num_bytes, GetCurrentAllocator(), pre_fence_visitor);
+ return AllocObjectWithAllocator<kInstrumented, true>(self,
+ klass,
+ num_bytes,
+ GetCurrentAllocator(),
+ pre_fence_visitor);
}
template <bool kInstrumented, typename PreFenceVisitor>
mirror::Object* AllocNonMovableObject(Thread* self,
- mirror::Class* klass,
+ ObjPtr<mirror::Class> klass,
size_t num_bytes,
const PreFenceVisitor& pre_fence_visitor)
REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_,
+ REQUIRES(!*gc_complete_lock_,
+ !*pending_task_lock_,
+ !*backtrace_lock_,
!Roles::uninterruptible_) {
- return AllocObjectWithAllocator<kInstrumented, true>(
- self, klass, num_bytes, GetCurrentNonMovingAllocator(), pre_fence_visitor);
+ return AllocObjectWithAllocator<kInstrumented, true>(self,
+ klass,
+ num_bytes,
+ GetCurrentNonMovingAllocator(),
+ pre_fence_visitor);
}
template <bool kInstrumented, bool kCheckLargeObject, typename PreFenceVisitor>
ALWAYS_INLINE mirror::Object* AllocObjectWithAllocator(Thread* self,
- mirror::Class* klass,
+ ObjPtr<mirror::Class> klass,
size_t byte_count,
AllocatorType allocator,
const PreFenceVisitor& pre_fence_visitor)
REQUIRES_SHARED(Locks::mutator_lock_)
- REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_,
+ REQUIRES(!*gc_complete_lock_,
+ !*pending_task_lock_,
+ !*backtrace_lock_,
!Roles::uninterruptible_);
AllocatorType GetCurrentAllocator() const {
@@ -241,7 +254,7 @@
void VisitObjectsPaused(ObjectCallback callback, void* arg)
REQUIRES(Locks::mutator_lock_, !Locks::heap_bitmap_lock_, !*gc_complete_lock_);
- void CheckPreconditionsForAllocObject(mirror::Class* c, size_t byte_count)
+ void CheckPreconditionsForAllocObject(ObjPtr<mirror::Class> c, size_t byte_count)
REQUIRES_SHARED(Locks::mutator_lock_);
void RegisterNativeAllocation(JNIEnv* env, size_t bytes)
@@ -263,7 +276,7 @@
// The given reference is believed to be to an object in the Java heap, check the soundness of it.
// TODO: NO_THREAD_SAFETY_ANALYSIS since we call this everywhere and it is impossible to find a
// proper lock ordering for it.
- void VerifyObjectBody(mirror::Object* o) NO_THREAD_SAFETY_ANALYSIS;
+ void VerifyObjectBody(ObjPtr<mirror::Object> o) NO_THREAD_SAFETY_ANALYSIS;
// Check sanity of all live references.
void VerifyHeap() REQUIRES(!Locks::heap_bitmap_lock_);
@@ -276,16 +289,16 @@
// A weaker test than IsLiveObject or VerifyObject that doesn't require the heap lock,
// and doesn't abort on error, allowing the caller to report more
// meaningful diagnostics.
- bool IsValidObjectAddress(ObjPtr<mirror::Object> obj) const REQUIRES_SHARED(Locks::mutator_lock_);
+ bool IsValidObjectAddress(const void* obj) const REQUIRES_SHARED(Locks::mutator_lock_);
// Faster alternative to IsHeapAddress since finding if an object is in the large object space is
// very slow.
- bool IsNonDiscontinuousSpaceHeapAddress(const mirror::Object* obj) const
+ bool IsNonDiscontinuousSpaceHeapAddress(const void* addr) const
REQUIRES_SHARED(Locks::mutator_lock_);
// Returns true if 'obj' is a live heap object, false otherwise (including for invalid addresses).
// Requires the heap lock to be held.
- bool IsLiveObjectLocked(mirror::Object* obj,
+ bool IsLiveObjectLocked(ObjPtr<mirror::Object> obj,
bool search_allocation_stack = true,
bool search_live_stack = true,
bool sorted = false)
@@ -321,19 +334,23 @@
// Implements VMDebug.countInstancesOfClass and JDWP VM_InstanceCount.
// The boolean decides whether to use IsAssignableFrom or == when comparing classes.
- void CountInstances(const std::vector<mirror::Class*>& classes,
+ void CountInstances(const std::vector<Handle<mirror::Class>>& classes,
bool use_is_assignable_from,
uint64_t* counts)
REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+
// Implements JDWP RT_Instances.
- void GetInstances(mirror::Class* c, int32_t max_count, std::vector<mirror::Object*>& instances)
+ void GetInstances(Handle<mirror::Class> c,
+ int32_t max_count,
+ std::vector<ObjPtr<mirror::Object>>& instances)
REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
+
// Implements JDWP OR_ReferringObjects.
- void GetReferringObjects(mirror::Object* o,
+ void GetReferringObjects(ObjPtr<mirror::Object> o,
int32_t max_count,
- std::vector<mirror::Object*>& referring_objects)
+ std::vector<ObjPtr<mirror::Object>>& referring_objects)
REQUIRES(!Locks::heap_bitmap_lock_, !*gc_complete_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -445,16 +462,14 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Write barrier for array operations that update many field positions
- ALWAYS_INLINE void WriteBarrierArray(const mirror::Object* dst,
- int start_offset ATTRIBUTE_UNUSED,
+ ALWAYS_INLINE void WriteBarrierArray(ObjPtr<mirror::Object> dst,
+ int start_offset,
// TODO: element_count or byte_count?
- size_t length ATTRIBUTE_UNUSED) {
- card_table_->MarkCard(dst);
- }
+ size_t length)
+ REQUIRES_SHARED(Locks::mutator_lock_);
- ALWAYS_INLINE void WriteBarrierEveryFieldOf(const mirror::Object* obj) {
- card_table_->MarkCard(obj);
- }
+ ALWAYS_INLINE void WriteBarrierEveryFieldOf(ObjPtr<mirror::Object> obj)
+ REQUIRES_SHARED(Locks::mutator_lock_);
accounting::CardTable* GetCardTable() const {
return card_table_.get();
@@ -464,7 +479,7 @@
return rb_table_.get();
}
- void AddFinalizerReference(Thread* self, mirror::Object** object);
+ void AddFinalizerReference(Thread* self, ObjPtr<mirror::Object>* object);
// Returns the number of bytes currently allocated.
size_t GetBytesAllocated() const {
@@ -527,12 +542,20 @@
// get the space that corresponds to an object's address. Current implementation searches all
// spaces in turn. If fail_ok is false then failing to find a space will cause an abort.
// TODO: consider using faster data structure like binary tree.
- space::ContinuousSpace* FindContinuousSpaceFromObject(const mirror::Object*, bool fail_ok) const
+ space::ContinuousSpace* FindContinuousSpaceFromObject(ObjPtr<mirror::Object>, bool fail_ok) const
REQUIRES_SHARED(Locks::mutator_lock_);
- space::DiscontinuousSpace* FindDiscontinuousSpaceFromObject(const mirror::Object*,
+
+ space::ContinuousSpace* FindContinuousSpaceFromAddress(const mirror::Object* addr) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ space::DiscontinuousSpace* FindDiscontinuousSpaceFromObject(ObjPtr<mirror::Object>,
bool fail_ok) const
REQUIRES_SHARED(Locks::mutator_lock_);
- space::Space* FindSpaceFromObject(const mirror::Object*, bool fail_ok) const
+
+ space::Space* FindSpaceFromObject(ObjPtr<mirror::Object> obj, bool fail_ok) const
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
+ space::Space* FindSpaceFromAddress(const void* ptr) const
REQUIRES_SHARED(Locks::mutator_lock_);
void DumpForSigQuit(std::ostream& os) REQUIRES(!*gc_complete_lock_, !native_histogram_lock_);
@@ -598,7 +621,7 @@
return boot_image_spaces_;
}
- bool ObjectIsInBootImageSpace(mirror::Object* obj) const
+ bool ObjectIsInBootImageSpace(ObjPtr<mirror::Object> obj) const
REQUIRES_SHARED(Locks::mutator_lock_);
bool IsInBootImageOatFile(const void* p) const
@@ -650,12 +673,6 @@
void DumpSpaces(std::ostream& stream) const REQUIRES_SHARED(Locks::mutator_lock_);
std::string DumpSpaces() const REQUIRES_SHARED(Locks::mutator_lock_);
- // Dump object should only be used by the signal handler.
- void DumpObject(std::ostream& stream, mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS;
- // Safe version of pretty type of which check to make sure objects are heap addresses.
- std::string SafeGetClassDescriptor(mirror::Class* klass) NO_THREAD_SAFETY_ANALYSIS;
- std::string SafePrettyTypeOf(mirror::Object* obj) NO_THREAD_SAFETY_ANALYSIS;
-
// GC performance measuring
void DumpGcPerformanceInfo(std::ostream& os)
REQUIRES(!*gc_complete_lock_, !native_histogram_lock_);
@@ -837,11 +854,11 @@
collector_type == kCollectorTypeMC ||
collector_type == kCollectorTypeHomogeneousSpaceCompact;
}
- bool ShouldAllocLargeObject(mirror::Class* c, size_t byte_count) const
+ bool ShouldAllocLargeObject(ObjPtr<mirror::Class> c, size_t byte_count) const
REQUIRES_SHARED(Locks::mutator_lock_);
ALWAYS_INLINE void CheckConcurrentGC(Thread* self,
size_t new_num_bytes_allocated,
- mirror::Object** obj)
+ ObjPtr<mirror::Object>* obj)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!*pending_task_lock_, !*gc_complete_lock_);
@@ -852,7 +869,7 @@
// We don't force this to be inlined since it is a slow path.
template <bool kInstrumented, typename PreFenceVisitor>
mirror::Object* AllocLargeObject(Thread* self,
- mirror::Class** klass,
+ ObjPtr<mirror::Class>* klass,
size_t byte_count,
const PreFenceVisitor& pre_fence_visitor)
REQUIRES_SHARED(Locks::mutator_lock_)
@@ -867,14 +884,14 @@
size_t* bytes_allocated,
size_t* usable_size,
size_t* bytes_tl_bulk_allocated,
- mirror::Class** klass)
+ ObjPtr<mirror::Class>* klass)
REQUIRES(!Locks::thread_suspend_count_lock_, !*gc_complete_lock_, !*pending_task_lock_)
REQUIRES_SHARED(Locks::mutator_lock_);
// Allocate into a specific space.
mirror::Object* AllocateInto(Thread* self,
space::AllocSpace* space,
- mirror::Class* c,
+ ObjPtr<mirror::Class> c,
size_t bytes)
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -899,10 +916,6 @@
template <bool kGrow>
ALWAYS_INLINE bool IsOutOfMemoryOnAllocation(AllocatorType allocator_type, size_t alloc_size);
- // Returns true if the address passed in is within the address range of a continuous space.
- bool IsValidContinuousSpaceObjectAddress(const mirror::Object* obj) const
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Run the finalizers. If timeout is non zero, then we use the VMRuntime version.
void RunFinalization(JNIEnv* env, uint64_t timeout);
@@ -914,7 +927,7 @@
void RequestCollectorTransition(CollectorType desired_collector_type, uint64_t delta_time)
REQUIRES(!*pending_task_lock_);
- void RequestConcurrentGCAndSaveObject(Thread* self, bool force_full, mirror::Object** obj)
+ void RequestConcurrentGCAndSaveObject(Thread* self, bool force_full, ObjPtr<mirror::Object>* obj)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!*pending_task_lock_);
bool IsGCRequestPending() const;
@@ -986,13 +999,13 @@
REQUIRES_SHARED(Locks::mutator_lock_);
// Push an object onto the allocation stack.
- void PushOnAllocationStack(Thread* self, mirror::Object** obj)
+ void PushOnAllocationStack(Thread* self, ObjPtr<mirror::Object>* obj)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!*gc_complete_lock_, !*pending_task_lock_);
- void PushOnAllocationStackWithInternalGC(Thread* self, mirror::Object** obj)
+ void PushOnAllocationStackWithInternalGC(Thread* self, ObjPtr<mirror::Object>* obj)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!*gc_complete_lock_, !*pending_task_lock_);
- void PushOnThreadLocalAllocationStackWithInternalGC(Thread* thread, mirror::Object** obj)
+ void PushOnThreadLocalAllocationStackWithInternalGC(Thread* thread, ObjPtr<mirror::Object>* obj)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!*gc_complete_lock_, !*pending_task_lock_);
@@ -1023,7 +1036,7 @@
void UpdateGcCountRateHistograms() REQUIRES(gc_complete_lock_);
// GC stress mode attempts to do one GC per unique backtrace.
- void CheckGcStressMode(Thread* self, mirror::Object** obj)
+ void CheckGcStressMode(Thread* self, ObjPtr<mirror::Object>* obj)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!*gc_complete_lock_, !*pending_task_lock_, !*backtrace_lock_);
diff --git a/runtime/handle_scope-inl.h b/runtime/handle_scope-inl.h
index cceb007..b212d09 100644
--- a/runtime/handle_scope-inl.h
+++ b/runtime/handle_scope-inl.h
@@ -28,24 +28,30 @@
namespace art {
template<size_t kNumReferences>
-inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value)
- : HandleScope(self->GetTopHandleScope(), kNumReferences), self_(self), pos_(0) {
- DCHECK_EQ(self, Thread::Current());
+inline FixedSizeHandleScope<kNumReferences>::FixedSizeHandleScope(BaseHandleScope* link,
+ mirror::Object* fill_value)
+ : HandleScope(link, kNumReferences) {
if (kDebugLocking) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
- static_assert(kNumReferences >= 1, "StackHandleScope must contain at least 1 reference");
- // TODO: Figure out how to use a compile assert.
- CHECK_EQ(&storage_[0], GetReferences());
+ static_assert(kNumReferences >= 1, "FixedSizeHandleScope must contain at least 1 reference");
+ DCHECK_EQ(&storage_[0], GetReferences()); // TODO: Figure out how to use a compile assert.
for (size_t i = 0; i < kNumReferences; ++i) {
SetReference(i, fill_value);
}
+}
+
+template<size_t kNumReferences>
+inline StackHandleScope<kNumReferences>::StackHandleScope(Thread* self, mirror::Object* fill_value)
+ : FixedSizeHandleScope<kNumReferences>(self->GetTopHandleScope(), fill_value),
+ self_(self) {
+ DCHECK_EQ(self, Thread::Current());
self_->PushHandleScope(this);
}
template<size_t kNumReferences>
inline StackHandleScope<kNumReferences>::~StackHandleScope() {
- HandleScope* top_handle_scope = self_->PopHandleScope();
+ BaseHandleScope* top_handle_scope = self_->PopHandleScope();
DCHECK_EQ(top_handle_scope, this);
if (kDebugLocking) {
Locks::mutator_lock_->AssertSharedHeld(self_);
@@ -66,7 +72,7 @@
}
inline mirror::Object* HandleScope::GetReference(size_t i) const {
- DCHECK_LT(i, number_of_references_);
+ DCHECK_LT(i, NumberOfReferences());
if (kDebugLocking) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
@@ -74,12 +80,12 @@
}
inline Handle<mirror::Object> HandleScope::GetHandle(size_t i) {
- DCHECK_LT(i, number_of_references_);
+ DCHECK_LT(i, NumberOfReferences());
return Handle<mirror::Object>(&GetReferences()[i]);
}
inline MutableHandle<mirror::Object> HandleScope::GetMutableHandle(size_t i) {
- DCHECK_LT(i, number_of_references_);
+ DCHECK_LT(i, NumberOfReferences());
return MutableHandle<mirror::Object>(&GetReferences()[i]);
}
@@ -87,7 +93,7 @@
if (kDebugLocking) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
- DCHECK_LT(i, number_of_references_);
+ DCHECK_LT(i, NumberOfReferences());
GetReferences()[i].Assign(object);
}
@@ -95,13 +101,13 @@
// A HandleScope should always contain something. One created by the
// jni_compiler should have a jobject/jclass as a native method is
// passed in a this pointer or a class
- DCHECK_GT(number_of_references_, 0U);
+ DCHECK_GT(NumberOfReferences(), 0U);
return &GetReferences()[0] <= handle_scope_entry &&
handle_scope_entry <= &GetReferences()[number_of_references_ - 1];
}
template<size_t kNumReferences> template<class T>
-inline MutableHandle<T> StackHandleScope<kNumReferences>::NewHandle(T* object) {
+inline MutableHandle<T> FixedSizeHandleScope<kNumReferences>::NewHandle(T* object) {
SetReference(pos_, object);
MutableHandle<T> h(GetHandle<T>(pos_));
pos_++;
@@ -109,24 +115,24 @@
}
template<size_t kNumReferences> template<class MirrorType, bool kPoison>
-inline MutableHandle<MirrorType> StackHandleScope<kNumReferences>::NewHandle(
+inline MutableHandle<MirrorType> FixedSizeHandleScope<kNumReferences>::NewHandle(
ObjPtr<MirrorType, kPoison> object) {
return NewHandle(object.Ptr());
}
template<size_t kNumReferences> template<class T>
-inline HandleWrapper<T> StackHandleScope<kNumReferences>::NewHandleWrapper(T** object) {
+inline HandleWrapper<T> FixedSizeHandleScope<kNumReferences>::NewHandleWrapper(T** object) {
return HandleWrapper<T>(object, NewHandle(*object));
}
template<size_t kNumReferences> template<class T>
-inline HandleWrapperObjPtr<T> StackHandleScope<kNumReferences>::NewHandleWrapper(
+inline HandleWrapperObjPtr<T> FixedSizeHandleScope<kNumReferences>::NewHandleWrapper(
ObjPtr<T>* object) {
return HandleWrapperObjPtr<T>(object, NewHandle(*object));
}
template<size_t kNumReferences>
-inline void StackHandleScope<kNumReferences>::SetReference(size_t i, mirror::Object* object) {
+inline void FixedSizeHandleScope<kNumReferences>::SetReference(size_t i, mirror::Object* object) {
if (kDebugLocking) {
Locks::mutator_lock_->AssertSharedHeld(Thread::Current());
}
@@ -135,12 +141,111 @@
GetReferences()[i].Assign(object);
}
+// Number of references contained within this handle scope.
+inline uint32_t BaseHandleScope::NumberOfReferences() const {
+ return LIKELY(!IsVariableSized())
+ ? AsHandleScope()->NumberOfReferences()
+ : AsVariableSized()->NumberOfReferences();
+}
+
+inline bool BaseHandleScope::Contains(StackReference<mirror::Object>* handle_scope_entry) const {
+ return LIKELY(!IsVariableSized())
+ ? AsHandleScope()->Contains(handle_scope_entry)
+ : AsVariableSized()->Contains(handle_scope_entry);
+}
+
+template <typename Visitor>
+inline void BaseHandleScope::VisitRoots(Visitor& visitor) {
+ if (LIKELY(!IsVariableSized())) {
+ AsHandleScope()->VisitRoots(visitor);
+ } else {
+ AsVariableSized()->VisitRoots(visitor);
+ }
+}
+
+inline VariableSizedHandleScope* BaseHandleScope::AsVariableSized() {
+ DCHECK(IsVariableSized());
+ return down_cast<VariableSizedHandleScope*>(this);
+}
+
+inline HandleScope* BaseHandleScope::AsHandleScope() {
+ DCHECK(!IsVariableSized());
+ return down_cast<HandleScope*>(this);
+}
+
+inline const VariableSizedHandleScope* BaseHandleScope::AsVariableSized() const {
+ DCHECK(IsVariableSized());
+ return down_cast<const VariableSizedHandleScope*>(this);
+}
+
+inline const HandleScope* BaseHandleScope::AsHandleScope() const {
+ DCHECK(!IsVariableSized());
+ return down_cast<const HandleScope*>(this);
+}
+
+template<class T>
+MutableHandle<T> VariableSizedHandleScope::NewHandle(T* object) {
+ if (current_scope_->RemainingSlots() == 0) {
+ current_scope_ = new LocalScopeType(current_scope_);
+ }
+ return current_scope_->NewHandle(object);
+}
+
template<class MirrorType, bool kPoison>
-inline MutableHandle<MirrorType> StackHandleScopeCollection::NewHandle(
+inline MutableHandle<MirrorType> VariableSizedHandleScope::NewHandle(
ObjPtr<MirrorType, kPoison> ptr) {
return NewHandle(ptr.Ptr());
}
+inline VariableSizedHandleScope::VariableSizedHandleScope(Thread* const self)
+ : BaseHandleScope(self->GetTopHandleScope()),
+ self_(self) {
+ current_scope_ = new LocalScopeType(/*link*/ nullptr);
+ self_->PushHandleScope(this);
+}
+
+inline VariableSizedHandleScope::~VariableSizedHandleScope() {
+ BaseHandleScope* top_handle_scope = self_->PopHandleScope();
+ DCHECK_EQ(top_handle_scope, this);
+ while (current_scope_ != nullptr) {
+ LocalScopeType* next = reinterpret_cast<LocalScopeType*>(current_scope_->GetLink());
+ delete current_scope_;
+ current_scope_ = next;
+ }
+}
+
+inline uint32_t VariableSizedHandleScope::NumberOfReferences() const {
+ uint32_t sum = 0;
+ const LocalScopeType* cur = current_scope_;
+ while (cur != nullptr) {
+ sum += cur->NumberOfReferences();
+ cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink());
+ }
+ return sum;
+}
+
+inline bool VariableSizedHandleScope::Contains(StackReference<mirror::Object>* handle_scope_entry)
+ const {
+ const LocalScopeType* cur = current_scope_;
+ while (cur != nullptr) {
+ if (cur->Contains(handle_scope_entry)) {
+ return true;
+ }
+ cur = reinterpret_cast<const LocalScopeType*>(cur->GetLink());
+ }
+ return false;
+}
+
+template <typename Visitor>
+inline void VariableSizedHandleScope::VisitRoots(Visitor& visitor) {
+ LocalScopeType* cur = current_scope_;
+ while (cur != nullptr) {
+ cur->VisitRoots(visitor);
+ cur = reinterpret_cast<LocalScopeType*>(cur->GetLink());
+ }
+}
+
+
} // namespace art
#endif // ART_RUNTIME_HANDLE_SCOPE_INL_H_
diff --git a/runtime/handle_scope.h b/runtime/handle_scope.h
index fc729a5..8a0aba6 100644
--- a/runtime/handle_scope.h
+++ b/runtime/handle_scope.h
@@ -29,26 +29,69 @@
namespace art {
+class HandleScope;
template<class MirrorType, bool kPoison> class ObjPtr;
+class Thread;
+class VariableSizedHandleScope;
namespace mirror {
class Object;
}
-class Thread;
+// Basic handle scope, tracked by a list. May be variable sized.
+class PACKED(4) BaseHandleScope {
+ public:
+ bool IsVariableSized() const {
+ return number_of_references_ == kNumReferencesVariableSized;
+ }
+
+ // Number of references contained within this handle scope.
+ ALWAYS_INLINE uint32_t NumberOfReferences() const;
+
+ ALWAYS_INLINE bool Contains(StackReference<mirror::Object>* handle_scope_entry) const;
+
+ template <typename Visitor>
+ ALWAYS_INLINE void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
+ // Link to previous BaseHandleScope or null.
+ BaseHandleScope* GetLink() const {
+ return link_;
+ }
+
+ ALWAYS_INLINE VariableSizedHandleScope* AsVariableSized();
+ ALWAYS_INLINE HandleScope* AsHandleScope();
+ ALWAYS_INLINE const VariableSizedHandleScope* AsVariableSized() const;
+ ALWAYS_INLINE const HandleScope* AsHandleScope() const;
+
+ protected:
+ BaseHandleScope(BaseHandleScope* link, uint32_t num_references)
+ : link_(link),
+ number_of_references_(num_references) {}
+
+ // Variable sized constructor.
+ BaseHandleScope(BaseHandleScope* link)
+ : link_(link),
+ number_of_references_(kNumReferencesVariableSized) {}
+
+ static constexpr int32_t kNumReferencesVariableSized = -1;
+
+ // Link-list of handle scopes. The root is held by a Thread.
+ BaseHandleScope* const link_;
+
+ // Number of handlerized references. -1 for variable sized handle scopes.
+ const int32_t number_of_references_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BaseHandleScope);
+};
// HandleScopes are scoped objects containing a number of Handles. They are used to allocate
// handles, for these handles (and the objects contained within them) to be visible/roots for the
// GC. It is most common to stack allocate HandleScopes using StackHandleScope.
-class PACKED(4) HandleScope {
+class PACKED(4) HandleScope : public BaseHandleScope {
public:
~HandleScope() {}
- // Number of references contained within this handle scope.
- uint32_t NumberOfReferences() const {
- return number_of_references_;
- }
-
// We have versions with and without explicit pointer size of the following. The first two are
// used at runtime, so OFFSETOF_MEMBER computes the right offsets automatically. The last one
// takes the pointer size explicitly so that at compile time we can cross-compile correctly.
@@ -59,11 +102,6 @@
// Returns the size of a HandleScope containing num_references handles.
static size_t SizeOf(PointerSize pointer_size, uint32_t num_references);
- // Link to previous HandleScope or null.
- HandleScope* GetLink() const {
- return link_;
- }
-
ALWAYS_INLINE mirror::Object* GetReference(size_t i) const
REQUIRES_SHARED(Locks::mutator_lock_);
@@ -93,11 +131,26 @@
}
// Placement new creation.
- static HandleScope* Create(void* storage, HandleScope* link, uint32_t num_references)
+ static HandleScope* Create(void* storage, BaseHandleScope* link, uint32_t num_references)
WARN_UNUSED {
return new (storage) HandleScope(link, num_references);
}
+ // Number of references contained within this handle scope.
+ ALWAYS_INLINE uint32_t NumberOfReferences() const {
+ DCHECK_GE(number_of_references_, 0);
+ return static_cast<uint32_t>(number_of_references_);
+ }
+
+ template <typename Visitor>
+ void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_) {
+ for (size_t i = 0, count = NumberOfReferences(); i < count; ++i) {
+ // GetReference returns a pointer to the stack reference within the handle scope. If this
+ // needs to be updated, it will be done by the root visitor.
+ visitor.VisitRootIfNonNull(GetHandle(i).GetReference());
+ }
+ }
+
protected:
// Return backing storage used for references.
ALWAYS_INLINE StackReference<mirror::Object>* GetReferences() const {
@@ -105,20 +158,11 @@
return reinterpret_cast<StackReference<mirror::Object>*>(address);
}
- explicit HandleScope(size_t number_of_references) :
- link_(nullptr), number_of_references_(number_of_references) {
- }
+ explicit HandleScope(size_t number_of_references) : HandleScope(nullptr, number_of_references) {}
// Semi-hidden constructor. Construction expected by generated code and StackHandleScope.
- HandleScope(HandleScope* link, uint32_t num_references) :
- link_(link), number_of_references_(num_references) {
- }
-
- // Link-list of handle scopes. The root is held by a Thread.
- HandleScope* const link_;
-
- // Number of handlerized references.
- const uint32_t number_of_references_;
+ HandleScope(BaseHandleScope* link, uint32_t num_references)
+ : BaseHandleScope(link, num_references) {}
// Storage for references.
// StackReference<mirror::Object> references_[number_of_references_]
@@ -165,14 +209,10 @@
ObjPtr<T>* const obj_;
};
-
-// Scoped handle storage of a fixed size that is usually stack allocated.
+// Fixed size handle scope that is not necessarily linked in the thread.
template<size_t kNumReferences>
-class PACKED(4) StackHandleScope FINAL : public HandleScope {
+class PACKED(4) FixedSizeHandleScope : public HandleScope {
public:
- explicit ALWAYS_INLINE StackHandleScope(Thread* self, mirror::Object* fill_value = nullptr);
- ALWAYS_INLINE ~StackHandleScope();
-
template<class T>
ALWAYS_INLINE MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_);
@@ -191,11 +231,15 @@
ALWAYS_INLINE void SetReference(size_t i, mirror::Object* object)
REQUIRES_SHARED(Locks::mutator_lock_);
- Thread* Self() const {
- return self_;
+ size_t RemainingSlots() const {
+ return kNumReferences - pos_;
}
private:
+ explicit ALWAYS_INLINE FixedSizeHandleScope(BaseHandleScope* link,
+ mirror::Object* fill_value = nullptr);
+ ALWAYS_INLINE ~FixedSizeHandleScope() {}
+
template<class T>
ALWAYS_INLINE MutableHandle<T> GetHandle(size_t i) REQUIRES_SHARED(Locks::mutator_lock_) {
DCHECK_LT(i, kNumReferences);
@@ -205,66 +249,65 @@
// Reference storage needs to be first as expected by the HandleScope layout.
StackReference<mirror::Object> storage_[kNumReferences];
+ // Position new handles will be created.
+ size_t pos_ = 0;
+
+ template<size_t kNumRefs> friend class StackHandleScope;
+ friend class VariableSizedHandleScope;
+};
+
+// Scoped handle storage of a fixed size that is stack allocated.
+template<size_t kNumReferences>
+class PACKED(4) StackHandleScope FINAL : public FixedSizeHandleScope<kNumReferences> {
+ public:
+ explicit ALWAYS_INLINE StackHandleScope(Thread* self, mirror::Object* fill_value = nullptr);
+ ALWAYS_INLINE ~StackHandleScope();
+
+ Thread* Self() const {
+ return self_;
+ }
+
+ private:
// The thread that the stack handle scope is a linked list upon. The stack handle scope will
// push and pop itself from this thread.
Thread* const self_;
-
- // Position new handles will be created.
- size_t pos_;
-
- template<size_t kNumRefs> friend class StackHandleScope;
};
-// Utility class to manage a collection (stack) of StackHandleScope. All the managed
-// scope handle have the same fixed sized.
-// Calls to NewHandle will create a new handle inside the top StackHandleScope.
-// When the handle scope becomes full a new one is created and push on top of the
-// previous.
-//
-// NB:
-// - it is not safe to use the *same* StackHandleScopeCollection intermix with
-// other StackHandleScopes.
-// - this is a an easy way around implementing a full ZoneHandleScope to manage an
-// arbitrary number of handles.
-class StackHandleScopeCollection {
+// Utility class to manage a variable sized handle scope by having a list of fixed size handle
+// scopes.
+// Calls to NewHandle will create a new handle inside the current FixedSizeHandleScope.
+// When the current handle scope becomes full a new one is created and put at the front of the
+// list.
+class VariableSizedHandleScope : public BaseHandleScope {
public:
- explicit StackHandleScopeCollection(Thread* const self) :
- self_(self),
- current_scope_num_refs_(0) {
- }
-
- ~StackHandleScopeCollection() {
- while (!scopes_.empty()) {
- delete scopes_.top();
- scopes_.pop();
- }
- }
+ explicit VariableSizedHandleScope(Thread* const self);
+ ~VariableSizedHandleScope();
template<class T>
- MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_) {
- if (scopes_.empty() || current_scope_num_refs_ >= kNumReferencesPerScope) {
- StackHandleScope<kNumReferencesPerScope>* scope =
- new StackHandleScope<kNumReferencesPerScope>(self_);
- scopes_.push(scope);
- current_scope_num_refs_ = 0;
- }
- current_scope_num_refs_++;
- return scopes_.top()->NewHandle(object);
- }
+ MutableHandle<T> NewHandle(T* object) REQUIRES_SHARED(Locks::mutator_lock_);
template<class MirrorType, bool kPoison>
MutableHandle<MirrorType> NewHandle(ObjPtr<MirrorType, kPoison> ptr)
REQUIRES_SHARED(Locks::mutator_lock_);
+ // Number of references contained within this handle scope.
+ ALWAYS_INLINE uint32_t NumberOfReferences() const;
+
+ ALWAYS_INLINE bool Contains(StackReference<mirror::Object>* handle_scope_entry) const;
+
+ template <typename Visitor>
+ void VisitRoots(Visitor& visitor) REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
static constexpr size_t kNumReferencesPerScope = 4;
Thread* const self_;
- std::stack<StackHandleScope<kNumReferencesPerScope>*> scopes_;
- size_t current_scope_num_refs_;
+ // Linked list of fixed size handle scopes.
+ using LocalScopeType = FixedSizeHandleScope<kNumReferencesPerScope>;
+ LocalScopeType* current_scope_;
- DISALLOW_COPY_AND_ASSIGN(StackHandleScopeCollection);
+ DISALLOW_COPY_AND_ASSIGN(VariableSizedHandleScope);
};
} // namespace art
diff --git a/runtime/handle_scope_test.cc b/runtime/handle_scope_test.cc
index c269a37..92063c4 100644
--- a/runtime/handle_scope_test.cc
+++ b/runtime/handle_scope_test.cc
@@ -15,6 +15,7 @@
*/
#include "base/enums.h"
+#include "common_runtime_test.h"
#include "gtest/gtest.h"
#include "handle_scope-inl.h"
#include "scoped_thread_state_change-inl.h"
@@ -22,51 +23,85 @@
namespace art {
-// Handle scope with a fixed size which is allocated on the stack.
-template<size_t kNumReferences>
-class NoThreadStackHandleScope : public HandleScope {
- public:
- explicit NoThreadStackHandleScope(HandleScope* link) : HandleScope(link, kNumReferences) {
- }
- ~NoThreadStackHandleScope() {
- }
-
- private:
- // references_storage_ needs to be first so that it matches the address of references_
- StackReference<mirror::Object> references_storage_[kNumReferences];
-};
+class HandleScopeTest : public CommonRuntimeTest {};
// Test the offsets computed for members of HandleScope. Because of cross-compiling
// it is impossible the use OFFSETOF_MEMBER, so we do some reasonable computations ourselves. This
// test checks whether we do the right thing.
-TEST(HandleScopeTest, Offsets) NO_THREAD_SAFETY_ANALYSIS {
+TEST_F(HandleScopeTest, Offsets) {
+ ScopedObjectAccess soa(Thread::Current());
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
// As the members of HandleScope are private, we cannot use OFFSETOF_MEMBER
// here. So do the inverse: set some data, and access it through pointers created from the offsets.
- NoThreadStackHandleScope<0x9ABC> test_table(reinterpret_cast<HandleScope*>(0x5678));
- test_table.SetReference(0, reinterpret_cast<mirror::Object*>(0x1234));
+ StackHandleScope<0x1> hs0(soa.Self());
+ static const size_t kNumReferences = 0x9ABC;
+ StackHandleScope<kNumReferences> test_table(soa.Self());
+ ObjPtr<mirror::Class> c = class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;");
+ test_table.SetReference(0, c.Ptr());
uint8_t* table_base_ptr = reinterpret_cast<uint8_t*>(&test_table);
{
- uintptr_t* link_ptr = reinterpret_cast<uintptr_t*>(table_base_ptr +
+ BaseHandleScope** link_ptr = reinterpret_cast<BaseHandleScope**>(table_base_ptr +
HandleScope::LinkOffset(kRuntimePointerSize));
- EXPECT_EQ(*link_ptr, static_cast<size_t>(0x5678));
+ EXPECT_EQ(*link_ptr, &hs0);
}
{
uint32_t* num_ptr = reinterpret_cast<uint32_t*>(table_base_ptr +
HandleScope::NumberOfReferencesOffset(kRuntimePointerSize));
- EXPECT_EQ(*num_ptr, static_cast<size_t>(0x9ABC));
+ EXPECT_EQ(*num_ptr, static_cast<size_t>(kNumReferences));
}
{
- // Assume sizeof(StackReference<mirror::Object>) == sizeof(uint32_t)
- // TODO: How can we make this assumption-less but still access directly and fully?
- EXPECT_EQ(sizeof(StackReference<mirror::Object>), sizeof(uint32_t));
-
- uint32_t* ref_ptr = reinterpret_cast<uint32_t*>(table_base_ptr +
+ auto* ref_ptr = reinterpret_cast<StackReference<mirror::Object>*>(table_base_ptr +
HandleScope::ReferencesOffset(kRuntimePointerSize));
- EXPECT_EQ(*ref_ptr, static_cast<uint32_t>(0x1234));
+ EXPECT_OBJ_PTR_EQ(ref_ptr->AsMirrorPtr(), c);
+ }
+}
+
+class CollectVisitor {
+ public:
+ void VisitRootIfNonNull(StackReference<mirror::Object>* ref) {
+ if (!ref->IsNull()) {
+ visited.insert(ref);
+ }
+ ++total_visited;
+ }
+
+ std::set<StackReference<mirror::Object>*> visited;
+ size_t total_visited = 0; // including null.
+};
+
+// Test functionality of variable sized handle scopes.
+TEST_F(HandleScopeTest, VariableSized) {
+ ScopedObjectAccess soa(Thread::Current());
+ VariableSizedHandleScope hs(soa.Self());
+ ClassLinker* const class_linker = Runtime::Current()->GetClassLinker();
+ Handle<mirror::Class> c =
+ hs.NewHandle(class_linker->FindSystemClass(soa.Self(), "Ljava/lang/Object;"));
+ // Test nested scopes.
+ StackHandleScope<1> inner(soa.Self());
+ inner.NewHandle(c->AllocObject(soa.Self()));
+ // Add a bunch of handles and make sure callbacks work.
+ static const size_t kNumHandles = 100;
+ std::vector<Handle<mirror::Object>> handles;
+ for (size_t i = 0; i < kNumHandles; ++i) {
+ BaseHandleScope* base = &hs;
+ ObjPtr<mirror::Object> o = c->AllocObject(soa.Self());
+ handles.push_back(hs.NewHandle(o));
+ EXPECT_OBJ_PTR_EQ(o, handles.back().Get());
+ EXPECT_TRUE(hs.Contains(handles.back().GetReference()));
+ EXPECT_TRUE(base->Contains(handles.back().GetReference()));
+ EXPECT_EQ(hs.NumberOfReferences(), base->NumberOfReferences());
+ }
+ CollectVisitor visitor;
+ BaseHandleScope* base = &hs;
+ base->VisitRoots(visitor);
+ EXPECT_LE(visitor.visited.size(), base->NumberOfReferences());
+ EXPECT_EQ(visitor.total_visited, base->NumberOfReferences());
+ for (StackReference<mirror::Object>* ref : visitor.visited) {
+ EXPECT_TRUE(base->Contains(ref));
}
}
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index e05f8f3..e357fa6 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -90,7 +90,7 @@
}
uint32_t idx = ExtractIndex(iref);
ObjPtr<mirror::Object> obj = table_[idx].GetReference()->Read<kReadBarrierOption>();
- VerifyObject(obj.Ptr());
+ VerifyObject(obj);
return obj;
}
diff --git a/runtime/indirect_reference_table.cc b/runtime/indirect_reference_table.cc
index d59bb39..6109ec6 100644
--- a/runtime/indirect_reference_table.cc
+++ b/runtime/indirect_reference_table.cc
@@ -99,7 +99,7 @@
size_t topIndex = segment_state_.parts.topIndex;
CHECK(obj != nullptr);
- VerifyObject(obj.Ptr());
+ VerifyObject(obj);
DCHECK(table_ != nullptr);
DCHECK_GE(segment_state_.parts.numHoles, prevState.parts.numHoles);
diff --git a/runtime/jni_internal.cc b/runtime/jni_internal.cc
index f0a7c16..273c67d 100644
--- a/runtime/jni_internal.cc
+++ b/runtime/jni_internal.cc
@@ -2481,7 +2481,7 @@
// Sanity check: If elements is not the same as the java array's data, it better not be a
// heap address. TODO: This might be slow to check, may be worth keeping track of which
// copies we make?
- if (heap->IsNonDiscontinuousSpaceHeapAddress(reinterpret_cast<mirror::Object*>(elements))) {
+ if (heap->IsNonDiscontinuousSpaceHeapAddress(elements)) {
soa.Vm()->JniAbortF("ReleaseArrayElements",
"invalid element pointer %p, array elements are %p",
reinterpret_cast<void*>(elements), array_data);
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 9d7f98f..7cbcac8 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -100,10 +100,10 @@
explicit SetLengthVisitor(int32_t length) : length_(length) {
}
- void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+ void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
// Avoid AsArray as object is not yet in live bitmap or allocation stack.
- Array* array = down_cast<Array*>(obj);
+ ObjPtr<Array> array = ObjPtr<Array>::DownCast(obj);
// DCHECK(array->IsArrayInstance());
array->SetLength(length_);
}
@@ -124,10 +124,10 @@
component_size_shift_(component_size_shift) {
}
- void operator()(Object* obj, size_t usable_size) const
+ void operator()(ObjPtr<Object> obj, size_t usable_size) const
REQUIRES_SHARED(Locks::mutator_lock_) {
// Avoid AsArray as object is not yet in live bitmap or allocation stack.
- Array* array = down_cast<Array*>(obj);
+ ObjPtr<Array> array = ObjPtr<Array>::DownCast(obj);
// DCHECK(array->IsArrayInstance());
int32_t length = (usable_size - header_size_) >> component_size_shift_;
DCHECK_GE(length, minimum_length_);
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index cc088b8..98d383d 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -707,9 +707,13 @@
if (!kCheckAddFinalizer) {
DCHECK(!IsFinalizable());
}
- mirror::Object* obj =
- heap->AllocObjectWithAllocator<kIsInstrumented, false>(self, this, this->object_size_,
- allocator_type, VoidFunctor());
+ // Note that the this pointer may be invalidated after the allocation.
+ ObjPtr<mirror::Object> obj =
+ heap->AllocObjectWithAllocator<kIsInstrumented, false>(self,
+ this,
+ this->object_size_,
+ allocator_type,
+ VoidFunctor());
if (add_finalizer && LIKELY(obj != nullptr)) {
heap->AddFinalizerReference(self, &obj);
if (UNLIKELY(self->IsExceptionPending())) {
@@ -717,7 +721,7 @@
obj = nullptr;
}
}
- return obj;
+ return obj.Ptr();
}
inline Object* Class::AllocObject(Thread* self) {
@@ -879,11 +883,11 @@
SetFieldBoolean<false, false>(GetSlowPathFlagOffset(), enabled);
}
-inline void Class::InitializeClassVisitor::operator()(
- mirror::Object* obj, size_t usable_size) const {
+inline void Class::InitializeClassVisitor::operator()(ObjPtr<mirror::Object> obj,
+ size_t usable_size) const {
DCHECK_LE(class_size_, usable_size);
// Avoid AsClass as object is not yet in live bitmap or allocation stack.
- mirror::Class* klass = down_cast<mirror::Class*>(obj);
+ ObjPtr<mirror::Class> klass = ObjPtr<mirror::Class>::DownCast(obj);
// DCHECK(klass->IsClass());
klass->SetClassSize(class_size_);
klass->SetPrimitiveType(Primitive::kPrimNot); // Default to not being primitive.
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index 40742d2..7606915 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -998,7 +998,7 @@
copy_bytes_(copy_bytes), imt_(imt), pointer_size_(pointer_size) {
}
- void operator()(mirror::Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+ void operator()(ObjPtr<mirror::Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
StackHandleScope<1> hs(self_);
Handle<mirror::Class> h_new_class_obj(hs.NewHandle(obj->AsClass()));
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index a0d6f37..725939a 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1232,7 +1232,7 @@
explicit InitializeClassVisitor(uint32_t class_size) : class_size_(class_size) {
}
- void operator()(mirror::Object* obj, size_t usable_size) const
+ void operator()(ObjPtr<mirror::Object> obj, size_t usable_size) const
REQUIRES_SHARED(Locks::mutator_lock_);
private:
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 9d3c26e..dbfe1d9 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -43,10 +43,10 @@
class CopyReferenceFieldsWithReadBarrierVisitor {
public:
- explicit CopyReferenceFieldsWithReadBarrierVisitor(Object* dest_obj)
+ explicit CopyReferenceFieldsWithReadBarrierVisitor(ObjPtr<Object> dest_obj)
: dest_obj_(dest_obj) {}
- void operator()(Object* obj, MemberOffset offset, bool /* is_static */) const
+ void operator()(ObjPtr<Object> obj, MemberOffset offset, bool /* is_static */) const
ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
// GetFieldObject() contains a RB.
Object* ref = obj->GetFieldObject<Object>(offset);
@@ -55,7 +55,7 @@
dest_obj_->SetFieldObjectWithoutWriteBarrier<false, false>(offset, ref);
}
- void operator()(mirror::Class* klass, mirror::Reference* ref) const
+ void operator()(ObjPtr<mirror::Class> klass, mirror::Reference* ref) const
ALWAYS_INLINE REQUIRES_SHARED(Locks::mutator_lock_) {
// Copy java.lang.ref.Reference.referent which isn't visited in
// Object::VisitReferences().
@@ -69,18 +69,18 @@
void VisitRoot(mirror::CompressedReference<mirror::Object>* root ATTRIBUTE_UNUSED) const {}
private:
- Object* const dest_obj_;
+ ObjPtr<Object> const dest_obj_;
};
Object* Object::CopyObject(Thread* self,
- mirror::Object* dest,
- mirror::Object* src,
+ ObjPtr<mirror::Object> dest,
+ ObjPtr<mirror::Object> src,
size_t num_bytes) {
// Copy instance data. Don't assume memcpy copies by words (b/32012820).
{
const size_t offset = sizeof(Object);
- uint8_t* src_bytes = reinterpret_cast<uint8_t*>(src) + offset;
- uint8_t* dst_bytes = reinterpret_cast<uint8_t*>(dest) + offset;
+ uint8_t* src_bytes = reinterpret_cast<uint8_t*>(src.Ptr()) + offset;
+ uint8_t* dst_bytes = reinterpret_cast<uint8_t*>(dest.Ptr()) + offset;
num_bytes -= offset;
DCHECK_ALIGNED(src_bytes, sizeof(uintptr_t));
DCHECK_ALIGNED(dst_bytes, sizeof(uintptr_t));
@@ -131,7 +131,7 @@
if (c->IsFinalizable()) {
heap->AddFinalizerReference(self, &dest);
}
- return dest;
+ return dest.Ptr();
}
// An allocation pre-fence visitor that copies the object.
@@ -141,7 +141,7 @@
: self_(self), orig_(orig), num_bytes_(num_bytes) {
}
- void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+ void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
Object::CopyObject(self_, obj, orig_->Get(), num_bytes_);
}
@@ -161,14 +161,14 @@
size_t num_bytes = SizeOf();
StackHandleScope<1> hs(self);
Handle<Object> this_object(hs.NewHandle(this));
- Object* copy;
+ ObjPtr<Object> copy;
CopyObjectVisitor visitor(self, &this_object, num_bytes);
if (heap->IsMovableObject(this)) {
copy = heap->AllocObject<true>(self, GetClass(), num_bytes, visitor);
} else {
copy = heap->AllocNonMovableObject<true>(self, GetClass(), num_bytes, visitor);
}
- return copy;
+ return copy.Ptr();
}
uint32_t Object::GenerateIdentityHashCode() {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 9ddf995..175b0c3 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -612,7 +612,9 @@
// A utility function that copies an object in a read barrier and
// write barrier-aware way. This is internally used by Clone() and
// Class::CopyOf().
- static Object* CopyObject(Thread* self, mirror::Object* dest, mirror::Object* src,
+ static Object* CopyObject(Thread* self,
+ ObjPtr<mirror::Object> dest,
+ ObjPtr<mirror::Object> src,
size_t num_bytes)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index aea6ff1..cf902af 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -43,10 +43,10 @@
explicit SetStringCountVisitor(int32_t count) : count_(count) {
}
- void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+ void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
// Avoid AsString as object is not yet in live bitmap or allocation stack.
- String* string = down_cast<String*>(obj);
+ ObjPtr<String> string = ObjPtr<String>::DownCast(obj);
string->SetCount(count_);
DCHECK(!string->IsCompressed() || kUseStringCompression);
}
@@ -63,10 +63,10 @@
: count_(count), src_array_(src_array), offset_(offset), high_byte_(high_byte) {
}
- void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+ void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
// Avoid AsString as object is not yet in live bitmap or allocation stack.
- String* string = down_cast<String*>(obj);
+ ObjPtr<String> string = ObjPtr<String>::DownCast(obj);
string->SetCount(count_);
DCHECK(!string->IsCompressed() || kUseStringCompression);
int32_t length = String::GetLengthFromCount(count_);
@@ -99,10 +99,10 @@
count_(count), src_array_(src_array), offset_(offset) {
}
- void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+ void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
// Avoid AsString as object is not yet in live bitmap or allocation stack.
- String* string = down_cast<String*>(obj);
+ ObjPtr<String> string = ObjPtr<String>::DownCast(obj);
string->SetCount(count_);
const uint16_t* const src = src_array_->GetData() + offset_;
const int32_t length = String::GetLengthFromCount(count_);
@@ -131,10 +131,10 @@
count_(count), src_string_(src_string), offset_(offset) {
}
- void operator()(Object* obj, size_t usable_size ATTRIBUTE_UNUSED) const
+ void operator()(ObjPtr<Object> obj, size_t usable_size ATTRIBUTE_UNUSED) const
REQUIRES_SHARED(Locks::mutator_lock_) {
// Avoid AsString as object is not yet in live bitmap or allocation stack.
- String* string = down_cast<String*>(obj);
+ ObjPtr<String> string = ObjPtr<String>::DownCast(obj);
string->SetCount(count_);
const int32_t length = String::GetLengthFromCount(count_);
bool compressible = kUseStringCompression && String::GetCompressionFlagFromCount(count_);
diff --git a/runtime/native/dalvik_system_VMDebug.cc b/runtime/native/dalvik_system_VMDebug.cc
index 8e81bc9..1852956 100644
--- a/runtime/native/dalvik_system_VMDebug.cc
+++ b/runtime/native/dalvik_system_VMDebug.cc
@@ -254,7 +254,9 @@
LOG(INFO) << "VMDebug infopoint " << id << " hit";
}
-static jlong VMDebug_countInstancesOfClass(JNIEnv* env, jclass, jclass javaClass,
+static jlong VMDebug_countInstancesOfClass(JNIEnv* env,
+ jclass,
+ jclass javaClass,
jboolean countAssignable) {
ScopedObjectAccess soa(env);
gc::Heap* const heap = Runtime::Current()->GetHeap();
@@ -263,13 +265,16 @@
if (c == nullptr) {
return 0;
}
- std::vector<mirror::Class*> classes {c.Ptr()};
+ VariableSizedHandleScope hs(soa.Self());
+ std::vector<Handle<mirror::Class>> classes {hs.NewHandle(c)};
uint64_t count = 0;
heap->CountInstances(classes, countAssignable, &count);
return count;
}
-static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env, jclass, jobjectArray javaClasses,
+static jlongArray VMDebug_countInstancesOfClasses(JNIEnv* env,
+ jclass,
+ jobjectArray javaClasses,
jboolean countAssignable) {
ScopedObjectAccess soa(env);
gc::Heap* const heap = Runtime::Current()->GetHeap();
@@ -279,14 +284,15 @@
if (decoded_classes == nullptr) {
return nullptr;
}
- std::vector<mirror::Class*> classes;
+ VariableSizedHandleScope hs(soa.Self());
+ std::vector<Handle<mirror::Class>> classes;
for (size_t i = 0, count = decoded_classes->GetLength(); i < count; ++i) {
- classes.push_back(decoded_classes->Get(i));
+ classes.push_back(hs.NewHandle(decoded_classes->Get(i)));
}
std::vector<uint64_t> counts(classes.size(), 0u);
// Heap::CountInstances can handle null and will put 0 for these classes.
heap->CountInstances(classes, countAssignable, &counts[0]);
- auto* long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size());
+ ObjPtr<mirror::LongArray> long_counts = mirror::LongArray::Alloc(soa.Self(), counts.size());
if (long_counts == nullptr) {
soa.Self()->AssertPendingOOMException();
return nullptr;
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index c20c8b8..6b9468d 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -296,10 +296,10 @@
std::unique_ptr<Backtrace> backtrace(Backtrace::Create(BACKTRACE_CURRENT_PROCESS, tid, map));
if (!backtrace->Unwind(0, reinterpret_cast<ucontext*>(ucontext_ptr))) {
os << prefix << "(backtrace::Unwind failed for thread " << tid
- << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")\n";
+ << ": " << backtrace->GetErrorString(backtrace->GetError()) << ")" << std::endl;
return;
} else if (backtrace->NumFrames() == 0) {
- os << prefix << "(no native stack frames for thread " << tid << ")\n";
+ os << prefix << "(no native stack frames for thread " << tid << ")" << std::endl;
return;
}
@@ -354,7 +354,7 @@
}
os << ")";
}
- os << "\n";
+ os << std::endl;
if (try_addr2line && use_addr2line) {
Addr2line(it->map.name, it->pc - it->map.start, os, prefix, &addr2line_state);
}
@@ -395,7 +395,7 @@
if (include_count) {
os << StringPrintf("#%02zd ", i);
}
- os << text << "\n";
+ os << text << std::endl;
}
}
diff --git a/runtime/obj_ptr-inl.h b/runtime/obj_ptr-inl.h
index f0a5f6f..d0be6dc 100644
--- a/runtime/obj_ptr-inl.h
+++ b/runtime/obj_ptr-inl.h
@@ -33,7 +33,7 @@
template<class MirrorType, bool kPoison>
inline void ObjPtr<MirrorType, kPoison>::AssertValid() const {
if (kPoison) {
- CHECK(IsValid()) << "Stale object pointer " << Ptr() << " , expected cookie "
+ CHECK(IsValid()) << "Stale object pointer " << PtrUnchecked() << " , expected cookie "
<< TrimCookie(Thread::Current()->GetPoisonObjectCookie()) << " but got " << GetCookie();
}
}
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index 2d9149a..de6683c 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -18,6 +18,7 @@
defaults: ["art_defaults"],
host_supported: true,
srcs: ["events.cc",
+ "heap.cc",
"object_tagging.cc",
"OpenjdkJvmTi.cc",
"transform.cc"],
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 36be2a0..05da585 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -39,6 +39,7 @@
#include "art_jvmti.h"
#include "base/mutex.h"
#include "events-inl.h"
+#include "heap.h"
#include "jni_env_ext-inl.h"
#include "object_tagging.h"
#include "obj_ptr-inl.h"
@@ -276,7 +277,8 @@
jclass klass,
const jvmtiHeapCallbacks* callbacks,
const void* user_data) {
- return ERR(NOT_IMPLEMENTED);
+ HeapUtil heap_util(&gObjectTagTable);
+ return heap_util.IterateThroughHeap(env, heap_filter, klass, callbacks, user_data);
}
static jvmtiError GetTag(jvmtiEnv* env, jobject object, jlong* tag_ptr) {
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 4d5b7e0..450f85e 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -128,11 +128,13 @@
public:
explicit JvmtiAllocationListener(EventHandler* handler) : handler_(handler) {}
- void ObjectAllocated(art::Thread* self, art::mirror::Object** obj, size_t byte_count)
+ void ObjectAllocated(art::Thread* self, art::ObjPtr<art::mirror::Object>* obj, size_t byte_count)
REQUIRES_SHARED(art::Locks::mutator_lock_) {
DCHECK_EQ(self, art::Thread::Current());
if (handler_->IsEventEnabledAnywhere(JVMTI_EVENT_VM_OBJECT_ALLOC)) {
+ art::StackHandleScope<1> hs(self);
+ auto h = hs.NewHandleWrapper(obj);
// jvmtiEventVMObjectAlloc parameters:
// jvmtiEnv *jvmti_env,
// JNIEnv* jni_env,
@@ -153,7 +155,7 @@
ScopedLocalRef<jobject> object(
jni_env, jni_env->AddLocalReference<jobject>(*obj));
ScopedLocalRef<jclass> klass(
- jni_env, jni_env->AddLocalReference<jclass>((*obj)->GetClass()));
+ jni_env, jni_env->AddLocalReference<jclass>(obj->Ptr()->GetClass()));
handler_->DispatchEvent(self,
JVMTI_EVENT_VM_OBJECT_ALLOC,
diff --git a/runtime/openjdkjvmti/heap.cc b/runtime/openjdkjvmti/heap.cc
new file mode 100644
index 0000000..95d9a1d
--- /dev/null
+++ b/runtime/openjdkjvmti/heap.cc
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "heap.h"
+
+#include "art_jvmti.h"
+#include "base/macros.h"
+#include "base/mutex.h"
+#include "gc/heap.h"
+#include "mirror/class.h"
+#include "object_callbacks.h"
+#include "object_tagging.h"
+#include "obj_ptr-inl.h"
+#include "runtime.h"
+#include "scoped_thread_state_change-inl.h"
+#include "thread-inl.h"
+
+namespace openjdkjvmti {
+
+struct IterateThroughHeapData {
+ IterateThroughHeapData(HeapUtil* _heap_util,
+ jint heap_filter,
+ art::ObjPtr<art::mirror::Class> klass,
+ const jvmtiHeapCallbacks* _callbacks,
+ const void* _user_data)
+ : heap_util(_heap_util),
+ filter_klass(klass),
+ callbacks(_callbacks),
+ user_data(_user_data),
+ filter_out_tagged((heap_filter & JVMTI_HEAP_FILTER_TAGGED) != 0),
+ filter_out_untagged((heap_filter & JVMTI_HEAP_FILTER_UNTAGGED) != 0),
+ filter_out_class_tagged((heap_filter & JVMTI_HEAP_FILTER_CLASS_TAGGED) != 0),
+ filter_out_class_untagged((heap_filter & JVMTI_HEAP_FILTER_CLASS_UNTAGGED) != 0),
+ any_filter(filter_out_tagged ||
+ filter_out_untagged ||
+ filter_out_class_tagged ||
+ filter_out_class_untagged),
+ stop_reports(false) {
+ }
+
+ bool ShouldReportByHeapFilter(jlong tag, jlong class_tag) {
+ if (!any_filter) {
+ return true;
+ }
+
+ if ((tag == 0 && filter_out_untagged) || (tag != 0 && filter_out_tagged)) {
+ return false;
+ }
+
+ if ((class_tag == 0 && filter_out_class_untagged) ||
+ (class_tag != 0 && filter_out_class_tagged)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ HeapUtil* heap_util;
+ art::ObjPtr<art::mirror::Class> filter_klass;
+ const jvmtiHeapCallbacks* callbacks;
+ const void* user_data;
+ const bool filter_out_tagged;
+ const bool filter_out_untagged;
+ const bool filter_out_class_tagged;
+ const bool filter_out_class_untagged;
+ const bool any_filter;
+
+ bool stop_reports;
+};
+
+static void IterateThroughHeapObjectCallback(art::mirror::Object* obj, void* arg)
+ REQUIRES_SHARED(art::Locks::mutator_lock_) {
+ IterateThroughHeapData* ithd = reinterpret_cast<IterateThroughHeapData*>(arg);
+ // Early return, as we can't really stop visiting.
+ if (ithd->stop_reports) {
+ return;
+ }
+
+ art::ScopedAssertNoThreadSuspension no_suspension("IterateThroughHeapCallback");
+
+ jlong tag = 0;
+ ithd->heap_util->GetTags()->GetTag(obj, &tag);
+
+ jlong class_tag = 0;
+ art::ObjPtr<art::mirror::Class> klass = obj->GetClass();
+ ithd->heap_util->GetTags()->GetTag(klass.Ptr(), &class_tag);
+ // For simplicity, even if we find a tag = 0, assume 0 = not tagged.
+
+ if (!ithd->ShouldReportByHeapFilter(tag, class_tag)) {
+ return;
+ }
+
+ // TODO: Handle array_primitive_value_callback.
+
+ if (ithd->filter_klass != nullptr) {
+ if (ithd->filter_klass != klass) {
+ return;
+ }
+ }
+
+ jlong size = obj->SizeOf();
+
+ jint length = -1;
+ if (obj->IsArrayInstance()) {
+ length = obj->AsArray()->GetLength();
+ }
+
+ jlong saved_tag = tag;
+ jint ret = ithd->callbacks->heap_iteration_callback(class_tag,
+ size,
+ &tag,
+ length,
+ const_cast<void*>(ithd->user_data));
+
+ if (tag != saved_tag) {
+ ithd->heap_util->GetTags()->Set(obj, tag);
+ }
+
+ ithd->stop_reports = (ret & JVMTI_VISIT_ABORT) != 0;
+
+ // TODO Implement array primitive and string primitive callback.
+ // TODO Implement primitive field callback.
+}
+
+jvmtiError HeapUtil::IterateThroughHeap(jvmtiEnv* env ATTRIBUTE_UNUSED,
+ jint heap_filter,
+ jclass klass,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data) {
+ if (callbacks == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ if (callbacks->array_primitive_value_callback != nullptr) {
+ // TODO: Implement.
+ return ERR(NOT_IMPLEMENTED);
+ }
+
+ art::Thread* self = art::Thread::Current();
+ art::ScopedObjectAccess soa(self); // Now we know we have the shared lock.
+
+ IterateThroughHeapData ithd(this,
+ heap_filter,
+ soa.Decode<art::mirror::Class>(klass),
+ callbacks,
+ user_data);
+
+ art::Runtime::Current()->GetHeap()->VisitObjects(IterateThroughHeapObjectCallback, &ithd);
+
+ return ERR(NONE);
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/heap.h b/runtime/openjdkjvmti/heap.h
new file mode 100644
index 0000000..fb9a216
--- /dev/null
+++ b/runtime/openjdkjvmti/heap.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
+#define ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
+
+#include "jvmti.h"
+
+namespace openjdkjvmti {
+
+class ObjectTagTable;
+
+class HeapUtil {
+ public:
+ explicit HeapUtil(ObjectTagTable* tags) : tags_(tags) {
+ }
+
+ jvmtiError IterateThroughHeap(jvmtiEnv* env,
+ jint heap_filter,
+ jclass klass,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data);
+
+ ObjectTagTable* GetTags() {
+ return tags_;
+ }
+
+ private:
+ ObjectTagTable* tags_;
+};
+
+} // namespace openjdkjvmti
+
+#endif // ART_RUNTIME_OPENJDKJVMTI_HEAP_H_
diff --git a/runtime/openjdkjvmti/object_tagging.cc b/runtime/openjdkjvmti/object_tagging.cc
index bb17cac..29d4830 100644
--- a/runtime/openjdkjvmti/object_tagging.cc
+++ b/runtime/openjdkjvmti/object_tagging.cc
@@ -87,6 +87,33 @@
return false;
}
+bool ObjectTagTable::Set(art::mirror::Object* obj, jlong new_tag) {
+ art::Thread* self = art::Thread::Current();
+ art::MutexLock mu(self, allow_disallow_lock_);
+ Wait(self);
+
+ for (auto& pair : tagged_objects_) {
+ if (pair.first.Read(nullptr) == obj) {
+ pair.second = new_tag;
+ return true;
+ }
+ }
+
+ // TODO refactor with Add.
+ if (first_free_ == tagged_objects_.size()) {
+ tagged_objects_.push_back(Entry(art::GcRoot<art::mirror::Object>(obj), new_tag));
+ first_free_++;
+ } else {
+ DCHECK_LT(first_free_, tagged_objects_.size());
+ DCHECK(tagged_objects_[first_free_].first.IsNull());
+ tagged_objects_[first_free_] = Entry(art::GcRoot<art::mirror::Object>(obj), new_tag);
+ // TODO: scan for free elements.
+ first_free_ = tagged_objects_.size();
+ }
+
+ return false;
+}
+
void ObjectTagTable::Sweep(art::IsMarkedVisitor* visitor) {
if (event_handler_->IsEventEnabledAnywhere(JVMTI_EVENT_OBJECT_FREE)) {
SweepImpl<true>(visitor);
diff --git a/runtime/openjdkjvmti/object_tagging.h b/runtime/openjdkjvmti/object_tagging.h
index 45f3e4f..b399e65 100644
--- a/runtime/openjdkjvmti/object_tagging.h
+++ b/runtime/openjdkjvmti/object_tagging.h
@@ -42,6 +42,10 @@
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_);
+ bool Set(art::mirror::Object* obj, jlong tag)
+ REQUIRES_SHARED(art::Locks::mutator_lock_)
+ REQUIRES(!allow_disallow_lock_);
+
bool GetTag(art::mirror::Object* obj, jlong* result)
REQUIRES_SHARED(art::Locks::mutator_lock_)
REQUIRES(!allow_disallow_lock_) {
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index e0ba8e7..a9f39d0 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -41,7 +41,7 @@
void ReferenceTable::Add(ObjPtr<mirror::Object> obj) {
DCHECK(obj != nullptr);
- VerifyObject(obj.Ptr());
+ VerifyObject(obj);
if (entries_.size() >= max_size_) {
LOG(FATAL) << "ReferenceTable '" << name_ << "' "
<< "overflowed (" << max_size_ << " entries)";
diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc
index aed6a2b..be97860 100644
--- a/runtime/runtime_android.cc
+++ b/runtime/runtime_android.cc
@@ -27,7 +27,6 @@
namespace art {
-static constexpr bool kDumpHeapObjectOnSigsevg = false;
static constexpr bool kUseSignalHandler = false;
struct sigaction old_action;
@@ -48,11 +47,6 @@
if (runtime != nullptr) {
// Print this out first in case DumpObject faults.
LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage();
- gc::Heap* heap = runtime->GetHeap();
- if (kDumpHeapObjectOnSigsevg && heap != nullptr && info != nullptr) {
- LOG(FATAL_WITHOUT_ABORT) << "Dump heap object at fault address: ";
- heap->DumpObject(LOG_STREAM(FATAL_WITHOUT_ABORT), reinterpret_cast<mirror::Object*>(info->si_addr));
- }
}
// Run the old signal handler.
old_action.sa_sigaction(signal_number, info, raw_context);
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index cee73e1..93704a9 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -21,6 +21,7 @@
#include <sys/utsname.h>
#include <inttypes.h>
+#include <iostream>
#include <sstream>
#include "base/dumpable.h"
@@ -35,7 +36,6 @@
namespace art {
-static constexpr bool kDumpHeapObjectOnSigsevg = false;
static constexpr bool kUseSigRTTimeout = true;
static constexpr bool kDumpNativeStackOnTimeout = true;
@@ -337,17 +337,21 @@
UContext thread_context(raw_context);
Backtrace thread_backtrace(raw_context);
- LOG(FATAL_WITHOUT_ABORT) << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
- << StringPrintf("Fatal signal %d (%s), code %d (%s)",
- signal_number, GetSignalName(signal_number),
- info->si_code,
- GetSignalCodeName(signal_number, info->si_code))
- << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << "\n"
- << "OS: " << Dumpable<OsInfo>(os_info) << "\n"
- << "Cmdline: " << cmd_line << "\n"
- << "Thread: " << tid << " \"" << thread_name << "\"\n"
- << "Registers:\n" << Dumpable<UContext>(thread_context) << "\n"
- << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace);
+ // Note: We are using cerr directly instead of LOG macros to ensure even just partial output
+ // makes it out. That means we lose the "dalvikvm..." prefix, but that is acceptable
+ // considering this is an abort situation.
+
+ std::cerr << "*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
+ << StringPrintf("Fatal signal %d (%s), code %d (%s)",
+ signal_number, GetSignalName(signal_number),
+ info->si_code,
+ GetSignalCodeName(signal_number, info->si_code))
+ << (has_address ? StringPrintf(" fault addr %p", info->si_addr) : "") << std::endl
+ << "OS: " << Dumpable<OsInfo>(os_info) << std::endl
+ << "Cmdline: " << cmd_line << std::endl
+ << "Thread: " << tid << " \"" << thread_name << "\"" << std::endl
+ << "Registers:\n" << Dumpable<UContext>(thread_context) << std::endl
+ << "Backtrace:\n" << Dumpable<Backtrace>(thread_backtrace) << std::endl;
if (kIsDebugBuild && signal_number == SIGSEGV) {
PrintFileToLog("/proc/self/maps", LogSeverity::FATAL_WITHOUT_ABORT);
}
@@ -357,23 +361,20 @@
// Special timeout signal. Try to dump all threads.
// Note: Do not use DumpForSigQuit, as that might disable native unwind, but the native parts
// are of value here.
- runtime->GetThreadList()->Dump(LOG_STREAM(FATAL_WITHOUT_ABORT), kDumpNativeStackOnTimeout);
+ runtime->GetThreadList()->Dump(std::cerr, kDumpNativeStackOnTimeout);
+ std::cerr << std::endl;
}
- gc::Heap* heap = runtime->GetHeap();
- LOG(FATAL_WITHOUT_ABORT) << "Fault message: " << runtime->GetFaultMessage();
- if (kDumpHeapObjectOnSigsevg && heap != nullptr && info != nullptr) {
- LOG(FATAL_WITHOUT_ABORT) << "Dump heap object at fault address: ";
- heap->DumpObject(LOG_STREAM(FATAL_WITHOUT_ABORT), reinterpret_cast<mirror::Object*>(info->si_addr));
- }
+ std::cerr << "Fault message: " << runtime->GetFaultMessage() << std::endl;
}
if (getenv("debug_db_uid") != nullptr || getenv("art_wait_for_gdb_on_crash") != nullptr) {
- LOG(FATAL_WITHOUT_ABORT) << "********************************************************\n"
- << "* Process " << getpid() << " thread " << tid << " \"" << thread_name
- << "\""
- << " has been suspended while crashing.\n"
- << "* Attach gdb:\n"
- << "* gdb -p " << tid << "\n"
- << "********************************************************\n";
+ std::cerr << "********************************************************\n"
+ << "* Process " << getpid() << " thread " << tid << " \"" << thread_name
+ << "\""
+ << " has been suspended while crashing.\n"
+ << "* Attach gdb:\n"
+ << "* gdb -p " << tid << "\n"
+ << "********************************************************"
+ << std::endl;
// Wait for debugger to attach.
while (true) {
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 3e2ecfe..45d3e34 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1826,7 +1826,7 @@
size_t Thread::NumHandleReferences() {
size_t count = 0;
- for (HandleScope* cur = tlsPtr_.top_handle_scope; cur != nullptr; cur = cur->GetLink()) {
+ for (BaseHandleScope* cur = tlsPtr_.top_handle_scope; cur != nullptr; cur = cur->GetLink()) {
count += cur->NumberOfReferences();
}
return count;
@@ -1835,7 +1835,7 @@
bool Thread::HandleScopeContains(jobject obj) const {
StackReference<mirror::Object>* hs_entry =
reinterpret_cast<StackReference<mirror::Object>*>(obj);
- for (HandleScope* cur = tlsPtr_.top_handle_scope; cur!= nullptr; cur = cur->GetLink()) {
+ for (BaseHandleScope* cur = tlsPtr_.top_handle_scope; cur!= nullptr; cur = cur->GetLink()) {
if (cur->Contains(hs_entry)) {
return true;
}
@@ -1847,12 +1847,8 @@
void Thread::HandleScopeVisitRoots(RootVisitor* visitor, uint32_t thread_id) {
BufferedRootVisitor<kDefaultBufferedRootCount> buffered_visitor(
visitor, RootInfo(kRootNativeStack, thread_id));
- for (HandleScope* cur = tlsPtr_.top_handle_scope; cur; cur = cur->GetLink()) {
- for (size_t j = 0, count = cur->NumberOfReferences(); j < count; ++j) {
- // GetReference returns a pointer to the stack reference within the handle scope. If this
- // needs to be updated, it will be done by the root visitor.
- buffered_visitor.VisitRootIfNonNull(cur->GetHandle(j).GetReference());
- }
+ for (BaseHandleScope* cur = tlsPtr_.top_handle_scope; cur; cur = cur->GetLink()) {
+ cur->VisitRoots(buffered_visitor);
}
}
@@ -1875,7 +1871,7 @@
if (LIKELY(HandleScopeContains(obj))) {
// Read from handle scope.
result = reinterpret_cast<StackReference<mirror::Object>*>(obj)->AsMirrorPtr();
- VerifyObject(result.Ptr());
+ VerifyObject(result);
} else {
tlsPtr_.jni_env->vm->JniAbortF(nullptr, "use of invalid jobject %p", obj);
expect_null = true;
diff --git a/runtime/thread.h b/runtime/thread.h
index 20b4cc1..376a69c 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -799,17 +799,17 @@
void HandleScopeVisitRoots(RootVisitor* visitor, uint32_t thread_id)
REQUIRES_SHARED(Locks::mutator_lock_);
- HandleScope* GetTopHandleScope() {
+ BaseHandleScope* GetTopHandleScope() {
return tlsPtr_.top_handle_scope;
}
- void PushHandleScope(HandleScope* handle_scope) {
+ void PushHandleScope(BaseHandleScope* handle_scope) {
DCHECK_EQ(handle_scope->GetLink(), tlsPtr_.top_handle_scope);
tlsPtr_.top_handle_scope = handle_scope;
}
- HandleScope* PopHandleScope() {
- HandleScope* handle_scope = tlsPtr_.top_handle_scope;
+ BaseHandleScope* PopHandleScope() {
+ BaseHandleScope* handle_scope = tlsPtr_.top_handle_scope;
DCHECK(handle_scope != nullptr);
tlsPtr_.top_handle_scope = tlsPtr_.top_handle_scope->GetLink();
return handle_scope;
@@ -1446,7 +1446,7 @@
mirror::Object* monitor_enter_object;
// Top of linked list of handle scopes or null for none.
- HandleScope* top_handle_scope;
+ BaseHandleScope* top_handle_scope;
// Needed to get the right ClassLoader in JNI_OnLoad, but also
// useful for testing.
diff --git a/runtime/verify_object-inl.h b/runtime/verify_object-inl.h
index 4892b49..43151dd 100644
--- a/runtime/verify_object-inl.h
+++ b/runtime/verify_object-inl.h
@@ -29,7 +29,7 @@
if (kVerifyObjectSupport > kVerifyObjectModeDisabled && obj != nullptr) {
if (kVerifyObjectSupport > kVerifyObjectModeFast) {
// Slow object verification, try the heap right away.
- Runtime::Current()->GetHeap()->VerifyObjectBody(obj.Ptr());
+ Runtime::Current()->GetHeap()->VerifyObjectBody(obj);
} else {
// Fast object verification, only call the heap if our quick sanity tests fail. The heap will
// print the diagnostic message.
@@ -40,7 +40,7 @@
failed = failed || !VerifyClassClass(c);
}
if (UNLIKELY(failed)) {
- Runtime::Current()->GetHeap()->VerifyObjectBody(obj.Ptr());
+ Runtime::Current()->GetHeap()->VerifyObjectBody(obj);
}
}
}
diff --git a/test/530-checker-loops2/src/Main.java b/test/530-checker-loops2/src/Main.java
index 7acf008..dca00bd 100644
--- a/test/530-checker-loops2/src/Main.java
+++ b/test/530-checker-loops2/src/Main.java
@@ -111,6 +111,24 @@
return result;
}
+ /// CHECK-START: int Main.periodicXorSequence(int) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: int Main.periodicXorSequence(int) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ /// CHECK-NOT: Deoptimize
+ private static int periodicXorSequence(int tc) {
+ int[] x = { 1, 3 };
+ // Loop with periodic sequence (0, 1).
+ int k = 0;
+ int result = 0;
+ for (int i = 0; i < tc; i++) {
+ result += x[k];
+ k ^= 1;
+ }
+ return result;
+ }
+
/// CHECK-START: int Main.justRightUp1() BCE (before)
/// CHECK-DAG: BoundsCheck
//
@@ -895,8 +913,9 @@
expectEquals(0, periodicIdiom(-1));
for (int tc = 0; tc < 32; tc++) {
int expected = (tc >> 1) << 2;
- if ((tc & 1) != 0)
+ if ((tc & 1) != 0) {
expected += 1;
+ }
expectEquals(expected, periodicIdiom(tc));
}
@@ -904,8 +923,9 @@
expectEquals(0, periodicSequence2(-1));
for (int tc = 0; tc < 32; tc++) {
int expected = (tc >> 1) << 2;
- if ((tc & 1) != 0)
+ if ((tc & 1) != 0) {
expected += 1;
+ }
expectEquals(expected, periodicSequence2(tc));
}
@@ -915,6 +935,16 @@
expectEquals(tc * 16, periodicSequence4(tc));
}
+ // Periodic adds (1, 3), one at the time.
+ expectEquals(0, periodicXorSequence(-1));
+ for (int tc = 0; tc < 32; tc++) {
+ int expected = (tc >> 1) << 2;
+ if ((tc & 1) != 0) {
+ expected += 1;
+ }
+ expectEquals(expected, periodicXorSequence(tc));
+ }
+
// Large bounds.
expectEquals(55, justRightUp1());
expectEquals(55, justRightUp2());
diff --git a/test/906-iterate-heap/build b/test/906-iterate-heap/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/906-iterate-heap/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/906-iterate-heap/expected.txt b/test/906-iterate-heap/expected.txt
new file mode 100644
index 0000000..72cd47d
--- /dev/null
+++ b/test/906-iterate-heap/expected.txt
@@ -0,0 +1,2 @@
+[{tag=1, class-tag=0, size=8, length=-1}, {tag=2, class-tag=100, size=8, length=-1}, {tag=3, class-tag=100, size=8, length=-1}, {tag=4, class-tag=0, size=32, length=5}, {tag=100, class-tag=0, size=<class>, length=-1}]
+[{tag=11, class-tag=0, size=8, length=-1}, {tag=12, class-tag=110, size=8, length=-1}, {tag=13, class-tag=110, size=8, length=-1}, {tag=14, class-tag=0, size=32, length=5}, {tag=110, class-tag=0, size=<class>, length=-1}]
diff --git a/test/906-iterate-heap/info.txt b/test/906-iterate-heap/info.txt
new file mode 100644
index 0000000..875a5f6
--- /dev/null
+++ b/test/906-iterate-heap/info.txt
@@ -0,0 +1 @@
+Tests basic functions in the jvmti plugin.
diff --git a/test/906-iterate-heap/iterate_heap.cc b/test/906-iterate-heap/iterate_heap.cc
new file mode 100644
index 0000000..ab1d8d8
--- /dev/null
+++ b/test/906-iterate-heap/iterate_heap.cc
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2013 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 "iterate_heap.h"
+
+#include <iostream>
+#include <pthread.h>
+#include <stdio.h>
+#include <vector>
+
+#include "base/logging.h"
+#include "jni.h"
+#include "openjdkjvmti/jvmti.h"
+#include "ScopedPrimitiveArray.h"
+#include "ti-agent/common_load.h"
+
+namespace art {
+namespace Test906IterateHeap {
+
+class IterationConfig {
+ public:
+ IterationConfig() {}
+ virtual ~IterationConfig() {}
+
+ virtual jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) = 0;
+};
+
+static jint JNICALL HeapIterationCallback(jlong class_tag,
+ jlong size,
+ jlong* tag_ptr,
+ jint length,
+ void* user_data) {
+ IterationConfig* config = reinterpret_cast<IterationConfig*>(user_data);
+ return config->Handle(class_tag, size, tag_ptr, length);
+}
+
+static bool Run(jint heap_filter, jclass klass_filter, IterationConfig* config) {
+ jvmtiHeapCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(jvmtiHeapCallbacks));
+ callbacks.heap_iteration_callback = HeapIterationCallback;
+
+ jvmtiError ret = jvmti_env->IterateThroughHeap(heap_filter,
+ klass_filter,
+ &callbacks,
+ config);
+ if (ret != JVMTI_ERROR_NONE) {
+ char* err;
+ jvmti_env->GetErrorName(ret, &err);
+ printf("Failure running IterateThroughHeap: %s\n", err);
+ return false;
+ }
+ return true;
+}
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapCount(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED,
+ jint heap_filter,
+ jclass klass_filter,
+ jint stop_after) {
+ class CountIterationConfig : public IterationConfig {
+ public:
+ CountIterationConfig(jint _counter, jint _stop_after)
+ : counter(_counter),
+ stop_after(_stop_after) {
+ }
+
+ jint Handle(jlong class_tag ATTRIBUTE_UNUSED,
+ jlong size ATTRIBUTE_UNUSED,
+ jlong* tag_ptr ATTRIBUTE_UNUSED,
+ jint length ATTRIBUTE_UNUSED) OVERRIDE {
+ counter++;
+ if (counter == stop_after) {
+ return JVMTI_VISIT_ABORT;
+ }
+ return 0;
+ }
+
+ jint counter;
+ const jint stop_after;
+ };
+
+ CountIterationConfig config(0, stop_after);
+ Run(heap_filter, klass_filter, &config);
+
+ if (config.counter > config.stop_after) {
+ printf("Error: more objects visited than signaled.");
+ }
+
+ return config.counter;
+}
+
+
+extern "C" JNIEXPORT jint JNICALL Java_Main_iterateThroughHeapData(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jint heap_filter,
+ jclass klass_filter,
+ jlongArray class_tags,
+ jlongArray sizes,
+ jlongArray tags,
+ jintArray lengths) {
+ class DataIterationConfig : public IterationConfig {
+ public:
+ jint Handle(jlong class_tag, jlong size, jlong* tag_ptr, jint length) OVERRIDE {
+ class_tags_.push_back(class_tag);
+ sizes_.push_back(size);
+ tags_.push_back(*tag_ptr);
+ lengths_.push_back(length);
+
+ return 0; // Continue.
+ }
+
+ std::vector<jlong> class_tags_;
+ std::vector<jlong> sizes_;
+ std::vector<jlong> tags_;
+ std::vector<jint> lengths_;
+ };
+
+ DataIterationConfig config;
+ if (!Run(heap_filter, klass_filter, &config)) {
+ return -1;
+ }
+
+ ScopedLongArrayRW s_class_tags(env, class_tags);
+ ScopedLongArrayRW s_sizes(env, sizes);
+ ScopedLongArrayRW s_tags(env, tags);
+ ScopedIntArrayRW s_lengths(env, lengths);
+
+ for (size_t i = 0; i != config.class_tags_.size(); ++i) {
+ s_class_tags[i] = config.class_tags_[i];
+ s_sizes[i] = config.sizes_[i];
+ s_tags[i] = config.tags_[i];
+ s_lengths[i] = config.lengths_[i];
+ }
+
+ return static_cast<jint>(config.class_tags_.size());
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_iterateThroughHeapAdd(JNIEnv* env ATTRIBUTE_UNUSED,
+ jclass klass ATTRIBUTE_UNUSED,
+ jint heap_filter,
+ jclass klass_filter) {
+ class AddIterationConfig : public IterationConfig {
+ public:
+ AddIterationConfig() {}
+
+ jint Handle(jlong class_tag ATTRIBUTE_UNUSED,
+ jlong size ATTRIBUTE_UNUSED,
+ jlong* tag_ptr,
+ jint length ATTRIBUTE_UNUSED) OVERRIDE {
+ jlong current_tag = *tag_ptr;
+ if (current_tag != 0) {
+ *tag_ptr = current_tag + 10;
+ }
+ return 0;
+ }
+ };
+
+ AddIterationConfig config;
+ Run(heap_filter, klass_filter, &config);
+}
+
+// Don't do anything
+jint OnLoad(JavaVM* vm,
+ char* options ATTRIBUTE_UNUSED,
+ void* reserved ATTRIBUTE_UNUSED) {
+ if (vm->GetEnv(reinterpret_cast<void**>(&jvmti_env), JVMTI_VERSION_1_0)) {
+ printf("Unable to get jvmti env!\n");
+ return 1;
+ }
+ return 0;
+}
+
+} // namespace Test906IterateHeap
+} // namespace art
diff --git a/test/906-iterate-heap/iterate_heap.h b/test/906-iterate-heap/iterate_heap.h
new file mode 100644
index 0000000..f25cdba
--- /dev/null
+++ b/test/906-iterate-heap/iterate_heap.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_
+#define ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_
+
+#include <jni.h>
+
+namespace art {
+namespace Test906IterateHeap {
+
+jint OnLoad(JavaVM* vm, char* options, void* reserved);
+
+} // namespace Test906IterateHeap
+} // namespace art
+
+#endif // ART_TEST_906_ITERATE_HEAP_ITERATE_HEAP_H_
diff --git a/test/906-iterate-heap/run b/test/906-iterate-heap/run
new file mode 100755
index 0000000..3e135a3
--- /dev/null
+++ b/test/906-iterate-heap/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+fi
+
+if [[ "$@" != *"--debuggable"* ]]; then
+ other_args=" -Xcompiler-option --debuggable "
+else
+ other_args=""
+fi
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=906-iterate-heap,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/906-iterate-heap/src/Main.java b/test/906-iterate-heap/src/Main.java
new file mode 100644
index 0000000..544a365
--- /dev/null
+++ b/test/906-iterate-heap/src/Main.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Collections;
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ System.loadLibrary(args[1]);
+
+ doTest();
+ }
+
+ public static void doTest() throws Exception {
+ A a = new A();
+ B b = new B();
+ B b2 = new B();
+ C c = new C();
+ A[] aArray = new A[5];
+
+ setTag(a, 1);
+ setTag(b, 2);
+ setTag(b2, 3);
+ setTag(aArray, 4);
+ setTag(B.class, 100);
+
+ int all = iterateThroughHeapCount(0, null, Integer.MAX_VALUE);
+ int tagged = iterateThroughHeapCount(HEAP_FILTER_OUT_UNTAGGED, null, Integer.MAX_VALUE);
+ int untagged = iterateThroughHeapCount(HEAP_FILTER_OUT_TAGGED, null, Integer.MAX_VALUE);
+ int taggedClass = iterateThroughHeapCount(HEAP_FILTER_OUT_CLASS_UNTAGGED, null,
+ Integer.MAX_VALUE);
+ int untaggedClass = iterateThroughHeapCount(HEAP_FILTER_OUT_CLASS_TAGGED, null,
+ Integer.MAX_VALUE);
+
+ if (all != tagged + untagged) {
+ throw new IllegalStateException("Instances: " + all + " != " + tagged + " + " + untagged);
+ }
+ if (all != taggedClass + untaggedClass) {
+ throw new IllegalStateException("By class: " + all + " != " + taggedClass + " + " +
+ untaggedClass);
+ }
+ if (tagged != 5) {
+ throw new IllegalStateException(tagged + " tagged objects");
+ }
+ if (taggedClass != 2) {
+ throw new IllegalStateException(tagged + " objects with tagged class");
+ }
+ if (all == tagged) {
+ throw new IllegalStateException("All objects tagged");
+ }
+ if (all == taggedClass) {
+ throw new IllegalStateException("All objects have tagged class");
+ }
+
+ long classTags[] = new long[100];
+ long sizes[] = new long[100];
+ long tags[] = new long[100];
+ int lengths[] = new int[100];
+
+ int n = iterateThroughHeapData(HEAP_FILTER_OUT_UNTAGGED, null, classTags, sizes, tags, lengths);
+ System.out.println(sort(n, classTags, sizes, tags, lengths));
+
+ iterateThroughHeapAdd(HEAP_FILTER_OUT_UNTAGGED, null);
+ n = iterateThroughHeapData(HEAP_FILTER_OUT_UNTAGGED, null, classTags, sizes, tags, lengths);
+ System.out.println(sort(n, classTags, sizes, tags, lengths));
+ }
+
+ static class A {
+ }
+
+ static class B {
+ }
+
+ static class C {
+ }
+
+ static class HeapElem implements Comparable<HeapElem> {
+ long classTag;
+ long size;
+ long tag;
+ int length;
+
+ public int compareTo(HeapElem other) {
+ if (tag != other.tag) {
+ return Long.compare(tag, other.tag);
+ }
+ if (classTag != other.classTag) {
+ return Long.compare(classTag, other.classTag);
+ }
+ if (size != other.size) {
+ return Long.compare(size, other.size);
+ }
+ return Integer.compare(length, other.length);
+ }
+
+ public String toString() {
+ return "{tag=" + tag + ", class-tag=" + classTag + ", size=" +
+ (tag >= 100 ? "<class>" : size) // Class size is dependent on 32-bit vs 64-bit,
+ // so strip it.
+ + ", length=" + length + "}";
+ }
+ }
+
+ private static ArrayList<HeapElem> sort(int n, long classTags[], long sizes[], long tags[],
+ int lengths[]) {
+ ArrayList<HeapElem> ret = new ArrayList<HeapElem>(n);
+ for (int i = 0; i < n; i++) {
+ HeapElem elem = new HeapElem();
+ elem.classTag = classTags[i];
+ elem.size = sizes[i];
+ elem.tag = tags[i];
+ elem.length = lengths[i];
+ ret.add(elem);
+ }
+ Collections.sort(ret);
+ return ret;
+ }
+
+ private static native void setTag(Object o, long tag);
+ private static native long getTag(Object o);
+
+ private final static int HEAP_FILTER_OUT_TAGGED = 0x4;
+ private final static int HEAP_FILTER_OUT_UNTAGGED = 0x8;
+ private final static int HEAP_FILTER_OUT_CLASS_TAGGED = 0x10;
+ private final static int HEAP_FILTER_OUT_CLASS_UNTAGGED = 0x20;
+
+ private static native int iterateThroughHeapCount(int heapFilter,
+ Class<?> klassFilter, int stopAfter);
+ private static native int iterateThroughHeapData(int heapFilter,
+ Class<?> klassFilter, long classTags[], long sizes[], long tags[], int lengths[]);
+ private static native int iterateThroughHeapAdd(int heapFilter,
+ Class<?> klassFilter);
+}
diff --git a/test/Android.bp b/test/Android.bp
index 4457e8a..45673f5 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -238,8 +238,8 @@
shared_libs: ["libartd"],
}
-art_cc_test_library {
- name: "libtiagent",
+art_cc_defaults {
+ name: "libtiagent-defaults",
defaults: ["libartagent-defaults"],
srcs: [
"ti-agent/common_load.cc",
@@ -248,10 +248,18 @@
"903-hello-tagging/tagging.cc",
"904-object-allocation/tracking.cc",
"905-object-free/tracking_free.cc",
+ "906-iterate-heap/iterate_heap.cc",
],
shared_libs: [
- "libart",
"libbase",
+ ],
+}
+
+art_cc_test_library {
+ name: "libtiagent",
+ defaults: ["libtiagent-defaults"],
+ shared_libs: [
+ "libart",
"libopenjdkjvmti",
],
}
@@ -259,20 +267,11 @@
art_cc_test_library {
name: "libtiagentd",
defaults: [
- "libartagent-defaults",
+ "libtiagent-defaults",
"art_debug_defaults",
],
- srcs: [
- "ti-agent/common_load.cc",
- "901-hello-ti-agent/basics.cc",
- "902-hello-transformation/transform.cc",
- "903-hello-tagging/tagging.cc",
- "904-object-allocation/tracking.cc",
- "905-object-free/tracking_free.cc",
- ],
shared_libs: [
"libartd",
- "libbase",
"libopenjdkjvmtid",
],
}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 29000f0..1a0f78e 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -263,7 +263,7 @@
# 147-stripped-dex-fallback isn't supported on device because --strip-dex
# requires the zip command.
# 569-checker-pattern-replacement tests behaviour present only on host.
-# 90{2,3,4,5} are not supported in current form due to linker
+# 90{2,3,4,5,6} are not supported in current form due to linker
# restrictions. See b/31681198
TEST_ART_BROKEN_TARGET_TESTS := \
147-stripped-dex-fallback \
@@ -272,6 +272,7 @@
903-hello-tagging \
904-object-allocation \
905-object-free \
+ 906-iterate-heap \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 2b8947e..c412636 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -29,6 +29,7 @@
#include "903-hello-tagging/tagging.h"
#include "904-object-allocation/tracking.h"
#include "905-object-free/tracking_free.h"
+#include "906-iterate-heap/iterate_heap.h"
namespace art {
@@ -50,6 +51,7 @@
{ "903-hello-tagging", Test903HelloTagging::OnLoad, nullptr },
{ "904-object-allocation", Test904ObjectAllocation::OnLoad, nullptr },
{ "905-object-free", Test905ObjectFree::OnLoad, nullptr },
+ { "906-iterate-heap", Test906IterateHeap::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {