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

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