forked from qt-creator/qt-creator
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:
@@ -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
|
||||||
|
|||||||
@@ -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';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user