Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qv4debugger.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4debugger.h"
5#include "qv4debugjob.h"
6#include "qv4datacollector.h"
7
8#include <private/qv4scopedvalue_p.h>
9#include <private/qv4script_p.h>
10#include <private/qqmlcontext_p.h>
11#include <private/qqmlengine_p.h>
12
14
18
19inline size_t qHash(const QV4Debugger::BreakPoint &b, size_t seed = 0) noexcept
20{
21 return qHash(b.fileName, seed) ^ b.lineNumber;
22}
23
26{
27 return a.lineNumber == b.lineNumber && a.fileName == b.fileName;
28}
29
31 : m_engine(engine)
32 , m_state(Running)
33 , m_stepping(NotStepping)
34 , m_pauseRequested(false)
35 , m_haveBreakPoints(false)
36 , m_breakOnThrow(false)
37 , m_returnedValue(engine, QV4::Value::undefinedValue())
38 , m_gatherSources(nullptr)
39 , m_runningJob(nullptr)
40 , m_collector(engine)
41{
42 static int debuggerId = qRegisterMetaType<QV4Debugger*>();
43 static int pauseReasonId = qRegisterMetaType<QV4Debugger::PauseReason>();
44 Q_UNUSED(debuggerId);
45 Q_UNUSED(pauseReasonId);
47 this, &QV4Debugger::runJobUnpaused, Qt::QueuedConnection);
48}
49
51{
52 return m_engine;
53}
54
56{
57 return &m_collector;
58}
59
61{
62 return &m_collector;
63}
64
66{
67 QMutexLocker locker(&m_lock);
68 if (m_state == Paused)
69 return;
70 m_pauseRequested = true;
71}
72
74{
75 QMutexLocker locker(&m_lock);
76 if (m_state != Paused)
77 return;
78
79 if (!m_returnedValue.isUndefined())
80 m_returnedValue.set(m_engine, QV4::Encode::undefined());
81
82 m_currentFrame = m_engine->currentStackFrame;
83 m_stepping = speed;
84 m_runningCondition.wakeAll();
85}
86
88{
89 return m_state;
90}
91
92void QV4Debugger::addBreakPoint(const QString &fileName, int lineNumber, const QString &condition)
93{
94 QMutexLocker locker(&m_lock);
95 m_breakPoints.insert(BreakPoint(fileName.mid(fileName.lastIndexOf('/') + 1),
96 lineNumber), condition);
97 m_haveBreakPoints = true;
98}
99
101{
102 QMutexLocker locker(&m_lock);
103 m_breakPoints.remove(BreakPoint(fileName.mid(fileName.lastIndexOf('/') + 1),
104 lineNumber));
105 m_haveBreakPoints = !m_breakPoints.isEmpty();
106}
107
109{
110 QMutexLocker locker(&m_lock);
111
112 m_breakOnThrow = onoff;
113}
114
116{
117 QMutexLocker locker(&m_lock);
118 m_pauseRequested = false;
119}
120
122{
124 state.fileName = QUrl(getFunction()->sourceFile()).fileName();
125 state.lineNumber = engine()->currentStackFrame->lineNumber();
126
127 return state;
128}
129
131 return m_pauseRequested || m_haveBreakPoints || m_gatherSources || m_stepping >= StepOver;
132}
133
134QVector<QV4::StackFrame> QV4Debugger::stackTrace(int frameLimit) const
135{
136 return m_engine->stackTrace(frameLimit);
137}
138
140{
141 if (m_runningJob) // do not re-enter when we're doing a job for the debugger.
142 return;
143
144 QMutexLocker locker(&m_lock);
145
146 if (m_gatherSources) {
147 m_gatherSources->run();
148 delete m_gatherSources;
149 m_gatherSources = nullptr;
150 }
151
152 switch (m_stepping) {
153 case StepOver:
154 if (m_currentFrame != m_engine->currentStackFrame)
155 break;
157 case StepIn:
158 pauseAndWait(Step);
159 return;
160 case StepOut:
161 case NotStepping:
162 break;
163 }
164
165 if (m_pauseRequested) { // Serve debugging requests from the agent
166 m_pauseRequested = false;
167 pauseAndWait(PauseRequest);
168 } else if (m_haveBreakPoints) {
169 if (QV4::Function *f = getFunction()) {
170 // lineNumber will be negative for Ret instructions, so those won't match
171 const int lineNumber = engine()->currentStackFrame->lineNumber();
172 if (reallyHitTheBreakPoint(f->sourceFile(), lineNumber))
173 pauseAndWait(BreakPointHit);
174 }
175 }
176}
177
179{
180 if (m_runningJob)
181 return;
182 QMutexLocker locker(&m_lock);
183
184 if (m_stepping == StepIn)
185 m_currentFrame = m_engine->currentStackFrame;
186}
187
189{
190 if (m_runningJob)
191 return;
192 Q_UNUSED(retVal); // TODO
193
194 QMutexLocker locker(&m_lock);
195
196 if (m_stepping != NotStepping && m_currentFrame == m_engine->currentStackFrame) {
197 m_currentFrame = m_currentFrame->parentFrame();
198 m_stepping = StepOver;
199 m_returnedValue.set(m_engine, retVal);
200 }
201}
202
204{
205 if (!m_breakOnThrow)
206 return;
207
208 if (m_runningJob) // do not re-enter when we're doing a job for the debugger.
209 return;
210
211 QMutexLocker locker(&m_lock);
212 pauseAndWait(Throwing);
213}
214
216{
217 if (m_engine->currentStackFrame)
218 return m_engine->currentStackFrame->v4Function;
219 else
220 return m_engine->globalCode;
221}
222
223void QV4Debugger::runJobUnpaused()
224{
225 QMutexLocker locker(&m_lock);
226 if (m_runningJob)
227 m_runningJob->run();
228 m_jobIsRunning.wakeAll();
229}
230
231void QV4Debugger::pauseAndWait(PauseReason reason)
232{
233 if (m_runningJob)
234 return;
235
236 m_state = Paused;
237 emit debuggerPaused(this, reason);
238
239 while (true) {
240 m_runningCondition.wait(&m_lock);
241 if (m_runningJob) {
242 m_runningJob->run();
243 m_jobIsRunning.wakeAll();
244 } else {
245 break;
246 }
247 }
248
249 m_state = Running;
250}
251
252bool QV4Debugger::reallyHitTheBreakPoint(const QString &filename, int linenr)
253{
254 const auto it = m_breakPoints.constFind(
255 BreakPoint(QUrl(filename).fileName(), linenr));
256 if (it == m_breakPoints.cend())
257 return false;
258 QString condition = it.value();
259 if (condition.isEmpty())
260 return true;
261
262 Q_ASSERT(m_runningJob == nullptr);
263 EvalJob evilJob(m_engine, condition);
264 m_runningJob = &evilJob;
265 m_runningJob->run();
266 m_runningJob = nullptr;
267
268 return evilJob.resultAsBoolean();
269}
270
272{
273 QMutexLocker locker(&m_lock);
274 runInEngine_havingLock(job);
275}
276
277void QV4Debugger::runInEngine_havingLock(QV4DebugJob *job)
278{
279 Q_ASSERT(job);
280 Q_ASSERT(m_runningJob == nullptr);
281
282 m_runningJob = job;
283 if (state() == Paused)
284 m_runningCondition.wakeAll();
285 else
287 m_jobIsRunning.wait(&m_lock);
288 m_runningJob = nullptr;
289}
290
292
293#include "moc_qv4debugger.cpp"
\inmodule QtCore
Definition qmutex.h:313
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
\inmodule QtCore
Definition qurl.h:94
QString fileName(ComponentFormattingOptions options=FullyDecoded) const
Definition qurl.cpp:2497
virtual void run()=0
void maybeBreakAtInstruction() override
QV4::Function * getFunction() const
void enteringFunction() override
void scheduleJob()
void runInEngine(QV4DebugJob *job)
void removeBreakPoint(const QString &fileName, int lineNumber)
QV4::ExecutionEngine * engine() const
void resume(Speed speed)
void debuggerPaused(QV4Debugger *self, QV4Debugger::PauseReason reason)
QVector< QV4::StackFrame > stackTrace(int frameLimit=-1) const
void setBreakOnThrow(bool onoff)
bool pauseAtNextOpportunity() const override
void leavingFunction(const QV4::ReturnedValue &retVal) override
State state() const
ExecutionState currentExecutionState() const
const QV4DataCollector * collector() const
QV4Debugger(QV4::ExecutionEngine *engine)
void aboutToThrow() override
void addBreakPoint(const QString &fileName, int lineNumber, const QString &condition=QString())
void clearPauseRequest()
void set(ExecutionEngine *engine, const Value &value)
bool wait(QMutex *, QDeadlineTimer=QDeadlineTimer(QDeadlineTimer::Forever))
QSet< QString >::iterator it
Combined button and popup list for selecting options.
quint64 ReturnedValue
@ QueuedConnection
#define Q_FALLTHROUGH()
size_t qHash(const QFileSystemWatcherPathKey &key, size_t seed=0)
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum condition
GLfloat GLfloat f
static Q_CONSTINIT QBasicAtomicInteger< unsigned > seed
Definition qrandom.cpp:196
bool operator==(const QRandomGenerator &rng1, const QRandomGenerator &rng2)
Definition qrandom.cpp:1220
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
#define Q_UNUSED(x)
connect(quitButton, &QPushButton::clicked, &app, &QCoreApplication::quit, Qt::QueuedConnection)
QObject::connect nullptr
BreakPoint(const QString &fileName, int line)
CppStackFrame * parentFrame() const
static constexpr ReturnedValue undefined()
CppStackFrame * currentStackFrame
StackTrace stackTrace(int frameLimit=-1) const