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