public JavaDebugDisassemblyStream(JavaDebugCodeContext executionContext) { Contract.Requires<ArgumentNullException>(executionContext != null, "executionContext"); _executionContext = executionContext; _bytecode = _executionContext.Location.GetMethod().GetBytecodes(); _disassembledMethod = BytecodeDisassembler.Disassemble(_bytecode); var constantPool = executionContext.Location.GetDeclaringType().GetConstantPool(); ReadOnlyCollection<ExceptionTableEntry> exceptionTable; try { exceptionTable = executionContext.Location.GetMethod().GetExceptionTable(); } catch (DebuggerException) { exceptionTable = new ReadOnlyCollection<ExceptionTableEntry>(new ExceptionTableEntry[0]); } _evaluationStackDepths = BytecodeDisassembler.GetEvaluationStackDepths(_disassembledMethod, constantPool, exceptionTable); }
public static ImmutableList <int?> GetEvaluationStackDepths(DisassembledMethod disassembledMethod, ReadOnlyCollection <ConstantPoolEntry> constantPool, ReadOnlyCollection <ExceptionTableEntry> exceptionTable) { Contract.Requires <ArgumentNullException>(disassembledMethod != null, "disassembledMethod"); Contract.Requires <ArgumentNullException>(constantPool != null, "constantPool"); Contract.Requires <ArgumentNullException>(exceptionTable != null, "exceptionTable"); Contract.Ensures(Contract.Result <ImmutableList <int?> >() != null); Contract.Ensures(Contract.Result <ImmutableList <int?> >().Count == disassembledMethod.Instructions.Count); int?[] depths = new int?[disassembledMethod.Instructions.Count]; Queue <int> workQueue = new Queue <int>(); // can obviously start at the beginning of the method depths[0] = 0; workQueue.Enqueue(0); // can also start inside each exception handler foreach (var entry in exceptionTable) { int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == entry.HandlerOffset); if (!depths[nextIndex].HasValue) { depths[nextIndex] = 1; workQueue.Enqueue(nextIndex); } } while (workQueue.Count > 0) { int index = workQueue.Dequeue(); JavaInstruction instruction = disassembledMethod.Instructions[index]; int netImpact; List <string> argumentSignatures = null; string returnTypeSignature = null; if (instruction.OpCode.FlowControl == JavaFlowControl.Call) { IConstantMemberReference memberReference = (IConstantMemberReference)constantPool[instruction.Operands.ConstantPoolIndex - 1]; ConstantNameAndType nameAndType = (ConstantNameAndType)constantPool[memberReference.NameAndTypeIndex - 1]; ConstantUtf8 descriptor = (ConstantUtf8)constantPool[nameAndType.DescriptorIndex - 1]; string signature = descriptor.Value; SignatureHelper.ParseMethodSignature(signature, out argumentSignatures, out returnTypeSignature); } switch (instruction.OpCode.StackBehaviorPop) { case JavaStackBehavior.Pop0: netImpact = 0; break; case JavaStackBehavior.Pop1: case JavaStackBehavior.PopI: case JavaStackBehavior.PopI8: case JavaStackBehavior.PopR4: case JavaStackBehavior.PopR8: case JavaStackBehavior.PopRef: netImpact = -1; break; case JavaStackBehavior.Pop1_Pop1: case JavaStackBehavior.PopI_PopI: case JavaStackBehavior.PopI8_PopI8: case JavaStackBehavior.PopI8_PopI: case JavaStackBehavior.PopR4_PopR4: case JavaStackBehavior.PopR8_PopR8: case JavaStackBehavior.PopRef_Pop1: case JavaStackBehavior.PopRef_PopI: netImpact = -2; break; case JavaStackBehavior.PopRef_PopI_PopI: case JavaStackBehavior.PopRef_PopI_PopI8: case JavaStackBehavior.PopRef_PopI_PopR4: case JavaStackBehavior.PopRef_PopI_PopR8: case JavaStackBehavior.PopRef_PopI_PopRef: case JavaStackBehavior.PopRef_PopRef: netImpact = -3; break; case JavaStackBehavior.PopVar: switch (instruction.OpCode.OpCode) { case JavaOpCodeTag.Dup_x2: case JavaOpCodeTag.Dup2: case JavaOpCodeTag.Dup2_x1: case JavaOpCodeTag.Dup2_x2: netImpact = 1; break; case JavaOpCodeTag.Invokestatic: netImpact = -argumentSignatures.Count; if (returnTypeSignature != "V") { netImpact++; } break; case JavaOpCodeTag.Invokeinterface: case JavaOpCodeTag.Invokespecial: case JavaOpCodeTag.Invokevirtual: netImpact = -argumentSignatures.Count - 1; if (returnTypeSignature != "V") { netImpact++; } break; case JavaOpCodeTag.Multianewarray: netImpact = -instruction.Operands.Dimensions; break; default: throw new FormatException(); } break; default: throw new FormatException(); } switch (instruction.OpCode.StackBehaviorPush) { case JavaStackBehavior.Push0: break; case JavaStackBehavior.Push1: case JavaStackBehavior.PushI: case JavaStackBehavior.PushI8: case JavaStackBehavior.PushR4: case JavaStackBehavior.PushR8: case JavaStackBehavior.PushRef: case JavaStackBehavior.PushRet: netImpact++; break; case JavaStackBehavior.Push1_Push1: netImpact += 2; break; case JavaStackBehavior.PushVar: // these are all handled in the pop section break; default: throw new FormatException(); } switch (instruction.OpCode.FlowControl) { case JavaFlowControl.Next: case JavaFlowControl.Break: case JavaFlowControl.Call: case JavaFlowControl.ConditionalBranch: case JavaFlowControl.Special: if (!depths[index + 1].HasValue) { depths[index + 1] = depths[index] + netImpact; workQueue.Enqueue(index + 1); } if (instruction.OpCode.FlowControl == JavaFlowControl.ConditionalBranch) { goto case JavaFlowControl.Branch; } break; case JavaFlowControl.Branch: switch (instruction.OpCode.OpCode) { case JavaOpCodeTag.Lookupswitch: { LookupSwitchData switchData = (LookupSwitchData)disassembledMethod.SwitchData[instruction.Operands.SwitchDataIndex]; foreach (var pair in switchData.Pairs) { int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + pair.Value); if (!depths[nextIndex].HasValue) { depths[nextIndex] = depths[index] + netImpact; workQueue.Enqueue(nextIndex); } } break; } case JavaOpCodeTag.Tableswitch: { TableSwitchData switchData = (TableSwitchData)disassembledMethod.SwitchData[instruction.Operands.SwitchDataIndex]; foreach (var offset in switchData.Offsets) { int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + offset); if (!depths[nextIndex].HasValue) { depths[nextIndex] = depths[index] + netImpact; workQueue.Enqueue(nextIndex); } } break; } default: { // single branch target int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + instruction.Operands.BranchTarget); if (!depths[nextIndex].HasValue) { depths[nextIndex] = depths[index] + netImpact; workQueue.Enqueue(nextIndex); } break; } } break; case JavaFlowControl.Return: // no work in this method following this instruction break; case JavaFlowControl.Throw: // 'catch' blocks are handled separately break; case JavaFlowControl.Meta: throw new NotImplementedException(); default: throw new FormatException(); } } return(new ImmutableList <int?>(depths)); }
private static int GetInstructionAtOffset(DisassembledMethod disassembledMethod, long codeIndex) { return disassembledMethod.Instructions.FindIndex(i => i.Offset == codeIndex); }
public static ImmutableList<int?> GetEvaluationStackDepths(DisassembledMethod disassembledMethod, ReadOnlyCollection<ConstantPoolEntry> constantPool, ReadOnlyCollection<ExceptionTableEntry> exceptionTable) { Contract.Requires<ArgumentNullException>(disassembledMethod != null, "disassembledMethod"); Contract.Requires<ArgumentNullException>(constantPool != null, "constantPool"); Contract.Requires<ArgumentNullException>(exceptionTable != null, "exceptionTable"); Contract.Ensures(Contract.Result<ImmutableList<int?>>() != null); Contract.Ensures(Contract.Result<ImmutableList<int?>>().Count == disassembledMethod.Instructions.Count); int?[] depths = new int?[disassembledMethod.Instructions.Count]; Queue<int> workQueue = new Queue<int>(); // can obviously start at the beginning of the method depths[0] = 0; workQueue.Enqueue(0); // can also start inside each exception handler foreach (var entry in exceptionTable) { int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == entry.HandlerOffset); if (!depths[nextIndex].HasValue) { depths[nextIndex] = 1; workQueue.Enqueue(nextIndex); } } while (workQueue.Count > 0) { int index = workQueue.Dequeue(); JavaInstruction instruction = disassembledMethod.Instructions[index]; int netImpact; List<string> argumentSignatures = null; string returnTypeSignature = null; if (instruction.OpCode.FlowControl == JavaFlowControl.Call) { IConstantMemberReference memberReference = (IConstantMemberReference)constantPool[instruction.Operands.ConstantPoolIndex - 1]; ConstantNameAndType nameAndType = (ConstantNameAndType)constantPool[memberReference.NameAndTypeIndex - 1]; ConstantUtf8 descriptor = (ConstantUtf8)constantPool[nameAndType.DescriptorIndex - 1]; string signature = descriptor.Value; SignatureHelper.ParseMethodSignature(signature, out argumentSignatures, out returnTypeSignature); } switch (instruction.OpCode.StackBehaviorPop) { case JavaStackBehavior.Pop0: netImpact = 0; break; case JavaStackBehavior.Pop1: case JavaStackBehavior.PopI: case JavaStackBehavior.PopI8: case JavaStackBehavior.PopR4: case JavaStackBehavior.PopR8: case JavaStackBehavior.PopRef: netImpact = -1; break; case JavaStackBehavior.Pop1_Pop1: case JavaStackBehavior.PopI_PopI: case JavaStackBehavior.PopI8_PopI8: case JavaStackBehavior.PopI8_PopI: case JavaStackBehavior.PopR4_PopR4: case JavaStackBehavior.PopR8_PopR8: case JavaStackBehavior.PopRef_Pop1: case JavaStackBehavior.PopRef_PopI: netImpact = -2; break; case JavaStackBehavior.PopRef_PopI_PopI: case JavaStackBehavior.PopRef_PopI_PopI8: case JavaStackBehavior.PopRef_PopI_PopR4: case JavaStackBehavior.PopRef_PopI_PopR8: case JavaStackBehavior.PopRef_PopI_PopRef: case JavaStackBehavior.PopRef_PopRef: netImpact = -3; break; case JavaStackBehavior.PopVar: switch (instruction.OpCode.OpCode) { case JavaOpCodeTag.Dup_x2: case JavaOpCodeTag.Dup2: case JavaOpCodeTag.Dup2_x1: case JavaOpCodeTag.Dup2_x2: netImpact = 1; break; case JavaOpCodeTag.Invokestatic: netImpact = -argumentSignatures.Count; if (returnTypeSignature != "V") netImpact++; break; case JavaOpCodeTag.Invokeinterface: case JavaOpCodeTag.Invokespecial: case JavaOpCodeTag.Invokevirtual: netImpact = -argumentSignatures.Count - 1; if (returnTypeSignature != "V") netImpact++; break; case JavaOpCodeTag.Multianewarray: netImpact = -instruction.Operands.Dimensions; break; default: throw new FormatException(); } break; default: throw new FormatException(); } switch (instruction.OpCode.StackBehaviorPush) { case JavaStackBehavior.Push0: break; case JavaStackBehavior.Push1: case JavaStackBehavior.PushI: case JavaStackBehavior.PushI8: case JavaStackBehavior.PushR4: case JavaStackBehavior.PushR8: case JavaStackBehavior.PushRef: case JavaStackBehavior.PushRet: netImpact++; break; case JavaStackBehavior.Push1_Push1: netImpact += 2; break; case JavaStackBehavior.PushVar: // these are all handled in the pop section break; default: throw new FormatException(); } switch (instruction.OpCode.FlowControl) { case JavaFlowControl.Next: case JavaFlowControl.Break: case JavaFlowControl.Call: case JavaFlowControl.ConditionalBranch: case JavaFlowControl.Special: if (!depths[index + 1].HasValue) { depths[index + 1] = depths[index] + netImpact; workQueue.Enqueue(index + 1); } if (instruction.OpCode.FlowControl == JavaFlowControl.ConditionalBranch) goto case JavaFlowControl.Branch; break; case JavaFlowControl.Branch: switch (instruction.OpCode.OpCode) { case JavaOpCodeTag.Lookupswitch: { LookupSwitchData switchData = (LookupSwitchData)disassembledMethod.SwitchData[instruction.Operands.SwitchDataIndex]; foreach (var pair in switchData.Pairs) { int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + pair.Value); if (!depths[nextIndex].HasValue) { depths[nextIndex] = depths[index] + netImpact; workQueue.Enqueue(nextIndex); } } break; } case JavaOpCodeTag.Tableswitch: { TableSwitchData switchData = (TableSwitchData)disassembledMethod.SwitchData[instruction.Operands.SwitchDataIndex]; foreach (var offset in switchData.Offsets) { int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + offset); if (!depths[nextIndex].HasValue) { depths[nextIndex] = depths[index] + netImpact; workQueue.Enqueue(nextIndex); } } break; } default: { // single branch target int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + instruction.Operands.BranchTarget); if (!depths[nextIndex].HasValue) { depths[nextIndex] = depths[index] + netImpact; workQueue.Enqueue(nextIndex); } break; } } break; case JavaFlowControl.Return: // no work in this method following this instruction break; case JavaFlowControl.Throw: // 'catch' blocks are handled separately break; case JavaFlowControl.Meta: throw new NotImplementedException(); default: throw new FormatException(); } if (workQueue.Count == 0) { for (int i = 0; i < depths.Length; i++) { if (depths[i].HasValue) continue; depths[i] = 1; workQueue.Enqueue(i); break; } } } return new ImmutableList<int?>(depths); }
public StepEventFilter(EventKind internalEventKind, RequestId requestId, SuspendPolicy suspendPolicy, IEnumerable<EventRequestModifier> modifiers, ThreadId thread, JvmtiEnvironment environment, JniEnvironment nativeEnvironment, StepSize size, StepDepth depth) : base(internalEventKind, requestId, suspendPolicy, modifiers, thread) { if (size == StepSize.Statement && JavaVM.DisableStatementStepping) size = StepSize.Line; _size = size; _depth = depth; // gather reference information for the thread using (var threadHandle = environment.VirtualMachine.GetLocalReferenceForThread(nativeEnvironment, thread)) { if (threadHandle.IsAlive) { jvmtiError error = environment.GetFrameLocation(threadHandle.Value, 0, out _lastMethod, out _lastLocation); if (error == jvmtiError.None) error = environment.GetFrameCount(threadHandle.Value, out _stackDepth); if (error == jvmtiError.None) _hasMethodInfo = true; UpdateLastLine(environment); if (error == jvmtiError.None && size == StepSize.Statement && (depth == StepDepth.Over || depth == StepDepth.Into)) { byte[] bytecode; JvmtiErrorHandler.ThrowOnFailure(environment.GetBytecodes(_lastMethod, out bytecode)); _disassembledMethod = BytecodeDisassembler.Disassemble(bytecode); TaggedReferenceTypeId declaringClass; JvmtiErrorHandler.ThrowOnFailure(environment.GetMethodDeclaringClass(nativeEnvironment, _lastMethod, out declaringClass)); using (var classHandle = environment.VirtualMachine.GetLocalReferenceForClass(nativeEnvironment, declaringClass.TypeId)) { int constantPoolCount; byte[] data; JvmtiErrorHandler.ThrowOnFailure(environment.GetConstantPool(classHandle.Value, out constantPoolCount, out data)); List<ConstantPoolEntry> entryList = new List<ConstantPoolEntry>(); int currentPosition = 0; for (int i = 0; i < constantPoolCount - 1; i++) { entryList.Add(ConstantPoolEntry.FromBytes(data, ref currentPosition)); switch (entryList.Last().Type) { case ConstantType.Double: case ConstantType.Long: // these entries take 2 slots entryList.Add(ConstantPoolEntry.Reserved); i++; break; default: break; } } _constantPool = entryList.AsReadOnly(); string classSignature; string classGenericSignature; JvmtiErrorHandler.ThrowOnFailure(environment.GetClassSignature(classHandle.Value, out classSignature, out classGenericSignature)); string methodName; string methodSignature; string methodGenericSignature; JvmtiErrorHandler.ThrowOnFailure(environment.GetMethodName(_lastMethod, out methodName, out methodSignature, out methodGenericSignature)); jobject classLoader; JvmtiErrorHandler.ThrowOnFailure(environment.GetClassLoader(classHandle.Value, out classLoader)); long classLoaderTag; JvmtiErrorHandler.ThrowOnFailure(environment.TagClassLoader(classLoader, out classLoaderTag)); ReadOnlyCollection<ExceptionTableEntry> exceptionTable; JvmtiErrorHandler.ThrowOnFailure(environment.VirtualMachine.GetExceptionTable(classLoaderTag, classSignature, methodName, methodSignature, out exceptionTable)); _evaluationStackDepths = BytecodeDisassembler.GetEvaluationStackDepths(_disassembledMethod, _constantPool, exceptionTable); } } } } }