RegionDescPtr selectHotCFG(HotTransContext& ctx, TransIDSet& selectedSet, TransIDVec* selectedVec /* = nullptr */) { ITRACE(1, "selectHotCFG: starting with maxBCInstrs = {}\n", ctx.maxBCInstrs); auto const region = DFS(ctx.profData, *ctx.cfg, selectedSet, selectedVec, ctx.maxBCInstrs, ctx.inlining) .formRegion(ctx.tid); if (region->empty()) return nullptr; ITRACE(3, "selectHotCFG: before region_prune_arcs:\n{}\n", show(*region)); region_prune_arcs(*region, ctx.inputTypes); ITRACE(3, "selectHotCFG: before chainRetransBlocks:\n{}\n", show(*region)); region->chainRetransBlocks(); // Relax the region guards. if (RuntimeOption::EvalRegionRelaxGuards) { ITRACE(3, "selectHotCFG: before optimizeProfiledGuards:\n{}\n", show(*region)); optimizeProfiledGuards(*region, *ctx.profData); } ITRACE(1, "selectHotCFG: final version after optimizeProfiledGuards:\n{}\n", show(*region)); return region; }
void VideoDecoderVP8::updateFormatInfo(vbp_data_vp8 *data) { uint32_t width = data->codec_data->frame_width; uint32_t height = data->codec_data->frame_height; ITRACE("updateFormatInfo: current size: %d x %d, new size: %d x %d", mVideoFormatInfo.width, mVideoFormatInfo.height, width, height); if ((mVideoFormatInfo.width != width || mVideoFormatInfo.height != height) && width && height) { if ((VideoDecoderBase::alignMB(mVideoFormatInfo.width) != width) || (VideoDecoderBase::alignMB(mVideoFormatInfo.height) != height)) { mSizeChanged = true; ITRACE("Video size is changed."); } mVideoFormatInfo.width = width; mVideoFormatInfo.height = height; } mVideoFormatInfo.cropLeft = data->codec_data->crop_left; mVideoFormatInfo.cropRight = data->codec_data->crop_right; mVideoFormatInfo.cropTop = data->codec_data->crop_top; mVideoFormatInfo.cropBottom = data->codec_data->crop_bottom; ITRACE("Cropping: left = %d, top = %d, right = %d, bottom = %d", data->codec_data->crop_left, data->codec_data->crop_top, data->codec_data->crop_right, data->codec_data->crop_bottom); mVideoFormatInfo.valid = true; setRenderRect(); }
void reclaimFunction(const Func* func) { BlockingLeaseHolder writer(Translator::WriteLease()); auto it = s_funcTCData.find(func); if (it == s_funcTCData.end()) return; ITRACE(1, "Tearing down func {} (id={})\n", func->fullName()->data(), func->getFuncId()); Trace::Indent _i; auto& data = it->second; auto& us = mcg->ustubs(); ITRACE(1, "Smashing prologues\n"); clobberFuncGuards(func); for (auto& caller : data.callers) { ITRACE(1, "Unsmashing call @ {} (guard = {})\n", caller.first, caller.second.isGuard); // It should be impossible to reach a prologue that has been reclaimed // through an immutable stub, as this would imply the function is still // reachable. auto addr = caller.second.isGuard ? us.bindCallStub : nullptr; smashCall(caller.first, addr); s_smashedCalls.erase(caller.first); } auto movedData = folly::makeMoveWrapper(std::move(data)); auto fname = func->fullName()->data(); auto fid = func->getFuncId(); // We just smashed all of those callers-- treadmill the free to avoid a // race (threads executing callers may end up inside the guard even though // the function is now unreachable). Once the following block runs the guards // should be unreachable. Treadmill::enqueue([fname, fid, movedData] { BlockingLeaseHolder writer(Translator::WriteLease()); ITRACE(1, "Reclaiming func {} (id={})\n", fname, fid); Trace::Indent _i; { ITRACE(1, "Reclaiming Prologues\n"); Trace::Indent _i; for (auto& loc : movedData->prologues) { reclaimTranslation(loc); } } for (auto* rec : movedData->srcRecs) { reclaimSrcRec(rec); } }); s_funcTCData.erase(it); }
/* * Records any type/reffiness predictions we depend on in the region. Guards * for locals and stack cells that are not used will be eliminated by the call * to relaxGuards. */ void RegionFormer::recordDependencies() { // Record the incrementally constructed reffiness predictions. assertx(!m_region->empty()); auto& frontBlock = *m_region->blocks().front(); for (auto const& dep : m_refDeps.m_arMap) { frontBlock.addReffinessPred(m_startSk, {dep.second.m_mask, dep.second.m_vals, dep.first}); } // Relax guards and record the ones that survived. auto& firstBlock = *m_region->blocks().front(); auto blockStart = firstBlock.start(); auto& unit = m_irgs.unit; auto const doRelax = RuntimeOption::EvalHHIRRelaxGuards; bool changed = false; if (doRelax) { Timer _t(Timer::selectTracelet_relaxGuards); // The IR is going to be discarded immediately, so skip reflowing // the types in relaxGuards to save JIT time. RelaxGuardsFlags flags = m_profiling ? RelaxSimple : RelaxNormal; changed = relaxGuards(unit, *m_irgs.irb->guards(), flags); } auto guardMap = std::map<RegionDesc::Location,Type>{}; ITRACE(2, "Visiting guards\n"); visitGuards(unit, [&](const RegionDesc::Location& loc, Type type) { Trace::Indent indent; ITRACE(3, "{}: {}\n", show(loc), type); if (type <= TCls) return; auto inret = guardMap.insert(std::make_pair(loc, type)); if (inret.second) return; auto& oldTy = inret.first->second; if (oldTy == TGen) { // This is the case that we see an inner type prediction for a GuardLoc // that got relaxed to Gen. return; } oldTy &= type; }); for (auto& kv : guardMap) { if (kv.second == TGen) { // Guard was relaxed to Gen---don't record it. continue; } auto const preCond = RegionDesc::TypedLocation { kv.first, kv.second }; ITRACE(1, "selectTracelet adding guard {}\n", show(preCond)); firstBlock.addPreCondition(blockStart, preCond); } if (changed) { printUnit(3, unit, " after guard relaxation ", nullptr, m_irgs.irb->guards()); } }
void reclaimTranslation(TransLoc loc) { BlockingLeaseHolder writer(Translator::WriteLease()); ITRACE(1, "Reclaiming translation M[{}, {}] C[{}, {}] F[{}, {}]\n", loc.mainStart(), loc.mainEnd(), loc.coldStart(), loc.coldEnd(), loc.frozenStart(), loc.frozenEnd()); Trace::Indent _i; auto& cache = mcg->code(); cache.blockFor(loc.mainStart()).free(loc.mainStart(), loc.mainSize()); cache.blockFor(loc.coldStart()).free(loc.coldStart(), loc.coldSize()); if (loc.coldStart() != loc.frozenStart()) { cache.blockFor(loc.frozenStart()).free(loc.frozenStart(), loc.frozenSize()); } for (auto it = s_smashedBranches.begin(); it != s_smashedBranches.end();) { auto br = it++; if (loc.contains(br->first)) { ITRACE(1, "Erasing smashed branch @ {} from SrcRec addr={}\n", br->first, (void*)br->second); br->second->removeIncomingBranch(br->first); s_smashedBranches.erase(br); } } // Erase meta-data about these regions of the TC { ITRACE(1, "Clearing translation meta-data\n"); Trace::Indent _i; clearTCMaps(loc.mainStart(), loc.mainEnd()); clearTCMaps(loc.coldCodeStart(), loc.coldEnd()); clearTCMaps(loc.frozenCodeStart(), loc.frozenEnd()); } if (debug) { // Ensure no one calls into the function ITRACE(1, "Overwriting function\n"); auto clearBlock = [] (CodeBlock& cb) { X64Assembler a {cb}; while (cb.available() >= 2) a.ud2(); if (cb.available() > 0) a.int3(); always_assert(!cb.available()); }; CodeBlock main, cold, frozen; main.init(loc.mainStart(), loc.mainSize(), "Dead Main"); cold.init(loc.coldStart(), loc.coldSize(), "Dead Cold"); frozen.init(loc.frozenStart(), loc.frozenSize(), "Dead Frozen"); clearBlock(main); clearBlock(cold); clearBlock(frozen); } }
/* * Save current state for block. If this is the first time saving state for * block, create a new snapshot. Otherwise merge the current state into the * existing snapshot. */ void FrameState::save(Block* block) { ITRACE(4, "Saving state for B{}: {}\n", block->id(), show(*this)); auto it = m_snapshots.find(block); if (it != m_snapshots.end()) { merge(it->second); ITRACE(4, "Merged state: {}\n", show(*this)); } else { auto& snapshot = m_snapshots[block] = createSnapshot(); snapshot.inlineSavedStates = m_inlineSavedStates; } }
UnwindAction checkHandlers(const EHEnt* eh, const ActRec* const fp, PC& pc, Fault& fault) { auto const func = fp->m_func; ITRACE(1, "checkHandlers: func {} ({})\n", func->fullName()->data(), func->unit()->filepath()->data()); for (int i = 0;; ++i) { // Skip the initial m_handledCount - 1 handlers that were // considered before. if (fault.m_handledCount <= i) { fault.m_handledCount++; switch (eh->m_type) { case EHEnt::Type::Fault: ITRACE(1, "checkHandlers: entering fault at {}: save {}\n", eh->m_fault, func->unit()->offsetOf(pc)); pc = func->unit()->entry() + eh->m_fault; DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionHandlerHook()); return UnwindAction::ResumeVM; case EHEnt::Type::Catch: // Note: we skip catch clauses if we have a pending C++ exception // as part of our efforts to avoid running more PHP code in the // face of such exceptions. if (ThreadInfo::s_threadInfo->m_pendingException == nullptr) { auto const obj = fault.m_userException; for (auto& idOff : eh->m_catches) { ITRACE(1, "checkHandlers: catch candidate {}\n", idOff.second); auto handler = func->unit()->at(idOff.second); auto const cls = Unit::lookupClass( func->unit()->lookupNamedEntityId(idOff.first) ); if (!cls || !obj->instanceof(cls)) continue; ITRACE(1, "checkHandlers: entering catch at {}\n", idOff.second); pc = handler; DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionHandlerHook()); return UnwindAction::ResumeVM; } } break; } } if (eh->m_parentIndex != -1) { eh = &func->ehtab()[eh->m_parentIndex]; } else { break; } } return UnwindAction::Propagate; }
GuardConstraint relaxConstraint(GuardConstraint origGc, Type knownType, Type toRelax) { ITRACE(4, "relaxConstraint({}, knownType = {}, toRelax = {})\n", origGc, knownType, toRelax); Trace::Indent _i; // AssertType can be given TCtx, which should never relax. if (toRelax.maybe(TCctx)) { always_assert(toRelax <= TCtx); return origGc; } auto const dstType = knownType & toRelax; always_assert_flog(typeFitsConstraint(dstType, origGc), "refine({}, {}) doesn't fit {}", knownType, toRelax, origGc); // Preserve origGc's weak property. GuardConstraint newGc{DataTypeGeneric}; newGc.weak = origGc.weak; while (true) { if (newGc.isSpecialized()) { // We need to ask for the right kind of specialization, so grab it from // origGc. if (origGc.wantArrayKind()) newGc.setWantArrayKind(); if (origGc.wantClass()) newGc.setDesiredClass(origGc.desiredClass()); } auto const relaxed = relaxType(toRelax, newGc.category); auto const newDstType = relaxed & knownType; if (typeFitsConstraint(newDstType, origGc)) break; ITRACE(5, "newDstType = {}, newGc = {}; incrementing constraint\n", newDstType, newGc); incCategory(newGc.category); } // DataTypeCountness can be relaxed to DataTypeGeneric in // optimizeProfiledGuards, so we can't rely on this category to give type // information through guards. Since relaxConstraint is used to relax the // DataTypeCategory for guards, we cannot return DataTypeCountness unless we // already had it to start with. Instead, we return DataTypeBoxCountness, // which won't be further relaxed by optimizeProfiledGuards. if (newGc.category == DataTypeCountness && origGc != DataTypeCountness) { newGc.category = DataTypeBoxAndCountness; } ITRACE(4, "Returning {}\n", newGc); // newGc shouldn't be any more specific than origGc. always_assert(newGc.category <= origGc.category); return newGc; }
bool UeventObserver::threadLoop() { if (mUeventFd == -1) { ETRACE("invalid uEvent file descriptor"); return false; } struct pollfd fds[2]; int nr; fds[0].fd = mUeventFd; fds[0].events = POLLIN; fds[0].revents = 0; fds[1].fd = mExitRDFd; fds[1].events = POLLIN; fds[1].revents = 0; nr = poll(fds, 2, -1); if (nr > 0 && fds[0].revents == POLLIN) { int count = recv(mUeventFd, mUeventMessage, UEVENT_MSG_LEN - 2, 0); if (count > 0) { onUevent(); } } else if (fds[1].revents) { close(mExitRDFd); mExitRDFd = -1; ITRACE("exiting wait"); return false; } // always looping return true; }
static req::ptr<File> libxml_streams_IO_open_wrapper( const char *filename, const char* mode, bool read_only) { ITRACE(1, "libxml_open_wrapper({}, {}, {})\n", filename, mode, read_only); Trace::Indent _i; auto strFilename = String::attach(StringData::Make(filename, CopyString)); /* FIXME: PHP calls stat() here if the wrapper has a non-null stat handler, * in order to skip the open of a missing file, thus suppressing warnings. * Our stat handlers are virtual, so there's no easy way to tell if stat * is supported, so instead we will just call stat() for plain files, since * of the default transports, only plain files have support for stat(). */ if (read_only) { int pathIndex = 0; Stream::Wrapper* wrapper = Stream::getWrapperFromURI(strFilename, &pathIndex); if (dynamic_cast<FileStreamWrapper*>(wrapper)) { if (!HHVM_FN(file_exists)(strFilename)) { return nullptr; } } } // PHP unescapes the URI here, but that should properly be done by the // wrapper. The wrapper should expect a valid URI, e.g. file:///foo%20bar return File::Open(strFilename, mode, 0, tl_libxml_request_data->m_streams_context); }
static xmlOutputBufferPtr libxml_create_output_buffer(const char *URI, xmlCharEncodingHandlerPtr encoder, int compression ATTRIBUTE_UNUSED) { ITRACE(1, "libxml_create_output_buffer({}, {}, {})\n", URI, (void*)encoder, compression); Trace::Indent _i; if (URI == nullptr) { return nullptr; } // PHP unescapes the URI here, but that should properly be done by the // wrapper. The wrapper should expect a valid URI, e.g. file:///foo%20bar auto stream = libxml_streams_IO_open_wrapper(URI, "wb", false); if (!stream || stream->isInvalid()) { return nullptr; } // Allocate the Output buffer front-end. xmlOutputBufferPtr ret = xmlAllocOutputBuffer(encoder); if (ret != nullptr) { ret->context = rememberStream(std::move(stream)); ret->writecallback = libxml_streams_IO_write; ret->closecallback = libxml_streams_IO_close; } return ret; }
bool removeUnreachable(IRUnit& unit) { ITRACE(2, "removing unreachable blocks\n"); Trace::Indent _i; boost::dynamic_bitset<> visited(unit.numBlocks()); jit::vector<Block*> blocks; jit::vector<Block*> stack; blocks.reserve(unit.numBlocks()); stack.reserve(unit.numBlocks()); // Find all blocks reachable from the entry block. stack.push_back(unit.entry()); while (!stack.empty()) { auto* b = stack.back(); stack.pop_back(); if (visited.test(b->id())) continue; visited.set(b->id()); blocks.push_back(b); if (auto* taken = b->taken()) { if (!visited.test(taken->id())) stack.push_back(taken); } if (auto* next = b->next()) { if (!visited.test(next->id())) stack.push_back(next); } } // Walk through the reachable blocks and erase any preds that weren't // found. jit::vector<IRInstruction*> deadInsts; for (auto* block : blocks) { for (auto &edge : block->preds()) { auto* inst = edge.inst(); always_assert(!inst->isTransient()); if (!visited.test(inst->block()->id())) { deadInsts.push_back(inst); } } } for (auto* inst : deadInsts) { ITRACE(3, "removing unreachable B{}\n", inst->block()->id()); inst->setNext(nullptr); inst->setTaken(nullptr); } return !deadInsts.empty(); }
bool removeUnreachable(IRUnit& unit) { ITRACE(2, "removing unreachable blocks\n"); Trace::Indent _i; boost::dynamic_bitset<> visited(unit.numBlocks()); jit::vector<Block*> blocks; jit::vector<Block*> stack; blocks.reserve(unit.numBlocks()); stack.reserve(unit.numBlocks()); // Find all blocks reachable from the entry block. stack.push_back(unit.entry()); while (!stack.empty()) { auto* b = stack.back(); stack.pop_back(); if (visited.test(b->id())) continue; visited.set(b->id()); blocks.push_back(b); if (auto* taken = b->taken()) { if (!visited.test(taken->id())) stack.push_back(taken); } if (auto* next = b->next()) { if (!visited.test(next->id())) stack.push_back(next); } } // Walk through the reachable blocks and erase any preds that weren't // found. bool modified = false; for (auto* block: blocks) { auto& preds = block->preds(); for (auto it = preds.begin(); it != preds.end(); ) { auto* inst = it->inst(); ++it; if (!visited.test(inst->block()->id())) { ITRACE(3, "removing unreachable B{}\n", inst->block()->id()); inst->setNext(nullptr); inst->setTaken(nullptr); modified = true; } } } return modified; }
/* * relaxConstraint returns the least specific TypeConstraint 'tc' that doesn't * prevent the intersection of knownType and relaxType(toRelax, tc) from * satisfying origTc. It is used in IRBuilder::constrainValue and * IRBuilder::constrainStack to determine how to constrain the typeParam and * src values of CheckType/CheckStk instructions, and the src values of * AssertType/AssertStk instructions. * * AssertType example: * t24:Obj<C> = AssertType<{Obj<C>|InitNull}> t4:Obj * * If constrainValue is called with (t24, DataTypeSpecialized), relaxConstraint * will be called with (DataTypeSpecialized, Obj<C>|InitNull, Obj). After a few * iterations it will determine that constraining Obj with DataTypeCountness * will still allow the result type of the AssertType instruction to satisfy * DataTypeSpecialized, because relaxType(Obj, DataTypeCountness) == Obj. */ TypeConstraint relaxConstraint(const TypeConstraint origTc, const Type knownType, const Type toRelax) { ITRACE(4, "relaxConstraint({}, knownType = {}, toRelax = {})\n", origTc, knownType, toRelax); Trace::Indent _i; auto const dstType = refineType(knownType, toRelax); always_assert_flog(typeFitsConstraint(dstType, origTc), "refine({}, {}) doesn't fit {}", knownType, toRelax, origTc); // Preserve origTc's weak property. TypeConstraint newTc{DataTypeGeneric, DataTypeGeneric}; newTc.weak = origTc.weak; while (true) { if (newTc.isSpecialized()) { // We need to ask for the right kind of specialization, so grab it from // origTc. if (origTc.wantArrayKind()) newTc.setWantArrayKind(); if (origTc.wantClass()) newTc.setDesiredClass(origTc.desiredClass()); } auto const relaxed = relaxType(toRelax, newTc); auto const newDstType = refineType(relaxed, knownType); if (typeFitsConstraint(newDstType, origTc)) break; ITRACE(5, "newDstType = {}, newTc = {}; ", newDstType, newTc); if (newTc.category == DataTypeGeneric || !typeFitsOuterConstraint(newDstType, origTc)) { FTRACE(5, "incrementing outer\n"); incCategory(newTc.category); } else if (!typeFitsInnerConstraint(newDstType, origTc)) { FTRACE(5, "incrementing inner\n"); incCategory(newTc.innerCat); } else { not_reached(); } } ITRACE(4, "Returning {}\n", newTc); // newTc shouldn't be any more specific than origTc. always_assert(newTc.category <= origTc.category && newTc.innerCat <= origTc.innerCat); return newTc; }
int libxml_streams_IO_write(void* context, const char* buffer, int len) { ITRACE(1, "libxml_IO_write({}, {}, {})\n", context, (void*)buffer, len); Trace::Indent _i; auto stream = getStream(context); int64_t ret = stream->write(String(buffer, len, CopyString)); return (ret < INT_MAX) ? ret : -1; }
///// Methods for managing and merge block state ///// void FrameState::startBlock(Block* block) { auto it = m_snapshots.find(block); if (it != m_snapshots.end()) { load(it->second); ITRACE(4, "Loading state for B{}: {}\n", block->id(), show(*this)); m_inlineSavedStates = it->second.inlineSavedStates; m_snapshots.erase(it); } }
bool removeUnreachable(IRUnit& unit) { ITRACE(2, "removing unreachable blocks\n"); Trace::Indent _i; smart::hash_set<Block*, pointer_hash<Block>> visited; smart::stack<Block*> stack; stack.push(unit.entry()); // Find all blocks reachable from the entry block. while (!stack.empty()) { auto* b = stack.top(); stack.pop(); if (visited.count(b)) continue; visited.insert(b); if (auto* taken = b->taken()) { if (!visited.count(taken)) stack.push(taken); } if (auto* next = b->next()) { if (!visited.count(next)) stack.push(next); } } // Walk through the reachable blocks and erase any preds that weren't // found. bool modified = false; for (auto* block : visited) { auto& preds = block->preds(); for (auto it = preds.begin(); it != preds.end(); ) { auto* inst = it->inst(); ++it; if (!visited.count(inst->block())) { ITRACE(3, "removing unreachable B{}\n", inst->block()->id()); inst->setNext(nullptr); inst->setTaken(nullptr); modified = true; } } } return modified; }
void retypeDests(IRInstruction* inst, const IRUnit* unit) { for (int i = 0; i < inst->numDsts(); ++i) { auto const ssa = inst->dst(i); auto const oldType = ssa->type(); retypeDst(inst, i); if (ssa->type() != oldType) { ITRACE(5, "reflowTypes: retyped {} in {}\n", oldType.toString(), inst->toString()); } } }
void FrameState::refineLocalType(uint32_t id, Type type, SSATmp* typeSource) { always_assert(id < m_locals.size()); auto& local = m_locals[id]; Type newType = refineType(local.type, type); ITRACE(2, "updating local {}'s type: {} -> {}\n", id, local.type, newType); always_assert_flog(newType != Type::Bottom, "Bad new type for local {}: {} & {} = {}", id, local.type, type, newType); local.type = newType; local.typeSource = typeSource; }
int libxml_streams_IO_close(void* context) { ITRACE(1, "libxml_IO_close({}), sweeping={}\n", context, MemoryManager::sweeping()); Trace::Indent _i; if (MemoryManager::sweeping()) { // Stream wrappers close themselves on sweep, so there's nothing to do here return 0; } return forgetStream(context) ? 0 : -1; }
bool IRBuilder::constrainLocal(uint32_t locId, SSATmp* typeSrc, TypeConstraint tc, const std::string& why) { if (!shouldConstrainGuards()) return false; always_assert(IMPLIES(tc.innerCat > DataTypeGeneric, tc.category >= DataTypeCountness)); ITRACE(1, "constrainLocal({}, {}, {}, {})\n", locId, typeSrc ? typeSrc->inst()->toString() : "null", tc, why); Indent _i; if (!typeSrc) return false; if (!typeSrc->isA(Type::FramePtr)) { return constrainValue(typeSrc, tc); } // When typeSrc is a FramePtr, that means we loaded the value the local had // coming into the trace. Trace through the FramePtr chain, looking for a // guard for this local id. If we find it, constrain the guard. If we don't // find it, there wasn't a guard for this local so there's nothing to // constrain. auto guard = guardForLocal(locId, typeSrc); while (guard) { if (guard->is(AssertLoc)) { // If the refined type of the local satisfies the constraint we're // trying to apply, we can stop here. This can happen if we assert a // more general type than what we already know. Otherwise we need to // keep tracing back to the guard. if (typeFitsConstraint(guard->typeParam(), tc)) return false; guard = guardForLocal(locId, guard->src(0)); } else { assert(guard->is(GuardLoc, CheckLoc)); ITRACE(2, "found guard to constrain\n"); return constrainGuard(guard, tc); } } ITRACE(2, "no guard to constrain\n"); return false; }
void retypeDests(IRInstruction* inst, const IRUnit* unit) { for (int i = 0; i < inst->numDsts(); ++i) { auto const ssa = inst->dst(i); auto const oldType = ssa->type(); retypeDst(inst, i); if (!ssa->type().equals(oldType)) { ITRACE(5, "reflowTypes: retyped {} in {}\n", oldType.toString(), inst->toString()); } } assert(checkOperandTypes(inst, unit)); }
int libxml_streams_IO_write(void* context, const char* buffer, int len) { ITRACE(1, "libxml_IO_write({}, {}, {})\n", context, (void*)buffer, len); Trace::Indent _i; Resource stream(static_cast<ResourceData*>(context)); String strBuffer(StringData::Make(buffer, len, CopyString)); Variant ret = HHVM_FN(fwrite)(stream, strBuffer); if (ret.isInteger() && ret.asInt64Val() < INT_MAX) { return (int)ret.asInt64Val(); } else { return -1; } }
/* * Returns true iff a guard to constrain was found, and tc was more specific * than the guard's existing constraint. Note that this doesn't necessarily * mean that the guard was constrained: tc.weak might be true. */ bool IRBuilder::constrainGuard(IRInstruction* inst, TypeConstraint tc) { if (!shouldConstrainGuards()) return false; auto& guard = m_guardConstraints[inst]; auto changed = false; auto const assertFits = typeFitsConstraint(guard.assertedType, tc); ITRACE(2, "constrainGuard({}, {}): existing constraint {}, assertFits: {}\n", *inst, tc, guard, assertFits ? "true" : "false"); Indent _i; // For category and innerCat, constrain the guard if the assertedType isn't // strong enough to fit what we want and tc is more specific than the // existing category. if (!assertFits && tc.innerCat > guard.innerCat) { if (!tc.weak) { ITRACE(1, "constraining inner type of {}: {} -> {}\n", *inst, guard.innerCat, tc.innerCat); guard.innerCat = tc.innerCat; } changed = true; } else { ITRACE(2, "not constraining innerCat\n"); } if (!assertFits && tc.category > guard.category) { if (!tc.weak) { ITRACE(1, "constraining {}: {} -> {}\n", *inst, guard.category, tc.category); guard.category = tc.category; } changed = true; } else { ITRACE(2, "not constraining category\n"); } // It's fairly common to have a local that we've asserted to be Obj, and then // later assert that it's Obj<C>|InitNull. We want to use their intersection, // so in this case we'd assert Obj<C>. always_assert(tc.assertedType.maybe(guard.assertedType)); auto assertCommon = tc.assertedType & guard.assertedType; if (assertCommon < guard.assertedType) { // We don't check tc.weak here because assertedType is supposed to be // statically known type information. ITRACE(1, "using {} to refine assertedType of {}: {} -> {}\n", tc.assertedType, *inst, guard.assertedType, assertCommon); guard.assertedType = assertCommon; } else { ITRACE(2, "not refining assertedType\n"); } return changed; }
UnwindAction checkHandlers(const EHEnt* eh, const ActRec* const fp, PC& pc, Fault& fault) { auto const func = fp->m_func; ITRACE(1, "checkHandlers: func {} ({})\n", func->fullName()->data(), func->unit()->filepath()->data()); for (int i = 0;; ++i) { // Skip the initial m_handledCount - 1 handlers that were // considered before. if (fault.m_handledCount <= i) { fault.m_handledCount++; switch (eh->m_type) { case EHEnt::Type::Fault: ITRACE(1, "checkHandlers: entering fault at {}: save {}\n", eh->m_handler, func->unit()->offsetOf(pc)); pc = func->unit()->at(eh->m_handler); DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionHandlerHook()); return UnwindAction::ResumeVM; case EHEnt::Type::Catch: ITRACE(1, "checkHandlers: entering catch at {}\n", eh->m_handler); pc = func->unit()->at(eh->m_handler); DEBUGGER_ATTACHED_ONLY(phpDebuggerExceptionHandlerHook()); return UnwindAction::ResumeVM; } } if (eh->m_parentIndex != -1) { eh = &func->ehtab()[eh->m_parentIndex]; } else { break; } } return UnwindAction::Propagate; }
int libxml_streams_IO_read(void* context, char* buffer, int len) { ITRACE(1, "libxml_IO_read({}, {}, {})\n", context, (void*)buffer, len); Trace::Indent _i; auto stream = getStream(context); assert(len >= 0); if (len > 0) { String str = stream->read(len); if (str.size() <= len) { std::memcpy(buffer, str.data(), str.size()); return str.size(); } } return -1; }
/* * Reclaim all translations associated with a SrcRec including the anchor * translation. */ void reclaimSrcRec(const SrcRec* rec) { assertOwnsCodeLock(); assertOwnsMetadataLock(); ITRACE(1, "Reclaiming SrcRec addr={} anchor={}\n", (void*)rec, rec->getFallbackTranslation()); Trace::Indent _i; auto anchor = rec->getFallbackTranslation(); code().blockFor(anchor).free(anchor, svcreq::stub_size()); for (auto& loc : rec->translations()) { reclaimTranslation(loc); } }
int libxml_streams_IO_read(void* context, char* buffer, int len) { ITRACE(1, "libxml_IO_read({}, {}, {})\n", context, (void*)buffer, len); Trace::Indent _i; Resource stream(static_cast<ResourceData*>(context)); assert(len >= 0); Variant ret = HHVM_FN(fread)(stream, len); if (ret.isString()) { const String& str = ret.asCStrRef(); if (str.size() <= len) { std::memcpy(buffer, str.data(), str.size()); return str.size(); } } return -1; }
int libxml_streams_IO_close(void* context) { ITRACE(1, "libxml_IO_close({}), sweeping={}\n", context, MemoryManager::sweeping()); Trace::Indent _i; if (MemoryManager::sweeping()) { // Stream wrappers close themselves on sweep, so there's nothing to do here return 0; } Resource stream(static_cast<ResourceData*>(context)); // Release the reference owned by context. Guaranteed to not go to zero since // we just created one belonging to stream. stream.get()->decRefCount(); return HHVM_FN(fclose)(stream) ? 0 : -1; }
static xmlParserInputPtr libxml_ext_entity_loader(const char *url, const char *id, xmlParserCtxtPtr context) { ITRACE(1, "libxml_ext_entity_loader({}, {}, {})\n", url, id, (void*)context); Trace::Indent _i; auto protocol = Stream::getWrapperProtocol(url); if (!allow_ext_entity_protocol(protocol)) { raise_warning("Protocol '%s' for external XML entity '%s' is disabled for" " security reasons. This may be changed using the" " hhvm.libxml.ext_entity_whitelist ini setting.", protocol.c_str(), url); return nullptr; } return s_default_entity_loader(url, id, context); }