pmm  1.0.0
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
pmm_executor.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_executor.c
22  *
23  * @brief Functions for executing a benchmark
24  *
25  * This file contains code for calling a benchmark executable from the
26  * pmm daemon, dealing with the system level requirements of spawning
27  * the benchmark process and parsing the output of said benchmark.
28  */
29 #if HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 //#include <string.h>
34 //#include <stdio.h>
35 #include <stdlib.h> // for free
36 #include <signal.h> // for sigset
37 #include <sys/signal.h> // for sigset
38 #include <pthread.h> // for pthreads/etc.
39 // TODO platform specific code follows, need to fix this
40 #include <sys/time.h> // for select
41 #include <sys/types.h> // for select, waitpid
42 #include <time.h> // for timeval
43 #include <paths.h>
44 #include <sys/wait.h> // for waitpid
45 #include <fcntl.h> // for fcntl
46 #include <unistd.h> // for fcntl, select
47 #include <errno.h> // for perror, errno, etc
48 #include <libgen.h> // for basename
49 
50 #include "pmm_model.h"
51 #include "pmm_selector.h"
52 #include "pmm_cfgparser.h"
53 #include "pmm_log.h"
54 #include "pmm_util.h"
55 
56 //! daemon executing variable or not
57 extern int executing_benchmark;
58 //! mutex for accessing executing_benchmark variable
59 extern pthread_mutex_t executing_benchmark_mutex;
60 
61 /* TODO
62 extern volatile sig_atomic_t sig_cleanup_received;
63 extern volatile sig_atomic_t sig_pause_received;
64 extern volatile sig_atomic_t sig_unpause_received;
65 */
66 
67 //! signal to indicate benchmark should be terminated
68 extern int signal_quit;
69 //! mutex for accessing singal_quit
70 extern pthread_mutex_t signal_quit_mutex;
71 
72 
73 /* local functions */
74 int my_popen(char *cmd, char **args, int n, pid_t *pid);
75 int set_non_blocking(int fd);
76 struct pmm_benchmark* parse_bench_output(char *output, int n_p, int *rargs);
77 
78 int read_benchmark_output(int fd, char **output_p, pid_t bench_pid);
79 
80 void sig_childexit(int sig);
81 double calculate_flops(struct timeval tv, long long int complexity);
82 double timeval_to_seconds(struct timeval tv);
83 void set_executing_benchmark(int i);
84 char*
85 pmm_bench_exit_status_to_string(int bench_status);
86 
87 /*!
88  * a roughly portable way of setting a file descriptor for non-blocking I/O.
89  *
90  * @param fd file descriptor to modify
91  *
92  * @return -1 on error, 0 on success
93  */
94 int set_non_blocking(int fd) {
95  int flags;
96 
97  /* If they have O_NONBLOCK, use the Posix way to do it
98  * Fixme: O_NONBLOCK is defined but broken on SunOS 4.1.x
99  * and AIX 3.2.5.
100  */
101 #if defined(O_NONBLOCK)
102 
103  if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
104  flags = 0;
105 
106  return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
107 
108 #else
109 
110  /* Otherwise, use the old way of doing it */
111  flags = 1;
112  return ioctl(fd, FIOBIO, &flags);
113 
114 #endif
115 
116 }
117 
118 /*!
119  * Forks a process that executes and external program with a an argument string
120  * and returns a file distriptor and sets the child processes pid
121  *
122  * @param cmd string with the full path of the program
123  * @param args array of strings with all arguments of the program
124  * @param n number of arguments in arg vector
125  * @param pid pointer to the pid which will be set to the pid of the
126  * external program
127  *
128  * @return file descriptor of the pipe opened to the command cmd.
129  *
130  * TODO test for file not found
131  *
132  */
133 int
134 my_popen(char *cmd, char **args, int n, pid_t *pid)
135 {
136  int p[2]; // this is our pipe of file descriptors
137  char **argv;
138  int i;
139 
140  if(pipe(p) < 0) {
141  ERRPRINTF("Error creating pipe.\n");
142  return -1;
143  }
144 
145 
146  /* build up argv for calling command */
147  argv = malloc((n+2) * sizeof *argv);
148 
149  argv[0] = basename(cmd); //TODO threadsafe????
150 
151  DBGPRINTF("cmd:%s base:%s\n", cmd, argv[0]);
152 
153  for(i=1; i<n+1; i++) {
154  DBGPRINTF("%s\n", args[i-1]);
155  argv[i] = args[i-1];
156  }
157 
158  argv[i] = (char *)NULL;
159 
160 
161 
162  // TODO change to sigaction
163  sigset(SIGCHLD, &sig_childexit);
164 
165  if((*pid = fork()) > 0) { // parent
166 
167  close(p[1]); // close write pipe
168 
169  free(argv); //this has been copied into the child so we can free
170  argv = NULL;
171 
172  return p[0]; // return read pipe
173  }
174  else if(*pid == 0) { // child
175 
176 
177  close(p[0]); // close read pipe
178 
179  // duplicate STDOUT to write pipe (for parent to read)
180  dup2(p[1], STDOUT_FILENO);
181 
182  // duplicate STDERR to write pipe (for parent to read)
183  dup2(p[1], STDERR_FILENO);
184 
185  close(p[1]);
186 
187 
188  // call the command 'cmd' using argv[]
189  execv(cmd, argv);
190 
191  // if we reach this exec has filed
192  ERRPRINTF("child: exec failure, exiting.\n");
193  exit(EXIT_FAILURE);
194 
195  }
196  else { // error
197  close(p[0]);
198  close(p[1]);
199  ERRPRINTF("Error forking.\n");
200  return -1;
201  }
202 }
203 
204 /*!
205  * SIGCHLD signal handler
206  *
207  * @param sig the signal
208  *
209  * @warning Do not use *PRINTF logging facility in this thread, it is not
210  * 'async-signal-safe' (though it is threadsafe).
211  */
212 void sig_childexit(int sig)
213 {
214 
215  (void)sig; //TODO unused
216 
217  printf("received SIGCHLD\n");
218  // if we wait here we miss the status in the "benchmark" function
219  //wait(0);
220 }
221 
222 /*!
223  * Given the output of a benchmark execution parse and create a new
224  * benchmark structure storing all the information gained
225  *
226  * @param output pointer to benchmark output string
227  * @param n_p number of parameters of benchmark
228  * @param rargs arguments passed to the benchmarked routine
229  *
230  * @return pointer to newly allocated benchmark reflecting the parsed output
231  */
232 struct pmm_benchmark*
233 parse_bench_output(char *output, int n_p, int *rargs)
234 {
235  //TODO seperate into seperate call
236  //TODO permit detection of routine that benchmark pertains to
237  //TODO change rargs to p or something reflecting the reset of the code
238  struct timeval wall_t, used_t;
239  long long complexity;
240  int rc;
241  struct pmm_benchmark *b;
242 
243  rc = sscanf(output, "%ld %ld\n%ld %ld\n%lld", &(wall_t.tv_sec),
244  &(wall_t.tv_usec), &(used_t.tv_sec), &(used_t.tv_usec),
245  &complexity);
246 
247  if(rc != 5) {
248  ERRPRINTF("Error parsing output\n");
249  return NULL;
250  }
251 
252  LOGPRINTF("wall secs: %ld usecs: %ld\n", wall_t.tv_sec, wall_t.tv_usec);
253  LOGPRINTF("used secs: %ld usecs: %ld\n", used_t.tv_sec, used_t.tv_usec);
254 
255  if(wall_t.tv_sec == 0 && wall_t.tv_usec == 0) {
256  ERRPRINTF("Zero execution wall-time parsed from bench output.\n");
257  return NULL;
258  }
259 
260  if(complexity == 0) {
261  ERRPRINTF("Zero complexity value parsed from bench output.\n");
262  return NULL;
263  }
264 
265  b = new_benchmark();
266 
267  if(b == NULL) {
268  ERRPRINTF("Errory allocating benchmark structure.\n");
269  return NULL;
270  }
271 
272  b->n_p = n_p;
273 
274  b->p = init_param_array_copy(rargs, b->n_p);
275  if(b->p == NULL) {
276  ERRPRINTF("Error copying benchmark parameters.\n");
277  return NULL;
278  }
279 
280  copy_timeval(&b->wall_t, &wall_t);
281  copy_timeval(&b->used_t, &used_t);
282 
283  b->next = (void *)NULL; //this will be delt with elswhere
284  b->previous = (void *)NULL;
285  b->complexity = complexity;
286  b->flops = calculate_flops(b->wall_t, (b->complexity));
288 
289  LOGPRINTF("flops: %f\n", b->flops);
290 
291  return b;
292 }
293 
294 
295 /*!
296  * calculate (FL)OPS (floating point operations per second or any other
297  * type of operations per second)
298  *
299  * @param tv timeval structure representing duration of computation
300  * @param complexity complexity of computation (volume of operations)
301  *
302  * @returns flops as a double
303  */
304 double calculate_flops(struct timeval tv, long long int complexity)
305 {
306  return complexity/timeval_to_seconds(tv);
307 }
308 
309 /*!
310  * convert a timeval to second in double
311  *
312  * @param tv timeval structure
313  *
314  * @return timeval as seconds
315  */
316 double timeval_to_seconds(struct timeval tv)
317 {
318  return (double)tv.tv_sec + (double)tv.tv_usec/1000000.0;
319 }
320 
321 /*!
322  * Convert params to an array of strings and call my_popen
323  *
324  * @param r pointer to routine that will be executed
325  * @param params pointer to array of parameters
326  * @param bench_pid pointer to pid_t to store spawned process pid
327  *
328  * @return int index of file descriptor that points to the stdout of the
329  * spawned process, or -1 if there is an error
330  */
331 int
332 spawn_benchmark_process(struct pmm_routine *r, int *params,
333  pid_t *bench_pid)
334 {
335 
336  char **arg_strings;
337  int arg_n;
338  int i, j;
339  int fd;
340 
341  LOGPRINTF("exe_path:%s\n", r->exe_path);
342  print_params(PMM_LOG, params, r->pd_set->n_p);
343 
344  // if we have some static args specified we need to count them and add
345  // them to the arg array
346  if(r->exe_args != NULL) {
347  char *tmp_args;
348  int tmp_args_len;
349  char *sep = " "; // tokenise on space, no quoted arguments supported
350  char *last = NULL;
351  char *tok;
352  int count;
353 
354  // copy the static args as strtok* modifies its inputs
355  tmp_args = strdup(r->exe_args);
356  if(tmp_args == NULL) {
357  ERRPRINTF("Error duplicating exe arg string.\n");
358  return -1;
359  }
360 
361  // check length of string for later
362  tmp_args_len = (int)strlen(tmp_args) +1;
363 
364  // tokenise string, counting as we go
365  count=0;
366  tok = strtok_r(tmp_args, sep, &last);
367  DBGPRINTF("token %d:%s\n", count, tok);
368  while(tok != NULL) {
369  count++;
370 
371  tok = strtok_r(NULL, sep, &last);
372  }
373 
374  // allocate array for number of static args (from tokens) and parameters
375  // of model
376  arg_n = r->pd_set->n_p+count;
377  arg_strings = malloc(arg_n * sizeof *arg_strings);
378  if(arg_strings == NULL) {
379  ERRPRINTF("Error allocating argument array.\n");
380  return -1;
381  }
382 
383  DBGPRINTF("arg_n:%d\n", arg_n);
384 
385  // strtok_r will have placed '\0' (null) characters in the tmp_args
386  // character array, uses these to find the tokens and copy each token
387  // to an element of the arg array
388  int new_find = 0;
389  j = 0;
390  for(i=0; i<tmp_args_len; i++) {
391  if(new_find == 0) {
392  if(tmp_args[i] != '\0') {
393  arg_strings[j++] = strdup(&tmp_args[i]);
394  }
395  new_find = 1;
396  }
397  if(new_find == 1) {
398  if(tmp_args[i] == '\0') {
399  new_find = 0;
400  }
401  }
402 
403  DBGPRINTF("%d:%c\n", i, tmp_args[i]);
404  }
405 
406  //check we got expected number of tokens
407  if(j!=count) {
408  free(arg_strings);
409  ERRPRINTF("Error copying tokens into array.\n");
410  return -1;
411  }
412 
413  free(tmp_args);
414  tmp_args = NULL;
415 
416  //copy the routine parameters in the array after the static arguments
417  for(i=0; i<arg_n; i++, j++) {
418  arg_strings[j] = malloc((1+snprintf(NULL, 0, "%d", params[i])) *
419  sizeof *arg_strings[j]);
420 
421  sprintf(arg_strings[j], "%d", params[i]);
422 
423  DBGPRINTF("arg_strings[%d]:%s\n", j, arg_strings[j]);
424  }
425 
426  }
427  else {
428  arg_n = r->pd_set->n_p;
429 
430  //build argument array strings to pass to the command
431  arg_strings = malloc(arg_n * sizeof *arg_strings);
432  for(i=0; i<arg_n; i++) {
433 
434  arg_strings[i] = malloc((1+snprintf(NULL, 0, "%d", params[i]))
435  * sizeof *arg_strings[i]);
436 
437  sprintf(arg_strings[i], "%d", params[i]);
438 
439  }
440  }
441 
442  fd = my_popen(r->exe_path, arg_strings, arg_n, bench_pid);
443 
444  for(i=0; i>arg_n; i++) {
445  free(arg_strings[i]);
446  arg_strings[i] = NULL;
447  }
448 
449  free(arg_strings);
450  arg_strings = NULL;
451 
452  return fd;
453 }
454 
455 /*!
456  * clean up a benchmark process
457  *
458  * closes file handles and kills the process
459  *
460  * @param fp file pointer to the benchmarks standard output
461  * @param bench_pid benchmark process id
462  *
463  */
464 void
465 cleanup_benchmark_process(FILE *fp, pid_t bench_pid)
466 {
467  //send kill don't bother calling waitpid on benchmark
468  if(kill(bench_pid, SIGKILL) != 0) {
469  ERRPRINTF("error sending kill to benchmark.\n");
470  }
471 
472  if(fclose(fp) < 0) {
473  ERRPRINTF("Error closing file pointer to command\n");
474  }
475 }
476 
477 /*!
478  *
479  * Read the output of a benchmark process
480  *
481  * @param fd benchmark process output file descriptor
482  * @param output_p pointer to a character array where output will be
483  * stored
484  * @param bench_pid process id of the benchmark process
485  *
486  * @return 0 on success, -1 if there was an error reading (output_p
487  * also set to NULL) 1 if a quit signal was received while waiting for output
488  */
489 int
490 read_benchmark_output(int fd, char **output_p, pid_t bench_pid)
491 {
492  FILE *fp;
493  int reading;
494 
495  char *output;
496  char *tmp_output;
497  int output_index;
498  int output_size;
499  int bs = 512;
500  int r_count;
501 
502  // variables for select()
503  struct timeval tv_select_wait;
504  fd_set read_set;
505  int select_ret;
506 
507  set_non_blocking(fd);
508 
509  fp = (void *)NULL;
510 
511  // get file pointer from file descriptor
512  fp = fdopen(fd, "r");
513  if(fp == NULL) {
514  ERRPRINTF("Error opening file descriptor\n");
515 
516  *output_p = NULL;
517 
518  return -1;
519  }
520 
521  // allocate some memory for the output string
522  output = calloc(bs, sizeof *output);
523  output_index = 0;
524  output_size=bs;
525 
526  reading = 1;
527  while(reading) {
528 
529  // select overwrites its parameters so we need to initalise them
530  // inside the loop
531  FD_ZERO(&read_set);
532  FD_SET(fd, &read_set);
533  tv_select_wait.tv_sec = 5;
534  tv_select_wait.tv_usec = 0;
535 
536  //DBGPRINTF("select() waiting on file descriptor for %ld.%ld secs.\n",
537  // tv_select_wait.tv_sec, tv_select_wait.tv_usec);
538 
539  // select on file descriptor
540  select_ret = select(fd+1, &read_set, NULL, NULL, &tv_select_wait);
541  if(select_ret < 0) { // error
542  if(errno == EINTR)
543  continue;
544 
545  ERRPRINTF("Error waiting for benchmark output.\n");
546 
547  cleanup_benchmark_process(fp, bench_pid);
548 
549  free(output);
550  output = NULL;
551 
552  *output_p = NULL;
553 
554  return -1;
555 
556  }
557  else if(select_ret == 0) { // no data is available to read
558 
559  //DBGPRINTF("no benchmark output available: %d\n", select_ret);
560 
561  }
562  else if(select_ret > 0) { // data available to read
563 
564  //DBGPRINTF("select() file descriptor ready %d\n", select_ret);
565 
566  //TODO check we are growing buffer correctly
567 
568  while( !feof(fp) ) {
569 
570  // try a read, if it is empty break back to the select loop
571  r_count = fread(&output[output_index], sizeof *output,
572  bs-1, fp);
573 
574  if(r_count == 0) { // finished reading back to select
575  //DBGPRINTF("read 0 chars, breaking to eof check.\n");
576  break;
577  }
578  else { // if it is not empty process the read
579  //DBGPRINTF("read %d, allocating:%d to output read buffer.\n",
580  // r_count, output_size+r_count);
581 
582  tmp_output = realloc(output,
583  output_size + r_count * sizeof *output);
584 
585  if(tmp_output == NULL) {
586 
587  ERRPRINTF("Error reallocating mem. to read buffer.\n");
588 
589  cleanup_benchmark_process(fp, bench_pid);
590 
591  free(output);
592  output = NULL;
593 
594  *output_p = NULL;
595 
596  return -1;
597 
598  }
599  else {
600  output = tmp_output;
601  }
602 
603  //DBGPRINTF("output:%p\n", output);
604  //DBGPRINTF("output_size:%d sizeof(output):%lu\n",
605  // output_size, sizeof(output[0]));
606  //DBGPRINTF("output_index (old):%d\n", output_index);
607  output_index += r_count;
608  output_size += r_count;
609  //DBGPRINTF("output_index (new):%d\n", output_index);
610  }
611  }
612 
613  if(feof(fp)) {
614  //DBGPRINTF("reached end of benchmark output\n");
615  output[output_index] = '\0';
616  reading = 0;
617  } else {
618  //DBGPRINTF("going into another select loop.\n");
619  }
620  }
621 
622  /*
623  if(sig_pause_received) {
624  // send pause signal to benchmark process
625  if(kill(bench_pid, SIGSTOP) != 0) {
626  perror("[benchmark]"); //TODO
627  ERRPRINTF("error sending SIGSTOP to benchmark process:%d", bench_pid);
628  }
629  }
630 
631  if(sig_unpause_received) {
632  // send unpause signal to benchmark process
633  if(kill(bench_pid, SIGCONT) != 0) {
634  perror("[benchmark]"); //TODO
635  ERRPRINTF("error sending SIGCONT to benchmark process:%d", bench_pid);
636  }
637  }
638  */
639 
640  //check that global shutdown variable has not been called before
641  //returning to select at start of while
642  pthread_mutex_lock(&signal_quit_mutex);
643  if(signal_quit) {
644  pthread_mutex_unlock(&signal_quit_mutex);
645 
646  LOGPRINTF("signal_quit set, closing files and freeing"
647  " memory\n");
648  LOGPRINTF("killing benchmark process pid:%d\n", (int)bench_pid);
649 
650  cleanup_benchmark_process(fp, bench_pid);
651 
652  free(output);
653  output = NULL;
654 
655  *output_p = NULL;
656 
657  return 1;
658  }
659  pthread_mutex_unlock(&signal_quit_mutex);
660 
661  //go back to select
662  }
663 
664  //finished reading now, close file
665 
666  if(fclose(fp) < 0) {
667  ERRPRINTF("Error closing file pointer to command\n");
668  }
669 
670  *output_p = output;
671  return 0;
672 }
673 
674 /*!
675  * Given a routine to benchmark, select a point, execute the benchmark and
676  * insert the new point into the model. Set the global 'executing_benchark' to
677  * false/0 when the operation is complete (or has failed).
678  *
679  * @param scheduled_r void pointer to a routine structure that has been
680  * scheduled for a benchmark execution
681  *
682  * @return void pointer to an integer with value of: 0 on success, 1 on quit from signal, -1 on failure
683  */
684 void*
685 benchmark(void *scheduled_r)
686 {
687  // TODO decompose some of the fuctionality of this to make it more legible
688  int *ret;
689  int temp_ret;
690  struct pmm_routine *r;
691  struct pmm_benchmark *bmark;
692  int *rargs = NULL; //new benchmark point
693 
694  pid_t bench_pid;
695  int bench_status;
696 
697  int fd;
698  char *output;
699 
700  r = (struct pmm_routine*)scheduled_r;
701 
702  ret = (int*)malloc(sizeof *ret);
703 
704  //evaluate current performance model approximation and pick new
705  //point on the approximation to measure with benchmark, TODO if model
706  //proves to be complete set complete status and return immidiately
707  if(r->construction_method == CM_NAIVE) {
708  rargs = multi_naive_select_new_bench(r);
709  }
710  else if(r->construction_method == CM_NAIVE_BISECT) {
712  }
713  else if(r->construction_method == CM_GBBP) {
714  // rargs = multi_gbbp_select_new_bench(r);
716  }
717  else if(r->construction_method == CM_GBBP_NAIVE) {
719  }
720  else if(r->construction_method == CM_RAND) {
722  }
723  else { // default
724  //rargs = naive_select_new_bench(r);
725  }
726 
727  if(rargs == NULL) {
728  ERRPRINTF("Error selecting new benchmark point.\n");
730  *ret = -1;
731 
732  return (void *)ret;
733  }
734 
735  // take routine and execute it passing in the parameters
736  // of the performance model coordinate experiment at
737  //
738  //LOGPRINTF("benchmark thread: started\n");
739 
740 
741  //LOGPRINTF("exe_path:%s\n", r->exe_path);
742  //print_params(rargs, r->n_p);
743 
744 
745  fd = spawn_benchmark_process(r, rargs, &bench_pid);
746  if(fd == -1) {
747  ERRPRINTF("Error spawning benchmark process, fd:%d pid:%d\n", (int)fd,
748  bench_pid);
749 
750  free(rargs);
751  rargs = NULL;
752 
753  //TODO send kill to bench_pid, just to be sure?
754 
756  *ret = -1; //failure
757 
758  return (void *)ret;
759  }
760 
761 
762  //LOGPRINTF("filedescriptor returned from popen: %d pid:%d\n", (int)fd,
763  // bench_pid);
764 
765 
766  temp_ret = read_benchmark_output(fd, &output, bench_pid);
767  if(temp_ret == -1) {
768  ERRPRINTF("Error reading benchmark output.\n");
769 
770  free(rargs);
771  rargs = NULL;
772 
774  *ret = -1; //failure
775 
776  return (void *)ret;
777  }
778  else if(temp_ret == 1) {
779  DBGPRINTF("Recieved quit while reading benchmark output.\n");
780 
781  free(rargs);
782  rargs = NULL;
783 
785  *ret = 1; //sigquit received
786 
787  return (void *)ret;
788  }
789 
790 
791  //wait for bench to terminate (and retrieve exit status)
792  if(waitpid(bench_pid, &bench_status, 0) != bench_pid) {
793  ERRPRINTF("Error waiting for benchmark to terminate.\n");
794  }
795 
796 
797  //check exit status
798  if(WEXITSTATUS(bench_status) != PMM_EXIT_SUCCESS) {
799  ERRPRINTF("benchmark exited with abnormal status:%s.\n",
800  pmm_bench_exit_status_to_string(WEXITSTATUS(bench_status)));
801 
802  ERRPRINTF("output was:\n--------\n%s---------\n", output);
803 
804  free(output);
805  output = NULL;
806 
807  free(rargs);
808  rargs = NULL;
809 
811  *ret = -1; //failure
812 
813  return (void *)ret;
814 
815  }
816 
817  DBGPRINTF("---output---\n%s------------\n", output);
818 
819 
820  //parse benchmark output
821  if((bmark = parse_bench_output(output, r->pd_set->n_p, rargs)) == NULL) {
822  ERRPRINTF("Error parsing benchmark output\n");
823 
824  free(output);
825  output = NULL;
826 
827  free(rargs);
828  rargs = NULL;
829 
831  *ret = -1; //failure
832 
833  return (void *)ret;
834  }
835 
836 
837  free(output);
838  output = NULL;
839 
840  //DBGPRINTF("bmark:%p\n", bmark);
841 
842  //TODO might need a mutex here
843  temp_ret = 0;
844  if(r->construction_method == CM_NAIVE) {
845 
846  DBGPRINTF("Inserting benchmark with naive construction method.\n");
847 
848  temp_ret = multi_naive_insert_bench(r, bmark);
849 
850  }
851 
853  temp_ret = naive_1d_bisect_insert_bench(r, bmark);
854  }
855  else if(r->construction_method == CM_GBBP ||
857  // note, we can use the same insertion methods for GBBP Diagonal and
858  // GBBP Naive because all steps in the insertion phase are common to
859  // both methods.
860 
861  DBGPRINTF("Inserting benchmark with GBBP.\n");
862 
863  temp_ret = multi_gbbp_insert_bench(NULL, r, bmark);
864 
865  }
866  else { // default, including CM_RAND
867  insert_bench(r->model, bmark);
868  }
869 
870  // check if benchmark insertion failed
871  if(temp_ret < 0) {
872  //interval processing failed, bench added though so we will write
873  //the model to save the result of the execution
874  if(temp_ret == -1) {
875  ERRPRINTF("Interval error when inserting new benchmark.\n");
876  write_model(r->model);
877  }
878  else {
879  ERRPRINTF("Error inserting new benchmark.\n");
880  }
881 
882  free(rargs);
883  rargs = NULL;
884 
886 
887  *ret = -1; //failure
888 
889  return (void *)ret;
890  }
891 
892 
893  // test if model has a max_completion
894  if(r->max_completion != -1) {
895  // set model complete if completion has exceeded or equaled max
896  if(r->model->unique_benches >= r->max_completion) {
897  r->model->complete = 1;
898  }
899  }
900 
901  //update number of unwritten benchmarks and the time spent benchmarking
902  //since last write
904  r->model->unwritten_num_execs += 1;
905 
906  //TODO write model here? why not!
907  if(r->model->complete == 1 ||
910  {
911  DBGPRINTF("Writing model ...\n");
912  temp_ret = write_model(r->model);
913  if(temp_ret < 0) {
914  ERRPRINTF("Error writing model to disk.\n");
915 
916  free(rargs);
917  rargs = NULL;
918 
920  *ret = -1; //failure
921 
922  return (void *)ret;
923  }
924  }
925  else {
926  DBGPRINTF("Not writing model "
927  "(unwritten_num_execs/thres:%d/%d "
928  "unwritten_time_spend/thres:%f/%d) ...\n",
933  }
934 
935  free(rargs);
936  rargs = NULL;
937 
938  LOGPRINTF("benchmark thread: finished.\n");
939 
941  *ret = 1;
942 
943  return (void *)ret;
944 }
945 
946 /*!
947  * set the executing_benchmark variable via mutex
948  *
949  * @param i value to set variable with
950  */
951 void
953 {
954  //TODO check return codes
955  LOGPRINTF("locking executing_benchmark.\n");
956  pthread_mutex_lock (&executing_benchmark_mutex);
958  LOGPRINTF("unlocking executing_benchmark.\n");
959  pthread_mutex_unlock (&executing_benchmark_mutex);
960 }
961 
962 /*!
963  * convert benchmark exit status to a string
964  *
965  * @param bench_status benchmark status represented by an int
966  *
967  * @return pointer to a character string
968  */
969 char*
971 {
972  switch(bench_status) {
973  case PMM_EXIT_SUCCESS :
974  return "success";
975 
976  case PMM_EXIT_ARGFAIL :
977  return "argument number fail";
978 
979  case PMM_EXIT_ARGPARSEFAIL :
980  return "argument parse failure";
981 
982  case PMM_EXIT_ALLOCFAIL :
983  return "memory allocation failutre";
984 
985  case PMM_EXIT_EXEFAIL :
986  return "routine execution failure";
987 
988  case PMM_EXIT_GENFAIL :
989  return "general failure";
990 
991  default:
992  return "unknown exit status";
993  }
994 }