1 /*
2    (c) Copyright 2001-2009  The world wide DirectFB Open Source Community (directfb.org)
3    (c) Copyright 2000-2004  Convergence (integrated media) GmbH
4 
5    All rights reserved.
6 
7    Written by Denis Oliver Kropp <dok@directfb.org>,
8               Andreas Hundt <andi@fischlustig.de>,
9               Sven Neumann <neo@directfb.org>,
10               Ville Syrjälä <syrjala@sci.fi> and
11               Claudio Ciccani <klan@users.sf.net>.
12 
13    This file is subject to the terms and conditions of the MIT License:
14 
15    Permission is hereby granted, free of charge, to any person
16    obtaining a copy of this software and associated documentation
17    files (the "Software"), to deal in the Software without restriction,
18    including without limitation the rights to use, copy, modify, merge,
19    publish, distribute, sublicense, and/or sell copies of the Software,
20    and to permit persons to whom the Software is furnished to do so,
21    subject to the following conditions:
22 
23    The above copyright notice and this permission notice shall be
24    included in all copies or substantial portions of the Software.
25 
26    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
29    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
30    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
31    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
32    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 */
34 
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <unistd.h>
39 #include <fcntl.h>
40 #include <signal.h>
41 #include <errno.h>
42 #include <netdb.h>
43 #include <pthread.h>
44 
45 #include <sys/types.h>
46 #include <sys/resource.h>
47 #include <sys/time.h>
48 
49 #include <direct/messages.h>
50 
51 #include <fusion/call.h>
52 #include <fusion/fusion.h>
53 #include <fusion/shm/pool.h>
54 
55 #ifndef HAVE_FORK
56 # define fork() -1
57 #endif
58 
59 #define MAX_NUM_BLOCKS 10000
60 
61 #define SIZE_ALIGNMASK   0x3
62 #define ALIGN_SIZE(s)    (((s) + SIZE_ALIGNMASK) & ~SIZE_ALIGNMASK)
63 
64 /**********************************************************************************************************************/
65 
66 static int parse_cmdline  ( int    argc,
67                             char  *argv[] );
68 static int show_usage     ( void );
69 
70 /**********************************************************************************************************************/
71 
72 static inline unsigned long
get_millis(void)73 get_millis( void )
74 {
75      struct timeval tv;
76 
77      gettimeofday( &tv, NULL );
78 
79      return tv.tv_sec * 1000 + tv.tv_usec / 1000;
80 }
81 
82 /**********************************************************************************************************************/
83 
84 static unsigned int   bit_rate;
85 static bool           run_busy;
86 static bool           do_fork;
87 static bool           do_thread;
88 
89 /**********************************************************************************************************************/
90 
91 static long           block_size = 184;
92 static long           num_blocks = 16;
93 
94 /**********************************************************************************************************************/
95 
96 static int fuser, fnice, fsystem, fidle, ftotal;
97 static int cuser, cnice, csystem, cidle, ctotal;
98 static int puser, pnice, psystem, pidle, ptotal;
99 static int duser, dnice, dsystem, didle, dtotal;
100 
101 static int
read_stat(void)102 read_stat( void )
103 {
104      char  dummy[4];
105      int   wa = 0, hi = 0, si = 0;
106      FILE *file;
107 
108      puser   = cuser;
109      pnice   = cnice;
110      psystem = csystem;
111      pidle   = cidle;
112      ptotal  = ctotal;
113 
114      file = fopen( "/proc/stat", "r" );
115      if (!file) {
116           perror( "Could not open '/proc/stat'" );
117           return 0;
118      }
119 
120      if (fscanf( file, "%3s %d %d %d %d %d %d %d", dummy, &cuser, &cnice, &csystem, &cidle, &wa, &hi, &si ) < 4) {
121           fprintf( stderr, "Parsing '/proc/stat' failed!\n" );
122           return 0;
123      }
124 
125      fclose( file );
126 
127      /* Compatibility with 2.6 split up idle times. */
128      cidle += wa + hi + si;
129 
130      /* Count nice as idle. */
131      cidle += cnice;
132      cnice  = 0;
133 
134      ctotal  = cuser + cnice + csystem + cidle;
135 
136      duser   = cuser - puser;
137      dnice   = cnice - pnice;
138      dsystem = csystem - psystem;
139      didle   = cidle - pidle;
140      dtotal  = ctotal - ptotal;
141 
142      if (!ftotal) {
143           fuser   = cuser;
144           fnice   = cnice;
145           fsystem = csystem;
146           fidle   = cidle;
147           ftotal  = ctotal;
148      }
149 
150      return 1;
151 }
152 
153 /**********************************************************************************************************************/
154 
155 static pthread_t       busy_thread;
156 static pthread_mutex_t busy_lock  = PTHREAD_MUTEX_INITIALIZER;
157 static unsigned int    busy_alive = 1;
158 static unsigned int    busy_count;
159 
160 static void *
busy_loop(void * arg)161 busy_loop( void *arg )
162 {
163      setpriority( PRIO_PROCESS, 0, 19 );
164 
165      while (busy_alive) {
166           int i;
167 
168           for (i=0; i<100000; i++);
169 
170           pthread_mutex_lock( &busy_lock );
171 
172           busy_count++;
173 
174           pthread_mutex_unlock( &busy_lock );
175      }
176 
177      return NULL;
178 }
179 
180 /**********************************************************************************************************************/
181 
182 static FusionCallHandlerResult
call_handler(int caller,int call_arg,void * call_ptr,void * ctx,unsigned int serial,int * ret_val)183 call_handler( int           caller,
184               int           call_arg,
185               void         *call_ptr,
186               void         *ctx,
187               unsigned int  serial,
188               int          *ret_val )
189 {
190      static u32 checksum = 0;
191 
192      int        i;
193      const u32 *values = call_ptr;
194 
195 
196      for (i=0; i<block_size/4; i++)
197           checksum += values[i];
198 
199      *ret_val = checksum;
200 
201      return FCHR_RETURN;
202 }
203 
204 /**********************************************************************************************************************/
205 
206 int
main(int argc,char * argv[])207 main( int argc, char *argv[] )
208 {
209      DirectResult         ret;
210      FusionWorld         *world;
211      FusionCall           call = {0};
212      FusionSHMPoolShared *pool;
213      void                *buffer;
214      int           i, max_busy = 0, active = 1;
215      long long     t1 = 0, t2;
216      long long     start       = 0;
217      long long     bytes       = 0;
218      long long     last_bytes  = 0;
219      unsigned long blocks      = 0;
220      unsigned long last_blocks = 0;
221      unsigned long last_busy   = 0;
222      u32           checksum    = 0;
223      bool          produce     = true;
224      int           delay       = 66000;
225 
226      if (parse_cmdline( argc, argv ))
227           return -1;
228 
229      if (bit_rate) {
230           int blocks_per_sec = (bit_rate * 1024 / 8) / block_size;
231 
232           if (blocks_per_sec < 100)
233                delay = 900000 / blocks_per_sec - 2000;
234           else
235                delay = 300000 * 100 / blocks_per_sec;
236 
237           num_blocks = bit_rate * 1024 / 8 / block_size * delay / 900000;
238 
239           if (num_blocks > MAX_NUM_BLOCKS)
240                num_blocks = MAX_NUM_BLOCKS;
241 
242           if (!num_blocks) {
243                num_blocks = 1;
244                delay = 970 * block_size / (bit_rate * 1024 / 8) * 1000 - 2000;
245           }
246      }
247 
248      sync();
249 
250      if (run_busy) {
251           pthread_create( &busy_thread, NULL, busy_loop, NULL );
252 
253           printf( "Calibrating...\n" );
254 
255           pthread_mutex_lock( &busy_lock );
256 
257           for (i=0; i<7; i++) {
258                int busy_rate;
259 
260                busy_count = 0;
261 
262                t1 = get_millis();
263                pthread_mutex_unlock( &busy_lock );
264 
265                usleep( 300000 );
266 
267                pthread_mutex_lock( &busy_lock );
268                t2 = get_millis();
269 
270                busy_rate = busy_count * 1000 / (t2 - t1);
271 
272                if (busy_rate > max_busy)
273                     max_busy = busy_rate;
274           }
275 
276           printf( "Calibrating done. (%d busy counts/sec)\n", max_busy );
277      }
278 
279      ret = fusion_enter( -1, 23, FER_MASTER, &world );
280      if (ret)
281           return ret;
282 
283      ret = fusion_call_init( &call, call_handler, NULL, world );
284      if (ret)
285           return ret;
286 
287      ret = fusion_shm_pool_create( world, "Stream Buffer", block_size + 8192, false, &pool );
288      if (ret)
289           return ret;
290 
291      ret = fusion_shm_pool_allocate( pool, block_size, false, true, &buffer );
292      if (ret)
293           return ret;
294 
295 
296 
297      /*
298       * Do the fork() magic!
299       */
300      if (do_fork) {
301           fusion_world_set_fork_action( world, FFA_FORK );
302 
303           switch (fork()) {
304                case -1:
305                     D_PERROR( "fork() failed!\n" );
306                     return -1;
307 
308                case 0:
309                     /* child continues as the producer */
310                     run_busy = false;
311                     break;
312 
313                default:
314                     /* parent is the consumer (callback in Fusion Dispatch thread) */
315                     produce = false;
316 
317                     usleep( 50000 );
318           }
319 
320           fusion_world_set_fork_action( world, FFA_CLOSE );
321      }
322 
323 
324      start = t1 = get_millis();
325 
326      if (run_busy) {
327           busy_count = 0;
328           pthread_mutex_unlock( &busy_lock );
329      }
330 
331 #ifdef LINUX_2_4
332      delay -= 10000;
333 #endif
334 
335      do {
336           if (bit_rate || !produce) {
337                if (delay > 10)
338                     usleep( delay );
339           }
340 
341           if (produce) {
342                for (i=0; i<num_blocks; i++) {
343                     int  n;
344                     u32  retsum;
345                     u32 *values = buffer;
346 
347                     for (n=0; n<block_size/4; n++) {
348                          values[n] = n;
349                          checksum += n;
350                     }
351 
352                     bytes += block_size;
353 
354                     fusion_call_execute( &call, do_thread ? FCEF_NODIRECT : FCEF_NONE,
355                                          0, buffer, (int*) &retsum );
356 
357                     if (retsum != checksum)
358                          D_ERROR( "Checksum returned by consumer (0x%08x) does not match 0x%08x!\n", retsum, checksum );
359                }
360 
361                blocks += num_blocks;
362           }
363 
364 
365           t2 = get_millis();
366           if (t2 - t1 > 2000) {
367                if (produce) {
368                     long long kbits = 0, avgkbits, total_time, diff_time, diff_bytes;
369 
370                     printf( "\n\n\n" );
371 
372                     total_time = t2 - start;
373                     diff_time  = t2 - t1;
374                     diff_bytes = bytes - last_bytes;
375 
376                     avgkbits   = (long long)bytes * 8LL * 1000LL / (long long)total_time / 1024LL;
377 
378                     if (diff_time)
379                          kbits = (long long)diff_bytes * 8LL * 1000LL / (long long)diff_time / 1024LL;
380 
381                     printf( "Total Time:       %7lld ms\n", total_time );
382                     printf( "Stream Size:      %7lld kb\n", bytes / 1024 );
383                     printf( "Stream Rate:      %7lld kb/sec -> %lld.%03lld MBit (avg. %lld.%03lld MBit)\n",
384                             kbits / 8,
385                             (kbits * 1000 / 1024) / 1000, (kbits * 1000 / 1024) % 1000,
386                             (avgkbits * 1000 / 1024) / 1000, (avgkbits * 1000 / 1024) % 1000 );
387                     printf( "\n" );
388 
389 
390                     if (last_bytes && bit_rate) {
391                          long long diff_bytes = (bytes - last_bytes) * 1000 / (t2 - t1);
392                          long long need_bytes = bit_rate * 1024 / 8;
393 
394                          if (diff_bytes) {
395                               int new_blocks = (num_blocks * need_bytes + diff_bytes/2) / diff_bytes;
396 
397                               num_blocks = (new_blocks + num_blocks + 1) / 2;
398 
399                               if (num_blocks > MAX_NUM_BLOCKS)
400                                    num_blocks = MAX_NUM_BLOCKS;
401                          }
402                     }
403 
404 
405                     read_stat();
406 
407                     if (ftotal != ctotal && dtotal) {
408                          int load, aload;
409 
410                          load  = 1000 - didle * 1000 / dtotal;
411                          aload = 1000 - (cidle - fidle) * 1000 / (ctotal - ftotal);
412 
413                          printf( "Overall Stats\n" );
414                          printf( "  Total Time:      %7lld ms\n", t2 - start );
415                          printf( "  Block Size:      %7ld\n", block_size );
416                          printf( "  Blocks/cycle:    %7ld\n", num_blocks );
417                          printf( "  Blocks/second:   %7lld\n", (blocks - last_blocks) * 1000 / diff_time );
418                          printf( "  Delay:           %7d\n", delay );
419                          printf( "  CPU Load:        %5d.%d %% (avg. %d.%d %%)\n",
420                                  load / 10, load % 10, aload / 10, aload % 10 );
421                     }
422 
423 
424                     last_bytes  = bytes;
425                     last_blocks = blocks;
426                }
427 
428                if (run_busy) {
429                     pthread_mutex_lock( &busy_lock );
430 
431                     if (last_busy) {
432                          int busy_diff;
433                          int busy_rate, busy_load;
434                          int abusy_rate, abusy_load;
435 
436                          busy_diff = busy_count - last_busy;
437                          busy_rate = max_busy - (busy_diff * 1000 / (t2 - t1));
438                          busy_load = busy_rate * 1000 / max_busy;
439                          abusy_rate = max_busy - (busy_count * 1000 / (t2 - start));
440                          abusy_load = abusy_rate * 1000 / max_busy;
441 
442                          printf( "  Real CPU Load:   %5d.%d %% (avg. %d.%d %%)\n",
443                                  busy_load / 10, busy_load % 10,
444                                  abusy_load / 10, abusy_load % 10 );
445                     }
446 
447                     last_busy = busy_count;
448 
449                     pthread_mutex_unlock( &busy_lock );
450                }
451 
452                t1 = t2;
453           }
454      } while (active > 0);
455 
456 
457      if (run_busy) {
458           busy_alive = 0;
459 
460           pthread_join( busy_thread, NULL );
461      }
462 
463      return -1;
464 }
465 
466 /**********************************************************************************************************************/
467 
468 static int
parse_cmdline(int argc,char * argv[])469 parse_cmdline( int argc, char *argv[] )
470 {
471      int   i;
472      char *end;
473 
474      for (i=1; i<argc; i++) {
475           if (!strcmp( argv[i], "-s" )) {
476                if (++i < argc) {
477                     block_size = strtoul( argv[i], &end, 10 );
478 
479                     if (end && *end) {
480                          D_ERROR( "Parse error in number '%s'!\n", argv[i] );
481                          return -1;
482                     }
483 
484                     if (block_size < 1)
485                          return show_usage();
486                }
487                else
488                     return show_usage();
489           }
490           else if (!strcmp( argv[i], "-b" )) {
491                if (++i < argc) {
492                     bit_rate = strtoul( argv[i], &end, 10 );
493 
494                     if (end && *end) {
495                          D_ERROR( "Parse error in number '%s'!\n", argv[i] );
496                          return -1;
497                     }
498                }
499                else
500                     return show_usage();
501           }
502           else if (!strcmp( argv[i], "-B" )) {
503                if (++i < argc) {
504                     bit_rate = strtoul( argv[i], &end, 10 ) * 1024;
505 
506                     if (end && *end) {
507                          D_ERROR( "Parse error in number '%s'!\n", argv[i] );
508                          return -1;
509                     }
510                }
511                else
512                     return show_usage();
513           }
514           else if (!strcmp( argv[i], "-c" )) {
515                run_busy = 1;
516           }
517           else if (!strcmp( argv[i], "-f" )) {
518                do_fork = 1;
519           }
520           else if (!strcmp( argv[i], "-t" )) {
521                do_thread = 1;
522           }
523           else
524                return show_usage();
525      }
526 
527      return 0;
528 }
529 
530 static int
show_usage(void)531 show_usage( void )
532 {
533      fprintf( stderr, "\n"
534                       "Usage:\n"
535                       "   fusion_stream [options]\n"
536                       "\n"
537                       "Options:\n"
538                       "   -s <1-n>  Size of each block of data\n"
539                       "   -b <0-n>  Designated bit rate in kbit (0 = unlimited)\n"
540                       "   -B <0-n>  Designated bit rate in Mbit (0 = unlimited)\n"
541                       "   -c        Run busy loop counting spare CPU cycles to get real CPU load\n"
542                       "   -f        Fork to have the producer in a separate process\n"
543                       "   -t        Force calls to be handled in a separate thread\n"
544                       "\n"
545               );
546 
547      return -1;
548 }
549 
550