1/*
2* Copyright (c) 2005, Bull S.A..  All rights reserved.
3* Created by: Sebastien Decugis
4
5* This program is free software; you can redistribute it and/or modify it
6* under the terms of version 2 of the GNU General Public License as
7* published by the Free Software Foundation.
8*
9* This program is distributed in the hope that it would be useful, but
10* WITHOUT ANY WARRANTY; without even the implied warranty of
11* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12*
13* You should have received a copy of the GNU General Public License along
14* with this program; if not, write the Free Software Foundation, Inc.,
15* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
16
17* This scalability sample aims to test the following assertion:
18*  -> The sem_open() duration does not depend on the # of opened semaphores
19*     in the system
20
21* The steps are:
22* -> Create semaphores until failure
23
24* The test fails if the sem_open duration tends to grow with the # of semaphores,
25* or if the failure at last semaphore creation is unexpected.
26*/
27
28/* We are testing conformance to IEEE Std 1003.1, 2003 Edition */
29#define _POSIX_C_SOURCE 200112L
30
31/********************************************************************************************/
32/****************************** standard includes *****************************************/
33/********************************************************************************************/
34#include <pthread.h>
35#include <stdarg.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <unistd.h>
40
41#include <math.h>
42#include <errno.h>
43#include <time.h>
44#include <semaphore.h>
45#include <fcntl.h>
46
47/********************************************************************************************/
48/******************************   Test framework   *****************************************/
49/********************************************************************************************/
50#include "testfrmw.h"
51#include "testfrmw.c"
52/* This header is responsible for defining the following macros:
53 * UNRESOLVED(ret, descr);
54 *    where descr is a description of the error and ret is an int (error code for example)
55 * FAILED(descr);
56 *    where descr is a short text saying why the test has failed.
57 * PASSED();
58 *    No parameter.
59 *
60 * Both three macros shall terminate the calling process.
61 * The testcase shall not terminate in any other maneer.
62 *
63 * The other file defines the functions
64 * void output_init()
65 * void output(char * string, ...)
66 *
67 * Those may be used to output information.
68 */
69
70/********************************************************************************************/
71/********************************** Configuration ******************************************/
72/********************************************************************************************/
73#ifndef SCALABILITY_FACTOR
74#define SCALABILITY_FACTOR 1
75#endif
76#ifndef VERBOSE
77#define VERBOSE 1
78#endif
79
80#define BLOCKSIZE (100 * SCALABILITY_FACTOR)
81
82#ifdef PLOT_OUTPUT
83#undef VERBOSE
84#define VERBOSE 0
85#endif
86
87/********************************************************************************************/
88/***********************************       Test     *****************************************/
89/********************************************************************************************/
90
91/* The next structure is used to save the tests measures */
92
93typedef struct __mes_t {
94	int nsem;
95	long _data_open;	/* As we store µsec values, a long type should be enough. */
96	long _data_close;	/* As we store µsec values, a long type should be enough. */
97
98	struct __mes_t *next;
99
100	struct __mes_t *prev;
101} mes_t;
102
103/* Forward declaration */
104int parse_measure(mes_t * measures);
105
106/* Structure to store created semaphores */
107
108typedef struct __test_t {
109	sem_t *sems[BLOCKSIZE];
110
111	struct __test_t *next;
112
113	struct __test_t *prev;
114} test_t;
115
116/* Test routine */
117int main(int argc, char *argv[])
118{
119	int ret, status, locerrno;
120	int nsem, i;
121
122	struct timespec ts_ref, ts_fin;
123	mes_t sentinel;
124	mes_t *m_cur, *m_tmp;
125
126	char sem_name[255];
127	test_t sems;
128
129	struct __test_t *sems_cur = &sems, *sems_tmp;
130
131	long SEM_MAX = sysconf(_SC_SEM_NSEMS_MAX);
132
133	/* Initialize the measure list */
134	m_cur = &sentinel;
135	m_cur->next = NULL;
136	m_cur->prev = NULL;
137
138	/* Initialize output routine */
139	output_init();
140
141	/* Initialize sems */
142	sems_cur->next = NULL;
143	sems_cur->prev = NULL;
144
145#if VERBOSE > 1
146	output("SEM_NSEMS_MAX: %ld\n", SEM_MAX);
147
148#endif
149
150#ifdef PLOT_OUTPUT
151	output("# COLUMNS 3 Semaphores sem_open sem_close\n");
152
153#endif
154
155	nsem = 0;
156	status = 0;
157
158	while (1) {		/* we will break */
159		/* Create a new block */
160		sems_tmp = malloc(sizeof(test_t));
161
162		if (sems_tmp == NULL) {
163			/* We stop here */
164#if VERBOSE > 0
165			output("malloc failed with error %d (%s)\n", errno,
166			       strerror(errno));
167#endif
168			/* We can proceed anyway */
169			status = 1;
170
171			break;
172		}
173
174		/* read clock */
175		ret = clock_gettime(CLOCK_REALTIME, &ts_ref);
176
177		if (ret != 0) {
178			UNRESOLVED(errno, "Unable to read clock");
179		}
180
181		/* Open all semaphores in the current block */
182		for (i = 0; i < BLOCKSIZE; i++) {
183			sprintf(sem_name, "/sem_open_scal_s%d", nsem);
184			sems_tmp->sems[i] =
185			    sem_open(sem_name, O_CREAT, 0777, 1);
186
187			if (sems_tmp->sems[i] == SEM_FAILED) {
188#if VERBOSE > 0
189				output("sem_open failed with error %d (%s)\n",
190				       errno, strerror(errno));
191#endif
192				/* Check error code */
193
194				switch (errno) {
195				case EMFILE:
196				case ENFILE:
197				case ENOSPC:
198				case ENOMEM:
199					status = 2;
200					break;
201				default:
202					UNRESOLVED(errno, "Unexpected error!");
203				}
204
205				break;
206			}
207
208			if ((SEM_MAX > 0) && (nsem > SEM_MAX)) {
209				/* Erreur */
210				FAILED
211				    ("sem_open opened more than SEM_NSEMS_MAX semaphores");
212			}
213
214			nsem++;
215		}
216
217		/* read clock */
218		ret = clock_gettime(CLOCK_REALTIME, &ts_fin);
219
220		if (ret != 0) {
221			UNRESOLVED(errno, "Unable to read clock");
222		}
223
224		if (status == 2) {
225			/* We were not able to fill this bloc, so we can discard it */
226
227			for (--i; i >= 0; i--) {
228				ret = sem_close(sems_tmp->sems[i]);
229
230				if (ret != 0) {
231					UNRESOLVED(errno, "Failed to close");
232				}
233
234			}
235
236			free(sems_tmp);
237			break;
238
239		}
240
241		sems_tmp->prev = sems_cur;
242		sems_cur->next = sems_tmp;
243		sems_cur = sems_tmp;
244		sems_cur->next = NULL;
245
246		/* add to the measure list */
247		m_tmp = malloc(sizeof(mes_t));
248
249		if (m_tmp == NULL) {
250			/* We stop here */
251#if VERBOSE > 0
252			output("malloc failed with error %d (%s)\n", errno,
253			       strerror(errno));
254#endif
255			/* We can proceed anyway */
256			status = 3;
257
258			break;
259		}
260
261		m_tmp->nsem = nsem;
262		m_tmp->next = NULL;
263		m_tmp->prev = m_cur;
264		m_cur->next = m_tmp;
265
266		m_cur = m_tmp;
267
268		m_cur->_data_open =
269		    ((ts_fin.tv_sec - ts_ref.tv_sec) * 1000000) +
270		    ((ts_fin.tv_nsec - ts_ref.tv_nsec) / 1000);
271		m_cur->_data_close = 0;
272	}
273
274	locerrno = errno;
275
276	/* Unlink all existing semaphores */
277#if VERBOSE > 0
278	output("Unlinking %d semaphores\n", nsem);
279#endif
280
281	for (i = 0; i <= nsem; i++) {
282		sprintf(sem_name, "/sem_open_scal_s%d", i);
283		sem_unlink(sem_name);
284	}
285
286	/* Free all semaphore blocs */
287#if VERBOSE > 0
288	output("Close and free semaphores (this can take up to 10 minutes)\n");
289
290#endif
291
292	/* Reverse list order */
293	while (sems_cur != &sems) {
294		/* read clock */
295		ret = clock_gettime(CLOCK_REALTIME, &ts_ref);
296
297		if (ret != 0) {
298			UNRESOLVED(errno, "Unable to read clock");
299		}
300
301		/* Empty the sems_cur block */
302
303		for (i = 0; i < BLOCKSIZE; i++) {
304			ret = sem_close(sems_cur->sems[i]);
305
306			if (ret != 0) {
307				UNRESOLVED(errno,
308					   "Failed to close a semaphore");
309			}
310		}
311
312		/* read clock */
313		ret = clock_gettime(CLOCK_REALTIME, &ts_fin);
314
315		if (ret != 0) {
316			UNRESOLVED(errno, "Unable to read clock");
317		}
318
319		/* add this measure to measure list */
320
321		m_cur->_data_close =
322		    ((ts_fin.tv_sec - ts_ref.tv_sec) * 1000000) +
323		    ((ts_fin.tv_nsec - ts_ref.tv_nsec) / 1000);
324
325		m_cur = m_cur->prev;
326
327		/* remove the sem bloc */
328		sems_cur = sems_cur->prev;
329
330		free(sems_cur->next);
331
332		sems_cur->next = NULL;
333	}
334
335#if VERBOSE > 0
336	output("Parse results\n");
337
338#endif
339
340	/* Compute the results */
341	ret = parse_measure(&sentinel);
342
343	/* Free the resources and output the results */
344
345#if VERBOSE > 5
346	output("Dump : \n");
347
348	output("  nsem  |  open  |  close \n");
349
350#endif
351
352	while (sentinel.next != NULL) {
353		m_cur = sentinel.next;
354#if (VERBOSE > 5) || defined(PLOT_OUTPUT)
355		output("%8.8i %1.1li.%6.6li %1.1li.%6.6li\n", m_cur->nsem,
356		       m_cur->_data_open / 1000000, m_cur->_data_open % 1000000,
357		       m_cur->_data_close / 1000000,
358		       m_cur->_data_close % 1000000);
359
360#endif
361		sentinel.next = m_cur->next;
362
363		free(m_cur);
364	}
365
366	if (ret != 0) {
367		FAILED
368		    ("The function is not scalable, add verbosity for more information");
369	}
370
371	/* Check status */
372	if (status) {
373		UNRESOLVED(locerrno,
374			   "Function is scalable, but test terminated with error");
375	}
376#if VERBOSE > 0
377	output("-----\n");
378
379	output("All test data destroyed\n");
380
381	output("Test PASSED\n");
382
383#endif
384
385	PASSED;
386}
387
388/***
389 * The next function will seek for the better model for each series of measurements.
390 *
391 * The tested models are: -- X = # threads; Y = latency
392 * -> Y = a;      -- Error is r1 = avg((Y - Yavg)²);
393 * -> Y = aX + b; -- Error is r2 = avg((Y -aX -b)²);
394 *                -- where a = avg ((X - Xavg)(Y - Yavg)) / avg((X - Xavg)²)
395 *                --         Note: We will call _q = sum((X - Xavg) * (Y - Yavg));
396 *                --                       and  _d = sum((X - Xavg)²);
397 *                -- and   b = Yavg - a * Xavg
398 * -> Y = c * X^a;-- Same as previous, but with log(Y) = a log(X) + b; and b = log(c). Error is r3
399 * -> Y = exp(aX + b); -- log(Y) = aX + b. Error is r4
400 *
401 * We compute each error factor (r1, r2, r3, r4) then search which is the smallest (with ponderation).
402 * The function returns 0 when r1 is the best for all cases (latency is constant) and !0 otherwise.
403 */
404
405struct row {
406	long X;			/* the X values -- copied from function argument */
407	long Y_o;		/* the Y values -- copied from function argument */
408	long Y_c;		/* the Y values -- copied from function argument */
409	long _x;		/* Value X - Xavg */
410	long _y_o;		/* Value Y - Yavg */
411	long _y_c;		/* Value Y - Yavg */
412	double LnX;		/* Natural logarithm of X values */
413	double LnY_o;		/* Natural logarithm of Y values */
414	double LnY_c;		/* Natural logarithm of Y values */
415	double _lnx;		/* Value LnX - LnXavg */
416	double _lny_o;		/* Value LnY - LnYavg */
417	double _lny_c;		/* Value LnY - LnYavg */
418};
419
420int parse_measure(mes_t * measures)
421{
422	int ret, r;
423
424	mes_t *cur;
425
426	double Xavg, Yavg_o, Yavg_c;
427	double LnXavg, LnYavg_o, LnYavg_c;
428
429	int N;
430
431	double r1_o, r2_o, r3_o, r4_o;
432	double r1_c, r2_c, r3_c, r4_c;
433
434	/* Some more intermediate vars */
435	long double _q_o[3];
436	long double _d_o[3];
437	long double _q_c[3];
438	long double _d_c[3];
439
440	long double t;		/* temp value */
441
442	struct row *Table = NULL;
443
444	/* This array contains the last element of each serie */
445	int array_max;
446
447	/* Initialize the datas */
448
449	array_max = -1;		/* means no data */
450	Xavg = 0.0;
451	LnXavg = 0.0;
452	Yavg_o = 0.0;
453	LnYavg_o = 0.0;
454	r1_o = 0.0;
455	r2_o = 0.0;
456	r3_o = 0.0;
457	r4_o = 0.0;
458	_q_o[0] = 0.0;
459	_q_o[1] = 0.0;
460	_q_o[2] = 0.0;
461	_d_o[0] = 0.0;
462	_d_o[1] = 0.0;
463	_d_o[2] = 0.0;
464	Yavg_c = 0.0;
465	LnYavg_c = 0.0;
466	r1_c = 0.0;
467	r2_c = 0.0;
468	r3_c = 0.0;
469	r4_c = 0.0;
470	_q_c[0] = 0.0;
471	_q_c[1] = 0.0;
472	_q_c[2] = 0.0;
473	_d_c[0] = 0.0;
474	_d_c[1] = 0.0;
475	_d_c[2] = 0.0;
476
477	N = 0;
478	cur = measures;
479
480#if VERBOSE > 1
481	output("Data analysis starting\n");
482#endif
483
484	/* We start with reading the list to find:
485	 * -> number of elements, to assign an array.
486	 * -> average values
487	 */
488
489	while (cur->next != NULL) {
490		cur = cur->next;
491
492		N++;
493
494		if (cur->_data_open != 0) {
495			array_max = N;
496			Xavg += (double)cur->nsem;
497			LnXavg += log((double)cur->nsem);
498			Yavg_o += (double)cur->_data_open;
499			LnYavg_o += log((double)cur->_data_open);
500			Yavg_c += (double)cur->_data_close;
501			LnYavg_c += log((double)cur->_data_close);
502		}
503
504	}
505
506	/* We have the sum; we can divide to obtain the average values */
507	if (array_max != -1) {
508		Xavg /= array_max;
509		LnXavg /= array_max;
510		Yavg_o /= array_max;
511		LnYavg_o /= array_max;
512		Yavg_c /= array_max;
513		LnYavg_c /= array_max;
514	}
515#if VERBOSE > 1
516	output(" Found %d rows\n", N);
517
518#endif
519
520	/* We will now alloc the array ... */
521
522	Table = calloc(N, sizeof(struct row));
523
524	if (Table == NULL) {
525		UNRESOLVED(errno, "Unable to alloc space for results parsing");
526	}
527
528	/* ... and fill it */
529	N = 0;
530
531	cur = measures;
532
533	while (cur->next != NULL) {
534		cur = cur->next;
535
536		Table[N].X = (long)cur->nsem;
537		Table[N].LnX = log((double)cur->nsem);
538
539		if (array_max > N) {
540			Table[N]._x = Table[N].X - Xavg;
541			Table[N]._lnx = Table[N].LnX - LnXavg;
542			Table[N].Y_o = cur->_data_open;
543			Table[N]._y_o = Table[N].Y_o - Yavg_o;
544			Table[N].LnY_o = log((double)cur->_data_open);
545			Table[N]._lny_o = Table[N].LnY_o - LnYavg_o;
546			Table[N].Y_c = cur->_data_close;
547			Table[N]._y_c = Table[N].Y_c - Yavg_c;
548			Table[N].LnY_c = log((double)cur->_data_close);
549			Table[N]._lny_c = Table[N].LnY_c - LnYavg_c;
550		}
551
552		N++;
553	}
554
555	/* We won't need the list anymore -- we'll work with the array which should be faster. */
556#if VERBOSE > 1
557	output(" Data was stored in an array.\n");
558
559#endif
560
561	/* We need to read the full array at least twice to compute all the error factors */
562
563	/* In the first pass, we'll compute:
564	 * -> r1 for each scenar.
565	 * -> "a" factor for linear (0), power (1) and exponential (2) approximations -- with using the _d and _q vars.
566	 */
567#if VERBOSE > 1
568	output("Starting first pass...\n");
569
570#endif
571	for (r = 0; r < array_max; r++) {
572		r1_o +=
573		    ((double)Table[r]._y_o / array_max) * (double)Table[r]._y_o;
574
575		_q_o[0] += Table[r]._y_o * Table[r]._x;
576		_d_o[0] += Table[r]._x * Table[r]._x;
577
578		_q_o[1] += Table[r]._lny_o * Table[r]._lnx;
579		_d_o[1] += Table[r]._lnx * Table[r]._lnx;
580
581		_q_o[2] += Table[r]._lny_o * Table[r]._x;
582		_d_o[2] += Table[r]._x * Table[r]._x;
583
584		r1_c +=
585		    ((double)Table[r]._y_c / array_max) * (double)Table[r]._y_c;
586
587		_q_c[0] += Table[r]._y_c * Table[r]._x;
588		_d_c[0] += Table[r]._x * Table[r]._x;
589
590		_q_c[1] += Table[r]._lny_c * Table[r]._lnx;
591		_d_c[1] += Table[r]._lnx * Table[r]._lnx;
592
593		_q_c[2] += Table[r]._lny_c * Table[r]._x;
594		_d_c[2] += Table[r]._x * Table[r]._x;
595
596	}
597
598	/* First pass is terminated; a2 = _q[0]/_d[0]; a3 = _q[1]/_d[1]; a4 = _q[2]/_d[2] */
599
600	/* In the first pass, we'll compute:
601	 * -> r2, r3, r4 for each scenar.
602	 */
603
604#if VERBOSE > 1
605	output("Starting second pass...\n");
606
607#endif
608	for (r = 0; r < array_max; r++) {
609		/* r2 = avg((y - ax -b)²);  t = (y - ax - b) = (y - yavg) - a (x - xavg); */
610		t = (Table[r]._y_o - ((_q_o[0] * Table[r]._x) / _d_o[0]));
611		r2_o += t * t / array_max;
612
613		t = (Table[r]._y_c - ((_q_c[0] * Table[r]._x) / _d_c[0]));
614		r2_c += t * t / array_max;
615
616		/* r3 = avg((y - c.x^a) ²);
617		   t = y - c * x ^ a
618		   = y - log (LnYavg - (_q[1]/_d[1]) * LnXavg) * x ^ (_q[1]/_d[1])
619		 */
620		t = (Table[r].Y_o
621		     - (logl(LnYavg_o - (_q_o[1] / _d_o[1]) * LnXavg)
622			* powl(Table[r].X, (_q_o[1] / _d_o[1]))
623		     ));
624		r3_o += t * t / array_max;
625
626		t = (Table[r].Y_c
627		     - (logl(LnYavg_c - (_q_c[1] / _d_c[1]) * LnXavg)
628			* powl(Table[r].X, (_q_c[1] / _d_c[1]))
629		     ));
630		r3_c += t * t / array_max;
631
632		/* r4 = avg((y - exp(ax+b))²);
633		   t = y - exp(ax+b)
634		   = y - exp(_q[2]/_d[2] * x + (LnYavg - (_q[2]/_d[2] * Xavg)));
635		   = y - exp(_q[2]/_d[2] * (x - Xavg) + LnYavg);
636		 */
637		t = (Table[r].Y_o
638		     - expl((_q_o[2] / _d_o[2]) * Table[r]._x + LnYavg_o));
639		r4_o += t * t / array_max;
640
641		t = (Table[r].Y_c
642		     - expl((_q_c[2] / _d_c[2]) * Table[r]._x + LnYavg_c));
643		r4_c += t * t / array_max;
644
645	}
646
647#if VERBOSE > 1
648	output("All computing terminated.\n");
649
650#endif
651	ret = 0;
652
653#if VERBOSE > 1
654	output(" # of data: %i\n", array_max);
655
656	output("  Model: Y = k\n");
657
658	output("   sem_open:\n");
659
660	output("       k = %g\n", Yavg_o);
661
662	output("    Divergence %g\n", r1_o);
663
664	output("   sem_close:\n");
665
666	output("       k = %g\n", Yavg_c);
667
668	output("    Divergence %g\n", r1_c);
669
670	output("  Model: Y = a * X + b\n");
671
672	output("   sem_open:\n");
673
674	output("       a = %Lg\n", _q_o[0] / _d_o[0]);
675
676	output("       b = %Lg\n", Yavg_o - ((_q_o[0] / _d_o[0]) * Xavg));
677
678	output("    Divergence %g\n", r2_o);
679
680	output("   sem_close:\n");
681
682	output("       a = %Lg\n", _q_c[0] / _d_c[0]);
683
684	output("       b = %Lg\n", Yavg_c - ((_q_c[0] / _d_c[0]) * Xavg));
685
686	output("    Divergence %g\n", r2_c);
687
688	output("  Model: Y = c * X ^ a\n");
689
690	output("   sem_open:\n");
691
692	output("       a = %Lg\n", _q_o[1] / _d_o[1]);
693
694	output("       c = %Lg\n",
695	       logl(LnYavg_o - (_q_o[1] / _d_o[1]) * LnXavg));
696
697	output("    Divergence %g\n", r3_o);
698
699	output("   sem_close:\n");
700
701	output("       a = %Lg\n", _q_c[1] / _d_c[1]);
702
703	output("       c = %Lg\n",
704	       logl(LnYavg_c - (_q_c[1] / _d_c[1]) * LnXavg));
705
706	output("    Divergence %g\n", r3_c);
707
708	output("  Model: Y = exp(a * X + b)\n");
709
710	output("   sem_open:\n");
711
712	output("       a = %Lg\n", _q_o[2] / _d_o[2]);
713
714	output("       b = %Lg\n", LnYavg_o - ((_q_o[2] / _d_o[2]) * Xavg));
715
716	output("    Divergence %g\n", r4_o);
717
718	output("   sem_close:\n");
719
720	output("       a = %Lg\n", _q_c[2] / _d_c[2]);
721
722	output("       b = %Lg\n", LnYavg_c - ((_q_c[2] / _d_c[2]) * Xavg));
723
724	output("    Divergence %g\n", r4_c);
725
726#endif
727
728	if (array_max != -1) {
729		/* Compare r1 to other values, with some ponderations */
730
731		if ((r1_o > 1.1 * r2_o) || (r1_o > 1.2 * r3_o) ||
732		    (r1_o > 1.3 * r4_o) || (r1_c > 1.1 * r2_c) ||
733		    (r1_c > 1.2 * r3_c) || (r1_c > 1.3 * r4_c))
734			ret++;
735
736#if VERBOSE > 1
737		else
738			output(" Sanction: OK\n");
739
740#endif
741
742	}
743
744	/* We need to free the array */
745	free(Table);
746
747	/* We're done */
748	return ret;
749}
750