diff --git a/easy_profiler_core/event_trace_status.h b/easy_profiler_core/event_trace_status.h
index 4a9f42d..12c4476 100644
--- a/easy_profiler_core/event_trace_status.h
+++ b/easy_profiler_core/event_trace_status.h
@@ -1,27 +1,27 @@
-
-
-
-#ifndef EASY_PROFILER__EVENT_TRACE_STATUS__H_
-#define EASY_PROFILER__EVENT_TRACE_STATUS__H_
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-namespace profiler {
-
- enum EventTracingEnableStatus : unsigned char
- {
- EVENT_TRACING_LAUNCHED_SUCCESSFULLY = 0,
- EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS,
- EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE,
- EVENT_TRACING_BAD_PROPERTIES_SIZE,
- EVENT_TRACING_OPEN_TRACE_ERROR,
- EVENT_TRACING_MISTERIOUS_ERROR,
- };
-
-} // END of namespace profiler.
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-#endif // EASY_PROFILER__EVENT_TRACE_STATUS__H_
+
+
+
+#ifndef EASY_PROFILER__EVENT_TRACE_STATUS__H_
+#define EASY_PROFILER__EVENT_TRACE_STATUS__H_
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+namespace profiler {
+
+ enum EventTracingEnableStatus : unsigned char
+ {
+ EVENT_TRACING_LAUNCHED_SUCCESSFULLY = 0,
+ EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS,
+ EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE,
+ EVENT_TRACING_BAD_PROPERTIES_SIZE,
+ EVENT_TRACING_OPEN_TRACE_ERROR,
+ EVENT_TRACING_MISTERIOUS_ERROR,
+ };
+
+} // END of namespace profiler.
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+#endif // EASY_PROFILER__EVENT_TRACE_STATUS__H_
diff --git a/easy_profiler_core/event_trace_win.cpp b/easy_profiler_core/event_trace_win.cpp
index 2d80aea..81187f4 100644
--- a/easy_profiler_core/event_trace_win.cpp
+++ b/easy_profiler_core/event_trace_win.cpp
@@ -1,483 +1,483 @@
-/************************************************************************
-* file name : event_trace_win.cpp
-* ----------------- :
-* creation time : 2016/09/04
-* author : Victor Zarubkin
-* email : v.s.zarubkin@gmail.com
-* ----------------- :
-* description : The file contains implementation of EasyEventTracer class used for tracing
-* : Windows system events to get context switches.
-* ----------------- :
-* change log : * 2016/09/04 Victor Zarubkin: initial commit.
-* :
-* : * 2016/09/13 Victor Zarubkin: get process id and process name
-* : of the owner of thread with id == CSwitch::NewThreadId.
-* :
-* : * 2016/09/17 Victor Zarubkin: added log messages printing.
-* ----------------- :
-* license : Lightweight profiler library for c++
-* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
-* :
-* :
-* : Licensed under the Apache License, Version 2.0 (the "License");
-* : you may not use this file except in compliance with the License.
-* : You may obtain a copy of the License at
-* :
-* : http://www.apache.org/licenses/LICENSE-2.0
-* :
-* : Unless required by applicable law or agreed to in writing, software
-* : distributed under the License is distributed on an "AS IS" BASIS,
-* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* : See the License for the specific language governing permissions and
-* : limitations under the License.
-* :
-* :
-* : GNU General Public License Usage
-* : Alternatively, this file may be used under the terms of the GNU
-* : General Public License as published by the Free Software Foundation,
-* : either version 3 of the License, or (at your option) any later version.
-* :
-* : This program is distributed in the hope that it will be useful,
-* : but WITHOUT ANY WARRANTY; without even the implied warranty of
-* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
-* : GNU General Public License for more details.
-* :
-* : You should have received a copy of the GNU General Public License
-* : along with this program.If not, see .
-************************************************************************/
-
-#ifdef _WIN32
-#include
-#include
-#include
-#include "easy/profiler.h"
-#include "profile_manager.h"
-#include "current_time.h"
-
-#include "event_trace_win.h"
-#include
-
-#if EASY_OPTION_LOG_ENABLED != 0
-# include
-# ifndef EASY_ETW_LOG
-# define EASY_ETW_LOG ::std::cerr
-# endif
-#endif
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-//extern ProfileManager& MANAGER;
-#define MANAGER ProfileManager::instance()
-
-::std::atomic_uint64_t TRACING_END_TIME = ATOMIC_VAR_INIT(~0ULL);
-
-namespace profiler {
-
- const decltype(EVENT_DESCRIPTOR::Opcode) SWITCH_CONTEXT_OPCODE = 36;
- const int RAW_TIMESTAMP_TIME_TYPE = 1;
-
- //////////////////////////////////////////////////////////////////////////
-
- struct ProcessInfo {
- std::string name;
- processid_t id = 0;
- int8_t valid = 0;
- };
-
- //////////////////////////////////////////////////////////////////////////
-
- // CSwitch class
- // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964744(v=vs.85).aspx
- // EventType = 36
- struct CSwitch
- {
- uint32_t NewThreadId;
- uint32_t OldThreadId;
- int8_t NewThreadPriority;
- int8_t OldThreadPriority;
- uint8_t PreviousCState;
- int8_t SpareByte;
- int8_t OldThreadWaitReason;
- int8_t OldThreadWaitMode;
- int8_t OldThreadState;
- int8_t OldThreadWaitIdealProcessor;
- uint32_t NewThreadWaitTime;
- uint32_t Reserved;
- };
-
- //////////////////////////////////////////////////////////////////////////
-
- typedef ::std::unordered_map thread_process_info_map;
- typedef ::std::unordered_map process_info_map;
-
- // Using static is safe because processTraceEvent() is called from one thread
- process_info_map PROCESS_INFO_TABLE;
- thread_process_info_map THREAD_PROCESS_INFO_TABLE = ([](){ thread_process_info_map initial; initial[0U] = nullptr; return ::std::move(initial); })();
-
- //////////////////////////////////////////////////////////////////////////
-
- void WINAPI processTraceEvent(PEVENT_RECORD _traceEvent)
- {
- if (_traceEvent->EventHeader.EventDescriptor.Opcode != SWITCH_CONTEXT_OPCODE)
- return;
-
- if (sizeof(CSwitch) != _traceEvent->UserDataLength)
- return;
-
- EASY_FUNCTION(::profiler::colors::White, ::profiler::OFF);
-
- auto _contextSwitchEvent = reinterpret_cast(_traceEvent->UserData);
- const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart);
- if (time > TRACING_END_TIME.load(::std::memory_order_acquire))
- return;
-
- processid_t pid = 0;
- const char* process_name = "";
-
- // Trying to get target process name and id
- auto it = THREAD_PROCESS_INFO_TABLE.find(_contextSwitchEvent->NewThreadId);
- if (it == THREAD_PROCESS_INFO_TABLE.end())
- {
- auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, _contextSwitchEvent->NewThreadId);
- if (hThread != nullptr)
- {
- pid = GetProcessIdOfThread(hThread);
- auto pinfo = &PROCESS_INFO_TABLE[pid];
-
- if (pinfo->valid == 0)
- {
- if (pinfo->name.empty())
- {
- static char numbuf[128] = {};
- sprintf(numbuf, "%u", pid);
- pinfo->name = numbuf;
- pinfo->id = pid;
- }
-
- /*
- According to documentation, using GetModuleBaseName() requires
- PROCESS_QUERY_INFORMATION | PROCESS_VM_READ access rights.
- But it works fine with PROCESS_QUERY_LIMITED_INFORMATION instead of PROCESS_QUERY_INFORMATION.
-
- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683196(v=vs.85).aspx
- */
-
- //auto hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
- //if (hProc == nullptr)
- auto hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, pid);
- if (hProc != nullptr)
- {
- static TCHAR buf[MAX_PATH] = {}; // Using static is safe because processTraceEvent() is called from one thread
- auto len = GetModuleBaseName(hProc, 0, buf, MAX_PATH);
-
- if (len != 0)
- {
- pinfo->name.reserve(pinfo->name.size() + 2 + len);
- pinfo->name.append(" ", 1);
- pinfo->name.append(buf, len);
- pinfo->valid = 1;
- }
-
- CloseHandle(hProc);
- }
- else
- {
- //auto err = GetLastError();
- //printf("OpenProcess(%u) fail: GetLastError() == %u\n", pid, err);
- pinfo->valid = -1;
-
- if (pid == 4) {
- pinfo->name.reserve(pinfo->name.size() + 8);
- pinfo->name.append(" System", 7);
- }
- }
- }
-
- process_name = pinfo->name.c_str();
- THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = pinfo;
-
- CloseHandle(hThread);
- }
- else
- {
- //printf("Can not OpenThread(%u);\n", _contextSwitchEvent->NewThreadId);
- THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = nullptr;
- }
- }
- else
- {
- auto pinfo = it->second;
- if (pinfo != nullptr)
- process_name = pinfo->name.c_str();
- else if (it->first == 0)
- process_name = "System Idle";
- else if (it->first == 4)
- process_name = "System";
- }
-
- MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, _contextSwitchEvent->NewThreadId, process_name);
- MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, pid, time);
- }
-
- //////////////////////////////////////////////////////////////////////////
-
-#ifndef EASY_MAGIC_STATIC_CPP11
- class EasyEventTracerInstance {
- friend EasyEventTracer;
- EasyEventTracer instance;
- } EASY_EVENT_TRACER;
-#endif
-
- EasyEventTracer& EasyEventTracer::instance()
- {
-#ifndef EASY_MAGIC_STATIC_CPP11
- return EASY_EVENT_TRACER.instance;
-#else
- static EasyEventTracer tracer;
- return tracer;
-#endif
- }
-
- EasyEventTracer::EasyEventTracer()
- {
- m_lowPriority = ATOMIC_VAR_INIT(EASY_OPTION_LOW_PRIORITY_EVENT_TRACING);
- }
-
- EasyEventTracer::~EasyEventTracer()
- {
- disable();
- }
-
- bool EasyEventTracer::isLowPriority() const
- {
- return m_lowPriority.load(::std::memory_order_acquire);
- }
-
- void EasyEventTracer::setLowPriority(bool _value)
- {
- m_lowPriority.store(_value, ::std::memory_order_release);
- }
-
- bool setPrivilege(HANDLE hToken, LPCSTR _privelegeName)
- {
- bool success = false;
-
- if (hToken)
- {
- LUID privilegyId;
- if (LookupPrivilegeValue(NULL, _privelegeName, &privilegyId))
- {
- TOKEN_PRIVILEGES tokenPrivilege;
- tokenPrivilege.PrivilegeCount = 1;
- tokenPrivilege.Privileges[0].Luid = privilegyId;
- tokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
- success = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivilege, sizeof(TOKEN_PRIVILEGES), NULL, NULL) != FALSE;
- }
- }
-
-#if EASY_OPTION_LOG_ENABLED != 0
- if (!success)
- EASY_ETW_LOG << "Warning: EasyProfiler failed to set " << _privelegeName << " privelege for the application.\n";
-#endif
-
- return success;
- }
-
- void EasyEventTracer::setProcessPrivileges()
- {
- static bool alreadySet = false;
- if (alreadySet)
- return;
-
- alreadySet = true;
-
- HANDLE hToken = nullptr;
- if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
- {
-#if EASY_OPTION_LOG_ENABLED != 0
- const bool success = setPrivilege(hToken, SE_DEBUG_NAME);
- if (!success)
- EASY_ETW_LOG << "Warning: Some context switch events could not get process name.\n";
-#else
- setPrivilege(hToken, SE_DEBUG_NAME);
-#endif
-
- CloseHandle(hToken);
- }
-#if EASY_OPTION_LOG_ENABLED != 0
- else
- {
- EASY_ETW_LOG << "Warning: EasyProfiler failed to open process to adjust priveleges.\n";
- }
-#endif
- }
-
- ::profiler::EventTracingEnableStatus EasyEventTracer::startTrace(bool _force, int _step)
- {
- auto startTraceResult = StartTrace(&m_sessionHandle, KERNEL_LOGGER_NAME, props());
- switch (startTraceResult)
- {
- case ERROR_SUCCESS:
- return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
-
- case ERROR_ALREADY_EXISTS:
- {
- if (_force)
- {
- // Try to stop another event tracing session to force launch self session.
-
- if (_step == 0)
- {
- /*
- According to https://msdn.microsoft.com/en-us/library/windows/desktop/aa363696(v=vs.85).aspx
- SessionHandle is ignored (and could be NULL) if SessionName is not NULL,
- and you only need to set the Wnode.BufferSize, Wnode.Guid, LoggerNameOffset, and LogFileNameOffset
- in EVENT_TRACE_PROPERTIES structure if ControlCode is EVENT_TRACE_CONTROL_STOP.
- All data is already set for m_properties to the moment. Simply copy m_properties and use the copy.
-
- This method supposed to be faster than launching console window and executing shell command,
- but if that would not work, return to using shell command "logman stop".
- */
-
- // static is safe because we are guarded by spin-lock m_spin
- static Properties p = ([]{ Properties prp; strncpy(prp.sessionName, KERNEL_LOGGER_NAME, sizeof(prp.sessionName)); return prp; })();
- p.base = m_properties.base; // Use copy of m_properties to make sure m_properties will not be changed
-
- // Stop another session
- ControlTrace(NULL, KERNEL_LOGGER_NAME, reinterpret_cast(&p), EVENT_TRACE_CONTROL_STOP);
-
- // Console window variant:
- //if (32 >= (int)ShellExecute(NULL, NULL, "logman", "stop \"" KERNEL_LOGGER_NAME "\" -ets", NULL, SW_HIDE))
- // return EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE;
- }
-
- if (_step < 4)
- {
- // Command executed successfully. Wait for a few time until tracing session finish.
- ::std::this_thread::sleep_for(::std::chrono::milliseconds(500));
- return startTrace(true, ++_step);
- }
- }
-
-#if EASY_OPTION_LOG_ENABLED != 0
- EASY_ETW_LOG << "Error: EasyProfiler.ETW not launched: ERROR_ALREADY_EXISTS. To stop another session execute cmd: logman stop \"" << KERNEL_LOGGER_NAME << "\" -ets\n";
-#endif
- return EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE;
- }
-
- case ERROR_ACCESS_DENIED:
-#if EASY_OPTION_LOG_ENABLED != 0
- EASY_ETW_LOG << "Error: EasyProfiler.ETW not launched: ERROR_ACCESS_DENIED. Try to launch your application as Administrator.\n";
-#endif
- return EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS;
-
- case ERROR_BAD_LENGTH:
-#if EASY_OPTION_LOG_ENABLED != 0
- EASY_ETW_LOG << "Error: EasyProfiler.ETW not launched: ERROR_BAD_LENGTH. It seems that your KERNEL_LOGGER_NAME differs from \"" << m_properties.sessionName << "\". Try to re-compile easy_profiler or contact EasyProfiler developers.\n";
-#endif
- return EVENT_TRACING_BAD_PROPERTIES_SIZE;
- }
-
-#if EASY_OPTION_LOG_ENABLED != 0
- EASY_ETW_LOG << "Error: EasyProfiler.ETW not launched: StartTrace() returned " << startTraceResult << ::std::endl;
-#endif
- return EVENT_TRACING_MISTERIOUS_ERROR;
- }
-
- ::profiler::EventTracingEnableStatus EasyEventTracer::enable(bool _force)
- {
- ::profiler::guard_lock<::profiler::spin_lock> lock(m_spin);
- if (m_bEnabled)
- return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
-
- /*
- Trying to set debug privilege for current process
- to be able to get other process information (process name).
- */
- EasyEventTracer::setProcessPrivileges();
-
- // Clear properties
- memset(&m_properties, 0, sizeof(m_properties));
- m_properties.base.Wnode.BufferSize = sizeof(m_properties);
- m_properties.base.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
- m_properties.base.Wnode.ClientContext = RAW_TIMESTAMP_TIME_TYPE;
- m_properties.base.Wnode.Guid = SystemTraceControlGuid;
- m_properties.base.LoggerNameOffset = sizeof(m_properties.base);
- m_properties.base.EnableFlags = EVENT_TRACE_FLAG_CSWITCH;
- m_properties.base.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
-
- // Start event tracing
- auto res = startTrace(_force);
- if (res != EVENT_TRACING_LAUNCHED_SUCCESSFULLY)
- return res;
-
- memset(&m_trace, 0, sizeof(m_trace));
- m_trace.LoggerName = KERNEL_LOGGER_NAME;
- m_trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
- m_trace.EventRecordCallback = ::profiler::processTraceEvent;
-
- m_openedHandle = OpenTrace(&m_trace);
- if (m_openedHandle == INVALID_PROCESSTRACE_HANDLE)
- {
-#if EASY_OPTION_LOG_ENABLED != 0
- EASY_ETW_LOG << "Error: EasyProfiler.ETW not launched: OpenTrace() returned invalid handle.\n";
-#endif
- return EVENT_TRACING_OPEN_TRACE_ERROR;
- }
-
- /*
- Have to launch a thread to process events because according to MSDN documentation:
-
- The ProcessTrace function blocks the thread until it delivers all events, the BufferCallback function returns FALSE,
- or you call CloseTrace. If the consumer is consuming events in real time, the ProcessTrace function returns after
- the controller stops the trace session. (Note that there may be a several-second delay before the function returns.)
-
- https://msdn.microsoft.com/en-us/library/windows/desktop/aa364093(v=vs.85).aspx
- */
- m_processThread = ::std::move(::std::thread([this]()
- {
- EASY_THREAD_SCOPE("EasyProfiler.ETW");
- ProcessTrace(&m_openedHandle, 1, 0, 0);
- }));
-
- // Set low priority for event tracing thread
- if (m_lowPriority.load(::std::memory_order_acquire))
- SetThreadPriority(m_processThread.native_handle(), THREAD_PRIORITY_LOWEST);
-
- m_bEnabled = true;
-
- return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
- }
-
- void EasyEventTracer::disable()
- {
- ::profiler::guard_lock<::profiler::spin_lock> lock(m_spin);
- if (!m_bEnabled)
- return;
-
- TRACING_END_TIME.store(getCurrentTime(), ::std::memory_order_release);
-
- ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP);
- CloseTrace(m_openedHandle);
-
- // Wait for ProcessTrace to finish to make sure no processTraceEvent() will be called later.
- if (m_processThread.joinable())
- m_processThread.join();
-
- m_bEnabled = false;
-
- // processTraceEvent() is not called anymore. Clean static maps is safe.
- PROCESS_INFO_TABLE.clear();
- THREAD_PROCESS_INFO_TABLE.clear();
- THREAD_PROCESS_INFO_TABLE[0U] = nullptr;
-
- TRACING_END_TIME.store(~0ULL, ::std::memory_order_release);
- }
-
-} // END of namespace profiler.
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-#endif // _WIN32
+/************************************************************************
+* file name : event_trace_win.cpp
+* ----------------- :
+* creation time : 2016/09/04
+* author : Victor Zarubkin
+* email : v.s.zarubkin@gmail.com
+* ----------------- :
+* description : The file contains implementation of EasyEventTracer class used for tracing
+* : Windows system events to get context switches.
+* ----------------- :
+* change log : * 2016/09/04 Victor Zarubkin: initial commit.
+* :
+* : * 2016/09/13 Victor Zarubkin: get process id and process name
+* : of the owner of thread with id == CSwitch::NewThreadId.
+* :
+* : * 2016/09/17 Victor Zarubkin: added log messages printing.
+* ----------------- :
+* license : Lightweight profiler library for c++
+* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
+* :
+* :
+* : Licensed under the Apache License, Version 2.0 (the "License");
+* : you may not use this file except in compliance with the License.
+* : You may obtain a copy of the License at
+* :
+* : http://www.apache.org/licenses/LICENSE-2.0
+* :
+* : Unless required by applicable law or agreed to in writing, software
+* : distributed under the License is distributed on an "AS IS" BASIS,
+* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* : See the License for the specific language governing permissions and
+* : limitations under the License.
+* :
+* :
+* : GNU General Public License Usage
+* : Alternatively, this file may be used under the terms of the GNU
+* : General Public License as published by the Free Software Foundation,
+* : either version 3 of the License, or (at your option) any later version.
+* :
+* : This program is distributed in the hope that it will be useful,
+* : but WITHOUT ANY WARRANTY; without even the implied warranty of
+* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+* : GNU General Public License for more details.
+* :
+* : You should have received a copy of the GNU General Public License
+* : along with this program.If not, see .
+************************************************************************/
+
+#ifdef _WIN32
+#include
+#include
+#include
+#include "easy/profiler.h"
+#include "profile_manager.h"
+#include "current_time.h"
+
+#include "event_trace_win.h"
+#include
+
+#if EASY_OPTION_LOG_ENABLED != 0
+# include
+# ifndef EASY_ETW_LOG
+# define EASY_ETW_LOG ::std::cerr
+# endif
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+//extern ProfileManager& MANAGER;
+#define MANAGER ProfileManager::instance()
+
+::std::atomic_uint64_t TRACING_END_TIME = ATOMIC_VAR_INIT(~0ULL);
+
+namespace profiler {
+
+ const decltype(EVENT_DESCRIPTOR::Opcode) SWITCH_CONTEXT_OPCODE = 36;
+ const int RAW_TIMESTAMP_TIME_TYPE = 1;
+
+ //////////////////////////////////////////////////////////////////////////
+
+ struct ProcessInfo {
+ std::string name;
+ processid_t id = 0;
+ int8_t valid = 0;
+ };
+
+ //////////////////////////////////////////////////////////////////////////
+
+ // CSwitch class
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964744(v=vs.85).aspx
+ // EventType = 36
+ struct CSwitch
+ {
+ uint32_t NewThreadId;
+ uint32_t OldThreadId;
+ int8_t NewThreadPriority;
+ int8_t OldThreadPriority;
+ uint8_t PreviousCState;
+ int8_t SpareByte;
+ int8_t OldThreadWaitReason;
+ int8_t OldThreadWaitMode;
+ int8_t OldThreadState;
+ int8_t OldThreadWaitIdealProcessor;
+ uint32_t NewThreadWaitTime;
+ uint32_t Reserved;
+ };
+
+ //////////////////////////////////////////////////////////////////////////
+
+ typedef ::std::unordered_map thread_process_info_map;
+ typedef ::std::unordered_map process_info_map;
+
+ // Using static is safe because processTraceEvent() is called from one thread
+ process_info_map PROCESS_INFO_TABLE;
+ thread_process_info_map THREAD_PROCESS_INFO_TABLE = ([](){ thread_process_info_map initial; initial[0U] = nullptr; return ::std::move(initial); })();
+
+ //////////////////////////////////////////////////////////////////////////
+
+ void WINAPI processTraceEvent(PEVENT_RECORD _traceEvent)
+ {
+ if (_traceEvent->EventHeader.EventDescriptor.Opcode != SWITCH_CONTEXT_OPCODE)
+ return;
+
+ if (sizeof(CSwitch) != _traceEvent->UserDataLength)
+ return;
+
+ EASY_FUNCTION(::profiler::colors::White, ::profiler::OFF);
+
+ auto _contextSwitchEvent = reinterpret_cast(_traceEvent->UserData);
+ const auto time = static_cast<::profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart);
+ if (time > TRACING_END_TIME.load(::std::memory_order_acquire))
+ return;
+
+ processid_t pid = 0;
+ const char* process_name = "";
+
+ // Trying to get target process name and id
+ auto it = THREAD_PROCESS_INFO_TABLE.find(_contextSwitchEvent->NewThreadId);
+ if (it == THREAD_PROCESS_INFO_TABLE.end())
+ {
+ auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, _contextSwitchEvent->NewThreadId);
+ if (hThread != nullptr)
+ {
+ pid = GetProcessIdOfThread(hThread);
+ auto pinfo = &PROCESS_INFO_TABLE[pid];
+
+ if (pinfo->valid == 0)
+ {
+ if (pinfo->name.empty())
+ {
+ static char numbuf[128] = {};
+ sprintf(numbuf, "%u", pid);
+ pinfo->name = numbuf;
+ pinfo->id = pid;
+ }
+
+ /*
+ According to documentation, using GetModuleBaseName() requires
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ access rights.
+ But it works fine with PROCESS_QUERY_LIMITED_INFORMATION instead of PROCESS_QUERY_INFORMATION.
+
+ See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683196(v=vs.85).aspx
+ */
+
+ //auto hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
+ //if (hProc == nullptr)
+ auto hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, pid);
+ if (hProc != nullptr)
+ {
+ static TCHAR buf[MAX_PATH] = {}; // Using static is safe because processTraceEvent() is called from one thread
+ auto len = GetModuleBaseName(hProc, 0, buf, MAX_PATH);
+
+ if (len != 0)
+ {
+ pinfo->name.reserve(pinfo->name.size() + 2 + len);
+ pinfo->name.append(" ", 1);
+ pinfo->name.append(buf, len);
+ pinfo->valid = 1;
+ }
+
+ CloseHandle(hProc);
+ }
+ else
+ {
+ //auto err = GetLastError();
+ //printf("OpenProcess(%u) fail: GetLastError() == %u\n", pid, err);
+ pinfo->valid = -1;
+
+ if (pid == 4) {
+ pinfo->name.reserve(pinfo->name.size() + 8);
+ pinfo->name.append(" System", 7);
+ }
+ }
+ }
+
+ process_name = pinfo->name.c_str();
+ THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = pinfo;
+
+ CloseHandle(hThread);
+ }
+ else
+ {
+ //printf("Can not OpenThread(%u);\n", _contextSwitchEvent->NewThreadId);
+ THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = nullptr;
+ }
+ }
+ else
+ {
+ auto pinfo = it->second;
+ if (pinfo != nullptr)
+ process_name = pinfo->name.c_str();
+ else if (it->first == 0)
+ process_name = "System Idle";
+ else if (it->first == 4)
+ process_name = "System";
+ }
+
+ MANAGER.beginContextSwitch(_contextSwitchEvent->OldThreadId, time, _contextSwitchEvent->NewThreadId, process_name);
+ MANAGER.endContextSwitch(_contextSwitchEvent->NewThreadId, pid, time);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+
+#ifndef EASY_MAGIC_STATIC_CPP11
+ class EasyEventTracerInstance {
+ friend EasyEventTracer;
+ EasyEventTracer instance;
+ } EASY_EVENT_TRACER;
+#endif
+
+ EasyEventTracer& EasyEventTracer::instance()
+ {
+#ifndef EASY_MAGIC_STATIC_CPP11
+ return EASY_EVENT_TRACER.instance;
+#else
+ static EasyEventTracer tracer;
+ return tracer;
+#endif
+ }
+
+ EasyEventTracer::EasyEventTracer()
+ {
+ m_lowPriority = ATOMIC_VAR_INIT(EASY_OPTION_LOW_PRIORITY_EVENT_TRACING);
+ }
+
+ EasyEventTracer::~EasyEventTracer()
+ {
+ disable();
+ }
+
+ bool EasyEventTracer::isLowPriority() const
+ {
+ return m_lowPriority.load(::std::memory_order_acquire);
+ }
+
+ void EasyEventTracer::setLowPriority(bool _value)
+ {
+ m_lowPriority.store(_value, ::std::memory_order_release);
+ }
+
+ bool setPrivilege(HANDLE hToken, LPCSTR _privelegeName)
+ {
+ bool success = false;
+
+ if (hToken)
+ {
+ LUID privilegyId;
+ if (LookupPrivilegeValue(NULL, _privelegeName, &privilegyId))
+ {
+ TOKEN_PRIVILEGES tokenPrivilege;
+ tokenPrivilege.PrivilegeCount = 1;
+ tokenPrivilege.Privileges[0].Luid = privilegyId;
+ tokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ success = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivilege, sizeof(TOKEN_PRIVILEGES), NULL, NULL) != FALSE;
+ }
+ }
+
+#if EASY_OPTION_LOG_ENABLED != 0
+ if (!success)
+ EASY_ETW_LOG << "Warning: EasyProfiler failed to set " << _privelegeName << " privelege for the application.\n";
+#endif
+
+ return success;
+ }
+
+ void EasyEventTracer::setProcessPrivileges()
+ {
+ static bool alreadySet = false;
+ if (alreadySet)
+ return;
+
+ alreadySet = true;
+
+ HANDLE hToken = nullptr;
+ if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
+ {
+#if EASY_OPTION_LOG_ENABLED != 0
+ const bool success = setPrivilege(hToken, SE_DEBUG_NAME);
+ if (!success)
+ EASY_ETW_LOG << "Warning: Some context switch events could not get process name.\n";
+#else
+ setPrivilege(hToken, SE_DEBUG_NAME);
+#endif
+
+ CloseHandle(hToken);
+ }
+#if EASY_OPTION_LOG_ENABLED != 0
+ else
+ {
+ EASY_ETW_LOG << "Warning: EasyProfiler failed to open process to adjust priveleges.\n";
+ }
+#endif
+ }
+
+ ::profiler::EventTracingEnableStatus EasyEventTracer::startTrace(bool _force, int _step)
+ {
+ auto startTraceResult = StartTrace(&m_sessionHandle, KERNEL_LOGGER_NAME, props());
+ switch (startTraceResult)
+ {
+ case ERROR_SUCCESS:
+ return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
+
+ case ERROR_ALREADY_EXISTS:
+ {
+ if (_force)
+ {
+ // Try to stop another event tracing session to force launch self session.
+
+ if (_step == 0)
+ {
+ /*
+ According to https://msdn.microsoft.com/en-us/library/windows/desktop/aa363696(v=vs.85).aspx
+ SessionHandle is ignored (and could be NULL) if SessionName is not NULL,
+ and you only need to set the Wnode.BufferSize, Wnode.Guid, LoggerNameOffset, and LogFileNameOffset
+ in EVENT_TRACE_PROPERTIES structure if ControlCode is EVENT_TRACE_CONTROL_STOP.
+ All data is already set for m_properties to the moment. Simply copy m_properties and use the copy.
+
+ This method supposed to be faster than launching console window and executing shell command,
+ but if that would not work, return to using shell command "logman stop".
+ */
+
+ // static is safe because we are guarded by spin-lock m_spin
+ static Properties p = ([]{ Properties prp; strncpy(prp.sessionName, KERNEL_LOGGER_NAME, sizeof(prp.sessionName)); return prp; })();
+ p.base = m_properties.base; // Use copy of m_properties to make sure m_properties will not be changed
+
+ // Stop another session
+ ControlTrace(NULL, KERNEL_LOGGER_NAME, reinterpret_cast(&p), EVENT_TRACE_CONTROL_STOP);
+
+ // Console window variant:
+ //if (32 >= (int)ShellExecute(NULL, NULL, "logman", "stop \"" KERNEL_LOGGER_NAME "\" -ets", NULL, SW_HIDE))
+ // return EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE;
+ }
+
+ if (_step < 4)
+ {
+ // Command executed successfully. Wait for a few time until tracing session finish.
+ ::std::this_thread::sleep_for(::std::chrono::milliseconds(500));
+ return startTrace(true, ++_step);
+ }
+ }
+
+#if EASY_OPTION_LOG_ENABLED != 0
+ EASY_ETW_LOG << "Error: EasyProfiler.ETW not launched: ERROR_ALREADY_EXISTS. To stop another session execute cmd: logman stop \"" << KERNEL_LOGGER_NAME << "\" -ets\n";
+#endif
+ return EVENT_TRACING_WAS_LAUNCHED_BY_SOMEBODY_ELSE;
+ }
+
+ case ERROR_ACCESS_DENIED:
+#if EASY_OPTION_LOG_ENABLED != 0
+ EASY_ETW_LOG << "Error: EasyProfiler.ETW not launched: ERROR_ACCESS_DENIED. Try to launch your application as Administrator.\n";
+#endif
+ return EVENT_TRACING_NOT_ENOUGH_ACCESS_RIGHTS;
+
+ case ERROR_BAD_LENGTH:
+#if EASY_OPTION_LOG_ENABLED != 0
+ EASY_ETW_LOG << "Error: EasyProfiler.ETW not launched: ERROR_BAD_LENGTH. It seems that your KERNEL_LOGGER_NAME differs from \"" << m_properties.sessionName << "\". Try to re-compile easy_profiler or contact EasyProfiler developers.\n";
+#endif
+ return EVENT_TRACING_BAD_PROPERTIES_SIZE;
+ }
+
+#if EASY_OPTION_LOG_ENABLED != 0
+ EASY_ETW_LOG << "Error: EasyProfiler.ETW not launched: StartTrace() returned " << startTraceResult << ::std::endl;
+#endif
+ return EVENT_TRACING_MISTERIOUS_ERROR;
+ }
+
+ ::profiler::EventTracingEnableStatus EasyEventTracer::enable(bool _force)
+ {
+ ::profiler::guard_lock<::profiler::spin_lock> lock(m_spin);
+ if (m_bEnabled)
+ return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
+
+ /*
+ Trying to set debug privilege for current process
+ to be able to get other process information (process name).
+ */
+ EasyEventTracer::setProcessPrivileges();
+
+ // Clear properties
+ memset(&m_properties, 0, sizeof(m_properties));
+ m_properties.base.Wnode.BufferSize = sizeof(m_properties);
+ m_properties.base.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
+ m_properties.base.Wnode.ClientContext = RAW_TIMESTAMP_TIME_TYPE;
+ m_properties.base.Wnode.Guid = SystemTraceControlGuid;
+ m_properties.base.LoggerNameOffset = sizeof(m_properties.base);
+ m_properties.base.EnableFlags = EVENT_TRACE_FLAG_CSWITCH;
+ m_properties.base.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
+
+ // Start event tracing
+ auto res = startTrace(_force);
+ if (res != EVENT_TRACING_LAUNCHED_SUCCESSFULLY)
+ return res;
+
+ memset(&m_trace, 0, sizeof(m_trace));
+ m_trace.LoggerName = KERNEL_LOGGER_NAME;
+ m_trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
+ m_trace.EventRecordCallback = ::profiler::processTraceEvent;
+
+ m_openedHandle = OpenTrace(&m_trace);
+ if (m_openedHandle == INVALID_PROCESSTRACE_HANDLE)
+ {
+#if EASY_OPTION_LOG_ENABLED != 0
+ EASY_ETW_LOG << "Error: EasyProfiler.ETW not launched: OpenTrace() returned invalid handle.\n";
+#endif
+ return EVENT_TRACING_OPEN_TRACE_ERROR;
+ }
+
+ /*
+ Have to launch a thread to process events because according to MSDN documentation:
+
+ The ProcessTrace function blocks the thread until it delivers all events, the BufferCallback function returns FALSE,
+ or you call CloseTrace. If the consumer is consuming events in real time, the ProcessTrace function returns after
+ the controller stops the trace session. (Note that there may be a several-second delay before the function returns.)
+
+ https://msdn.microsoft.com/en-us/library/windows/desktop/aa364093(v=vs.85).aspx
+ */
+ m_processThread = ::std::move(::std::thread([this]()
+ {
+ EASY_THREAD_SCOPE("EasyProfiler.ETW");
+ ProcessTrace(&m_openedHandle, 1, 0, 0);
+ }));
+
+ // Set low priority for event tracing thread
+ if (m_lowPriority.load(::std::memory_order_acquire))
+ SetThreadPriority(m_processThread.native_handle(), THREAD_PRIORITY_LOWEST);
+
+ m_bEnabled = true;
+
+ return EVENT_TRACING_LAUNCHED_SUCCESSFULLY;
+ }
+
+ void EasyEventTracer::disable()
+ {
+ ::profiler::guard_lock<::profiler::spin_lock> lock(m_spin);
+ if (!m_bEnabled)
+ return;
+
+ TRACING_END_TIME.store(getCurrentTime(), ::std::memory_order_release);
+
+ ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP);
+ CloseTrace(m_openedHandle);
+
+ // Wait for ProcessTrace to finish to make sure no processTraceEvent() will be called later.
+ if (m_processThread.joinable())
+ m_processThread.join();
+
+ m_bEnabled = false;
+
+ // processTraceEvent() is not called anymore. Clean static maps is safe.
+ PROCESS_INFO_TABLE.clear();
+ THREAD_PROCESS_INFO_TABLE.clear();
+ THREAD_PROCESS_INFO_TABLE[0U] = nullptr;
+
+ TRACING_END_TIME.store(~0ULL, ::std::memory_order_release);
+ }
+
+} // END of namespace profiler.
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+#endif // _WIN32
diff --git a/easy_profiler_core/event_trace_win.h b/easy_profiler_core/event_trace_win.h
index 21c27f3..8c11b8e 100644
--- a/easy_profiler_core/event_trace_win.h
+++ b/easy_profiler_core/event_trace_win.h
@@ -1,120 +1,120 @@
-/************************************************************************
-* file name : event_trace_win.h
-* ----------------- :
-* creation time : 2016/09/04
-* author : Victor Zarubkin
-* email : v.s.zarubkin@gmail.com
-* ----------------- :
-* description : The file contains declaration of EasyEventTracer class used for tracing
-* : Windows system events to get context switches.
-* ----------------- :
-* change log : * 2016/09/04 Victor Zarubkin: initial commit.
-* :
-* : *
-* ----------------- :
-* license : Lightweight profiler library for c++
-* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
-* :
-* :
-* : Licensed under the Apache License, Version 2.0 (the "License");
-* : you may not use this file except in compliance with the License.
-* : You may obtain a copy of the License at
-* :
-* : http://www.apache.org/licenses/LICENSE-2.0
-* :
-* : Unless required by applicable law or agreed to in writing, software
-* : distributed under the License is distributed on an "AS IS" BASIS,
-* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* : See the License for the specific language governing permissions and
-* : limitations under the License.
-* :
-* :
-* : GNU General Public License Usage
-* : Alternatively, this file may be used under the terms of the GNU
-* : General Public License as published by the Free Software Foundation,
-* : either version 3 of the License, or (at your option) any later version.
-* :
-* : This program is distributed in the hope that it will be useful,
-* : but WITHOUT ANY WARRANTY; without even the implied warranty of
-* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
-* : GNU General Public License for more details.
-* :
-* : You should have received a copy of the GNU General Public License
-* : along with this program.If not, see .
-************************************************************************/
-
-#ifndef EASY_PROFILER__EVENT_TRACE_WINDOWS__H_
-#define EASY_PROFILER__EVENT_TRACE_WINDOWS__H_
-#ifdef _WIN32
-
-#define INITGUID // This is to enable using SystemTraceControlGuid in evntrace.h.
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "event_trace_status.h"
-#include "spin_lock.h"
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-namespace profiler {
-
- class EasyEventTracer EASY_FINAL
- {
-#ifndef EASY_MAGIC_STATIC_CPP11
- friend class EasyEventTracerInstance;
-#endif
-
-#pragma pack(push, 1)
- struct Properties {
- EVENT_TRACE_PROPERTIES base;
- char sessionName[sizeof(KERNEL_LOGGER_NAME)];
- };
-#pragma pack(pop)
-
- ::std::thread m_processThread;
- Properties m_properties;
- EVENT_TRACE_LOGFILE m_trace;
- ::profiler::spin_lock m_spin;
- ::std::atomic_bool m_lowPriority;
- TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE;
- TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE;
- bool m_bEnabled = false;
-
- public:
-
- static EasyEventTracer& instance();
- ~EasyEventTracer();
-
- bool isLowPriority() const;
-
- ::profiler::EventTracingEnableStatus enable(bool _force = false);
- void disable();
- void setLowPriority(bool _value);
- static void setProcessPrivileges();
-
- private:
-
- EasyEventTracer();
-
- inline EVENT_TRACE_PROPERTIES* props()
- {
- return reinterpret_cast(&m_properties);
- }
-
- ::profiler::EventTracingEnableStatus startTrace(bool _force, int _step = 0);
-
- }; // END of class EasyEventTracer.
-
-} // END of namespace profiler.
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-#endif // _WIN32
-#endif // EASY_PROFILER__EVENT_TRACE_WINDOWS__H_
+/************************************************************************
+* file name : event_trace_win.h
+* ----------------- :
+* creation time : 2016/09/04
+* author : Victor Zarubkin
+* email : v.s.zarubkin@gmail.com
+* ----------------- :
+* description : The file contains declaration of EasyEventTracer class used for tracing
+* : Windows system events to get context switches.
+* ----------------- :
+* change log : * 2016/09/04 Victor Zarubkin: initial commit.
+* :
+* : *
+* ----------------- :
+* license : Lightweight profiler library for c++
+* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
+* :
+* :
+* : Licensed under the Apache License, Version 2.0 (the "License");
+* : you may not use this file except in compliance with the License.
+* : You may obtain a copy of the License at
+* :
+* : http://www.apache.org/licenses/LICENSE-2.0
+* :
+* : Unless required by applicable law or agreed to in writing, software
+* : distributed under the License is distributed on an "AS IS" BASIS,
+* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* : See the License for the specific language governing permissions and
+* : limitations under the License.
+* :
+* :
+* : GNU General Public License Usage
+* : Alternatively, this file may be used under the terms of the GNU
+* : General Public License as published by the Free Software Foundation,
+* : either version 3 of the License, or (at your option) any later version.
+* :
+* : This program is distributed in the hope that it will be useful,
+* : but WITHOUT ANY WARRANTY; without even the implied warranty of
+* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+* : GNU General Public License for more details.
+* :
+* : You should have received a copy of the GNU General Public License
+* : along with this program.If not, see .
+************************************************************************/
+
+#ifndef EASY_PROFILER__EVENT_TRACE_WINDOWS__H_
+#define EASY_PROFILER__EVENT_TRACE_WINDOWS__H_
+#ifdef _WIN32
+
+#define INITGUID // This is to enable using SystemTraceControlGuid in evntrace.h.
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include "event_trace_status.h"
+#include "spin_lock.h"
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+namespace profiler {
+
+ class EasyEventTracer EASY_FINAL
+ {
+#ifndef EASY_MAGIC_STATIC_CPP11
+ friend class EasyEventTracerInstance;
+#endif
+
+#pragma pack(push, 1)
+ struct Properties {
+ EVENT_TRACE_PROPERTIES base;
+ char sessionName[sizeof(KERNEL_LOGGER_NAME)];
+ };
+#pragma pack(pop)
+
+ ::std::thread m_processThread;
+ Properties m_properties;
+ EVENT_TRACE_LOGFILE m_trace;
+ ::profiler::spin_lock m_spin;
+ ::std::atomic_bool m_lowPriority;
+ TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE;
+ TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE;
+ bool m_bEnabled = false;
+
+ public:
+
+ static EasyEventTracer& instance();
+ ~EasyEventTracer();
+
+ bool isLowPriority() const;
+
+ ::profiler::EventTracingEnableStatus enable(bool _force = false);
+ void disable();
+ void setLowPriority(bool _value);
+ static void setProcessPrivileges();
+
+ private:
+
+ EasyEventTracer();
+
+ inline EVENT_TRACE_PROPERTIES* props()
+ {
+ return reinterpret_cast(&m_properties);
+ }
+
+ ::profiler::EventTracingEnableStatus startTrace(bool _force, int _step = 0);
+
+ }; // END of class EasyEventTracer.
+
+} // END of namespace profiler.
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+#endif // _WIN32
+#endif // EASY_PROFILER__EVENT_TRACE_WINDOWS__H_
diff --git a/easy_profiler_core/hashed_cstr.h b/easy_profiler_core/hashed_cstr.h
index 28e2a15..6a214d5 100644
--- a/easy_profiler_core/hashed_cstr.h
+++ b/easy_profiler_core/hashed_cstr.h
@@ -1,17 +1,17 @@
-/************************************************************************
-* file name : hashed_str.h
-* ----------------- :
-* creation time : 2016/09/11
-* author : Victor Zarubkin
-* email : v.s.zarubkin@gmail.com
-* ----------------- :
-* description : The file contains definition of C-strings with calculated hash-code.
-* : These strings may be used as optimized keys for std::unordered_map.
-* ----------------- :
-* change log : * 2016/09/11 Victor Zarubkin: Initial commit. Moved sources from reader.cpp
-* :
-* : *
-* ----------------- :
+/************************************************************************
+* file name : hashed_str.h
+* ----------------- :
+* creation time : 2016/09/11
+* author : Victor Zarubkin
+* email : v.s.zarubkin@gmail.com
+* ----------------- :
+* description : The file contains definition of C-strings with calculated hash-code.
+* : These strings may be used as optimized keys for std::unordered_map.
+* ----------------- :
+* change log : * 2016/09/11 Victor Zarubkin: Initial commit. Moved sources from reader.cpp
+* :
+* : *
+* ----------------- :
* license : Lightweight profiler library for c++
* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
* :
@@ -26,248 +26,248 @@
* : GNU General Public License for more details.
* :
* : You should have received a copy of the GNU General Public License
-* : along with this program.If not, see .
-************************************************************************/
-
-#ifndef EASY_PROFILER__HASHED_CSTR__H_
-#define EASY_PROFILER__HASHED_CSTR__H_
-
-#include
-#include
-#include
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-#ifdef _WIN32
-
-namespace profiler {
-
- /** \brief Simple C-string pointer with length.
-
- It is used as base class for a key in std::unordered_map.
- It is used to get better performance than std::string.
- It simply stores a pointer and a length, there is no
- any memory allocation and copy.
-
- \warning Make sure you know what you are doing. You have to be sure that
- pointed C-string will exist until you finish using this cstring.
-
- \ingroup profiler
- */
- class cstring
- {
- protected:
-
- const char* m_str;
- size_t m_len;
-
- public:
-
- cstring(const char* _str) : m_str(_str), m_len(strlen(_str))
- {
- }
-
- cstring(const char* _str, size_t _len) : m_str(_str), m_len(_len)
- {
- }
-
- cstring(const cstring&) = default;
- cstring& operator = (const cstring&) = default;
-
- inline bool operator == (const cstring& _other) const
- {
- return m_len == _other.m_len && !strncmp(m_str, _other.m_str, m_len);
- }
-
- inline bool operator != (const cstring& _other) const
- {
- return !operator == (_other);
- }
-
- inline bool operator < (const cstring& _other) const
- {
- if (m_len == _other.m_len)
- {
- return strncmp(m_str, _other.m_str, m_len) < 0;
- }
-
- return m_len < _other.m_len;
- }
-
- inline const char* c_str() const
- {
- return m_str;
- }
-
- inline size_t size() const
- {
- return m_len;
- }
-
- }; // END of class cstring.
-
- /** \brief cstring with precalculated hash.
-
- This is used to calculate hash for C-string and to cache it
- to be used in the future without recurring hash calculatoin.
-
- \note This class is used as a key in std::unordered_map.
-
- \ingroup profiler
- */
- class hashed_cstr : public cstring
- {
- typedef cstring Parent;
-
- size_t m_hash;
-
- public:
-
- hashed_cstr(const char* _str) : Parent(_str), m_hash(0)
- {
- m_hash = ::std::_Hash_seq((const unsigned char *)m_str, m_len);
- }
-
- hashed_cstr(const char* _str, size_t _hash_code) : Parent(_str), m_hash(_hash_code)
- {
- }
-
- hashed_cstr(const char* _str, size_t _len, size_t _hash_code) : Parent(_str, _len), m_hash(_hash_code)
- {
- }
-
- hashed_cstr(const hashed_cstr&) = default;
- hashed_cstr& operator = (const hashed_cstr&) = default;
-
- inline bool operator == (const hashed_cstr& _other) const
- {
- return m_hash == _other.m_hash && Parent::operator == (_other);
- }
-
- inline bool operator != (const hashed_cstr& _other) const
- {
- return !operator == (_other);
- }
-
- inline size_t hcode() const
- {
- return m_hash;
- }
-
- }; // END of class hashed_cstr.
-
-} // END of namespace profiler.
-
-namespace std {
-
- /** \brief Simply returns precalculated hash of a C-string. */
- template <> struct hash<::profiler::hashed_cstr> {
- typedef ::profiler::hashed_cstr argument_type;
- typedef size_t result_type;
- inline size_t operator () (const ::profiler::hashed_cstr& _str) const {
- return _str.hcode();
- }
- };
-
-} // END of namespace std.
-
-#else ////////////////////////////////////////////////////////////////////
-
-// TODO: Create hashed_cstr for Linux (need to use Linux version of std::_Hash_seq)
-
-#endif
-
-namespace profiler {
-
- class hashed_stdstring
- {
- ::std::string m_str;
- size_t m_hash;
-
- public:
-
- hashed_stdstring(const char* _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str))
- {
- }
-
- hashed_stdstring(const ::std::string& _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str))
- {
- }
-
- hashed_stdstring(::std::string&& _str) : m_str(::std::forward<::std::string&&>(_str)), m_hash(::std::hash<::std::string>()(m_str))
- {
- }
-
- hashed_stdstring(hashed_stdstring&& _other) : m_str(::std::move(_other.m_str)), m_hash(_other.m_hash)
- {
- }
-
- hashed_stdstring(const char* _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code)
- {
- }
-
- hashed_stdstring(const ::std::string& _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code)
- {
- }
-
- hashed_stdstring(::std::string&& _str, size_t _hash_code) : m_str(::std::forward<::std::string&&>(_str)), m_hash(_hash_code)
- {
- }
-
- hashed_stdstring(const hashed_stdstring&) = default;
- hashed_stdstring& operator = (const hashed_stdstring&) = default;
-
- hashed_stdstring& operator = (hashed_stdstring&& _other)
- {
- m_str = ::std::move(_other.m_str);
- m_hash = _other.m_hash;
- return *this;
- }
-
- inline bool operator == (const hashed_stdstring& _other) const
- {
- return m_hash == _other.m_hash && m_str == _other.m_str;
- }
-
- inline bool operator != (const hashed_stdstring& _other) const
- {
- return !operator == (_other);
- }
-
- inline size_t hcode() const
- {
- return m_hash;
- }
-
- inline const char* c_str() const
- {
- return m_str.c_str();
- }
-
- inline size_t size() const
- {
- return m_str.size();
- }
-
- }; // END of class hashed_stdstring.
-
-} // END of namespace profiler.
-
-namespace std {
-
- /** \brief Simply returns precalculated hash of a std::string. */
- template <> struct hash<::profiler::hashed_stdstring> {
- typedef ::profiler::hashed_stdstring argument_type;
- typedef size_t result_type;
- inline size_t operator () (const ::profiler::hashed_stdstring& _str) const {
- return _str.hcode();
- }
- };
-
-} // END of namespace std.
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-#endif // EASY_PROFILER__HASHED_CSTR__H_
+* : along with this program.If not, see .
+************************************************************************/
+
+#ifndef EASY_PROFILER__HASHED_CSTR__H_
+#define EASY_PROFILER__HASHED_CSTR__H_
+
+#include
+#include
+#include
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+#ifdef _WIN32
+
+namespace profiler {
+
+ /** \brief Simple C-string pointer with length.
+
+ It is used as base class for a key in std::unordered_map.
+ It is used to get better performance than std::string.
+ It simply stores a pointer and a length, there is no
+ any memory allocation and copy.
+
+ \warning Make sure you know what you are doing. You have to be sure that
+ pointed C-string will exist until you finish using this cstring.
+
+ \ingroup profiler
+ */
+ class cstring
+ {
+ protected:
+
+ const char* m_str;
+ size_t m_len;
+
+ public:
+
+ cstring(const char* _str) : m_str(_str), m_len(strlen(_str))
+ {
+ }
+
+ cstring(const char* _str, size_t _len) : m_str(_str), m_len(_len)
+ {
+ }
+
+ cstring(const cstring&) = default;
+ cstring& operator = (const cstring&) = default;
+
+ inline bool operator == (const cstring& _other) const
+ {
+ return m_len == _other.m_len && !strncmp(m_str, _other.m_str, m_len);
+ }
+
+ inline bool operator != (const cstring& _other) const
+ {
+ return !operator == (_other);
+ }
+
+ inline bool operator < (const cstring& _other) const
+ {
+ if (m_len == _other.m_len)
+ {
+ return strncmp(m_str, _other.m_str, m_len) < 0;
+ }
+
+ return m_len < _other.m_len;
+ }
+
+ inline const char* c_str() const
+ {
+ return m_str;
+ }
+
+ inline size_t size() const
+ {
+ return m_len;
+ }
+
+ }; // END of class cstring.
+
+ /** \brief cstring with precalculated hash.
+
+ This is used to calculate hash for C-string and to cache it
+ to be used in the future without recurring hash calculatoin.
+
+ \note This class is used as a key in std::unordered_map.
+
+ \ingroup profiler
+ */
+ class hashed_cstr : public cstring
+ {
+ typedef cstring Parent;
+
+ size_t m_hash;
+
+ public:
+
+ hashed_cstr(const char* _str) : Parent(_str), m_hash(0)
+ {
+ m_hash = ::std::_Hash_seq((const unsigned char *)m_str, m_len);
+ }
+
+ hashed_cstr(const char* _str, size_t _hash_code) : Parent(_str), m_hash(_hash_code)
+ {
+ }
+
+ hashed_cstr(const char* _str, size_t _len, size_t _hash_code) : Parent(_str, _len), m_hash(_hash_code)
+ {
+ }
+
+ hashed_cstr(const hashed_cstr&) = default;
+ hashed_cstr& operator = (const hashed_cstr&) = default;
+
+ inline bool operator == (const hashed_cstr& _other) const
+ {
+ return m_hash == _other.m_hash && Parent::operator == (_other);
+ }
+
+ inline bool operator != (const hashed_cstr& _other) const
+ {
+ return !operator == (_other);
+ }
+
+ inline size_t hcode() const
+ {
+ return m_hash;
+ }
+
+ }; // END of class hashed_cstr.
+
+} // END of namespace profiler.
+
+namespace std {
+
+ /** \brief Simply returns precalculated hash of a C-string. */
+ template <> struct hash<::profiler::hashed_cstr> {
+ typedef ::profiler::hashed_cstr argument_type;
+ typedef size_t result_type;
+ inline size_t operator () (const ::profiler::hashed_cstr& _str) const {
+ return _str.hcode();
+ }
+ };
+
+} // END of namespace std.
+
+#else ////////////////////////////////////////////////////////////////////
+
+// TODO: Create hashed_cstr for Linux (need to use Linux version of std::_Hash_seq)
+
+#endif
+
+namespace profiler {
+
+ class hashed_stdstring
+ {
+ ::std::string m_str;
+ size_t m_hash;
+
+ public:
+
+ hashed_stdstring(const char* _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str))
+ {
+ }
+
+ hashed_stdstring(const ::std::string& _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str))
+ {
+ }
+
+ hashed_stdstring(::std::string&& _str) : m_str(::std::forward<::std::string&&>(_str)), m_hash(::std::hash<::std::string>()(m_str))
+ {
+ }
+
+ hashed_stdstring(hashed_stdstring&& _other) : m_str(::std::move(_other.m_str)), m_hash(_other.m_hash)
+ {
+ }
+
+ hashed_stdstring(const char* _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code)
+ {
+ }
+
+ hashed_stdstring(const ::std::string& _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code)
+ {
+ }
+
+ hashed_stdstring(::std::string&& _str, size_t _hash_code) : m_str(::std::forward<::std::string&&>(_str)), m_hash(_hash_code)
+ {
+ }
+
+ hashed_stdstring(const hashed_stdstring&) = default;
+ hashed_stdstring& operator = (const hashed_stdstring&) = default;
+
+ hashed_stdstring& operator = (hashed_stdstring&& _other)
+ {
+ m_str = ::std::move(_other.m_str);
+ m_hash = _other.m_hash;
+ return *this;
+ }
+
+ inline bool operator == (const hashed_stdstring& _other) const
+ {
+ return m_hash == _other.m_hash && m_str == _other.m_str;
+ }
+
+ inline bool operator != (const hashed_stdstring& _other) const
+ {
+ return !operator == (_other);
+ }
+
+ inline size_t hcode() const
+ {
+ return m_hash;
+ }
+
+ inline const char* c_str() const
+ {
+ return m_str.c_str();
+ }
+
+ inline size_t size() const
+ {
+ return m_str.size();
+ }
+
+ }; // END of class hashed_stdstring.
+
+} // END of namespace profiler.
+
+namespace std {
+
+ /** \brief Simply returns precalculated hash of a std::string. */
+ template <> struct hash<::profiler::hashed_stdstring> {
+ typedef ::profiler::hashed_stdstring argument_type;
+ typedef size_t result_type;
+ inline size_t operator () (const ::profiler::hashed_stdstring& _str) const {
+ return _str.hcode();
+ }
+ };
+
+} // END of namespace std.
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+#endif // EASY_PROFILER__HASHED_CSTR__H_
diff --git a/easy_profiler_core/outstream.h b/easy_profiler_core/outstream.h
index edc7843..6ffc8e7 100644
--- a/easy_profiler_core/outstream.h
+++ b/easy_profiler_core/outstream.h
@@ -1,16 +1,16 @@
-/************************************************************************
-* file name : outstream.h
-* ----------------- :
-* creation time : 2016/09/11
-* authors : Sergey Yagovtsev, Victor Zarubkin
-* emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com
-* ----------------- :
-* description : The file contains definition of output stream helpers.
-* ----------------- :
-* change log : * 2016/09/11 Victor Zarubkin: Initial commit. Moved sources from profiler_manager.h/.cpp
-* :
-* : *
-* ----------------- :
+/************************************************************************
+* file name : outstream.h
+* ----------------- :
+* creation time : 2016/09/11
+* authors : Sergey Yagovtsev, Victor Zarubkin
+* emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com
+* ----------------- :
+* description : The file contains definition of output stream helpers.
+* ----------------- :
+* change log : * 2016/09/11 Victor Zarubkin: Initial commit. Moved sources from profiler_manager.h/.cpp
+* :
+* : *
+* ----------------- :
* license : Lightweight profiler library for c++
* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
* :
@@ -25,51 +25,51 @@
* : GNU General Public License for more details.
* :
* : You should have received a copy of the GNU General Public License
-* : along with this program.If not, see .
-************************************************************************/
-
-#ifndef EASY_PROFILER__OUTPUT_STREAM__H_
-#define EASY_PROFILER__OUTPUT_STREAM__H_
-
-#include
-#include
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-namespace profiler {
-
- class OStream
- {
- ::std::stringstream m_stream;
-
- public:
-
- explicit OStream() : m_stream(std::ios_base::out | std::ios_base::binary)
- {
-
- }
-
- template void write(const char* _data, T _size)
- {
- m_stream.write(_data, _size);
- }
-
- template void write(const T& _data)
- {
- m_stream.write((const char*)&_data, sizeof(T));
- }
-
- const ::std::stringstream& stream() const
- {
- return m_stream;
- }
-
- }; // END of class OStream.
-
-} // END of namespace profiler.
-
-//////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////
-
-#endif // EASY_PROFILER__OUTPUT_STREAM__H_
+* : along with this program.If not, see .
+************************************************************************/
+
+#ifndef EASY_PROFILER__OUTPUT_STREAM__H_
+#define EASY_PROFILER__OUTPUT_STREAM__H_
+
+#include
+#include
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+namespace profiler {
+
+ class OStream
+ {
+ ::std::stringstream m_stream;
+
+ public:
+
+ explicit OStream() : m_stream(std::ios_base::out | std::ios_base::binary)
+ {
+
+ }
+
+ template void write(const char* _data, T _size)
+ {
+ m_stream.write(_data, _size);
+ }
+
+ template void write(const T& _data)
+ {
+ m_stream.write((const char*)&_data, sizeof(T));
+ }
+
+ const ::std::stringstream& stream() const
+ {
+ return m_stream;
+ }
+
+ }; // END of class OStream.
+
+} // END of namespace profiler.
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+#endif // EASY_PROFILER__OUTPUT_STREAM__H_
diff --git a/profiler_gui/blocks_graphics_view.cpp b/profiler_gui/blocks_graphics_view.cpp
index 3296ed9..3f3fdf2 100644
--- a/profiler_gui/blocks_graphics_view.cpp
+++ b/profiler_gui/blocks_graphics_view.cpp
@@ -1469,12 +1469,12 @@ void EasyGraphicsView::onIdleTimeout()
{
auto threadRoot = item->root();
- ::profiler::block_index_t ind = 0;
- auto it = ::std::lower_bound(threadRoot->sync.begin(), threadRoot->sync.end(), itemBlock.node->begin(), [](::profiler::block_index_t _cs_index, ::profiler::timestamp_t _val)
- {
- return EASY_GLOBALS.gui_blocks[_cs_index].tree.node->begin() < _val;
- });
-
+ ::profiler::block_index_t ind = 0;
+ auto it = ::std::lower_bound(threadRoot->sync.begin(), threadRoot->sync.end(), itemBlock.node->begin(), [](::profiler::block_index_t _cs_index, ::profiler::timestamp_t _val)
+ {
+ return EASY_GLOBALS.gui_blocks[_cs_index].tree.node->begin() < _val;
+ });
+
if (it != threadRoot->sync.end())
{
ind = it - threadRoot->sync.begin();
@@ -1486,20 +1486,20 @@ void EasyGraphicsView::onIdleTimeout()
ind = static_cast<::profiler::block_index_t>(threadRoot->sync.size());
}
- ::profiler::timestamp_t idleTime = 0;
- for (::profiler::block_index_t ncs = static_cast<::profiler::block_index_t>(threadRoot->sync.size()); ind < ncs; ++ind)
- {
- auto cs_index = threadRoot->sync[ind];
- const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
-
- if (cs->begin() > itemBlock.node->end())
- break;
-
- if (itemBlock.node->begin() <= cs->begin() && cs->end() <= itemBlock.node->end())
- idleTime += cs->duration();
+ ::profiler::timestamp_t idleTime = 0;
+ for (::profiler::block_index_t ncs = static_cast<::profiler::block_index_t>(threadRoot->sync.size()); ind < ncs; ++ind)
+ {
+ auto cs_index = threadRoot->sync[ind];
+ const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
+
+ if (cs->begin() > itemBlock.node->end())
+ break;
+
+ if (itemBlock.node->begin() <= cs->begin() && cs->end() <= itemBlock.node->end())
+ idleTime += cs->duration();
}
- auto active_time = duration - idleTime;
+ auto active_time = duration - idleTime;
auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration);
lay->addWidget(new QLabel("Active time:", widget), row, 0, Qt::AlignRight);
lay->addWidget(new QLabel(QString("%1 (%2%)").arg(::profiler_gui::timeStringRealNs(EASY_GLOBALS.time_units, active_time, 3)).arg(QString::number(active_percent, 'g', 3)), widget), row, 1, 1, 3, Qt::AlignLeft);
diff --git a/profiler_gui/blocks_tree_widget.cpp b/profiler_gui/blocks_tree_widget.cpp
index 91f4e5b..8d74470 100644
--- a/profiler_gui/blocks_tree_widget.cpp
+++ b/profiler_gui/blocks_tree_widget.cpp
@@ -20,34 +20,34 @@
* :
* : * 2016/08/18 Victor Zarubkin: Moved sources of TreeWidgetItem into tree_widget_item.h/.cpp
* ----------------- :
-* license : Lightweight profiler library for c++
-* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
-* :
-* :
-* : Licensed under the Apache License, Version 2.0 (the "License");
-* : you may not use this file except in compliance with the License.
-* : You may obtain a copy of the License at
-* :
-* : http://www.apache.org/licenses/LICENSE-2.0
-* :
-* : Unless required by applicable law or agreed to in writing, software
-* : distributed under the License is distributed on an "AS IS" BASIS,
-* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* : See the License for the specific language governing permissions and
-* : limitations under the License.
-* :
-* :
-* : GNU General Public License Usage
-* : Alternatively, this file may be used under the terms of the GNU
-* : General Public License as published by the Free Software Foundation,
-* : either version 3 of the License, or (at your option) any later version.
-* :
-* : This program is distributed in the hope that it will be useful,
-* : but WITHOUT ANY WARRANTY; without even the implied warranty of
-* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
-* : GNU General Public License for more details.
-* :
-* : You should have received a copy of the GNU General Public License
+* license : Lightweight profiler library for c++
+* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
+* :
+* :
+* : Licensed under the Apache License, Version 2.0 (the "License");
+* : you may not use this file except in compliance with the License.
+* : You may obtain a copy of the License at
+* :
+* : http://www.apache.org/licenses/LICENSE-2.0
+* :
+* : Unless required by applicable law or agreed to in writing, software
+* : distributed under the License is distributed on an "AS IS" BASIS,
+* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* : See the License for the specific language governing permissions and
+* : limitations under the License.
+* :
+* :
+* : GNU General Public License Usage
+* : Alternatively, this file may be used under the terms of the GNU
+* : General Public License as published by the Free Software Foundation,
+* : either version 3 of the License, or (at your option) any later version.
+* :
+* : This program is distributed in the hope that it will be useful,
+* : but WITHOUT ANY WARRANTY; without even the implied warranty of
+* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+* : GNU General Public License for more details.
+* :
+* : You should have received a copy of the GNU General Public License
* : along with this program.If not, see .
************************************************************************/
@@ -70,16 +70,16 @@
#include "blocks_tree_widget.h"
#include "globals.h"
-#ifdef _WIN32
-#include
-#endif
-
-#ifdef max
-#undef max
-#endif
-
-#ifdef min
-#undef min
+#ifdef _WIN32
+#include
+#endif
+
+#ifdef max
+#undef max
+#endif
+
+#ifdef min
+#undef min
#endif
//////////////////////////////////////////////////////////////////////////
@@ -136,7 +136,7 @@ EasyTreeWidget::EasyTreeWidget(QWidget* _parent)
setItemsExpandable(true);
setAnimated(true);
setSortingEnabled(false);
- setColumnCount(COL_COLUMNS_NUMBER);
+ setColumnCount(COL_COLUMNS_NUMBER);
auto header_item = new QTreeWidgetItem();
auto f = header()->font();
@@ -433,8 +433,8 @@ void EasyTreeWidget::clearSilent(bool _global)
delete item;
}, ::std::move(topLevelItems));
-#ifdef _WIN32
- SetThreadPriority(deleter_thread.native_handle(), THREAD_PRIORITY_LOWEST);
+#ifdef _WIN32
+ SetThreadPriority(deleter_thread.native_handle(), THREAD_PRIORITY_LOWEST);
#endif
deleter_thread.detach();
@@ -1087,8 +1087,8 @@ void EasyTreeWidget::loadSettings()
memcpy(m_columnsHiddenStatus, byteArray.constData(), ::std::min(sizeof(m_columnsHiddenStatus), (size_t)byteArray.size()));
}
- auto state = settings.value("headerState").toByteArray();
- if (!state.isEmpty())
+ auto state = settings.value("headerState").toByteArray();
+ if (!state.isEmpty())
header()->restoreState(state);
settings.endGroup();
@@ -1101,7 +1101,7 @@ void EasyTreeWidget::saveSettings()
settings.setValue("color_rows", m_bColorRows);
settings.setValue("regime", static_cast(m_mode));
settings.setValue("columns", QByteArray(m_columnsHiddenStatus, COL_COLUMNS_NUMBER));
- settings.setValue("headerState", header()->saveState());
+ settings.setValue("headerState", header()->saveState());
settings.endGroup();
}
@@ -1120,16 +1120,16 @@ EasyHierarchyWidget::EasyHierarchyWidget(QWidget* _parent) : Parent(_parent)
m_searchBox->setFixedWidth(200);
m_searchBox->setContentsMargins(5, 0, 0, 0);
- QMenu* menu = new QMenu(this);
- m_searchButton = menu->menuAction();
- m_searchButton->setText("Find next");
- m_searchButton->setIcon(QIcon(":/Search-next"));
- m_searchButton->setData(true);
- connect(m_searchButton, &QAction::triggered, this, &This::findNext);
-
- auto actionGroup = new QActionGroup(this);
- actionGroup->setExclusive(true);
-
+ QMenu* menu = new QMenu(this);
+ m_searchButton = menu->menuAction();
+ m_searchButton->setText("Find next");
+ m_searchButton->setIcon(QIcon(":/Search-next"));
+ m_searchButton->setData(true);
+ connect(m_searchButton, &QAction::triggered, this, &This::findNext);
+
+ auto actionGroup = new QActionGroup(this);
+ actionGroup->setExclusive(true);
+
auto a = new QAction(tr("Find next"), actionGroup);
a->setCheckable(true);
a->setChecked(true);
@@ -1139,18 +1139,18 @@ EasyHierarchyWidget::EasyHierarchyWidget(QWidget* _parent) : Parent(_parent)
a = new QAction(tr("Find previous"), actionGroup);
a->setCheckable(true);
connect(a, &QAction::triggered, this, &This::findPrevFromMenu);
- menu->addAction(a);
-
- menu->addSeparator();
- a = menu->addAction("Case sensitive");
+ menu->addAction(a);
+
+ menu->addSeparator();
+ a = menu->addAction("Case sensitive");
a->setCheckable(true);
a->setChecked(m_bCaseSensitiveSearch);
connect(a, &QAction::triggered, [this](bool _checked){ m_bCaseSensitiveSearch = _checked; });
- menu->addAction(a);
-
- auto tb = new QToolBar(this);
- tb->setIconSize(::profiler_gui::ICONS_SIZE);
- tb->setContentsMargins(0, 0, 0, 0);
+ menu->addAction(a);
+
+ auto tb = new QToolBar(this);
+ tb->setIconSize(::profiler_gui::ICONS_SIZE);
+ tb->setContentsMargins(0, 0, 0, 0);
tb->addAction(m_searchButton);
tb->addWidget(m_searchBox);
diff --git a/profiler_gui/common_types.h b/profiler_gui/common_types.h
index b69f325..3ab28d1 100644
--- a/profiler_gui/common_types.h
+++ b/profiler_gui/common_types.h
@@ -1,17 +1,17 @@
-/************************************************************************
-* file name : common_types.h
-* ----------------- :
-* creation time : 2016/07/31
-* author : Victor Zarubkin
-* email : v.s.zarubkin@gmail.com
-* ----------------- :
-* description : The file contains declaration of common types for both GraphicsView
-* : and TreeWidget.
-* ----------------- :
-* change log : * 2016/07/31 Victor Zarubkin: initial commit.
-* :
-* : *
-* ----------------- :
+/************************************************************************
+* file name : common_types.h
+* ----------------- :
+* creation time : 2016/07/31
+* author : Victor Zarubkin
+* email : v.s.zarubkin@gmail.com
+* ----------------- :
+* description : The file contains declaration of common types for both GraphicsView
+* : and TreeWidget.
+* ----------------- :
+* change log : * 2016/07/31 Victor Zarubkin: initial commit.
+* :
+* : *
+* ----------------- :
* license : Lightweight profiler library for c++
* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
* :
@@ -26,7 +26,7 @@
* : GNU General Public License for more details.
* :
* : You should have received a copy of the GNU General Public License
-* : along with this program.If not, see .
+* : along with this program.If not, see .
************************************************************************/
#ifndef EASY_PROFILER__GUI_COMMON_TYPES_H
diff --git a/profiler_gui/descriptors_tree_widget.cpp b/profiler_gui/descriptors_tree_widget.cpp
index ce60e76..737665a 100644
--- a/profiler_gui/descriptors_tree_widget.cpp
+++ b/profiler_gui/descriptors_tree_widget.cpp
@@ -12,34 +12,34 @@
* :
* : *
* ----------------- :
-* license : Lightweight profiler library for c++
-* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
-* :
-* :
-* : Licensed under the Apache License, Version 2.0 (the "License");
-* : you may not use this file except in compliance with the License.
-* : You may obtain a copy of the License at
-* :
-* : http://www.apache.org/licenses/LICENSE-2.0
-* :
-* : Unless required by applicable law or agreed to in writing, software
-* : distributed under the License is distributed on an "AS IS" BASIS,
-* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* : See the License for the specific language governing permissions and
-* : limitations under the License.
-* :
-* :
-* : GNU General Public License Usage
-* : Alternatively, this file may be used under the terms of the GNU
-* : General Public License as published by the Free Software Foundation,
-* : either version 3 of the License, or (at your option) any later version.
-* :
-* : This program is distributed in the hope that it will be useful,
-* : but WITHOUT ANY WARRANTY; without even the implied warranty of
-* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
-* : GNU General Public License for more details.
-* :
-* : You should have received a copy of the GNU General Public License
+* license : Lightweight profiler library for c++
+* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
+* :
+* :
+* : Licensed under the Apache License, Version 2.0 (the "License");
+* : you may not use this file except in compliance with the License.
+* : You may obtain a copy of the License at
+* :
+* : http://www.apache.org/licenses/LICENSE-2.0
+* :
+* : Unless required by applicable law or agreed to in writing, software
+* : distributed under the License is distributed on an "AS IS" BASIS,
+* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* : See the License for the specific language governing permissions and
+* : limitations under the License.
+* :
+* :
+* : GNU General Public License Usage
+* : Alternatively, this file may be used under the terms of the GNU
+* : General Public License as published by the Free Software Foundation,
+* : either version 3 of the License, or (at your option) any later version.
+* :
+* : This program is distributed in the hope that it will be useful,
+* : but WITHOUT ANY WARRANTY; without even the implied warranty of
+* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+* : GNU General Public License for more details.
+* :
+* : You should have received a copy of the GNU General Public License
* : along with this program.If not, see .
************************************************************************/
@@ -62,16 +62,16 @@
#include "descriptors_tree_widget.h"
#include "globals.h"
-#ifdef _WIN32
-#include
-#endif
-
-#ifdef max
-#undef max
-#endif
-
-#ifdef min
-#undef min
+#ifdef _WIN32
+#include
+#endif
+
+#ifdef max
+#undef max
+#endif
+
+#ifdef min
+#undef min
#endif
//////////////////////////////////////////////////////////////////////////
@@ -92,23 +92,23 @@ enum DescColumns
{
switch (_status)
{
- case ::profiler::OFF:
- return ::profiler::ON;
-
- case ::profiler::ON:
- return ::profiler::FORCE_ON;
-
- case ::profiler::FORCE_ON:
- return ::profiler::OFF_RECURSIVE;
-
- case ::profiler::OFF_RECURSIVE:
- return ::profiler::ON_WITHOUT_CHILDREN;
-
- case ::profiler::ON_WITHOUT_CHILDREN:
- return ::profiler::FORCE_ON_WITHOUT_CHILDREN;
-
- case ::profiler::FORCE_ON_WITHOUT_CHILDREN:
- return ::profiler::OFF;
+ case ::profiler::OFF:
+ return ::profiler::ON;
+
+ case ::profiler::ON:
+ return ::profiler::FORCE_ON;
+
+ case ::profiler::FORCE_ON:
+ return ::profiler::OFF_RECURSIVE;
+
+ case ::profiler::OFF_RECURSIVE:
+ return ::profiler::ON_WITHOUT_CHILDREN;
+
+ case ::profiler::ON_WITHOUT_CHILDREN:
+ return ::profiler::FORCE_ON_WITHOUT_CHILDREN;
+
+ case ::profiler::FORCE_ON_WITHOUT_CHILDREN:
+ return ::profiler::OFF;
}
return ::profiler::OFF;
@@ -118,23 +118,23 @@ const char* statusText(::profiler::EasyBlockStatus _status)
{
switch (_status)
{
- case ::profiler::OFF:
- return "OFF";
-
- case ::profiler::ON:
- return "ON";
-
- case ::profiler::FORCE_ON:
- return "FORCE_ON";
-
- case ::profiler::OFF_RECURSIVE:
- return "OFF_RECURSIVE";
-
- case ::profiler::ON_WITHOUT_CHILDREN:
- return "ON_WITHOUT_CHILDREN";
-
- case ::profiler::FORCE_ON_WITHOUT_CHILDREN:
- return "FORCE_ON_WITHOUT_CHILDREN";
+ case ::profiler::OFF:
+ return "OFF";
+
+ case ::profiler::ON:
+ return "ON";
+
+ case ::profiler::FORCE_ON:
+ return "FORCE_ON";
+
+ case ::profiler::OFF_RECURSIVE:
+ return "OFF_RECURSIVE";
+
+ case ::profiler::ON_WITHOUT_CHILDREN:
+ return "ON_WITHOUT_CHILDREN";
+
+ case ::profiler::FORCE_ON_WITHOUT_CHILDREN:
+ return "FORCE_ON_WITHOUT_CHILDREN";
}
return "";
@@ -144,23 +144,23 @@ const char* statusText(::profiler::EasyBlockStatus _status)
{
switch (_status)
{
- case ::profiler::OFF:
- return ::profiler::colors::Red900;
-
- case ::profiler::ON:
- return ::profiler::colors::LightGreen900;
-
- case ::profiler::FORCE_ON:
- return ::profiler::colors::LightGreen900;
-
- case ::profiler::OFF_RECURSIVE:
- return ::profiler::colors::Red900;
-
- case ::profiler::ON_WITHOUT_CHILDREN:
- return ::profiler::colors::Lime900;
-
- case ::profiler::FORCE_ON_WITHOUT_CHILDREN:
- return ::profiler::colors::Lime900;
+ case ::profiler::OFF:
+ return ::profiler::colors::Red900;
+
+ case ::profiler::ON:
+ return ::profiler::colors::LightGreen900;
+
+ case ::profiler::FORCE_ON:
+ return ::profiler::colors::LightGreen900;
+
+ case ::profiler::OFF_RECURSIVE:
+ return ::profiler::colors::Red900;
+
+ case ::profiler::ON_WITHOUT_CHILDREN:
+ return ::profiler::colors::Lime900;
+
+ case ::profiler::FORCE_ON_WITHOUT_CHILDREN:
+ return ::profiler::colors::Lime900;
}
return ::profiler::colors::Black;
@@ -338,8 +338,8 @@ void EasyDescTreeWidget::clearSilent(bool _global)
delete item;
}, ::std::move(topLevelItems));
-#ifdef _WIN32
- SetThreadPriority(deleter_thread.native_handle(), THREAD_PRIORITY_LOWEST);
+#ifdef _WIN32
+ SetThreadPriority(deleter_thread.native_handle(), THREAD_PRIORITY_LOWEST);
#endif
deleter_thread.detach();
@@ -734,16 +734,16 @@ EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent)
- QMenu* menu = new QMenu(this);
- m_searchButton = menu->menuAction();
- m_searchButton->setText("Find next");
- m_searchButton->setIcon(QIcon(":/Search-next"));
- m_searchButton->setData(true);
- connect(m_searchButton, &QAction::triggered, this, &This::findNext);
-
- auto actionGroup = new QActionGroup(this);
- actionGroup->setExclusive(true);
-
+ QMenu* menu = new QMenu(this);
+ m_searchButton = menu->menuAction();
+ m_searchButton->setText("Find next");
+ m_searchButton->setIcon(QIcon(":/Search-next"));
+ m_searchButton->setData(true);
+ connect(m_searchButton, &QAction::triggered, this, &This::findNext);
+
+ auto actionGroup = new QActionGroup(this);
+ actionGroup->setExclusive(true);
+
auto a = new QAction(tr("Find next"), actionGroup);
a->setCheckable(true);
a->setChecked(true);
@@ -753,15 +753,15 @@ EasyDescWidget::EasyDescWidget(QWidget* _parent) : Parent(_parent)
a = new QAction(tr("Find previous"), actionGroup);
a->setCheckable(true);
connect(a, &QAction::triggered, this, &This::findPrevFromMenu);
- menu->addAction(a);
-
- menu->addSeparator();
- a = menu->addAction("Case sensitive");
+ menu->addAction(a);
+
+ menu->addSeparator();
+ a = menu->addAction("Case sensitive");
a->setCheckable(true);
a->setChecked(m_bCaseSensitiveSearch);
connect(a, &QAction::triggered, [this](bool _checked){ m_bCaseSensitiveSearch = _checked; });
- menu->addAction(a);
-
+ menu->addAction(a);
+
tb->addSeparator();
tb->addAction(m_searchButton);
tb->addWidget(m_searchBox);
diff --git a/profiler_gui/easy_graphics_scrollbar.cpp b/profiler_gui/easy_graphics_scrollbar.cpp
index e0e75e7..6526922 100644
--- a/profiler_gui/easy_graphics_scrollbar.cpp
+++ b/profiler_gui/easy_graphics_scrollbar.cpp
@@ -772,7 +772,7 @@ void EasyHystogramItem::setSource(::profiler::thread_id_t _thread_id, const ::pr
else
{
const auto& root = EASY_GLOBALS.profiler_blocks[_thread_id];
- m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, root);
+ m_threadName = ::profiler_gui::decoratedThreadName(EASY_GLOBALS.use_decorated_thread_name, root);
if (root.children.empty())
m_threadDuration = 0;
diff --git a/profiler_gui/icons/collapse.svg b/profiler_gui/icons/collapse.svg
index 21f1502..a51ddf5 100644
--- a/profiler_gui/icons/collapse.svg
+++ b/profiler_gui/icons/collapse.svg
@@ -1,44 +1,44 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/colors-black.svg b/profiler_gui/icons/colors-black.svg
index 09398d9..7b2c3b4 100644
--- a/profiler_gui/icons/colors-black.svg
+++ b/profiler_gui/icons/colors-black.svg
@@ -1,59 +1,59 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/colors.svg b/profiler_gui/icons/colors.svg
index 9cd72c9..c352876 100644
--- a/profiler_gui/icons/colors.svg
+++ b/profiler_gui/icons/colors.svg
@@ -1,73 +1,73 @@
-
-
-
+
+
+
diff --git a/profiler_gui/icons/delete.svg b/profiler_gui/icons/delete.svg
index 4090c77..9b06500 100644
--- a/profiler_gui/icons/delete.svg
+++ b/profiler_gui/icons/delete.svg
@@ -1,45 +1,45 @@
-
-
-
+
+
+
diff --git a/profiler_gui/icons/expand.svg b/profiler_gui/icons/expand.svg
index 2cd7796..8578b47 100644
--- a/profiler_gui/icons/expand.svg
+++ b/profiler_gui/icons/expand.svg
@@ -1,43 +1,43 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/lan.svg b/profiler_gui/icons/lan.svg
index 069eb1c..a5fa668 100644
--- a/profiler_gui/icons/lan.svg
+++ b/profiler_gui/icons/lan.svg
@@ -1,58 +1,58 @@
-
-
-
+
+
+
diff --git a/profiler_gui/icons/lan_on.svg b/profiler_gui/icons/lan_on.svg
index e4ea6a1..53d26d4 100644
--- a/profiler_gui/icons/lan_on.svg
+++ b/profiler_gui/icons/lan_on.svg
@@ -1,58 +1,58 @@
-
-
-
+
+
+
diff --git a/profiler_gui/icons/list.svg b/profiler_gui/icons/list.svg
index af6bf94..11d825f 100644
--- a/profiler_gui/icons/list.svg
+++ b/profiler_gui/icons/list.svg
@@ -1,63 +1,63 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/logo.svg b/profiler_gui/icons/logo.svg
index e7f39a8..eda0c91 100644
--- a/profiler_gui/icons/logo.svg
+++ b/profiler_gui/icons/logo.svg
@@ -1,51 +1,51 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/off.svg b/profiler_gui/icons/off.svg
index dd5cc6e..61af352 100644
--- a/profiler_gui/icons/off.svg
+++ b/profiler_gui/icons/off.svg
@@ -1,46 +1,46 @@
-
-
-
+
+
+
diff --git a/profiler_gui/icons/open-folder.svg b/profiler_gui/icons/open-folder.svg
index 416bf57..b49800d 100644
--- a/profiler_gui/icons/open-folder.svg
+++ b/profiler_gui/icons/open-folder.svg
@@ -1,42 +1,42 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/open-folder2.svg b/profiler_gui/icons/open-folder2.svg
index b6454a9..5d28eae 100644
--- a/profiler_gui/icons/open-folder2.svg
+++ b/profiler_gui/icons/open-folder2.svg
@@ -1,44 +1,44 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/play.svg b/profiler_gui/icons/play.svg
index 256e444..64babea 100644
--- a/profiler_gui/icons/play.svg
+++ b/profiler_gui/icons/play.svg
@@ -1,43 +1,43 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/reload-folder2.svg b/profiler_gui/icons/reload-folder2.svg
index d3f047c..f06e608 100644
--- a/profiler_gui/icons/reload-folder2.svg
+++ b/profiler_gui/icons/reload-folder2.svg
@@ -1,52 +1,52 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/reload.svg b/profiler_gui/icons/reload.svg
index 18aac75..46a2732 100644
--- a/profiler_gui/icons/reload.svg
+++ b/profiler_gui/icons/reload.svg
@@ -1,75 +1,75 @@
-
-
-
+
+
+
diff --git a/profiler_gui/icons/search-next.svg b/profiler_gui/icons/search-next.svg
index a1f3ae7..a8cb193 100644
--- a/profiler_gui/icons/search-next.svg
+++ b/profiler_gui/icons/search-next.svg
@@ -1,57 +1,57 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/search-prev.svg b/profiler_gui/icons/search-prev.svg
index b629110..4875b06 100644
--- a/profiler_gui/icons/search-prev.svg
+++ b/profiler_gui/icons/search-prev.svg
@@ -1,57 +1,57 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/settings.svg b/profiler_gui/icons/settings.svg
index 07714c0..9f71600 100644
--- a/profiler_gui/icons/settings.svg
+++ b/profiler_gui/icons/settings.svg
@@ -1,70 +1,70 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/statistics.svg b/profiler_gui/icons/statistics.svg
index 2d94ef2..3fcc347 100644
--- a/profiler_gui/icons/statistics.svg
+++ b/profiler_gui/icons/statistics.svg
@@ -1,47 +1,47 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/statistics2.svg b/profiler_gui/icons/statistics2.svg
index 0943c0b..10ee74f 100644
--- a/profiler_gui/icons/statistics2.svg
+++ b/profiler_gui/icons/statistics2.svg
@@ -1,48 +1,48 @@
-
-
-
+
+
+
diff --git a/profiler_gui/icons/stop.svg b/profiler_gui/icons/stop.svg
index 964b50d..b6f3c5a 100644
--- a/profiler_gui/icons/stop.svg
+++ b/profiler_gui/icons/stop.svg
@@ -1,43 +1,43 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/wifi.svg b/profiler_gui/icons/wifi.svg
index 6279e63..fed86c8 100644
--- a/profiler_gui/icons/wifi.svg
+++ b/profiler_gui/icons/wifi.svg
@@ -1,46 +1,46 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/icons/wifi_on.svg b/profiler_gui/icons/wifi_on.svg
index e1e9146..ee4dd4c 100644
--- a/profiler_gui/icons/wifi_on.svg
+++ b/profiler_gui/icons/wifi_on.svg
@@ -1,46 +1,46 @@
-
-
-
-
+
+
+
+
diff --git a/profiler_gui/main_window.cpp b/profiler_gui/main_window.cpp
index 46635f9..1108d06 100644
--- a/profiler_gui/main_window.cpp
+++ b/profiler_gui/main_window.cpp
@@ -1,2245 +1,2245 @@
-/************************************************************************
-* file name : main_window.cpp
-* ----------------- :
-* creation time : 2016/06/26
-* author : Victor Zarubkin
-* email : v.s.zarubkin@gmail.com
-* ----------------- :
-* description : The file contains implementation of MainWindow for easy_profiler GUI.
-* ----------------- :
-* change log : * 2016/06/26 Victor Zarubkin: Initial commit.
-* :
-* : * 2016/06/27 Victor Zarubkin: Passing blocks number to EasyTreeWidget::setTree().
-* :
-* : * 2016/06/29 Victor Zarubkin: Added menu with tests.
-* :
-* : * 2016/06/30 Sergey Yagovtsev: Open file by command line argument
-* :
-* : *
-* ----------------- :
-* license : Lightweight profiler library for c++
-* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
-* :
-* :
-* : Licensed under the Apache License, Version 2.0 (the "License");
-* : you may not use this file except in compliance with the License.
-* : You may obtain a copy of the License at
-* :
-* : http://www.apache.org/licenses/LICENSE-2.0
-* :
-* : Unless required by applicable law or agreed to in writing, software
-* : distributed under the License is distributed on an "AS IS" BASIS,
-* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* : See the License for the specific language governing permissions and
-* : limitations under the License.
-* :
-* :
-* : GNU General Public License Usage
-* : Alternatively, this file may be used under the terms of the GNU
-* : General Public License as published by the Free Software Foundation,
-* : either version 3 of the License, or (at your option) any later version.
-* :
-* : This program is distributed in the hope that it will be useful,
-* : but WITHOUT ANY WARRANTY; without even the implied warranty of
-* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
-* : GNU General Public License for more details.
-* :
-* : You should have received a copy of the GNU General Public License
-* : along with this program.If not, see .
-************************************************************************/
-
-#include
-#include
-
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "main_window.h"
-#include "blocks_tree_widget.h"
-#include "blocks_graphics_view.h"
-#include "descriptors_tree_widget.h"
-#include "globals.h"
-#include "easy/easy_net.h"
-
-#ifdef max
-#undef max
-#endif
-
-#ifdef min
-#undef min
-#endif
-
-//////////////////////////////////////////////////////////////////////////
-
-const int LOADER_TIMER_INTERVAL = 40;
-const auto NETWORK_CACHE_FILE = "easy_profiler_stream.cache";
-
-//////////////////////////////////////////////////////////////////////////
-
-inline void clear_stream(std::stringstream& _stream)
-{
-#if defined(__GNUC__) && __GNUC__ < 5
- // gcc 4 has a known bug which has been solved in gcc 5:
- // std::stringstream has no swap() method :(
- _stream.str(std::string());
-#else
- std::stringstream().swap(_stream);
-#endif
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("localhost"), m_lastPort(::profiler::DEFAULT_PORT)
-{
- { QIcon icon(":/logo"); if (!icon.isNull()) QApplication::setWindowIcon(icon); }
-
- setObjectName("ProfilerGUI_MainWindow");
- setWindowTitle("EasyProfiler");
- setDockNestingEnabled(true);
- setAcceptDrops(true);
- resize(800, 600);
-
- setStatusBar(new QStatusBar());
-
- m_graphicsView = new QDockWidget("Diagram", this);
- m_graphicsView->setObjectName("ProfilerGUI_Diagram");
- m_graphicsView->setMinimumHeight(50);
- m_graphicsView->setAllowedAreas(Qt::AllDockWidgetAreas);
-
- auto graphicsView = new EasyGraphicsViewWidget(this);
- m_graphicsView->setWidget(graphicsView);
-
- m_treeWidget = new QDockWidget("Hierarchy", this);
- m_treeWidget->setObjectName("ProfilerGUI_Hierarchy");
- m_treeWidget->setMinimumHeight(50);
- m_treeWidget->setAllowedAreas(Qt::AllDockWidgetAreas);
-
- auto treeWidget = new EasyHierarchyWidget(this);
- m_treeWidget->setWidget(treeWidget);
-
- addDockWidget(Qt::TopDockWidgetArea, m_graphicsView);
- addDockWidget(Qt::BottomDockWidgetArea, m_treeWidget);
-
-#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0
- auto descTree = new EasyDescWidget();
- m_descTreeWidget = new QDockWidget("Blocks");
- m_descTreeWidget->setObjectName("ProfilerGUI_Blocks");
- m_descTreeWidget->setMinimumHeight(50);
- m_descTreeWidget->setAllowedAreas(Qt::AllDockWidgetAreas);
- m_descTreeWidget->setWidget(descTree);
- addDockWidget(Qt::BottomDockWidgetArea, m_descTreeWidget);
-#endif
-
-
- loadSettings();
-
-
- auto toolbar = addToolBar("FileToolbar");
- toolbar->setIconSize(::profiler_gui::ICONS_SIZE);
- toolbar->setObjectName("ProfilerGUI_FileToolbar");
- toolbar->setContentsMargins(1, 0, 1, 0);
-
- m_loadActionMenu = new QMenu(this);
- auto action = m_loadActionMenu->menuAction();
- action->setText("Open file");
- action->setIcon(QIcon(":/Open"));
- connect(action, &QAction::triggered, this, &This::onOpenFileClicked);
- toolbar->addAction(action);
-
- for (const auto& f : m_lastFiles)
- {
- action = new QAction(f, this);
- connect(action, &QAction::triggered, this, &This::onOpenFileClicked);
- m_loadActionMenu->addAction(action);
- }
-
- m_saveAction = toolbar->addAction(QIcon(":/Save"), tr("Save"), this, SLOT(onSaveFileClicked(bool)));
- m_deleteAction = toolbar->addAction(QIcon(":/Delete"), tr("Clear all"), this, SLOT(onDeleteClicked(bool)));
-
- m_saveAction->setEnabled(false);
- m_deleteAction->setEnabled(false);
-
-
-
- toolbar = addToolBar("ProfileToolbar");
- toolbar->setIconSize(::profiler_gui::ICONS_SIZE);
- toolbar->setObjectName("ProfilerGUI_ProfileToolbar");
- toolbar->setContentsMargins(1, 0, 1, 0);
-
- toolbar->addAction(QIcon(":/List"), tr("Blocks"), this, SLOT(onEditBlocksClicked(bool)));
- m_captureAction = toolbar->addAction(QIcon(":/Start"), tr("Capture"), this, SLOT(onCaptureClicked(bool)));
- m_captureAction->setEnabled(false);
-
- toolbar->addSeparator();
- m_connectAction = toolbar->addAction(QIcon(":/Connection"), tr("Connect"), this, SLOT(onConnectClicked(bool)));
-
- auto lbl = new QLabel("Address:", toolbar);
- lbl->setContentsMargins(5, 0, 2, 0);
- toolbar->addWidget(lbl);
- m_addressEdit = new QLineEdit();
- m_addressEdit->setToolTip("Enter IP-address or host name");
- //QRegExp rx("^0*(2(5[0-5]|[0-4]\\d)|1?\\d{1,2})(\\.0*(2(5[0-5]|[0-4]\\d)|1?\\d{1,2})){3}$");
- //m_addressEdit->setValidator(new QRegExpValidator(rx, m_addressEdit));
- m_addressEdit->setText(m_lastAddress);
- m_addressEdit->setFixedWidth((m_addressEdit->fontMetrics().width(QString("255.255.255.255")) * 3) / 2);
- toolbar->addWidget(m_addressEdit);
-
- lbl = new QLabel("Port:", toolbar);
- lbl->setContentsMargins(5, 0, 2, 0);
- toolbar->addWidget(lbl);
- m_portEdit = new QLineEdit();
- m_portEdit->setValidator(new QIntValidator(1, 65535, m_portEdit));
- m_portEdit->setText(QString::number(m_lastPort));
- m_portEdit->setFixedWidth(m_portEdit->fontMetrics().width(QString("000000")) + 10);
- toolbar->addWidget(m_portEdit);
-
- connect(m_addressEdit, &QLineEdit::returnPressed, [this](){ onConnectClicked(true); });
- connect(m_portEdit, &QLineEdit::returnPressed, [this](){ onConnectClicked(true); });
-
-
-
- toolbar = addToolBar("SetupToolbar");
- toolbar->setIconSize(::profiler_gui::ICONS_SIZE);
- toolbar->setObjectName("ProfilerGUI_SetupToolbar");
- toolbar->setContentsMargins(1, 0, 1, 0);
-
- toolbar->addAction(QIcon(":/Expand"), "Expand all", this, SLOT(onExpandAllClicked(bool)));
- toolbar->addAction(QIcon(":/Collapse"), "Collapse all", this, SLOT(onCollapseAllClicked(bool)));
-
- toolbar->addSeparator();
- auto menu = new QMenu("Settings", this);
- QToolButton* toolButton = new QToolButton(toolbar);
- toolButton->setIcon(QIcon(":/Settings"));
- toolButton->setMenu(menu);
- toolButton->setPopupMode(QToolButton::InstantPopup);
- toolbar->addWidget(toolButton);
-
- action = menu->addAction("Statistics enabled");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.enable_statistics);
- connect(action, &QAction::triggered, this, &This::onEnableDisableStatistics);
- if (EASY_GLOBALS.enable_statistics)
- {
- auto f = action->font();
- f.setBold(true);
- action->setFont(f);
- SET_ICON(action, ":/Stats");
- }
- else
- {
- action->setText("Statistics disabled");
- SET_ICON(action, ":/Stats-off");
- }
-
-
- menu->addSeparator();
- auto submenu = menu->addMenu("View");
- submenu->setToolTipsVisible(true);
- action = submenu->addAction("Draw items' borders");
- action->setToolTip("Draw borders for blocks on diagram.\nThis reduces performance.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.draw_graphics_items_borders);
- connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.draw_graphics_items_borders = _checked; refreshDiagram(); });
-
- action = submenu->addAction("Overlap narrow children");
- action->setToolTip("Children blocks will be overlaped by narrow\nparent blocks. See also \'Blocks narrow size\'.\nThis improves performance.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.hide_narrow_children);
- connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.hide_narrow_children = _checked; refreshDiagram(); });
-
- action = submenu->addAction("Hide min-size blocks");
- action->setToolTip("Hides blocks which screen size\nis less than \'Min blocks size\'.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.hide_minsize_blocks);
- connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.hide_minsize_blocks = _checked; refreshDiagram(); });
-
- action = submenu->addAction("Build hierarchy only for current thread");
- action->setToolTip("Hierarchy tree will be built\nfor blocks from current thread only.\nThis improves performance\nand saves a lot of memory.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.only_current_thread_hierarchy);
- connect(action, &QAction::triggered, this, &This::onHierarchyFlagChange);
-
- action = submenu->addAction("Add zero blocks to hierarchy");
- action->setToolTip("Zero duration blocks will be added into hierarchy tree.\nThis reduces performance and increases memory consumption.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.add_zero_blocks_to_hierarchy);
- connect(action, &QAction::triggered, [this](bool _checked)
- {
- EASY_GLOBALS.add_zero_blocks_to_hierarchy = _checked;
- emit EASY_GLOBALS.events.hierarchyFlagChanged(_checked);
- });
-
- action = submenu->addAction("Enable zero duration blocks on diagram");
- action->setToolTip("If checked then allows diagram to paint zero duration blocks\nwith 1px width on each scale. Otherwise, such blocks will be resized\nto 250ns duration.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.enable_zero_length);
- connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.enable_zero_length = _checked; refreshDiagram(); });
-
- action = submenu->addAction("Highlight similar blocks");
- action->setToolTip("Highlight all visible blocks which are similar\nto the current selected block.\nThis reduces performance.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.highlight_blocks_with_same_id);
- connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.highlight_blocks_with_same_id = _checked; refreshDiagram(); });
-
- action = submenu->addAction("Collapse blocks on tree reset");
- action->setToolTip("This collapses all blocks on diagram\nafter hierarchy tree reset.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.collapse_items_on_tree_close);
- connect(action, &QAction::triggered, this, &This::onCollapseItemsAfterCloseChanged);
-
- action = submenu->addAction("Expand all on file open");
- action->setToolTip("If checked then all blocks on diagram\nwill be initially expanded.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.all_items_expanded_by_default);
- connect(action, &QAction::triggered, this, &This::onAllItemsExpandedByDefaultChange);
-
- action = submenu->addAction("Bind diagram and tree expand");
- action->setToolTip("Expanding/collapsing blocks at diagram expands/collapses\nblocks at hierarchy tree and wise versa.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.bind_scene_and_tree_expand_status);
- connect(action, &QAction::triggered, this, &This::onBindExpandStatusChange);
-
- action = submenu->addAction("Selecting block changes current thread");
- action->setToolTip("Automatically select thread while selecting a block.\nIf not checked then you will have to select current thread\nmanually double clicking on thread name on a diagram.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.selecting_block_changes_thread);
- connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.selecting_block_changes_thread = _checked; });
-
- action = submenu->addAction("Draw event indicators");
- action->setToolTip("Display event indicators under the blocks\n(even if event-blocks are not visible).\nThis slightly reduces performance.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.enable_event_indicators);
- connect(action, &QAction::triggered, this, &This::onEventIndicatorsChange);
-
- action = submenu->addAction("Use decorated thread names");
- action->setToolTip("Add \'Thread\' word into thread name if there is no one already.\nExamples: \'Render\' will change to \'Render Thread\'\n\'WorkerThread\' will not change.");
- action->setCheckable(true);
- action->setChecked(EASY_GLOBALS.use_decorated_thread_name);
- connect(action, &QAction::triggered, [this](bool _checked)
- {
- EASY_GLOBALS.use_decorated_thread_name = _checked;
- emit EASY_GLOBALS.events.threadNameDecorationChanged();
- });
-
- submenu->addSeparator();
- auto actionGroup = new QActionGroup(this);
- actionGroup->setExclusive(true);
-
- action = new QAction("Chrono text at top", actionGroup);
- action->setToolTip("Draw duration of selected interval\nat the top of the screen.");
- action->setCheckable(true);
- action->setData(static_cast(::profiler_gui::ChronoTextPosition_Top));
- if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Top)
- action->setChecked(true);
- submenu->addAction(action);
- connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged);
-
- action = new QAction("Chrono text at center", actionGroup);
- action->setToolTip("Draw duration of selected interval\nat the center of the screen.");
- action->setCheckable(true);
- action->setData(static_cast(::profiler_gui::ChronoTextPosition_Center));
- if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Center)
- action->setChecked(true);
- submenu->addAction(action);
- connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged);
-
- action = new QAction("Chrono text at bottom", actionGroup);
- action->setToolTip("Draw duration of selected interval\nat the bottom of the screen.");
- action->setCheckable(true);
- action->setData(static_cast(::profiler_gui::ChronoTextPosition_Bottom));
- if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Bottom)
- action->setChecked(true);
- submenu->addAction(action);
- connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged);
-
- submenu->addSeparator();
- auto w = new QWidget(submenu);
- auto l = new QHBoxLayout(w);
- l->setContentsMargins(33, 1, 1, 1);
- l->addWidget(new QLabel("Min blocks spacing, px", w), 0, Qt::AlignLeft);
- auto spinbox = new QSpinBox(w);
- spinbox->setMinimum(0);
- spinbox->setValue(EASY_GLOBALS.blocks_spacing);
- spinbox->setFixedWidth(50);
- connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onSpacingChange(int)));
- l->addWidget(spinbox);
- w->setLayout(l);
- auto waction = new QWidgetAction(submenu);
- waction->setDefaultWidget(w);
- submenu->addAction(waction);
-
- w = new QWidget(submenu);
- l = new QHBoxLayout(w);
- l->setContentsMargins(33, 1, 1, 1);
- l->addWidget(new QLabel("Min blocks size, px", w), 0, Qt::AlignLeft);
- spinbox = new QSpinBox(w);
- spinbox->setMinimum(1);
- spinbox->setValue(EASY_GLOBALS.blocks_size_min);
- spinbox->setFixedWidth(50);
- connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onMinSizeChange(int)));
- l->addWidget(spinbox);
- w->setLayout(l);
- waction = new QWidgetAction(submenu);
- waction->setDefaultWidget(w);
- submenu->addAction(waction);
-
- w = new QWidget(submenu);
- l = new QHBoxLayout(w);
- l->setContentsMargins(33, 1, 1, 1);
- l->addWidget(new QLabel("Blocks narrow size, px", w), 0, Qt::AlignLeft);
- spinbox = new QSpinBox(w);
- spinbox->setMinimum(1);
- spinbox->setValue(EASY_GLOBALS.blocks_narrow_size);
- spinbox->setFixedWidth(50);
- connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onNarrowSizeChange(int)));
- l->addWidget(spinbox);
- w->setLayout(l);
- waction = new QWidgetAction(submenu);
- waction->setDefaultWidget(w);
- submenu->addAction(waction);
-
-
- submenu = menu->addMenu("Units");
- actionGroup = new QActionGroup(this);
- actionGroup->setExclusive(true);
- action = new QAction("Auto", actionGroup);
- action->setCheckable(true);
- action->setData(static_cast(::profiler_gui::TimeUnits_auto));
- if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_auto)
- action->setChecked(true);
- submenu->addAction(action);
- connect(action, &QAction::triggered, this, &This::onUnitsChanged);
-
- action = new QAction("Milliseconds", actionGroup);
- action->setCheckable(true);
- action->setData(static_cast(::profiler_gui::TimeUnits_ms));
- if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_ms)
- action->setChecked(true);
- submenu->addAction(action);
- connect(action, &QAction::triggered, this, &This::onUnitsChanged);
-
- action = new QAction("Microseconds", actionGroup);
- action->setCheckable(true);
- action->setData(static_cast(::profiler_gui::TimeUnits_us));
- if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_us)
- action->setChecked(true);
- submenu->addAction(action);
- connect(action, &QAction::triggered, this, &This::onUnitsChanged);
-
- action = new QAction("Nanoseconds", actionGroup);
- action->setCheckable(true);
- action->setData(static_cast(::profiler_gui::TimeUnits_ns));
- if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_ns)
- action->setChecked(true);
- submenu->addAction(action);
- connect(action, &QAction::triggered, this, &This::onUnitsChanged);
-
-
- submenu = menu->addMenu("Remote");
- m_eventTracingEnableAction = submenu->addAction("Event tracing enabled");
- m_eventTracingEnableAction->setCheckable(true);
- m_eventTracingEnableAction->setEnabled(false);
- connect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange);
-
- m_eventTracingPriorityAction = submenu->addAction("Low priority event tracing");
- m_eventTracingPriorityAction->setCheckable(true);
- m_eventTracingPriorityAction->setChecked(EASY_OPTION_LOW_PRIORITY_EVENT_TRACING);
- m_eventTracingPriorityAction->setEnabled(false);
- connect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange);
-
-
- submenu = menu->addMenu("Encoding");
- actionGroup = new QActionGroup(this);
- actionGroup->setExclusive(true);
-
- auto default_codec_mib = QTextCodec::codecForLocale()->mibEnum();
- foreach(int mib, QTextCodec::availableMibs())
- {
- auto codec = QTextCodec::codecForMib(mib)->name();
-
- action = new QAction(codec, actionGroup);
- action->setCheckable(true);
- if (mib == default_codec_mib)
- action->setChecked(true);
-
- submenu->addAction(action);
- connect(action, &QAction::triggered, this, &This::onEncodingChanged);
- }
-
- auto tb_height = toolbar->height() + 4;
- toolbar = addToolBar("FrameToolbar");
- toolbar->setIconSize(::profiler_gui::ICONS_SIZE);
- toolbar->setObjectName("ProfilerGUI_FrameToolbar");
- toolbar->setContentsMargins(1, 0, 1, 0);
- toolbar->setMinimumHeight(tb_height);
-
- lbl = new QLabel("Frame time:", toolbar);
- lbl->setContentsMargins(5, 2, 2, 2);
- toolbar->addWidget(lbl);
-
- m_frameTimeEdit = new QLineEdit();
- m_frameTimeEdit->setFixedWidth(70);
- auto val = new QDoubleValidator(m_frameTimeEdit);
- val->setLocale(QLocale::c());
- val->setBottom(0);
- m_frameTimeEdit->setValidator(val);
- m_frameTimeEdit->setText(QString::number(EASY_GLOBALS.frame_time * 1e-3));
- connect(m_frameTimeEdit, &QLineEdit::editingFinished, this, &This::onFrameTimeEditFinish);
- toolbar->addWidget(m_frameTimeEdit);
-
- lbl = new QLabel("ms", toolbar);
- lbl->setContentsMargins(5, 2, 1, 1);
- toolbar->addWidget(lbl);
-
-
- connect(graphicsView->view(), &EasyGraphicsView::intervalChanged, treeWidget->tree(), &EasyTreeWidget::setTreeBlocks);
- connect(&m_readerTimer, &QTimer::timeout, this, &This::onFileReaderTimeout);
- connect(&m_listenerTimer, &QTimer::timeout, this, &This::onListenerTimerTimeout);
-
-
- m_progress = new QProgressDialog("Loading file...", "Cancel", 0, 100, this);
- m_progress->setFixedWidth(300);
- m_progress->setWindowTitle("EasyProfiler");
- m_progress->setModal(true);
- m_progress->setValue(100);
- //m_progress->hide();
- connect(m_progress, &QProgressDialog::canceled, this, &This::onFileReaderCancel);
-
- loadGeometry();
-
- if(QCoreApplication::arguments().size() > 1)
- {
- auto opened_filename = QCoreApplication::arguments().at(1);
- loadFile(opened_filename);
- }
-
- connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blockStatusChanged, this, &This::onBlockStatusChange);
- connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blocksRefreshRequired, this, &This::onGetBlockDescriptionsClicked);
-}
-
-EasyMainWindow::~EasyMainWindow()
-{
- delete m_progress;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::dragEnterEvent(QDragEnterEvent* drag_event)
-{
- if (drag_event->mimeData()->hasUrls())
- drag_event->acceptProposedAction();
-}
-
-void EasyMainWindow::dragMoveEvent(QDragMoveEvent* drag_event)
-{
- if (drag_event->mimeData()->hasUrls())
- drag_event->acceptProposedAction();
-}
-
-void EasyMainWindow::dragLeaveEvent(QDragLeaveEvent* drag_event)
-{
- drag_event->accept();
-}
-
-void EasyMainWindow::dropEvent(QDropEvent* drop_event)
-{
- const auto& urls = drop_event->mimeData()->urls();
- if (!urls.empty())
- loadFile(urls.front().toLocalFile());
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onOpenFileClicked(bool)
-{
- auto action = qobject_cast(sender());
- if (action == nullptr)
- return;
-
- if (action == m_loadActionMenu->menuAction())
- {
- auto filename = QFileDialog::getOpenFileName(this, "Open profiler log", m_lastFiles.empty() ? QString() : m_lastFiles.front(), "Profiler Log File (*.prof);;All Files (*.*)");
- if (!filename.isEmpty())
- loadFile(filename);
- }
- else
- {
- loadFile(action->text());
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::addFileToList(const QString& filename)
-{
- m_lastFiles.push_front(filename);
-
- auto action = new QAction(filename, this);
- connect(action, &QAction::triggered, this, &This::onOpenFileClicked);
- auto fileActions = m_loadActionMenu->actions();
- if (fileActions.empty())
- m_loadActionMenu->addAction(action);
- else
- m_loadActionMenu->insertAction(fileActions.front(), action);
-
- if (m_lastFiles.size() > 10)
- {
- // Keep 10 files at the list
- m_lastFiles.pop_back();
- m_loadActionMenu->removeAction(fileActions.back());
- delete fileActions.back();
- }
-}
-
-void EasyMainWindow::loadFile(const QString& filename)
-{
- const auto i = filename.lastIndexOf(QChar('/'));
- const auto j = filename.lastIndexOf(QChar('\\'));
- m_progress->setLabelText(QString("Loading %1...").arg(filename.mid(::std::max(i, j) + 1)));
-
- m_progress->setValue(0);
- m_progress->show();
- m_readerTimer.start(LOADER_TIMER_INTERVAL);
- m_reader.load(filename);
-}
-
-void EasyMainWindow::readStream(::std::stringstream& data)
-{
- m_progress->setLabelText(tr("Reading from stream..."));
-
- m_progress->setValue(0);
- m_progress->show();
- m_readerTimer.start(LOADER_TIMER_INTERVAL);
- m_reader.load(data);
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onSaveFileClicked(bool)
-{
- if (m_serializedBlocks.empty())
- return;
-
- QString lastFile = m_lastFiles.empty() ? QString() : m_lastFiles.front();
-
- const auto i = lastFile.lastIndexOf(QChar('/'));
- const auto j = lastFile.lastIndexOf(QChar('\\'));
- auto k = ::std::max(i, j);
-
- QString dir;
- if (k > 0)
- dir = lastFile.mid(0, ++k);
-
- auto filename = QFileDialog::getSaveFileName(this, "Save profiler log", dir, "Profiler Log File (*.prof);;All Files (*.*)");
- if (!filename.isEmpty())
- {
- bool inOk = false, outOk = false;
- int8_t retry1 = -1;
- while (++retry1 < 4)
- {
- ::std::ifstream inFile(m_bNetworkFileRegime ? NETWORK_CACHE_FILE : lastFile.toStdString().c_str(), ::std::fstream::binary);
- if (!inFile.is_open())
- {
- ::std::this_thread::sleep_for(::std::chrono::milliseconds(500));
- continue;
- }
-
- inOk = true;
-
- int8_t retry2 = -1;
- while (++retry2 < 4)
- {
- ::std::ofstream outFile(filename.toStdString(), ::std::fstream::binary);
- if (!outFile.is_open())
- {
- ::std::this_thread::sleep_for(::std::chrono::milliseconds(500));
- continue;
- }
-
- outFile << inFile.rdbuf();
- outOk = true;
- break;
- }
-
- break;
- }
-
- if (outOk)
- {
- if (m_bNetworkFileRegime)
- QFile::remove(QString(NETWORK_CACHE_FILE));
- addFileToList(filename);
- m_bNetworkFileRegime = false;
- }
- else if (inOk)
- {
- QMessageBox::warning(this, "Warning", "Can not open destination file.\nSaving incomplete.", QMessageBox::Close);
- }
- else
- {
- if (m_bNetworkFileRegime)
- QMessageBox::warning(this, "Warning", "Can not open network cache file.\nSaving incomplete.", QMessageBox::Close);
- else
- QMessageBox::warning(this, "Warning", "Can not open source file.\nSaving incomplete.", QMessageBox::Close);
- }
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::clear()
-{
- static_cast(m_treeWidget->widget())->clear(true);
- static_cast(m_graphicsView->widget())->clear();
-
-#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0
- static_cast(m_descTreeWidget->widget())->clear();
-#endif
- if (m_dialogDescTree != nullptr)
- m_dialogDescTree->clear();
-
- EASY_GLOBALS.selected_thread = 0;
- ::profiler_gui::set_max(EASY_GLOBALS.selected_block);
- ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id);
- EASY_GLOBALS.profiler_blocks.clear();
- EASY_GLOBALS.descriptors.clear();
- EASY_GLOBALS.gui_blocks.clear();
-
- m_serializedBlocks.clear();
- m_serializedDescriptors.clear();
-
- m_saveAction->setEnabled(false);
- m_deleteAction->setEnabled(false);
-
- m_bNetworkFileRegime = false;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::refreshDiagram()
-{
- static_cast(m_graphicsView->widget())->view()->scene()->update();
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onDeleteClicked(bool)
-{
- auto button = QMessageBox::question(this, "Clear all profiled data", "All profiled data is going to be deleted!\nContinue?", QMessageBox::Yes, QMessageBox::No);
- if (button == QMessageBox::Yes)
- clear();
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onExitClicked(bool)
-{
- close();
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onEncodingChanged(bool)
-{
- auto _sender = qobject_cast(sender());
- auto name = _sender->text();
- QTextCodec *codec = QTextCodec::codecForName(name.toStdString().c_str());
- QTextCodec::setCodecForLocale(codec);
-}
-
-void EasyMainWindow::onChronoTextPosChanged(bool)
-{
- auto _sender = qobject_cast(sender());
- EASY_GLOBALS.chrono_text_position = static_cast<::profiler_gui::ChronometerTextPosition>(_sender->data().toInt());
- refreshDiagram();
-}
-
-void EasyMainWindow::onUnitsChanged(bool)
-{
- auto _sender = qobject_cast(sender());
- EASY_GLOBALS.time_units = static_cast<::profiler_gui::TimeUnits>(_sender->data().toInt());
-}
-
-void EasyMainWindow::onEventIndicatorsChange(bool _checked)
-{
- EASY_GLOBALS.enable_event_indicators = _checked;
- refreshDiagram();
-}
-
-void EasyMainWindow::onEnableDisableStatistics(bool _checked)
-{
- EASY_GLOBALS.enable_statistics = _checked;
-
- auto action = qobject_cast(sender());
- if (action != nullptr)
- {
- auto f = action->font();
- f.setBold(_checked);
- action->setFont(f);
-
- if (_checked)
- {
- action->setText("Statistics enabled");
- SET_ICON(action, ":/Stats");
- }
- else
- {
- action->setText("Statistics disabled");
- SET_ICON(action, ":/Stats-off");
- }
- }
-}
-
-void EasyMainWindow::onCollapseItemsAfterCloseChanged(bool _checked)
-{
- EASY_GLOBALS.collapse_items_on_tree_close = _checked;
-}
-
-void EasyMainWindow::onAllItemsExpandedByDefaultChange(bool _checked)
-{
- EASY_GLOBALS.all_items_expanded_by_default = _checked;
-}
-
-void EasyMainWindow::onBindExpandStatusChange(bool _checked)
-{
- EASY_GLOBALS.bind_scene_and_tree_expand_status = _checked;
-}
-
-void EasyMainWindow::onHierarchyFlagChange(bool _checked)
-{
- EASY_GLOBALS.only_current_thread_hierarchy = _checked;
- emit EASY_GLOBALS.events.hierarchyFlagChanged(_checked);
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onExpandAllClicked(bool)
-{
- for (auto& block : EASY_GLOBALS.gui_blocks)
- block.expanded = true;
-
- emit EASY_GLOBALS.events.itemsExpandStateChanged();
-
- auto tree = static_cast(m_treeWidget->widget())->tree();
- const QSignalBlocker b(tree);
- tree->expandAll();
-}
-
-void EasyMainWindow::onCollapseAllClicked(bool)
-{
- for (auto& block : EASY_GLOBALS.gui_blocks)
- block.expanded = false;
-
- emit EASY_GLOBALS.events.itemsExpandStateChanged();
-
- auto tree = static_cast(m_treeWidget->widget())->tree();
- const QSignalBlocker b(tree);
- tree->collapseAll();
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onSpacingChange(int _value)
-{
- EASY_GLOBALS.blocks_spacing = _value;
- refreshDiagram();
-}
-
-void EasyMainWindow::onMinSizeChange(int _value)
-{
- EASY_GLOBALS.blocks_size_min = _value;
- refreshDiagram();
-}
-
-void EasyMainWindow::onNarrowSizeChange(int _value)
-{
- EASY_GLOBALS.blocks_narrow_size = _value;
- refreshDiagram();
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onEditBlocksClicked(bool)
-{
- if (m_descTreeDialog != nullptr)
- {
- m_descTreeDialog->raise();
- return;
- }
-
- m_descTreeDialog = new QDialog();
- m_descTreeDialog->setAttribute(Qt::WA_DeleteOnClose, true);
- m_descTreeDialog->setWindowTitle("EasyProfiler");
- m_descTreeDialog->resize(800, 600);
- connect(m_descTreeDialog, &QDialog::finished, this, &This::onDescTreeDialogClose);
-
- auto l = new QVBoxLayout(m_descTreeDialog);
- m_dialogDescTree = new EasyDescWidget(m_descTreeDialog);
- l->addWidget(m_dialogDescTree);
- m_descTreeDialog->setLayout(l);
-
- m_dialogDescTree->build();
- m_descTreeDialog->show();
-}
-
-void EasyMainWindow::onDescTreeDialogClose(int)
-{
- disconnect(m_descTreeDialog, &QDialog::finished, this, &This::onDescTreeDialogClose);
- m_dialogDescTree = nullptr;
- m_descTreeDialog = nullptr;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::closeEvent(QCloseEvent* close_event)
-{
- saveSettingsAndGeometry();
-
- if (m_descTreeDialog != nullptr)
- {
- m_descTreeDialog->reject();
- m_descTreeDialog = nullptr;
- m_dialogDescTree = nullptr;
- }
-
- Parent::closeEvent(close_event);
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::loadSettings()
-{
- QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
- settings.beginGroup("main");
-
- auto last_files = settings.value("last_files");
- if (!last_files.isNull())
- m_lastFiles = last_files.toStringList();
-
- auto last_addr = settings.value("ip_address");
- if (!last_addr.isNull())
- m_lastAddress = last_addr.toString();
-
- auto last_port = settings.value("port");
- if (!last_port.isNull())
- m_lastPort = (uint16_t)last_port.toUInt();
-
-
- auto val = settings.value("chrono_text_position");
- if (!val.isNull())
- EASY_GLOBALS.chrono_text_position = static_cast<::profiler_gui::ChronometerTextPosition>(val.toInt());
-
- val = settings.value("time_units");
- if (!val.isNull())
- EASY_GLOBALS.time_units = static_cast<::profiler_gui::TimeUnits>(val.toInt());
-
-
- val = settings.value("frame_time");
- if (!val.isNull())
- EASY_GLOBALS.frame_time = val.toFloat();
-
- val = settings.value("blocks_spacing");
- if (!val.isNull())
- EASY_GLOBALS.blocks_spacing = val.toInt();
-
- val = settings.value("blocks_size_min");
- if (!val.isNull())
- EASY_GLOBALS.blocks_size_min = val.toInt();
-
- val = settings.value("blocks_narrow_size");
- if (!val.isNull())
- EASY_GLOBALS.blocks_narrow_size = val.toInt();
-
-
- auto flag = settings.value("draw_graphics_items_borders");
- if (!flag.isNull())
- EASY_GLOBALS.draw_graphics_items_borders = flag.toBool();
-
- flag = settings.value("hide_narrow_children");
- if (!flag.isNull())
- EASY_GLOBALS.hide_narrow_children = flag.toBool();
-
- flag = settings.value("hide_minsize_blocks");
- if (!flag.isNull())
- EASY_GLOBALS.hide_minsize_blocks = flag.toBool();
-
- flag = settings.value("collapse_items_on_tree_close");
- if (!flag.isNull())
- EASY_GLOBALS.collapse_items_on_tree_close = flag.toBool();
-
- flag = settings.value("all_items_expanded_by_default");
- if (!flag.isNull())
- EASY_GLOBALS.all_items_expanded_by_default = flag.toBool();
-
- flag = settings.value("only_current_thread_hierarchy");
- if (!flag.isNull())
- EASY_GLOBALS.only_current_thread_hierarchy = flag.toBool();
-
- flag = settings.value("enable_zero_length");
- if (!flag.isNull())
- EASY_GLOBALS.enable_zero_length = flag.toBool();
-
- flag = settings.value("add_zero_blocks_to_hierarchy");
- if (!flag.isNull())
- EASY_GLOBALS.add_zero_blocks_to_hierarchy = flag.toBool();
-
-
- flag = settings.value("highlight_blocks_with_same_id");
- if (!flag.isNull())
- EASY_GLOBALS.highlight_blocks_with_same_id = flag.toBool();
-
- flag = settings.value("bind_scene_and_tree_expand_status");
- if (!flag.isNull())
- EASY_GLOBALS.bind_scene_and_tree_expand_status = flag.toBool();
-
- flag = settings.value("selecting_block_changes_thread");
- if (!flag.isNull())
- EASY_GLOBALS.selecting_block_changes_thread = flag.toBool();
-
- flag = settings.value("enable_event_indicators");
- if (!flag.isNull())
- EASY_GLOBALS.enable_event_indicators = flag.toBool();
-
- flag = settings.value("use_decorated_thread_name");
- if (!flag.isNull())
- EASY_GLOBALS.use_decorated_thread_name = flag.toBool();
-
- flag = settings.value("enable_statistics");
- if (!flag.isNull())
- EASY_GLOBALS.enable_statistics = flag.toBool();
-
- QString encoding = settings.value("encoding", "UTF-8").toString();
- auto default_codec_mib = QTextCodec::codecForName(encoding.toStdString().c_str())->mibEnum();
- auto default_codec = QTextCodec::codecForMib(default_codec_mib);
- QTextCodec::setCodecForLocale(default_codec);
-
- settings.endGroup();
-}
-
-void EasyMainWindow::loadGeometry()
-{
- QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
- settings.beginGroup("main");
-
- auto geometry = settings.value("geometry").toByteArray();
- if (!geometry.isEmpty())
- restoreGeometry(geometry);
-
- auto state = settings.value("windowState").toByteArray();
- if (!state.isEmpty())
- restoreState(state);
-
- settings.endGroup();
-}
-
-void EasyMainWindow::saveSettingsAndGeometry()
-{
- QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
- settings.beginGroup("main");
-
- settings.setValue("geometry", this->saveGeometry());
- settings.setValue("windowState", this->saveState());
- settings.setValue("last_files", m_lastFiles);
- settings.setValue("ip_address", m_lastAddress);
- settings.setValue("port", (quint32)m_lastPort);
- settings.setValue("chrono_text_position", static_cast(EASY_GLOBALS.chrono_text_position));
- settings.setValue("time_units", static_cast(EASY_GLOBALS.time_units));
- settings.setValue("frame_time", EASY_GLOBALS.frame_time);
- settings.setValue("blocks_spacing", EASY_GLOBALS.blocks_spacing);
- settings.setValue("blocks_size_min", EASY_GLOBALS.blocks_size_min);
- settings.setValue("blocks_narrow_size", EASY_GLOBALS.blocks_narrow_size);
- settings.setValue("draw_graphics_items_borders", EASY_GLOBALS.draw_graphics_items_borders);
- settings.setValue("hide_narrow_children", EASY_GLOBALS.hide_narrow_children);
- settings.setValue("hide_minsize_blocks", EASY_GLOBALS.hide_minsize_blocks);
- settings.setValue("collapse_items_on_tree_close", EASY_GLOBALS.collapse_items_on_tree_close);
- settings.setValue("all_items_expanded_by_default", EASY_GLOBALS.all_items_expanded_by_default);
- settings.setValue("only_current_thread_hierarchy", EASY_GLOBALS.only_current_thread_hierarchy);
- settings.setValue("enable_zero_length", EASY_GLOBALS.enable_zero_length);
- settings.setValue("add_zero_blocks_to_hierarchy", EASY_GLOBALS.add_zero_blocks_to_hierarchy);
- settings.setValue("highlight_blocks_with_same_id", EASY_GLOBALS.highlight_blocks_with_same_id);
- settings.setValue("bind_scene_and_tree_expand_status", EASY_GLOBALS.bind_scene_and_tree_expand_status);
- settings.setValue("selecting_block_changes_thread", EASY_GLOBALS.selecting_block_changes_thread);
- settings.setValue("enable_event_indicators", EASY_GLOBALS.enable_event_indicators);
- settings.setValue("use_decorated_thread_name", EASY_GLOBALS.use_decorated_thread_name);
- settings.setValue("enable_statistics", EASY_GLOBALS.enable_statistics);
- settings.setValue("encoding", QTextCodec::codecForLocale()->name());
-
- settings.endGroup();
-}
-
-void EasyMainWindow::setDisconnected(bool _showMessage)
-{
- if (_showMessage)
- QMessageBox::warning(this, "Warning", "Application was disconnected", QMessageBox::Close);
-
- EASY_GLOBALS.connected = false;
- m_captureAction->setEnabled(false);
- SET_ICON(m_connectAction, ":/Connection");
-
- m_eventTracingEnableAction->setEnabled(false);
- m_eventTracingPriorityAction->setEnabled(false);
-
- emit EASY_GLOBALS.events.connectionChanged(false);
-
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onListenerTimerTimeout()
-{
- if (!m_listener.connected())
- m_listenerDialog->reject();
-}
-
-void EasyMainWindow::onListenerDialogClose(int)
-{
- m_listenerTimer.stop();
- disconnect(m_listenerDialog, &QDialog::finished, this, &This::onListenerDialogClose);
- m_listenerDialog = nullptr;
-
- switch (m_listener.regime())
- {
- case LISTENER_CAPTURE:
- {
- m_listenerDialog = new QMessageBox(QMessageBox::Information, "Receiving data...", "This process may take some time.", QMessageBox::NoButton, this);
- m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true);
- m_listenerDialog->show();
-
- m_listener.stopCapture();
-
- m_listenerDialog->reject();
- m_listenerDialog = nullptr;
-
- if (m_listener.size() != 0)
- {
- readStream(m_listener.data());
- m_listener.clearData();
- }
-
- break;
- }
-
- case LISTENER_DESCRIBE:
- {
- break;
- }
-
- default:
- return;
- }
-
- if (!m_listener.connected())
- {
- setDisconnected();
- }
-}
-
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onFileReaderTimeout()
-{
- if (m_reader.done())
- {
- auto nblocks = m_reader.size();
- if (nblocks != 0)
- {
- static_cast(m_treeWidget->widget())->clear(true);
-
- ::profiler::SerializedData serialized_blocks;
- ::profiler::SerializedData serialized_descriptors;
- ::profiler::descriptors_list_t descriptors;
- ::profiler::blocks_t blocks;
- ::profiler::thread_blocks_tree_t threads_map;
- QString filename;
- uint32_t descriptorsNumberInFile = 0;
- m_reader.get(serialized_blocks, serialized_descriptors, descriptors, blocks, threads_map, descriptorsNumberInFile, filename);
-
- if (threads_map.size() > 0xff)
- {
- if (m_reader.isFile())
- qWarning() << "Warning: file " << filename << " contains " << threads_map.size() << " threads!";
- else
- qWarning() << "Warning: input stream contains " << threads_map.size() << " threads!";
- qWarning() << "Warning: Currently, maximum number of displayed threads is 255! Some threads will not be displayed.";
- }
-
- m_bNetworkFileRegime = !m_reader.isFile();
- if (!m_bNetworkFileRegime)
- {
- auto index = m_lastFiles.indexOf(filename, 0);
- if (index == -1)
- {
- // This file is totally new. Add it to the list.
- addFileToList(filename);
- }
- else if (index != 0)
- {
- // This file has been already loaded. Move it to the front.
- m_lastFiles.move(index, 0);
- auto fileActions = m_loadActionMenu->actions();
- auto action = fileActions.at(index);
- m_loadActionMenu->removeAction(action);
- m_loadActionMenu->insertAction(fileActions.front(), action);
- }
- }
- m_serializedBlocks = ::std::move(serialized_blocks);
- m_serializedDescriptors = ::std::move(serialized_descriptors);
- m_descriptorsNumberInFile = descriptorsNumberInFile;
- EASY_GLOBALS.selected_thread = 0;
- ::profiler_gui::set_max(EASY_GLOBALS.selected_block);
- ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id);
- EASY_GLOBALS.profiler_blocks.swap(threads_map);
- EASY_GLOBALS.descriptors.swap(descriptors);
-
- EASY_GLOBALS.gui_blocks.clear();
- EASY_GLOBALS.gui_blocks.resize(nblocks);
- memset(EASY_GLOBALS.gui_blocks.data(), 0, sizeof(::profiler_gui::EasyBlock) * nblocks);
- for (decltype(nblocks) i = 0; i < nblocks; ++i) {
- auto& guiblock = EASY_GLOBALS.gui_blocks[i];
- guiblock.tree = ::std::move(blocks[i]);
-#ifdef EASY_TREE_WIDGET__USE_VECTOR
- ::profiler_gui::set_max(guiblock.tree_item);
-#endif
- }
-
- static_cast(m_graphicsView->widget())->view()->setTree(EASY_GLOBALS.profiler_blocks);
-
-#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0
- static_cast(m_descTreeWidget->widget())->build();
-#endif
- if (m_dialogDescTree != nullptr)
- m_dialogDescTree->build();
-
- m_saveAction->setEnabled(true);
- m_deleteAction->setEnabled(true);
- }
- else
- {
- QMessageBox::warning(this, "Warning", QString("Can not read profiled blocks.\n\nReason:\n%1").arg(m_reader.getError()), QMessageBox::Close);
-
- if (m_reader.isFile())
- {
- auto index = m_lastFiles.indexOf(m_reader.filename(), 0);
- if (index >= 0)
- {
- // Remove unexisting file from list
- m_lastFiles.removeAt(index);
- auto action = m_loadActionMenu->actions().at(index);
- m_loadActionMenu->removeAction(action);
- delete action;
- }
- }
- }
-
- m_reader.interrupt();
-
- m_readerTimer.stop();
- m_progress->setValue(100);
- //m_progress->hide();
-
- if (EASY_GLOBALS.all_items_expanded_by_default)
- {
- onExpandAllClicked(true);
- }
- }
- else
- {
- m_progress->setValue(m_reader.progress());
- }
-}
-
-void EasyMainWindow::onFileReaderCancel()
-{
- m_readerTimer.stop();
- m_reader.interrupt();
- m_progress->setValue(100);
- //m_progress->hide();
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-EasyFileReader::EasyFileReader()
-{
-
-}
-
-EasyFileReader::~EasyFileReader()
-{
- interrupt();
-}
-
-const bool EasyFileReader::isFile() const
-{
- return m_isFile;
-}
-
-bool EasyFileReader::done() const
-{
- return m_bDone.load(::std::memory_order_acquire);
-}
-
-int EasyFileReader::progress() const
-{
- return m_progress.load(::std::memory_order_acquire);
-}
-
-unsigned int EasyFileReader::size() const
-{
- return m_size.load(::std::memory_order_acquire);
-}
-
-const QString& EasyFileReader::filename() const
-{
- return m_filename;
-}
-
-void EasyFileReader::load(const QString& _filename)
-{
- interrupt();
-
- m_isFile = true;
- m_filename = _filename;
- m_thread = ::std::thread([this](bool _enableStatistics) {
- m_size.store(fillTreesFromFile(m_progress, m_filename.toStdString().c_str(), m_serializedBlocks, m_serializedDescriptors,
- m_descriptors, m_blocks, m_blocksTree, m_descriptorsNumberInFile, _enableStatistics, m_errorMessage), ::std::memory_order_release);
- m_progress.store(100, ::std::memory_order_release);
- m_bDone.store(true, ::std::memory_order_release);
- }, EASY_GLOBALS.enable_statistics);
-}
-
-void EasyFileReader::load(::std::stringstream& _stream)
-{
- interrupt();
-
- m_isFile = false;
- m_filename.clear();
-
-#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__llvm__)
- // gcc 4 has a known bug which has been solved in gcc 5:
- // std::stringstream has no swap() method :(
- // have to copy all contents... Use gcc 5 or higher!
-#pragma message "Warning: in gcc 4 and lower std::stringstream has no swap()! Memory consumption may increase! Better use gcc 5 or higher instead."
- m_stream.str(_stream.str());
-#else
- m_stream.swap(_stream);
-#endif
-
- m_thread = ::std::thread([this](bool _enableStatistics) {
- ::std::ofstream cache_file(NETWORK_CACHE_FILE, ::std::fstream::binary);
- if (cache_file.is_open()) {
- cache_file << m_stream.str();
- cache_file.close();
- }
- m_size.store(fillTreesFromStream(m_progress, m_stream, m_serializedBlocks, m_serializedDescriptors, m_descriptors,
- m_blocks, m_blocksTree, m_descriptorsNumberInFile, _enableStatistics, m_errorMessage), ::std::memory_order_release);
- m_progress.store(100, ::std::memory_order_release);
- m_bDone.store(true, ::std::memory_order_release);
- }, EASY_GLOBALS.enable_statistics);
-}
-
-void EasyFileReader::interrupt()
-{
- m_progress.store(-100, ::std::memory_order_release);
- if (m_thread.joinable())
- m_thread.join();
-
- m_bDone.store(false, ::std::memory_order_release);
- m_progress.store(0, ::std::memory_order_release);
- m_size.store(0, ::std::memory_order_release);
- m_serializedBlocks.clear();
- m_serializedDescriptors.clear();
- m_descriptors.clear();
- m_blocks.clear();
- m_blocksTree.clear();
- m_descriptorsNumberInFile = 0;
-
- clear_stream(m_stream);
- clear_stream(m_errorMessage);
-}
-
-void EasyFileReader::get(::profiler::SerializedData& _serializedBlocks, ::profiler::SerializedData& _serializedDescriptors,
- ::profiler::descriptors_list_t& _descriptors, ::profiler::blocks_t& _blocks,
- ::profiler::thread_blocks_tree_t& _tree, uint32_t& _descriptorsNumberInFile, QString& _filename)
-{
- if (done())
- {
- m_serializedBlocks.swap(_serializedBlocks);
- m_serializedDescriptors.swap(_serializedDescriptors);
- ::profiler::descriptors_list_t(::std::move(m_descriptors)).swap(_descriptors);
- m_blocks.swap(_blocks);
- m_blocksTree.swap(_tree);
- m_filename.swap(_filename);
- _descriptorsNumberInFile = m_descriptorsNumberInFile;
- }
-}
-
-QString EasyFileReader::getError()
-{
- return QString(m_errorMessage.str().c_str());
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onEventTracingPriorityChange(bool _checked)
-{
- if (EASY_GLOBALS.connected)
- m_listener.send(profiler::net::BoolMessage(profiler::net::MESSAGE_TYPE_EVENT_TRACING_PRIORITY, _checked));
-}
-
-void EasyMainWindow::onEventTracingEnableChange(bool _checked)
-{
- if (EASY_GLOBALS.connected)
- m_listener.send(profiler::net::BoolMessage(profiler::net::MESSAGE_TYPE_EVENT_TRACING_STATUS, _checked));
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onFrameTimeEditFinish()
-{
- auto text = m_frameTimeEdit->text();
- if (text.contains(QChar(',')))
- {
- text.remove(QChar('.')).replace(QChar(','), QChar('.'));
- m_frameTimeEdit->setText(text);
- }
-
- EASY_GLOBALS.frame_time = text.toFloat() * 1e3f;
- emit EASY_GLOBALS.events.timelineMarkerChanged();
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onConnectClicked(bool)
-{
- auto text = m_addressEdit->text();
-// auto parts = text.split(QChar('.'));
-// if (parts.size() != 4)
-// {
-// QMessageBox::warning(this, "Warning", "Invalid IP-Address", QMessageBox::Close);
-//
-// if (EASY_GLOBALS.connected)
-// {
-// // Restore last values
-// m_addressEdit->setText(m_lastAddress);
-// m_portEdit->setText(QString::number(m_lastPort));
-// }
-//
-// return;
-// }
-//
-// for (auto& part : parts)
-// {
-// int i = 0;
-// for (; i < part.size(); ++i)
-// {
-// if (part[i] != QChar('0'))
-// break;
-// }
-//
-// if (i < part.size())
-// part = part.mid(i);
-// else
-// part = "0";
-// }
-
- QString& address = text;// parts.join(QChar('.'));
- const decltype(m_lastPort) port = m_portEdit->text().toUShort();
- //m_addressEdit->setText(address);
-
- const bool isReconnecting = (EASY_GLOBALS.connected && m_listener.port() == port && address.toStdString() == m_listener.address());
- if (EASY_GLOBALS.connected)
- {
- if (QMessageBox::question(this, isReconnecting ? "Reconnect" : "New connection", QString("Current connection will be broken\n\n%1")
- .arg(isReconnecting ? "Re-connect?" : "Establish new connection?"),
- QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
- {
- if (!isReconnecting)
- {
- // Restore last values
- m_addressEdit->setText(m_lastAddress);
- m_portEdit->setText(QString::number(m_lastPort));
- }
-
- return;
- }
- }
-
- profiler::net::EasyProfilerStatus reply(false, false, false);
- if (!m_listener.connect(address.toStdString().c_str(), port, reply))
- {
- if (EASY_GLOBALS.connected && !isReconnecting)
- {
- if (QMessageBox::warning(this, "Warning", QString("Cannot connect to %1\n\nRestore previous connection?").arg(address),
- QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
- {
- if (!m_listener.connect(m_lastAddress.toStdString().c_str(), m_lastPort, reply))
- {
- QMessageBox::warning(this, "Warning", "Cannot restore previous connection", QMessageBox::Close);
- setDisconnected(false);
- m_lastAddress = ::std::move(address);
- m_lastPort = port;
- }
- else
- {
- m_addressEdit->setText(m_lastAddress);
- m_portEdit->setText(QString::number(m_lastPort));
- // QMessageBox::information(this, "Information", "Previous connection restored", QMessageBox::Close);
- }
- }
- else
- {
- setDisconnected(false);
- m_lastAddress = ::std::move(address);
- m_lastPort = port;
- }
- }
- else
- {
- QMessageBox::warning(this, "Warning", QString("Cannot connect to %1").arg(address), QMessageBox::Close);
- if (EASY_GLOBALS.connected)
- setDisconnected(false);
-
- if (!isReconnecting)
- {
- m_lastAddress = ::std::move(address);
- m_lastPort = port;
- }
- }
-
- return;
- }
-
- m_lastAddress = ::std::move(address);
- m_lastPort = port;
-
- qInfo() << "Connected successfully";
- EASY_GLOBALS.connected = true;
- m_captureAction->setEnabled(true);
- SET_ICON(m_connectAction, ":/Connection-on");
-
- disconnect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange);
- disconnect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange);
-
- m_eventTracingEnableAction->setEnabled(true);
- m_eventTracingPriorityAction->setEnabled(true);
-
- m_eventTracingEnableAction->setChecked(reply.isEventTracingEnabled);
- m_eventTracingPriorityAction->setChecked(reply.isLowPriorityEventTracing);
-
- connect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange);
- connect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange);
-
- emit EASY_GLOBALS.events.connectionChanged(true);
-}
-
-void EasyMainWindow::onCaptureClicked(bool)
-{
- if (!EASY_GLOBALS.connected)
- {
- QMessageBox::warning(this, "Warning", "No connection with profiling app", QMessageBox::Close);
- return;
- }
-
- if (m_listener.regime() != LISTENER_IDLE)
- {
- if (m_listener.regime() == LISTENER_CAPTURE)
- QMessageBox::warning(this, "Warning", "Already capturing frames.\nFinish old capturing session first.", QMessageBox::Close);
- else
- QMessageBox::warning(this, "Warning", "Capturing blocks description.\nFinish old capturing session first.", QMessageBox::Close);
- return;
- }
-
- if (!m_listener.startCapture())
- {
- // Connection lost. Try to restore connection.
-
- profiler::net::EasyProfilerStatus reply(false, false, false);
- if (!m_listener.connect(m_lastAddress.toStdString().c_str(), m_lastPort, reply))
- {
- setDisconnected();
- return;
- }
-
- if (!m_listener.startCapture())
- {
- setDisconnected();
- return;
- }
- }
-
- m_listenerTimer.start(250);
-
- m_listenerDialog = new QMessageBox(QMessageBox::Information, "Capturing frames...", "Close this dialog to stop capturing.", QMessageBox::NoButton, this);
-
- auto button = new QToolButton(m_listenerDialog);
- button->setAutoRaise(true);
- button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
- button->setIconSize(::profiler_gui::ICONS_SIZE);
- button->setIcon(QIcon(":/Stop"));
- button->setText("Stop");
- m_listenerDialog->addButton(button, QMessageBox::AcceptRole);
-
- m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true);
- connect(m_listenerDialog, &QDialog::finished, this, &This::onListenerDialogClose);
- m_listenerDialog->show();
-}
-
-void EasyMainWindow::onGetBlockDescriptionsClicked(bool)
-{
- if (!EASY_GLOBALS.connected)
- {
- QMessageBox::warning(this, "Warning", "No connection with profiling app", QMessageBox::Close);
- return;
- }
-
- if (m_listener.regime() != LISTENER_IDLE)
- {
- if (m_listener.regime() == LISTENER_DESCRIBE)
- QMessageBox::warning(this, "Warning", "Already capturing blocks description.\nFinish old capturing session first.", QMessageBox::Close);
- else
- QMessageBox::warning(this, "Warning", "Capturing capturing frames.\nFinish old capturing session first.", QMessageBox::Close);
- return;
- }
-
- m_listenerDialog = new QMessageBox(QMessageBox::Information, "Waiting for blocks...", "This may take some time.", QMessageBox::NoButton, this);
- m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true);
- m_listenerDialog->show();
-
- m_listener.requestBlocksDescription();
-
- m_listenerDialog->reject();
- m_listenerDialog = nullptr;
-
- if (m_listener.size() != 0)
- {
- // Read descriptions from stream
-
- decltype(EASY_GLOBALS.descriptors) descriptors;
- decltype(m_serializedDescriptors) serializedDescriptors;
- ::std::stringstream errorMessage;
- if (readDescriptionsFromStream(m_listener.data(), serializedDescriptors, descriptors, errorMessage))
- {
- // Merge old and new descriptions
-
- bool cancel = false;
- const bool doFlush = m_descriptorsNumberInFile > descriptors.size();
- if (doFlush && !m_serializedBlocks.empty())
- {
- auto button = QMessageBox::question(this, "Information",
- QString("New blocks description number = %1\nis less than the old one = %2.\nTo avoid possible conflicts\nall profiled data will be deleted.\nContinue?")
- .arg(descriptors.size())
- .arg(m_descriptorsNumberInFile),
- QMessageBox::Yes, QMessageBox::No);
-
- if (button == QMessageBox::Yes)
- clear(); // Clear all contents because new descriptors list conflicts with old one
- else
- cancel = true;
- }
-
- if (!cancel)
- {
- if (!doFlush && m_descriptorsNumberInFile < EASY_GLOBALS.descriptors.size())
- {
- // There are dynamically added descriptors, add them to the new list too
-
- auto newnumber = static_cast(descriptors.size());
- auto size = static_cast(EASY_GLOBALS.descriptors.size());
- auto diff = newnumber - size;
- decltype(newnumber) failnumber = 0;
-
- descriptors.reserve(descriptors.size() + EASY_GLOBALS.descriptors.size() - m_descriptorsNumberInFile);
- for (auto i = m_descriptorsNumberInFile; i < size; ++i)
- {
- auto id = EASY_GLOBALS.descriptors[i]->id();
- if (id < newnumber)
- descriptors.push_back(descriptors[id]);
- else
- ++failnumber;
- }
-
- if (failnumber != 0)
- {
- // There are some errors...
-
- // revert changes
- descriptors.resize(newnumber);
-
- // clear all profiled data to avoid conflicts
- auto button = QMessageBox::question(this, "Information",
- "There are errors while merging block descriptions lists.\nTo avoid possible conflicts\nall profiled data will be deleted.\nContinue?",
- QMessageBox::Yes, QMessageBox::No);
-
- if (button == QMessageBox::Yes)
- clear(); // Clear all contents because new descriptors list conflicts with old one
- else
- cancel = true;
- }
-
- if (!cancel && diff != 0)
- {
- for (auto& b : EASY_GLOBALS.gui_blocks)
- {
- if (b.tree.node->id() >= m_descriptorsNumberInFile)
- b.tree.node->setId(b.tree.node->id() + diff);
- }
-
- m_descriptorsNumberInFile = newnumber;
- }
- }
-
- if (!cancel)
- {
- EASY_GLOBALS.descriptors.swap(descriptors);
- m_serializedDescriptors.swap(serializedDescriptors);
- m_descriptorsNumberInFile = static_cast(EASY_GLOBALS.descriptors.size());
-
- if (m_descTreeDialog != nullptr)
- {
-#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0
- static_cast(m_descTreeWidget->widget())->build();
-#endif
- m_dialogDescTree->build();
- m_descTreeDialog->raise();
- }
- else
- {
- onEditBlocksClicked(true);
- }
- }
- }
- }
- else
- {
- QMessageBox::warning(this, "Warning", QString("Can not read blocks description from stream.\n\nReason:\n%1").arg(errorMessage.str().c_str()), QMessageBox::Close);
- }
-
- m_listener.clearData();
- }
-
- if (!m_listener.connected())
- {
- setDisconnected();
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyMainWindow::onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status)
-{
- if (EASY_GLOBALS.connected)
- m_listener.send(profiler::net::BlockStatusMessage(_id, static_cast(_status)));
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-EasySocketListener::EasySocketListener() : m_receivedSize(0), m_port(0), m_regime(LISTENER_IDLE)
-{
- m_bInterrupt = ATOMIC_VAR_INIT(false);
- m_bConnected = ATOMIC_VAR_INIT(false);
- m_bStopReceive = ATOMIC_VAR_INIT(false);
-}
-
-EasySocketListener::~EasySocketListener()
-{
- m_bInterrupt.store(true, ::std::memory_order_release);
- if (m_thread.joinable())
- m_thread.join();
-}
-
-bool EasySocketListener::connected() const
-{
- return m_bConnected.load(::std::memory_order_acquire);
-}
-
-EasyListenerRegime EasySocketListener::regime() const
-{
- return m_regime;
-}
-
-uint64_t EasySocketListener::size() const
-{
- return m_receivedSize;
-}
-
-::std::stringstream& EasySocketListener::data()
-{
- return m_receivedData;
-}
-
+/************************************************************************
+* file name : main_window.cpp
+* ----------------- :
+* creation time : 2016/06/26
+* author : Victor Zarubkin
+* email : v.s.zarubkin@gmail.com
+* ----------------- :
+* description : The file contains implementation of MainWindow for easy_profiler GUI.
+* ----------------- :
+* change log : * 2016/06/26 Victor Zarubkin: Initial commit.
+* :
+* : * 2016/06/27 Victor Zarubkin: Passing blocks number to EasyTreeWidget::setTree().
+* :
+* : * 2016/06/29 Victor Zarubkin: Added menu with tests.
+* :
+* : * 2016/06/30 Sergey Yagovtsev: Open file by command line argument
+* :
+* : *
+* ----------------- :
+* license : Lightweight profiler library for c++
+* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
+* :
+* :
+* : Licensed under the Apache License, Version 2.0 (the "License");
+* : you may not use this file except in compliance with the License.
+* : You may obtain a copy of the License at
+* :
+* : http://www.apache.org/licenses/LICENSE-2.0
+* :
+* : Unless required by applicable law or agreed to in writing, software
+* : distributed under the License is distributed on an "AS IS" BASIS,
+* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* : See the License for the specific language governing permissions and
+* : limitations under the License.
+* :
+* :
+* : GNU General Public License Usage
+* : Alternatively, this file may be used under the terms of the GNU
+* : General Public License as published by the Free Software Foundation,
+* : either version 3 of the License, or (at your option) any later version.
+* :
+* : This program is distributed in the hope that it will be useful,
+* : but WITHOUT ANY WARRANTY; without even the implied warranty of
+* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+* : GNU General Public License for more details.
+* :
+* : You should have received a copy of the GNU General Public License
+* : along with this program.If not, see .
+************************************************************************/
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "main_window.h"
+#include "blocks_tree_widget.h"
+#include "blocks_graphics_view.h"
+#include "descriptors_tree_widget.h"
+#include "globals.h"
+#include "easy/easy_net.h"
+
+#ifdef max
+#undef max
+#endif
+
+#ifdef min
+#undef min
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+
+const int LOADER_TIMER_INTERVAL = 40;
+const auto NETWORK_CACHE_FILE = "easy_profiler_stream.cache";
+
+//////////////////////////////////////////////////////////////////////////
+
+inline void clear_stream(std::stringstream& _stream)
+{
+#if defined(__GNUC__) && __GNUC__ < 5
+ // gcc 4 has a known bug which has been solved in gcc 5:
+ // std::stringstream has no swap() method :(
+ _stream.str(std::string());
+#else
+ std::stringstream().swap(_stream);
+#endif
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+EasyMainWindow::EasyMainWindow() : Parent(), m_lastAddress("localhost"), m_lastPort(::profiler::DEFAULT_PORT)
+{
+ { QIcon icon(":/logo"); if (!icon.isNull()) QApplication::setWindowIcon(icon); }
+
+ setObjectName("ProfilerGUI_MainWindow");
+ setWindowTitle("EasyProfiler");
+ setDockNestingEnabled(true);
+ setAcceptDrops(true);
+ resize(800, 600);
+
+ setStatusBar(new QStatusBar());
+
+ m_graphicsView = new QDockWidget("Diagram", this);
+ m_graphicsView->setObjectName("ProfilerGUI_Diagram");
+ m_graphicsView->setMinimumHeight(50);
+ m_graphicsView->setAllowedAreas(Qt::AllDockWidgetAreas);
+
+ auto graphicsView = new EasyGraphicsViewWidget(this);
+ m_graphicsView->setWidget(graphicsView);
+
+ m_treeWidget = new QDockWidget("Hierarchy", this);
+ m_treeWidget->setObjectName("ProfilerGUI_Hierarchy");
+ m_treeWidget->setMinimumHeight(50);
+ m_treeWidget->setAllowedAreas(Qt::AllDockWidgetAreas);
+
+ auto treeWidget = new EasyHierarchyWidget(this);
+ m_treeWidget->setWidget(treeWidget);
+
+ addDockWidget(Qt::TopDockWidgetArea, m_graphicsView);
+ addDockWidget(Qt::BottomDockWidgetArea, m_treeWidget);
+
+#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0
+ auto descTree = new EasyDescWidget();
+ m_descTreeWidget = new QDockWidget("Blocks");
+ m_descTreeWidget->setObjectName("ProfilerGUI_Blocks");
+ m_descTreeWidget->setMinimumHeight(50);
+ m_descTreeWidget->setAllowedAreas(Qt::AllDockWidgetAreas);
+ m_descTreeWidget->setWidget(descTree);
+ addDockWidget(Qt::BottomDockWidgetArea, m_descTreeWidget);
+#endif
+
+
+ loadSettings();
+
+
+ auto toolbar = addToolBar("FileToolbar");
+ toolbar->setIconSize(::profiler_gui::ICONS_SIZE);
+ toolbar->setObjectName("ProfilerGUI_FileToolbar");
+ toolbar->setContentsMargins(1, 0, 1, 0);
+
+ m_loadActionMenu = new QMenu(this);
+ auto action = m_loadActionMenu->menuAction();
+ action->setText("Open file");
+ action->setIcon(QIcon(":/Open"));
+ connect(action, &QAction::triggered, this, &This::onOpenFileClicked);
+ toolbar->addAction(action);
+
+ for (const auto& f : m_lastFiles)
+ {
+ action = new QAction(f, this);
+ connect(action, &QAction::triggered, this, &This::onOpenFileClicked);
+ m_loadActionMenu->addAction(action);
+ }
+
+ m_saveAction = toolbar->addAction(QIcon(":/Save"), tr("Save"), this, SLOT(onSaveFileClicked(bool)));
+ m_deleteAction = toolbar->addAction(QIcon(":/Delete"), tr("Clear all"), this, SLOT(onDeleteClicked(bool)));
+
+ m_saveAction->setEnabled(false);
+ m_deleteAction->setEnabled(false);
+
+
+
+ toolbar = addToolBar("ProfileToolbar");
+ toolbar->setIconSize(::profiler_gui::ICONS_SIZE);
+ toolbar->setObjectName("ProfilerGUI_ProfileToolbar");
+ toolbar->setContentsMargins(1, 0, 1, 0);
+
+ toolbar->addAction(QIcon(":/List"), tr("Blocks"), this, SLOT(onEditBlocksClicked(bool)));
+ m_captureAction = toolbar->addAction(QIcon(":/Start"), tr("Capture"), this, SLOT(onCaptureClicked(bool)));
+ m_captureAction->setEnabled(false);
+
+ toolbar->addSeparator();
+ m_connectAction = toolbar->addAction(QIcon(":/Connection"), tr("Connect"), this, SLOT(onConnectClicked(bool)));
+
+ auto lbl = new QLabel("Address:", toolbar);
+ lbl->setContentsMargins(5, 0, 2, 0);
+ toolbar->addWidget(lbl);
+ m_addressEdit = new QLineEdit();
+ m_addressEdit->setToolTip("Enter IP-address or host name");
+ //QRegExp rx("^0*(2(5[0-5]|[0-4]\\d)|1?\\d{1,2})(\\.0*(2(5[0-5]|[0-4]\\d)|1?\\d{1,2})){3}$");
+ //m_addressEdit->setValidator(new QRegExpValidator(rx, m_addressEdit));
+ m_addressEdit->setText(m_lastAddress);
+ m_addressEdit->setFixedWidth((m_addressEdit->fontMetrics().width(QString("255.255.255.255")) * 3) / 2);
+ toolbar->addWidget(m_addressEdit);
+
+ lbl = new QLabel("Port:", toolbar);
+ lbl->setContentsMargins(5, 0, 2, 0);
+ toolbar->addWidget(lbl);
+ m_portEdit = new QLineEdit();
+ m_portEdit->setValidator(new QIntValidator(1, 65535, m_portEdit));
+ m_portEdit->setText(QString::number(m_lastPort));
+ m_portEdit->setFixedWidth(m_portEdit->fontMetrics().width(QString("000000")) + 10);
+ toolbar->addWidget(m_portEdit);
+
+ connect(m_addressEdit, &QLineEdit::returnPressed, [this](){ onConnectClicked(true); });
+ connect(m_portEdit, &QLineEdit::returnPressed, [this](){ onConnectClicked(true); });
+
+
+
+ toolbar = addToolBar("SetupToolbar");
+ toolbar->setIconSize(::profiler_gui::ICONS_SIZE);
+ toolbar->setObjectName("ProfilerGUI_SetupToolbar");
+ toolbar->setContentsMargins(1, 0, 1, 0);
+
+ toolbar->addAction(QIcon(":/Expand"), "Expand all", this, SLOT(onExpandAllClicked(bool)));
+ toolbar->addAction(QIcon(":/Collapse"), "Collapse all", this, SLOT(onCollapseAllClicked(bool)));
+
+ toolbar->addSeparator();
+ auto menu = new QMenu("Settings", this);
+ QToolButton* toolButton = new QToolButton(toolbar);
+ toolButton->setIcon(QIcon(":/Settings"));
+ toolButton->setMenu(menu);
+ toolButton->setPopupMode(QToolButton::InstantPopup);
+ toolbar->addWidget(toolButton);
+
+ action = menu->addAction("Statistics enabled");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.enable_statistics);
+ connect(action, &QAction::triggered, this, &This::onEnableDisableStatistics);
+ if (EASY_GLOBALS.enable_statistics)
+ {
+ auto f = action->font();
+ f.setBold(true);
+ action->setFont(f);
+ SET_ICON(action, ":/Stats");
+ }
+ else
+ {
+ action->setText("Statistics disabled");
+ SET_ICON(action, ":/Stats-off");
+ }
+
+
+ menu->addSeparator();
+ auto submenu = menu->addMenu("View");
+ submenu->setToolTipsVisible(true);
+ action = submenu->addAction("Draw items' borders");
+ action->setToolTip("Draw borders for blocks on diagram.\nThis reduces performance.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.draw_graphics_items_borders);
+ connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.draw_graphics_items_borders = _checked; refreshDiagram(); });
+
+ action = submenu->addAction("Overlap narrow children");
+ action->setToolTip("Children blocks will be overlaped by narrow\nparent blocks. See also \'Blocks narrow size\'.\nThis improves performance.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.hide_narrow_children);
+ connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.hide_narrow_children = _checked; refreshDiagram(); });
+
+ action = submenu->addAction("Hide min-size blocks");
+ action->setToolTip("Hides blocks which screen size\nis less than \'Min blocks size\'.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.hide_minsize_blocks);
+ connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.hide_minsize_blocks = _checked; refreshDiagram(); });
+
+ action = submenu->addAction("Build hierarchy only for current thread");
+ action->setToolTip("Hierarchy tree will be built\nfor blocks from current thread only.\nThis improves performance\nand saves a lot of memory.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.only_current_thread_hierarchy);
+ connect(action, &QAction::triggered, this, &This::onHierarchyFlagChange);
+
+ action = submenu->addAction("Add zero blocks to hierarchy");
+ action->setToolTip("Zero duration blocks will be added into hierarchy tree.\nThis reduces performance and increases memory consumption.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.add_zero_blocks_to_hierarchy);
+ connect(action, &QAction::triggered, [this](bool _checked)
+ {
+ EASY_GLOBALS.add_zero_blocks_to_hierarchy = _checked;
+ emit EASY_GLOBALS.events.hierarchyFlagChanged(_checked);
+ });
+
+ action = submenu->addAction("Enable zero duration blocks on diagram");
+ action->setToolTip("If checked then allows diagram to paint zero duration blocks\nwith 1px width on each scale. Otherwise, such blocks will be resized\nto 250ns duration.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.enable_zero_length);
+ connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.enable_zero_length = _checked; refreshDiagram(); });
+
+ action = submenu->addAction("Highlight similar blocks");
+ action->setToolTip("Highlight all visible blocks which are similar\nto the current selected block.\nThis reduces performance.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.highlight_blocks_with_same_id);
+ connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.highlight_blocks_with_same_id = _checked; refreshDiagram(); });
+
+ action = submenu->addAction("Collapse blocks on tree reset");
+ action->setToolTip("This collapses all blocks on diagram\nafter hierarchy tree reset.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.collapse_items_on_tree_close);
+ connect(action, &QAction::triggered, this, &This::onCollapseItemsAfterCloseChanged);
+
+ action = submenu->addAction("Expand all on file open");
+ action->setToolTip("If checked then all blocks on diagram\nwill be initially expanded.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.all_items_expanded_by_default);
+ connect(action, &QAction::triggered, this, &This::onAllItemsExpandedByDefaultChange);
+
+ action = submenu->addAction("Bind diagram and tree expand");
+ action->setToolTip("Expanding/collapsing blocks at diagram expands/collapses\nblocks at hierarchy tree and wise versa.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.bind_scene_and_tree_expand_status);
+ connect(action, &QAction::triggered, this, &This::onBindExpandStatusChange);
+
+ action = submenu->addAction("Selecting block changes current thread");
+ action->setToolTip("Automatically select thread while selecting a block.\nIf not checked then you will have to select current thread\nmanually double clicking on thread name on a diagram.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.selecting_block_changes_thread);
+ connect(action, &QAction::triggered, [this](bool _checked){ EASY_GLOBALS.selecting_block_changes_thread = _checked; });
+
+ action = submenu->addAction("Draw event indicators");
+ action->setToolTip("Display event indicators under the blocks\n(even if event-blocks are not visible).\nThis slightly reduces performance.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.enable_event_indicators);
+ connect(action, &QAction::triggered, this, &This::onEventIndicatorsChange);
+
+ action = submenu->addAction("Use decorated thread names");
+ action->setToolTip("Add \'Thread\' word into thread name if there is no one already.\nExamples: \'Render\' will change to \'Render Thread\'\n\'WorkerThread\' will not change.");
+ action->setCheckable(true);
+ action->setChecked(EASY_GLOBALS.use_decorated_thread_name);
+ connect(action, &QAction::triggered, [this](bool _checked)
+ {
+ EASY_GLOBALS.use_decorated_thread_name = _checked;
+ emit EASY_GLOBALS.events.threadNameDecorationChanged();
+ });
+
+ submenu->addSeparator();
+ auto actionGroup = new QActionGroup(this);
+ actionGroup->setExclusive(true);
+
+ action = new QAction("Chrono text at top", actionGroup);
+ action->setToolTip("Draw duration of selected interval\nat the top of the screen.");
+ action->setCheckable(true);
+ action->setData(static_cast(::profiler_gui::ChronoTextPosition_Top));
+ if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Top)
+ action->setChecked(true);
+ submenu->addAction(action);
+ connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged);
+
+ action = new QAction("Chrono text at center", actionGroup);
+ action->setToolTip("Draw duration of selected interval\nat the center of the screen.");
+ action->setCheckable(true);
+ action->setData(static_cast(::profiler_gui::ChronoTextPosition_Center));
+ if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Center)
+ action->setChecked(true);
+ submenu->addAction(action);
+ connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged);
+
+ action = new QAction("Chrono text at bottom", actionGroup);
+ action->setToolTip("Draw duration of selected interval\nat the bottom of the screen.");
+ action->setCheckable(true);
+ action->setData(static_cast(::profiler_gui::ChronoTextPosition_Bottom));
+ if (EASY_GLOBALS.chrono_text_position == ::profiler_gui::ChronoTextPosition_Bottom)
+ action->setChecked(true);
+ submenu->addAction(action);
+ connect(action, &QAction::triggered, this, &This::onChronoTextPosChanged);
+
+ submenu->addSeparator();
+ auto w = new QWidget(submenu);
+ auto l = new QHBoxLayout(w);
+ l->setContentsMargins(33, 1, 1, 1);
+ l->addWidget(new QLabel("Min blocks spacing, px", w), 0, Qt::AlignLeft);
+ auto spinbox = new QSpinBox(w);
+ spinbox->setMinimum(0);
+ spinbox->setValue(EASY_GLOBALS.blocks_spacing);
+ spinbox->setFixedWidth(50);
+ connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onSpacingChange(int)));
+ l->addWidget(spinbox);
+ w->setLayout(l);
+ auto waction = new QWidgetAction(submenu);
+ waction->setDefaultWidget(w);
+ submenu->addAction(waction);
+
+ w = new QWidget(submenu);
+ l = new QHBoxLayout(w);
+ l->setContentsMargins(33, 1, 1, 1);
+ l->addWidget(new QLabel("Min blocks size, px", w), 0, Qt::AlignLeft);
+ spinbox = new QSpinBox(w);
+ spinbox->setMinimum(1);
+ spinbox->setValue(EASY_GLOBALS.blocks_size_min);
+ spinbox->setFixedWidth(50);
+ connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onMinSizeChange(int)));
+ l->addWidget(spinbox);
+ w->setLayout(l);
+ waction = new QWidgetAction(submenu);
+ waction->setDefaultWidget(w);
+ submenu->addAction(waction);
+
+ w = new QWidget(submenu);
+ l = new QHBoxLayout(w);
+ l->setContentsMargins(33, 1, 1, 1);
+ l->addWidget(new QLabel("Blocks narrow size, px", w), 0, Qt::AlignLeft);
+ spinbox = new QSpinBox(w);
+ spinbox->setMinimum(1);
+ spinbox->setValue(EASY_GLOBALS.blocks_narrow_size);
+ spinbox->setFixedWidth(50);
+ connect(spinbox, SIGNAL(valueChanged(int)), this, SLOT(onNarrowSizeChange(int)));
+ l->addWidget(spinbox);
+ w->setLayout(l);
+ waction = new QWidgetAction(submenu);
+ waction->setDefaultWidget(w);
+ submenu->addAction(waction);
+
+
+ submenu = menu->addMenu("Units");
+ actionGroup = new QActionGroup(this);
+ actionGroup->setExclusive(true);
+ action = new QAction("Auto", actionGroup);
+ action->setCheckable(true);
+ action->setData(static_cast(::profiler_gui::TimeUnits_auto));
+ if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_auto)
+ action->setChecked(true);
+ submenu->addAction(action);
+ connect(action, &QAction::triggered, this, &This::onUnitsChanged);
+
+ action = new QAction("Milliseconds", actionGroup);
+ action->setCheckable(true);
+ action->setData(static_cast(::profiler_gui::TimeUnits_ms));
+ if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_ms)
+ action->setChecked(true);
+ submenu->addAction(action);
+ connect(action, &QAction::triggered, this, &This::onUnitsChanged);
+
+ action = new QAction("Microseconds", actionGroup);
+ action->setCheckable(true);
+ action->setData(static_cast(::profiler_gui::TimeUnits_us));
+ if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_us)
+ action->setChecked(true);
+ submenu->addAction(action);
+ connect(action, &QAction::triggered, this, &This::onUnitsChanged);
+
+ action = new QAction("Nanoseconds", actionGroup);
+ action->setCheckable(true);
+ action->setData(static_cast(::profiler_gui::TimeUnits_ns));
+ if (EASY_GLOBALS.time_units == ::profiler_gui::TimeUnits_ns)
+ action->setChecked(true);
+ submenu->addAction(action);
+ connect(action, &QAction::triggered, this, &This::onUnitsChanged);
+
+
+ submenu = menu->addMenu("Remote");
+ m_eventTracingEnableAction = submenu->addAction("Event tracing enabled");
+ m_eventTracingEnableAction->setCheckable(true);
+ m_eventTracingEnableAction->setEnabled(false);
+ connect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange);
+
+ m_eventTracingPriorityAction = submenu->addAction("Low priority event tracing");
+ m_eventTracingPriorityAction->setCheckable(true);
+ m_eventTracingPriorityAction->setChecked(EASY_OPTION_LOW_PRIORITY_EVENT_TRACING);
+ m_eventTracingPriorityAction->setEnabled(false);
+ connect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange);
+
+
+ submenu = menu->addMenu("Encoding");
+ actionGroup = new QActionGroup(this);
+ actionGroup->setExclusive(true);
+
+ auto default_codec_mib = QTextCodec::codecForLocale()->mibEnum();
+ foreach(int mib, QTextCodec::availableMibs())
+ {
+ auto codec = QTextCodec::codecForMib(mib)->name();
+
+ action = new QAction(codec, actionGroup);
+ action->setCheckable(true);
+ if (mib == default_codec_mib)
+ action->setChecked(true);
+
+ submenu->addAction(action);
+ connect(action, &QAction::triggered, this, &This::onEncodingChanged);
+ }
+
+ auto tb_height = toolbar->height() + 4;
+ toolbar = addToolBar("FrameToolbar");
+ toolbar->setIconSize(::profiler_gui::ICONS_SIZE);
+ toolbar->setObjectName("ProfilerGUI_FrameToolbar");
+ toolbar->setContentsMargins(1, 0, 1, 0);
+ toolbar->setMinimumHeight(tb_height);
+
+ lbl = new QLabel("Frame time:", toolbar);
+ lbl->setContentsMargins(5, 2, 2, 2);
+ toolbar->addWidget(lbl);
+
+ m_frameTimeEdit = new QLineEdit();
+ m_frameTimeEdit->setFixedWidth(70);
+ auto val = new QDoubleValidator(m_frameTimeEdit);
+ val->setLocale(QLocale::c());
+ val->setBottom(0);
+ m_frameTimeEdit->setValidator(val);
+ m_frameTimeEdit->setText(QString::number(EASY_GLOBALS.frame_time * 1e-3));
+ connect(m_frameTimeEdit, &QLineEdit::editingFinished, this, &This::onFrameTimeEditFinish);
+ toolbar->addWidget(m_frameTimeEdit);
+
+ lbl = new QLabel("ms", toolbar);
+ lbl->setContentsMargins(5, 2, 1, 1);
+ toolbar->addWidget(lbl);
+
+
+ connect(graphicsView->view(), &EasyGraphicsView::intervalChanged, treeWidget->tree(), &EasyTreeWidget::setTreeBlocks);
+ connect(&m_readerTimer, &QTimer::timeout, this, &This::onFileReaderTimeout);
+ connect(&m_listenerTimer, &QTimer::timeout, this, &This::onListenerTimerTimeout);
+
+
+ m_progress = new QProgressDialog("Loading file...", "Cancel", 0, 100, this);
+ m_progress->setFixedWidth(300);
+ m_progress->setWindowTitle("EasyProfiler");
+ m_progress->setModal(true);
+ m_progress->setValue(100);
+ //m_progress->hide();
+ connect(m_progress, &QProgressDialog::canceled, this, &This::onFileReaderCancel);
+
+ loadGeometry();
+
+ if(QCoreApplication::arguments().size() > 1)
+ {
+ auto opened_filename = QCoreApplication::arguments().at(1);
+ loadFile(opened_filename);
+ }
+
+ connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blockStatusChanged, this, &This::onBlockStatusChange);
+ connect(&EASY_GLOBALS.events, &::profiler_gui::EasyGlobalSignals::blocksRefreshRequired, this, &This::onGetBlockDescriptionsClicked);
+}
+
+EasyMainWindow::~EasyMainWindow()
+{
+ delete m_progress;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::dragEnterEvent(QDragEnterEvent* drag_event)
+{
+ if (drag_event->mimeData()->hasUrls())
+ drag_event->acceptProposedAction();
+}
+
+void EasyMainWindow::dragMoveEvent(QDragMoveEvent* drag_event)
+{
+ if (drag_event->mimeData()->hasUrls())
+ drag_event->acceptProposedAction();
+}
+
+void EasyMainWindow::dragLeaveEvent(QDragLeaveEvent* drag_event)
+{
+ drag_event->accept();
+}
+
+void EasyMainWindow::dropEvent(QDropEvent* drop_event)
+{
+ const auto& urls = drop_event->mimeData()->urls();
+ if (!urls.empty())
+ loadFile(urls.front().toLocalFile());
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onOpenFileClicked(bool)
+{
+ auto action = qobject_cast(sender());
+ if (action == nullptr)
+ return;
+
+ if (action == m_loadActionMenu->menuAction())
+ {
+ auto filename = QFileDialog::getOpenFileName(this, "Open profiler log", m_lastFiles.empty() ? QString() : m_lastFiles.front(), "Profiler Log File (*.prof);;All Files (*.*)");
+ if (!filename.isEmpty())
+ loadFile(filename);
+ }
+ else
+ {
+ loadFile(action->text());
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::addFileToList(const QString& filename)
+{
+ m_lastFiles.push_front(filename);
+
+ auto action = new QAction(filename, this);
+ connect(action, &QAction::triggered, this, &This::onOpenFileClicked);
+ auto fileActions = m_loadActionMenu->actions();
+ if (fileActions.empty())
+ m_loadActionMenu->addAction(action);
+ else
+ m_loadActionMenu->insertAction(fileActions.front(), action);
+
+ if (m_lastFiles.size() > 10)
+ {
+ // Keep 10 files at the list
+ m_lastFiles.pop_back();
+ m_loadActionMenu->removeAction(fileActions.back());
+ delete fileActions.back();
+ }
+}
+
+void EasyMainWindow::loadFile(const QString& filename)
+{
+ const auto i = filename.lastIndexOf(QChar('/'));
+ const auto j = filename.lastIndexOf(QChar('\\'));
+ m_progress->setLabelText(QString("Loading %1...").arg(filename.mid(::std::max(i, j) + 1)));
+
+ m_progress->setValue(0);
+ m_progress->show();
+ m_readerTimer.start(LOADER_TIMER_INTERVAL);
+ m_reader.load(filename);
+}
+
+void EasyMainWindow::readStream(::std::stringstream& data)
+{
+ m_progress->setLabelText(tr("Reading from stream..."));
+
+ m_progress->setValue(0);
+ m_progress->show();
+ m_readerTimer.start(LOADER_TIMER_INTERVAL);
+ m_reader.load(data);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onSaveFileClicked(bool)
+{
+ if (m_serializedBlocks.empty())
+ return;
+
+ QString lastFile = m_lastFiles.empty() ? QString() : m_lastFiles.front();
+
+ const auto i = lastFile.lastIndexOf(QChar('/'));
+ const auto j = lastFile.lastIndexOf(QChar('\\'));
+ auto k = ::std::max(i, j);
+
+ QString dir;
+ if (k > 0)
+ dir = lastFile.mid(0, ++k);
+
+ auto filename = QFileDialog::getSaveFileName(this, "Save profiler log", dir, "Profiler Log File (*.prof);;All Files (*.*)");
+ if (!filename.isEmpty())
+ {
+ bool inOk = false, outOk = false;
+ int8_t retry1 = -1;
+ while (++retry1 < 4)
+ {
+ ::std::ifstream inFile(m_bNetworkFileRegime ? NETWORK_CACHE_FILE : lastFile.toStdString().c_str(), ::std::fstream::binary);
+ if (!inFile.is_open())
+ {
+ ::std::this_thread::sleep_for(::std::chrono::milliseconds(500));
+ continue;
+ }
+
+ inOk = true;
+
+ int8_t retry2 = -1;
+ while (++retry2 < 4)
+ {
+ ::std::ofstream outFile(filename.toStdString(), ::std::fstream::binary);
+ if (!outFile.is_open())
+ {
+ ::std::this_thread::sleep_for(::std::chrono::milliseconds(500));
+ continue;
+ }
+
+ outFile << inFile.rdbuf();
+ outOk = true;
+ break;
+ }
+
+ break;
+ }
+
+ if (outOk)
+ {
+ if (m_bNetworkFileRegime)
+ QFile::remove(QString(NETWORK_CACHE_FILE));
+ addFileToList(filename);
+ m_bNetworkFileRegime = false;
+ }
+ else if (inOk)
+ {
+ QMessageBox::warning(this, "Warning", "Can not open destination file.\nSaving incomplete.", QMessageBox::Close);
+ }
+ else
+ {
+ if (m_bNetworkFileRegime)
+ QMessageBox::warning(this, "Warning", "Can not open network cache file.\nSaving incomplete.", QMessageBox::Close);
+ else
+ QMessageBox::warning(this, "Warning", "Can not open source file.\nSaving incomplete.", QMessageBox::Close);
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::clear()
+{
+ static_cast(m_treeWidget->widget())->clear(true);
+ static_cast(m_graphicsView->widget())->clear();
+
+#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0
+ static_cast(m_descTreeWidget->widget())->clear();
+#endif
+ if (m_dialogDescTree != nullptr)
+ m_dialogDescTree->clear();
+
+ EASY_GLOBALS.selected_thread = 0;
+ ::profiler_gui::set_max(EASY_GLOBALS.selected_block);
+ ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id);
+ EASY_GLOBALS.profiler_blocks.clear();
+ EASY_GLOBALS.descriptors.clear();
+ EASY_GLOBALS.gui_blocks.clear();
+
+ m_serializedBlocks.clear();
+ m_serializedDescriptors.clear();
+
+ m_saveAction->setEnabled(false);
+ m_deleteAction->setEnabled(false);
+
+ m_bNetworkFileRegime = false;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::refreshDiagram()
+{
+ static_cast(m_graphicsView->widget())->view()->scene()->update();
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onDeleteClicked(bool)
+{
+ auto button = QMessageBox::question(this, "Clear all profiled data", "All profiled data is going to be deleted!\nContinue?", QMessageBox::Yes, QMessageBox::No);
+ if (button == QMessageBox::Yes)
+ clear();
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onExitClicked(bool)
+{
+ close();
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onEncodingChanged(bool)
+{
+ auto _sender = qobject_cast(sender());
+ auto name = _sender->text();
+ QTextCodec *codec = QTextCodec::codecForName(name.toStdString().c_str());
+ QTextCodec::setCodecForLocale(codec);
+}
+
+void EasyMainWindow::onChronoTextPosChanged(bool)
+{
+ auto _sender = qobject_cast(sender());
+ EASY_GLOBALS.chrono_text_position = static_cast<::profiler_gui::ChronometerTextPosition>(_sender->data().toInt());
+ refreshDiagram();
+}
+
+void EasyMainWindow::onUnitsChanged(bool)
+{
+ auto _sender = qobject_cast(sender());
+ EASY_GLOBALS.time_units = static_cast<::profiler_gui::TimeUnits>(_sender->data().toInt());
+}
+
+void EasyMainWindow::onEventIndicatorsChange(bool _checked)
+{
+ EASY_GLOBALS.enable_event_indicators = _checked;
+ refreshDiagram();
+}
+
+void EasyMainWindow::onEnableDisableStatistics(bool _checked)
+{
+ EASY_GLOBALS.enable_statistics = _checked;
+
+ auto action = qobject_cast(sender());
+ if (action != nullptr)
+ {
+ auto f = action->font();
+ f.setBold(_checked);
+ action->setFont(f);
+
+ if (_checked)
+ {
+ action->setText("Statistics enabled");
+ SET_ICON(action, ":/Stats");
+ }
+ else
+ {
+ action->setText("Statistics disabled");
+ SET_ICON(action, ":/Stats-off");
+ }
+ }
+}
+
+void EasyMainWindow::onCollapseItemsAfterCloseChanged(bool _checked)
+{
+ EASY_GLOBALS.collapse_items_on_tree_close = _checked;
+}
+
+void EasyMainWindow::onAllItemsExpandedByDefaultChange(bool _checked)
+{
+ EASY_GLOBALS.all_items_expanded_by_default = _checked;
+}
+
+void EasyMainWindow::onBindExpandStatusChange(bool _checked)
+{
+ EASY_GLOBALS.bind_scene_and_tree_expand_status = _checked;
+}
+
+void EasyMainWindow::onHierarchyFlagChange(bool _checked)
+{
+ EASY_GLOBALS.only_current_thread_hierarchy = _checked;
+ emit EASY_GLOBALS.events.hierarchyFlagChanged(_checked);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onExpandAllClicked(bool)
+{
+ for (auto& block : EASY_GLOBALS.gui_blocks)
+ block.expanded = true;
+
+ emit EASY_GLOBALS.events.itemsExpandStateChanged();
+
+ auto tree = static_cast(m_treeWidget->widget())->tree();
+ const QSignalBlocker b(tree);
+ tree->expandAll();
+}
+
+void EasyMainWindow::onCollapseAllClicked(bool)
+{
+ for (auto& block : EASY_GLOBALS.gui_blocks)
+ block.expanded = false;
+
+ emit EASY_GLOBALS.events.itemsExpandStateChanged();
+
+ auto tree = static_cast(m_treeWidget->widget())->tree();
+ const QSignalBlocker b(tree);
+ tree->collapseAll();
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onSpacingChange(int _value)
+{
+ EASY_GLOBALS.blocks_spacing = _value;
+ refreshDiagram();
+}
+
+void EasyMainWindow::onMinSizeChange(int _value)
+{
+ EASY_GLOBALS.blocks_size_min = _value;
+ refreshDiagram();
+}
+
+void EasyMainWindow::onNarrowSizeChange(int _value)
+{
+ EASY_GLOBALS.blocks_narrow_size = _value;
+ refreshDiagram();
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onEditBlocksClicked(bool)
+{
+ if (m_descTreeDialog != nullptr)
+ {
+ m_descTreeDialog->raise();
+ return;
+ }
+
+ m_descTreeDialog = new QDialog();
+ m_descTreeDialog->setAttribute(Qt::WA_DeleteOnClose, true);
+ m_descTreeDialog->setWindowTitle("EasyProfiler");
+ m_descTreeDialog->resize(800, 600);
+ connect(m_descTreeDialog, &QDialog::finished, this, &This::onDescTreeDialogClose);
+
+ auto l = new QVBoxLayout(m_descTreeDialog);
+ m_dialogDescTree = new EasyDescWidget(m_descTreeDialog);
+ l->addWidget(m_dialogDescTree);
+ m_descTreeDialog->setLayout(l);
+
+ m_dialogDescTree->build();
+ m_descTreeDialog->show();
+}
+
+void EasyMainWindow::onDescTreeDialogClose(int)
+{
+ disconnect(m_descTreeDialog, &QDialog::finished, this, &This::onDescTreeDialogClose);
+ m_dialogDescTree = nullptr;
+ m_descTreeDialog = nullptr;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::closeEvent(QCloseEvent* close_event)
+{
+ saveSettingsAndGeometry();
+
+ if (m_descTreeDialog != nullptr)
+ {
+ m_descTreeDialog->reject();
+ m_descTreeDialog = nullptr;
+ m_dialogDescTree = nullptr;
+ }
+
+ Parent::closeEvent(close_event);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::loadSettings()
+{
+ QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
+ settings.beginGroup("main");
+
+ auto last_files = settings.value("last_files");
+ if (!last_files.isNull())
+ m_lastFiles = last_files.toStringList();
+
+ auto last_addr = settings.value("ip_address");
+ if (!last_addr.isNull())
+ m_lastAddress = last_addr.toString();
+
+ auto last_port = settings.value("port");
+ if (!last_port.isNull())
+ m_lastPort = (uint16_t)last_port.toUInt();
+
+
+ auto val = settings.value("chrono_text_position");
+ if (!val.isNull())
+ EASY_GLOBALS.chrono_text_position = static_cast<::profiler_gui::ChronometerTextPosition>(val.toInt());
+
+ val = settings.value("time_units");
+ if (!val.isNull())
+ EASY_GLOBALS.time_units = static_cast<::profiler_gui::TimeUnits>(val.toInt());
+
+
+ val = settings.value("frame_time");
+ if (!val.isNull())
+ EASY_GLOBALS.frame_time = val.toFloat();
+
+ val = settings.value("blocks_spacing");
+ if (!val.isNull())
+ EASY_GLOBALS.blocks_spacing = val.toInt();
+
+ val = settings.value("blocks_size_min");
+ if (!val.isNull())
+ EASY_GLOBALS.blocks_size_min = val.toInt();
+
+ val = settings.value("blocks_narrow_size");
+ if (!val.isNull())
+ EASY_GLOBALS.blocks_narrow_size = val.toInt();
+
+
+ auto flag = settings.value("draw_graphics_items_borders");
+ if (!flag.isNull())
+ EASY_GLOBALS.draw_graphics_items_borders = flag.toBool();
+
+ flag = settings.value("hide_narrow_children");
+ if (!flag.isNull())
+ EASY_GLOBALS.hide_narrow_children = flag.toBool();
+
+ flag = settings.value("hide_minsize_blocks");
+ if (!flag.isNull())
+ EASY_GLOBALS.hide_minsize_blocks = flag.toBool();
+
+ flag = settings.value("collapse_items_on_tree_close");
+ if (!flag.isNull())
+ EASY_GLOBALS.collapse_items_on_tree_close = flag.toBool();
+
+ flag = settings.value("all_items_expanded_by_default");
+ if (!flag.isNull())
+ EASY_GLOBALS.all_items_expanded_by_default = flag.toBool();
+
+ flag = settings.value("only_current_thread_hierarchy");
+ if (!flag.isNull())
+ EASY_GLOBALS.only_current_thread_hierarchy = flag.toBool();
+
+ flag = settings.value("enable_zero_length");
+ if (!flag.isNull())
+ EASY_GLOBALS.enable_zero_length = flag.toBool();
+
+ flag = settings.value("add_zero_blocks_to_hierarchy");
+ if (!flag.isNull())
+ EASY_GLOBALS.add_zero_blocks_to_hierarchy = flag.toBool();
+
+
+ flag = settings.value("highlight_blocks_with_same_id");
+ if (!flag.isNull())
+ EASY_GLOBALS.highlight_blocks_with_same_id = flag.toBool();
+
+ flag = settings.value("bind_scene_and_tree_expand_status");
+ if (!flag.isNull())
+ EASY_GLOBALS.bind_scene_and_tree_expand_status = flag.toBool();
+
+ flag = settings.value("selecting_block_changes_thread");
+ if (!flag.isNull())
+ EASY_GLOBALS.selecting_block_changes_thread = flag.toBool();
+
+ flag = settings.value("enable_event_indicators");
+ if (!flag.isNull())
+ EASY_GLOBALS.enable_event_indicators = flag.toBool();
+
+ flag = settings.value("use_decorated_thread_name");
+ if (!flag.isNull())
+ EASY_GLOBALS.use_decorated_thread_name = flag.toBool();
+
+ flag = settings.value("enable_statistics");
+ if (!flag.isNull())
+ EASY_GLOBALS.enable_statistics = flag.toBool();
+
+ QString encoding = settings.value("encoding", "UTF-8").toString();
+ auto default_codec_mib = QTextCodec::codecForName(encoding.toStdString().c_str())->mibEnum();
+ auto default_codec = QTextCodec::codecForMib(default_codec_mib);
+ QTextCodec::setCodecForLocale(default_codec);
+
+ settings.endGroup();
+}
+
+void EasyMainWindow::loadGeometry()
+{
+ QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
+ settings.beginGroup("main");
+
+ auto geometry = settings.value("geometry").toByteArray();
+ if (!geometry.isEmpty())
+ restoreGeometry(geometry);
+
+ auto state = settings.value("windowState").toByteArray();
+ if (!state.isEmpty())
+ restoreState(state);
+
+ settings.endGroup();
+}
+
+void EasyMainWindow::saveSettingsAndGeometry()
+{
+ QSettings settings(::profiler_gui::ORGANAZATION_NAME, ::profiler_gui::APPLICATION_NAME);
+ settings.beginGroup("main");
+
+ settings.setValue("geometry", this->saveGeometry());
+ settings.setValue("windowState", this->saveState());
+ settings.setValue("last_files", m_lastFiles);
+ settings.setValue("ip_address", m_lastAddress);
+ settings.setValue("port", (quint32)m_lastPort);
+ settings.setValue("chrono_text_position", static_cast(EASY_GLOBALS.chrono_text_position));
+ settings.setValue("time_units", static_cast(EASY_GLOBALS.time_units));
+ settings.setValue("frame_time", EASY_GLOBALS.frame_time);
+ settings.setValue("blocks_spacing", EASY_GLOBALS.blocks_spacing);
+ settings.setValue("blocks_size_min", EASY_GLOBALS.blocks_size_min);
+ settings.setValue("blocks_narrow_size", EASY_GLOBALS.blocks_narrow_size);
+ settings.setValue("draw_graphics_items_borders", EASY_GLOBALS.draw_graphics_items_borders);
+ settings.setValue("hide_narrow_children", EASY_GLOBALS.hide_narrow_children);
+ settings.setValue("hide_minsize_blocks", EASY_GLOBALS.hide_minsize_blocks);
+ settings.setValue("collapse_items_on_tree_close", EASY_GLOBALS.collapse_items_on_tree_close);
+ settings.setValue("all_items_expanded_by_default", EASY_GLOBALS.all_items_expanded_by_default);
+ settings.setValue("only_current_thread_hierarchy", EASY_GLOBALS.only_current_thread_hierarchy);
+ settings.setValue("enable_zero_length", EASY_GLOBALS.enable_zero_length);
+ settings.setValue("add_zero_blocks_to_hierarchy", EASY_GLOBALS.add_zero_blocks_to_hierarchy);
+ settings.setValue("highlight_blocks_with_same_id", EASY_GLOBALS.highlight_blocks_with_same_id);
+ settings.setValue("bind_scene_and_tree_expand_status", EASY_GLOBALS.bind_scene_and_tree_expand_status);
+ settings.setValue("selecting_block_changes_thread", EASY_GLOBALS.selecting_block_changes_thread);
+ settings.setValue("enable_event_indicators", EASY_GLOBALS.enable_event_indicators);
+ settings.setValue("use_decorated_thread_name", EASY_GLOBALS.use_decorated_thread_name);
+ settings.setValue("enable_statistics", EASY_GLOBALS.enable_statistics);
+ settings.setValue("encoding", QTextCodec::codecForLocale()->name());
+
+ settings.endGroup();
+}
+
+void EasyMainWindow::setDisconnected(bool _showMessage)
+{
+ if (_showMessage)
+ QMessageBox::warning(this, "Warning", "Application was disconnected", QMessageBox::Close);
+
+ EASY_GLOBALS.connected = false;
+ m_captureAction->setEnabled(false);
+ SET_ICON(m_connectAction, ":/Connection");
+
+ m_eventTracingEnableAction->setEnabled(false);
+ m_eventTracingPriorityAction->setEnabled(false);
+
+ emit EASY_GLOBALS.events.connectionChanged(false);
+
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onListenerTimerTimeout()
+{
+ if (!m_listener.connected())
+ m_listenerDialog->reject();
+}
+
+void EasyMainWindow::onListenerDialogClose(int)
+{
+ m_listenerTimer.stop();
+ disconnect(m_listenerDialog, &QDialog::finished, this, &This::onListenerDialogClose);
+ m_listenerDialog = nullptr;
+
+ switch (m_listener.regime())
+ {
+ case LISTENER_CAPTURE:
+ {
+ m_listenerDialog = new QMessageBox(QMessageBox::Information, "Receiving data...", "This process may take some time.", QMessageBox::NoButton, this);
+ m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true);
+ m_listenerDialog->show();
+
+ m_listener.stopCapture();
+
+ m_listenerDialog->reject();
+ m_listenerDialog = nullptr;
+
+ if (m_listener.size() != 0)
+ {
+ readStream(m_listener.data());
+ m_listener.clearData();
+ }
+
+ break;
+ }
+
+ case LISTENER_DESCRIBE:
+ {
+ break;
+ }
+
+ default:
+ return;
+ }
+
+ if (!m_listener.connected())
+ {
+ setDisconnected();
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onFileReaderTimeout()
+{
+ if (m_reader.done())
+ {
+ auto nblocks = m_reader.size();
+ if (nblocks != 0)
+ {
+ static_cast(m_treeWidget->widget())->clear(true);
+
+ ::profiler::SerializedData serialized_blocks;
+ ::profiler::SerializedData serialized_descriptors;
+ ::profiler::descriptors_list_t descriptors;
+ ::profiler::blocks_t blocks;
+ ::profiler::thread_blocks_tree_t threads_map;
+ QString filename;
+ uint32_t descriptorsNumberInFile = 0;
+ m_reader.get(serialized_blocks, serialized_descriptors, descriptors, blocks, threads_map, descriptorsNumberInFile, filename);
+
+ if (threads_map.size() > 0xff)
+ {
+ if (m_reader.isFile())
+ qWarning() << "Warning: file " << filename << " contains " << threads_map.size() << " threads!";
+ else
+ qWarning() << "Warning: input stream contains " << threads_map.size() << " threads!";
+ qWarning() << "Warning: Currently, maximum number of displayed threads is 255! Some threads will not be displayed.";
+ }
+
+ m_bNetworkFileRegime = !m_reader.isFile();
+ if (!m_bNetworkFileRegime)
+ {
+ auto index = m_lastFiles.indexOf(filename, 0);
+ if (index == -1)
+ {
+ // This file is totally new. Add it to the list.
+ addFileToList(filename);
+ }
+ else if (index != 0)
+ {
+ // This file has been already loaded. Move it to the front.
+ m_lastFiles.move(index, 0);
+ auto fileActions = m_loadActionMenu->actions();
+ auto action = fileActions.at(index);
+ m_loadActionMenu->removeAction(action);
+ m_loadActionMenu->insertAction(fileActions.front(), action);
+ }
+ }
+ m_serializedBlocks = ::std::move(serialized_blocks);
+ m_serializedDescriptors = ::std::move(serialized_descriptors);
+ m_descriptorsNumberInFile = descriptorsNumberInFile;
+ EASY_GLOBALS.selected_thread = 0;
+ ::profiler_gui::set_max(EASY_GLOBALS.selected_block);
+ ::profiler_gui::set_max(EASY_GLOBALS.selected_block_id);
+ EASY_GLOBALS.profiler_blocks.swap(threads_map);
+ EASY_GLOBALS.descriptors.swap(descriptors);
+
+ EASY_GLOBALS.gui_blocks.clear();
+ EASY_GLOBALS.gui_blocks.resize(nblocks);
+ memset(EASY_GLOBALS.gui_blocks.data(), 0, sizeof(::profiler_gui::EasyBlock) * nblocks);
+ for (decltype(nblocks) i = 0; i < nblocks; ++i) {
+ auto& guiblock = EASY_GLOBALS.gui_blocks[i];
+ guiblock.tree = ::std::move(blocks[i]);
+#ifdef EASY_TREE_WIDGET__USE_VECTOR
+ ::profiler_gui::set_max(guiblock.tree_item);
+#endif
+ }
+
+ static_cast(m_graphicsView->widget())->view()->setTree(EASY_GLOBALS.profiler_blocks);
+
+#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0
+ static_cast(m_descTreeWidget->widget())->build();
+#endif
+ if (m_dialogDescTree != nullptr)
+ m_dialogDescTree->build();
+
+ m_saveAction->setEnabled(true);
+ m_deleteAction->setEnabled(true);
+ }
+ else
+ {
+ QMessageBox::warning(this, "Warning", QString("Can not read profiled blocks.\n\nReason:\n%1").arg(m_reader.getError()), QMessageBox::Close);
+
+ if (m_reader.isFile())
+ {
+ auto index = m_lastFiles.indexOf(m_reader.filename(), 0);
+ if (index >= 0)
+ {
+ // Remove unexisting file from list
+ m_lastFiles.removeAt(index);
+ auto action = m_loadActionMenu->actions().at(index);
+ m_loadActionMenu->removeAction(action);
+ delete action;
+ }
+ }
+ }
+
+ m_reader.interrupt();
+
+ m_readerTimer.stop();
+ m_progress->setValue(100);
+ //m_progress->hide();
+
+ if (EASY_GLOBALS.all_items_expanded_by_default)
+ {
+ onExpandAllClicked(true);
+ }
+ }
+ else
+ {
+ m_progress->setValue(m_reader.progress());
+ }
+}
+
+void EasyMainWindow::onFileReaderCancel()
+{
+ m_readerTimer.stop();
+ m_reader.interrupt();
+ m_progress->setValue(100);
+ //m_progress->hide();
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+EasyFileReader::EasyFileReader()
+{
+
+}
+
+EasyFileReader::~EasyFileReader()
+{
+ interrupt();
+}
+
+const bool EasyFileReader::isFile() const
+{
+ return m_isFile;
+}
+
+bool EasyFileReader::done() const
+{
+ return m_bDone.load(::std::memory_order_acquire);
+}
+
+int EasyFileReader::progress() const
+{
+ return m_progress.load(::std::memory_order_acquire);
+}
+
+unsigned int EasyFileReader::size() const
+{
+ return m_size.load(::std::memory_order_acquire);
+}
+
+const QString& EasyFileReader::filename() const
+{
+ return m_filename;
+}
+
+void EasyFileReader::load(const QString& _filename)
+{
+ interrupt();
+
+ m_isFile = true;
+ m_filename = _filename;
+ m_thread = ::std::thread([this](bool _enableStatistics) {
+ m_size.store(fillTreesFromFile(m_progress, m_filename.toStdString().c_str(), m_serializedBlocks, m_serializedDescriptors,
+ m_descriptors, m_blocks, m_blocksTree, m_descriptorsNumberInFile, _enableStatistics, m_errorMessage), ::std::memory_order_release);
+ m_progress.store(100, ::std::memory_order_release);
+ m_bDone.store(true, ::std::memory_order_release);
+ }, EASY_GLOBALS.enable_statistics);
+}
+
+void EasyFileReader::load(::std::stringstream& _stream)
+{
+ interrupt();
+
+ m_isFile = false;
+ m_filename.clear();
+
+#if defined(__GNUC__) && __GNUC__ < 5 && !defined(__llvm__)
+ // gcc 4 has a known bug which has been solved in gcc 5:
+ // std::stringstream has no swap() method :(
+ // have to copy all contents... Use gcc 5 or higher!
+#pragma message "Warning: in gcc 4 and lower std::stringstream has no swap()! Memory consumption may increase! Better use gcc 5 or higher instead."
+ m_stream.str(_stream.str());
+#else
+ m_stream.swap(_stream);
+#endif
+
+ m_thread = ::std::thread([this](bool _enableStatistics) {
+ ::std::ofstream cache_file(NETWORK_CACHE_FILE, ::std::fstream::binary);
+ if (cache_file.is_open()) {
+ cache_file << m_stream.str();
+ cache_file.close();
+ }
+ m_size.store(fillTreesFromStream(m_progress, m_stream, m_serializedBlocks, m_serializedDescriptors, m_descriptors,
+ m_blocks, m_blocksTree, m_descriptorsNumberInFile, _enableStatistics, m_errorMessage), ::std::memory_order_release);
+ m_progress.store(100, ::std::memory_order_release);
+ m_bDone.store(true, ::std::memory_order_release);
+ }, EASY_GLOBALS.enable_statistics);
+}
+
+void EasyFileReader::interrupt()
+{
+ m_progress.store(-100, ::std::memory_order_release);
+ if (m_thread.joinable())
+ m_thread.join();
+
+ m_bDone.store(false, ::std::memory_order_release);
+ m_progress.store(0, ::std::memory_order_release);
+ m_size.store(0, ::std::memory_order_release);
+ m_serializedBlocks.clear();
+ m_serializedDescriptors.clear();
+ m_descriptors.clear();
+ m_blocks.clear();
+ m_blocksTree.clear();
+ m_descriptorsNumberInFile = 0;
+
+ clear_stream(m_stream);
+ clear_stream(m_errorMessage);
+}
+
+void EasyFileReader::get(::profiler::SerializedData& _serializedBlocks, ::profiler::SerializedData& _serializedDescriptors,
+ ::profiler::descriptors_list_t& _descriptors, ::profiler::blocks_t& _blocks,
+ ::profiler::thread_blocks_tree_t& _tree, uint32_t& _descriptorsNumberInFile, QString& _filename)
+{
+ if (done())
+ {
+ m_serializedBlocks.swap(_serializedBlocks);
+ m_serializedDescriptors.swap(_serializedDescriptors);
+ ::profiler::descriptors_list_t(::std::move(m_descriptors)).swap(_descriptors);
+ m_blocks.swap(_blocks);
+ m_blocksTree.swap(_tree);
+ m_filename.swap(_filename);
+ _descriptorsNumberInFile = m_descriptorsNumberInFile;
+ }
+}
+
+QString EasyFileReader::getError()
+{
+ return QString(m_errorMessage.str().c_str());
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onEventTracingPriorityChange(bool _checked)
+{
+ if (EASY_GLOBALS.connected)
+ m_listener.send(profiler::net::BoolMessage(profiler::net::MESSAGE_TYPE_EVENT_TRACING_PRIORITY, _checked));
+}
+
+void EasyMainWindow::onEventTracingEnableChange(bool _checked)
+{
+ if (EASY_GLOBALS.connected)
+ m_listener.send(profiler::net::BoolMessage(profiler::net::MESSAGE_TYPE_EVENT_TRACING_STATUS, _checked));
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onFrameTimeEditFinish()
+{
+ auto text = m_frameTimeEdit->text();
+ if (text.contains(QChar(',')))
+ {
+ text.remove(QChar('.')).replace(QChar(','), QChar('.'));
+ m_frameTimeEdit->setText(text);
+ }
+
+ EASY_GLOBALS.frame_time = text.toFloat() * 1e3f;
+ emit EASY_GLOBALS.events.timelineMarkerChanged();
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onConnectClicked(bool)
+{
+ auto text = m_addressEdit->text();
+// auto parts = text.split(QChar('.'));
+// if (parts.size() != 4)
+// {
+// QMessageBox::warning(this, "Warning", "Invalid IP-Address", QMessageBox::Close);
+//
+// if (EASY_GLOBALS.connected)
+// {
+// // Restore last values
+// m_addressEdit->setText(m_lastAddress);
+// m_portEdit->setText(QString::number(m_lastPort));
+// }
+//
+// return;
+// }
+//
+// for (auto& part : parts)
+// {
+// int i = 0;
+// for (; i < part.size(); ++i)
+// {
+// if (part[i] != QChar('0'))
+// break;
+// }
+//
+// if (i < part.size())
+// part = part.mid(i);
+// else
+// part = "0";
+// }
+
+ QString& address = text;// parts.join(QChar('.'));
+ const decltype(m_lastPort) port = m_portEdit->text().toUShort();
+ //m_addressEdit->setText(address);
+
+ const bool isReconnecting = (EASY_GLOBALS.connected && m_listener.port() == port && address.toStdString() == m_listener.address());
+ if (EASY_GLOBALS.connected)
+ {
+ if (QMessageBox::question(this, isReconnecting ? "Reconnect" : "New connection", QString("Current connection will be broken\n\n%1")
+ .arg(isReconnecting ? "Re-connect?" : "Establish new connection?"),
+ QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
+ {
+ if (!isReconnecting)
+ {
+ // Restore last values
+ m_addressEdit->setText(m_lastAddress);
+ m_portEdit->setText(QString::number(m_lastPort));
+ }
+
+ return;
+ }
+ }
+
+ profiler::net::EasyProfilerStatus reply(false, false, false);
+ if (!m_listener.connect(address.toStdString().c_str(), port, reply))
+ {
+ if (EASY_GLOBALS.connected && !isReconnecting)
+ {
+ if (QMessageBox::warning(this, "Warning", QString("Cannot connect to %1\n\nRestore previous connection?").arg(address),
+ QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes)
+ {
+ if (!m_listener.connect(m_lastAddress.toStdString().c_str(), m_lastPort, reply))
+ {
+ QMessageBox::warning(this, "Warning", "Cannot restore previous connection", QMessageBox::Close);
+ setDisconnected(false);
+ m_lastAddress = ::std::move(address);
+ m_lastPort = port;
+ }
+ else
+ {
+ m_addressEdit->setText(m_lastAddress);
+ m_portEdit->setText(QString::number(m_lastPort));
+ // QMessageBox::information(this, "Information", "Previous connection restored", QMessageBox::Close);
+ }
+ }
+ else
+ {
+ setDisconnected(false);
+ m_lastAddress = ::std::move(address);
+ m_lastPort = port;
+ }
+ }
+ else
+ {
+ QMessageBox::warning(this, "Warning", QString("Cannot connect to %1").arg(address), QMessageBox::Close);
+ if (EASY_GLOBALS.connected)
+ setDisconnected(false);
+
+ if (!isReconnecting)
+ {
+ m_lastAddress = ::std::move(address);
+ m_lastPort = port;
+ }
+ }
+
+ return;
+ }
+
+ m_lastAddress = ::std::move(address);
+ m_lastPort = port;
+
+ qInfo() << "Connected successfully";
+ EASY_GLOBALS.connected = true;
+ m_captureAction->setEnabled(true);
+ SET_ICON(m_connectAction, ":/Connection-on");
+
+ disconnect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange);
+ disconnect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange);
+
+ m_eventTracingEnableAction->setEnabled(true);
+ m_eventTracingPriorityAction->setEnabled(true);
+
+ m_eventTracingEnableAction->setChecked(reply.isEventTracingEnabled);
+ m_eventTracingPriorityAction->setChecked(reply.isLowPriorityEventTracing);
+
+ connect(m_eventTracingEnableAction, &QAction::triggered, this, &This::onEventTracingEnableChange);
+ connect(m_eventTracingPriorityAction, &QAction::triggered, this, &This::onEventTracingPriorityChange);
+
+ emit EASY_GLOBALS.events.connectionChanged(true);
+}
+
+void EasyMainWindow::onCaptureClicked(bool)
+{
+ if (!EASY_GLOBALS.connected)
+ {
+ QMessageBox::warning(this, "Warning", "No connection with profiling app", QMessageBox::Close);
+ return;
+ }
+
+ if (m_listener.regime() != LISTENER_IDLE)
+ {
+ if (m_listener.regime() == LISTENER_CAPTURE)
+ QMessageBox::warning(this, "Warning", "Already capturing frames.\nFinish old capturing session first.", QMessageBox::Close);
+ else
+ QMessageBox::warning(this, "Warning", "Capturing blocks description.\nFinish old capturing session first.", QMessageBox::Close);
+ return;
+ }
+
+ if (!m_listener.startCapture())
+ {
+ // Connection lost. Try to restore connection.
+
+ profiler::net::EasyProfilerStatus reply(false, false, false);
+ if (!m_listener.connect(m_lastAddress.toStdString().c_str(), m_lastPort, reply))
+ {
+ setDisconnected();
+ return;
+ }
+
+ if (!m_listener.startCapture())
+ {
+ setDisconnected();
+ return;
+ }
+ }
+
+ m_listenerTimer.start(250);
+
+ m_listenerDialog = new QMessageBox(QMessageBox::Information, "Capturing frames...", "Close this dialog to stop capturing.", QMessageBox::NoButton, this);
+
+ auto button = new QToolButton(m_listenerDialog);
+ button->setAutoRaise(true);
+ button->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+ button->setIconSize(::profiler_gui::ICONS_SIZE);
+ button->setIcon(QIcon(":/Stop"));
+ button->setText("Stop");
+ m_listenerDialog->addButton(button, QMessageBox::AcceptRole);
+
+ m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true);
+ connect(m_listenerDialog, &QDialog::finished, this, &This::onListenerDialogClose);
+ m_listenerDialog->show();
+}
+
+void EasyMainWindow::onGetBlockDescriptionsClicked(bool)
+{
+ if (!EASY_GLOBALS.connected)
+ {
+ QMessageBox::warning(this, "Warning", "No connection with profiling app", QMessageBox::Close);
+ return;
+ }
+
+ if (m_listener.regime() != LISTENER_IDLE)
+ {
+ if (m_listener.regime() == LISTENER_DESCRIBE)
+ QMessageBox::warning(this, "Warning", "Already capturing blocks description.\nFinish old capturing session first.", QMessageBox::Close);
+ else
+ QMessageBox::warning(this, "Warning", "Capturing capturing frames.\nFinish old capturing session first.", QMessageBox::Close);
+ return;
+ }
+
+ m_listenerDialog = new QMessageBox(QMessageBox::Information, "Waiting for blocks...", "This may take some time.", QMessageBox::NoButton, this);
+ m_listenerDialog->setAttribute(Qt::WA_DeleteOnClose, true);
+ m_listenerDialog->show();
+
+ m_listener.requestBlocksDescription();
+
+ m_listenerDialog->reject();
+ m_listenerDialog = nullptr;
+
+ if (m_listener.size() != 0)
+ {
+ // Read descriptions from stream
+
+ decltype(EASY_GLOBALS.descriptors) descriptors;
+ decltype(m_serializedDescriptors) serializedDescriptors;
+ ::std::stringstream errorMessage;
+ if (readDescriptionsFromStream(m_listener.data(), serializedDescriptors, descriptors, errorMessage))
+ {
+ // Merge old and new descriptions
+
+ bool cancel = false;
+ const bool doFlush = m_descriptorsNumberInFile > descriptors.size();
+ if (doFlush && !m_serializedBlocks.empty())
+ {
+ auto button = QMessageBox::question(this, "Information",
+ QString("New blocks description number = %1\nis less than the old one = %2.\nTo avoid possible conflicts\nall profiled data will be deleted.\nContinue?")
+ .arg(descriptors.size())
+ .arg(m_descriptorsNumberInFile),
+ QMessageBox::Yes, QMessageBox::No);
+
+ if (button == QMessageBox::Yes)
+ clear(); // Clear all contents because new descriptors list conflicts with old one
+ else
+ cancel = true;
+ }
+
+ if (!cancel)
+ {
+ if (!doFlush && m_descriptorsNumberInFile < EASY_GLOBALS.descriptors.size())
+ {
+ // There are dynamically added descriptors, add them to the new list too
+
+ auto newnumber = static_cast(descriptors.size());
+ auto size = static_cast(EASY_GLOBALS.descriptors.size());
+ auto diff = newnumber - size;
+ decltype(newnumber) failnumber = 0;
+
+ descriptors.reserve(descriptors.size() + EASY_GLOBALS.descriptors.size() - m_descriptorsNumberInFile);
+ for (auto i = m_descriptorsNumberInFile; i < size; ++i)
+ {
+ auto id = EASY_GLOBALS.descriptors[i]->id();
+ if (id < newnumber)
+ descriptors.push_back(descriptors[id]);
+ else
+ ++failnumber;
+ }
+
+ if (failnumber != 0)
+ {
+ // There are some errors...
+
+ // revert changes
+ descriptors.resize(newnumber);
+
+ // clear all profiled data to avoid conflicts
+ auto button = QMessageBox::question(this, "Information",
+ "There are errors while merging block descriptions lists.\nTo avoid possible conflicts\nall profiled data will be deleted.\nContinue?",
+ QMessageBox::Yes, QMessageBox::No);
+
+ if (button == QMessageBox::Yes)
+ clear(); // Clear all contents because new descriptors list conflicts with old one
+ else
+ cancel = true;
+ }
+
+ if (!cancel && diff != 0)
+ {
+ for (auto& b : EASY_GLOBALS.gui_blocks)
+ {
+ if (b.tree.node->id() >= m_descriptorsNumberInFile)
+ b.tree.node->setId(b.tree.node->id() + diff);
+ }
+
+ m_descriptorsNumberInFile = newnumber;
+ }
+ }
+
+ if (!cancel)
+ {
+ EASY_GLOBALS.descriptors.swap(descriptors);
+ m_serializedDescriptors.swap(serializedDescriptors);
+ m_descriptorsNumberInFile = static_cast(EASY_GLOBALS.descriptors.size());
+
+ if (m_descTreeDialog != nullptr)
+ {
+#if EASY_GUI_USE_DESCRIPTORS_DOCK_WINDOW != 0
+ static_cast(m_descTreeWidget->widget())->build();
+#endif
+ m_dialogDescTree->build();
+ m_descTreeDialog->raise();
+ }
+ else
+ {
+ onEditBlocksClicked(true);
+ }
+ }
+ }
+ }
+ else
+ {
+ QMessageBox::warning(this, "Warning", QString("Can not read blocks description from stream.\n\nReason:\n%1").arg(errorMessage.str().c_str()), QMessageBox::Close);
+ }
+
+ m_listener.clearData();
+ }
+
+ if (!m_listener.connected())
+ {
+ setDisconnected();
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyMainWindow::onBlockStatusChange(::profiler::block_id_t _id, ::profiler::EasyBlockStatus _status)
+{
+ if (EASY_GLOBALS.connected)
+ m_listener.send(profiler::net::BlockStatusMessage(_id, static_cast(_status)));
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+EasySocketListener::EasySocketListener() : m_receivedSize(0), m_port(0), m_regime(LISTENER_IDLE)
+{
+ m_bInterrupt = ATOMIC_VAR_INIT(false);
+ m_bConnected = ATOMIC_VAR_INIT(false);
+ m_bStopReceive = ATOMIC_VAR_INIT(false);
+}
+
+EasySocketListener::~EasySocketListener()
+{
+ m_bInterrupt.store(true, ::std::memory_order_release);
+ if (m_thread.joinable())
+ m_thread.join();
+}
+
+bool EasySocketListener::connected() const
+{
+ return m_bConnected.load(::std::memory_order_acquire);
+}
+
+EasyListenerRegime EasySocketListener::regime() const
+{
+ return m_regime;
+}
+
+uint64_t EasySocketListener::size() const
+{
+ return m_receivedSize;
+}
+
+::std::stringstream& EasySocketListener::data()
+{
+ return m_receivedData;
+}
+
const ::std::string& EasySocketListener::address() const
{
return m_address;
}
-uint16_t EasySocketListener::port() const
-{
- return m_port;
-}
-
-void EasySocketListener::clearData()
-{
- clear_stream(m_receivedData);
- m_receivedSize = 0;
-}
-
-bool EasySocketListener::connect(const char* _ipaddress, uint16_t _port, profiler::net::EasyProfilerStatus& _reply)
-{
- if (connected())
- {
- m_bInterrupt.store(true, ::std::memory_order_release);
- if (m_thread.joinable())
- m_thread.join();
-
- m_bConnected.store(false, ::std::memory_order_release);
- m_bInterrupt.store(false, ::std::memory_order_release);
- }
-
- m_address.clear();
- m_port = 0;
-
- m_easySocket.flush();
- m_easySocket.init();
- int res = m_easySocket.setAddress(_ipaddress, _port);
- res = m_easySocket.connect();
-
- const bool isConnected = res == 0;
- if (isConnected)
- {
- static const size_t buffer_size = sizeof(profiler::net::EasyProfilerStatus) << 1;
- char buffer[buffer_size] = {};
- int bytes = 0;
-
- while (true)
- {
- bytes = m_easySocket.receive(buffer, buffer_size);
-
- if (bytes == -1)
- {
- if (m_easySocket.isDisconnected())
- return false;
- bytes = 0;
- continue;
- }
-
- break;
- }
-
- if (bytes == 0)
- {
- m_address = _ipaddress;
- m_port = _port;
- m_bConnected.store(isConnected, ::std::memory_order_release);
- return isConnected;
- }
-
- size_t seek = bytes;
- while (seek < sizeof(profiler::net::EasyProfilerStatus))
- {
- bytes = m_easySocket.receive(buffer + seek, buffer_size - seek);
-
- if (bytes == -1)
- {
- if (m_easySocket.isDisconnected())
- return false;
- break;
- }
-
- seek += bytes;
- }
-
- auto message = reinterpret_cast(buffer);
- if (message->isEasyNetMessage() && message->type == profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION)
- _reply = *message;
-
- m_address = _ipaddress;
- m_port = _port;
- }
-
- m_bConnected.store(isConnected, ::std::memory_order_release);
- return isConnected;
-}
-
-bool EasySocketListener::startCapture()
-{
- clearData();
-
- profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE);
- m_easySocket.send(&request, sizeof(request));
-
- if (m_easySocket.isDisconnected()) {
- m_bConnected.store(false, ::std::memory_order_release);
- return false;
- }
-
- m_regime = LISTENER_CAPTURE;
- m_thread = ::std::thread(&EasySocketListener::listenCapture, this);
-
- return true;
-}
-
-void EasySocketListener::stopCapture()
-{
- if (!m_thread.joinable() || m_regime != LISTENER_CAPTURE)
- return;
-
- m_bStopReceive.store(true, ::std::memory_order_release);
-
- //profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE);
- //m_easySocket.send(&request, sizeof(request));
-
- m_thread.join();
-
- if (m_easySocket.isDisconnected())
- m_bConnected.store(false, ::std::memory_order_release);
-
- m_regime = LISTENER_IDLE;
- m_bStopReceive.store(false, ::std::memory_order_release);
-}
-
-void EasySocketListener::requestBlocksDescription()
-{
- clearData();
-
- profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_BLOCKS_DESCRIPTION);
- m_easySocket.send(&request, sizeof(request));
-
- if(m_easySocket.isDisconnected() ){
- m_bConnected.store(false, ::std::memory_order_release);
- }
-
- m_regime = LISTENER_DESCRIBE;
- listenDescription();
- m_regime = LISTENER_IDLE;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasySocketListener::listenCapture()
-{
- // TODO: Merge functions listenCapture() and listenDescription()
-
- static const int buffer_size = 8 * 1024 * 1024;
- char* buffer = new char[buffer_size];
- int seek = 0, bytes = 0;
- auto timeBegin = ::std::chrono::system_clock::now();
-
- bool isListen = true, disconnected = false;
- while (isListen && !m_bInterrupt.load(::std::memory_order_acquire))
- {
- if (m_bStopReceive.load(::std::memory_order_acquire))
- {
- profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE);
- m_easySocket.send(&request, sizeof(request));
- m_bStopReceive.store(false, ::std::memory_order_release);
- }
-
- if ((bytes - seek) == 0)
- {
- bytes = m_easySocket.receive(buffer, buffer_size);
-
- if (bytes == -1)
- {
- if (m_easySocket.isDisconnected())
- {
- m_bConnected.store(false, ::std::memory_order_release);
- isListen = false;
- disconnected = true;
- }
-
- seek = 0;
- bytes = 0;
-
- continue;
- }
-
- seek = 0;
- }
-
- if (bytes == 0)
- {
- isListen = false;
- break;
- }
-
- char* buf = buffer + seek;
-
- if (bytes > 0)
- {
- auto message = reinterpret_cast(buf);
- if (!message->isEasyNetMessage())
- continue;
-
- switch (message->type)
- {
- case profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION:
- {
- qInfo() << "Receive MESSAGE_TYPE_ACCEPTED_CONNECTION";
- //m_easySocket.send(&request, sizeof(request));
- seek += sizeof(profiler::net::Message);
- break;
- }
-
- case profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING:
- {
- qInfo() << "Receive MESSAGE_TYPE_REPLY_START_CAPTURING";
- seek += sizeof(profiler::net::Message);
- break;
- }
-
- case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_END:
- {
- qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS_END";
- seek += sizeof(profiler::net::Message);
-
- const auto dt = ::std::chrono::duration_cast(::std::chrono::system_clock::now() - timeBegin);
- const auto bytesNumber = m_receivedData.str().size();
- qInfo() << "recieved " << bytesNumber << " bytes, " << dt.count() << " ms, average speed = " << double(bytesNumber) * 1e3 / double(dt.count()) / 1024. << " kBytes/sec";
-
- seek = 0;
- bytes = 0;
-
- isListen = false;
-
- break;
- }
-
- case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS:
- {
- qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS";
-
- seek += sizeof(profiler::net::DataMessage);
- profiler::net::DataMessage* dm = (profiler::net::DataMessage*)message;
- timeBegin = std::chrono::system_clock::now();
-
- int neededSize = dm->size;
-
-
- buf = buffer + seek;
- auto bytesNumber = ::std::min((int)dm->size, bytes - seek);
- m_receivedSize += bytesNumber;
- m_receivedData.write(buf, bytesNumber);
- neededSize -= bytesNumber;
-
- if (neededSize == 0)
- seek += bytesNumber;
- else
- {
- seek = 0;
- bytes = 0;
- }
-
-
- int loaded = 0;
- while (neededSize > 0)
- {
- bytes = m_easySocket.receive(buffer, buffer_size);
-
- if (bytes == -1)
- {
- if (m_easySocket.isDisconnected())
- {
- m_bConnected.store(false, ::std::memory_order_release);
- isListen = false;
- disconnected = true;
- neededSize = 0;
- }
-
- break;
- }
-
- buf = buffer;
- int toWrite = ::std::min(bytes, neededSize);
- m_receivedSize += toWrite;
- m_receivedData.write(buf, toWrite);
- neededSize -= toWrite;
- loaded += toWrite;
- seek = toWrite;
- }
-
- if (m_bStopReceive.load(::std::memory_order_acquire))
- {
- profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE);
- m_easySocket.send(&request, sizeof(request));
- m_bStopReceive.store(false, ::std::memory_order_release);
- }
-
- break;
- }
-
- default:
- //qInfo() << "Receive unknown " << message->type;
- break;
- }
- }
- }
-
- if (disconnected)
- clearData();
-
- delete [] buffer;
-}
-
-void EasySocketListener::listenDescription()
-{
- // TODO: Merge functions listenDescription() and listenCapture()
-
- static const int buffer_size = 8 * 1024 * 1024;
- char* buffer = new char[buffer_size];
- int seek = 0, bytes = 0;
-
- bool isListen = true, disconnected = false;
- while (isListen && !m_bInterrupt.load(::std::memory_order_acquire))
- {
- if ((bytes - seek) == 0)
- {
- bytes = m_easySocket.receive(buffer, buffer_size);
-
- if (bytes == -1)
- {
- if (m_easySocket.isDisconnected())
- {
- m_bConnected.store(false, ::std::memory_order_release);
- isListen = false;
- disconnected = true;
- }
-
- seek = 0;
- bytes = 0;
-
- continue;
- }
-
- seek = 0;
- }
-
- if (bytes == 0)
- {
- isListen = false;
- break;
- }
-
- char* buf = buffer + seek;
-
- if (bytes > 0)
- {
- auto message = reinterpret_cast(buf);
- if (!message->isEasyNetMessage())
- continue;
-
- switch (message->type)
- {
- case profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION:
- {
- qInfo() << "Receive MESSAGE_TYPE_ACCEPTED_CONNECTION";
- seek += sizeof(profiler::net::Message);
- break;
- }
-
- case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION_END:
- {
- qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION_END";
- seek += sizeof(profiler::net::Message);
-
- seek = 0;
- bytes = 0;
-
- isListen = false;
-
- break;
- }
-
- case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION:
- {
- qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS";
-
- seek += sizeof(profiler::net::DataMessage);
- profiler::net::DataMessage* dm = (profiler::net::DataMessage*)message;
- int neededSize = dm->size;
-
- buf = buffer + seek;
- auto bytesNumber = ::std::min((int)dm->size, bytes - seek);
- m_receivedSize += bytesNumber;
- m_receivedData.write(buf, bytesNumber);
- neededSize -= bytesNumber;
-
- if (neededSize == 0)
- seek += bytesNumber;
- else{
- seek = 0;
- bytes = 0;
- }
-
- int loaded = 0;
- while (neededSize > 0)
- {
- bytes = m_easySocket.receive(buffer, buffer_size);
-
- if (bytes == -1)
- {
- if (m_easySocket.isDisconnected())
- {
- m_bConnected.store(false, ::std::memory_order_release);
- isListen = false;
- disconnected = true;
- neededSize = 0;
- }
-
- break;
- }
-
- buf = buffer;
- int toWrite = ::std::min(bytes, neededSize);
- m_receivedSize += toWrite;
- m_receivedData.write(buf, toWrite);
- neededSize -= toWrite;
- loaded += toWrite;
- seek = toWrite;
- }
-
- break;
- }
-
- default:
- break;
- }
- }
- }
-
- if (disconnected)
- clearData();
-
- delete[] buffer;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
+uint16_t EasySocketListener::port() const
+{
+ return m_port;
+}
+
+void EasySocketListener::clearData()
+{
+ clear_stream(m_receivedData);
+ m_receivedSize = 0;
+}
+
+bool EasySocketListener::connect(const char* _ipaddress, uint16_t _port, profiler::net::EasyProfilerStatus& _reply)
+{
+ if (connected())
+ {
+ m_bInterrupt.store(true, ::std::memory_order_release);
+ if (m_thread.joinable())
+ m_thread.join();
+
+ m_bConnected.store(false, ::std::memory_order_release);
+ m_bInterrupt.store(false, ::std::memory_order_release);
+ }
+
+ m_address.clear();
+ m_port = 0;
+
+ m_easySocket.flush();
+ m_easySocket.init();
+ int res = m_easySocket.setAddress(_ipaddress, _port);
+ res = m_easySocket.connect();
+
+ const bool isConnected = res == 0;
+ if (isConnected)
+ {
+ static const size_t buffer_size = sizeof(profiler::net::EasyProfilerStatus) << 1;
+ char buffer[buffer_size] = {};
+ int bytes = 0;
+
+ while (true)
+ {
+ bytes = m_easySocket.receive(buffer, buffer_size);
+
+ if (bytes == -1)
+ {
+ if (m_easySocket.isDisconnected())
+ return false;
+ bytes = 0;
+ continue;
+ }
+
+ break;
+ }
+
+ if (bytes == 0)
+ {
+ m_address = _ipaddress;
+ m_port = _port;
+ m_bConnected.store(isConnected, ::std::memory_order_release);
+ return isConnected;
+ }
+
+ size_t seek = bytes;
+ while (seek < sizeof(profiler::net::EasyProfilerStatus))
+ {
+ bytes = m_easySocket.receive(buffer + seek, buffer_size - seek);
+
+ if (bytes == -1)
+ {
+ if (m_easySocket.isDisconnected())
+ return false;
+ break;
+ }
+
+ seek += bytes;
+ }
+
+ auto message = reinterpret_cast(buffer);
+ if (message->isEasyNetMessage() && message->type == profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION)
+ _reply = *message;
+
+ m_address = _ipaddress;
+ m_port = _port;
+ }
+
+ m_bConnected.store(isConnected, ::std::memory_order_release);
+ return isConnected;
+}
+
+bool EasySocketListener::startCapture()
+{
+ clearData();
+
+ profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_START_CAPTURE);
+ m_easySocket.send(&request, sizeof(request));
+
+ if (m_easySocket.isDisconnected()) {
+ m_bConnected.store(false, ::std::memory_order_release);
+ return false;
+ }
+
+ m_regime = LISTENER_CAPTURE;
+ m_thread = ::std::thread(&EasySocketListener::listenCapture, this);
+
+ return true;
+}
+
+void EasySocketListener::stopCapture()
+{
+ if (!m_thread.joinable() || m_regime != LISTENER_CAPTURE)
+ return;
+
+ m_bStopReceive.store(true, ::std::memory_order_release);
+
+ //profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE);
+ //m_easySocket.send(&request, sizeof(request));
+
+ m_thread.join();
+
+ if (m_easySocket.isDisconnected())
+ m_bConnected.store(false, ::std::memory_order_release);
+
+ m_regime = LISTENER_IDLE;
+ m_bStopReceive.store(false, ::std::memory_order_release);
+}
+
+void EasySocketListener::requestBlocksDescription()
+{
+ clearData();
+
+ profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_BLOCKS_DESCRIPTION);
+ m_easySocket.send(&request, sizeof(request));
+
+ if(m_easySocket.isDisconnected() ){
+ m_bConnected.store(false, ::std::memory_order_release);
+ }
+
+ m_regime = LISTENER_DESCRIBE;
+ listenDescription();
+ m_regime = LISTENER_IDLE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasySocketListener::listenCapture()
+{
+ // TODO: Merge functions listenCapture() and listenDescription()
+
+ static const int buffer_size = 8 * 1024 * 1024;
+ char* buffer = new char[buffer_size];
+ int seek = 0, bytes = 0;
+ auto timeBegin = ::std::chrono::system_clock::now();
+
+ bool isListen = true, disconnected = false;
+ while (isListen && !m_bInterrupt.load(::std::memory_order_acquire))
+ {
+ if (m_bStopReceive.load(::std::memory_order_acquire))
+ {
+ profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE);
+ m_easySocket.send(&request, sizeof(request));
+ m_bStopReceive.store(false, ::std::memory_order_release);
+ }
+
+ if ((bytes - seek) == 0)
+ {
+ bytes = m_easySocket.receive(buffer, buffer_size);
+
+ if (bytes == -1)
+ {
+ if (m_easySocket.isDisconnected())
+ {
+ m_bConnected.store(false, ::std::memory_order_release);
+ isListen = false;
+ disconnected = true;
+ }
+
+ seek = 0;
+ bytes = 0;
+
+ continue;
+ }
+
+ seek = 0;
+ }
+
+ if (bytes == 0)
+ {
+ isListen = false;
+ break;
+ }
+
+ char* buf = buffer + seek;
+
+ if (bytes > 0)
+ {
+ auto message = reinterpret_cast(buf);
+ if (!message->isEasyNetMessage())
+ continue;
+
+ switch (message->type)
+ {
+ case profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION:
+ {
+ qInfo() << "Receive MESSAGE_TYPE_ACCEPTED_CONNECTION";
+ //m_easySocket.send(&request, sizeof(request));
+ seek += sizeof(profiler::net::Message);
+ break;
+ }
+
+ case profiler::net::MESSAGE_TYPE_REPLY_START_CAPTURING:
+ {
+ qInfo() << "Receive MESSAGE_TYPE_REPLY_START_CAPTURING";
+ seek += sizeof(profiler::net::Message);
+ break;
+ }
+
+ case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_END:
+ {
+ qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS_END";
+ seek += sizeof(profiler::net::Message);
+
+ const auto dt = ::std::chrono::duration_cast(::std::chrono::system_clock::now() - timeBegin);
+ const auto bytesNumber = m_receivedData.str().size();
+ qInfo() << "recieved " << bytesNumber << " bytes, " << dt.count() << " ms, average speed = " << double(bytesNumber) * 1e3 / double(dt.count()) / 1024. << " kBytes/sec";
+
+ seek = 0;
+ bytes = 0;
+
+ isListen = false;
+
+ break;
+ }
+
+ case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS:
+ {
+ qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS";
+
+ seek += sizeof(profiler::net::DataMessage);
+ profiler::net::DataMessage* dm = (profiler::net::DataMessage*)message;
+ timeBegin = std::chrono::system_clock::now();
+
+ int neededSize = dm->size;
+
+
+ buf = buffer + seek;
+ auto bytesNumber = ::std::min((int)dm->size, bytes - seek);
+ m_receivedSize += bytesNumber;
+ m_receivedData.write(buf, bytesNumber);
+ neededSize -= bytesNumber;
+
+ if (neededSize == 0)
+ seek += bytesNumber;
+ else
+ {
+ seek = 0;
+ bytes = 0;
+ }
+
+
+ int loaded = 0;
+ while (neededSize > 0)
+ {
+ bytes = m_easySocket.receive(buffer, buffer_size);
+
+ if (bytes == -1)
+ {
+ if (m_easySocket.isDisconnected())
+ {
+ m_bConnected.store(false, ::std::memory_order_release);
+ isListen = false;
+ disconnected = true;
+ neededSize = 0;
+ }
+
+ break;
+ }
+
+ buf = buffer;
+ int toWrite = ::std::min(bytes, neededSize);
+ m_receivedSize += toWrite;
+ m_receivedData.write(buf, toWrite);
+ neededSize -= toWrite;
+ loaded += toWrite;
+ seek = toWrite;
+ }
+
+ if (m_bStopReceive.load(::std::memory_order_acquire))
+ {
+ profiler::net::Message request(profiler::net::MESSAGE_TYPE_REQUEST_STOP_CAPTURE);
+ m_easySocket.send(&request, sizeof(request));
+ m_bStopReceive.store(false, ::std::memory_order_release);
+ }
+
+ break;
+ }
+
+ default:
+ //qInfo() << "Receive unknown " << message->type;
+ break;
+ }
+ }
+ }
+
+ if (disconnected)
+ clearData();
+
+ delete [] buffer;
+}
+
+void EasySocketListener::listenDescription()
+{
+ // TODO: Merge functions listenDescription() and listenCapture()
+
+ static const int buffer_size = 8 * 1024 * 1024;
+ char* buffer = new char[buffer_size];
+ int seek = 0, bytes = 0;
+
+ bool isListen = true, disconnected = false;
+ while (isListen && !m_bInterrupt.load(::std::memory_order_acquire))
+ {
+ if ((bytes - seek) == 0)
+ {
+ bytes = m_easySocket.receive(buffer, buffer_size);
+
+ if (bytes == -1)
+ {
+ if (m_easySocket.isDisconnected())
+ {
+ m_bConnected.store(false, ::std::memory_order_release);
+ isListen = false;
+ disconnected = true;
+ }
+
+ seek = 0;
+ bytes = 0;
+
+ continue;
+ }
+
+ seek = 0;
+ }
+
+ if (bytes == 0)
+ {
+ isListen = false;
+ break;
+ }
+
+ char* buf = buffer + seek;
+
+ if (bytes > 0)
+ {
+ auto message = reinterpret_cast(buf);
+ if (!message->isEasyNetMessage())
+ continue;
+
+ switch (message->type)
+ {
+ case profiler::net::MESSAGE_TYPE_ACCEPTED_CONNECTION:
+ {
+ qInfo() << "Receive MESSAGE_TYPE_ACCEPTED_CONNECTION";
+ seek += sizeof(profiler::net::Message);
+ break;
+ }
+
+ case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION_END:
+ {
+ qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION_END";
+ seek += sizeof(profiler::net::Message);
+
+ seek = 0;
+ bytes = 0;
+
+ isListen = false;
+
+ break;
+ }
+
+ case profiler::net::MESSAGE_TYPE_REPLY_BLOCKS_DESCRIPTION:
+ {
+ qInfo() << "Receive MESSAGE_TYPE_REPLY_BLOCKS";
+
+ seek += sizeof(profiler::net::DataMessage);
+ profiler::net::DataMessage* dm = (profiler::net::DataMessage*)message;
+ int neededSize = dm->size;
+
+ buf = buffer + seek;
+ auto bytesNumber = ::std::min((int)dm->size, bytes - seek);
+ m_receivedSize += bytesNumber;
+ m_receivedData.write(buf, bytesNumber);
+ neededSize -= bytesNumber;
+
+ if (neededSize == 0)
+ seek += bytesNumber;
+ else{
+ seek = 0;
+ bytes = 0;
+ }
+
+ int loaded = 0;
+ while (neededSize > 0)
+ {
+ bytes = m_easySocket.receive(buffer, buffer_size);
+
+ if (bytes == -1)
+ {
+ if (m_easySocket.isDisconnected())
+ {
+ m_bConnected.store(false, ::std::memory_order_release);
+ isListen = false;
+ disconnected = true;
+ neededSize = 0;
+ }
+
+ break;
+ }
+
+ buf = buffer;
+ int toWrite = ::std::min(bytes, neededSize);
+ m_receivedSize += toWrite;
+ m_receivedData.write(buf, toWrite);
+ neededSize -= toWrite;
+ loaded += toWrite;
+ seek = toWrite;
+ }
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+
+ if (disconnected)
+ clearData();
+
+ delete[] buffer;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
diff --git a/profiler_gui/resources.qrc b/profiler_gui/resources.qrc
index 1b5c988..ece37d3 100644
--- a/profiler_gui/resources.qrc
+++ b/profiler_gui/resources.qrc
@@ -1,29 +1,29 @@
-
-
- icons/logo.svg
- icons/off.svg
- icons/open-folder2.svg
- icons/reload-folder2.svg
- icons/reload.svg
- icons/expand.svg
- icons/collapse.svg
- icons/colors.svg
- icons/colors-black.svg
- icons/save.svg
- icons/statistics.svg
- icons/statistics2.svg
- icons/lan.svg
- icons/lan_on.svg
- icons/wifi.svg
- icons/wifi_on.svg
- icons/lan.svg
- icons/lan_on.svg
- icons/play.svg
- icons/stop.svg
- icons/delete.svg
- icons/list.svg
- icons/search-next.svg
- icons/search-prev.svg
- icons/settings.svg
-
-
+
+
+ icons/logo.svg
+ icons/off.svg
+ icons/open-folder2.svg
+ icons/reload-folder2.svg
+ icons/reload.svg
+ icons/expand.svg
+ icons/collapse.svg
+ icons/colors.svg
+ icons/colors-black.svg
+ icons/save.svg
+ icons/statistics.svg
+ icons/statistics2.svg
+ icons/lan.svg
+ icons/lan_on.svg
+ icons/wifi.svg
+ icons/wifi_on.svg
+ icons/lan.svg
+ icons/lan_on.svg
+ icons/play.svg
+ icons/stop.svg
+ icons/delete.svg
+ icons/list.svg
+ icons/search-next.svg
+ icons/search-prev.svg
+ icons/settings.svg
+
+
diff --git a/profiler_gui/tree_widget_loader.cpp b/profiler_gui/tree_widget_loader.cpp
index deb2852..774db24 100644
--- a/profiler_gui/tree_widget_loader.cpp
+++ b/profiler_gui/tree_widget_loader.cpp
@@ -1,317 +1,317 @@
-/************************************************************************
-* file name : tree_widget_loader.h
-* ----------------- :
-* creation time : 2016/08/18
-* author : Victor Zarubkin
-* email : v.s.zarubkin@gmail.com
-* ----------------- :
-* description : The file contains implementation of EasyTreeWidgetLoader which aim is
-* : to load EasyProfiler blocks hierarchy in separate thread.
-* ----------------- :
-* change log : * 2016/08/18 Victor Zarubkin: moved sources from blocks_tree_widget.h/.cpp
-* : and renamed Prof* to Easy*.
-* :
-* : *
-* ----------------- :
-* license : Lightweight profiler library for c++
-* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
-* :
-* :
-* : Licensed under the Apache License, Version 2.0 (the "License");
-* : you may not use this file except in compliance with the License.
-* : You may obtain a copy of the License at
-* :
-* : http://www.apache.org/licenses/LICENSE-2.0
-* :
-* : Unless required by applicable law or agreed to in writing, software
-* : distributed under the License is distributed on an "AS IS" BASIS,
-* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* : See the License for the specific language governing permissions and
-* : limitations under the License.
-* :
-* :
-* : GNU General Public License Usage
-* : Alternatively, this file may be used under the terms of the GNU
-* : General Public License as published by the Free Software Foundation,
-* : either version 3 of the License, or (at your option) any later version.
-* :
-* : This program is distributed in the hope that it will be useful,
-* : but WITHOUT ANY WARRANTY; without even the implied warranty of
-* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
-* : GNU General Public License for more details.
-* :
-* : You should have received a copy of the GNU General Public License
-* : along with this program.If not, see .
-************************************************************************/
-
-#include "tree_widget_loader.h"
-#include "tree_widget_item.h"
-#include "globals.h"
-
-#ifdef _WIN32
-#include
-#endif
-
-#ifdef max
-#undef max
-#endif
-
-#ifdef min
-#undef min
-#endif
-
-//////////////////////////////////////////////////////////////////////////
-
-EasyTreeWidgetLoader::EasyTreeWidgetLoader()
- : m_bDone(ATOMIC_VAR_INIT(false))
- , m_bInterrupt(ATOMIC_VAR_INIT(false))
- , m_progress(ATOMIC_VAR_INIT(0))
- , m_mode(EasyTreeMode_Full)
-{
-}
-
-EasyTreeWidgetLoader::~EasyTreeWidgetLoader()
-{
- interrupt(true);
-}
-
-bool EasyTreeWidgetLoader::done() const
-{
- return m_bDone.load();
-}
-
-void EasyTreeWidgetLoader::setDone()
-{
- m_bDone.store(true);
- //m_progress.store(100);
-}
-
-void EasyTreeWidgetLoader::setProgress(int _progress)
-{
- m_progress.store(_progress);
-}
-
-bool EasyTreeWidgetLoader::interrupted() const
-{
- return m_bInterrupt.load();
-}
-
-int EasyTreeWidgetLoader::progress() const
-{
- return m_progress.load();
-}
-
-void EasyTreeWidgetLoader::takeTopLevelItems(ThreadedItems& _output)
-{
- if (done())
- {
- _output = ::std::move(m_topLevelItems);
- m_topLevelItems.clear();
- }
-}
-
-void EasyTreeWidgetLoader::takeItems(Items& _output)
-{
- if (done())
- {
- _output = ::std::move(m_items);
- m_items.clear();
- }
-}
-
-void EasyTreeWidgetLoader::interrupt(bool _wait)
-{
- m_bInterrupt.store(true);
- if (m_thread.joinable())
- m_thread.join();
-
- m_bInterrupt.store(false);
- m_bDone.store(false);
- m_progress.store(0);
-
- if (!_wait)
- {
- auto deleter_thread = ::std::thread([](decltype(m_topLevelItems) _items) {
- for (auto item : _items)
- delete item.second;
- }, ::std::move(m_topLevelItems));
-
-#ifdef _WIN32
- SetThreadPriority(deleter_thread.native_handle(), THREAD_PRIORITY_LOWEST);
-#endif
-
- deleter_thread.detach();
- }
- else
- {
- for (auto item : m_topLevelItems)
- delete item.second;
- }
-
- m_items.clear();
- m_topLevelItems.clear();
- m_iditems.clear();
-}
-
-void EasyTreeWidgetLoader::fillTree(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, bool _colorizeRows, EasyTreeMode _mode)
-{
- interrupt();
- m_mode = _mode;
- m_thread = ::std::thread(&EasyTreeWidgetLoader::setTreeInternal1, this,
- ::std::ref(_beginTime), _blocksNumber, ::std::ref(_blocksTree), _colorizeRows,
- EASY_GLOBALS.add_zero_blocks_to_hierarchy, EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.time_units);
-}
-
-void EasyTreeWidgetLoader::fillTreeBlocks(const::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _beginTime, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, bool _colorizeRows, EasyTreeMode _mode)
-{
- interrupt();
- m_mode = _mode;
- m_thread = ::std::thread(&EasyTreeWidgetLoader::setTreeInternal2, this,
- _beginTime, ::std::ref(_blocks), _left, _right, _strict, _colorizeRows,
- EASY_GLOBALS.add_zero_blocks_to_hierarchy, EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.time_units);
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-void EasyTreeWidgetLoader::setTreeInternal1(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, bool _colorizeRows, bool _addZeroBlocks, bool _decoratedThreadNames, ::profiler_gui::TimeUnits _units)
-{
- m_items.reserve(_blocksNumber + _blocksTree.size()); // _blocksNumber does not include Thread root blocks
-
- ::profiler::timestamp_t finishtime = 0;
- for (const auto& threadTree : _blocksTree)
- {
- const auto node_block = blocksTree(threadTree.second.children.front()).node;
- const auto startTime = node_block->begin();
- const auto endTime = node_block->end();
-
- if (_beginTime > startTime)
- _beginTime = startTime;
-
- if (finishtime < endTime)
- finishtime = endTime;
- }
-
- //const QSignalBlocker b(this);
- const auto u_thread = ::profiler_gui::toUnicode("thread");
- int i = 0;
- const int total = static_cast(_blocksTree.size());
- for (const auto& threadTree : _blocksTree)
- {
- if (interrupted())
- break;
-
- const auto& root = threadTree.second;
- auto item = new EasyTreeWidgetItem();
- item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, root, u_thread));
-
- ::profiler::timestamp_t duration = 0;
- if (!root.children.empty())
- duration = blocksTree(root.children.back()).node->end() - blocksTree(root.children.front()).node->begin();
-
- item->setTimeSmart(COL_DURATION, _units, duration);
- item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);
- item->setTextColor(::profiler_gui::SELECTED_THREAD_FOREGROUND);
-
- //_items.push_back(item);
-
- item->setTimeSmart(COL_SELF_DURATION, _units, root.profiled_time);
-
- ::profiler::timestamp_t children_duration = 0;
- const auto children_items_number = setTreeInternal(root, 0, _beginTime, root.children, item, nullptr, _beginTime, finishtime + 1000000000ULL, false, children_duration, _colorizeRows, _addZeroBlocks, _units);
-
- if (children_items_number > 0)
- {
- //total_items += children_items_number + 1;
- //addTopLevelItem(item);
- //m_roots[threadTree.first] = item;
- m_topLevelItems.emplace_back(root.thread_id, item);
- }
- else
- {
- //_items.pop_back();
- delete item;
- }
-
- setProgress((100 * ++i) / total);
- }
-
- setDone();
- //return total_items;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-// auto calculateTotalChildrenNumber(const ::profiler::BlocksTree& _tree) -> decltype(_tree.children.size())
-// {
-// auto children_number = _tree.children.size();
-// for (auto i : _tree.children)
-// children_number += calculateTotalChildrenNumber(blocksTree(i));
-// return children_number;
-// }
-
-typedef ::std::unordered_map<::profiler::thread_id_t, ::profiler::block_index_t, ::profiler_gui::do_no_hash<::profiler::thread_id_t>::hasher_t> BeginEndIndicesMap;
-
-void EasyTreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _beginTime, const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, bool _colorizeRows, bool _addZeroBlocks, bool _decoratedThreadNames, ::profiler_gui::TimeUnits _units)
-{
- //size_t blocksNumber = 0;
- //for (const auto& block : _blocks)
- // blocksNumber += calculateTotalChildrenNumber(*block.tree);
- // //blocksNumber += block.tree->total_children_number;
- //m_items.reserve(blocksNumber + _blocks.size()); // blocksNumber does not include root blocks
-
- BeginEndIndicesMap beginEndMap;
- RootsMap threadsMap;
-
- auto const setTree = (m_mode == EasyTreeMode_Full) ? &EasyTreeWidgetLoader::setTreeInternal : &EasyTreeWidgetLoader::setTreeInternalPlain;
-
- const auto u_thread = ::profiler_gui::toUnicode("thread");
- int i = 0, total = static_cast(_blocks.size());
- //const QSignalBlocker b(this);
- for (const auto& block : _blocks)
- {
- if (interrupted())
- break;
-
- auto& gui_block = easyBlock(block.tree);
- const auto startTime = gui_block.tree.node->begin();
- const auto endTime = gui_block.tree.node->end();
- if (startTime > _right || endTime < _left)
- {
- setProgress((90 * ++i) / total);
- continue;
- }
-
- ::profiler::timestamp_t duration = 0;
- EasyTreeWidgetItem* thread_item = nullptr;
- ::profiler::block_index_t& firstCswitch = beginEndMap[block.root->thread_id];
- auto thread_item_it = threadsMap.find(block.root->thread_id);
- if (thread_item_it != threadsMap.end())
- {
- thread_item = thread_item_it->second;
- }
- else
- {
- thread_item = new EasyTreeWidgetItem();
- thread_item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, *block.root, u_thread));
-
- if (!block.root->children.empty())
- duration = blocksTree(block.root->children.back()).node->end() - blocksTree(block.root->children.front()).node->begin();
-
- thread_item->setTimeSmart(COL_DURATION, _units, duration);
- thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);
- thread_item->setTextColor(::profiler_gui::SELECTED_THREAD_FOREGROUND);
-
- // Sum of all children durations:
- thread_item->setTimeSmart(COL_SELF_DURATION, _units, block.root->profiled_time);
-
- threadsMap.insert(::std::make_pair(block.root->thread_id, thread_item));
-
- firstCswitch = 0;
- auto it = ::std::lower_bound(block.root->sync.begin(), block.root->sync.end(), _left, [](::profiler::block_index_t ind, decltype(_left) _val)
- {
- return EASY_GLOBALS.gui_blocks[ind].tree.node->begin() < _val;
- });
-
+/************************************************************************
+* file name : tree_widget_loader.h
+* ----------------- :
+* creation time : 2016/08/18
+* author : Victor Zarubkin
+* email : v.s.zarubkin@gmail.com
+* ----------------- :
+* description : The file contains implementation of EasyTreeWidgetLoader which aim is
+* : to load EasyProfiler blocks hierarchy in separate thread.
+* ----------------- :
+* change log : * 2016/08/18 Victor Zarubkin: moved sources from blocks_tree_widget.h/.cpp
+* : and renamed Prof* to Easy*.
+* :
+* : *
+* ----------------- :
+* license : Lightweight profiler library for c++
+* : Copyright(C) 2016 Sergey Yagovtsev, Victor Zarubkin
+* :
+* :
+* : Licensed under the Apache License, Version 2.0 (the "License");
+* : you may not use this file except in compliance with the License.
+* : You may obtain a copy of the License at
+* :
+* : http://www.apache.org/licenses/LICENSE-2.0
+* :
+* : Unless required by applicable law or agreed to in writing, software
+* : distributed under the License is distributed on an "AS IS" BASIS,
+* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* : See the License for the specific language governing permissions and
+* : limitations under the License.
+* :
+* :
+* : GNU General Public License Usage
+* : Alternatively, this file may be used under the terms of the GNU
+* : General Public License as published by the Free Software Foundation,
+* : either version 3 of the License, or (at your option) any later version.
+* :
+* : This program is distributed in the hope that it will be useful,
+* : but WITHOUT ANY WARRANTY; without even the implied warranty of
+* : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
+* : GNU General Public License for more details.
+* :
+* : You should have received a copy of the GNU General Public License
+* : along with this program.If not, see .
+************************************************************************/
+
+#include "tree_widget_loader.h"
+#include "tree_widget_item.h"
+#include "globals.h"
+
+#ifdef _WIN32
+#include
+#endif
+
+#ifdef max
+#undef max
+#endif
+
+#ifdef min
+#undef min
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+
+EasyTreeWidgetLoader::EasyTreeWidgetLoader()
+ : m_bDone(ATOMIC_VAR_INIT(false))
+ , m_bInterrupt(ATOMIC_VAR_INIT(false))
+ , m_progress(ATOMIC_VAR_INIT(0))
+ , m_mode(EasyTreeMode_Full)
+{
+}
+
+EasyTreeWidgetLoader::~EasyTreeWidgetLoader()
+{
+ interrupt(true);
+}
+
+bool EasyTreeWidgetLoader::done() const
+{
+ return m_bDone.load();
+}
+
+void EasyTreeWidgetLoader::setDone()
+{
+ m_bDone.store(true);
+ //m_progress.store(100);
+}
+
+void EasyTreeWidgetLoader::setProgress(int _progress)
+{
+ m_progress.store(_progress);
+}
+
+bool EasyTreeWidgetLoader::interrupted() const
+{
+ return m_bInterrupt.load();
+}
+
+int EasyTreeWidgetLoader::progress() const
+{
+ return m_progress.load();
+}
+
+void EasyTreeWidgetLoader::takeTopLevelItems(ThreadedItems& _output)
+{
+ if (done())
+ {
+ _output = ::std::move(m_topLevelItems);
+ m_topLevelItems.clear();
+ }
+}
+
+void EasyTreeWidgetLoader::takeItems(Items& _output)
+{
+ if (done())
+ {
+ _output = ::std::move(m_items);
+ m_items.clear();
+ }
+}
+
+void EasyTreeWidgetLoader::interrupt(bool _wait)
+{
+ m_bInterrupt.store(true);
+ if (m_thread.joinable())
+ m_thread.join();
+
+ m_bInterrupt.store(false);
+ m_bDone.store(false);
+ m_progress.store(0);
+
+ if (!_wait)
+ {
+ auto deleter_thread = ::std::thread([](decltype(m_topLevelItems) _items) {
+ for (auto item : _items)
+ delete item.second;
+ }, ::std::move(m_topLevelItems));
+
+#ifdef _WIN32
+ SetThreadPriority(deleter_thread.native_handle(), THREAD_PRIORITY_LOWEST);
+#endif
+
+ deleter_thread.detach();
+ }
+ else
+ {
+ for (auto item : m_topLevelItems)
+ delete item.second;
+ }
+
+ m_items.clear();
+ m_topLevelItems.clear();
+ m_iditems.clear();
+}
+
+void EasyTreeWidgetLoader::fillTree(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, bool _colorizeRows, EasyTreeMode _mode)
+{
+ interrupt();
+ m_mode = _mode;
+ m_thread = ::std::thread(&EasyTreeWidgetLoader::setTreeInternal1, this,
+ ::std::ref(_beginTime), _blocksNumber, ::std::ref(_blocksTree), _colorizeRows,
+ EASY_GLOBALS.add_zero_blocks_to_hierarchy, EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.time_units);
+}
+
+void EasyTreeWidgetLoader::fillTreeBlocks(const::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _beginTime, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, bool _colorizeRows, EasyTreeMode _mode)
+{
+ interrupt();
+ m_mode = _mode;
+ m_thread = ::std::thread(&EasyTreeWidgetLoader::setTreeInternal2, this,
+ _beginTime, ::std::ref(_blocks), _left, _right, _strict, _colorizeRows,
+ EASY_GLOBALS.add_zero_blocks_to_hierarchy, EASY_GLOBALS.use_decorated_thread_name, EASY_GLOBALS.time_units);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void EasyTreeWidgetLoader::setTreeInternal1(::profiler::timestamp_t& _beginTime, const unsigned int _blocksNumber, const ::profiler::thread_blocks_tree_t& _blocksTree, bool _colorizeRows, bool _addZeroBlocks, bool _decoratedThreadNames, ::profiler_gui::TimeUnits _units)
+{
+ m_items.reserve(_blocksNumber + _blocksTree.size()); // _blocksNumber does not include Thread root blocks
+
+ ::profiler::timestamp_t finishtime = 0;
+ for (const auto& threadTree : _blocksTree)
+ {
+ const auto node_block = blocksTree(threadTree.second.children.front()).node;
+ const auto startTime = node_block->begin();
+ const auto endTime = node_block->end();
+
+ if (_beginTime > startTime)
+ _beginTime = startTime;
+
+ if (finishtime < endTime)
+ finishtime = endTime;
+ }
+
+ //const QSignalBlocker b(this);
+ const auto u_thread = ::profiler_gui::toUnicode("thread");
+ int i = 0;
+ const int total = static_cast(_blocksTree.size());
+ for (const auto& threadTree : _blocksTree)
+ {
+ if (interrupted())
+ break;
+
+ const auto& root = threadTree.second;
+ auto item = new EasyTreeWidgetItem();
+ item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, root, u_thread));
+
+ ::profiler::timestamp_t duration = 0;
+ if (!root.children.empty())
+ duration = blocksTree(root.children.back()).node->end() - blocksTree(root.children.front()).node->begin();
+
+ item->setTimeSmart(COL_DURATION, _units, duration);
+ item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);
+ item->setTextColor(::profiler_gui::SELECTED_THREAD_FOREGROUND);
+
+ //_items.push_back(item);
+
+ item->setTimeSmart(COL_SELF_DURATION, _units, root.profiled_time);
+
+ ::profiler::timestamp_t children_duration = 0;
+ const auto children_items_number = setTreeInternal(root, 0, _beginTime, root.children, item, nullptr, _beginTime, finishtime + 1000000000ULL, false, children_duration, _colorizeRows, _addZeroBlocks, _units);
+
+ if (children_items_number > 0)
+ {
+ //total_items += children_items_number + 1;
+ //addTopLevelItem(item);
+ //m_roots[threadTree.first] = item;
+ m_topLevelItems.emplace_back(root.thread_id, item);
+ }
+ else
+ {
+ //_items.pop_back();
+ delete item;
+ }
+
+ setProgress((100 * ++i) / total);
+ }
+
+ setDone();
+ //return total_items;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+// auto calculateTotalChildrenNumber(const ::profiler::BlocksTree& _tree) -> decltype(_tree.children.size())
+// {
+// auto children_number = _tree.children.size();
+// for (auto i : _tree.children)
+// children_number += calculateTotalChildrenNumber(blocksTree(i));
+// return children_number;
+// }
+
+typedef ::std::unordered_map<::profiler::thread_id_t, ::profiler::block_index_t, ::profiler_gui::do_no_hash<::profiler::thread_id_t>::hasher_t> BeginEndIndicesMap;
+
+void EasyTreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _beginTime, const ::profiler_gui::TreeBlocks& _blocks, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, bool _colorizeRows, bool _addZeroBlocks, bool _decoratedThreadNames, ::profiler_gui::TimeUnits _units)
+{
+ //size_t blocksNumber = 0;
+ //for (const auto& block : _blocks)
+ // blocksNumber += calculateTotalChildrenNumber(*block.tree);
+ // //blocksNumber += block.tree->total_children_number;
+ //m_items.reserve(blocksNumber + _blocks.size()); // blocksNumber does not include root blocks
+
+ BeginEndIndicesMap beginEndMap;
+ RootsMap threadsMap;
+
+ auto const setTree = (m_mode == EasyTreeMode_Full) ? &EasyTreeWidgetLoader::setTreeInternal : &EasyTreeWidgetLoader::setTreeInternalPlain;
+
+ const auto u_thread = ::profiler_gui::toUnicode("thread");
+ int i = 0, total = static_cast(_blocks.size());
+ //const QSignalBlocker b(this);
+ for (const auto& block : _blocks)
+ {
+ if (interrupted())
+ break;
+
+ auto& gui_block = easyBlock(block.tree);
+ const auto startTime = gui_block.tree.node->begin();
+ const auto endTime = gui_block.tree.node->end();
+ if (startTime > _right || endTime < _left)
+ {
+ setProgress((90 * ++i) / total);
+ continue;
+ }
+
+ ::profiler::timestamp_t duration = 0;
+ EasyTreeWidgetItem* thread_item = nullptr;
+ ::profiler::block_index_t& firstCswitch = beginEndMap[block.root->thread_id];
+ auto thread_item_it = threadsMap.find(block.root->thread_id);
+ if (thread_item_it != threadsMap.end())
+ {
+ thread_item = thread_item_it->second;
+ }
+ else
+ {
+ thread_item = new EasyTreeWidgetItem();
+ thread_item->setText(COL_NAME, ::profiler_gui::decoratedThreadName(_decoratedThreadNames, *block.root, u_thread));
+
+ if (!block.root->children.empty())
+ duration = blocksTree(block.root->children.back()).node->end() - blocksTree(block.root->children.front()).node->begin();
+
+ thread_item->setTimeSmart(COL_DURATION, _units, duration);
+ thread_item->setBackgroundColor(::profiler_gui::SELECTED_THREAD_BACKGROUND);
+ thread_item->setTextColor(::profiler_gui::SELECTED_THREAD_FOREGROUND);
+
+ // Sum of all children durations:
+ thread_item->setTimeSmart(COL_SELF_DURATION, _units, block.root->profiled_time);
+
+ threadsMap.insert(::std::make_pair(block.root->thread_id, thread_item));
+
+ firstCswitch = 0;
+ auto it = ::std::lower_bound(block.root->sync.begin(), block.root->sync.end(), _left, [](::profiler::block_index_t ind, decltype(_left) _val)
+ {
+ return EASY_GLOBALS.gui_blocks[ind].tree.node->begin() < _val;
+ });
+
if (it != block.root->sync.end())
{
firstCswitch = it - block.root->sync.begin();
@@ -321,667 +321,667 @@ void EasyTreeWidgetLoader::setTreeInternal2(const ::profiler::timestamp_t& _begi
else
{
firstCswitch = static_cast<::profiler::block_index_t>(block.root->sync.size());
- }
- }
-
- bool hasContextSwitch = false;
- ::profiler::timestamp_t idleTime = 0;
- for (::profiler::block_index_t ind = firstCswitch, ncs = static_cast<::profiler::block_index_t>(block.root->sync.size()); ind < ncs; ++ind)
- {
- auto cs_index = block.root->sync[ind];
- const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
-
- if (cs->begin() > endTime)
- {
- if (!hasContextSwitch)
- firstCswitch = ind;
- break;
- }
-
- if (startTime <= cs->begin() && cs->end() <= endTime)
- {
- if (!hasContextSwitch)
- {
- firstCswitch = ind;
- hasContextSwitch = true;
- }
-
- idleTime += cs->duration();
- }
- }
-
- auto item = new EasyTreeWidgetItem(block.tree, thread_item);
- duration = endTime - startTime;
-
- auto name = *gui_block.tree.node->name() != 0 ? gui_block.tree.node->name() : easyDescriptor(gui_block.tree.node->id()).name();
- item->setText(COL_NAME, ::profiler_gui::toUnicode(name));
- item->setTimeSmart(COL_DURATION, _units, duration);
-
- auto active_time = duration - idleTime;
- auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration);
- item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
- item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
- item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
-
- item->setTimeMs(COL_BEGIN, startTime - _beginTime);
- item->setTimeMs(COL_END, endTime - _beginTime);
-
- item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0);
-
- auto percentage_per_thread = ::profiler_gui::percent(duration, block.root->profiled_time);
- item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread);
- item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread));
-
- if (gui_block.tree.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also
- {
- const ::profiler::BlockStatistics* per_thread_stats = gui_block.tree.per_thread_stats;
- const ::profiler::BlockStatistics* per_parent_stats = gui_block.tree.per_parent_stats;
- const ::profiler::BlockStatistics* per_frame_stats = gui_block.tree.per_frame_stats;
-
-
- if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
- {
- item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration(), "min ");
- item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration(), "max ");
- item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration());
- item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration);
- }
-
- item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number);
- item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number));
-
- percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, block.root->profiled_time);
- item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
- item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
-
-
- if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
- {
- item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration(), "min ");
- item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration(), "max ");
- item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration());
- item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration);
- }
-
- item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number);
- item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number));
-
-
- if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
- {
- item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration(), "min ");
- item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration(), "max ");
- item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration());
- item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration);
- }
-
- item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number);
- item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number));
- }
- else
- {
- item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
- item->setText(COL_PERCENT_SUM_PER_THREAD, "");
- }
-
- const auto color = easyDescriptor(gui_block.tree.node->id()).color();
- //const auto bgColor = ::profiler_gui::fromProfilerRgb(::profiler::colors::get_red(color), ::profiler::colors::get_green(color), ::profiler::colors::get_blue(color));
- const auto fgColor = ::profiler_gui::textColorForRgb(color);//0x00ffffff - bgColor;
- item->setBackgroundColor(color);
- item->setTextColor(fgColor);
-
-#ifdef EASY_TREE_WIDGET__USE_VECTOR
- auto item_index = static_cast(m_items.size());
- m_items.push_back(item);
-#endif
-
- size_t children_items_number = 0;
- ::profiler::timestamp_t children_duration = 0;
- if (!gui_block.tree.children.empty())
- {
- m_iditems.clear();
- children_items_number = (this->*setTree)(*block.root, firstCswitch, _beginTime, gui_block.tree.children, item, item, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
- if (interrupted())
- break;
- }
-
- int percentage = 100;
- auto self_duration = duration - children_duration;
- if (children_duration > 0 && duration > 0)
- {
- percentage = static_cast(0.5 + 100. * static_cast(self_duration) / static_cast(duration));
- }
-
- item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
- item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
- item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
-
- if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right))
- {
- //total_items += children_items_number + 1;
-#ifdef EASY_TREE_WIDGET__USE_VECTOR
- gui_block.tree_item = item_index;
-#endif
-
- if (_colorizeRows)
- item->colorize(_colorizeRows);
-
- if (gui_block.expanded)
- item->setExpanded(true);
-
-#ifndef EASY_TREE_WIDGET__USE_VECTOR
- m_items.insert(::std::make_pair(block.tree, item));
-#endif
- }
- else
- {
-#ifdef EASY_TREE_WIDGET__USE_VECTOR
- m_items.pop_back();
-#endif
- delete item;
- }
-
- setProgress((90 * ++i) / total);
- }
-
- i = 0;
- total = static_cast(threadsMap.size());
- for (auto& it : threadsMap)
- {
- auto item = it.second;
-
- if (item->childCount() > 0)
- {
- //addTopLevelItem(item);
- //m_roots[it.first] = item;
-
- //_items.push_back(item);
- m_topLevelItems.emplace_back(it.first, item);
-
- //++total_items;
- }
- else
- {
- delete item;
- }
-
- setProgress(90 + (10 * ++i) / total);
- }
-
- setDone();
- //return total_items;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-size_t EasyTreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _threadRoot, ::profiler::block_index_t _firstCswitch, const ::profiler::timestamp_t& _beginTime, const ::profiler::BlocksTree::children_t& _children, EasyTreeWidgetItem* _parent, EasyTreeWidgetItem* _frame, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, ::profiler::timestamp_t& _duration, bool _colorizeRows, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units)
-{
- auto const setTree = m_mode == EasyTreeMode_Full ? &EasyTreeWidgetLoader::setTreeInternal : &EasyTreeWidgetLoader::setTreeInternalPlain;
-
- size_t total_items = 0;
- for (auto child_index : _children)
- {
- if (interrupted())
- break;
-
- auto& gui_block = easyBlock(child_index);
- const auto& child = gui_block.tree;
- const auto startTime = child.node->begin();
- const auto endTime = child.node->end();
- const auto duration = endTime - startTime;
-
- if (duration == 0 && !_addZeroBlocks)
- continue;
-
- _duration += duration;
-
- if (startTime > _right || endTime < _left)
- continue;
-
- bool hasContextSwitch = false;
- ::profiler::timestamp_t idleTime = 0;
- for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind)
- {
- auto cs_index = _threadRoot.sync[ind];
- const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
-
- if (cs->begin() > endTime)
- {
- if (!hasContextSwitch)
- _firstCswitch = ind;
- break;
- }
-
- if (startTime <= cs->begin() && cs->end() <= endTime)
- {
- if (!hasContextSwitch)
- {
- _firstCswitch = ind;
- hasContextSwitch = true;
- }
-
- idleTime += cs->duration();
- }
- }
-
- auto item = new EasyTreeWidgetItem(child_index, _parent);
-
- auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name();
- item->setText(COL_NAME, ::profiler_gui::toUnicode(name));
- item->setTimeSmart(COL_DURATION, _units, duration);
-
- auto active_time = duration - idleTime;
- auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration);
- item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
- item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
- item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
-
- item->setTimeMs(COL_BEGIN, startTime - _beginTime);
- item->setTimeMs(COL_END, endTime - _beginTime);
- item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
-
- if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also
- {
- const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats;
- const ::profiler::BlockStatistics* per_parent_stats = child.per_parent_stats;
- const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats;
-
- auto parent_duration = _parent->duration();
- auto percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, parent_duration);
- auto percentage_sum = ::profiler_gui::percent(per_parent_stats->total_duration, parent_duration);
- item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage);
- item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage));
- item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, percentage_sum);
- item->setText(COL_PERCENT_SUM_PER_PARENT, QString::number(percentage_sum));
-
- if (_frame != nullptr)
- {
- if (_parent != _frame)
- {
- parent_duration = _frame->duration();
- percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, parent_duration);
- percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, parent_duration);
- }
-
- item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage);
- item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage));
- item->setData(COL_PERCENT_SUM_PER_FRAME, Qt::UserRole, percentage_sum);
- item->setText(COL_PERCENT_SUM_PER_FRAME, QString::number(percentage_sum));
- }
- else
- {
- item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0);
- item->setData(COL_PERCENT_SUM_PER_FRAME, Qt::UserRole, 0);
-
- auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time);
- item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread);
- item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread));
- }
-
-
- if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
- {
- item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration(), "min ");
- item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration(), "max ");
- item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration());
- item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration);
- }
-
- item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number);
- item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number));
-
- auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time);
- item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
- item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
-
-
- if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
- {
- item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration(), "min ");
- item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration(), "max ");
- item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration());
- item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration);
- }
-
- item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number);
- item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number));
-
-
- if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
- {
- item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration(), "min ");
- item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration(), "max ");
- item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration());
- item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration);
- }
-
- item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number);
- item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number));
- }
- else
- {
- if (_frame == nullptr)
- {
- auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time);
- item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread);
- item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread));
- }
- else
- {
- item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, 0);
- }
-
- item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, 0);
- item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
- }
-
- const auto color = easyDescriptor(child.node->id()).color();
- //const auto bgColor = ::profiler_gui::fromProfilerRgb(::profiler::colors::get_red(color), ::profiler::colors::get_green(color), ::profiler::colors::get_blue(color));
- const auto fgColor = ::profiler_gui::textColorForRgb(color);// 0x00ffffff - bgColor;
- item->setBackgroundColor(color);
- item->setTextColor(fgColor);
-
-#ifdef EASY_TREE_WIDGET__USE_VECTOR
- auto item_index = static_cast(m_items.size());
- m_items.push_back(item);
-#endif
-
- size_t children_items_number = 0;
- ::profiler::timestamp_t children_duration = 0;
- if (!child.children.empty())
- {
- m_iditems.clear();
- children_items_number = (this->*setTree)(_threadRoot, _firstCswitch, _beginTime, child.children, item, _frame ? _frame : item, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
- if (interrupted())
- break;
- }
-
- int percentage = 100;
- auto self_duration = duration - children_duration;
- if (children_duration > 0 && duration > 0)
- {
- percentage = ::profiler_gui::percent(self_duration, duration);
- }
-
- item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
- item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
- item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
-
- if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right))
- {
- total_items += children_items_number + 1;
-#ifdef EASY_TREE_WIDGET__USE_VECTOR
- gui_block.tree_item = item_index;
-#endif
-
- if (_colorizeRows)
- item->colorize(_colorizeRows);
-
- if (gui_block.expanded)
- item->setExpanded(true);
-
-#ifndef EASY_TREE_WIDGET__USE_VECTOR
- m_items.insert(::std::make_pair(child_index, item));
-#endif
- }
- else
- {
-#ifdef EASY_TREE_WIDGET__USE_VECTOR
- m_items.pop_back();
-#endif
- delete item;
- }
- }
-
- return total_items;
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-::profiler::timestamp_t EasyTreeWidgetLoader::calculateChildrenDurationRecursive(const ::profiler::BlocksTree::children_t& _children, ::profiler::block_id_t _id)
-{
- ::profiler::timestamp_t total_duration = 0;
-
- for (auto child_index : _children)
- {
- if (interrupted())
- break;
-
- const auto& gui_block = easyBlock(child_index);
- total_duration += gui_block.tree.node->duration();
- if (gui_block.tree.node->id() == _id)
- total_duration += calculateChildrenDurationRecursive(gui_block.tree.children, _id);
- }
-
- return total_duration;
-}
-
-size_t EasyTreeWidgetLoader::setTreeInternalPlain(const ::profiler::BlocksTreeRoot& _threadRoot, ::profiler::block_index_t _firstCswitch, const ::profiler::timestamp_t& _beginTime, const ::profiler::BlocksTree::children_t& _children, EasyTreeWidgetItem*, EasyTreeWidgetItem* _frame, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, ::profiler::timestamp_t& _duration, bool _colorizeRows, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units)
-{
- size_t total_items = 0;
- for (auto child_index : _children)
- {
- if (interrupted())
- break;
-
- const auto& gui_block = easyBlock(child_index);
- const auto& child = gui_block.tree;
- const auto startTime = child.node->begin();
- const auto endTime = child.node->end();
- const auto duration = endTime - startTime;
-
- _duration += duration;
-
- auto it = m_iditems.find(child.node->id());
- if (it != m_iditems.end())
- {
- ++total_items;
-
- size_t children_items_number = 0;
- ::profiler::timestamp_t children_duration = 0;
- if (!child.children.empty())
- {
- children_items_number = setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, _frame, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
- if (interrupted())
- break;
- }
-
- if (it->second != nullptr && child.per_frame_stats != nullptr)
- {
- auto item = it->second;
-
- //auto children_duration = calculateChildrenDurationRecursive(child.children, it->first);
- if (children_duration != 0)
- {
- auto self_duration = item->data(COL_SELF_DURATION, Qt::UserRole).toULongLong() - children_duration;
-
- int percentage = 100;
- if (child.per_frame_stats->total_duration > 0)
- percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration);
-
- item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
- item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
- item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
- }
-
- bool hasContextSwitch = false;
- ::profiler::timestamp_t idleTime = 0;
- for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind)
- {
- auto cs_index = _threadRoot.sync[ind];
- const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
-
- if (cs->begin() > endTime)
- {
- if (!hasContextSwitch)
- _firstCswitch = ind;
- break;
- }
-
- if (startTime <= cs->begin() && cs->end() <= endTime)
- {
- if (!hasContextSwitch)
- {
- _firstCswitch = ind;
- hasContextSwitch = true;
- }
-
- idleTime += cs->duration();
- }
- }
-
- auto active_time = item->data(COL_ACTIVE_TIME, Qt::UserRole).toULongLong() - idleTime;
- auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration);
- item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
- item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
- item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
- }
-
- continue;
- }
-
- if (startTime > _right || endTime < _left)
- continue;
-
- bool hasContextSwitch = false;
- ::profiler::timestamp_t idleTime = 0;
- for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind)
- {
- auto cs_index = _threadRoot.sync[ind];
- const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
-
- if (cs->begin() > endTime)
- {
- if (!hasContextSwitch)
- _firstCswitch = ind;
- break;
- }
-
- if (startTime <= cs->begin() && cs->end() <= endTime)
- {
- if (!hasContextSwitch)
- {
- _firstCswitch = ind;
- hasContextSwitch = true;
- }
-
- idleTime += cs->duration();
- }
- }
-
- auto item = new EasyTreeWidgetItem(child_index, _frame);
-
- auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name();
- item->setText(COL_NAME, ::profiler_gui::toUnicode(name));
-
- if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also
- {
- const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats;
- if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
- {
- item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration(), "min ");
- item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration(), "max ");
- item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration());
- }
-
- item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration);
- item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number);
- item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number));
-
- auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time);
- item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
- item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
-
- const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats;
- const auto percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, _frame->duration());
- item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage_sum);
- item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage_sum));
-
- if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
- {
- item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration(), "min ");
- item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration(), "max ");
- item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration());
- }
-
- item->setTimeSmart(COL_DURATION, _units, per_frame_stats->total_duration);
- item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number);
- item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number));
- }
- else
- {
- item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
- item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0);
- }
-
- const auto color = easyDescriptor(child.node->id()).color();
- const auto fgColor = ::profiler_gui::textColorForRgb(color);// 0x00ffffff - bgColor;
- item->setBackgroundColor(color);
- item->setTextColor(fgColor);
-
-#ifdef EASY_TREE_WIDGET__USE_VECTOR
- auto item_index = static_cast(m_items.size());
- m_items.push_back(item);
-#endif
- m_iditems[child.node->id()] = nullptr;
-
- size_t children_items_number = 0;
- ::profiler::timestamp_t children_duration = 0;
- if (!child.children.empty())
- {
- children_items_number = setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, _frame, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
- if (interrupted())
- break;
- }
-
- m_iditems[child.node->id()] = item;
-
- if (child.per_frame_stats != nullptr)
- {
- int percentage = 100;
- auto self_duration = child.per_frame_stats->total_duration - children_duration;
- if (child.per_frame_stats->total_duration > 0)
- percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration);
-
- item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
- item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
- item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
-
- auto active_time = child.per_frame_stats->total_duration - idleTime;
- auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration);
- item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
- item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
- item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
- }
-
- if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right))
- {
- total_items += children_items_number + 1;
-#ifdef EASY_TREE_WIDGET__USE_VECTOR
- gui_block.tree_item = item_index;
-#endif
-
- if (_colorizeRows)
- item->colorize(_colorizeRows);
-
- if (gui_block.expanded)
- item->setExpanded(true);
-
-#ifndef EASY_TREE_WIDGET__USE_VECTOR
- m_items.insert(::std::make_pair(child_index, item));
-#endif
- }
- else
- {
-#ifdef EASY_TREE_WIDGET__USE_VECTOR
- m_items.pop_back();
-#endif
- delete item;
- m_iditems.erase(gui_block.tree.node->id());
- }
- }
-
- return total_items;
-}
-
-//////////////////////////////////////////////////////////////////////////
+ }
+ }
+
+ bool hasContextSwitch = false;
+ ::profiler::timestamp_t idleTime = 0;
+ for (::profiler::block_index_t ind = firstCswitch, ncs = static_cast<::profiler::block_index_t>(block.root->sync.size()); ind < ncs; ++ind)
+ {
+ auto cs_index = block.root->sync[ind];
+ const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
+
+ if (cs->begin() > endTime)
+ {
+ if (!hasContextSwitch)
+ firstCswitch = ind;
+ break;
+ }
+
+ if (startTime <= cs->begin() && cs->end() <= endTime)
+ {
+ if (!hasContextSwitch)
+ {
+ firstCswitch = ind;
+ hasContextSwitch = true;
+ }
+
+ idleTime += cs->duration();
+ }
+ }
+
+ auto item = new EasyTreeWidgetItem(block.tree, thread_item);
+ duration = endTime - startTime;
+
+ auto name = *gui_block.tree.node->name() != 0 ? gui_block.tree.node->name() : easyDescriptor(gui_block.tree.node->id()).name();
+ item->setText(COL_NAME, ::profiler_gui::toUnicode(name));
+ item->setTimeSmart(COL_DURATION, _units, duration);
+
+ auto active_time = duration - idleTime;
+ auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration);
+ item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
+ item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
+ item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
+
+ item->setTimeMs(COL_BEGIN, startTime - _beginTime);
+ item->setTimeMs(COL_END, endTime - _beginTime);
+
+ item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0);
+
+ auto percentage_per_thread = ::profiler_gui::percent(duration, block.root->profiled_time);
+ item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread);
+ item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread));
+
+ if (gui_block.tree.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also
+ {
+ const ::profiler::BlockStatistics* per_thread_stats = gui_block.tree.per_thread_stats;
+ const ::profiler::BlockStatistics* per_parent_stats = gui_block.tree.per_parent_stats;
+ const ::profiler::BlockStatistics* per_frame_stats = gui_block.tree.per_frame_stats;
+
+
+ if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
+ {
+ item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration(), "min ");
+ item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration(), "max ");
+ item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration());
+ item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration);
+ }
+
+ item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number);
+ item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number));
+
+ percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, block.root->profiled_time);
+ item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
+ item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
+
+
+ if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
+ {
+ item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration(), "min ");
+ item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration(), "max ");
+ item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration());
+ item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration);
+ }
+
+ item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number);
+ item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number));
+
+
+ if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
+ {
+ item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration(), "min ");
+ item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration(), "max ");
+ item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration());
+ item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration);
+ }
+
+ item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number);
+ item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number));
+ }
+ else
+ {
+ item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
+ item->setText(COL_PERCENT_SUM_PER_THREAD, "");
+ }
+
+ const auto color = easyDescriptor(gui_block.tree.node->id()).color();
+ //const auto bgColor = ::profiler_gui::fromProfilerRgb(::profiler::colors::get_red(color), ::profiler::colors::get_green(color), ::profiler::colors::get_blue(color));
+ const auto fgColor = ::profiler_gui::textColorForRgb(color);//0x00ffffff - bgColor;
+ item->setBackgroundColor(color);
+ item->setTextColor(fgColor);
+
+#ifdef EASY_TREE_WIDGET__USE_VECTOR
+ auto item_index = static_cast(m_items.size());
+ m_items.push_back(item);
+#endif
+
+ size_t children_items_number = 0;
+ ::profiler::timestamp_t children_duration = 0;
+ if (!gui_block.tree.children.empty())
+ {
+ m_iditems.clear();
+ children_items_number = (this->*setTree)(*block.root, firstCswitch, _beginTime, gui_block.tree.children, item, item, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
+ if (interrupted())
+ break;
+ }
+
+ int percentage = 100;
+ auto self_duration = duration - children_duration;
+ if (children_duration > 0 && duration > 0)
+ {
+ percentage = static_cast(0.5 + 100. * static_cast(self_duration) / static_cast(duration));
+ }
+
+ item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
+ item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
+ item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
+
+ if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right))
+ {
+ //total_items += children_items_number + 1;
+#ifdef EASY_TREE_WIDGET__USE_VECTOR
+ gui_block.tree_item = item_index;
+#endif
+
+ if (_colorizeRows)
+ item->colorize(_colorizeRows);
+
+ if (gui_block.expanded)
+ item->setExpanded(true);
+
+#ifndef EASY_TREE_WIDGET__USE_VECTOR
+ m_items.insert(::std::make_pair(block.tree, item));
+#endif
+ }
+ else
+ {
+#ifdef EASY_TREE_WIDGET__USE_VECTOR
+ m_items.pop_back();
+#endif
+ delete item;
+ }
+
+ setProgress((90 * ++i) / total);
+ }
+
+ i = 0;
+ total = static_cast(threadsMap.size());
+ for (auto& it : threadsMap)
+ {
+ auto item = it.second;
+
+ if (item->childCount() > 0)
+ {
+ //addTopLevelItem(item);
+ //m_roots[it.first] = item;
+
+ //_items.push_back(item);
+ m_topLevelItems.emplace_back(it.first, item);
+
+ //++total_items;
+ }
+ else
+ {
+ delete item;
+ }
+
+ setProgress(90 + (10 * ++i) / total);
+ }
+
+ setDone();
+ //return total_items;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+size_t EasyTreeWidgetLoader::setTreeInternal(const ::profiler::BlocksTreeRoot& _threadRoot, ::profiler::block_index_t _firstCswitch, const ::profiler::timestamp_t& _beginTime, const ::profiler::BlocksTree::children_t& _children, EasyTreeWidgetItem* _parent, EasyTreeWidgetItem* _frame, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, ::profiler::timestamp_t& _duration, bool _colorizeRows, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units)
+{
+ auto const setTree = m_mode == EasyTreeMode_Full ? &EasyTreeWidgetLoader::setTreeInternal : &EasyTreeWidgetLoader::setTreeInternalPlain;
+
+ size_t total_items = 0;
+ for (auto child_index : _children)
+ {
+ if (interrupted())
+ break;
+
+ auto& gui_block = easyBlock(child_index);
+ const auto& child = gui_block.tree;
+ const auto startTime = child.node->begin();
+ const auto endTime = child.node->end();
+ const auto duration = endTime - startTime;
+
+ if (duration == 0 && !_addZeroBlocks)
+ continue;
+
+ _duration += duration;
+
+ if (startTime > _right || endTime < _left)
+ continue;
+
+ bool hasContextSwitch = false;
+ ::profiler::timestamp_t idleTime = 0;
+ for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind)
+ {
+ auto cs_index = _threadRoot.sync[ind];
+ const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
+
+ if (cs->begin() > endTime)
+ {
+ if (!hasContextSwitch)
+ _firstCswitch = ind;
+ break;
+ }
+
+ if (startTime <= cs->begin() && cs->end() <= endTime)
+ {
+ if (!hasContextSwitch)
+ {
+ _firstCswitch = ind;
+ hasContextSwitch = true;
+ }
+
+ idleTime += cs->duration();
+ }
+ }
+
+ auto item = new EasyTreeWidgetItem(child_index, _parent);
+
+ auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name();
+ item->setText(COL_NAME, ::profiler_gui::toUnicode(name));
+ item->setTimeSmart(COL_DURATION, _units, duration);
+
+ auto active_time = duration - idleTime;
+ auto active_percent = duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, duration);
+ item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
+ item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
+ item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
+
+ item->setTimeMs(COL_BEGIN, startTime - _beginTime);
+ item->setTimeMs(COL_END, endTime - _beginTime);
+ item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
+
+ if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also
+ {
+ const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats;
+ const ::profiler::BlockStatistics* per_parent_stats = child.per_parent_stats;
+ const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats;
+
+ auto parent_duration = _parent->duration();
+ auto percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, parent_duration);
+ auto percentage_sum = ::profiler_gui::percent(per_parent_stats->total_duration, parent_duration);
+ item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage);
+ item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage));
+ item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, percentage_sum);
+ item->setText(COL_PERCENT_SUM_PER_PARENT, QString::number(percentage_sum));
+
+ if (_frame != nullptr)
+ {
+ if (_parent != _frame)
+ {
+ parent_duration = _frame->duration();
+ percentage = duration == 0 ? 0 : ::profiler_gui::percent(duration, parent_duration);
+ percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, parent_duration);
+ }
+
+ item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage);
+ item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage));
+ item->setData(COL_PERCENT_SUM_PER_FRAME, Qt::UserRole, percentage_sum);
+ item->setText(COL_PERCENT_SUM_PER_FRAME, QString::number(percentage_sum));
+ }
+ else
+ {
+ item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0);
+ item->setData(COL_PERCENT_SUM_PER_FRAME, Qt::UserRole, 0);
+
+ auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time);
+ item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread);
+ item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread));
+ }
+
+
+ if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
+ {
+ item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration(), "min ");
+ item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration(), "max ");
+ item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration());
+ item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration);
+ }
+
+ item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number);
+ item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number));
+
+ auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time);
+ item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
+ item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
+
+
+ if (per_parent_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
+ {
+ item->setTimeSmart(COL_MIN_PER_PARENT, _units, easyBlock(per_parent_stats->min_duration_block).tree.node->duration(), "min ");
+ item->setTimeSmart(COL_MAX_PER_PARENT, _units, easyBlock(per_parent_stats->max_duration_block).tree.node->duration(), "max ");
+ item->setTimeSmart(COL_AVERAGE_PER_PARENT, _units, per_parent_stats->average_duration());
+ item->setTimeSmart(COL_DURATION_SUM_PER_PARENT, _units, per_parent_stats->total_duration);
+ }
+
+ item->setData(COL_NCALLS_PER_PARENT, Qt::UserRole, per_parent_stats->calls_number);
+ item->setText(COL_NCALLS_PER_PARENT, QString::number(per_parent_stats->calls_number));
+
+
+ if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
+ {
+ item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration(), "min ");
+ item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration(), "max ");
+ item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration());
+ item->setTimeSmart(COL_DURATION_SUM_PER_FRAME, _units, per_frame_stats->total_duration);
+ }
+
+ item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number);
+ item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number));
+ }
+ else
+ {
+ if (_frame == nullptr)
+ {
+ auto percentage_per_thread = ::profiler_gui::percent(duration, _threadRoot.profiled_time);
+ item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, percentage_per_thread);
+ item->setText(COL_PERCENT_PER_PARENT, QString::number(percentage_per_thread));
+ }
+ else
+ {
+ item->setData(COL_PERCENT_PER_PARENT, Qt::UserRole, 0);
+ }
+
+ item->setData(COL_PERCENT_SUM_PER_PARENT, Qt::UserRole, 0);
+ item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
+ }
+
+ const auto color = easyDescriptor(child.node->id()).color();
+ //const auto bgColor = ::profiler_gui::fromProfilerRgb(::profiler::colors::get_red(color), ::profiler::colors::get_green(color), ::profiler::colors::get_blue(color));
+ const auto fgColor = ::profiler_gui::textColorForRgb(color);// 0x00ffffff - bgColor;
+ item->setBackgroundColor(color);
+ item->setTextColor(fgColor);
+
+#ifdef EASY_TREE_WIDGET__USE_VECTOR
+ auto item_index = static_cast(m_items.size());
+ m_items.push_back(item);
+#endif
+
+ size_t children_items_number = 0;
+ ::profiler::timestamp_t children_duration = 0;
+ if (!child.children.empty())
+ {
+ m_iditems.clear();
+ children_items_number = (this->*setTree)(_threadRoot, _firstCswitch, _beginTime, child.children, item, _frame ? _frame : item, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
+ if (interrupted())
+ break;
+ }
+
+ int percentage = 100;
+ auto self_duration = duration - children_duration;
+ if (children_duration > 0 && duration > 0)
+ {
+ percentage = ::profiler_gui::percent(self_duration, duration);
+ }
+
+ item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
+ item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
+ item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
+
+ if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right))
+ {
+ total_items += children_items_number + 1;
+#ifdef EASY_TREE_WIDGET__USE_VECTOR
+ gui_block.tree_item = item_index;
+#endif
+
+ if (_colorizeRows)
+ item->colorize(_colorizeRows);
+
+ if (gui_block.expanded)
+ item->setExpanded(true);
+
+#ifndef EASY_TREE_WIDGET__USE_VECTOR
+ m_items.insert(::std::make_pair(child_index, item));
+#endif
+ }
+ else
+ {
+#ifdef EASY_TREE_WIDGET__USE_VECTOR
+ m_items.pop_back();
+#endif
+ delete item;
+ }
+ }
+
+ return total_items;
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+::profiler::timestamp_t EasyTreeWidgetLoader::calculateChildrenDurationRecursive(const ::profiler::BlocksTree::children_t& _children, ::profiler::block_id_t _id)
+{
+ ::profiler::timestamp_t total_duration = 0;
+
+ for (auto child_index : _children)
+ {
+ if (interrupted())
+ break;
+
+ const auto& gui_block = easyBlock(child_index);
+ total_duration += gui_block.tree.node->duration();
+ if (gui_block.tree.node->id() == _id)
+ total_duration += calculateChildrenDurationRecursive(gui_block.tree.children, _id);
+ }
+
+ return total_duration;
+}
+
+size_t EasyTreeWidgetLoader::setTreeInternalPlain(const ::profiler::BlocksTreeRoot& _threadRoot, ::profiler::block_index_t _firstCswitch, const ::profiler::timestamp_t& _beginTime, const ::profiler::BlocksTree::children_t& _children, EasyTreeWidgetItem*, EasyTreeWidgetItem* _frame, ::profiler::timestamp_t _left, ::profiler::timestamp_t _right, bool _strict, ::profiler::timestamp_t& _duration, bool _colorizeRows, bool _addZeroBlocks, ::profiler_gui::TimeUnits _units)
+{
+ size_t total_items = 0;
+ for (auto child_index : _children)
+ {
+ if (interrupted())
+ break;
+
+ const auto& gui_block = easyBlock(child_index);
+ const auto& child = gui_block.tree;
+ const auto startTime = child.node->begin();
+ const auto endTime = child.node->end();
+ const auto duration = endTime - startTime;
+
+ _duration += duration;
+
+ auto it = m_iditems.find(child.node->id());
+ if (it != m_iditems.end())
+ {
+ ++total_items;
+
+ size_t children_items_number = 0;
+ ::profiler::timestamp_t children_duration = 0;
+ if (!child.children.empty())
+ {
+ children_items_number = setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, _frame, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
+ if (interrupted())
+ break;
+ }
+
+ if (it->second != nullptr && child.per_frame_stats != nullptr)
+ {
+ auto item = it->second;
+
+ //auto children_duration = calculateChildrenDurationRecursive(child.children, it->first);
+ if (children_duration != 0)
+ {
+ auto self_duration = item->data(COL_SELF_DURATION, Qt::UserRole).toULongLong() - children_duration;
+
+ int percentage = 100;
+ if (child.per_frame_stats->total_duration > 0)
+ percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration);
+
+ item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
+ item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
+ item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
+ }
+
+ bool hasContextSwitch = false;
+ ::profiler::timestamp_t idleTime = 0;
+ for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind)
+ {
+ auto cs_index = _threadRoot.sync[ind];
+ const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
+
+ if (cs->begin() > endTime)
+ {
+ if (!hasContextSwitch)
+ _firstCswitch = ind;
+ break;
+ }
+
+ if (startTime <= cs->begin() && cs->end() <= endTime)
+ {
+ if (!hasContextSwitch)
+ {
+ _firstCswitch = ind;
+ hasContextSwitch = true;
+ }
+
+ idleTime += cs->duration();
+ }
+ }
+
+ auto active_time = item->data(COL_ACTIVE_TIME, Qt::UserRole).toULongLong() - idleTime;
+ auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration);
+ item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
+ item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
+ item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
+ }
+
+ continue;
+ }
+
+ if (startTime > _right || endTime < _left)
+ continue;
+
+ bool hasContextSwitch = false;
+ ::profiler::timestamp_t idleTime = 0;
+ for (::profiler::block_index_t ind = _firstCswitch, ncs = static_cast<::profiler::block_index_t>(_threadRoot.sync.size()); ind < ncs; ++ind)
+ {
+ auto cs_index = _threadRoot.sync[ind];
+ const auto cs = EASY_GLOBALS.gui_blocks[cs_index].tree.node;
+
+ if (cs->begin() > endTime)
+ {
+ if (!hasContextSwitch)
+ _firstCswitch = ind;
+ break;
+ }
+
+ if (startTime <= cs->begin() && cs->end() <= endTime)
+ {
+ if (!hasContextSwitch)
+ {
+ _firstCswitch = ind;
+ hasContextSwitch = true;
+ }
+
+ idleTime += cs->duration();
+ }
+ }
+
+ auto item = new EasyTreeWidgetItem(child_index, _frame);
+
+ auto name = *child.node->name() != 0 ? child.node->name() : easyDescriptor(child.node->id()).name();
+ item->setText(COL_NAME, ::profiler_gui::toUnicode(name));
+
+ if (child.per_thread_stats != nullptr) // if there is per_thread_stats then there are other stats also
+ {
+ const ::profiler::BlockStatistics* per_thread_stats = child.per_thread_stats;
+ if (per_thread_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
+ {
+ item->setTimeSmart(COL_MIN_PER_THREAD, _units, easyBlock(per_thread_stats->min_duration_block).tree.node->duration(), "min ");
+ item->setTimeSmart(COL_MAX_PER_THREAD, _units, easyBlock(per_thread_stats->max_duration_block).tree.node->duration(), "max ");
+ item->setTimeSmart(COL_AVERAGE_PER_THREAD, _units, per_thread_stats->average_duration());
+ }
+
+ item->setTimeSmart(COL_DURATION_SUM_PER_THREAD, _units, per_thread_stats->total_duration);
+ item->setData(COL_NCALLS_PER_THREAD, Qt::UserRole, per_thread_stats->calls_number);
+ item->setText(COL_NCALLS_PER_THREAD, QString::number(per_thread_stats->calls_number));
+
+ auto percentage_per_thread = ::profiler_gui::percent(per_thread_stats->total_duration, _threadRoot.profiled_time);
+ item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, percentage_per_thread);
+ item->setText(COL_PERCENT_SUM_PER_THREAD, QString::number(percentage_per_thread));
+
+ const ::profiler::BlockStatistics* per_frame_stats = child.per_frame_stats;
+ const auto percentage_sum = ::profiler_gui::percent(per_frame_stats->total_duration, _frame->duration());
+ item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, percentage_sum);
+ item->setText(COL_PERCENT_PER_FRAME, QString::number(percentage_sum));
+
+ if (per_frame_stats->calls_number > 1 || !EASY_GLOBALS.display_only_relevant_stats)
+ {
+ item->setTimeSmart(COL_MIN_PER_FRAME, _units, easyBlock(per_frame_stats->min_duration_block).tree.node->duration(), "min ");
+ item->setTimeSmart(COL_MAX_PER_FRAME, _units, easyBlock(per_frame_stats->max_duration_block).tree.node->duration(), "max ");
+ item->setTimeSmart(COL_AVERAGE_PER_FRAME, _units, per_frame_stats->average_duration());
+ }
+
+ item->setTimeSmart(COL_DURATION, _units, per_frame_stats->total_duration);
+ item->setData(COL_NCALLS_PER_FRAME, Qt::UserRole, per_frame_stats->calls_number);
+ item->setText(COL_NCALLS_PER_FRAME, QString::number(per_frame_stats->calls_number));
+ }
+ else
+ {
+ item->setData(COL_PERCENT_SUM_PER_THREAD, Qt::UserRole, 0);
+ item->setData(COL_PERCENT_PER_FRAME, Qt::UserRole, 0);
+ }
+
+ const auto color = easyDescriptor(child.node->id()).color();
+ const auto fgColor = ::profiler_gui::textColorForRgb(color);// 0x00ffffff - bgColor;
+ item->setBackgroundColor(color);
+ item->setTextColor(fgColor);
+
+#ifdef EASY_TREE_WIDGET__USE_VECTOR
+ auto item_index = static_cast(m_items.size());
+ m_items.push_back(item);
+#endif
+ m_iditems[child.node->id()] = nullptr;
+
+ size_t children_items_number = 0;
+ ::profiler::timestamp_t children_duration = 0;
+ if (!child.children.empty())
+ {
+ children_items_number = setTreeInternalPlain(_threadRoot, _firstCswitch, _beginTime, child.children, _frame, _frame, _left, _right, _strict, children_duration, _colorizeRows, _addZeroBlocks, _units);
+ if (interrupted())
+ break;
+ }
+
+ m_iditems[child.node->id()] = item;
+
+ if (child.per_frame_stats != nullptr)
+ {
+ int percentage = 100;
+ auto self_duration = child.per_frame_stats->total_duration - children_duration;
+ if (child.per_frame_stats->total_duration > 0)
+ percentage = ::profiler_gui::percent(self_duration, child.per_frame_stats->total_duration);
+
+ item->setTimeSmart(COL_SELF_DURATION, _units, self_duration);
+ item->setData(COL_SELF_DURATION_PERCENT, Qt::UserRole, percentage);
+ item->setText(COL_SELF_DURATION_PERCENT, QString::number(percentage));
+
+ auto active_time = child.per_frame_stats->total_duration - idleTime;
+ auto active_percent = child.per_frame_stats->total_duration == 0 ? 100. : ::profiler_gui::percentReal(active_time, child.per_frame_stats->total_duration);
+ item->setTimeSmart(COL_ACTIVE_TIME, _units, active_time);
+ item->setText(COL_ACTIVE_PERCENT, QString::number(active_percent, 'g', 3));
+ item->setData(COL_ACTIVE_PERCENT, Qt::UserRole, active_percent);
+ }
+
+ if (children_items_number > 0 || !_strict || (startTime >= _left && endTime <= _right))
+ {
+ total_items += children_items_number + 1;
+#ifdef EASY_TREE_WIDGET__USE_VECTOR
+ gui_block.tree_item = item_index;
+#endif
+
+ if (_colorizeRows)
+ item->colorize(_colorizeRows);
+
+ if (gui_block.expanded)
+ item->setExpanded(true);
+
+#ifndef EASY_TREE_WIDGET__USE_VECTOR
+ m_items.insert(::std::make_pair(child_index, item));
+#endif
+ }
+ else
+ {
+#ifdef EASY_TREE_WIDGET__USE_VECTOR
+ m_items.pop_back();
+#endif
+ delete item;
+ m_iditems.erase(gui_block.tree.node->id());
+ }
+ }
+
+ return total_items;
+}
+
+//////////////////////////////////////////////////////////////////////////
diff --git a/sample/main.cpp b/sample/main.cpp
index 8fa03b9..2586525 100644
--- a/sample/main.cpp
+++ b/sample/main.cpp
@@ -1,240 +1,240 @@
-//#define FULL_DISABLE_PROFILER
-#include "easy/profiler.h"
-#include
-#include
-#include
-#include
-#include
-#include "easy/reader.h"
-#include
-#include
-
-std::condition_variable cv;
-std::mutex cv_m;
-int g_i = 0;
-
-int OBJECTS = 500;
-int MODELLING_STEPS = 1500;
-int RENDER_STEPS = 1500;
-int RESOURCE_LOADING_COUNT = 50;
-
-//#define SAMPLE_NETWORK_TEST
-
-void localSleep(int magic=200000)
-{
- //PROFILER_BEGIN_FUNCTION_BLOCK_GROUPED(profiler::colors::Blue);
- volatile int i = 0;
- for (; i < magic; ++i);
-}
-
-void loadingResources(){
- EASY_FUNCTION(profiler::colors::DarkCyan);
- localSleep();
-// std::this_thread::sleep_for(std::chrono::milliseconds(50));
-}
-
-void prepareMath(){
- EASY_FUNCTION(profiler::colors::Green);
- int* intarray = new int[OBJECTS];
- for (int i = 0; i < OBJECTS; ++i)
- intarray[i] = i * i;
- delete[] intarray;
- //std::this_thread::sleep_for(std::chrono::milliseconds(3));
-}
-
-void calcIntersect(){
- EASY_FUNCTION(profiler::colors::Gold);
- //int* intarray = new int[OBJECTS * OBJECTS];
- int* intarray = new int[OBJECTS];
- for (int i = 0; i < OBJECTS; ++i)
- {
- for (int j = i; j < OBJECTS; ++j)
- //intarray[i * OBJECTS + j] = i * j - i / 2 + (OBJECTS - j) * 5;
- intarray[j] = i * j - i / 2 + (OBJECTS - j) * 5;
- }
- delete[] intarray;
- //std::this_thread::sleep_for(std::chrono::milliseconds(4));
-}
-
-double multModel(double i)
-{
- EASY_FUNCTION(profiler::colors::PaleGold);
- return i * sin(i) * cos(i);
-}
-
-void calcPhys(){
- EASY_FUNCTION(profiler::colors::Amber);
- double* intarray = new double[OBJECTS];
- for (int i = 0; i < OBJECTS; ++i)
- intarray[i] = multModel(double(i)) + double(i / 3) - double((OBJECTS - i) / 2);
- calcIntersect();
- delete[] intarray;
-}
-
-double calcSubbrain(int i)
-{
- EASY_FUNCTION(profiler::colors::Navy);
- return i * i * i - i / 10 + (OBJECTS - i) * 7 ;
-}
-
-void calcBrain(){
- EASY_FUNCTION(profiler::colors::LightBlue);
- double* intarray = new double[OBJECTS];
- for (int i = 0; i < OBJECTS; ++i)
- intarray[i] = calcSubbrain(i) + double(i * 180 / 3);
- delete[] intarray;
- //std::this_thread::sleep_for(std::chrono::milliseconds(3));
-}
-
-void calculateBehavior(){
- EASY_FUNCTION(profiler::colors::Blue);
- calcPhys();
- calcBrain();
-}
-
-void modellingStep(){
- EASY_FUNCTION();
- prepareMath();
- calculateBehavior();
-}
-
-void prepareRender(){
- EASY_FUNCTION(profiler::colors::Brick);
- localSleep();
- //std::this_thread::sleep_for(std::chrono::milliseconds(8));
-
-}
-
-int multPhys(int i)
-{
- EASY_FUNCTION(profiler::colors::Red700, profiler::ON);
- return i * i * i * i / 100;
-}
-
-int calcPhysicForObject(int i)
-{
- EASY_FUNCTION(profiler::colors::Red);
- return multPhys(i) + i / 3 - (OBJECTS - i) * 15;
-}
-
-void calculatePhysics(){
- EASY_FUNCTION(profiler::colors::Red);
- unsigned int* intarray = new unsigned int[OBJECTS];
- for (int i = 0; i < OBJECTS; ++i)
- intarray[i] = calcPhysicForObject(i);
- delete[] intarray;
- //std::this_thread::sleep_for(std::chrono::milliseconds(8));
-}
-
-void frame(){
- EASY_FUNCTION(profiler::colors::Magenta);
- prepareRender();
- calculatePhysics();
-}
-
-void loadingResourcesThread(){
- //std::unique_lock lk(cv_m);
- //cv.wait(lk, []{return g_i == 1; });
- EASY_THREAD("Resource loading");
-#ifdef SAMPLE_NETWORK_TEST
- while (true) {
-#else
- for(int i = 0; i < RESOURCE_LOADING_COUNT; i++){
-#endif
- loadingResources();
- EASY_EVENT("Resources Loading!", profiler::colors::Cyan);
- localSleep(1200000);
- //std::this_thread::sleep_for(std::chrono::milliseconds(20));
- }
-}
-
-void modellingThread(){
- //std::unique_lock lk(cv_m);
- //cv.wait(lk, []{return g_i == 1; });
- EASY_THREAD("Modelling");
-#ifdef SAMPLE_NETWORK_TEST
- while (true) {
-#else
- for (int i = 0; i < MODELLING_STEPS; i++){
-#endif
- modellingStep();
- localSleep(1200000);
- //std::this_thread::sleep_for(std::chrono::milliseconds(20));
- }
-}
-
-void renderThread(){
- //std::unique_lock lk(cv_m);
- //cv.wait(lk, []{return g_i == 1; });
- EASY_THREAD("Render");
-#ifdef SAMPLE_NETWORK_TEST
- while (true) {
-#else
- for (int i = 0; i < RENDER_STEPS; i++){
-#endif
- frame();
- localSleep(1200000);
- //std::this_thread::sleep_for(std::chrono::milliseconds(20));
- }
-}
-
-//////////////////////////////////////////////////////////////////////////
-
-int main(int argc, char* argv[])
-{
- if (argc > 1 && argv[1]){
- OBJECTS = std::atoi(argv[1]);
- }
- if (argc > 2 && argv[2]){
- MODELLING_STEPS = std::atoi(argv[2]);
- }
- if (argc > 3 && argv[3]){
- RENDER_STEPS = std::atoi(argv[3]);
- }
- if (argc > 4 && argv[4]){
- RESOURCE_LOADING_COUNT = std::atoi(argv[4]);
- }
-
- std::cout << "Objects count: " << OBJECTS << std::endl;
- std::cout << "Render steps: " << MODELLING_STEPS << std::endl;
- std::cout << "Modelling steps: " << RENDER_STEPS << std::endl;
- std::cout << "Resource loading count: " << RESOURCE_LOADING_COUNT << std::endl;
-
- auto start = std::chrono::system_clock::now();
-
-#ifndef SAMPLE_NETWORK_TEST
- EASY_PROFILER_ENABLE;
-#endif
-
- EASY_MAIN_THREAD;
- profiler::startListen();
-
- std::vector threads;
- for (int i=0; i < 3; i++) {
- threads.emplace_back(loadingResourcesThread);
- threads.emplace_back(renderThread);
- threads.emplace_back(modellingThread);
- }
-
- cv_m.lock();
- g_i = 1;
- cv_m.unlock();
- cv.notify_all();
-
- modellingThread();
-
- for(auto& t : threads)
- t.join();
-
- auto end = std::chrono::system_clock::now();
- auto elapsed =
- std::chrono::duration_cast(end - start);
-
- std::cout << "Elapsed time: " << elapsed.count() << " usec" << std::endl;
-
- auto blocks_count = profiler::dumpBlocksToFile("test.prof");
-
- std::cout << "Blocks count: " << blocks_count << std::endl;
-
- return 0;
-}
+//#define FULL_DISABLE_PROFILER
+#include "easy/profiler.h"
+#include
+#include
+#include
+#include
+#include
+#include "easy/reader.h"
+#include
+#include
+
+std::condition_variable cv;
+std::mutex cv_m;
+int g_i = 0;
+
+int OBJECTS = 500;
+int MODELLING_STEPS = 1500;
+int RENDER_STEPS = 1500;
+int RESOURCE_LOADING_COUNT = 50;
+
+//#define SAMPLE_NETWORK_TEST
+
+void localSleep(int magic=200000)
+{
+ //PROFILER_BEGIN_FUNCTION_BLOCK_GROUPED(profiler::colors::Blue);
+ volatile int i = 0;
+ for (; i < magic; ++i);
+}
+
+void loadingResources(){
+ EASY_FUNCTION(profiler::colors::DarkCyan);
+ localSleep();
+// std::this_thread::sleep_for(std::chrono::milliseconds(50));
+}
+
+void prepareMath(){
+ EASY_FUNCTION(profiler::colors::Green);
+ int* intarray = new int[OBJECTS];
+ for (int i = 0; i < OBJECTS; ++i)
+ intarray[i] = i * i;
+ delete[] intarray;
+ //std::this_thread::sleep_for(std::chrono::milliseconds(3));
+}
+
+void calcIntersect(){
+ EASY_FUNCTION(profiler::colors::Gold);
+ //int* intarray = new int[OBJECTS * OBJECTS];
+ int* intarray = new int[OBJECTS];
+ for (int i = 0; i < OBJECTS; ++i)
+ {
+ for (int j = i; j < OBJECTS; ++j)
+ //intarray[i * OBJECTS + j] = i * j - i / 2 + (OBJECTS - j) * 5;
+ intarray[j] = i * j - i / 2 + (OBJECTS - j) * 5;
+ }
+ delete[] intarray;
+ //std::this_thread::sleep_for(std::chrono::milliseconds(4));
+}
+
+double multModel(double i)
+{
+ EASY_FUNCTION(profiler::colors::PaleGold);
+ return i * sin(i) * cos(i);
+}
+
+void calcPhys(){
+ EASY_FUNCTION(profiler::colors::Amber);
+ double* intarray = new double[OBJECTS];
+ for (int i = 0; i < OBJECTS; ++i)
+ intarray[i] = multModel(double(i)) + double(i / 3) - double((OBJECTS - i) / 2);
+ calcIntersect();
+ delete[] intarray;
+}
+
+double calcSubbrain(int i)
+{
+ EASY_FUNCTION(profiler::colors::Navy);
+ return i * i * i - i / 10 + (OBJECTS - i) * 7 ;
+}
+
+void calcBrain(){
+ EASY_FUNCTION(profiler::colors::LightBlue);
+ double* intarray = new double[OBJECTS];
+ for (int i = 0; i < OBJECTS; ++i)
+ intarray[i] = calcSubbrain(i) + double(i * 180 / 3);
+ delete[] intarray;
+ //std::this_thread::sleep_for(std::chrono::milliseconds(3));
+}
+
+void calculateBehavior(){
+ EASY_FUNCTION(profiler::colors::Blue);
+ calcPhys();
+ calcBrain();
+}
+
+void modellingStep(){
+ EASY_FUNCTION();
+ prepareMath();
+ calculateBehavior();
+}
+
+void prepareRender(){
+ EASY_FUNCTION(profiler::colors::Brick);
+ localSleep();
+ //std::this_thread::sleep_for(std::chrono::milliseconds(8));
+
+}
+
+int multPhys(int i)
+{
+ EASY_FUNCTION(profiler::colors::Red700, profiler::ON);
+ return i * i * i * i / 100;
+}
+
+int calcPhysicForObject(int i)
+{
+ EASY_FUNCTION(profiler::colors::Red);
+ return multPhys(i) + i / 3 - (OBJECTS - i) * 15;
+}
+
+void calculatePhysics(){
+ EASY_FUNCTION(profiler::colors::Red);
+ unsigned int* intarray = new unsigned int[OBJECTS];
+ for (int i = 0; i < OBJECTS; ++i)
+ intarray[i] = calcPhysicForObject(i);
+ delete[] intarray;
+ //std::this_thread::sleep_for(std::chrono::milliseconds(8));
+}
+
+void frame(){
+ EASY_FUNCTION(profiler::colors::Magenta);
+ prepareRender();
+ calculatePhysics();
+}
+
+void loadingResourcesThread(){
+ //std::unique_lock lk(cv_m);
+ //cv.wait(lk, []{return g_i == 1; });
+ EASY_THREAD("Resource loading");
+#ifdef SAMPLE_NETWORK_TEST
+ while (true) {
+#else
+ for(int i = 0; i < RESOURCE_LOADING_COUNT; i++){
+#endif
+ loadingResources();
+ EASY_EVENT("Resources Loading!", profiler::colors::Cyan);
+ localSleep(1200000);
+ //std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ }
+}
+
+void modellingThread(){
+ //std::unique_lock lk(cv_m);
+ //cv.wait(lk, []{return g_i == 1; });
+ EASY_THREAD("Modelling");
+#ifdef SAMPLE_NETWORK_TEST
+ while (true) {
+#else
+ for (int i = 0; i < MODELLING_STEPS; i++){
+#endif
+ modellingStep();
+ localSleep(1200000);
+ //std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ }
+}
+
+void renderThread(){
+ //std::unique_lock lk(cv_m);
+ //cv.wait(lk, []{return g_i == 1; });
+ EASY_THREAD("Render");
+#ifdef SAMPLE_NETWORK_TEST
+ while (true) {
+#else
+ for (int i = 0; i < RENDER_STEPS; i++){
+#endif
+ frame();
+ localSleep(1200000);
+ //std::this_thread::sleep_for(std::chrono::milliseconds(20));
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char* argv[])
+{
+ if (argc > 1 && argv[1]){
+ OBJECTS = std::atoi(argv[1]);
+ }
+ if (argc > 2 && argv[2]){
+ MODELLING_STEPS = std::atoi(argv[2]);
+ }
+ if (argc > 3 && argv[3]){
+ RENDER_STEPS = std::atoi(argv[3]);
+ }
+ if (argc > 4 && argv[4]){
+ RESOURCE_LOADING_COUNT = std::atoi(argv[4]);
+ }
+
+ std::cout << "Objects count: " << OBJECTS << std::endl;
+ std::cout << "Render steps: " << MODELLING_STEPS << std::endl;
+ std::cout << "Modelling steps: " << RENDER_STEPS << std::endl;
+ std::cout << "Resource loading count: " << RESOURCE_LOADING_COUNT << std::endl;
+
+ auto start = std::chrono::system_clock::now();
+
+#ifndef SAMPLE_NETWORK_TEST
+ EASY_PROFILER_ENABLE;
+#endif
+
+ EASY_MAIN_THREAD;
+ profiler::startListen();
+
+ std::vector threads;
+ for (int i=0; i < 3; i++) {
+ threads.emplace_back(loadingResourcesThread);
+ threads.emplace_back(renderThread);
+ threads.emplace_back(modellingThread);
+ }
+
+ cv_m.lock();
+ g_i = 1;
+ cv_m.unlock();
+ cv.notify_all();
+
+ modellingThread();
+
+ for(auto& t : threads)
+ t.join();
+
+ auto end = std::chrono::system_clock::now();
+ auto elapsed =
+ std::chrono::duration_cast(end - start);
+
+ std::cout << "Elapsed time: " << elapsed.count() << " usec" << std::endl;
+
+ auto blocks_count = profiler::dumpBlocksToFile("test.prof");
+
+ std::cout << "Blocks count: " << blocks_count << std::endl;
+
+ return 0;
+}