Nanotrace: Add asynchronous trace points

With asynchronous traces you can follow complex tasks.

Change-Id: Ia0fd20f34f3529c59eff5d222c8d87ac5dacd514
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
This commit is contained in:
Marco Bubke
2023-10-13 12:16:32 +02:00
parent 33084f228e
commit 1faa8e29e2
6 changed files with 358 additions and 82 deletions

View File

@@ -4,13 +4,17 @@
#include "nanotracehr.h" #include "nanotracehr.h"
#include <QCoreApplication> #include <QCoreApplication>
#include <QThread>
#include <fstream> #include <fstream>
#include <iomanip> #include <iomanip>
#include <iostream>
#include <limits> #include <limits>
#include <thread> #include <thread>
#ifdef Q_OS_UNIX
# include <pthread.h>
#endif
namespace NanotraceHR { namespace NanotraceHR {
namespace { namespace {
@@ -18,11 +22,50 @@ namespace {
template<typename TraceEvent> template<typename TraceEvent>
void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId) void printEvent(std::ostream &out, const TraceEvent &event, qint64 processId, std::thread::id threadId)
{ {
out << R"({"ph":"X","name":")" << event.name << R"(","cat":")" << event.category out << R"({"ph":")" << event.type << R"(","name":")" << event.name << R"(","cat":")"
<< R"(","ts":")" << static_cast<double>(event.start.time_since_epoch().count()) / 1000 << event.category << R"(","ts":")"
<< R"(","dur":")" << static_cast<double>(event.duration.count()) / 1000 << R"(","pid":")" << static_cast<double>(event.time.time_since_epoch().count()) / 1000 << R"(","pid":")"
<< processId << R"(","tid":")" << threadId << R"(","args":)" << event.arguments << "}"; << processId << R"(","tid":")" << threadId << R"(")";
if (event.type == 'X')
out << R"(,"dur":)" << static_cast<double>(event.duration.count()) / 1000;
if (event.id != 0)
out << R"(,"id":)" << event.id;
if (event.arguments.size())
out << R"(,"args":)" << event.arguments;
out << "}";
} }
void writeMetaEvent(TraceFile<true> *file, std::string_view key, std::string_view value)
{
std::lock_guard lock{file->fileMutex};
auto &out = file->out;
if (out.is_open()) {
file->out << R"({"name":")" << key << R"(","ph":"M", "pid":")"
<< QCoreApplication::applicationPid() << R"(","tid":")"
<< std::this_thread::get_id() << R"(","args":{"name":")" << value << R"("}})"
<< ",\n";
}
}
std::string getThreadName()
{
std::array<char, 200> buffer;
buffer[0] = 0;
#ifdef Q_OS_UNIX
auto rc = pthread_getname_np(pthread_self(), buffer.data(), buffer.size());
if (rc != 0)
return {};
#endif
return buffer.data();
}
} // namespace } // namespace
template<typename TraceEvent> template<typename TraceEvent>
@@ -42,7 +85,6 @@ void flushEvents(const Utils::span<TraceEvent> events,
printEvent(out, event, processId, threadId); printEvent(out, event, processId, threadId);
out << ",\n"; out << ",\n";
} }
out << std::flush;
} }
} }
@@ -57,8 +99,12 @@ void openFile(EnabledTraceFile &file)
{ {
std::lock_guard lock{file.fileMutex}; std::lock_guard lock{file.fileMutex};
if (file.out = std::ofstream{file.filePath, std::ios::trunc}; file.out.good()) if (file.out = std::ofstream{file.filePath, std::ios::trunc}; file.out.good()) {
file.out << std::fixed << std::setprecision(3) << R"({"traceEvents": [)"; file.out << std::fixed << std::setprecision(3) << R"({"traceEvents": [)";
file.out << R"({"name":"process_name","ph":"M", "pid":)"
<< QCoreApplication::applicationPid() << R"(,"args":{"name":"QtCreator"}})"
<< ",\n";
}
} }
void finalizeFile(EnabledTraceFile &file) void finalizeFile(EnabledTraceFile &file)
@@ -109,4 +155,45 @@ EnabledEventQueue<StringTraceEvent> &globalEventQueue()
return s_globalEventQueue; return s_globalEventQueue;
} }
template<typename TraceEvent>
EventQueue<TraceEvent, std::true_type>::EventQueue(EnabledTraceFile *file,
TraceEventsSpan eventsOne,
TraceEventsSpan eventsTwo)
: file{file}
, eventsOne{eventsOne}
, eventsTwo{eventsTwo}
, currentEvents{eventsOne}
{
if (auto thread = QThread::currentThread()) {
connection = QObject::connect(QCoreApplication::instance(),
&QCoreApplication::aboutToQuit,
thread,
[&] { flush(); });
auto name = getThreadName();
if (name.size()) {
writeMetaEvent(file, "thread_name", name);
}
}
}
template<typename TraceEvent>
EventQueue<TraceEvent, std::true_type>::~EventQueue()
{
flush();
if (connection)
QObject::disconnect(connection);
}
template<typename TraceEvent>
void EventQueue<TraceEvent, std::true_type>::flush()
{
std::lock_guard lock{mutex};
if (isEnabled == IsEnabled::Yes && eventsIndex > 0) {
flushEvents(currentEvents.subspan(0, eventsIndex), std::this_thread::get_id(), *this);
eventsIndex = 0;
}
}
template class NANOTRACE_EXPORT EventQueue<StringViewTraceEvent, std::true_type>;
template class NANOTRACE_EXPORT EventQueue<StringTraceEvent, std::true_type>;
} // namespace NanotraceHR } // namespace NanotraceHR

View File

@@ -7,7 +7,10 @@
#include <utils/span.h> #include <utils/span.h>
#include <QMetaObject>
#include <array> #include <array>
#include <atomic>
#include <chrono> #include <chrono>
#include <fstream> #include <fstream>
#include <future> #include <future>
@@ -33,15 +36,34 @@ constexpr bool isTracerActive()
#endif #endif
} }
template<std::size_t size> namespace Literals {
std::string_view toStringView(Utils::span<const char, size> string) struct TracerLiteral
{ {
return {string.data(), string.size()}; friend constexpr TracerLiteral operator""_t(const char *text, size_t size);
}
constexpr operator std::string_view() const { return text; }
private:
constexpr TracerLiteral(std::string_view text)
: text{text}
{}
std::string_view text;
};
constexpr TracerLiteral operator""_t(const char *text, size_t size)
{
return {std::string_view{text, size}};
}
} // namespace Literals
using namespace Literals;
template<typename String> template<typename String>
struct TraceEvent struct TraceEvent
{ {
using StringType = String;
using ArgumentType = std::conditional_t<std::is_same_v<String, std::string_view>, TracerLiteral, String>;
TraceEvent() = default; TraceEvent() = default;
TraceEvent(const TraceEvent &) = delete; TraceEvent(const TraceEvent &) = delete;
TraceEvent(TraceEvent &&) = delete; TraceEvent(TraceEvent &&) = delete;
@@ -52,8 +74,10 @@ struct TraceEvent
String name; String name;
String category; String category;
String arguments; String arguments;
TimePoint start; TimePoint time;
Duration duration; Duration duration;
std::size_t id = 0;
char type = ' ';
}; };
using StringViewTraceEvent = TraceEvent<std::string_view>; using StringViewTraceEvent = TraceEvent<std::string_view>;
@@ -126,7 +150,10 @@ public:
template<typename TraceEvent, typename Enabled> template<typename TraceEvent, typename Enabled>
class EventQueue class EventQueue
{}; {
public:
using IsActive = std::false_type;
};
template<typename TraceEvent> template<typename TraceEvent>
class EventQueue<TraceEvent, std::true_type> class EventQueue<TraceEvent, std::true_type>
@@ -134,15 +161,13 @@ class EventQueue<TraceEvent, std::true_type>
using TraceEventsSpan = Utils::span<TraceEvent>; using TraceEventsSpan = Utils::span<TraceEvent>;
public: public:
EventQueue() = default; using IsActive = std::true_type;
~EventQueue() EventQueue(EnabledTraceFile *file, TraceEventsSpan eventsOne, TraceEventsSpan eventsTwo);
{
if (isEnabled == IsEnabled::Yes && eventsIndex > 0) { ~EventQueue();
flushEvents(currentEvents.subspan(0, eventsIndex), std::this_thread::get_id(), *this);
eventsIndex = 0; void flush();
}
}
EventQueue(const EventQueue &) = delete; EventQueue(const EventQueue &) = delete;
EventQueue(EventQueue &&) = delete; EventQueue(EventQueue &&) = delete;
@@ -154,14 +179,17 @@ public:
TraceEventsSpan eventsTwo; TraceEventsSpan eventsTwo;
TraceEventsSpan currentEvents; TraceEventsSpan currentEvents;
std::size_t eventsIndex = 0; std::size_t eventsIndex = 0;
IsEnabled isEnabled = IsEnabled::No; IsEnabled isEnabled = IsEnabled::Yes;
QMetaObject::Connection connection;
std::mutex mutex;
}; };
extern template class NANOTRACE_EXPORT EventQueue<StringViewTraceEvent, std::true_type>;
extern template class NANOTRACE_EXPORT EventQueue<StringTraceEvent, std::true_type>;
template<typename TraceEvent, std::size_t eventCount, typename Enabled> template<typename TraceEvent, std::size_t eventCount, typename Enabled>
class EventQueueData class EventQueueData
{ {
using TraceEvents = std::array<TraceEvent, eventCount>;
public: public:
using IsActive = Enabled; using IsActive = Enabled;
@@ -197,7 +225,7 @@ struct EventQueueDataPointer<TraceEvent, eventCount, std::true_type>
EnabledEventQueue<TraceEvent> createEventQueue() const EnabledEventQueue<TraceEvent> createEventQueue() const
{ {
if constexpr (isTracerActive()) { if constexpr (isTracerActive()) {
return {&data->file, data->eventsOne, data->eventsTwo, data->eventsOne, 0, IsEnabled::Yes}; return {&data->file, data->eventsOne, data->eventsTwo};
} else { } else {
return {}; return {};
} }
@@ -229,38 +257,63 @@ TraceEvent &getTraceEvent(EnabledEventQueue<TraceEvent> &eventQueue)
return eventQueue.currentEvents[eventQueue.eventsIndex++]; return eventQueue.currentEvents[eventQueue.eventsIndex++];
} }
namespace Literals { template<bool enabled>
struct TracerLiteral class Token
{ {
friend constexpr TracerLiteral operator""_t(const char *text, size_t size); public:
using IsActive = std::false_type;
constexpr operator std::string_view() const { return text; } constexpr std::size_t operator*() const { return 0; }
private: constexpr explicit operator bool() const { return false; }
constexpr TracerLiteral(std::string_view text)
: text{text}
{}
std::string_view text; static constexpr bool isActive() { return false; }
}; };
constexpr TracerLiteral operator""_t(const char *text, size_t size) template<typename TraceEvent, bool enabled>
{ class Category;
return {std::string_view{text, size}};
}
} // namespace Literals
using namespace Literals; template<>
class Token<true>
{
friend Category<StringViewTraceEvent, true>;
friend Category<StringTraceEvent, true>;
Token(std::size_t id)
: m_id{id}
{}
public:
using IsActive = std::true_type;
constexpr std::size_t operator*() const { return m_id; }
constexpr explicit operator bool() const { return m_id; }
static constexpr bool isActive() { return true; }
private:
std::size_t m_id;
};
template<typename TraceEvent, bool enabled> template<typename TraceEvent, bool enabled>
class Category class Category
{ {
public: public:
using IsActive = std::false_type; using IsActive = std::false_type;
using ArgumentType = typename TraceEvent::ArgumentType;
Category(TracerLiteral, EventQueue<TraceEvent, std::true_type> &) {} Category(ArgumentType, EventQueue<TraceEvent, std::true_type> &) {}
Category(TracerLiteral, EventQueue<TraceEvent, std::false_type> &) {} Category(ArgumentType, EventQueue<TraceEvent, std::false_type> &) {}
Token<false> beginAsynchronous(ArgumentType) { return {}; }
void tickAsynchronous(Token<false>, ArgumentType) {}
void endAsynchronous(Token<false>, ArgumentType) {}
static constexpr bool isActive() { return false; }
}; };
template<typename TraceEvent> template<typename TraceEvent>
@@ -268,8 +321,67 @@ class Category<TraceEvent, true>
{ {
public: public:
using IsActive = std::true_type; using IsActive = std::true_type;
TracerLiteral name; using ArgumentType = typename TraceEvent::ArgumentType;
EnabledEventQueue<TraceEvent> &eventQueue; using StringType = typename TraceEvent::StringType;
template<typename EventQueue>
Category(ArgumentType name, EventQueue &queue)
: m_name{std::move(name)}
, m_eventQueue{queue}
{
static_assert(std::is_same_v<typename EventQueue::IsActive, std::true_type>,
"A active category is not possible with an inactive event queue!");
}
Token<true> beginAsynchronous(ArgumentType traceName)
{
auto id = ++idCounter;
auto &traceEvent = getTraceEvent(m_eventQueue);
traceEvent.name = std::move(traceName);
traceEvent.category = m_name;
traceEvent.time = Clock::now();
traceEvent.type = 'b';
traceEvent.id = id;
return id;
}
void tickAsynchronous(Token<true> token, ArgumentType traceName)
{
if (!token)
return;
auto &traceEvent = getTraceEvent(m_eventQueue);
traceEvent.name = std::move(traceName);
traceEvent.category = m_name;
traceEvent.time = Clock::now();
traceEvent.type = 'n';
traceEvent.id = *token;
}
void endAsynchronous(Token<true> token, ArgumentType traceName)
{
if (!token)
return;
auto &traceEvent = getTraceEvent(m_eventQueue);
traceEvent.name = std::move(traceName);
traceEvent.category = m_name;
traceEvent.time = Clock::now();
traceEvent.type = 'e';
traceEvent.id = *token;
}
EnabledEventQueue<TraceEvent> &eventQueue() const { return m_eventQueue; }
std::string_view name() const { return m_name; }
static constexpr bool isActive() { return true; }
private:
StringType m_name;
EnabledEventQueue<TraceEvent> &m_eventQueue;
inline static std::atomic<std::size_t> idCounter = 0;
}; };
template<bool enabled> template<bool enabled>
@@ -277,18 +389,15 @@ using StringViewCategory = Category<StringViewTraceEvent, enabled>;
template<bool enabled> template<bool enabled>
using StringCategory = Category<StringTraceEvent, enabled>; using StringCategory = Category<StringTraceEvent, enabled>;
class DisabledCategory
{
using IsActive = std::false_type;
};
template<typename Category> template<typename Category>
class Tracer class Tracer
{ {
public: public:
constexpr Tracer(TracerLiteral, Category &, TracerLiteral) {} using ArgumentType = typename Category::ArgumentType;
constexpr Tracer(TracerLiteral, Category &) {} constexpr Tracer(ArgumentType, Category &, ArgumentType) {}
constexpr Tracer(ArgumentType, Category &) {}
~Tracer() {} ~Tracer() {}
}; };
@@ -297,22 +406,22 @@ template<>
class Tracer<StringViewCategory<true>> class Tracer<StringViewCategory<true>>
{ {
public: public:
constexpr Tracer(TracerLiteral name, StringViewCategory<true> &category, TracerLiteral arguments) Tracer(TracerLiteral name, StringViewCategory<true> &category, TracerLiteral arguments)
: m_name{name} : m_name{name}
, m_arguments{arguments} , m_arguments{arguments}
, m_category{category} , m_category{category}
{ {
if constexpr (isTracerActive()) { if constexpr (isTracerActive()) {
if (category.eventQueue.isEnabled == IsEnabled::Yes) if (category.eventQueue().isEnabled == IsEnabled::Yes)
m_start = Clock::now(); m_start = Clock::now();
} }
} }
constexpr Tracer(TracerLiteral name, StringViewCategory<true> &category) Tracer(TracerLiteral name, StringViewCategory<true> &category)
: Tracer{name, category, "{}"_t} : Tracer{name, category, ""_t}
{ {
if constexpr (isTracerActive()) { if constexpr (isTracerActive()) {
if (category.eventQueue.isEnabled == IsEnabled::Yes) if (category.eventQueue().isEnabled == IsEnabled::Yes)
m_start = Clock::now(); m_start = Clock::now();
} }
} }
@@ -320,14 +429,15 @@ public:
~Tracer() ~Tracer()
{ {
if constexpr (isTracerActive()) { if constexpr (isTracerActive()) {
if (m_category.eventQueue.isEnabled == IsEnabled::Yes) { if (m_category.eventQueue().isEnabled == IsEnabled::Yes) {
auto duration = Clock::now() - m_start; auto duration = Clock::now() - m_start;
auto &traceEvent = getTraceEvent(m_category.eventQueue); auto &traceEvent = getTraceEvent(m_category.eventQueue());
traceEvent.name = m_name; traceEvent.name = m_name;
traceEvent.category = m_category.name; traceEvent.category = m_category.name();
traceEvent.arguments = m_arguments; traceEvent.arguments = m_arguments;
traceEvent.start = m_start; traceEvent.time = m_start;
traceEvent.duration = duration; traceEvent.duration = duration;
traceEvent.type = 'X';
} }
} }
} }
@@ -349,16 +459,16 @@ public:
, m_category{category} , m_category{category}
{ {
if constexpr (isTracerActive()) { if constexpr (isTracerActive()) {
if (category.eventQueue.isEnabled == IsEnabled::Yes) if (category.eventQueue().isEnabled == IsEnabled::Yes)
m_start = Clock::now(); m_start = Clock::now();
} }
} }
Tracer(std::string name, StringViewCategory<true> &category) Tracer(std::string name, StringViewCategory<true> &category)
: Tracer{std::move(name), category, "{}"} : Tracer{std::move(name), category, ""}
{ {
if constexpr (isTracerActive()) { if constexpr (isTracerActive()) {
if (category.eventQueue.isEnabled == IsEnabled::Yes) if (category.eventQueue().isEnabled == IsEnabled::Yes)
m_start = Clock::now(); m_start = Clock::now();
} }
} }
@@ -366,14 +476,15 @@ public:
~Tracer() ~Tracer()
{ {
if constexpr (isTracerActive()) { if constexpr (isTracerActive()) {
if (m_category.eventQueue.isEnabled == IsEnabled::Yes) { if (m_category.eventQueue().isEnabled == IsEnabled::Yes) {
auto duration = Clock::now() - m_start; auto duration = Clock::now() - m_start;
auto &traceEvent = getTraceEvent(m_category.eventQueue); auto &traceEvent = getTraceEvent(m_category.eventQueue());
traceEvent.name = std::move(m_name); traceEvent.name = std::move(m_name);
traceEvent.category = m_category.name; traceEvent.category = m_category.name();
traceEvent.arguments = std::move(m_arguments); traceEvent.arguments = std::move(m_arguments);
traceEvent.start = m_start; traceEvent.time = m_start;
traceEvent.duration = duration; traceEvent.duration = duration;
traceEvent.type = 'X';
} }
} }
} }
@@ -403,7 +514,7 @@ public:
} }
GlobalTracer(std::string name, std::string category) GlobalTracer(std::string name, std::string category)
: GlobalTracer{std::move(name), std::move(category), "{}"} : GlobalTracer{std::move(name), std::move(category), ""}
{} {}
~GlobalTracer() ~GlobalTracer()
@@ -415,8 +526,9 @@ public:
traceEvent.name = std::move(m_name); traceEvent.name = std::move(m_name);
traceEvent.category = std::move(m_category); traceEvent.category = std::move(m_category);
traceEvent.arguments = std::move(m_arguments); traceEvent.arguments = std::move(m_arguments);
traceEvent.start = std::move(m_start); traceEvent.time = std::move(m_start);
traceEvent.duration = std::move(duration); traceEvent.duration = std::move(duration);
traceEvent.type = 'X';
} }
} }
} }

View File

@@ -401,8 +401,8 @@ public:
const_iterator end() const & { return iterator{m_statement, false}; } const_iterator end() const & { return iterator{m_statement, false}; }
private: private:
NanotraceHR::Tracer<decltype(sqliteHighLevelCategory())> tracer{"range"_t, NanotraceHR::Tracer<std::decay_t<decltype(sqliteHighLevelCategory())>> tracer{
sqliteHighLevelCategory()}; "range"_t, sqliteHighLevelCategory()};
StatementImplementation &m_statement; StatementImplementation &m_statement;
}; };

View File

@@ -16,9 +16,13 @@ option(USE_PROJECTSTORAGE "Use ProjectStorage" ${ENV_QDS_USE_PROJECTSTORAGE})
add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "") add_feature_info("ProjectStorage" ${USE_PROJECTSTORAGE} "")
env_with_default("QTC_ENABLE_PROJECT_STORAGE_TRACING" ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING OFF) env_with_default("QTC_ENABLE_PROJECT_STORAGE_TRACING" ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING OFF)
option(ENABLE_PROJECT_STORAGE_TRACING "Enable sqlite tarcing" ${ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING}) option(ENABLE_PROJECT_STORAGE_TRACING "Enable sqlite tracing" ${ENV_QTC_ENABLE_PROJECT_STORAGE_TRACING})
add_feature_info("Sqlite tracing" ${ENABLE_PROJECT_STORAGE_TRACING} "") add_feature_info("Sqlite tracing" ${ENABLE_PROJECT_STORAGE_TRACING} "")
env_with_default("QTC_ENABLE_IMAGE_CACHE_TRACING" ENV_QTC_ENABLE_IMAGE_CACHE_TRACING OFF)
option(ENABLE_IMAGE_CACHE_TRACING "Enable image cache tracing" ${ENV_QTC_ENABLE_IMAGE_CACHE_TRACING})
add_feature_info("Image cache tracing" ${ENABLE_IMAGE_CACHE_TRACING} "")
add_qtc_library(QmlDesignerUtils STATIC add_qtc_library(QmlDesignerUtils STATIC
DEPENDS DEPENDS
@@ -93,6 +97,7 @@ extend_qtc_library(QmlDesignerCore
DEPENDS Nanotrace DEPENDS Nanotrace
DEFINES DEFINES
$<$<BOOL:${ENABLE_PROJECT_STORAGE_TRACING}>:ENABLE_PROJECT_STORAGE_TRACING> $<$<BOOL:${ENABLE_PROJECT_STORAGE_TRACING}>:ENABLE_PROJECT_STORAGE_TRACING>
$<$<BOOL:${ENABLE_IMAGE_CACHE_TRACING}>:ENABLE_IMAGE_CACHE_TRACING>
) )
extend_qtc_library(QmlDesignerCore extend_qtc_library(QmlDesignerCore

View File

@@ -7,18 +7,39 @@
#include "imagecachestorage.h" #include "imagecachestorage.h"
#include "timestampprovider.h" #include "timestampprovider.h"
#include <QScopeGuard>
#include <thread> #include <thread>
namespace QmlDesigner { namespace QmlDesigner {
using namespace NanotraceHR::Literals;
namespace {
using TraceFile = NanotraceHR::TraceFile<imageCacheTracingIsEnabled()>;
TraceFile traceFile{"qml_designer.json"};
thread_local auto eventQueueData = NanotraceHR::makeEventQueueData<NanotraceHR::StringViewTraceEvent, 10000>(
traceFile);
thread_local NanotraceHR::EventQueue eventQueue = eventQueueData.createEventQueue();
thread_local NanotraceHR::StringViewCategory<imageCacheTracingIsEnabled()> category{"image cache"_t,
eventQueue};
} // namespace
NanotraceHR::StringViewCategory<imageCacheTracingIsEnabled()> &imageCacheCategory()
{
return category;
}
AsynchronousImageCache::AsynchronousImageCache(ImageCacheStorageInterface &storage, AsynchronousImageCache::AsynchronousImageCache(ImageCacheStorageInterface &storage,
ImageCacheGeneratorInterface &generator, ImageCacheGeneratorInterface &generator,
TimeStampProviderInterface &timeStampProvider) TimeStampProviderInterface &timeStampProvider)
: m_storage(storage) : m_storage(storage)
, m_generator(generator) , m_generator(generator)
, m_timeStampProvider(timeStampProvider) , m_timeStampProvider(timeStampProvider)
{ {}
}
AsynchronousImageCache::~AsynchronousImageCache() AsynchronousImageCache::~AsynchronousImageCache()
{ {
@@ -31,6 +52,7 @@ void AsynchronousImageCache::request(Utils::SmallStringView name,
ImageCache::CaptureImageCallback captureCallback, ImageCache::CaptureImageCallback captureCallback,
ImageCache::AbortCallback abortCallback, ImageCache::AbortCallback abortCallback,
ImageCache::AuxiliaryData auxiliaryData, ImageCache::AuxiliaryData auxiliaryData,
ImageCacheTraceToken traceToken,
ImageCacheStorageInterface &storage, ImageCacheStorageInterface &storage,
ImageCacheGeneratorInterface &generator, ImageCacheGeneratorInterface &generator,
TimeStampProviderInterface &timeStampProvider) TimeStampProviderInterface &timeStampProvider)
@@ -40,6 +62,11 @@ void AsynchronousImageCache::request(Utils::SmallStringView name,
const auto timeStamp = timeStampProvider.timeStamp(name); const auto timeStamp = timeStampProvider.timeStamp(name);
auto requestImageFromStorage = [&](RequestType requestType) { auto requestImageFromStorage = [&](RequestType requestType) {
imageCacheCategory().tickAsynchronous(traceToken, "start fetching image from storage"_t);
QScopeGuard finally{[=] {
imageCacheCategory().tickAsynchronous(traceToken, "end fetching image from storage"_t);
}};
switch (requestType) { switch (requestType) {
case RequestType::Image: case RequestType::Image:
return storage.fetchImage(id, timeStamp); return storage.fetchImage(id, timeStamp);
@@ -56,14 +83,19 @@ void AsynchronousImageCache::request(Utils::SmallStringView name,
const auto entry = requestImageFromStorage(requestType); const auto entry = requestImageFromStorage(requestType);
if (entry) { if (entry) {
if (entry->isNull()) if (entry->isNull()) {
abortCallback(ImageCache::AbortReason::Failed); abortCallback(ImageCache::AbortReason::Failed);
else imageCacheCategory().endAsynchronous(traceToken,
"abort image request because entry in database is null"_t);
} else {
captureCallback(*entry); captureCallback(*entry);
imageCacheCategory().endAsynchronous(traceToken, "image request delivered from storage"_t);
}
} else { } else {
auto callback = auto callback =
[captureCallback = std::move(captureCallback), [captureCallback = std::move(captureCallback),
requestType](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) { requestType,
traceToken](const QImage &image, const QImage &midSizeImage, const QImage &smallImage) {
auto selectImage = [](RequestType requestType, auto selectImage = [](RequestType requestType,
const QImage &image, const QImage &image,
const QImage &midSizeImage, const QImage &midSizeImage,
@@ -82,7 +114,12 @@ void AsynchronousImageCache::request(Utils::SmallStringView name,
return image; return image;
}; };
captureCallback(selectImage(requestType, image, midSizeImage, smallImage)); captureCallback(selectImage(requestType, image, midSizeImage, smallImage));
imageCacheCategory().endAsynchronous(traceToken,
"image request delivered from generation"_t);
}; };
imageCacheCategory().tickAsynchronous(traceToken, "request image generation"_t);
generator.generateImage(name, generator.generateImage(name,
extraId, extraId,
timeStamp, timeStamp,
@@ -98,12 +135,15 @@ void AsynchronousImageCache::requestImage(Utils::SmallStringView name,
Utils::SmallStringView extraId, Utils::SmallStringView extraId,
ImageCache::AuxiliaryData auxiliaryData) ImageCache::AuxiliaryData auxiliaryData)
{ {
auto traceToken = imageCacheCategory().beginAsynchronous(
"request image in asynchornous image cache"_t);
m_taskQueue.addTask(std::move(name), m_taskQueue.addTask(std::move(name),
std::move(extraId), std::move(extraId),
std::move(captureCallback), std::move(captureCallback),
std::move(abortCallback), std::move(abortCallback),
std::move(auxiliaryData), std::move(auxiliaryData),
RequestType::Image); RequestType::Image,
traceToken);
} }
void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name, void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name,
@@ -112,12 +152,15 @@ void AsynchronousImageCache::requestMidSizeImage(Utils::SmallStringView name,
Utils::SmallStringView extraId, Utils::SmallStringView extraId,
ImageCache::AuxiliaryData auxiliaryData) ImageCache::AuxiliaryData auxiliaryData)
{ {
auto traceToken = imageCacheCategory().beginAsynchronous(
"request mid size image in asynchornous image cache"_t);
m_taskQueue.addTask(std::move(name), m_taskQueue.addTask(std::move(name),
std::move(extraId), std::move(extraId),
std::move(captureCallback), std::move(captureCallback),
std::move(abortCallback), std::move(abortCallback),
std::move(auxiliaryData), std::move(auxiliaryData),
RequestType::MidSizeImage); RequestType::MidSizeImage,
traceToken);
} }
void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name, void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name,
@@ -126,12 +169,15 @@ void AsynchronousImageCache::requestSmallImage(Utils::SmallStringView name,
Utils::SmallStringView extraId, Utils::SmallStringView extraId,
ImageCache::AuxiliaryData auxiliaryData) ImageCache::AuxiliaryData auxiliaryData)
{ {
auto traceToken = imageCacheCategory().beginAsynchronous(
"request small size image in asynchornous image cache"_t);
m_taskQueue.addTask(std::move(name), m_taskQueue.addTask(std::move(name),
std::move(extraId), std::move(extraId),
std::move(captureCallback), std::move(captureCallback),
std::move(abortCallback), std::move(abortCallback),
std::move(auxiliaryData), std::move(auxiliaryData),
RequestType::SmallImage); RequestType::SmallImage,
traceToken);
} }
void AsynchronousImageCache::clean() void AsynchronousImageCache::clean()

View File

@@ -6,6 +6,7 @@
#include "asynchronousimagecacheinterface.h" #include "asynchronousimagecacheinterface.h"
#include <imagecache/taskqueue.h> #include <imagecache/taskqueue.h>
#include <nanotrace/nanotracehr.h>
#include <condition_variable> #include <condition_variable>
#include <deque> #include <deque>
@@ -21,6 +22,19 @@ class ImageCacheStorageInterface;
class ImageCacheGeneratorInterface; class ImageCacheGeneratorInterface;
class ImageCacheCollectorInterface; class ImageCacheCollectorInterface;
constexpr bool imageCacheTracingIsEnabled()
{
#ifdef ENABLE_IMAGE_CACHE_TRACING
return NanotraceHR::isTracerActive();
#else
return false;
#endif
}
using ImageCacheTraceToken = NanotraceHR::Token<imageCacheTracingIsEnabled()>;
NanotraceHR::StringViewCategory<imageCacheTracingIsEnabled()> &imageCacheCategory();
class AsynchronousImageCache final : public AsynchronousImageCacheInterface class AsynchronousImageCache final : public AsynchronousImageCacheInterface
{ {
public: public:
@@ -53,18 +67,21 @@ private:
struct Entry struct Entry
{ {
Entry() = default; Entry() = default;
Entry(Utils::PathString name, Entry(Utils::PathString name,
Utils::SmallString extraId, Utils::SmallString extraId,
ImageCache::CaptureImageCallback &&captureCallback, ImageCache::CaptureImageCallback &&captureCallback,
ImageCache::AbortCallback &&abortCallback, ImageCache::AbortCallback &&abortCallback,
ImageCache::AuxiliaryData &&auxiliaryData, ImageCache::AuxiliaryData &&auxiliaryData,
RequestType requestType) RequestType requestType,
ImageCacheTraceToken traceToken)
: name{std::move(name)} : name{std::move(name)}
, extraId{std::move(extraId)} , extraId{std::move(extraId)}
, captureCallback{std::move(captureCallback)} , captureCallback{std::move(captureCallback)}
, abortCallback{std::move(abortCallback)} , abortCallback{std::move(abortCallback)}
, auxiliaryData{std::move(auxiliaryData)} , auxiliaryData{std::move(auxiliaryData)}
, requestType{requestType} , requestType{requestType}
, traceToken{traceToken}
{} {}
Utils::PathString name; Utils::PathString name;
@@ -73,6 +90,7 @@ private:
ImageCache::AbortCallback abortCallback; ImageCache::AbortCallback abortCallback;
ImageCache::AuxiliaryData auxiliaryData; ImageCache::AuxiliaryData auxiliaryData;
RequestType requestType = RequestType::Image; RequestType requestType = RequestType::Image;
ImageCacheTraceToken traceToken;
}; };
static void request(Utils::SmallStringView name, static void request(Utils::SmallStringView name,
@@ -81,6 +99,7 @@ private:
ImageCache::CaptureImageCallback captureCallback, ImageCache::CaptureImageCallback captureCallback,
ImageCache::AbortCallback abortCallback, ImageCache::AbortCallback abortCallback,
ImageCache::AuxiliaryData auxiliaryData, ImageCache::AuxiliaryData auxiliaryData,
ImageCacheTraceToken traceToken,
ImageCacheStorageInterface &storage, ImageCacheStorageInterface &storage,
ImageCacheGeneratorInterface &generator, ImageCacheGeneratorInterface &generator,
TimeStampProviderInterface &timeStampProvider); TimeStampProviderInterface &timeStampProvider);
@@ -95,6 +114,7 @@ private:
std::move(entry.captureCallback), std::move(entry.captureCallback),
std::move(entry.abortCallback), std::move(entry.abortCallback),
std::move(entry.auxiliaryData), std::move(entry.auxiliaryData),
entry.traceToken,
storage, storage,
generator, generator,
timeStampProvider); timeStampProvider);
@@ -107,7 +127,13 @@ private:
struct Clean struct Clean
{ {
void operator()(Entry &entry) { entry.abortCallback(ImageCache::AbortReason::Abort); } void operator()(Entry &entry)
{
using namespace NanotraceHR::Literals;
entry.abortCallback(ImageCache::AbortReason::Abort);
imageCacheCategory().endAsynchronous(entry.traceToken, "aborted for cleanup"_t);
}
}; };
private: private: