summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/art.go32
-rw-r--r--compiler/optimizing/loop_optimization.cc271
-rw-r--r--compiler/optimizing/loop_optimization.h28
-rw-r--r--compiler/optimizing/nodes.h10
-rw-r--r--runtime/arch/arm/context_arm.h2
-rw-r--r--runtime/arch/arm64/context_arm64.h2
-rw-r--r--runtime/arch/x86/context_x86.h2
-rw-r--r--runtime/arch/x86_64/context_x86_64.h2
-rw-r--r--runtime/art_method.cc13
-rw-r--r--runtime/art_method.h6
-rw-r--r--runtime/debugger.cc24
-rw-r--r--runtime/gc/space/region_space-inl.h60
-rw-r--r--runtime/gc/space/region_space.cc88
-rw-r--r--runtime/gc/space/region_space.h44
-rw-r--r--runtime/interpreter/interpreter_switch_impl.cc64
-rw-r--r--runtime/openjdkjvmti/Android.bp1
-rw-r--r--runtime/openjdkjvmti/OpenjdkJvmTi.cc14
-rw-r--r--runtime/openjdkjvmti/art_jvmti.h12
-rw-r--r--runtime/openjdkjvmti/events-inl.h27
-rw-r--r--runtime/openjdkjvmti/events.cc44
-rw-r--r--runtime/openjdkjvmti/ti_breakpoint.cc114
-rw-r--r--runtime/openjdkjvmti/ti_breakpoint.h94
-rw-r--r--runtime/openjdkjvmti/ti_redefine.cc8
-rw-r--r--runtime/openjdkjvmti/ti_redefine.h9
-rw-r--r--test/593-checker-shift-and-simplifier/expected.txt1
-rw-r--r--test/593-checker-shift-and-simplifier/smali/SmaliTests.smali58
-rw-r--r--test/593-checker-shift-and-simplifier/src/Main.java32
-rwxr-xr-xtest/633-checker-rtp-getclass/build23
-rw-r--r--test/633-checker-rtp-getclass/smali/SmaliTests.smali65
-rw-r--r--test/633-checker-rtp-getclass/src/Main.java41
-rw-r--r--test/656-checker-simd-opt/expected.txt1
-rw-r--r--test/656-checker-simd-opt/info.txt1
-rw-r--r--test/656-checker-simd-opt/src/Main.java69
-rw-r--r--test/988-method-trace/expected.txt44
-rw-r--r--test/988-method-trace/src/art/Test988.java23
-rw-r--r--test/988-method-trace/src/art/Trace.java11
-rw-r--r--test/989-method-trace-throw/src/art/Trace.java11
-rw-r--r--test/990-field-trace/src/art/Trace.java11
-rw-r--r--test/991-field-trace-2/src/art/Trace.java11
-rw-r--r--test/992-source-data/expected.txt2
-rw-r--r--test/992-source-data/src/art/Target2.java (renamed from test/992-source-data/src/art/Test2.java)2
-rw-r--r--test/992-source-data/src/art/Test992.java2
-rw-r--r--test/993-breakpoints/breakpoints.cc69
-rw-r--r--test/993-breakpoints/expected.txt613
-rw-r--r--test/993-breakpoints/info.txt7
-rwxr-xr-xtest/993-breakpoints/run18
-rw-r--r--test/993-breakpoints/src/Main.java21
-rw-r--r--test/993-breakpoints/src/art/Breakpoint.java202
-rw-r--r--test/993-breakpoints/src/art/Test993.java498
-rw-r--r--test/994-breakpoint-line/expected.txt34
-rw-r--r--test/994-breakpoint-line/info.txt5
-rwxr-xr-xtest/994-breakpoint-line/run18
-rw-r--r--test/994-breakpoint-line/src/Main.java21
-rw-r--r--test/994-breakpoint-line/src/art/Breakpoint.java202
-rw-r--r--test/994-breakpoint-line/src/art/Test994.java72
-rw-r--r--test/995-breakpoints-throw/expected.txt34
-rw-r--r--test/995-breakpoints-throw/info.txt6
-rwxr-xr-xtest/995-breakpoints-throw/run18
-rw-r--r--test/995-breakpoints-throw/src/Main.java21
-rw-r--r--test/995-breakpoints-throw/src/art/Breakpoint.java202
-rw-r--r--test/995-breakpoints-throw/src/art/Test995.java136
-rw-r--r--test/996-breakpoint-obsolete/expected.txt14
-rw-r--r--test/996-breakpoint-obsolete/info.txt4
-rw-r--r--test/996-breakpoint-obsolete/obsolete_breakpoints.cc76
-rwxr-xr-xtest/996-breakpoint-obsolete/run18
-rw-r--r--test/996-breakpoint-obsolete/src/Main.java21
-rw-r--r--test/996-breakpoint-obsolete/src/art/Breakpoint.java202
-rw-r--r--test/996-breakpoint-obsolete/src/art/Redefinition.java91
-rw-r--r--test/996-breakpoint-obsolete/src/art/Test996.java152
-rw-r--r--test/997-single-step/expected.txt12
-rw-r--r--test/997-single-step/info.txt3
-rwxr-xr-xtest/997-single-step/run18
-rw-r--r--test/997-single-step/src/Main.java21
-rw-r--r--test/997-single-step/src/art/Breakpoint.java202
-rw-r--r--test/997-single-step/src/art/Test997.java82
-rw-r--r--test/997-single-step/src/art/Trace.java56
-rw-r--r--test/Android.bp2
-rw-r--r--test/Android.run-test-jvmti-java-library.mk12
-rwxr-xr-xtest/etc/run-test-jar8
-rw-r--r--test/knownfailures.json26
-rwxr-xr-xtest/run-test8
-rwxr-xr-xtest/testrunner/testrunner.py6
-rw-r--r--test/ti-agent/common_helper.cc259
-rw-r--r--test/ti-stress/stress.cc27
-rwxr-xr-xtools/buildbot-build.sh2
85 files changed, 4552 insertions, 346 deletions
diff --git a/build/art.go b/build/art.go
index f52c63525a..db626fd19c 100644
--- a/build/art.go
+++ b/build/art.go
@@ -83,20 +83,20 @@ func globalFlags(ctx android.BaseContext) ([]string, []string) {
// the debug version. So make the gap consistent (and adjust for the worst).
if len(ctx.AConfig().SanitizeDevice()) > 0 || len(ctx.AConfig().SanitizeHost()) > 0 {
cflags = append(cflags,
- "-DART_STACK_OVERFLOW_GAP_arm=8192",
- "-DART_STACK_OVERFLOW_GAP_arm64=8192",
- "-DART_STACK_OVERFLOW_GAP_mips=16384",
- "-DART_STACK_OVERFLOW_GAP_mips64=16384",
- "-DART_STACK_OVERFLOW_GAP_x86=16384",
- "-DART_STACK_OVERFLOW_GAP_x86_64=20480")
+ "-DART_STACK_OVERFLOW_GAP_arm=8192",
+ "-DART_STACK_OVERFLOW_GAP_arm64=8192",
+ "-DART_STACK_OVERFLOW_GAP_mips=16384",
+ "-DART_STACK_OVERFLOW_GAP_mips64=16384",
+ "-DART_STACK_OVERFLOW_GAP_x86=16384",
+ "-DART_STACK_OVERFLOW_GAP_x86_64=20480")
} else {
cflags = append(cflags,
- "-DART_STACK_OVERFLOW_GAP_arm=8192",
- "-DART_STACK_OVERFLOW_GAP_arm64=8192",
- "-DART_STACK_OVERFLOW_GAP_mips=16384",
- "-DART_STACK_OVERFLOW_GAP_mips64=16384",
- "-DART_STACK_OVERFLOW_GAP_x86=8192",
- "-DART_STACK_OVERFLOW_GAP_x86_64=8192")
+ "-DART_STACK_OVERFLOW_GAP_arm=8192",
+ "-DART_STACK_OVERFLOW_GAP_arm64=8192",
+ "-DART_STACK_OVERFLOW_GAP_mips=16384",
+ "-DART_STACK_OVERFLOW_GAP_mips64=16384",
+ "-DART_STACK_OVERFLOW_GAP_x86=8192",
+ "-DART_STACK_OVERFLOW_GAP_x86_64=8192")
}
return cflags, asflags
@@ -168,10 +168,10 @@ func globalDefaults(ctx android.LoadHookContext) {
Cflags []string
}
}
- Cflags []string
- Asflags []string
+ Cflags []string
+ Asflags []string
Sanitize struct {
- Recover []string
+ Recover []string
}
}
@@ -182,7 +182,7 @@ func globalDefaults(ctx android.LoadHookContext) {
if envTrue(ctx, "ART_DEX_FILE_ACCESS_TRACKING") {
p.Cflags = append(p.Cflags, "-DART_DEX_FILE_ACCESS_TRACKING")
- p.Sanitize.Recover = []string {
+ p.Sanitize.Recover = []string{
"address",
}
}
diff --git a/compiler/optimizing/loop_optimization.cc b/compiler/optimizing/loop_optimization.cc
index d2493137fe..32f40024d3 100644
--- a/compiler/optimizing/loop_optimization.cc
+++ b/compiler/optimizing/loop_optimization.cc
@@ -31,6 +31,9 @@ namespace art {
// Enables vectorization (SIMDization) in the loop optimizer.
static constexpr bool kEnableVectorization = true;
+// All current SIMD targets want 16-byte alignment.
+static constexpr size_t kAlignedBase = 16;
+
// Remove the instruction from the graph. A bit more elaborate than the usual
// instruction removal, since there may be a cycle in the use structure.
static void RemoveFromCycle(HInstruction* instruction) {
@@ -283,6 +286,9 @@ HLoopOptimization::HLoopOptimization(HGraph* graph,
simplified_(false),
vector_length_(0),
vector_refs_(nullptr),
+ vector_peeling_candidate_(nullptr),
+ vector_runtime_test_a_(nullptr),
+ vector_runtime_test_b_(nullptr),
vector_map_(nullptr) {
}
@@ -422,23 +428,6 @@ void HLoopOptimization::TraverseLoopsInnerToOuter(LoopNode* node) {
// Optimization.
//
-bool HLoopOptimization::CanRemoveCycle() {
- for (HInstruction* i : *iset_) {
- // We can never remove instructions that have environment
- // uses when we compile 'debuggable'.
- if (i->HasEnvironmentUses() && graph_->IsDebuggable()) {
- return false;
- }
- // A deoptimization should never have an environment input removed.
- for (const HUseListNode<HEnvironment*>& use : i->GetEnvUses()) {
- if (use.GetUser()->GetHolder()->IsDeoptimize()) {
- return false;
- }
- }
- }
- return true;
-}
-
void HLoopOptimization::SimplifyInduction(LoopNode* node) {
HBasicBlock* header = node->loop_info->GetHeader();
HBasicBlock* preheader = node->loop_info->GetPreHeader();
@@ -565,7 +554,7 @@ void HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
if (kEnableVectorization) {
iset_->clear(); // prepare phi induction
if (TrySetSimpleLoopHeader(header) &&
- CanVectorize(node, body, trip_count) &&
+ ShouldVectorize(node, body, trip_count) &&
TryAssignLastValue(node->loop_info, phi, preheader, /*collect_loop_uses*/ true)) {
Vectorize(node, body, exit, trip_count);
graph_->SetHasSIMD(true); // flag SIMD usage
@@ -580,10 +569,11 @@ void HLoopOptimization::OptimizeInnerLoop(LoopNode* node) {
// Intel Press, June, 2004 (http://www.aartbik.com/).
//
-bool HLoopOptimization::CanVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count) {
+bool HLoopOptimization::ShouldVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count) {
// Reset vector bookkeeping.
vector_length_ = 0;
vector_refs_->clear();
+ vector_peeling_candidate_ = nullptr;
vector_runtime_test_a_ =
vector_runtime_test_b_= nullptr;
@@ -600,12 +590,9 @@ bool HLoopOptimization::CanVectorize(LoopNode* node, HBasicBlock* block, int64_t
}
}
- // Heuristics. Does vectorization seem profitable?
- // TODO: refine
- if (vector_length_ == 0) {
- return false; // nothing found
- } else if (0 < trip_count && trip_count < vector_length_) {
- return false; // insufficient iterations
+ // Does vectorization seem profitable?
+ if (!IsVectorizationProfitable(trip_count)) {
+ return false;
}
// Data dependence analysis. Find each pair of references with same type, where
@@ -645,6 +632,9 @@ bool HLoopOptimization::CanVectorize(LoopNode* node, HBasicBlock* block, int64_t
}
}
+ // Consider dynamic loop peeling for alignment.
+ SetPeelingCandidate(trip_count);
+
// Success!
return true;
}
@@ -657,28 +647,52 @@ void HLoopOptimization::Vectorize(LoopNode* node,
HBasicBlock* header = node->loop_info->GetHeader();
HBasicBlock* preheader = node->loop_info->GetPreHeader();
- // A cleanup is needed for any unknown trip count or for a known trip count
- // with remainder iterations after vectorization.
- bool needs_cleanup = trip_count == 0 || (trip_count % vector_length_) != 0;
+ // Pick a loop unrolling factor for the vector loop.
+ uint32_t unroll = GetUnrollingFactor(block, trip_count);
+ uint32_t chunk = vector_length_ * unroll;
+
+ // A cleanup loop is needed, at least, for any unknown trip count or
+ // for a known trip count with remainder iterations after vectorization.
+ bool needs_cleanup = trip_count == 0 || (trip_count % chunk) != 0;
// Adjust vector bookkeeping.
iset_->clear(); // prepare phi induction
bool is_simple_loop_header = TrySetSimpleLoopHeader(header); // fills iset_
DCHECK(is_simple_loop_header);
+ vector_header_ = header;
+ vector_body_ = block;
+
+ // Generate dynamic loop peeling trip count, if needed:
+ // ptc = <peeling-needed-for-candidate>
+ HInstruction* ptc = nullptr;
+ if (vector_peeling_candidate_ != nullptr) {
+ DCHECK_LT(vector_length_, trip_count) << "dynamic peeling currently requires known trip count";
+ //
+ // TODO: Implement this. Compute address of first access memory location and
+ // compute peeling factor to obtain kAlignedBase alignment.
+ //
+ needs_cleanup = true;
+ }
- // Generate preheader:
+ // Generate loop control:
// stc = <trip-count>;
- // vtc = stc - stc % VL;
+ // vtc = stc - (stc - ptc) % chunk;
+ // i = 0;
HInstruction* stc = induction_range_.GenerateTripCount(node->loop_info, graph_, preheader);
HInstruction* vtc = stc;
if (needs_cleanup) {
- DCHECK(IsPowerOfTwo(vector_length_));
+ DCHECK(IsPowerOfTwo(chunk));
+ HInstruction* diff = stc;
+ if (ptc != nullptr) {
+ diff = Insert(preheader, new (global_allocator_) HSub(induc_type, stc, ptc));
+ }
HInstruction* rem = Insert(
preheader, new (global_allocator_) HAnd(induc_type,
- stc,
- graph_->GetIntConstant(vector_length_ - 1)));
+ diff,
+ graph_->GetIntConstant(chunk - 1)));
vtc = Insert(preheader, new (global_allocator_) HSub(induc_type, stc, rem));
}
+ vector_index_ = graph_->GetIntConstant(0);
// Generate runtime disambiguation test:
// vtc = a != b ? vtc : 0;
@@ -691,16 +705,31 @@ void HLoopOptimization::Vectorize(LoopNode* node,
needs_cleanup = true;
}
- // Generate vector loop:
- // for (i = 0; i < vtc; i += VL)
+ // Generate dynamic peeling loop for alignment, if needed:
+ // for ( ; i < ptc; i += 1)
+ // <loop-body>
+ if (ptc != nullptr) {
+ vector_mode_ = kSequential;
+ GenerateNewLoop(node,
+ block,
+ graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit),
+ vector_index_,
+ ptc,
+ graph_->GetIntConstant(1),
+ /*unroll*/ 1);
+ }
+
+ // Generate vector loop, possibly further unrolled:
+ // for ( ; i < vtc; i += chunk)
// <vectorized-loop-body>
vector_mode_ = kVector;
GenerateNewLoop(node,
block,
- graph_->TransformLoopForVectorization(header, block, exit),
- graph_->GetIntConstant(0),
+ graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit),
+ vector_index_,
vtc,
- graph_->GetIntConstant(vector_length_));
+ graph_->GetIntConstant(vector_length_), // increment per unroll
+ unroll);
HLoopInformation* vloop = vector_header_->GetLoopInformation();
// Generate cleanup loop, if needed:
@@ -711,9 +740,10 @@ void HLoopOptimization::Vectorize(LoopNode* node,
GenerateNewLoop(node,
block,
graph_->TransformLoopForVectorization(vector_header_, vector_body_, exit),
- vector_phi_,
+ vector_index_,
stc,
- graph_->GetIntConstant(1));
+ graph_->GetIntConstant(1),
+ /*unroll*/ 1);
}
// Remove the original loop by disconnecting the body block
@@ -722,8 +752,9 @@ void HLoopOptimization::Vectorize(LoopNode* node,
while (!header->GetFirstInstruction()->IsGoto()) {
header->RemoveInstruction(header->GetFirstInstruction());
}
- // Update loop hierarchy: the old header now resides in the
- // same outer loop as the old preheader.
+ // Update loop hierarchy: the old header now resides in the same outer loop
+ // as the old preheader. Note that we don't bother putting sequential
+ // loops back in the hierarchy at this point.
header->SetLoopInformation(preheader->GetLoopInformation()); // outward
node->loop_info = vloop;
}
@@ -733,44 +764,64 @@ void HLoopOptimization::GenerateNewLoop(LoopNode* node,
HBasicBlock* new_preheader,
HInstruction* lo,
HInstruction* hi,
- HInstruction* step) {
+ HInstruction* step,
+ uint32_t unroll) {
+ DCHECK(unroll == 1 || vector_mode_ == kVector);
Primitive::Type induc_type = Primitive::kPrimInt;
// Prepare new loop.
- vector_map_->clear();
vector_preheader_ = new_preheader,
vector_header_ = vector_preheader_->GetSingleSuccessor();
vector_body_ = vector_header_->GetSuccessors()[1];
- vector_phi_ = new (global_allocator_) HPhi(global_allocator_,
- kNoRegNumber,
- 0,
- HPhi::ToPhiType(induc_type));
+ HPhi* phi = new (global_allocator_) HPhi(global_allocator_,
+ kNoRegNumber,
+ 0,
+ HPhi::ToPhiType(induc_type));
// Generate header and prepare body.
// for (i = lo; i < hi; i += step)
// <loop-body>
- HInstruction* cond = new (global_allocator_) HAboveOrEqual(vector_phi_, hi);
- vector_header_->AddPhi(vector_phi_);
+ HInstruction* cond = new (global_allocator_) HAboveOrEqual(phi, hi);
+ vector_header_->AddPhi(phi);
vector_header_->AddInstruction(cond);
vector_header_->AddInstruction(new (global_allocator_) HIf(cond));
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- bool vectorized_def = VectorizeDef(node, it.Current(), /*generate_code*/ true);
- DCHECK(vectorized_def);
- }
- // Generate body from the instruction map, but in original program order.
- HEnvironment* env = vector_header_->GetFirstInstruction()->GetEnvironment();
- for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
- auto i = vector_map_->find(it.Current());
- if (i != vector_map_->end() && !i->second->IsInBlock()) {
- Insert(vector_body_, i->second);
- // Deal with instructions that need an environment, such as the scalar intrinsics.
- if (i->second->NeedsEnvironment()) {
- i->second->CopyEnvironmentFromWithLoopPhiAdjustment(env, vector_header_);
+ vector_index_ = phi;
+ for (uint32_t u = 0; u < unroll; u++) {
+ // Clear map, leaving loop invariants setup during unrolling.
+ if (u == 0) {
+ vector_map_->clear();
+ } else {
+ for (auto i = vector_map_->begin(); i != vector_map_->end(); ) {
+ if (i->second->IsVecReplicateScalar()) {
+ DCHECK(node->loop_info->IsDefinedOutOfTheLoop(i->first));
+ ++i;
+ } else {
+ i = vector_map_->erase(i);
+ }
+ }
+ }
+ // Generate instruction map.
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ bool vectorized_def = VectorizeDef(node, it.Current(), /*generate_code*/ true);
+ DCHECK(vectorized_def);
+ }
+ // Generate body from the instruction map, but in original program order.
+ HEnvironment* env = vector_header_->GetFirstInstruction()->GetEnvironment();
+ for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
+ auto i = vector_map_->find(it.Current());
+ if (i != vector_map_->end() && !i->second->IsInBlock()) {
+ Insert(vector_body_, i->second);
+ // Deal with instructions that need an environment, such as the scalar intrinsics.
+ if (i->second->NeedsEnvironment()) {
+ i->second->CopyEnvironmentFromWithLoopPhiAdjustment(env, vector_header_);
+ }
}
}
+ vector_index_ = new (global_allocator_) HAdd(induc_type, vector_index_, step);
+ Insert(vector_body_, vector_index_);
}
- // Finalize increment and phi.
- HInstruction* inc = new (global_allocator_) HAdd(induc_type, vector_phi_, step);
- vector_phi_->AddInput(lo);
- vector_phi_->AddInput(Insert(vector_body_, inc));
+ // Finalize phi for the loop index.
+ phi->AddInput(lo);
+ phi->AddInput(vector_index_);
+ vector_index_ = phi;
}
// TODO: accept reductions at left-hand-side, mixed-type store idioms, etc.
@@ -795,7 +846,7 @@ bool HLoopOptimization::VectorizeDef(LoopNode* node,
VectorizeUse(node, value, generate_code, type, restrictions)) {
if (generate_code) {
GenerateVecSub(index, offset);
- GenerateVecMem(instruction, vector_map_->Get(index), vector_map_->Get(value), type);
+ GenerateVecMem(instruction, vector_map_->Get(index), vector_map_->Get(value), offset, type);
} else {
vector_refs_->insert(ArrayReference(base, offset, type, /*lhs*/ true));
}
@@ -852,7 +903,7 @@ bool HLoopOptimization::VectorizeUse(LoopNode* node,
induction_range_.IsUnitStride(instruction, index, &offset)) {
if (generate_code) {
GenerateVecSub(index, offset);
- GenerateVecMem(instruction, vector_map_->Get(index), nullptr, type);
+ GenerateVecMem(instruction, vector_map_->Get(index), nullptr, offset, type);
} else {
vector_refs_->insert(ArrayReference(base, offset, type, /*lhs*/ false));
}
@@ -1164,7 +1215,7 @@ void HLoopOptimization::GenerateVecInv(HInstruction* org, Primitive::Type type)
void HLoopOptimization::GenerateVecSub(HInstruction* org, HInstruction* offset) {
if (vector_map_->find(org) == vector_map_->end()) {
- HInstruction* subscript = vector_phi_;
+ HInstruction* subscript = vector_index_;
if (offset != nullptr) {
subscript = new (global_allocator_) HAdd(Primitive::kPrimInt, subscript, offset);
if (org->IsPhi()) {
@@ -1178,17 +1229,27 @@ void HLoopOptimization::GenerateVecSub(HInstruction* org, HInstruction* offset)
void HLoopOptimization::GenerateVecMem(HInstruction* org,
HInstruction* opa,
HInstruction* opb,
+ HInstruction* offset,
Primitive::Type type) {
HInstruction* vector = nullptr;
if (vector_mode_ == kVector) {
// Vector store or load.
+ HInstruction* base = org->InputAt(0);
if (opb != nullptr) {
vector = new (global_allocator_) HVecStore(
- global_allocator_, org->InputAt(0), opa, opb, type, vector_length_);
+ global_allocator_, base, opa, opb, type, vector_length_);
} else {
bool is_string_char_at = org->AsArrayGet()->IsStringCharAt();
vector = new (global_allocator_) HVecLoad(
- global_allocator_, org->InputAt(0), opa, type, vector_length_, is_string_char_at);
+ global_allocator_, base, opa, type, vector_length_, is_string_char_at);
+ }
+ // Known dynamically enforced alignment?
+ // TODO: detect offset + constant differences.
+ // TODO: long run, static alignment analysis?
+ if (vector_peeling_candidate_ != nullptr &&
+ vector_peeling_candidate_->base == base &&
+ vector_peeling_candidate_->offset == offset) {
+ vector->AsVecMemoryOperation()->SetAlignment(Alignment(kAlignedBase, 0));
}
} else {
// Scalar store or load.
@@ -1444,10 +1505,57 @@ bool HLoopOptimization::VectorizeHalvingAddIdiom(LoopNode* node,
}
//
+// Vectorization heuristics.
+//
+
+bool HLoopOptimization::IsVectorizationProfitable(int64_t trip_count) {
+ // Current heuristic: non-empty body with sufficient number
+ // of iterations (if known).
+ // TODO: refine by looking at e.g. operation count, alignment, etc.
+ if (vector_length_ == 0) {
+ return false; // nothing found
+ } else if (0 < trip_count && trip_count < vector_length_) {
+ return false; // insufficient iterations
+ }
+ return true;
+}
+
+void HLoopOptimization::SetPeelingCandidate(int64_t trip_count ATTRIBUTE_UNUSED) {
+ // Current heuristic: none.
+ // TODO: implement
+}
+
+uint32_t HLoopOptimization::GetUnrollingFactor(HBasicBlock* block, int64_t trip_count) {
+ // Current heuristic: unroll by 2 on ARM64/X86 for large known trip
+ // counts and small loop bodies.
+ // TODO: refine with operation count, remaining iterations, etc.
+ // Artem had some really cool ideas for this already.
+ switch (compiler_driver_->GetInstructionSet()) {
+ case kArm64:
+ case kX86:
+ case kX86_64: {
+ size_t num_instructions = block->GetInstructions().CountSize();
+ if (num_instructions <= 10 && trip_count >= 4 * vector_length_) {
+ return 2;
+ }
+ return 1;
+ }
+ default:
+ return 1;
+ }
+}
+
+//
// Helpers.
//
bool HLoopOptimization::TrySetPhiInduction(HPhi* phi, bool restrict_uses) {
+ // Special case Phis that have equivalent in a debuggable setup. Our graph checker isn't
+ // smart enough to follow strongly connected components (and it's probably not worth
+ // it to make it so). See b/33775412.
+ if (graph_->IsDebuggable() && phi->HasEquivalentPhi()) {
+ return false;
+ }
DCHECK(iset_->empty());
ArenaSet<HInstruction*>* set = induction_range_.LookupCycle(phi);
if (set != nullptr) {
@@ -1576,8 +1684,8 @@ bool HLoopOptimization::TryReplaceWithLastValue(HLoopInformation* loop_info,
size_t index = it->GetIndex();
++it; // increment before replacing
if (iset_->find(user->GetHolder()) == iset_->end()) { // not excluded?
- HLoopInformation* other_loop_info = user->GetHolder()->GetBlock()->GetLoopInformation();
// Only update environment uses after the loop.
+ HLoopInformation* other_loop_info = user->GetHolder()->GetBlock()->GetLoopInformation();
if (other_loop_info == nullptr || !other_loop_info->IsIn(*loop_info)) {
user->RemoveAsUserOfInput(index);
user->SetRawEnvAt(index, replacement);
@@ -1614,4 +1722,21 @@ void HLoopOptimization::RemoveDeadInstructions(const HInstructionList& list) {
}
}
+bool HLoopOptimization::CanRemoveCycle() {
+ for (HInstruction* i : *iset_) {
+ // We can never remove instructions that have environment
+ // uses when we compile 'debuggable'.
+ if (i->HasEnvironmentUses() && graph_->IsDebuggable()) {
+ return false;
+ }
+ // A deoptimization should never have an environment input removed.
+ for (const HUseListNode<HEnvironment*>& use : i->GetEnvUses()) {
+ if (use.GetUser()->GetHolder()->IsDeoptimize()) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
} // namespace art
diff --git a/compiler/optimizing/loop_optimization.h b/compiler/optimizing/loop_optimization.h
index cc6343aeb5..de4bd85fc8 100644
--- a/compiler/optimizing/loop_optimization.h
+++ b/compiler/optimizing/loop_optimization.h
@@ -116,14 +116,15 @@ class HLoopOptimization : public HOptimization {
void OptimizeInnerLoop(LoopNode* node);
// Vectorization analysis and synthesis.
- bool CanVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count);
+ bool ShouldVectorize(LoopNode* node, HBasicBlock* block, int64_t trip_count);
void Vectorize(LoopNode* node, HBasicBlock* block, HBasicBlock* exit, int64_t trip_count);
void GenerateNewLoop(LoopNode* node,
HBasicBlock* block,
HBasicBlock* new_preheader,
HInstruction* lo,
HInstruction* hi,
- HInstruction* step);
+ HInstruction* step,
+ uint32_t unroll);
bool VectorizeDef(LoopNode* node, HInstruction* instruction, bool generate_code);
bool VectorizeUse(LoopNode* node,
HInstruction* instruction,
@@ -133,10 +134,11 @@ class HLoopOptimization : public HOptimization {
bool TrySetVectorType(Primitive::Type type, /*out*/ uint64_t* restrictions);
bool TrySetVectorLength(uint32_t length);
void GenerateVecInv(HInstruction* org, Primitive::Type type);
- void GenerateVecSub(HInstruction* org, HInstruction* off);
+ void GenerateVecSub(HInstruction* org, HInstruction* offset);
void GenerateVecMem(HInstruction* org,
HInstruction* opa,
HInstruction* opb,
+ HInstruction* offset,
Primitive::Type type);
void GenerateVecOp(HInstruction* org,
HInstruction* opa,
@@ -151,6 +153,11 @@ class HLoopOptimization : public HOptimization {
Primitive::Type type,
uint64_t restrictions);
+ // Vectorization heuristics.
+ bool IsVectorizationProfitable(int64_t trip_count);
+ void SetPeelingCandidate(int64_t trip_count);
+ uint32_t GetUnrollingFactor(HBasicBlock* block, int64_t trip_count);
+
// Helpers.
bool TrySetPhiInduction(HPhi* phi, bool restrict_uses);
bool TrySetSimpleLoopHeader(HBasicBlock* block);
@@ -208,20 +215,25 @@ class HLoopOptimization : public HOptimization {
// Contents reside in phase-local heap memory.
ArenaSet<ArrayReference>* vector_refs_;
+ // Dynamic loop peeling candidate for alignment.
+ const ArrayReference* vector_peeling_candidate_;
+
+ // Dynamic data dependence test of the form a != b.
+ HInstruction* vector_runtime_test_a_;
+ HInstruction* vector_runtime_test_b_;
+
// Mapping used during vectorization synthesis for both the scalar peeling/cleanup
- // loop (simd_ is false) and the actual vector loop (simd_ is true). The data
+ // loop (mode is kSequential) and the actual vector loop (mode is kVector). The data
// structure maps original instructions into the new instructions.
// Contents reside in phase-local heap memory.
ArenaSafeMap<HInstruction*, HInstruction*>* vector_map_;
// Temporary vectorization bookkeeping.
+ VectorMode vector_mode_; // synthesis mode
HBasicBlock* vector_preheader_; // preheader of the new loop
HBasicBlock* vector_header_; // header of the new loop
HBasicBlock* vector_body_; // body of the new loop
- HInstruction* vector_runtime_test_a_;
- HInstruction* vector_runtime_test_b_; // defines a != b runtime test
- HPhi* vector_phi_; // the Phi representing the normalized loop index
- VectorMode vector_mode_; // selects synthesis mode
+ HInstruction* vector_index_; // normalized index of the new loop
friend class LoopOptimizationTest;
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index ffa16dd787..b21c4a5aa7 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -2612,6 +2612,16 @@ class HPhi FINAL : public HVariableInputSizeInstruction {
&& other->AsPhi()->GetRegNumber() == GetRegNumber();
}
+ bool HasEquivalentPhi() const {
+ if (GetPrevious() != nullptr && GetPrevious()->AsPhi()->GetRegNumber() == GetRegNumber()) {
+ return true;
+ }
+ if (GetNext() != nullptr && GetNext()->AsPhi()->GetRegNumber() == GetRegNumber()) {
+ return true;
+ }
+ return false;
+ }
+
// Returns the next equivalent phi (starting from the current one) or null if there is none.
// An equivalent phi is a phi having the same dex register and type.
// It assumes that phis with the same dex register are adjacent.
diff --git a/runtime/arch/arm/context_arm.h b/runtime/arch/arm/context_arm.h
index 2623ee9315..fa9aa46d4d 100644
--- a/runtime/arch/arm/context_arm.h
+++ b/runtime/arch/arm/context_arm.h
@@ -25,7 +25,7 @@
namespace art {
namespace arm {
-class ArmContext : public Context {
+class ArmContext FINAL : public Context {
public:
ArmContext() {
Reset();
diff --git a/runtime/arch/arm64/context_arm64.h b/runtime/arch/arm64/context_arm64.h
index 105e78461d..36aded07c4 100644
--- a/runtime/arch/arm64/context_arm64.h
+++ b/runtime/arch/arm64/context_arm64.h
@@ -25,7 +25,7 @@
namespace art {
namespace arm64 {
-class Arm64Context : public Context {
+class Arm64Context FINAL : public Context {
public:
Arm64Context() {
Reset();
diff --git a/runtime/arch/x86/context_x86.h b/runtime/arch/x86/context_x86.h
index f482d9ffcb..303dfe361c 100644
--- a/runtime/arch/x86/context_x86.h
+++ b/runtime/arch/x86/context_x86.h
@@ -25,7 +25,7 @@
namespace art {
namespace x86 {
-class X86Context : public Context {
+class X86Context FINAL : public Context {
public:
X86Context() {
Reset();
diff --git a/runtime/arch/x86_64/context_x86_64.h b/runtime/arch/x86_64/context_x86_64.h
index 46f2b63848..f8e2845983 100644
--- a/runtime/arch/x86_64/context_x86_64.h
+++ b/runtime/arch/x86_64/context_x86_64.h
@@ -25,7 +25,7 @@
namespace art {
namespace x86_64 {
-class X86_64Context : public Context {
+class X86_64Context FINAL : public Context {
public:
X86_64Context() {
Reset();
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 155498639e..3e7ed9799d 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -61,6 +61,19 @@ DEFINE_RUNTIME_DEBUG_FLAG(ArtMethod, kCheckDeclaringClassState);
static_assert(ArtMethod::kRuntimeMethodDexMethodIndex == DexFile::kDexNoIndex,
"Wrong runtime-method dex method index");
+ArtMethod* ArtMethod::GetCanonicalMethod(PointerSize pointer_size) {
+ if (LIKELY(!IsDefault())) {
+ return this;
+ } else {
+ mirror::Class* declaring_class = GetDeclaringClass();
+ ArtMethod* ret = declaring_class->FindDeclaredVirtualMethod(declaring_class->GetDexCache(),
+ GetDexMethodIndex(),
+ pointer_size);
+ DCHECK(ret != nullptr);
+ return ret;
+ }
+}
+
ArtMethod* ArtMethod::GetNonObsoleteMethod() {
DCHECK_EQ(kRuntimePointerSize, Runtime::Current()->GetClassLinker()->GetImagePointerSize());
if (LIKELY(!IsObsolete())) {
diff --git a/runtime/art_method.h b/runtime/art_method.h
index 96306af177..4b3e8efdad 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -483,6 +483,12 @@ class ArtMethod FINAL {
}
}
+ // Takes a method and returns a 'canonical' one if the method is default (and therefore
+ // potentially copied from some other class). For example, this ensures that the debugger does not
+ // get confused as to which method we are in.
+ ArtMethod* GetCanonicalMethod(PointerSize pointer_size = kRuntimePointerSize)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
ArtMethod* GetSingleImplementation(PointerSize pointer_size)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index 12bdb32fec..cc12439074 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -77,25 +77,10 @@ static uint16_t CappedAllocRecordCount(size_t alloc_record_count) {
return alloc_record_count;
}
-// Takes a method and returns a 'canonical' one if the method is default (and therefore potentially
-// copied from some other class). This ensures that the debugger does not get confused as to which
-// method we are in.
-static ArtMethod* GetCanonicalMethod(ArtMethod* m)
- REQUIRES_SHARED(Locks::mutator_lock_) {
- if (LIKELY(!m->IsDefault())) {
- return m;
- } else {
- mirror::Class* declaring_class = m->GetDeclaringClass();
- return declaring_class->FindDeclaredVirtualMethod(declaring_class->GetDexCache(),
- m->GetDexMethodIndex(),
- kRuntimePointerSize);
- }
-}
-
class Breakpoint : public ValueObject {
public:
Breakpoint(ArtMethod* method, uint32_t dex_pc, DeoptimizationRequest::Kind deoptimization_kind)
- : method_(GetCanonicalMethod(method)),
+ : method_(method->GetCanonicalMethod(kRuntimePointerSize)),
dex_pc_(dex_pc),
deoptimization_kind_(deoptimization_kind) {
CHECK(deoptimization_kind_ == DeoptimizationRequest::kNothing ||
@@ -125,7 +110,7 @@ class Breakpoint : public ValueObject {
// Returns true if the method of this breakpoint and the passed in method should be considered the
// same. That is, they are either the same method or they are copied from the same method.
bool IsInMethod(ArtMethod* m) const REQUIRES_SHARED(Locks::mutator_lock_) {
- return method_ == GetCanonicalMethod(m);
+ return method_ == m->GetCanonicalMethod(kRuntimePointerSize);
}
private:
@@ -1367,7 +1352,8 @@ JDWP::FieldId Dbg::ToFieldId(const ArtField* f) {
static JDWP::MethodId ToMethodId(ArtMethod* m)
REQUIRES_SHARED(Locks::mutator_lock_) {
- return static_cast<JDWP::MethodId>(reinterpret_cast<uintptr_t>(GetCanonicalMethod(m)));
+ return static_cast<JDWP::MethodId>(
+ reinterpret_cast<uintptr_t>(m->GetCanonicalMethod(kRuntimePointerSize)));
}
static ArtField* FromFieldId(JDWP::FieldId fid)
@@ -2887,7 +2873,7 @@ static void SetEventLocation(JDWP::EventLocation* location, ArtMethod* m, uint32
if (m == nullptr) {
memset(location, 0, sizeof(*location));
} else {
- location->method = GetCanonicalMethod(m);
+ location->method = m->GetCanonicalMethod(kRuntimePointerSize);
location->dex_pc = (m->IsNative() || m->IsProxyMethod()) ? static_cast<uint32_t>(-1) : dex_pc;
}
}
diff --git a/runtime/gc/space/region_space-inl.h b/runtime/gc/space/region_space-inl.h
index fc24fc2974..82e8f20154 100644
--- a/runtime/gc/space/region_space-inl.h
+++ b/runtime/gc/space/region_space-inl.h
@@ -48,58 +48,32 @@ inline mirror::Object* RegionSpace::AllocNonvirtual(size_t num_bytes, size_t* by
mirror::Object* obj;
if (LIKELY(num_bytes <= kRegionSize)) {
// Non-large object.
- if (!kForEvac) {
- obj = current_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- } else {
- DCHECK(evac_region_ != nullptr);
- obj = evac_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- }
+ obj = (kForEvac ? evac_region_ : current_region_)->Alloc(num_bytes,
+ bytes_allocated,
+ usable_size,
+ bytes_tl_bulk_allocated);
if (LIKELY(obj != nullptr)) {
return obj;
}
MutexLock mu(Thread::Current(), region_lock_);
// Retry with current region since another thread may have updated it.
- if (!kForEvac) {
- obj = current_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- } else {
- obj = evac_region_->Alloc(num_bytes, bytes_allocated, usable_size,
- bytes_tl_bulk_allocated);
- }
+ obj = (kForEvac ? evac_region_ : current_region_)->Alloc(num_bytes,
+ bytes_allocated,
+ usable_size,
+ bytes_tl_bulk_allocated);
if (LIKELY(obj != nullptr)) {
return obj;
}
- if (!kForEvac) {
- // Retain sufficient free regions for full evacuation.
- if ((num_non_free_regions_ + 1) * 2 > num_regions_) {
- return nullptr;
- }
- for (size_t i = 0; i < num_regions_; ++i) {
- Region* r = &regions_[i];
- if (r->IsFree()) {
- r->Unfree(this, time_);
- r->SetNewlyAllocated();
- ++num_non_free_regions_;
- obj = r->Alloc(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
- CHECK(obj != nullptr);
- current_region_ = r;
- return obj;
- }
- }
- } else {
- for (size_t i = 0; i < num_regions_; ++i) {
- Region* r = &regions_[i];
- if (r->IsFree()) {
- r->Unfree(this, time_);
- ++num_non_free_regions_;
- obj = r->Alloc(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
- CHECK(obj != nullptr);
- evac_region_ = r;
- return obj;
- }
+ Region* r = AllocateRegion(kForEvac);
+ if (LIKELY(r != nullptr)) {
+ if (kForEvac) {
+ evac_region_ = r;
+ } else {
+ current_region_ = r;
}
+ obj = r->Alloc(num_bytes, bytes_allocated, usable_size, bytes_tl_bulk_allocated);
+ CHECK(obj != nullptr);
+ return obj;
}
} else {
// Large object.
diff --git a/runtime/gc/space/region_space.cc b/runtime/gc/space/region_space.cc
index 8d8c4885ef..26b7282a0a 100644
--- a/runtime/gc/space/region_space.cc
+++ b/runtime/gc/space/region_space.cc
@@ -29,6 +29,9 @@ namespace space {
// value of the region size, evaculate the region.
static constexpr uint kEvaculateLivePercentThreshold = 75U;
+// If we protect the cleared regions.
+static constexpr bool kProtectClearedRegions = true;
+
MemMap* RegionSpace::CreateMemMap(const std::string& name, size_t capacity,
uint8_t* requested_begin) {
CHECK_ALIGNED(capacity, kRegionSize);
@@ -449,21 +452,14 @@ bool RegionSpace::AllocNewTlab(Thread* self, size_t min_bytes) {
MutexLock mu(self, region_lock_);
RevokeThreadLocalBuffersLocked(self);
// Retain sufficient free regions for full evacuation.
- if ((num_non_free_regions_ + 1) * 2 > num_regions_) {
- return false;
- }
- for (size_t i = 0; i < num_regions_; ++i) {
- Region* r = &regions_[i];
- if (r->IsFree()) {
- r->Unfree(this, time_);
- ++num_non_free_regions_;
- r->SetNewlyAllocated();
- r->SetTop(r->End());
- r->is_a_tlab_ = true;
- r->thread_ = self;
- self->SetTlab(r->Begin(), r->Begin() + min_bytes, r->End());
- return true;
- }
+
+ Region* r = AllocateRegion(/*for_evac*/ false);
+ if (r != nullptr) {
+ r->is_a_tlab_ = true;
+ r->thread_ = self;
+ r->SetTop(r->End());
+ self->SetTlab(r->Begin(), r->Begin() + min_bytes, r->End());
+ return true;
}
return false;
}
@@ -543,6 +539,68 @@ size_t RegionSpace::AllocationSizeNonvirtual(mirror::Object* obj, size_t* usable
return num_bytes;
}
+void RegionSpace::Region::Clear(bool zero_and_release_pages) {
+ top_.StoreRelaxed(begin_);
+ state_ = RegionState::kRegionStateFree;
+ type_ = RegionType::kRegionTypeNone;
+ objects_allocated_.StoreRelaxed(0);
+ alloc_time_ = 0;
+ live_bytes_ = static_cast<size_t>(-1);
+ if (zero_and_release_pages) {
+ ZeroAndReleasePages(begin_, end_ - begin_);
+ }
+ if (kProtectClearedRegions) {
+ mprotect(begin_, end_ - begin_, PROT_NONE);
+ }
+ is_newly_allocated_ = false;
+ is_a_tlab_ = false;
+ thread_ = nullptr;
+}
+
+RegionSpace::Region* RegionSpace::AllocateRegion(bool for_evac) {
+ if (!for_evac && (num_non_free_regions_ + 1) * 2 > num_regions_) {
+ return nullptr;
+ }
+ for (size_t i = 0; i < num_regions_; ++i) {
+ Region* r = &regions_[i];
+ if (r->IsFree()) {
+ r->Unfree(this, time_);
+ ++num_non_free_regions_;
+ if (!for_evac) {
+ // Evac doesn't count as newly allocated.
+ r->SetNewlyAllocated();
+ }
+ return r;
+ }
+ }
+ return nullptr;
+}
+
+void RegionSpace::Region::MarkAsAllocated(RegionSpace* region_space, uint32_t alloc_time) {
+ DCHECK(IsFree());
+ alloc_time_ = alloc_time;
+ region_space->AdjustNonFreeRegionLimit(idx_);
+ type_ = RegionType::kRegionTypeToSpace;
+ if (kProtectClearedRegions) {
+ mprotect(Begin(), kRegionSize, PROT_READ | PROT_WRITE);
+ }
+}
+
+void RegionSpace::Region::Unfree(RegionSpace* region_space, uint32_t alloc_time) {
+ MarkAsAllocated(region_space, alloc_time);
+ state_ = RegionState::kRegionStateAllocated;
+}
+
+void RegionSpace::Region::UnfreeLarge(RegionSpace* region_space, uint32_t alloc_time) {
+ MarkAsAllocated(region_space, alloc_time);
+ state_ = RegionState::kRegionStateLarge;
+}
+
+void RegionSpace::Region::UnfreeLargeTail(RegionSpace* region_space, uint32_t alloc_time) {
+ MarkAsAllocated(region_space, alloc_time);
+ state_ = RegionState::kRegionStateLargeTail;
+}
+
} // namespace space
} // namespace gc
} // namespace art
diff --git a/runtime/gc/space/region_space.h b/runtime/gc/space/region_space.h
index 323ccdbd74..8907b07bf2 100644
--- a/runtime/gc/space/region_space.h
+++ b/runtime/gc/space/region_space.h
@@ -284,20 +284,7 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
return type_;
}
- void Clear(bool zero_and_release_pages) {
- top_.StoreRelaxed(begin_);
- state_ = RegionState::kRegionStateFree;
- type_ = RegionType::kRegionTypeNone;
- objects_allocated_.StoreRelaxed(0);
- alloc_time_ = 0;
- live_bytes_ = static_cast<size_t>(-1);
- if (zero_and_release_pages) {
- ZeroAndReleasePages(begin_, end_ - begin_);
- }
- is_newly_allocated_ = false;
- is_a_tlab_ = false;
- thread_ = nullptr;
- }
+ void Clear(bool zero_and_release_pages);
ALWAYS_INLINE mirror::Object* Alloc(size_t num_bytes, size_t* bytes_allocated,
size_t* usable_size,
@@ -315,31 +302,16 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
// Given a free region, declare it non-free (allocated).
void Unfree(RegionSpace* region_space, uint32_t alloc_time)
- REQUIRES(region_space->region_lock_) {
- DCHECK(IsFree());
- state_ = RegionState::kRegionStateAllocated;
- type_ = RegionType::kRegionTypeToSpace;
- alloc_time_ = alloc_time;
- region_space->AdjustNonFreeRegionLimit(idx_);
- }
+ REQUIRES(region_space->region_lock_);
void UnfreeLarge(RegionSpace* region_space, uint32_t alloc_time)
- REQUIRES(region_space->region_lock_) {
- DCHECK(IsFree());
- state_ = RegionState::kRegionStateLarge;
- type_ = RegionType::kRegionTypeToSpace;
- alloc_time_ = alloc_time;
- region_space->AdjustNonFreeRegionLimit(idx_);
- }
+ REQUIRES(region_space->region_lock_);
void UnfreeLargeTail(RegionSpace* region_space, uint32_t alloc_time)
- REQUIRES(region_space->region_lock_) {
- DCHECK(IsFree());
- state_ = RegionState::kRegionStateLargeTail;
- type_ = RegionType::kRegionTypeToSpace;
- alloc_time_ = alloc_time;
- region_space->AdjustNonFreeRegionLimit(idx_);
- }
+ REQUIRES(region_space->region_lock_);
+
+ void MarkAsAllocated(RegionSpace* region_space, uint32_t alloc_time)
+ REQUIRES(region_space->region_lock_);
void SetNewlyAllocated() {
is_newly_allocated_ = true;
@@ -539,6 +511,8 @@ class RegionSpace FINAL : public ContinuousMemMapAllocSpace {
}
}
+ Region* AllocateRegion(bool for_evac) REQUIRES(region_lock_);
+
Mutex region_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
uint32_t time_; // The time as the number of collections since the startup.
diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc
index 45788e7617..de8c44e304 100644
--- a/runtime/interpreter/interpreter_switch_impl.cc
+++ b/runtime/interpreter/interpreter_switch_impl.cc
@@ -17,6 +17,7 @@
#include "interpreter_switch_impl.h"
#include "base/enums.h"
+#include "base/memory_tool.h"
#include "experimental_flags.h"
#include "interpreter_common.h"
#include "jit/jit.h"
@@ -64,13 +65,22 @@ namespace interpreter {
}
// Code to run before each dex instruction.
-#define PREAMBLE() \
- do { \
- if (UNLIKELY(instrumentation->HasDexPcListeners())) { \
- instrumentation->DexPcMovedEvent(self, shadow_frame.GetThisObject(code_item->ins_size_), \
- shadow_frame.GetMethod(), dex_pc); \
+#define PREAMBLE_SAVE(save_ref) \
+ { \
+ if (UNLIKELY(instrumentation->HasDexPcListeners()) && \
+ UNLIKELY(!DoDexPcMoveEvent(self, \
+ code_item, \
+ shadow_frame, \
+ dex_pc, \
+ instrumentation, \
+ save_ref))) { \
+ HANDLE_PENDING_EXCEPTION(); \
+ break; \
} \
- } while (false)
+ } \
+ do {} while (false)
+
+#define PREAMBLE() PREAMBLE_SAVE(nullptr)
#define BRANCH_INSTRUMENTATION(offset) \
do { \
@@ -104,6 +114,46 @@ namespace interpreter {
} \
} while (false)
+// Unlike most other events the DexPcMovedEvent can be sent when there is a pending exception (if
+// the next instruction is MOVE_EXCEPTION). This means it needs to be handled carefully to be able
+// to detect exceptions thrown by the DexPcMovedEvent itself. These exceptions could be thrown by
+// jvmti-agents while handling breakpoint or single step events. We had to move this into its own
+// function because it was making ExecuteSwitchImpl have too large a stack.
+#ifdef ADDRESS_SANITIZER
+NO_INLINE
+#endif // ADDRESS_SANITIZER
+static bool DoDexPcMoveEvent(Thread* self,
+ const DexFile::CodeItem* code_item,
+ const ShadowFrame& shadow_frame,
+ uint32_t dex_pc,
+ const instrumentation::Instrumentation* instrumentation,
+ JValue* save_ref)
+ REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(instrumentation->HasDexPcListeners());
+ StackHandleScope<2> hs(self);
+ Handle<mirror::Throwable> thr(hs.NewHandle(self->GetException()));
+ mirror::Object* null_obj = nullptr;
+ HandleWrapper<mirror::Object> h(
+ hs.NewHandleWrapper(LIKELY(save_ref == nullptr) ? &null_obj : save_ref->GetGCRoot()));
+ self->ClearException();
+ instrumentation->DexPcMovedEvent(self,
+ shadow_frame.GetThisObject(code_item->ins_size_),
+ shadow_frame.GetMethod(),
+ dex_pc);
+ if (UNLIKELY(self->IsExceptionPending())) {
+ // We got a new exception in the dex-pc-moved event. We just let this exception replace the old
+ // one.
+ // TODO It would be good to add the old exception to the suppressed exceptions of the new one if
+ // possible.
+ return false;
+ } else {
+ if (UNLIKELY(!thr.IsNull())) {
+ self->SetException(thr.Get());
+ }
+ return true;
+ }
+}
+
template<bool do_access_check, bool transaction_active>
JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
ShadowFrame& shadow_frame, JValue result_register,
@@ -198,7 +248,7 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item,
inst = inst->Next_1xx();
break;
case Instruction::MOVE_RESULT_OBJECT:
- PREAMBLE();
+ PREAMBLE_SAVE(&result_register);
shadow_frame.SetVRegReference(inst->VRegA_11x(inst_data), result_register.GetL());
inst = inst->Next_1xx();
break;
diff --git a/runtime/openjdkjvmti/Android.bp b/runtime/openjdkjvmti/Android.bp
index e38f265c5a..619a49aa71 100644
--- a/runtime/openjdkjvmti/Android.bp
+++ b/runtime/openjdkjvmti/Android.bp
@@ -27,6 +27,7 @@ cc_defaults {
"fixed_up_dex_file.cc",
"object_tagging.cc",
"OpenjdkJvmTi.cc",
+ "ti_breakpoint.cc",
"ti_class.cc",
"ti_class_definition.cc",
"ti_class_loader.cc",
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 0896210f1c..e3768b358f 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -48,6 +48,7 @@
#include "scoped_thread_state_change-inl.h"
#include "thread-current-inl.h"
#include "thread_list.h"
+#include "ti_breakpoint.h"
#include "ti_class.h"
#include "ti_dump.h"
#include "ti_field.h"
@@ -619,20 +620,17 @@ class JvmtiFunctions {
return ERR(NOT_IMPLEMENTED);
}
- static jvmtiError SetBreakpoint(jvmtiEnv* env,
- jmethodID method ATTRIBUTE_UNUSED,
- jlocation location ATTRIBUTE_UNUSED) {
+
+ static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
- return ERR(NOT_IMPLEMENTED);
+ return BreakpointUtil::SetBreakpoint(env, method, location);
}
- static jvmtiError ClearBreakpoint(jvmtiEnv* env,
- jmethodID method ATTRIBUTE_UNUSED,
- jlocation location ATTRIBUTE_UNUSED) {
+ static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location) {
ENSURE_VALID_ENV(env);
ENSURE_HAS_CAP(env, can_generate_breakpoint_events);
- return ERR(NOT_IMPLEMENTED);
+ return BreakpointUtil::ClearBreakpoint(env, method, location);
}
static jvmtiError SetFieldAccessWatch(jvmtiEnv* env, jclass klass, jfieldID field) {
diff --git a/runtime/openjdkjvmti/art_jvmti.h b/runtime/openjdkjvmti/art_jvmti.h
index b5f12191e6..2d5d527e68 100644
--- a/runtime/openjdkjvmti/art_jvmti.h
+++ b/runtime/openjdkjvmti/art_jvmti.h
@@ -34,6 +34,7 @@
#include <memory>
#include <type_traits>
+#include <unordered_map>
#include <unordered_set>
#include <jni.h>
@@ -46,10 +47,12 @@
#include "java_vm_ext.h"
#include "jni_env_ext.h"
#include "jvmti.h"
+#include "ti_breakpoint.h"
namespace art {
class ArtField;
-}
+class ArtMethod;
+} // namespace art
namespace openjdkjvmti {
@@ -76,6 +79,9 @@ struct ArtJvmTiEnv : public jvmtiEnv {
std::unordered_set<art::ArtField*> access_watched_fields;
std::unordered_set<art::ArtField*> modify_watched_fields;
+ // Set of breakpoints is unique to each jvmtiEnv.
+ std::unordered_set<Breakpoint> breakpoints;
+
ArtJvmTiEnv(art::JavaVMExt* runtime, EventHandler* event_handler);
static ArtJvmTiEnv* AsArtJvmTiEnv(jvmtiEnv* env) {
@@ -223,10 +229,10 @@ const jvmtiCapabilities kPotentialCapabilities = {
.can_get_source_debug_extension = 1,
.can_access_local_variables = 0,
.can_maintain_original_method_order = 0,
- .can_generate_single_step_events = 0,
+ .can_generate_single_step_events = 1,
.can_generate_exception_events = 0,
.can_generate_frame_pop_events = 0,
- .can_generate_breakpoint_events = 0,
+ .can_generate_breakpoint_events = 1,
.can_suspend = 0,
.can_redefine_any_class = 0,
.can_get_current_thread_cpu_time = 0,
diff --git a/runtime/openjdkjvmti/events-inl.h b/runtime/openjdkjvmti/events-inl.h
index af99233f90..f30d7cecb3 100644
--- a/runtime/openjdkjvmti/events-inl.h
+++ b/runtime/openjdkjvmti/events-inl.h
@@ -22,6 +22,7 @@
#include "events.h"
#include "jni_internal.h"
#include "ScopedLocalRef.h"
+#include "ti_breakpoint.h"
#include "art_jvmti.h"
@@ -217,6 +218,32 @@ inline void EventHandler::DispatchEvent(ArtJvmTiEnv* env, art::Thread* thread, A
}
}
+// Need to give custom specializations for Breakpoint since it needs to filter out which particular
+// methods/dex_pcs agents get notified on.
+template <>
+inline void EventHandler::DispatchEvent<ArtJvmtiEvent::kBreakpoint>(art::Thread* thread,
+ JNIEnv* jnienv,
+ jthread jni_thread,
+ jmethodID jmethod,
+ jlocation location) const {
+ art::ArtMethod* method = art::jni::DecodeArtMethod(jmethod);
+ for (ArtJvmTiEnv* env : envs) {
+ // Search for a breakpoint on this particular method and location.
+ if (env != nullptr &&
+ ShouldDispatch<ArtJvmtiEvent::kBreakpoint>(env, thread) &&
+ env->breakpoints.find({method, location}) != env->breakpoints.end()) {
+ // We temporarily clear any pending exceptions so the event can call back into java code.
+ ScopedLocalRef<jthrowable> thr(jnienv, jnienv->ExceptionOccurred());
+ jnienv->ExceptionClear();
+ auto callback = impl::GetCallback<ArtJvmtiEvent::kBreakpoint>(env);
+ (*callback)(env, jnienv, jni_thread, jmethod, location);
+ if (thr.get() != nullptr && !jnienv->ExceptionCheck()) {
+ jnienv->Throw(thr.get());
+ }
+ }
+ }
+}
+
// Need to give custom specializations for FieldAccess and FieldModification since they need to
// filter out which particular fields agents want to get notified on.
// TODO The spec allows us to do shortcuts like only allow one agent to ever set these watches. This
diff --git a/runtime/openjdkjvmti/events.cc b/runtime/openjdkjvmti/events.cc
index 989b9af591..f749daa918 100644
--- a/runtime/openjdkjvmti/events.cc
+++ b/runtime/openjdkjvmti/events.cc
@@ -423,14 +423,30 @@ class JvmtiMethodTraceListener FINAL : public art::instrumentation::Instrumentat
}
}
- // Call-back for when the dex pc moves in a method. We don't currently have any events associated
- // with this.
- void DexPcMoved(art::Thread* self ATTRIBUTE_UNUSED,
+ // Call-back for when the dex pc moves in a method.
+ void DexPcMoved(art::Thread* self,
art::Handle<art::mirror::Object> this_object ATTRIBUTE_UNUSED,
- art::ArtMethod* method ATTRIBUTE_UNUSED,
- uint32_t new_dex_pc ATTRIBUTE_UNUSED)
+ art::ArtMethod* method,
+ uint32_t new_dex_pc)
REQUIRES_SHARED(art::Locks::mutator_lock_) OVERRIDE {
- return;
+ DCHECK(!method->IsRuntimeMethod());
+ // Default methods might be copied to multiple classes. We need to get the canonical version of
+ // this method so that we can check for breakpoints correctly.
+ // TODO We should maybe do this on other events to ensure that we are consistent WRT default
+ // methods. This could interact with obsolete methods if we ever let interface redefinition
+ // happen though.
+ method = method->GetCanonicalMethod();
+ art::JNIEnvExt* jnienv = self->GetJniEnv();
+ jmethodID jmethod = art::jni::EncodeArtMethod(method);
+ jlocation location = static_cast<jlocation>(new_dex_pc);
+ // Step event is reported first according to the spec.
+ if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kSingleStep)) {
+ RunEventCallback<ArtJvmtiEvent::kSingleStep>(self, jnienv, jmethod, location);
+ }
+ // Next we do the Breakpoint events. The Dispatch code will filter the individual
+ if (event_handler_->IsEventEnabledAnywhere(ArtJvmtiEvent::kBreakpoint)) {
+ RunEventCallback<ArtJvmtiEvent::kBreakpoint>(self, jnienv, jmethod, location);
+ }
}
// Call-back for when we read from a field.
@@ -563,6 +579,9 @@ static uint32_t GetInstrumentationEventsFor(ArtJvmtiEvent event) {
return art::instrumentation::Instrumentation::kFieldWritten;
case ArtJvmtiEvent::kFieldAccess:
return art::instrumentation::Instrumentation::kFieldRead;
+ case ArtJvmtiEvent::kBreakpoint:
+ case ArtJvmtiEvent::kSingleStep:
+ return art::instrumentation::Instrumentation::kDexPcMoved;
default:
LOG(FATAL) << "Unknown event ";
return 0;
@@ -580,6 +599,8 @@ static void SetupTraceListener(JvmtiMethodTraceListener* listener,
art::gc::kCollectorTypeInstrumentation);
art::ScopedSuspendAll ssa("jvmti method tracing installation");
if (enable) {
+ // TODO Depending on the features being used we should be able to avoid deoptimizing everything
+ // like we do here.
if (!instr->AreAllMethodsDeoptimized()) {
instr->EnableMethodTracing("jvmti-tracing", /*needs_interpreter*/true);
}
@@ -601,6 +622,17 @@ void EventHandler::HandleEventType(ArtJvmtiEvent event, bool enable) {
SetupGcPauseTracking(gc_pause_listener_.get(), event, enable);
return;
+ case ArtJvmtiEvent::kBreakpoint:
+ case ArtJvmtiEvent::kSingleStep: {
+ ArtJvmtiEvent other = (event == ArtJvmtiEvent::kBreakpoint) ? ArtJvmtiEvent::kSingleStep
+ : ArtJvmtiEvent::kBreakpoint;
+ // We only need to do anything if there isn't already a listener installed/held-on by the
+ // other jvmti event that uses DexPcMoved.
+ if (!IsEventEnabledAnywhere(other)) {
+ SetupTraceListener(method_trace_listener_.get(), event, enable);
+ }
+ return;
+ }
case ArtJvmtiEvent::kMethodEntry:
case ArtJvmtiEvent::kMethodExit:
case ArtJvmtiEvent::kFieldAccess:
diff --git a/runtime/openjdkjvmti/ti_breakpoint.cc b/runtime/openjdkjvmti/ti_breakpoint.cc
new file mode 100644
index 0000000000..6d0e2c60c1
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_breakpoint.cc
@@ -0,0 +1,114 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#include <functional>
+
+#include "ti_breakpoint.h"
+
+#include "art_jvmti.h"
+#include "art_method-inl.h"
+#include "base/enums.h"
+#include "dex_file_annotations.h"
+#include "events-inl.h"
+#include "jni_internal.h"
+#include "mirror/class-inl.h"
+#include "mirror/object_array-inl.h"
+#include "modifiers.h"
+#include "runtime_callbacks.h"
+#include "scoped_thread_state_change-inl.h"
+#include "ScopedLocalRef.h"
+#include "thread-current-inl.h"
+#include "thread_list.h"
+#include "ti_phase.h"
+
+namespace openjdkjvmti {
+
+size_t Breakpoint::hash() const {
+ return std::hash<uintptr_t> {}(reinterpret_cast<uintptr_t>(method_))
+ ^ std::hash<jlocation> {}(location_);
+}
+
+Breakpoint::Breakpoint(art::ArtMethod* m, jlocation loc) : method_(m), location_(loc) {
+ DCHECK(!m->IsDefault() || !m->IsCopied() || !m->IsInvokable())
+ << "Flags are: 0x" << std::hex << m->GetAccessFlags();
+}
+
+void BreakpointUtil::RemoveBreakpointsInClass(ArtJvmTiEnv* env, art::mirror::Class* klass) {
+ std::vector<Breakpoint> to_remove;
+ for (const Breakpoint& b : env->breakpoints) {
+ if (b.GetMethod()->GetDeclaringClass() == klass) {
+ to_remove.push_back(b);
+ }
+ }
+ for (const Breakpoint& b : to_remove) {
+ auto it = env->breakpoints.find(b);
+ DCHECK(it != env->breakpoints.end());
+ env->breakpoints.erase(it);
+ }
+}
+
+jvmtiError BreakpointUtil::SetBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
+ ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ // Need to get mutator_lock_ so we can find the interface version of any default methods.
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method)->GetCanonicalMethod();
+ if (location < 0 || static_cast<uint32_t>(location) >=
+ art_method->GetCodeItem()->insns_size_in_code_units_) {
+ return ERR(INVALID_LOCATION);
+ }
+ auto res_pair = env->breakpoints.insert(/* Breakpoint */ {art_method, location});
+ if (!res_pair.second) {
+ // Didn't get inserted because it's already present!
+ return ERR(DUPLICATE);
+ }
+ return OK;
+}
+
+jvmtiError BreakpointUtil::ClearBreakpoint(jvmtiEnv* jenv, jmethodID method, jlocation location) {
+ ArtJvmTiEnv* env = ArtJvmTiEnv::AsArtJvmTiEnv(jenv);
+ if (method == nullptr) {
+ return ERR(INVALID_METHODID);
+ }
+ // Need to get mutator_lock_ so we can find the interface version of any default methods.
+ art::ScopedObjectAccess soa(art::Thread::Current());
+ auto pos = env->breakpoints.find(
+ /* Breakpoint */ {art::jni::DecodeArtMethod(method)->GetCanonicalMethod(), location});
+ if (pos == env->breakpoints.end()) {
+ return ERR(NOT_FOUND);
+ }
+ env->breakpoints.erase(pos);
+ return OK;
+}
+
+} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_breakpoint.h b/runtime/openjdkjvmti/ti_breakpoint.h
new file mode 100644
index 0000000000..c3dbef7baf
--- /dev/null
+++ b/runtime/openjdkjvmti/ti_breakpoint.h
@@ -0,0 +1,94 @@
+/* Copyright (C) 2017 The Android Open Source Project
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This file implements interfaces from the file jvmti.h. This implementation
+ * is licensed under the same terms as the file jvmti.h. The
+ * copyright and license information for the file jvmti.h follows.
+ *
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+#ifndef ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
+#define ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
+
+#include "jni.h"
+#include "jvmti.h"
+
+#include "base/mutex.h"
+
+namespace art {
+class ArtMethod;
+namespace mirror {
+class Class;
+} // namespace mirror
+} // namespace art
+
+namespace openjdkjvmti {
+
+struct ArtJvmTiEnv;
+
+class Breakpoint {
+ public:
+ Breakpoint(art::ArtMethod* m, jlocation loc);
+
+ // Get the hash code of this breakpoint.
+ size_t hash() const;
+
+ bool operator==(const Breakpoint& other) const {
+ return method_ == other.method_ && location_ == other.location_;
+ }
+
+ art::ArtMethod* GetMethod() const {
+ return method_;
+ }
+
+ jlocation GetLocation() const {
+ return location_;
+ }
+
+ private:
+ art::ArtMethod* method_;
+ jlocation location_;
+};
+
+class BreakpointUtil {
+ public:
+ static jvmtiError SetBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location);
+ static jvmtiError ClearBreakpoint(jvmtiEnv* env, jmethodID method, jlocation location);
+ // Used by class redefinition to remove breakpoints on redefined classes.
+ static void RemoveBreakpointsInClass(ArtJvmTiEnv* env, art::mirror::Class* klass)
+ REQUIRES(art::Locks::mutator_lock_);
+};
+
+} // namespace openjdkjvmti
+
+namespace std {
+template<> struct hash<openjdkjvmti::Breakpoint> {
+ size_t operator()(const openjdkjvmti::Breakpoint& b) const {
+ return b.hash();
+ }
+};
+
+} // namespace std
+#endif // ART_RUNTIME_OPENJDKJVMTI_TI_BREAKPOINT_H_
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index 5422f48664..debee913ee 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -64,6 +64,7 @@
#include "object_lock.h"
#include "runtime.h"
#include "ScopedLocalRef.h"
+#include "ti_breakpoint.h"
#include "ti_class_loader.h"
#include "transform.h"
#include "verifier/method_verifier.h"
@@ -380,7 +381,7 @@ jvmtiError Redefiner::RedefineClassesDirect(ArtJvmTiEnv* env,
art::jit::ScopedJitSuspend suspend_jit;
// Get shared mutator lock so we can lock all the classes.
art::ScopedObjectAccess soa(self);
- Redefiner r(runtime, self, error_msg);
+ Redefiner r(env, runtime, self, error_msg);
for (const ArtClassDefinition& def : definitions) {
// Only try to transform classes that have been modified.
if (def.IsModified()) {
@@ -1200,6 +1201,10 @@ bool Redefiner::ClassRedefinition::FinishRemainingAllocations(
return true;
}
+void Redefiner::ClassRedefinition::UnregisterJvmtiBreakpoints() {
+ BreakpointUtil::RemoveBreakpointsInClass(driver_->env_, GetMirrorClass());
+}
+
void Redefiner::ClassRedefinition::UnregisterBreakpoints() {
DCHECK(art::Dbg::IsDebuggerActive());
art::JDWP::JdwpState* state = art::Dbg::GetJdwpState();
@@ -1342,6 +1347,7 @@ jvmtiError Redefiner::Run() {
// TODO Rewrite so we don't do a stack walk for each and every class.
redef.FindAndAllocateObsoleteMethods(klass);
redef.UpdateClass(klass, data.GetNewDexCache(), data.GetOriginalDexFile());
+ redef.UnregisterJvmtiBreakpoints();
}
RestoreObsoleteMethodMapsIfUnneeded(holder);
// TODO We should check for if any of the redefined methods are intrinsic methods here and, if any
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index ec4a8b2789..27d7c3d726 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -199,6 +199,8 @@ class Redefiner {
void ReleaseDexFile() REQUIRES_SHARED(art::Locks::mutator_lock_);
void UnregisterBreakpoints() REQUIRES_SHARED(art::Locks::mutator_lock_);
+ // This should be done with all threads suspended.
+ void UnregisterJvmtiBreakpoints() REQUIRES(art::Locks::mutator_lock_);
private:
Redefiner* driver_;
@@ -208,6 +210,7 @@ class Redefiner {
art::ArrayRef<const unsigned char> original_dex_file_;
};
+ ArtJvmTiEnv* env_;
jvmtiError result_;
art::Runtime* runtime_;
art::Thread* self_;
@@ -216,10 +219,12 @@ class Redefiner {
// mirror::Class difficult and confusing.
std::string* error_msg_;
- Redefiner(art::Runtime* runtime,
+ Redefiner(ArtJvmTiEnv* env,
+ art::Runtime* runtime,
art::Thread* self,
std::string* error_msg)
- : result_(ERR(INTERNAL)),
+ : env_(env),
+ result_(ERR(INTERNAL)),
runtime_(runtime),
self_(self),
redefinitions_(),
diff --git a/test/593-checker-shift-and-simplifier/expected.txt b/test/593-checker-shift-and-simplifier/expected.txt
index b0aad4deb5..f8d85db6ac 100644
--- a/test/593-checker-shift-and-simplifier/expected.txt
+++ b/test/593-checker-shift-and-simplifier/expected.txt
@@ -1 +1,2 @@
passed
+passed
diff --git a/test/593-checker-shift-and-simplifier/smali/SmaliTests.smali b/test/593-checker-shift-and-simplifier/smali/SmaliTests.smali
new file mode 100644
index 0000000000..6b0d68306b
--- /dev/null
+++ b/test/593-checker-shift-and-simplifier/smali/SmaliTests.smali
@@ -0,0 +1,58 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LSmaliTests;
+.super Ljava/lang/Object;
+
+# A very particular set of operations that caused a double removal by the
+# ARM64 simplifier doing "forward" removals (b/27851582).
+
+## CHECK-START-ARM: int SmaliTests.operations() instruction_simplifier_arm (before)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
+## CHECK-DAG: And [<<Not>>,<<Shl>>]
+
+## CHECK-START-ARM: int SmaliTests.operations() instruction_simplifier_arm (after)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
+
+## CHECK-START-ARM64: int SmaliTests.operations() instruction_simplifier_arm64 (before)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
+## CHECK-DAG: And [<<Not>>,<<Shl>>]
+
+## CHECK-START-ARM64: int SmaliTests.operations() instruction_simplifier_arm64 (after)
+## CHECK-DAG: <<Get:i\d+>> ArrayGet
+## CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
+## CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
+.method public static operations()I
+ .registers 6
+ .prologue
+
+ # int r = a[0];
+ sget-object v4, LMain;->a:[I
+ const/4 v5, 0x0
+ aget v2, v4, v5
+ # int n = ~r;
+ not-int v1, v2
+ # int s = r << 2;
+ shl-int/lit8 v3, v2, 0x2
+ # int a = s & n;
+ and-int v0, v3, v1
+ # return a
+ return v0
+.end method
diff --git a/test/593-checker-shift-and-simplifier/src/Main.java b/test/593-checker-shift-and-simplifier/src/Main.java
index c9826bc546..f0ef0e6949 100644
--- a/test/593-checker-shift-and-simplifier/src/Main.java
+++ b/test/593-checker-shift-and-simplifier/src/Main.java
@@ -14,30 +14,20 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+
public class Main {
- private static int[] a = { 10 };
+ static int[] a = { 10 };
// A very particular set of operations that caused a double removal by the
// ARM64 simplifier doing "forward" removals (b/27851582).
- /// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (before)
- /// CHECK-DAG: <<Get:i\d+>> ArrayGet
- /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
- /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
- /// CHECK-DAG: And [<<Not>>,<<Shl>>]
- //
/// CHECK-START-ARM: int Main.operations() instruction_simplifier_arm (after)
/// CHECK-DAG: <<Get:i\d+>> ArrayGet
/// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
/// CHECK-DAG: DataProcWithShifterOp [<<Not>>,<<Get>>] kind:And+LSL shift:2
- /// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (before)
- /// CHECK-DAG: <<Get:i\d+>> ArrayGet
- /// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
- /// CHECK-DAG: <<Shl:i\d+>> Shl [<<Get>>,i{{\d+}}]
- /// CHECK-DAG: And [<<Not>>,<<Shl>>]
- //
/// CHECK-START-ARM64: int Main.operations() instruction_simplifier_arm64 (after)
/// CHECK-DAG: <<Get:i\d+>> ArrayGet
/// CHECK-DAG: <<Not:i\d+>> Not [<<Get>>]
@@ -56,5 +46,21 @@ public class Main {
} else {
System.out.println("passed");
}
+
+ if ($noinline$runSmaliTest("operations") != 32) {
+ System.out.println("failed");
+ } else {
+ System.out.println("passed");
+ }
+ }
+
+ public static int $noinline$runSmaliTest(String name) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name);
+ return (Integer) m.invoke(null);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
}
}
diff --git a/test/633-checker-rtp-getclass/build b/test/633-checker-rtp-getclass/build
new file mode 100755
index 0000000000..49292c9ac1
--- /dev/null
+++ b/test/633-checker-rtp-getclass/build
@@ -0,0 +1,23 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# This checker test is incompatible with jack bytecode output,
+# so force it to use javac/dx.
+export USE_JACK=false
+# Also disable desugar because it is missing in jack platform builds.
+export DESUGAR=false
+
+./default-build "$@"
diff --git a/test/633-checker-rtp-getclass/smali/SmaliTests.smali b/test/633-checker-rtp-getclass/smali/SmaliTests.smali
new file mode 100644
index 0000000000..9ea04498d7
--- /dev/null
+++ b/test/633-checker-rtp-getclass/smali/SmaliTests.smali
@@ -0,0 +1,65 @@
+# Copyright (C) 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+.class public LSmaliTests;
+.super Ljava/lang/Object;
+
+# Checker test to make sure the only inlined instruction is SubMain.bar.
+
+## CHECK-START: int SmaliTests.$opt$noinline$foo(Main) inliner (after)
+## CHECK-DAG: InvokeVirtual method_name:Main.foo
+## CHECK-DAG: <<Const:i\d+>> IntConstant 3
+## CHECK: begin_block
+## CHECK: BoundType klass:SubMain
+## CHECK: Return [<<Const>>]
+## CHECK-NOT: begin_block
+## CHECK: end_block
+.method public static $opt$noinline$foo(LMain;)I
+ .registers 3
+ .param p0, "o" # LMain;
+ .prologue
+
+ # if (doThrow) { throw new Error(); }
+ sget-boolean v0, LMain;->doThrow:Z
+ if-eqz v0, :doThrow_false
+ new-instance v0, Ljava/lang/Error;
+ invoke-direct {v0}, Ljava/lang/Error;-><init>()V
+ throw v0
+
+ :doThrow_false
+ # if (o.getClass() == Main.class || o.getClass() != SubMain.class)
+ invoke-virtual {p0}, LMain;->getClass()Ljava/lang/Class;
+ move-result-object v0
+ const-class v1, LMain;
+ if-eq v0, v1, :class_is_Main_and_not_SubMain
+
+ invoke-virtual {p0}, LMain;->getClass()Ljava/lang/Class;
+ move-result-object v0
+ const-class v1, LSubMain;
+ if-eq v0, v1, :else
+
+ :class_is_Main_and_not_SubMain
+ # return o.foo()
+ invoke-virtual {p0}, LMain;->foo()I
+ move-result v0
+ return v0
+
+ :else
+ # return o.bar()
+ invoke-virtual {p0}, LMain;->bar()I
+ move-result v0
+ return v0
+.end method
+
+
diff --git a/test/633-checker-rtp-getclass/src/Main.java b/test/633-checker-rtp-getclass/src/Main.java
index f29c139f63..d1145af2a5 100644
--- a/test/633-checker-rtp-getclass/src/Main.java
+++ b/test/633-checker-rtp-getclass/src/Main.java
@@ -14,34 +14,13 @@
* limitations under the License.
*/
+import java.lang.reflect.Method;
+
public class Main {
public static void main(String[] args) {
- System.out.println($opt$noinline$foo(new Main()));
- System.out.println($opt$noinline$foo(new SubMain()));
- System.out.println($opt$noinline$foo(new SubSubMain()));
- }
-
-
- // Checker test to make sure the only inlined instruction is
- // SubMain.bar.
- /// CHECK-START: int Main.$opt$noinline$foo(Main) inliner (after)
- /// CHECK-DAG: InvokeVirtual method_name:Main.foo
- /// CHECK-DAG: <<Const:i\d+>> IntConstant 3
- /// CHECK: begin_block
- /// CHECK: BoundType klass:SubMain
- /// CHECK: Return [<<Const>>]
- /// CHECK-NOT: begin_block
- /// CHECK: end_block
- public static int $opt$noinline$foo(Main o) {
- if (doThrow) { throw new Error(); }
- // To exercise the bug on Jack, we need two getClass compares.
- if (o.getClass() == Main.class || o.getClass() != SubMain.class) {
- return o.foo();
- } else {
- // We used to wrongly bound the type of o to `Main` here and then realize that's
- // impossible and mark this branch as dead.
- return o.bar();
- }
+ System.out.println($noinline$runSmaliTest("$opt$noinline$foo", new Main()));
+ System.out.println($noinline$runSmaliTest("$opt$noinline$foo", new SubMain()));
+ System.out.println($noinline$runSmaliTest("$opt$noinline$foo", new SubSubMain()));
}
public int bar() {
@@ -53,6 +32,16 @@ public class Main {
}
public static boolean doThrow = false;
+
+ public static int $noinline$runSmaliTest(String name, Main input) {
+ try {
+ Class<?> c = Class.forName("SmaliTests");
+ Method m = c.getMethod(name, Main.class);
+ return (Integer) m.invoke(null, input);
+ } catch (Exception ex) {
+ throw new Error(ex);
+ }
+ }
}
class SubMain extends Main {
diff --git a/test/656-checker-simd-opt/expected.txt b/test/656-checker-simd-opt/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/656-checker-simd-opt/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/656-checker-simd-opt/info.txt b/test/656-checker-simd-opt/info.txt
new file mode 100644
index 0000000000..185d2b1b95
--- /dev/null
+++ b/test/656-checker-simd-opt/info.txt
@@ -0,0 +1 @@
+Tests around optimizations of SIMD code.
diff --git a/test/656-checker-simd-opt/src/Main.java b/test/656-checker-simd-opt/src/Main.java
new file mode 100644
index 0000000000..0d0885c85a
--- /dev/null
+++ b/test/656-checker-simd-opt/src/Main.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Tests for SIMD related optimizations.
+ */
+public class Main {
+
+ /// CHECK-START: void Main.unroll(float[], float[]) loop_optimization (before)
+ /// CHECK-DAG: <<Cons:f\d+>> FloatConstant 2.5 loop:none
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get:f\d+>> ArrayGet loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Mul:f\d+>> Mul [<<Get>>,<<Cons>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Mul>>] loop:<<Loop>> outer_loop:none
+ //
+ /// CHECK-START-ARM64: void Main.unroll(float[], float[]) loop_optimization (after)
+ /// CHECK-DAG: <<Cons:f\d+>> FloatConstant 2.5 loop:none
+ /// CHECK-DAG: <<Incr:i\d+>> IntConstant 4 loop:none
+ /// CHECK-DAG: <<Repl:d\d+>> VecReplicateScalar [<<Cons>>] loop:none
+ /// CHECK-NOT: VecReplicateScalar
+ /// CHECK-DAG: <<Phi:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Mul1:d\d+>> VecMul [<<Get1>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Mul1>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Phi>>,<<Incr>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Add>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Mul2:d\d+>> VecMul [<<Get2>>,<<Repl>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: VecStore [{{l\d+}},<<Add>>,<<Mul2>>] loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Add [<<Add>>,<<Incr>>] loop:<<Loop>> outer_loop:none
+ private static void unroll(float[] x, float[] y) {
+ for (int i = 0; i < 100; i++) {
+ x[i] = y[i] * 2.5f;
+ }
+ }
+
+ public static void main(String[] args) {
+ float[] x = new float[100];
+ float[] y = new float[100];
+ for (int i = 0; i < 100; i++) {
+ x[i] = 0.0f;
+ y[i] = 2.0f;
+ }
+ unroll(x, y);
+ for (int i = 0; i < 100; i++) {
+ expectEquals(5.0f, x[i]);
+ expectEquals(2.0f, y[i]);
+ }
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/988-method-trace/expected.txt b/test/988-method-trace/expected.txt
index 30ad532f6c..8e42a48865 100644
--- a/test/988-method-trace/expected.txt
+++ b/test/988-method-trace/expected.txt
@@ -1,4 +1,4 @@
-.<= public static native void art.Trace.enableTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
+.<= public static native void art.Trace.enableTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
<= public static void art.Trace.enableMethodTracing(java.lang.Class,java.lang.reflect.Method,java.lang.reflect.Method,java.lang.Thread) -> <null: null>
=> art.Test988$IterOp()
.=> public java.lang.Object()
@@ -143,11 +143,11 @@ fibonacci(5)=5
......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- at art.Test988.iter_fibonacci(Test988.java:209)
- at art.Test988$IterOp.applyAsInt(Test988.java:204)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:267)
- at Main.main(Main.java:19)
+ art.Test988.iter_fibonacci(Test988.java:228)
+ art.Test988$IterOp.applyAsInt(Test988.java:223)
+ art.Test988.doFibTest(Test988.java:316)
+ art.Test988.run(Test988.java:286)
+ <additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
...<= public java.lang.Error(java.lang.String) -> <null: null>
@@ -163,11 +163,11 @@ fibonacci(5)=5
...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- at art.Test988.iter_fibonacci(Test988.java:209)
- at art.Test988$IterOp.applyAsInt(Test988.java:204)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:267)
- at Main.main(Main.java:19)
+ art.Test988.iter_fibonacci(Test988.java:228)
+ art.Test988$IterOp.applyAsInt(Test988.java:223)
+ art.Test988.doFibTest(Test988.java:316)
+ art.Test988.run(Test988.java:286)
+ <additional hidden frames>
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
@@ -244,11 +244,11 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
......=> private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace()
......<= private static native java.lang.Object java.lang.Throwable.nativeFillInStackTrace() -> <class [Ljava.lang.Object;: <non-deterministic>>
.....<= public synchronized java.lang.Throwable java.lang.Throwable.fillInStackTrace() -> <class java.lang.Error: java.lang.Error: Bad argument: -19 < 0
- at art.Test988.fibonacci(Test988.java:231)
- at art.Test988$RecurOp.applyAsInt(Test988.java:226)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:268)
- at Main.main(Main.java:19)
+ art.Test988.fibonacci(Test988.java:250)
+ art.Test988$RecurOp.applyAsInt(Test988.java:245)
+ art.Test988.doFibTest(Test988.java:316)
+ art.Test988.run(Test988.java:287)
+ <additional hidden frames>
>
....<= public java.lang.Throwable(java.lang.String) -> <null: null>
...<= public java.lang.Error(java.lang.String) -> <null: null>
@@ -264,14 +264,14 @@ fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
...<= private void java.util.ArrayList.ensureExplicitCapacity(int) -> <null: null>
..<= private void java.util.ArrayList.ensureCapacityInternal(int) -> <null: null>
fibonacci(-19) -> java.lang.Error: Bad argument: -19 < 0
- at art.Test988.fibonacci(Test988.java:231)
- at art.Test988$RecurOp.applyAsInt(Test988.java:226)
- at art.Test988.doFibTest(Test988.java:297)
- at art.Test988.run(Test988.java:268)
- at Main.main(Main.java:19)
+ art.Test988.fibonacci(Test988.java:250)
+ art.Test988$RecurOp.applyAsInt(Test988.java:245)
+ art.Test988.doFibTest(Test988.java:316)
+ art.Test988.run(Test988.java:287)
+ <additional hidden frames>
.<= public boolean java.util.ArrayList.add(java.lang.Object) -> <class java.lang.Boolean: true>
<= public static void art.Test988.doFibTest(int,java.util.function.IntUnaryOperator) -> <null: null>
=> public static native java.lang.Thread java.lang.Thread.currentThread()
-<= public static native java.lang.Thread java.lang.Thread.currentThread() -> <class java.lang.Thread: <non-deterministic>>
+<= public static native java.lang.Thread java.lang.Thread.currentThread() -> <<non-deterministic>: <non-deterministic>>
=> public static native void art.Trace.disableTracing(java.lang.Thread)
diff --git a/test/988-method-trace/src/art/Test988.java b/test/988-method-trace/src/art/Test988.java
index 6a45c0eaa2..e40c612851 100644
--- a/test/988-method-trace/src/art/Test988.java
+++ b/test/988-method-trace/src/art/Test988.java
@@ -31,6 +31,7 @@ public class Test988 {
// Methods with non-deterministic output that should not be printed.
static Set<Method> NON_DETERMINISTIC_OUTPUT_METHODS = new HashSet<>();
+ static Set<Method> NON_DETERMINISTIC_OUTPUT_TYPE_METHODS = new HashSet<>();
static {
try {
@@ -39,6 +40,7 @@ public class Test988 {
} catch (Exception e) {}
try {
NON_DETERMINISTIC_OUTPUT_METHODS.add(Thread.class.getDeclaredMethod("currentThread"));
+ NON_DETERMINISTIC_OUTPUT_TYPE_METHODS.add(Thread.class.getDeclaredMethod("currentThread"));
} catch (Exception e) {}
}
@@ -66,7 +68,16 @@ public class Test988 {
return arrayToString(val);
} else if (val instanceof Throwable) {
StringWriter w = new StringWriter();
- ((Throwable) val).printStackTrace(new PrintWriter(w));
+ Throwable thr = ((Throwable) val);
+ w.write(thr.getClass().getName() + ": " + thr.getMessage() + "\n");
+ for (StackTraceElement e : thr.getStackTrace()) {
+ if (e.getClassName().startsWith("art.")) {
+ w.write("\t" + e + "\n");
+ } else {
+ w.write("\t<additional hidden frames>\n");
+ break;
+ }
+ }
return w.toString();
} else {
return val.toString();
@@ -134,8 +145,16 @@ public class Test988 {
if (val != null) {
klass = val.getClass();
}
+ String klass_print;
+ if (klass == null) {
+ klass_print = "null";
+ } else if (NON_DETERMINISTIC_OUTPUT_TYPE_METHODS.contains(m)) {
+ klass_print = "<non-deterministic>";
+ } else {
+ klass_print = klass.toString();
+ }
System.out.println(
- whitespace(cnt) + "<= " + m + " -> <" + klass + ": " + print + ">");
+ whitespace(cnt) + "<= " + m + " -> <" + klass_print + ": " + print + ">");
}
}
diff --git a/test/988-method-trace/src/art/Trace.java b/test/988-method-trace/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/988-method-trace/src/art/Trace.java
+++ b/test/988-method-trace/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/989-method-trace-throw/src/art/Trace.java b/test/989-method-trace-throw/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/989-method-trace-throw/src/art/Trace.java
+++ b/test/989-method-trace-throw/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/990-field-trace/src/art/Trace.java b/test/990-field-trace/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/990-field-trace/src/art/Trace.java
+++ b/test/990-field-trace/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/991-field-trace-2/src/art/Trace.java b/test/991-field-trace-2/src/art/Trace.java
index 9c27c9f69e..ba3d397b0b 100644
--- a/test/991-field-trace-2/src/art/Trace.java
+++ b/test/991-field-trace-2/src/art/Trace.java
@@ -25,6 +25,7 @@ public class Trace {
Method exitMethod,
Method fieldAccess,
Method fieldModify,
+ Method singleStep,
Thread thr);
public static native void disableTracing(Thread thr);
@@ -32,14 +33,20 @@ public class Trace {
Method fieldAccess,
Method fieldModify,
Thread thr) {
- enableTracing(methodClass, null, null, fieldAccess, fieldModify, thr);
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
}
public static void enableMethodTracing(Class<?> methodClass,
Method entryMethod,
Method exitMethod,
Thread thr) {
- enableTracing(methodClass, entryMethod, exitMethod, null, null, thr);
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
}
public static native void watchFieldAccess(Field f);
diff --git a/test/992-source-data/expected.txt b/test/992-source-data/expected.txt
index 480d8a4fe7..4db8df0ada 100644
--- a/test/992-source-data/expected.txt
+++ b/test/992-source-data/expected.txt
@@ -1,6 +1,6 @@
class art.Test992 is defined in file "Test992.java"
class art.Test992$Target1 is defined in file "Test992.java"
-class art.Test2 is defined in file "Test2.java"
+class art.Target2 is defined in file "Target2.java"
int does not have a known source file because java.lang.RuntimeException: JVMTI_ERROR_ABSENT_INFORMATION
class java.lang.Integer is defined in file "Integer.java"
class java.lang.Object is defined in file "Object.java"
diff --git a/test/992-source-data/src/art/Test2.java b/test/992-source-data/src/art/Target2.java
index dbb1089c5e..7c29d88871 100644
--- a/test/992-source-data/src/art/Test2.java
+++ b/test/992-source-data/src/art/Target2.java
@@ -16,4 +16,4 @@
package art;
-public class Test2 {}
+public class Target2 {}
diff --git a/test/992-source-data/src/art/Test992.java b/test/992-source-data/src/art/Test992.java
index db6ea73856..d9ab112726 100644
--- a/test/992-source-data/src/art/Test992.java
+++ b/test/992-source-data/src/art/Test992.java
@@ -25,7 +25,7 @@ public class Test992 {
public static void run() {
doTest(Test992.class);
doTest(Target1.class);
- doTest(Test2.class);
+ doTest(Target2.class);
doTest(Integer.TYPE);
doTest(Integer.class);
doTest(Object.class);
diff --git a/test/993-breakpoints/breakpoints.cc b/test/993-breakpoints/breakpoints.cc
new file mode 100644
index 0000000000..129207098d
--- /dev/null
+++ b/test/993-breakpoints/breakpoints.cc
@@ -0,0 +1,69 @@
+/*
+ * 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 <inttypes.h>
+#include <memory>
+#include <stdio.h>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jni.h"
+#include "jvmti.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
+
+namespace art {
+namespace Test993Breakpoints {
+
+extern "C" JNIEXPORT
+jobject JNICALL Java_art_Test993_constructNative(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject target,
+ jclass clazz) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ return env->NewObject(clazz, method);
+}
+
+extern "C" JNIEXPORT
+void JNICALL Java_art_Test993_invokeNative(JNIEnv* env,
+ jclass klass ATTRIBUTE_UNUSED,
+ jobject target,
+ jclass clazz,
+ jobject thizz) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ if (thizz == nullptr) {
+ env->CallStaticVoidMethod(clazz, method);
+ } else {
+ env->CallVoidMethod(thizz, method);
+ }
+}
+
+} // namespace Test993Breakpoints
+} // namespace art
+
diff --git a/test/993-breakpoints/expected.txt b/test/993-breakpoints/expected.txt
new file mode 100644
index 0000000000..962154734b
--- /dev/null
+++ b/test/993-breakpoints/expected.txt
@@ -0,0 +1,613 @@
+Running static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Invoking "Test993::breakpoint"
+ Breaking on [public static void art.Test993.breakpoint() @ 41]
+ Native invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Reflective invoking: public static void art.Test993.breakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+ Invoking "Test993::breakpoint"
+ Breakpoint: public static void art.Test993.breakpoint() @ line=41
+Running private static invoke
+ Breaking on []
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Invoking "Test993::privateBreakpoint"
+ Breaking on [private static void art.Test993.privateBreakpoint() @ 45]
+ Native invoking: private static void art.Test993.privateBreakpoint() args: [this: null]
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+ Invoking "Test993::privateBreakpoint"
+ Breakpoint: private static void art.Test993.privateBreakpoint() @ line=45
+Running interface static invoke
+ Breaking on []
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Invoking "Breakable::iBreakpoint"
+ Breaking on [public static void art.Test993$Breakable.iBreakpoint() @ 51]
+ Native invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Reflective invoking: public static void art.Test993$Breakable.iBreakpoint() args: [this: null]
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+ Invoking "Breakable::iBreakpoint"
+ Breakpoint: public static void art.Test993$Breakable.iBreakpoint() @ line=51
+Running TestClass1 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Invoking "new TestClass1().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass1ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Invoking "new TestClass1ext().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass1ext.breakit() @ 74]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass1ext.breakit() args: [this: TestClass1Ext]
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass1)new TestClass1ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass1ext().breakit()"
+ Breakpoint: public void art.Test993$TestClass1ext.breakit() @ line=74
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass2 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Invoking "new TestClass2().breakit()"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2().breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass2ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Invoking "new TestClass2ext().breakit())"
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breaking on [public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass2.breakit() @ 83, public void art.Test993$TestClass2ext.breakit() @ 91]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Native invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Reflective invoking: public void art.Test993$TestClass2ext.breakit() args: [this: TestClass2ext]
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((Breakable)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "((TestClass2)new TestClass2ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+ Invoking "new TestClass2ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass2ext.breakit() @ line=91
+ Breakpoint: public void art.Test993$TestClass2.breakit() @ line=83
+Running TestClass3 invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Invoking "new TestClass3().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running TestClass3ext invokes
+ Breaking on []
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Invoking "new TestClass3ext().breakit())"
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Breaking on [public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breaking on [public default void art.Test993$Breakable.breakit() @ 55, public void art.Test993$TestClass3.breakit() @ 99, public void art.Test993$TestClass3ext.breakit() @ 108]
+ Native invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Native invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public default void art.Test993$Breakable.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Reflective invoking: public void art.Test993$TestClass3ext.breakit() args: [this: TestClass3ext]
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((Breakable)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "((TestClass3)new TestClass3ext()).breakit()"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+ Invoking "new TestClass3ext().breakit())"
+ Breakpoint: public void art.Test993$TestClass3ext.breakit() @ line=108
+ Breakpoint: public void art.Test993$TestClass3.breakit() @ line=99
+ Breakpoint: public default void art.Test993$Breakable.breakit() @ line=55
+Running private instance invoke
+ Breaking on []
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breaking on [private void art.Test993$TestClass4.privateMethod() @ 118]
+ Native invoking: private void art.Test993$TestClass4.privateMethod() args: [this: TestClass4]
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+ Invoking "new TestClass4().callPrivateMethod()"
+ Breakpoint: private void art.Test993$TestClass4.privateMethod() @ line=118
+Running TestClass1 constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Created: TestClass1
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1(), type: class art.Test993$TestClass1
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Reflective constructor: public art.Test993$TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+ Constructing: new TestClass1()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1
+Running TestClass1ext constructor
+ Breaking on []
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Created: TestClass1Ext
+ Breaking on [public art.Test993$TestClass1() @ 62, public art.Test993$TestClass1ext() @ 70]
+ Native constructor: public art.Test993$TestClass1ext(), type: class art.Test993$TestClass1ext
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Reflective constructor: public art.Test993$TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
+ Constructing: new TestClass1ext()
+ Breakpoint: public art.Test993$TestClass1ext() @ line=70
+ Breakpoint: public art.Test993$TestClass1() @ line=62
+ Created: TestClass1Ext
diff --git a/test/993-breakpoints/info.txt b/test/993-breakpoints/info.txt
new file mode 100644
index 0000000000..b5eb5464b7
--- /dev/null
+++ b/test/993-breakpoints/info.txt
@@ -0,0 +1,7 @@
+Test basic JVMTI breakpoint functionality.
+
+This test places a breakpoint on the first instruction of a number of functions
+that are entered in every way possible for the given class of method.
+
+It also tests that breakpoints don't interfere with each other by having
+multiple breakpoints be set at once.
diff --git a/test/993-breakpoints/run b/test/993-breakpoints/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/993-breakpoints/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/993-breakpoints/src/Main.java b/test/993-breakpoints/src/Main.java
new file mode 100644
index 0000000000..b11f6f8174
--- /dev/null
+++ b/test/993-breakpoints/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test993.run();
+ }
+}
diff --git a/test/993-breakpoints/src/art/Breakpoint.java b/test/993-breakpoints/src/art/Breakpoint.java
new file mode 100644
index 0000000000..2a370ebd40
--- /dev/null
+++ b/test/993-breakpoints/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/993-breakpoints/src/art/Test993.java b/test/993-breakpoints/src/art/Test993.java
new file mode 100644
index 0000000000..781ebffc0f
--- /dev/null
+++ b/test/993-breakpoints/src/art/Test993.java
@@ -0,0 +1,498 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Set;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.function.IntUnaryOperator;
+import java.util.function.Supplier;
+
+public class Test993 {
+
+ public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
+
+ // A function we can use as a start breakpoint.
+ public static void breakpoint() {
+ return;
+ }
+
+ private static void privateBreakpoint() {
+ return;
+ }
+
+ // An interface with a default method we can break on.
+ static interface Breakable {
+ public static void iBreakpoint() {
+ return;
+ }
+
+ public default void breakit() {
+ return;
+ }
+ }
+
+ // A class that has a default method we breakpoint on.
+ public static class TestClass1 implements Breakable {
+ public TestClass1() {
+ super();
+ }
+ public String toString() { return "TestClass1"; }
+ }
+
+ // A class that overrides a default method that we can breakpoint on and calls super.
+ public static class TestClass1ext extends TestClass1 {
+ public TestClass1ext() {
+ super();
+ }
+ public String toString() { return "TestClass1Ext"; }
+ public void breakit() {
+ super.breakit();
+ }
+ }
+
+
+ // A class that overrides a default method that we can breakpoint on.
+ public static class TestClass2 implements Breakable {
+ public String toString() { return "TestClass2"; }
+ public void breakit() {
+ return;
+ }
+ }
+
+ // A class that overrides a default method that we can breakpoint on and calls super.
+ public static class TestClass2ext extends TestClass2 {
+ public String toString() { return "TestClass2ext"; }
+ public void breakit() {
+ super.breakit();
+ }
+ }
+
+ // A class that overrides a default method and calls it directly with interface invoke-super
+ public static class TestClass3 implements Breakable {
+ public String toString() { return "TestClass3"; }
+ public void breakit() {
+ Breakable.super.breakit();
+ }
+ }
+
+ // A class that overrides a default method that we can breakpoint on and calls super to a class
+ // that uses interface-invoke-super.
+ public static class TestClass3ext extends TestClass3 {
+ public String toString() { return "TestClass3ext"; }
+ public void breakit() {
+ super.breakit();
+ }
+ }
+
+ public static class TestClass4 {
+ public String toString() { return "TestClass4"; }
+ public void callPrivateMethod() {
+ privateMethod();
+ }
+ private void privateMethod() {
+ return;
+ }
+ }
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ System.out.println("\t\t\tBreakpoint: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ }
+
+ public static interface ThrowRunnable extends Runnable {
+ public default void run() {
+ try {
+ runThrow();
+ } catch (Exception e) {
+ throw new Error("Caught error while running " + this, e);
+ }
+ }
+ public void runThrow() throws Exception;
+ }
+
+ public static class InvokeDirect implements Runnable {
+ String msg;
+ Runnable r;
+ public InvokeDirect(String msg, Runnable r) {
+ this.msg = msg;
+ this.r = r;
+ }
+ @Override
+ public void run() {
+ System.out.println("\t\tInvoking \"" + msg + "\"");
+ r.run();
+ }
+ }
+
+ public static class InvokeReflect implements ThrowRunnable {
+ Method m;
+ Object this_arg;
+ public InvokeReflect(Method m, Object this_arg) {
+ this.m = m;
+ this.this_arg = this_arg;
+ }
+
+ @Override
+ public void runThrow() throws Exception {
+ System.out.println("\t\tReflective invoking: " + m + " args: [this: " + this_arg + "]");
+ m.invoke(this_arg);
+ }
+ }
+
+ public static class InvokeNative implements Runnable {
+ Method m;
+ Object this_arg;
+ public InvokeNative(Method m, Object this_arg) {
+ this.m = m;
+ this.this_arg = this_arg;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tNative invoking: " + m + " args: [this: " + this_arg + "]");
+ invokeNative(m, m.getDeclaringClass(), this_arg);
+ }
+ }
+
+ public static native void invokeNative(Method m, Class<?> clazz, Object thizz);
+
+ public static class ConstructDirect implements Runnable {
+ String msg;
+ Supplier<Object> s;
+ public ConstructDirect(String msg, Supplier<Object> s) {
+ this.msg = msg;
+ this.s = s;
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tConstructing: " + msg);
+ System.out.println("\t\t\tCreated: " + s.get());
+ }
+ }
+
+ public static class ConstructReflect implements ThrowRunnable {
+ Constructor<?> m;
+ public ConstructReflect(Constructor<?> m) {
+ this.m = m;
+ }
+
+ @Override
+ public void runThrow() throws Exception {
+ System.out.println("\t\tReflective constructor: " + m);
+ System.out.println("\t\t\tCreated: " + m.newInstance());
+ }
+ }
+
+ public static class ConstructNative implements Runnable {
+ Constructor<?> m;
+ Class type;
+ public ConstructNative(Constructor<?> m) {
+ this.m = m;
+ this.type = m.getDeclaringClass();
+ }
+
+ @Override
+ public void run() {
+ System.out.println("\t\tNative constructor: " + m + ", type: " + type);
+ System.out.println("\t\t\tCreated: " + constructNative(m, type));
+ }
+ }
+
+ public static native Object constructNative(Constructor m, Class<?> clazz);
+
+ private static <T> List<List<T>> combinations(List<T> items, int len) {
+ if (len > items.size()) {
+ throw new Error("Bad length" + len + " " + items);
+ }
+ if (len == 1) {
+ List<List<T>> out = new ArrayList<>();
+ for (T t : items) {
+ out.add(Arrays.asList(t));
+ }
+ return out;
+ }
+ List<List<T>> out = new ArrayList<>();
+ for (int rem = 0; rem <= items.size() - len; rem++) {
+ for (List<T> others : combinations(items.subList(rem + 1, items.size()), len - 1)) {
+ List<T> newone = new ArrayList<>();
+ newone.add(items.get(rem));
+ newone.addAll(others);
+ out.add(newone);
+ }
+ }
+ return out;
+ }
+
+ private static <T> List<List<T>> allCombinations(List<T> items) {
+ List<List<T>> out = new ArrayList<List<T>>();
+ out.add(new ArrayList<>());
+ for (int i = 0; i < items.size(); i++) {
+ out.addAll(combinations(items, i + 1));
+ }
+ return out;
+ }
+
+ private static Breakpoint.Manager.BP BP(Executable m) {
+ return new Breakpoint.Manager.BP(m);
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test993.class,
+ Test993.class.getDeclaredMethod("notifyBreakpointReached",
+ Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ runMethodTests();
+ runConstructorTests();
+
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+
+ public static void runConstructorTests() throws Exception {
+ // The constructors we will be breaking on.
+ Constructor<?> tc1_construct = TestClass1.class.getConstructor();
+ Constructor<?> tc1ext_construct = TestClass1ext.class.getConstructor();
+
+ Runnable[] tc1_constructors = new Runnable[] {
+ new ConstructNative(tc1_construct),
+ new ConstructReflect(tc1_construct),
+ new ConstructDirect("new TestClass1()", TestClass1::new),
+ };
+ Breakpoint.Manager.BP[] tc1_bps = new Breakpoint.Manager.BP[] {
+ BP(tc1_construct),
+ };
+ runTestGroups("TestClass1 constructor", tc1_constructors, tc1_bps);
+
+ Runnable[] tc1ext_constructors = new Runnable[] {
+ new ConstructNative(tc1ext_construct),
+ new ConstructReflect(tc1ext_construct),
+ new ConstructDirect("new TestClass1ext()", TestClass1ext::new),
+ };
+ Breakpoint.Manager.BP[] tc1ext_bps = new Breakpoint.Manager.BP[] {
+ BP(tc1_construct), BP(tc1ext_construct),
+ };
+ runTestGroups("TestClass1ext constructor", tc1ext_constructors, tc1ext_bps);
+ }
+
+ public static void runMethodTests() throws Exception {
+ // The methods we will be breaking on.
+ Method breakpoint_method = Test993.class.getDeclaredMethod("breakpoint");
+ Method private_breakpoint_method = Test993.class.getDeclaredMethod("privateBreakpoint");
+ Method i_breakpoint_method = Breakable.class.getDeclaredMethod("iBreakpoint");
+ Method breakit_method = Breakable.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc1ext = TestClass1ext.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc2 = TestClass2.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc2ext = TestClass2ext.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc3 = TestClass3.class.getDeclaredMethod("breakit");
+ Method breakit_method_tc3ext = TestClass3ext.class.getDeclaredMethod("breakit");
+ Method private_method = TestClass4.class.getDeclaredMethod("privateMethod");
+
+ // Static class function
+ Runnable[] static_invokes = new Runnable[] {
+ new InvokeNative(breakpoint_method, null),
+
+ new InvokeReflect(breakpoint_method, null),
+
+ new InvokeDirect("Test993::breakpoint", Test993::breakpoint),
+ };
+ Breakpoint.Manager.BP[] static_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakpoint_method)
+ };
+ runTestGroups("static invoke", static_invokes, static_breakpoints);
+
+ // Static private class function
+ Runnable[] private_static_invokes = new Runnable[] {
+ new InvokeNative(private_breakpoint_method, null),
+
+ new InvokeDirect("Test993::privateBreakpoint", Test993::privateBreakpoint),
+ };
+ Breakpoint.Manager.BP[] private_static_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(private_breakpoint_method)
+ };
+ runTestGroups("private static invoke", private_static_invokes, private_static_breakpoints);
+
+ // Static interface function.
+ Runnable[] i_static_invokes = new Runnable[] {
+ new InvokeNative(i_breakpoint_method, null),
+
+ new InvokeReflect(i_breakpoint_method, null),
+
+ new InvokeDirect("Breakable::iBreakpoint", Breakable::iBreakpoint),
+ };
+ Breakpoint.Manager.BP[] i_static_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(i_breakpoint_method)
+ };
+ runTestGroups("interface static invoke", i_static_invokes, i_static_breakpoints);
+
+ // Call default method through a class.
+ Runnable[] tc1_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass1()),
+
+ new InvokeReflect(breakit_method, new TestClass1()),
+
+ new InvokeDirect("((Breakable)new TestClass1()).breakit()",
+ () -> ((Breakable)new TestClass1()).breakit()),
+ new InvokeDirect("new TestClass1().breakit()",
+ () -> new TestClass1().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc1_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method)
+ };
+ runTestGroups("TestClass1 invokes", tc1_invokes, tc1_breakpoints);
+
+ // Call default method through an override and normal invoke-super
+ Runnable[] tc1ext_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass1ext()),
+ new InvokeNative(breakit_method_tc1ext, new TestClass1ext()),
+
+ new InvokeReflect(breakit_method, new TestClass1ext()),
+ new InvokeReflect(breakit_method_tc1ext, new TestClass1ext()),
+
+ new InvokeDirect("((Breakable)new TestClass1ext()).breakit()",
+ () -> ((Breakable)new TestClass1ext()).breakit()),
+ new InvokeDirect("((TestClass1)new TestClass1ext()).breakit()",
+ () -> ((TestClass1)new TestClass1ext()).breakit()),
+ new InvokeDirect("new TestClass1ext().breakit()",
+ () -> new TestClass1ext().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc1ext_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc1ext)
+ };
+ runTestGroups("TestClass1ext invokes", tc1ext_invokes, tc1ext_breakpoints);
+
+ // Override default/interface method.
+ Runnable[] tc2_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass2()),
+ new InvokeNative(breakit_method_tc2, new TestClass2()),
+
+ new InvokeReflect(breakit_method, new TestClass2()),
+ new InvokeReflect(breakit_method_tc2, new TestClass2()),
+
+ new InvokeDirect("((Breakable)new TestClass2()).breakit()",
+ () -> ((Breakable)new TestClass2()).breakit()),
+ new InvokeDirect("new TestClass2().breakit()",
+ () -> new TestClass2().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc2_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc2)
+ };
+ runTestGroups("TestClass2 invokes", tc2_invokes, tc2_breakpoints);
+
+ // Call overridden method using invoke-super
+ Runnable[] tc2ext_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass2ext()),
+ new InvokeNative(breakit_method_tc2, new TestClass2ext()),
+ new InvokeNative(breakit_method_tc2ext, new TestClass2ext()),
+
+ new InvokeReflect(breakit_method, new TestClass2ext()),
+ new InvokeReflect(breakit_method_tc2, new TestClass2ext()),
+ new InvokeReflect(breakit_method_tc2ext, new TestClass2ext()),
+
+ new InvokeDirect("((Breakable)new TestClass2ext()).breakit()",
+ () -> ((Breakable)new TestClass2ext()).breakit()),
+ new InvokeDirect("((TestClass2)new TestClass2ext()).breakit()",
+ () -> ((TestClass2)new TestClass2ext()).breakit()),
+ new InvokeDirect("new TestClass2ext().breakit())",
+ () -> new TestClass2ext().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc2ext_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc2), BP(breakit_method_tc2ext)
+ };
+ runTestGroups("TestClass2ext invokes", tc2ext_invokes, tc2ext_breakpoints);
+
+ // Override default method and call it using interface-invoke-super
+ Runnable[] tc3_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass3()),
+ new InvokeNative(breakit_method_tc3, new TestClass3()),
+
+ new InvokeReflect(breakit_method, new TestClass3()),
+ new InvokeReflect(breakit_method_tc3, new TestClass3()),
+
+ new InvokeDirect("((Breakable)new TestClass3()).breakit()",
+ () -> ((Breakable)new TestClass3()).breakit()),
+ new InvokeDirect("new TestClass3().breakit())",
+ () -> new TestClass3().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc3_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc3)
+ };
+ runTestGroups("TestClass3 invokes", tc3_invokes, tc3_breakpoints);
+
+ // Call overridden method using invoke-super
+ Runnable[] tc3ext_invokes = new Runnable[] {
+ new InvokeNative(breakit_method, new TestClass3ext()),
+ new InvokeNative(breakit_method_tc3, new TestClass3ext()),
+ new InvokeNative(breakit_method_tc3ext, new TestClass3ext()),
+
+ new InvokeReflect(breakit_method, new TestClass3ext()),
+ new InvokeReflect(breakit_method_tc3, new TestClass3ext()),
+ new InvokeReflect(breakit_method_tc3ext, new TestClass3ext()),
+
+ new InvokeDirect("((Breakable)new TestClass3ext()).breakit()",
+ () -> ((Breakable)new TestClass3ext()).breakit()),
+ new InvokeDirect("((TestClass3)new TestClass3ext()).breakit()",
+ () -> ((TestClass3)new TestClass3ext()).breakit()),
+ new InvokeDirect("new TestClass3ext().breakit())",
+ () -> new TestClass3ext().breakit()),
+ };
+ Breakpoint.Manager.BP[] tc3ext_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(breakit_method), BP(breakit_method_tc3), BP(breakit_method_tc3ext)
+ };
+ runTestGroups("TestClass3ext invokes", tc3ext_invokes, tc3ext_breakpoints);
+
+ // private instance method.
+ Runnable[] private_instance_invokes = new Runnable[] {
+ new InvokeNative(private_method, new TestClass4()),
+
+ new InvokeDirect("new TestClass4().callPrivateMethod()",
+ () -> new TestClass4().callPrivateMethod()),
+ };
+ Breakpoint.Manager.BP[] private_instance_breakpoints = new Breakpoint.Manager.BP[] {
+ BP(private_method)
+ };
+ runTestGroups(
+ "private instance invoke", private_instance_invokes, private_instance_breakpoints);
+ }
+
+ private static void runTestGroups(String name,
+ Runnable[] invokes,
+ Breakpoint.Manager.BP[] breakpoints) throws Exception {
+ System.out.println("Running " + name);
+ for (List<Breakpoint.Manager.BP> bps : allCombinations(Arrays.asList(breakpoints))) {
+ System.out.println("\tBreaking on " + bps);
+ for (Runnable test : invokes) {
+ MANAGER.clearAllBreakpoints();
+ MANAGER.setBreakpoints(bps.toArray(new Breakpoint.Manager.BP[0]));
+ test.run();
+ }
+ }
+ }
+}
diff --git a/test/994-breakpoint-line/expected.txt b/test/994-breakpoint-line/expected.txt
new file mode 100644
index 0000000000..5899659b3c
--- /dev/null
+++ b/test/994-breakpoint-line/expected.txt
@@ -0,0 +1,34 @@
+Breaking on line: 29 calling with arg: true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=29
+ argument was true
+Breaking on line: 29 calling with arg: false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=29
+ argument was false
+Breaking on line: 30 calling with arg: true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=30
+ argument was true
+Breaking on line: 30 calling with arg: false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=30
+ argument was false
+Breaking on line: 31 calling with arg: true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=31
+ argument was true
+Breaking on line: 31 calling with arg: false
+ argument was false
+Breaking on line: 33 calling with arg: true
+ argument was true
+Breaking on line: 33 calling with arg: false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=33
+ argument was false
+Breaking on line: 35 calling with arg: true
+ argument was true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=35
+Breaking on line: 35 calling with arg: false
+ argument was false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=35
+Breaking on line: 36 calling with arg: true
+ argument was true
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=36
+Breaking on line: 36 calling with arg: false
+ argument was false
+ Breakpoint reached: public static void art.Test994.doMultiPath(boolean) @ line=36
diff --git a/test/994-breakpoint-line/info.txt b/test/994-breakpoint-line/info.txt
new file mode 100644
index 0000000000..210dea0471
--- /dev/null
+++ b/test/994-breakpoint-line/info.txt
@@ -0,0 +1,5 @@
+Test basic JVMTI breakpoint functionality.
+
+This test ensures we can place breakpoints on particular lines of a method. It
+sets breakpoints on each line in turn of a function with multiple execution
+paths and then runs the function, receiving the breakpoint events.
diff --git a/test/994-breakpoint-line/run b/test/994-breakpoint-line/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/994-breakpoint-line/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/994-breakpoint-line/src/Main.java b/test/994-breakpoint-line/src/Main.java
new file mode 100644
index 0000000000..39cfeb3ee4
--- /dev/null
+++ b/test/994-breakpoint-line/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test994.run();
+ }
+}
diff --git a/test/994-breakpoint-line/src/art/Breakpoint.java b/test/994-breakpoint-line/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/994-breakpoint-line/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/994-breakpoint-line/src/art/Test994.java b/test/994-breakpoint-line/src/art/Test994.java
new file mode 100644
index 0000000000..6a1c354b39
--- /dev/null
+++ b/test/994-breakpoint-line/src/art/Test994.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test994 {
+ public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
+ public static void doNothing() {}
+
+ // Method with multiple paths we can break on.
+ public static void doMultiPath(boolean bit) {
+ doNothing();
+ if (bit) {
+ System.out.println("\targument was true");
+ } else {
+ System.out.println("\targument was false");
+ }
+ doNothing();
+ }
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ System.out.println(
+ "\tBreakpoint reached: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test994.class,
+ Test994.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ Method multipath_method = Test994.class.getDeclaredMethod("doMultiPath", Boolean.TYPE);
+
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(multipath_method);
+
+ // Make sure everything is in the same order.
+ Arrays.sort(lines);
+
+ boolean[] values = new boolean[] { true, false };
+
+ for (Breakpoint.LineNumber line : lines) {
+ MANAGER.clearAllBreakpoints();
+ MANAGER.setBreakpoint(multipath_method, line.location);
+ for (boolean arg : values) {
+ System.out.println("Breaking on line: " + line.line + " calling with arg: " + arg);
+ doMultiPath(arg);
+ }
+ }
+
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+}
diff --git a/test/995-breakpoints-throw/expected.txt b/test/995-breakpoints-throw/expected.txt
new file mode 100644
index 0000000000..a565b7cf14
--- /dev/null
+++ b/test/995-breakpoints-throw/expected.txt
@@ -0,0 +1,34 @@
+Test "call Test995::breakpoint": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Test "call Test995::breakpoint": No error caught with handler "do nothing"
+Test "call Test995::breakpoint": Finished running with handler "do nothing"
+Test "call Test995::breakpointCatch": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpointCatch() @ line=48
+Test "call Test995::breakpointCatch": No error caught with handler "do nothing"
+Test "call Test995::breakpointCatch": Finished running with handler "do nothing"
+Test "call Test995::breakpointCatchLate": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpointCatchLate() @ line=38
+Test "call Test995::breakpointCatchLate": No error caught with handler "do nothing"
+Test "call Test995::breakpointCatchLate": Finished running with handler "do nothing"
+Test "catch subroutine Test995::breakpoint": Running breakpoint with handler "do nothing"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Test "catch subroutine Test995::breakpoint": No error caught with handler "do nothing"
+Test "catch subroutine Test995::breakpoint": Finished running with handler "do nothing"
+Test "call Test995::breakpoint": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Test "call Test995::breakpoint": Caught error java.lang.Error:"throwing error!" with handler "throw"
+Test "call Test995::breakpoint": Finished running with handler "throw"
+Test "call Test995::breakpointCatch": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpointCatch() @ line=48
+Caught java.lang.Error: "throwing error!"
+Test "call Test995::breakpointCatch": No error caught with handler "throw"
+Test "call Test995::breakpointCatch": Finished running with handler "throw"
+Test "call Test995::breakpointCatchLate": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpointCatchLate() @ line=38
+Test "call Test995::breakpointCatchLate": Caught error java.lang.Error:"throwing error!" with handler "throw"
+Test "call Test995::breakpointCatchLate": Finished running with handler "throw"
+Test "catch subroutine Test995::breakpoint": Running breakpoint with handler "throw"
+ Breakpoint: public static void art.Test995.breakpoint() @ line=34
+Caught java.lang.Error:"throwing error!"
+Test "catch subroutine Test995::breakpoint": No error caught with handler "throw"
+Test "catch subroutine Test995::breakpoint": Finished running with handler "throw"
diff --git a/test/995-breakpoints-throw/info.txt b/test/995-breakpoints-throw/info.txt
new file mode 100644
index 0000000000..80f9cf94bf
--- /dev/null
+++ b/test/995-breakpoints-throw/info.txt
@@ -0,0 +1,6 @@
+Test basic JVMTI breakpoint functionality.
+
+Tests that it is possible to throw exceptions while handling breakpoint events
+and that they are handled appropriately. This includes checking that it is
+possible for the method being breakpointed to catch exceptions thrown by the
+handler.
diff --git a/test/995-breakpoints-throw/run b/test/995-breakpoints-throw/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/995-breakpoints-throw/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/995-breakpoints-throw/src/Main.java b/test/995-breakpoints-throw/src/Main.java
new file mode 100644
index 0000000000..6f80b43255
--- /dev/null
+++ b/test/995-breakpoints-throw/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test995.run();
+ }
+}
diff --git a/test/995-breakpoints-throw/src/art/Breakpoint.java b/test/995-breakpoints-throw/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/995-breakpoints-throw/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/995-breakpoints-throw/src/art/Test995.java b/test/995-breakpoints-throw/src/art/Test995.java
new file mode 100644
index 0000000000..a4023fb80a
--- /dev/null
+++ b/test/995-breakpoints-throw/src/art/Test995.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test995 {
+ public static final Breakpoint.Manager MANAGER = new Breakpoint.Manager();
+ public static BreakpointHandler HANDLER = null;
+
+ public static void doNothing() { }
+
+ public static interface BreakpointHandler {
+ public void breakpointReached(Executable e, long loc);
+ }
+
+ public static void breakpoint() {
+ return;
+ }
+
+ public static void breakpointCatchLate() {
+ doNothing();
+ try {
+ doNothing();
+ } catch (Throwable t) {
+ System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ }
+ }
+
+ public static void breakpointCatch() {
+ try {
+ doNothing();
+ } catch (Throwable t) {
+ System.out.println("Caught " + t.getClass().getName() + ": \"" + t.getMessage() + "\"");
+ }
+ }
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ System.out.println("\tBreakpoint: " + e + " @ line=" + Breakpoint.locationToLine(e, loc));
+ HANDLER.breakpointReached(e, loc);
+ }
+
+
+ public static BreakpointHandler makeHandler(String name, BreakpointHandler h) {
+ return new BreakpointHandler() {
+ public String toString() {
+ return name;
+ }
+ public void breakpointReached(Executable e, long loc) {
+ h.breakpointReached(e, loc);
+ }
+ };
+ }
+
+ public static Runnable makeTest(String name, Runnable test) {
+ return new Runnable() {
+ public String toString() { return name; }
+ public void run() { test.run(); }
+ };
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test995.class,
+ Test995.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ Method breakpoint_method = Test995.class.getDeclaredMethod("breakpoint");
+ Method breakpoint_catch_method = Test995.class.getDeclaredMethod("breakpointCatch");
+ Method breakpoint_catch_late_method = Test995.class.getDeclaredMethod("breakpointCatchLate");
+ MANAGER.setBreakpoint(breakpoint_method, Breakpoint.getStartLocation(breakpoint_method));
+ MANAGER.setBreakpoint(
+ breakpoint_catch_method, Breakpoint.getStartLocation(breakpoint_catch_method));
+ MANAGER.setBreakpoint(
+ breakpoint_catch_late_method, Breakpoint.getStartLocation(breakpoint_catch_late_method));
+
+ BreakpointHandler[] handlers = new BreakpointHandler[] {
+ makeHandler("do nothing", (e, l) -> {}),
+ makeHandler("throw", (e, l) -> { throw new Error("throwing error!"); }),
+ };
+
+ Runnable[] tests = new Runnable[] {
+ makeTest("call Test995::breakpoint", Test995::breakpoint),
+ makeTest("call Test995::breakpointCatch", Test995::breakpointCatch),
+ makeTest("call Test995::breakpointCatchLate", Test995::breakpointCatchLate),
+ makeTest("catch subroutine Test995::breakpoint",
+ () -> {
+ try {
+ breakpoint();
+ } catch (Throwable t) {
+ System.out.printf("Caught %s:\"%s\"\n", t.getClass().getName(), t.getMessage());
+ }
+ }),
+ };
+
+ for (BreakpointHandler handler : handlers) {
+ for (Runnable test : tests) {
+ try {
+ HANDLER = handler;
+ System.out.printf("Test \"%s\": Running breakpoint with handler \"%s\"\n",
+ test, handler);
+ test.run();
+ System.out.printf("Test \"%s\": No error caught with handler \"%s\"\n",
+ test, handler);
+ } catch (Throwable e) {
+ System.out.printf("Test \"%s\": Caught error %s:\"%s\" with handler \"%s\"\n",
+ test, e.getClass().getName(), e.getMessage(), handler);
+ }
+ System.out.printf("Test \"%s\": Finished running with handler \"%s\"\n", test, handler);
+ HANDLER = null;
+ }
+ }
+
+ MANAGER.clearAllBreakpoints();
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+}
diff --git a/test/996-breakpoint-obsolete/expected.txt b/test/996-breakpoint-obsolete/expected.txt
new file mode 100644
index 0000000000..e0d419e3f5
--- /dev/null
+++ b/test/996-breakpoint-obsolete/expected.txt
@@ -0,0 +1,14 @@
+Initially setting breakpoint to line 42
+Running transform without redefinition.
+Should be after first breakpoint.
+Breakpoint reached: public void art.Test996$Transform.run(java.lang.Runnable) @ line=42
+Running transform with redefinition.
+Redefining calling function!
+Setting breakpoint on now obsolete method to line 40
+Breakpoint reached: public void art.Test996$Transform.run(java.lang.Runnable) @ line=40
+Should be after first breakpoint.
+Running transform post redefinition. Should not hit any breakpoints.
+Doing nothing transformed
+Setting initial breakpoint on redefined method.
+Doing nothing transformed
+Breakpoint reached: public void art.Test996$Transform.run(java.lang.Runnable) @ line=8
diff --git a/test/996-breakpoint-obsolete/info.txt b/test/996-breakpoint-obsolete/info.txt
new file mode 100644
index 0000000000..58536acece
--- /dev/null
+++ b/test/996-breakpoint-obsolete/info.txt
@@ -0,0 +1,4 @@
+Test JVMTI breakpoint/obsolete method interaction.
+
+This checks that redefining a class will clear breakpoints on the class's
+methods and that it is possible to set breakpoints on obsolete methods.
diff --git a/test/996-breakpoint-obsolete/obsolete_breakpoints.cc b/test/996-breakpoint-obsolete/obsolete_breakpoints.cc
new file mode 100644
index 0000000000..b6a67e4a08
--- /dev/null
+++ b/test/996-breakpoint-obsolete/obsolete_breakpoints.cc
@@ -0,0 +1,76 @@
+/*
+ * 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 <inttypes.h>
+#include <memory>
+#include <stdio.h>
+
+#include "android-base/logging.h"
+#include "android-base/stringprintf.h"
+
+#include "jni.h"
+#include "jvmti.h"
+#include "scoped_local_ref.h"
+
+// Test infrastructure
+#include "jni_binder.h"
+#include "jni_helper.h"
+#include "jvmti_helper.h"
+#include "test_env.h"
+#include "ti_macros.h"
+
+namespace art {
+namespace Test996ObsoleteBreakpoints {
+
+static constexpr jint kNumFrames = 10;
+
+static jmethodID GetFirstObsoleteMethod(JNIEnv* env, jvmtiEnv* jvmti_env) {
+ jint frame_count;
+ jvmtiFrameInfo frames[kNumFrames];
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->GetStackTrace(nullptr, // current thread
+ 0,
+ kNumFrames,
+ frames,
+ &frame_count))) {
+ return nullptr;
+ }
+ for (jint i = 0; i < frame_count; i++) {
+ jboolean is_obsolete = false;
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->IsMethodObsolete(frames[i].method, &is_obsolete))) {
+ return nullptr;
+ }
+ if (is_obsolete) {
+ return frames[i].method;
+ }
+ }
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Unable to find obsolete method!");
+ return nullptr;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Test996_setBreakpointOnObsoleteMethod(
+ JNIEnv* env, jclass k ATTRIBUTE_UNUSED, jlong loc) {
+ jmethodID method = GetFirstObsoleteMethod(env, jvmti_env);
+ if (method == nullptr) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetBreakpoint(method, loc));
+}
+
+} // namespace Test996ObsoleteBreakpoints
+} // namespace art
diff --git a/test/996-breakpoint-obsolete/run b/test/996-breakpoint-obsolete/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/996-breakpoint-obsolete/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/996-breakpoint-obsolete/src/Main.java b/test/996-breakpoint-obsolete/src/Main.java
new file mode 100644
index 0000000000..1b9b0a9b4b
--- /dev/null
+++ b/test/996-breakpoint-obsolete/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test996.run();
+ }
+}
diff --git a/test/996-breakpoint-obsolete/src/art/Breakpoint.java b/test/996-breakpoint-obsolete/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/996-breakpoint-obsolete/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/996-breakpoint-obsolete/src/art/Redefinition.java b/test/996-breakpoint-obsolete/src/art/Redefinition.java
new file mode 100644
index 0000000000..56d2938a01
--- /dev/null
+++ b/test/996-breakpoint-obsolete/src/art/Redefinition.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.ArrayList;
+// Common Redefinition functions. Placed here for use by CTS
+public class Redefinition {
+ public static final class CommonClassDefinition {
+ public final Class<?> target;
+ public final byte[] class_file_bytes;
+ public final byte[] dex_file_bytes;
+
+ public CommonClassDefinition(Class<?> target, byte[] class_file_bytes, byte[] dex_file_bytes) {
+ this.target = target;
+ this.class_file_bytes = class_file_bytes;
+ this.dex_file_bytes = dex_file_bytes;
+ }
+ }
+
+ // A set of possible test configurations. Test should set this if they need to.
+ // This must be kept in sync with the defines in ti-agent/common_helper.cc
+ public static enum Config {
+ COMMON_REDEFINE(0),
+ COMMON_RETRANSFORM(1),
+ COMMON_TRANSFORM(2);
+
+ private final int val;
+ private Config(int val) {
+ this.val = val;
+ }
+ }
+
+ public static void setTestConfiguration(Config type) {
+ nativeSetTestConfiguration(type.val);
+ }
+
+ private static native void nativeSetTestConfiguration(int type);
+
+ // Transforms the class
+ public static native void doCommonClassRedefinition(Class<?> target,
+ byte[] classfile,
+ byte[] dexfile);
+
+ public static void doMultiClassRedefinition(CommonClassDefinition... defs) {
+ ArrayList<Class<?>> classes = new ArrayList<>();
+ ArrayList<byte[]> class_files = new ArrayList<>();
+ ArrayList<byte[]> dex_files = new ArrayList<>();
+
+ for (CommonClassDefinition d : defs) {
+ classes.add(d.target);
+ class_files.add(d.class_file_bytes);
+ dex_files.add(d.dex_file_bytes);
+ }
+ doCommonMultiClassRedefinition(classes.toArray(new Class<?>[0]),
+ class_files.toArray(new byte[0][]),
+ dex_files.toArray(new byte[0][]));
+ }
+
+ public static void addMultiTransformationResults(CommonClassDefinition... defs) {
+ for (CommonClassDefinition d : defs) {
+ addCommonTransformationResult(d.target.getCanonicalName(),
+ d.class_file_bytes,
+ d.dex_file_bytes);
+ }
+ }
+
+ public static native void doCommonMultiClassRedefinition(Class<?>[] targets,
+ byte[][] classfiles,
+ byte[][] dexfiles);
+ public static native void doCommonClassRetransformation(Class<?>... target);
+ public static native void setPopRetransformations(boolean pop);
+ public static native void popTransformationFor(String name);
+ public static native void enableCommonRetransformation(boolean enable);
+ public static native void addCommonTransformationResult(String target_name,
+ byte[] class_bytes,
+ byte[] dex_bytes);
+}
diff --git a/test/996-breakpoint-obsolete/src/art/Test996.java b/test/996-breakpoint-obsolete/src/art/Test996.java
new file mode 100644
index 0000000000..f3166c33c7
--- /dev/null
+++ b/test/996-breakpoint-obsolete/src/art/Test996.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+import java.util.Base64;
+
+public class Test996 {
+ // The line we are going to break on. This should be the println in the Transform class. We set a
+ // breakpoint here after we have redefined the class.
+ public static final int TRANSFORM_BREAKPOINT_REDEFINED_LINE = 40;
+
+ // The line we initially set a breakpoint on. This should be the doNothing call. This should be
+ // cleared by the redefinition and should only be caught on the initial run.
+ public static final int TRANSFORM_BREAKPOINT_INITIAL_LINE = 42;
+
+ // A function that doesn't do anything. Used for giving places to break on in a function.
+ public static void doNothing() {}
+
+ public static final class Transform {
+ public void run(Runnable r) {
+ r.run();
+ // Make sure we don't change anything above this line to keep all the breakpoint stuff
+ // working. We will be putting a breakpoint before this line in the runnable.
+ System.out.println("Should be after first breakpoint.");
+ // This is set as a breakpoint prior to redefinition. It should not be hit.
+ doNothing();
+ }
+ }
+
+ /* ******************************************************************************************** */
+ // Try to keep all edits to this file below the above line. If edits need to be made above this
+ // line be sure to update the TRANSFORM_BREAKPOINT_REDEFINED_LINE and
+ // TRANSFORM_BREAKPOINT_INITIAL_LINE to their appropriate values.
+
+ public static final int TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE = 8;
+
+ // The base64 encoding of the following class. The redefined 'run' method should have the same
+ // instructions as the original. This means that the locations of each line should stay the same
+ // and the set of valid locations will not change. We use this to ensure that breakpoints are
+ // removed from the redefined method.
+ // public static final class Transform {
+ // public void run(Runnable r) {
+ // r.run();
+ // System.out.println("Doing nothing transformed");
+ // doNothing(); // try to catch non-removed breakpoints
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAKAoACAARCwASABMJABQAFQgAFgoAFwAYCgAZABoHABsHAB4BAAY8aW5pdD4BAAMo" +
+ "KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQADcnVuAQAXKExqYXZhL2xhbmcvUnVubmFibGU7" +
+ "KVYBAApTb3VyY2VGaWxlAQAMVGVzdDk5Ni5qYXZhDAAJAAoHAB8MAA0ACgcAIAwAIQAiAQAZRG9p" +
+ "bmcgbm90aGluZyB0cmFuc2Zvcm1lZAcAIwwAJAAlBwAmDAAnAAoBABVhcnQvVGVzdDk5NiRUcmFu" +
+ "c2Zvcm0BAAlUcmFuc2Zvcm0BAAxJbm5lckNsYXNzZXMBABBqYXZhL2xhbmcvT2JqZWN0AQASamF2" +
+ "YS9sYW5nL1J1bm5hYmxlAQAQamF2YS9sYW5nL1N5c3RlbQEAA291dAEAFUxqYXZhL2lvL1ByaW50" +
+ "U3RyZWFtOwEAE2phdmEvaW8vUHJpbnRTdHJlYW0BAAdwcmludGxuAQAVKExqYXZhL2xhbmcvU3Ry" +
+ "aW5nOylWAQALYXJ0L1Rlc3Q5OTYBAAlkb05vdGhpbmcAMQAHAAgAAAAAAAIAAQAJAAoAAQALAAAA" +
+ "HQABAAEAAAAFKrcAAbEAAAABAAwAAAAGAAEAAAAEAAEADQAOAAEACwAAADYAAgACAAAAEiu5AAIB" +
+ "ALIAAxIEtgAFuAAGsQAAAAEADAAAABIABAAAAAYABgAHAA4ACAARAAkAAgAPAAAAAgAQAB0AAAAK" +
+ "AAEABwAZABwAGQ==");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQBzn3TiKGAiM0fubj25v816W0k+niqj+SQcBAAAcAAAAHhWNBIAAAAAAAAAAFgDAAAW" +
+ "AAAAcAAAAAoAAADIAAAAAwAAAPAAAAABAAAAFAEAAAYAAAAcAQAAAQAAAEwBAACwAgAAbAEAANoB" +
+ "AADiAQAA/QEAABYCAAAlAgAASQIAAGkCAACAAgAAlAIAAKoCAAC+AgAA0gIAAOACAADrAgAA7gIA" +
+ "APICAAD/AgAACgMAABADAAAVAwAAHgMAACMDAAACAAAAAwAAAAQAAAAFAAAABgAAAAcAAAAIAAAA" +
+ "CQAAAAoAAAANAAAADQAAAAkAAAAAAAAADgAAAAkAAADMAQAADgAAAAkAAADUAQAACAAEABIAAAAA" +
+ "AAAAAAAAAAAAAQAUAAAAAQAAABAAAAAEAAIAEwAAAAUAAAAAAAAABgAAABQAAAAAAAAAEQAAAAUA" +
+ "AAAAAAAACwAAALwBAABHAwAAAAAAAAIAAAA4AwAAPgMAAAEAAQABAAAAKgMAAAQAAABwEAQAAAAO" +
+ "AAQAAgACAAAALwMAAA4AAAByEAUAAwBiAAAAGgEBAG4gAwAQAHEAAgAAAA4AbAEAAAAAAAAAAAAA" +
+ "AAAAAAEAAAAGAAAAAQAAAAcABjxpbml0PgAZRG9pbmcgbm90aGluZyB0cmFuc2Zvcm1lZAAXTGFy" +
+ "dC9UZXN0OTk2JFRyYW5zZm9ybTsADUxhcnQvVGVzdDk5NjsAIkxkYWx2aWsvYW5ub3RhdGlvbi9F" +
+ "bmNsb3NpbmdDbGFzczsAHkxkYWx2aWsvYW5ub3RhdGlvbi9Jbm5lckNsYXNzOwAVTGphdmEvaW8v" +
+ "UHJpbnRTdHJlYW07ABJMamF2YS9sYW5nL09iamVjdDsAFExqYXZhL2xhbmcvUnVubmFibGU7ABJM" +
+ "amF2YS9sYW5nL1N0cmluZzsAEkxqYXZhL2xhbmcvU3lzdGVtOwAMVGVzdDk5Ni5qYXZhAAlUcmFu" +
+ "c2Zvcm0AAVYAAlZMAAthY2Nlc3NGbGFncwAJZG9Ob3RoaW5nAARuYW1lAANvdXQAB3ByaW50bG4A" +
+ "A3J1bgAFdmFsdWUABAAHDgAGAQAHDjx4PAACAgEVGAECAwIPBBkRFwwAAAEBAIGABPgCAQGQAwAA" +
+ "ABAAAAAAAAAAAQAAAAAAAAABAAAAFgAAAHAAAAACAAAACgAAAMgAAAADAAAAAwAAAPAAAAAEAAAA" +
+ "AQAAABQBAAAFAAAABgAAABwBAAAGAAAAAQAAAEwBAAADEAAAAQAAAGwBAAABIAAAAgAAAHgBAAAG" +
+ "IAAAAQAAALwBAAABEAAAAgAAAMwBAAACIAAAFgAAANoBAAADIAAAAgAAACoDAAAEIAAAAgAAADgD" +
+ "AAAAIAAAAQAAAEcDAAAAEAAAAQAAAFgDAAA=");
+
+ public static void notifyBreakpointReached(Thread thr, Executable e, long loc) {
+ int line = Breakpoint.locationToLine(e, loc);
+ if (line == -1 && e.getName().equals("run") && e.getDeclaringClass().equals(Transform.class)) {
+ // RI always reports line = -1 for obsolete methods. Just replace it with the real line for
+ // consistency.
+ line = TRANSFORM_BREAKPOINT_REDEFINED_LINE;
+ }
+ System.out.println("Breakpoint reached: " + e + " @ line=" + line);
+ }
+
+ public static void run() throws Exception {
+ // Set up breakpoints
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ Breakpoint.startBreakpointWatch(
+ Test996.class,
+ Test996.class.getDeclaredMethod(
+ "notifyBreakpointReached", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+
+ Transform t = new Transform();
+ Method non_obsolete_run_method = Transform.class.getDeclaredMethod("run", Runnable.class);
+ final long obsolete_breakpoint_location =
+ Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_REDEFINED_LINE);
+
+ System.out.println("Initially setting breakpoint to line " + TRANSFORM_BREAKPOINT_INITIAL_LINE);
+ long initial_breakpoint_location =
+ Breakpoint.lineToLocation(non_obsolete_run_method, TRANSFORM_BREAKPOINT_INITIAL_LINE);
+ Breakpoint.setBreakpoint(non_obsolete_run_method, initial_breakpoint_location);
+
+ System.out.println("Running transform without redefinition.");
+ t.run(() -> {});
+
+ System.out.println("Running transform with redefinition.");
+ t.run(() -> {
+ System.out.println("Redefining calling function!");
+ // This should clear the breakpoint set to TRANSFORM_BREAKPOINT_INITIAL_LINE
+ Redefinition.doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ System.out.println("Setting breakpoint on now obsolete method to line " +
+ TRANSFORM_BREAKPOINT_REDEFINED_LINE);
+ setBreakpointOnObsoleteMethod(obsolete_breakpoint_location);
+ });
+ System.out.println("Running transform post redefinition. Should not hit any breakpoints.");
+ t.run(() -> {});
+
+ System.out.println("Setting initial breakpoint on redefined method.");
+ long final_breakpoint_location =
+ Breakpoint.lineToLocation(non_obsolete_run_method,
+ TRANSFORM_BREAKPOINT_POST_REDEFINITION_LINE);
+ Breakpoint.setBreakpoint(non_obsolete_run_method, final_breakpoint_location);
+ t.run(() -> {});
+
+ Breakpoint.stopBreakpointWatch(Thread.currentThread());
+ }
+
+ public static native void setBreakpointOnObsoleteMethod(long location);
+}
diff --git a/test/997-single-step/expected.txt b/test/997-single-step/expected.txt
new file mode 100644
index 0000000000..69c554ca7f
--- /dev/null
+++ b/test/997-single-step/expected.txt
@@ -0,0 +1,12 @@
+Stepping through doMultiPath(true)
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=41
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=42
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=43
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=47
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=48
+Stepping through doMultiPath(false)
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=41
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=42
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=45
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=47
+Single step: public static void art.Test997.doMultiPath(boolean) @ line=48
diff --git a/test/997-single-step/info.txt b/test/997-single-step/info.txt
new file mode 100644
index 0000000000..e4a584e46f
--- /dev/null
+++ b/test/997-single-step/info.txt
@@ -0,0 +1,3 @@
+Test basic JVMTI single step functionality.
+
+Ensures that we can receive single step events from JVMTI.
diff --git a/test/997-single-step/run b/test/997-single-step/run
new file mode 100755
index 0000000000..51875a7e86
--- /dev/null
+++ b/test/997-single-step/run
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright 2017 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Ask for stack traces to be dumped to a file rather than to stdout.
+./default-run "$@" --jvmti
diff --git a/test/997-single-step/src/Main.java b/test/997-single-step/src/Main.java
new file mode 100644
index 0000000000..1927f04d50
--- /dev/null
+++ b/test/997-single-step/src/Main.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ art.Test997.run();
+ }
+}
diff --git a/test/997-single-step/src/art/Breakpoint.java b/test/997-single-step/src/art/Breakpoint.java
new file mode 100644
index 0000000000..bbb89f707f
--- /dev/null
+++ b/test/997-single-step/src/art/Breakpoint.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Executable;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Objects;
+
+public class Breakpoint {
+ public static class Manager {
+ public static class BP {
+ public final Executable method;
+ public final long location;
+
+ public BP(Executable method) {
+ this(method, getStartLocation(method));
+ }
+
+ public BP(Executable method, long location) {
+ this.method = method;
+ this.location = location;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return (other instanceof BP) &&
+ method.equals(((BP)other).method) &&
+ location == ((BP)other).location;
+ }
+
+ @Override
+ public String toString() {
+ return method.toString() + " @ " + getLine();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(method, location);
+ }
+
+ public int getLine() {
+ try {
+ LineNumber[] lines = getLineNumberTable(method);
+ int best = -1;
+ for (LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+ }
+
+ private Set<BP> breaks = new HashSet<>();
+
+ public void setBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.add(b)) {
+ Breakpoint.setBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void setBreakpoint(Executable method, long location) {
+ setBreakpoints(new BP(method, location));
+ }
+
+ public void clearBreakpoints(BP... bs) {
+ for (BP b : bs) {
+ if (breaks.remove(b)) {
+ Breakpoint.clearBreakpoint(b.method, b.location);
+ }
+ }
+ }
+ public void clearBreakpoint(Executable method, long location) {
+ clearBreakpoints(new BP(method, location));
+ }
+
+ public void clearAllBreakpoints() {
+ clearBreakpoints(breaks.toArray(new BP[0]));
+ }
+ }
+
+ public static void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ Thread thr) {
+ startBreakpointWatch(methodClass, breakpointReached, false, thr);
+ }
+
+ /**
+ * Enables the trapping of breakpoint events.
+ *
+ * If allowRecursive == true then breakpoints will be sent even if one is currently being handled.
+ */
+ public static native void startBreakpointWatch(Class<?> methodClass,
+ Executable breakpointReached,
+ boolean allowRecursive,
+ Thread thr);
+ public static native void stopBreakpointWatch(Thread thr);
+
+ public static final class LineNumber implements Comparable<LineNumber> {
+ public final long location;
+ public final int line;
+
+ private LineNumber(long loc, int line) {
+ this.location = loc;
+ this.line = line;
+ }
+
+ public boolean equals(Object other) {
+ return other instanceof LineNumber && ((LineNumber)other).line == line &&
+ ((LineNumber)other).location == location;
+ }
+
+ public int compareTo(LineNumber other) {
+ int v = Integer.valueOf(line).compareTo(Integer.valueOf(other.line));
+ if (v != 0) {
+ return v;
+ } else {
+ return Long.valueOf(location).compareTo(Long.valueOf(other.location));
+ }
+ }
+ }
+
+ public static native void setBreakpoint(Executable m, long loc);
+ public static void setBreakpoint(Executable m, LineNumber l) {
+ setBreakpoint(m, l.location);
+ }
+
+ public static native void clearBreakpoint(Executable m, long loc);
+ public static void clearBreakpoint(Executable m, LineNumber l) {
+ clearBreakpoint(m, l.location);
+ }
+
+ private static native Object[] getLineNumberTableNative(Executable m);
+ public static LineNumber[] getLineNumberTable(Executable m) {
+ Object[] nativeTable = getLineNumberTableNative(m);
+ long[] location = (long[])(nativeTable[0]);
+ int[] lines = (int[])(nativeTable[1]);
+ if (lines.length != location.length) {
+ throw new Error("Lines and locations have different lengths!");
+ }
+ LineNumber[] out = new LineNumber[lines.length];
+ for (int i = 0; i < lines.length; i++) {
+ out[i] = new LineNumber(location[i], lines[i]);
+ }
+ return out;
+ }
+
+ public static native long getStartLocation(Executable m);
+
+ public static int locationToLine(Executable m, long location) {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ int best = -1;
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.location > location) {
+ break;
+ } else {
+ best = l.line;
+ }
+ }
+ return best;
+ } catch (Exception e) {
+ return -1;
+ }
+ }
+
+ public static long lineToLocation(Executable m, int line) throws Exception {
+ try {
+ Breakpoint.LineNumber[] lines = Breakpoint.getLineNumberTable(m);
+ for (Breakpoint.LineNumber l : lines) {
+ if (l.line == line) {
+ return l.location;
+ }
+ }
+ throw new Exception("Unable to find line " + line + " in " + m);
+ } catch (Exception e) {
+ throw new Exception("Unable to get line number info for " + m, e);
+ }
+ }
+}
+
diff --git a/test/997-single-step/src/art/Test997.java b/test/997-single-step/src/art/Test997.java
new file mode 100644
index 0000000000..a7a522dcca
--- /dev/null
+++ b/test/997-single-step/src/art/Test997.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.util.Arrays;
+import java.lang.reflect.Executable;
+import java.lang.reflect.Method;
+
+public class Test997 {
+ static final int NO_LAST_LINE_NUMBER = -1;
+ static int LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
+ static Method DO_MULTIPATH_METHOD;
+
+ static {
+ try {
+ DO_MULTIPATH_METHOD = Test997.class.getDeclaredMethod("doMultiPath", Boolean.TYPE);
+ } catch (Exception e) {
+ throw new Error("could not find method doMultiPath", e);
+ }
+ }
+
+ // Function that acts simply to ensure there are multiple lines.
+ public static void doNothing() {}
+
+ // Method with multiple paths we can break on.
+ public static void doMultiPath(boolean bit) {
+ doNothing();
+ if (bit) {
+ doNothing();
+ } else {
+ doNothing();
+ }
+ doNothing();
+ }
+
+ public static void notifySingleStep(Thread thr, Executable e, long loc) {
+ if (!e.equals(DO_MULTIPATH_METHOD)) {
+ // Only report steps in doMultiPath
+ return;
+ }
+ int cur_line = Breakpoint.locationToLine(e, loc);
+ // Only report anything when the line number changes. This is so we can run this test against
+ // both the RI and ART and also to prevent front-end compiler changes from affecting output.
+ if (LAST_LINE_NUMBER == NO_LAST_LINE_NUMBER || LAST_LINE_NUMBER != cur_line) {
+ LAST_LINE_NUMBER = cur_line;
+ System.out.println("Single step: " + e + " @ line=" + cur_line);
+ }
+ }
+
+ public static void resetTest() {
+ LAST_LINE_NUMBER = NO_LAST_LINE_NUMBER;
+ }
+
+ public static void run() throws Exception {
+ boolean[] values = new boolean[] { true, false };
+ Trace.enableSingleStepTracing(Test997.class,
+ Test997.class.getDeclaredMethod(
+ "notifySingleStep", Thread.class, Executable.class, Long.TYPE),
+ Thread.currentThread());
+ for (boolean arg : values) {
+ System.out.println("Stepping through doMultiPath(" + arg + ")");
+ resetTest();
+ doMultiPath(arg);
+ }
+
+ Trace.disableTracing(Thread.currentThread());
+ }
+}
diff --git a/test/997-single-step/src/art/Trace.java b/test/997-single-step/src/art/Trace.java
new file mode 100644
index 0000000000..ba3d397b0b
--- /dev/null
+++ b/test/997-single-step/src/art/Trace.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package art;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public class Trace {
+ public static native void enableTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Method fieldAccess,
+ Method fieldModify,
+ Method singleStep,
+ Thread thr);
+ public static native void disableTracing(Thread thr);
+
+ public static void enableFieldTracing(Class<?> methodClass,
+ Method fieldAccess,
+ Method fieldModify,
+ Thread thr) {
+ enableTracing(methodClass, null, null, fieldAccess, fieldModify, null, thr);
+ }
+
+ public static void enableMethodTracing(Class<?> methodClass,
+ Method entryMethod,
+ Method exitMethod,
+ Thread thr) {
+ enableTracing(methodClass, entryMethod, exitMethod, null, null, null, thr);
+ }
+
+ public static void enableSingleStepTracing(Class<?> methodClass,
+ Method singleStep,
+ Thread thr) {
+ enableTracing(methodClass, null, null, null, null, singleStep, thr);
+ }
+
+ public static native void watchFieldAccess(Field f);
+ public static native void watchFieldModification(Field f);
+ public static native void watchAllFieldAccesses();
+ public static native void watchAllFieldModifications();
+}
diff --git a/test/Android.bp b/test/Android.bp
index 23ffc7e5a3..0dff01b6cf 100644
--- a/test/Android.bp
+++ b/test/Android.bp
@@ -281,6 +281,8 @@ art_cc_defaults {
"989-method-trace-throw/method_trace.cc",
"991-field-trace-2/field_trace.cc",
"992-source-data/source_file.cc",
+ "993-breakpoints/breakpoints.cc",
+ "996-breakpoint-obsolete/obsolete_breakpoints.cc",
],
shared_libs: [
"libbase",
diff --git a/test/Android.run-test-jvmti-java-library.mk b/test/Android.run-test-jvmti-java-library.mk
index 60ce6c7003..753fe9a330 100644
--- a/test/Android.run-test-jvmti-java-library.mk
+++ b/test/Android.run-test-jvmti-java-library.mk
@@ -22,6 +22,7 @@ include $(CLEAR_VARS)
LOCAL_SHIM_CLASSES := \
902-hello-transformation/src/art/Redefinition.java \
903-hello-tagging/src/art/Main.java \
+ 989-method-trace-throw/src/art/Trace.java \
LOCAL_SRC_FILES := $(LOCAL_SHIM_CLASSES)
@@ -77,6 +78,12 @@ LOCAL_SRC_FILES += \
984-obsolete-invoke/src/art/Test984.java \
985-re-obsolete/src/art/Test985.java \
986-native-method-bind/src/art/Test986.java \
+ 988-method-trace/src/art/Test988.java \
+ 989-method-trace-throw/src/art/Test989.java \
+ 990-field-trace/src/art/Test990.java \
+ 991-field-trace-2/src/art/Test991.java \
+ 992-source-data/src/art/Test992.java \
+ 992-source-data/src/art/Target2.java \
JVMTI_RUN_TEST_GENERATED_NUMBERS := \
901 \
@@ -119,6 +126,11 @@ JVMTI_RUN_TEST_GENERATED_NUMBERS := \
984 \
985 \
986 \
+ 988 \
+ 989 \
+ 990 \
+ 991 \
+ 992 \
# Try to enforce that the directories correspond to the Java files we pull in.
JVMTI_RUN_TEST_DIR_CHECK := $(sort $(foreach DIR,$(JVMTI_RUN_TEST_GENERATED_NUMBERS), \
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index d24edccd8b..ede485a81b 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -63,6 +63,7 @@ TEST_VDEX="n"
TEST_IS_NDEBUG="n"
APP_IMAGE="y"
JVMTI_STRESS="n"
+JVMTI_STEP_STRESS="n"
JVMTI_FIELD_STRESS="n"
JVMTI_TRACE_STRESS="n"
JVMTI_REDEFINE_STRESS="n"
@@ -163,6 +164,10 @@ while true; do
JVMTI_STRESS="y"
JVMTI_REDEFINE_STRESS="y"
shift
+ elif [ "x$1" = "x--jvmti-step-stress" ]; then
+ JVMTI_STRESS="y"
+ JVMTI_STEP_STRESS="y"
+ shift
elif [ "x$1" = "x--jvmti-field-stress" ]; then
JVMTI_STRESS="y"
JVMTI_FIELD_STRESS="y"
@@ -426,6 +431,9 @@ if [[ "$JVMTI_STRESS" = "y" ]]; then
if [[ "$JVMTI_FIELD_STRESS" = "y" ]]; then
agent_args="${agent_args},field"
fi
+ if [[ "$JVMTI_STEP_STRESS" = "y" ]]; then
+ agent_args="${agent_args},step"
+ fi
if [[ "$JVMTI_TRACE_STRESS" = "y" ]]; then
agent_args="${agent_args},trace"
fi
diff --git a/test/knownfailures.json b/test/knownfailures.json
index df8fa606f8..d3d92c651e 100644
--- a/test/knownfailures.json
+++ b/test/knownfailures.json
@@ -502,7 +502,7 @@
"645-checker-abs-simd",
"706-checker-scheduler"],
"description": ["Checker tests are not compatible with jvmti."],
- "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress"
+ "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress | step-stress"
},
{
"tests": [
@@ -510,7 +510,7 @@
"964-default-iface-init-gen"
],
"description": ["Tests that just take too long with jvmti-stress"],
- "variant": "jvmti-stress | redefine-stress | trace-stress"
+ "variant": "jvmti-stress | redefine-stress | trace-stress | step-stress"
},
{
"tests": [
@@ -541,7 +541,7 @@
"981-dedup-original-dex"
],
"description": ["Tests that require exact knowledge of the number of plugins and agents."],
- "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress"
+ "variant": "jvmti-stress | redefine-stress | trace-stress | field-stress | step-stress"
},
{
"tests": [
@@ -579,7 +579,7 @@
"004-ThreadStress"
],
"description": "The thread stress test just takes too long with field-stress",
- "variant": "jvmti-stress | field-stress"
+ "variant": "jvmti-stress | field-stress | step-stress"
},
{
"tests": [
@@ -594,16 +594,7 @@
},
{
"tests": [
- "953-invoke-polymorphic-compiler"
- ],
- "description": "Test throws VerifyError when run with --build-with-javac-dx.",
- "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"},
- "bug": "b/62722425"
- },
- {
- "tests": [
- "567-checker-compare",
- "633-checker-rtp-getclass"
+ "567-checker-compare"
],
"description": "Checker tests failing when run with --build-with-javac-dx.",
"env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"},
@@ -674,5 +665,12 @@
"description": [ "Flake on gcstress" ],
"bug": "b/62562923",
"variant": "gcstress & jit & target"
+ },
+ {
+ "tests": ["004-JniTest"],
+ "description": [ "Tests failing with --build-with-javac-dx since the new annotation",
+ "lookup changes" ],
+ "bug": "b/63089991",
+ "env_vars": {"ANDROID_COMPILE_WITH_JACK": "false"}
}
]
diff --git a/test/run-test b/test/run-test
index 9fe149697a..486b465a89 100755
--- a/test/run-test
+++ b/test/run-test
@@ -145,6 +145,7 @@ gc_verify="false"
gc_stress="false"
jvmti_trace_stress="false"
jvmti_field_stress="false"
+jvmti_step_stress="false"
jvmti_redefine_stress="false"
strace="false"
always_clean="no"
@@ -242,6 +243,9 @@ while true; do
basic_verify="true"
gc_stress="true"
shift
+ elif [ "x$1" = "x--jvmti-step-stress" ]; then
+ jvmti_step_stress="true"
+ shift
elif [ "x$1" = "x--jvmti-redefine-stress" ]; then
jvmti_redefine_stress="true"
shift
@@ -464,6 +468,9 @@ fi
if [ "$jvmti_redefine_stress" = "true" ]; then
run_args="${run_args} --no-app-image --jvmti-redefine-stress"
fi
+if [ "$jvmti_step_stress" = "true" ]; then
+ run_args="${run_args} --no-app-image --jvmti-step-stress"
+fi
if [ "$jvmti_field_stress" = "true" ]; then
run_args="${run_args} --no-app-image --jvmti-field-stress"
fi
@@ -679,6 +686,7 @@ if [ "$usage" = "yes" ]; then
echo " --gcstress Run with gc stress testing"
echo " --gcverify Run with gc verification"
echo " --jvmti-trace-stress Run with jvmti method tracing stress testing"
+ echo " --jvmti-step-stress Run with jvmti single step stress testing"
echo " --jvmti-redefine-stress"
echo " Run with jvmti method redefinition stress testing"
echo " --always-clean Delete the test files even if the test fails."
diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py
index b6a5963cf4..68e1856adb 100755
--- a/test/testrunner/testrunner.py
+++ b/test/testrunner/testrunner.py
@@ -148,7 +148,7 @@ def gather_test_info():
VARIANT_TYPE_DICT['jni'] = {'jni', 'forcecopy', 'checkjni'}
VARIANT_TYPE_DICT['address_sizes'] = {'64', '32'}
VARIANT_TYPE_DICT['jvmti'] = {'no-jvmti', 'jvmti-stress', 'redefine-stress', 'trace-stress',
- 'field-stress'}
+ 'field-stress', 'step-stress'}
VARIANT_TYPE_DICT['compiler'] = {'interp-ac', 'interpreter', 'jit', 'optimizing',
'regalloc_gc', 'speed-profile'}
@@ -445,6 +445,8 @@ def run_tests(tests):
options_test += ' --jvmti-trace-stress'
elif jvmti == 'redefine-stress':
options_test += ' --jvmti-redefine-stress'
+ elif jvmti == 'step-stress':
+ options_test += ' --jvmti-step-stress'
if address_size == '64':
options_test += ' --64'
@@ -965,6 +967,8 @@ def parse_option():
JVMTI_TYPES.add('redefine-stress')
if options['field_stress']:
JVMTI_TYPES.add('field-stress')
+ if options['step_stress']:
+ JVMTI_TYPES.add('step-stress')
if options['trace_stress']:
JVMTI_TYPES.add('trace-stress')
if options['no_jvmti']:
diff --git a/test/ti-agent/common_helper.cc b/test/ti-agent/common_helper.cc
index 4fe58db169..0eb71f8371 100644
--- a/test/ti-agent/common_helper.cc
+++ b/test/ti-agent/common_helper.cc
@@ -38,6 +38,9 @@ static void SetupCommonRetransform();
static void SetupCommonRedefine();
static void SetupCommonTransform();
+// Taken from art/runtime/modifiers.h
+static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
+
template <bool is_redefine>
static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
JNIEnv* env,
@@ -69,22 +72,6 @@ static void throwCommonRedefinitionError(jvmtiEnv* jvmti,
env->ThrowNew(env->FindClass("java/lang/Exception"), message.c_str());
}
-namespace common_trace {
-
-// Taken from art/runtime/modifiers.h
-static constexpr uint32_t kAccStatic = 0x0008; // field, method, ic
-
-struct TraceData {
- jclass test_klass;
- jmethodID enter_method;
- jmethodID exit_method;
- jmethodID field_access;
- jmethodID field_modify;
- bool in_callback;
- bool access_watch_on_load;
- bool modify_watch_on_load;
-};
-
static jobject GetJavaField(jvmtiEnv* jvmti, JNIEnv* env, jclass field_klass, jfieldID f) {
jint mods = 0;
if (JvmtiErrorToException(env, jvmti, jvmti->GetFieldModifiers(field_klass, f, &mods))) {
@@ -175,6 +162,221 @@ static jobject GetJavaValue(jvmtiEnv* jvmtienv,
return GetJavaValueByType(env, type[0], value);
}
+namespace common_breakpoint {
+
+struct BreakpointData {
+ jclass test_klass;
+ jmethodID breakpoint_method;
+ bool in_callback;
+ bool allow_recursive;
+};
+
+extern "C" void breakpointCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thread,
+ jmethodID method,
+ jlocation location) {
+ BreakpointData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->in_callback && !data->allow_recursive) {
+ return;
+ }
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->breakpoint_method,
+ thread,
+ method_arg,
+ static_cast<jlong>(location));
+ jnienv->DeleteLocalRef(method_arg);
+ data->in_callback = false;
+}
+
+extern "C" JNIEXPORT jobjectArray JNICALL Java_art_Breakpoint_getLineNumberTableNative(
+ JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return nullptr;
+ }
+ jint nlines;
+ jvmtiLineNumberEntry* lines = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->GetLineNumberTable(method, &nlines, &lines))) {
+ return nullptr;
+ }
+ jintArray lines_array = env->NewIntArray(nlines);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ jlongArray locs_array = env->NewLongArray(nlines);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ ScopedLocalRef<jclass> object_class(env, env->FindClass("java/lang/Object"));
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ jobjectArray ret = env->NewObjectArray(2, object_class.get(), nullptr);
+ if (env->ExceptionCheck()) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return nullptr;
+ }
+ jint* temp_lines = env->GetIntArrayElements(lines_array, /*isCopy*/nullptr);
+ jlong* temp_locs = env->GetLongArrayElements(locs_array, /*isCopy*/nullptr);
+ for (jint i = 0; i < nlines; i++) {
+ temp_lines[i] = lines[i].line_number;
+ temp_locs[i] = lines[i].start_location;
+ }
+ env->ReleaseIntArrayElements(lines_array, temp_lines, 0);
+ env->ReleaseLongArrayElements(locs_array, temp_locs, 0);
+ env->SetObjectArrayElement(ret, 0, locs_array);
+ env->SetObjectArrayElement(ret, 1, lines_array);
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(lines));
+ return ret;
+}
+
+extern "C" JNIEXPORT jlong JNICALL Java_art_Breakpoint_getStartLocation(JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return 0;
+ }
+ jlong start = 0;
+ jlong end = end;
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->GetMethodLocation(method, &start, &end));
+ return start;
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_clearBreakpoint(JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target,
+ jlocation location) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->ClearBreakpoint(method, location));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_setBreakpoint(JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jobject target,
+ jlocation location) {
+ jmethodID method = env->FromReflectedMethod(target);
+ if (env->ExceptionCheck()) {
+ return;
+ }
+ JvmtiErrorToException(env, jvmti_env, jvmti_env->SetBreakpoint(method, location));
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_startBreakpointWatch(
+ JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jclass method_klass,
+ jobject method,
+ jboolean allow_recursive,
+ jthread thr) {
+ BreakpointData* data = nullptr;
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->Allocate(sizeof(BreakpointData),
+ reinterpret_cast<unsigned char**>(&data)))) {
+ return;
+ }
+ memset(data, 0, sizeof(BreakpointData));
+ data->test_klass = reinterpret_cast<jclass>(env->NewGlobalRef(method_klass));
+ data->breakpoint_method = env->FromReflectedMethod(method);
+ data->in_callback = false;
+ data->allow_recursive = allow_recursive;
+
+ void* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ return;
+ } else if (old_data != nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+ return;
+ }
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
+ return;
+ }
+ jvmtiEventCallbacks cb;
+ memset(&cb, 0, sizeof(cb));
+ cb.Breakpoint = breakpointCB;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
+ return;
+ }
+ if (JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_BREAKPOINT,
+ thr))) {
+ return;
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_art_Breakpoint_stopBreakpointWatch(
+ JNIEnv* env,
+ jclass k ATTRIBUTE_UNUSED,
+ jthread thr) {
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_BREAKPOINT,
+ thr))) {
+ return;
+ }
+}
+
+} // namespace common_breakpoint
+
+namespace common_trace {
+
+struct TraceData {
+ jclass test_klass;
+ jmethodID enter_method;
+ jmethodID exit_method;
+ jmethodID field_access;
+ jmethodID field_modify;
+ jmethodID single_step;
+ bool in_callback;
+ bool access_watch_on_load;
+ bool modify_watch_on_load;
+};
+
+static void singleStepCB(jvmtiEnv* jvmti,
+ JNIEnv* jnienv,
+ jthread thread,
+ jmethodID method,
+ jlocation location) {
+ TraceData* data = nullptr;
+ if (JvmtiErrorToException(jnienv, jvmti,
+ jvmti->GetEnvironmentLocalStorage(reinterpret_cast<void**>(&data)))) {
+ return;
+ }
+ if (data->in_callback) {
+ return;
+ }
+ CHECK(data->single_step != nullptr);
+ data->in_callback = true;
+ jobject method_arg = GetJavaMethod(jvmti, jnienv, method);
+ jnienv->CallStaticVoidMethod(data->test_klass,
+ data->single_step,
+ thread,
+ method_arg,
+ static_cast<jlong>(location));
+ jnienv->DeleteLocalRef(method_arg);
+ data->in_callback = false;
+}
+
static void fieldAccessCB(jvmtiEnv* jvmti,
JNIEnv* jnienv,
jthread thr ATTRIBUTE_UNUSED,
@@ -481,6 +683,7 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
jobject exit,
jobject field_access,
jobject field_modify,
+ jobject single_step,
jthread thr) {
TraceData* data = nullptr;
if (JvmtiErrorToException(env,
@@ -495,8 +698,17 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
data->exit_method = exit != nullptr ? env->FromReflectedMethod(exit) : nullptr;
data->field_access = field_access != nullptr ? env->FromReflectedMethod(field_access) : nullptr;
data->field_modify = field_modify != nullptr ? env->FromReflectedMethod(field_modify) : nullptr;
+ data->single_step = single_step != nullptr ? env->FromReflectedMethod(single_step) : nullptr;
data->in_callback = false;
+ void* old_data = nullptr;
+ if (JvmtiErrorToException(env, jvmti_env, jvmti_env->GetEnvironmentLocalStorage(&old_data))) {
+ return;
+ } else if (old_data != nullptr) {
+ ScopedLocalRef<jclass> rt_exception(env, env->FindClass("java/lang/RuntimeException"));
+ env->ThrowNew(rt_exception.get(), "Environment already has local storage set!");
+ return;
+ }
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEnvironmentLocalStorage(data))) {
return;
}
@@ -508,6 +720,7 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
cb.FieldAccess = fieldAccessCB;
cb.FieldModification = fieldModificationCB;
cb.ClassPrepare = classPrepareCB;
+ cb.SingleStep = singleStepCB;
if (JvmtiErrorToException(env, jvmti_env, jvmti_env->SetEventCallbacks(&cb, sizeof(cb)))) {
return;
}
@@ -543,6 +756,14 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_enableTracing(
thr))) {
return;
}
+ if (single_step != nullptr &&
+ JvmtiErrorToException(env,
+ jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_SINGLE_STEP,
+ thr))) {
+ return;
+ }
}
extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
@@ -571,6 +792,12 @@ extern "C" JNIEXPORT void JNICALL Java_art_Trace_disableTracing(
thr))) {
return;
}
+ if (JvmtiErrorToException(env, jvmti_env,
+ jvmti_env->SetEventNotificationMode(JVMTI_DISABLE,
+ JVMTI_EVENT_SINGLE_STEP,
+ thr))) {
+ return;
+ }
}
} // namespace common_trace
diff --git a/test/ti-stress/stress.cc b/test/ti-stress/stress.cc
index 40fcc4f11d..d197acd216 100644
--- a/test/ti-stress/stress.cc
+++ b/test/ti-stress/stress.cc
@@ -17,6 +17,7 @@
#include <jni.h>
#include <stdio.h>
#include <iostream>
+#include <iomanip>
#include <fstream>
#include <memory>
#include <stdio.h>
@@ -40,6 +41,7 @@ struct StressData {
bool trace_stress;
bool redefine_stress;
bool field_stress;
+ bool step_stress;
};
static void WriteToFile(const std::string& fname, jint data_len, const unsigned char* data) {
@@ -586,6 +588,21 @@ void JNICALL ClassPrepareHook(jvmtiEnv* jvmtienv,
}
}
+void JNICALL SingleStepHook(jvmtiEnv* jvmtienv,
+ JNIEnv* env,
+ jthread thread,
+ jmethodID method,
+ jlocation location) {
+ ScopedThreadInfo info(jvmtienv, env, thread);
+ ScopedMethodInfo method_info(jvmtienv, env, method);
+ if (!method_info.Init()) {
+ LOG(ERROR) << "Unable to get method info!";
+ return;
+ }
+ LOG(INFO) << "Single step at location: 0x" << std::setw(8) << std::setfill('0') << std::hex
+ << location << " in method " << method_info << " thread: " << info.GetName();
+}
+
// The hook we are using.
void JNICALL ClassFileLoadHookSecretNoOp(jvmtiEnv* jvmti,
JNIEnv* jni_env ATTRIBUTE_UNUSED,
@@ -645,6 +662,8 @@ static void ReadOptions(StressData* data, char* options) {
std::string cur = GetOption(ops);
if (cur == "trace") {
data->trace_stress = true;
+ } else if (cur == "step") {
+ data->step_stress = true;
} else if (cur == "field") {
data->field_stress = true;
} else if (cur == "redefine") {
@@ -776,6 +795,7 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
cb.FieldAccess = FieldAccessHook;
cb.FieldModification = FieldModificationHook;
cb.ClassPrepare = ClassPrepareHook;
+ cb.SingleStep = SingleStepHook;
if (jvmti->SetEventCallbacks(&cb, sizeof(cb)) != JVMTI_ERROR_NONE) {
LOG(ERROR) << "Unable to set class file load hook cb!";
return 1;
@@ -837,6 +857,13 @@ extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM* vm,
return 1;
}
}
+ if (data->step_stress) {
+ if (jvmti->SetEventNotificationMode(JVMTI_ENABLE,
+ JVMTI_EVENT_SINGLE_STEP,
+ nullptr) != JVMTI_ERROR_NONE) {
+ return 1;
+ }
+ }
return 0;
}
diff --git a/tools/buildbot-build.sh b/tools/buildbot-build.sh
index bf7692ab15..75694c340c 100755
--- a/tools/buildbot-build.sh
+++ b/tools/buildbot-build.sh
@@ -68,7 +68,7 @@ if $using_jack; then
fi
if [[ $mode == "host" ]]; then
- make_command="make $j_arg $showcommands build-art-host-tests $common_targets"
+ make_command="make $j_arg $showcommands build-art-host-tests $common_targets dx-tests"
make_command+=" ${out_dir}/host/linux-x86/lib/libjavacoretests.so "
make_command+=" ${out_dir}/host/linux-x86/lib64/libjavacoretests.so"
elif [[ $mode == "target" ]]; then