Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
fc6fde1ae2ced4e361d1983ce08fb80a8d1029d5
[simgrid.git] / src / instr / instr_platform.cpp
1 /* Copyright (c) 2010-2020. The SimGrid Team. All rights reserved.          */
2
3 /* This program is free software; you can redistribute it and/or modify it
4  * under the terms of the license (GNU LGPL) which comes with this package. */
5
6 #include "src/instr/instr_private.hpp"
7
8 #include "simgrid/kernel/routing/NetPoint.hpp"
9 #include "simgrid/kernel/routing/NetZoneImpl.hpp"
10 #include "simgrid/s4u/Actor.hpp"
11 #include "simgrid/s4u/Comm.hpp"
12 #include "simgrid/s4u/Engine.hpp"
13 #include "simgrid/s4u/Exec.hpp"
14 #include "simgrid/s4u/Host.hpp"
15 #include "simgrid/s4u/VirtualMachine.hpp"
16 #include "src/surf/cpu_interface.hpp"
17 #include "src/surf/network_interface.hpp"
18 #include "src/surf/surf_interface.hpp"
19 #include "src/surf/xml/platf_private.hpp"
20 #include "surf/surf.hpp"
21 #include "xbt/graph.h"
22
23 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(instr_routing, instr, "Tracing platform hierarchy");
24
25 static std::vector<simgrid::instr::NetZoneContainer*> currentContainer; /* push and pop, used only in creation */
26
27 std::string instr_pid(simgrid::s4u::Actor const& proc)
28 {
29   return std::string(proc.get_name()) + "-" + std::to_string(proc.get_pid());
30 }
31
32 static const char* instr_node_name(const s_xbt_node_t* node)
33 {
34   return static_cast<char*>(xbt_graph_node_get_data(node));
35 }
36
37 static container_t lowestCommonAncestor(const simgrid::instr::Container* a1, const simgrid::instr::Container* a2)
38 {
39   // this is only an optimization (since most of a1 and a2 share the same parent)
40   if (a1->father_ == a2->father_)
41     return a1->father_;
42
43   // create an array with all ancestors of a1
44   std::vector<container_t> ancestors_a1;
45   container_t p = a1->father_;
46   while (p) {
47     ancestors_a1.push_back(p);
48     p = p->father_;
49   }
50
51   // create an array with all ancestors of a2
52   std::vector<container_t> ancestors_a2;
53   p = a2->father_;
54   while (p) {
55     ancestors_a2.push_back(p);
56     p = p->father_;
57   }
58
59   // find the lowest ancestor
60   p     = nullptr;
61   int i = ancestors_a1.size() - 1;
62   int j = ancestors_a2.size() - 1;
63   while (i >= 0 && j >= 0) {
64     container_t a1p = ancestors_a1.at(i);
65     const simgrid::instr::Container* a2p = ancestors_a2.at(j);
66     if (a1p == a2p) {
67       p = a1p;
68     } else {
69       break;
70     }
71     i--;
72     j--;
73   }
74   return p;
75 }
76
77 static void linkContainers(container_t src, container_t dst, std::set<std::string>* filter)
78 {
79   // ignore loopback
80   if (src->get_name() == "__loopback__" || dst->get_name() == "__loopback__") {
81     XBT_DEBUG("  linkContainers: ignoring loopback link");
82     return;
83   }
84
85   // find common father
86   container_t father = lowestCommonAncestor(src, dst);
87   if (not father) {
88     xbt_die("common father unknown, this is a tracing problem");
89   }
90
91   // check if we already register this pair (we only need one direction)
92   std::string aux1 = src->get_name() + dst->get_name();
93   std::string aux2 = dst->get_name() + src->get_name();
94   if (filter->find(aux1) != filter->end()) {
95     XBT_DEBUG("  linkContainers: already registered %s <-> %s (1)", src->get_cname(), dst->get_cname());
96     return;
97   }
98   if (filter->find(aux2) != filter->end()) {
99     XBT_DEBUG("  linkContainers: already registered %s <-> %s (2)", dst->get_cname(), src->get_cname());
100     return;
101   }
102
103   // ok, not found, register it
104   filter->insert(aux1);
105   filter->insert(aux2);
106
107   // declare type
108   std::string link_typename = father->type_->get_name() + "-" + src->type_->get_name() +
109                               std::to_string(src->type_->get_id()) + "-" + dst->type_->get_name() +
110                               std::to_string(dst->type_->get_id());
111   simgrid::instr::LinkType* link = father->type_->by_name_or_create(link_typename, src->type_, dst->type_);
112   link->set_calling_container(father);
113
114   // create the link
115   static long long counter = 0;
116
117   std::string key = std::to_string(counter);
118   counter++;
119
120   link->start_event(src, "topology", key);
121   link->end_event(dst, "topology", key);
122
123   XBT_DEBUG("  linkContainers %s <-> %s", src->get_cname(), dst->get_cname());
124 }
125
126 static void recursiveGraphExtraction(const simgrid::s4u::NetZone* netzone, container_t container,
127                                      std::set<std::string>* filter)
128 {
129   if (not TRACE_platform_topology()) {
130     XBT_DEBUG("Graph extraction disabled by user.");
131     return;
132   }
133   XBT_DEBUG("Graph extraction for NetZone = %s", netzone->get_cname());
134   if (not netzone->get_children().empty()) {
135     // bottom-up recursion
136     for (auto const& nz_son : netzone->get_children()) {
137       container_t child_container = container->children_.at(nz_son->get_name());
138       recursiveGraphExtraction(nz_son, child_container, filter);
139     }
140   }
141
142   xbt_graph_t graph                        = xbt_graph_new_graph(0, nullptr);
143   std::map<std::string, xbt_node_t>* nodes = new std::map<std::string, xbt_node_t>();
144   std::map<std::string, xbt_edge_t>* edges = new std::map<std::string, xbt_edge_t>();
145
146   netzone->get_impl()->get_graph(graph, nodes, edges);
147   for (auto elm : *edges) {
148     const xbt_edge* edge = elm.second;
149     linkContainers(simgrid::instr::Container::by_name(static_cast<const char*>(edge->src->data)),
150                    simgrid::instr::Container::by_name(static_cast<const char*>(edge->dst->data)), filter);
151   }
152   delete nodes;
153   delete edges;
154   xbt_graph_free_graph(graph, xbt_free_f, xbt_free_f, nullptr);
155 }
156
157 /*
158  * Callbacks
159  */
160 static void instr_netzone_on_creation(simgrid::s4u::NetZone const& netzone)
161 {
162   std::string id = netzone.get_name();
163   if (simgrid::instr::Container::get_root() == nullptr) {
164     simgrid::instr::NetZoneContainer* root = new simgrid::instr::NetZoneContainer(id, 0, nullptr);
165     xbt_assert(simgrid::instr::Container::get_root() == root);
166
167     if (TRACE_smpi_is_enabled()) {
168       simgrid::instr::ContainerType* mpi = root->type_->by_name_or_create<simgrid::instr::ContainerType>("MPI");
169       if (not TRACE_smpi_is_grouped())
170         mpi->by_name_or_create<simgrid::instr::StateType>("MPI_STATE");
171       root->type_->by_name_or_create("MPI_LINK", mpi, mpi);
172       // TODO See if we can move this to the LoadBalancer plugin
173       root->type_->by_name_or_create("MIGRATE_LINK", mpi, mpi);
174       mpi->by_name_or_create<simgrid::instr::StateType>("MIGRATE_STATE");
175     }
176
177     if (TRACE_needs_platform()) {
178       currentContainer.push_back(root);
179     }
180     return;
181   }
182
183   if (TRACE_needs_platform()) {
184     simgrid::instr::NetZoneContainer* container =
185         new simgrid::instr::NetZoneContainer(id, currentContainer.size(), currentContainer.back());
186     currentContainer.push_back(container);
187   }
188 }
189
190 static void instr_link_on_creation(simgrid::s4u::Link const& link)
191 {
192   if (currentContainer.empty()) // No ongoing parsing. Are you creating the loopback?
193     return;
194
195   container_t container = new simgrid::instr::Container(link.get_name(), "LINK", currentContainer.back());
196
197   if ((TRACE_categorized() || TRACE_uncategorized() || TRACE_platform()) && (not TRACE_disable_link())) {
198     simgrid::instr::VariableType* bandwidth = container->type_->by_name_or_create("bandwidth", "");
199     bandwidth->set_calling_container(container);
200     bandwidth->set_event(0, link.get_bandwidth());
201     simgrid::instr::VariableType* latency = container->type_->by_name_or_create("latency", "");
202     latency->set_calling_container(container);
203     latency->set_event(0, link.get_latency());
204   }
205   if (TRACE_uncategorized()) {
206     container->type_->by_name_or_create("bandwidth_used", "0.5 0.5 0.5");
207   }
208 }
209
210 static void instr_host_on_creation(simgrid::s4u::Host const& host)
211 {
212   simgrid::instr::Container* container  = new simgrid::instr::HostContainer(host, currentContainer.back());
213   const simgrid::instr::Container* root = simgrid::instr::Container::get_root();
214
215   if ((TRACE_categorized() || TRACE_uncategorized() || TRACE_platform()) && (not TRACE_disable_speed())) {
216     simgrid::instr::VariableType* speed = container->type_->by_name_or_create("speed", "");
217     speed->set_calling_container(container);
218     speed->set_event(0, host.get_speed());
219
220     simgrid::instr::VariableType* cores = container->type_->by_name_or_create("core_count", "");
221     cores->set_calling_container(container);
222     cores->set_event(0, host.get_core_count());
223   }
224
225   if (TRACE_uncategorized())
226     container->type_->by_name_or_create("speed_used", "0.5 0.5 0.5");
227
228   if (TRACE_smpi_is_enabled() && TRACE_smpi_is_grouped()) {
229     simgrid::instr::ContainerType* mpi = container->type_->by_name_or_create<simgrid::instr::ContainerType>("MPI");
230     mpi->by_name_or_create<simgrid::instr::StateType>("MPI_STATE");
231     // TODO See if we can move this to the LoadBalancer plugin
232     root->type_->by_name_or_create("MIGRATE_LINK", mpi, mpi);
233     mpi->by_name_or_create<simgrid::instr::StateType>("MIGRATE_STATE");
234   }
235 }
236
237 static void instr_host_on_speed_change(simgrid::s4u::Host const& host)
238 {
239   simgrid::instr::Container::by_name(host.get_name())
240       ->get_variable("speed")
241       ->set_event(surf_get_clock(), host.get_core_count() * host.get_available_speed());
242 }
243
244 static void instr_action_on_state_change(simgrid::kernel::resource::Action const& action,
245                                          simgrid::kernel::resource::Action::State /* previous */)
246 {
247   int n = action.get_variable()->get_number_of_constraint();
248
249   for (int i = 0; i < n; i++) {
250     double value = action.get_variable()->get_value() * action.get_variable()->get_constraint_weight(i);
251     /* Beware of composite actions: ptasks put links and cpus together. Extra pb: we cannot dynamic_cast from void* */
252     simgrid::kernel::resource::Resource* resource = action.get_variable()->get_constraint(i)->get_id();
253     const simgrid::kernel::resource::Cpu* cpu     = dynamic_cast<simgrid::kernel::resource::Cpu*>(resource);
254
255     if (cpu != nullptr)
256       TRACE_surf_resource_set_utilization("HOST", "speed_used", cpu->get_cname(), action.get_category(), value,
257                                           action.get_last_update(), SIMIX_get_clock() - action.get_last_update());
258
259     const simgrid::kernel::resource::LinkImpl* link = dynamic_cast<simgrid::kernel::resource::LinkImpl*>(resource);
260
261     if (link != nullptr)
262       TRACE_surf_resource_set_utilization("LINK", "bandwidth_used", link->get_cname(), action.get_category(), value,
263                                           action.get_last_update(), SIMIX_get_clock() - action.get_last_update());
264   }
265 }
266
267 static void instr_link_on_bandwidth_change(simgrid::s4u::Link const& link)
268 {
269   simgrid::instr::Container::by_name(link.get_name())
270       ->get_variable("bandwidth")
271       ->set_event(surf_get_clock(), sg_bandwidth_factor * link.get_bandwidth());
272 }
273
274 static void instr_netpoint_on_creation(simgrid::kernel::routing::NetPoint const& netpoint)
275 {
276   if (netpoint.is_router())
277     new simgrid::instr::RouterContainer(netpoint.get_name(), currentContainer.back());
278 }
279
280 static void instr_on_platform_created()
281 {
282   currentContainer.clear();
283   std::set<std::string>* filter = new std::set<std::string>();
284   XBT_DEBUG("Starting graph extraction.");
285   recursiveGraphExtraction(simgrid::s4u::Engine::get_instance()->get_netzone_root(),
286                            simgrid::instr::Container::get_root(), filter);
287   XBT_DEBUG("Graph extraction finished.");
288   delete filter;
289   TRACE_paje_dump_buffer(true);
290 }
291
292 static void instr_actor_on_creation(simgrid::s4u::Actor const& actor)
293 {
294   const simgrid::instr::Container* root = simgrid::instr::Container::get_root();
295   simgrid::instr::Container* container  = simgrid::instr::Container::by_name(actor.get_host()->get_name());
296
297   container->create_child(instr_pid(actor), "ACTOR");
298   simgrid::instr::ContainerType* actor_type =
299       container->type_->by_name_or_create<simgrid::instr::ContainerType>("ACTOR");
300   simgrid::instr::StateType* state = actor_type->by_name_or_create<simgrid::instr::StateType>("ACTOR_STATE");
301   state->add_entity_value("suspend", "1 0 1");
302   state->add_entity_value("sleep", "1 1 0");
303   state->add_entity_value("receive", "1 0 0");
304   state->add_entity_value("send", "0 0 1");
305   state->add_entity_value("execute", "0 1 1");
306   root->type_->by_name_or_create("ACTOR_LINK", actor_type, actor_type);
307   root->type_->by_name_or_create("ACTOR_TASK_LINK", actor_type, actor_type);
308
309   std::string container_name = instr_pid(actor);
310   actor.on_exit([container_name](bool failed) {
311     if (failed)
312       // kill means that this actor no longer exists, let's destroy it
313       simgrid::instr::Container::by_name(container_name)->remove_from_parent();
314   });
315 }
316
317 static void instr_actor_on_host_change(simgrid::s4u::Actor const& actor,
318                                        simgrid::s4u::Host const& /*previous_location*/)
319 {
320   static long long int counter = 0;
321   container_t container = simgrid::instr::Container::by_name(instr_pid(actor));
322   simgrid::instr::LinkType* link = simgrid::instr::Container::get_root()->get_link("ACTOR_LINK");
323
324   // start link
325   link->start_event(container, "M", std::to_string(counter));
326   // destroy existing container of this process
327   container->remove_from_parent();
328   // create new container on the new_host location
329   simgrid::instr::Container::by_name(actor.get_host()->get_name())->create_child(instr_pid(actor), "ACTOR");
330   // end link
331   link->end_event(simgrid::instr::Container::by_name(instr_pid(actor)), "M", std::to_string(counter));
332   counter++;
333 }
334
335 static void instr_vm_on_creation(simgrid::s4u::Host const& host)
336 {
337   const simgrid::instr::Container* container = new simgrid::instr::HostContainer(host, currentContainer.back());
338   const simgrid::instr::Container* root      = simgrid::instr::Container::get_root();
339   simgrid::instr::ContainerType* vm = container->type_->by_name_or_create<simgrid::instr::ContainerType>("VM");
340   simgrid::instr::StateType* state  = vm->by_name_or_create<simgrid::instr::StateType>("VM_STATE");
341   state->add_entity_value("suspend", "1 0 1");
342   state->add_entity_value("sleep", "1 1 0");
343   state->add_entity_value("receive", "1 0 0");
344   state->add_entity_value("send", "0 0 1");
345   state->add_entity_value("execute", "0 1 1");
346   root->type_->by_name_or_create("VM_LINK", vm, vm);
347   root->type_->by_name_or_create("VM_ACTOR_LINK", vm, vm);
348 }
349
350 void instr_define_callbacks()
351 {
352   // always need the callbacks to zones (we need only the root zone), to create the rootContainer and the rootType
353   // properly
354   if (TRACE_needs_platform()) {
355     simgrid::s4u::Engine::on_platform_created.connect(instr_on_platform_created);
356     simgrid::s4u::Host::on_creation.connect(instr_host_on_creation);
357     simgrid::s4u::Host::on_speed_change.connect(instr_host_on_speed_change);
358     simgrid::s4u::Link::on_creation.connect(instr_link_on_creation);
359     simgrid::s4u::Link::on_bandwidth_change.connect(instr_link_on_bandwidth_change);
360     simgrid::s4u::NetZone::on_seal.connect(
361         [](simgrid::s4u::NetZone const& /*netzone*/) { currentContainer.pop_back(); });
362     simgrid::kernel::routing::NetPoint::on_creation.connect(instr_netpoint_on_creation);
363   }
364   simgrid::s4u::NetZone::on_creation.connect(instr_netzone_on_creation);
365
366   simgrid::kernel::resource::CpuAction::on_state_change.connect(instr_action_on_state_change);
367   simgrid::s4u::Link::on_communication_state_change.connect(instr_action_on_state_change);
368
369   if (TRACE_actor_is_enabled()) {
370     simgrid::s4u::Actor::on_creation.connect(instr_actor_on_creation);
371     simgrid::s4u::Actor::on_destruction.connect([](simgrid::s4u::Actor const& actor) {
372       auto container = simgrid::instr::Container::by_name_or_null(instr_pid(actor));
373       if (container != nullptr)
374         container->remove_from_parent();
375     });
376     simgrid::s4u::Actor::on_suspend.connect([](simgrid::s4u::Actor const& actor) {
377       simgrid::instr::Container::by_name(instr_pid(actor))->get_state("ACTOR_STATE")->push_event("suspend");
378     });
379     simgrid::s4u::Actor::on_resume.connect([](simgrid::s4u::Actor const& actor) {
380       simgrid::instr::Container::by_name(instr_pid(actor))->get_state("ACTOR_STATE")->pop_event();
381     });
382     simgrid::s4u::Actor::on_sleep.connect([](simgrid::s4u::Actor const& actor) {
383       simgrid::instr::Container::by_name(instr_pid(actor))->get_state("ACTOR_STATE")->push_event("sleep");
384     });
385     simgrid::s4u::Actor::on_wake_up.connect([](simgrid::s4u::Actor const& actor) {
386       simgrid::instr::Container::by_name(instr_pid(actor))->get_state("ACTOR_STATE")->pop_event();
387     });
388     simgrid::s4u::Exec::on_start.connect([](simgrid::s4u::Actor const& actor, simgrid::s4u::Exec const&) {
389       simgrid::instr::Container::by_name(instr_pid(actor))->get_state("ACTOR_STATE")->push_event("execute");
390     });
391     simgrid::s4u::Exec::on_completion.connect([](simgrid::s4u::Actor const& actor, simgrid::s4u::Exec const&) {
392       simgrid::instr::Container::by_name(instr_pid(actor))->get_state("ACTOR_STATE")->pop_event();
393     });
394     simgrid::s4u::Comm::on_sender_start.connect([](simgrid::s4u::Actor const& actor) {
395       simgrid::instr::Container::by_name(instr_pid(actor))->get_state("ACTOR_STATE")->push_event("send");
396     });
397     simgrid::s4u::Comm::on_receiver_start.connect([](simgrid::s4u::Actor const& actor) {
398       simgrid::instr::Container::by_name(instr_pid(actor))->get_state("ACTOR_STATE")->push_event("receive");
399     });
400     simgrid::s4u::Comm::on_completion.connect([](simgrid::s4u::Actor const& actor) {
401       simgrid::instr::Container::by_name(instr_pid(actor))->get_state("ACTOR_STATE")->pop_event();
402     });
403     simgrid::s4u::Actor::on_host_change.connect(instr_actor_on_host_change);
404   }
405
406   if (TRACE_vm_is_enabled()) {
407     simgrid::s4u::Host::on_creation.connect(instr_vm_on_creation);
408     simgrid::s4u::VirtualMachine::on_start.connect([](simgrid::s4u::VirtualMachine const& vm) {
409       simgrid::instr::Container::by_name(vm.get_name())->get_state("VM_STATE")->push_event("start");
410     });
411     simgrid::s4u::VirtualMachine::on_started.connect([](simgrid::s4u::VirtualMachine const& vm) {
412       simgrid::instr::Container::by_name(vm.get_name())->get_state("VM_STATE")->pop_event();
413     });
414     simgrid::s4u::VirtualMachine::on_suspend.connect([](simgrid::s4u::VirtualMachine const& vm) {
415       simgrid::instr::Container::by_name(vm.get_name())->get_state("VM_STATE")->push_event("suspend");
416     });
417     simgrid::s4u::VirtualMachine::on_resume.connect([](simgrid::s4u::VirtualMachine const& vm) {
418       simgrid::instr::Container::by_name(vm.get_name())->get_state("VM_STATE")->pop_event();
419     });
420     simgrid::s4u::Host::on_destruction.connect([](simgrid::s4u::Host const& host) {
421       simgrid::instr::Container::by_name(host.get_name())->remove_from_parent();
422     });
423   }
424 }
425 /*
426  * user categories support
427  */
428 static void recursiveNewVariableType(const std::string& new_typename, const std::string& color,
429                                      simgrid::instr::Type* root)
430 {
431   if (root->get_name() == "HOST" || root->get_name() == "VM")
432     root->by_name_or_create(std::string("p") + new_typename, color);
433
434   if (root->get_name() == "LINK")
435     root->by_name_or_create(std::string("b") + new_typename, color);
436
437   for (auto const& elm : root->children_) {
438     recursiveNewVariableType(new_typename, color, elm.second.get());
439   }
440 }
441
442 void instr_new_variable_type(const std::string& new_typename, const std::string& color)
443 {
444   recursiveNewVariableType(new_typename, color, simgrid::instr::Container::get_root()->type_);
445 }
446
447 static void recursiveNewUserVariableType(const std::string& father_type, const std::string& new_typename,
448                                          const std::string& color, simgrid::instr::Type* root)
449 {
450   if (root->get_name() == father_type) {
451     root->by_name_or_create(new_typename, color);
452   }
453   for (auto const& elm : root->children_)
454     recursiveNewUserVariableType(father_type, new_typename, color, elm.second.get());
455 }
456
457 void instr_new_user_variable_type(const std::string& father_type, const std::string& new_typename,
458                                   const std::string& color)
459 {
460   recursiveNewUserVariableType(father_type, new_typename, color, simgrid::instr::Container::get_root()->type_);
461 }
462
463 static void recursiveNewUserStateType(const std::string& father_type, const std::string& new_typename,
464                                       simgrid::instr::Type* root)
465 {
466   if (root->get_name() == father_type)
467     root->by_name_or_create<simgrid::instr::StateType>(new_typename);
468
469   for (auto const& elm : root->children_)
470     recursiveNewUserStateType(father_type, new_typename, elm.second.get());
471 }
472
473 void instr_new_user_state_type(const std::string& father_type, const std::string& new_typename)
474 {
475   recursiveNewUserStateType(father_type, new_typename, simgrid::instr::Container::get_root()->type_);
476 }
477
478 static void recursiveNewValueForUserStateType(const std::string& type_name, const char* val, const std::string& color,
479                                               simgrid::instr::Type* root)
480 {
481   if (root->get_name() == type_name)
482     static_cast<simgrid::instr::StateType*>(root)->add_entity_value(val, color);
483
484   for (auto const& elm : root->children_)
485     recursiveNewValueForUserStateType(type_name, val, color, elm.second.get());
486 }
487
488 void instr_new_value_for_user_state_type(const std::string& type_name, const char* value, const std::string& color)
489 {
490   recursiveNewValueForUserStateType(type_name, value, color, simgrid::instr::Container::get_root()->type_);
491 }
492
493 #define GRAPHICATOR_SUPPORT_FUNCTIONS
494
495 static void recursiveXBTGraphExtraction(const s_xbt_graph_t* graph, std::map<std::string, xbt_node_t>* nodes,
496                                         std::map<std::string, xbt_edge_t>* edges, const_sg_netzone_t netzone,
497                                         container_t container)
498 {
499   if (not netzone->get_children().empty()) {
500     // bottom-up recursion
501     for (auto const& netzone_child : netzone->get_children()) {
502       container_t child_container = container->children_.at(netzone_child->get_name());
503       recursiveXBTGraphExtraction(graph, nodes, edges, netzone_child, child_container);
504     }
505   }
506
507   netzone->get_impl()->get_graph(graph, nodes, edges);
508 }
509
510 xbt_graph_t instr_routing_platform_graph()
511 {
512   xbt_graph_t ret                          = xbt_graph_new_graph(0, nullptr);
513   std::map<std::string, xbt_node_t>* nodes = new std::map<std::string, xbt_node_t>();
514   std::map<std::string, xbt_edge_t>* edges = new std::map<std::string, xbt_edge_t>();
515   recursiveXBTGraphExtraction(ret, nodes, edges, simgrid::s4u::Engine::get_instance()->get_netzone_root(),
516                               simgrid::instr::Container::get_root());
517   delete nodes;
518   delete edges;
519   return ret;
520 }
521
522 void instr_routing_platform_graph_export_graphviz(const s_xbt_graph_t* g, const char* filename)
523 {
524   unsigned int cursor = 0;
525   xbt_node_t node     = nullptr;
526   xbt_edge_t edge     = nullptr;
527
528   FILE* file = fopen(filename, "w");
529   xbt_assert(file, "Failed to open %s \n", filename);
530
531   if (g->directed)
532     fprintf(file, "digraph test {\n");
533   else
534     fprintf(file, "graph test {\n");
535
536   fprintf(file, "  graph [overlap=scale]\n");
537
538   fprintf(file, "  node [shape=box, style=filled]\n");
539   fprintf(file, "  node [width=.3, height=.3, style=filled, color=skyblue]\n\n");
540
541   xbt_dynar_foreach (g->nodes, cursor, node) {
542     fprintf(file, "  \"%s\";\n", instr_node_name(node));
543   }
544   xbt_dynar_foreach (g->edges, cursor, edge) {
545     const char* src_s = instr_node_name(edge->src);
546     const char* dst_s = instr_node_name(edge->dst);
547     if (g->directed)
548       fprintf(file, "  \"%s\" -> \"%s\";\n", src_s, dst_s);
549     else
550       fprintf(file, "  \"%s\" -- \"%s\";\n", src_s, dst_s);
551   }
552   fprintf(file, "}\n");
553   fclose(file);
554 }