1/****************************************************************************
2**
3** Copyright (C) 2015 Klaralvdalens Datakonsult AB (KDAB).
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt3D module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qsysteminformationservice_p.h"
41#include "qsysteminformationservice_p_p.h"
42
43#ifdef Q_OS_ANDROID
44#include <QtCore/QStandardPaths>
45#endif
46
47#include <QtCore/QThreadPool>
48#include <QtCore/QCoreApplication>
49#include <QtCore/QFile>
50#include <QtCore/QDateTime>
51#include <QtCore/QUrl>
52#include <QtCore/QDir>
53#include <QtGui/QDesktopServices>
54
55#include <Qt3DCore/QAspectEngine>
56#include <Qt3DCore/QAbstractAspect>
57#include <Qt3DCore/private/qabstractaspect_p.h>
58#include <Qt3DCore/private/qaspectengine_p.h>
59#include <Qt3DCore/private/aspectcommanddebugger_p.h>
60
61QT_BEGIN_NAMESPACE
62
63namespace {
64
65struct FrameHeader
66{
67 FrameHeader()
68 : frameId(0)
69 , jobCount(0)
70 , frameType(WorkerJob)
71 {
72 }
73
74 enum FrameType {
75 WorkerJob = 0,
76 Submission
77 };
78
79 quint32 frameId;
80 quint16 jobCount;
81 quint16 frameType; // Submission or worker job
82};
83
84}
85namespace Qt3DCore {
86
87QSystemInformationServicePrivate::QSystemInformationServicePrivate(QAspectEngine *aspectEngine,
88 const QString &description)
89 : QAbstractServiceProviderPrivate(QServiceLocator::SystemInformation, description)
90 , m_aspectEngine(aspectEngine)
91 , m_submissionStorage(nullptr)
92 , m_frameId(0)
93 , m_commandDebugger(nullptr)
94{
95 m_traceEnabled = qEnvironmentVariableIsSet(varName: "QT3D_TRACE_ENABLED");
96 m_graphicsTraceEnabled = qEnvironmentVariableIsSet(varName: "QT3D_GRAPHICS_TRACE_ENABLED");
97 if (m_traceEnabled || m_graphicsTraceEnabled)
98 m_jobsStatTimer.start();
99
100 const bool commandServerEnabled = qEnvironmentVariableIsSet(varName: "QT3D_COMMAND_SERVER_ENABLED");
101 if (commandServerEnabled) {
102 m_commandDebugger = new Debug::AspectCommandDebugger(q_func());
103 m_commandDebugger->initialize();
104 }
105}
106
107QSystemInformationServicePrivate::~QSystemInformationServicePrivate() = default;
108
109QSystemInformationServicePrivate *QSystemInformationServicePrivate::get(QSystemInformationService *q)
110{
111 return q->d_func();
112}
113
114// Called by the jobs
115void QSystemInformationServicePrivate::addJobLogStatsEntry(QSystemInformationServicePrivate::JobRunStats &stats)
116{
117 if (!m_traceEnabled && !m_graphicsTraceEnabled)
118 return;
119
120 if (!m_jobStatsCached.hasLocalData()) {
121 auto jobVector = new QVector<JobRunStats>;
122 m_jobStatsCached.setLocalData(jobVector);
123 QMutexLocker lock(&m_localStoragesMutex);
124 m_localStorages.push_back(t: jobVector);
125 }
126 m_jobStatsCached.localData()->push_back(t: stats);
127}
128
129// Called from Submission thread (which can be main thread in Manual drive mode)
130void QSystemInformationServicePrivate::addSubmissionLogStatsEntry(QSystemInformationServicePrivate::JobRunStats &stats)
131{
132 if (!m_traceEnabled && !m_graphicsTraceEnabled)
133 return;
134
135 QMutexLocker lock(&m_localStoragesMutex);
136 if (!m_jobStatsCached.hasLocalData()) {
137 m_submissionStorage = new QVector<JobRunStats>;
138 m_jobStatsCached.setLocalData(m_submissionStorage);
139 }
140
141 // Handle the case where submission thread is also the main thread (Scene/Manual drive modes with no RenderThread)
142 if (m_submissionStorage == nullptr && m_jobStatsCached.hasLocalData())
143 m_submissionStorage = new QVector<JobRunStats>;
144
145 // When having no submission thread this can be null
146 m_submissionStorage->push_back(t: stats);
147}
148
149// Called after jobs have been executed (MainThread QAspectJobManager::enqueueJobs)
150void QSystemInformationServicePrivate::writeFrameJobLogStats()
151{
152 if (!m_traceEnabled && !m_graphicsTraceEnabled)
153 return;
154
155 using JobRunStats = QSystemInformationServicePrivate::JobRunStats;
156
157 if (!m_traceFile) {
158 const QString fileName = QStringLiteral("trace_") + QCoreApplication::applicationName() +
159 QDateTime::currentDateTime().toString(QStringLiteral("_yyMMdd-hhmmss_")) +
160 QSysInfo::productType() + QStringLiteral("_") + QSysInfo::buildAbi() + QStringLiteral(".qt3d");
161#ifdef Q_OS_ANDROID
162 m_traceFile.reset(new QFile(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation) + QStringLiteral("/") + fileName));
163#else
164 // TODO fix for iOS
165 m_traceFile.reset(other: new QFile(fileName));
166#endif
167 if (!m_traceFile->open(flags: QFile::WriteOnly|QFile::Truncate))
168 qCritical(msg: "Failed to open trace file");
169 }
170
171 // Write Aspect + Job threads
172 {
173 FrameHeader header;
174 header.frameId = m_frameId;
175 header.jobCount = 0;
176
177 for (const QVector<JobRunStats> *storage : qAsConst(t&: m_localStorages))
178 header.jobCount += storage->size();
179
180 m_traceFile->write(data: reinterpret_cast<char *>(&header), len: sizeof(FrameHeader));
181
182 for (QVector<JobRunStats> *storage : qAsConst(t&: m_localStorages)) {
183 for (const JobRunStats &stat : *storage)
184 m_traceFile->write(data: reinterpret_cast<const char *>(&stat), len: sizeof(JobRunStats));
185 storage->clear();
186 }
187 }
188
189 // Write submission thread
190 {
191 QMutexLocker lock(&m_localStoragesMutex);
192 const int submissionJobSize = m_submissionStorage != nullptr ? m_submissionStorage->size() : 0;
193 if (submissionJobSize > 0) {
194 FrameHeader header;
195 header.frameId = m_frameId;
196 header.jobCount = submissionJobSize;
197 header.frameType = FrameHeader::Submission;
198
199 m_traceFile->write(data: reinterpret_cast<char *>(&header), len: sizeof(FrameHeader));
200
201 for (const JobRunStats &stat : *m_submissionStorage)
202 m_traceFile->write(data: reinterpret_cast<const char *>(&stat), len: sizeof(JobRunStats));
203 m_submissionStorage->clear();
204 }
205 }
206
207 m_traceFile->flush();
208 ++m_frameId;
209}
210
211void QSystemInformationServicePrivate::updateTracing()
212{
213 if (m_traceEnabled || m_graphicsTraceEnabled) {
214 if (!m_jobsStatTimer.isValid())
215 m_jobsStatTimer.start();
216 } else {
217 m_traceFile.reset();
218 }
219}
220
221
222QTaskLogger::QTaskLogger(QSystemInformationService *service, const JobId &jobId, Type type)
223 : m_service(service && service->isTraceEnabled() ? service : nullptr)
224 , m_type(type)
225{
226 m_stats.jobId = jobId;
227 if (m_service) {
228 m_stats.startTime = QSystemInformationServicePrivate::get(q: m_service)->m_jobsStatTimer.nsecsElapsed();
229 m_stats.threadId = reinterpret_cast<quint64>(QThread::currentThreadId());
230 }
231}
232
233QTaskLogger::QTaskLogger(QSystemInformationService *service,
234 const quint32 jobType,
235 const quint32 instance,
236 QTaskLogger::Type type)
237 : m_service(service && service->isTraceEnabled() ? service : nullptr)
238 , m_type(type)
239{
240 m_stats.jobId.typeAndInstance[0] = jobType;
241 m_stats.jobId.typeAndInstance[1] = instance;
242 if (m_service) {
243 m_stats.startTime = QSystemInformationServicePrivate::get(q: m_service)->m_jobsStatTimer.nsecsElapsed();
244 m_stats.threadId = reinterpret_cast<quint64>(QThread::currentThreadId());
245 }
246}
247
248QTaskLogger::~QTaskLogger() {
249 if (m_service) {
250 auto dservice = QSystemInformationServicePrivate::get(q: m_service);
251 if (m_stats.endTime == 0L)
252 m_stats.endTime = dservice->m_jobsStatTimer.nsecsElapsed();
253 switch (m_type) {
254 case AspectJob: dservice->addJobLogStatsEntry(stats&: m_stats); break;
255 case Submission: dservice->addSubmissionLogStatsEntry(stats&: m_stats); break;
256 }
257 }
258}
259
260void QTaskLogger::end(qint64 t)
261{
262 m_stats.endTime = t > 0 || !m_service ? t : QSystemInformationServicePrivate::get(q: m_service)->m_jobsStatTimer.nsecsElapsed();
263}
264
265qint64 QTaskLogger::restart()
266{
267 if (m_service)
268 m_stats.startTime = QSystemInformationServicePrivate::get(q: m_service)->m_jobsStatTimer.nsecsElapsed();
269 return m_stats.startTime;
270}
271
272
273/* !\internal
274 \class Qt3DCore::QSystemInformationService
275 \inmodule Qt3DCore
276 \brief Interface for a Qt3D system information service
277
278 This is an interface class that should be subclassesd by providers of the
279 system information service.
280*/
281
282/*
283 Creates an instance of QSystemInformationService, with a \a description for
284 the new service. This constructor is protected so only subclasses can
285 instantiate a QSystemInformationService object.
286*/
287
288QSystemInformationService::QSystemInformationService(QAspectEngine *aspectEngine)
289 : QAbstractServiceProvider(*new QSystemInformationServicePrivate(aspectEngine, QLatin1String("Default System Information Service")))
290{
291}
292
293QSystemInformationService::QSystemInformationService(QAspectEngine *aspectEngine, const QString &description)
294 : QAbstractServiceProvider(*new QSystemInformationServicePrivate(aspectEngine, description))
295{
296}
297
298/*
299 \internal
300*/
301QSystemInformationService::QSystemInformationService(QSystemInformationServicePrivate &dd)
302 : QAbstractServiceProvider(dd)
303{
304}
305
306bool QSystemInformationService::isTraceEnabled() const
307{
308 Q_D(const QSystemInformationService);
309 return d->m_traceEnabled;
310}
311
312bool QSystemInformationService::isGraphicsTraceEnabled() const
313{
314 Q_D(const QSystemInformationService);
315 return d->m_graphicsTraceEnabled;
316}
317
318bool QSystemInformationService::isCommandServerEnabled() const
319{
320 Q_D(const QSystemInformationService);
321 return d->m_commandDebugger != nullptr;
322}
323
324void QSystemInformationService::setTraceEnabled(bool traceEnabled)
325{
326 Q_D(QSystemInformationService);
327 if (d->m_traceEnabled != traceEnabled) {
328 d->m_traceEnabled = traceEnabled;
329 emit traceEnabledChanged(traceEnabled: d->m_traceEnabled);
330 d->updateTracing();
331 }
332}
333
334void QSystemInformationService::setGraphicsTraceEnabled(bool graphicsTraceEnabled)
335{
336 Q_D(QSystemInformationService);
337 if (d->m_graphicsTraceEnabled != graphicsTraceEnabled) {
338 d->m_graphicsTraceEnabled = graphicsTraceEnabled;
339 emit graphicsTraceEnabledChanged(graphicsTraceEnabled: d->m_graphicsTraceEnabled);
340 d->updateTracing();
341 }
342}
343
344/*
345 \fn QStringList Qt3DCore::QSystemInformationService::aspectNames() const
346
347 Returns a string list containing the names of all registered aspects.
348*/
349QStringList QSystemInformationService::aspectNames() const
350{
351 Q_D(const QSystemInformationService);
352 if (!d->m_aspectEngine)
353 return {};
354
355 QStringList res;
356 const auto aspects = d->m_aspectEngine->aspects();
357 if (aspects.isEmpty())
358 return { QLatin1String("No loaded aspects") };
359
360 QAspectEnginePrivate *dengine = QAspectEnginePrivate::get(engine: d->m_aspectEngine);
361 for (auto aspect: aspects) {
362 const QString name = dengine->m_factory.aspectName(aspect);
363 if (!name.isEmpty())
364 res << name;
365 else
366 res << QLatin1String("<unnamed>");
367 }
368
369 return res;
370}
371
372/*
373 \fn int Qt3DCore::QSystemInformationService::threadPoolThreadCount() const
374
375 Returns the maximum number of threads in the Qt3D task manager's threadpool.
376*/
377int QSystemInformationService::threadPoolThreadCount() const
378{
379 return QThreadPool::globalInstance()->maxThreadCount();
380}
381
382void QSystemInformationService::writePreviousFrameTraces()
383{
384 Q_D(QSystemInformationService);
385 d->writeFrameJobLogStats();
386}
387
388void QSystemInformationService::revealLogFolder()
389{
390 QDesktopServices::openUrl(url: QUrl::fromLocalFile(localfile: QDir::currentPath()));
391}
392
393QVariant QSystemInformationService::executeCommand(const QString &command)
394{
395 Q_D(QSystemInformationService);
396
397 if (command == QLatin1String("tracing on")) {
398 setTraceEnabled(true);
399 return {isTraceEnabled()};
400 }
401
402 if (command == QLatin1String("tracing off")) {
403 setTraceEnabled(false);
404 return {isTraceEnabled()};
405 }
406
407 if (command == QLatin1String("glprofiling on")) {
408 setGraphicsTraceEnabled(true);
409 return {isTraceEnabled()};
410 }
411
412 if (command == QLatin1String("glprofiling off")) {
413 setGraphicsTraceEnabled(false);
414 return {isTraceEnabled()};
415 }
416
417 return d->m_aspectEngine->executeCommand(command);
418}
419
420void QSystemInformationService::dumpCommand(const QString &command)
421{
422 QVariant res = executeCommand(command);
423 QObject *obj = res.value<QObject *>();
424 if (obj) {
425 auto reply = qobject_cast<Qt3DCore::Debug::AsynchronousCommandReply*>(object: obj);
426 if (reply) {
427 connect(sender: reply, signal: &Debug::AsynchronousCommandReply::finished, context: this, slot: [reply]() {
428 qWarning() << qPrintable( QLatin1String(reply->data()) );
429 });
430 return;
431 }
432 }
433 qWarning() << qPrintable(res.toString());
434}
435
436}
437
438QT_END_NAMESPACE
439

source code of qt3d/src/core/services/qsysteminformationservice.cpp