Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
SimDag Revolution: SD_workstation becomes sg_host
[simgrid.git] / src / simdag / sd_dotloader.cpp
1 /* Copyright (c) 2009-2016. The SimGrid Team.
2  * All rights reserved.                                                     */
3
4 /* This program is free software; you can redistribute it and/or modify it
5  * under the terms of the license (GNU LGPL) which comes with this package. */
6
7 #include "src/simdag/simdag_private.h"
8 #include "simgrid/simdag.h"
9 #include "xbt/misc.h"
10 #include "xbt/log.h"
11 #include <stdbool.h>
12 #include <string.h>
13 #include <libgen.h>
14
15 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(sd_dotparse, sd, "Parsing DOT files");
16
17 #undef CLEANUP
18
19 #ifdef HAVE_CGRAPH_H
20 #include <graphviz/cgraph.h>
21 #elif HAVE_AGRAPH_H
22 #include <graphviz/agraph.h>
23 #define agnxtnode(dot, node)    agnxtnode(node)
24 #define agfstin(dot, node)      agfstin(node)
25 #define agnxtin(dot, edge)      agnxtin(edge)
26 #define agfstout(dot, node)     agfstout(node)
27 #define agnxtout(dot, edge)     agnxtout(edge)
28 #endif
29
30 typedef enum {
31   sequential =0,
32   parallel
33 } seq_par_t;
34
35 xbt_dynar_t SD_dotload_generic(const char * filename, seq_par_t seq_or_par);
36
37 static xbt_dynar_t result;
38 static xbt_dict_t jobs;
39 static xbt_dict_t computers;
40 static Agraph_t *dag_dot;
41 static bool schedule = true;
42
43 static void dot_task_p_free(void *task) {
44   SD_task_t *t = (SD_task_t *)task;
45   SD_task_destroy(*t);
46 }
47
48 /** @brief loads a DOT file describing a DAG
49  * 
50  * See http://www.graphviz.org/doc/info/lang.html
51  * for more details.
52  * To obtain information about transfers and tasks, two attributes are
53  * required : size on task (execution time in Flop) and size on edge
54  * (the amount of data transfer in bit).
55  * if they aren't here, there choose to be equal to zero.
56  */
57 xbt_dynar_t SD_dotload(const char *filename) {
58   computers = xbt_dict_new_homogeneous(NULL);
59   schedule = false;
60   SD_dotload_generic(filename, sequential);
61   xbt_dynar_t computer = NULL;
62   xbt_dict_cursor_t dict_cursor;
63   char *computer_name;
64   xbt_dict_foreach(computers,dict_cursor,computer_name,computer){
65     xbt_dynar_free(&computer);
66   }
67   xbt_dict_free(&computers);
68   return result;
69 }
70
71 xbt_dynar_t SD_dotload_with_sched(const char *filename) {
72   computers = xbt_dict_new_homogeneous(NULL);
73   SD_dotload_generic(filename, sequential);
74
75   if(schedule){
76     xbt_dynar_t computer = NULL;
77     xbt_dict_cursor_t dict_cursor;
78     char *computer_name;
79     const sg_host_t *workstations = sg_host_list ();
80     xbt_dict_foreach(computers,dict_cursor,computer_name,computer){
81       int count_computer = atoi(computer_name);
82       unsigned int count=0;
83       SD_task_t task;
84       SD_task_t task_previous = NULL;
85       xbt_dynar_foreach(computer,count,task){
86         /* add dependency between the previous and the task to avoid
87          * parallel execution */
88         if(task != NULL ){
89           if(task_previous != NULL &&
90              !SD_task_dependency_exists(task_previous, task))
91             SD_task_dependency_add(NULL, NULL, task_previous, task);
92           SD_task_schedulel(task, 1, workstations[count_computer]);
93           task_previous = task;
94         }
95       }
96       xbt_dynar_free(&computer);
97     }
98     xbt_dict_free(&computers);
99     if(acyclic_graph_detail(result))
100       return result;
101     else
102       XBT_WARN("There is at least one cycle in the provided task graph");
103   }else{
104     XBT_WARN("The scheduling is ignored");
105   }
106   xbt_dynar_t computer = NULL;
107   xbt_dict_cursor_t dict_cursor;
108   char *computer_name;
109   xbt_dict_foreach(computers,dict_cursor,computer_name,computer){
110     xbt_dynar_free(&computer);
111   }
112   xbt_dict_free(&computers);
113   xbt_dynar_free(&result);
114   return NULL;
115 }
116
117 xbt_dynar_t SD_PTG_dotload(const char * filename) {
118   xbt_dynar_t result = SD_dotload_generic(filename, parallel);
119   if (!acyclic_graph_detail(result)) {
120     XBT_ERROR("The DOT described in %s is not a DAG. It contains a cycle.",
121               basename((char*)filename));
122     xbt_dynar_free(&result);
123     /* (result == NULL) here */
124   }
125   return result;
126 }
127
128 #ifdef HAVE_CGRAPH_H
129 static int edge_compare(const void *a, const void *b)
130 {
131   unsigned va = AGSEQ(*(Agedge_t **)a);
132   unsigned vb = AGSEQ(*(Agedge_t **)b);
133   return va == vb ? 0 : (va < vb ? -1 : 1);
134 }
135 #endif
136
137 xbt_dynar_t SD_dotload_generic(const char * filename, seq_par_t seq_or_par){
138   xbt_assert(filename, "Unable to use a null file descriptor\n");
139   unsigned int i;
140   result = xbt_dynar_new(sizeof(SD_task_t), dot_task_p_free);
141   jobs = xbt_dict_new_homogeneous(NULL);
142   FILE *in_file = fopen(filename, "r");
143   if (in_file == NULL)
144     xbt_die("Failed to open file: %s", filename);
145   dag_dot = agread(in_file, NIL(Agdisc_t *));
146   SD_task_t root, end, task;
147   /*
148    * Create all the nodes
149    */
150   Agnode_t *node = NULL;
151   for (node = agfstnode(dag_dot); node; node = agnxtnode(dag_dot, node)) {
152
153     char *name = agnameof(node);
154     double amount = atof(agget(node, (char *) "size"));
155     double alpha = 0.0;
156
157     if (seq_or_par == sequential){
158       XBT_DEBUG("See <job id=%s amount =%.0f>", name, amount);
159     } else {
160       if (!strcmp(agget(node, (char *) "alpha"), "")){
161         alpha = atof(agget(node, (char *) "alpha"));
162         if (alpha == -1.){
163           XBT_DEBUG("negative alpha value provided. Set to 0.");
164           alpha = 0.0 ;
165         }
166       } else {
167         XBT_DEBUG("no alpha value provided. Set to 0");
168         alpha = 0.0 ;
169       }
170
171       XBT_DEBUG("See <job id=%s amount =%.0f alpha = %.3f>",
172           name, amount, alpha);
173     }
174
175     if (!(task = (SD_task_t)xbt_dict_get_or_null(jobs, name))) {
176       if (seq_or_par == sequential){
177         task = SD_task_create_comp_seq(name, NULL , amount);
178       } else {
179         task = SD_task_create_comp_par_amdahl(name, NULL , amount, alpha);
180       }
181       xbt_dict_set(jobs, name, task, NULL);
182       if (!strcmp(name, "root")){
183       /* by design the root task is always SCHEDULABLE */
184       SD_task_set_state(task, SD_SCHEDULABLE);
185       /* Put it at the beginning of the dynar */
186         xbt_dynar_insert_at(result, 0, &task);
187       } else {
188         if (!strcmp(name, "end")){
189           XBT_DEBUG("Declaration of the 'end' node, don't store it yet.");
190           end = task;
191           /* Should be inserted later in the dynar */
192         } else {
193           xbt_dynar_push(result, &task);
194         }
195       }
196
197       if((seq_or_par == sequential) &&
198           (schedule ||
199               XBT_LOG_ISENABLED(sd_dotparse, xbt_log_priority_verbose))){
200         /* try to take the information to schedule the task only if all is
201          * right*/
202         int performer, order;
203         char *char_performer = agget(node, (char *) "performer");
204         char *char_order = agget(node, (char *) "order");
205         /* performer is the computer which execute the task */
206         performer =
207             ((!char_performer || !strcmp(char_performer,"")) ? -1:atoi(char_performer));
208         /* order is giving the task order on one computer */
209         order = ((!char_order || !strcmp(char_order, ""))? -1:atoi(char_order));
210
211         XBT_DEBUG ("Task '%s' is scheduled on workstation '%d' in position '%d'",
212                     task->name, performer, order);
213         xbt_dynar_t computer = NULL;
214         if(performer != -1 && order != -1){
215           /* required parameters are given */
216           computer = (xbt_dynar_t)xbt_dict_get_or_null(computers, char_performer);
217           if(computer == NULL){
218             computer = xbt_dynar_new(sizeof(SD_task_t), NULL);
219             xbt_dict_set(computers, char_performer, computer, NULL);
220           }
221           if(performer < xbt_dict_length(host_list)){
222             /* the wanted computer is available */
223             SD_task_t *task_test = NULL;
224             if((unsigned int)order < computer->used)
225               task_test = (SD_task_t *)xbt_dynar_get_ptr(computer,order);
226             if(task_test != NULL && *task_test != NULL && *task_test != task){
227               /* the user gives the same order to several tasks */
228               schedule = false;
229               XBT_VERB("The task %s starts on the computer %s at the position : %s like the task %s",
230                      (*task_test)->name, char_performer, char_order,
231                      task->name);
232             }else{
233               /* the parameter seems to be ok */
234               xbt_dynar_set_as(computer, order, SD_task_t, task);
235             }
236           }else{
237             /* the platform has not enough processors to schedule the DAG like
238              * the user wants*/
239             schedule = false;
240             XBT_VERB("The schedule is ignored, there are not enough computers");
241           }
242         }
243         else {
244           /* one of required parameters is not given */
245           schedule = false;
246           XBT_VERB("The schedule is ignored, the task %s is not correctly scheduled",
247               task->name);
248         }
249       }
250     } else {
251       XBT_WARN("Task '%s' is defined more than once", name);
252     }
253   }
254
255   /*
256    * Check if 'root' and 'end' nodes have been explicitly declared.
257    * If not, create them.
258    */
259   if (!(root = (SD_task_t)xbt_dict_get_or_null(jobs, "root"))){
260     if (seq_or_par == sequential)
261       root = SD_task_create_comp_seq("root", NULL, 0);
262     else
263       root = SD_task_create_comp_par_amdahl("root", NULL, 0, 0);
264     /* by design the root task is always SCHEDULABLE */
265     SD_task_set_state(root, SD_SCHEDULABLE);
266     /* Put it at the beginning of the dynar */
267       xbt_dynar_insert_at(result, 0, &root);
268   }
269
270   if (!(end = (SD_task_t)xbt_dict_get_or_null(jobs, "end"))){
271     if (seq_or_par == sequential)
272       end = SD_task_create_comp_seq("end", NULL, 0);
273     else
274       end = SD_task_create_comp_par_amdahl("end", NULL, 0, 0);
275     /* Should be inserted later in the dynar */
276   }
277
278   /*
279    * Create edges
280    */
281   xbt_dynar_t edges = xbt_dynar_new(sizeof(Agedge_t*), NULL);
282   for (node = agfstnode(dag_dot); node; node = agnxtnode(dag_dot, node)) {
283     unsigned cursor;
284     Agedge_t * edge;
285     xbt_dynar_reset(edges);
286     for (edge = agfstout(dag_dot, node); edge; edge = agnxtout(dag_dot, edge))
287       xbt_dynar_push_as(edges, Agedge_t *, edge);
288 #ifdef HAVE_CGRAPH_H
289     /* Hack: circumvent a bug in libcgraph, where the edges are not always given
290      * back in creation order.  We sort them again, according to their sequence
291      * id.  The problem appears to be solved (i.e.: I did not test it) in
292      * graphviz' mercurial repository by the following changeset:
293      *    changeset:   8431:d5f1fb7e8103
294      *    user:        Emden Gansner <erg@research.att.com>
295      *    date:        Tue Oct 11 12:38:58 2011 -0400
296      *    summary:     Make sure edges are stored in node creation order
297      * It should be fixed in graphviz 2.30 and above.
298      */
299     xbt_dynar_sort(edges, edge_compare);
300 #endif
301     xbt_dynar_foreach(edges, cursor, edge) {
302       SD_task_t src, dst;
303       char *src_name=agnameof(agtail(edge));
304       char *dst_name=agnameof(aghead(edge));
305       double size = atof(agget(edge, (char *) "size"));
306
307       src = (SD_task_t)xbt_dict_get_or_null(jobs, src_name);
308       dst = (SD_task_t)xbt_dict_get_or_null(jobs, dst_name);
309
310       if (size > 0) {
311         char *name = (char*)xbt_malloc((strlen(src_name)+strlen(dst_name)+6)*sizeof(char));
312         sprintf(name, "%s->%s", src_name, dst_name);
313         XBT_DEBUG("See <transfer id=%s amount = %.0f>", name, size);
314         if (!(task = (SD_task_t)xbt_dict_get_or_null(jobs, name))) {
315           if (seq_or_par == sequential)
316             task = SD_task_create_comm_e2e(name, NULL , size);
317           else
318             task = SD_task_create_comm_par_mxn_1d_block(name, NULL , size);
319           SD_task_dependency_add(NULL, NULL, src, task);
320           SD_task_dependency_add(NULL, NULL, task, dst);
321           xbt_dict_set(jobs, name, task, NULL);
322           xbt_dynar_push(result, &task);
323         } else {
324           XBT_WARN("Task '%s' is defined more than once", name);
325         }
326         xbt_free(name);
327       } else {
328         SD_task_dependency_add(NULL, NULL, src, dst);
329       }
330     }
331   }
332   xbt_dynar_free(&edges);
333
334   /* all compute and transfer tasks have been created, put the "end" node at
335    * the end of dynar
336    */
337   XBT_DEBUG("All tasks have been created, put %s at the end of the dynar",
338       end->name);
339   xbt_dynar_push(result, &end);
340
341   /* Connect entry tasks to 'root', and exit tasks to 'end'*/
342
343   xbt_dynar_foreach (result, i, task){
344     if (task == root || task == end)
345       continue;
346     if (xbt_dynar_is_empty(task->tasks_before)) {
347       XBT_DEBUG("file '%s' has no source. Add dependency from 'root'",
348           task->name);
349       SD_task_dependency_add(NULL, NULL, root, task);
350     } else if (xbt_dynar_is_empty(task->tasks_after)) {
351       XBT_DEBUG("file '%s' has no destination. Add dependency to 'end'",
352           task->name);
353       SD_task_dependency_add(NULL, NULL, task, end);
354     }
355   }
356
357   agclose(dag_dot);
358   xbt_dict_free(&jobs);
359   fclose(in_file);
360
361   if (!acyclic_graph_detail(result)) {
362     XBT_ERROR("The DOT described in %s is not a DAG. It contains a cycle.",
363               basename((char*)filename));
364     xbt_dynar_free(&result);
365     /* (result == NULL) here */
366   }
367   return result;
368 }