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

Revision 612, 13.6 kB (checked in by ppalmers, 15 years ago)

- Remove dependencies between BeBoB and generic AVC code (functionblocks)
- Clean up the code at some places

  • 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 #include <time.h>
33
34 // #define DO_MESSAGE_BUFFER_PRINT
35
36 #ifndef DO_MESSAGE_BUFFER_PRINT
37         #warning Printing debug info without ringbuffer, not RT-safe!
38 #endif
39
40 using namespace std;
41
42 struct ColorEntry  {
43     const char* preSequence;
44     const char* postSequence;
45 };
46
47 ColorEntry colorTable[] = {
48     { "\033[31mFatal",   "\033[0m" },
49     { "\033[31mError",   "\033[0m" },
50     { "\033[31mWarning", "\033[0m" },
51     { "Debug",           ""        },
52 };
53
54
55 DebugModule::DebugModule( std::string name,  debug_level_t level )
56     : m_name( name )
57     , m_level( level )
58 {
59     if ( !DebugModuleManager::instance()->registerModule( *this ) ) {
60         cerr << "Could not register DebugModule (" << name
61              << ") at DebugModuleManager"
62              << endl;
63     }
64 }
65
66 DebugModule::~DebugModule()
67 {
68 //     if ( m_level >= eDL_VeryVerbose ) {
69 //         cout << "Unregistering "
70 //              << this->getName()
71 //              << " at DebugModuleManager"
72 //              << endl;
73 //     }
74     if ( !DebugModuleManager::instance()->unregisterModule( *this ) ) {
75         cerr << "Could not unregister DebugModule at DebugModuleManager"
76              << endl;
77     }
78
79 }
80
81 void
82 DebugModule::printShort( debug_level_t level,
83                          const char* format,
84                          ... ) const
85 {
86     if ( level > m_level ) {
87         return;
88     }
89
90     va_list arg;
91
92     va_start( arg, format );
93
94     DebugModuleManager::instance()->va_print( format, arg );
95
96     va_end( arg );
97 }
98
99 void
100 DebugModule::print( debug_level_t level,
101                     const char*   file,
102                     const char*   function,
103                     unsigned int  line,
104                     const char*   format,
105                     ... ) const
106 {
107     if ( level > m_level ) {
108         return;
109     }
110
111     va_list arg;
112     va_start( arg, format );
113
114     // remove the path info from the filename
115     const char *f = file;
116     const char *fname = file;
117     while((f=strstr(f, "/"))) {
118         f++; // move away from delimiter
119         fname=f;
120     }
121    
122     // add a timing timestamp
123     struct timespec ts;
124     clock_gettime(CLOCK_MONOTONIC, &ts);
125     uint32_t ts_usec=(uint32_t)(ts.tv_sec * 1000000LL + ts.tv_nsec / 1000LL);
126    
127     DebugModuleManager::instance()->print( "%010lu: %s (%s)[%4d] %s: ",
128                  ts_usec, getPreSequence( level ),
129                  fname,  line,  function );
130     DebugModuleManager::instance()->va_print( format, arg );
131     DebugModuleManager::instance()->print( "%s", getPostSequence( level ) );
132     va_end( arg );
133 }
134
135 const char*
136 DebugModule::getPreSequence( debug_level_t level ) const
137 {
138     if ( ( level <= eDL_Normal ) && ( level >= eDL_Fatal ) ) {
139         return colorTable[level].preSequence;
140     }
141     return colorTable[eDL_Normal].preSequence;
142 }
143
144 const char*
145 DebugModule::getPostSequence( debug_level_t level ) const
146 {
147     if ( ( level <= eDL_Normal ) && ( level >= eDL_Fatal ) ) {
148         return colorTable[level].postSequence;
149     }
150     return colorTable[eDL_Normal].postSequence;
151 }
152
153 //--------------------------------------
154
155 DebugModuleManager* DebugModuleManager::m_instance = 0;
156
157 DebugModuleManager::DebugModuleManager()
158     : mb_initialized(0)
159     , mb_inbuffer(0)
160     , mb_outbuffer(0)
161     , mb_overruns(0)
162
163 {
164
165 }
166
167 DebugModuleManager::~DebugModuleManager()
168 {
169     // cleanin up leftover modules
170     for ( DebugModuleVectorIterator it = m_debugModules.begin();
171           it != m_debugModules.end();
172           ++it )
173     {
174         fprintf(stderr,"Cleaning up leftover debug module: %s\n",(*it)->getName().c_str());
175         m_debugModules.erase( it );
176         delete *it;
177     }
178
179     if (!mb_initialized)
180         return;
181
182     pthread_mutex_lock(&mb_write_lock);
183     mb_initialized = 0;
184     pthread_cond_signal(&mb_ready_cond);
185     pthread_mutex_unlock(&mb_write_lock);
186
187     pthread_join(mb_writer_thread, NULL);
188     mb_flush();
189
190     if (mb_overruns)
191         fprintf(stderr, "WARNING: %d message buffer overruns!\n",
192             mb_overruns);
193     else
194         fprintf(stderr, "no message buffer overruns\n");
195
196     pthread_mutex_destroy(&mb_write_lock);
197     pthread_cond_destroy(&mb_ready_cond);
198
199 }
200
201 bool
202 DebugModuleManager::init()
203 {
204     if (mb_initialized)
205         return true;
206
207         // if ( m_level >= eDL_VeryVerbose )
208         //         cout << "DebugModuleManager init..." << endl;
209
210     pthread_mutex_init(&mb_write_lock, NULL);
211     pthread_mutex_init(&mb_flush_lock, NULL);
212     pthread_cond_init(&mb_ready_cond, NULL);
213
214      mb_overruns = 0;
215      mb_initialized = 1;
216
217     if (pthread_create(&mb_writer_thread, NULL, &mb_thread_func, (void *)this) != 0)
218          mb_initialized = 0;
219
220     return true;
221 }
222
223 DebugModuleManager*
224 DebugModuleManager::instance()
225 {
226     if ( !m_instance ) {
227         m_instance = new DebugModuleManager;
228         if ( !m_instance ) {
229             cerr << "DebugModuleManager::instance Failed to create "
230                  << "DebugModuleManager" << endl;
231         }
232         if ( !m_instance->init() ) {
233             cerr << "DebugModuleManager::instance Failed to init "
234                  << "DebugModuleManager" << endl;
235         }
236     }
237     return m_instance;
238 }
239
240 bool
241 DebugModuleManager::registerModule( DebugModule& debugModule )
242 {
243     bool already_present=false;
244
245     for ( DebugModuleVectorIterator it = m_debugModules.begin();
246           it != m_debugModules.end();
247           ++it )
248     {
249         if ( *it == &debugModule ) {
250             already_present=true;
251             return true;
252         }
253     }
254
255     if (already_present) {
256         cerr << "DebugModuleManager::registerModule: Module already registered: "
257             << "DebugModule (" << debugModule.getName() << ")" << endl;
258     } else {
259         m_debugModules.push_back( &debugModule );
260     }
261     return true;
262 }
263
264 bool
265 DebugModuleManager::unregisterModule( DebugModule& debugModule )
266 {
267
268     for ( DebugModuleVectorIterator it = m_debugModules.begin();
269           it != m_debugModules.end();
270           ++it )
271     {
272         if ( *it == &debugModule ) {
273             m_debugModules.erase( it );
274             return true;
275         }
276     }
277
278     cerr << "DebugModuleManager::unregisterModule: Could not unregister "
279          << "DebugModule (" << debugModule.getName() << ")" << endl;
280     return false;
281 }
282
283 bool
284 DebugModuleManager::setMgrDebugLevel( std::string name, debug_level_t level )
285 {
286     for ( DebugModuleVectorIterator it = m_debugModules.begin();
287           it != m_debugModules.end();
288           ++it )
289     {
290         if ( (*it)->getName() == name ) {
291             return (*it)->setLevel( level );
292         }
293     }
294
295     cerr << "setDebugLevel: Did not find DebugModule ("
296          << name << ")" << endl;
297     return false;
298 }
299
300 void
301 DebugModuleManager::flush()
302 {
303 //     mb_flush();
304 }
305
306 void
307 DebugModuleManager::mb_flush()
308 {
309     /* called WITHOUT the mb_write_lock */
310    
311     /* the flush lock is to allow a flush from multiple threads
312      * this allows a code section that outputs a lot of debug messages
313      * and that can be blocked to flush the buffer itself such that it
314      * does not overflow.
315      */
316     DebugModuleManager *m=DebugModuleManager::instance();
317     pthread_mutex_lock(&m->mb_flush_lock);
318     while (mb_outbuffer != mb_inbuffer) {
319         fputs(mb_buffers[mb_outbuffer], stderr);
320         mb_outbuffer = MB_NEXT(mb_outbuffer);
321     }
322     pthread_mutex_unlock(&m->mb_flush_lock);
323 }
324
325 void *
326 DebugModuleManager::mb_thread_func(void *arg)
327 {
328
329     DebugModuleManager *m=static_cast<DebugModuleManager *>(arg);
330
331     /* The mutex is only to eliminate collisions between multiple
332      * writer threads and protect the condition variable. */
333      pthread_mutex_lock(&m->mb_write_lock);
334
335     while (m->mb_initialized) {
336          pthread_cond_wait(&m->mb_ready_cond, &m->mb_write_lock);
337
338          /* releasing the mutex reduces contention */
339          pthread_mutex_unlock(&m->mb_write_lock);
340          m->mb_flush();
341          pthread_mutex_lock(&m->mb_write_lock);
342     }
343
344      pthread_mutex_unlock(&m->mb_write_lock);
345
346     return NULL;
347 }
348
349 void
350 DebugModuleManager::print(const char *fmt, ...)
351 {
352     char msg[MB_BUFFERSIZE];
353     va_list ap;
354
355 #ifdef DO_MESSAGE_BUFFER_PRINT
356     unsigned int ntries=5;
357     struct timespec wait = {0,50000};
358 #endif
359
360     /* format the message first, to reduce lock contention */
361     va_start(ap, fmt);
362     if (vsnprintf(msg, MB_BUFFERSIZE, fmt, ap) >= MB_BUFFERSIZE) {
363         print("WARNING: message truncated!\n");
364     }
365     va_end(ap);
366
367     if (!mb_initialized) {
368         /* Unable to print message with realtime safety.
369          * Complain and print it anyway. */
370         fprintf(stderr, "ERROR: messagebuffer not initialized: %s",
371             msg);
372         return;
373     }
374    
375 #ifdef DO_MESSAGE_BUFFER_PRINT
376     while (ntries) { // try a few times
377         if (pthread_mutex_trylock(&mb_write_lock) == 0) {
378             strncpy(mb_buffers[mb_inbuffer], msg, MB_BUFFERSIZE);
379             mb_inbuffer = MB_NEXT(mb_inbuffer);
380             pthread_cond_signal(&mb_ready_cond);
381             pthread_mutex_unlock(&mb_write_lock);
382             break;
383         } else {
384             nanosleep(&wait, NULL);
385             ntries--;
386         }
387     }
388     if (ntries==0) {  /* lock collision */
389         //         atomic_add(&mb_overruns, 1);
390         // FIXME: atomicity
391         mb_overruns++; // skip the atomicness for now
392     }
393 #else
394     fprintf(stderr,msg);
395 #endif
396 }
397
398 void
399 DebugModuleManager::va_print (const char *fmt, va_list ap)
400 {
401     char msg[MB_BUFFERSIZE];
402
403 #ifdef DO_MESSAGE_BUFFER_PRINT
404     unsigned int ntries=5;
405     struct timespec wait = {0,50000};
406 #endif
407
408     /* format the message first, to reduce lock contention */
409     if (vsnprintf(msg, MB_BUFFERSIZE, fmt, ap) >= MB_BUFFERSIZE) {
410         print("WARNING: message truncated!\n");
411     }
412    
413     if (!mb_initialized) {
414         /* Unable to print message with realtime safety.
415          * Complain and print it anyway. */
416         fprintf(stderr, "ERROR: messagebuffer not initialized: %s",
417             msg);
418         return;
419     }
420    
421 #ifdef DO_MESSAGE_BUFFER_PRINT
422     while (ntries) { // try a few times
423         if (pthread_mutex_trylock(&mb_write_lock) == 0) {
424             strncpy(mb_buffers[mb_inbuffer], msg, MB_BUFFERSIZE);
425             mb_inbuffer = MB_NEXT(mb_inbuffer);
426             pthread_cond_signal(&mb_ready_cond);
427             pthread_mutex_unlock(&mb_write_lock);
428             break;
429         } else {
430             nanosleep(&wait, NULL);
431             ntries--;
432         }
433     }
434    
435     if (ntries==0) {  /* lock collision */
436 //         atomic_add(&mb_overruns, 1);
437         // FIXME: atomicity
438         mb_overruns++; // skip the atomicness for now
439     }
440 #else
441     fprintf(stderr,msg);
442 #endif
443 }
444
445 //----------------------------------------
446
447 unsigned char
448 toAscii( unsigned char c )
449 {
450     if ( ( c > 31 ) && ( c < 126) ) {
451         return c;
452     } else {
453         return '.';
454     }
455 }
456
457 /* converts a quadlet to a uchar * buffer
458  * not implemented optimally, but clear
459  */
460 void
461 quadlet2char( quadlet_t quadlet, unsigned char* buff )
462 {
463     *(buff)   = (quadlet>>24)&0xFF;
464     *(buff+1) = (quadlet>>16)&0xFF;
465     *(buff+2) = (quadlet>> 8)&0xFF;
466     *(buff+3) = (quadlet)    &0xFF;
467 }
468
469 void
470 hexDump( unsigned char *data_start, unsigned int length )
471 {
472     unsigned int i=0;
473     unsigned int byte_pos;
474     unsigned int bytes_left;
475
476     if ( length <= 0 ) {
477         return;
478     }
479     if ( length >= 7 ) {
480         for ( i = 0; i < (length-7); i += 8 ) {
481             printf( "%04X: %02X %02X %02X %02X %02X %02X %02X %02X "
482                     "- [%c%c%c%c%c%c%c%c]\n",
483
484                     i,
485
486                     *(data_start+i+0),
487                     *(data_start+i+1),
488                     *(data_start+i+2),
489                     *(data_start+i+3),
490                     *(data_start+i+4),
491                     *(data_start+i+5),
492                     *(data_start+i+6),
493                     *(data_start+i+7),
494
495                     toAscii( *(data_start+i+0) ),
496                     toAscii( *(data_start+i+1) ),
497                     toAscii( *(data_start+i+2) ),
498                     toAscii( *(data_start+i+3) ),
499                     toAscii( *(data_start+i+4) ),
500                     toAscii( *(data_start+i+5) ),
501                     toAscii( *(data_start+i+6) ),
502                     toAscii( *(data_start+i+7) )
503                 );
504         }
505     }
506     byte_pos = i;
507     bytes_left = length - byte_pos;
508
509     printf( "%04X:" ,i );
510     for ( i = byte_pos; i < length; i += 1 ) {
511         printf( " %02X", *(data_start+i) );
512     }
513     for ( i=0; i < 8-bytes_left; i+=1 ) {
514         printf( "   " );
515     }
516
517     printf( " - [" );
518     for ( i = byte_pos; i < length; i += 1) {
519         printf( "%c", toAscii(*(data_start+i)));
520     }
521     for ( i = 0; i < 8-bytes_left; i += 1) {
522         printf( " " );
523     }
524
525     printf( "]" );
526     printf( "\n" );
527 }
528
529 void
530 hexDumpQuadlets( quadlet_t *data, unsigned int length )
531 {
532     unsigned int i=0;
533
534     if ( length <= 0 ) {
535         return;
536     }
537     for (i = 0; i< length; i += 1) {
538         printf( "%02d %04X: %08X (%08X)"
539                 "\n", i, i*4, data[i],ntohl(data[i]));
540     }
541 }
542
543
Note: See TracBrowser for help on using the browser.