fixed misc/stats.c:399: warning: comparison between signed and unsigned in function...
[vlc.git] / src / misc / stats.c
1 /*****************************************************************************
2  * stats.c: Statistics handling
3  *****************************************************************************
4  * Copyright (C) 2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: ClĂ©ment Stenac <zorglub@videolan.org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program 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
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #include <vlc/vlc.h>
33 #include <stdio.h>                                               /* required */
34
35 #include "input/input_internal.h"
36
37 /*****************************************************************************
38  * Local prototypes
39  *****************************************************************************/
40 static int CounterUpdate( vlc_object_t *p_this,
41                           counter_t *p_counter,
42                           vlc_value_t val, vlc_value_t * );
43 static void TimerDump( vlc_object_t *p_this, counter_t *p_counter, bool);
44
45 /*****************************************************************************
46  * Exported functions
47  *****************************************************************************/
48
49 /**
50  * Create a statistics counter
51  * \param p_this a VLC object
52  * \param i_type the type of stored data. One of VLC_VAR_STRING,
53  * VLC_VAR_INTEGER, VLC_VAR_FLOAT
54  * \param i_compute_type the aggregation type. One of STATS_LAST (always
55  * keep the last value), STATS_COUNTER (increment by the passed value),
56  * STATS_MAX (keep the maximum passed value), STATS_MIN, or STATS_DERIVATIVE
57  * (keep a time derivative of the value)
58  */
59 counter_t * __stats_CounterCreate( vlc_object_t *p_this,
60                                    int i_type, int i_compute_type )
61 {
62     counter_t *p_counter = (counter_t*) malloc( sizeof( counter_t ) ) ;
63     (void)p_this;
64
65     if( !p_counter ) return NULL;
66     p_counter->i_compute_type = i_compute_type;
67     p_counter->i_type = i_type;
68     p_counter->i_samples = 0;
69     p_counter->pp_samples = NULL;
70     p_counter->psz_name = NULL;
71
72     p_counter->update_interval = 0;
73     p_counter->last_update = 0;
74
75     return p_counter;
76 }
77
78 /** Update a counter element with new values
79  * \param p_this a VLC object
80  * \param p_counter the counter to update
81  * \param val the vlc_value union containing the new value to aggregate. For
82  * more information on how data is aggregated, \see __stats_Create
83  * \param val_new a pointer that will be filled with new data
84  */
85 int __stats_Update( vlc_object_t *p_this, counter_t *p_counter,
86                     vlc_value_t val, vlc_value_t *val_new )
87 {
88     if( !p_this->p_libvlc->b_stats || !p_counter ) return VLC_EGENERIC;
89     return CounterUpdate( p_this, p_counter, val, val_new );
90 }
91
92 /** Get the aggregated value for a counter
93  * \param p_this an object
94  * \param p_counter the counter
95  * \param val a pointer to an initialized vlc_value union. It will contain the
96  * retrieved value
97  * \return an error code
98  */
99 int __stats_Get( vlc_object_t *p_this, counter_t *p_counter, vlc_value_t *val )
100 {
101     if( !p_this->p_libvlc->b_stats || !p_counter || p_counter->i_samples == 0 )
102     {
103         val->i_int = val->f_float = 0.0;
104         return VLC_EGENERIC;
105     }
106
107     switch( p_counter->i_compute_type )
108     {
109     case STATS_LAST:
110     case STATS_MIN:
111     case STATS_MAX:
112     case STATS_COUNTER:
113         *val = p_counter->pp_samples[0]->value;
114         break;
115     case STATS_DERIVATIVE:
116         /* Not ready yet */
117         if( p_counter->i_samples < 2 )
118         {
119             val->i_int = 0; val->f_float = 0.0;
120             return VLC_EGENERIC;
121         }
122         if( p_counter->i_type == VLC_VAR_INTEGER )
123         {
124             float f = ( p_counter->pp_samples[0]->value.i_int -
125                         p_counter->pp_samples[1]->value.i_int ) /
126                     (float)(  p_counter->pp_samples[0]->date -
127                               p_counter->pp_samples[1]->date );
128             val->i_int = (int)f;
129         }
130         else
131         {
132             float f = (float)( p_counter->pp_samples[0]->value.f_float -
133                                p_counter->pp_samples[1]->value.f_float ) /
134                       (float)( p_counter->pp_samples[0]->date -
135                                p_counter->pp_samples[1]->date );
136             val->f_float = f;
137         }
138         break;
139     }
140     return VLC_SUCCESS;;
141 }
142
143 input_stats_t *stats_NewInputStats( input_thread_t *p_input )
144 {
145     (void)p_input;
146     input_stats_t *p_stats = malloc( sizeof(input_stats_t) );
147
148     if( !p_stats )
149         return NULL;
150
151     memset( p_stats, 0, sizeof(*p_stats) );
152     vlc_mutex_init( p_input, &p_stats->lock );
153     stats_ReinitInputStats( p_stats );
154
155     return p_stats;
156 }
157
158 void stats_ComputeInputStats( input_thread_t *p_input, input_stats_t *p_stats )
159 {
160     if( !p_input->p_libvlc->b_stats ) return;
161
162     vlc_mutex_lock( &p_input->p->counters.counters_lock );
163     vlc_mutex_lock( &p_stats->lock );
164
165     /* Input */
166     stats_GetInteger( p_input, p_input->p->counters.p_read_packets,
167                       &p_stats->i_read_packets );
168     stats_GetInteger( p_input, p_input->p->counters.p_read_bytes,
169                       &p_stats->i_read_bytes );
170     stats_GetFloat( p_input, p_input->p->counters.p_input_bitrate,
171                     &p_stats->f_input_bitrate );
172     stats_GetInteger( p_input, p_input->p->counters.p_demux_read,
173                       &p_stats->i_demux_read_bytes );
174     stats_GetFloat( p_input, p_input->p->counters.p_demux_bitrate,
175                     &p_stats->f_demux_bitrate );
176
177     /* Decoders */
178     stats_GetInteger( p_input, p_input->p->counters.p_decoded_video,
179                       &p_stats->i_decoded_video );
180     stats_GetInteger( p_input, p_input->p->counters.p_decoded_audio,
181                       &p_stats->i_decoded_audio );
182
183     /* Sout */
184     if( p_input->p->counters.p_sout_send_bitrate )
185     {
186         stats_GetInteger( p_input, p_input->p->counters.p_sout_sent_packets,
187                           &p_stats->i_sent_packets );
188         stats_GetInteger( p_input, p_input->p->counters.p_sout_sent_bytes,
189                           &p_stats->i_sent_bytes );
190         stats_GetFloat  ( p_input, p_input->p->counters.p_sout_send_bitrate,
191                           &p_stats->f_send_bitrate );
192     }
193
194     /* Aout */
195     stats_GetInteger( p_input, p_input->p->counters.p_played_abuffers,
196                       &p_stats->i_played_abuffers );
197     stats_GetInteger( p_input, p_input->p->counters.p_lost_abuffers,
198                       &p_stats->i_lost_abuffers );
199
200     /* Vouts */
201     stats_GetInteger( p_input, p_input->p->counters.p_displayed_pictures,
202                       &p_stats->i_displayed_pictures );
203     stats_GetInteger( p_input, p_input->p->counters.p_lost_pictures,
204                       &p_stats->i_lost_pictures );
205
206     vlc_mutex_unlock( &p_stats->lock );
207     vlc_mutex_unlock( &p_input->p->counters.counters_lock );
208 }
209
210 void stats_ReinitInputStats( input_stats_t *p_stats )
211 {
212     vlc_mutex_lock( &p_stats->lock );
213     p_stats->i_read_packets = p_stats->i_read_bytes =
214     p_stats->f_input_bitrate = p_stats->f_average_input_bitrate =
215     p_stats->i_demux_read_packets = p_stats->i_demux_read_bytes =
216     p_stats->f_demux_bitrate = p_stats->f_average_demux_bitrate =
217     p_stats->i_displayed_pictures = p_stats->i_lost_pictures =
218     p_stats->i_played_abuffers = p_stats->i_lost_abuffers =
219     p_stats->i_decoded_video = p_stats->i_decoded_audio =
220     p_stats->i_sent_bytes = p_stats->i_sent_packets = p_stats->f_send_bitrate
221      = 0;
222     vlc_mutex_unlock( &p_stats->lock );
223 }
224
225 void stats_DumpInputStats( input_stats_t *p_stats  )
226 {
227     vlc_mutex_lock( &p_stats->lock );
228     /* f_bitrate is in bytes / microsecond
229      * *1000 => bytes / millisecond => kbytes / seconds */
230     fprintf( stderr, "Input : %i (%i bytes) - %f kB/s - "
231                      "Demux : %i (%i bytes) - %f kB/s\n"
232                      " - Vout : %i/%i - Aout : %i/%i - Sout : %f\n",
233                     p_stats->i_read_packets, p_stats->i_read_bytes,
234                     p_stats->f_input_bitrate * 1000,
235                     p_stats->i_demux_read_packets, p_stats->i_demux_read_bytes,
236                     p_stats->f_demux_bitrate * 1000,
237                     p_stats->i_displayed_pictures, p_stats->i_lost_pictures,
238                     p_stats->i_played_abuffers, p_stats->i_lost_abuffers,
239                     p_stats->f_send_bitrate );
240     vlc_mutex_unlock( &p_stats->lock );
241 }
242
243 void __stats_ComputeGlobalStats( vlc_object_t *p_obj, global_stats_t *p_stats )
244 {
245     vlc_list_t *p_list;
246     int i_index;
247
248     if( !p_obj->p_libvlc->b_stats ) return;
249
250     vlc_mutex_lock( &p_stats->lock );
251
252     p_list = vlc_list_find( p_obj, VLC_OBJECT_INPUT, FIND_ANYWHERE );
253     if( p_list )
254     {
255         float f_total_in = 0, f_total_out = 0,f_total_demux = 0;
256         for( i_index = 0; i_index < p_list->i_count ; i_index ++ )
257         {
258             float f_in = 0, f_out = 0, f_demux = 0;
259             input_thread_t *p_input = (input_thread_t *)
260                              p_list->p_values[i_index].p_object;
261             vlc_mutex_lock( &p_input->p->counters.counters_lock );
262             stats_GetFloat( p_obj, p_input->p->counters.p_input_bitrate, &f_in );
263             if( p_input->p->counters.p_sout_send_bitrate )
264                 stats_GetFloat( p_obj, p_input->p->counters.p_sout_send_bitrate,
265                                     &f_out );
266             stats_GetFloat( p_obj, p_input->p->counters.p_demux_bitrate,
267                                 &f_demux );
268             vlc_mutex_unlock( &p_input->p->counters.counters_lock );
269             f_total_in += f_in; f_total_out += f_out;f_total_demux += f_demux;
270         }
271         p_stats->f_input_bitrate = f_total_in;
272         p_stats->f_output_bitrate = f_total_out;
273         p_stats->f_demux_bitrate = f_total_demux;
274         vlc_list_release( p_list );
275     }
276
277     vlc_mutex_unlock( &p_stats->lock );
278 }
279
280 void __stats_TimerStart( vlc_object_t *p_obj, const char *psz_name,
281                          unsigned int i_id )
282 {
283     int i;
284     counter_t *p_counter = NULL;
285     if( !p_obj->p_libvlc->b_stats ) return;
286     vlc_mutex_lock( &p_obj->p_libvlc->timer_lock );
287
288     for( i = 0 ; i < p_obj->p_libvlc->i_timers; i++ )
289     {
290         if( p_obj->p_libvlc->pp_timers[i]->i_id == i_id
291             && p_obj->p_libvlc->pp_timers[i]->p_obj == p_obj )
292         {
293             p_counter = p_obj->p_libvlc->pp_timers[i];
294             break;
295         }
296     }
297     if( !p_counter )
298     {
299         counter_sample_t *p_sample;
300         p_counter = stats_CounterCreate( p_obj->p_libvlc, VLC_VAR_TIME,
301                                          STATS_TIMER );
302         if( !p_counter )
303         {
304             vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
305             return;
306         }
307         p_counter->psz_name = strdup( psz_name );
308         p_counter->i_id = i_id;
309         p_counter->p_obj = p_obj;
310         INSERT_ELEM( p_obj->p_libvlc->pp_timers, p_obj->p_libvlc->i_timers,
311                      p_obj->p_libvlc->i_timers, p_counter );
312
313         /* 1st sample : if started: start_date, else last_time, b_started */
314         p_sample = (counter_sample_t *)malloc( sizeof( counter_sample_t ) );
315         INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
316                      p_counter->i_samples, p_sample );
317         p_sample->date = 0; p_sample->value.b_bool = 0;
318         /* 2nd sample : global_time, i_samples */
319         p_sample = (counter_sample_t *)malloc( sizeof( counter_sample_t ) );
320         INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
321                      p_counter->i_samples, p_sample );
322         p_sample->date = 0; p_sample->value.i_int = 0;
323     }
324     if( p_counter->pp_samples[0]->value.b_bool == true )
325     {
326         msg_Warn( p_obj, "timer '%s' was already started !", psz_name );
327         vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
328         return;
329     }
330     p_counter->pp_samples[0]->value.b_bool = true;
331     p_counter->pp_samples[0]->date = mdate();
332     vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
333 }
334
335 void __stats_TimerStop( vlc_object_t *p_obj, unsigned int i_id )
336 {
337     counter_t *p_counter = NULL;
338     int i;
339     if( !p_obj->p_libvlc->b_stats ) return;
340     vlc_mutex_lock( &p_obj->p_libvlc->timer_lock );
341     for( i = 0 ; i < p_obj->p_libvlc->i_timers; i++ )
342     {
343         if( p_obj->p_libvlc->pp_timers[i]->i_id == i_id
344             && p_obj->p_libvlc->pp_timers[i]->p_obj == p_obj )
345         {
346             p_counter = p_obj->p_libvlc->pp_timers[i];
347             break;
348         }
349     }
350     if( !p_counter || p_counter->i_samples != 2 )
351     {
352         msg_Err( p_obj, "timer does not exist" );
353         vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
354         return;
355     }
356     p_counter->pp_samples[0]->value.b_bool = false;
357     p_counter->pp_samples[1]->value.i_int += 1;
358     p_counter->pp_samples[0]->date = mdate() - p_counter->pp_samples[0]->date;
359     p_counter->pp_samples[1]->date += p_counter->pp_samples[0]->date;
360     vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
361 }
362
363 void __stats_TimerDump( vlc_object_t *p_obj, unsigned int i_id )
364 {
365     counter_t *p_counter = NULL;
366     int i;
367     if( !p_obj->p_libvlc->b_stats ) return;
368     vlc_mutex_lock( &p_obj->p_libvlc->timer_lock );
369     for( i = 0 ; i < p_obj->p_libvlc->i_timers; i++ )
370     {
371         if( p_obj->p_libvlc->pp_timers[i]->i_id == i_id
372             && p_obj->p_libvlc->pp_timers[i]->p_obj == p_obj )
373         {
374             p_counter = p_obj->p_libvlc->pp_timers[i];
375             break;
376         }
377     }
378     TimerDump( p_obj, p_counter, true );
379     vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
380 }
381
382 void __stats_TimersDumpAll( vlc_object_t *p_obj )
383 {
384     int i;
385     if( !p_obj->p_libvlc->b_stats ) return;
386     vlc_mutex_lock( &p_obj->p_libvlc->timer_lock );
387     for ( i = 0 ; i< p_obj->p_libvlc->i_timers ; i++ )
388         TimerDump( p_obj, p_obj->p_libvlc->pp_timers[i], false );
389     vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
390 }
391
392 void __stats_TimerClean( vlc_object_t *p_obj, unsigned int i_id )
393 {
394     int i;
395     vlc_mutex_lock( &p_obj->p_libvlc->timer_lock );
396     for ( i = p_obj->p_libvlc->i_timers -1 ; i >= 0; i-- )
397     {
398         counter_t *p_counter = p_obj->p_libvlc->pp_timers[i];
399         if( p_counter->i_id == i_id && p_counter->p_obj == p_obj )
400         {
401             REMOVE_ELEM( p_obj->p_libvlc->pp_timers,
402                          p_obj->p_libvlc->i_timers, i );
403             stats_CounterClean( p_counter );
404         }
405     }
406     vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
407 }
408
409 void __stats_TimersCleanAll( vlc_object_t *p_obj )
410 {
411     int i;
412     vlc_mutex_lock( &p_obj->p_libvlc->timer_lock );
413     for ( i = p_obj->p_libvlc->i_timers -1 ; i >= 0; i-- )
414     {
415         counter_t *p_counter = p_obj->p_libvlc->pp_timers[i];
416         REMOVE_ELEM( p_obj->p_libvlc->pp_timers, p_obj->p_libvlc->i_timers, i );
417         stats_CounterClean( p_counter );
418     }
419     vlc_mutex_unlock( &p_obj->p_libvlc->timer_lock );
420 }
421
422 void stats_CounterClean( counter_t *p_c )
423 {
424     int i;
425     if( p_c )
426     {
427         i = p_c->i_samples - 1 ;
428         while( i >= 0 )
429         {
430             counter_sample_t *p_s = p_c->pp_samples[i];
431             REMOVE_ELEM( p_c->pp_samples, p_c->i_samples, i );
432             free( p_s );
433             i--;
434         }
435         free( p_c->psz_name );
436         free( p_c );
437     }
438 }
439
440
441 /********************************************************************
442  * Following functions are local
443  ********************************************************************/
444
445 /**
446  * Update a statistics counter, according to its type
447  * If needed, perform a bit of computation (derivative, mostly)
448  * This function must be entered with stats handler lock
449  * \param p_counter the counter to update
450  * \param val the "new" value
451  * \return an error code
452  */
453 static int CounterUpdate( vlc_object_t *p_handler,
454                           counter_t *p_counter,
455                           vlc_value_t val, vlc_value_t *new_val )
456 {
457     switch( p_counter->i_compute_type )
458     {
459     case STATS_LAST:
460     case STATS_MIN:
461     case STATS_MAX:
462         if( p_counter->i_samples > 1)
463         {
464             msg_Err( p_handler, "LAST counter has several samples !" );
465             return VLC_EGENERIC;
466         }
467         if( p_counter->i_type != VLC_VAR_FLOAT &&
468             p_counter->i_type != VLC_VAR_INTEGER &&
469             p_counter->i_compute_type != STATS_LAST )
470         {
471             msg_Err( p_handler, "unable to compute MIN or MAX for this type");
472             return VLC_EGENERIC;
473         }
474
475         if( p_counter->i_samples == 0 )
476         {
477             counter_sample_t *p_new = (counter_sample_t*)malloc(
478                                                sizeof( counter_sample_t ) );
479             p_new->value.psz_string = NULL;
480
481             INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
482                          p_counter->i_samples, p_new );
483         }
484         if( p_counter->i_samples == 1 )
485         {
486             /* Update if : LAST or (MAX and bigger) or (MIN and bigger) */
487             if( p_counter->i_compute_type == STATS_LAST ||
488                 ( p_counter->i_compute_type == STATS_MAX &&
489                    ( ( p_counter->i_type == VLC_VAR_INTEGER &&
490                        p_counter->pp_samples[0]->value.i_int > val.i_int ) ||
491                      ( p_counter->i_type == VLC_VAR_FLOAT &&
492                        p_counter->pp_samples[0]->value.f_float > val.f_float )
493                    ) ) ||
494                 ( p_counter->i_compute_type == STATS_MIN &&
495                    ( ( p_counter->i_type == VLC_VAR_INTEGER &&
496                        p_counter->pp_samples[0]->value.i_int < val.i_int ) ||
497                      ( p_counter->i_type == VLC_VAR_FLOAT &&
498                        p_counter->pp_samples[0]->value.f_float < val.f_float )
499                    ) ) )
500             {
501                 if( p_counter->i_type == VLC_VAR_STRING &&
502                     p_counter->pp_samples[0]->value.psz_string )
503                 {
504                     free( p_counter->pp_samples[0]->value.psz_string );
505                 }
506                 p_counter->pp_samples[0]->value = val;
507                 *new_val = p_counter->pp_samples[0]->value;
508             }
509         }
510         break;
511     case STATS_DERIVATIVE:
512     {
513         counter_sample_t *p_new, *p_old;
514         if( mdate() - p_counter->last_update < p_counter->update_interval )
515         {
516             return VLC_EGENERIC;
517         }
518         p_counter->last_update = mdate();
519         if( p_counter->i_type != VLC_VAR_FLOAT &&
520             p_counter->i_type != VLC_VAR_INTEGER )
521         {
522             msg_Err( p_handler, "Unable to compute DERIVATIVE for this type");
523             return VLC_EGENERIC;
524         }
525         /* Insert the new one at the beginning */
526         p_new = (counter_sample_t*)malloc( sizeof( counter_sample_t ) );
527         p_new->value = val;
528         p_new->date = p_counter->last_update;
529         INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
530                      0, p_new );
531
532         if( p_counter->i_samples == 3 )
533         {
534             p_old = p_counter->pp_samples[2];
535             REMOVE_ELEM( p_counter->pp_samples, p_counter->i_samples, 2 );
536             free( p_old );
537         }
538         break;
539     }
540     case STATS_COUNTER:
541         if( p_counter->i_samples > 1)
542         {
543             msg_Err( p_handler, "LAST counter has several samples !" );
544             return VLC_EGENERIC;
545         }
546         if( p_counter->i_samples == 0 )
547         {
548             counter_sample_t *p_new = (counter_sample_t*)malloc(
549                                                sizeof( counter_sample_t ) );
550             p_new->value.psz_string = NULL;
551
552             INSERT_ELEM( p_counter->pp_samples, p_counter->i_samples,
553                          p_counter->i_samples, p_new );
554         }
555         if( p_counter->i_samples == 1 )
556         {
557             switch( p_counter->i_type )
558             {
559             case VLC_VAR_INTEGER:
560                 p_counter->pp_samples[0]->value.i_int += val.i_int;
561                 if( new_val )
562                     new_val->i_int = p_counter->pp_samples[0]->value.i_int;
563                 break;
564             case VLC_VAR_FLOAT:
565                 p_counter->pp_samples[0]->value.f_float += val.f_float;
566                 if( new_val )
567                     new_val->f_float = p_counter->pp_samples[0]->value.f_float;
568             default:
569                 msg_Err( p_handler, "Trying to increment invalid variable %s",
570                          p_counter->psz_name );
571                 return VLC_EGENERIC;
572             }
573         }
574         break;
575     }
576     return VLC_SUCCESS;
577 }
578
579 static void TimerDump( vlc_object_t *p_obj, counter_t *p_counter,
580                        bool b_total )
581 {
582     mtime_t last, total;
583     int i_total;
584     if( !p_counter || p_counter->i_samples != 2 )
585     {
586         msg_Err( p_obj, "timer %s does not exist", p_counter->psz_name );
587         return;
588     }
589     i_total = p_counter->pp_samples[1]->value.i_int;
590     total = p_counter->pp_samples[1]->date;
591     if( p_counter->pp_samples[0]->value.b_bool == true )
592     {
593         last = mdate() - p_counter->pp_samples[0]->date;
594         i_total += 1;
595         total += last;
596     }
597     else
598     {
599         last = p_counter->pp_samples[0]->date;
600     }
601     if( b_total )
602     {
603         msg_Dbg( p_obj,
604              "TIMER %s : %.3f ms - Total %.3f ms / %i intvls (Avg %.3f ms)",
605              p_counter->psz_name, (float)last/1000, (float)total/1000, i_total,
606              (float)(total)/(1000*(float)i_total ) );
607     }
608     else
609     {
610         msg_Dbg( p_obj,
611              "TIMER %s : Total %.3f ms / %i intvls (Avg %.3f ms)",
612              p_counter->psz_name, (float)total/1000, i_total,
613              (float)(total)/(1000*(float)i_total ) );
614     }
615 }