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