]> git.sesse.net Git - vlc/blob - src/interface/intf_msg.c
Now using buffer I/O to write debug logs (huge performance increase).
[vlc] / src / interface / intf_msg.c
1 /*****************************************************************************
2  * intf_msg.c: messages interface
3  * This library provides basic functions for threads to interact with user
4  * interface, such as message output. See config.h for output configuration.
5  *****************************************************************************
6  * Copyright (C) 1998, 1999, 2000 VideoLAN
7  *
8  * Authors:
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  * 
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include "defs.h"
29
30 #include <errno.h>                                                  /* errno */
31 #include <fcntl.h>                     /* O_CREAT, O_TRUNC, O_WRONLY, O_SYNC */
32 #include <stdio.h>                                               /* required */
33 #include <stdarg.h>                                       /* va_list for BSD */
34 #include <stdlib.h>                                              /* malloc() */
35 #include <string.h>                                            /* strerror() */
36 #include <unistd.h>                                      /* close(), write() */
37
38 #include "config.h"
39 #include "common.h"
40 #include "threads.h"
41 #include "mtime.h"
42 #include "plugins.h"
43 #include "intf_msg.h"
44 #include "interface.h"
45 #include "intf_console.h"
46
47 #include "main.h"
48
49 /*****************************************************************************
50  * intf_msg_item_t
51  *****************************************************************************
52  * Store a single message. Messages have a maximal size of INTF_MSG_MSGSIZE.
53  * If DEBUG is defined, messages have a date field and debug messages are
54  * printed with a date to allow more precise profiling.
55  *****************************************************************************/
56 typedef struct
57 {
58     int     i_type;                               /* message type, see below */
59     char *  psz_msg;                                   /* the message itself */
60
61 #ifdef DEBUG
62     /* Debugging informations - in DEBUG mode, debug messages have calling
63      * location informations printed */
64     mtime_t date;                                     /* date of the message */
65     char *  psz_file;               /* file in which the function was called */
66     char *  psz_function;     /* function from which the function was called */
67     int     i_line;                 /* line at which the function was called */
68 #endif
69 } intf_msg_item_t;
70
71 /* Message types */
72 #define INTF_MSG_STD    0                                /* standard message */
73 #define INTF_MSG_ERR    1                                   /* error message */
74 #define INTF_MSG_INTF   2                               /* interface message */
75 #define INTF_MSG_DBG    3                                   /* debug message */
76 #define INTF_MSG_WARN   4                                  /* warning message*/
77
78
79 /*****************************************************************************
80  * intf_msg_t
81  *****************************************************************************
82  * Store all data requiered by messages interfaces. It has a single reference
83  * int p_main.
84  *****************************************************************************/
85 typedef struct intf_msg_s
86 {
87 #ifdef INTF_MSG_QUEUE
88     /* Message queue */
89     vlc_mutex_t             lock;                      /* message queue lock */
90     int                     i_count;            /* number of messages stored */
91     intf_msg_item_t         msg[INTF_MSG_QSIZE];            /* message queue */
92 #endif
93
94 #ifdef DEBUG_LOG
95     /* Log file */
96     FILE *                  p_log_file;                          /* log file */
97 #endif
98
99 #if !defined(INTF_MSG_QUEUE) && !defined(DEBUG_LOG)
100     /* If neither messages queue, neither log file is used, then the structure
101      * is empty. However, empty structures are not allowed in C. Therefore, a
102      * dummy integer is used to fill it. */
103     int                     i_dummy;                        /* unused filler */
104 #endif
105 //    int                     i_warning_level;
106 } intf_msg_t;
107
108 /*****************************************************************************
109  * Local prototypes
110  *****************************************************************************/
111
112 static void QueueMsg        ( intf_msg_t *p_msg, int i_type,
113                               char *psz_format, va_list ap );
114 static void PrintMsg        ( intf_msg_item_t *p_msg );
115 #ifdef DEBUG
116 static void QueueDbgMsg     ( intf_msg_t *p_msg, char *psz_file,
117                               char *psz_function, int i_line,
118                               char *psz_format, va_list ap );
119 #endif
120 #ifdef INTF_MSG_QUEUE
121 static void FlushLockedMsg  ( intf_msg_t *p_msg );
122 #endif
123
124
125 /*****************************************************************************
126  * intf_MsgCreate: initialize messages interface                         (ok ?)
127  *****************************************************************************
128  * This functions has to be called before any call to other intf_*Msg functions.
129  * It set up the locks and the message queue if it is used.
130  *****************************************************************************/
131 p_intf_msg_t intf_MsgCreate( void )
132 {
133     p_intf_msg_t p_msg;
134
135     /* Allocate structure */
136     p_msg = malloc( sizeof( intf_msg_t ) );
137     if( p_msg == NULL )
138     {
139         errno = ENOMEM;
140     }
141     else
142     {
143 #ifdef INTF_MSG_QUEUE
144     /* Message queue initialization */
145     vlc_mutex_init( &p_msg->lock );                        /* intialize lock */
146     p_msg->i_count = 0;                                    /* queue is empty */
147 #endif
148
149     
150 #ifdef DEBUG_LOG
151         /* Log file initialization - on failure, file pointer will be null,
152          * and no log will be issued, but this is not considered as an
153          * error */
154         p_msg->p_log_file = fopen( DEBUG_LOG, "w" );
155 #endif
156     }
157     return( p_msg );
158 }
159
160 /*****************************************************************************
161  * intf_MsgDestroy: free resources allocated by intf_InitMsg            (ok ?)
162  *****************************************************************************
163  * This functions prints all messages remaining in queue, then free all the
164  * resources allocated by intf_InitMsg.
165  * No other messages interface functions should be called after this one.
166  *****************************************************************************/
167 void intf_MsgDestroy( void )
168 {
169     intf_FlushMsg();                         /* print all remaining messages */
170
171 #ifdef DEBUG_LOG
172     /* Close log file if any */
173     if( p_main->p_msg->p_log_file != NULL )
174     {
175         fclose( p_main->p_msg->p_log_file );
176     }
177 #endif
178
179     /* Free structure */
180     free( p_main->p_msg );
181 }
182
183 /*****************************************************************************
184  * intf_Msg: print a message                                             (ok ?)
185  *****************************************************************************
186  * This function queue a message for later printing, or print it immediately
187  * if the queue isn't used.
188  *****************************************************************************/
189 void intf_Msg( char *psz_format, ... )
190 {
191     va_list ap;
192
193     va_start( ap, psz_format );
194     QueueMsg( p_main->p_msg, INTF_MSG_STD, psz_format, ap );
195     va_end( ap );
196 }
197
198 /*****************************************************************************
199  * intf_ErrMsg : print an error message                                  (ok ?)
200  *****************************************************************************
201  * This function is the same as intf_Msg, except that it prints its messages
202  * on stderr.
203  *****************************************************************************/
204 void intf_ErrMsg( char *psz_format, ... )
205 {
206     va_list ap;
207
208     va_start( ap, psz_format );
209     QueueMsg( p_main->p_msg, INTF_MSG_ERR, psz_format, ap );
210     va_end( ap );
211 }
212
213 /*****************************************************************************
214  * intf_WarnMsg : print a warning message
215  *****************************************************************************
216  * This function is the same as intf_Msg, except that it concerns warning
217  * messages for testing purpose.
218  *****************************************************************************/
219 void intf_WarnMsg( int i_level, char *psz_format, ... )
220 {
221     va_list ap;
222     
223     if( i_level >= p_main->p_intf->i_warning_level )
224     {
225         va_start( ap, psz_format );
226         QueueMsg( p_main->p_msg, INTF_MSG_WARN, psz_format, ap );
227         va_end( ap );
228     }
229 }
230
231
232 /*****************************************************************************
233  * intf_IntfMsg : print an interface message                             (ok ?)
234  *****************************************************************************
235  * In opposition to all other intf_*Msg function, this function does not print
236  * it's message on default terminal (stdout or stderr), but send it to
237  * interface (in fact to the X11 console). This means that the interface MUST
238  * be initialized and a X11 console openned before this function is used, and
239  * that once the console is closed, this call is vorbidden.
240  * Practically, only the interface thread itself should call this function, and
241  * flush all messages before intf_CloseX11Console() is called.
242  *****************************************************************************/
243 void intf_IntfMsg(char *psz_format, ...)
244 {
245     va_list ap;
246
247     va_start( ap, psz_format );
248     QueueMsg( p_main->p_msg, INTF_MSG_INTF, psz_format, ap );
249     va_end( ap );
250 }
251
252 /*****************************************************************************
253  * _intf_DbgMsg: print a debugging message                               (ok ?)
254  *****************************************************************************
255  * This function prints a debugging message. Compared to other intf_*Msg
256  * functions, it is only defined if DEBUG is defined and require a file name,
257  * a function name and a line number as additionnal debugging informations. It
258  * also prints a debugging header for each received line.
259  *****************************************************************************/
260 #ifdef DEBUG
261 void _intf_DbgMsg( char *psz_file, char *psz_function, int i_line,
262                    char *psz_format, ...)
263 {
264     va_list ap;
265
266     va_start( ap, psz_format );
267     QueueDbgMsg( p_main->p_msg, psz_file, psz_function, i_line,
268                  psz_format, ap );
269     va_end( ap );
270 }
271 #endif
272
273 /*****************************************************************************
274  * intf_MsgImm: print a message                                          (ok ?)
275  *****************************************************************************
276  * This function prints a message immediately. If the queue is used, all
277  * waiting messages are also printed.
278  *****************************************************************************/
279 void intf_MsgImm( char *psz_format, ... )
280 {
281     va_list ap;
282
283     va_start( ap, psz_format );
284     QueueMsg( p_main->p_msg, INTF_MSG_STD, psz_format, ap );
285     va_end( ap );
286     intf_FlushMsg();
287 }
288
289 /*****************************************************************************
290  * intf_ErrMsgImm: print an error message immediately                    (ok ?)
291  *****************************************************************************
292  * This function is the same as intf_MsgImm, except that it prints its message
293  * on stderr.
294  *****************************************************************************/
295 void intf_ErrMsgImm(char *psz_format, ...)
296 {
297     va_list ap;
298
299     va_start( ap, psz_format );
300     QueueMsg( p_main->p_msg, INTF_MSG_ERR, psz_format, ap );
301     va_end( ap );
302     intf_FlushMsg();
303 }
304
305 /*****************************************************************************
306  * intf_WarnMsgImm : print a warning message
307  *****************************************************************************
308  * This function is the same as intf_MsgImm, except that it concerns warning
309  * messages for testing purpose.
310  *****************************************************************************/
311 void intf_WarnMsgImm( int i_level, char *psz_format, ... )
312 {
313     va_list ap;
314
315     if( i_level >= p_main->p_intf->i_warning_level )
316     {
317         va_start( ap, psz_format );
318         QueueMsg( p_main->p_msg, INTF_MSG_WARN, psz_format, ap );
319         va_end( ap );
320     }
321     intf_FlushMsg();
322 }
323
324
325
326 /*****************************************************************************
327  * _intf_DbgMsgImm: print a debugging message immediately                (ok ?)
328  *****************************************************************************
329  * This function is the same as intf_DbgMsgImm, except that it prints its
330  * message immediately. It should only be called through the macro
331  * intf_DbgMsgImm().
332  *****************************************************************************/
333 #ifdef DEBUG
334 void _intf_DbgMsgImm( char *psz_file, char *psz_function, int i_line,
335                       char *psz_format, ...)
336 {
337     va_list ap;
338
339     va_start( ap, psz_format );
340     QueueDbgMsg( p_main->p_msg, psz_file, psz_function, i_line,
341                  psz_format, ap );
342     va_end( ap );
343     intf_FlushMsg();
344 }
345 #endif
346
347 /*****************************************************************************
348  * intf_FlushMsg                                                        (ok ?)
349  *****************************************************************************
350  * Print all messages remaining in queue: get lock and call FlushLockedMsg.
351  * This function does nothing if the message queue isn't used.
352  * This function is only implemented if message queue is used. If not, it is
353  * an empty macro.
354  *****************************************************************************/
355 #ifdef INTF_MSG_QUEUE
356 void intf_FlushMsg( void )
357 {
358     vlc_mutex_lock( &p_main->p_msg->lock );                      /* get lock */
359     FlushLockedMsg( p_main->p_msg );                       /* flush messages */
360     vlc_mutex_unlock( &p_main->p_msg->lock );              /* give lock back */
361 }
362 #endif
363
364 /* following functions are local */
365
366 /*****************************************************************************
367  * QueueMsg: add a message to a queue
368  *****************************************************************************
369  * This function provide basic functionnalities to other intf_*Msg functions.
370  * It add a message to a queue (after having printed all stored messages if it
371  * is full. If the message can't be converted to string in memory, it exit the
372  * program. If the queue is not used, it prints the message immediately.
373  *****************************************************************************/
374 static void QueueMsg( intf_msg_t *p_msg, int i_type, char *psz_format, va_list ap )
375 {
376     char *                  psz_str;             /* formatted message string */
377     intf_msg_item_t *       p_msg_item;                /* pointer to message */
378
379 #ifndef INTF_MSG_QUEUE /*................................... instant mode ...*/
380     intf_msg_item_t         msg_item;                             /* message */
381     p_msg_item =           &msg_item;
382 #endif /*....................................................................*/
383
384     /*
385      * Convert message to string
386      */
387 #ifdef SYS_BEOS
388     psz_str = (char*) malloc( strlen(psz_format) + INTF_MAX_MSG_SIZE );
389     vsprintf( psz_str, psz_format, ap );
390 #else
391     vasprintf( &psz_str, psz_format, ap );
392 #endif
393     if( psz_str == NULL )
394     {
395         fprintf(stderr, "warning: can't store following message (%s): ",
396                 strerror(errno) );
397         vfprintf(stderr, psz_format, ap );
398         exit( errno );
399     }
400
401 #ifdef INTF_MSG_QUEUE /*...................................... queue mode ...*/
402     vlc_mutex_lock( &p_msg->lock );                              /* get lock */
403     if( p_msg->i_count == INTF_MSG_QSIZE )          /* flush queue if needed */
404     {
405 #ifdef DEBUG               /* in debug mode, queue overflow causes a warning */
406         fprintf(stderr, "warning: message queue overflow\n" );
407 #endif
408         FlushLockedMsg( p_msg );
409     }
410     p_msg_item = p_msg->msg + p_msg->i_count++;            /* select message */
411 #endif /*.............................................. end of queue mode ...*/
412
413     /*
414      * Fill message information fields
415      */
416     p_msg_item->i_type =     i_type;
417     p_msg_item->psz_msg =    psz_str;
418
419 #ifdef INTF_MSG_QUEUE /*......................................... queue mode */
420     vlc_mutex_unlock( &p_msg->lock );                      /* give lock back */
421 #else /*....................................................... instant mode */
422     PrintMsg( p_msg_item );                                 /* print message */
423     free( psz_str );                                    /* free message data */
424 #endif /*....................................................................*/
425 }
426
427 /*****************************************************************************
428  * QueueDbgMsg: add a message to a queue with debugging informations
429  *****************************************************************************
430  * This function is the same as QueueMsg, except that it is only defined when
431  * DEBUG is define, and require additionnal debugging informations.
432  *****************************************************************************/
433 #ifdef DEBUG
434 static void QueueDbgMsg(intf_msg_t *p_msg, char *psz_file, char *psz_function,
435                         int i_line, char *psz_format, va_list ap)
436 {
437     char *                  psz_str;             /* formatted message string */
438     intf_msg_item_t *       p_msg_item;                /* pointer to message */
439
440 #ifndef INTF_MSG_QUEUE /*................................... instant mode ...*/
441     intf_msg_item_t         msg_item;                             /* message */
442     p_msg_item =           &msg_item;
443 #endif /*....................................................................*/
444
445     /*
446      * Convert message to string
447      */
448 #ifdef SYS_BEOS
449     psz_str = (char*) malloc( INTF_MAX_MSG_SIZE );
450     vsprintf( psz_str, psz_format, ap );
451 #else
452     vasprintf( &psz_str, psz_format, ap );
453 #endif
454     if( psz_str == NULL )
455     {
456         fprintf(stderr, "warning: can't store following message (%s): ",
457                 strerror(errno) );
458         fprintf(stderr, INTF_MSG_DBG_FORMAT, psz_file, psz_function, i_line );
459         vfprintf(stderr, psz_format, ap );
460         exit( errno );
461     }
462
463 #ifdef INTF_MSG_QUEUE /*...................................... queue mode ...*/
464     vlc_mutex_lock( &p_msg->lock );                              /* get lock */
465     if( p_msg->i_count == INTF_MSG_QSIZE )          /* flush queue if needed */
466     {
467 #ifdef DEBUG               /* in debug mode, queue overflow causes a warning */
468         fprintf(stderr, "warning: message queue overflow\n" );
469 #endif
470         FlushLockedMsg( p_msg );
471     }
472     p_msg_item = p_msg->msg + p_msg->i_count++;            /* select message */
473 #endif /*.............................................. end of queue mode ...*/
474
475     /*
476      * Fill message information fields
477      */
478     p_msg_item->i_type =       INTF_MSG_DBG;
479     p_msg_item->psz_msg =      psz_str;
480     p_msg_item->psz_file =     psz_file;
481     p_msg_item->psz_function = psz_function;
482     p_msg_item->i_line =       i_line;
483     p_msg_item->date =         mdate();
484
485 #ifdef INTF_MSG_QUEUE /*......................................... queue mode */
486     vlc_mutex_unlock( &p_msg->lock );                      /* give lock back */
487 #else /*....................................................... instant mode */
488     PrintMsg( p_msg_item );                                 /* print message */
489     free( psz_str );                                    /* free message data */
490 #endif /*....................................................................*/
491 }
492 #endif
493
494 /*****************************************************************************
495  * FlushLockedMsg                                                       (ok ?)
496  *****************************************************************************
497  * Print all messages remaining in queue. MESSAGE QUEUE MUST BE LOCKED, since
498  * this function does not check the lock. This function is only defined if
499  * INTF_MSG_QUEUE is defined.
500  *****************************************************************************/
501 #ifdef INTF_MSG_QUEUE
502 static void FlushLockedMsg ( intf_msg_t *p_msg )
503 {
504     int i_index;
505
506     for( i_index = 0; i_index < p_msg->i_count; i_index++ )
507     {
508         /* Print message and free message data */
509         PrintMsg( &p_msg->msg[i_index] );
510         free( p_msg->msg[i_index].psz_msg );
511     }
512
513     p_msg->i_count = 0;
514 }
515 #endif
516
517 /*****************************************************************************
518  * PrintMsg: print a message                                             (ok ?)
519  *****************************************************************************
520  * Print a single message. The message data is not freed. This function exists
521  * in two version. The DEBUG version prints a date with each message, and is
522  * able to log messages (if DEBUG_LOG is defined).
523  * The normal one just prints messages to the screen.
524  *****************************************************************************/
525 #ifdef DEBUG
526
527 static void PrintMsg( intf_msg_item_t *p_msg )
528 {
529     char    psz_date[MSTRTIME_MAX_SIZE];            /* formatted time buffer */
530     char *  psz_msg;                                       /* message buffer */
531
532     /* Format message - the message is formatted here because in case the log
533      * file is used, it avoids another format string parsing */
534     switch( p_msg->i_type )
535     {
536     case INTF_MSG_STD:                                   /* regular messages */
537     case INTF_MSG_ERR:
538         asprintf( &psz_msg, "%s", p_msg->psz_msg );
539         break;
540
541     case INTF_MSG_WARN:                                   /* Warning message */
542         asprintf( &psz_msg, "%s", p_msg->psz_msg );
543         break;
544         
545     case INTF_MSG_INTF:                                /* interface messages */
546         asprintf( &psz_msg, "%s", p_msg->psz_msg );
547         break;
548
549     case INTF_MSG_DBG:                                     /* debug messages */
550         mstrtime( psz_date, p_msg->date );
551         asprintf( &psz_msg, "(%s) " INTF_MSG_DBG_FORMAT "%s",
552                   psz_date, p_msg->psz_file, p_msg->psz_function, p_msg->i_line,
553                   p_msg->psz_msg );
554         break;
555     }
556
557     /* Check if formatting function succeeded */
558     if( psz_msg == NULL )
559     {
560         fprintf( stderr, "error: can not format message (%s): %s\n",
561                  strerror( errno ), p_msg->psz_msg );
562         return;
563     }
564
565     /*
566      * Print messages
567      */
568     switch( p_msg->i_type )
569     {
570     case INTF_MSG_STD:                                  /* standard messages */
571         fprintf( stdout, psz_msg );
572         break;
573     case INTF_MSG_ERR:                                     /* error messages */
574     case INTF_MSG_WARN:
575 #ifndef DEBUG_LOG_ONLY
576     case INTF_MSG_DBG:                                 /* debugging messages */
577 #endif
578         fprintf( stderr, psz_msg );
579         break;
580     case INTF_MSG_INTF:                                /* interface messages */
581         intf_ConsolePrint( p_main->p_intf->p_console, psz_msg );
582         break;
583     }
584
585 #ifdef DEBUG_LOG
586     /* Append all messages to log file */
587     if( p_main->p_msg->p_log_file != NULL )
588     {
589         fwrite( psz_msg, strlen( psz_msg ), 1, p_main->p_msg->p_log_file );
590     }
591 #endif
592
593     /* Free formatted message */
594     free( psz_msg );
595 }
596
597 #else
598
599 static void PrintMsg( intf_msg_item_t *p_msg )
600 {
601     /*
602      * Print messages on screen
603      */
604     switch( p_msg->i_type )
605     {
606     case INTF_MSG_STD:                                  /* standard messages */
607     case INTF_MSG_DBG:                                     /* debug messages */
608         fprintf( stdout, p_msg->psz_msg );
609         break;
610     case INTF_MSG_ERR:                                     /* error messages */
611     case INTF_MSG_WARN:
612         fprintf( stderr, p_msg->psz_msg );                /* warning message */
613         break;
614     case INTF_MSG_INTF:                                /* interface messages */
615         intf_ConsolePrint( p_main->p_intf->p_console, p_msg->psz_msg );
616         break;
617     }
618 }
619
620 #endif