1 /*---------------------------------------------------------------------------*
2 Project: Horizon
3 File: dbg_Instrument.cpp
4
5 Copyright (C)2009-2012 Nintendo Co., Ltd. All rights reserved.
6
7 These coded instructions, statements, and computer programs contain
8 proprietary information of Nintendo of America Inc. and/or Nintendo
9 Company Ltd., and are protected by Federal copyright law. They may
10 not be disclosed to third parties or copied or duplicated in any form,
11 in whole or in part, without the prior written consent of Nintendo.
12
13 $Rev: 46347 $
14 *---------------------------------------------------------------------------*/
15
16 #include <nn.h>
17 #include <nn/dbg/dbg_Instrument.h>
18
19 #include <cstdio>
20 #include <cstdarg>
21 #include <cstring>
22
23
24 namespace
25 {
26 #define NN_DBG_INS_PRINTF_BUFFER_LENGTH 1024
27
ins_VPrintf(const char * fmt,va_list arg)28 void ins_VPrintf(const char* fmt, va_list arg)
29 {
30 char buf[NN_DBG_INS_PRINTF_BUFFER_LENGTH];
31 int length = ::std::vsnprintf(buf, sizeof(buf), fmt, arg);
32 if (length >= NN_DBG_INS_PRINTF_BUFFER_LENGTH)
33 {
34 length = NN_DBG_INS_PRINTF_BUFFER_LENGTH - 1; // need not count \0 of tail end
35 }
36 NN_PUT(buf, length);
37 }
ins_Printf(const char * fmt,...)38 void ins_Printf(const char* fmt, ...)
39 {
40 va_list vlist;
41
42 va_start(vlist, fmt);
43 ins_VPrintf(fmt, vlist);
44 va_end(vlist);
45 }
46
47 // Critical section for process exclusion
48 nn::os::CriticalSection sCriticalSection = nn::WithInitialize();
49
50 // Measurement: List
51 nn::dbg::CTR::Instrument* sListHead = NULL;
52
53 // Measurement: function for output
54 void (*sLogFuncForInstrument)( const char*, ... ) = ins_Printf;
55
56 // Statistic: function for output
57 void (*sLogFuncForStatistics)( const char*, ... ) = ins_Printf;
58 }
59
60 namespace nn {
61 namespace dbg {
62 namespace CTR {
63
64 //================================================================
65 // Measurement
66 //================================================================
67 // Add to list
AddToList(Instrument * pInfo)68 void Instrument::AddToList( Instrument* pInfo )
69 {
70 if ( ! sListHead )
71 {
72 sListHead = pInfo;
73 }
74 else
75 {
76 Instrument* p = sListHead;
77 while( p->mNext )
78 {
79 p = p->mNext;
80 }
81 p->mNext = pInfo;
82 }
83
84 pInfo->mNext = NULL;
85 }
86
87 // Delete from the list
DeleteFromList(Instrument * pInfo)88 void Instrument::DeleteFromList( Instrument* pInfo )
89 {
90 Instrument* p = sListHead;
91 Instrument* prev = NULL;
92 while(p)
93 {
94 if ( p == pInfo )
95 {
96 if ( prev )
97 {
98 prev->mNext = p->mNext;
99 }
100 else
101 {
102 sListHead = p->mNext;
103 }
104 break;
105 }
106 prev = p;
107 p++;
108 }
109 }
110
111 // Search by thread ID
SearchInfo(bit32 threadId)112 Instrument* Instrument::SearchInfo( bit32 threadId )
113 {
114 Instrument* p = sListHead;
115 while( p && p->mThreadId != threadId )
116 {
117 p = p->mNext;
118 }
119 return p;
120 }
121
122 // Pointer operation function: in final position?
IsBottom_BufferPtr(void * p)123 bool Instrument::IsBottom_BufferPtr( void* p )
124 {
125 p = reinterpret_cast<u8*>(p) + GetRecordSize();
126 return ( p >= mBufferBottom )? true: false;
127 }
128 // Pointer operation function: advance one
Inc_BufferPtr(void * p)129 void* Instrument::Inc_BufferPtr( void* p )
130 {
131 p = reinterpret_cast<u8*>(p) + GetRecordSize();
132 if ( p >= mBufferBottom )
133 {
134 p = mBufferTop;
135 }
136 return p;
137 }
138 // Pointer operation function: go back one
Dec_BufferPtr(void * p)139 void* Instrument::Dec_BufferPtr( void* p )
140 {
141 p = reinterpret_cast<u8*>(p) - GetRecordSize();
142 if ( p < mBufferTop )
143 {
144 p = reinterpret_cast<u8*>(mBufferBottom) - GetRecordSize();
145 }
146 return p;
147 }
148
149 // Initialization
Initialize(void * pBuffer,size_t bufferSize,s32 type)150 void Instrument::Initialize( void* pBuffer, size_t bufferSize, s32 type )
151 {
152 nn::os::CriticalSection::ScopedLock lock(sCriticalSection);
153 bit32 id = nn::os::Thread::GetCurrentId();
154
155 // Check if already registered
156 if ( SearchInfo( id ) )
157 {
158 return;
159 }
160
161 mThreadId = id;
162 mCount = 0;
163 mType = type;
164
165 // Ring mode and stack mode are not possible
166 if ( IsRingBuffer() && ! IsTraceLog() )
167 {
168 return;
169 }
170
171 u32 size1 = GetRecordSize();
172 mBufferSize = (bufferSize / size1) * size1;
173 mBufferTop = (bufferSize>0)? pBuffer: NULL;
174 mBufferBottom = (bufferSize>0)? (reinterpret_cast<u8*>(mBufferTop) + mBufferSize): NULL;
175
176 // Clear
177 Clear();
178
179 // Add to the list
180 AddToList( this );
181 }
182
183 // End processing
Finalize(void)184 void Instrument::Finalize(void)
185 {
186 nn::os::CriticalSection::ScopedLock lock(sCriticalSection);
187
188 // Delete from the list
189 DeleteFromList( this );
190 }
191
192 // Clear buffer
Clear(void)193 void Instrument::Clear(void)
194 {
195 mBufferOrigin = mBufferTop;
196 mBufferPtr = mBufferTop;
197 mBufferCount = 0;
198 }
199
200 // Enable measurement
Enable(void)201 void Instrument::Enable( void )
202 {
203 mIsAvailable = true;
204 }
205
206 // Disable measurement
Disable(void)207 void Instrument::Disable( void )
208 {
209 mIsAvailable = false;
210 }
211
212 // Measurement Entry
EntryFunc(void * func_address,void * call_site)213 void Instrument::EntryFunc( void *func_address, void *call_site )
214 {
215 if ( ! mIsAvailable )
216 {
217 return;
218 }
219
220 // Number of times called
221 mCount++;
222 mBufferCount++;
223
224 // No buffer set
225 if ( ! mBufferTop || ! mBufferPtr )
226 {
227 return;
228 }
229
230 // Record with system tick
231 if ( IsRecordTick() )
232 {
233 RecordForLogWithTick* p = reinterpret_cast<RecordForLogWithTick*>(mBufferPtr);
234 p->mAddress = reinterpret_cast<u32>(func_address) & 0xfffffffe;
235 p->mCall = reinterpret_cast<u32>(call_site);
236 p->mTick = nn::os::Tick::GetSystemCurrent();
237 }
238 // Record without system tick
239 else
240 {
241 RecordForLog* p = reinterpret_cast<RecordForLog*>(mBufferPtr);
242 p->mAddress = reinterpret_cast<u32>(func_address);
243 p->mCall = reinterpret_cast<u32>(call_site);
244 }
245
246 // Next pointer
247 mBufferPtr = Inc_BufferPtr( mBufferPtr );
248 if ( mBufferPtr == mBufferOrigin )
249 {
250 if ( IsRingBuffer() )
251 {
252 mBufferOrigin = Inc_BufferPtr( mBufferOrigin );
253 }
254 else
255 {
256 mBufferPtr = NULL;
257 }
258 }
259 }
260
261 // Measurement Exit
ExitFunc(void * func_address,void * call_site)262 void Instrument::ExitFunc( void *func_address, void *call_site )
263 {
264 NN_UNUSED_VAR( func_address );
265 NN_UNUSED_VAR( call_site );
266 if ( ! mIsAvailable )
267 {
268 return;
269 }
270
271 // No buffer set
272 if ( ! mBufferTop || ! mBufferPtr )
273 {
274 return;
275 }
276
277 // Log mode
278 if ( IsTraceLog() )
279 {
280 // Only when recording with system tick
281 if ( IsRecordTick() )
282 {
283 RecordForLogWithTick* p = reinterpret_cast<RecordForLogWithTick*>(mBufferPtr);
284 p->mAddress = (reinterpret_cast<u32>(func_address) & 0xfffffffe) | 0x1;
285 p->mCall = reinterpret_cast<u32>(call_site);
286 p->mTick = nn::os::Tick::GetSystemCurrent();
287
288 // Next pointer
289 mBufferPtr = Inc_BufferPtr( mBufferPtr );
290 if ( mBufferPtr == mBufferOrigin )
291 {
292 if ( IsRingBuffer() )
293 {
294 mBufferOrigin = Inc_BufferPtr( mBufferOrigin );
295 }
296 else
297 {
298 mBufferPtr = NULL;
299 }
300 }
301 }
302 }
303 // Stack mode
304 else
305 {
306 if ( mBufferPtr != mBufferOrigin )
307 {
308 mBufferPtr = reinterpret_cast<u8*>(mBufferPtr) - GetRecordSize();
309 }
310 }
311 }
312
313 // Output function configuration
SetLogFunction(void (* logFunc)(const char *,...))314 void Instrument::SetLogFunction( void (*logFunc)( const char*, ... ) )
315 {
316 nn::os::CriticalSection::ScopedLock lock(sCriticalSection);
317 sLogFuncForInstrument = logFunc;
318 }
319
320 // Result output
Dump(void)321 void Instrument::Dump( void )
322 {
323 nn::os::CriticalSection::ScopedLock lock(sCriticalSection);
324
325 // No buffer set
326 if ( ! mBufferTop )
327 {
328 return;
329 }
330
331 sLogFuncForInstrument("count: %d\n", mCount );
332 // Record with system tick
333 if ( IsRecordTick() )
334 {
335 RecordForLogWithTick* p = reinterpret_cast<RecordForLogWithTick*>(mBufferOrigin);
336
337 while( p != mBufferPtr )
338 {
339 if ( p->mAddress & 1 )
340 {
341 sLogFuncForInstrument("func: %08x out tick: %llx\n", p->mAddress & 0xfffffffe, p->mTick);
342 }
343 else
344 {
345 sLogFuncForInstrument("func: %08x in caller: %08x tick: %llx\n", p->mAddress & 0xfffffffe, p->mCall, p->mTick);
346 }
347 if ( ! IsRingBuffer() && IsBottom_BufferPtr(p) )
348 {
349 break;
350 }
351 p = reinterpret_cast<RecordForLogWithTick*>(Inc_BufferPtr(p));
352 }
353 }
354 // Record without system tick
355 else
356 {
357 RecordForLog* p = reinterpret_cast<RecordForLog*>(mBufferOrigin);
358
359 while( p != mBufferPtr )
360 {
361 sLogFuncForInstrument("func: %08x caller: %08x\n", p->mAddress, p->mCall);
362 if ( ! IsRingBuffer() && IsBottom_BufferPtr(p) )
363 {
364 break;
365 }
366 p = reinterpret_cast<RecordForLog*>(Inc_BufferPtr(p));
367 }
368 }
369 sLogFuncForInstrument("\n");
370 }
371
372 //================================================================
373 // Statistic
374 //================================================================
375 // Subroutine for output: align digits
arrangeTick(f32 tick)376 static f32 arrangeTick( f32 tick )
377 {
378 return
379 ( tick > 1000000.f ) ? (tick / 1000000.f) :
380 ( tick > 1000.f ) ? (tick / 1000.f) :
381 tick;
382 }
383 // Subroutine for output: units when aligning digits
arrangeTickUnit(f32 tick)384 static const char* arrangeTickUnit( f32 tick )
385 {
386 return
387 ( tick > 1000000.f ) ? "s " :
388 ( tick > 1000.f ) ? "ms" :
389 "us";
390 }
391
392 // Initialization
Initialize(void * buffer,size_t bufferSize)393 void Statistics::Initialize( void* buffer, size_t bufferSize )
394 {
395 size_t size1 = sizeof(StatisticsRecord);
396
397 // Buffer information
398 mBuffer = reinterpret_cast<StatisticsRecord*>(buffer);
399 mBufferSize = (bufferSize / size1) * size1;
400 mBufferBottom = (mBuffer && bufferSize>0)? (reinterpret_cast<u8*>(mBuffer) + mBufferSize): NULL;
401
402 Clear();
403 }
404
405 // Totals
Collect(Instrument * pInfo,bool isClear)406 void Statistics::Collect( Instrument* pInfo, bool isClear )
407 {
408 nn::os::CriticalSection::ScopedLock lock(sCriticalSection);
409 if ( ! mBuffer )
410 {
411 return;
412 }
413
414 // Check each item of the measurement buffer
415 RecordForLogWithTick* pRecord = reinterpret_cast<RecordForLogWithTick*>(pInfo->mBufferOrigin);
416 while( pRecord != pInfo->mBufferPtr )
417 {
418 //NN_LOG("pRecord=%x pInfo->mBufferPtr=%x ring=%d bottom=%x\n", pRecord, pInfo->mBufferPtr, pInfo->IsRingBuffer(), pInfo->mBufferBottom );
419 // exit line when there is time measurement
420 if ( pInfo->IsRecordTick() && (pRecord->mAddress & 1) )
421 {
422 StatisticsRecord* p = mBuffer;
423 while( p<mBufferBottom )
424 {
425 if ( (pRecord->mAddress & 0xfffffffe) == p->mAddress )
426 {
427 // When there is entry
428 if ( p->mTickStart )
429 {
430 p->mTickSum += (pRecord->mTick - p->mTickStart);
431 p->mTickStart = 0;
432 p->mTickCount++;
433 break;
434 }
435 }
436 p++;
437 }
438 }
439 // entry line with time measurement or entry line without time measurement
440 else
441 {
442 // Search if already exists
443 bool isFound = false;
444 StatisticsRecord* pBlank = NULL;
445 StatisticsRecord* p = mBuffer;
446 while( p<mBufferBottom )
447 {
448 if ( ! p->mAddress && ! pBlank )
449 {
450 pBlank = p;
451 }
452 // Update record since the address of the record in question exists
453 else if ( pRecord->mAddress == p->mAddress )
454 {
455 p->mEntryCount++;
456 p->mTickStart = pRecord->mTick;
457 isFound = true;
458 break;
459 }
460 p++;
461 }
462
463 // Did not find
464 if ( ! isFound )
465 {
466 //Register new since there is a space
467 if ( pBlank )
468 {
469 pBlank->mAddress = pRecord->mAddress;
470 pBlank->mEntryCount = 1;
471 pBlank->mTickCount = 0;
472 pBlank->mTickStart = pRecord->mTick;
473 pBlank->mTickSum = 0;
474 }
475 //Since there is no space, record that there was an ignored line
476 else
477 {
478 mIgnoredLines++;
479 }
480 }
481 }
482
483 // Did it scan to the end for a normal buffer?
484 if ( ! pInfo->IsRingBuffer() && ( reinterpret_cast<u8*>(pRecord) + pInfo->GetRecordSize() >= pInfo->mBufferBottom ) )
485 {
486 break;
487 }
488
489 // Next record
490 pRecord = reinterpret_cast<RecordForLogWithTick*>(pInfo->Inc_BufferPtr( pRecord ));
491 }
492
493 // Clear measurement buffer content
494 if ( isClear )
495 {
496 pInfo->Clear();
497 }
498 }
499
500 // information display
Dump(bool isArrangeTick)501 void Statistics::Dump( bool isArrangeTick )
502 {
503 nn::os::CriticalSection::ScopedLock lock(sCriticalSection);
504 StatisticsRecord* p = mBuffer;
505 if ( ! p )
506 {
507 return;
508 }
509 while( p<mBufferBottom )
510 {
511 if ( p->mAddress )
512 {
513 f32 sumMicroSec = p->mTickSum / (nn::os::Tick::TICKS_PER_SECOND / (1000.f * 1000.f));
514 u32 ave = p->mTickSum / p->mTickCount;
515 f32 aveMicroSec = ave / (nn::os::Tick::TICKS_PER_SECOND / (1000.f * 1000.f));
516
517 if ( isArrangeTick )
518 {
519 sLogFuncForStatistics( "func: %08x count:%8d (%8d) sum: %10.2f %s (ave: %10.2f %s)\n",
520 p->mAddress, p->mEntryCount, p->mTickCount,
521 arrangeTick(sumMicroSec),
522 arrangeTickUnit(sumMicroSec),
523 arrangeTick(aveMicroSec),
524 arrangeTickUnit(aveMicroSec) );
525 }
526 else
527 {
528 sLogFuncForStatistics( "func: %08x count:%8d (%8d) sum: %10.2f us (ave: %10.2f us)\n",
529 p->mAddress, p->mEntryCount, p->mTickCount,
530 sumMicroSec, aveMicroSec );
531 }
532 }
533 p++;
534 }
535 sLogFuncForStatistics("\n");
536 }
537 // Discard information
Clear(void)538 void Statistics::Clear(void)
539 {
540 StatisticsRecord* p = mBuffer;
541 if ( ! p )
542 {
543 return;
544 }
545 while( p<mBufferBottom )
546 {
547 p->mAddress = NULL;
548 p->mEntryCount = 0;
549 p->mTickCount = 0;
550 p->mTickSum = 0;
551 p->mTickStart = 0;
552 p++;
553 }
554
555 mIgnoredLines = 0;
556 }
557
558 // Output function setting
SetLogFunction(void (* logFunc)(const char *,...))559 void Statistics::SetLogFunction( void (*logFunc)( const char*, ... ) )
560 {
561 nn::os::CriticalSection::ScopedLock lock(sCriticalSection);
562 sLogFuncForStatistics = logFunc;
563 }
564 }
565
566 //================================================================================
567 // Call function for GCC format measurements
568 //================================================================================
569 extern "C"{
570
571 #ifdef NN_BUILD_VERBOSE
__cyg_profile_func_enter(void * func_address,void * call_site)572 NN_WEAK_SYMBOL void __cyg_profile_func_enter( void *func_address, void *call_site )
573 {
574 nn::dbg::CTR::Instrument* pInfo = nn::dbg::CTR::Instrument::SearchInfo( nn::os::Thread::GetCurrentId() );
575 if ( pInfo )
576 {
577 pInfo->EntryFunc( func_address, call_site );
578 }
579 }
580
581
__cyg_profile_func_exit(void * func_address,void * call_site)582 NN_WEAK_SYMBOL void __cyg_profile_func_exit ( void *func_address, void *call_site )
583 {
584 nn::dbg::CTR::Instrument* pInfo = nn::dbg::CTR::Instrument::SearchInfo( nn::os::Thread::GetCurrentId() );
585 if ( pInfo )
586 {
587 pInfo->ExitFunc( func_address, call_site );
588 }
589 }
590
591 #else
592
593 NN_WEAK_SYMBOL void __cyg_profile_func_enter( void *func_address, void *call_site )
594 {
595 NN_UNUSED_VAR( func_address );
596 NN_UNUSED_VAR( call_site );
597 // Do nothing
598 }
599 NN_WEAK_SYMBOL void __cyg_profile_func_exit ( void *func_address, void *call_site )
600 {
601 NN_UNUSED_VAR( func_address );
602 NN_UNUSED_VAR( call_site );
603 // Do nothing
604 }
605
606 #endif
607
608 } // namespace CTR
609 } // namespace dbg
610 } // namespace nn
611