/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | | Copyright (c) 1998-2010 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 2.00 of the Zend license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.zend.com/license/2_00.txt. | | If you did not receive a copy of the Zend license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@zend.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "hphp/runtime/base/types.h" #include "hphp/runtime/base/hphp_system.h" #include "hphp/runtime/base/code_coverage.h" #include "hphp/runtime/base/smart_allocator.h" #include "hphp/runtime/vm/jit/target-cache.h" #include "hphp/util/lock.h" #include "hphp/util/alloc.h" using std::map; namespace HPHP { /////////////////////////////////////////////////////////////////////////////// static Mutex s_thread_info_mutex; static std::set s_thread_infos; __thread char* ThreadInfo::t_stackbase = 0; IMPLEMENT_THREAD_LOCAL_NO_CHECK_HOT(ThreadInfo, ThreadInfo::s_threadInfo); ThreadInfo::ThreadInfo() : m_stacklimit(0), m_executing(Idling) { assert(!t_stackbase); t_stackbase = static_cast(stack_top_ptr()); map &wrappers = ObjectAllocatorCollector::getWrappers(); m_allocators.resize(wrappers.rbegin()->first + 1); for (map::iterator it = wrappers.begin(); it != wrappers.end(); it++) { m_allocators[it->first] = it->second(); assert(it->second() != nullptr); } m_mm = MemoryManager::TheMemoryManager(); m_profiler = nullptr; m_pendingException = nullptr; m_coverage = new CodeCoverage(); Transl::TargetCache::threadInit(); onSessionInit(); Lock lock(s_thread_info_mutex); s_thread_infos.insert(this); } ThreadInfo::~ThreadInfo() { t_stackbase = 0; Lock lock(s_thread_info_mutex); s_thread_infos.erase(this); delete m_coverage; Transl::TargetCache::threadExit(); } bool ThreadInfo::valid(ThreadInfo* info) { Lock lock(s_thread_info_mutex); return s_thread_infos.find(info) != s_thread_infos.end(); } void ThreadInfo::GetExecutionSamples(std::map &counts) { Lock lock(s_thread_info_mutex); for (std::set::const_iterator iter = s_thread_infos.begin(); iter != s_thread_infos.end(); ++iter) { ++counts[(*iter)->m_executing]; } } void ThreadInfo::onSessionInit() { m_reqInjectionData.onSessionInit(); // Take the address of the cached per-thread stackLimit, and use this to allow // some slack for (a) stack usage above the caller of reset() and (b) stack // usage after the position gets checked. // If we're not in a threaded environment, then Util::s_stackSize will be // zero. Use getrlimit to figure out what the size of the stack is to // calculate an approximation of where the bottom of the stack should be. if (Util::s_stackSize == 0) { struct rlimit rl; getrlimit(RLIMIT_STACK, &rl); m_stacklimit = t_stackbase - (rl.rlim_cur - StackSlack); } else { m_stacklimit = (char *)Util::s_stackLimit + StackSlack; assert(uintptr_t(m_stacklimit) < (Util::s_stackLimit + Util::s_stackSize)); } } void ThreadInfo::clearPendingException() { m_reqInjectionData.clearPendingExceptionFlag(); if (m_pendingException != nullptr) delete m_pendingException; m_pendingException = nullptr; } void ThreadInfo::setPendingException(Exception* e) { m_reqInjectionData.setPendingExceptionFlag(); if (m_pendingException != nullptr) delete m_pendingException; m_pendingException = e; } void ThreadInfo::onSessionExit() { m_reqInjectionData.reset(); Transl::TargetCache::requestExit(); } void RequestInjectionData::onSessionInit() { Transl::TargetCache::requestInit(); cflagsPtr = Transl::TargetCache::conditionFlagsPtr(); reset(); started = time(0); } void RequestInjectionData::reset() { __sync_fetch_and_and(getConditionFlags(), 0); m_coverage = RuntimeOption::RecordCodeCoverage; m_debugger = false; m_debuggerIntr = false; updateJit(); while (!interrupts.empty()) interrupts.pop(); } void RequestInjectionData::updateJit() { m_jit = RuntimeOption::EvalJit && !(RuntimeOption::EvalJitDisabledByHphpd && m_debugger) && !m_debuggerIntr && !m_coverage && !shouldProfile(); } void RequestInjectionData::setMemExceededFlag() { __sync_fetch_and_or(getConditionFlags(), RequestInjectionData::MemExceededFlag); } void RequestInjectionData::setTimedOutFlag() { __sync_fetch_and_or(getConditionFlags(), RequestInjectionData::TimedOutFlag); } void RequestInjectionData::setSignaledFlag() { __sync_fetch_and_or(getConditionFlags(), RequestInjectionData::SignaledFlag); } void RequestInjectionData::setEventHookFlag() { __sync_fetch_and_or(getConditionFlags(), RequestInjectionData::EventHookFlag); } void RequestInjectionData::clearEventHookFlag() { __sync_fetch_and_and(getConditionFlags(), ~RequestInjectionData::EventHookFlag); } void RequestInjectionData::setPendingExceptionFlag() { __sync_fetch_and_or(getConditionFlags(), RequestInjectionData::PendingExceptionFlag); } void RequestInjectionData::clearPendingExceptionFlag() { __sync_fetch_and_and(getConditionFlags(), ~RequestInjectionData::PendingExceptionFlag); } void RequestInjectionData::setInterceptFlag() { __sync_fetch_and_or(getConditionFlags(), RequestInjectionData::InterceptFlag); } void RequestInjectionData::clearInterceptFlag() { __sync_fetch_and_and(getConditionFlags(), ~RequestInjectionData::InterceptFlag); } void RequestInjectionData::setDebuggerSignalFlag() { __sync_fetch_and_or(getConditionFlags(), RequestInjectionData::DebuggerSignalFlag); } ssize_t RequestInjectionData::fetchAndClearFlags() { return __sync_fetch_and_and(getConditionFlags(), (RequestInjectionData::EventHookFlag | RequestInjectionData::InterceptFlag)); } /////////////////////////////////////////////////////////////////////////////// }