1 /************************************************************************
2 * file name         : profile_manager.cpp
3 * ----------------- :
4 * creation time     : 2016/02/16
5 * authors           : Sergey Yagovtsev, Victor Zarubkin
6 * emails            : yse.sey@gmail.com, v.s.zarubkin@gmail.com
7 * ----------------- :
8 * description       : The file contains implementation of Profile manager and implement access c-function
9 *                   :
10 * license           : Lightweight profiler library for c++
11 *                   : Copyright(C) 2016-2017  Sergey Yagovtsev, Victor Zarubkin
12 *                   :
13 *                   : Licensed under either of
14 *                   :     * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
15 *                   :     * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
16 *                   : at your option.
17 *                   :
18 *                   : The MIT License
19 *                   :
20 *                   : Permission is hereby granted, free of charge, to any person obtaining a copy
21 *                   : of this software and associated documentation files (the "Software"), to deal
22 *                   : in the Software without restriction, including without limitation the rights
23 *                   : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
24 *                   : of the Software, and to permit persons to whom the Software is furnished
25 *                   : to do so, subject to the following conditions:
26 *                   :
27 *                   : The above copyright notice and this permission notice shall be included in all
28 *                   : copies or substantial portions of the Software.
29 *                   :
30 *                   : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
31 *                   : INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
32 *                   : PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33 *                   : LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
34 *                   : TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
35 *                   : USE OR OTHER DEALINGS IN THE SOFTWARE.
36 *                   :
37 *                   : The Apache License, Version 2.0 (the "License")
38 *                   :
39 *                   : You may not use this file except in compliance with the License.
40 *                   : You may obtain a copy of the License at
41 *                   :
42 *                   : http://www.apache.org/licenses/LICENSE-2.0
43 *                   :
44 *                   : Unless required by applicable law or agreed to in writing, software
45 *                   : distributed under the License is distributed on an "AS IS" BASIS,
46 *                   : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
47 *                   : See the License for the specific language governing permissions and
48 *                   : limitations under the License.
49 ************************************************************************/
50 
51 #include <algorithm>
52 #include <fstream>
53 #include <future>
54 #include "profile_manager.h"
55 
56 #include <easy/profiler.h>
57 #include <easy/arbitrary_value.h>
58 #include <easy/serialized_block.h>
59 #include <easy/easy_net.h>
60 
61 #ifndef _WIN32
62 # include <easy/easy_socket.h>
63 #endif
64 
65 #include "event_trace_win.h"
66 #include "current_time.h"
67 #include "current_thread.h"
68 
69 #ifdef __APPLE__
70 #include <mach/clock.h>
71 #include <mach/mach.h>
72 #endif
73 
74 #if EASY_OPTION_LOG_ENABLED != 0
75 # include <iostream>
76 
77 # ifndef EASY_ERRORLOG
78 #  define EASY_ERRORLOG ::std::cerr
79 # endif
80 
81 # ifndef EASY_LOG
82 #  define EASY_LOG ::std::cerr
83 # endif
84 
85 # ifndef EASY_ERROR
86 #  define EASY_ERROR(LOG_MSG) EASY_ERRORLOG << "EasyProfiler ERROR: " << LOG_MSG
87 # endif
88 
89 # ifndef EASY_WARNING
90 #  define EASY_WARNING(LOG_MSG) EASY_ERRORLOG << "EasyProfiler WARNING: " << LOG_MSG
91 # endif
92 
93 # ifndef EASY_LOGMSG
94 #  define EASY_LOGMSG(LOG_MSG) EASY_LOG << "EasyProfiler INFO: " << LOG_MSG
95 # endif
96 
97 # ifndef EASY_LOG_ONLY
98 #  define EASY_LOG_ONLY(CODE) CODE
99 # endif
100 
101 #else
102 
103 # ifndef EASY_ERROR
104 #  define EASY_ERROR(LOG_MSG)
105 # endif
106 
107 # ifndef EASY_WARNING
108 #  define EASY_WARNING(LOG_MSG)
109 # endif
110 
111 # ifndef EASY_LOGMSG
112 #  define EASY_LOGMSG(LOG_MSG)
113 # endif
114 
115 # ifndef EASY_LOG_ONLY
116 #  define EASY_LOG_ONLY(CODE)
117 # endif
118 
119 #endif
120 
121 #ifdef min
122 # undef min
123 #endif
124 
125 #ifndef EASY_ENABLE_BLOCK_STATUS
126 # define EASY_ENABLE_BLOCK_STATUS 1
127 #endif
128 
129 #if !defined(_WIN32) && !defined(EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS)
130 # define EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS 0
131 #endif
132 
133 #ifndef EASY_OPTION_IMPLICIT_THREAD_REGISTRATION
134 # define EASY_OPTION_IMPLICIT_THREAD_REGISTRATION 0
135 #endif
136 
137 //////////////////////////////////////////////////////////////////////////
138 //////////////////////////////////////////////////////////////////////////
139 
140 using namespace profiler;
141 
142 //////////////////////////////////////////////////////////////////////////
143 
144 #if !defined(EASY_PROFILER_VERSION_MAJOR) || !defined(EASY_PROFILER_VERSION_MINOR) || !defined(EASY_PROFILER_VERSION_PATCH)
145 # ifdef _WIN32
146 #  error EASY_PROFILER_VERSION_MAJOR and EASY_PROFILER_VERSION_MINOR and EASY_PROFILER_VERSION_PATCH macros must be defined
147 # else
148 #  error "EASY_PROFILER_VERSION_MAJOR and EASY_PROFILER_VERSION_MINOR and EASY_PROFILER_VERSION_PATCH macros must be defined"
149 # endif
150 #endif
151 
152 # define EASY_PROFILER_PRODUCT_VERSION "v" EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MAJOR) "." \
153                                            EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MINOR) "." \
154                                            EASY_STRINGIFICATION(EASY_PROFILER_VERSION_PATCH)
155 
156 # define EASY_VERSION_INT(v_major, v_minor, v_patch) ((static_cast<uint32_t>(v_major) << 24) | (static_cast<uint32_t>(v_minor) << 16) | static_cast<uint32_t>(v_patch))
157 extern const uint32_t PROFILER_SIGNATURE = ('E' << 24) | ('a' << 16) | ('s' << 8) | 'y';
158 extern const uint32_t EASY_CURRENT_VERSION = EASY_VERSION_INT(EASY_PROFILER_VERSION_MAJOR, EASY_PROFILER_VERSION_MINOR, EASY_PROFILER_VERSION_PATCH);
159 # undef EASY_VERSION_INT
160 
161 //////////////////////////////////////////////////////////////////////////
162 
163 # define EASY_PROF_DISABLED 0
164 # define EASY_PROF_ENABLED 1
165 # define EASY_PROF_DUMP 2
166 
167 //////////////////////////////////////////////////////////////////////////
168 
169 //auto& MANAGER = ProfileManager::instance();
170 # define MANAGER ProfileManager::instance()
171 EASY_CONSTEXPR uint8_t FORCE_ON_FLAG = profiler::FORCE_ON & ~profiler::ON;
172 
173 #if defined(EASY_CHRONO_CLOCK)
174 #include <chrono>
175 const int64_t CPU_FREQUENCY = EASY_CHRONO_CLOCK::period::den / EASY_CHRONO_CLOCK::period::num;
176 # define TICKS_TO_US(ticks) ticks * 1000000LL / CPU_FREQUENCY
177 #elif defined(_WIN32)
__anon0a7745d40102()178 const decltype(LARGE_INTEGER::QuadPart) CPU_FREQUENCY = ([](){ LARGE_INTEGER freq; QueryPerformanceFrequency(&freq); return freq.QuadPart; })();
179 # define TICKS_TO_US(ticks) ticks * 1000000LL / CPU_FREQUENCY
180 #else
181 # ifndef __APPLE__
182 #  include <time.h>
183 # endif
calculate_cpu_frequency()184 int64_t calculate_cpu_frequency()
185 {
186     double g_TicksPerNanoSec;
187     uint64_t begin = 0, end = 0;
188 #ifdef __APPLE__
189     clock_serv_t cclock;
190     mach_timespec_t begints, endts;
191     host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
192     clock_get_time(cclock, &begints);
193 #else
194     struct timespec begints, endts;
195     clock_gettime(CLOCK_MONOTONIC, &begints);
196 #endif
197     begin = getCurrentTime();
198     volatile uint64_t i;
199     for (i = 0; i < 100000000; i++); /* must be CPU intensive */
200     end = getCurrentTime();
201 #ifdef __APPLE__
202     clock_get_time(cclock, &endts);
203     mach_port_deallocate(mach_task_self(), cclock);
204 #else
205     clock_gettime(CLOCK_MONOTONIC, &endts);
206 #endif
207     struct timespec tmpts;
208     const int NANO_SECONDS_IN_SEC = 1000000000;
209     tmpts.tv_sec = endts.tv_sec - begints.tv_sec;
210     tmpts.tv_nsec = endts.tv_nsec - begints.tv_nsec;
211     if (tmpts.tv_nsec < 0)
212     {
213         tmpts.tv_sec--;
214         tmpts.tv_nsec += NANO_SECONDS_IN_SEC;
215     }
216 
217     uint64_t nsecElapsed = tmpts.tv_sec * 1000000000LL + tmpts.tv_nsec;
218     g_TicksPerNanoSec = (double)(end - begin) / (double)nsecElapsed;
219 
220     int64_t cpu_frequency = int(g_TicksPerNanoSec * 1000000);
221 
222     return cpu_frequency;
223 }
224 
225 static std::atomic<int64_t> CPU_FREQUENCY = ATOMIC_VAR_INIT(1);
226 # define TICKS_TO_US(ticks) ticks * 1000 / CPU_FREQUENCY.load(std::memory_order_acquire)
227 #endif
228 
229 extern const profiler::color_t EASY_COLOR_INTERNAL_EVENT = 0xffffffff; // profiler::colors::White
230 EASY_CONSTEXPR profiler::color_t EASY_COLOR_THREAD_END = 0xff212121; // profiler::colors::Dark
231 EASY_CONSTEXPR profiler::color_t EASY_COLOR_START = 0xff4caf50; // profiler::colors::Green
232 EASY_CONSTEXPR profiler::color_t EASY_COLOR_END = 0xfff44336; // profiler::colors::Red
233 
234 //////////////////////////////////////////////////////////////////////////
235 
236 EASY_THREAD_LOCAL static ::ThreadStorage* THIS_THREAD = nullptr;
237 EASY_THREAD_LOCAL static bool THIS_THREAD_IS_MAIN = false;
238 
239 EASY_THREAD_LOCAL static profiler::timestamp_t THIS_THREAD_FRAME_T_MAX = 0ULL;
240 EASY_THREAD_LOCAL static profiler::timestamp_t THIS_THREAD_FRAME_T_CUR = 0ULL;
241 EASY_THREAD_LOCAL static profiler::timestamp_t THIS_THREAD_FRAME_T_ACC = 0ULL;
242 EASY_THREAD_LOCAL static uint32_t THIS_THREAD_N_FRAMES = 0;
243 EASY_THREAD_LOCAL static bool THIS_THREAD_FRAME_T_RESET_MAX = false;
244 EASY_THREAD_LOCAL static bool THIS_THREAD_FRAME_T_RESET_AVG = false;
245 
246 #ifdef EASY_CXX11_TLS_AVAILABLE
247 thread_local static profiler::ThreadGuard THIS_THREAD_GUARD; // thread guard for monitoring thread life time
248 #endif
249 
250 //////////////////////////////////////////////////////////////////////////
251 
252 #ifdef BUILD_WITH_EASY_PROFILER
253 # define EASY_EVENT_RES(res, name, ...)\
254     EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), MANAGER.addBlockDescriptor(\
255         ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\
256             __FILE__, __LINE__, ::profiler::BlockType::Event, ::profiler::extract_color(__VA_ARGS__)));\
257     res = MANAGER.storeBlock(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name))
258 
259 # define EASY_FORCE_EVENT(timestamp, name, ...)\
260     EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), addBlockDescriptor(\
261         ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\
262             __FILE__, __LINE__, ::profiler::BlockType::Event, ::profiler::extract_color(__VA_ARGS__)));\
263     storeBlockForce(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name), timestamp)
264 
265 # define EASY_FORCE_EVENT2(timestamp, name, ...)\
266     EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), addBlockDescriptor(\
267         ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\
268             __FILE__, __LINE__, ::profiler::BlockType::Event, ::profiler::extract_color(__VA_ARGS__)));\
269     storeBlockForce2(EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name), timestamp)
270 
271 # define EASY_FORCE_EVENT3(ts, timestamp, name, ...)\
272     EASY_LOCAL_STATIC_PTR(const ::profiler::BaseBlockDescriptor*, EASY_UNIQUE_DESC(__LINE__), addBlockDescriptor(\
273         ::profiler::extract_enable_flag(__VA_ARGS__), EASY_UNIQUE_LINE_ID, EASY_COMPILETIME_NAME(name),\
274             __FILE__, __LINE__, ::profiler::BlockType::Event, ::profiler::extract_color(__VA_ARGS__)));\
275     storeBlockForce2(ts, EASY_UNIQUE_DESC(__LINE__), EASY_RUNTIME_NAME(name), timestamp)
276 #else
277 # ifndef EASY_PROFILER_API_DISABLED
278 #  define EASY_PROFILER_API_DISABLED
279 # endif
280 # define EASY_EVENT_RES(res, name, ...)
281 # define EASY_FORCE_EVENT(timestamp, name, ...)
282 # define EASY_FORCE_EVENT2(timestamp, name, ...)
283 # define EASY_FORCE_EVENT3(ts, timestamp, name, ...)
284 #endif
285 
286 //////////////////////////////////////////////////////////////////////////
287 
288 extern "C" {
289 
290 #if !defined(EASY_PROFILER_API_DISABLED)
currentTime()291     PROFILER_API timestamp_t currentTime()
292     {
293         return getCurrentTime();
294     }
295 
toNanoseconds(timestamp_t _ticks)296     PROFILER_API timestamp_t toNanoseconds(timestamp_t _ticks)
297     {
298 #if defined(EASY_CHRONO_CLOCK) || defined(_WIN32)
299         return _ticks * 1000000000LL / CPU_FREQUENCY;
300 #else
301         return _ticks / CPU_FREQUENCY.load(std::memory_order_acquire);
302 #endif
303     }
304 
toMicroseconds(timestamp_t _ticks)305     PROFILER_API timestamp_t toMicroseconds(timestamp_t _ticks)
306     {
307         return TICKS_TO_US(_ticks);
308     }
309 
registerDescription(EasyBlockStatus _status,const char * _autogenUniqueId,const char * _name,const char * _filename,int _line,block_type_t _block_type,color_t _color,bool _copyName)310     PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus _status, const char* _autogenUniqueId, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color, bool _copyName)
311     {
312         return MANAGER.addBlockDescriptor(_status, _autogenUniqueId, _name, _filename, _line, _block_type, _color, _copyName);
313     }
314 
endBlock()315     PROFILER_API void endBlock()
316     {
317         MANAGER.endBlock();
318     }
319 
setEnabled(bool isEnable)320     PROFILER_API void setEnabled(bool isEnable)
321     {
322         MANAGER.setEnabled(isEnable);
323     }
324 
isEnabled()325     PROFILER_API bool isEnabled()
326     {
327         return MANAGER.isEnabled();
328     }
329 
storeValue(const BaseBlockDescriptor * _desc,DataType _type,const void * _data,size_t _size,bool _isArray,ValueId _vin)330     PROFILER_API void storeValue(const BaseBlockDescriptor* _desc, DataType _type, const void* _data, size_t _size, bool _isArray, ValueId _vin)
331     {
332         MANAGER.storeValue(_desc, _type, _data, _size, _isArray, _vin);
333     }
334 
storeEvent(const BaseBlockDescriptor * _desc,const char * _runtimeName)335     PROFILER_API void storeEvent(const BaseBlockDescriptor* _desc, const char* _runtimeName)
336     {
337         MANAGER.storeBlock(_desc, _runtimeName);
338     }
339 
storeBlock(const BaseBlockDescriptor * _desc,const char * _runtimeName,timestamp_t _beginTime,timestamp_t _endTime)340     PROFILER_API void storeBlock(const BaseBlockDescriptor* _desc, const char* _runtimeName, timestamp_t _beginTime, timestamp_t _endTime)
341     {
342         MANAGER.storeBlock(_desc, _runtimeName, _beginTime, _endTime);
343     }
344 
beginBlock(Block & _block)345     PROFILER_API void beginBlock(Block& _block)
346     {
347         MANAGER.beginBlock(_block);
348     }
349 
beginNonScopedBlock(const BaseBlockDescriptor * _desc,const char * _runtimeName)350     PROFILER_API void beginNonScopedBlock(const BaseBlockDescriptor* _desc, const char* _runtimeName)
351     {
352         MANAGER.beginNonScopedBlock(_desc, _runtimeName);
353     }
354 
dumpBlocksToFile(const char * filename)355     PROFILER_API uint32_t dumpBlocksToFile(const char* filename)
356     {
357         return MANAGER.dumpBlocksToFile(filename);
358     }
359 
registerThreadScoped(const char * name,ThreadGuard & threadGuard)360     PROFILER_API const char* registerThreadScoped(const char* name, ThreadGuard& threadGuard)
361     {
362         return MANAGER.registerThread(name, threadGuard);
363     }
364 
registerThread(const char * name)365     PROFILER_API const char* registerThread(const char* name)
366     {
367         return MANAGER.registerThread(name);
368     }
369 
setEventTracingEnabled(bool _isEnable)370     PROFILER_API void setEventTracingEnabled(bool _isEnable)
371     {
372         MANAGER.setEventTracingEnabled(_isEnable);
373     }
374 
isEventTracingEnabled()375     PROFILER_API bool isEventTracingEnabled()
376     {
377         return MANAGER.isEventTracingEnabled();
378     }
379 
380 # ifdef _WIN32
setLowPriorityEventTracing(bool _isLowPriority)381     PROFILER_API void setLowPriorityEventTracing(bool _isLowPriority)
382     {
383         EasyEventTracer::instance().setLowPriority(_isLowPriority);
384     }
385 
isLowPriorityEventTracing()386     PROFILER_API bool isLowPriorityEventTracing()
387     {
388         return EasyEventTracer::instance().isLowPriority();
389     }
390 # else
setLowPriorityEventTracing(bool)391     PROFILER_API void setLowPriorityEventTracing(bool) { }
isLowPriorityEventTracing()392     PROFILER_API bool isLowPriorityEventTracing() { return false; }
393 # endif
394 
setContextSwitchLogFilename(const char * name)395     PROFILER_API void setContextSwitchLogFilename(const char* name)
396     {
397         return MANAGER.setContextSwitchLogFilename(name);
398     }
399 
getContextSwitchLogFilename()400     PROFILER_API const char* getContextSwitchLogFilename()
401     {
402         return MANAGER.getContextSwitchLogFilename();
403     }
404 
startListen(uint16_t _port)405     PROFILER_API void   startListen(uint16_t _port)
406     {
407         return MANAGER.startListen(_port);
408     }
409 
stopListen()410     PROFILER_API void   stopListen()
411     {
412         return MANAGER.stopListen();
413     }
414 
isListening()415     PROFILER_API bool isListening()
416     {
417         return MANAGER.isListening();
418     }
419 
isMainThread()420     PROFILER_API bool isMainThread()
421     {
422         return THIS_THREAD_IS_MAIN;
423     }
424 
this_thread_frameTime(Duration _durationCast)425     PROFILER_API timestamp_t this_thread_frameTime(Duration _durationCast)
426     {
427         if (_durationCast == profiler::TICKS)
428             return THIS_THREAD_FRAME_T_CUR;
429         return TICKS_TO_US(THIS_THREAD_FRAME_T_CUR);
430     }
431 
this_thread_frameTimeLocalMax(Duration _durationCast)432     PROFILER_API timestamp_t this_thread_frameTimeLocalMax(Duration _durationCast)
433     {
434         THIS_THREAD_FRAME_T_RESET_MAX = true;
435         if (_durationCast == profiler::TICKS)
436             return THIS_THREAD_FRAME_T_MAX;
437         return TICKS_TO_US(THIS_THREAD_FRAME_T_MAX);
438     }
439 
this_thread_frameTimeLocalAvg(Duration _durationCast)440     PROFILER_API timestamp_t this_thread_frameTimeLocalAvg(Duration _durationCast)
441     {
442         THIS_THREAD_FRAME_T_RESET_AVG = true;
443         auto avgDuration = THIS_THREAD_N_FRAMES > 0 ? THIS_THREAD_FRAME_T_ACC / THIS_THREAD_N_FRAMES : 0;
444         if (_durationCast == profiler::TICKS)
445             return avgDuration;
446         return TICKS_TO_US(avgDuration);
447     }
448 
main_thread_frameTime(Duration _durationCast)449     PROFILER_API timestamp_t main_thread_frameTime(Duration _durationCast)
450     {
451         const auto ticks = THIS_THREAD_IS_MAIN ? THIS_THREAD_FRAME_T_CUR : MANAGER.curFrameDuration();
452         if (_durationCast == profiler::TICKS)
453             return ticks;
454         return TICKS_TO_US(ticks);
455     }
456 
main_thread_frameTimeLocalMax(Duration _durationCast)457     PROFILER_API timestamp_t main_thread_frameTimeLocalMax(Duration _durationCast)
458     {
459         if (THIS_THREAD_IS_MAIN)
460         {
461             THIS_THREAD_FRAME_T_RESET_MAX = true;
462             if (_durationCast == profiler::TICKS)
463                 return THIS_THREAD_FRAME_T_MAX;
464             return TICKS_TO_US(THIS_THREAD_FRAME_T_MAX);
465         }
466 
467         if (_durationCast == profiler::TICKS)
468             return MANAGER.maxFrameDuration();
469         return TICKS_TO_US(MANAGER.maxFrameDuration());
470     }
471 
main_thread_frameTimeLocalAvg(Duration _durationCast)472     PROFILER_API timestamp_t main_thread_frameTimeLocalAvg(Duration _durationCast)
473     {
474         if (THIS_THREAD_IS_MAIN)
475         {
476             THIS_THREAD_FRAME_T_RESET_AVG = true;
477             auto avgDuration = THIS_THREAD_N_FRAMES > 0 ? THIS_THREAD_FRAME_T_ACC / THIS_THREAD_N_FRAMES : 0;
478             if (_durationCast == profiler::TICKS)
479                 return avgDuration;
480             return TICKS_TO_US(avgDuration);
481         }
482 
483         if (_durationCast == profiler::TICKS)
484             return MANAGER.avgFrameDuration();
485         return TICKS_TO_US(MANAGER.avgFrameDuration());
486     }
487 
488 #else
489     PROFILER_API timestamp_t currentTime() { return 0; }
490     PROFILER_API timestamp_t toNanoseconds(timestamp_t) { return 0; }
491     PROFILER_API timestamp_t toMicroseconds(timestamp_t) { return 0; }
492     PROFILER_API const BaseBlockDescriptor* registerDescription(EasyBlockStatus, const char*, const char*, const char*, int, block_type_t, color_t, bool) { return reinterpret_cast<const BaseBlockDescriptor*>(0xbad); }
493     PROFILER_API void endBlock() { }
494     PROFILER_API void setEnabled(bool) { }
495     PROFILER_API bool isEnabled() { return false; }
496     PROFILER_API void storeValue(const BaseBlockDescriptor*, DataType, const void*, size_t, bool, ValueId) {}
497     PROFILER_API void storeEvent(const BaseBlockDescriptor*, const char*) { }
498     PROFILER_API void storeBlock(const BaseBlockDescriptor*, const char*, timestamp_t, timestamp_t) { }
499     PROFILER_API void beginBlock(Block&) { }
500     PROFILER_API void beginNonScopedBlock(const BaseBlockDescriptor*, const char*) { }
501     PROFILER_API uint32_t dumpBlocksToFile(const char*) { return 0; }
502     PROFILER_API const char* registerThreadScoped(const char*, ThreadGuard&) { return ""; }
503     PROFILER_API const char* registerThread(const char*) { return ""; }
504     PROFILER_API void setEventTracingEnabled(bool) { }
505     PROFILER_API bool isEventTracingEnabled() { return false; }
506     PROFILER_API void setLowPriorityEventTracing(bool) { }
507     PROFILER_API bool isLowPriorityEventTracing(bool) { return false; }
508     PROFILER_API void setContextSwitchLogFilename(const char*) { }
509     PROFILER_API const char* getContextSwitchLogFilename() { return ""; }
510     PROFILER_API void startListen(uint16_t) { }
511     PROFILER_API void stopListen() { }
512     PROFILER_API bool isListening() { return false; }
513 
514     PROFILER_API bool isMainThread() { return false; }
515     PROFILER_API timestamp_t this_thread_frameTime(Duration) { return 0; }
516     PROFILER_API timestamp_t this_thread_frameTimeLocalMax(Duration) { return 0; }
517     PROFILER_API timestamp_t this_thread_frameTimeLocalAvg(Duration) { return 0; }
518     PROFILER_API timestamp_t main_thread_frameTime(Duration) { return 0; }
519     PROFILER_API timestamp_t main_thread_frameTimeLocalMax(Duration) { return 0; }
520     PROFILER_API timestamp_t main_thread_frameTimeLocalAvg(Duration) { return 0; }
521 #endif
522 
versionMajor()523     PROFILER_API uint8_t versionMajor()
524     {
525         static_assert(0 <= EASY_PROFILER_VERSION_MAJOR && EASY_PROFILER_VERSION_MAJOR <= 255, "EASY_PROFILER_VERSION_MAJOR must be defined in range [0, 255]");
526         return EASY_PROFILER_VERSION_MAJOR;
527     }
528 
versionMinor()529     PROFILER_API uint8_t versionMinor()
530     {
531         static_assert(0 <= EASY_PROFILER_VERSION_MINOR && EASY_PROFILER_VERSION_MINOR <= 255, "EASY_PROFILER_VERSION_MINOR must be defined in range [0, 255]");
532         return EASY_PROFILER_VERSION_MINOR;
533     }
534 
versionPatch()535     PROFILER_API uint16_t versionPatch()
536     {
537         static_assert(0 <= EASY_PROFILER_VERSION_PATCH && EASY_PROFILER_VERSION_PATCH <= 65535, "EASY_PROFILER_VERSION_PATCH must be defined in range [0, 65535]");
538         return EASY_PROFILER_VERSION_PATCH;
539     }
540 
version()541     PROFILER_API uint32_t version()
542     {
543         return EASY_CURRENT_VERSION;
544     }
545 
versionName()546     PROFILER_API const char* versionName()
547     {
548         return EASY_PROFILER_PRODUCT_VERSION
549 #ifdef EASY_PROFILER_API_DISABLED
550             "_disabled"
551 #endif
552             ;
553     }
554 
555 }
556 
557 //////////////////////////////////////////////////////////////////////////
558 
SerializedBlock(const Block & block,uint16_t name_length)559 SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length)
560     : BaseBlockData(block)
561 {
562     char* pName = const_cast<char*>(name());
563     if (name_length) strncpy(pName, block.name(), name_length);
564     pName[name_length] = 0;
565 }
566 
SerializedCSwitch(const CSwitchBlock & block,uint16_t name_length)567 SerializedCSwitch::SerializedCSwitch(const CSwitchBlock& block, uint16_t name_length)
568     : CSwitchEvent(block)
569 {
570     char* pName = const_cast<char*>(name());
571     if (name_length) strncpy(pName, block.name(), name_length);
572     pName[name_length] = 0;
573 }
574 
575 //////////////////////////////////////////////////////////////////////////
576 
BaseBlockDescriptor(block_id_t _id,EasyBlockStatus _status,int _line,block_type_t _block_type,color_t _color)577 BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, EasyBlockStatus _status, int _line,
578                                          block_type_t _block_type, color_t _color) EASY_NOEXCEPT
579     : m_id(_id)
580     , m_line(_line)
581     , m_type(_block_type)
582     , m_color(_color)
583     , m_status(_status)
584 {
585 
586 }
587 
588 //////////////////////////////////////////////////////////////////////////
589 
590 #ifndef EASY_BLOCK_DESC_FULL_COPY
591 # define EASY_BLOCK_DESC_FULL_COPY 1
592 #endif
593 
594 #if EASY_BLOCK_DESC_FULL_COPY == 0
595 # define EASY_BLOCK_DESC_STRING const char*
596 # define EASY_BLOCK_DESC_STRING_LEN(s) static_cast<uint16_t>(strlen(s) + 1)
597 # define EASY_BLOCK_DESC_STRING_VAL(s) s
598 #else
599 # define EASY_BLOCK_DESC_STRING std::string
600 # define EASY_BLOCK_DESC_STRING_LEN(s) static_cast<uint16_t>(s.size() + 1)
601 # define EASY_BLOCK_DESC_STRING_VAL(s) s.c_str()
602 #endif
603 
604 class BlockDescriptor : public BaseBlockDescriptor
605 {
606     friend ProfileManager;
607 
608     EASY_BLOCK_DESC_STRING m_filename; ///< Source file name where this block is declared
609     EASY_BLOCK_DESC_STRING     m_name; ///< Static name of all blocks of the same type (blocks can have dynamic name) which is, in pair with descriptor id, a unique block identifier
610 
611 public:
612 
BlockDescriptor(block_id_t _id,EasyBlockStatus _status,const char * _name,const char * _filename,int _line,block_type_t _block_type,color_t _color)613     BlockDescriptor(block_id_t _id, EasyBlockStatus _status, const char* _name, const char* _filename, int _line, block_type_t _block_type, color_t _color)
614         : BaseBlockDescriptor(_id, _status, _line, _block_type, _color)
615         , m_filename(_filename)
616         , m_name(_name)
617     {
618     }
619 
name() const620     const char* name() const {
621         return EASY_BLOCK_DESC_STRING_VAL(m_name);
622     }
623 
filename() const624     const char* filename() const {
625         return EASY_BLOCK_DESC_STRING_VAL(m_filename);
626     }
627 
nameSize() const628     uint16_t nameSize() const {
629         return EASY_BLOCK_DESC_STRING_LEN(m_name);
630     }
631 
filenameSize() const632     uint16_t filenameSize() const {
633         return EASY_BLOCK_DESC_STRING_LEN(m_filename);
634     }
635 
636 }; // END of class BlockDescriptor.
637 
638 //////////////////////////////////////////////////////////////////////////
639 
~ThreadGuard()640 ThreadGuard::~ThreadGuard()
641 {
642 #ifndef EASY_PROFILER_API_DISABLED
643     if (m_id != 0 && THIS_THREAD != nullptr && THIS_THREAD->id == m_id)
644     {
645         bool isMarked = false;
646         EASY_EVENT_RES(isMarked, "ThreadFinished", EASY_COLOR_THREAD_END, ::profiler::FORCE_ON);
647         THIS_THREAD->profiledFrameOpened.store(false, std::memory_order_release);
648         THIS_THREAD->expired.store(isMarked ? 2 : 1, std::memory_order_release);
649         THIS_THREAD = nullptr;
650     }
651 #endif
652 }
653 
654 //////////////////////////////////////////////////////////////////////////
655 
ProfileManager()656 ProfileManager::ProfileManager() :
657 #ifdef _WIN32
658     m_processId(GetProcessId(GetCurrentProcess()))
659 #else
660     m_processId((processid_t)getpid())
661 #endif
662     , m_usedMemorySize(0)
663     , m_beginTime(0)
664     , m_endTime(0)
665 {
666     m_profilerStatus = ATOMIC_VAR_INIT(EASY_PROF_DISABLED);
667     m_isEventTracingEnabled = ATOMIC_VAR_INIT(EASY_OPTION_EVENT_TRACING_ENABLED);
668     m_isAlreadyListening = ATOMIC_VAR_INIT(false);
669     m_stopDumping = ATOMIC_VAR_INIT(false);
670     m_stopListen = ATOMIC_VAR_INIT(false);
671 
672     m_mainThreadId = ATOMIC_VAR_INIT(0);
673     m_frameMax = ATOMIC_VAR_INIT(0);
674     m_frameAvg = ATOMIC_VAR_INIT(0);
675     m_frameCur = ATOMIC_VAR_INIT(0);
676     m_frameMaxReset = ATOMIC_VAR_INIT(false);
677     m_frameAvgReset = ATOMIC_VAR_INIT(false);
678 
679 #if !defined(EASY_PROFILER_API_DISABLED) && EASY_OPTION_START_LISTEN_ON_STARTUP != 0
680     startListen(profiler::DEFAULT_PORT);
681 #endif
682 
683 #if !defined(EASY_PROFILER_API_DISABLED) && !defined(EASY_CHRONO_CLOCK) && !defined(_WIN32)
684     const int64_t cpu_frequency = calculate_cpu_frequency();
685     CPU_FREQUENCY.store(cpu_frequency, std::memory_order_release);
686 #endif
687 }
688 
~ProfileManager()689 ProfileManager::~ProfileManager()
690 {
691 #ifndef EASY_PROFILER_API_DISABLED
692     stopListen();
693 #endif
694 
695     for (auto desc : m_descriptors) {
696 #if EASY_BLOCK_DESC_FULL_COPY == 0
697         if (desc)
698             desc->~BlockDescriptor();
699         free(desc);
700 #else
701         delete desc;
702 #endif
703     }
704 }
705 
706 #ifndef EASY_MAGIC_STATIC_AVAILABLE
707 class ProfileManagerInstance {
708     friend ProfileManager;
709     ProfileManager instance;
710 } PROFILE_MANAGER;
711 #endif
712 
713 //////////////////////////////////////////////////////////////////////////
714 
instance()715 ProfileManager& ProfileManager::instance()
716 {
717 #ifndef EASY_MAGIC_STATIC_AVAILABLE
718     return PROFILE_MANAGER.instance;
719 #else
720     ///C++11 makes possible to create Singleton without any warry about thread-safeness
721     ///http://preshing.com/20130930/double-checked-locking-is-fixed-in-cpp11/
722     static ProfileManager profileManager;
723     return profileManager;
724 #endif
725 }
726 
727 //////////////////////////////////////////////////////////////////////////
728 
_threadStorage(profiler::thread_id_t _thread_id)729 ThreadStorage& ProfileManager::_threadStorage(profiler::thread_id_t _thread_id)
730 {
731     return m_threads[_thread_id];
732 }
733 
_findThreadStorage(profiler::thread_id_t _thread_id)734 ThreadStorage* ProfileManager::_findThreadStorage(profiler::thread_id_t _thread_id)
735 {
736     auto it = m_threads.find(_thread_id);
737     return it != m_threads.end() ? &it->second : nullptr;
738 }
739 
740 //////////////////////////////////////////////////////////////////////////
741 
addBlockDescriptor(EasyBlockStatus _defaultStatus,const char * _autogenUniqueId,const char * _name,const char * _filename,int _line,block_type_t _block_type,color_t _color,bool _copyName)742 const BaseBlockDescriptor* ProfileManager::addBlockDescriptor(EasyBlockStatus _defaultStatus,
743                                                         const char* _autogenUniqueId,
744                                                         const char* _name,
745                                                         const char* _filename,
746                                                         int _line,
747                                                         block_type_t _block_type,
748                                                         color_t _color,
749                                                         bool _copyName)
750 {
751     guard_lock_t lock(m_storedSpin);
752 
753     descriptors_map_t::key_type key(_autogenUniqueId);
754     auto it = m_descriptorsMap.find(key);
755     if (it != m_descriptorsMap.end())
756         return m_descriptors[it->second];
757 
758     const auto nameLen = strlen(_name);
759     m_usedMemorySize += sizeof(profiler::SerializedBlockDescriptor) + nameLen + strlen(_filename) + 2;
760 
761 #if EASY_BLOCK_DESC_FULL_COPY == 0
762     BlockDescriptor* desc = nullptr;
763 
764     if (_copyName)
765     {
766         void* data = malloc(sizeof(BlockDescriptor) + nameLen + 1);
767         char* name = reinterpret_cast<char*>(data) + sizeof(BlockDescriptor);
768         strncpy(name, _name, nameLen);
769         desc = ::new (data)BlockDescriptor(static_cast<block_id_t>(m_descriptors.size()), _defaultStatus, name, _filename, _line, _block_type, _color);
770     }
771     else
772     {
773         void* data = malloc(sizeof(BlockDescriptor));
774         desc = ::new (data)BlockDescriptor(static_cast<block_id_t>(m_descriptors.size()), _defaultStatus, _name, _filename, _line, _block_type, _color);
775     }
776 #else
777     auto desc = new BlockDescriptor(static_cast<block_id_t>(m_descriptors.size()), _defaultStatus, _name, _filename, _line, _block_type, _color);
778 #endif
779 
780     m_descriptors.emplace_back(desc);
781     m_descriptorsMap.emplace(key, desc->id());
782 
783     return desc;
784 }
785 
786 //////////////////////////////////////////////////////////////////////////
787 
storeValue(const BaseBlockDescriptor * _desc,DataType _type,const void * _data,size_t _size,bool _isArray,ValueId _vin)788 void ProfileManager::storeValue(const BaseBlockDescriptor* _desc, DataType _type, const void* _data, size_t _size, bool _isArray, ValueId _vin)
789 {
790     const auto state = m_profilerStatus.load(std::memory_order_acquire);
791     if (state != EASY_PROF_ENABLED || (_desc->m_status & profiler::ON) == 0)
792         return;
793 
794     if (THIS_THREAD == nullptr)
795         registerThread();
796 
797 #if EASY_ENABLE_BLOCK_STATUS != 0
798     if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0)
799         return;
800 #endif
801 
802     THIS_THREAD->storeValue(getCurrentTime(), _desc->id(), _type, _data, _size, _isArray, _vin);
803 }
804 
805 //////////////////////////////////////////////////////////////////////////
806 
storeBlock(const profiler::BaseBlockDescriptor * _desc,const char * _runtimeName)807 bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName)
808 {
809     const auto state = m_profilerStatus.load(std::memory_order_acquire);
810     if (state == EASY_PROF_DISABLED || (_desc->m_status & profiler::ON) == 0)
811         return false;
812 
813     if (state == EASY_PROF_DUMP)
814     {
815         if (THIS_THREAD == nullptr || THIS_THREAD->halt)
816             return false;
817     }
818     else if (THIS_THREAD == nullptr)
819     {
820         registerThread();
821     }
822 
823 #if EASY_ENABLE_BLOCK_STATUS != 0
824     if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0)
825         return false;
826 #endif
827 
828     const auto time = getCurrentTime();
829     THIS_THREAD->storeBlock(profiler::Block(time, time, _desc->id(), _runtimeName));
830 
831     return true;
832 }
833 
storeBlock(const profiler::BaseBlockDescriptor * _desc,const char * _runtimeName,profiler::timestamp_t _beginTime,profiler::timestamp_t _endTime)834 bool ProfileManager::storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime)
835 {
836     const auto state = m_profilerStatus.load(std::memory_order_acquire);
837     if (state == EASY_PROF_DISABLED || (_desc->m_status & profiler::ON) == 0)
838         return false;
839 
840     if (state == EASY_PROF_DUMP)
841     {
842         if (THIS_THREAD == nullptr || THIS_THREAD->halt)
843             return false;
844     }
845     else if (THIS_THREAD == nullptr)
846     {
847         registerThread();
848     }
849 
850 #if EASY_ENABLE_BLOCK_STATUS != 0
851     if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0)
852         return false;
853 #endif
854 
855     profiler::Block b(_beginTime, _endTime, _desc->id(), _runtimeName);
856     THIS_THREAD->storeBlock(b);
857     b.m_end = b.m_begin;
858 
859     return true;
860 }
861 
862 //////////////////////////////////////////////////////////////////////////
863 
storeBlockForce(const profiler::BaseBlockDescriptor * _desc,const char * _runtimeName,::profiler::timestamp_t & _timestamp)864 void ProfileManager::storeBlockForce(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t& _timestamp)
865 {
866     if ((_desc->m_status & profiler::ON) == 0)
867         return;
868 
869     if (THIS_THREAD == nullptr)
870         registerThread();
871 
872 #if EASY_ENABLE_BLOCK_STATUS != 0
873     if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0)
874         return;
875 #endif
876 
877     _timestamp = getCurrentTime();
878     THIS_THREAD->storeBlock(profiler::Block(_timestamp, _timestamp, _desc->id(), _runtimeName));
879 }
880 
storeBlockForce2(const profiler::BaseBlockDescriptor * _desc,const char * _runtimeName,::profiler::timestamp_t _timestamp)881 void ProfileManager::storeBlockForce2(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp)
882 {
883     if ((_desc->m_status & profiler::ON) == 0)
884         return;
885 
886     if (THIS_THREAD == nullptr)
887         registerThread();
888 
889 #if EASY_ENABLE_BLOCK_STATUS != 0
890     if (!THIS_THREAD->allowChildren && (_desc->m_status & FORCE_ON_FLAG) == 0)
891         return;
892 #endif
893 
894     THIS_THREAD->storeBlock(profiler::Block(_timestamp, _timestamp, _desc->id(), _runtimeName));
895 }
896 
storeBlockForce2(ThreadStorage & _registeredThread,const profiler::BaseBlockDescriptor * _desc,const char * _runtimeName,::profiler::timestamp_t _timestamp)897 void ProfileManager::storeBlockForce2(ThreadStorage& _registeredThread, const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp)
898 {
899     _registeredThread.storeBlock(profiler::Block(_timestamp, _timestamp, _desc->id(), _runtimeName));
900 }
901 
902 //////////////////////////////////////////////////////////////////////////
903 
beginBlock(Block & _block)904 void ProfileManager::beginBlock(Block& _block)
905 {
906     if (THIS_THREAD == nullptr)
907         registerThread();
908 
909     if (++THIS_THREAD->stackSize > 1)
910     {
911         _block.m_status = profiler::OFF;
912         THIS_THREAD->blocks.openedList.emplace_back(_block);
913         return;
914     }
915 
916     bool empty = true;
917     const auto state = m_profilerStatus.load(std::memory_order_acquire);
918     switch (state)
919     {
920         case EASY_PROF_DISABLED:
921         {
922             _block.m_status = profiler::OFF;
923             THIS_THREAD->halt = false;
924             THIS_THREAD->blocks.openedList.emplace_back(_block);
925             beginFrame();
926             return;
927         }
928 
929         case EASY_PROF_DUMP:
930         {
931             const bool halt = THIS_THREAD->halt;
932             if (halt || THIS_THREAD->blocks.openedList.empty())
933             {
934                 _block.m_status = profiler::OFF;
935                 THIS_THREAD->blocks.openedList.emplace_back(_block);
936 
937                 if (!halt)
938                 {
939                     THIS_THREAD->halt = true;
940                     beginFrame();
941                 }
942 
943                 return;
944             }
945 
946             empty = false;
947             break;
948         }
949 
950         default:
951         {
952             empty = THIS_THREAD->blocks.openedList.empty();
953             break;
954         }
955     }
956 
957     THIS_THREAD->stackSize = 0;
958     THIS_THREAD->halt = false;
959 
960     auto blockStatus = _block.m_status;
961 #if EASY_ENABLE_BLOCK_STATUS != 0
962     if (THIS_THREAD->allowChildren)
963     {
964 #endif
965         if (blockStatus & profiler::ON)
966             _block.start();
967 #if EASY_ENABLE_BLOCK_STATUS != 0
968         THIS_THREAD->allowChildren = ((blockStatus & profiler::OFF_RECURSIVE) == 0);
969     }
970     else if (blockStatus & FORCE_ON_FLAG)
971     {
972         _block.start();
973         _block.m_status = profiler::FORCE_ON_WITHOUT_CHILDREN;
974     }
975     else
976     {
977         _block.m_status = profiler::OFF_RECURSIVE;
978     }
979 #endif
980 
981     if (empty)
982     {
983         beginFrame();
984         THIS_THREAD->profiledFrameOpened.store(true, std::memory_order_release);
985     }
986 
987     THIS_THREAD->blocks.openedList.emplace_back(_block);
988 }
989 
beginNonScopedBlock(const profiler::BaseBlockDescriptor * _desc,const char * _runtimeName)990 void ProfileManager::beginNonScopedBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName)
991 {
992     if (THIS_THREAD == nullptr)
993         registerThread();
994 
995     NonscopedBlock& b = THIS_THREAD->nonscopedBlocks.push(_desc, _runtimeName, false);
996     beginBlock(b);
997     b.copyname();
998 }
999 
beginContextSwitch(profiler::thread_id_t _thread_id,profiler::timestamp_t _time,profiler::thread_id_t _target_thread_id,const char * _target_process,bool _lockSpin)1000 void ProfileManager::beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin)
1001 {
1002     auto ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
1003     if (ts != nullptr)
1004         // Dirty hack: _target_thread_id will be written to the field "block_id_t m_id"
1005         // and will be available calling method id().
1006         ts->sync.openedList.emplace_back(_time, _target_thread_id, _target_process);
1007 }
1008 
1009 //////////////////////////////////////////////////////////////////////////
1010 
endBlock()1011 void ProfileManager::endBlock()
1012 {
1013     if (--THIS_THREAD->stackSize > 0)
1014     {
1015         THIS_THREAD->popSilent();
1016         return;
1017     }
1018 
1019     THIS_THREAD->stackSize = 0;
1020     if (THIS_THREAD->halt || m_profilerStatus.load(std::memory_order_acquire) == EASY_PROF_DISABLED)
1021     {
1022         THIS_THREAD->popSilent();
1023         endFrame();
1024         return;
1025     }
1026 
1027     if (THIS_THREAD->blocks.openedList.empty())
1028         return;
1029 
1030     Block& top = THIS_THREAD->blocks.openedList.back();
1031     if (top.m_status & profiler::ON)
1032     {
1033         if (!top.finished())
1034             top.finish();
1035         THIS_THREAD->storeBlock(top);
1036     }
1037     else
1038     {
1039         top.m_end = top.m_begin; // this is to restrict endBlock() call inside ~Block()
1040     }
1041 
1042     if (!top.m_isScoped)
1043         THIS_THREAD->nonscopedBlocks.pop();
1044 
1045     THIS_THREAD->blocks.openedList.pop_back();
1046     const bool empty = THIS_THREAD->blocks.openedList.empty();
1047     if (empty)
1048     {
1049         THIS_THREAD->profiledFrameOpened.store(false, std::memory_order_release);
1050         endFrame();
1051 #if EASY_ENABLE_BLOCK_STATUS != 0
1052         THIS_THREAD->allowChildren = true;
1053     }
1054     else
1055     {
1056         THIS_THREAD->allowChildren =
1057             ((THIS_THREAD->blocks.openedList.back().get().m_status & profiler::OFF_RECURSIVE) == 0);
1058     }
1059 #else
1060     }
1061 #endif
1062 }
1063 
endContextSwitch(profiler::thread_id_t _thread_id,processid_t _process_id,profiler::timestamp_t _endtime,bool _lockSpin)1064 void ProfileManager::endContextSwitch(profiler::thread_id_t _thread_id, processid_t _process_id, profiler::timestamp_t _endtime, bool _lockSpin)
1065 {
1066     ThreadStorage* ts = nullptr;
1067     if (_process_id == m_processId)
1068     {
1069         // Implicit thread registration.
1070         // If thread owned by current process then create new ThreadStorage if there is no one
1071 #if EASY_OPTION_IMPLICIT_THREAD_REGISTRATION != 0
1072         ts = _lockSpin ? &threadStorage(_thread_id) : &_threadStorage(_thread_id);
1073 # if !defined(_WIN32) && !defined(EASY_CXX11_TLS_AVAILABLE)
1074 #  if EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS != 0
1075 #   pragma message "Warning: Implicit thread registration together with removing empty unguarded threads may cause application crash because there is no possibility to check thread state (dead or alive) for pthreads and removed ThreadStorage may be reused if thread is still alive."
1076 #  else
1077 #   pragma message "Warning: Implicit thread registration without removing empty unguarded threads may lead to memory leak because there is no possibility to check thread state (dead or alive) for pthreads."
1078 #  endif
1079 # endif
1080 #endif
1081     }
1082     else
1083     {
1084         // If thread owned by another process OR _process_id IS UNKNOWN then do not create ThreadStorage for this
1085         ts = _lockSpin ? findThreadStorage(_thread_id) : _findThreadStorage(_thread_id);
1086     }
1087 
1088     if (ts == nullptr || ts->sync.openedList.empty())
1089         return;
1090 
1091     CSwitchBlock& lastBlock = ts->sync.openedList.back();
1092     lastBlock.m_end = _endtime;
1093 
1094     ts->storeCSwitch(lastBlock);
1095     ts->sync.openedList.pop_back();
1096 }
1097 
1098 //////////////////////////////////////////////////////////////////////////
1099 
beginFrame()1100 void ProfileManager::beginFrame()
1101 {
1102     THIS_THREAD->beginFrame();
1103 }
1104 
endFrame()1105 void ProfileManager::endFrame()
1106 {
1107     if (!THIS_THREAD->frameOpened)
1108         return;
1109 
1110     const profiler::timestamp_t duration = THIS_THREAD->endFrame();
1111 
1112     if (THIS_THREAD_FRAME_T_RESET_MAX) THIS_THREAD_FRAME_T_MAX = 0;
1113     THIS_THREAD_FRAME_T_RESET_MAX = false;
1114 
1115     THIS_THREAD_FRAME_T_CUR = duration;
1116     if (duration > THIS_THREAD_FRAME_T_MAX)
1117         THIS_THREAD_FRAME_T_MAX = duration;
1118 
1119     THIS_THREAD_FRAME_T_RESET_AVG = THIS_THREAD_FRAME_T_RESET_AVG || THIS_THREAD_N_FRAMES > 10000;
1120 
1121     if (THIS_THREAD_IS_MAIN)
1122     {
1123         if (m_frameAvgReset.exchange(false, std::memory_order_release) || THIS_THREAD_FRAME_T_RESET_AVG)
1124         {
1125             if (THIS_THREAD_N_FRAMES > 0)
1126                 m_frameAvg.store(THIS_THREAD_FRAME_T_ACC / THIS_THREAD_N_FRAMES, std::memory_order_release);
1127             THIS_THREAD_FRAME_T_RESET_AVG = false;
1128             THIS_THREAD_FRAME_T_ACC = duration;
1129             THIS_THREAD_N_FRAMES = 1;
1130         }
1131         else
1132         {
1133             THIS_THREAD_FRAME_T_ACC += duration;
1134             ++THIS_THREAD_N_FRAMES;
1135             m_frameAvg.store(THIS_THREAD_FRAME_T_ACC / THIS_THREAD_N_FRAMES, std::memory_order_release);
1136         }
1137 
1138         const auto maxDuration = m_frameMax.load(std::memory_order_acquire);
1139         if (m_frameMaxReset.exchange(false, std::memory_order_release) || duration > maxDuration)
1140             m_frameMax.store(duration, std::memory_order_release);
1141 
1142         m_frameCur.store(duration, std::memory_order_release);
1143 
1144         return;
1145     }
1146 
1147     const auto reset = (uint32_t)!THIS_THREAD_FRAME_T_RESET_AVG;
1148     THIS_THREAD_FRAME_T_RESET_AVG = false;
1149     THIS_THREAD_N_FRAMES = 1 + reset * THIS_THREAD_N_FRAMES;
1150     THIS_THREAD_FRAME_T_ACC = duration + reset * THIS_THREAD_FRAME_T_ACC;
1151 }
1152 
maxFrameDuration()1153 profiler::timestamp_t ProfileManager::maxFrameDuration()
1154 {
1155     auto duration = m_frameMax.load(std::memory_order_acquire);
1156     m_frameMaxReset.store(true, std::memory_order_release);
1157     return duration;
1158 }
1159 
avgFrameDuration()1160 profiler::timestamp_t ProfileManager::avgFrameDuration()
1161 {
1162     auto duration = m_frameAvg.load(std::memory_order_acquire);
1163     m_frameAvgReset.store(true, std::memory_order_release);
1164     return duration;
1165 }
1166 
curFrameDuration() const1167 profiler::timestamp_t ProfileManager::curFrameDuration() const
1168 {
1169     return m_frameCur.load(std::memory_order_acquire);
1170 }
1171 
1172 //////////////////////////////////////////////////////////////////////////
1173 
enableEventTracer()1174 void ProfileManager::enableEventTracer()
1175 {
1176 #ifdef _WIN32
1177     if (m_isEventTracingEnabled.load(std::memory_order_acquire))
1178         EasyEventTracer::instance().enable(true);
1179 #endif
1180 }
1181 
disableEventTracer()1182 void ProfileManager::disableEventTracer()
1183 {
1184 #ifdef _WIN32
1185     EasyEventTracer::instance().disable();
1186 #endif
1187 }
1188 
setEnabled(bool isEnable)1189 void ProfileManager::setEnabled(bool isEnable)
1190 {
1191     guard_lock_t lock(m_dumpSpin);
1192 
1193     auto time = getCurrentTime();
1194     const auto status = isEnable ? EASY_PROF_ENABLED : EASY_PROF_DISABLED;
1195     const auto prev = m_profilerStatus.exchange(status, std::memory_order_release);
1196     if (prev == status)
1197         return;
1198 
1199     if (isEnable)
1200     {
1201         EASY_LOGMSG("Enabled profiling\n");
1202         enableEventTracer();
1203         m_beginTime = time;
1204     }
1205     else
1206     {
1207         EASY_LOGMSG("Disabled profiling\n");
1208         disableEventTracer();
1209         m_endTime = time;
1210     }
1211 }
1212 
isEnabled() const1213 bool ProfileManager::isEnabled() const
1214 {
1215     return m_profilerStatus.load(std::memory_order_acquire) == EASY_PROF_ENABLED;
1216 }
1217 
setEventTracingEnabled(bool _isEnable)1218 void ProfileManager::setEventTracingEnabled(bool _isEnable)
1219 {
1220     m_isEventTracingEnabled.store(_isEnable, std::memory_order_release);
1221 }
1222 
isEventTracingEnabled() const1223 bool ProfileManager::isEventTracingEnabled() const
1224 {
1225     return m_isEventTracingEnabled.load(std::memory_order_acquire);
1226 }
1227 
1228 //////////////////////////////////////////////////////////////////////////
1229 
checkThreadExpired(ThreadStorage & _registeredThread)1230 char ProfileManager::checkThreadExpired(ThreadStorage& _registeredThread)
1231 {
1232     const char val = _registeredThread.expired.load(std::memory_order_acquire);
1233     if (val != 0)
1234         return val;
1235 
1236     if (_registeredThread.guarded)
1237         return 0;
1238 
1239 #ifdef _WIN32
1240     // Check thread state for Windows
1241 
1242     DWORD exitCode = 0;
1243     auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)_registeredThread.id);
1244     if (hThread == nullptr || GetExitCodeThread(hThread, &exitCode) == FALSE || exitCode != STILL_ACTIVE)
1245     {
1246         // Thread has been expired
1247         _registeredThread.expired.store(1, std::memory_order_release);
1248         if (hThread != nullptr)
1249             CloseHandle(hThread);
1250         return 1;
1251     }
1252 
1253     if (hThread != nullptr)
1254         CloseHandle(hThread);
1255 
1256     return 0;
1257 #else
1258     // Check thread state for Linux and MacOS/iOS
1259 
1260     // This would drop the application if pthread already died
1261     //return pthread_kill(_registeredThread.pthread_id, 0) != 0 ? 1 : 0;
1262 
1263     // There is no function to check external pthread state in Linux! :((
1264 
1265 #ifndef EASY_CXX11_TLS_AVAILABLE
1266 #pragma message "Warning: Your compiler does not support thread_local C++11 feature. Please use EASY_THREAD_SCOPE as much as possible. Otherwise, there is a possibility of memory leak if there are a lot of rapidly created and destroyed threads."
1267 #endif
1268 
1269     return 0;
1270 #endif
1271 }
1272 
1273 //////////////////////////////////////////////////////////////////////////
1274 
dumpBlocksToStream(profiler::OStream & _outputStream,bool _lockSpin,bool _async)1275 uint32_t ProfileManager::dumpBlocksToStream(profiler::OStream& _outputStream, bool _lockSpin, bool _async)
1276 {
1277     EASY_LOGMSG("dumpBlocksToStream(_lockSpin = " << _lockSpin << ")...\n");
1278 
1279     if (_lockSpin)
1280         m_dumpSpin.lock();
1281 
1282     const auto state = m_profilerStatus.load(std::memory_order_acquire);
1283 
1284 #ifndef _WIN32
1285     const bool eventTracingEnabled = m_isEventTracingEnabled.load(std::memory_order_acquire);
1286 #endif
1287 
1288     if (state == EASY_PROF_ENABLED) {
1289         m_profilerStatus.store(EASY_PROF_DUMP, std::memory_order_release);
1290         disableEventTracer();
1291         m_endTime = getCurrentTime();
1292     }
1293 
1294 
1295     // This is to make sure that no new descriptors or new threads will be
1296     // added until we finish sending data.
1297     //m_spin.lock();
1298     // This is the only place using both spins, so no dead-lock will occur
1299 
1300     if (_async && m_stopDumping.load(std::memory_order_acquire))
1301     {
1302         if (_lockSpin)
1303             m_dumpSpin.unlock();
1304         return 0;
1305     }
1306 
1307     // Wait for some time to be sure that all operations which began before setEnabled(false) will be finished.
1308     // This is much better than inserting spin-lock or atomic variable check into each store operation.
1309     std::this_thread::sleep_for(std::chrono::milliseconds(20));
1310 
1311     // wait for all threads finish opened frames
1312     EASY_LOG_ONLY(bool logged = false);
1313     for (auto thread_it = m_threads.begin(), end = m_threads.end(); thread_it != end;)
1314     {
1315         if (_async && m_stopDumping.load(std::memory_order_acquire))
1316         {
1317             if (_lockSpin)
1318                 m_dumpSpin.unlock();
1319             return 0;
1320         }
1321 
1322         if (!thread_it->second.profiledFrameOpened.load(std::memory_order_acquire))
1323         {
1324             ++thread_it;
1325             EASY_LOG_ONLY(logged = false);
1326         }
1327         else
1328         {
1329             EASY_LOG_ONLY(
1330                 if (!logged)
1331                 {
1332                     logged = true;
1333                     if (thread_it->second.named)
1334                         EASY_WARNING("Waiting for thread \"" << thread_it->second.name << "\" finish opened frame (which is top EASY_BLOCK for this thread)...\n");
1335                     else
1336                         EASY_WARNING("Waiting for thread " << thread_it->first << " finish opened frame (which is top EASY_BLOCK for this thread)...\n");
1337                 }
1338             );
1339 
1340             std::this_thread::sleep_for(std::chrono::milliseconds(10));
1341         }
1342     }
1343 
1344     m_profilerStatus.store(EASY_PROF_DISABLED, std::memory_order_release);
1345 
1346     EASY_LOGMSG("All threads have closed frames\n");
1347     EASY_LOGMSG("Disabled profiling\n");
1348 
1349     m_spin.lock();
1350     m_storedSpin.lock();
1351     // TODO: think about better solution because this one is not 100% safe...
1352 
1353     const profiler::timestamp_t now = getCurrentTime();
1354     const profiler::timestamp_t endtime = m_endTime == 0 ? now : std::min(now, m_endTime);
1355 
1356 #ifndef _WIN32
1357     if (eventTracingEnabled)
1358     {
1359         // Read thread context switch events from temporary file
1360 
1361         if (_async && m_stopDumping.load(std::memory_order_acquire))
1362         {
1363             m_spin.unlock();
1364             m_storedSpin.unlock();
1365             if (_lockSpin)
1366                 m_dumpSpin.unlock();
1367             return 0;
1368         }
1369 
1370         EASY_LOGMSG("Writing context switch events...\n");
1371 
1372         uint64_t timestamp = 0;
1373         profiler::thread_id_t thread_from = 0, thread_to = 0;
1374 
1375         std::ifstream infile(m_csInfoFilename.c_str());
1376         if(infile.is_open())
1377         {
1378             EASY_LOG_ONLY(uint32_t num = 0);
1379             std::string next_task_name;
1380             pid_t process_to = 0;
1381             while (infile >> timestamp >> thread_from >> thread_to >> next_task_name >> process_to)
1382             {
1383                 if (_async && m_stopDumping.load(std::memory_order_acquire))
1384                 {
1385                     m_spin.unlock();
1386                     m_storedSpin.unlock();
1387                     if (_lockSpin)
1388                         m_dumpSpin.unlock();
1389                     return 0;
1390                 }
1391 
1392                 beginContextSwitch(thread_from, timestamp, thread_to, next_task_name.c_str(), false);
1393                 endContextSwitch(thread_to, (processid_t)process_to, timestamp, false);
1394                 EASY_LOG_ONLY(++num);
1395             }
1396 
1397             EASY_LOGMSG("Done, " << num << " context switch events wrote\n");
1398         }
1399         EASY_LOG_ONLY(
1400             else {
1401                 EASY_ERROR("Can not open context switch log-file \"" << m_csInfoFilename << "\"\n");
1402             }
1403         )
1404     }
1405 #endif
1406 
1407     bool mainThreadExpired = false;
1408 
1409     // Calculate used memory total size and total blocks number
1410     uint64_t usedMemorySize = 0;
1411     uint32_t blocks_number = 0;
1412     for (auto thread_it = m_threads.begin(), end = m_threads.end(); thread_it != end;)
1413     {
1414         if (_async && m_stopDumping.load(std::memory_order_acquire))
1415         {
1416             m_spin.unlock();
1417             m_storedSpin.unlock();
1418             if (_lockSpin)
1419                 m_dumpSpin.unlock();
1420             return 0;
1421         }
1422 
1423         auto& thread = thread_it->second;
1424         uint32_t num = static_cast<uint32_t>(thread.blocks.closedList.size()) + static_cast<uint32_t>(thread.sync.closedList.size());
1425         const char expired = ProfileManager::checkThreadExpired(thread);
1426 
1427 #ifdef _WIN32
1428         if (num == 0 && expired != 0)
1429 #elif defined(EASY_CXX11_TLS_AVAILABLE)
1430         // Removing !guarded thread when thread_local feature is supported is safe.
1431         if (num == 0 && (expired != 0 || !thread.guarded))
1432 #elif EASY_OPTION_REMOVE_EMPTY_UNGUARDED_THREADS != 0
1433 # pragma message "Warning: Removing !guarded thread without thread_local support may cause an application crash, but fixes potential memory leak when using pthreads."
1434         // Removing !guarded thread may cause an application crash if a thread would start to write blocks after ThreadStorage remove.
1435         // TODO: Find solution to check thread state for pthread or to nullify THIS_THREAD pointer for removed ThreadStorage
1436         if (num == 0 && (expired != 0 || !t.guarded))
1437 #else
1438 # pragma message "Warning: Can not check pthread state (dead or alive). This may cause memory leak because ThreadStorage-s would not be removed ever during an application launched."
1439         if (num == 0 && expired != 0)
1440 #endif
1441         {
1442             // Remove thread if it contains no profiled information and has been finished (or is not guarded --deprecated).
1443             profiler::thread_id_t id = thread_it->first;
1444             if (!mainThreadExpired && m_mainThreadId.compare_exchange_weak(id, 0, std::memory_order_release, std::memory_order_acquire))
1445                 mainThreadExpired = true;
1446             m_threads.erase(thread_it++);
1447             continue;
1448         }
1449 
1450         if (expired == 1)
1451         {
1452             EASY_FORCE_EVENT3(thread, endtime, "ThreadExpired", EASY_COLOR_THREAD_END);
1453             ++num;
1454         }
1455 
1456         usedMemorySize += thread.blocks.usedMemorySize + thread.sync.usedMemorySize;
1457         blocks_number += num;
1458         ++thread_it;
1459     }
1460 
1461     // Write profiler signature and version
1462     _outputStream.write(PROFILER_SIGNATURE);
1463     _outputStream.write(EASY_CURRENT_VERSION);
1464     _outputStream.write(m_processId);
1465 
1466     // Write CPU frequency to let GUI calculate real time value from CPU clocks
1467 #if defined(EASY_CHRONO_CLOCK) || defined(_WIN32)
1468     _outputStream.write(CPU_FREQUENCY);
1469 #else
1470     EASY_LOGMSG("Calculating CPU frequency\n");
1471     const int64_t cpu_frequency = calculate_cpu_frequency();
1472     _outputStream.write(cpu_frequency * 1000LL);
1473     EASY_LOGMSG("Done calculating CPU frequency\n");
1474 
1475     CPU_FREQUENCY.store(cpu_frequency, std::memory_order_release);
1476 #endif
1477 
1478     // Write begin and end time
1479     _outputStream.write(m_beginTime);
1480     _outputStream.write(m_endTime);
1481 
1482     // Write blocks number and used memory size
1483     _outputStream.write(blocks_number);
1484     _outputStream.write(usedMemorySize);
1485     _outputStream.write(static_cast<uint32_t>(m_descriptors.size()));
1486     _outputStream.write(m_usedMemorySize);
1487 
1488     // Write block descriptors
1489     for (const auto descriptor : m_descriptors)
1490     {
1491         const auto name_size = descriptor->nameSize();
1492         const auto filename_size = descriptor->filenameSize();
1493         const auto size = static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor) + name_size + filename_size);
1494 
1495         _outputStream.write(size);
1496         _outputStream.write<profiler::BaseBlockDescriptor>(*descriptor);
1497         _outputStream.write(name_size);
1498         _outputStream.write(descriptor->name(), name_size);
1499         _outputStream.write(descriptor->filename(), filename_size);
1500     }
1501 
1502     // Write blocks and context switch events for each thread
1503     for (auto thread_it = m_threads.begin(), end = m_threads.end(); thread_it != end;)
1504     {
1505         if (_async && m_stopDumping.load(std::memory_order_acquire))
1506         {
1507             m_spin.unlock();
1508             m_storedSpin.unlock();
1509             if (_lockSpin)
1510                 m_dumpSpin.unlock();
1511             return 0;
1512         }
1513 
1514         auto& thread = thread_it->second;
1515 
1516         _outputStream.write(thread_it->first);
1517 
1518         const auto name_size = static_cast<uint16_t>(thread.name.size() + 1);
1519         _outputStream.write(name_size);
1520         _outputStream.write(name_size > 1 ? thread.name.c_str() : "", name_size);
1521 
1522         _outputStream.write(thread.sync.closedList.size());
1523         if (!thread.sync.closedList.empty())
1524             thread.sync.closedList.serialize(_outputStream);
1525 
1526         _outputStream.write(thread.blocks.closedList.size());
1527         if (!thread.blocks.closedList.empty())
1528             thread.blocks.closedList.serialize(_outputStream);
1529 
1530         thread.clearClosed();
1531         //t.blocks.openedList.clear();
1532         thread.sync.openedList.clear();
1533 
1534         if (thread.expired.load(std::memory_order_acquire) != 0)
1535         {
1536             // Remove expired thread after writing all profiled information
1537             profiler::thread_id_t id = thread_it->first;
1538             if (!mainThreadExpired && m_mainThreadId.compare_exchange_weak(id, 0, std::memory_order_release, std::memory_order_acquire))
1539                 mainThreadExpired = true;
1540             m_threads.erase(thread_it++);
1541         }
1542         else
1543         {
1544             ++thread_it;
1545         }
1546     }
1547 
1548     m_storedSpin.unlock();
1549     m_spin.unlock();
1550 
1551     if (_lockSpin)
1552         m_dumpSpin.unlock();
1553 
1554     EASY_LOGMSG("Done dumpBlocksToStream(). Dumped " << blocks_number << " blocks\n");
1555 
1556     return blocks_number;
1557 }
1558 
dumpBlocksToFile(const char * _filename)1559 uint32_t ProfileManager::dumpBlocksToFile(const char* _filename)
1560 {
1561     EASY_LOGMSG("dumpBlocksToFile(\"" << _filename << "\")...\n");
1562 
1563     std::ofstream outputFile(_filename, std::fstream::binary);
1564     if (!outputFile.is_open())
1565     {
1566         EASY_ERROR("Can not open \"" << _filename << "\" for writing\n");
1567         return 0;
1568     }
1569 
1570     profiler::OStream outputStream;
1571 
1572     // Replace outputStream buffer to outputFile buffer to avoid redundant copying
1573     typedef ::std::basic_iostream<std::stringstream::char_type, std::stringstream::traits_type> stringstream_parent;
1574     stringstream_parent& s = outputStream.stream();
1575     auto oldbuf = s.rdbuf(outputFile.rdbuf());
1576 
1577     // Write data directly to file
1578     const auto blocksNumber = dumpBlocksToStream(outputStream, true, false);
1579 
1580     // Restore old outputStream buffer to avoid possible second memory free on stringstream destructor
1581     s.rdbuf(oldbuf);
1582 
1583     EASY_LOGMSG("Done dumpBlocksToFile()\n");
1584 
1585     return blocksNumber;
1586 }
1587 
registerThread()1588 void ProfileManager::registerThread()
1589 {
1590     THIS_THREAD = &threadStorage(getCurrentThreadId());
1591 
1592 #ifdef EASY_CXX11_TLS_AVAILABLE
1593     THIS_THREAD->guarded = true;
1594     THIS_THREAD_GUARD.m_id = THIS_THREAD->id;
1595 #endif
1596 }
1597 
registerThread(const char * name,ThreadGuard & threadGuard)1598 const char* ProfileManager::registerThread(const char* name, ThreadGuard& threadGuard)
1599 {
1600     if (THIS_THREAD == nullptr)
1601         THIS_THREAD = &threadStorage(getCurrentThreadId());
1602 
1603     THIS_THREAD->guarded = true;
1604     if (!THIS_THREAD->named)
1605     {
1606         THIS_THREAD->named = true;
1607         THIS_THREAD->name = name;
1608 
1609         if (THIS_THREAD->name == "Main")
1610         {
1611             profiler::thread_id_t id = 0;
1612             THIS_THREAD_IS_MAIN = m_mainThreadId.compare_exchange_weak(id, THIS_THREAD->id, std::memory_order_release, std::memory_order_acquire);
1613         }
1614 
1615 #ifdef EASY_CXX11_TLS_AVAILABLE
1616         THIS_THREAD_GUARD.m_id = THIS_THREAD->id;
1617     }
1618 
1619     (void)threadGuard; // this is just to prevent from warning about unused variable
1620 #else
1621     }
1622 
1623     threadGuard.m_id = THIS_THREAD->id;
1624 #endif
1625 
1626     return THIS_THREAD->name.c_str();
1627 }
1628 
registerThread(const char * name)1629 const char* ProfileManager::registerThread(const char* name)
1630 {
1631     if (THIS_THREAD == nullptr)
1632         THIS_THREAD = &threadStorage(getCurrentThreadId());
1633 
1634     if (!THIS_THREAD->named)
1635     {
1636         THIS_THREAD->named = true;
1637         THIS_THREAD->name = name;
1638 
1639         if (THIS_THREAD->name == "Main")
1640         {
1641             profiler::thread_id_t id = 0;
1642             THIS_THREAD_IS_MAIN = m_mainThreadId.compare_exchange_weak(id, THIS_THREAD->id, std::memory_order_release, std::memory_order_acquire);
1643         }
1644 
1645 #ifdef EASY_CXX11_TLS_AVAILABLE
1646         THIS_THREAD->guarded = true;
1647         THIS_THREAD_GUARD.m_id = THIS_THREAD->id;
1648 #endif
1649     }
1650 
1651     return THIS_THREAD->name.c_str();
1652 }
1653 
setBlockStatus(block_id_t _id,EasyBlockStatus _status)1654 void ProfileManager::setBlockStatus(block_id_t _id, EasyBlockStatus _status)
1655 {
1656     if (m_profilerStatus.load(std::memory_order_acquire) != EASY_PROF_DISABLED)
1657         return; // Changing blocks statuses is restricted while profile session is active
1658 
1659     guard_lock_t lock(m_storedSpin);
1660     if (_id < m_descriptors.size())
1661     {
1662         auto desc = m_descriptors[_id];
1663         lock.unlock();
1664         desc->m_status = _status;
1665     }
1666 }
1667 
startListen(uint16_t _port)1668 void ProfileManager::startListen(uint16_t _port)
1669 {
1670     if (!m_isAlreadyListening.exchange(true, std::memory_order_release))
1671     {
1672         m_stopListen.store(false, std::memory_order_release);
1673         m_listenThread = std::thread(&ProfileManager::listen, this, _port);
1674     }
1675 }
1676 
stopListen()1677 void ProfileManager::stopListen()
1678 {
1679     m_stopListen.store(true, std::memory_order_release);
1680     if (m_listenThread.joinable())
1681         m_listenThread.join();
1682     m_isAlreadyListening.store(false, std::memory_order_release);
1683 
1684     EASY_LOGMSG("Listening stopped\n");
1685 }
1686 
isListening() const1687 bool ProfileManager::isListening() const
1688 {
1689     return m_isAlreadyListening.load(std::memory_order_acquire);
1690 }
1691 
1692 //////////////////////////////////////////////////////////////////////////
1693 
1694 template <class T>
join(std::future<T> & futureResult)1695 inline void join(std::future<T>& futureResult)
1696 {
1697     if (futureResult.valid())
1698         futureResult.get();
1699 }
1700 
listen(uint16_t _port)1701 void ProfileManager::listen(uint16_t _port)
1702 {
1703     EASY_THREAD_SCOPE("EasyProfiler.Listen");
1704 
1705     EASY_LOGMSG("Listening started\n");
1706 
1707     profiler::OStream os;
1708     std::future<uint32_t> dumpingResult;
1709     bool dumping = false;
1710 
1711     const auto stopDumping = [this, &dumping, &dumpingResult, &os]
1712     {
1713         dumping = false;
1714         m_stopDumping.store(true, std::memory_order_release);
1715         join(dumpingResult);
1716         os.clear();
1717     };
1718 
1719     EasySocket socket;
1720     profiler::net::Message replyMessage(profiler::net::MessageType::Reply_Capturing_Started);
1721 
1722     socket.bind(_port);
1723     int bytes = 0;
1724     while (!m_stopListen.load(std::memory_order_acquire))
1725     {
1726         if (dumping)
1727             stopDumping();
1728 
1729         socket.listen();
1730         socket.accept();
1731 
1732         bool hasConnect = true;
1733 
1734         // Send reply
1735         {
1736             const bool wasLowPriorityET =
1737 #ifdef _WIN32
1738                 EasyEventTracer::instance().isLowPriority();
1739 #else
1740                 false;
1741 #endif
1742             const profiler::net::EasyProfilerStatus connectionReply(
1743                 m_profilerStatus.load(std::memory_order_acquire) == EASY_PROF_ENABLED,
1744                 m_isEventTracingEnabled.load(std::memory_order_acquire), wasLowPriorityET);
1745 
1746             bytes = socket.send(&connectionReply, sizeof(profiler::net::EasyProfilerStatus));
1747             hasConnect = bytes > 0;
1748         }
1749 
1750         while (hasConnect && !m_stopListen.load(std::memory_order_acquire))
1751         {
1752             if (dumping)
1753             {
1754                 if (!dumpingResult.valid())
1755                 {
1756                     dumping = false;
1757                     socket.setReceiveTimeout(0);
1758                     os.clear();
1759                 }
1760                 else if (dumpingResult.wait_for(std::chrono::milliseconds(0)) == std::future_status::ready)
1761                 {
1762                     dumping = false;
1763                     dumpingResult.get();
1764 
1765                     const auto size = os.stream().tellp();
1766                     static const decltype(size) badSize = -1;
1767                     if (size != badSize)
1768                     {
1769                         const profiler::net::DataMessage dm(static_cast<uint32_t>(size),
1770                                                             profiler::net::MessageType::Reply_Blocks);
1771 
1772                         const size_t packet_size = sizeof(dm) + dm.size;
1773                         std::string sendbuf;
1774                         sendbuf.reserve(packet_size + 1);
1775 
1776                         if (sendbuf.capacity() >= packet_size) // check if there is enough memory
1777                         {
1778                             sendbuf.append((const char*) &dm, sizeof(dm));
1779                             sendbuf += os.stream().str(); // TODO: Avoid double-coping data from stringstream!
1780                             os.clear();
1781 
1782                             bytes = socket.send(sendbuf.c_str(), packet_size);
1783                             hasConnect = bytes > 0;
1784                             if (!hasConnect)
1785                                 break;
1786                         }
1787                         else
1788                         {
1789                             EASY_ERROR("Can not send blocks. Not enough memory for allocating " << packet_size
1790                                                                                                 << " bytes");
1791                             os.clear();
1792                         }
1793                     }
1794                     else
1795                     {
1796                         EASY_ERROR("Can not send blocks. Bad std::stringstream.tellp() == -1");
1797                         os.clear();
1798                     }
1799 
1800                     replyMessage.type = profiler::net::MessageType::Reply_Blocks_End;
1801                     bytes = socket.send(&replyMessage, sizeof(replyMessage));
1802                     hasConnect = bytes > 0;
1803                     if (!hasConnect)
1804                         break;
1805 
1806                     socket.setReceiveTimeout(0);
1807                 }
1808             }
1809 
1810             char buffer[256] = {};
1811             bytes = socket.receive(buffer, 255);
1812 
1813             hasConnect = socket.isConnected();
1814             if (!hasConnect || bytes < static_cast<int>(sizeof(profiler::net::Message)))
1815                 continue;
1816 
1817             auto message = (const profiler::net::Message*)buffer;
1818             if (!message->isEasyNetMessage())
1819                 continue;
1820 
1821             switch (message->type)
1822             {
1823                 case profiler::net::MessageType::Ping:
1824                 {
1825                     EASY_LOGMSG("receive MessageType::Ping\n");
1826                     break;
1827                 }
1828 
1829                 case profiler::net::MessageType::Request_MainThread_FPS:
1830                 {
1831                     profiler::timestamp_t maxDuration = maxFrameDuration(), avgDuration = avgFrameDuration();
1832 
1833                     maxDuration = TICKS_TO_US(maxDuration);
1834                     avgDuration = TICKS_TO_US(avgDuration);
1835 
1836                     const profiler::net::TimestampMessage reply(profiler::net::MessageType::Reply_MainThread_FPS,
1837                                                                 (uint32_t)maxDuration, (uint32_t)avgDuration);
1838 
1839                     bytes = socket.send(&reply, sizeof(profiler::net::TimestampMessage));
1840                     hasConnect = bytes > 0;
1841 
1842                     break;
1843                 }
1844 
1845                 case profiler::net::MessageType::Request_Start_Capture:
1846                 {
1847                     EASY_LOGMSG("receive MessageType::Request_Start_Capture\n");
1848 
1849                     ::profiler::timestamp_t t = 0;
1850                     EASY_FORCE_EVENT(t, "StartCapture", EASY_COLOR_START, profiler::OFF);
1851 
1852                     m_dumpSpin.lock();
1853                     const auto prev = m_profilerStatus.exchange(EASY_PROF_ENABLED, std::memory_order_release);
1854                     if (prev != EASY_PROF_ENABLED) {
1855                         enableEventTracer();
1856                         m_beginTime = t;
1857                     }
1858                     m_dumpSpin.unlock();
1859 
1860                     replyMessage.type = profiler::net::MessageType::Reply_Capturing_Started;
1861                     bytes = socket.send(&replyMessage, sizeof(replyMessage));
1862                     hasConnect = bytes > 0;
1863 
1864                     break;
1865                 }
1866 
1867                 case profiler::net::MessageType::Request_Stop_Capture:
1868                 {
1869                     EASY_LOGMSG("receive MessageType::Request_Stop_Capture\n");
1870 
1871                     if (dumping)
1872                         break;
1873 
1874                     m_dumpSpin.lock();
1875                     auto time = getCurrentTime();
1876                     const auto prev = m_profilerStatus.exchange(EASY_PROF_DUMP, std::memory_order_release);
1877                     if (prev == EASY_PROF_ENABLED) {
1878                         disableEventTracer();
1879                         m_endTime = time;
1880                     }
1881                     EASY_FORCE_EVENT2(m_endTime, "StopCapture", EASY_COLOR_END, profiler::OFF);
1882 
1883                     dumping = true;
1884                     socket.setReceiveTimeout(500); // We have to check if dumping ready or not
1885 
1886                     m_stopDumping.store(false, std::memory_order_release);
1887                     dumpingResult = std::async(std::launch::async, [this, &os]
1888                     {
1889                         auto result = dumpBlocksToStream(os, false, true);
1890                         m_dumpSpin.unlock();
1891                         return result;
1892                     });
1893 
1894                     break;
1895                 }
1896 
1897                 case profiler::net::MessageType::Request_Blocks_Description:
1898                 {
1899                     EASY_LOGMSG("receive MessageType::Request_Blocks_Description\n");
1900 
1901                     if (dumping)
1902                         stopDumping();
1903 
1904                     // Write profiler signature and version
1905                     os.write(PROFILER_SIGNATURE);
1906                     os.write(EASY_CURRENT_VERSION);
1907 
1908                     // Write block descriptors
1909                     m_storedSpin.lock();
1910                     os.write(static_cast<uint32_t>(m_descriptors.size()));
1911                     os.write(m_usedMemorySize);
1912                     for (const auto descriptor : m_descriptors)
1913                     {
1914                         const auto name_size = descriptor->nameSize();
1915                         const auto filename_size = descriptor->filenameSize();
1916                         const auto size = static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor)
1917                                                                 + name_size + filename_size);
1918 
1919                         os.write(size);
1920                         os.write<profiler::BaseBlockDescriptor>(*descriptor);
1921                         os.write(name_size);
1922                         os.write(descriptor->name(), name_size);
1923                         os.write(descriptor->filename(), filename_size);
1924                     }
1925                     m_storedSpin.unlock();
1926                     // END of Write block descriptors.
1927 
1928                     const auto size = os.stream().tellp();
1929                     static const decltype(size) badSize = -1;
1930                     if (size != badSize)
1931                     {
1932                         const profiler::net::DataMessage dm(static_cast<uint32_t>(size),
1933                                                             profiler::net::MessageType::Reply_Blocks_Description);
1934 
1935                         const size_t packet_size = sizeof(dm) + dm.size;
1936                         std::string sendbuf;
1937                         sendbuf.reserve(packet_size + 1);
1938 
1939                         if (sendbuf.capacity() >= packet_size) // check if there is enough memory
1940                         {
1941                             sendbuf.append((const char*)&dm, sizeof(dm));
1942                             sendbuf += os.stream().str(); // TODO: Avoid double-coping data from stringstream!
1943                             os.clear();
1944 
1945                             bytes = socket.send(sendbuf.c_str(), packet_size);
1946                             //hasConnect = bytes > 0;
1947                         }
1948                         else
1949                         {
1950                             EASY_ERROR("Can not send block descriptions. Not enough memory for allocating " << packet_size << " bytes");
1951                         }
1952                     }
1953                     else
1954                     {
1955                         EASY_ERROR("Can not send block descriptions. Bad std::stringstream.tellp() == -1");
1956                     }
1957 
1958                     replyMessage.type = profiler::net::MessageType::Reply_Blocks_Description_End
1959                         ;
1960                     bytes = socket.send(&replyMessage, sizeof(replyMessage));
1961                     hasConnect = bytes > 0;
1962 
1963                     break;
1964                 }
1965 
1966                 case profiler::net::MessageType::Change_Block_Status:
1967                 {
1968                     auto data = reinterpret_cast<const profiler::net::BlockStatusMessage*>(message);
1969 
1970                     EASY_LOGMSG("receive MessageType::ChangeBLock_Status id=" << data->id << " status=" << data->status << std::endl);
1971 
1972                     setBlockStatus(data->id, static_cast<::profiler::EasyBlockStatus>(data->status));
1973 
1974                     break;
1975                 }
1976 
1977                 case profiler::net::MessageType::Change_Event_Tracing_Status:
1978                 {
1979                     auto data = reinterpret_cast<const profiler::net::BoolMessage*>(message);
1980 
1981                     EASY_LOGMSG("receive MessageType::Change_Event_Tracing_Status on=" << data->flag << std::endl);
1982 
1983                     m_isEventTracingEnabled.store(data->flag, std::memory_order_release);
1984                     break;
1985                 }
1986 
1987                 case profiler::net::MessageType::Change_Event_Tracing_Priority:
1988                 {
1989 #if defined(_WIN32) || EASY_OPTION_LOG_ENABLED != 0
1990                     auto data = reinterpret_cast<const profiler::net::BoolMessage*>(message);
1991 #endif
1992 
1993                     EASY_LOGMSG("receive MessageType::Change_Event_Tracing_Priority low=" << data->flag << std::endl);
1994 
1995 #if defined(_WIN32)
1996                     EasyEventTracer::instance().setLowPriority(data->flag);
1997 #endif
1998                     break;
1999                 }
2000 
2001                 default:
2002                     break;
2003             }
2004         }
2005     }
2006 
2007     if (dumping)
2008     {
2009         m_stopDumping.store(true, std::memory_order_release);
2010         join(dumpingResult);
2011     }
2012 }
2013 
2014 //////////////////////////////////////////////////////////////////////////
2015 
2016