profiler.cpp 22.3 KB
Newer Older
Pascal Palenda's avatar
Pascal Palenda committed
1
2
3
4
#include <ITA/simulation_scheduler/profiler/profiler.h>

// simulation scheduler includes
#include "../src/ITA/simulation_scheduler/profiler/profiler_key.h"
5
#include "../src/ITA/simulation_scheduler/profiler/profiler_data.h"
6
#include "../src/ITA/simulation_scheduler/profiler/profiler_thread_storage.h"
Pascal Palenda's avatar
Pascal Palenda committed
7
#include <ITA/simulation_scheduler/utils/utils.h>
Pascal Palenda's avatar
Pascal Palenda committed
8
#include <ITA/simulation_scheduler/profiler/profiler_types.h>
Pascal Palenda's avatar
Pascal Palenda committed
9
10
11
12
13
14
15
16

// std includes
#include <ostream>
#include <stack>
#include <thread>
#include <unordered_map>
#include <sstream>
#include <algorithm>
Pascal Palenda's avatar
Pascal Palenda committed
17
#include <mutex>
Pascal Palenda's avatar
Pascal Palenda committed
18
19
20

// ITA includes
#include <ITAClock.h>
21
#include <ITAStopWatch.h>
Pascal Palenda's avatar
Pascal Palenda committed
22
23
24

// misc includes
#include <libjson.h>
25
#include <VistaBase/VistaTimeUtils.h>
Pascal Palenda's avatar
Pascal Palenda committed
26

27
28
#ifdef WITH_PROFILER

Pascal Palenda's avatar
Pascal Palenda committed
29
30
31
32
33
34
namespace ITA
{
	namespace simulation_scheduler
	{
		namespace profiler
		{
35
			class ProfilerInstance;
Pascal Palenda's avatar
Pascal Palenda committed
36

37
38
		#if _MSC_VER >= 1900 && FALSE
		#define MAGIC_STATICS
39
40
41
		#define THREAD_LOCAL thread_local
		#else
		#define THREAD_LOCAL __declspec( thread )
42
43
		#endif

44
			static THREAD_LOCAL SProfilerThreadStorage* THIS_THREAD = nullptr;
Pascal Palenda's avatar
Pascal Palenda committed
45

Pascal Palenda's avatar
Pascal Palenda committed
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
			///
			/// \brief Profiler class.
			///
			/// This singleton class keeps all profiler data.
			/// It should be used through the public interface defined in profiler.h.
			///
			/// It keeps a record of opened sections and saves the results of closed sections and event counts.
			///
			/// This implementation was inspired by [easy_profiler by yse](https://github.com/yse/easy_profiler).
			///
			class CProfiler
			{
			public:
				///
				/// \brief Get the  instance.
				/// \note This implementation is thread safe.
				///
63
				static CProfiler& getInstance ( );
Pascal Palenda's avatar
Pascal Palenda committed
64
65
66
67
68
69
70
71
72

				///
				/// \brief Add an event to the profiler.
				/// \param sName name of the section.
				/// \param sFunctionName name of the function that started the section.
				/// \param sFile name of the file in which the section was started.
				/// 
				void addEventCount ( const std::string& sName, const std::string& sFunctionName, const std::string& sFile )
				{
Pascal Palenda's avatar
Pascal Palenda committed
73
74
75
					if ( !m_bEnabled )
						return;

76
77
78
79
					if ( THIS_THREAD == nullptr )
						registerThread ( );

					THIS_THREAD->m_mEventCount [{sName, sFunctionName, sFile, std::this_thread::get_id ( )}]++;
Pascal Palenda's avatar
Pascal Palenda committed
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
				}

				///
				/// \brief Start a section.
				///
				/// This function adds an open section to the stack for the current thread.
				/// \param oSection the section object to add to the profiler.
				/// \param sName name of the section.
				/// \param sFunctionName name of the function that started the section.
				/// \param sFile name of the file in which the section was started.
				/// 
				void startSection ( CSection& oSection,
									const std::string& sName,
									const std::string& sFunctionName,
									const std::string& sFile )
				{
Pascal Palenda's avatar
Pascal Palenda committed
96
97
98
					if ( !m_bEnabled )
						return;

99
100
101
					if ( THIS_THREAD == nullptr )
						registerThread ( );

Pascal Palenda's avatar
Pascal Palenda committed
102
103
					oSection.start ( );

104
					THIS_THREAD->m_msOpenSections [std::this_thread::get_id ( )].push ( { {sName, sFunctionName, sFile, std::this_thread::get_id ( )}, oSection } );
Pascal Palenda's avatar
Pascal Palenda committed
105
106
107
108
109
110
111
				}

				///
				/// \brief End the last opened section in the current thread.
				/// 
				void endSection ( )
				{
Pascal Palenda's avatar
Pascal Palenda committed
112
113
114
					if ( !m_bEnabled )
						return;

115
116
117
118
119
					if ( THIS_THREAD == nullptr )
						registerThread ( );

					const auto iterator = THIS_THREAD->m_msOpenSections.find ( std::this_thread::get_id ( ) );
					if ( iterator != THIS_THREAD->m_msOpenSections.end ( ) )
Pascal Palenda's avatar
Pascal Palenda committed
120
121
122
123
124
					{
						auto& openSections = iterator->second;
						if ( !openSections.top ( ).second.get ( ).finished ( ) )
							openSections.top ( ).second.get ( ).end ( );

125
						THIS_THREAD->m_mSections [openSections.top ( ).first].addSection ( openSections.top ( ).second );
Pascal Palenda's avatar
Pascal Palenda committed
126
127
128
129
130
131
132
133
134
135

						openSections.pop ( );
					}
				}

				///
				/// \brief End all open sections for all threads.
				/// 
				void endAllSections ( )
				{
Pascal Palenda's avatar
Pascal Palenda committed
136
					std::lock_guard<std::mutex> lock ( m_oMutex );
137
138

					for ( auto& thread : m_mThreadStorage )
Pascal Palenda's avatar
Pascal Palenda committed
139
					{
Pascal Palenda's avatar
Pascal Palenda committed
140
						for ( auto& key : thread.second.m_msOpenSections )
Pascal Palenda's avatar
Pascal Palenda committed
141
						{
Pascal Palenda's avatar
Pascal Palenda committed
142
							while ( !key.second.empty ( ) )
143
144
							{
								auto& openSections = key.second;
Pascal Palenda's avatar
Pascal Palenda committed
145

146
147
								if ( !openSections.top ( ).second.get ( ).finished ( ) )
									openSections.top ( ).second.get ( ).end ( );
Pascal Palenda's avatar
Pascal Palenda committed
148

149
								thread.second.m_mSections [openSections.top ( ).first].addSection ( openSections.top ( ).second );
Pascal Palenda's avatar
Pascal Palenda committed
150

151
152
								openSections.pop ( );
							}
Pascal Palenda's avatar
Pascal Palenda committed
153
154
155
156
						}
					}
				}

Pascal Palenda's avatar
Pascal Palenda committed
157
158
159
160
161
				///
				/// \brief Name the current thread.
				///
				/// Only the first invocation of this has an effect.
				///
Pascal Palenda's avatar
Pascal Palenda committed
162
				void nameThread ( const std::string& sName )
Pascal Palenda's avatar
Pascal Palenda committed
163
				{
Pascal Palenda's avatar
Pascal Palenda committed
164
					std::lock_guard<std::mutex> lock ( m_oMutex );
Pascal Palenda's avatar
Pascal Palenda committed
165
166
167
168
					if ( m_mThreadNames.find ( std::this_thread::get_id ( ) ) == m_mThreadNames.end ( ) )
						m_mThreadNames [std::this_thread::get_id ( )] = sName;
				}

Pascal Palenda's avatar
Pascal Palenda committed
169
170
171
172
173
174
175
176
177
178
				///
				/// \brief Add a value to the profiler.
				///
				/// The value must be of type: bool, char, int, float, double, string.
				/// \param sName name of the value.
				/// \param value the value to be saved in the profiler.
				/// \param sFunctionName name of the function that issued the event.
				/// \param sFile name of the file in which the event was issued.
				///
				void addValue ( const std::string& sName, const ValueData& value, const std::string& sFunctionName, const std::string& sFile )
179
				{
Pascal Palenda's avatar
Pascal Palenda committed
180
181
182
					if ( !m_bEnabled )
						return;

183
184
					if ( THIS_THREAD == nullptr )
						registerThread ( );
Pascal Palenda's avatar
Pascal Palenda committed
185

186
187
188
189
190
191
					THIS_THREAD->m_mValues [{sName, sFunctionName, sFile, std::this_thread::get_id ( )}].addValue ( value );
				}

				///
				/// \brief Register a thread with a thread storage.
				/// 
Pascal Palenda's avatar
Pascal Palenda committed
192
				void registerThread ( )
193
				{
Pascal Palenda's avatar
Pascal Palenda committed
194
					THIS_THREAD = &getThreadStorageReferenceById ( std::this_thread::get_id ( ) );
195
196
197
198
199
200
201
202
				}

				///
				/// \brief Thread save access to thread storage map.
				/// \param oThreadId ID of the thread storage to be retrieved.
				/// \return reference to the desired thread storage.
				/// 
				SProfilerThreadStorage& getThreadStorageReferenceById ( std::thread::id oThreadId )
Pascal Palenda's avatar
Pascal Palenda committed
203
				{
Pascal Palenda's avatar
Pascal Palenda committed
204
					std::lock_guard<std::mutex> lock ( m_oMutex );
Pascal Palenda's avatar
Pascal Palenda committed
205

206
					return m_mThreadStorage [oThreadId];
Pascal Palenda's avatar
Pascal Palenda committed
207
208
				}

Pascal Palenda's avatar
Pascal Palenda committed
209
210
211
212
				///
				/// \brief Parses the profiler data as a JSON file.
				/// \return a string stream containing the profiler data as a JSON file.
				/// 
213
				std::stringstream profilerToJSON ( )
Pascal Palenda's avatar
Pascal Palenda committed
214
215
216
217
218
219
				{
					JSONNode jnRoot;
					jnRoot.set_name ( "Profiler" );

					jnRoot.push_back ( JSONNode ( "Time Unit", "us - 1e-6" ) );

Pascal Palenda's avatar
Pascal Palenda committed
220
					jnRoot.push_back ( JSONNode ( "CPU Frequency", ITAClock::getDefaultClock ( )->getFrequency ( ) ) );
221

Pascal Palenda's avatar
Pascal Palenda committed
222
223
224
225
226
227
					JSONNode jnSection;
					jnSection.set_name ( "Profile Sections" );

					JSONNode jnEventCount;
					jnEventCount.set_name ( "Profile EventCounts" );

Pascal Palenda's avatar
Pascal Palenda committed
228
					JSONNode jnValue;
229
					jnValue.set_name ( "Profile Values" );
Pascal Palenda's avatar
Pascal Palenda committed
230

231
232
233
234
235
236
237
238
239
240
					// Disable profiling while we write to file.
					m_bEnabled = false;

					// Wait a bit for sections to end
					VistaTimeUtils::Sleep ( 50 );

					// Now we force all sections to end.
					endAllSections ( );

					// Lock the mutex as we want thread safe access to the thread storage.
241
242
					std::lock_guard<std::mutex> lock ( m_oMutex );

243
244
					ITAStopWatch swSection, swEvent, swValue, swThread;

245
					for ( const auto& threadStorage : m_mThreadStorage )
Pascal Palenda's avatar
Pascal Palenda committed
246
					{
247
						swThread.start ( );
248
249
						for ( const auto& sectionPair : threadStorage.second.m_mSections )
						{
250
							swSection.start ( );
251
							jnSection.push_back ( profileSectionToJSON ( sectionPair, m_mThreadNames ) );
252
							swSection.stop ( );
253
254
255
256
						}

						for ( const auto& eventCountPair : threadStorage.second.m_mEventCount )
						{
257
							swEvent.start ( );
258
							jnEventCount.push_back ( profileEventCountToJSON ( eventCountPair, m_mThreadNames ) );
259
							swEvent.stop ( );
260
261
262
263
						}

						for ( const auto& valuePair : threadStorage.second.m_mValues )
						{
264
							swValue.start ( );
265
							jnValue.push_back ( profileValuesToJSON ( valuePair, m_mThreadNames ) );
266
							swValue.stop ( );
267
						}
268
						swThread.stop ( );
Pascal Palenda's avatar
Pascal Palenda committed
269
270
					}

271
272
273
274
275
					jnRoot.push_back ( JSONNode ( "Section SW", swSection.ToString ( ) ) );
					jnRoot.push_back ( JSONNode ( "Event SW", swEvent.ToString ( ) ) );
					jnRoot.push_back ( JSONNode ( "Value SW", swValue.ToString ( ) ) );
					jnRoot.push_back ( JSONNode ( "Thread SW", swThread.ToString ( ) + ", sum=" + std::to_string ( swThread.sum ( ) ) ) );

276
277
278
279
					jnRoot.push_back ( jnSection );

					jnRoot.push_back ( jnEventCount );

280
					jnRoot.push_back ( jnValue );
Pascal Palenda's avatar
Pascal Palenda committed
281

282
283
284
					// Reenable profiling
					m_bEnabled = true;

Pascal Palenda's avatar
Pascal Palenda committed
285
					std::stringstream ss;
286
					ss << jnRoot.write ( );
Pascal Palenda's avatar
Pascal Palenda committed
287
288
289
290
291
292
					return ss;
				}

				///
				/// \brief Parses the given eventCount pair to a JSON Node.
				/// \param eventCountPair pair with the Key and the ProfileEventCount to be parsed.
Pascal Palenda's avatar
Pascal Palenda committed
293
				/// \param mThreadNames map, containing the names of the threads.
Pascal Palenda's avatar
Pascal Palenda committed
294
295
				/// \return a JSON Node with the data of the given eventCount pair.
				/// 
Pascal Palenda's avatar
Pascal Palenda committed
296
				static JSONNode profileEventCountToJSON ( const std::pair<ProfilerKey, ProfileEventCount>& eventCountPair,
Pascal Palenda's avatar
Pascal Palenda committed
297
														  const std::unordered_map<std::thread::id, std::string>& mThreadNames )
Pascal Palenda's avatar
Pascal Palenda committed
298
				{
Pascal Palenda's avatar
Pascal Palenda committed
299
300
301
					std::string sThreadName;

					const auto threadNameIterator = mThreadNames.find ( eventCountPair.first.iThreadId );
Pascal Palenda's avatar
Pascal Palenda committed
302

Pascal Palenda's avatar
Pascal Palenda committed
303
304
					if ( threadNameIterator != mThreadNames.end ( ) )
						sThreadName = threadNameIterator->second;
Pascal Palenda's avatar
Pascal Palenda committed
305

Pascal Palenda's avatar
Pascal Palenda committed
306
					JSONNode jnRoot;
Pascal Palenda's avatar
Pascal Palenda committed
307
					if ( !sThreadName.empty ( ) )
Pascal Palenda's avatar
Pascal Palenda committed
308
						jnRoot.set_name ( "[" + eventCountPair.first.sFunction + "][" + eventCountPair.first.sName + "][" + sThreadName + "]" );
Pascal Palenda's avatar
Pascal Palenda committed
309
					else
Pascal Palenda's avatar
Pascal Palenda committed
310
						jnRoot.set_name ( "[" + eventCountPair.first.sFunction + "][" + eventCountPair.first.sName + "][" + std::to_string ( eventCountPair.first.iThreadId.hash ( ) ) + "]" );
Pascal Palenda's avatar
Pascal Palenda committed
311

Pascal Palenda's avatar
Pascal Palenda committed
312
					jnRoot.push_back ( JSONNode ( "Name", eventCountPair.first.sName ) );
Pascal Palenda's avatar
Pascal Palenda committed
313
314
315
316
					jnRoot.push_back ( JSONNode ( "File Name", eventCountPair.first.sFile ) );
					jnRoot.push_back ( JSONNode ( "Function Name", eventCountPair.first.sFunction ) );
					jnRoot.push_back ( JSONNode ( "Thread Id", eventCountPair.first.iThreadId.hash ( ) ) );

Pascal Palenda's avatar
Pascal Palenda committed
317
318
					if ( !sThreadName.empty ( ) )
						jnRoot.push_back ( JSONNode ( "Thread Name", sThreadName ) );
Pascal Palenda's avatar
Pascal Palenda committed
319

Pascal Palenda's avatar
Pascal Palenda committed
320
321
322
323
324
325
326
327
328
329
330
					jnRoot.push_back ( JSONNode ( "Count", eventCountPair.second.vTimeStamps.size ( ) ) );

					std::vector<double> vdTimeStamps ( eventCountPair.second.vTimeStamps.size ( ) );

					std::transform ( eventCountPair.second.vTimeStamps.begin ( ),
									 eventCountPair.second.vTimeStamps.end ( ),
									 vdTimeStamps.begin ( ),
									 [] ( const double& time ) -> double
					{
						return time * 1e6;
					} );
Pascal Palenda's avatar
Pascal Palenda committed
331

332
					/*JSONNode jnTimeStampArray ( JSON_ARRAY );
333
					jnTimeStampArray.set_name ( "TimeStamps" );
334
335
					jnTimeStampArray.reserve ( vdTimeStamps.size ( ) );
					JSONNode jnData ( JSON_NUMBER );
336
337
338

					for ( const auto& timeStamp : vdTimeStamps )
					{
339
340
						jnData = timeStamp;
						jnTimeStampArray.push_back ( jnData );
341
342
					}

343
344
					jnRoot.push_back ( jnTimeStampArray );*/

345
					jnRoot.push_back ( JSONNode ( "Time Stamps", utils::DataTypeUtils::convertToString ( vdTimeStamps, 8, "," ) ) );
Pascal Palenda's avatar
Pascal Palenda committed
346
347
348
349
350
351
352
353
354

					return jnRoot;
				}

				///
				/// \brief Parses the given section pair to a JSON Node.
				///
				/// This function also calculates and add the min, max, mean, variance and standard deviation of the section to the JSON Node.
				/// \param sectionPair pair with the Key and the ProfileSection to be parsed.
Pascal Palenda's avatar
Pascal Palenda committed
355
				/// \param mThreadNames map, containing the names of the threads.
Pascal Palenda's avatar
Pascal Palenda committed
356
357
				/// \return a JSON Node with the data of the given section pair.
				/// 
Pascal Palenda's avatar
Pascal Palenda committed
358
359
				static JSONNode profileSectionToJSON ( const std::pair<ProfilerKey, ProfileSection>& sectionPair,
													   const std::unordered_map<std::thread::id, std::string>& mThreadNames )
Pascal Palenda's avatar
Pascal Palenda committed
360
				{
Pascal Palenda's avatar
Pascal Palenda committed
361
362
363
364
365
366
					std::string sThreadName;

					const auto threadNameIterator = mThreadNames.find ( sectionPair.first.iThreadId );

					if ( threadNameIterator != mThreadNames.end ( ) )
						sThreadName = threadNameIterator->second;
Pascal Palenda's avatar
Pascal Palenda committed
367

Pascal Palenda's avatar
Pascal Palenda committed
368
					JSONNode jnRoot;
Pascal Palenda's avatar
Pascal Palenda committed
369
					if ( !sThreadName.empty ( ) )
Pascal Palenda's avatar
Pascal Palenda committed
370
						jnRoot.set_name ( "[" + sectionPair.first.sFunction + "][" + sectionPair.first.sName + "][" + sThreadName + "]" );
Pascal Palenda's avatar
Pascal Palenda committed
371
					else
Pascal Palenda's avatar
Pascal Palenda committed
372
						jnRoot.set_name ( "[" + sectionPair.first.sFunction + "][" + sectionPair.first.sName + "][" + std::to_string ( sectionPair.first.iThreadId.hash ( ) ) + "]" );
Pascal Palenda's avatar
Pascal Palenda committed
373

Pascal Palenda's avatar
Pascal Palenda committed
374
					jnRoot.push_back ( JSONNode ( "Name", sectionPair.first.sName ) );
Pascal Palenda's avatar
Pascal Palenda committed
375
376
377
					jnRoot.push_back ( JSONNode ( "File Name", sectionPair.first.sFile ) );
					jnRoot.push_back ( JSONNode ( "Function Name", sectionPair.first.sFunction ) );
					jnRoot.push_back ( JSONNode ( "Thread Id", sectionPair.first.iThreadId.hash ( ) ) );
Pascal Palenda's avatar
Pascal Palenda committed
378
379
380

					if ( !sThreadName.empty ( ) )
						jnRoot.push_back ( JSONNode ( "Thread Name", sThreadName ) );
Pascal Palenda's avatar
Pascal Palenda committed
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417

					jnRoot.push_back ( JSONNode ( "Count", sectionPair.second.vpStartEndTimes.size ( ) ) );

					std::vector<double> vdDeltaTime ( sectionPair.second.vpStartEndTimes.size ( ) );
					std::vector<double> vdStartTime ( sectionPair.second.vpStartEndTimes.size ( ) );
					std::vector<double> vdEndTime ( sectionPair.second.vpStartEndTimes.size ( ) );

					std::transform ( sectionPair.second.vpStartEndTimes.begin ( ),
									 sectionPair.second.vpStartEndTimes.end ( ),
									 vdDeltaTime.begin ( ),
									 [] ( const std::pair<double, double>& interval ) -> double
					{
						return ( interval.second - interval.first ) * 1e6;
					} );

					std::transform ( sectionPair.second.vpStartEndTimes.begin ( ),
									 sectionPair.second.vpStartEndTimes.end ( ),
									 vdStartTime.begin ( ),
									 [] ( const std::pair<double, double>& interval ) -> double
					{
						return interval.first * 1e6;
					} );

					std::transform ( sectionPair.second.vpStartEndTimes.begin ( ),
									 sectionPair.second.vpStartEndTimes.end ( ),
									 vdEndTime.begin ( ),
									 [] ( const std::pair<double, double>& interval ) -> double
					{
						return interval.second * 1e6;
					} );

					jnRoot.push_back ( JSONNode ( "Min", *std::min_element ( vdDeltaTime.begin ( ), vdDeltaTime.end ( ) ) ) );
					jnRoot.push_back ( JSONNode ( "Max", *std::max_element ( vdDeltaTime.begin ( ), vdDeltaTime.end ( ) ) ) );
					jnRoot.push_back ( JSONNode ( "Mean", utils::Statistics::calculateMean ( vdDeltaTime ) ) );
					jnRoot.push_back ( JSONNode ( "Variance", utils::Statistics::calculateVariance ( vdDeltaTime ) ) );
					jnRoot.push_back ( JSONNode ( "StdDeviation", utils::Statistics::calculateStandardDeviation ( vdDeltaTime ) ) );

418
					/*JSONNode jnStartTimeArray ( JSON_ARRAY );
419
					jnStartTimeArray.set_name ( "Start Times" );
420
421
					jnStartTimeArray.reserve ( vdStartTime.size ( ) );
					JSONNode jnData( JSON_NUMBER );
422
423
424

					for ( const auto& timeStamp : vdStartTime )
					{
425
426
						jnData = timeStamp;
						jnStartTimeArray.push_back ( jnData );
427
428
429
430
431
432
					}

					jnRoot.push_back ( jnStartTimeArray );

					JSONNode jnEndTimeArray ( JSON_ARRAY );
					jnEndTimeArray.set_name ( "End Times" );
433
					jnEndTimeArray.reserve ( vdStartTime.size ( ) );
434

Pascal Palenda's avatar
Pascal Palenda committed
435
					for ( const auto& timeStamp : vdEndTime )
436
					{
437
438
						jnData = timeStamp;
						jnEndTimeArray.push_back ( jnData );
439
440
					}

441
442
					jnRoot.push_back ( jnEndTimeArray );*/

443
444
					jnRoot.push_back ( JSONNode ( "Start Times", utils::DataTypeUtils::convertToString ( vdStartTime, 8, "," ) ) );
					jnRoot.push_back ( JSONNode ( "End Times", utils::DataTypeUtils::convertToString ( vdEndTime, 8, "," ) ) );
Pascal Palenda's avatar
Pascal Palenda committed
445
446
447
448

					return jnRoot;
				}

Pascal Palenda's avatar
Pascal Palenda committed
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
				///
				/// \brief Parses the given ProfileValue pair to a JSON Node.
				/// \param valuePair pair with the Key and the ProfileEventCount to be parsed.
				/// \param mThreadNames map, containing the names of the threads.
				/// \return a JSON Node with the data of the given ProfileValue pair.
				/// 
				static JSONNode profileValuesToJSON ( const std::pair<ProfilerKey, ProfileValue>& valuePair,
													  const std::unordered_map<std::thread::id, std::string>& mThreadNames )
				{
					std::string sThreadName;

					const auto threadNameIterator = mThreadNames.find ( valuePair.first.iThreadId );

					if ( threadNameIterator != mThreadNames.end ( ) )
						sThreadName = threadNameIterator->second;

					JSONNode jnRoot;
					if ( !sThreadName.empty ( ) )
Pascal Palenda's avatar
Pascal Palenda committed
467
						jnRoot.set_name ( "[" + valuePair.first.sFunction + "][" + valuePair.first.sName + "][" + sThreadName + "]" );
Pascal Palenda's avatar
Pascal Palenda committed
468
					else
Pascal Palenda's avatar
Pascal Palenda committed
469
						jnRoot.set_name ( "[" + valuePair.first.sFunction + "][" + valuePair.first.sName + "][" + std::to_string ( valuePair.first.iThreadId.hash ( ) ) + "]" );
Pascal Palenda's avatar
Pascal Palenda committed
470

Pascal Palenda's avatar
Pascal Palenda committed
471
					jnRoot.push_back ( JSONNode ( "Name", valuePair.first.sName ) );
Pascal Palenda's avatar
Pascal Palenda committed
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
					jnRoot.push_back ( JSONNode ( "File Name", valuePair.first.sFile ) );
					jnRoot.push_back ( JSONNode ( "Function Name", valuePair.first.sFunction ) );
					jnRoot.push_back ( JSONNode ( "Thread Id", valuePair.first.iThreadId.hash ( ) ) );

					if ( !sThreadName.empty ( ) )
						jnRoot.push_back ( JSONNode ( "Thread Name", sThreadName ) );

					jnRoot.push_back ( JSONNode ( "Count", valuePair.second.size ( ) ) );

					std::tuple<JSONNode, JSONNode> valueNodes;

					// store the tim stamps
					switch ( valuePair.second.getDataType ( ) )
					{
						case DataType::Bool:
							valueNodes = valueDataToJSON<bool> ( valuePair.second );
							break;
						case DataType::Char:
							valueNodes = valueDataToJSON<char> ( valuePair.second );
							break;
						case DataType::Int:
							valueNodes = valueDataToJSON<int> ( valuePair.second );
							break;
						case DataType::UnsignedInt:
							valueNodes = valueDataToJSON<unsigned int> ( valuePair.second );
							break;
498
499
500
						case DataType::UnsignedLongInt:
							valueNodes = valueDataToJSON<unsigned long int> ( valuePair.second );
							break;
Pascal Palenda's avatar
Pascal Palenda committed
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
						case DataType::Float:
							valueNodes = valueDataToJSON<float> ( valuePair.second );
							break;
						case DataType::Double:
							valueNodes = valueDataToJSON<double> ( valuePair.second );
							break;
						default:
							ITA_EXCEPT_INVALID_PARAMETER ( "Unkown data type" );
					}

					jnRoot.push_back ( std::get<0> ( valueNodes ) );
					jnRoot.push_back ( std::get<1> ( valueNodes ) );

					return jnRoot;
				}

				///
				/// \brief Parse a ProfileValue to two JSONNodes for data and time stamp.
				/// \tparam T type of the desired ProfileValue.
				/// \param oProfileValue the ProfileValue to be parsed.
				/// \return returns a tuple with the JSONNode for the time stamp and data. 
				/// 
				template<class T>
				static std::tuple<JSONNode, JSONNode> valueDataToJSON ( const ProfileValue& oProfileValue )
				{
					std::vector<std::pair<T, double>> dataVector = oProfileValue.getData<T> ( );

528
					/*JSONNode jnTimeStampArray ( JSON_ARRAY );
529
					jnTimeStampArray.set_name ( "TimeStamps" );
530
					jnTimeStampArray.reserve ( dataVector.size ( ) );
531
532
					JSONNode jnDataArray ( JSON_ARRAY );
					jnDataArray.set_name ( "Data" );
533
534
					jnDataArray.reserve ( dataVector.size ( ) );
					JSONNode jnData ( JSON_NUMBER );
535
536

					for ( const auto& data : dataVector )
Pascal Palenda's avatar
Pascal Palenda committed
537
					{
538
539
540
541
						jnData = data.first;
						jnDataArray.push_back ( jnData );
						jnData = data.second;
						jnTimeStampArray.push_back ( jnData );
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
					}*/

					std::vector<T> vTData ( dataVector.size ( ) );
					std::vector<double> vdTimeStamps ( dataVector.size ( ) );

					std::transform ( dataVector.begin ( ),
									 dataVector.end ( ),
									 vTData.begin ( ),
									 [] ( const std::pair<T, double>& p ) -> T
					{
						return p.first;
					} );

					std::transform ( dataVector.begin ( ),
									 dataVector.end ( ),
									 vdTimeStamps.begin ( ),
									 [] ( const std::pair<T, double>& p ) -> double
					{
						return p.second;
					} );

563
564
					JSONNode jnTimeStampArray ( "Time Stamps", utils::DataTypeUtils::convertToString ( vdTimeStamps, 8, "," ) );
					JSONNode jnDataArray ( "Data", utils::DataTypeUtils::convertToString ( vTData, 8, "," ) );
Pascal Palenda's avatar
Pascal Palenda committed
565

566
567
568
					return std::make_pair ( jnTimeStampArray, jnDataArray );
				}

Pascal Palenda's avatar
Pascal Palenda committed
569
570
			private:

Pascal Palenda's avatar
Pascal Palenda committed
571
			#ifndef MAGIC_STATICS
572
				friend ProfilerInstance;
Pascal Palenda's avatar
Pascal Palenda committed
573
			#endif
574

Pascal Palenda's avatar
Pascal Palenda committed
575
576
577
578
579
580
581
582
583
				CProfiler ( ) = default;

				~CProfiler ( ) = default;

				CProfiler ( const CProfiler& other ) = delete;

				CProfiler& operator=( const CProfiler& other ) = delete;

				///
584
				/// \brief Map, storing the thread storage.
Pascal Palenda's avatar
Pascal Palenda committed
585
				/// 
586
				std::unordered_map<std::thread::id, SProfilerThreadStorage> m_mThreadStorage;
Pascal Palenda's avatar
Pascal Palenda committed
587
588
589
590
591

				///
				/// \brief Map, storing the names of named threads.
				/// 
				std::unordered_map<std::thread::id, std::string> m_mThreadNames;
Pascal Palenda's avatar
Pascal Palenda committed
592
593
594
595
596

				///
				/// \brief Mutex for write access.
				/// 
				std::mutex m_oMutex;
Pascal Palenda's avatar
Pascal Palenda committed
597
598
599
600
601
602
603

				///
				/// \brief True, if profiling is activated.
				///
				/// This is used to stop profiling while we save data.
				/// 
				bool m_bEnabled = true;
Pascal Palenda's avatar
Pascal Palenda committed
604
			};
Pascal Palenda's avatar
Pascal Palenda committed
605

606
607
608
609
610
611
			class ProfilerInstance final
			{
				friend CProfiler;
				CProfiler instance;
			} PROFILE_MANAGER;

Pascal Palenda's avatar
Pascal Palenda committed
612
			CProfiler& CProfiler::getInstance ( )
613
614
615
616
617
618
619
620
			{
			#ifdef MAGIC_STATICS
				static CProfiler instance;
				return instance;
			#else				
				return PROFILE_MANAGER.instance;
			#endif
			}
Pascal Palenda's avatar
Pascal Palenda committed
621
622
623
624
625
626
627
628
629
630
631

			void CSection::start ( )
			{
				m_dBegin = ITAClock::getDefaultClock ( )->getTime ( );
			}

			void CSection::end ( )
			{
				m_dEnd = ITAClock::getDefaultClock ( )->getTime ( );
			}

Pascal Palenda's avatar
Pascal Palenda committed
632
			bool CSection::finished ( ) const
Pascal Palenda's avatar
Pascal Palenda committed
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
			{
				return m_dEnd >= m_dBegin;
			}

			CSection::~CSection ( )
			{
				if ( !finished ( ) )
					profiler::endSection ( );
			}

			void startSection ( CSection& oSection,
								const std::string& sName,
								const std::string& sFunctionName,
								const std::string& sFile )
			{
				CProfiler::getInstance ( ).startSection ( oSection, sName, sFunctionName, sFile );
			}

			void endSection ( )
			{
				CProfiler::getInstance ( ).endSection ( );
			}

			void endAllSections ( )
			{
				CProfiler::getInstance ( ).endAllSections ( );
			}

			void addEventCount ( const std::string& sName, const std::string& sFunctionName, const std::string& sFile )
			{
				CProfiler::getInstance ( ).addEventCount ( sName, sFunctionName, sFile );
			}

Pascal Palenda's avatar
Pascal Palenda committed
666
667
668
669
670
			void storeValue ( const std::string& sName, const ValueData& valueData, const std::string& sFunctionName, const std::string& sFile )
			{
				CProfiler::getInstance ( ).addValue ( sName, valueData, sFunctionName, sFile );
			}

Pascal Palenda's avatar
Pascal Palenda committed
671
672
673
674
675
			void nameThread ( const std::string& sName )
			{
				CProfiler::getInstance ( ).nameThread ( sName );
			}

Pascal Palenda's avatar
Pascal Palenda committed
676
677
678
679
680
			std::stringstream profilerToJSON ( )
			{
				return CProfiler::getInstance ( ).profilerToJSON ( );
			}
		} // namespace profiler
Pascal Palenda's avatar
Pascal Palenda committed
681
682

		void storeProfilerData ( const std::string& sProfilerFileName )
683
684
		{
		#ifdef WITH_PROFILER
685
686
			PROFILER_EVENT_COUNT ( "STOP" );

687
688
689
690
691
692
			std::ofstream myProfile;
			myProfile.open ( sProfilerFileName );
			myProfile << profiler::profilerToJSON ( ).str ( );
			myProfile.close ( );
		#endif
		}
Pascal Palenda's avatar
Pascal Palenda committed
693
694
695
	} // namespace simulation_scheduler
} // namespace ITA

696
697
#endif

Pascal Palenda's avatar
Pascal Palenda committed
698