pmm  1.0.0
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
pmm_argparser.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  *
22  * @file pmm_argparser.c
23  *
24  * @brief Parsing command line arguments
25  *
26  * This file contains the command line argument parsing code for the pmm
27  * daemon and the pmm_view utility
28  */
29 #if HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32 
33 #include <stdio.h> // for printf
34 #include <stdlib.h> // for malloc
35 #include <getopt.h> // for getopts
36 #include <string.h> // for strcmp/strdup/strtok
37 
38 #include "pmm_argparser.h"
39 #include "pmm_model.h" // for pmm_config
40 #include "pmm_log.h" // for printing
41 
42 //TODO put pmm_view and pmm argument parsing in seperate files
43 
44 int
45 parse_slice_str(char *slice_str, struct pmm_view_options *options);
46 int
47 count_tokens_in_str(char *str, char *delimiters);
48 
49 /*!
50  * print command line usage for pmm building daemon
51  */
52 void usage() {
53  printf("Usage: pmmd [-dh] [-c file] [-l file]\n");
54  printf("Options:\n");
55  printf(" -c file : specify config file\n");
56  printf(" -l file : specify log file\n");
57  printf(" -d : run in daemon (background) mode\n");
58  printf(" -h : print this help\n");
59  printf(" -b : exit after all models are built\n");
60  printf(" -p : pause after each benchmark execution\n");
61  printf("\n");
62 }
63 
64 /*!
65  * Parse arguments for pmm building daemon.
66  *
67  * @param cfg pointer to config structure
68  * @param argc number of command line arguments
69  * @param argv pointer to command line arguments (array of character
70  * arrays)
71  *
72  */
73 void parse_args(struct pmm_config *cfg, int argc, char **argv) {
74  //TODO return on failure
75  int c;
76  int option_index;
77 
78  while(1) {
79  static struct option long_options[] =
80  {
81  {"daemon", no_argument, 0, 'd'},
82  {"config-file", required_argument, 0, 'c'},
83  {"log-file", required_argument, 0, 'l'},
84  {"help", no_argument, 0, 'h'},
85  {"build-only", no_argument, 0, 'b'},
86  {"pause", no_argument, 0, 'p'},
87  {0, 0, 0, 0}
88  };
89 
90  option_index = 0;
91 
92  c = getopt_long(argc, argv, "dc:l:hbp", long_options, &option_index);
93 
94  // getopt_long returns -1 when arg list is exhausted
95  if(c == -1) {
96  break;
97  }
98 
99  switch(c) {
100  case 'd':
101  cfg->daemon = 1;
102  break;
103 
104  case 'c':
105  cfg->configfile = optarg;
106  break;
107 
108  case 'l':
109  cfg->logfile = optarg;
110  break;
111 
112  case 'b':
113  cfg->build_only = 1;
114  break;
115 
116  case 'p':
117  cfg->pause = 1;
118  break;
119 
120  case 'h':
121  default:
122  usage();
123  exit(EXIT_SUCCESS);
124  }
125  }
126 
127  if(optind < argc) {
128  usage_pmm_view();
129  exit(EXIT_FAILURE);
130  }
131 }
132 
133 /*!
134  * print command line usage for pmm_view tool
135  */
137 {
138  printf("Usage: pmm_view -h | -c file [ -l | -r routine [-p param -p ...] "
139  "[-ai] [-I|-w wait]]\n");
140  printf("Options:\n");
141  printf(" -c file : specify config file\n");
142  printf(" -f model : specify model file to plot\n");
143  printf(" -h : print this help\n");
144  printf(" -l : list routines\n");
145  printf(" -r routine : specify routine to plot\n");
146  printf(" -a : plot averaged datapoints\n");
147  printf(" -i : plot intervals (not supported in slice plot)\n");
148  printf(" -I : enter interactive mode after plotting\n");
149  printf(" -m : plot maximum speed datapoints only\n");
150  printf(" -p param : specify a parameter axis to plot (max 2)\n");
151  printf(" -P : plot using palette\n");
152  printf(" -s slice : specify a slice of model to plot (see man page)\n");
153  printf(" -S style : specify a gnuplot plot style (see gnuplot help)\n");
154  printf(" -t [n] : interploate model with n points (default 100)\n");
155  printf(" -w wait : replot model every 'wait' seconds\n");
156  printf(" -o file : write plot to file (with png or ps extension)\n");
157  printf("\n");
158 }
159 
160 
161 /*!
162  * Parse arguments for pmm_view tool
163  *
164  * @param options pointer to pmm_view options structure
165  * @param argc number of command line arguments
166  * @param argv pointer to command line arguments (array of character
167  * arrays)
168  *
169  * @return 0 on successful parsing, -1 on error
170  */
171 int
173  int argc, char **argv)
174 {
175  int c;
176  int option_index;
177 
178 
179  options->action = PMM_VIEW_NULL;
180  options->enter_interactive = 0;
181  options->wait_period = 0;
182  options->config_file = (void*)NULL;
183  options->plot_output_file = (void*)NULL;
184  options->plot_output_grayscale = 0;
185  options->plot_average = 0;
186  options->plot_intervals = 0;
187  options->plot_params_index = -1;
188  options->plot_max = 0;
189  options->plot_style = (void*)NULL;
190  options->slice_arr_size = -1;
191  options->slice_interpolate = 0;
192  options->plot_palette = 0;
193  options->n_plots = 0;
194 
195  while(1) {
196  static struct option long_options[] =
197  {
198  {"config-file", required_argument, 0, 'c'},
199  {"model-file", required_argument, 0, 'f'},
200  {"help", no_argument, 0, 'h'},
201  {"list-routines", no_argument, 0, 'l'},
202  {"routine", required_argument, 0, 'r'},
203  {"plot-average", no_argument, 0, 'a'},
204  {"plot-intervals", no_argument, 0, 'i'},
205  {"interactive-mode", no_argument, 0, 'I'},
206  {"plot-max", no_argument, 0, 'm'},
207  {"param-index", required_argument, 0, 'p'},
208  {"palette", no_argument, 0, 'P'},
209  {"slice", required_argument, 0, 's'},
210  {"plot-style", required_argument, 0, 'S'},
211  {"interpolate", optional_argument, 0, 't'},
212  {"wait-period", required_argument, 0, 'w'},
213  {"output-file", required_argument, 0, 'o'},
214  {"greyscale", no_argument, 0, 'g'},
215  {0, 0, 0, 0}
216  };
217 
218  option_index = 0;
219 
220  c = getopt_long(argc, argv, "c:f:hlr:aiImp:Ps:S:t:w:o:g", long_options,
221  &option_index);
222 
223  // getopt_long returns -1 when arg list is exhausted
224  if(c == -1) {
225  break; //exit while loop
226  }
227 
228  switch(c) {
229  case 'c':
230  options->config_file = optarg;
231  break;
232 
233  case 'a':
234  if(options->plot_max != 0) {
235  ERRPRINTF("Cannot plot average and max at the same time.\n");
236  return -1;
237  }
238  options->plot_average = 1;
239  break;
240 
241  case 'i':
242  options->plot_intervals = 1;
243  break;
244 
245  case 'I':
246  if(options->wait_period != 0) {
247  ERRPRINTF("Cannot enter interactive mode when replotting is set"
248  "by -w <period>\n");
249  return -1;
250  }
251 
252  options->enter_interactive = 1;
253  break;
254 
255  case 'r':
256  // if no action set, set to DISPLAY_ROUTINE
257  if(options->action == PMM_VIEW_NULL) {
258  options->action = PMM_VIEW_DISPLAY_ROUTINE;
259  }
260  // otherwise, test that it is already set to DISPLAY_ROUTINE
261  else if(options->action != PMM_VIEW_DISPLAY_ROUTINE) {
262  ERRPRINTF("Cannot do more than one action (list routines, "
263  "view routines or view model files.\n");
264  return -1;
265  }
266 
267  options->routine_names[options->n_plots] = optarg;
268 
269  options->n_plots = options->n_plots + 1;
270 
271  if(options->n_plots >= PMM_MAX_PLOTS) {
272  ERRPRINTF("Cannot plot more than %d routines at once.\n",
273  PMM_MAX_PLOTS);
274  return -1;
275  }
276  break;
277 
278  case 'm':
279  if(options->plot_average != 0) {
280  ERRPRINTF("Cannot plot average and max at the same time.\n");
281  return -1;
282  }
283  options->plot_max = 1;
284  break;
285 
286  case 'p':
287  if(options->plot_params_index >= 1) {
288  ERRPRINTF("cannot specify more than 2 parameter boundaries.\n");
289  return -1;
290  }
291 
292  if(options->slice_arr_size != -1) {
293  ERRPRINTF("Cannot specify parameter boundary and slice plotting"
294  " at the same time.\n");
295  return -1;
296  }
297 
298  options->plot_params_index++;
299  options->plot_params[options->plot_params_index] = atoi(optarg);
300  break;
301 
302  case 'P':
303  if(options->plot_style != NULL) {
304  ERRPRINTF("Cannot specify palette plot and style together.\n");
305  }
306  options->plot_palette = 1;
307  break;
308 
309  case 's':
310  if(options->plot_params_index != -1) {
311  ERRPRINTF("Cannot specify parameter boundary and slice plotting"
312  " at the same time.\n");
313  return -1;
314  }
315 
316  if(parse_slice_str(optarg, options) < 0) {
317  ERRPRINTF("Error parsing slice string\n");
318  return -1;
319  }
320  break;
321 
322  case 'S':
323  if(options->plot_palette != 0) {
324  ERRPRINTF("Cannot specify palette plot and style together.\n");
325  return -1;
326  }
327  options->plot_style = optarg;
328  break;
329 
330  case 't':
331  if(optarg == NULL) {
332  options->slice_interpolate = 100;
333  }
334  else {
335  options->slice_interpolate = atoi(optarg);
336  }
337  break;
338 
339  case 'f':
340  // if no action set, set to DISPLAY_FILE
341  if(options->action == PMM_VIEW_NULL) {
342  options->action = PMM_VIEW_DISPLAY_FILE;
343  }
344  // otherwise, test that it is already set to DISPLAY_FILE
345  else if(options->action != PMM_VIEW_DISPLAY_FILE) {
346  ERRPRINTF("Cannot do more than one action (list routines, "
347  "view routines or view model files.\n");
348  return -1;
349  }
350 
351  options->model_files[options->n_plots] = optarg;
352 
353  options->n_plots = options->n_plots + 1;
354 
355  if(options->n_plots >= PMM_MAX_PLOTS) {
356  ERRPRINTF("Cannot plot more than %d routines at once.\n",
357  PMM_MAX_PLOTS);
358  return -1;
359  }
360  break;
361 
362  case 'w':
363  if(options->enter_interactive != 0) {
364  ERRPRINTF("Cannot replot when interactive mode is set by -I\n");
365  return -1;
366  }
367  options->wait_period = atoi(optarg);
368  break;
369 
370  case 'l':
371  // if option is NULL set to PRINT_LIST
372  if(options->action == PMM_VIEW_NULL) {
373  options->action = PMM_VIEW_PRINT_LIST;
374  }
375  // otherwise, action has already been set, this is error
376  else {
377  ERRPRINTF("Cannot do more than one action (list routines, "
378  "view routines or view model files.\n");
379  return -1;
380  }
381  break;
382  case 'o':
383  options->plot_output_file = optarg;
384  break;
385 
386  case 'g':
387  options->plot_output_grayscale = 1;
388  break;
389 
390  case 'h':
391  default:
392  return -1;
393  }
394 
395  }
396 
397  if(optind < argc) {
398  return -1;
399  }
400 
401  //make sure an action was specified
402  if(options->action == PMM_VIEW_NULL) {
403  return -1;
404  }
405 
406  return 0; //success
407 }
408 
409 /*!
410  * Count the number of tokens in a atring seperated by a delimiter
411  *
412  * @param str pointer to the string
413  * @param delimiters pointer to a string listing the delimiters
414  *
415  * @return number of tokens or -1 on error
416  *
417  * \warning Not thread safe.
418  */
419 int
420 count_tokens_in_str(char *str, char *delimiters)
421 {
422  char *str_copy;
423  char *token;
424  int count;
425 
426  count = 0;
427 
428  str_copy = strdup(str);
429  if(str_copy == NULL) {
430  ERRPRINTF("Error duplicating string.\n");
431  return -1;
432  }
433 
434  token = strtok(str_copy, delimiters);
435  while(token != NULL) {
436 
437  token = strtok(NULL, delimiters);
438  count++;
439 
440  }
441 
442  free(str_copy);
443  str_copy = NULL;
444 
445  return count;
446 }
447 
448 /*!
449  * Parse the slice string into a arrays of parameter index and parameter value
450  * of a pmm_view_options structure.
451  *
452  * The slice string format is a comma seperated list of pairs. Each pair is
453  * seperated by a colon. The first element of the pair describes a parameter
454  * index in the form pN, where N is a number. The second element of the pair
455  * is either min, max or a integer value, describing the value that parameter
456  * should be fixed at.
457  *
458  * To specify a slice all but 1 or 2 parameters must have their values fixed
459  * as specified by the string. If 1 parameter is left free the model will
460  * be a 2d plot, if 2 parameters are left free the plot will be a 3d surface.
461  *
462  * @param slice_str pointer to the slice string
463  * @param options pointer to the pmm_view_options structure
464  *
465  * @return 0 on success, -1 on failure
466  *
467  * \warning Not thread safe.
468  */
469 int
470 parse_slice_str(char *slice_str, struct pmm_view_options *options)
471 {
472 
473  char *str_copy;
474  const char *delimiters = ":,";
475  char *token;
476  enum states {SLICE_PINDEX, SLICE_PVALUE} state;
477 
478 
479  int i;
480  int n; //number of tokens
481  int p_index = 0; /* TODO no need for switch statement below which emits
482  a warning if p_index is not initialized because parsing
483  into p_index is a conditional operation. Same can be
484  achieved by replacing the switch and states with a
485  two step parsing of the string */
486 
487  int p_value;
488 
489 
490 
491  n = count_tokens_in_str(slice_str, ",");
492  if(n == -1) {
493  ERRPRINTF("Error counting tokens in slice string.\n");
494  return -1;
495  }
496  if(n == 0) {
497  ERRPRINTF("No tokens in slice string.\n");
498  return -1;
499  }
500 
501  options->slice_i_arr = malloc(n * sizeof *(options->slice_i_arr));
502  options->slice_val_arr = malloc(n * sizeof *(options->slice_val_arr));
503 
504  if(options->slice_i_arr == NULL || options->slice_val_arr == NULL) {
505  ERRPRINTF("Error allocating memory.\n");
506  return -1;
507  }
508 
509  options->slice_arr_size = n;
510 
511  str_copy = strdup(slice_str);
512  if(str_copy == NULL) {
513  ERRPRINTF("Error duplicating slice string.\n");
514  return -1;
515  }
516 
517  state = SLICE_PINDEX;
518  token = strtok(str_copy, delimiters);
519 
520  i=0;
521  while(token != NULL) {
522  DBGPRINTF("token:%s\n", token);
523  switch(state) {
524  case SLICE_PINDEX :
525  if(strncmp(token, "p", 1) == 0) {
526  p_index = atoi(&(token[1]));
527  state = SLICE_PVALUE;
528  }
529  else {
530  ERRPRINTF("Error parsing slice string.\n");
531 
532  free(str_copy);
533  str_copy = NULL;
534 
535  return -1;
536  }
537  break;
538 
539  case SLICE_PVALUE :
540  if(strcmp(token, "max") == 0) {
541  options->slice_i_arr[i] = p_index;
542  options->slice_val_arr[i] = -1;
543  i++;
544  }
545  else if(strcmp(token, "min") == 0) {
546  options->slice_i_arr[i] = p_index;
547  options->slice_val_arr[i] = -2;
548  i++;
549  }
550  else {
551 
552  p_value = atoi(token);
553 
554  options->slice_i_arr[i] = p_index;
555  options->slice_val_arr[i] = p_value;
556  i++;
557  }
558 
559  DBGPRINTF("slice_i:%d slice_val:%d i:%d\n",
560  options->slice_i_arr[i-1],
561  options->slice_val_arr[i-1],
562  i-1);
563 
564  state = SLICE_PINDEX;
565 
566  break;
567 
568  default :
569  break;
570  }
571 
572  token = strtok(NULL, delimiters);
573 
574  }
575 
576  if(i != n) {
577  ERRPRINTF("Unexpected number of slice tokens parsed (i:%d n:%d).\n",
578  i, n);
579 
580  free(str_copy);
581  str_copy = NULL;
582 
583  return -1;
584  }
585 
586 
587  return 0;
588 }