File: probe.h

package info (click to toggle)
gammaray 3.3.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 21,612 kB
  • sloc: cpp: 94,643; ansic: 2,227; sh: 336; python: 164; yacc: 90; lex: 82; xml: 61; makefile: 26
file content (354 lines) | stat: -rw-r--r-- 11,528 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
/*
  probe.h

  This file is part of GammaRay, the Qt application inspection and manipulation tool.

  SPDX-FileCopyrightText: 2010 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
  Author: Volker Krause <volker.krause@kdab.com>

  SPDX-License-Identifier: GPL-2.0-or-later

  Contact KDAB at <info@kdab.com> for commercial licensing options.
*/

#ifndef GAMMARAY_PROBE_H
#define GAMMARAY_PROBE_H

#include "gammaray_core_export.h"
#include "signalspycallbackset.h"

#include <common/sourcelocation.h>

#include <QObject>
#include <QList>
#include <QPoint>
#include <QSet>
#include <QVector>

#include <memory>

QT_BEGIN_NAMESPACE
class QAbstractItemModel;
class QItemSelectionModel;
class QModelIndex;
class QThread;
class QTimer;
class QMutex;
class QRecursiveMutex;
class QSignalSpyCallbackSet;
QT_END_NAMESPACE

namespace GammaRay {
class ProbeCreator;
class ObjectListModel;
class ObjectTreeModel;
class MainWindow;
class BenchSuite;
class Server;
class ToolManager;
class ProblemCollector;
class MetaObjectRegistry;
namespace Execution {
class Trace;
}

/*!
 * Central entity of GammaRay: The probe is tracking the Qt application under test
 *
 * @note The Probe lifetime is strongly coupled with the QCoreApplication lifetime, if there's
 * no QCoreApplication instance, then there's no probe.
 *
 * To get a hold of the probe, call Probe::instance()
 */
class GAMMARAY_CORE_EXPORT Probe : public QObject
{
    Q_OBJECT
public:
    ~Probe() override;

    /*!
     * Returns the current instance of the probe.
     *
     * @note You must hold the object lock when using the probe's object tracking
     * functionality.
     *
     * @sa objectLock()
     */
    static Probe *instance();

    /*!
     * Returns true if the probe is initialized, false otherwise.
     */
    static bool isInitialized();

    ///@cond internal
    static void objectAdded(QObject *obj, bool fromCtor = false);
    static void objectRemoved(QObject *obj);
    ///@endcond

    /*!
     * Returns a list of all QObjects we know about.
     *
     * @note This getter can be used without the object lock. Do acquire the
     * object lock and check the pointer with @e isValidObject though, before
     * dereferencing any of the QObject pointers.
     */
    const QVector<QObject *> &allQObjects() const;

    /*!
     * Returns the object list model.
     * @return a pointer to a QAbstractItemModel instance.
     */
    QAbstractItemModel *objectListModel() const;
    /*!
     * Returns the object tree model.
     * @return a pointer to a QAbstractItemModel instance.
     */
    QAbstractItemModel *objectTreeModel() const;
    /*!
     * Register a model for remote usage.
     * @param objectName Unique identifier for the model, typically in reverse domain notation.
     * @param model The model to register.
     */
    static void registerModel(const QString &objectName, QAbstractItemModel *model);
    /*!
     * Install a global event filter.
     * Use this rather than installing the filter manually on QCoreApplication,
     * this will filter out GammaRay-internal events and objects already for you.
     */
    void installGlobalEventFilter(QObject *filter);
    /*!
     * Returns @c true if we haven't been able to track all objects from startup, ie. usually
     * when attaching at runtime.
     * If this is the case, we try to discover QObjects by walking the hierarchy, starting
     * from known singletons, and by watching out for unknown receivers of events.
     * This is far from complete obviously, and plug-ins can help finding more objects, using
     * specific knowledge about the types they are responsible for.
     *
     * Connect to the objectAdded(QObject*) signal on probe(), and call discoverObject(QObject*)
     * for "your" objects.
     *
     * @since 2.5
     */
    static bool needsObjectDiscovery();
    /*!
     * Notify the probe about QObjects your plug-in can discover by using information about
     * the types it can handle.
     * Only use this if needsObjectDiscovery() returns @c true to maximise performance.
     *
     * @see needsObjectDiscovery()
     * @since 2.0
     */
    void discoverObject(QObject *object);
    /*!
     * Notify the probe about the user selecting one of "your" objects via in-app interaction.
     * If you know the exact position the user interacted with, pass that in as @p pos.
     *
     * @since 2.0
     */
    void selectObject(QObject *object, const QPoint &pos = QPoint());
    void selectObject(QObject *object, const QString &toolId,
                      const QPoint &pos = QPoint());
    /*!
     * Notify the probe about the user selecting one of "your" objects.
     *
     * @since 2.1
     */
    void selectObject(void *object, const QString &typeName);


    /*!
     * Mark an object as favorite. Favorite objects might be shown
     * in a separate view
     * Connect to the objectFavorited(QObject*) signal on probe() to
     * know when an object gets marked as favorited
     */
    void markObjectAsFavorite(QObject *object);
    /*!
     * Unmark an object as favorite
     *
     * Connect to the objectUnfavorited(QObject*) signal on probe() to
     * know when an object gets removed as favorited
     */
    void removeObjectAsFavorite(QObject *object);

    /*!
     * Register a signal spy callback set.
     * Signal indexes provided as arguments are mapped to method indexes, ie. argument semantics
     * are the same with Qt4 and Qt5.
     *
     * @since 2.2
     */
    void registerSignalSpyCallbackSet(const SignalSpyCallbackSet &callbacks);

    /*! Returns the source code location @p object was created at. */
    static SourceLocation objectCreationSourceLocation(const QObject *object);
    /*! Returns the entire stack trace for the creation of @p object. */
    static Execution::Trace objectCreationStackTrace(QObject *object);

    ///@cond internal
    QObject *window() const;
    void setWindow(QObject *window);
    ///@endcond

    MetaObjectRegistry *metaObjectRegistry() const;

    /*!
     * Lock this to check the validity of a QObject
     * and to access it safely afterwards.
     */
    static QRecursiveMutex *objectLock();

    /*!
     * Check whether @p obj is still valid.
     *
     * @note The objectLock must be locked when this is called!
     */
    bool isValidObject(const QObject *obj) const
    {
        /// TODO: can we somehow assert(s_lock().isLocked()) ?!
        ///   -> Not with a recursive mutex. Make it non-recursive, and you can do Q_ASSERT(!s_lock().tryLock());
        return m_validObjects.contains(obj);
    }

    /*!
     * Determines if the specified QObject belongs to the GammaRay Probe or Window.
     *
     * These objects should not be tracked or shown to the user,
     * hence must be explicitly filtered.
     * @param obj is a pointer to a QObject instance.
     *
     * @return true if the specified QObject belongs to the GammaRay Probe
     * or Window; false otherwise.
     */
    bool filterObject(QObject *obj) const;

    ///@cond internal
    static void startupHookReceived();
    template<typename Func>
    static void executeSignalCallback(const Func &func);
    ///@endcond

    ProblemCollector *problemCollector() const;

signals:
    /*!
     * Emitted when the user selected @p object at position @p pos in the probed application.
     */
    void objectSelected(QObject *object, const QPoint &pos);
    void nonQObjectSelected(void *object, const QString &typeName);

    /*!
     * Emitted for newly created QObjects.
     *
     * Note:
     * - This signal is always emitted from the thread the probe exists in.
     * - The signal is emitted delayed enough for the QObject to have been fully constructed,
     *   i.e. on the next event loop re-entry.
     * - The signal is not emitted if the object has been destroyed completely again meanwhile,
     *   e.g. for objects that only existed on the stack.
     * - For objects created and destroyed in other threads, this signal might be emitted after
     *   its dtor has been entered (in case of short-lived objects), but before it has been finished.
     *   At this point the dtor might have already emitted the destroyed() signal and informed smart
     *   pointers about the destruction. This means you must not rely on any of this for object lifetime
     *   tracking for objects from other threads. Use objectDestroyed() instead.
     * - Do not put @p obj into a QWeakPointer, even if it's exclusively handled in the same thread as
     *   the Probe instance. Qt4 asserts if target code tries to put @p obj into a QSharedPointer afterwards.
     * - The objectLock() is locked.
     */
    void objectCreated(QObject *obj);

    /*!
     * Emitted for destroyed objects.
     *
     * Note:
     * - This signal is emitted from the thread the probe exists in.
     * - The signal is emitted from the end of the QObject dtor, dereferencing @p obj is no longer
     *   safe at this point.
     * - In a multi-threaded application, this signal might reach you way after @p obj has been
     *   destroyed, see isValidObject() for a way to check if the object is still valid before accessing it.
     * - The objectLock() is locked.
     */
    void objectDestroyed(QObject *obj);
    void objectReparented(QObject *obj);
    void objectFavorited(QObject *obj);
    void objectUnfavorited(QObject *obj);

    void aboutToDetach();

protected:
    ///@cond internal
    bool eventFilter(QObject *receiver, QEvent *event) override;
    ///@endcond

private slots:
    void delayedInit();
    void shutdown();

    void processQueuedObjectChanges();
    static void handleObjectDestroyed(QObject *obj);

private:
    friend class ProbeCreator;
    friend class BenchSuite;

    /* Returns @c true if we have working hooks in QtCore, that is we are notified reliably
     * about every QObject creation/destruction.
     * @since 2.0
     */
    QT_DEPRECATED static bool hasReliableObjectTracking();

    void objectFullyConstructed(QObject *obj);

    void queueCreatedObject(QObject *obj);
    void queueDestroyedObject(QObject *obj);
    bool isObjectCreationQueued(QObject *obj) const;
    void purgeChangesForObject(QObject *obj);
    void notifyQueuedObjectChanges();

    void findExistingObjects();

    /*! Check if we are capable of showing widgets. */
    static bool canShowWidgets();
    static void showInProcessUi();

    static void createProbe(bool findExisting);
    void resendServerAddress();

    explicit Probe(QObject *parent = nullptr);
    static QAtomicPointer<Probe> s_instance;

    /*! Set up all needed signal spy callbacks. */
    void setupSignalSpyCallbacks();

    ObjectListModel *m_objectListModel;
    ObjectTreeModel *m_objectTreeModel;
    ProblemCollector *m_problemCollector;
    ToolManager *m_toolManager;
    QObject *m_window;
    QSet<const QObject *> m_validObjects;
    MetaObjectRegistry *m_metaObjectRegistry;

    // all delayed object changes need to go through a single queue, as the order is crucial
    struct ObjectChange
    {
        QObject *obj;
        enum Type
        {
            Create,
            Destroy
        } type;
    };
    QVector<ObjectChange> m_queuedObjectChanges;

    QList<QObject *> m_pendingReparents;
    QTimer *m_queueTimer;
    QVector<QObject *> m_globalEventFilters;
    QVector<SignalSpyCallbackSet> m_signalSpyCallbacks;

    QSignalSpyCallbackSet *m_previousSignalSpyCallbackSet;
    Server *m_server;
};
}

#endif // GAMMARAY_PROBE_H