Keep track of whether we're jitting or not
Rather than test RuntimeOption::EvalJit and 5 thread locals to determine whether or not to run the jit on each re-entry, maintain one thread local. Make various RequestInjectionData fields private to ensure that the jit flag is kept in sync.
Esse commit está contido em:
@@ -509,7 +509,8 @@ ssize_t check_request_surprise(ThreadInfo *info) {
|
||||
bool do_timedout, do_memExceeded, do_signaled;
|
||||
|
||||
ssize_t flags = p.fetchAndClearFlags();
|
||||
do_timedout = (flags & RequestInjectionData::TimedOutFlag) && !p.debugger;
|
||||
do_timedout = (flags & RequestInjectionData::TimedOutFlag) &&
|
||||
!p.getDebugger();
|
||||
do_memExceeded = (flags & RequestInjectionData::MemExceededFlag);
|
||||
do_signaled = (flags & RequestInjectionData::SignaledFlag);
|
||||
|
||||
|
||||
@@ -503,7 +503,7 @@ void execute_command_line_end(int xhprof, bool coverage, const char *program) {
|
||||
}
|
||||
hphp_context_exit(g_context.getNoCheck(), true, true, program);
|
||||
hphp_session_exit();
|
||||
if (coverage && ti->m_reqInjectionData.coverage &&
|
||||
if (coverage && ti->m_reqInjectionData.getCoverage() &&
|
||||
!RuntimeOption::CodeCoverageOutputFile.empty()) {
|
||||
ti->m_coverage->Report(RuntimeOption::CodeCoverageOutputFile);
|
||||
}
|
||||
|
||||
@@ -128,12 +128,21 @@ void RequestInjectionData::onSessionInit() {
|
||||
|
||||
void RequestInjectionData::reset() {
|
||||
__sync_fetch_and_and(getConditionFlags(), 0);
|
||||
coverage = RuntimeOption::RecordCodeCoverage;
|
||||
debugger = false;
|
||||
debuggerIntr = false;
|
||||
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 &&
|
||||
!VM::shouldProfile();
|
||||
}
|
||||
|
||||
void RequestInjectionData::setMemExceededFlag() {
|
||||
__sync_fetch_and_or(getConditionFlags(),
|
||||
RequestInjectionData::MemExceededFlag);
|
||||
|
||||
@@ -394,8 +394,9 @@ public:
|
||||
|
||||
RequestInjectionData()
|
||||
: cflagsPtr(nullptr), surprisePage(nullptr), started(0), timeoutSeconds(-1),
|
||||
debugger(false), dummySandbox(false),
|
||||
debuggerIntr(false), coverage(false) {
|
||||
m_debugger(false), m_dummySandbox(false),
|
||||
m_debuggerIntr(false), m_coverage(false),
|
||||
m_jit(false) {
|
||||
}
|
||||
|
||||
inline volatile ssize_t* getConditionFlags() {
|
||||
@@ -411,12 +412,37 @@ public:
|
||||
|
||||
time_t started; // when a request was started
|
||||
int timeoutSeconds; // how many seconds to timeout
|
||||
private:
|
||||
bool m_debugger; // whether there is a DebuggerProxy attached to me
|
||||
bool m_dummySandbox; // indicating it is from a dummy sandbox thread
|
||||
bool m_debuggerIntr; // indicating we should force interrupt for debugger
|
||||
bool m_coverage; // is coverage being collected
|
||||
bool m_jit; // is the jit enabled
|
||||
public:
|
||||
bool getJit() const { return m_jit; }
|
||||
bool getDebugger() const { return m_debugger; }
|
||||
void setDebugger(bool d) {
|
||||
m_debugger = d;
|
||||
updateJit();
|
||||
}
|
||||
static uint32_t debuggerReadOnlyOffset() {
|
||||
return offsetof(RequestInjectionData, m_debugger);
|
||||
}
|
||||
bool getDebuggerIntr() const { return m_debuggerIntr; }
|
||||
void setDebuggerIntr(bool d) {
|
||||
m_debuggerIntr = d;
|
||||
updateJit();
|
||||
}
|
||||
bool getCoverage() const { return m_coverage; }
|
||||
void setCoverage(bool flag) {
|
||||
m_coverage = flag;
|
||||
updateJit();
|
||||
}
|
||||
bool getDummySandbox() const { return m_dummySandbox; }
|
||||
void setDummySandbox(bool ds) { m_dummySandbox = ds; }
|
||||
void updateJit();
|
||||
|
||||
bool debugger; // whether there is a DebuggerProxy attached to me
|
||||
bool dummySandbox; // indicating it is from a dummy sandbox thread
|
||||
bool debuggerIntr; // indicating we should force interrupt for debugger
|
||||
std::stack<void *> interrupts; // CmdInterrupts this thread's handling
|
||||
bool coverage; // is coverage being collected
|
||||
|
||||
void reset();
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ void Debugger::DebuggerSession(const DebuggerClientOptions& options,
|
||||
hphp_invoke_simple(options.extension);
|
||||
}
|
||||
ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
|
||||
ti->m_reqInjectionData.debugger = true;
|
||||
ti->m_reqInjectionData.setDebugger(true);
|
||||
if (!restart) {
|
||||
DebuggerDummyEnv dde;
|
||||
Debugger::InterruptSessionStarted(file.c_str());
|
||||
@@ -154,7 +154,7 @@ void Debugger::DebuggerSession(const DebuggerClientOptions& options,
|
||||
void Debugger::InterruptSessionStarted(const char *file,
|
||||
const char *error /* = NULL */) {
|
||||
TRACE(2, "Debugger::InterruptSessionStarted\n");
|
||||
ThreadInfo::s_threadInfo->m_reqInjectionData.debugger = true;
|
||||
ThreadInfo::s_threadInfo->m_reqInjectionData.setDebugger(true);
|
||||
Interrupt(SessionStarted, file, nullptr, error);
|
||||
}
|
||||
|
||||
@@ -165,14 +165,14 @@ void Debugger::InterruptSessionEnded(const char *file) {
|
||||
|
||||
void Debugger::InterruptRequestStarted(const char *url) {
|
||||
TRACE(2, "Debugger::InterruptRequestStarted\n");
|
||||
if (ThreadInfo::s_threadInfo->m_reqInjectionData.debugger) {
|
||||
if (ThreadInfo::s_threadInfo->m_reqInjectionData.getDebugger()) {
|
||||
Interrupt(RequestStarted, url);
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::InterruptRequestEnded(const char *url) {
|
||||
TRACE(2, "Debugger::InterruptRequestEnded: url=%s\n", url);
|
||||
if (ThreadInfo::s_threadInfo->m_reqInjectionData.debugger) {
|
||||
if (ThreadInfo::s_threadInfo->m_reqInjectionData.getDebugger()) {
|
||||
Interrupt(RequestEnded, url);
|
||||
}
|
||||
CStrRef sandboxId = g_context->getSandboxId();
|
||||
@@ -181,7 +181,7 @@ void Debugger::InterruptRequestEnded(const char *url) {
|
||||
|
||||
void Debugger::InterruptPSPEnded(const char *url) {
|
||||
TRACE(2, "Debugger::InterruptPSPEnded\n");
|
||||
if (ThreadInfo::s_threadInfo->m_reqInjectionData.debugger) {
|
||||
if (ThreadInfo::s_threadInfo->m_reqInjectionData.getDebugger()) {
|
||||
Interrupt(PSPEnded, url);
|
||||
}
|
||||
}
|
||||
@@ -193,7 +193,7 @@ bool Debugger::InterruptException(CVarRef e) {
|
||||
TRACE(2, "Debugger::InterruptException\n");
|
||||
if (RuntimeOption::EnableDebugger) {
|
||||
ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
|
||||
if (ti->m_reqInjectionData.debugger) {
|
||||
if (ti->m_reqInjectionData.getDebugger()) {
|
||||
HPHP::VM::Transl::VMRegAnchor _;
|
||||
InterruptVMHook(ExceptionThrown, e);
|
||||
}
|
||||
@@ -232,7 +232,7 @@ void Debugger::Interrupt(int type, const char *program,
|
||||
// Some cmds require us to interpret all instructions until the cmd
|
||||
// completes. Setting this will ensure we stay out of JIT code and in the
|
||||
// interpreter so phpDebuggerOpcodeHook has a chance to work.
|
||||
rjdata.debuggerIntr = proxy->needVMInterrupts();
|
||||
rjdata.setDebuggerIntr(proxy->needVMInterrupts());
|
||||
} else {
|
||||
TRACE(3, "proxy == null\n");
|
||||
// Debugger clients are disconnected abnormally, or this sandbox is not
|
||||
@@ -300,7 +300,7 @@ bool Debugger::isThreadDebugging(int64_t tid) {
|
||||
ThreadInfoMap::const_accessor acc;
|
||||
if (m_threadInfos.find(acc, tid)) {
|
||||
ThreadInfo* ti = acc->second;
|
||||
return (ti->m_reqInjectionData.debugger);
|
||||
return (ti->m_reqInjectionData.getDebugger());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -358,7 +358,7 @@ void Debugger::registerSandbox(const DSandboxInfo &sandbox) {
|
||||
// find out whether this sandbox is being debugged
|
||||
DebuggerProxyPtr proxy = findProxy(sid);
|
||||
if (proxy) {
|
||||
ti->m_reqInjectionData.debugger = true;
|
||||
ti->m_reqInjectionData.setDebugger(true);
|
||||
proxy->writeInjTablesToThread();
|
||||
}
|
||||
}
|
||||
@@ -392,19 +392,19 @@ void Debugger::requestInterrupt(DebuggerProxyPtr proxy) {
|
||||
TRACE(2, "Debugger::requestInterrupt\n");
|
||||
const StringData* sid = StringData::GetStaticString(proxy->getSandboxId());
|
||||
FOREACH_SANDBOX_THREAD_BEGIN(sid, ti)
|
||||
ti->m_reqInjectionData.debuggerIntr = true;
|
||||
ti->m_reqInjectionData.setDebuggerIntr(true);
|
||||
FOREACH_SANDBOX_THREAD_END()
|
||||
|
||||
sid = StringData::GetStaticString(proxy->getDummyInfo().id());
|
||||
FOREACH_SANDBOX_THREAD_BEGIN(sid, ti)
|
||||
ti->m_reqInjectionData.debuggerIntr = true;
|
||||
ti->m_reqInjectionData.setDebuggerIntr(true);
|
||||
FOREACH_SANDBOX_THREAD_END()
|
||||
}
|
||||
|
||||
void Debugger::setDebuggerFlag(const StringData* sandboxId, bool flag) {
|
||||
TRACE(2, "Debugger::setDebuggerFlag\n");
|
||||
FOREACH_SANDBOX_THREAD_BEGIN(sandboxId, ti)
|
||||
ti->m_reqInjectionData.debugger = flag;
|
||||
ti->m_reqInjectionData.setDebugger(flag);
|
||||
FOREACH_SANDBOX_THREAD_END()
|
||||
}
|
||||
|
||||
|
||||
@@ -662,7 +662,7 @@ BreakPointInfoPtr DebuggerProxy::getBreakPointAtCmd(CmdInterrupt& cmd) {
|
||||
void DebuggerProxy::readInjTablesFromThread() {
|
||||
TRACE(2, "DebuggerProxy::readInjTablesFromThread\n");
|
||||
ThreadInfo* ti = ThreadInfo::s_threadInfo.getNoCheck();
|
||||
if (ti->m_reqInjectionData.dummySandbox) {
|
||||
if (ti->m_reqInjectionData.getDummySandbox()) {
|
||||
return;
|
||||
}
|
||||
if (m_injTables) {
|
||||
|
||||
@@ -58,7 +58,7 @@ void DummySandbox::stop() {
|
||||
TRACE(2, "DummySandbox::stop\n");
|
||||
m_stopped = true;
|
||||
ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
|
||||
if (ti->m_reqInjectionData.dummySandbox) {
|
||||
if (ti->m_reqInjectionData.getDummySandbox()) {
|
||||
// called from dummy sandbox thread itself, schedule retirement
|
||||
Debugger::RetireDummySandboxThread(this);
|
||||
} else {
|
||||
@@ -82,7 +82,7 @@ struct CLISession : boost::noncopyable {
|
||||
TRACE(2, "CLISession::~CLISession\n");
|
||||
Debugger::UnregisterSandbox(g_context->getSandboxId());
|
||||
ThreadInfo::s_threadInfo.getNoCheck()->
|
||||
m_reqInjectionData.debugger = false;
|
||||
m_reqInjectionData.setDebugger(false);
|
||||
execute_command_line_end(0, false, nullptr);
|
||||
Eval::DebuggerClient::Shutdown();
|
||||
}
|
||||
@@ -94,7 +94,7 @@ void DummySandbox::run() {
|
||||
TRACE(2, "DummySandbox::run\n");
|
||||
ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
|
||||
Debugger::RegisterThread();
|
||||
ti->m_reqInjectionData.dummySandbox = true;
|
||||
ti->m_reqInjectionData.setDummySandbox(true);
|
||||
while (!m_stopped) {
|
||||
try {
|
||||
CLISession hphpSession;
|
||||
@@ -136,7 +136,7 @@ void DummySandbox::run() {
|
||||
g_context->setSandboxId(m_proxy->getDummyInfo().id());
|
||||
}
|
||||
|
||||
ti->m_reqInjectionData.debugger = true;
|
||||
ti->m_reqInjectionData.setDebugger(true);
|
||||
{
|
||||
DebuggerDummyEnv dde;
|
||||
Debugger::InterruptSessionStarted(nullptr, msg.c_str());
|
||||
|
||||
@@ -1528,7 +1528,7 @@ Array f_fb_call_user_func_array_safe(CVarRef function, CArrRef params) {
|
||||
|
||||
Variant f_fb_get_code_coverage(bool flush) {
|
||||
ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
|
||||
if (ti->m_reqInjectionData.coverage) {
|
||||
if (ti->m_reqInjectionData.getCoverage()) {
|
||||
Array ret = ti->m_coverage->Report();
|
||||
if (flush) {
|
||||
ti->m_coverage->Reset();
|
||||
@@ -1541,7 +1541,7 @@ Variant f_fb_get_code_coverage(bool flush) {
|
||||
void f_fb_enable_code_coverage() {
|
||||
ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
|
||||
ti->m_coverage->Reset();
|
||||
ti->m_reqInjectionData.coverage = true;
|
||||
ti->m_reqInjectionData.setCoverage(true);;
|
||||
if (g_vmContext->isNested()) {
|
||||
raise_notice("Calling fb_enable_code_coverage from a nested "
|
||||
"VM instance may cause unpredicable results");
|
||||
@@ -1551,7 +1551,7 @@ void f_fb_enable_code_coverage() {
|
||||
|
||||
Variant f_fb_disable_code_coverage() {
|
||||
ThreadInfo *ti = ThreadInfo::s_threadInfo.getNoCheck();
|
||||
ti->m_reqInjectionData.coverage = false;
|
||||
ti->m_reqInjectionData.setCoverage(false);
|
||||
Array ret = ti->m_coverage->Report();
|
||||
ti->m_coverage->Reset();
|
||||
return ret;
|
||||
|
||||
@@ -174,7 +174,7 @@ const StaticString s_include("include");
|
||||
DECODE_JMP(type, var); \
|
||||
pc += sizeof(type)
|
||||
#define DECODE_IVA(var) \
|
||||
int32_t var UNUSED = decodeVariableSizeImm(&pc); \
|
||||
int32_t var UNUSED = decodeVariableSizeImm(&pc); \
|
||||
ONTRACE(2, \
|
||||
Trace::trace("decode: Immediate int32 %" PRIi64"\n", \
|
||||
(int64_t)var));
|
||||
@@ -1854,11 +1854,7 @@ void VMExecutionContext::enterVMWork(ActRec* enterFnAr) {
|
||||
start = enterFnAr->m_func->getFuncBody();
|
||||
}
|
||||
Stats::inc(Stats::VMEnter);
|
||||
if (RuntimeOption::EvalJit &&
|
||||
!shouldProfile() &&
|
||||
!ThreadInfo::s_threadInfo->m_reqInjectionData.coverage &&
|
||||
!(RuntimeOption::EvalJitDisabledByHphpd && isDebuggerAttached()) &&
|
||||
LIKELY(!DEBUGGER_FORCE_INTR)) {
|
||||
if (LIKELY(ThreadInfo::s_threadInfo->m_reqInjectionData.getJit())) {
|
||||
Transl::SrcKey sk(curFunc(), m_pc);
|
||||
(void) curUnit()->offsetOf(m_pc); /* assert */
|
||||
tx64->enterTC(sk, start);
|
||||
@@ -7228,7 +7224,8 @@ inline void VMExecutionContext::dispatchImpl(int numInstrs) {
|
||||
const void **optab = optabDirect;
|
||||
InjectionTableInt64* injTable = g_vmContext->m_injTables ?
|
||||
g_vmContext->m_injTables->getInt64Table(InstHookTypeBCPC) : nullptr;
|
||||
bool collectCoverage = ThreadInfo::s_threadInfo->m_reqInjectionData.coverage;
|
||||
bool collectCoverage = ThreadInfo::s_threadInfo->
|
||||
m_reqInjectionData.getCoverage();
|
||||
if (injTable) {
|
||||
optab = optabInst;
|
||||
} else if (collectCoverage) {
|
||||
|
||||
@@ -55,7 +55,8 @@ void phpRemoveBreakPoint(const Unit* unit, Offset offset);
|
||||
|
||||
// Is this thread being debugged?
|
||||
static inline bool isDebuggerAttached() {
|
||||
return ThreadInfo::s_threadInfo.getNoCheck()->m_reqInjectionData.debugger;
|
||||
return ThreadInfo::s_threadInfo.getNoCheck()->
|
||||
m_reqInjectionData.getDebugger();
|
||||
}
|
||||
|
||||
#define DEBUGGER_ATTACHED_ONLY(code) do { \
|
||||
@@ -71,7 +72,7 @@ bool isDebuggerAttachedProcess();
|
||||
// out of JIT code. Second, that phpDebuggerOpcodeHook will continue to allow
|
||||
// debugger interrupts for every opcode executed (modulo filters.)
|
||||
#define DEBUGGER_FORCE_INTR \
|
||||
(ThreadInfo::s_threadInfo.getNoCheck()->m_reqInjectionData.debuggerIntr)
|
||||
(ThreadInfo::s_threadInfo.getNoCheck()->m_reqInjectionData.getDebuggerIntr())
|
||||
|
||||
// Map which holds a set of PCs and supports reasonably fast addition and
|
||||
// lookup. Used by the debugger to decide if a given PC falls within an
|
||||
|
||||
@@ -12009,7 +12009,7 @@ void TranslatorX64::addDbgGuardImpl(SrcKey sk, SrcRec& srcRec) {
|
||||
// Emit the checks for debugger attach
|
||||
emitTLSLoad<ThreadInfo>(a, ThreadInfo::s_threadInfo, rScratch);
|
||||
static COff dbgOff = offsetof(ThreadInfo, m_reqInjectionData) +
|
||||
offsetof(RequestInjectionData, debugger);
|
||||
RequestInjectionData::debuggerReadOnlyOffset();
|
||||
a. load_reg64_disp_reg32(rScratch, dbgOff, rScratch);
|
||||
a. testb((int8_t)0xff, rbyte(rScratch));
|
||||
// Branch to a special REQ_INTERPRET if attached
|
||||
|
||||
@@ -13,6 +13,12 @@
|
||||
| license@php.net so we can mail you a copy immediately. |
|
||||
+----------------------------------------------------------------------+
|
||||
*/
|
||||
#include "runtime/vm/type_profile.h"
|
||||
#include "runtime/base/runtime_option.h"
|
||||
#include "runtime/base/stats.h"
|
||||
#include "runtime/vm/translator/translator.h"
|
||||
#include "util/trace.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
@@ -20,13 +26,6 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "util/trace.h"
|
||||
#include "runtime/base/types.h"
|
||||
#include "runtime/base/runtime_option.h"
|
||||
#include "runtime/base/stats.h"
|
||||
#include "runtime/vm/translator/translator.h"
|
||||
#include "runtime/vm/type_profile.h"
|
||||
|
||||
namespace HPHP {
|
||||
namespace VM {
|
||||
|
||||
@@ -173,7 +172,13 @@ static inline bool profileThisRequest() {
|
||||
|
||||
void
|
||||
profileRequestStart() {
|
||||
profileOn = profileThisRequest();
|
||||
bool p = profileThisRequest();
|
||||
if (p != profileOn) {
|
||||
profileOn = p;
|
||||
if (!ThreadInfo::s_threadInfo.isNull()) {
|
||||
ThreadInfo::s_threadInfo->m_reqInjectionData.updateJit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void profileRequestEnd() {
|
||||
|
||||
@@ -16,7 +16,13 @@
|
||||
#ifndef TYPE_PROFILE_H_
|
||||
#define TYPE_PROFILE_H_
|
||||
|
||||
#include "runtime/base/types.h"
|
||||
#include "runtime/vm/hhbc.h"
|
||||
|
||||
namespace HPHP {
|
||||
|
||||
class StringData;
|
||||
|
||||
namespace VM {
|
||||
|
||||
struct TypeProfileKey {
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário