root/trunk/libffado/src/debugmodule/debugmodule.cpp

Revision 494, 12.6 kB (checked in by ppalmers, 15 years ago)

- switch over to a generic ffado_timestamp_t for the timestamped buffer (currently float)
- implemented some experimental stream phase sync method

- various small things

NOTE: not a very stable commit

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 /*
2  * Copyright (C) 2005-2007 by Daniel Wagner
3  * Copyright (C) 2005-2007 by Pieter Palmers
4  *
5  * This file is part of FFADO
6  * FFADO = Free Firewire (pro-)audio drivers for linux
7  *
8  * FFADO is based upon FreeBoB
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License version 2.1, as published by the Free Software Foundation;
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
22  * MA 02110-1301 USA
23  */
24
25 #include "debugmodule.h"
26
27 #include <stdarg.h>
28 #include <netinet/in.h>
29
30 #include <iostream>
31
32 using namespace std;
33
34 struct ColorEntry  {
35     const char* preSequence;
36     const char* postSequence;
37 };
38
39 ColorEntry colorTable[] = {
40     { "\033[31mFatal",   "\033[0m" },
41     { "\033[31mError",   "\033[0m" },
42     { "\033[31mWarning", "\033[0m" },
43     { "Debug",           ""        },
44 };
45
46
47 DebugModule::DebugModule( std::string name,  debug_level_t level )
48     : m_name( name )
49     , m_level( level )
50 {
51     if ( !DebugModuleManager::instance()->registerModule( *this ) ) {
52         cerr << "Could not register DebugModule (" << name
53              << ") at DebugModuleManager"
54              << endl;
55     }
56 }
57
58 DebugModule::~DebugModule()
59 {
60 //     if ( m_level >= eDL_VeryVerbose ) {
61 //         cout << "Unregistering "
62 //              << this->getName()
63 //              << " at DebugModuleManager"
64 //              << endl;
65 //     }
66     if ( !DebugModuleManager::instance()->unregisterModule( *this ) ) {
67         cerr << "Could not unregister DebugModule at DebugModuleManager"
68              << endl;
69     }
70
71 }
72
73 void
74 DebugModule::printShort( debug_level_t level,
75                          const char* format,
76                          ... ) const
77 {
78     if ( level > m_level ) {
79         return;
80     }
81
82     va_list arg;
83
84     va_start( arg, format );
85
86     DebugModuleManager::instance()->va_print( format, arg );
87
88     va_end( arg );
89 }
90
91 void
92 DebugModule::print( debug_level_t level,
93                     const char*   file,
94                     const char*   function,
95                     unsigned int  line,
96                     const char*   format,
97                     ... ) const
98 {
99     if ( level > m_level ) {
100         return;
101     }
102
103     va_list arg;
104     va_start( arg, format );
105
106     // remove the path info from the filename
107     const char *f = file;
108     const char *fname = file;
109     while((f=strstr(f, "/"))) {
110         f++; // move away from delimiter
111         fname=f;
112     }
113
114     DebugModuleManager::instance()->print( "%s (%s)[%4d] %s: ", getPreSequence( level ),
115                  fname,  line,  function );
116     DebugModuleManager::instance()->va_print( format, arg );
117     DebugModuleManager::instance()->print( "%s", getPostSequence( level ) );
118     va_end( arg );
119 }
120
121 const char*
122 DebugModule::getPreSequence( debug_level_t level ) const
123 {
124     if ( ( level <= eDL_Normal ) && ( level >= eDL_Fatal ) ) {
125         return colorTable[level].preSequence;
126     }
127     return colorTable[eDL_Normal].preSequence;
128 }
129
130 const char*
131 DebugModule::getPostSequence( debug_level_t level ) const
132 {
133     if ( ( level <= eDL_Normal ) && ( level >= eDL_Fatal ) ) {
134         return colorTable[level].postSequence;
135     }
136     return colorTable[eDL_Normal].postSequence;
137 }
138
139 //--------------------------------------
140
141 DebugModuleManager* DebugModuleManager::m_instance = 0;
142
143 DebugModuleManager::DebugModuleManager()
144     : mb_initialized(0)
145     , mb_inbuffer(0)
146     , mb_outbuffer(0)
147     , mb_overruns(0)
148
149 {
150
151 }
152
153 DebugModuleManager::~DebugModuleManager()
154 {
155     // cleanin up leftover modules
156     for ( DebugModuleVectorIterator it = m_debugModules.begin();
157           it != m_debugModules.end();
158           ++it )
159     {
160         fprintf(stderr,"Cleaning up leftover debug module: %s\n",(*it)->getName().c_str());
161         m_debugModules.erase( it );
162         delete *it;
163     }
164
165     if (!mb_initialized)
166         return;
167
168     pthread_mutex_lock(&mb_write_lock);
169     mb_initialized = 0;
170     pthread_cond_signal(&mb_ready_cond);
171     pthread_mutex_unlock(&mb_write_lock);
172
173     pthread_join(mb_writer_thread, NULL);
174     mb_flush();
175
176     if (mb_overruns)
177         fprintf(stderr, "WARNING: %d message buffer overruns!\n",
178             mb_overruns);
179     else
180         fprintf(stderr, "no message buffer overruns\n");
181
182     pthread_mutex_destroy(&mb_write_lock);
183     pthread_cond_destroy(&mb_ready_cond);
184
185 }
186
187 bool
188 DebugModuleManager::init()
189 {
190     if (mb_initialized)
191         return true;
192
193         // if ( m_level >= eDL_VeryVerbose )
194         //         cout << "DebugModuleManager init..." << endl;
195
196     pthread_mutex_init(&mb_write_lock, NULL);
197     pthread_cond_init(&mb_ready_cond, NULL);
198
199      mb_overruns = 0;
200      mb_initialized = 1;
201
202     if (pthread_create(&mb_writer_thread, NULL, &mb_thread_func, (void *)this) != 0)
203          mb_initialized = 0;
204
205     return true;
206 }
207
208 DebugModuleManager*
209 DebugModuleManager::instance()
210 {
211     if ( !m_instance ) {
212         m_instance = new DebugModuleManager;
213         if ( !m_instance ) {
214             cerr << "DebugModuleManager::instance Failed to create "
215                  << "DebugModuleManager" << endl;
216         }
217         if ( !m_instance->init() ) {
218             cerr << "DebugModuleManager::instance Failed to init "
219                  << "DebugModuleManager" << endl;
220         }
221     }
222     return m_instance;
223 }
224
225 bool
226 DebugModuleManager::registerModule( DebugModule& debugModule )
227 {
228     bool already_present=false;
229
230     for ( DebugModuleVectorIterator it = m_debugModules.begin();
231           it != m_debugModules.end();
232           ++it )
233     {
234         if ( *it == &debugModule ) {
235             already_present=true;
236             return true;
237         }
238     }
239
240     if (already_present) {
241         cerr << "DebugModuleManager::registerModule: Module already registered: "
242             << "DebugModule (" << debugModule.getName() << ")" << endl;
243     } else {
244         m_debugModules.push_back( &debugModule );
245     }
246     return true;
247 }
248
249 bool
250 DebugModuleManager::unregisterModule( DebugModule& debugModule )
251 {
252
253     for ( DebugModuleVectorIterator it = m_debugModules.begin();
254           it != m_debugModules.end();
255           ++it )
256     {
257         if ( *it == &debugModule ) {
258             m_debugModules.erase( it );
259             return true;
260         }
261     }
262
263     cerr << "DebugModuleManager::unregisterModule: Could not unregister "
264          << "DebugModule (" << debugModule.getName() << ")" << endl;
265     return false;
266 }
267
268 bool
269 DebugModuleManager::setMgrDebugLevel( std::string name, debug_level_t level )
270 {
271     for ( DebugModuleVectorIterator it = m_debugModules.begin();
272           it != m_debugModules.end();
273           ++it )
274     {
275         if ( (*it)->getName() == name ) {
276             return (*it)->setLevel( level );
277         }
278     }
279
280     cerr << "setDebugLevel: Did not find DebugModule ("
281          << name << ")" << endl;
282     return false;
283 }
284
285 void
286 DebugModuleManager::sync()
287 {
288     mb_flush();
289 }
290
291 void
292 DebugModuleManager::mb_flush()
293 {
294     /* called WITHOUT the mb_write_lock */
295     while (mb_outbuffer != mb_inbuffer) {
296         fputs(mb_buffers[mb_outbuffer], stderr);
297         mb_outbuffer = MB_NEXT(mb_outbuffer);
298     }
299 }
300
301 void *
302 DebugModuleManager::mb_thread_func(void *arg)
303 {
304
305     DebugModuleManager *m=static_cast<DebugModuleManager *>(arg);
306
307     /* The mutex is only to eliminate collisions between multiple
308      * writer threads and protect the condition variable. */
309      pthread_mutex_lock(&m->mb_write_lock);
310
311     while (m->mb_initialized) {
312          pthread_cond_wait(&m->mb_ready_cond, &m->mb_write_lock);
313
314          /* releasing the mutex reduces contention */
315          pthread_mutex_unlock(&m->mb_write_lock);
316          m->mb_flush();
317          pthread_mutex_lock(&m->mb_write_lock);
318     }
319
320      pthread_mutex_unlock(&m->mb_write_lock);
321
322     return NULL;
323 }
324
325 void
326 DebugModuleManager::print(const char *fmt, ...)
327 {
328     char msg[MB_BUFFERSIZE];
329     va_list ap;
330     unsigned int ntries=5;
331     struct timespec wait = {0,50000};
332    
333     /* format the message first, to reduce lock contention */
334     va_start(ap, fmt);
335     if (vsnprintf(msg, MB_BUFFERSIZE, fmt, ap) >= MB_BUFFERSIZE) {
336         print("WARNING: message truncated!\n");
337     }
338     va_end(ap);
339
340     if (!mb_initialized) {
341         /* Unable to print message with realtime safety.
342          * Complain and print it anyway. */
343         fprintf(stderr, "ERROR: messagebuffer not initialized: %s",
344             msg);
345         return;
346     }
347    
348     while (ntries) { // try a few times
349         if (pthread_mutex_trylock(&mb_write_lock) == 0) {
350             strncpy(mb_buffers[mb_inbuffer], msg, MB_BUFFERSIZE);
351             mb_inbuffer = MB_NEXT(mb_inbuffer);
352             pthread_cond_signal(&mb_ready_cond);
353             pthread_mutex_unlock(&mb_write_lock);
354             break;
355         } else {
356             nanosleep(&wait, NULL);
357             ntries--;
358         }
359     }
360    
361     if (ntries==0) {  /* lock collision */
362 //         atomic_add(&mb_overruns, 1);
363         // FIXME: atomicity
364         mb_overruns++; // skip the atomicness for now
365     }
366 }
367
368
369 void
370 DebugModuleManager::va_print (const char *fmt, va_list ap)
371 {
372     char msg[MB_BUFFERSIZE];
373     unsigned int ntries=5;
374     struct timespec wait = {0,50000};
375
376     /* format the message first, to reduce lock contention */
377     if (vsnprintf(msg, MB_BUFFERSIZE, fmt, ap) >= MB_BUFFERSIZE) {
378         print("WARNING: message truncated!\n");
379     }
380    
381     if (!mb_initialized) {
382         /* Unable to print message with realtime safety.
383          * Complain and print it anyway. */
384         fprintf(stderr, "ERROR: messagebuffer not initialized: %s",
385             msg);
386         return;
387     }
388
389     while (ntries) { // try a few times
390         if (pthread_mutex_trylock(&mb_write_lock) == 0) {
391             strncpy(mb_buffers[mb_inbuffer], msg, MB_BUFFERSIZE);
392             mb_inbuffer = MB_NEXT(mb_inbuffer);
393             pthread_cond_signal(&mb_ready_cond);
394             pthread_mutex_unlock(&mb_write_lock);
395             break;
396         } else {
397             nanosleep(&wait, NULL);
398             ntries--;
399         }
400     }
401    
402     if (ntries==0) {  /* lock collision */
403 //         atomic_add(&mb_overruns, 1);
404         // FIXME: atomicity
405         mb_overruns++; // skip the atomicness for now
406     }
407 }
408
409 //----------------------------------------
410
411 unsigned char
412 toAscii( unsigned char c )
413 {
414     if ( ( c > 31 ) && ( c < 126) ) {
415         return c;
416     } else {
417         return '.';
418     }
419 }
420
421 /* converts a quadlet to a uchar * buffer
422  * not implemented optimally, but clear
423  */
424 void
425 quadlet2char( quadlet_t quadlet, unsigned char* buff )
426 {
427     *(buff)   = (quadlet>>24)&0xFF;
428     *(buff+1) = (quadlet>>16)&0xFF;
429     *(buff+2) = (quadlet>> 8)&0xFF;
430     *(buff+3) = (quadlet)    &0xFF;
431 }
432
433 void
434 hexDump( unsigned char *data_start, unsigned int length )
435 {
436     unsigned int i=0;
437     unsigned int byte_pos;
438     unsigned int bytes_left;
439
440     if ( length <= 0 ) {
441         return;
442     }
443     if ( length >= 7 ) {
444         for ( i = 0; i < (length-7); i += 8 ) {
445             printf( "%04X: %02X %02X %02X %02X %02X %02X %02X %02X "
446                     "- [%c%c%c%c%c%c%c%c]\n",
447
448                     i,
449
450                     *(data_start+i+0),
451                     *(data_start+i+1),
452                     *(data_start+i+2),
453                     *(data_start+i+3),
454                     *(data_start+i+4),
455                     *(data_start+i+5),
456                     *(data_start+i+6),
457                     *(data_start+i+7),
458
459                     toAscii( *(data_start+i+0) ),
460                     toAscii( *(data_start+i+1) ),
461                     toAscii( *(data_start+i+2) ),
462                     toAscii( *(data_start+i+3) ),
463                     toAscii( *(data_start+i+4) ),
464                     toAscii( *(data_start+i+5) ),
465                     toAscii( *(data_start+i+6) ),
466                     toAscii( *(data_start+i+7) )
467                 );
468         }
469     }
470     byte_pos = i;
471     bytes_left = length - byte_pos;
472
473     printf( "%04X:" ,i );
474     for ( i = byte_pos; i < length; i += 1 ) {
475         printf( " %02X", *(data_start+i) );
476     }
477     for ( i=0; i < 8-bytes_left; i+=1 ) {
478         printf( "   " );
479     }
480
481     printf( " - [" );
482     for ( i = byte_pos; i < length; i += 1) {
483         printf( "%c", toAscii(*(data_start+i)));
484     }
485     for ( i = 0; i < 8-bytes_left; i += 1) {
486         printf( " " );
487     }
488
489     printf( "]" );
490     printf( "\n" );
491 }
492
493 void
494 hexDumpQuadlets( quadlet_t *data, unsigned int length )
495 {
496     unsigned int i=0;
497
498     if ( length <= 0 ) {
499         return;
500     }
501     for (i = 0; i< length; i += 1) {
502         printf( "%02d %04X: %08X (%08X)"
503                 "\n", i, i*4, data[i],ntohl(data[i]));
504     }
505 }
506
507
Note: See TracBrowser for help on using the browser.