pmm  1.0.0
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
pmm_model.c
Go to the documentation of this file.
1 /*
2  Copyright (C) 2008-2010 Robert Higgins
3  Author: Robert Higgins <robert.higgins@ucd.ie>
4 
5  This file is part of PMM.
6 
7  PMM is free software: you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  PMM is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  GNU General Public License for more details.
16 
17  You should have received a copy of the GNU General Public License
18  along with PMM. If not, see <http://www.gnu.org/licenses/>.
19 */
20 /*!
21  * @file pmm_model.c
22  *
23  * @brief functions for operating on PMM model, benchmark and other data
24  * structures
25  *
26  * this file contains the code for operating on model and associated data
27  * structures of the pmm applications
28  */
29 #if HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include <stdlib.h> // for malloc
34 #include <time.h> // for mktime/etc.
35 #include <ctype.h> // for isdigit
36 #include <string.h> // for strcpy/memset
37 
38 #include "pmm_model.h"
39 #include "pmm_octave.h"
40 #include "pmm_interval.h"
41 #include "pmm_param.h"
42 #include "pmm_load.h"
43 #include "pmm_log.h"
44 
45 /*
46  * TODO model completion should be a member of the benchmark structure,
47  * not the routine structure
48  */
49 
50 /******************************80 columns**************************************/
51 
52  /*!
53  * Allocates memory for the pmm_config structure. Sets some default values
54  * in the config.
55  *
56  * @return pointer to newly allocated pmm_config structure or NULL on failure
57  */
59  struct pmm_config *c;
60 
61  c = malloc(sizeof *c);
62 
63  c->routines = malloc(5 * sizeof *(c->routines));
64  if(c->routines == NULL) {
65  ERRPRINTF("allocation of memory to routine array failed.\n");
66 
67  free(c);
68  c = NULL;
69 
70  return NULL;
71  }
72  c->allocated = 5;
73  c->used = 0;
74 
75  c->loadhistory = (void *)NULL;
76 
77  c->daemon = 0;
78  c->build_only = 0;
79  c->configfile = SYSCONFDIR"/pmmd.conf";
80  c->logfile = "./pmmd.log"; //TODO set default log file directory
81 
82  c->ts_main_sleep_period.tv_sec = 1;
83  c->ts_main_sleep_period.tv_nsec = 0;
84  c->num_execs_threshold = 20;
85  c->time_spend_threshold = 60;
86 
87  c->pause = 0;
88 
89  return c;
90 }
91 
92 /*!
93  * Allocates memory for a new routine and sets some default values
94  *
95  * @return pointer to newly allocated routine structure
96  */
97 struct pmm_routine*
99 {
100  struct pmm_routine *r;
101 
102  r = malloc(sizeof *r);
103 
104  r->name = NULL;
105  r->exe_path = NULL;
106  r->exe_args = NULL;
107 
108  r->pd_set = new_paramdef_set();
109 
110  r->condition = CC_INVALID;
111  r->priority = -1;
112  r->executable = -1;
113 
115  r->min_sample_num = -1;
116  r->min_sample_time = -1;
117  r->max_completion = -1;
118 
119  r->model = new_model();
120 
121  r->model->parent_routine = r;
122 
123  return r;
124 }
125 
126 /*!
127  * Create a new model structure. Note pmm_bench_list member will not yet be
128  * allocated.
129  *
130  * @return pointer to newly allocated pmm_model structure
131  */
133 {
134  struct pmm_model *m;
135 
136  m = malloc(sizeof *m);
137 
138  m->model_path = (void *)NULL;
139  m->unwritten_num_execs = 0;
140  m->unwritten_time_spend = 0.0;
141 
142  m->mtime = 0;
143 
144  m->n_p = -1;
145  m->completion = 0;
146  m->complete = 0;
147  m->unique_benches = 0;
148 
149  m->bench_list = (void *)NULL; // init when setting n_p
150 
152 
153  m->parent_routine = (void *)NULL;
154 
155  return m;
156 }
157 
158 /*!
159  * Create an empty benchmark list
160  *
161  * @param m pointer to the parent model
162  * @param n_p number of parameters of the benchmarks in the list
163  *
164  * @return pointer to newly allocated pmm_bench_list
165  */
166 struct pmm_bench_list*
168  int n_p)
169 {
170  struct pmm_bench_list *bl;
171 
172  bl = malloc(sizeof *bl);
173 
174  if(bl == NULL) {
175  ERRPRINTF("Error allocating memory.\n");
176  return NULL;
177  }
178 
179  bl->size = 0;
180  bl->n_p = n_p;
181  bl->first = NULL;
182  bl->last = NULL;
183  bl->parent_model = m;
184 
185  return bl;
186 }
187 
188 /*!
189  * Initialise the model benchmark list with zero speed benchmarks at relevant
190  * points. For each parameter definition if the end value of a parameter is
191  * defined as having a zero speed (i.e. it is not a 'nonzero_end'), then create
192  * zero speed benchmarks at the end point on that parameter axis, i.e. for
193  * parameter i create a zero speed benchmark at at:
194  *
195  * start_0,start_1,...,end_i,...,start_n-1,start_n.
196  *
197  * The, if any parameter has a zero speed end defined, assume we cannot
198  * ever benchmark at end_0,end_1,...,end_n and set a zero speed benchmark at
199  * this point also.
200  *
201  * @param m pointer to the model that the bench list belongs to
202  * @param pd_set pointer to the parameter definition set of the model
203  *
204  * @return 0 on success, -1 on failure
205  */
206 int
207 init_bench_list(struct pmm_model *m, struct pmm_paramdef_set *pd_set)
208 {
209  int i;
210  int ret;
211  int all_nonzero_end;
212  struct pmm_benchmark *b;
213 
214  m->bench_list = malloc(sizeof *(m->bench_list));
215  if(m->bench_list == NULL) {
216  ERRPRINTF("Error allocating memory.\n");
217  return -1; //failure;
218  }
219 
220  m->bench_list->size = 0;
221  m->bench_list->n_p = pd_set->n_p;
222 
223  m->bench_list->first = NULL;
224  m->bench_list->last = NULL;
225 
226  m->bench_list->parent_model = m;
227 
228  // check if any parameters are set to nonzero end (bench at this point
229  // instead of assuming it to have zero speed)
230  all_nonzero_end = 1;
231  for(i=0; i<pd_set->n_p; i++) {
232  if(pd_set->pd_array[i].nonzero_end != 1) {
233  b = new_benchmark();
234 
235  b->n_p = pd_set->n_p;
236 
237  b->p = init_param_array_start(pd_set);
238  if(b->p == NULL) {
239  ERRPRINTF("Error allocating memory.\n");
240  free_benchmark(&b);
241  return -1; //failure
242  }
243 
244  b->p[i] = pd_set->pd_array[i].end;
245 
246  b->flops = 0.;
247 
248  if(insert_bench_into_list(m->bench_list, b) < 0) {
249  ERRPRINTF("Error inserting end axial point into list.\n");
250  //free_benchmark(&b); not sure about freeing here ....
251  return -1;
252  }
253 
254  all_nonzero_end = 0;
255 
256  }
257  }
258 
259  // if not all parameters have nonzero_end set, then we should not benchmark
260  // at the end,end...,end point, so set a zero speed benchmark here.
261  if(all_nonzero_end != 1) {
262 
263  b = new_benchmark();
264 
265  b->n_p = pd_set->n_p;
266  b->p = init_param_array_end(pd_set);
267  if(b->p == NULL) {
268  ERRPRINTF("Error initialising end parameters.\n");
269  return -1;
270  }
271 
272  b->flops = 0.;
273 
274 
275  ret = insert_bench_into_list(m->bench_list, b);
276  if(ret < 0) {
277  ERRPRINTF("Error inserting initial bench into list.\n");
278  //free_benchmark(&b) not sure about freeing here ...
279  return -1;
280  }
281  }
282 
283  return 0; // success
284 }
285 
286 
287 
288 /*!
289  * Zero data-fields of a benchmark structure, leaving the parameter array p and
290  * the next and previous pointers untouched
291  *
292  * @param b pointer to benchmark to zero
293  *
294  * @pre b is non-NULL
295  */
296 void
298 {
299  b->complexity = 0;
300  b->flops = 0;
301  b->seconds = 0;
302 
303  b->wall_t.tv_sec = 0;
304  b->wall_t.tv_usec = 0;
305 
306  b->used_t.tv_sec = 0;
307  b->used_t.tv_usec = 0;
308 
309  return;
310 }
311 
312 /*!
313  * Allocate and return a benchmark structure with values set to invalids,
314  * with the parameter array p still unallocated and next/previous pointers
315  * set to NULL
316  *
317  * @return pointer to a newly allocated benchmark structure
318  */
319 struct pmm_benchmark*
321 {
322  struct pmm_benchmark *b;
323 
324  b = malloc(sizeof *b);
325 
326  b->n_p = -1;
327  b->p = (void *)NULL; //init when assigning parameters
328 
329  b->complexity = -1;
330  b->flops = -1;
331  b->seconds = -1;
332 
333  b->wall_t.tv_sec = -1;
334  b->wall_t.tv_usec = -1;
335 
336  b->used_t.tv_sec = -1;
337  b->used_t.tv_usec = -1;
338 
339  b->next = (void *)NULL; //set when inserting into model
340  b->previous = (void *)NULL;
341 
342  return b;
343 }
344 
345 /*!
346  * Initialize and return a benchmark structure with zero speed in a position
347  * described by the parameter array argument (params).
348  *
349  * @param params pointer to parameter array describing benchmark position
350  * @param n_p number of parameters
351  *
352  * @return pointer to a newly allocated benchmark structure with zero speed in
353  * given position
354  */
355 struct pmm_benchmark*
356 init_zero_benchmark(int *params, int n_p)
357 {
358  struct pmm_benchmark *b;
359 
360  b = new_benchmark();
361 
362  if(b == NULL) {
363  ERRPRINTF("Error allocating new zero speed benchmark.\n");
364  return NULL;
365  }
366 
367  b->n_p = n_p;
368  b->p = init_param_array_copy(params, n_p);
369  if(b->p == NULL) {
370  ERRPRINTF("Error copy parameter array.\n");
371 
372  free_benchmark(&b);
373 
374  return NULL;
375  }
376 
377  b->flops = 0.;
378 
379  return b;
380 }
381 
382 /*!
383  * This function adds a pmm_routine structure to the routines array of
384  * the pmm_config structure. The array is reallocated as required, adding
385  * space for 5 routines at a time to prevent frequent reallocation.
386  *
387  * @param c pointer to the config structure
388  * @param r pointer to the routine
389  *
390  * @return 0 on success, -1 on failure
391  */
392 int
393 add_routine(struct pmm_config *c, struct pmm_routine *r) {
394  // if we still have space in the routines array
395  if(c->used < c->allocated) {
396 
397  // add routine
398  c->routines[c->used] = r;
399  c->used++;
400  }
401  // otherwise ...
402  else {
403  // reallocate the routines array
404  c->allocated+=5;
405  c->routines = realloc(c->routines,
406  sizeof(struct pmm_routine*)*c->allocated);
407  if(c->routines == NULL) {
408  ERRPRINTF("Reallocation of memory to routine array failed.\n");
409  return -1;
410  }
411 
412  // and add the routine
413  c->routines[c->used] = r;
414  c->used++;
415  }
416 
417  r->parent_config = c;
418 
419  return 0;
420 }
421 
422 
423 /*!
424  * Insert benchmark between two benchmarks in a sorted list
425  *
426  * @param list_start pointer to a reference for the beginning of the list
427  * @param list_end pointer to a reference for the end of the list
428  * @param b pointer to the benchmark to insert
429  *
430  * @return 0 on success if benchmark was not unique to the list, 1 on success if
431  * the benchmark was unique to the list, -1 on failure
432  */
433 int
435  struct pmm_benchmark **list_end,
436  struct pmm_benchmark *b)
437 {
438  int done;
439  int ret;
440  int unique = 0;
441  struct pmm_benchmark *this;
442 
443  done = 0;
444  this = *list_start;
445 
446  while(this != NULL) {
447  if((ret = params_cmp(b->p, this->p, b->n_p)) <= 0) {
448  if(insert_bench_into_sorted_list_before(list_start, list_end,
449  this, b) < 0)
450  {
451  ERRPRINTF("Error inserting bench into list.\n");
452  return -1;
453  }
454 
455  // if params_cmp did not match the new benchmark, set unique flag
456  if(ret != 0)
457  unique = 1;
458 
459  done = 1;
460  break;
461  }
462 
463  this = this->next;
464  }
465 
466  if(done != 1) {
467  if(insert_bench_into_sorted_list_after(list_start, list_end,*list_end,b)
468  < 0)
469  {
470  ERRPRINTF("Error inserting bench into list.\n");
471  return -1;
472  }
473 
474  // this is a new bench so set unqiue
475  unique = 1;
476  }
477 
478  return unique;
479 }
480 
481 /*!
482  * @return 0 on success, -1 on failure
483  */
484 int
486  struct pmm_benchmark *b)
487 {
488  int ret;
489 
490  if((ret = insert_bench_into_sorted_list(&(bl->first), &(bl->last), b)) < 0)
491  {
494  ERRPRINTF("Error inserting bench into list.\n");
495  return -1;
496  }
497 
498  // in any case, if we reach this point, a benchmark has been inserted
499  bl->size++;
500  bl->parent_model->completion++;
501  bl->parent_model->unique_benches += ret;
502 
503  return 0;
504 
505 }
506 
507 
508 /*!
509  * Add benchmark to a model in the appropriate substructure of
510  * the model
511  *
512  * @param m pointer to the model
513  * @param b pointer to the benchmark
514  *
515  * @post parameter b becomes part of the model and must not be freed until
516  * the model itself is freed or it is removed from the model explicitly
517  *
518  * @return 0 on success, -1 on failure
519  */
520 int
521 insert_bench(struct pmm_model *m, struct pmm_benchmark *b)
522 {
523  int ret;
524 
525  ret = insert_bench_into_list(m->bench_list, b);
526  if(ret < 0) {
527  ERRPRINTF("Error inserting bench in bench list\n");
528  return ret;
529  }
530 
531  return ret;
532 }
533 
534 
535 /*!
536  *
537  * test if a benchmark is at the starting point of a model (not 0,0)
538  *
539  * @param n number of parameters defintions
540  * @param pd_array pointer to parameter defintions array
541  * @param b pointer to benchmark
542  *
543  * @return 0 if benchmark is not at the origin of the model (all start values)
544  * @return 1 if benchmark is at the origin of the model
545  * @return -1 if there is a mismatch between number of parameters
546  */
547 int is_benchmark_at_origin(int n, struct pmm_paramdef *pd_array,
548  struct pmm_benchmark *b)
549 {
550  int i;
551 
552  if(n!=b->n_p) {
553  ERRPRINTF("paramdef array and benchmark param array size mismatch\n");
554  return -1;
555  }
556 
557  for(i=0; i<n; i++) {
558  LOGPRINTF("b->p[%d]:%d paramdef[%d].start:%d\n", i, b->p[i], i,
559  pd_array[i].start);
560  if(b->p[i] != pd_array[i].start) {
561  return 0;
562  }
563  }
564 
565  return 1; // all bench parameters are start, bench is at origin
566 }
567 
568 /*!
569  * An empty model is defined as one that has no experimentally obtained
570  * benchmark points in it. Thus:
571  *
572  * The bench list may only have: points at the end parameter values on each
573  * parameter axis, e.g. [0,0,end] for the 3rd axis of a 3 parameter
574  * model, and a single point at the end parameter value of all boundaries,
575  * i.e. [end,end,end] of a 3 parameter model
576  *
577  * This is slightly complicated to test however, another property of non-
578  * experimental points in the model is that they all have zero speed. Just test
579  * that!
580  *
581  * @param m pointer to the model to test
582  *
583  * @return 0 if model is not empty, 1 if model is empty
584  */
585 int
587 {
588  struct pmm_benchmark *b;
589 
590 
591  b = m->bench_list->first;
592  if(b == NULL) {
593  ERRPRINTF("Mesh model not initialized.\n");
594  }
595 
596  while(b != NULL) {
597  if(b->flops != 0.0) {
598  return 0;
599  }
600  b=b->next;
601  }
602 
603  //list is empty of experimental points
604  return 1;
605 }
606 
607 /*!
608  * count the number of benchmarks in a model
609  *
610  * @param m pointer to model
611  * @return number of benchmarks
612  */
613 int
615 {
616  int c = 0; //counter
617 
619 
620  return c;
621 }
622 
623 
624 /*!
625  * Count the number of benchmarks in a list (this manually counts, does not
626  * return the size variable of the list).
627  *
628  * @param bl pointer to the bench list to count
629  *
630  * @return number of benchmarks in the list
631  */
632 int
634 {
635  int c = 0; //counter
636  struct pmm_benchmark *b;
637 
638  b = bl->first;
639  while(b != NULL) {
640  c++;
641  b = b->next;
642  }
643 
644  LOGPRINTF("c:%d\n", c);
645  return c;
646 }
647 
648 /*!
649  * Count the number of unique benchmark points in a sorted list of benchmarks.
650  *
651  * @param first pointer the first benchmark in the list
652  *
653  * @return number of unqiue benchmarks in the list
654  */
655 int
657 {
658  int count;
659  struct pmm_benchmark *b;
660 
661  count = 0;
662  b = first;
663 
664  while(b != NULL) {
665  count++;
667  }
668 
669  return count;
670 }
671 
672 /*!
673  * Test if a benchmark is on one of the parameter axes of the model (i.e. all
674  * but one parameter is at a start),
675  *
676  * @param m pointer to the model the benchmark belongs to
677  * @param b pointer to the benchmark
678  *
679  * @return the plane index of the axis the benchmark belongs to or -1 if
680  * on all boundaries (i.e. at origin), or -2 if on no boundaries
681  */
682 int
684  struct pmm_benchmark *b)
685 {
686  return param_on_axis(b->p, b->n_p, m->parent_routine->pd_set->pd_array);
687 }
688 
689 /*!
690  * Copy a benchmark from one pointer to another. next and previous elments
691  * of the benchmark structure are not copied however. The copy exists outside
692  * of any benchmark list that the source might belong to.
693  *
694  * @param dst pointer to destination benchmark
695  * @param src pointer to source benchmark
696  *
697  * @return -1 on failure, 0 on success
698  *
699  * @pre dst structure is non NULL, but it's p array element is NULL
700  *
701  */
702 int copy_benchmark(struct pmm_benchmark *dst, struct pmm_benchmark *src)
703 {
704 
705  dst->n_p = src->n_p;
706 
707  dst->p = init_param_array_copy(src->p, src->n_p);
708  if(dst->p == NULL) {
709  ERRPRINTF("Error copying parameter array.\n");
710  return -1;
711  }
712 
713  dst->complexity = src->complexity;
714  dst->flops = src->flops;
715  dst->seconds = src->seconds;
716 
717  //copy from -> to
718  copy_timeval(&dst->wall_t, &src->wall_t);
719  copy_timeval(&dst->used_t, &src->used_t);
720 
721  dst->next = (void *)NULL;
722  dst->previous = (void *)NULL;
723 
724  return 0;
725 }
726 
727 
728 /*!
729  * Insert benchmark into a sorted list in a position directly before a
730  * certain benchmark.
731  *
732  * @param list_first pointer to the first benchmark in the list
733  * @param list_last pointer to the last benchmark in the list
734  * @param before pointer to the benchmark before which we will insert
735  * @param b pointer to the benchmark we will insert
736  *
737  * @pre the benchmark b has parameter value lower than the benchmark 'before'
738  * @pre after is NULL pointer only if list is empty
739  *
740  * @return 0 on success, -1 on failure
741  */
742 int
744  struct pmm_benchmark **list_last,
745  struct pmm_benchmark *before,
746  struct pmm_benchmark *b)
747 {
748  //if before is NULL then we may have an empty list
749  if(before == NULL) {
750 
751  if(*list_first == NULL && *list_last == NULL) { //if empty list
752  b->previous = (void *)NULL;
753  b->next = (void *)NULL;
754 
755  *list_first = b;
756  *list_last = b;
757 
758  }
759  else { //else error, shouldn't have been passed a NULL benchmark
760  ERRPRINTF("Insertion before NULL benchmark in non-empty list.\n");
761  return -1;
762  }
763  }
764  else if(before == *list_first) { //if inserting before first element in list
765  b->previous = (void *)NULL;
766  b->next = before;
767  before->previous = b;
768  *list_first = b;
769 
770  }
771  else { //inserted midway through the list
772  b->previous = before->previous;
773  b->next = before;
774  before->previous->next = b;
775  before->previous = b;
776 
777  }
778 
779  return 0;
780 }
781 
782 /*!
783  * Insert benchmark into a sorted list in a position directly after a
784  * certain benchmark.
785  *
786  * @param list_first pointer to the first benchmark in the list
787  * @param list_last pointer to the last benchmark in the list
788  * @param after pointer to the benchmark after which we will insert
789  * @param b pointer to the benchmark we will insert
790  *
791  * @pre the benchmark b has parameter value higher than the benchmark 'after'
792  * @pre after is NULL pointer only if list is empty
793  *
794  * @return 0 on success, -1 on failure
795  */
796 int
798  struct pmm_benchmark **list_last,
799  struct pmm_benchmark *after,
800  struct pmm_benchmark *b)
801 {
802  //if after is NULL then we may have an empty list
803  if(after == NULL) {
804 
805  if(*list_first == NULL && *list_last == NULL) { //if empty list
806  b->previous = (void *)NULL;
807  b->next = (void *)NULL;
808 
809  *list_first = b;
810  *list_last = b;
811 
812  }
813  else { //else error, shouldn't have been passed a NULL benchmark
814  ERRPRINTF("Insertion after NULL benchmark in non-empty list.\n");
815  return -1;
816  }
817  }
818  else if(after == *list_last) { //if inserting after last element in list
819  b->previous = after;
820  b->next = (void *)NULL;
821  after->next = b;
822  *list_last = b;
823 
824  }
825  else { //inserted midway through the list
826  b->previous = after;
827  b->next = after->next;
828  after->next->previous = b;
829  after->next = b;
830  }
831 
832  return 0;
833 }
834 
835 /*!
836  * Remove a benchmark from a sorted benchmark list
837  *
838  * @param list_first pointer to the first bench in the list
839  * @param list_last pointer to the last bench in the list
840  * @param b pointer to the benchmark to remove from the list
841  *
842  * @return 0 on success, -1 on failure
843  *
844  * @pre b is part of the list
845  * @post b is removed from the list but still allocated (having next/previous
846  * pointers set to NULL
847  */
848 int
850  struct pmm_benchmark **list_last,
851  struct pmm_benchmark *b)
852 {
853 
854  if(b == *list_first && b == *list_last) { //list only has one point
855  if(b->next != NULL || b->previous != NULL) {
856  ERRPRINTF("benchmark is both first and last of list, but has "
857  "non-NULL next / previous pointers.\n");
858  return -1;
859  }
860 
861  *list_first = NULL;
862  *list_last = NULL;
863 
864  }
865  else if(b == *list_first) { //b is first in the list
866  if(b->next != NULL) {
867  ERRPRINTF("benchmark is first in list but has non-NULL next "
868  "pointer.\n");
869  return -1;
870  }
871 
872  *list_first = b->previous;
873 
874  if(b->previous != NULL) {
875  b->previous->next = NULL;
876  b->previous = NULL;
877  }
878  }
879  else if(b == *list_last) { //b is last in list
880  if(b->previous != NULL) {
881  ERRPRINTF("benchmark is last in list but has non-NULL previous"
882  "pointer.\n");
883  return -1;
884  }
885 
886  *list_last = b->next;
887 
888  if(b->next != NULL) {
889  b->next->previous = NULL;
890  b->next = NULL;
891  }
892  }
893  else { //b is at some point in the middle of the list
894  if(b->next == NULL || b->previous == NULL) {
895  ERRPRINTF("benchmark not at start / end of list but has NULL next "
896  "/ previous pointers.\n");
897  return -1;
898  }
899 
900  b->next->previous = b->previous;
901  b->previous->next = b->next;
902  b->next = NULL;
903  b->previous = NULL;
904  }
905 
906  return 0; //success
907 }
908 
909 /*!
910  * Remove a benchmark from a bench list
911  *
912  * @param bl pointer to the bench list
913  * @param b pointer to the benchmark to remove from the model
914  *
915  * @return 0 on success, -1 on failure
916  *
917  * @pre b is part of the bench list
918  * @post b is removed from the list but still allocated (having next/previous
919  * pointers set to NULL
920  */
921 int
923  struct pmm_benchmark *b)
924 {
925 
926  if(remove_bench_from_sorted_list(&(bl->first), &(bl->last), b) < 0) {
927  ERRPRINTF("Error removing benchmark from bench list.\n");
928  return -1;
929  }
930 
931  bl->size--;
932  bl->parent_model->completion--;
933 
934  return 0; //success
935 }
936 
937 
938 
939 /*
940  * Takes a benchmark structure with flops and size members set and uses them to
941  * calculate and set the execution time timeval used_t member of the benchmark
942  *
943  * @param[in,out] b pointer to benchmark structure
944  *
945  * @return 0 if benchmark does not have valid data or 1 on success
946  */
947 /*
948 int
949 flops_to_time_t(struct pmm_benchmark *b) {
950  double d_secs;
951  if(b->flops <= 0 || b->size < 0) {
952  return 0;
953  }
954 
955  d_secs = b->size/b->flops;
956 
957  b->used_t.tv_sec = floor(d_secs);
958 
959  b->used_t.tv_usec = floor(1000*(d_secs - b->used_t.tv_sec));
960  return 0;
961 }
962 */
963 
964 
965 //TODO make some consistency where functions that allocate and return memory do
966 //TODO so by setting a pointer that is passed into the function as a parameter.
967 //TODO Functions that do not allocate, but do return pointers do so by simply
968 //TODO returning the pointer, not by setting a parameter passed into the
969 //TODO function.
970 //
971 
972 
973 #define SEARCH_FORWARDS 0
974 #define SEARCH_BACKWARDS 1
975 
976 /*!
977  * Searches a sorted bench list for the fastest instance of a benchmark with
978  * matching parameters and returns a pointer to that benchmark
979  *
980  * @param start pointer to start bench to commence search
981  * @param param the parameter array to search for
982  *
983  * @return pointer to the fastest benchmark in the list, in place, or NULL on
984  * error
985  *
986  * @pre start must point to a benchmark that is positioned in the sorted list
987  * before, or as the very first instance of a benchmark matching the target
988  * parameters, otherwise the max returned may not be correct
989  *
990  */
991 struct pmm_benchmark*
993  int *param)
994 {
995  int done;
996  struct pmm_benchmark *first_match, *last_match, *ret_b, *tmp_b;
997 
998  search_sorted_bench_list(SEARCH_FORWARDS, start, param, start->n_p,
999  &first_match, &last_match);
1000 
1001  if(first_match == NULL) { //no match was found
1002  return NULL;
1003  }
1004  else if(last_match == NULL) { //only one match was found
1005  ret_b = first_match;
1006 
1007  return ret_b;
1008  }
1009  else { //multiple matches were found ranging from first to last
1010  ret_b = first_match;
1011  tmp_b = first_match->next;
1012 
1013  done = 0;
1014  while(!done) {
1015  if(tmp_b->flops > ret_b->flops) {
1016  ret_b = tmp_b;
1017  }
1018  if(tmp_b == last_match) {
1019  done = 1;
1020  }
1021  tmp_b = tmp_b->next;
1022  }
1023 
1024  return ret_b;
1025  }
1026 }
1027 
1028 /*!
1029  * Searches a sorted bench list for multiple instances of a benchmark with
1030  * matching parameters and returns a newly allocated benchmark that represents
1031  * an average of the search hits.
1032  *
1033  * @param start pointer to start bench to commence search
1034  * @param param the parameter array to search for
1035  *
1036  * @return pointer to a newly allocated benchmark that is an average of any
1037  * benchmarks found in the boundary model with matching parameters or
1038  * NULL if no benchmark with matching parameters is found
1039  *
1040  * @pre start must point to a benchmark that is positioned in the sorted list
1041  * before, or as the very first instance of a benchmark matching the target
1042  * parameters, otherwise the average returned will not be accurate
1043  *
1044  */
1045 struct pmm_benchmark*
1047  int *param)
1048 {
1049  struct pmm_benchmark *first_match, *last_match, *ret_b;
1050 
1051  search_sorted_bench_list(SEARCH_FORWARDS, start, param, start->n_p,
1052  &first_match, &last_match);
1053 
1054  if(first_match == NULL) { //no match was found
1055  return NULL;
1056  }
1057  else if(last_match == NULL) { //only one match was found
1058  ret_b = new_benchmark();
1059  copy_benchmark(ret_b, first_match);
1060 
1061  return ret_b;
1062  }
1063  else { //multiple matches were found ranging from first to last
1064  ret_b = new_benchmark();
1065  ret_b->n_p = first_match->n_p;
1066 
1067  ret_b->p = init_param_array_copy(param, ret_b->n_p);
1068  if(ret_b->p == NULL) {
1069  ERRPRINTF("Error copying parameter array.\n");
1070  return NULL;
1071  //TODO this error is not caught as null is not spec'd as error
1072  }
1073 
1074  ret_b->complexity = first_match->complexity;
1075 
1076  avg_bench_list(first_match, last_match, ret_b);
1077  return ret_b;
1078  }
1079 }
1080 
1081 /*!
1082  * Finds the first and last positions of a sequence of benchmarks with matching
1083  * parameters in a sorted benchmark list
1084  *
1085  * @param bl pointer to the bench listl to search
1086  * @param param the parameters to search for
1087  * @param first pointer to the first matching benchmark
1088  * @param last pointer to the last matching benchmark
1089  *
1090  * @return number of benchmarks found
1091  *
1092  * @pre parameter array param has the same number of parameters as the
1093  * model and the bench list must be sorted
1094  *
1095  * @post first and last are NULL if no matching benchmark is found, first is
1096  * non-NULL and last is NULL if only one matching benchmark is found, finally
1097  * first and last are non-NULL if a sequence of matching benchmarks are found
1098  */
1099 int
1101  int *param,
1102  struct pmm_benchmark **first,
1103  struct pmm_benchmark **last)
1104 {
1105 
1106  return search_sorted_bench_list(SEARCH_FORWARDS, bl->first, param, bl->n_p,
1107  first, last);
1108 
1109 }
1110 
1111 /*!
1112  * Finds the first and last positions of a sequence of benchmarks with matching
1113  * parameters that are part of a sorted benchmark list, search from start
1114  *
1115  * @param direction use SEARCH_[BACK|FOR]WARDS macros
1116  * @param start pointer to search start point in the list
1117  * @param param pointer to array of parameters to search for
1118  * @param n_p the number of parameters in the param array
1119  * @param first pointer to first matching bench in the model
1120  * @param last pointer to last matching bench in the model
1121  *
1122  * @return number of benchmarks found or <0 on error
1123  *
1124  * @pre parameter array param has the same number of parameters as the
1125  * benchmarks in the list and the list is sorted properly
1126  * @pre benchmark list mst be sorted
1127  *
1128  * @post first and last are NULL if no matching benchmark is found, first is
1129  * non-NULL and last is NULL if only one matching benchmark is found, finally
1130  * first and last are non-NULL if a sequence of matching benchmarks are found.
1131  * They do not depend on the search direction, i.e. first and last are not
1132  * reordered if the search direction is reversed, they will always be presented
1133  * in the order that occurs in the model.
1134  *
1135  */
1136 int
1138  struct pmm_benchmark *start,
1139  int *param,
1140  int n_p,
1141  struct pmm_benchmark **first,
1142  struct pmm_benchmark **last)
1143 {
1144  struct pmm_benchmark *b;
1145  int count;
1146 
1147  count = 0;
1148  *first = NULL;
1149  *last = NULL;
1150 
1151  if(direction != SEARCH_FORWARDS && direction != SEARCH_BACKWARDS) {
1152  ERRPRINTF("Direction of search not set correctly.\n");
1153  return -1;
1154  }
1155 
1156  b = start;
1157 
1158  switch (direction) {
1159  case SEARCH_FORWARDS:
1160 
1161  while(b != NULL) {
1162  if(*first == NULL) {
1163 
1164  if( params_cmp(b->p, param, n_p) == 0) {
1165  *first = b;
1166  count++;
1167  }
1168  }
1169  else { //first match is found
1170 
1171  //if we still find matching benches, note the position
1172  if(params_cmp(b->p, param, n_p) == 0) {
1173  *last = b;
1174  count++;
1175  }
1176  else { //else we are finished
1177  break;
1178  }
1179  }
1180 
1181  b=b->next;
1182  }
1183 
1184  break;
1185 
1186  case SEARCH_BACKWARDS:
1187 
1188  while(b != NULL) {
1189  if(*last == NULL) {
1190 
1191  if( params_cmp(b->p, param, n_p) == 0) {
1192  *last = b;
1193  count++;
1194  }
1195  }
1196  else { //first match is found
1197 
1198  //if we still find matching benches, note the position
1199  if(params_cmp(b->p, param, n_p) == 0) {
1200  *first = b;
1201  count++;
1202  }
1203  else { //else we are finished
1204 
1205  // if we only found one bench first will not be
1206  // set to anything. The function is specified to
1207  // return the list segment without search direction
1208  // specificity ... so swap first and last
1209  if(*first == NULL) {
1210  *first = *last;
1211  *last = NULL;
1212  }
1213 
1214  break;
1215  }
1216  }
1217 
1218  b=b->previous;
1219  }
1220 
1221  break;
1222 
1223  default:
1224  break;
1225  //never
1226  }
1227 
1228 
1229  return count;
1230 }
1231 
1232 /*!
1233  * find the next benchmark in a sorted list that does not have identical
1234  * parameters to b. As models may have more than one benchmark for the same
1235  * point, looking at the ->next element of a benchmark may give us a second
1236  * benchmark at the exact same point. Hence the need for this function
1237  *
1238  * @param b pointer to a benchmark that is in a sorted list
1239  *
1240  * @return pointer to the next benchmark in the list (that b belongs to), which
1241  * has a different set of parameters associated with it. This may be null if
1242  * the benchmark b is the last benchmark in the list
1243  */
1244 struct pmm_benchmark*
1246 {
1247  struct pmm_benchmark *next;
1248  struct pmm_benchmark *first, *last;
1249 
1250  //search the bench list for all benchmarks with params of b, starting
1251  //the search at b, using the result we know where the next not matching
1252  //bench will be
1253  search_sorted_bench_list(SEARCH_FORWARDS, b, b->p, b->n_p, &first, &last);
1254 
1255  if(last == NULL) { //there is only 1 matching benchmark in the list
1256 
1257  if(first != NULL) {
1258  next = first->next;
1259  }
1260  else { //this really should never happen
1261  ERRPRINTF("Could not find b when searching itself.\n");
1263  exit(EXIT_FAILURE);
1264  next = NULL;
1265  }
1266  }
1267  else {
1268  next = last->next;
1269  }
1270 
1271 
1272  return next;
1273 }
1274 
1275 /*!
1276  * find the previous benchmark in a sorted list that does not have identical
1277  * parameters to b. As models may have more than one benchmark for the same
1278  * point, looking at the ->previous element of a benchmark may give us a second
1279  * benchmark at the exact same point. Hence the need for this function
1280  *
1281  * @param b pointer to a benchmark that is in a sorted list
1282  *
1283  * @return pointer to the previous benchmark in the list (that b belongs to),
1284  * which has a different set of parameters associated with it. This may be NULL
1285  * if the benchmark b is the first benchmark in the list.
1286  */
1287 struct pmm_benchmark*
1289 {
1290  struct pmm_benchmark *previous;
1291  struct pmm_benchmark *first, *last;
1292 
1293  //search the bench list for all benchmarks with params of b, starting
1294  //the search at b for expediency and looking backwards
1295  search_sorted_bench_list(SEARCH_BACKWARDS, b, b->p, b->n_p, &first, &last);
1296 
1297  //previous different benchmark to b will be before the first in the list of
1298  //benchmarks matching b
1299  if(first != NULL) {
1300  previous = first->previous;
1301  }
1302  else { //this should never happen
1303  ERRPRINTF("Could not find b when searching itself!\n");
1305  exit(EXIT_FAILURE);
1306  previous = NULL;
1307  }
1308 
1309  return previous;
1310 }
1311 /*!
1312  * Averages the flops and execution times of a sequence of benchmarks and
1313  * copies this information into a benchmark structure pointed to by b
1314  *
1315  * @param start pointer to the first benchmark in the sequence
1316  * @param end pointer to the last benchmark in the sequence
1317  * @param avg_b pointer to the benchmark where the average is to be stored
1318  *
1319  * @pre benchmarks in the sequence defined by start & end are all at the same
1320  * point, i.e. the values in their p array are identical
1321  * @pre b is a non-NULL allocated structure
1322  */
1323 void
1324 avg_bench_list(struct pmm_benchmark *start, struct pmm_benchmark *end,
1325  struct pmm_benchmark *avg_b)
1326 {
1327  double divisor = 0.0;
1328 
1329  zero_benchmark(avg_b); //zero ret_b benchmark data
1330 
1331  divisor = (double)sum_bench_list(start, end, avg_b);
1332 
1333  div_bench(avg_b, divisor, avg_b);
1334 
1335  return;
1336 }
1337 
1338 /*!
1339  * Summates the data of benchmarks in a list marked by start and end into
1340  * an empty benchmark sum
1341  *
1342  * @param start pointer to the first benchmark in the list of benchmarks to
1343  * be summed
1344  * @param end pointer to the last benchmark in thet list of benchmarks to
1345  * be summed
1346  * @param sum pointer to the benchmark where the summed data is to
1347  * be stored
1348  *
1349  * @return the number of benchmarks added to the sum as an integer
1350  *
1351  * @pre all benchmarks are allocated and non NULL. sum is initialized with
1352  * zero data values
1353  */
1354 int
1355 sum_bench_list(struct pmm_benchmark *start, struct pmm_benchmark *end,
1356  struct pmm_benchmark *sum)
1357 {
1358  struct pmm_benchmark *this;
1359  int counter = 0;
1360  int done = 0;
1361 
1362  this = start;
1363 
1364  while(!done) {
1365 
1366  add_bench(sum, this, sum);
1367 
1368  counter++;
1369 
1370  if(this == end) {
1371  done = 1;
1372  }
1373  this = this->next;
1374  }
1375 
1376  return counter;
1377 }
1378 
1379 /*!
1380  * Adds performance data of one benchmark to another (a+b) storing result in
1381  * res. Parameters added are: flops, seconds, wall_t, used_t.
1382  *
1383  * @param a pointer to first benchmark to add
1384  * @param b pointer to second benchmark to add
1385  * @param res pointer to benchmark where result of addition is stored
1386  *
1387  * @pre all benchmarks are non-NULL
1388  * @pre all benchmarks have non-performance data identical (param array, etc).
1389  */
1390 void
1392  struct pmm_benchmark *res)
1393 {
1394  res->flops = a->flops + b->flops;
1395  res->seconds = a->seconds + b->seconds;
1396 
1397  timeval_add(&(a->used_t), &(b->used_t), &(res->used_t));
1398  timeval_add(&(a->wall_t), &(b->wall_t), &(res->wall_t));
1399 
1400  return;
1401 }
1402 
1403 
1404 /*!
1405  * Divides a the performance data fields of a benchmark by some float. Relevant
1406  * parameters are flops, seconds, wall_t and used_t.
1407  *
1408  * @param b pointer to the benchmark to be divided
1409  * @param d the devisor as a double
1410  * @param res pointer to the location to store the result
1411  *
1412  * @pre all benchmarks are non-NULL
1413  * @pre divisor is non-zero and positive
1414  * @pre all benchmarks have identical non-performance data (param array, etc).
1415  */
1416 void
1417 div_bench(struct pmm_benchmark *b, double d, struct pmm_benchmark *res)
1418 {
1419 
1420  if(d == 0.0) {
1421  ERRPRINTF("Divide by zero error.\n");
1422  return;
1423  }
1424  else if(d < 0.0) {
1425  ERRPRINTF("Divide by negative unsupported.\n");
1426  return;
1427  }
1428  else if(d == 1.0) {
1429  return; // nothing to do
1430  }
1431 
1432  res->flops = b->flops/d;
1433  res->seconds = b->seconds/d;
1434 
1435  //only copy the timeval if the result is not self assigned
1436  //(i.e. when it is not not b = b/d)
1437  if(b != res) {
1438  //copy from b -> to res
1439  copy_timeval(&(res->wall_t), &(b->wall_t));
1440  copy_timeval(&(res->used_t), &(b->used_t));
1441  }
1442 
1443  timeval_div(&(res->wall_t), d);
1444  timeval_div(&(res->used_t), d);
1445 
1446  return;
1447 }
1448 
1449 /*!
1450  * Copies timeval structures
1451  *
1452  * @param dst pointer to the timeval to copy to
1453  * @param src pointer to the timeval to copy from
1454  *
1455  * @pre src and dst are non-NULL
1456  *
1457  */
1458 void
1459 copy_timeval(struct timeval *dst, struct timeval *src)
1460 {
1461  dst->tv_sec = src->tv_sec;
1462  dst->tv_usec = src->tv_usec;
1463 }
1464 
1465 /*!
1466  * Converts timeval to a double in seconds units
1467  *
1468  * @param tv pointer to a timeval structure
1469  *
1470  * @return value of the timeval in seconds as a double
1471  */
1472 double
1473 timeval_to_double(struct timeval *tv)
1474 {
1475  return tv->tv_sec + tv->tv_usec/1000000.0;
1476 }
1477 
1478 /*!
1479  *
1480  * Converts a double to a timeval
1481  *
1482  * @param d double value to convert from
1483  * @param tv pointer to timeval
1484  */
1485 void
1486 double_to_timeval(double d, struct timeval *tv)
1487 {
1488  tv->tv_sec = (int)d;
1489  tv->tv_usec = (int)(100000.0* (d - tv->tv_sec));
1490 
1491  return;
1492 }
1493 
1494 /*!
1495  * Convert a double to a timespec
1496  *
1497  * @param d double value to convert
1498  * @param ts pointer to timespec
1499  */
1500 void
1501 double_to_timespec(double d, struct timespec *ts)
1502 {
1503  ts->tv_sec = (int)d;
1504  ts->tv_nsec = (long)(1000000000.0 * (d - ts->tv_sec));
1505 
1506  return;
1507 }
1508 
1509 /*!
1510  * Add two timevals, to another, tv_res = tv_a + tv_b
1511  *
1512  * @param tv_a first timeval to add
1513  * @param tv_b second timeval to add
1514  * @param tv_res pointer to timeval to store result
1515  */
1516 void
1517 timeval_add(struct timeval *tv_a, struct timeval *tv_b, struct timeval *tv_res)
1518 {
1519  tv_res->tv_sec = tv_a->tv_sec + tv_b->tv_sec;
1520  tv_res->tv_usec = tv_a->tv_usec + tv_b->tv_usec;
1521  if(tv_res->tv_usec >= 1000000) {
1522  tv_res->tv_sec++;
1523  tv_res->tv_usec -= 1000000;
1524  }
1525 
1526  return;
1527 }
1528 
1529 /*!
1530  * Divide timeval by a double
1531  *
1532  * @param tv the timeval
1533  * @param d the divisor as a double
1534  *
1535  * @pre divisor should be non zero
1536  */
1537 void
1538 timeval_div(struct timeval *tv, double d)
1539 {
1540  double result_d;
1541 
1542  if(d == 0) {
1543  ERRPRINTF("Divide by zero error.\n");
1544  return;
1545  }
1546  if(d == 1.0) {
1547  return;
1548  }
1549 
1550  result_d = timeval_to_double(tv) / d;
1551 
1552  double_to_timeval(result_d, tv);
1553 }
1554 
1555 /*!
1556  * Compare two timevals
1557  *
1558  * @param a pointer to first timeval
1559  * @param b pointer to second timeval
1560  *
1561  * @return -1 if a < b, 0 if a = b and 1 if a > b
1562  */
1563 int
1564 timeval_cmp(struct timeval *a, struct timeval *b)
1565 {
1566  if (a->tv_sec < b->tv_sec) {
1567  return -1;
1568  }
1569  else if (a->tv_sec > b->tv_sec) {
1570  return 1;
1571  }
1572  else if (a->tv_usec < b->tv_usec) { // tv_sec equal now compare tv_usec
1573  return -1;
1574  }
1575  else if (a->tv_usec > b->tv_usec) {
1576  return 1;
1577  }
1578 
1579  return 0;
1580 }
1581 
1582 /*!
1583  * Searches a benchmark list for the first occurance of a benchmark and returns
1584  * a pointer to that benchmark or NULL if no benchmark is found
1585  *
1586  * @param bl bench list to search
1587  * @param p the parameter array to search for
1588  *
1589  * @return pointer to the first occurance of a benchmark with matching
1590  * parameters or NULL if no such benchmark is found
1591  *
1592  * @pre the number of parameters of the param array and the list model are equal
1593  *
1594  */
1595 struct pmm_benchmark*
1597  int *p)
1598 {
1599  struct pmm_benchmark *b;
1600 
1601  b = bl->first;
1602 
1603  while(b != NULL) {
1604  if(params_cmp(b->p, p, bl->n_p == 0)) {
1605  return b;
1606  }
1607  b = b->next;
1608  }
1609 
1610  return b; //b should be NULL at this point
1611 
1612 }
1613 
1614 /*!
1615  * test if the data elements of benchmarks are equal (i.e. everything but the
1616  * pointers)
1617  *
1618  * @param b1 pointer to first benchmark
1619  * @param b2 pointer to second benchmark
1620  *
1621  * @return 0 if benchmarks are not equal, 1 if benchmarks are equal
1622  */
1623 int
1625 {
1626  if(b1 == NULL || b2 == NULL) {
1627  return 0;
1628  }
1629 
1630  if(b1->n_p != b2->n_p) {
1631  return 0;
1632  }
1633 
1634  if(params_cmp(b1->p, b2->p, b1->n_p) != 0) { //if params are not equal
1635  return 0;
1636  }
1637 
1638  if(b1->complexity != b2->complexity ||
1639  b1->flops != b2->flops ||
1640  b1->seconds != b2->seconds ||
1641  timeval_cmp(&(b1->wall_t), &(b2->wall_t)) != 0 ||
1642  timeval_cmp(&(b1->used_t), &(b2->used_t)) != 0)
1643  {
1644  return 0;
1645  }
1646 
1647  return 1;
1648 }
1649 
1650 /*!
1651  * Returns pointer to the first instance of benchmark from the model that has
1652  * the same parameters as those requested, or null if no benchmark exists with
1653  * matching parameters
1654  *
1655  * @param m pointer to the model
1656  * @param p pointer to the target parameter array
1657  *
1658  * @return pointer to the first instance of a benchmark with parameters matching
1659  * the param array
1660  *
1661  * @pre the parameter array has the same size as the number of parameters the
1662  * model has been built in terms of
1663  */
1664 struct pmm_benchmark*
1665 get_first_bench(struct pmm_model *m, int *p)
1666 {
1667 
1668  struct pmm_benchmark *b;
1669 
1670  b = (void *)NULL;
1671 
1673 
1674  return b;
1675 }
1676 
1677 
1678 /*!
1679  * Returns pointer to the a newly allocated benchmark that represents the
1680  * average of any benchmarks found in the model with parameters matching
1681  * an aligned version of those described by param, or null if no benchmark
1682  * exists with matching parameters
1683  *
1684  * @param m pointer to the model
1685  * @param param pointer to the first element of the target parameter array
1686  *
1687  * @return pointer to a newly allocated benchmark that is an average of all
1688  * benchmarks with matching aligned parameters or NULL on error
1689  *
1690  * @pre the parameter array has the same size as the number of parameters the
1691  * model has been built in terms of
1692  */
1693 struct pmm_benchmark*
1694 get_avg_aligned_bench(struct pmm_model *m, int *param)
1695 {
1696  int *p_aligned;
1697  struct pmm_benchmark *b;
1698 
1699  b = (void*)NULL;
1700 
1701  p_aligned = init_param_array_copy(param, m->n_p);
1702  if(p_aligned == NULL) {
1703  ERRPRINTF("Error copying param array.\n");
1704  return NULL;
1705  }
1706 
1707  align_params(p_aligned, m->parent_routine->pd_set);
1708 
1709  b = get_avg_bench(m, p_aligned);
1710 
1711  free(p_aligned);
1712  p_aligned = NULL;
1713 
1714  return b;
1715 }
1716 
1717 /*!
1718  * Returns pointer to the a newly allocated benchmark that represents the
1719  * average of any benchmarks found in the model that have the same parameters
1720  * as those described by param, or null if no benchmark exists with
1721  * matching parameters
1722  *
1723  * @param m pointer to the model
1724  * @param p pointer to the target parameter array
1725  *
1726  * @return pointer to a newly allocated benchmark that is an average of all
1727  * benchmarks with matching parameters
1728  *
1729  * @pre the parameter array has the same size as the number of parameters the
1730  * model has been built in terms of
1731  */
1732 struct pmm_benchmark*
1733 get_avg_bench(struct pmm_model *m, int *p)
1734 {
1735  struct pmm_benchmark *b;
1736 
1737  b = (void *)NULL;
1738 
1740 
1741  return b;
1742 
1743 }
1744 
1745 /*!
1746  * Calculate some statistics about the benchmarking of a particular point in the
1747  * model, as described by the param parameter array. (Each point stored in a
1748  * model may have multiple benchmarks, essentially, the sum of the time used by
1749  * these benchmarks and the number of benchmarks are calculated.
1750  *
1751  * @param m pointer to the model
1752  * @param param pointer to an array of parameter values that
1753  * @param time_spent pointer to double where the time spent is stored
1754  * @param num_execs pointer to int where number of executions is stored
1755  *
1756  * @return double that is the number of secounds spent benchmarking the point
1757  * described by param
1758  */
1759 void
1760 calc_bench_exec_stats(struct pmm_model *m, int *param,
1761  double *time_spent, int *num_execs)
1762 {
1763  struct pmm_benchmark *b;
1764  struct pmm_benchmark *first, *last;
1765 
1766  *num_execs = 0;
1767  first = NULL;
1768  last = NULL;
1769 
1770  //allocate bench and zero ... don't care about copying param array
1771  b = new_benchmark();
1772  zero_benchmark(b);
1773 
1774 
1775  // find a sublist of matching benchmarks
1776  get_sublist_from_bench_list(m->bench_list, param, &first, &last);
1777 
1778  if(first == NULL) {
1779  // nothing found, leave b empty
1780  }
1781  else if(last == NULL) {
1782  // only one bench found in boundary (only need the wall_t)
1783  copy_timeval(&(b->wall_t), &(first->wall_t));
1784  *num_execs = 1;
1785  }
1786  else {
1787  // sum the benchmarks found between first and last
1788  *num_execs = sum_bench_list(first, last, b);
1789  }
1790 
1791  *time_spent = timeval_to_double(&(b->wall_t));
1792 
1793  free_benchmark(&b);
1794 
1795  return;
1796 }
1797 
1798 /*!
1799  * Calculate some basic statistics about a model
1800  *
1801  * @param m pointer to the model
1802  *
1803  * @return time spend executing benchmarks in model
1804  */
1805 double
1807 {
1808  struct pmm_benchmark *b;
1809  double total_time;
1810 
1811  b = m->bench_list->first;
1812 
1813  total_time = 0.0;
1814  while(b != NULL) {
1815  total_time += timeval_to_double(&(b->wall_t));
1816 
1817  b = b->next;
1818  }
1819 
1820  return total_time;
1821 }
1822 
1823 /*!
1824  * Function removes benchmarks from a model that have parameters that match
1825  * a target set. Removed benchmarks are all not deallocated, their addresses
1826  * are stored in an array passed to the function as removed_array
1827  *
1828  * @param m pointer to the model
1829  * @param p pointer to an array of parameters
1830  * @param removed_array pointer to unallocated array of benchmarks
1831  *
1832  * @return the number of benchmarks found and removed from the model or -1 on
1833  * error
1834  *
1835  * @pre removed_array is unallocated
1836  * @post removed_array is allocated with a size matching the number of
1837  * benchmarks found and removed
1838  */
1839 int
1841  struct pmm_benchmark ***removed_array)
1842 {
1843  int n, c;
1844 
1845  struct pmm_benchmark *first, *last, *this, *temp;
1846 
1847 
1848  n = get_sublist_from_bench_list(m->bench_list, p, &first, &last);
1849 
1850  if(n == 0) {
1851  return 0; //nothing found
1852  }
1853 
1854  DBGPRINTF("allocating remove_array of %d elemenets of size %lu.\n",
1855  n, sizeof(*removed_array));
1856 
1857  (*removed_array) = malloc(n * sizeof *(*removed_array));
1858  if((*removed_array) == NULL) {
1859  ERRPRINTF("Error allocating memory.\n");
1860  return -1;
1861  }
1862 
1863  this = first;
1864  c = 0;
1865  while(c < n) {
1866  DBGPRINTF("removing %p temporarily\n", this);
1867  print_benchmark(PMM_DBG, this);
1868 
1869  // store the next as this will be unplugged by remove_bench_...
1870  temp = this->next;
1871 
1872  //remove benchmarks between first/last from model
1874 
1875 
1876  DBGPRINTF("saving %p at position %d in removed_array %p\n",
1877  this, c, (*removed_array));
1878 
1879  //add removed to array
1880  (*removed_array)[c] = this;
1881  c++;
1882 
1883  this = temp; //next
1884  }
1885 
1886 
1887  return n;
1888 }
1889 
1890 /*!
1891  * find_oldapprox will find the approximation of performance at a target point
1892  * in a model, ignoring any data points that happen to be at that target point.
1893  * The main use of this function is to find what the approximation of a model
1894  * was before a data point was added to the model.
1895  *
1896  * @param m pointer to the model
1897  * @param p pointer to a parameter array describing the target point
1898  *
1899  * @return a pointer to a newly allocated benchmark that gives an approximation
1900  * of the model at the target point, ignoring any data at the exact point or
1901  * NULL on error
1902  */
1903 struct pmm_benchmark *
1904 find_oldapprox(struct pmm_model *m, int *p)
1905 {
1906  struct pmm_benchmark *b;
1907  struct pmm_benchmark **removed_benchmarks_array; //array of pointers
1908  int i;
1909  int n_removed;
1910 
1911  // remove benchmarks from model with matching parameter to p
1912  n_removed = remove_benchmarks_at_param(m, p, &removed_benchmarks_array);
1913 
1914 
1915  //lookup version of model with benchmarks removed
1916  b = lookup_model(m, p);
1917  if(b == NULL) {
1918  ERRPRINTF("Error looking up model.\n");
1919  }
1920 
1921  //re-insert removed benchmarks back into model
1922  for(i=0; i<n_removed; i++) {
1923  insert_bench(m, removed_benchmarks_array[i]);
1924  }
1925 
1926  //return approximation b
1927  return b;
1928 }
1929 
1930 /*!
1931  * Call the appropriate lookup function to find value of model
1932  * given a set of parameters
1933  *
1934  * @param m pointer to model
1935  * @param p array of parameters
1936  *
1937  * @return newly allocated benchmark describing performance at
1938  * p or NULL on failure
1939  */
1940 struct pmm_benchmark* lookup_model(struct pmm_model *m, int *p)
1941 {
1942  struct pmm_benchmark *b;
1943 
1944  b = NULL;
1945 
1946  if(m->n_p == 1) {
1947  b = interpolate_1d_model(m->bench_list, p);
1948  }
1949  else {
1950 #ifdef ENABLE_OCTAVE
1951  // n-dimensional interpolation within boundaries via octave
1952  b = interpolate_griddatan(m, p);
1953 #else
1954  ERRPRINTF("Cannot interpolate 2D+ models without octave support "
1955  "compiled.\n");
1956  return NULL;
1957 #endif /* ENABLE_OCTAVE */
1958  }
1959 
1960  return b;
1961 }
1962 
1963 /*!
1964  * Find the speed approximation given by a 1-D or single parameter model at
1965  * a point described by the parameter array p (size of 1!).
1966  *
1967  * @param bl pointer to the bench list
1968  * @param p pointer to parameter array
1969  *
1970  * @return pointer to a newly allocated benchmark structure which describes
1971  * the flops performance at the point p.
1972  *
1973  *
1974  * TODO this does not use the average of benchmarks with the same parameters
1975  */
1976 struct pmm_benchmark*
1978  int *p)
1979 {
1980  struct pmm_benchmark *b, *cur;
1981 
1982  if(bl->n_p != 1) {
1983  ERRPRINTF("1d interpolation cannot use %dd data.\n", bl->n_p);
1984  return NULL;
1985  }
1986 
1987  b = new_benchmark();
1988  b->n_p = bl->n_p;
1989 
1990  b->p = init_param_array_copy(p, b->n_p);
1991  if(b->p == NULL) {
1992  ERRPRINTF("Error copying parameter array\n");
1993  return NULL;
1994  //TODO this error is not caught as NULL is spec'd as OK return
1995  }
1996 
1997 
1998  cur = bl->first;
1999 
2000  while(cur) {
2001  if(cur->p[0] < p[0]) {
2002  // if model parameter is less than target skip to next bench in
2003  // model
2004  }
2005  else if(cur->p[0] == p[0]) {
2006  // we got lucky
2007  b->flops = cur->flops;
2008 
2009  DBGPRINTF("benchmark at interpolation point: %f\n", b->flops);
2010  return b;
2011  }
2012  else if(cur->p[0] > p[0]) {
2013  // our search has passed the target, we can now interpolate between
2014  // cur (greater than target) and cur->previous (less than target)
2015 
2016  if(cur->previous != NULL) {
2017  //let's interpolate!
2018  //
2019  // the formula to calcuate the flops is as follows:
2020  //
2021  // x1,y1 are p[0] and flops of previous
2022  // x2,y2 are p[0] and flops of cur
2023  // xb,yb are p[0] and flops of b;
2024  //
2025  //
2026  // m = (y2 - y1)/(x2 - x1) (formula for slope)
2027  //
2028  // y - y1 = m(x - x1) (formula for a line)
2029  //
2030  // yb = m(xb - x1) + y1
2031  //
2032  {
2033  int x1, x2, xb;
2034  double y1, y2, yb;
2035 
2036  double m;
2037 
2038  x1=cur->p[0];
2039  x2=cur->previous->p[0];
2040  xb=b->p[0];
2041 
2042  y1=cur->flops;
2043  y2=cur->previous->flops;
2044 
2045  m = ((double)(y2-y1))/((double)(x2-x1));
2046 
2047  yb = m*((double)(xb-x1)) + (double)y1;
2048 
2049  b->flops = yb;
2050 
2051  DBGPRINTF("interploated between %d and %d to %f\n",
2052  x1, x2, b->flops);
2053  return b;
2054 
2055 
2056  }
2057  }
2058  else {
2059  //cur has no preivous, i.e. it is the first benchmark in the
2060  //list model. We will assume speed for a smaller size is
2061  //equal to the first benchmark.
2062  b->flops = cur->flops;
2063  DBGPRINTF("< than first bench, interpolated to:%f\n", b->flops);
2064  return b;
2065  }
2066  }
2067  cur = cur->next;
2068  }
2069 
2070  //we have searched through the model and not found a benchmark
2071  //greater than the target, at minimum, the model will have a zero
2072  //speed benchmark at paramdef.end so we can assume that the target is
2073  //greater than this and speed is zero
2074  //
2075  //TODO this may not be best if nonzero end is set and the end bench is
2076  //not actually 0 speed.
2077  //
2078 
2079  b->flops = .0;
2080  return b;
2081 }
2082 
2083 
2084 #define MAX_CMP(x,y) (x) > (y) ? (x) : (y)
2085 #define MIN_CMP(x,y) (x) < (y) ? (x) : (y)
2086 #define PMM_PERC 0.05 // percentage threshold for GBBP
2087 
2088 /*!
2089  *
2090  * This function determines if the 'cut' of the model at a specific benchmarked
2091  * point, b1, contains the range of execution speeds that are defined by the cut
2092  * of the model at a second benchmark point, b2.
2093  *
2094  * The cut is defined as range of speeds between the maximum and minimum speeds
2095  * as predicted by the model. If C_b1 contains C_b2, then b1_max > b2_max and
2096  * b1_min < b2_min. As illustrated by the diagram below.
2097  *
2098  * i.e.
2099  * \verbatim
2100  * | b1_max
2101  * s| + b2_max
2102  * p| ...|...............+....
2103  * e| cut@b1- | | -cut@b2
2104  * e| | |
2105  * d| ...|...............+....
2106  * | + b2_min
2107  * | b1_min
2108  * -+------------------------------------------
2109  * | ^ size ^
2110  * b1 b2
2111  * \endverbatim
2112  *
2113  * @param h pointer to the load history
2114  * @param b1 pointer to first benchmark point
2115  * @param b2 pointer to second benchmark point
2116  *
2117  * @returns 1 if the values of the model at b1 contain the values of
2118  * the model at b2, 0 otherwise
2119  */
2121  struct pmm_benchmark *b2) {
2122 
2123  //for now we just test if b1->flops and b2->flops are within a percent
2124  //of eachother
2125  //
2126 
2127  double max, min;
2128 
2129  (void)h; //TODO unused
2130 
2131  DBGPRINTF("b1->flops:%f b2->flops:%f\n", b1->flops, b2->flops);
2132 
2133  max = MAX_CMP(b1->flops, b2->flops);
2134  min = MIN_CMP(b1->flops, b2->flops);
2135 
2136  DBGPRINTF("max:%f min:%f\n", max, min);
2137 
2138  DBGPRINTF("max/min %f min/max %f\n", max/min, min/max);
2139 
2140 
2141  if(max/min <= 1+PMM_PERC && min/max >= 1-PMM_PERC) {
2142  DBGPRINTF("b1 contains b2\n");
2143  return 1; //true
2144  }
2145  else {
2146  DBGPRINTF("b1 does not contain b2\n");
2147  return 0; //false
2148  }
2149 
2150  //TODO implement bench_cut_contains properly (i.e. using loadhistory)
2151  //take t0, the optimal execution time of the benchmark
2152 
2153  //using the load history band caculate the upper and lower execution times
2154  //of the benchmark
2155 
2156  //convert execution times into speed of computation (divide by size)
2157 
2158  //compare upper and lower execution speeds
2159 }
2160 
2161 /*!
2162  * This function determines if the 'cut' of the model at a specific benchmarked
2163  * point, b1, overlaps the range of execution speeds that are defined by the cut
2164  * of the model at a second benchmark point, b2.
2165  *
2166  * The cut is defined as range of speeds between the maximum and minimum speeds
2167  * as predicted by the model. If C_b1 intersects C_b2, then b1_max > b2_max and
2168  * b1_min < b2_max or, b1_max > b2_min and b1_min < b2_min. As illustrated by
2169  * the diagram below.
2170  *
2171  * \verbatim
2172  * i.e.
2173  * | b1_max
2174  * s| +
2175  * p| |
2176  * e| cut@b1- | b2_max
2177  * e| ...|...............+...
2178  * d| | | -cut@b2
2179  * | + |
2180  * | b1_min .....+...
2181  * | b2_min
2182  * -+------------------------------------------
2183  * | ^ size ^
2184  * b1 b2
2185  * \endverbatim
2186  *
2187  * @param h pointer to the load history
2188  * @param b1 pointer to first benchmark point
2189  * @param b2 pointer to second benchmark point
2190  *
2191  * @return 1 if values of model at b1 intersect values of model at b2, 0
2192  * otherwise
2193  */
2195  struct pmm_benchmark *b2) {
2196  //for now we just test if b1->flops and b2->flops are within a percent
2197  //of eachother ... same as 'bench_cut_contains'
2198 
2199  double max, min;
2200 
2201  (void)h; //TODO unused
2202 
2203  DBGPRINTF("b1->flops:%f b2->flops:%f\n", b1->flops, b2->flops);
2204 
2205  max = MAX_CMP(b1->flops, b2->flops);
2206  min = MIN_CMP(b1->flops, b2->flops);
2207 
2208  DBGPRINTF("max:%f min:%f\n", max, min);
2209 
2210  DBGPRINTF("max/min %f min/max %f\n", max/min, min/max);
2211 
2212  if(max/min <= 1+PMM_PERC && min/max >= 1-PMM_PERC) {
2213  DBGPRINTF("b1 intersects b2\n");
2214  return 1; //true
2215  }
2216  else {
2217  DBGPRINTF("b1 does not intersects b2\n");
2218  return 0; //false
2219  }
2220 
2221  //TODO implement bench_cut_intersects properly (i.e. using loadhistory)
2222 }
2223 
2224 /*!
2225  * This function determines if the 'cut' of the model at a specific benchmarked
2226  * point, b1, has no overlap on the range of execution speeds that are defined
2227  * o by the cut of the model at a second benchmark point, b2. And further,
2228  * that the cut of b1 lies higher
2229  *
2230  * The cut is defined as range of speeds between the maximum and minimum speeds
2231  * as predicted by the model. If C_b1 is greater than C_b2, then b1_min > b2_max
2232  * . As illustrated by the diagram below.
2233  *
2234  * \verbatim
2235  * i.e.
2236  * | b1_max
2237  * s| +
2238  * p| |
2239  * e| cut@b1- |
2240  * e| ...|
2241  * d| |
2242  * | +
2243  * | b1_min b2_max
2244  * | ......+...
2245  * | | -cut@b2
2246  * | ......+...
2247  * | b2_min
2248  * -+------------------------------------------
2249  * | ^ size ^
2250  * b1 b2
2251  * \endverbatim
2252  * @param h pointer to the load history
2253  * @param b1 pointer to first benchmark point
2254  * @param b2 pointer to second benchmark point
2255  *
2256  * @returns 1 if the values of the model at b1 are greater than the values of
2257  * the model at b2, 0 otherwise
2258  */
2260  struct pmm_benchmark *b2) {
2261 
2262  (void)h; //TODO unused
2263 
2264  if(b1->flops > b2->flops*(1.0 + PMM_PERC)) {
2265  return 1; //b1 greater than b2 (by a percentage)
2266  }
2267  else {
2268  return 0; //false
2269  }
2270  //TODO implement bench_cut_greater properly (i.e. using loadhistory)
2271 }
2272 
2273 /*!
2274  * This function performs the inverse of of bench_cut_greater
2275  *
2276  * @param h pointer to the load history
2277  * @param b1 pointer to first benchmark point
2278  * @param b2 pointer to second benchmark point
2279  *
2280  * @returns 1 if the values of the model at b1 are less than the values of
2281  * the model at b2, 0 otherwise
2282  */
2284  struct pmm_benchmark *b2) {
2285 
2286  (void)h; //TODO unused
2287 
2288  if(b1->flops < b2->flops*(1.0 - PMM_PERC)) {
2289  return 1; //b1 less than b2 (by a percentage)
2290  }
2291  else {
2292  return 0; //false
2293  }
2294  //TODO implement bench_cut_less properly (i.e. using loadhistory)
2295 }
2296 
2297 /*!
2298  * convert a construction method enum to a char array description
2299  *
2300  * @param method the construction method
2301  *
2302  * @returns pointer to a character array describing the method
2303  */
2304 char*
2306 {
2307  switch (method) {
2308  case CM_NAIVE:
2309  return "naive";
2310  case CM_NAIVE_BISECT:
2311  return "naive_bisect";
2312  case CM_RAND:
2313  return "random";
2314  case CM_GBBP:
2315  return "gbbp";
2316  case CM_GBBP_NAIVE:
2317  return "gbbp_naive";
2318  case CM_INVALID:
2319  return "invalid";
2320  default:
2321  return "unknown";
2322  }
2323 }
2324 
2325 /*!
2326  * convert a construction condition enum to a char array description
2327  *
2328  * @param condition the construction method
2329  *
2330  * @returns pointer to a character array describing the condition
2331  */
2332 char*
2334 {
2335  switch (condition) {
2336  case CC_INVALID:
2337  return "invalid";
2338  case CC_NOW:
2339  return "now";
2340  case CC_UNTIL:
2341  return "until";
2342  case CC_PERIODIC:
2343  return "periodic";
2344  case CC_IDLE:
2345  return "idle";
2346  case CC_NOUSERS:
2347  return "no users";
2348  default:
2349  return "unknown";
2350  }
2351 }
2352 
2353 /*
2354  * Test if 3 points (parameters of a model) are collinear (in n dimensions)
2355  *
2356  * For example, in 3 dimensions, points: \f$P_1, P_2, P_3\f$, defined by
2357  * \f$(x_1,y_2), (x_2,y_2), (x_3,y_3)\f$ respectively, are collinear if
2358  * and only if:
2359  *
2360  * \f$(x_2-x_1)/(x_3-x_1)=(y_2-y_1)/(y_3-y_1)=(z_2-z_1)/(z_3-z_1)\f$
2361  *
2362  * That is, the ratio of the distance from \f$P_1\f$ to \f$P_2\f$ and from
2363  * \f$P_1\f$ to \f$P_3\f$ in each seperate dimension, must be equal. This
2364  * is trivially expanded to N dimensions.
2365  *
2366  * Expanded to \f$n\f$ dimensions, where \f$P_i\f$ is defined by
2367  * \f$(a_i0, a_i1, ... a_in)\f$, and the ratio of the distance between points
2368  * in the \f$n^{th}\f$-dimensial plane is given by:
2369  *
2370  * \f$q_n = (a_{2n}-a_{2n})/(a_{3n}-a_{1n})\f$
2371  *
2372  * Points \f$P_0, P_1, P_2\f$ are collinear if for all \f$n\f$:
2373  *
2374  * \f$q_n = q_0\f$
2375  *
2376  *
2377  * @param a pointer to a parameter array
2378  * @param b pointer to a parameter array
2379  * @param c pointer to a parameter array
2380  * @param n number of elements in the arrays
2381  *
2382  * @return 0 if points are not collinear, 1 if points are collinear, -1 on error
2383  *
2384  * @pre all parameter arrays have the same number of elements
2385  */
2386 /* TODO
2387 int
2388 is_collinear_params(int *a, int *b, int *c, int n)
2389 {
2390  int *d1, *d2, *c;
2391 
2392  d1 = malloc
2393 
2394  //find difference between b -> a, and c -> a
2395  for(i=0; i<n; i++) {
2396  d1[i] = b[i] - a[i];
2397  d2[i] = c[i] - a[i];
2398  }
2399 
2400 
2401 }
2402 */
2403 
2404 /*!
2405  * prints the linked list of benchmark points in a pmm_model
2406  *
2407  * @param output output stream to print to
2408  * @param m model to print
2409  */
2410 void print_model(const char *output, struct pmm_model *m) {
2411  char mtime_str[80];
2412  struct tm *ts;
2413 
2414  SWITCHPRINTF(output, "path: %s\n", m->model_path);
2415  SWITCHPRINTF(output, "unwritten execs: %d\n", m->unwritten_num_execs);
2416  SWITCHPRINTF(output, "unwritten time: %f\n", m->unwritten_time_spend);
2417 
2418  ts = localtime(&(m->mtime));
2419  strftime(mtime_str, sizeof(mtime_str), "%a %Y-%m-%d %H:%M:%S %Z", ts);
2420  SWITCHPRINTF(output, "modified time: %s\n", mtime_str);
2421 
2422  free(ts);
2423  ts = NULL;
2424 
2425  SWITCHPRINTF(output, "completion:%d\n", m->completion);
2426  SWITCHPRINTF(output, "uniqe benches:%d\n", m->unique_benches);
2427 
2428  SWITCHPRINTF(output, "--- bench list ---\n");
2429  print_bench_list(output, m->bench_list);
2430  SWITCHPRINTF(output, "--- end bench list ---\n");
2431 
2432  SWITCHPRINTF(output, "--- interval list ---\n");
2433  print_interval_list(output, m->interval_list);
2434  SWITCHPRINTF(output, "--- end interval list ---\n");
2435 
2436 
2437 }
2438 
2439 /*!
2440  * Print a pmm_bench_list structure to the log
2441  *
2442  * @param output output stream to print two
2443  * @param bl pointer to the pmm_bench_list structure
2444  */
2445 //TODO it is not an output stream but one of PMM_DBG/PMM_ERR/PMM_LOG
2446 void
2447 print_bench_list(const char *output, struct pmm_bench_list *bl)
2448 {
2449  struct pmm_benchmark *b;
2450 
2451  SWITCHPRINTF(output, "size: %d\n", bl->size);
2452  SWITCHPRINTF(output, "n_p: %d\n", bl->n_p);
2453 
2454  SWITCHPRINTF(output, "first: %p\n", bl->first);
2455  SWITCHPRINTF(output, "last: %p\n", bl->last);
2456 
2457  SWITCHPRINTF(output, "--- begin benchmarks ---\n");
2458 
2459  b = bl->first;
2460 
2461  while(b != NULL) {
2462  print_benchmark(output, b);
2463  b = b->next;
2464  }
2465 
2466  SWITCHPRINTF(output, "--- end benchmarks ---\n");
2467 
2468  return;
2469 }
2470 
2471 /*!
2472  * print a benchmark
2473  *
2474  * @param output output stream to print to
2475  * @param b pointer to benchmark
2476  */
2477 void
2478 print_benchmark(const char *output, struct pmm_benchmark *b) {
2479  int i;
2480 
2481  SWITCHPRINTF(output, "----benchmark----\n");
2482  for(i=0; i<b->n_p; i++) {
2483  SWITCHPRINTF(output, "p[%d]: %d\n", i, b->p[i]);
2484  }
2485  SWITCHPRINTF(output, "flops: %f\n", b->flops);
2486  SWITCHPRINTF(output, "seconds: %f\n", b->seconds);
2487  SWITCHPRINTF(output, "wall sec:%ld wall usec:%ld\n", b->wall_t.tv_sec,
2488  b->wall_t.tv_usec);
2489  SWITCHPRINTF(output, "used sec:%ld used usec:%ld\n", b->used_t.tv_sec,
2490  b->used_t.tv_usec);
2491 
2492  SWITCHPRINTF(output, "previous:%p\n", b->previous);
2493 
2494  SWITCHPRINTF(output, "current:%p\n", b);
2495 
2496  SWITCHPRINTF(output, "next:%p\n", b->next);
2497  SWITCHPRINTF(output, "--end-benchmark--\n");
2498 }
2499 
2500 /*!
2501  * prints the elements of a pmm_routine structure
2502  *
2503  * @param output output stream to print to
2504  * @param r pointer to routine
2505  */
2506 void print_routine(const char *output, struct pmm_routine *r) {
2507 
2508  SWITCHPRINTF(output, "-- rountine --\n");
2509  SWITCHPRINTF(output, "name: %s\n", r->name);
2510  SWITCHPRINTF(output, "exe_path: %s\n", r->exe_path);
2511 
2512  if(r->exe_args != NULL)
2513  SWITCHPRINTF(output, "exe_args: %s\n", r->exe_args);
2514 
2515  print_paramdef_set(output, r->pd_set);
2516 
2517  SWITCHPRINTF(output, "condition: %s\n", construction_condition_to_string(r->condition));
2518  SWITCHPRINTF(output, "priority:%d\n", r->priority);
2519  SWITCHPRINTF(output, "executable:%d\n", r->executable);
2520 
2521  SWITCHPRINTF(output, "min_sample_num:%d\n", r->min_sample_num);
2522  SWITCHPRINTF(output, "min_sample_time:%d\n", r->min_sample_time);
2523  SWITCHPRINTF(output, "max_completion:%d\n", r->max_completion);
2524  SWITCHPRINTF(output, "construction method: %s\n",
2526 
2527  //print_model(output, r->model);
2528  SWITCHPRINTF(output, "model completion:%d\n", r->model->completion);
2529 
2530  SWITCHPRINTF(output, "-- end routine --\n");
2531  return;
2532 }
2533 
2534 /*!
2535  * function is used to set string variables of the pmm_routine structure
2536  * and other string variables. Takes a pointer to the destination char*
2537  * and the source char* and then allocates enough memory to copy
2538  * the source into the destination.
2539  *
2540  * @param dst pointer to location fo destination string
2541  * @param src pointer to string to set
2542  *
2543  * @return 0 on failure, 1 on success
2544  *
2545  */
2546 int set_str(char **dst, char *src) {
2547  size_t len;
2548 
2549  /*
2550  * TODO consider setting a routine string that has already some memory
2551  * allocated (from a previous set, i.e. resetting str ...
2552  *
2553  * or in english, FREE/malloc or realloc dst before copying src, duh!
2554  */
2555 
2556  if(src == NULL) {
2557  ERRPRINTF("null source string\n");
2558  return 0; //failure
2559  }
2560 
2561  len = strlen(src) + 1;
2562 
2563  // LOGPRINTF("len:%d\n", len);
2564 
2565  *dst = malloc(sizeof(char)*len); //this is a bit ptr crazy,
2566 
2567  if(*dst == NULL) {
2568  ERRPRINTF("Allocation of memory to string failed.\n");
2569  return 0; //failure
2570  }
2571 
2572  strcpy(*dst, src);
2573 
2574  // LOGPRINTF("src:%s dst:%s\n", src, *dst);
2575 
2576  return 1; //success
2577 }
2578 
2579 /*!
2580  * converts a ISO 8601 time string to a time_t value
2581  *
2582  * @param date pointer to charater string representing a date
2583  * @return date converted to a time_t value
2584  */
2585 time_t parseISO8601Date(char *date) {
2586  struct tm tm_t;
2587  struct tm* tm_p;
2588  time_t t, t2, offset = 0;
2589  int success = 0;
2590  char *pos;
2591 
2592  if(date == NULL) {
2593  ERRPRINTF("Error date string is null.\n");
2594  return (time_t)-1;
2595  }
2596 
2597  tm_p = &tm_t;
2598 
2599  memset(tm_p, 0, sizeof(struct tm));
2600 
2601  // we expect at least something like "2003-08-07T15:28:19" and
2602  // don't require the second fractions and the timezone info
2603  // the most specific format: YYYY-MM-DDThh:mm:ss.sTZD
2604 
2605  // full specified variant
2606  pos = (char *) strptime((const char *)date,
2607  (const char *)"%t%Y-%m-%dT%H:%M%t",
2608  tm_p);
2609 
2610  if(pos != NULL)
2611  {
2612  // Parse seconds
2613  if (*pos == ':') {
2614  pos++;
2615  }
2616  if (isdigit(pos[0]) && !isdigit(pos[1])) {
2617  tm_t.tm_sec = pos[0] - '0';
2618  pos++;
2619  }
2620  else if (isdigit(pos[0]) && isdigit(pos[1])) {
2621  tm_t.tm_sec = 10*(pos[0]-'0') + pos[1] - '0';
2622  pos +=2;
2623  }
2624  // Parse timezone
2625  if (*pos == 'Z') {
2626  offset = 0;
2627  }
2628  else if ((*pos == '+' || *pos == '-') &&
2629  isdigit(pos[1]) && isdigit(pos[2]) &&
2630  strlen(pos) >= 3) {
2631 
2632  offset = (10*(pos[1] - '0') + (pos[2] - '0')) * 60 * 60;
2633 
2634  if (pos[3] == ':' && isdigit(pos[4]) && isdigit(pos[5])) {
2635  offset += (10*(pos[4] - '0') + (pos[5] - '0')) * 60;
2636  }
2637  else if (isdigit(pos[3]) && isdigit(pos[4])) {
2638  offset += (10*(pos[3] - '0') + (pos[4] - '0')) * 60;
2639  }
2640 
2641  offset *= (pos[0] == '+') ? 1 : -1;
2642 
2643  }
2644  success = 1;
2645 
2646  } // only date ... not enough for us, set success to 0
2647  //else if(NULL != strptime((const char *)date, "%t%Y-%m-%d", &tm_t)) {
2648  // success = 0;
2649  //}
2650  // there were others combinations too...
2651 
2652  if(1 == success) {
2653 
2654  // if((time_t)(-1) != (t = mktime(&tm_t))) {
2655 
2656  t = mktime(&tm_t);
2657  if(t != (time_t)(-1)) {
2658  // Correct for the local timezone
2659  t = t - offset;
2660  t2 = mktime(gmtime(&t));
2661  t = t - (t2 - t);
2662 
2663  return t;
2664  }
2665  else {
2666  ERRPRINTF("Time conversion error: mktime failed.\n");
2667  }
2668  } else {
2669  ERRPRINTF("Invalid ISO8601 date format.\n");
2670  }
2671 
2672  return (time_t)-1;
2673 }
2674 
2675 
2676 /*!
2677  * Function checks the routine structure for misconfigurations
2678  *
2679  * @param r pointer to routine
2680  *
2681  * @returns 1 if routine is OK, 0 if routine is BAD
2682  *
2683  */
2684 int check_routine(struct pmm_routine *r) {
2685  int ret = 1;
2686 
2687  // TODO add checking of path names and strings
2688  if(r->pd_set->n_p <= 0) {
2689  ERRPRINTF("Number of paramaters for routine not set correctly.\n");
2690  print_routine(PMM_ERR, r);
2691  ret = 0;
2692  }
2693 
2694  /* TODO after changing to enum do we need to check this any more?
2695  if(r->condition < 0 || r->condition > 4) {
2696  ERRPRINTF("Execution Condition for routine not set correctly.\n");
2697  print_routine(PMM_ERR, r);
2698  ret = 0;
2699  }
2700  */
2701 
2702  if(r->priority < 0) {
2703  ERRPRINTF("Priority for routine not set correctly.\n");
2704  print_routine(PMM_ERR, r);
2705  ret = 0;
2706  }
2707 
2708  return ret;
2709 }
2710 
2711 /*!
2712  * prints the elements of the pmm_config structure for debugging
2713  *
2714  * @param output pointer to output stream
2715  * @param cfg pointer to config structure
2716  */
2717 void print_config(const char *output, struct pmm_config *cfg) {
2718 
2719  int i;
2720 
2721  SWITCHPRINTF(output, "daemon: ");
2722  if(cfg->daemon) {
2723  SWITCHPRINTF(output, "yes\n");
2724  } else {
2725  SWITCHPRINTF(output, "no\n");
2726  }
2727 
2728  SWITCHPRINTF(output, "main sleep period: %ds %lds\n",
2729  (int) cfg->ts_main_sleep_period.tv_sec,
2730  cfg->ts_main_sleep_period.tv_nsec);
2731  SWITCHPRINTF(output, "log file: %s\n", cfg->logfile);
2732  SWITCHPRINTF(output, "config file: %s\n", cfg->configfile);
2733  SWITCHPRINTF(output, "load path: %s\n", cfg->loadhistory->load_path);
2734  SWITCHPRINTF(output, "routine array size: %d\n", cfg->allocated);
2735  SWITCHPRINTF(output, "routine array used: %d\n", cfg->used);
2736 
2737  SWITCHPRINTF(output, "pause: %d\n", cfg->pause);
2738 
2739  for(i=0; i<cfg->used; i++) {
2740  print_routine(output, cfg->routines[i]);
2741  }
2742 
2743  return;
2744 }
2745 
2746 /*!
2747  * frees a configure structure and members it contains
2748  *
2749  * @param cfg pointer to address of config structure
2750  */
2751 void free_config(struct pmm_config **cfg) {
2752  int i;
2753 
2754  if((*cfg)->loadhistory != NULL)
2755  free_loadhistory(&((*cfg)->loadhistory));
2756 
2757  for(i=0; i<(*cfg)->used; i++) {
2758  free_routine(&((*cfg)->routines[i]));
2759  }
2760 
2761  free((*cfg)->routines);
2762  (*cfg)->routines = NULL;
2763 
2764  free(*cfg);
2765  *cfg = NULL;
2766 }
2767 
2768 /*!
2769  * frees a routine structure and members it contains
2770  *
2771  * @param r pointer to address of routine structure
2772  */
2773 void free_routine(struct pmm_routine **r) {
2774 
2775  if((*r)->model != NULL)
2776  free_model(&((*r)->model));
2777 
2778  //free some malloced 'strings'
2779  free((*r)->name);
2780  (*r)->name = NULL;
2781 
2782  if((*r)->exe_args != NULL) {
2783  free((*r)->exe_args);
2784  (*r)->exe_args = NULL;
2785  }
2786 
2787  free((*r)->exe_path);
2788  (*r)->exe_path = NULL;
2789 
2790  //free paramdef set
2791  if((*r)->pd_set != NULL)
2792  free_paramdef_set(&(*r)->pd_set);
2793 
2794  free(*r);
2795  *r = NULL;
2796 }
2797 
2798 /*!
2799  * frees a model structure and members it contains
2800  *
2801  * @param m pointer to address of the model structure
2802  */
2803 void free_model(struct pmm_model **m) {
2804 
2805  if((*m)->bench_list != NULL)
2806  free_bench_list(&((*m)->bench_list));
2807 
2808  if((*m)->interval_list != NULL)
2809  free_interval_list(&((*m)->interval_list));
2810 
2811  if((*m)->pd_set != NULL)
2812  free_paramdef_set(&((*m)->pd_set));
2813 
2814  free((*m)->model_path);
2815  (*m)->model_path = NULL;
2816 
2817  free(*m);
2818  *m = NULL;
2819 }
2820 
2821 /*!
2822  * frees a benchmark list structure and members it contains
2823  *
2824  * @param bl pointer to address of the benchmark list
2825  */
2827 {
2828 
2829  free_benchmark_list_backwards(&((*bl)->first));
2830 
2831  free(*bl);
2832  *bl = NULL;
2833 }
2834 
2835 /*!
2836  * frees a benchmark list structure and members it contains by
2837  * progressing forwards through the list
2838  *
2839  * @param last_b pointer to address of the benchmark
2840  */
2842  struct pmm_benchmark *this, *next;
2843  //TODO check naming last_b yet we remove forwards ?
2844 
2845  this = *last_b;
2846 
2847  while(this != NULL) {
2848  next = this->next;
2849  free_benchmark(&this);
2850  this = next;
2851  }
2852 
2853  return;
2854 }
2855 
2856 /*!
2857  * frees a benchmark list structure and members it contains by
2858  * progressing backwards through the list
2859  *
2860  * @param first_b pointer to address of the benchmark
2861  */
2863  struct pmm_benchmark *this, *prev;
2864 
2865  this = *first_b;
2866 
2867  while(this != NULL) {
2868  prev = this->previous;
2869  free_benchmark(&this);
2870  this = prev;
2871  }
2872 
2873  return;
2874 }
2875 
2876 /*!
2877  * frees a benchmark
2878  *
2879  * @param b pointer to address of the benchmark
2880  */
2882 {
2883  free((*b)->p);
2884  (*b)->p = NULL;
2885 
2886  free(*b);
2887  *b = NULL;
2888 }
2889