pmm  1.0.0
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
pmm_cfgparser.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_cfgparser.c
23  *
24  * @brief Functions for handling xml data files
25  *
26  * This file contains code for parsing and writing xml files which store
27  * the various data structures of the program, those being models, load history
28  * and the pmm daemon configuration file
29  */
30 #if HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 
34 #include <stdio.h> // for perror
35 #include <string.h> // for strcmp
36 #include <stdlib.h> // for mkstemp, atoi
37 #include <time.h> // for timeval
38 #include <errno.h> // for perror
39 #include <sys/stat.h> // for open
40 #include <sys/types.h> // for open
41 #include <unistd.h> // for fcntl
42 #include <fcntl.h> // for fcntl/open
43 #include <libgen.h> // for dirname
44 #include <limits.h> // for PATH_MAX
45 
46 #include <libxml/xmlmemory.h>
47 #include <libxml/parser.h>
48 
49 
50 #include "pmm_model.h"
51 #include "pmm_cfgparser.h"
52 #include "pmm_muparse.h"
53 #include "pmm_interval.h"
54 #include "pmm_param.h"
55 #include "pmm_load.h"
56 
57 #include "pmm_log.h"
58 
59 
60 struct pmm_loadhistory* parse_loadconfig(xmlDocPtr, xmlNodePtr node);
61 
62 int sync_parent_dir(char *file_path);
63 
64 int
65 parse_paramdef_set(struct pmm_paramdef_set *pd_set, xmlDocPtr doc,
66  xmlNodePtr node);
67 int
68 parse_paramdef(struct pmm_paramdef *pd_array, int n_p, xmlDocPtr doc,
69  xmlNodePtr node);
70 int
71 parse_param_constraint(struct pmm_paramdef_set *pd_set, xmlDocPtr doc,
72  xmlNodePtr node);
73 
74 int
75 parse_bench_list(struct pmm_model *m, xmlDocPtr doc, xmlNodePtr node);
76 int
77 parse_routine_construction(struct pmm_routine *r, xmlDocPtr doc,
78  xmlNodePtr node);
79 
80 
81 /*
82  * TODO Finish this code
83  *
84  * Convert hours and minutes to a time structure
85  *
86 struct tm hrmm_to_tm(char *key)
87  struct tm time;
88 
89 
90 }*/
91 
92 /*
93  * TODO finish this routine, decide on how/if to support a crontab like
94  * period, with week day support and other things
95  *
96 void parse_routine_periodic(node, doc, struct pmm_routine *r) {
97 
98  while(node != NULL) {
99  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
100 
101  if(!xmlStrcmp(cnode->name (const xmlChar *) "starttime")) {
102  r->between_start = hrmm_to_tm(key);
103  }
104  else if(!xmlStrcmp(cnode->name (const xmlChar *) "endtime")) {
105  r->between_end = hrmm_to_tm(key);
106  }
107 
108  free(key);
109  key = NULL;
110 
111  node=node->next;
112  }
113 }*/
114 
115 /*!
116  * Parse a routine from an xml document
117  *
118  * @param doc pointer to the xml document
119  * @param node pointer to the node describing the routine
120  *
121  * @returns pointer to a routine structure containing the parsed data, or NULL
122  * on failure
123  */
124 struct pmm_routine*
125 parse_routine(xmlDocPtr doc, xmlNodePtr node)
126 {
127 
128  char *key;
129  struct pmm_routine *r;
130  xmlNodePtr cnode;
131 
132  r = new_routine();
133  if(r == NULL) {
134  ERRPRINTF("Error creating new routine.\n");
135  return NULL;
136  }
137 
138  // get the children of the routine node
139  cnode = node->xmlChildrenNode;
140 
141  // iterate through the children of the routine node
142  while(cnode != NULL) {
143 
144  // get the value associated with the each node
145  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
146 
147  // if the name of the cnode is ... do something with the key
148  if(!xmlStrcmp(cnode->name, (const xmlChar *) "name")) {
149  if(!set_str(&(r->name), key)) {
150  ERRPRINTF("set_str failed setting name\n");
151  return NULL; //TODO possibly free allocated routine before
152  }
153  }
154  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "exe_path")) {
155  if(!set_str(&(r->exe_path), key)) {
156  ERRPRINTF("set_str failed setting exe_path\n");
157  return NULL;
158  }
159  }
160  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "exe_args")) {
161  if(!set_str(&(r->exe_args), key)) {
162  ERRPRINTF("set_str failed setting exe_args\n");
163  return NULL;
164  }
165  }
166  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "model_path")) {
167  if(!set_str(&(r->model->model_path), key)) {
168  ERRPRINTF("set_str failed failed setting model_path\n");
169  return NULL;
170  }
171  }
172  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "parameters")) {
173  if(parse_paramdef_set(r->pd_set, doc, cnode) < 0)
174  {
175  ERRPRINTF("Error parsing parameter definitions.\n");
176  return NULL;
177  }
178  }
179  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "condition")) {
180  if(strcmp("now", key) == 0) {
181  r->condition = CC_NOW;
182  }
183  /* TODO and add code to support CC_BEFORE
184  else if(strcmp("before", key) == 0) {
185  r->condition = CC_UNTIL;
186  } */
187  else if(strcmp("idle", key) == 0) {
188  r->condition = CC_IDLE;
189  }
190  else if(strcmp("nousers", key) == 0) {
191  r->condition = CC_NOUSERS;
192  }
193  /* TODO add code to support CC_PERIODIC
194  else if(strcmp("periodic", key) == 0) {
195  r->condition = CC_PERIODIC;
196 
197  parse_routine_periodic(cnode->xmlChildren, doc, &r);
198 
199  }
200  */
201  else {
202  ERRPRINTF("Configuration error, routine:%s, condition:%s\n",
203  r->name,
204  key);
205  return NULL;
206  }
207  }
208  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "priority")) {
209  r->priority = atoi((char *)key);
210  }
211  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "construction")) {
212  if(parse_routine_construction(r, doc, cnode) < 0) {
213  ERRPRINTF("Error parsing construction method definition.\n");
214  return NULL;
215  }
216  }
217  else {
218  // probably a text : null tag
219  // TODO suppress these and check everywhere else
220  //ERRPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
221  }
222 
223  free(key);
224  key = NULL;
225 
226  cnode=cnode->next;
227  }
228 
229  if(!check_routine(r)) {
230  ERRPRINTF("Routine not parsed correctly.\n");
231  return NULL;
232  }
233 
234  return r;
235 }
236 
237 /*!
238  * Parse multiple xml parameter definitions into a paramdef array
239  *
240  * @param pd_set pointer to parameter definition set structure
241  * @param doc pointer to the xml document to parse
242  * @param node pointer to the node of the xml tree describing the
243  * parameter definitions
244  *
245  * @return 0 on success, -1 on failure
246  */
247 int
249  xmlNodePtr node)
250 {
251  char *key;
252  xmlNodePtr cnode;
253  int i;
254 #ifdef HAVE_MUPARSER
255  double d;
256 #endif
257 
258  cnode = node->xmlChildrenNode;
259 
260  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
261 
262  // if the name of the cnode is ... do something with the key
263  if(!xmlStrcmp(cnode->name, (const xmlChar *) "n_p")) {
264  pd_set->n_p = atoi((char *)key);
265 
266  if(pd_set->n_p <= 0) {
267  ERRPRINTF("Number of parameters must be greater than 0.\n");
268  return -1;
269  }
270 
271  pd_set->pd_array = malloc(pd_set->n_p * sizeof *(pd_set->pd_array));
272 
273  if(pd_set->pd_array == NULL) {
274  ERRPRINTF("Error allocating memory.\n");
275  return -1;
276  }
277  }
278  else {
279  ERRPRINTF("First element of parameter_array xml must be size (n_p)\n.");
280  return -1;
281  }
282 
283  free(key);
284  key = NULL;
285 
286  cnode = cnode->next;
287 
288 
289  // from this point we can assume that r->paramdef_array has been allocated
290 
291 
292  // iterate through the children of the routine node
293  i = 0;
294  while(cnode != NULL) {
295 
296  // get the value associated with the each cnode
297  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
298 
299  // if the name of the cnode is ... do something with the key
300  if(!xmlStrcmp(cnode->name, (const xmlChar *) "param"))
301  {
302 
303  if(parse_paramdef(pd_set->pd_array, pd_set->n_p, doc, cnode) < 0) {
304  ERRPRINTF("Error parsring parameter defintion.\n");
305  return -1;
306  }
307  i++;
308 
309  }
310  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "param_constraint"))
311  {
312  if(parse_param_constraint(pd_set, doc, cnode) < 0) {
313  ERRPRINTF("Error parsing parameter contraint.\n");
314  return -1;
315  }
316  }
317  else
318  {
319  // probably a text : null tag
320  // TODO suppress these and check everywhere else
321  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
322  }
323 
324 
325  free(key);
326  key = NULL;
327 
328  cnode=cnode->next;
329  }
330 
331  if(i != pd_set->n_p) {
332  ERRPRINTF("Parsed unexpected number of param_defs. (%d of %d).\n",
333  i, pd_set->n_p);
334  return -1;
335  }
336 
337 #ifdef HAVE_MUPARSER
338  // if the pc_formula is set, construct the muParser for it
339  if(pd_set->pc_formula != NULL) {
341 
342  if(evaluate_constraint(pd_set->pc_parser, &d) == -1) {
343  ERRPRINTF("Error setting up param constraint formula parser.\n");
344  return -1;
345  }
346  }
347 #endif
348 
349 
350 
351  return 0; //success
352 
353 }
354 
355 /*!
356  * parse an xml parameter constraint defintiion into the paramdef set structure
357  *
358  * @param pd_set pointer to the paramdef set structure
359  * @param doc pointer to the xml document
360  * @param node pointer to the node of the xml document describing the
361  * parameter constraints
362  *
363  * @return 0 on success, -1 on failure
364  */
365 int
367  xmlNodePtr node)
368 {
369  char *key;
370  xmlNodePtr cnode;
371 
372  // get the children of the parameters node
373  cnode = node->xmlChildrenNode;
374 
375 
376  // iterate through the children of the routine node
377  while(cnode != NULL) {
378 
379  // get the value associated with the each cnode
380  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
381 
382  // if the name of the cnode is ... do something with the key
383  if(!xmlStrcmp(cnode->name, (const xmlChar *) "formula")) {
384  if(!set_str(&(pd_set->pc_formula), key)) {
385  ERRPRINTF("set_str failed setting name\n");
386  return -1;
387  }
388  }
389  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "max")) {
390  pd_set->pc_max = atoi((char *)key);
391  }
392  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "min")) {
393  pd_set->pc_min = atoi((char *)key);
394  }
395  else {
396  // probably a text : null tag
397  // TODO suppress these and check everywhere else
398  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
399  }
400 
401  free(key);
402  key = NULL;
403 
404  cnode=cnode->next;
405  }
406 
407  return 0; //success
408 }
409 /*!
410  * parse an xml parameter definition into the paramdef array of a routine
411  *
412  * @param pd_array pointer to the parameter definition array
413  * @param n_p number of parameter defintions
414  * @param doc pointer to the xml document
415  * @param node pointer to node of xml document describing parameter def
416  *
417  * @return 0 on success, -1 on failure
418  */
419 int
420 parse_paramdef(struct pmm_paramdef *pd_array, int n_p, xmlDocPtr doc,
421  xmlNodePtr node)
422 {
423  char *key;
424  xmlNodePtr cnode;
425  struct pmm_paramdef p;
426 
427  // get the children of the parameters node
428  cnode = node->xmlChildrenNode;
429 
430  p.order = -1; // check this after parsing xml
431  p.nonzero_end = 0; // default is that end is zero speed
432  p.stride = 1; //default stride is 1
433  p.offset = 0; //default offset is 0
434 
435  // iterate through the children of the routine node
436  while(cnode != NULL) {
437 
438  // get the value associated with the each cnode
439  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
440 
441  // if the name of the cnode is ... do something with the key
442  if(!xmlStrcmp(cnode->name, (const xmlChar *) "order")) {
443  p.order = atoi((char *)key);
444 
445  }
446  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "name")) {
447  if(!set_str(&(p.name), key)) {
448  ERRPRINTF("set_str failed setting name\n");
449  return -1;
450  }
451  }
452  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "max") ||
453  !xmlStrcmp(cnode->name, (const xmlChar *) "end")) {
454  p.end = atoi((char *)key);
455  }
456  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "min") ||
457  !xmlStrcmp(cnode->name, (const xmlChar *) "start")) {
458  p.start = atoi((char *)key);
459  }
460  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "stride")) {
461  p.stride = atoi((char *)key);
462  }
463  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "offset")) {
464  p.offset = atoi((char *)key);
465  }
466  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "fuzzy_max") ||
467  !xmlStrcmp(cnode->name, (const xmlChar *) "nonzero_end")) {
468  if(strcmp("yes", key) == 0 ||
469  strcmp("true", key) == 0 ||
470  strcmp("1", key) == 0)
471  {
472  p.nonzero_end = 1;
473  }
474  else if(strcmp("false", key) == 0 ||
475  strcmp("no", key) == 0 ||
476  strcmp("0", key) == 0)
477  {
478  p.nonzero_end = 0;
479 
480  }
481  else {
482  ERRPRINTF("Error parsing nonzero_end value : %s\n", key);
483  return -1;
484  }
485  }
486  else {
487  // probably a text : null tag
488  // TODO suppress these and check everywhere else
489  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
490  }
491 
492  free(key);
493  key = NULL;
494 
495  cnode=cnode->next;
496  }
497 
498  if(p.order < 0 || p.order > n_p) {
499  ERRPRINTF("paramdef has an order that is out of bounds order:%d.\n",
500  p.order);
501  free(p.name);
502  p.name = NULL;
503 
504  return -1;
505  }
506 
507  //copy p into the order-th element of r's paramdef_array
508  if(copy_paramdef(&(pd_array[p.order]), &p) < 0) {
509  ERRPRINTF("Error copying parsed paramdef into parameter array.\n");
510  free(p.name);
511  p.name = NULL;
512 
513  return -1;
514  }
515 
516  free(p.name);
517  p.name = NULL;
518 
519  return 0; //success
520 
521 }
522 
523 /*!
524  * Parse routine construction information from xml specification.
525  *
526  * @param r pointer to the corresponding routine
527  * @param doc pointer to the xml document
528  * @param node pointer to the construction information node in the xml doc
529  *
530  * @return 0 on success, -1 on failure
531  */
532 int
533 parse_routine_construction(struct pmm_routine *r, xmlDocPtr doc,
534  xmlNodePtr node)
535 {
536  char *key;
537  xmlNodePtr cnode;
538 
539  // get the children of the routine node
540  cnode = node->xmlChildrenNode;
541 
542  // iterate through the children of the routine node
543  while(cnode != NULL) {
544 
545  // get the value associated with the each cnode
546  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
547 
548  // if the name of the cnode is ... do something with the key
549  if(!xmlStrcmp(cnode->name, (const xmlChar *) "method")) {
550  if(!xmlStrcmp((const xmlChar *) "naive", (xmlChar *) key))
551  {
553  }
554  else if(!xmlStrcmp((const xmlChar *)"naive_bisect", (xmlChar *)key))
555  {
557  }
558  else if(!xmlStrcmp((const xmlChar *) "gbbp", (xmlChar *) key))
559  {
561  }
562  else if(!xmlStrcmp((const xmlChar *) "gbbp_naive", (xmlChar *) key))
563  {
565  }
566  else if(!xmlStrcmp((const xmlChar *) "rand", (xmlChar *) key))
567  {
569  }
570  else
571  {
572  LOGPRINTF("construction method unrecognised: %s\n", key);
574  }
575  }
576  if(!xmlStrcmp(cnode->name, (const xmlChar *) "min_sample_num")) {
577  r->min_sample_num = atoi(key);
578  }
579  if(!xmlStrcmp(cnode->name, (const xmlChar *) "min_sample_time")) {
580  r->min_sample_time = atoi(key);
581  }
582  if(!xmlStrcmp(cnode->name, (const xmlChar *) "max_completion")) {
583  r->max_completion = atoi(key);
584  }
585  else {
586  // probably a text : null tag
587  // TODO suppress these and check everywhere else
588  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
589  }
590 
591  free(key);
592  key = NULL;
593 
594  cnode=cnode->next;
595  }
596 
597  return 0; //success
598 
599 }
600 
601 /*!
602  * Parse xml config file into config structure.
603  *
604  * @param cfg pointer to config structure to store parsed data
605  *
606  * @return 0 on success, -1 on failure
607  *
608  * @pre cfg points to an allocated pmm_config structure
609  * @pre cfg structure member 'configfile' must be set to the config file
610  * path or this function will return an error
611  */
612 int
613 parse_config(struct pmm_config *cfg) {
614 
615  xmlDocPtr doc;
616  xmlNodePtr root, cnode;
617  char *key;
618  struct pmm_routine *r;
619  int routine_count = 0; // number of routines
620 
621  // parse the file into a doc tree
622  doc = xmlReadFile(cfg->configfile, NULL, XML_PARSE_NOBLANKS);
623 
624  // check file was parsed
625  if(doc == NULL) {
626  ERRPRINTF("Config file: %s not parsed correctly\n", cfg->configfile);
627  return -1; //failure
628  }
629 
630  // get root node of doc tree
631  root = xmlDocGetRootElement(doc);
632 
633  // check there is a root node (i.e. doc tree is not empty)
634  if(root == NULL) {
635  ERRPRINTF("Config file: %s empty\n", cfg->configfile);
636  xmlFreeDoc(doc);
637  return -1;
638  }
639 
640  // check that the root node is a "config" type
641  if(xmlStrcmp(root->name, (const xmlChar *) "config")) {
642  ERRPRINTF("Config file is of wrong type. root node: %s\n", root->name);
643  xmlFreeDoc(doc);
644  return -1;
645  }
646 
647  // get the child of the root node
648  cnode = root->xmlChildrenNode;
649 
650  // iterate through the children of the root counting the number of
651  // routines
652  while(cnode != NULL) {
653 
654  if(!xmlStrcmp(cnode->name, (const xmlChar *) "main_sleep_period")) {
655  // get the value associated with the cnode
656  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
657  double_to_timespec(atof(key), &(cfg->ts_main_sleep_period));
658  free(key);
659  key = NULL;
660  }
661  if(!xmlStrcmp(cnode->name,
662  (const xmlChar *) "model_write_time_threshold"))
663  {
664  // get the value associated with the cnode
665  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
666  cfg->time_spend_threshold = atoi(key);
667  free(key);
668  key = NULL;
669  }
670  if(!xmlStrcmp(cnode->name,
671  (const xmlChar *) "model_write_execs_threshold"))
672  {
673  // get the value associated with the cnode
674  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
675  cfg->num_execs_threshold = atoi(key);
676  free(key);
677  key = NULL;
678  }
679  // if we get a "load_monitor" cnode parse the load monitor config
680  if(!xmlStrcmp(cnode->name, (const xmlChar *) "load_monitor")) {
681  cfg->loadhistory = parse_loadconfig(doc, cnode);
682 
683  if(cfg->loadhistory == NULL) {
684  ERRPRINTF("Error parsing load history configuration.\n");
685  return -1;
686  }
687  }
688  // if we get a "routine" cnode parse the routine config
689  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "routine")) {
690  routine_count++;
691 
692  r = parse_routine(doc, cnode);
693  if(r == NULL) {
694  ERRPRINTF("Error parsing routine.\n");
695  return -1;
696  }
697 
698  if(add_routine(cfg, r) < 0) {
699  ERRPRINTF("Error adding routine to configuration.\n");
700  return -1;
701  }
702  }
703  else {
704  // probably a text : null tag
705  // TODO suppress these and check everywhere else
706  //LOGPRINTF("unexpected tag: %s\n", cnode->name);
707  }
708 
709  cnode=cnode->next;
710  }
711 
712 
713  xmlFreeDoc(doc);
714 
715  return 0;
716 }
717 
718 
719 /*!
720  * Parse load history configuration information from an xml document.
721  *
722  * @param doc pointer to the xml document
723  * @param node pointer to the node describing the load history config
724  *
725  * @return pointer to loadhistory structure or NULL on failure
726  *
727  * TODO FIX naming
728  */
729 struct pmm_loadhistory*
730 parse_loadconfig(xmlDocPtr doc, xmlNodePtr node)
731 {
732 
733  char *key;
734  struct pmm_loadhistory *h;
735  xmlNodePtr cnode;
736 
737  h = new_loadhistory();
738  if(h == NULL) {
739  ERRPRINTF("Error creating new loadhistory structure.\n");
740  return NULL;
741  }
742 
743  // get the children of the loadconfig node
744  cnode = node->xmlChildrenNode;
745 
746  // iterate through the children of the loadconfig node
747  while(cnode != NULL) {
748 
749  // get the value associated with the each cnode
750  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
751 
752  // if the name of the cnode is ... do something with the key
753  if(!xmlStrcmp(cnode->name, (const xmlChar *) "load_path")) {
754  if(!set_str(&(h->load_path), key)) {
755  ERRPRINTF("set_str failed setting load_path\n");
756  return NULL; //TODO free load history before returning
757  }
758  }
759  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "write_period")) {
760  h->write_period = atoi(key);
761  }
762  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "history_size")) {
763  if(init_loadhistory(h, atoi(key)) < 0) {
764  ERRPRINTF("Error initializing load history.\n");
765  return NULL;
766  }
767  }
768  else {
769  // probably a text : null tag
770  // TODO suppress these and check everywhere else
771  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
772  }
773 
774  free(key);
775  key = NULL;
776 
777  cnode=cnode->next;
778  }
779 
780  if(!check_loadhistory(h)) {
781  ERRPRINTF("load history config not parsed correctly.\n");
782  return NULL;
783  }
784 
785  return h;
786 }
787 
788 /*!
789  * Parse xml description of a timeval into a newly allocated timeval structure
790  *
791  * @param doc pointer to the xml document
792  * @param node pointer to the node describing the timeval
793  *
794  * @return pointer to a timeval structure or NULL on failure
795  */
796 struct timeval* parse_timeval(xmlDocPtr doc, xmlNodePtr node) {
797  char *key;
798  struct timeval *t;
799  xmlNodePtr cnode;
800 
801  t = malloc(sizeof *t);
802  if(t == NULL) {
803  ERRPRINTF("Error allocating timeval structure");
804  return NULL;
805  }
806 
807  cnode = node->xmlChildrenNode;
808 
809  while(cnode != NULL) {
810  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
811 
812  // if the name of the cnode is ... do something with the key
813  if(!xmlStrcmp(cnode->name, (const xmlChar *) "secs")) {
814  t->tv_sec = atoll((char *)key);
815  }
816  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "usecs")) {
817  t->tv_usec = atoll((char *)key);
818  }
819  else {
820  // probably a text : null tag
821  // TODO suppress these and check everywhere else
822  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
823  }
824 
825  free(key);
826  key = NULL;
827 
828  cnode = cnode->next;
829  }
830 
831 
832  return t;
833 }
834 
835 /*!
836  * Parse a timeval from an xml doc.
837  *
838  * @param t pointer to the timeval structure to store to
839  * @param doc pointer to the xml document
840  * @param node pointer to the timeval node in the xml doc.
841  *
842  * @return 0 on success, or -1 on failure
843  */
844 int
845 parse_timeval_p(struct timeval *t, xmlDocPtr doc, xmlNodePtr node)
846 {
847  char *key;
848  xmlNodePtr cnode;
849 
850  cnode = node->xmlChildrenNode;
851 
852  while(cnode != NULL) {
853  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
854 
855  // LOGPRINTF("%s : %s\n", cnode->name, key);
856 
857  // if the name of the cnode is ... do something with the key
858  if(!xmlStrcmp(cnode->name, (const xmlChar *) "secs")) {
859  t->tv_sec = atoll((char *)key);
860  }
861  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "usecs")) {
862  t->tv_usec = atoll((char *)key);
863  }
864  else {
865  // probably a text : null tag
866  // TODO suppress these and check everywhere else
867  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
868  }
869 
870  free(key);
871  key = NULL;
872 
873  cnode = cnode->next;
874  }
875 
876  //check_timeval(t); //TODO
877 
878  return 0; //success
879 }
880 
881 /*!
882  * Parse an xml parameter array into a parameter array and size integer
883  *
884  * @param p pointer to the parameter array pointer
885  * @param n_p pointer to the integer to store the size in
886  * @param doc pointer to the xml document
887  * @param node pointer to the xml node that describes the param array
888  *
889  * @return 0 on succes, -1 on failure
890  */
891 int parse_parameter_array_p(int **p, int *n_p, xmlDocPtr doc, xmlNodePtr node)
892 {
893  char *key;
894  xmlNodePtr cnode;
895  int i;
896 
897  cnode = node->xmlChildrenNode;
898 
899  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
900 
901  // if the name of the cnode is ... do something with the key
902  if(!xmlStrcmp(cnode->name, (const xmlChar *) "n_p")) {
903  *n_p = atoi((char *)key);
904 
905  if(*n_p <= 0) {
906  ERRPRINTF("Number of parameters must be greater than 0.\n");
907  return -1;
908  }
909 
910  *p = malloc(*n_p * sizeof *(*p));
911 
912  if(*p == NULL) {
913  ERRPRINTF("Error allocating memory.\n");
914  return -1;
915  }
916  }
917  else {
918  ERRPRINTF("First element of parameter_array xml must be size (n_p)"
919  "got: %s\n", cnode->name);
920  return -1;
921  }
922 
923  free(key);
924  key = NULL;
925 
926  cnode = cnode->next;
927 
928 
929  // from this point we can assume that b->p has been allocated
930 
931  i=0;
932  while(cnode != NULL) {
933 
934  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
935 
936  if(!xmlStrcmp(cnode->name, (const xmlChar *) "parameter")) {
937  if(i < *n_p) {
938  (*p)[i++] = atoll((char *)key);
939  }
940  else {
941  ERRPRINTF("Parsed more parameters than expected.\n");
942  free(key);
943  key = NULL;
944 
945  return -1;
946  }
947  }
948  else {
949  // probably a text : null tag
950  // TODO suppress these and check everywhere else
951  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
952  }
953 
954  free(key);
955  key = NULL;
956 
957  cnode = cnode->next;
958  }
959 
960  if(i != *n_p) {
961  ERRPRINTF("Parsed unexpected number of parameters. (%d of %d).\n",
962  i, *n_p);
963  return -1;
964  }
965 
966 
967  return 0; //success
968 }
969 
970 /*!
971  * Parse a benchmark from xml document.
972  *
973  * @param doc pointer to the xml document
974  * @param node pointer to the node describing the benchmark
975  *
976  * @return pointer to a newly allocated benchmark representing the parsed
977  * information or NULL on error
978  */
979 struct pmm_benchmark* parse_benchmark(xmlDocPtr doc, xmlNodePtr node) {
980  char *key;
981  struct pmm_benchmark *b;
982  xmlNodePtr cnode;
983 
984  b = new_benchmark();
985  if(b == NULL) {
986  ERRPRINTF("Error allocating new benchmark.\n");
987  return NULL;
988  }
989 
990  // get the children of the benchmark node
991  cnode = node->xmlChildrenNode;
992 
993  // iterate through the children of the routine node
994  while(cnode != NULL) {
995 
996  // get the value associated with the each cnode
997  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
998 
999  //LOGPRINTF("%s : %s\n", cnode->name, key);
1000 
1001  // if the name of the cnode is ... do something with the key
1002  if(!xmlStrcmp(cnode->name, (const xmlChar *) "parameter_array")) {
1003  if(parse_parameter_array_p(&(b->p), &(b->n_p), doc, cnode) < 0) {
1004  ERRPRINTF("Error parsing parameter_array.\n");
1005  free_benchmark(&b);
1006  return NULL;
1007  }
1008  }
1009  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "complexity")) {
1010  if(sscanf((char *)key, "%lld", &(b->complexity)) != 1) {
1011  ERRPRINTF("Error parsing complexity.\n");
1012  free_benchmark(&b);
1013  return NULL;
1014  }
1015  }
1016  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "flops")) {
1017  if(sscanf((char *)key, "%lf", &(b->flops)) != 1) {
1018  ERRPRINTF("Error parsing flops.\n");
1019  free_benchmark(&b);
1020  return NULL;
1021  }
1022  }
1023  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "seconds")) {
1024  if(sscanf((char *)key, "%lf", &(b->seconds)) != 1) {
1025  ERRPRINTF("Error parsing seconds.\n");
1026  free_benchmark(&b);
1027  return NULL;
1028  }
1029  }
1030  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "used_time")) {
1031  if(parse_timeval_p(&(b->used_t), doc, cnode) < 0) {
1032  ERRPRINTF("Error parsing used_time timeval.\n");
1033  free_benchmark(&b);
1034  return NULL;
1035  }
1036  }
1037  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "wall_time")) {
1038  if(parse_timeval_p(&(b->wall_t), doc, cnode) < 0) {
1039  ERRPRINTF("Error parsing wall_time timeval.\n");
1040  free_benchmark(&b);
1041  return NULL;
1042  }
1043  }
1044  else {
1045  // probably a text : null tag
1046  // TODO suppress these and check everywhere else
1047  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
1048  }
1049 
1050  free(key);
1051  key = NULL;
1052 
1053  cnode=cnode->next;
1054  }
1055 
1056  /* TODO Check benchmark structure is parsed correctly
1057  if(!check_benchmark(b)) {
1058  ERRPRINTF("Benchmark parsed incorrectly.\n");
1059  free(key);
1060  key = NULL;
1061 
1062  exit(EXIT_FAILURE);
1063  }
1064  */
1065 
1066  return b;
1067 }
1068 
1069 /*!
1070  * Parse an interval from an xml document.
1071  *
1072  * @param doc pointer to the xml document
1073  * @param node pointer to the node describing the interval
1074  *
1075  * @return newly allocated interval or NULL on failure.
1076  */
1077 struct pmm_interval* parse_interval(xmlDocPtr doc, xmlNodePtr node) {
1078  char *key;
1079  struct pmm_interval *i;
1080  xmlNodePtr cnode;
1081 
1082  i = new_interval();
1083  if(i == NULL) {
1084  ERRPRINTF("Error creating new interval.\n");
1085  return NULL;
1086  }
1087 
1088  // get the children of the benchmark node
1089  cnode = node->xmlChildrenNode;
1090 
1091  // iterate through the children of the routine node
1092  while(cnode != NULL) {
1093 
1094  // get the value associated with the each cnode
1095  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
1096 
1097  // if the name of the cnode is ... do something with the key
1098  if(!xmlStrcmp(cnode->name, (const xmlChar *) "plane")) {
1099  i->plane = atoi((char *)key);
1100  }
1101  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "climb_step")) {
1102  i->climb_step = atoi((char *)key);
1103  }
1104  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "start")) {
1105  //TODO fix this to check parameter_array node
1106  //(passing xmlChildrenNode skips over this node entirely)
1107  if(parse_parameter_array_p(&(i->start), &(i->n_p), doc,
1108  cnode->xmlChildrenNode) < 0)
1109  {
1110  ERRPRINTF("Error parsing parameter_array.\n");
1111  return NULL;
1112  }
1113  }
1114  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "end")) {
1115  //TODO fix this to check parameter_array node
1116  //(passing xmlChildrenNode skips over this node entirely)
1117  if(parse_parameter_array_p(&(i->end), &(i->n_p), doc,
1118  cnode->xmlChildrenNode) < 0)
1119  {
1120  ERRPRINTF("Error parsing parameter_array.\n");
1121  return NULL;
1122  }
1123  }
1124  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "type")) {
1125  if(!xmlStrcmp((const xmlChar *) "gbbp_empty", (xmlChar *) key)) {
1126  i->type = IT_GBBP_EMPTY;
1127  }
1128  else if(!xmlStrcmp((const xmlChar *) "gbbp_climb", (xmlChar *) key)) {
1129  i->type = IT_GBBP_CLIMB;
1130  }
1131  else if(!xmlStrcmp((const xmlChar *) "gbbp_bisect", (xmlChar *) key)) {
1132  i->type = IT_GBBP_BISECT;
1133  }
1134  else if(!xmlStrcmp((const xmlChar *) "gbbp_inflect", (xmlChar *) key)) {
1135  i->type = IT_GBBP_INFLECT;
1136  }
1137  else if(!xmlStrcmp((const xmlChar *) "point", (xmlChar *) key)) {
1138  i->type = IT_POINT;
1139  }
1140  else if(!xmlStrcmp((const xmlChar *) "boundary_complete", (xmlChar *) key)) {
1142  }
1143  else if(!xmlStrcmp((const xmlChar *) "complete", (xmlChar *) key)) {
1144  i->type = IT_COMPLETE;
1145  }
1146  }
1147  else {
1148  // probably a text : null tag
1149  // TODO suppress these and check everywhere else
1150  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
1151  }
1152 
1153  free(key);
1154  key = NULL;
1155 
1156  cnode=cnode->next;
1157  }
1158 
1159  /* TODO Check structure is parsed correctly
1160  if(!check_load(r)) {
1161  ERRPRINTF("Load parsing error.\n");
1162  free(key);
1163  key = NULL;
1164 
1165  exit(EXIT_FAILURE);
1166  }
1167  */
1168 
1169  return i;
1170 }
1171 
1172 
1173 /*!
1174  * Parse a load record from an xml document.
1175  *
1176  * @param doc pointer to the xml document
1177  * @param node pointer to the node describing the load
1178  *
1179  * @return pointer to a newly allocated load record or NULL on failure
1180  */
1181 struct pmm_load* parse_load(xmlDocPtr doc, xmlNodePtr node) {
1182 
1183  char *key;
1184  struct pmm_load *l;
1185  xmlNodePtr cnode;
1186 
1187  l = new_load();
1188  if(l == NULL) {
1189  ERRPRINTF("Error creating new load structure.\n");
1190  return NULL;
1191  }
1192 
1193  // get the children of the routine node
1194  cnode = node->xmlChildrenNode;
1195 
1196  // iterate through the children of the routine node
1197  while(cnode != NULL) {
1198 
1199  // get the value associated with the each cnode
1200  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
1201 
1202  //LOGPRINTF("%s : %s\n", cnode->name, key);
1203 
1204  // if the name of the cnode is ... do something with the key
1205  if(!xmlStrcmp(cnode->name, (const xmlChar *) "time")) {
1206  if((l->time = parseISO8601Date((char *)key)) == (time_t)-1) {
1207  ERRPRINTF("Error parsign time string.\n");
1208  free(key);
1209  return NULL;
1210  }
1211  }
1212  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "one_min")) {
1213  if(sscanf((char *)key, "%lf", &(l->load[0])) != 1) {
1214  ERRPRINTF("Error parsing 1 minute load.\n");
1215  free(key);
1216  return NULL;
1217  }
1218  }
1219  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "five_min")) {
1220  if(sscanf((char *)key, "%lf", &(l->load[1])) != 1) {
1221  ERRPRINTF("Error parsing 5 minute load.\n");
1222  free(key);
1223  return NULL;
1224  }
1225  }
1226  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "fifteen_min")) {
1227  if(sscanf((char *)key, "%lf", &(l->load[2])) != 1) {
1228  ERRPRINTF("Error parsing 15 minute load.\n");
1229  free(key);
1230  return NULL;
1231  }
1232 
1233  }
1234  else {
1235  // probably a text : null tag
1236  // TODO suppress these and check everywhere else
1237  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
1238  }
1239 
1240  free(key);
1241  key = NULL;
1242  cnode=cnode->next;
1243  }
1244 
1245  /* TODO Check Load structure is parsed correctly FREE if it is not!
1246  if(!check_load(r)) {
1247  ERRPRINTF("Load parsing error.\n");
1248  free(key);
1249  key = NULL;
1250 
1251  exit(EXIT_FAILURE);
1252  }
1253  */
1254 
1255  return l;
1256 }
1257 
1258 /*!
1259  * Parse the load history file.
1260  *
1261  * @param h pointer to a load history structure, already populated with
1262  * config data (i.e. the load history file path)
1263  *
1264  * @return 0 on success, -1 on failure.
1265  */
1266 int
1268 
1269  xmlDocPtr doc;
1270  xmlNodePtr root, cnode;
1271  struct pmm_load *l;
1272 
1273  int fd;
1274  struct flock fl;
1275 
1276  //open file descriptor on load history file
1277  fd = open(h->load_path, O_RDWR);
1278  if(fd == -1) {
1279  ERRPRINTF("Error opening load history file:%s\n", h->load_path);
1280  perror("open");
1281 
1282  if(errno == ENOENT) { //path does not exist
1283  return -1; //return code to init new history
1284  }
1285  else { //some other error
1286  return -2;
1287  }
1288  }
1289 
1290  //set up a read lock
1291  fl.l_type = F_RDLCK;
1292  fl.l_whence = SEEK_SET;
1293  fl.l_start = 0;
1294  fl.l_len = 0;
1295  fl.l_pid = 0;
1296 
1297  //get read lock on file
1298  if(fcntl(fd, F_SETLKW, &fl) == -1) {
1299  ERRPRINTF("Error getting read lock on file:%s\n", h->load_path);
1300  perror("fnctl");
1301  return -2; //failure
1302  }
1303 
1304  // parse the load in file to a doc tree
1305  doc = xmlReadFd(fd, h->load_path, NULL, XML_PARSE_NOBLANKS);
1306  if(doc == NULL) {
1307  ERRPRINTF("Load history file: %s not parsed correctly\n", h->load_path);
1308 
1309  xmlFreeDoc(doc);
1310  if(fd != -1) {
1311  close(fd);
1312  }
1313 
1314  return -2; // failure
1315  }
1316 
1317  //close file and free lock
1318  if(close(fd) < 0) {
1319  ERRPRINTF("Error closing load history file:%s.\n", h->load_path);
1320  perror("close");
1321 
1322  return -2; // failure
1323  }
1324 
1325  // get root node of doc tree
1326  root = xmlDocGetRootElement(doc);
1327 
1328  // check there is a root node (i.e. doc tree is not empty)
1329  if(root == NULL) {
1330  ERRPRINTF("Load history file: %s empty\n", h->load_path);
1331 
1332  xmlFreeDoc(doc);
1333  return -1; //return code to init new history
1334  }
1335 
1336  // check that the root node is a "loadhistory" type
1337  if(xmlStrcmp(root->name, (const xmlChar *) "loadhistory")) {
1338  ERRPRINTF("Load history file is of wrong type. root node: %s\n",
1339  root->name);
1340 
1341  xmlFreeDoc(doc);
1342  return -2; //failure
1343  }
1344 
1345  // get the child of the root node
1346  cnode = root->xmlChildrenNode;
1347 
1348  // iterate through the children of the root adding loads to the load history
1349  // structure
1350  while(cnode != NULL) {
1351 
1352  // LOGPRINTF("%s\n", cnode->name);
1353 
1354  // if we get a load cnode parse it and add to the load history struct
1355  if(!xmlStrcmp(cnode->name, (const xmlChar *) "load")) {
1356 
1357  l = parse_load(doc, cnode);
1358  if(l == NULL) {
1359  ERRPRINTF("Error parsing load.\n");
1360 
1361  xmlFreeDoc(doc);
1362  return -1; //failure
1363  }
1364 
1365  add_load(h, l);
1366 
1367  // add load actually copys the load object into the history so we
1368  // may free it at this point
1369  free(l);
1370  l = NULL;
1371  }
1372  else {
1373  // probably a text : null tag
1374  // TODO suppress these and check everywhere else
1375  //LOGPRINTF("unexpected tag: %s\n", cnode->name);
1376  }
1377 
1378  cnode=cnode->next;
1379  }
1380 
1381  xmlFreeDoc(doc);
1382  return 0; //success
1383 }
1384 
1385 /*!
1386  * Parse a bench list from an xml document into a model.
1387  *
1388  * @param m pointer to the model
1389  * @param doc pointer to the xml document
1390  * @param node pointer to the node describing the bench list
1391  *
1392  * @return 0 on success, -1 on failure
1393  */
1394 int
1395 parse_bench_list(struct pmm_model *m, xmlDocPtr doc, xmlNodePtr node)
1396 {
1397  int ret;
1398  char *key;
1399  struct pmm_benchmark *b;
1400  xmlNodePtr cnode;
1401 
1402  int size;
1403 
1404 
1405  cnode = node->xmlChildrenNode;
1406 
1407  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
1408 
1409  // if the name of the cnode is ... do something with the key
1410  // first node must be the size fail if otherwise
1411  // TODO parsing "size" from the xml first is not really required
1412  if(!xmlStrcmp(cnode->name, (const xmlChar *) "size")) {
1413 
1414  size = atoi((char *)key);
1415 
1416  m->bench_list = new_bench_list(m, m->n_p);
1417 
1418  }
1419  else {
1420  ERRPRINTF("First element of bench_list xml must be size "
1421  "got: %s\n.", cnode->name);
1422  return -1;
1423  }
1424 
1425  free(key);
1426  key = NULL;
1427 
1428  cnode = cnode->next;
1429 
1430  // iterate through the rest of the children of the bench_list node
1431  while(cnode != NULL) {
1432 
1433  // if the name of the cnode is ... do something with the key
1434  if(!xmlStrcmp(cnode->name, (const xmlChar *) "benchmark")) {
1435 
1436  b = parse_benchmark(doc, cnode);
1437 
1438  if(b == NULL) {
1439  ERRPRINTF("Error parsing benchmark.\n");
1440  return -1;
1441  }
1442 
1443  ret = insert_bench_into_list(m->bench_list, b);
1444  if(ret < 0) {
1445  free_benchmark(&b);
1446  ERRPRINTF("Error inserting bench into bench list.\n");
1447  return -1;
1448  }
1449  }
1450  else {
1451  // probably a text : null tag
1452  // TODO suppress these and check everywhere else
1453  //LOGPRINTF("unexpected tag: %s\n", cnode->name);
1454  }
1455 
1456  cnode=cnode->next;
1457  }
1458 
1459  if(m->bench_list->size != size) {
1460  ERRPRINTF("%d benchmarks parsed, expected %d\n", m->bench_list->size,
1461  size);
1462  return -1;
1463  }
1464 
1465  return 0; //success
1466 }
1467 
1468 /*!
1469  * Parse an interval list from an xml document into a model.
1470  *
1471  * @param m pointer to the model
1472  * @param doc pointer to the xml document
1473  * @param node pointer to the node beginning the description of the
1474  * interval list
1475  *
1476  * @return 0 on success, -1 on failure
1477  */
1478 int parse_interval_list(struct pmm_model *m, xmlDocPtr doc, xmlNodePtr node)
1479 {
1480 
1481  struct pmm_interval *i;
1482  xmlNodePtr cnode;
1483 
1484  // get the children of the routine node
1485  cnode = node->xmlChildrenNode;
1486 
1487  // iterate through the children of the routine node
1488  while(cnode != NULL) {
1489 
1490  // if the name of the cnode is ... do something
1491  if(!xmlStrcmp(cnode->name, (const xmlChar *) "interval")) {
1492  i = parse_interval(doc, cnode);
1493  if(i == NULL) {
1494  ERRPRINTF("Error parsing interval.\n");
1495  return -1;
1496  }
1497 
1498  //to preserve the order of the list as it is read from disk
1499  //new intervals are added to the end of the interval_list structure
1501  }
1502  else {
1503  // probably a text : null tag
1504  // TODO suppress these and check everywhere else
1505  //LOGPRINTF("unexpected tag: %s\n", cnode->name);
1506  }
1507 
1508  cnode=cnode->next;
1509  }
1510 
1511  return 0; //success
1512 }
1513 
1514 /*!
1515  * Parses a model from an xml file. The model structure is expected to have
1516  * the path of the file already stored.
1517  *
1518  * @param m pointer to the model
1519  *
1520  * @return 0 on success, -1 if no model file exists, -2 on other error
1521  */
1522 int parse_model(struct pmm_model *m)
1523 {
1524 
1525  xmlDocPtr doc;
1526  xmlNodePtr root, cnode;
1527  char *key;
1528 
1529  int fd;
1530  struct flock fl;
1531 
1532  int completion;
1533 
1534  struct pmm_paramdef_set *pd_set;
1535 
1536  pd_set = new_paramdef_set();
1537 
1538  if(m->bench_list != NULL) {
1539  ERRPRINTF("Error, attempting to parse model file into non empty model:"
1540  " %s completion:%d\n", m->model_path, m->completion);
1541  return -2;
1542  }
1543 
1544  //open file descriptor on model file
1545  fd = open(m->model_path, O_RDWR);
1546  if(fd == -1) {
1547  ERRPRINTF("Error opening model file:%s\n", m->model_path);
1548  perror("open");
1549 
1550  if(errno == ENOENT) { //file does not exist
1551  return -1; //return code to init new model
1552  }
1553  else { //some other error
1554  return -2;
1555  }
1556  }
1557 
1558  //set up a read lock
1559  fl.l_type = F_RDLCK;
1560  fl.l_whence = SEEK_SET;
1561  fl.l_start = 0;
1562  fl.l_len = 0;
1563  fl.l_pid = 0;
1564 
1565  //get read lock on file
1566  if(fcntl(fd, F_SETLKW, &fl) == -1) {
1567  ERRPRINTF("Error getting read lock on file:%s\n", m->model_path);
1568  perror("fnctl");
1569  return -2;
1570  }
1571 
1572  // parse the model in file to a doc tree
1573  doc = xmlReadFd(fd, m->model_path, NULL, XML_PARSE_NOBLANKS);
1574  if(doc == NULL) {
1575  ERRPRINTF("Model file: %s not parsed correctly continuing with new "
1576  "model\n", m->model_path);
1577 
1578  xmlFreeDoc(doc);
1579 
1580  if(fd == -1) {
1581  close(fd);
1582  }
1583 
1584  return -1; // file not read, return code to init new model
1585  }
1586 
1587  //close file and free lock
1588  if(close(fd) < 0) {
1589  ERRPRINTF("Error closing model file:%s.\n", m->model_path);
1590  perror("close");
1591 
1592  return -2; // failure
1593  }
1594 
1595  // get root node of doc tree
1596  root = xmlDocGetRootElement(doc);
1597 
1598  // check there is a root node (i.e. doc tree is not empty)
1599  if(root == NULL) {
1600  ERRPRINTF("Model file: %s empty\n", m->model_path);
1601  xmlFreeDoc(doc);
1602  return -1; // file empty, return code to init new model
1603  }
1604 
1605  // check that the root node is a "model" type
1606  if(xmlStrcmp(root->name, (const xmlChar *) "model")) {
1607  ERRPRINTF("Model xml has wrong type. root node: %s\n", root->name);
1608  xmlFreeDoc(doc);
1609  return -2; // model file corrupt, return failure;
1610  }
1611 
1612  // get the child of the root node
1613  cnode = root->xmlChildrenNode;
1614 
1615  // iterate through the children of the root adding data to the model
1616  while(cnode != NULL) {
1617 
1618  // get the value associated with the each cnode
1619  key = (char *)xmlNodeListGetString(doc, cnode->xmlChildrenNode, 1);
1620 
1621  // if the name of the cnode is ... do something with the key
1622  if(!xmlStrcmp(cnode->name, (const xmlChar *) "n_p")) {
1623  m->n_p = atoi((char *)key);
1624 
1625  if(m->parent_routine != NULL &&
1626  m->n_p != m->parent_routine->pd_set->n_p)
1627  {
1628  ERRPRINTF("model / routine parameter mismatch m:%d r:%d.\n",
1629  m->n_p, m->parent_routine->pd_set->n_p);
1630  xmlFreeDoc(doc);
1631  return -2; //failure
1632  }
1633  }
1634  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "completion")) {
1635  // store the completion variable so we can check later
1636  // that the number of parsed benchmarks matches
1637  completion = atoi((char *)key);
1638  }
1639  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "complete")) {
1640  m->complete = atoi((char *)key);
1641  }
1642  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "parameters")) {
1643 
1644 
1645  if(parse_paramdef_set(pd_set, doc, cnode) < 0)
1646  {
1647  ERRPRINTF("Error parsing parameter definitions.\n");
1648  xmlFreeDoc(doc);
1649  return -2; //failure
1650  }
1651 
1652 
1653  if(pd_set->n_p != m->n_p) {
1654  ERRPRINTF("Parameter definition and model mismatch.\n");
1655  }
1656 
1657  // if we have a parent routine, check that the param definitions
1658  // in the model match the routine
1659  if(m->parent_routine != NULL) {
1660  if(isequal_paramdef_set(m->parent_routine->pd_set, pd_set) != 0)
1661  {
1662  ERRPRINTF("Current parameter definitions do not match "
1663  "those initially used to build model.\n");
1664 
1665  ERRPRINTF("model:\n");
1666  print_paramdef_set(PMM_ERR, pd_set);
1667 
1668  ERRPRINTF("routine:\n");
1670 
1671  free_paramdef_set(&pd_set);
1672 
1673  return -2; // failure
1674  }
1675  // finished with the parsed model parameter definitions now
1676  free_paramdef_set(&pd_set);
1677  }
1678  else {
1679  m->pd_set = pd_set;
1680  }
1681 
1682 
1683  }
1684  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "bench_list")) {
1685  if(parse_bench_list(m, doc, cnode) < 0) {
1686  ERRPRINTF("Error parsing bench list.\n");
1687  xmlFreeDoc(doc);
1688  return -2; //failure
1689  }
1690  }
1691 
1692  else if(!xmlStrcmp(cnode->name, (const xmlChar *) "interval_list")) {
1693 
1694  if(parse_interval_list(m, doc, cnode) < 0) {
1695  ERRPRINTF("Error parsing interval list.\n");
1696  xmlFreeDoc(doc);
1697  return -2; //failure
1698  }
1699 
1700  }
1701  else {
1702  // probably a text : null tag
1703  // TODO suppress these and check everywhere else
1704  //LOGPRINTF("unexpected tag: %s / %s\n", cnode->name, (char *)key);
1705  }
1706 
1707  free(key);
1708  key = NULL;
1709 
1710  cnode=cnode->next;
1711  }
1712 
1713  xmlFreeDoc(doc);
1714 
1715 
1716  if(m->completion != completion) {
1717  ERRPRINTF("Model completion mismatch, model: %s, %d benchmarks parsed, %d expected\n",
1718  m->model_path, m->completion, completion);
1719  return -2;
1720  }
1721 
1722  return 0; //success
1723 }
1724 
1725 /*!
1726  * Parse models of routines listed in the config structure.
1727  *
1728  * @param c pointer to the config with all routine details
1729  *
1730  * @return 0 on success, -1 on failure
1731  */
1732 int parse_models(struct pmm_config *c)
1733 {
1734  int i, rc;
1735  struct pmm_routine *r;
1736  struct pmm_model *m;
1737 
1738  for(i=0; i<c->used; i++) {
1739  r = c->routines[i];
1740  m = r->model;
1741 
1742  LOGPRINTF("Loading model: %s for routine: %s\n",
1743  r->name, m->model_path);
1744 
1745  rc = parse_model(m);
1746  if(rc < 0) {
1747  if(rc == -1) {
1748  //model parsing failed, so initialize model with definitions
1749  //from routine
1750 
1751  m->n_p = r->pd_set->n_p;
1752  if(init_bench_list(m, r->pd_set) < 0){
1753  ERRPRINTF("Error initialising bench list.\n");
1754  return -1; //failure
1755  }
1756 
1757  }
1758  else {
1759  ERRPRINTF("Error parsing model.\n");
1760  return -1; //failure
1761  }
1762  }
1763  }
1764 
1765  return 0; //success
1766 }
1767 
1768 /*!
1769  * write an interval structure to disk in xml
1770  *
1771  * @param writer xmlTextWriterPtr to use to write with
1772  * @param i pointer to interval structure to write to disk
1773  *
1774  * @return 0 on success, -1 on failure
1775  */
1776 int write_interval_xtwp(xmlTextWriterPtr writer, struct pmm_interval *i)
1777 {
1778  int rc;
1779 
1780 
1781  // Start an element named interval
1782  rc = xmlTextWriterStartElement(writer, BAD_CAST "interval");
1783  if (rc < 0) {
1784  ERRPRINTF("Error @ xmlTextWriterStartElement (interval)\n");
1785  return rc;
1786  }
1787 
1788  switch (i->type) {
1789  case IT_GBBP_EMPTY :
1790  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "type", "%s",
1791  "gbbp_empty");
1792  break;
1793  case IT_GBBP_CLIMB :
1794  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "type", "%s",
1795  "gbbp_climb");
1796  break;
1797 
1798  case IT_GBBP_BISECT :
1799  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "type", "%s",
1800  "gbbp_bisect");
1801  break;
1802 
1803  case IT_GBBP_INFLECT :
1804  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "type", "%s",
1805  "gbbp_inflect");
1806  break;
1807 
1808  case IT_BOUNDARY_COMPLETE :
1809  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "type", "%s",
1810  "boundary_complete");
1811  break;
1812 
1813  case IT_POINT :
1814  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "type", "%s",
1815  "point");
1816  break;
1817 
1818  case IT_COMPLETE :
1819  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "type", "%s",
1820  "complete");
1821  break;
1822 
1823  default:
1824  ERRPRINTF("Invalid interval type: %s (%d)\n",
1826  print_interval(PMM_ERR, i);
1827  return -1; // fail
1828  }
1829 
1830  if (rc < 0) {
1831  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (type)\n");
1832  return rc;
1833  }
1834 
1835 
1836  switch (i->type) {
1837  case IT_GBBP_EMPTY :
1838  case IT_GBBP_CLIMB :
1839  // Add an element with name "climb_step" and value to interval.
1840  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "climb_step",
1841  "%d", i->climb_step);
1842  if (rc < 0) {
1843  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (plane)\n");
1844  return rc;
1845  }
1846  case IT_GBBP_BISECT :
1847  case IT_GBBP_INFLECT :
1848  // Add an element with name "plane" and value to interval.
1849  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "plane",
1850  "%d", i->plane);
1851  if (rc < 0) {
1852  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (plane)\n");
1853  return rc;
1854  }
1855 
1856 
1857  // Add an element with name "start"
1858  rc = xmlTextWriterStartElement(writer, BAD_CAST "start");
1859  if (rc < 0) {
1860  ERRPRINTF("Error @ xmlTextWriterStartElement (start)\n");
1861  return rc;
1862  }
1863  // write the start parameter array
1864  rc = write_parameter_array_xtwp(writer, i->start, i->n_p);
1865  if (rc < 0) {
1866  ERRPRINTF("Error writing parameter array (start)\n");
1867  return rc;
1868  }
1869  // Close the start element
1870  rc = xmlTextWriterEndElement(writer);
1871  if (rc < 0) {
1872  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
1873  return rc;
1874  }
1875 
1876 
1877  // Add an element with name "end"
1878  rc = xmlTextWriterStartElement(writer, BAD_CAST "end");
1879  if (rc < 0) {
1880  ERRPRINTF("Error @ xmlTextWriterStartElement (end)\n");
1881  return rc;
1882  }
1883  // write the end parameter array
1884  rc = write_parameter_array_xtwp(writer, i->end, i->n_p);
1885  if (rc < 0) {
1886  ERRPRINTF("Error writing parameter array (end)\n");
1887  return rc;
1888  }
1889  // Close the end element
1890  rc = xmlTextWriterEndElement(writer);
1891  if (rc < 0) {
1892  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
1893  return rc;
1894  }
1895 
1896  break;
1897 
1898  case IT_POINT :
1899  // Add an element with name "start"
1900  rc = xmlTextWriterStartElement(writer, BAD_CAST "start");
1901  if (rc < 0) {
1902  ERRPRINTF("Error @ xmlTextWriterStartElement (start)\n");
1903  return rc;
1904  }
1905  // write the start parameter array
1906  rc = write_parameter_array_xtwp(writer, i->start, i->n_p);
1907  if (rc < 0) {
1908  ERRPRINTF("Error writing parameter array (start)\n");
1909  return rc;
1910  }
1911  // Close the start element
1912  rc = xmlTextWriterEndElement(writer);
1913  if (rc < 0) {
1914  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
1915  return rc;
1916  }
1917 
1918  break;
1919 
1920  case IT_COMPLETE :
1921  case IT_BOUNDARY_COMPLETE :
1922  //nothing
1923  break;
1924 
1925  default :
1926  ERRPRINTF("Invalid interval type: %s (%d)\n",
1928  print_interval(PMM_ERR, i);
1929  return -1; //failure
1930  }
1931 
1932 
1933 
1934 
1935  // Close the interval element.
1936  rc = xmlTextWriterEndElement(writer);
1937  if (rc < 0) {
1938  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
1939  return rc;
1940  }
1941 
1942  return 0; //success
1943 }
1944 
1945 /*!
1946  * write the load history to file
1947  *
1948  * opens an xmlTextWriter and writes the history to the file specified
1949  * in the history structure
1950  *
1951  * @param h pointer to the load history
1952  *
1953  * @return 0 on success, -1 on failure
1954  */
1955 int
1957 {
1958  char *temp_file;
1959 
1960  int rc;
1961  xmlTextWriterPtr writer;
1962  xmlOutputBufferPtr output_buffer;
1963 
1964  struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0};
1965  int temp_fd, hist_fd;
1966 
1967  DBGPRINTF("writing file: %s\n", h->load_path);
1968 
1969  // create file name for mkstemp
1970  if(asprintf(&temp_file, "%s.XXXXXX", h->load_path)
1971  > PATH_MAX)
1972  {
1973  ERRPRINTF("filename too long: %s\n", temp_file);
1974 
1975  free(temp_file);
1976  temp_file = NULL;
1977 
1978  return -1; //fail
1979  }
1980 
1981 
1982  //open temp file
1983  temp_fd = mkstemp(temp_file);
1984  if(temp_fd == -1) {
1985  ERRPRINTF("Error opening temp file for writing: %s\n", temp_file);
1986  perror("mkstemp");
1987  return -1; //fail
1988  }
1989 
1990 
1991  //create output buffer
1992  output_buffer = xmlOutputBufferCreateFd(temp_fd, NULL);
1993  if(output_buffer == NULL) {
1994  ERRPRINTF("Error creating xml output buffer.\n");
1995 
1996  unlink(temp_file);
1997  close(temp_fd);
1998  free(temp_file);
1999  temp_file = NULL;
2000 
2001  return -1; //fail
2002  }
2003 
2004 
2005 
2006  //create xml writer
2007  writer = xmlNewTextWriter(output_buffer);
2008  if (writer == NULL) {
2009  ERRPRINTF("Error creating the xml writer\n");
2010 
2011  unlink(temp_file);
2012  close(temp_fd);
2013  free(temp_file);
2014  temp_file = NULL;
2015 
2016  return -1; //fail
2017  }
2018 
2019  rc = xmlTextWriterSetIndent(writer, 1);
2020  if(rc < 0) {
2021  ERRPRINTF("Error setting indent\n");
2022 
2023  xmlFreeTextWriter(writer);
2024  unlink(temp_file);
2025  close(temp_fd);
2026  free(temp_file);
2027  temp_file = NULL;
2028 
2029  return -1;
2030  }
2031 
2032  //write history
2033  rc = write_loadhistory_xtwp(writer, h);
2034  if(rc < 0) {
2035  ERRPRINTF("Error writing history, partial history saved in: %s\n",
2036  temp_file);
2037 
2038  xmlFreeTextWriter(writer);
2039  close(temp_fd);
2040  free(temp_file);
2041  temp_file = NULL;
2042 
2043  return -1;
2044  }
2045 
2046  xmlFreeTextWriter(writer);
2047 
2048  if(fsync(temp_fd) < 0) {
2049  ERRPRINTF("Error syncing data for file, remove:%s manually.\n",
2050  temp_file);
2051  perror("fsync");
2052 
2053  close(temp_fd);
2054  free(temp_file);
2055  temp_file = NULL;
2056 
2057  return -1;
2058 
2059  }
2060 
2061  //close temp file
2062  if(close(temp_fd) < 0) {
2063  ERRPRINTF("Error closing file, remove:%s manually\n", temp_file);
2064 
2065  free(temp_file);
2066  temp_file = NULL;
2067  return -1;
2068  }
2069 
2070  //open historyl file
2071  hist_fd = open(h->load_path, O_WRONLY|O_CREAT, S_IRWXU);
2072  if(hist_fd < 0) {
2073  ERRPRINTF("Error opening load file: %s.\n", h->load_path);
2074 
2075  free(temp_file);
2076  temp_file = NULL;
2077 
2078  return -1;
2079  }
2080 
2081  //set up a write lock
2082  fl.l_type = F_WRLCK;
2083  fl.l_whence = SEEK_SET;
2084  fl.l_start = 0;
2085  fl.l_len = 0;
2086  fl.l_pid = 0;
2087 
2088  //get write lock on load history file so reading processes will block
2089  if(fcntl(hist_fd, F_SETLKW, &fl) == -1) {
2090  ERRPRINTF("Error aquiring lock on load history file: %s\n",
2091  h->load_path);
2092  perror("fcntl");
2093 
2094  close(hist_fd);
2095  free(temp_file);
2096  temp_file = NULL;
2097 
2098  return -1;
2099  }
2100 
2101  //rename temp file to history file
2102  //TODO if we port to windows, this will not work
2103  if(rename(temp_file, h->load_path) < 0) {
2104  ERRPRINTF("Error renaming %s to %s, leaving %s on filesystem\n",
2105  temp_file, h->load_path, temp_file);
2106 
2107  close(hist_fd);
2108  free(temp_file);
2109  temp_file = NULL;
2110 
2111  return -1;
2112  }
2113 
2114  free(temp_file);
2115  temp_file = NULL;
2116 
2117  //close and free lock
2118  if(close(hist_fd) < 0) {
2119  ERRPRINTF("Error closing file: %s\n.", h->load_path);
2120 
2121  return -1;
2122  }
2123 
2124  //sync dir so we are sure everything is committed to the filesystem
2125  if(sync_parent_dir(h->load_path) < 0) {
2126  ERRPRINTF("Error syncing parent dir of: %s.\n", h->load_path);
2127 
2128  return -1;
2129  }
2130 
2131  return 0; //success
2132 }
2133 
2134 
2135 #define TIME_STR_SIZE 256 /*!< size of ISO 8601 time string */
2136 /*!
2137  *
2138  * Write load history to the xmlTextWriterPtr
2139  *
2140  * @param writer an xmlTextWriter pointer
2141  * @param h pointer to load load history structure
2142  *
2143  * @return 0 on success, -1 on failure
2144  */
2145 int write_loadhistory_xtwp(xmlTextWriterPtr writer, struct pmm_loadhistory *h)
2146 {
2147  int rc;
2148  int i;
2149  char time_str[TIME_STR_SIZE+1] = "";
2150  struct tm time_tm;
2151 
2152  // start document with standard version/enconding
2153  rc = xmlTextWriterStartDocument(writer, NULL, "ISO-8859-1", NULL);
2154  if (rc < 0) {
2155  ERRPRINTF("Error @ xmlTextWriterStartDocument\n");
2156  return rc;
2157  }
2158 
2159  // start root element loadhistory
2160  rc = xmlTextWriterStartElement(writer, BAD_CAST "loadhistory");
2161  if (rc < 0) {
2162  ERRPRINTF("Error @ xmlTextWriterStartElement (loadhistory)\n");
2163  return rc;
2164  }
2165 
2166  i = h->start_i;
2167  while(i != h->end_i) {
2168 
2169  // Start an element named load as child of loadhistory.
2170  rc = xmlTextWriterStartElement(writer, BAD_CAST "load");
2171  if (rc < 0) {
2172  ERRPRINTF("Error @ xmlTextWriterStartElement (load)\n");
2173  return rc;
2174  }
2175 
2176  // convert time_t value to ISO 8601
2177  localtime_r(&(h->history[i].time), &time_tm);
2178  if(0 == strftime(time_str, TIME_STR_SIZE,
2179  "%Y-%m-%dT%H:%M:%S%z",
2180  &time_tm))
2181  {
2182  ERRPRINTF("Error converting time_t to ISO 8601 string.\n");
2183  return -1;
2184  }
2185 
2186  // Add an element with name "time" and value to load.
2187  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "time",
2188  "%s", time_str);
2189  if (rc < 0) {
2190  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (time)\n");
2191  return rc;
2192  }
2193 
2194  // Add a comment with the human readable version of the time value
2195  //rc = xmlTextWriterWriteFormatComment(writer, "%s",
2196  // ctime((time_t*)&(h->history[i].time)));
2197  //if(rc < 0) {
2198  // ERRPRINTF("Error @ xmlTextWriterFormatComment (time)\n");
2199  // return rc;
2200  //}
2201 
2202 
2203  // Add an element with name one_min and value to load
2204  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "one_min",
2205  "%f", h->history[i].load[0]);
2206  if (rc < 0) {
2207  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (one_min)\n");
2208  return rc;
2209  }
2210  // Add an element with name five_min and value to load
2211  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "five_min",
2212  "%f", h->history[i].load[1]);
2213  if (rc < 0) {
2214  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (five_min)\n");
2215  return rc;
2216  }
2217  // Add an element with name fifteen_min and value to load
2218  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "fifteen_min",
2219  "%f", h->history[i].load[2]);
2220  if (rc < 0) {
2221  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (fifteen_min)\n");
2222  return rc;
2223  }
2224 
2225  // Close the load element.
2226  rc = xmlTextWriterEndElement(writer);
2227  if (rc < 0) {
2228  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
2229  return rc;
2230  }
2231 
2232  i = (i + 1) % h->size_mod;
2233 
2234  }
2235 
2236  // Close the root element named loadhistory (and all other open tags).
2237  rc = xmlTextWriterEndDocument(writer);
2238  if (rc < 0) {
2239  ERRPRINTF("Error @ xmlTextWriterEndDocument\n");
2240  return rc;
2241  }
2242 
2243  return 0; //success
2244 }
2245 
2246 /*!
2247  * Check if a file descriptor is locked
2248  *
2249  * @param fd the file descriptor to check
2250  * @param type the type of lock to test FD_WRLCK, FD_RDLCK, FD_RWLCK
2251  *
2252  * @return -1 on error, 0 if file is not locked, pid of owning process if file
2253  * is locked
2254  */
2255 int check_lock(int fd, int type)
2256 {
2257  struct flock fl;
2258 
2259  fl.l_type = type;
2260  fl.l_whence = SEEK_SET;
2261  fl.l_start = 0;
2262  fl.l_len = 0;
2263  fl.l_pid = 0;
2264 
2265  if(fcntl(fd, F_GETLK, &fl) == -1) {
2266  ERRPRINTF("Error checking lock status on fd: %d\n", fd);
2267  perror("fcntl\n");
2268  return -1;
2269  }
2270 
2271 
2272  if(fl.l_type == F_UNLCK) {
2273  return 0;
2274  }
2275  else {
2276  return fl.l_pid;
2277  }
2278 }
2279 
2280 /*!
2281  * write all models in the configuration
2282  *
2283  * @param cfg pointer to the configuration
2284  *
2285  * @return 0 on success, -1 on failure
2286  */
2287 int
2289 {
2290  int i;
2291  int ret = 0;
2292 
2293  LOGPRINTF("Writing models to disk.\n");
2294 
2295 
2296  for(i=0; i<cfg->used; i++) {
2297  if(write_model(cfg->routines[i]->model) < 0) {
2298  ERRPRINTF("Error writing model for routine: %s.\n",
2299  cfg->routines[i]->name);
2300  ret = -1;
2301  }
2302  }
2303 
2304  return ret;
2305 }
2306 
2307 /*!
2308  * write an individual model to disk
2309  *
2310  * @param m pointer to the model
2311  *
2312  * @return 0 on success, -1 on failure
2313  */
2314 int
2316 {
2317  char *temp_file;
2318 
2319  int rc;
2320  xmlTextWriterPtr writer;
2321  xmlOutputBufferPtr output_buffer;
2322 
2323  struct flock fl;
2324  int temp_fd, model_fd;
2325 
2326  DBGPRINTF("writing model file: %s\n", m->model_path);
2327 
2328  // create file name for mkstemp
2329  if(asprintf(&temp_file, "%s.XXXXXX", m->model_path)
2330  > PATH_MAX)
2331  {
2332  ERRPRINTF("filename too long: %s\n", temp_file);
2333 
2334  free(temp_file);
2335  temp_file = NULL;
2336  return -1; //fail
2337  }
2338 
2339  //open temp file
2340  temp_fd = mkstemp(temp_file);
2341  if(temp_fd == -1) {
2342  ERRPRINTF("Error opening temp file for writing: %s\n", temp_file);
2343  perror("mkstemp");
2344  return -1; //fail
2345  }
2346 
2347 
2348  //create output buffer
2349  output_buffer = xmlOutputBufferCreateFd(temp_fd, NULL);
2350  if(output_buffer == NULL) {
2351  ERRPRINTF("Error creating xml output buffer.\n");
2352 
2353  unlink(temp_file);
2354  close(temp_fd);
2355  free(temp_file);
2356  temp_file = NULL;
2357 
2358  return -1; //fail
2359  }
2360 
2361  //create xml writer
2362  writer = xmlNewTextWriter(output_buffer);
2363  if (writer == NULL) {
2364  ERRPRINTF("Error creating the xml writer\n");
2365 
2366  unlink(temp_file);
2367  close(temp_fd);
2368  free(temp_file);
2369  temp_file = NULL;
2370 
2371  return -1; //fail
2372  }
2373 
2374  rc = xmlTextWriterSetIndent(writer, 1);
2375  if(rc < 0) {
2376  ERRPRINTF("Error setting indent\n");
2377 
2378  xmlFreeTextWriter(writer);
2379  unlink(temp_file);
2380  close(temp_fd);
2381  free(temp_file);
2382  temp_file = NULL;
2383 
2384  return -1;
2385  }
2386 
2387  //write model
2388  rc = write_model_xtwp(writer, m);
2389  if(rc < 0) {
2390  ERRPRINTF("Error writing model, partial model saved in: %s\n",
2391  temp_file);
2392 
2393  xmlFreeTextWriter(writer);
2394  close(temp_fd);
2395  free(temp_file);
2396  temp_file = NULL;
2397 
2398  return -1;
2399  }
2400 
2401  xmlFreeTextWriter(writer);
2402 
2403  if(fsync(temp_fd) < 0) {
2404  ERRPRINTF("Error syncing data for file, remove:%s manually.\n",
2405  temp_file);
2406  perror("fsync");
2407 
2408  close(temp_fd);
2409  free(temp_file);
2410  temp_file = NULL;
2411 
2412  return -1;
2413 
2414  }
2415 
2416  //close temp file
2417  if(close(temp_fd) < 0) {
2418  ERRPRINTF("Error closing file, remove:%s manually\n", temp_file);
2419 
2420  free(temp_file);
2421  temp_file = NULL;
2422  return -1;
2423  }
2424 
2425  //open model file
2426  model_fd = open(m->model_path, O_WRONLY|O_CREAT, S_IRWXU);
2427  if(model_fd < 0) {
2428  ERRPRINTF("Error opening model file: %s.\n", m->model_path);
2429 
2430  free(temp_file);
2431  temp_file = NULL;
2432 
2433  return -1;
2434  }
2435 
2436  //set up a write lock
2437  fl.l_type = F_WRLCK;
2438  fl.l_whence = SEEK_SET;
2439  fl.l_start = 0;
2440  fl.l_len = 0;
2441  fl.l_pid = 0;
2442 
2443  //get write lock on model file so reading processes will block
2444  if(fcntl(model_fd, F_SETLKW, &fl) == -1) {
2445  ERRPRINTF("Error aquiring lock on model file: %s\n", m->model_path);
2446  perror("fcntl");
2447 
2448  close(model_fd);
2449  free(temp_file);
2450  temp_file = NULL;
2451 
2452  return -1;
2453  }
2454 
2455  //rename temp file to model file
2456  //TODO if we port to windows, this will not work
2457  if(rename(temp_file, m->model_path) < 0) {
2458  ERRPRINTF("Error renaming %s to %s, leaving %s on filesystem\n",
2459  temp_file, m->model_path, temp_file);
2460 
2461  close(model_fd);
2462  free(temp_file);
2463  temp_file = NULL;
2464 
2465  return -1;
2466  }
2467 
2468  free(temp_file);
2469  temp_file = NULL;
2470 
2471  //close and free lock
2472  if(close(model_fd) < 0) {
2473  ERRPRINTF("Error closing file: %s\n.", m->model_path);
2474 
2475  return -1;
2476  }
2477 
2478 
2479  //sync dir so we are sure everything is committed to the filesystem
2480  if(sync_parent_dir(m->model_path) < 0) {
2481  ERRPRINTF("Error syncing parent dir of: %s.\n", m->model_path);
2482 
2483  return -1;
2484  }
2485 
2486  m->unwritten_num_execs = 0;
2487  m->unwritten_time_spend = 0;
2488 
2489  return 0; //success
2490 }
2491 
2492 /*!
2493  * attempt to sync and close an open file descriptor.
2494  *
2495  * @param fd file discriptor
2496  *
2497  * @return 0 on success, -1 on failure
2498  */
2499 int
2501 {
2502  int ret = 0;
2503 
2504  //sync file
2505  if(fsync(fd) < 0) {
2506  ERRPRINTF("Error syncing data for file descriptor: %d.\n", fd);
2507  perror("fsync");
2508 
2509  ret = -1;
2510  }
2511 
2512  //close file
2513  if(close(fd) < 0) {
2514  ERRPRINTF("Error closing file descriptor: %d.\n", fd);
2515  perror("close");
2516 
2517  ret = -1;
2518  }
2519 
2520  return ret;
2521 }
2522 
2523 /*!
2524  * attempt to sync the directory contained by a file in a particular path
2525  *
2526  * @param file_path pointer to character array giving path of file
2527  *
2528  * @return 0 on success, -1 on failure
2529  */
2530 int
2531 sync_parent_dir(char *file_path)
2532 {
2533  char *dir_c;
2534  char *dir_name;
2535  int dir_fd;
2536 
2537  int ret = 0;
2538 
2539  dir_c = strdup(file_path);
2540  if(dir_c == NULL) {
2541  ERRPRINTF("Could not allocate memory to copy file_path.\n");
2542  perror("strdup");
2543 
2544  free(dir_c);
2545  dir_c = NULL;
2546 
2547  return -1;
2548  }
2549 
2550  dir_name = dirname(dir_c);
2551 
2552  dir_fd = open(dir_name, O_RDONLY);
2553  if(dir_fd < 0) {
2554  ERRPRINTF("Error opening directory: %s.\n", dir_name);
2555  perror("open");
2556 
2557  free(dir_c);
2558  dir_c = NULL;
2559 
2560  return -1;
2561  }
2562 
2563  if(fsync(dir_fd) < 0) {
2564  ERRPRINTF("Error fsycing directory: %s.\n", dir_name);
2565  perror("fsync");
2566 
2567  ret = -1;
2568  }
2569 
2570  if(close(dir_fd) < 0) {
2571  ERRPRINTF("Error closing directory: %s.\n", dir_name);
2572  perror("close");
2573 
2574  ret = -1;
2575  }
2576 
2577  free(dir_c);
2578  dir_c = NULL;
2579 
2580  return ret;
2581 }
2582 
2583 /*!
2584  * Write a model list to an xmlTextWriterPtr object
2585  *
2586  * @param writer xmlTextWriter pointer
2587  * @param m pointer to the model
2588  *
2589  * @returns 0 on success, -1 on failure
2590  */
2591 int write_model_xtwp(xmlTextWriterPtr writer, struct pmm_model *m)
2592 {
2593  int rc;
2594  struct pmm_interval *i;
2595 
2596  // start document with standard version/enconding
2597  rc = xmlTextWriterStartDocument(writer, NULL, "ISO-8859-1", NULL);
2598  if (rc < 0) {
2599  ERRPRINTF("Error @ xmlTextWriterStartDocument\n");
2600  return rc;
2601  }
2602 
2603  // start root element model
2604  rc = xmlTextWriterStartElement(writer, BAD_CAST "model");
2605  if (rc < 0) {
2606  ERRPRINTF("Error @ xmlTextWriterStartElement (model)\n");
2607  return rc;
2608  }
2609 
2610  // Add an element with name "completion" and value to the model
2611  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "completion",
2612  "%d", m->completion);
2613  if (rc < 0) {
2614  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (completion)\n");
2615  return rc;
2616  }
2617 
2618  // Add an element with name "n_p" and value to the model
2619  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "n_p",
2620  "%d", m->n_p);
2621  if (rc < 0) {
2622  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (n_p)\n");
2623  return rc;
2624  }
2625 
2626  // Add an element with name "complete" and value to the model
2627  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "complete",
2628  "%d", m->complete);
2629  if (rc < 0) {
2630  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (complete)\n");
2631  return rc;
2632  }
2633 
2634  // write the parameter definitions from the parent routine
2635  rc = write_paramdef_set_xtwp(writer, m->parent_routine->pd_set);
2636  if(rc < 0) {
2637  ERRPRINTF("Error in write_paramdef_set_xtwp.\n");
2638  return rc;
2639  }
2640 
2641  // start writing bench list
2642  rc = write_bench_list_xtwp(writer, m->bench_list);
2643  if(rc < 0) {
2644  ERRPRINTF("Error in write_bench_list_xtwp.\n");
2645  return rc;
2646  }
2647 
2648  // start interval_list element
2649  rc = xmlTextWriterStartElement(writer, BAD_CAST "interval_list");
2650  if (rc < 0) {
2651  ERRPRINTF("Error @ xmlTextWriterStartElement (interval_list)\n");
2652  return rc;
2653  }
2654 
2655  i = m->interval_list->top;
2656  while(i != NULL) {
2657 
2658  rc = write_interval_xtwp(writer, i);
2659  if (rc < 0) {
2660  ERRPRINTF("Error writing interval.\n");
2661  print_interval(PMM_ERR, i);
2662  return rc;
2663  }
2664 
2665  i = i->previous;
2666  }
2667 
2668  // Close the interval_list element
2669  rc = xmlTextWriterEndElement(writer);
2670  if (rc < 0) {
2671  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
2672  return rc;
2673  }
2674 
2675  // Close the root element named model (and all other open tags).
2676  rc = xmlTextWriterEndDocument(writer);
2677  if (rc < 0) {
2678  ERRPRINTF("Error @ xmlTextWriterEndDocument\n");
2679  return rc;
2680  }
2681 
2682  return 0; //success
2683 }
2684 
2685 /*!
2686  * Write a benchmark list to an xmlTextWriterPtr object
2687  *
2688  * @param writer xmlTextWriter pointer
2689  * @param bench_list pointer to the benchmark list
2690  *
2691  * @returns 0 on success, -1 on failure
2692  */
2693 int write_bench_list_xtwp(xmlTextWriterPtr writer,
2694  struct pmm_bench_list *bench_list)
2695 {
2696 
2697  int rc;
2698  struct pmm_benchmark *b;
2699 
2700  // start bench_list element
2701  rc = xmlTextWriterStartElement(writer, BAD_CAST "bench_list");
2702  if(rc < 0) {
2703  ERRPRINTF("Error @ xmlTextWriterStartElement (bench_list)\n");
2704  return rc;
2705  }
2706 
2707  // Add an element with the name "size" and value: size of bench list
2708  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "size", "%d",
2709  bench_list->size);
2710  if (rc < 0) {
2711  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (size)\n");
2712  return rc;
2713  }
2714 
2715 
2716  b = bench_list->first;
2717  while(b != NULL) {
2718  rc = write_benchmark_xtwp(writer, b);
2719 
2720  if(rc < 0) {
2721  ERRPRINTF("Error in write_benchmark_xtwp\n");
2722  return rc;
2723  }
2724  b = b->next;
2725  }
2726 
2727  // Close the bench_list element
2728  rc = xmlTextWriterEndElement(writer);
2729  if (rc < 0) {
2730  ERRPRINTF("Error @ xmlTextWriterEndElement (bench_list)\n");
2731  return rc;
2732  }
2733 
2734  return 0; //success
2735 }
2736 
2737 /*
2738  * TODO change all this so it can be reused for communication via socket
2739  */
2740 
2741 /*!
2742  * Write the parameter definition set to an xmlTextWriterPtr object
2743  *
2744  * @param writer xmlTextWriter pointer
2745  * @param pd_set pointer to the parameter defintion set
2746  *
2747  * @returns 0 on success, -1 on failure
2748  */
2749 int
2750 write_paramdef_set_xtwp(xmlTextWriterPtr writer,
2751  struct pmm_paramdef_set *pd_set)
2752 {
2753  int rc;
2754  int i;
2755 
2756  // start and element named parameters
2757  rc = xmlTextWriterStartElement(writer, BAD_CAST "parameters");
2758  if (rc < 0) {
2759  ERRPRINTF("Error @ xmlTextWriterStartElement (parameters)\n");
2760  return rc;
2761  }
2762 
2763  // add an element with name "n_p" and value number of parameters
2764  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "n_p",
2765  "%d", pd_set->n_p);
2766  if (rc < 0) {
2767  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (n_p)\n");
2768  return rc;
2769  }
2770 
2771  //write each parameter definition
2772  for(i=0; i<pd_set->n_p; i++) {
2773  rc = write_paramdef_xtwp(writer, &(pd_set->pd_array[i]));
2774  if (rc < 0) {
2775  ERRPRINTF("Error writing parameter definition\n");
2776  return rc;
2777  }
2778  }
2779 
2780  if(pd_set->pc_formula != NULL) {
2781  // start and element named param_constraint
2782  rc = xmlTextWriterStartElement(writer, BAD_CAST "param_constraint");
2783  if (rc < 0) {
2784  ERRPRINTF("Error @ xmlTextWriterStartElement (param_constraint)\n");
2785  return rc;
2786  }
2787 
2788  // add an element with name "pc_formula" and value of the formula string
2789  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "formula",
2790  "%s", pd_set->pc_formula);
2791  if (rc < 0) {
2792  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (pc_formula)\n");
2793  return rc;
2794  }
2795  // add an element with name "pc_max" and value of max parameter product
2796  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "pc_max",
2797  "%d", pd_set->pc_max);
2798  if (rc < 0) {
2799  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (pc_max)\n");
2800  return rc;
2801  }
2802  // add an element with name "pc_min" and value of min parameter product
2803  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "pc_min",
2804  "%d", pd_set->pc_min);
2805  if (rc < 0) {
2806  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (pc_min)\n");
2807  return rc;
2808  }
2809 
2810  // Close the param_constraint element.
2811  rc = xmlTextWriterEndElement(writer);
2812  if (rc < 0) {
2813  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
2814  return rc;
2815  }
2816  }
2817 
2818  // Close the parameters element.
2819  rc = xmlTextWriterEndElement(writer);
2820  if (rc < 0) {
2821  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
2822  return rc;
2823  }
2824 
2825  return 0;
2826 }
2827 
2828 /*!
2829  * Write a parameter definition to an xmlTextWriterPtr object
2830  *
2831  * @param writer xmlTextWriter pointer
2832  * @param pd pointer to the parameter definition to write
2833  *
2834  * @return 0 on success -1 on error
2835  */
2836 int
2837 write_paramdef_xtwp(xmlTextWriterPtr writer, struct pmm_paramdef *pd)
2838 {
2839  int rc;
2840 
2841  // start an element named param
2842  rc = xmlTextWriterStartElement(writer, BAD_CAST "param");
2843  if (rc < 0) {
2844  ERRPRINTF("Error @ xmlTextWriterStartElement (param)\n");
2845  return rc;
2846  }
2847 
2848  //add an element with name "order" and value of the order
2849  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "order", "%d",
2850  pd->order);
2851  if (rc < 0) {
2852  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (order)\n");
2853  return rc;
2854  }
2855 
2856  //add an element with name "name" and value of the name
2857  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "name", "%s",
2858  pd->name);
2859  if (rc < 0) {
2860  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (name)\n");
2861  return rc;
2862  }
2863 
2864  //add an element with name "start" and value of the start
2865  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "start", "%d",
2866  pd->start);
2867  if (rc < 0) {
2868  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (start)\n");
2869  return rc;
2870  }
2871 
2872  //add an element with name "end" and value of the end
2873  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "end", "%d",
2874  pd->end);
2875  if (rc < 0) {
2876  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (end)\n");
2877  return rc;
2878  }
2879 
2880  //add an element with name "stride" and value of the stride
2881  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "stride", "%d",
2882  pd->stride);
2883  if (rc < 0) {
2884  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (stride)\n");
2885  return rc;
2886  }
2887 
2888  //add an element with name "stride" and value of the stride
2889  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "offset", "%d",
2890  pd->offset);
2891  if (rc < 0) {
2892  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (offset)\n");
2893  return rc;
2894  }
2895 
2896  //add an element with name "nonzero_end" and value of the nonzero_end
2897  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "nonzero_end", "%d",
2898  pd->nonzero_end);
2899  if (rc < 0) {
2900  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (nonzero_end)\n");
2901  return rc;
2902  }
2903 
2904  // Close the param element.
2905  rc = xmlTextWriterEndElement(writer);
2906  if (rc < 0) {
2907  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
2908  return rc;
2909  }
2910 
2911  return 0;
2912 }
2913 
2914 
2915 /*!
2916  * Write a parameter array to an xmlTextWriterPtr object
2917  *
2918  * In the form:
2919  *
2920  * <parameter_array>
2921  * <n_p>n</n_p>
2922  * <parameter>p[0]</parameter>
2923  * <parameter> ....
2924  * </parameter_array>
2925  *
2926  * @param writer writer to use
2927  * @param p parameter array
2928  * @param n number of parameters in array
2929  *
2930  * @return 0 on succes, -1 on failure
2931  */
2932 int
2933 write_parameter_array_xtwp(xmlTextWriterPtr writer, int *p, int n)
2934 {
2935  int rc;
2936  int i;
2937 
2938 
2939  // start and element named parameter_array
2940  rc = xmlTextWriterStartElement(writer, BAD_CAST "parameter_array");
2941  if (rc < 0) {
2942  ERRPRINTF("Error @ xmlTextWriterStartElement (parameter_array)\n");
2943  return rc;
2944  }
2945 
2946  // add an element with name "n_p" and value number of parameters
2947  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "n_p",
2948  "%d", n);
2949  if (rc < 0) {
2950  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (n_p)\n");
2951  return rc;
2952  }
2953 
2954  for(i=0; i<n; i++) {
2955  //add an element with name "parameter" and value of the param
2956  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "parameter",
2957  "%d", p[i]);
2958  if (rc < 0) {
2959  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement "
2960  "(parameter)\n");
2961  return rc;
2962  }
2963  }
2964 
2965  // Close the parameter_array element.
2966  rc = xmlTextWriterEndElement(writer);
2967  if (rc < 0) {
2968  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
2969  return rc;
2970  }
2971 
2972  return 0;
2973 }
2974 
2975 /*!
2976  * Write a benchmark to an xmlTextWriterPtr object
2977  *
2978  * @param writer xmlTextWriter pointer
2979  * @param b pointer to the benchmark
2980  *
2981  * @return 0 on success -1 on error
2982  */
2983 int
2984 write_benchmark_xtwp(xmlTextWriterPtr writer, struct pmm_benchmark *b)
2985 {
2986  int rc;
2987 
2988  // Start an element named benchmark
2989  rc = xmlTextWriterStartElement(writer, BAD_CAST "benchmark");
2990  if (rc < 0) {
2991  ERRPRINTF("Error @ xmlTextWriterStartElement (bench)\n");
2992  return rc;
2993  }
2994 
2995  //write parameters
2996  rc = write_parameter_array_xtwp(writer, b->p, b->n_p);
2997  if(rc < 0) {
2998  ERRPRINTF("Error writing parameter array.\n");
2999  return rc;
3000  }
3001 
3002  // Add an element with name "complexity" and value to bench.
3003  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "complexity",
3004  "%lld", b->complexity);
3005  if (rc < 0) {
3006  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (complexity)\n");
3007  return rc;
3008  }
3009 
3010  // Add an element with name "flops" and value to benchmark
3011  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "flops",
3012  "%f", b->flops);
3013  if (rc < 0) {
3014  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (flops)\n");
3015  return rc;
3016  }
3017  // Add an element with name seconds and value to benchmark
3018  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "seconds",
3019  "%f", b->seconds);
3020  if (rc < 0) {
3021  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (seconds)\n");
3022  return rc;
3023  }
3024 
3025  // Start an element named used_time
3026  rc = xmlTextWriterStartElement(writer, BAD_CAST "used_time");
3027  if (rc < 0) {
3028  ERRPRINTF("Error @ xmlTextWriterStartElement (used_time)\n");
3029  return rc;
3030  }
3031 
3032  write_timeval_xtwp(writer, &(b->used_t));
3033 
3034  // Close the used_time element.
3035  rc = xmlTextWriterEndElement(writer);
3036  if (rc < 0) {
3037  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
3038  return rc;
3039  }
3040 
3041  // Start an element named wall_time
3042  rc = xmlTextWriterStartElement(writer, BAD_CAST "wall_time");
3043  if (rc < 0) {
3044  ERRPRINTF("Error @ xmlTextWriterStartElement (wall_time)\n");
3045  return rc;
3046  }
3047 
3048  write_timeval_xtwp(writer, &(b->wall_t));
3049 
3050  // Close the wall_time element.
3051  rc = xmlTextWriterEndElement(writer);
3052  if (rc < 0) {
3053  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
3054  return rc;
3055  }
3056 
3057  // Close the benchmark element.
3058  rc = xmlTextWriterEndElement(writer);
3059  if (rc < 0) {
3060  ERRPRINTF("Error @ xmlTextWriterEndElement\n");
3061  return rc;
3062  }
3063 
3064  return 0; //success
3065 }
3066 
3067 /*!
3068  * Write a timeval to an xmlTextWriterPtr object
3069  *
3070  * @param writer xmlTextWriter pointer
3071  * @param t pointer to the timeval
3072  *
3073  * @return 0 on success -1 on error
3074  */
3075 int
3076 write_timeval_xtwp(xmlTextWriterPtr writer, struct timeval *t)
3077 {
3078  int rc;
3079 
3080  // Add an element with name "secs" and value
3081  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "secs",
3082  "%ld", t->tv_sec);
3083  if (rc < 0) {
3084  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (secs)\n");
3085  return rc;
3086  }
3087 
3088  // Add an element with name "usecs" and value.
3089  rc = xmlTextWriterWriteFormatElement(writer, BAD_CAST "usecs",
3090  "%ld", t->tv_usec);
3091  if (rc < 0) {
3092  ERRPRINTF("Error @ xmlTextWriterWriteFormatElement (usecs)\n");
3093  return rc;
3094  }
3095 
3096  return 0; // success
3097 
3098 }
3099 
3100 /*!
3101  * initialized libxml2 parser
3102  */
3103 void
3105 {
3106  xmlInitParser();
3107 }
3108 
3109 /*!
3110  * clean up libxml parser
3111  */
3112 void
3114 {
3115  xmlCleanupParser();
3116 }