void VirtualMachine::execute(const InstructionList& script) { ScriptValue emptyVar; emptyVar.Flags = ScriptValue::VariableRef; emptyVar.Type = -2; emptyVar.Pointer = 0x0; std::map<std::string, ScriptValue>::iterator it; for(int i = 0; i < script.size(); ++i) { const Instruction& cmd = script[i]; bool jump = true; switch(cmd.Type) { case PUSH: push(cmd.Value); break; case GETVAR: it = Variables.find(cmd.Name); if(it == Variables.end()) it = Variables.emplace(cmd.Name, emptyVar).first; pushRef(&((*it).second)); break; case EOL: //clearStack(); break; case FUNCALL: { std::vector<int> arguments; arguments.reserve(cmd.NumArguments); for(int i = 0; i < cmd.NumArguments; ++i) arguments.push_back(peek(i).Type); FunctionDeclaration* fn = Environment->resolveFunctionCall(cmd.Name, arguments); if(fn) (*(fn->FnPtr))(this); else { std::cout << "RUNTIME ERROR: No function matching " << cmd.Name << "("; for(int i = 0; i < cmd.NumArguments; ++i) { if(i != 0) std::cout << ", "; std::cout << Config->NativeTypes[peek(i).Type].Name; } std::cout << ")" << std::endl; } break; } case OBJCALL: { std::vector<int> arguments; arguments.reserve(cmd.NumArguments); for(int i = 0; i < cmd.NumArguments; ++i) arguments.push_back(peek(i+1).Type); FunctionDeclaration* fn = Environment->resolveMethodCall(peek(0).Type, cmd.Name, arguments); if(fn) (*(fn->FnPtr))(this); else { std::cout << "RUNTIME ERROR: No function matching " << cmd.Name << "("; for(int i = 0; i < cmd.NumArguments; ++i) { if(i != 0) std::cout << ", "; std::cout << Config->NativeTypes[peek(1+i).Type].Name; } std::cout << ") in " << Config->NativeTypes[peek(0).Type].Name << std::endl; } break; } case SUBCALL: { int targetPos = -1; for(int j = 0; j < script.size(); ++j) { if(script[j].Type == LABEL) if(script[j].LabelID == cmd.LabelID) { targetPos = j; break; } } if(targetPos != -1) { CallStack[CallStackPosition].ReturnAddress = i; CallStack[CallStackPosition].StackPosition = StackPosition; CallStackPosition++; i = targetPos; } break; } case SUBENTER: { int targetPos = -1; for(int j = i; j < script.size(); ++j) { if(script[j].Type == LABEL) if(script[j].LabelID == cmd.LabelID) { targetPos = j; break; } } if(targetPos != -1) { CallStack[CallStackPosition].ReturnAddress = targetPos; CallStack[CallStackPosition].StackPosition = StackPosition; CallStackPosition++; } break; } case RETURN: if(CallStackPosition > 0) { ScriptValue retValue = pop(); CallStackPosition--; i = CallStack[CallStackPosition].ReturnAddress; StackPosition = CallStack[CallStackPosition].StackPosition; push(retValue); } break; case JUMPTO: case JUMPIF: case JUMPIFN: if(cmd.Type == JUMPTO ) jump = true; else if(cmd.Type == JUMPIF ) jump = pop().as<int>(); else if(cmd.Type == JUMPIFN) jump = !pop().as<int>(); if(jump) { int targetPos = -1; for(int j = 0; j < script.size(); ++j) { if(script[j].Type == LABEL) if(script[j].LabelID == cmd.LabelID) { targetPos = j; break; } } if(targetPos != -1) i = targetPos; } break; case LABEL: // Ignore. break; default: std::cout << "Unimplemented Instruction " << cmd.Type << std::endl; break; } } }
/// <summary>Handle <c>ICorProfilerCallback::JITCompilationStarted</c></summary> /// <remarks>The 'workhorse' </remarks> HRESULT STDMETHODCALLTYPE CCodeInjection::JITCompilationStarted( /* [in] */ FunctionID functionId, /* [in] */ BOOL fIsSafeToBlock) { ModuleID moduleId; mdToken funcToken; std::wstring methodName = GetMethodName(functionId, moduleId, funcToken); ATLTRACE(_T("::JITCompilationStarted(%X -> %s)"), functionId, W2CT(methodName.c_str())); if (L"ProfilerTarget.Program.OnMethodToInstrument" == methodName && m_targetMethodRef !=0 ) { // get method body LPCBYTE pMethodHeader = NULL; ULONG iMethodSize = 0; COM_FAIL_RETURN(m_profilerInfo3->GetILFunctionBody( moduleId, funcToken, &pMethodHeader, &iMethodSize), S_OK); CComPtr<IMetaDataEmit> metaDataEmit; COM_FAIL_RETURN(m_profilerInfo3->GetModuleMetaData(moduleId, ofRead | ofWrite, IID_IMetaDataEmit, (IUnknown**)&metaDataEmit), S_OK); // parse IL Method instMethod((IMAGE_COR_ILMETHOD*)pMethodHeader); // <-- instMethod.SetMinimumStackSize(3); // should be correct for this sample // NOTE: build signature (in the knowledge that the method we are instrumenting currently has no local vars) static COR_SIGNATURE localSignature[] = { IMAGE_CEE_CS_CALLCONV_LOCAL_SIG, 0x02, ELEMENT_TYPE_ARRAY, ELEMENT_TYPE_OBJECT, 01, 00, 00, ELEMENT_TYPE_ARRAY, ELEMENT_TYPE_OBJECT, 01, 00, 00 }; mdSignature signature; COM_FAIL_RETURN(metaDataEmit->GetTokenFromSig(localSignature, sizeof(localSignature), &signature), S_OK); instMethod.m_header.LocalVarSigTok = signature; // insert new IL block InstructionList instructions; // NOTE: this IL will be different for an instance method or if the local vars signature is different instructions.push_back(new Instruction(CEE_NOP)); instructions.push_back(new Instruction(CEE_LDC_I4_2)); instructions.push_back(new Instruction(CEE_NEWARR, m_objectTypeRef)); instructions.push_back(new Instruction(CEE_STLOC_1)); instructions.push_back(new Instruction(CEE_LDLOC_1)); instructions.push_back(new Instruction(CEE_LDC_I4_0)); instructions.push_back(new Instruction(CEE_LDARG_0)); instructions.push_back(new Instruction(CEE_STELEM_REF)); instructions.push_back(new Instruction(CEE_LDLOC_1)); instructions.push_back(new Instruction(CEE_LDC_I4_1)); instructions.push_back(new Instruction(CEE_LDARG_1)); instructions.push_back(new Instruction(CEE_STELEM_REF)); instructions.push_back(new Instruction(CEE_LDLOC_1)); instructions.push_back(new Instruction(CEE_STLOC_0)); instructions.push_back(new Instruction(CEE_LDLOC_0)); instructions.push_back(new Instruction(CEE_CALL, m_targetMethodRef)); instMethod.InsertSequenceInstructionsAtOriginalOffset( 0, instructions); instMethod.DumpIL(); // allocate memory CComPtr<IMethodMalloc> methodMalloc; COM_FAIL_RETURN(m_profilerInfo3->GetILFunctionBodyAllocator( moduleId, &methodMalloc), S_OK); void* pNewMethod = methodMalloc->Alloc(instMethod.GetMethodSize()); // write new method instMethod.WriteMethod((IMAGE_COR_ILMETHOD*)pNewMethod); COM_FAIL_RETURN(m_profilerInfo3->SetILFunctionBody(moduleId, funcToken, (LPCBYTE) pNewMethod), S_OK); // update IL maps ULONG mapSize = instMethod.GetILMapSize(); void* pMap = CoTaskMemAlloc(mapSize * sizeof(COR_IL_MAP)); instMethod.PopulateILMap(mapSize, (COR_IL_MAP*)pMap); COM_FAIL_RETURN(m_profilerInfo3->SetILInstrumentedCodeMap( functionId, TRUE, mapSize, (COR_IL_MAP*)pMap), S_OK); CoTaskMemFree(pMap); } return S_OK; }
bool ThreadPlanStepRange::SetNextBranchBreakpoint() { if (m_next_branch_bp_sp) return true; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_STEP)); // Stepping through ranges using breakpoints doesn't work yet, but with this // off we fall back to instruction // single stepping. if (!m_use_fast_step) return false; lldb::addr_t cur_addr = GetThread().GetRegisterContext()->GetPC(); // Find the current address in our address ranges, and fetch the disassembly // if we haven't already: size_t pc_index; size_t range_index; InstructionList *instructions = GetInstructionsForAddress(cur_addr, range_index, pc_index); if (instructions == nullptr) return false; else { Target &target = GetThread().GetProcess()->GetTarget(); uint32_t branch_index; branch_index = instructions->GetIndexOfNextBranchInstruction(pc_index, target); Address run_to_address; // If we didn't find a branch, run to the end of the range. if (branch_index == UINT32_MAX) { uint32_t last_index = instructions->GetSize() - 1; if (last_index - pc_index > 1) { InstructionSP last_inst = instructions->GetInstructionAtIndex(last_index); size_t last_inst_size = last_inst->GetOpcode().GetByteSize(); run_to_address = last_inst->GetAddress(); run_to_address.Slide(last_inst_size); } } else if (branch_index - pc_index > 1) { run_to_address = instructions->GetInstructionAtIndex(branch_index)->GetAddress(); } if (run_to_address.IsValid()) { const bool is_internal = true; m_next_branch_bp_sp = GetTarget().CreateBreakpoint(run_to_address, is_internal, false); if (m_next_branch_bp_sp) { if (log) { lldb::break_id_t bp_site_id = LLDB_INVALID_BREAK_ID; BreakpointLocationSP bp_loc = m_next_branch_bp_sp->GetLocationAtIndex(0); if (bp_loc) { BreakpointSiteSP bp_site = bp_loc->GetBreakpointSite(); if (bp_site) { bp_site_id = bp_site->GetID(); } } log->Printf("ThreadPlanStepRange::SetNextBranchBreakpoint - Setting " "breakpoint %d (site %d) to run to address 0x%" PRIx64, m_next_branch_bp_sp->GetID(), bp_site_id, run_to_address.GetLoadAddress( &m_thread.GetProcess()->GetTarget())); } m_next_branch_bp_sp->SetThreadID(m_thread.GetID()); m_next_branch_bp_sp->SetBreakpointKind("next-branch-location"); return true; } else return false; } } return false; }
Value Interpreter::exec(const InstructionList &instructions, Bindings &bindings) { Stack stack; ClosureValues closureValues; for(InstructionList::const_iterator it = instructions.begin() ; it != instructions.end() ; ++it) { Instruction::Type type = it->type(); const Value &value = it->value(); switch(type) { case Instruction::PUSH: if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " push " << value << '\n'; } stack.push_back(value); break; case Instruction::CALL: { if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " call " << value << '\n'; } Value result = handleFunction(it->sourceLocation(), value, stack, bindings); stack.push_back(result); if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " return value " << result << '\n'; } } break; case Instruction::JUMP: { int instructionsToSkip = getInstructionsToSkip(type, value); int remaining = instructions.end() - it; if(remaining < instructionsToSkip) { throw CompilerBug("insufficient instructions available to skip! (remaining: " + str(remaining) + " < instructionsToSkip: " + str(instructionsToSkip) + ")"); } if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " jumping back " << instructionsToSkip << '\n'; } it += instructionsToSkip; } break; case Instruction::LOOP: { int instructionsToSkip = getInstructionsToSkip(type, value); int instructionsAvailable = instructions.size(); // Note: signed type is important! if(instructionsAvailable < instructionsToSkip) { throw CompilerBug("insufficient instructions available to loop! (instructionsToSkip: " + str(instructionsToSkip) + " > instructions.size(): " + str(instructions.size()) + ")"); } if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " looping back " << instructionsToSkip << " instructions\n"; } it -= instructionsToSkip; } break; case Instruction::CLOSE: { if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " close " << value << '\n'; } Value result = handleClose(value, stack, closureValues, bindings); stack.push_back(result); } break; case Instruction::COND_JUMP: { if(stack.empty()) { throw CompilerBug("empty stack when testing conditional jump"); } int instructionsToSkip = getInstructionsToSkip(type, value); int remaining = instructions.end() - it; if(remaining < instructionsToSkip) { throw CompilerBug("insufficient instructions available to skip! (remaining: " + str(remaining) + " < instructionsToSkip: " + str(instructionsToSkip) + ")"); } Value top = pop(stack); if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " jumping back " << instructionsToSkip << " if " << top << '\n'; } if(top.isFalsey()) { it += instructionsToSkip; } } break; case Instruction::REF_LOCAL: handleRef(Bindings::Local, value, stack, bindings); if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " local ref '" << value.string() << "' is " << stack.back() << '\n'; } break; case Instruction::INIT_LOCAL: { const Value &intialisedValue = handleInit(Bindings::Local, value, stack, bindings); if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " local init '" << value.string() << "' to " << intialisedValue << '\n'; } } break; case Instruction::ASSIGN_LOCAL: { const Value &assignedValue = handleAssign(Bindings::Local, value, stack, bindings); if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " local assign '" << value.string() << "' to " << assignedValue << '\n'; } } break; case Instruction::REF_GLOBAL: handleRef(Bindings::Global, value, stack, bindings); if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " global ref '" << value.string() << "' is " << stack.back() << '\n'; } break; case Instruction::INIT_GLOBAL: { const Value &intialisedValue = handleInit(Bindings::Global, value, stack, bindings); if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " global init '" << value.string() << "' to " << intialisedValue << '\n'; } } break; case Instruction::ASSIGN_GLOBAL: { const Value &assignedValue = handleAssign(Bindings::Global, value, stack, bindings); if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " global assign '" << value.string() << "' to " << assignedValue << '\n'; } } break; case Instruction::REF_CLOSURE: handleRef(Bindings::Closure, value, stack, bindings); if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " closure ref '" << value.string() << "' is " << stack.back() << '\n'; } break; case Instruction::INIT_CLOSURE: { Identifier identifier = Identifier(value.string()); Bindings::ValuePtr &binding = bindings.getPointer(identifier); closureValues.push_back(ClosedNameAndValue(identifier, binding)); if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " closure init '" << value.string() << "' is " << *binding << '\n'; } } break; case Instruction::ASSIGN_CLOSURE: { const Value &assignedValue = handleAssign(Bindings::Closure, value, stack, bindings); if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " closure assign '" << value.string() << "' to " << assignedValue << '\n'; } } break; case Instruction::MEMBER_ACCESS: { if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " member access " << value << '\n'; } assert(value.isString()); const std::string &memberName = value.string(); Value top = pop(stack); if(!top.isObject()) { throw ExecutionError(it->sourceLocation(), "Member access instruction requires an object but got " + str(top)); } const Value::Object &object = top.object(); Value::Object::const_iterator memberIterator = object.find(memberName); if (memberIterator == object.end()) { throw ExecutionError(it->sourceLocation(), "Unknown member name " + memberName + " for " + str(top)); } if(settings_.trace) { std::cout << "DEBUG: " << it->sourceLocation() << " member access " << value.string() << "." << memberName << " was " << memberIterator->second << '\n'; } stack.push_back(memberIterator->second); } break; default: throw CompilerBug("unhandled instruction type: " + str(type)); } if (settings_.trace) { if (stack.empty()) { std::cout << "Stack is empty\n"; } else { std::cout << "Stack contains " << stack.size() << " entries:\n"; int index = 0; for(Stack::const_iterator it = stack.begin() ; it != stack.end() ; ++it) { ++index; std::cout << index << ": " << *it << '\n'; } } if (closureValues.empty()) { std::cout << "closureValues is empty\n"; } else { std::cout << "closureValues contains " << closureValues.size() << " entries:\n"; int index = 0; for(const ClosedNameAndValue &closedValue: closureValues) { ++index; std::cout << index << ": " << closedValue.first << " -> " << *closedValue.second << " @ " << closedValue.second << '\n'; } } } } return stack.empty() ? Value::nil() : pop(stack); }