Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
security commit before using the ax!
[simgrid.git] / src / simdag / sd_dotloader.c
1 /* Copyright (c) 2009-2013. 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 "private.h"
8 #include "simdag/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 void dot_add_task(Agnode_t * dag_node);
36 void dot_add_parallel_task(Agnode_t * dag_node);
37 void dot_add_input_dependencies(SD_task_t current_job, Agedge_t * edge,
38                                 seq_par_t seq_or_par);
39 void dot_add_output_dependencies(SD_task_t current_job, Agedge_t * edge,
40                                  seq_par_t seq_or_par);
41 xbt_dynar_t SD_dotload_generic(const char * filename);
42 xbt_dynar_t SD_dotload_generic_alt(const char * filename, seq_par_t seq_or_par);
43
44 static double dot_parse_double(const char *string) {
45   if (string == NULL)
46     return -1;
47   double value = -1;
48   char *err;
49
50   errno = 0;
51   value = strtod(string,&err);
52   if(errno) {
53     XBT_WARN("Failed to convert string to double: %s\n",strerror(errno));
54     return -1;
55   }
56   return value;
57 }
58
59
60 static int dot_parse_int(const char *string) {
61   if (string == NULL)
62     return -10;
63   int ret = 0;
64   int value = -1;
65
66   ret = sscanf(string, "%d", &value);
67   if (ret != 1)
68     XBT_WARN("%s is not an integer", string);
69   return value;
70 }
71
72 static xbt_dynar_t result;
73 static xbt_dict_t jobs;
74 static xbt_dict_t files;
75 static xbt_dict_t computers;
76 static SD_task_t root_task, end_task;
77 static Agraph_t *dag_dot;
78 static bool schedule = true;
79
80 static void dot_task_free(void *task) {
81   SD_task_t t = task;
82   SD_task_destroy(t);
83 }
84
85 static void dot_task_p_free(void *task) {
86   SD_task_t *t = task;
87   SD_task_destroy(*t);
88 }
89
90 #ifdef HAVE_TRACING
91 static void TRACE_sd_dotloader (SD_task_t task, const char *category) {
92   if (category && strlen (category)){
93     if (task->category)
94       XBT_DEBUG("Change the category of %s from %s to %s",
95           task->name, task->category, category);
96     else
97       XBT_DEBUG("Set the category of %s to %s",task->name, category);
98     TRACE_category (category);
99     TRACE_sd_set_task_category(task, category);
100   }
101 }
102 #endif
103
104 /** @brief loads a DOT file describing a DAG
105  * 
106  * See http://www.graphviz.org/doc/info/lang.html
107  * for more details.
108  * To obtain information about transfers and tasks, two attributes are
109  * required : size on task (execution time in Flop) and size on edge
110  * (the amount of data transfer in bit).
111  * if they aren't here, there choose to be equal to zero.
112  */
113 xbt_dynar_t SD_dotload(const char *filename) {
114   computers = xbt_dict_new_homogeneous(NULL);
115 //  SD_dotload_generic_alt(filename, sequential);
116   SD_dotload_generic(filename);
117   xbt_dynar_t computer = NULL;
118   xbt_dict_cursor_t dict_cursor;
119   char *computer_name;
120   xbt_dict_foreach(computers,dict_cursor,computer_name,computer){
121     xbt_dynar_free(&computer);
122   }
123   xbt_dict_free(&computers);
124   return result;
125 }
126
127 xbt_dynar_t SD_dotload_with_sched(const char *filename) {
128   computers = xbt_dict_new_homogeneous(NULL);
129 //  SD_dotload_generic_alt(filename, sequential);
130   SD_dotload_generic(filename);
131
132   if(schedule){
133     xbt_dynar_t computer = NULL;
134     xbt_dict_cursor_t dict_cursor;
135     char *computer_name;
136     const SD_workstation_t *workstations = SD_workstation_get_list ();
137     xbt_dict_foreach(computers,dict_cursor,computer_name,computer){
138       int count_computer = atoi(computer_name);
139       unsigned int count=0;
140       SD_task_t task;
141       SD_task_t task_previous = NULL;
142       xbt_dynar_foreach(computer,count,task){
143         /* add dependency between the previous and the task to avoid
144          * parallel execution */
145         if(task != NULL ){
146           if(task_previous != NULL &&
147              !SD_task_dependency_exists(task_previous, task))
148             SD_task_dependency_add(NULL, NULL, task_previous, task);
149           SD_task_schedulel(task, 1, workstations[count_computer]);
150           task_previous = task;
151         }
152       }
153       xbt_dynar_free(&computer);
154     }
155     xbt_dict_free(&computers);
156     if(acyclic_graph_detail(result))
157       return result;
158     else
159       XBT_WARN("There is at least one cycle in the provided task graph");
160   }else{
161     XBT_WARN("The scheduling is ignored");
162   }
163   SD_task_t task;
164   unsigned int count;
165   xbt_dynar_t computer = NULL;
166   xbt_dict_cursor_t dict_cursor;
167   char *computer_name;
168   xbt_dict_foreach(computers,dict_cursor,computer_name,computer){
169     xbt_dynar_free(&computer);
170   }
171   xbt_dict_free(&computers);
172   xbt_dynar_foreach(result,count,task){
173      SD_task_destroy(task);
174   }
175   return NULL;
176 }
177
178 xbt_dynar_t SD_PTG_dotload(const char * filename) {
179   xbt_dynar_t result = SD_dotload_generic_alt(filename, parallel);
180   if (!acyclic_graph_detail(result)) {
181     XBT_ERROR("The DOT described in %s is not a DAG. It contains a cycle.",
182               basename((char*)filename));
183     xbt_dynar_free(&result);
184     /* (result == NULL) here */
185   }
186   return result;
187 }
188
189 xbt_dynar_t SD_dotload_generic_alt(const char * filename, seq_par_t seq_or_par){
190   xbt_assert(filename, "Unable to use a null file descriptor\n");
191   unsigned int i;
192   result = xbt_dynar_new(sizeof(SD_task_t), dot_task_p_free);
193   jobs = xbt_dict_new_homogeneous(NULL);
194   FILE *in_file = fopen(filename, "r");
195   dag_dot = agread(in_file, NIL(Agdisc_t *));
196   SD_task_t root, end, task;
197   /*
198    * Create all the nodes
199    */
200   Agnode_t *node = NULL;
201   for (node = agfstnode(dag_dot); node; node = agnxtnode(dag_dot, node)) {
202
203     char *name = agnameof(node);
204     double amount = atof(agget(node, (char *) "size"));
205     double alpha;
206
207     if (seq_or_par == sequential){
208       XBT_DEBUG("See <job id=%s amount =%.0f>", name, amount);
209     } else {
210       alpha = atof(agget(node, (char *) "alpha"));
211       if (alpha == -1.)
212         alpha = 0.0 ;
213       XBT_DEBUG("See <job id=%s amount =%.0f alpha = %.3f>",
214           name, amount, alpha);
215     }
216
217     if (!(task = xbt_dict_get_or_null(jobs, name))) {
218       if (seq_or_par == sequential){
219         task = SD_task_create_comp_seq(name, NULL , amount);
220       } else {
221         task = SD_task_create_comp_par_amdahl(name, NULL , amount, alpha);
222       }
223 #ifdef HAVE_TRACING
224       TRACE_sd_dotloader (task, agget (node, (char*)"category"));
225 #endif
226       xbt_dict_set(jobs, name, task, NULL);
227       if (!strcmp(name, "root")){
228       /* by design the root task is always SCHEDULABLE */
229       __SD_task_set_state(task, SD_SCHEDULABLE);
230       /* Put it at the beginning of the dynar */
231         xbt_dynar_insert_at(result, 0, &task);
232       } else {
233         if (!strcmp(name, "end")){
234           XBT_DEBUG("Declaration of the 'end' node, don't store it yet.");
235           end = task;
236           /* Should be inserted later in the dynar */
237         } else {
238           xbt_dynar_push(result, &task);
239         }
240       }
241
242       if(schedule && seq_or_par == sequential){
243         /* try to take the information to schedule the task only if all is
244          * right*/
245         /* performer is the computer which execute the task */
246         int performer = -1;
247         char * char_performer = agget(node, (char *) "performer");
248         if (char_performer)
249           performer = atoi(char_performer);
250
251         /* order is giving the task order on one computer */
252         int order = -1;
253         char * char_order = agget(node, (char *) "order");
254         if (char_order)
255           order = atoi(char_order);
256         XBT_DEBUG ("Task '%s' is scheduled on workstation '%d' in position '%d'",
257                     task->name, performer, order);
258         xbt_dynar_t computer = NULL;
259         if(performer != -1 && order != -1){
260           /* required parameters are given */
261           computer = xbt_dict_get_or_null(computers, char_performer);
262           if(computer == NULL){
263             computer = xbt_dynar_new(sizeof(SD_task_t), NULL);
264             xbt_dict_set(computers, char_performer, computer, NULL);
265           }
266           if(performer < xbt_lib_length(host_lib)){
267             /* the wanted computer is available */
268             SD_task_t *task_test = NULL;
269             if(order < computer->used)
270               task_test = xbt_dynar_get_ptr(computer,order);
271             if(task_test != NULL && *task_test != NULL && *task_test != task){
272               /* the user gives the same order to several tasks */
273               schedule = false;
274               XBT_VERB("The task %s starts on the computer %s at the position : %s like the task %s",
275                      (*task_test)->name, char_performer, char_order,
276                      task->name);
277             }else{
278               /* the parameter seems to be ok */
279               xbt_dynar_set_as(computer, order, SD_task_t, task);
280             }
281           }else{
282             /* the platform has not enough processors to schedule the DAG like
283              * the user wants*/
284             schedule = false;
285             XBT_VERB("The schedule is ignored, there are not enough computers");
286           }
287         }
288         else {
289           /* one of required parameters is not given */
290           schedule = false;
291           XBT_VERB("The schedule is ignored, the task %s is not correctly scheduled",
292               task->name);
293         }
294       }
295     } else {
296       XBT_WARN("Task '%s' is defined more than once", name);
297     }
298   }
299
300   /*
301    * Check if 'root' and 'end' nodes have been explicitly declared.
302    * If not, create them.
303    */
304   if (!(root = xbt_dict_get_or_null(jobs, "root"))){
305     if (seq_or_par == sequential)
306       root = SD_task_create_comp_seq("root", NULL, 0);
307     else
308       root = SD_task_create_comp_par_amdahl("root", NULL, 0, 0);
309     /* by design the root task is always SCHEDULABLE */
310     __SD_task_set_state(root, SD_SCHEDULABLE);
311     /* Put it at the beginning of the dynar */
312       xbt_dynar_insert_at(result, 0, &root);
313   }
314
315   if (!(end = xbt_dict_get_or_null(jobs, "end"))){
316     if (seq_or_par == sequential)
317       end = SD_task_create_comp_seq("end", NULL, 0);
318     else
319       end = SD_task_create_comp_par_amdahl("end", NULL, 0, 0);
320     /* Should be inserted later in the dynar */
321   }
322
323   /*
324    * Create edges
325    */
326   node = NULL;
327   for (node = agfstnode(dag_dot); node; node = agnxtnode(dag_dot, node)) {
328     Agedge_t * edge = NULL;
329     for (edge = agfstout(dag_dot, node); edge; edge = agnxtout(dag_dot, edge)) {
330       SD_task_t src, dst;
331       char *src_name=agnameof(agtail(edge));
332       char *dst_name=agnameof(aghead(edge));
333       double size = atof(agget(edge, (char *) "size"));
334
335       src = xbt_dict_get_or_null(jobs, src_name);
336       dst  = xbt_dict_get_or_null(jobs, dst_name);
337
338       if (size > 0) {
339         char *name =
340             xbt_malloc((strlen(src_name)+strlen(dst_name)+6)*sizeof(char));
341         sprintf(name, "%s->%s", src_name, dst_name);
342         XBT_DEBUG("See <transfer id=%s amount = %.0f>", name, size);
343         if (!(task = xbt_dict_get_or_null(jobs, name))) {
344           if (seq_or_par == sequential)
345             task = SD_task_create_comm_e2e(name, NULL , size);
346           else
347             task = SD_task_create_comm_par_mxn_1d_block(name, NULL , size);
348 #ifdef HAVE_TRACING
349           TRACE_sd_dotloader (task, agget (node, (char*)"category"));
350 #endif
351           SD_task_dependency_add(NULL, NULL, src, task);
352           SD_task_dependency_add(NULL, NULL, task, dst);
353           xbt_dict_set(jobs, name, task, NULL);
354           xbt_dynar_push(result, &task);
355         } else {
356           XBT_WARN("Task '%s' is defined more than once", name);
357         }
358       } else {
359         SD_task_dependency_add(NULL, NULL, src, dst);
360       }
361     }
362   }
363
364   /* all compute and transfer tasks have been created, put the "end" node at
365    * the end of dynar
366    */
367   XBT_DEBUG("All tasks have been created, put %s at the end of the dynar",
368       end->name);
369   xbt_dynar_push(result, &end);
370
371   /* Connect entry tasks to 'root', and exit tasks to 'end'*/
372
373   xbt_dynar_foreach (result, i, task){
374     if (task == root || task == end)
375       continue;
376     if (xbt_dynar_is_empty(task->tasks_before)) {
377       XBT_DEBUG("file '%s' has no source. Add dependency from 'root'",
378           task->name);
379       SD_task_dependency_add(NULL, NULL, root, task);
380     } else if (xbt_dynar_is_empty(task->tasks_after)) {
381       XBT_DEBUG("file '%s' has no destination. Add dependency to 'end'",
382           task->name);
383       SD_task_dependency_add(NULL, NULL, task, end);
384     }
385   }
386
387   agclose(dag_dot);
388   xbt_dict_free(&jobs);
389
390   return result;
391 }
392
393 xbt_dynar_t SD_dotload_generic(const char * filename) {
394   xbt_assert(filename, "Unable to use a null file descriptor\n");
395   FILE *in_file = fopen(filename, "r");
396   dag_dot = agread(in_file, NIL(Agdisc_t *));
397
398   result = xbt_dynar_new(sizeof(SD_task_t), dot_task_p_free);
399   files = xbt_dict_new_homogeneous(NULL);
400   jobs = xbt_dict_new_homogeneous(NULL);
401   computers = xbt_dict_new_homogeneous(NULL);
402   root_task = SD_task_create_comp_seq("root", NULL, 0);
403   /* by design the root task is always SCHEDULABLE */
404   __SD_task_set_state(root_task, SD_SCHEDULABLE);
405
406   xbt_dict_set(jobs, "root", root_task, NULL);
407   xbt_dynar_push(result, &root_task);
408   end_task = SD_task_create_comp_seq("end", NULL, 0);
409   xbt_dict_set(jobs, "end", end_task, NULL);
410
411   Agnode_t *dag_node = NULL;
412   for (dag_node = agfstnode(dag_dot); dag_node; dag_node = agnxtnode(dag_dot, dag_node)) {
413     dot_add_task(dag_node);
414   }
415   agclose(dag_dot);
416   xbt_dict_free(&jobs);
417   /* And now, post-process the files.
418    * We want a file task per pair of computation tasks exchanging the file.
419    * Duplicate on need
420    * Files not produced in the system are said to be produced by root task
421    * (top of DAG).
422    * Files not consumed in the system are said to be consumed by end task
423    * (bottom of DAG).
424    */
425   xbt_dict_cursor_t cursor;
426   SD_task_t file;
427   char *name;
428   xbt_dict_foreach(files, cursor, name, file) {
429     XBT_DEBUG("Considering file '%s' stored in the dictionary",
430         file->name);
431     if (xbt_dynar_is_empty(file->tasks_before)) {
432       XBT_DEBUG("file '%s' has no source. Add dependency from 'root'",
433           file->name);
434       SD_task_dependency_add(NULL, NULL, root_task, file);
435     } else if (xbt_dynar_is_empty(file->tasks_after)) {
436       XBT_DEBUG("file '%s' has no destination. Add dependency to 'end'",
437           file->name);
438       SD_task_dependency_add(NULL, NULL, file, end_task);
439     }
440     xbt_dynar_push(result, &file);
441   }
442
443   /* Push end task last */
444   xbt_dynar_push(result, &end_task);
445
446   xbt_dict_free(&files);
447   fclose(in_file);
448   if (!acyclic_graph_detail(result)) {
449     XBT_ERROR("The DOT described in %s is not a DAG. It contains a cycle.",
450               basename((char*)filename));
451     xbt_dynar_free(&result);
452     /* (result == NULL) here */
453   }
454   return result;
455 }
456
457 /* dot_add_task create a sd_task and all transfers required for this
458  * task. The execution time of the task is given by the attribute size.
459  * The unit of size is the Flop.*/
460 void dot_add_task(Agnode_t * dag_node) {
461   char *name = agnameof(dag_node);
462   SD_task_t current_job;
463   double runtime = dot_parse_double(agget(dag_node, (char *) "size"));
464
465   XBT_DEBUG("See <job id=%s runtime=%s %.0f>", name,
466         agget(dag_node, (char *) "size"), runtime);
467
468   if (!strcmp(name, "root")){
469     XBT_WARN("'root' node is explicitly declared in the DOT file. Update it");
470     root_task->amount = runtime;
471     root_task->computation_amount[0]= runtime;
472 #ifdef HAVE_TRACING
473     TRACE_sd_dotloader (root_task, agget (dag_node, (char*)"category"));
474 #endif
475   }
476
477   if (!strcmp(name, "end")){
478     XBT_WARN("'end' node is explicitly declared in the DOT file. Update it");
479     end_task->amount = runtime;
480     end_task->computation_amount[0]= runtime;
481 #ifdef HAVE_TRACING
482     TRACE_sd_dotloader (end_task, agget (dag_node, (char*)"category"));
483 #endif
484   }
485
486   current_job = xbt_dict_get_or_null(jobs, name);
487   if (!current_job) {
488     current_job =
489         SD_task_create_comp_seq(name, NULL , runtime);
490 #ifdef HAVE_TRACING
491     TRACE_sd_dotloader (current_job, agget (dag_node, (char*)"category"));
492 #endif
493     xbt_dict_set(jobs, name, current_job, NULL);
494     xbt_dynar_push(result, &current_job);
495   }
496   Agedge_t *e;
497   int count = 0;
498
499   for (e = agfstin(dag_dot, dag_node); e; e = agnxtin(dag_dot, e)) {
500     dot_add_input_dependencies(current_job, e, sequential);
501     count++;
502   }
503   if (count == 0 && current_job != root_task) {
504     SD_task_dependency_add(NULL, NULL, root_task, current_job);
505   }
506   count = 0;
507   for (e = agfstout(dag_dot, dag_node); e; e = agnxtout(dag_dot, e)) {
508     dot_add_output_dependencies(current_job, e, sequential);
509     count++;
510   }
511   if (count == 0 && current_job != end_task) {
512     SD_task_dependency_add(NULL, NULL, current_job, end_task);
513   }
514
515   if(schedule || XBT_LOG_ISENABLED(sd_dotparse, xbt_log_priority_verbose)){
516     /* try to take the information to schedule the task only if all is
517      * right*/
518     /* performer is the computer which execute the task */
519     unsigned long performer = -1;
520     char * char_performer = agget(dag_node, (char *) "performer");
521     if (char_performer != NULL)
522       performer = (long) dot_parse_int(char_performer);
523
524     /* order is giving the task order on one computer */
525     unsigned long order = -1;
526     char * char_order = agget(dag_node, (char *) "order");
527     if (char_order != NULL)
528       order = (long) dot_parse_int(char_order);
529     xbt_dynar_t computer = NULL;
530     if(performer != -1 && order != -1){
531       /* required parameters are given */
532       computer = xbt_dict_get_or_null(computers, char_performer);
533       if(computer == NULL){
534         computer = xbt_dynar_new(sizeof(SD_task_t), NULL);
535         xbt_dict_set(computers, char_performer, computer, NULL);
536       }
537       if(performer < xbt_lib_length(host_lib)){
538         /* the wanted computer is available */
539         SD_task_t *task_test = NULL;
540         if(order < computer->used)
541           task_test = xbt_dynar_get_ptr(computer,order);
542         if(task_test != NULL && *task_test != NULL && *task_test != current_job){
543           /* the user gives the same order to several tasks */
544           schedule = false;
545           XBT_VERB("The task %s starts on the computer %s at the position : %s like the task %s",
546                  (*task_test)->name, char_performer, char_order,
547                  current_job->name);
548         }else{
549           /* the parameter seems to be ok */
550           xbt_dynar_set_as(computer, order, SD_task_t, current_job);
551         }
552       }else{
553         /* the platform has not enough processors to schedule the DAG like
554          * the user wants*/
555         schedule = false;
556         XBT_VERB("The schedule is ignored, there are not enough computers");
557       }
558     }
559     else {
560       /* one of required parameters is not given */
561       schedule = false;
562       XBT_VERB("The schedule is ignored, the task %s is not correctly scheduled",
563           current_job->name);
564     }
565   }
566 }
567
568 /* dot_add_output_dependencies create the dependencies between a task
569  * and a transfers. This is given by the edges in the dot file. 
570  * The amount of data transfers is given by the attribute size on the
571  * edge. */
572 void dot_add_input_dependencies(SD_task_t current_job, Agedge_t * edge,
573                                 seq_par_t seq_or_par) {
574   SD_task_t file = NULL;
575   char *name_tail=agnameof(agtail(edge));
576   char *name_head=agnameof(aghead(edge));
577   char *name = xbt_malloc((strlen(name_head)+strlen(name_tail)+6)*sizeof(char));
578   sprintf(name, "%s->%s", name_tail, name_head);
579   double size = dot_parse_double(agget(edge, (char *) "size"));
580   XBT_DEBUG("add input -- edge: %s, size : %e, get size : %s",
581       name, size, agget(edge, (char *) "size"));
582
583   if (size > 0) {
584     file = xbt_dict_get_or_null(files, name);
585     if (file == NULL) {
586       if (seq_or_par == sequential){
587           file = SD_task_create_comm_e2e(name, NULL, size);
588       } else {
589           file = SD_task_create_comm_par_mxn_1d_block(name, NULL, size);
590       }
591 #ifdef HAVE_TRACING
592       TRACE_sd_dotloader (file, agget (edge, (char*)"category"));
593 #endif
594       XBT_DEBUG("add input -- adding %s to the dict as new file", name);
595       xbt_dict_set(files, name, file, NULL);
596     } else {
597       XBT_WARN("%s already exists", name);
598       if (SD_task_get_amount(file) != size) {
599         XBT_WARN("Ignoring file %s size redefinition from %.0f to %.0f",
600               name, SD_task_get_amount(file), size);
601       }
602     }
603     SD_task_dependency_add(NULL, NULL, file, current_job);
604   } else {
605     file = xbt_dict_get_or_null(jobs, name_tail);
606     if (file != NULL) {
607       SD_task_dependency_add(NULL, NULL, file, current_job);
608     }
609   }
610   free(name);
611 }
612
613 /* dot_add_output_dependencies create the dependencies between a
614  * transfers and a task. This is given by the edges in the dot file.
615  * The amount of data transfers is given by the attribute size on the
616  * edge. */
617 void dot_add_output_dependencies(SD_task_t current_job, Agedge_t * edge,
618                                  seq_par_t seq_or_par) {
619   SD_task_t file;
620   char *name_tail=agnameof(agtail(edge));
621   char *name_head=agnameof(aghead(edge));
622   char *name = xbt_malloc((strlen(name_head)+strlen(name_tail)+6)*sizeof(char));
623   sprintf(name, "%s->%s", name_tail, name_head);
624   double size = dot_parse_double(agget(edge, (char *) "size"));
625   XBT_DEBUG("add_output -- edge: %s, size : %e, get size : %s",
626       name, size, agget(edge, (char *) "size"));
627
628   if (size > 0) {
629     file = xbt_dict_get_or_null(files, name);
630     if (file == NULL) {
631       if (seq_or_par == sequential){
632           file = SD_task_create_comm_e2e(name, NULL, size);
633       } else {
634           file = SD_task_create_comm_par_mxn_1d_block(name, NULL, size);
635       }
636 #ifdef HAVE_TRACING
637       TRACE_sd_dotloader (file, agget (edge, (char*)"category"));
638 #endif
639       XBT_DEBUG("add output -- adding %s to the dict as new file", name);
640       xbt_dict_set(files, name, file, NULL);
641     } else {
642       XBT_WARN("%s already exists", name);
643       if (SD_task_get_amount(file) != size) {
644         XBT_WARN("Ignoring file %s size redefinition from %.0f to %.0f",
645               name, SD_task_get_amount(file), size);
646       }
647     }
648     SD_task_dependency_add(NULL, NULL, current_job, file);
649     if (xbt_dynar_length(file->tasks_before) > 1) {
650       XBT_WARN("File %s created at more than one location...", file->name);
651     }
652   } else {
653     file = xbt_dict_get_or_null(jobs, name_head);
654     if (file != NULL) {
655       SD_task_dependency_add(NULL, NULL, current_job, file);
656     }
657   }
658   free(name);
659 }