d/control: Recommend gdb
[gammaray-debian.git] / probe.cpp
blob25e0841869e398c7521573e92ad652c4398d91de
1 /*
2 probe.cpp
4 This file is part of GammaRay, the Qt application inspection and
5 manipulation tool.
7 Copyright (C) 2010-2011 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com
8 Author: Volker Krause <volker.krause@kdab.com>
9 Author: Stephen Kelly <stephen.kelly@kdab.com>
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 //krazy:excludeall=null,captruefalse,staticobjects
26 #include "probe.h"
27 #include "mainwindow.h"
28 #include "objectlistmodel.h"
29 #include "objecttreemodel.h"
30 #include "connectionmodel.h"
31 #include "toolmodel.h"
32 #include "readorwritelocker.h"
33 #include "tools/modelinspector/modeltest.h"
34 #include "hooking/functionoverwriterfactory.h"
36 #include <QtCore/QCoreApplication>
37 #include <QtCore/QThread>
38 #include <QMouseEvent>
39 #include <QDialog>
40 #include <QtCore/QTimer>
41 #include <QApplication>
43 #include <iostream>
44 #include <stdio.h>
46 #ifndef Q_OS_WIN
47 #include <dlfcn.h>
48 #else
49 #include <windows.h>
50 #endif
52 #include <assert.h>
54 #ifdef Q_OS_MAC
55 #include <dlfcn.h>
56 #include <inttypes.h>
57 #include <sys/mman.h>
58 #include <sys/types.h>
59 #include <sys/errno.h>
60 #endif
62 #define IF_DEBUG(x)
64 using namespace GammaRay;
65 using namespace std;
67 Probe *Probe::s_instance = 0;
68 bool functionsOverwritten = false;
70 #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
72 namespace GammaRay
75 static bool probeConnectCallback(void ** args)
77 QObject *sender = reinterpret_cast<QObject*>(args[0]);
78 const char *signal = reinterpret_cast<const char*>(args[1]);
79 QObject *receiver = reinterpret_cast<QObject*>(args[2]);
80 const char *method = reinterpret_cast<const char*>(args[3]);
81 const Qt::ConnectionType *type = reinterpret_cast<Qt::ConnectionType*>(args[4]);
82 Probe::connectionAdded(sender, signal, receiver, method, *type);
83 return false;
86 static bool probeDisconnectCallback(void ** args)
88 QObject *sender = reinterpret_cast<QObject*>(args[0]);
89 const char *signal = reinterpret_cast<const char*>(args[1]);
90 QObject *receiver = reinterpret_cast<QObject*>(args[2]);
91 const char *method = reinterpret_cast<const char*>(args[3]);
92 Probe::connectionRemoved(sender, signal, receiver, method);
93 return false;
97 #endif // QT_VERSION
99 // useful for debugging, dumps the object and all it's parents
100 // also useable from GDB!
101 void dumpObject(QObject *obj)
103 if (!obj) {
104 cout << "QObject(0x0)" << endl;
105 return;
108 do {
109 cout << obj->metaObject()->className() << "(" << hex << obj << ")";
110 obj = obj->parent();
111 if (obj) {
112 cout << " <- ";
114 } while(obj);
115 cout << endl;
118 struct Listener
120 Listener()
121 : filterThread(0),
122 trackDestroyed(true)
125 QThread *filterThread;
126 bool trackDestroyed;
129 Q_GLOBAL_STATIC(Listener, s_listener)
130 Q_GLOBAL_STATIC(QVector<QObject*>, s_addedBeforeProbeInsertion)
132 // ensures proper information is returned by isValidObject by
133 // locking it in objectAdded/Removed
134 class ObjectLock : public QReadWriteLock
136 public:
137 ObjectLock()
138 : QReadWriteLock(QReadWriteLock::Recursive)
141 Q_GLOBAL_STATIC(ObjectLock, s_lock)
143 ProbeCreator::ProbeCreator(Type type)
144 : m_type(type)
146 //push object into the main thread, as windows creates a
147 //different thread where this runs in
148 moveToThread(QApplication::instance()->thread());
149 // delay to foreground thread
150 QMetaObject::invokeMethod(this, "createProbe", Qt::QueuedConnection);
153 void ProbeCreator::createProbe()
155 QWriteLocker lock(s_lock());
156 // make sure we are in the ui thread
157 Q_ASSERT(QThread::currentThread() == qApp->thread());
159 if (!qApp || Probe::isInitialized()) {
160 // never create it twice
161 return;
164 // Exit early instead of asserting in QWidgetPrivate::init()
165 const QApplication * const qGuiApplication = qApp; // qobject_cast<const QApplication *>(qApp);
166 if (!qGuiApplication || qGuiApplication->type() == QApplication::Tty) {
167 cerr << "Unable to attach to a non-GUI application.\n"
168 << "Your application needs to use QApplication, "
169 << "otherwise GammaRay can not work." << endl;
170 return;
173 IF_DEBUG(cout << "setting up new probe instance" << endl;)
174 s_listener()->filterThread = QThread::currentThread();
175 Q_ASSERT(!Probe::s_instance);
176 Probe::s_instance = new Probe;
177 s_listener()->filterThread = 0;
178 IF_DEBUG(cout << "done setting up new probe instance" << endl;)
180 Q_ASSERT(Probe::instance());
181 QMetaObject::invokeMethod(Probe::instance(), "delayedInit", Qt::QueuedConnection);
182 foreach (QObject *obj, *(s_addedBeforeProbeInsertion())) {
183 Probe::objectAdded(obj);
185 s_addedBeforeProbeInsertion()->clear();
187 if (m_type == CreateAndFindExisting) {
188 Probe::findExistingObjects();
191 deleteLater();
194 Probe::Probe(QObject *parent):
195 QObject(parent),
196 m_objectListModel(new ObjectListModel(this)),
197 m_objectTreeModel(new ObjectTreeModel(this)),
198 m_connectionModel(new ConnectionModel(this)),
199 m_toolModel(new ToolModel(this)),
200 m_window(0),
201 m_queueTimer(new QTimer(this))
203 Q_ASSERT(thread() == qApp->thread());
204 IF_DEBUG(cout << "attaching GammaRay probe" << endl;)
206 if (qgetenv("GAMMARAY_MODELTEST") == "1") {
207 new ModelTest(m_objectListModel, m_objectListModel);
208 new ModelTest(m_objectTreeModel, m_objectTreeModel);
209 new ModelTest(m_connectionModel, m_connectionModel);
210 new ModelTest(m_toolModel, m_toolModel);
213 #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
214 QInternal::registerCallback(QInternal::ConnectCallback, &GammaRay::probeConnectCallback);
215 QInternal::registerCallback(QInternal::DisconnectCallback, &GammaRay::probeDisconnectCallback);
216 #endif
218 m_queueTimer->setSingleShot(true);
219 m_queueTimer->setInterval(0);
220 connect(m_queueTimer, SIGNAL(timeout()),
221 this, SLOT(queuedObjectsFullyConstructed()));
224 Probe::~Probe()
226 IF_DEBUG(cerr << "detaching GammaRay probe" << endl;)
228 #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0))
229 QInternal::unregisterCallback(QInternal::ConnectCallback, &GammaRay::probeConnectCallback);
230 QInternal::unregisterCallback(QInternal::DisconnectCallback, &GammaRay::probeDisconnectCallback);
231 #endif
233 s_instance = 0;
236 void Probe::setWindow(GammaRay::MainWindow *window)
238 m_window = window;
241 GammaRay::MainWindow *Probe::window() const
243 return m_window;
246 Probe *GammaRay::Probe::instance()
248 if (!qApp) {
249 return NULL;
252 return s_instance;
255 bool Probe::isInitialized()
257 return s_instance && qApp;
260 void Probe::delayedInit()
262 if (qgetenv("GAMMARAY_UNSET_PRELOAD") == "1") {
263 qputenv("LD_PRELOAD", "");
265 if (qgetenv("GAMMARAY_UNSET_DYLD") == "1") {
266 qputenv("DYLD_INSERT_LIBRARIES", "");
267 qputenv("DYLD_FORCE_FLAT_NAMESPACE", "");
270 QCoreApplication::instance()->installEventFilter(s_instance);
272 IF_DEBUG(cout << "creating GammaRay::MainWindow" << endl;)
273 s_listener()->filterThread = QThread::currentThread();
274 GammaRay::MainWindow *window = new GammaRay::MainWindow;
275 s_listener()->filterThread = 0;
276 IF_DEBUG(cout << "creation done" << endl;)
278 window->setAttribute(Qt::WA_DeleteOnClose);
279 instance()->setWindow(window);
280 instance()->setParent(window);
281 window->show();
284 bool Probe::filterObject(QObject *obj)
286 Probe *p = Probe::instance();
287 Q_ASSERT(p);
288 if (obj->thread() != p->thread()) {
289 // shortcut, never filter objects from a different thread
290 return false;
292 return obj == p || obj == p->window() ||
293 Util::descendantOf(p, obj) ||
294 Util::descendantOf(p->window(), obj);
297 QAbstractItemModel *Probe::objectListModel() const
299 return m_objectListModel;
302 QAbstractItemModel *Probe::objectTreeModel() const
304 return m_objectTreeModel;
307 QAbstractItemModel *Probe::connectionModel() const
309 return m_connectionModel;
312 ToolModel *Probe::toolModel() const
314 return m_toolModel;
317 QObject *Probe::probe() const
319 return const_cast<GammaRay::Probe*>(this);
322 bool Probe::isValidObject(QObject *obj) const
324 ///TODO: can we somehow assert(s_lock().isLocked()) ?!
325 return m_validObjects.contains(obj);
328 QReadWriteLock *Probe::objectLock() const
330 return s_lock();
333 void Probe::objectAdded(QObject *obj, bool fromCtor)
335 QWriteLocker lock(s_lock());
336 if (s_listener()->filterThread == obj->thread()) {
337 // Ignore
338 IF_DEBUG(cout
339 << "objectAdded Ignore: "
340 << hex << obj
341 << (fromCtor ? " (from ctor)" : "") << endl;)
342 return;
343 } else if (isInitialized()) {
344 if (filterObject(obj)) {
345 IF_DEBUG(cout
346 << "objectAdded Filter: "
347 << hex << obj
348 << (fromCtor ? " (from ctor)" : "") << endl;)
349 return;
350 } else if (instance()->m_validObjects.contains(obj)) {
351 // this happens when we get a child event before the objectAdded call from the ctor
352 // or when we add an item from s_addedBeforeProbeInsertion who got added already
353 // due to the add-parent-before-child logic
354 IF_DEBUG(cout
355 << "objectAdded Known: "
356 << hex << obj
357 << (fromCtor ? " (from ctor)" : "") << endl;)
358 Q_ASSERT(fromCtor || s_addedBeforeProbeInsertion()->contains(obj));
359 return;
362 // make sure we already know the parent
363 if (obj->parent() && !instance()->m_validObjects.contains(obj->parent())) {
364 objectAdded(obj->parent(), fromCtor);
366 Q_ASSERT(!obj->parent() || instance()->m_validObjects.contains(obj->parent()));
368 instance()->m_validObjects << obj;
369 if (s_listener()->trackDestroyed) {
370 // when we did not use a preload variant that
371 // overwrites qt_removeObject we must track object
372 // deletion manually
373 connect(obj, SIGNAL(destroyed(QObject*)),
374 instance(), SLOT(handleObjectDestroyed(QObject*)),
375 Qt::DirectConnection);
378 if (!fromCtor && obj->parent() && instance()->m_queuedObjects.contains(obj->parent())) {
379 // when a child event triggers a call to objectAdded while inside the ctor
380 // the parent is already tracked but it's call to objectFullyConstructed
381 // was delayed. hence we must do the same for the child for integrity
382 fromCtor = true;
385 IF_DEBUG(cout << "objectAdded: " << hex << obj
386 << (fromCtor ? " (from ctor)" : "")
387 << ", p: " << obj->parent() << endl;)
389 if (fromCtor) {
390 Q_ASSERT(!instance()->m_queuedObjects.contains(obj));
391 instance()->m_queuedObjects << obj;
392 if (!instance()->m_queueTimer->isActive()) {
393 // timers must not be started from a different thread
394 QMetaObject::invokeMethod(instance()->m_queueTimer, "start", Qt::AutoConnection);
396 } else {
397 instance()->objectFullyConstructed(obj);
399 } else {
400 IF_DEBUG(cout
401 << "objectAdded Before: "
402 << hex << obj
403 << (fromCtor ? " (from ctor)" : "") << endl;)
404 s_addedBeforeProbeInsertion()->push_back(obj);
408 void Probe::queuedObjectsFullyConstructed()
410 QWriteLocker lock(s_lock());
412 IF_DEBUG(cout << Q_FUNC_INFO << " " << m_queuedObjects.size() << endl;)
414 // must be called from the main thread via timeout
415 Q_ASSERT(QThread::currentThread() == thread());
417 // when this is called no object must be in the queue twice
418 // otherwise the cleanup procedures failed
419 Q_ASSERT(m_queuedObjects.size() == m_queuedObjects.toSet().size());
421 foreach (QObject *obj, m_queuedObjects) {
422 objectFullyConstructed(obj);
425 IF_DEBUG(cout << Q_FUNC_INFO << " done" << endl;)
427 m_queuedObjects.clear();
430 void Probe::objectFullyConstructed(QObject *obj)
432 // must be write locked
433 Q_ASSERT(!s_lock()->tryLockForRead());
435 if (!m_validObjects.contains(obj)) {
436 // deleted already
437 IF_DEBUG(cout << "stale fully constructed: " << hex << obj << endl;)
438 return;
439 } else if (filterObject(obj)) {
440 // when the call was delayed from the ctor construction,
441 // the parent might not have been set properly yet. hence
442 // apply the filter again
443 m_validObjects.remove(obj);
444 IF_DEBUG(cout << "now filtered fully constructed: " << hex << obj << endl;)
445 return;
448 IF_DEBUG(cout << "fully constructed: " << hex << obj << endl;)
450 // ensure we know the parent already
451 if (obj->parent() && !m_validObjects.contains(obj->parent())) {
452 objectAdded(obj->parent());
454 Q_ASSERT(!obj->parent() || m_validObjects.contains(obj->parent()));
456 m_objectListModel->objectAdded(obj);
457 m_toolModel->objectAdded(obj);
459 emit objectCreated(obj);
462 void Probe::objectRemoved(QObject *obj)
464 QWriteLocker lock(s_lock());
465 if (isInitialized()) {
466 IF_DEBUG(cout << "object removed:" << hex << obj << " " << obj->parent() << endl;)
468 bool success = instance()->m_validObjects.remove(obj);
469 if (!success) {
470 // object was not tracked by the probe, probably a gammaray object
471 return;
474 instance()->m_queuedObjects.removeOne(obj);
476 instance()->m_objectListModel->objectRemoved(obj);
478 instance()->connectionRemoved(obj, 0, 0, 0);
479 instance()->connectionRemoved(0, 0, obj, 0);
481 emit instance()->objectDestroyed(obj);
482 } else if (s_addedBeforeProbeInsertion()) {
483 for (QVector<QObject*>::iterator it = s_addedBeforeProbeInsertion()->begin();
484 it != s_addedBeforeProbeInsertion()->end();) {
485 if (*it == obj) {
486 it = s_addedBeforeProbeInsertion()->erase(it);
487 } else {
488 ++it;
494 void Probe::handleObjectDestroyed(QObject *obj)
496 objectRemoved(obj);
499 void Probe::connectionAdded(QObject *sender, const char *signal, QObject *receiver,
500 const char *method, Qt::ConnectionType type)
502 if (!isInitialized() || !sender || !receiver ||
503 s_listener()->filterThread == QThread::currentThread())
505 return;
508 ReadOrWriteLocker lock(s_lock());
509 if (filterObject(sender) || filterObject(receiver)) {
510 return;
513 instance()->m_connectionModel->connectionAdded(sender, signal, receiver, method, type);
516 void Probe::connectionRemoved(QObject *sender, const char *signal,
517 QObject *receiver, const char *method)
519 if (!isInitialized() || !s_listener() ||
520 s_listener()->filterThread == QThread::currentThread())
522 return;
525 ReadOrWriteLocker lock(s_lock());
526 if ((sender && filterObject(sender)) || (receiver && filterObject(receiver))) {
527 return;
530 instance()->m_connectionModel->connectionRemoved(sender, signal, receiver, method);
533 bool Probe::eventFilter(QObject *receiver, QEvent *event)
535 if (s_listener()->filterThread == receiver->thread()) {
536 return QObject::eventFilter(receiver, event);
539 if (event->type() == QEvent::ChildAdded || event->type() == QEvent::ChildRemoved) {
540 QChildEvent *childEvent = static_cast<QChildEvent*>(event);
541 QObject *obj = childEvent->child();
543 QWriteLocker lock(s_lock());
544 const bool tracked = m_validObjects.contains(obj);
545 const bool filtered = filterObject(obj);
547 IF_DEBUG(cout << "child event: " << hex << obj << ", p: " << obj->parent() << dec
548 << ", tracked: " << tracked
549 << ", filtered: " << filtered
550 << ", type: " << (childEvent->added() ? "added" : "removed") << endl;)
552 if (!filtered && childEvent->added()) {
553 if (!tracked) {
554 // was not tracked before, add to all models
555 // child added events are sent before qt_addObject is called,
556 // so we assumes this comes from the ctor
557 objectAdded(obj, true);
558 } else if (!m_queuedObjects.contains(obj)) {
559 // object is known already, just update the position in the tree
560 // BUT: only when we did not queue this item before
561 IF_DEBUG(cout << "update pos: " << hex << obj << endl;)
562 emit objectReparanted(obj);
564 } else if (tracked) {
565 objectRemoved(obj);
568 if (event->type() == QEvent::MouseButtonRelease) {
569 QMouseEvent *mouseEv = static_cast<QMouseEvent*>(event);
570 if (mouseEv->button() == Qt::LeftButton &&
571 mouseEv->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier)) {
572 QWidget *widget = QApplication::widgetAt(mouseEv->globalPos());
573 if (widget) {
574 emit widgetSelected(widget, widget->mapFromGlobal(mouseEv->globalPos()));
579 // make modal dialogs non-modal so that the gammaray window is still reachable
580 if (event->type() == QEvent::Show) {
581 QDialog *dlg = qobject_cast<QDialog*>(receiver);
582 if (dlg) {
583 dlg->setWindowModality(Qt::NonModal);
587 // we have no preloading hooks, so recover all objects we see
588 if (s_listener()->trackDestroyed && event->type() != QEvent::ChildAdded &&
589 event->type() != QEvent::ChildRemoved && !filterObject(receiver)) {
590 QWriteLocker lock(s_lock());
591 const bool tracked = m_validObjects.contains(receiver);
592 if (!tracked) {
593 objectAdded(receiver);
596 return QObject::eventFilter(receiver, event);
599 void Probe::findExistingObjects()
601 addObjectRecursive(QCoreApplication::instance());
602 foreach (QObject *obj, QApplication::topLevelWidgets()) {
603 addObjectRecursive(obj);
607 void Probe::addObjectRecursive(QObject *obj)
609 if (!obj) {
610 return;
612 objectRemoved(obj); // in case we find it twice
613 objectAdded(obj);
614 foreach (QObject *child, obj->children()) {
615 addObjectRecursive(child);
619 // taken from qobject.cpp
620 const int gammaray_flagged_locations_count = 2;
621 static const char *gammaray_flagged_locations[gammaray_flagged_locations_count] = {0};
623 const char *Probe::connectLocation(const char *member)
625 for (int i = 0; i < gammaray_flagged_locations_count; ++i) {
626 if (member == gammaray_flagged_locations[i]) {
627 // signature includes location information after the first null-terminator
628 const char *location = member + qstrlen(member) + 1;
629 if (*location != '\0') {
630 return location;
632 return 0;
635 return 0;
638 extern "C" Q_DECL_EXPORT void qt_startup_hook()
640 s_listener()->trackDestroyed = false;
642 new ProbeCreator(ProbeCreator::CreateOnly);
643 #if !defined Q_OS_WIN && !defined Q_OS_MAC
644 if (!functionsOverwritten) {
645 static void(*next_qt_startup_hook)() = (void (*)()) dlsym(RTLD_NEXT, "qt_startup_hook");
646 next_qt_startup_hook();
648 #endif
651 extern "C" Q_DECL_EXPORT void qt_addObject(QObject *obj)
653 Probe::objectAdded(obj, true);
654 #if !defined Q_OS_WIN && !defined Q_OS_MAC
655 if (!functionsOverwritten) {
656 static void (*next_qt_addObject)(QObject *obj) =
657 (void (*)(QObject *obj)) dlsym(RTLD_NEXT, "qt_addObject");
658 next_qt_addObject(obj);
660 #endif
663 extern "C" Q_DECL_EXPORT void qt_removeObject(QObject *obj)
665 Probe::objectRemoved(obj);
666 #if !defined Q_OS_WIN && !defined Q_OS_MAC
667 if (!functionsOverwritten) {
668 static void (*next_qt_removeObject)(QObject *obj) =
669 (void (*)(QObject *obj)) dlsym(RTLD_NEXT, "qt_removeObject");
670 next_qt_removeObject(obj);
672 #endif
675 #ifndef GAMMARAY_UNKNOWN_CXX_MANGLED_NAMES
676 #ifndef Q_OS_WIN
677 Q_DECL_EXPORT const char *qFlagLocation(const char *method)
678 #else
679 Q_DECL_EXPORT const char *myFlagLocation(const char *method)
680 #endif
682 static int gammaray_idx = 0;
683 gammaray_flagged_locations[gammaray_idx] = method;
684 gammaray_idx = (gammaray_idx+1) % gammaray_flagged_locations_count;
686 #ifndef Q_OS_WIN
687 static const char *(*next_qFlagLocation)(const char *method) =
688 (const char * (*)(const char *method)) dlsym(RTLD_NEXT, "_Z13qFlagLocationPKc");
690 Q_ASSERT_X(next_qFlagLocation, "",
691 "Recompile with GAMMARAY_UNKNOWN_CXX_MANGLED_NAMES enabled, "
692 "your compiler uses an unsupported C++ name mangling scheme");
693 return next_qFlagLocation(method);
694 #else
695 return method;
696 #endif
698 #endif
700 void overwriteQtFunctions()
702 functionsOverwritten = true;
703 AbstractFunctionOverwriter *overwriter = FunctionOverwriterFactory::createFunctionOverwriter();
705 overwriter->overwriteFunction(QLatin1String("qt_startup_hook"), (void*)qt_startup_hook);
706 overwriter->overwriteFunction(QLatin1String("qt_addObject"), (void*)qt_addObject);
707 overwriter->overwriteFunction(QLatin1String("qt_removeObject"), (void*)qt_removeObject);
708 #if defined(Q_OS_WIN)
709 #ifdef ARCH_64
710 overwriter->overwriteFunction(
711 QLatin1String("?qFlagLocation@@YAPEBDPEBD@Z"), (void*)myFlagLocation);
712 #else
713 overwriter->overwriteFunction(
714 QLatin1String("?qFlagLocation@@YAPBDPBD@Z"), (void*)myFlagLocation);
715 #endif
716 #endif
719 #ifdef Q_OS_WIN
720 extern "C" Q_DECL_EXPORT void gammaray_probe_inject();
722 extern "C" BOOL WINAPI DllMain(HINSTANCE/*hInstance*/, DWORD dwReason, LPVOID/*lpvReserved*/)
724 switch(dwReason) {
725 case DLL_PROCESS_ATTACH:
727 overwriteQtFunctions();
729 gammaray_probe_inject();
730 break;
732 case DLL_PROCESS_DETACH:
734 //Unloading does not work, because we overwrite existing code
735 exit(-1);
736 break;
739 return TRUE;
741 #endif
743 extern "C" Q_DECL_EXPORT void gammaray_probe_inject()
745 if (!qApp) {
746 return;
748 printf("gammaray_probe_inject()\n");
749 // make it possible to re-attach
750 new ProbeCreator(ProbeCreator::CreateAndFindExisting);
753 #ifdef Q_OS_MAC
754 // we need a way to execute some code upon load, so let's abuse
755 // static initialization
756 class HitMeBabyOneMoreTime
758 public:
759 HitMeBabyOneMoreTime()
761 overwriteQtFunctions();
765 static HitMeBabyOneMoreTime britney;
766 #endif
768 #include "probe.moc"