void preloadRepo() { auto& repo = Repo::get(); auto units = repo.enumerateUnits(RepoIdLocal, true, false); if (units.size() == 0) { units = repo.enumerateUnits(RepoIdCentral, true, false); } if (!units.size()) return; std::vector<std::thread> workers; auto numWorkers = Process::GetCPUCount(); // Compute a batch size that causes each thread to process approximately 16 // batches. Even if the batches are somewhat imbalanced in what they contain, // the straggler workers are very unlikey to take more than 10% longer than // the first worker to finish. size_t batchSize{std::max(units.size() / numWorkers / 16, size_t(1))}; std::atomic<size_t> index{0}; for (auto worker = 0; worker < numWorkers; ++worker) { workers.push_back(std::thread([&] { hphp_session_init(); hphp_context_init(); while (true) { auto begin = index.fetch_add(batchSize); auto end = std::min(begin + batchSize, units.size()); if (begin >= end) break; auto unitCount = end - begin; for (auto i = size_t{0}; i < unitCount; ++i) { auto& kv = units[begin + i]; try { lookupUnit(String(RuntimeOption::SourceRoot + kv.first).get(), "", nullptr); } catch (...) { // swallow errors silently } } } hphp_context_exit(); hphp_session_exit(); hphp_thread_exit(); })); } for (auto& worker : workers) { worker.join(); } }
bool HHVM_FUNCTION(could_include, const String& file) { return lookupUnit(file.get(), "", nullptr /* initial_opt */) != nullptr; }
bool RunToLocationCommand::executeImpl(DebuggerSession* session, folly::dynamic* /*responseMsg*/ ) { folly::dynamic& message = getMessage(); const folly::dynamic& args = tryGetObject(message, "arguments", s_emptyArgs); const folly::dynamic& source = tryGetObject(args, "source", s_emptyArgs); const std::string& filePath = tryGetString(source, "path", ""); if (filePath.empty()) { throw DebuggerCommandException( "Run to location source path not specified." ); } const ClientPreferences& prefs = m_debugger->getClientPreferences(); const std::string& path = File::TranslatePath(String(filePath)).toCppString(); BreakpointManager* bpMgr = session->getBreakpointManager(); int line = tryGetInt(args, "line", -1); if (!prefs.linesStartAt1) { // If client lines start at 0, make them 1 based. line++; } if (line <= 0) { throw DebuggerCommandException( "Invalid continue to line specified." ); } // See if there's already a breakpoint at this file + line. const auto bpIds = bpMgr->getBreakpointIdsByFile(path); for (auto it = bpIds.begin(); it != bpIds.end(); it++) { Breakpoint* bp = bpMgr->getBreakpointById(*it); if (bp->m_line == line) { // There's already a breakpoint installed at the run to location. // Just resume the request thread and let it hit. return true; } } // Find a compilation unit to place a temp bp in. HPHP::String unitPath(path.c_str()); const auto compilationUnit = lookupUnit(unitPath.get(), "", nullptr); if (compilationUnit == nullptr) { throw DebuggerCommandException( "Could not find a loaded compilation unit to run to location in!" ); } std::pair<int, int> runLocation = m_debugger->calibrateBreakpointLineInUnit(compilationUnit, line); if (runLocation.first < 0) { throw DebuggerCommandException( "Could not find a suitable location in the compilation unit to run to!" ); } if (!phpAddBreakPointLine(compilationUnit, runLocation.first)) { throw DebuggerCommandException( "Failed to install temporary breakpoint at location." ); } RequestInfo* ri = m_debugger->getRequestInfo(); ri->m_runToLocationInfo.path = path; ri->m_runToLocationInfo.line = line; // Tell the user where we're running to. Resolving the file path and // calibrating the source line could have modified things a bit. std::string userMsg = "Resuming request "; userMsg += std::to_string(targetThreadId(session)); userMsg += " and running to resolved location "; userMsg += path; userMsg += ":"; userMsg += std::to_string(runLocation.second); m_debugger->sendUserMessage(userMsg.c_str(), DebugTransport::OutputLevelInfo); // Resume only this request thread. return true; }
// (PHP-CALLBACK entry-point) This manually gets a lock where needed but // avoids holding one most of the time as this can be a quite slow operation. void runCallback() { hphp_session_init(Treadmill::SessionKind::Watchman); auto context = g_context.getNoCheck(); SCOPE_EXIT { hphp_context_exit(); hphp_session_exit(); { std::lock_guard<std::mutex> g(s_sharedDataMutex); processNextUpdate(); } }; try { std::string json_data; { std::lock_guard<std::mutex> g(s_sharedDataMutex); if (m_unprocessedCallbackData.empty()) { return; } auto& data = m_unprocessedCallbackData.back(); json_data = toJson(data); m_unprocessedCallbackData.pop_back(); } bool initial; auto unit = lookupUnit( String(m_callbackFile.c_str()).get(), "", &initial, Native::s_noNativeFuncs); if (!unit) { throw std::runtime_error( folly::sformat("Unit '{}' no longer exists.", m_callbackFile)); } auto unit_result = Variant::attach(context->invokeUnit(unit)); auto func = Unit::loadFunc(String(m_callbackFunc.c_str()).get()); if (!func) { throw std::runtime_error( folly::sformat("Callback '{}' no longer exists", m_callbackFunc)); } String str_path(m_path.c_str()); String str_query(m_query.c_str()); String str_name(m_name.c_str()); String str_json_data(json_data.c_str()); String str_socket_path(m_socketPath.c_str()); TypedValue args[] = { str_path.toCell(), str_query.toCell(), str_name.toCell(), str_json_data.toCell(), str_socket_path.toCell(), }; tvDecRefGen( context->invokeFuncFew(func, nullptr, // thisOrCls nullptr, // invName 5, // argc args) ); } catch(Exception& e) { if (m_error.empty()) { m_error = e.getMessage(); } } catch(Object& e) { if (m_error.empty()) { try { m_error = e->invokeToString().data(); } catch(...) { m_error = "PHP exception which cannot be turned into a string"; } } } catch(const std::exception& e) { if (m_error.empty()) { m_error = folly::exceptionStr(e).toStdString(); } } catch(...) { if (m_error.empty()) { m_error = "Unknown error (non std::exception)"; } } }