aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorKnud Dollereder <knud.dollereder@qt.io>2021-10-12 12:07:44 +0200
committerKnud Dollereder <knud.dollereder@qt.io>2022-01-25 14:25:04 +0000
commitebac821e9ee3b1e9e1ba73610346c2ea1ad92523 (patch)
treef1e84622ac74d22cca648d9c32b16fa50c75a3f5 /src
parenta03a50a262760d870433347e5e397f1577ba70cd (diff)
Replace minitrace with nanotrace
Minitrace is a library to produce json traces suitable for chromes built-in trace viewer. Unfortunately it lacks the ability to add custom arguments to the json files which is needed in our case in order to examine Design Studios state from within the evaluating python script. Nanotrace is a rewrite of minitrace that adds this ability. Additional benefits are higher precision and the fact that it is written in c++. Change-Id: Id2610b93188483d88547369c3fa8dacbe4dee9f7 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/libs/3rdparty/CMakeLists.txt1
-rw-r--r--src/libs/3rdparty/minitrace/CMakeLists.txt9
-rw-r--r--src/libs/3rdparty/minitrace/private/minitrace.c484
-rw-r--r--src/libs/3rdparty/minitrace/private/minitrace.h270
-rw-r--r--src/libs/3rdparty/minitrace/trace.h62
-rw-r--r--src/libs/CMakeLists.txt1
-rw-r--r--src/libs/nanotrace/CMakeLists.txt12
-rw-r--r--src/libs/nanotrace/nanotrace.cpp286
-rw-r--r--src/libs/nanotrace/nanotrace.h114
-rw-r--r--src/libs/nanotrace/python/assets/stylesheet.css5
-rw-r--r--src/libs/nanotrace/python/figures.py207
-rw-r--r--src/libs/nanotrace/python/nanotrace.py82
-rw-r--r--src/libs/nanotrace/python/reader.py210
-rw-r--r--src/plugins/qmldesigner/CMakeLists.txt5
-rw-r--r--src/plugins/qmldesigner/designercore/include/nodeinstanceview.h3
-rw-r--r--src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp22
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp5
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanager.h2
-rw-r--r--src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h2
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp21
-rw-r--r--src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp14
-rw-r--r--src/plugins/qmldesigner/qmldesignercore.cmake2
-rw-r--r--src/plugins/qmldesigner/qmldesignerplugin.cpp37
-rw-r--r--src/tools/qml2puppet/CMakeLists.txt18
24 files changed, 1048 insertions, 826 deletions
diff --git a/src/libs/3rdparty/CMakeLists.txt b/src/libs/3rdparty/CMakeLists.txt
index 8297f25690c..7cf97ab87fc 100644
--- a/src/libs/3rdparty/CMakeLists.txt
+++ b/src/libs/3rdparty/CMakeLists.txt
@@ -1,3 +1,2 @@
add_subdirectory(cplusplus)
add_subdirectory(syntax-highlighting)
-add_subdirectory(minitrace)
diff --git a/src/libs/3rdparty/minitrace/CMakeLists.txt b/src/libs/3rdparty/minitrace/CMakeLists.txt
deleted file mode 100644
index 726e022cb14..00000000000
--- a/src/libs/3rdparty/minitrace/CMakeLists.txt
+++ /dev/null
@@ -1,9 +0,0 @@
-
-# Enable with BUILD_LIBRARY_MINITRACE=ON
-add_qtc_library(Minitrace
- BUILD_DEFAULT OFF
- DEFINES MINITRACE_LIBRARY
- PUBLIC_DEFINES MTR_ENABLED
- SOURCES private/minitrace.c
-)
-
diff --git a/src/libs/3rdparty/minitrace/private/minitrace.c b/src/libs/3rdparty/minitrace/private/minitrace.c
deleted file mode 100644
index 2877f048e86..00000000000
--- a/src/libs/3rdparty/minitrace/private/minitrace.c
+++ /dev/null
@@ -1,484 +0,0 @@
-// minitrace
-// Copyright 2014 by Henrik Rydgård
-// http://www.github.com/hrydgard/minitrace
-// Released under the MIT license.
-
-// See minitrace.h for basic documentation.
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#ifdef _WIN32
-#pragma warning (disable:4996)
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#define __thread __declspec(thread)
-#define pthread_mutex_t CRITICAL_SECTION
-#define pthread_mutex_init(a, b) InitializeCriticalSection(a)
-#define pthread_mutex_lock(a) EnterCriticalSection(a)
-#define pthread_mutex_unlock(a) LeaveCriticalSection(a)
-#define pthread_mutex_destroy(a) DeleteCriticalSection(a)
-#else
-#include <signal.h>
-#include <pthread.h>
-#include <sys/time.h>
-#include <unistd.h>
-#endif
-
-#include "minitrace.h"
-
-#ifdef __GNUC__
-#define ATTR_NORETURN __attribute__((noreturn))
-#else
-#define ATTR_NORETURN
-#endif
-
-#define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0])
-#define TRUE 1
-#define FALSE 0
-
-// Ugh, this struct is already pretty heavy.
-// Will probably need to move arguments to a second buffer to support more than one.
-typedef struct raw_event {
- const char *name;
- const char *cat;
- void *id;
- int64_t ts;
- uint32_t pid;
- uint32_t tid;
- char ph;
- mtr_arg_type arg_type;
- const char *arg_name;
- union {
- const char *a_str;
- int a_int;
- double a_double;
- };
-} raw_event_t;
-
-static raw_event_t *event_buffer;
-static raw_event_t *flush_buffer;
-static volatile int event_count;
-static int is_tracing = FALSE;
-static int is_flushing = FALSE;
-static int events_in_progress = 0;
-static int64_t time_offset;
-static int first_line = 1;
-static FILE *f;
-static __thread int cur_thread_id; // Thread local storage
-static int cur_process_id;
-static pthread_mutex_t mutex;
-static pthread_mutex_t event_mutex;
-
-#define STRING_POOL_SIZE 100
-static char *str_pool[100];
-
-// forward declaration
-void mtr_flush_with_state(int);
-
-// Tiny portability layer.
-// Exposes:
-// get_cur_thread_id()
-// get_cur_process_id()
-// mtr_time_s()
-// pthread basics
-#ifdef _WIN32
-static int get_cur_thread_id() {
- return (int)GetCurrentThreadId();
-}
-static int get_cur_process_id() {
- return (int)GetCurrentProcessId();
-}
-
-static uint64_t _frequency = 0;
-static uint64_t _starttime = 0;
-double mtr_time_s() {
- if (_frequency == 0) {
- QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency);
- QueryPerformanceCounter((LARGE_INTEGER*)&_starttime);
- }
- __int64 time;
- QueryPerformanceCounter((LARGE_INTEGER*)&time);
- return ((double) (time - _starttime) / (double) _frequency);
-}
-
-// Ctrl+C handling for Windows console apps
-static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) {
- if (is_tracing && fdwCtrlType == CTRL_C_EVENT) {
- printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
- mtr_flush();
- mtr_shutdown();
- }
- ExitProcess(1);
-}
-
-void mtr_register_sigint_handler() {
- // For console apps:
- SetConsoleCtrlHandler(&CtrlHandler, TRUE);
-}
-
-#else
-
-static inline int get_cur_thread_id() {
- return (int)(intptr_t)pthread_self();
-}
-static inline int get_cur_process_id() {
- return (int)getpid();
-}
-
-#if defined(BLACKBERRY)
-double mtr_time_s() {
- struct timespec time;
- clock_gettime(CLOCK_MONOTONIC, &time); // Linux must use CLOCK_MONOTONIC_RAW due to time warps
- return time.tv_sec + time.tv_nsec / 1.0e9;
-}
-#else
-double mtr_time_s() {
- static time_t start;
- struct timeval tv;
- gettimeofday(&tv, NULL);
- if (start == 0) {
- start = tv.tv_sec;
- }
- tv.tv_sec -= start;
- return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0;
-}
-#endif // !BLACKBERRY
-
-static void termination_handler(int signum) ATTR_NORETURN;
-static void termination_handler(int signum) {
- (void) signum;
- if (is_tracing) {
- printf("Ctrl-C detected! Flushing trace and shutting down.\n\n");
- mtr_flush();
- fwrite("\n]}\n", 1, 4, f);
- fclose(f);
- }
- exit(1);
-}
-
-void mtr_register_sigint_handler() {
-#ifndef MTR_ENABLED
- return;
-#endif
- // Avoid altering set-to-be-ignored handlers while registering.
- if (signal(SIGINT, &termination_handler) == SIG_IGN)
- signal(SIGINT, SIG_IGN);
-}
-
-#endif
-
-void mtr_init_from_stream(void *stream) {
-#ifndef MTR_ENABLED
- return;
-#endif
- event_buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t));
- flush_buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t));
- is_tracing = 1;
- event_count = 0;
- f = (FILE *)stream;
- const char *header = "{\"traceEvents\":[\n";
- fwrite(header, 1, strlen(header), f);
- time_offset = (uint64_t)(mtr_time_s() * 1000000);
- first_line = 1;
- pthread_mutex_init(&mutex, 0);
- pthread_mutex_init(&event_mutex, 0);
-}
-
-void mtr_init(const char *json_file) {
-#ifndef MTR_ENABLED
- return;
-#endif
- mtr_init_from_stream(fopen(json_file, "wb"));
-}
-
-void mtr_shutdown() {
- int i;
-#ifndef MTR_ENABLED
- return;
-#endif
- pthread_mutex_lock(&mutex);
- is_tracing = FALSE;
- pthread_mutex_unlock(&mutex);
- mtr_flush_with_state(TRUE);
-
- fwrite("\n]}\n", 1, 4, f);
- fclose(f);
- pthread_mutex_destroy(&mutex);
- pthread_mutex_destroy(&event_mutex);
- f = 0;
- free(event_buffer);
- event_buffer = 0;
- for (i = 0; i < STRING_POOL_SIZE; i++) {
- if (str_pool[i]) {
- free(str_pool[i]);
- str_pool[i] = 0;
- }
- }
-}
-
-const char *mtr_pool_string(const char *str) {
- int i;
- for (i = 0; i < STRING_POOL_SIZE; i++) {
- if (!str_pool[i]) {
- str_pool[i] = (char*)malloc(strlen(str) + 1);
- strcpy(str_pool[i], str);
- return str_pool[i];
- } else {
- if (!strcmp(str, str_pool[i]))
- return str_pool[i];
- }
- }
- return "string pool full";
-}
-
-void mtr_start() {
-#ifndef MTR_ENABLED
- return;
-#endif
- pthread_mutex_lock(&mutex);
- is_tracing = TRUE;
- pthread_mutex_unlock(&mutex);
-}
-
-void mtr_stop() {
-#ifndef MTR_ENABLED
- return;
-#endif
- pthread_mutex_lock(&mutex);
- is_tracing = FALSE;
- pthread_mutex_unlock(&mutex);
-}
-
-// TODO: fwrite more than one line at a time.
-// Flushing is thread safe and process async
-// using double-buffering mechanism.
-// Aware: only one flushing process may be
-// running at any point of time
-void mtr_flush_with_state(int is_last) {
-#ifndef MTR_ENABLED
- return;
-#endif
- int i = 0;
- char linebuf[1024];
- char arg_buf[1024];
- char id_buf[256];
- int event_count_copy = 0;
- int events_in_progress_copy = 1;
- raw_event_t *event_buffer_tmp = NULL;
-
- // small critical section to swap buffers
- // - no any new events can be spawn while
- // swapping since they tied to the same mutex
- // - checks for any flushing in process
- pthread_mutex_lock(&mutex);
- // if not flushing already
- if (is_flushing) {
- pthread_mutex_unlock(&mutex);
- return;
- }
- is_flushing = TRUE;
- event_count_copy = event_count;
- event_buffer_tmp = flush_buffer;
- flush_buffer = event_buffer;
- event_buffer = event_buffer_tmp;
- event_count = 0;
- // waiting for any unfinished events before swap
- while (events_in_progress_copy != 0) {
- pthread_mutex_lock(&event_mutex);
- events_in_progress_copy = events_in_progress;
- pthread_mutex_unlock(&event_mutex);
- }
- pthread_mutex_unlock(&mutex);
-
- for (i = 0; i < event_count_copy; i++) {
- raw_event_t *raw = &flush_buffer[i];
- int len;
- switch (raw->arg_type) {
- case MTR_ARG_TYPE_INT:
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":%i", raw->arg_name, raw->a_int);
- break;
- case MTR_ARG_TYPE_STRING_CONST:
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
- break;
- case MTR_ARG_TYPE_STRING_COPY:
- if (strlen(raw->a_str) > 700) {
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%.*s\"", raw->arg_name, 700, raw->a_str);
- } else {
- snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str);
- }
- break;
- case MTR_ARG_TYPE_NONE:
- arg_buf[0] = '\0';
- break;
- }
- if (raw->id) {
- switch (raw->ph) {
- case 'S':
- case 'T':
- case 'F':
- // TODO: Support full 64-bit pointers
- snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"id\":\"0x%08x\"", (uint32_t)(uintptr_t)raw->id);
- break;
- case 'X':
- snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"dur\":%i", (int)raw->a_double);
- break;
- }
- } else {
- id_buf[0] = 0;
- }
- const char *cat = raw->cat;
-#ifdef _WIN32
- // On Windows, we often end up with backslashes in category.
- char temp[256];
- {
- int len = (int)strlen(cat);
- int i;
- if (len > 255) len = 255;
- for (i = 0; i < len; i++) {
- temp[i] = cat[i] == '\\' ? '/' : cat[i];
- }
- temp[len] = 0;
- cat = temp;
- }
-#endif
-
- len = snprintf(linebuf, ARRAY_SIZE(linebuf), "%s{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ",\"ph\":\"%c\",\"name\":\"%s\",\"args\":{%s}%s}",
- first_line ? "" : ",\n",
- cat, raw->pid, raw->tid, raw->ts - time_offset, raw->ph, raw->name, arg_buf, id_buf);
- fwrite(linebuf, 1, len, f);
- first_line = 0;
-
- if (raw->arg_type == MTR_ARG_TYPE_STRING_COPY) {
- free((void*)raw->a_str);
- }
- #ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
- free(raw->name);
- free(raw->cat);
- #endif
- }
-
- pthread_mutex_lock(&mutex);
- is_flushing = is_last;
- pthread_mutex_unlock(&mutex);
-}
-
-void mtr_flush() {
- mtr_flush_with_state(FALSE);
-}
-
-void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id) {
-#ifndef MTR_ENABLED
- return;
-#endif
- pthread_mutex_lock(&mutex);
- if (!is_tracing || event_count >= INTERNAL_MINITRACE_BUFFER_SIZE) {
- pthread_mutex_unlock(&mutex);
- return;
- }
- raw_event_t *ev = &event_buffer[event_count];
- ++event_count;
- pthread_mutex_lock(&event_mutex);
- ++events_in_progress;
- pthread_mutex_unlock(&event_mutex);
- pthread_mutex_unlock(&mutex);
-
- double ts = mtr_time_s();
- if (!cur_thread_id) {
- cur_thread_id = get_cur_thread_id();
- }
- if (!cur_process_id) {
- cur_process_id = get_cur_process_id();
- }
-
-#ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
- const size_t category_len = strlen(category);
- ev->cat = malloc(category_len + 1);
- strcpy(ev->cat, category);
-
- const size_t name_len = strlen(name);
- ev->name = malloc(name_len + 1);
- strcpy(ev->name, name);
-
-#else
- ev->cat = category;
- ev->name = name;
-#endif
-
- ev->id = id;
- ev->ph = ph;
- if (ev->ph == 'X') {
- double x;
- memcpy(&x, id, sizeof(double));
- ev->ts = (int64_t)(x * 1000000);
- ev->a_double = (ts - x) * 1000000;
- } else {
- ev->ts = (int64_t)(ts * 1000000);
- }
- ev->tid = cur_thread_id;
- ev->pid = cur_process_id;
- ev->arg_type = MTR_ARG_TYPE_NONE;
-
- pthread_mutex_lock(&event_mutex);
- --events_in_progress;
- pthread_mutex_unlock(&event_mutex);
-}
-
-void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value) {
-#ifndef MTR_ENABLED
- return;
-#endif
- pthread_mutex_lock(&mutex);
- if (!is_tracing || event_count >= INTERNAL_MINITRACE_BUFFER_SIZE) {
- pthread_mutex_unlock(&mutex);
- return;
- }
- raw_event_t *ev = &event_buffer[event_count];
- ++event_count;
- pthread_mutex_lock(&event_mutex);
- ++events_in_progress;
- pthread_mutex_unlock(&event_mutex);
- pthread_mutex_unlock(&mutex);
-
- if (!cur_thread_id) {
- cur_thread_id = get_cur_thread_id();
- }
- if (!cur_process_id) {
- cur_process_id = get_cur_process_id();
- }
- double ts = mtr_time_s();
-
-#ifdef MTR_COPY_EVENT_CATEGORY_AND_NAME
- const size_t category_len = strlen(category);
- ev->cat = malloc(category_len + 1);
- strcpy(ev->cat, category);
-
- const size_t name_len = strlen(name);
- ev->name = malloc(name_len + 1);
- strcpy(ev->name, name);
-
-#else
- ev->cat = category;
- ev->name = name;
-#endif
-
- ev->id = id;
- ev->ts = (int64_t)(ts * 1000000);
- ev->ph = ph;
- ev->tid = cur_thread_id;
- ev->pid = cur_process_id;
- ev->arg_type = arg_type;
- ev->arg_name = arg_name;
- switch (arg_type) {
- case MTR_ARG_TYPE_INT: ev->a_int = (int)(uintptr_t)arg_value; break;
- case MTR_ARG_TYPE_STRING_CONST: ev->a_str = (const char*)arg_value; break;
- case MTR_ARG_TYPE_STRING_COPY: ev->a_str = strdup((const char*)arg_value); break;
- case MTR_ARG_TYPE_NONE: break;
- }
-
- pthread_mutex_lock(&event_mutex);
- --events_in_progress;
- pthread_mutex_unlock(&event_mutex);
-}
-
diff --git a/src/libs/3rdparty/minitrace/private/minitrace.h b/src/libs/3rdparty/minitrace/private/minitrace.h
deleted file mode 100644
index 2047eedb78c..00000000000
--- a/src/libs/3rdparty/minitrace/private/minitrace.h
+++ /dev/null
@@ -1,270 +0,0 @@
-// Minitrace
-//
-// Copyright 2014 by Henrik Rydgård
-// http://www.github.com/hrydgard/minitrace
-// Released under the MIT license.
-//
-// Ultra-light dependency free library for performance tracing C/C++ applications.
-// Produces traces compatible with Google Chrome's trace viewer.
-// Simply open "about:tracing" in Chrome and load the produced JSON.
-//
-// This contains far less template magic than the original libraries from Chrome
-// because this is meant to be usable from C.
-//
-// See README.md for a tutorial.
-//
-// The trace format is documented here:
-// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit
-// More:
-// http://www.altdevblogaday.com/2012/08/21/using-chrometracing-to-view-your-inline-profiling-data/
-
-#ifndef MINITRACE_H
-#define MINITRACE_H
-
-#include <inttypes.h>
-
-// If MTR_ENABLED is not defined, Minitrace does nothing and has near zero overhead.
-// Preferably, set this flag in your build system. If you can't just uncomment this line.
-// #define MTR_ENABLED
-
-// By default, will collect up to 1000000 events, then you must flush.
-// It's recommended that you simply call mtr_flush on a background thread
-// occasionally. It's safe...ish.
-#define INTERNAL_MINITRACE_BUFFER_SIZE 1000000
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// Initializes Minitrace. Must be called very early during startup of your executable,
-// before any MTR_ statements.
-void mtr_init(const char *json_file);
-// Same as above, but allows passing in a custom stream (FILE *), as returned by
-// fopen(). It should be opened for writing, preferably in binary mode to avoid
-// processing of line endings (i.e. the "wb" mode).
-void mtr_init_from_stream(void *stream);
-
-// Shuts down minitrace cleanly, flushing the trace buffer.
-void mtr_shutdown(void);
-
-// Lets you enable and disable Minitrace at runtime.
-// May cause strange discontinuities in the output.
-// Minitrace is enabled on startup by default.
-void mtr_start(void);
-void mtr_stop(void);
-
-// Flushes the collected data to disk, clearing the buffer for new data.
-void mtr_flush(void);
-
-// Returns the current time in seconds. Used internally by Minitrace. No caching.
-double mtr_time_s(void);
-
-// Registers a handler that will flush the trace on Ctrl+C.
-// Works on Linux and MacOSX, and in Win32 console applications.
-void mtr_register_sigint_handler(void);
-
-// Utility function that should rarely be used.
-// If str is semi dynamic, store it permanently in a small pool so we don't need to malloc it.
-// The pool fills up fast though and performance isn't great.
-// Returns a fixed string if the pool is full.
-const char *mtr_pool_string(const char *str);
-
-// Commented-out types will be supported in the future.
-typedef enum {
- MTR_ARG_TYPE_NONE = 0,
- MTR_ARG_TYPE_INT = 1, // I
- // MTR_ARG_TYPE_FLOAT = 2, // TODO
- // MTR_ARG_TYPE_DOUBLE = 3, // TODO
- MTR_ARG_TYPE_STRING_CONST = 8, // C
- MTR_ARG_TYPE_STRING_COPY = 9,
- // MTR_ARG_TYPE_JSON_COPY = 10,
-} mtr_arg_type;
-
-// TODO: Add support for more than one argument (metadata) per event
-// Having more costs speed and memory.
-#define MTR_MAX_ARGS 1
-
-// Only use the macros to call these.
-void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id);
-void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value);
-
-#ifdef MTR_ENABLED
-
-// c - category. Can be filtered by in trace viewer (or at least that's the intention).
-// A good use is to pass __FILE__, there are macros further below that will do it for you.
-// n - name. Pass __FUNCTION__ in most cases, unless you are marking up parts of one.
-
-// Scopes. In C++, use MTR_SCOPE. In C, always match them within the same scope.
-#define MTR_BEGIN(c, n) internal_mtr_raw_event(c, n, 'B', 0)
-#define MTR_END(c, n) internal_mtr_raw_event(c, n, 'E', 0)
-#define MTR_SCOPE(c, n) MTRScopedTrace ____mtr_scope(c, n)
-#define MTR_SCOPE_LIMIT(c, n, l) MTRScopedTraceLimit ____mtr_scope(c, n, l)
-
-// Async events. Can span threads. ID identifies which events to connect in the view.
-#define MTR_START(c, n, id) internal_mtr_raw_event(c, n, 'S', (void *)(id))
-#define MTR_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 'T', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
-#define MTR_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'F', (void *)(id))
-
-// Flow events. Like async events, but displayed in a more fancy way in the viewer.
-#define MTR_FLOW_START(c, n, id) internal_mtr_raw_event(c, n, 's', (void *)(id))
-#define MTR_FLOW_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 't', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step))
-#define MTR_FLOW_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'f', (void *)(id))
-
-// The same macros, but with a single named argument which shows up as metadata in the viewer.
-// _I for int.
-// _C is for a const string arg.
-// _S will copy the string, freeing on flush (expensive but sometimes necessary).
-// but required if the string was generated dynamically.
-
-// Note that it's fine to match BEGIN_S with END and BEGIN with END_S, etc.
-#define MTR_BEGIN_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-#define MTR_END_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-#define MTR_SCOPE_C(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-
-#define MTR_BEGIN_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
-#define MTR_END_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
-#define MTR_SCOPE_S(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval))
-
-#define MTR_BEGIN_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
-#define MTR_END_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
-#define MTR_SCOPE_I(c, n, aname, aintval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval))
-
-// Instant events. For things with no duration.
-#define MTR_INSTANT(c, n) internal_mtr_raw_event(c, n, 'I', 0)
-#define MTR_INSTANT_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval))
-#define MTR_INSTANT_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'I', 0, MTR_ARG_TYPE_INT, aname, (void *)(aintval))
-
-// Counters (can't do multi-value counters yet)
-#define MTR_COUNTER(c, n, val) internal_mtr_raw_event_arg(c, n, 'C', 0, MTR_ARG_TYPE_INT, n, (void *)(intptr_t)(val))
-
-// Metadata. Call at the start preferably. Must be const strings.
-
-#define MTR_META_PROCESS_NAME(n) internal_mtr_raw_event_arg("", "process_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
-#define MTR_META_THREAD_NAME(n) internal_mtr_raw_event_arg("", "thread_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n))
-#define MTR_META_THREAD_SORT_INDEX(i) internal_mtr_raw_event_arg("", "thread_sort_index", 'M', 0, MTR_ARG_TYPE_INT, "sort_index", (void *)(i))
-
-#else
-
-#define MTR_BEGIN(c, n)
-#define MTR_END(c, n)
-#define MTR_SCOPE(c, n)
-#define MTR_START(c, n, id)
-#define MTR_STEP(c, n, id, step)
-#define MTR_FINISH(c, n, id)
-#define MTR_FLOW_START(c, n, id)
-#define MTR_FLOW_STEP(c, n, id, step)
-#define MTR_FLOW_FINISH(c, n, id)
-#define MTR_INSTANT(c, n)
-
-#define MTR_BEGIN_C(c, n, aname, astrval)
-#define MTR_END_C(c, n, aname, astrval)
-#define MTR_SCOPE_C(c, n, aname, astrval)
-
-#define MTR_BEGIN_S(c, n, aname, astrval)
-#define MTR_END_S(c, n, aname, astrval)
-#define MTR_SCOPE_S(c, n, aname, astrval)
-
-#define MTR_BEGIN_I(c, n, aname, aintval)
-#define MTR_END_I(c, n, aname, aintval)
-#define MTR_SCOPE_I(c, n, aname, aintval)
-
-#define MTR_INSTANT(c, n)
-#define MTR_INSTANT_C(c, n, aname, astrval)
-#define MTR_INSTANT_I(c, n, aname, aintval)
-
-// Counters (can't do multi-value counters yet)
-#define MTR_COUNTER(c, n, val)
-
-// Metadata. Call at the start preferably. Must be const strings.
-
-#define MTR_META_PROCESS_NAME(n)
-
-#define MTR_META_THREAD_NAME(n)
-#define MTR_META_THREAD_SORT_INDEX(i)
-
-#endif
-
-// Shortcuts for simple function timing with automatic categories and names.
-
-#define MTR_BEGIN_FUNC() MTR_BEGIN(__FILE__, __FUNCTION__)
-#define MTR_END_FUNC() MTR_END(__FILE__, __FUNCTION__)
-#define MTR_SCOPE_FUNC() MTR_SCOPE(__FILE__, __FUNCTION__)
-#define MTR_INSTANT_FUNC() MTR_INSTANT(__FILE__, __FUNCTION__)
-#define MTR_SCOPE_FUNC_LIMIT_S(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, l)
-#define MTR_SCOPE_FUNC_LIMIT_MS(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, (double)l * 0.000001)
-
-// Same, but with a single argument of the usual types.
-#define MTR_BEGIN_FUNC_S(aname, arg) MTR_BEGIN_S(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_END_FUNC_S(aname, arg) MTR_END_S(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_SCOPE_FUNC_S(aname, arg) MTR_SCOPE_S(__FILE__, __FUNCTION__, aname, arg)
-
-#define MTR_BEGIN_FUNC_C(aname, arg) MTR_BEGIN_C(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_END_FUNC_C(aname, arg) MTR_END_C(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_SCOPE_FUNC_C(aname, arg) MTR_SCOPE_C(__FILE__, __FUNCTION__, aname, arg)
-
-#define MTR_BEGIN_FUNC_I(aname, arg) MTR_BEGIN_I(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_END_FUNC_I(aname, arg) MTR_END_I(__FILE__, __FUNCTION__, aname, arg)
-#define MTR_SCOPE_FUNC_I(aname, arg) MTR_SCOPE_I(__FILE__, __FUNCTION__, aname, arg)
-
-#ifdef __cplusplus
-}
-
-#ifdef MTR_ENABLED
-// These are optimized to use X events (combined B and E). Much easier to do in C++ than in C.
-class MTRScopedTrace {
-public:
- MTRScopedTrace(const char *category, const char *name)
- : category_(category), name_(name) {
- start_time_ = mtr_time_s();
- }
- ~MTRScopedTrace() {
- internal_mtr_raw_event(category_, name_, 'X', &start_time_);
- }
-
-private:
- const char *category_;
- const char *name_;
- double start_time_;
-};
-
-// Only outputs a block if execution time exceeded the limit.
-// TODO: This will effectively call mtr_time_s twice at the end, which is bad.
-class MTRScopedTraceLimit {
-public:
- MTRScopedTraceLimit(const char *category, const char *name, double limit_s)
- : category_(category), name_(name), limit_(limit_s) {
- start_time_ = mtr_time_s();
- }
- ~MTRScopedTraceLimit() {
- double end_time = mtr_time_s();
- if (end_time - start_time_ >= limit_) {
- internal_mtr_raw_event(category_, name_, 'X', &start_time_);
- }
- }
-
-private:
- const char *category_;
- const char *name_;
- double start_time_;
- double limit_;
-};
-
-class MTRScopedTraceArg {
-public:
- MTRScopedTraceArg(const char *category, const char *name, mtr_arg_type arg_type, const char *arg_name, void *arg_value)
- : category_(category), name_(name) {
- internal_mtr_raw_event_arg(category, name, 'B', 0, arg_type, arg_name, arg_value);
- }
- ~MTRScopedTraceArg() {
- internal_mtr_raw_event(category_, name_, 'E', 0);
- }
-
-private:
- const char *category_;
- const char *name_;
-};
-#endif
-
-#endif
-
-#endif
diff --git a/src/libs/3rdparty/minitrace/trace.h b/src/libs/3rdparty/minitrace/trace.h
deleted file mode 100644
index 1de9ae95df3..00000000000
--- a/src/libs/3rdparty/minitrace/trace.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of Qt Creator.
-**
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-****************************************************************************/
-
-#pragma once
-
-#include "private/minitrace.h"
-
-#ifdef MTR_ENABLED
-
-#define MINITRACE_INITIALIZE(FILENAME, PROCESSNAME, THREADNAME) \
- Minitrace::initialize(FILENAME, PROCESSNAME, THREADNAME)
-
-#define MINITRACE_SHUTDOWN() Minitrace::shutdown()
-
-#else
-
-#define MINITRACE_INITIALIZE(FILENAME, PROCESSNAME, THREADNAME)
-
-#define MINITRACE_SHUTDOWN()
-
-#endif
-
-
-namespace Minitrace
-{
-
-inline void initialize(const char* fileName, const char* processName, const char* threadName)
-{
- mtr_init(fileName);
- MTR_META_PROCESS_NAME(processName);
- MTR_META_THREAD_NAME(threadName);
-}
-
-inline void shutdown()
-{
- mtr_flush();
- mtr_shutdown();
-}
-
-}
diff --git a/src/libs/CMakeLists.txt b/src/libs/CMakeLists.txt
index df1826f7342..aee880b4ddb 100644
--- a/src/libs/CMakeLists.txt
+++ b/src/libs/CMakeLists.txt
@@ -7,6 +7,7 @@ add_subdirectory(utils)
add_subdirectory(languageutils)
add_subdirectory(cplusplus)
add_subdirectory(modelinglib)
+add_subdirectory(nanotrace)
add_subdirectory(qmljs)
add_subdirectory(qmldebug)
add_subdirectory(qmleditorwidgets)
diff --git a/src/libs/nanotrace/CMakeLists.txt b/src/libs/nanotrace/CMakeLists.txt
new file mode 100644
index 00000000000..053d7b1c487
--- /dev/null
+++ b/src/libs/nanotrace/CMakeLists.txt
@@ -0,0 +1,12 @@
+
+add_qtc_library(Nanotrace
+ BUILD_DEFAULT OFF
+ DEFINES NANOTRACE_LIBRARY
+ PUBLIC_DEFINES NANOTRACE_ENABLED
+ SOURCES nanotrace.cpp nanotrace.h
+ PROPERTIES
+ CXX_VISIBILITY_PRESET default
+ VISIBILITY_INLINES_HIDDEN OFF
+ WINDOWS_EXPORT_ALL_SYMBOLS ON
+)
+
diff --git a/src/libs/nanotrace/nanotrace.cpp b/src/libs/nanotrace/nanotrace.cpp
new file mode 100644
index 00000000000..36c7fd74590
--- /dev/null
+++ b/src/libs/nanotrace/nanotrace.cpp
@@ -0,0 +1,286 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+
+#include "nanotrace.h"
+
+#include <chrono>
+#include <thread>
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <unistd.h>
+#endif
+
+namespace Nanotrace
+{
+
+Arg::Arg(const std::string &name, const SupportedType &val)
+ : m_name(name)
+ , m_value(val)
+{ }
+
+std::string Arg::name() const
+{
+ return m_name;
+}
+
+struct ConvertArgValueToString {
+ std::string operator()(const int &v) { return std::to_string(v); }
+ std::string operator()(const int64_t &v) { return std::to_string(v); }
+ std::string operator()(const double &v) { return std::to_string(v); }
+ std::string operator()(const std::string &v) { return "\"" + v + "\""; }
+};
+
+std::string Arg::value() const
+{
+ return std::visit(ConvertArgValueToString(), m_value);
+}
+
+
+struct InitEvent
+{
+ bool initialized = false;
+
+ int32_t pid;
+ std::string processName;
+
+ std::thread::id tid;
+ std::string threadName;
+
+ std::string filePath;
+
+ TimePoint ts;
+
+ int64_t overhead;
+};
+
+
+struct TraceEvent
+{
+ int32_t pid;
+
+ std::thread::id tid;
+
+ std::string name;
+ std::string cat;
+
+ char ph;
+
+ int64_t oh;
+ int64_t ts;
+
+ std::vector< Nanotrace::Arg > args;
+
+ friend std::ostream& operator<<(std::ostream& stream, const TraceEvent& event);
+};
+
+
+constexpr size_t eventCount = 10000;
+
+static std::vector< TraceEvent > events;
+
+static thread_local InitEvent initEvent;
+
+static int32_t getProcessId() {
+#ifdef _WIN32
+return static_cast< int32_t >(GetCurrentProcessId());
+#else
+return static_cast< int32_t >(getpid());
+#endif
+}
+
+std::ostream& operator<<(std::ostream &stream, const TraceEvent &event)
+{
+ stream
+ << "{ \"cat\":\"" << event.cat
+ << "\", \"pid\":" << event.pid
+ << ", \"tid\":\"" << event.tid << "\""
+ << ", \"ts\":" << event.ts
+ << ", \"ph\":\"" << event.ph
+ << "\", \"name\":\"" << event.name
+ << "\", \"args\": { \"overhead\": " << event.oh;
+
+ for (auto&& arg : event.args)
+ stream << ", \"" << arg.name() <<"\": " << arg.value();
+
+ stream << " } }";
+ return stream;
+}
+
+
+void init(const std::string &process, const std::string &thread, const std::string &path)
+{
+ auto now = Clock::now();
+ initEvent.initialized = true;
+ initEvent.pid = getProcessId();
+ initEvent.processName = process;
+ initEvent.tid = std::this_thread::get_id();
+ initEvent.threadName = thread;
+ initEvent.filePath = path;
+ initEvent.ts = now;
+
+ events.reserve(eventCount);
+ events.push_back(TraceEvent {
+ getProcessId(),
+ std::this_thread::get_id(),
+ "process_name",
+ "",
+ 'M',
+ 0,
+ 0,
+ {{"name", process}}
+ } );
+
+ events.push_back(TraceEvent {
+ getProcessId(),
+ std::this_thread::get_id(),
+ "thread_name",
+ "",
+ 'M',
+ 0,
+ 0,
+ {{"name", thread}}
+ } );
+
+ if (std::ofstream stream(path, std::ios::trunc); stream.good())
+ stream << "{ \"traceEvents\": [\n";
+ else
+ std::cout << "Nanotrace::init: stream not good" << std::endl;
+
+ events.push_back(TraceEvent {
+ getProcessId(),
+ std::this_thread::get_id(),
+ "Initialize",
+ "Initialize",
+ 'I',
+ 0,
+ std::chrono::duration_cast< Units >(Clock::now() - now).count(),
+ {}
+ } );
+
+ initEvent.overhead = std::chrono::duration_cast< Units >(Clock::now() - now).count();
+}
+
+void shutdown()
+{
+ flush();
+
+ if (std::ofstream stream(initEvent.filePath, std::ios::app); stream.good())
+ stream << "\n] }";
+ else
+ std::cout << "Nanotrace::shutdown: stream not good" << std::endl;
+}
+
+void addTracePoint(
+ const std::string &name,
+ const std::string &cat,
+ char phase,
+ std::initializer_list< Nanotrace::Arg > arguments)
+{
+ if (!initEvent.initialized)
+ return;
+
+ auto now = Clock::now();
+ auto beg = std::chrono::duration_cast< Units >(now - initEvent.ts);
+
+ events.push_back( TraceEvent {
+ getProcessId(),
+ std::this_thread::get_id(),
+ name,
+ cat,
+ phase,
+ initEvent.overhead,
+ beg.count(),
+ {arguments}
+ } );
+
+ if (events.size() >= eventCount - 1)
+ flush();
+
+ initEvent.overhead = std::chrono::duration_cast< Units >(Clock::now() - now).count();
+}
+
+void flush()
+{
+ if (events.empty())
+ return;
+
+ if (std::ofstream stream(initEvent.filePath, std::ios::app); stream.good()) {
+ stream << events[0];
+ for (size_t i=1; i<events.size(); ++i) {
+ stream << ",\n" << events[i];
+ }
+ } else {
+ std::cout << "Nanotrace::flush: stream not good" << std::endl;
+ }
+
+ events.clear();
+}
+
+ScopeTracer::ScopeTracer(
+ const std::string &name,
+ const std::string &cat,
+ std::initializer_list< Nanotrace::Arg > arguments)
+ : m_start(Clock::now())
+ , m_name(name)
+ , m_cat(cat)
+ , m_args(arguments)
+{ }
+
+ScopeTracer::~ScopeTracer()
+{
+ if (!initEvent.initialized)
+ return;
+
+ auto now = Clock::now();
+ auto beg = std::chrono::duration_cast< Units >(m_start - initEvent.ts);
+
+ m_args.push_back(Arg("dur", std::chrono::duration_cast< Units >(now - m_start).count()));
+
+ events.push_back(TraceEvent {
+ getProcessId(),
+ std::this_thread::get_id(),
+ m_name,
+ m_cat,
+ 'X',
+ initEvent.overhead,
+ beg.count(),
+ { m_args }
+ } );
+
+ if (events.size() >= eventCount - 1)
+ flush();
+
+ initEvent.overhead = std::chrono::duration_cast< Units >(Clock::now() - now).count();
+}
+
+} // End namespace Nanotrace.
+
+
diff --git a/src/libs/nanotrace/nanotrace.h b/src/libs/nanotrace/nanotrace.h
new file mode 100644
index 00000000000..c90407d6365
--- /dev/null
+++ b/src/libs/nanotrace/nanotrace.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2022 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <string>
+#include <variant>
+#include <vector>
+
+
+#ifdef NANOTRACE_ENABLED
+
+#define NANOTRACE_INIT(process, thread, filepath) Nanotrace::init(process, thread, filepath)
+#define NANOTRACE_SHUTDOWN() Nanotrace::shutdown()
+
+#define NANOTRACE_SCOPE(cat, name) Nanotrace::ScopeTracer nanotraceScopedTracerObject(name, cat, {})
+#define NANOTRACE_INSTANT(cat, name) Nanotrace::addTracePoint(name, cat, 'I', {})
+#define NANOTRACE_BEGIN(cat, name) Nanotrace::addTracePoint(name, cat, 'B', {})
+#define NANOTRACE_END(cat, name) Nanotrace::addTracePoint(name, cat, 'E', {})
+
+#define NANOTRACE_SCOPE_ARGS(cat, name, ...) Nanotrace::ScopeTracer nanotraceScopedTracerObject(name, cat, {__VA_ARGS__})
+#define NANOTRACE_INSTANT_ARGS(cat, name, ...) Nanotrace::addTracePoint(name, cat, 'I', {__VA_ARGS__})
+#define NANOTRACE_BEGIN_ARGS(cat, name, ...) Nanotrace::addTracePoint(name, cat, 'B', {__VA_ARGS__})
+
+#else
+
+#define NANOTRACE_INIT(process, thread, filepath)
+#define NANOTRACE_SHUTDOWN()
+
+#define NANOTRACE_SCOPE(cat, name)
+#define NANOTRACE_INSTANT(cat, name)
+#define NANOTRACE_BEGIN(cat, name)
+#define NANOTRACE_END(cat, name)
+
+#define NANOTRACE_SCOPE_ARGS(cat, name, ...)
+#define NANOTRACE_INSTANT_ARGS(cat, name, ...)
+#define NANOTRACE_BEGIN_ARGS(cat, name, ...)
+
+#endif
+
+
+namespace Nanotrace
+{
+
+using Units = std::chrono::nanoseconds;
+using Clock = std::chrono::high_resolution_clock;
+using TimePoint = std::chrono::time_point< Clock >;
+
+class Arg
+{
+public:
+ using SupportedType = std::variant<int, int64_t, double, std::string>;
+
+ Arg(const std::string &name, const SupportedType &val);
+ std::string name() const;
+ std::string value() const;
+
+private:
+ std::string m_name;
+ SupportedType m_value;
+};
+
+void init(const std::string &process, const std::string &thread, const std::string &path);
+
+void shutdown();
+
+void flush();
+
+void addTracePoint(
+ const std::string &name,
+ const std::string &cat,
+ char phase,
+ std::initializer_list< Nanotrace::Arg > arguments);
+
+class ScopeTracer
+{
+public:
+ ScopeTracer(
+ const std::string &name,
+ const std::string &cat,
+ std::initializer_list< Nanotrace::Arg > arguments);
+ ~ScopeTracer();
+
+private:
+ TimePoint m_start;
+ std::string m_name;
+ std::string m_cat;
+ std::vector< Nanotrace::Arg > m_args;
+};
+
+} // End namespace Nanotrace.
+
diff --git a/src/libs/nanotrace/python/assets/stylesheet.css b/src/libs/nanotrace/python/assets/stylesheet.css
new file mode 100644
index 00000000000..198e16f9845
--- /dev/null
+++ b/src/libs/nanotrace/python/assets/stylesheet.css
@@ -0,0 +1,5 @@
+
+body {
+ margin: 0;
+ background-color: #111111
+}
diff --git a/src/libs/nanotrace/python/figures.py b/src/libs/nanotrace/python/figures.py
new file mode 100644
index 00000000000..082990d6c44
--- /dev/null
+++ b/src/libs/nanotrace/python/figures.py
@@ -0,0 +1,207 @@
+############################################################################
+#
+# Copyright (C) 2022 The Qt Company Ltd.
+# Contact: https://www.qt.io/licensing/
+#
+# This file is part of Qt Creator.
+#
+# Commercial License Usage
+# Licensees holding valid commercial Qt licenses may use this file in
+# accordance with the commercial license agreement provided with the
+# Software or, alternatively, in accordance with the terms contained in
+# a written agreement between you and The Qt Company. For licensing terms
+# and conditions see https://www.qt.io/terms-conditions. For further
+# information use the contact form at https://www.qt.io/contact-us.
+#
+# GNU General Public License Usage
+# Alternatively, this file may be used under the terms of the GNU
+# General Public License version 3 as published by the Free Software
+# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+# included in the packaging of this file. Please review the following
+# information to ensure the GNU General Public License requirements will
+# be met: https://www.gnu.org/licenses/gpl-3.0.html.
+#
+############################################################################
+import pandas as pd
+import plotly.graph_objects as go
+import plotly.subplots as sp
+import plotly.express as px
+
+def readWriteAppend(data, name, rowA, rowB, group, colorIdx):
+ if rowA.empty or rowB.empty:
+ return
+ data['processName'].append(name)
+ data['threadName'].append(rowA['threadName'])
+ data['name'].append("ReadWrite")
+ data['begin'].append(rowA['begin'])
+ data['duration'].append(rowB['begin']-rowA['begin'])
+ data['fromName'].append(rowA['command'])
+ data['toName'].append(rowB['command'])
+ data['fromCounter'].append(rowA['counter'])
+ data['toCounter'].append(rowB['counter'])
+ data['group'].append(group)
+ data['color'].append(px.colors.qualitative.Plotly[colorIdx])
+
+
+def recursiveAdd(data, dFromRowD, dfd, dfp, writeCmd, readCmd, trackName, counter):
+ dFromRow = dFromRowD.squeeze()
+
+ dToMask = (dfd['name']=="writeCommand") & (dfd['command']==writeCmd) & (dfd['begin']>dFromRow['begin'])
+ dToRowD = dfd.loc[ dToMask ].head(1)
+ dToRow = dToRowD.squeeze()
+ if dToRowD.empty: return
+
+ dfd.drop([dFromRowD.index[0], dToRowD.index[0]], errors='ignore', inplace=True)
+
+ pFromMask = (dfp['name']=="readCommand") & (dfp['command']==writeCmd) & (dfp['counter']==dToRow['counter'])
+ pFromRowD = dfp.loc[ pFromMask ].head(1)
+ pFromRow = pFromRowD.squeeze()
+ if pFromRowD.empty: return
+
+ pToMask = (dfp['name']=="writeCommand") & (dfp['command']==readCmd) & (dfp['begin']>pFromRow['begin'])
+ pToRowD = dfp.loc[ pToMask ].head(1)
+ pToRow = pToRowD.squeeze()
+ if pToRowD.empty: return
+
+ dfp.drop([pFromRowD.index[0], pToRowD.index[0]], errors='ignore', inplace=True)
+
+ name = trackName + " (" + str(counter) + ")"
+ readWriteAppend(data, name, dFromRow, dToRow, counter, 0)
+ readWriteAppend(data, name, pFromRow, pToRow, counter, counter+1)
+
+ dFromMask = (dfd['name']=="readCommand") & (dfd['command']==readCmd) & (dfd['counter']==pToRow['counter'])
+ dFromRowD = dfd.loc[ dFromMask ].head(1)
+ if not dFromRowD.empty:
+ recursiveAdd(data, dFromRowD, dfd, dfp, writeCmd, readCmd, trackName, counter)
+
+def createDsToPuppetDataframe(dfdo, dfpo, writeCmd, readCmd, trackName):
+ if dfdo.empty or dfpo.empty:
+ return pd.DataFrame()
+
+ cmdMask = lambda df: (df['type'] == "I") & (df['category']=="Update") & ((df['command'] == writeCmd) | (df['command'] == readCmd))
+ dfd = dfdo.loc[cmdMask(dfdo) | (dfdo['category'] == "Initialize")].copy()
+ dfp = dfpo.loc[cmdMask(dfpo)].copy()
+
+ data = {
+ "processName": [],
+ "threadName": [],
+ "name": [],
+ "begin": [],
+ "duration": [],
+ "fromName": [],
+ "toName": [],
+ "fromCounter": [],
+ "toCounter": [],
+ "group": [],
+ "color": []
+ }
+
+ dFromMask = (dfd['category']=="Initialize") & (dfd['name']=="Initialize") & (dfd['command']=="Initialize")
+ dFromRowD = dfd.loc[ dFromMask ].head(1)
+
+ group = 0
+ nothingChanged = False
+ while not nothingChanged:
+ size = len(data['processName'])
+ recursiveAdd(data, dFromRowD, dfd, dfp, writeCmd, readCmd, trackName, group)
+ nothingChanged = size == len(data['processName'])
+ group = group + 1
+
+ df = pd.DataFrame(data)
+ return df
+
+def createDsToPuppetTrace(df):
+ cd = list(zip(
+ df.fromName.tolist(),
+ df.toName.tolist(),
+ df.duration.tolist(),
+ df.fromCounter.tolist(),
+ df.toCounter.tolist(),
+ df.name.tolist()))
+
+ return go.Bar(
+ base = df.begin,
+ x = df.duration,
+ y = df.processName,
+ marker = dict(color=df.color),
+ insidetextanchor = 'middle',
+ orientation = 'h',
+ name = df.processName.iloc[0],
+ customdata = cd,
+ hovertemplate =
+ '<b>%{customdata[5]}</b><br>' +
+ '<b>Name From</b>: %{customdata[0]} (%{customdata[3]})<br>' +
+ '<b>Name To</b>: %{customdata[1]} (%{customdata[4]})<br>' +
+ '<b>Time From</b>: %{base} <b>To</b>: %{x}<br>' +
+ '<b>Duration</b>: %{customdata[2]}',
+ )
+
+def addDsToPuppetTrace(figure, pos, dfd, dfp, writeCmd, readCmd, trackName):
+ df = createDsToPuppetDataframe(dfd, dfp, writeCmd, readCmd, trackName)
+ if not df.empty:
+ figure.add_trace(createDsToPuppetTrace(df), row=pos, col=1)
+
+def createTimeTrace(df):
+ cd = list(zip(df.command.tolist(), df.duration.tolist()))
+ return go.Bar(
+ base = df.begin,
+ x = df.duration,
+ y = df.processName,
+ text = df.name,
+ insidetextanchor = 'middle',
+ orientation = 'h',
+ name = df.processName[0],
+ customdata = cd,
+ hovertemplate =
+ '<b>%{text}</b><br>' +
+ '<b>Command</b>: %{customdata[0]}<br>' +
+ '<b>From</b>: %{base: .1f} <b>To</b>: %{x: .1f}<br>' +
+ '<b>Duration</b>: %{customdata[1]: .1f}',
+ )
+
+def addTimeTrace(figure, pos, df):
+ if not df.empty:
+ figure.add_trace(createTimeTrace(df), row=pos, col=1)
+
+
+colors = {
+ 'background': '#111111',
+ 'text': '#7FDBFF',
+}
+
+style = dict(
+ plot_bgcolor=colors['background'],
+ paper_bgcolor=colors['background'],
+ font_color=colors['text']
+)
+
+axis = dict(
+ titlefont_size=16,
+ tickfont_size=14,
+ showgrid=False,
+)
+
+def create(dfs):
+ dfd = dfs['QmlDesigner']
+ dfe = dfs['Puppet']['Editor']
+ dfp = dfs['Puppet']['Preview']
+ dfr = dfs['Puppet']['Render']
+
+ figureCount = 2
+ titles = ["DS To Puppet", "Duration events"]
+ figure = sp.make_subplots(rows=figureCount, cols=1, subplot_titles=titles, vertical_spacing=0.3, shared_xaxes=True)
+
+ addDsToPuppetTrace(figure, 1, dfd, dfr, "ChangeAuxiliaryCommand", "PixmapChangedCommand", "ChangeAux to PixChanged")
+ addDsToPuppetTrace(figure, 1, dfd, dfp, "ChangeAuxiliaryCommand", "StatePreviewImageChangedCommand", "ChangeAux to PrevImgChanged")
+
+ addTimeTrace(figure, 2, dfe.loc[ dfe['type'] != 'I' ].reset_index())
+ addTimeTrace(figure, 2, dfp.loc[ dfp['type'] != 'I' ].reset_index())
+ addTimeTrace(figure, 2, dfr.loc[ dfr['type'] != 'I' ].reset_index())
+ addTimeTrace(figure, 2, dfd.loc[ dfd['type'] != 'I' ].reset_index())
+
+ figure.update_layout(barmode='stack', height=500, **style)
+ figure.update_xaxes(dict(**axis, title='Time (ms)', tickmode = 'auto', showticklabels=True, linecolor='green'))
+ figure.update_yaxes(dict(**axis, fixedrange=True, type='category'))
+
+ return figure
+
diff --git a/src/libs/nanotrace/python/nanotrace.py b/src/libs/nanotrace/python/nanotrace.py
new file mode 100644
index 00000000000..3e68bdcf180
--- /dev/null
+++ b/src/libs/nanotrace/python/nanotrace.py
@@ -0,0 +1,82 @@
+############################################################################
+#
+# Copyright (C) 2022 The Qt Company Ltd.
+# Contact: https://www.qt.io/licensing/
+#
+# This file is part of Qt Creator.
+#
+# Commercial License Usage
+# Licensees holding valid commercial Qt licenses may use this file in
+# accordance with the commercial license agreement provided with the
+# Software or, alternatively, in accordance with the terms contained in
+# a written agreement between you and The Qt Company. For licensing terms
+# and conditions see https://www.qt.io/terms-conditions. For further
+# information use the contact form at https://www.qt.io/contact-us.
+#
+# GNU General Public License Usage
+# Alternatively, this file may be used under the terms of the GNU
+# General Public License version 3 as published by the Free Software
+# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+# included in the packaging of this file. Please review the following
+# information to ensure the GNU General Public License requirements will
+# be met: https://www.gnu.org/licenses/gpl-3.0.html.
+#
+############################################################################
+import reader as rd
+import figures as fgs
+
+import os
+import base64
+import dash
+import dash_core_components as dcc
+import dash_html_components as html
+import pandas as pd
+
+uploadStyle = {
+ 'width': '100%',
+ 'height': '60px',
+ 'lineHeight': '60px',
+ 'borderWidth': '1px',
+ 'borderStyle': 'dashed',
+ 'borderRadius': '5px',
+ 'textAlign': 'center'
+}
+
+external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
+app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
+app.layout = html.Div(className="row", children= [
+ dcc.Upload(id='upload-data', children=['Drag and Drop or ', html.A('Select a File')], style=uploadStyle, multiple=True),
+ html.Div(id='output-data-upload'),
+])
+
+def createLayout(fig):
+ return html.Div([
+ html.Div(className="six columns", style={"width": "100%", "height": "500px"}, children = [ dcc.Graph(id='timeline-graph', figure=fig)]),
+ ] )
+
+def decodeContent(content):
+ content_type, content_string = content.split(',')
+ return base64.b64decode(content_string)
+
+@app.callback(dash.dependencies.Output('output-data-upload', 'children'),
+ dash.dependencies.Input('upload-data', 'contents'),
+ dash.dependencies.State('upload-data', 'filename'),
+ dash.dependencies.State('upload-data', 'last_modified'))
+def update_output(list_of_contents, list_of_names, list_of_dates):
+ if list_of_names is None:
+ return []
+
+ if len(list_of_names) == 1:
+ name, ext = os.path.splitext(list_of_names[0])
+ if rd.hasExtension(list_of_names[0], ".zip"):
+ decoded = decodeContent(list_of_contents[0])
+ dfs = rd.openZipArchive(decoded)
+ return createLayout(fgs.create(dfs))
+
+ return []
+
+if __name__ == '__main__':
+ app.run_server(debug=True)
+
+
+
diff --git a/src/libs/nanotrace/python/reader.py b/src/libs/nanotrace/python/reader.py
new file mode 100644
index 00000000000..b9b41be3a5a
--- /dev/null
+++ b/src/libs/nanotrace/python/reader.py
@@ -0,0 +1,210 @@
+############################################################################
+#
+# Copyright (C) 2022 The Qt Company Ltd.
+# Contact: https://www.qt.io/licensing/
+#
+# This file is part of Qt Creator.
+#
+# Commercial License Usage
+# Licensees holding valid commercial Qt licenses may use this file in
+# accordance with the commercial license agreement provided with the
+# Software or, alternatively, in accordance with the terms contained in
+# a written agreement between you and The Qt Company. For licensing terms
+# and conditions see https://www.qt.io/terms-conditions. For further
+# information use the contact form at https://www.qt.io/contact-us.
+#
+# GNU General Public License Usage
+# Alternatively, this file may be used under the terms of the GNU
+# General Public License version 3 as published by the Free Software
+# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+# included in the packaging of this file. Please review the following
+# information to ensure the GNU General Public License requirements will
+# be met: https://www.gnu.org/licenses/gpl-3.0.html.
+#
+############################################################################
+import os
+import io
+import json
+
+import pandas as pd
+import zipfile as zf
+
+def nsToMs(ns):
+ return ns / 1000000
+
+def appendEvent(data, event):
+ data['processId'].append(event['pid'])
+ data['threadId'].append(event['tid'])
+ data['type'].append(event['ph'])
+ data['category'].append(event['cat'])
+ data['name'].append(event['name'])
+ data['begin'].append(nsToMs(event['ts']))
+
+ duration = 1.0
+ command = ""
+ counter = int(-1)
+
+ if event['cat'] == "Initialize":
+ command = "Initialize"
+
+ if "dur" in event['args']:
+ duration = nsToMs(event['args']['dur'])
+
+ if "name" in event['args']:
+ command = event["args"]["name"].split("::")[-1]
+
+ if "counter" in event['args']:
+ counter = event['args']['counter']
+
+ data['duration'].append(duration)
+ data['command'].append(command)
+ data['counter'].append(counter)
+
+def readTraceFile(file):
+ pdata = {
+ "processId": [],
+ "processName": []
+ }
+ tdata = {
+ "threadId": [],
+ "threadName": []
+ }
+ data = {
+ "processId": [],
+ "threadId": [],
+ "type": [],
+ "category": [],
+ "name": [],
+ "begin": [],
+ "duration": [],
+ "command": [],
+ "counter": []
+ }
+
+ for event in json.loads(file)["traceEvents"]:
+ if event["ph"] == "M" and event["name"] == "process_name":
+ pdata["processId"].append(event["pid"])
+ pdata["processName"].append(event["args"]["name"])
+ elif event["ph"] == "M" and event["name"] == "thread_name":
+ tdata["threadId"].append(event["tid"])
+ tdata["threadName"].append(event["args"]["name"])
+ else:
+ appendEvent(data, event)
+
+ pdf = pd.DataFrame(pdata)
+ tdf = pd.DataFrame(tdata)
+ ddf = pd.DataFrame(data)
+
+ # Replace pid/tid with processName/threadName and sort.
+ df = ddf.merge(right=pdf).merge(right=tdf)
+ df.drop(inplace=True, columns=["processId", "threadId"])
+ df.sort_values("begin").reset_index(inplace=True, drop=True)
+
+ # Merge Begin/End rows
+ for idx, row in df.iterrows():
+ if row['type'] == 'B':
+ nextEndMask = (df['type']=='E') & (df.index>idx)
+ relatedMask = (df['category']==row['category']) & (df['name']==row['name'])
+ nextRelatedRow = df.loc[nextEndMask & relatedMask].head(1).squeeze()
+
+ if nextRelatedRow.empty:
+ df.loc[idx, 'duration'] = 1.0
+ else:
+ df.loc[idx, 'type'] = 'BE'
+ df.loc[idx, 'duration'] = nextRelatedRow['begin'] - row['begin']
+
+ # Make columns categorial
+ categories = ['type', 'category', 'name', 'command', 'processName', 'threadName']
+ df[categories] = df[categories].astype('category')
+
+ df.drop(df[ df['type']=='E'].index, inplace=True)
+ df.reset_index(inplace=True, drop=True)
+
+ return df
+
+def computePuppetOffset(dfd, dfp):
+ puppetSyncWriteMask = (dfp['category'] == "Sync") & (dfp['name']=="writeCommand") & (dfp['type']=="I")
+ puppetSyncWriteDf = dfp.loc[ puppetSyncWriteMask ].head(1)
+ puppetSyncWriteRow = puppetSyncWriteDf.squeeze()
+ if puppetSyncWriteDf.empty: return 0.0
+
+ cmdName = puppetSyncWriteRow['command']
+
+ puppetSyncReadMask = (dfp['category'] == "Sync") & (dfp['name']=="readCommand") & (dfp['command']==cmdName)
+ puppetSyncReadDf = dfp.loc[ puppetSyncReadMask ].head(1)
+ puppetSyncReadRow = puppetSyncReadDf.squeeze()
+ if puppetSyncReadDf.empty: return 0.0
+
+ designerSyncReadMask = (dfd['category'] == "Sync") & (dfd['name']=="readCommand") & (dfd['command']==cmdName)
+ designerSyncWriteDf = dfd.loc[ designerSyncReadMask ].head(1)
+ designerSyncWriteRow = designerSyncWriteDf.squeeze()
+
+ if designerSyncWriteDf.empty: return 0.0
+
+ puppetCenter = puppetSyncReadRow['begin'] - puppetSyncWriteRow['begin']
+ return designerSyncWriteRow['begin'] - puppetCenter
+
+def SynchronizeDataframes(dfs):
+ dfd = dfs['QmlDesigner']
+ dfe = dfs['Puppet']['Editor']
+ dfp = dfs['Puppet']['Preview']
+ dfr = dfs['Puppet']['Render']
+ dfe['begin'] = dfe['begin'] + computePuppetOffset(dfd, dfe)
+ dfp['begin'] = dfp['begin'] + computePuppetOffset(dfd, dfp)
+ dfr['begin'] = dfr['begin'] + computePuppetOffset(dfd, dfr)
+ return dfs
+
+def collectDataframes(dfs):
+ out = { "QmlDesigner": {}, "Puppet": { "Editor": {}, "Preview": {}, "Render": {} } }
+ for df in dfs:
+ if not df.empty:
+ df = df.sort_values(by="threadName", ascending=True)
+ if df['processName'][0] == 'QmlDesigner':
+ out['QmlDesigner'] = df.sort_index()
+ elif df['processName'][0] == 'EditorModePuppet':
+ out['Puppet']['Editor'] = df.sort_index()
+ elif df['processName'][0] == 'PreviewModePuppet':
+ out['Puppet']['Preview'] = df.sort_index()
+ elif df['processName'][0] == 'RenderModePuppet':
+ out['Puppet']['Render'] = df.sort_index()
+
+ return SynchronizeDataframes(out)
+
+def hasExtension(filename, ext):
+ name, e = os.path.splitext(filename)
+ return True if e == ext else False
+
+def extractZipArchive(zip_file):
+ dfs = []
+ for filename in zip_file.namelist():
+ invalid = filename.startswith('__MACOSX')
+ if not invalid and hasExtension(filename, ".json"):
+ with zip_file.open(filename) as file_object:
+ data = file_object.read().strip()
+ dfs.append(readTraceFile(data))
+ return collectDataframes(dfs)
+
+def openZipArchive(data):
+ zip_str = io.BytesIO(data)
+ try:
+ with zf.ZipFile(zip_str, 'r') as zip_file:
+ return extractZipArchive(zip_file)
+ except IOError:
+ print("Failed to open zip file: \"" + path + "\"")
+ sys.exit(2)
+
+def readZipArchive(path):
+ try:
+ with zf.ZipFile(path) as zip_file:
+ return extractZipArchive(zip_file)
+ except IOError:
+ print("Failed to read zip file: \"" + path + "\"")
+ sys.exit(2)
+
+def openTraceFile(path):
+ try:
+ with open(path, "r") as file_object:
+ return readTraceFile(file_object.read())
+ except IOError:
+ print("Failed to open file: \"" + path + "\"")
+ sys.exit(2)
diff --git a/src/plugins/qmldesigner/CMakeLists.txt b/src/plugins/qmldesigner/CMakeLists.txt
index 8cdc99d001c..fcf71902c8e 100644
--- a/src/plugins/qmldesigner/CMakeLists.txt
+++ b/src/plugins/qmldesigner/CMakeLists.txt
@@ -556,3 +556,8 @@ extend_qtc_plugin(QmlDesigner
connectsignaldialog.cpp connectsignaldialog.h
shortcutwidget.cpp shortcutwidget.h
)
+
+extend_qtc_plugin(QmlDesigner
+ CONDITION TARGET Nanotrace
+ DEPENDS Nanotrace
+)
diff --git a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
index 0bb481d22ce..e29498e1438 100644
--- a/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
+++ b/src/plugins/qmldesigner/designercore/include/nodeinstanceview.h
@@ -154,6 +154,9 @@ public:
m_crashCallback = std::move(crashCallback);
}
+ void startNanotrace();
+ void endNanotrace();
+
protected:
void timerEvent(QTimerEvent *event) override;
diff --git a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp
index f1e1e933b65..72552fdb7ae 100644
--- a/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/baseconnectionmanager.cpp
@@ -27,6 +27,8 @@
#include "endpuppetcommand.h"
#include "nodeinstanceserverproxy.h"
#include "nodeinstanceview.h"
+#include "nanotracecommand.h"
+#include "nanotrace/nanotrace.h"
#include <QLocalSocket>
#include <QTimer>
@@ -126,6 +128,26 @@ void BaseConnectionManager::readDataStream(Connection &connection)
in >> command;
connection.blockSize = 0;
+#ifdef NANOTRACE_ENABLED
+ if (command.userType() != QMetaType::type("PuppetAliveCommand")) {
+ if (command.userType() == QMetaType::type("SyncNanotraceCommand")) {
+ SyncNanotraceCommand cmd = command.value<SyncNanotraceCommand>();
+ NANOTRACE_INSTANT_ARGS("Sync", "readCommand",
+ {"name", cmd.name().toStdString()},
+ {"counter", commandCounter});
+
+ writeCommand(command);
+ // Do not dispatch this command.
+ continue;
+
+ } else {
+ NANOTRACE_INSTANT_ARGS("Update", "readCommand",
+ {"name", command.typeName()},
+ {"counter", commandCounter});
+ }
+ }
+#endif
+
commandList.append(command);
}
diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp b/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp
index 3f28389b539..7db7913c569 100644
--- a/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanager.cpp
@@ -122,6 +122,11 @@ void ConnectionManager::writeCommand(const QVariant &command)
m_writeCommandCounter++;
}
+quint32 ConnectionManager::writeCounter() const
+{
+ return m_writeCommandCounter;
+}
+
void ConnectionManager::processFinished(int exitCode, QProcess::ExitStatus exitStatus, const QString &connectionName)
{
qWarning() << "Process" << connectionName <<(exitStatus == QProcess::CrashExit ? "crashed:" : "finished:")
diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanager.h b/src/plugins/qmldesigner/designercore/instances/connectionmanager.h
index fec3adac060..4f25724adba 100644
--- a/src/plugins/qmldesigner/designercore/instances/connectionmanager.h
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanager.h
@@ -56,6 +56,8 @@ public:
void writeCommand(const QVariant &command) override;
+ quint32 writeCounter() const override;
+
protected:
using BaseConnectionManager::processFinished;
void processFinished(int exitCode, QProcess::ExitStatus exitStatus, const QString &connectionName) override;
diff --git a/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h
index 319c7407676..ce4206ca981 100644
--- a/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h
+++ b/src/plugins/qmldesigner/designercore/instances/connectionmanagerinterface.h
@@ -77,6 +77,8 @@ public:
virtual void writeCommand(const QVariant &command) = 0;
+ virtual quint32 writeCounter() const { return 0; }
+
protected:
virtual void dispatchCommand(const QVariant &command, Connection &connection) = 0;
virtual void processFinished(int exitCode, QProcess::ExitStatus exitStatus, const QString &connectionName) = 0;
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
index 663af8f0f43..4f09eb7cf1a 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceserverproxy.cpp
@@ -48,6 +48,7 @@
#include <endpuppetcommand.h>
#include <informationchangedcommand.h>
#include <inputeventcommand.h>
+#include <nanotracecommand.h>
#include <pixmapchangedcommand.h>
#include <puppettocreatorcommand.h>
#include <removeinstancescommand.h>
@@ -73,6 +74,7 @@
#include <qtsupport/baseqtversion.h>
#include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtsupportconstants.h>
+#include <nanotrace/nanotrace.h>
#include <QCoreApplication>
#include <QDir>
@@ -111,6 +113,8 @@ NodeInstanceServerProxy::~NodeInstanceServerProxy()
void NodeInstanceServerProxy::dispatchCommand(const QVariant &command)
{
+ NANOTRACE_SCOPE_ARGS("Update", "dispatchCommand", {"name", command.typeName()});
+
static const int informationChangedCommandType = QMetaType::type("InformationChangedCommand");
static const int valuesChangedCommandType = QMetaType::type("ValuesChangedCommand");
static const int valuesModifiedCommandType = QMetaType::type("ValuesModifiedCommand");
@@ -122,6 +126,7 @@ void NodeInstanceServerProxy::dispatchCommand(const QVariant &command)
static const int debugOutputCommandType = QMetaType::type("DebugOutputCommand");
static const int changeSelectionCommandType = QMetaType::type("ChangeSelectionCommand");
static const int puppetToCreatorCommandType = QMetaType::type("PuppetToCreatorCommand");
+ static const int SyncNanotraceCommandType = QMetaType::type("SyncNanotraceCommand");
qCInfo(instanceViewBenchmark) << "dispatching command" << command.userType() << command.typeName();
if (command.userType() == informationChangedCommandType) {
@@ -146,6 +151,8 @@ void NodeInstanceServerProxy::dispatchCommand(const QVariant &command)
nodeInstanceClient()->selectionChanged(command.value<ChangeSelectionCommand>());
} else if (command.userType() == puppetToCreatorCommandType) {
nodeInstanceClient()->handlePuppetToCreatorCommand(command.value<PuppetToCreatorCommand>());
+ } else if (command.userType() == SyncNanotraceCommandType) {
+ // ignore.
} else {
Q_ASSERT(false);
}
@@ -184,6 +191,20 @@ QString NodeInstanceServerProxy::qrcMappingString() const
void NodeInstanceServerProxy::writeCommand(const QVariant &command)
{
+#ifdef NANOTRACE_ENABLED
+ if (command.userType() == QMetaType::type("SyncNanotraceCommand")) {
+ SyncNanotraceCommand cmd = command.value<SyncNanotraceCommand>();
+ NANOTRACE_INSTANT_ARGS("Sync", "writeCommand",
+ {"name", cmd.name().toStdString()},
+ {"counter", m_connectionManager.writeCounter()});
+
+ } else {
+ NANOTRACE_INSTANT_ARGS("Update", "writeCommand",
+ {"name", command.typeName()},
+ {"counter", m_connectionManager.writeCounter()});
+ }
+#endif
+
m_connectionManager.writeCommand(command);
}
diff --git a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
index f922a955171..5b83b637953 100644
--- a/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
+++ b/src/plugins/qmldesigner/designercore/instances/nodeinstanceview.cpp
@@ -73,6 +73,8 @@
#include "variantproperty.h"
#include "view3dactioncommand.h"
#include "requestmodelnodepreviewimagecommand.h"
+#include "nanotracecommand.h"
+#include "nanotrace/nanotrace.h"
#include <designersettings.h>
#include <metainfo.h>
@@ -1779,6 +1781,18 @@ QVariant NodeInstanceView::previewImageDataForImageNode(const ModelNode &modelNo
return modelNodePreviewImageDataToVariant(imageData);
}
+void NodeInstanceView::startNanotrace()
+{
+ NANOTRACE_INIT("QmlDesigner", "MainThread", "nanotrace_qmldesigner.json");
+ m_connectionManager.writeCommand(QVariant::fromValue(StartNanotraceCommand(QDir::currentPath())));
+}
+
+void NodeInstanceView::endNanotrace()
+{
+ NANOTRACE_SHUTDOWN();
+ m_connectionManager.writeCommand(QVariant::fromValue(EndNanotraceCommand()) );
+}
+
QVariant NodeInstanceView::previewImageDataForGenericNode(const ModelNode &modelNode, const ModelNode &renderNode)
{
ModelNodePreviewImageData imageData;
diff --git a/src/plugins/qmldesigner/qmldesignercore.cmake b/src/plugins/qmldesigner/qmldesignercore.cmake
index 3332a532bee..83870a36fee 100644
--- a/src/plugins/qmldesigner/qmldesignercore.cmake
+++ b/src/plugins/qmldesigner/qmldesignercore.cmake
@@ -339,6 +339,8 @@ function(extend_with_qmldesigner_core target_name)
commands/endpuppetcommand.h
commands/informationchangedcommand.cpp
commands/informationchangedcommand.h
+ commands/nanotracecommand.cpp
+ commands/nanotracecommand.h
commands/inputeventcommand.cpp
commands/inputeventcommand.h
commands/pixmapchangedcommand.cpp
diff --git a/src/plugins/qmldesigner/qmldesignerplugin.cpp b/src/plugins/qmldesigner/qmldesignerplugin.cpp
index 0f18abeae60..35f21ced65a 100644
--- a/src/plugins/qmldesigner/qmldesignerplugin.cpp
+++ b/src/plugins/qmldesigner/qmldesignerplugin.cpp
@@ -85,6 +85,9 @@
#include <QWindow>
#include <QApplication>
+#include "nanotrace/nanotrace.h"
+#include <modelnodecontextmenu_helper.h>
+
static Q_LOGGING_CATEGORY(qmldesignerLog, "qtc.qmldesigner", QtWarningMsg)
using namespace QmlDesigner::Internal;
@@ -233,6 +236,40 @@ bool QmlDesignerPlugin::initialize(const QStringList & /*arguments*/, QString *e
if (QFontDatabase::addApplicationFont(fontPath) < 0)
qCWarning(qmldesignerLog) << "Could not add font " << fontPath << "to font database";
+#ifdef NANOTRACE_ENABLED
+ auto handleShutdownNanotraceAction = [](const SelectionContext &) {};
+ auto shutdownNanotraceIcon = []() { return QIcon(); };
+ auto startNanotraceAction = new ModelNodeAction("Start Nanotrace",
+ QObject::tr("Start Nanotrace"),
+ shutdownNanotraceIcon(),
+ QObject::tr("Start Nanotrace"),
+ ComponentCoreConstants::eventListCategory,
+ QKeySequence(),
+ 220,
+ handleShutdownNanotraceAction);
+
+ connect(startNanotraceAction->defaultAction(), &QAction::triggered, [this]() {
+ d->viewManager.nodeInstanceView()->startNanotrace();
+ });
+
+ designerActionManager().addDesignerAction(startNanotraceAction);
+
+ auto shutDownNanotraceAction = new ModelNodeAction("ShutDown Nanotrace",
+ QObject::tr("ShutDown Nanotrace"),
+ shutdownNanotraceIcon(),
+ QObject::tr("ShutDown Nanotrace"),
+ ComponentCoreConstants::eventListCategory,
+ QKeySequence(),
+ 220,
+ handleShutdownNanotraceAction);
+
+ connect(shutDownNanotraceAction->defaultAction(), &QAction::triggered, [this]() {
+ d->viewManager.nodeInstanceView()->endNanotrace();
+ });
+
+ designerActionManager().addDesignerAction(shutDownNanotraceAction);
+#endif
+
return true;
}
diff --git a/src/tools/qml2puppet/CMakeLists.txt b/src/tools/qml2puppet/CMakeLists.txt
index 60aaee4a528..4a642acbd9a 100644
--- a/src/tools/qml2puppet/CMakeLists.txt
+++ b/src/tools/qml2puppet/CMakeLists.txt
@@ -76,6 +76,7 @@ extend_qtc_library(qml2puppet_static
debugoutputcommand.cpp debugoutputcommand.h
endpuppetcommand.cpp endpuppetcommand.h
informationchangedcommand.cpp informationchangedcommand.h
+ nanotracecommand.cpp nanotracecommand.h
pixmapchangedcommand.cpp pixmapchangedcommand.h
puppetalivecommand.cpp puppetalivecommand.h
removeinstancescommand.cpp removeinstancescommand.h
@@ -96,6 +97,14 @@ extend_qtc_library(qml2puppet_static
scenecreatedcommand.h
)
+extend_qtc_library(qml2puppet_static
+ PUBLIC_INCLUDES "${PROJECT_SOURCE_DIR}/src/libs"
+)
+extend_qtc_library(qml2puppet_static
+ CONDITION TARGET Nanotrace
+ DEPENDS Nanotrace
+)
+
add_qtc_executable(qml2puppet
CONDITION TARGET Qt5::QuickPrivate
DEPENDS
@@ -248,6 +257,15 @@ extend_qtc_executable(qml2puppet
FEATURE_INFO "multilanguage-support in qml2puppet"
)
+extend_qtc_executable(qml2puppet
+ PUBLIC_INCLUDES "${PROJECT_SOURCE_DIR}/src/libs"
+)
+
+extend_qtc_executable(qml2puppet
+ CONDITION TARGET Nanotrace
+ DEPENDS Nanotrace
+)
+
# Crashpad
# only windows requires separate crashpad client per process until client->SetHandlerIPCPipe()
# is implemented (check the TODO inside startCrashpad())