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