Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Reduce code duplication.
[simgrid.git] / src / plugins / link_load.cpp
1 /* Copyright (c) 2017-2022. 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 <simgrid/plugins/load.h>
7 #include <simgrid/s4u/Engine.hpp>
8
9 #include "src/kernel/activity/CommImpl.hpp"
10 #include "src/kernel/resource/NetworkModel.hpp"
11
12 #include <limits>
13
14 SIMGRID_REGISTER_PLUGIN(link_load, "Link cumulated load.", &sg_link_load_plugin_init)
15
16 /** @defgroup plugin_link_load Plugin Link Cumulated Load
17
18  This is the link cumulated load plugin.
19  It enables to monitor how links are used over time by cumulating the amount of bytes that go through the link.
20
21  Usage:
22  - Call sg_link_load_plugin_init() between the SimGrid engine initialization and the platform loading.
23  - Track the links you want to monitor with sg_link_load_track() — you can untrack them later with
24  sg_link_load_untrack().
25  - Query the cumulated data about your link via these functions:
26    - sg_link_get_cum_load() to get the total load (in bytes) that went though the link since last reset.
27    - sg_link_get_avg_load() to get the average load (in bytes) that went though the link since last reset.
28    - sg_link_get_min_instantaneous_load() to get the peak minimum load (in bytes per second) observed on the link since
29  last reset.
30    - sg_link_get_max_instantaneous_load() to get the peak maximum load (in bytes per second) observer on the link since
31  last reset.
32  - Reset the counters on any tracked link via sg_link_load_reset().
33 */
34
35 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(link_load, kernel, "Logging specific to the LinkLoad plugin");
36
37 namespace simgrid::plugin {
38
39 class LinkLoad {
40   s4u::Link* link_{};      /*< The link onto which this data is attached*/
41   bool is_tracked_{false}; /*<Whether the link is tracked or not*/
42
43   double cumulated_bytes_{};      /*< Cumulated load since last reset*/
44   double min_bytes_per_second_{}; /*< Minimum instantaneous load observed since last reset*/
45   double max_bytes_per_second_{}; /*< Maximum instantaneous load observed since last reset*/
46   double last_reset_{};           /*< Timestamp of the last reset (init timestamp by default)*/
47   double last_updated_{};         /*< Timestamp of the last energy update event*/
48
49 public:
50   static xbt::Extension<s4u::Link, LinkLoad> EXTENSION_ID;
51
52   explicit LinkLoad(s4u::Link* ptr);
53
54   void track();
55   void untrack();
56   void reset();
57   void update();
58   double get_average_bytes();
59
60   /// Getter methods.
61   bool is_tracked() const { return is_tracked_; }
62   double get_cumulated_bytes();
63   double get_min_bytes_per_second();
64   double get_max_bytes_per_second();
65 };
66
67 xbt::Extension<s4u::Link, LinkLoad> LinkLoad::EXTENSION_ID;
68
69 LinkLoad::LinkLoad(s4u::Link* ptr) : link_(ptr)
70 {
71   XBT_DEBUG("Instantiating a LinkLoad for link '%s'", link_->get_cname());
72 }
73
74 void LinkLoad::track()
75 {
76   xbt_assert(not is_tracked_, "Trying to track load of link '%s' while it is already tracked, aborting.",
77              link_->get_cname());
78   XBT_DEBUG("Tracking load of link '%s'", link_->get_cname());
79
80   is_tracked_ = true;
81   reset();
82 }
83
84 void LinkLoad::untrack()
85 {
86   xbt_assert(is_tracked_, "Trying to untrack load of link '%s' while it is not tracked, aborting.", link_->get_cname());
87   XBT_DEBUG("Untracking load of link '%s'", link_->get_cname());
88
89   is_tracked_ = false;
90 }
91
92 void LinkLoad::reset()
93 {
94   XBT_DEBUG("Resetting load of link '%s'", link_->get_cname());
95
96   cumulated_bytes_      = 0.0;
97   min_bytes_per_second_ = std::numeric_limits<double>::max();
98   max_bytes_per_second_ = std::numeric_limits<double>::lowest();
99   XBT_DEBUG("min_bytes_per_second_ = %g", min_bytes_per_second_);
100   XBT_DEBUG("max_bytes_per_second_ = %g", max_bytes_per_second_);
101   last_reset_   = simgrid::s4u::Engine::get_clock();
102   last_updated_ = last_reset_;
103 }
104
105 void LinkLoad::update()
106 {
107   XBT_DEBUG("Updating load of link '%s'", link_->get_cname());
108   xbt_assert(is_tracked_,
109              "Trying to update load of link '%s' while it is NOT tracked, aborting."
110              " Please track your link with sg_link_load_track before trying to access any of its load metrics.",
111              link_->get_cname());
112
113   double current_instantaneous_bytes_per_second = link_->get_usage();
114   double now                                    = simgrid::s4u::Engine::get_clock();
115
116   // Update minimum/maximum observed values if needed
117   min_bytes_per_second_ = std::min(min_bytes_per_second_, current_instantaneous_bytes_per_second);
118   max_bytes_per_second_ = std::max(max_bytes_per_second_, current_instantaneous_bytes_per_second);
119
120   // Update cumulated load
121   double duration_since_last_update = now - last_updated_;
122   double bytes_since_last_update    = duration_since_last_update * current_instantaneous_bytes_per_second;
123   XBT_DEBUG("Cumulated %g bytes since last update (duration of %g seconds)", bytes_since_last_update,
124             duration_since_last_update);
125   xbt_assert(bytes_since_last_update >= 0, "LinkLoad plugin inconsistency: negative amount of bytes is accumulated.");
126
127   cumulated_bytes_ += bytes_since_last_update;
128   last_updated_ = now;
129 }
130
131 double LinkLoad::get_cumulated_bytes()
132 {
133   update();
134   return cumulated_bytes_;
135 }
136 double LinkLoad::get_min_bytes_per_second()
137 {
138   update();
139   return min_bytes_per_second_;
140 }
141 double LinkLoad::get_max_bytes_per_second()
142 {
143   update();
144   return max_bytes_per_second_;
145 }
146
147 double LinkLoad::get_average_bytes()
148 {
149   update();
150
151   double now = simgrid::s4u::Engine::get_clock();
152   if (now > last_reset_)
153     return cumulated_bytes_ / (now - last_reset_);
154   else
155     return 0;
156 }
157
158 } // namespace simgrid::plugin
159
160 using simgrid::plugin::LinkLoad;
161
162 /* **************************** events  callback *************************** */
163 static void on_communication(const simgrid::kernel::activity::CommImpl& comm)
164 {
165   for (const auto* link : comm.get_traversed_links()) {
166     if (link != nullptr && link->get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI) {
167       auto* link_load = link->extension<LinkLoad>();
168       XBT_DEBUG("Update %s on Comm Start/End", link->get_cname());
169       if (link_load->is_tracked())
170         link_load->update();
171     }
172   }
173 }
174
175 /* **************************** Public interface *************************** */
176
177 /**
178  * @ingroup plugin_link_load
179  * @brief Initialize the link cumulated load plugin.
180  * @pre The energy plugin should NOT be initialized.
181  */
182 void sg_link_load_plugin_init()
183 {
184   xbt_assert(simgrid::s4u::Engine::get_instance()->get_host_count() == 0 &&
185                  simgrid::s4u::Engine::get_instance()->get_link_count() == 0,
186              "Please call sg_link_load_plugin_init() BEFORE initializing the platform.");
187   xbt_assert(not LinkLoad::EXTENSION_ID.valid(), "Double call to sg_link_load_plugin_init. Aborting.");
188   LinkLoad::EXTENSION_ID = simgrid::s4u::Link::extension_create<LinkLoad>();
189
190   // Attach new LinkLoad links created in the future.
191   simgrid::s4u::Link::on_creation_cb([](simgrid::s4u::Link& link) {
192     if (link.get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI) {
193       XBT_DEBUG("Wired link '%s' created. Attaching a LinkLoad to it.", link.get_cname());
194       link.extension_set(new LinkLoad(&link));
195     } else {
196       XBT_DEBUG("Wireless link '%s' created. NOT attaching any LinkLoad to it.", link.get_cname());
197     }
198   });
199
200   // Call this plugin on some of the links' events.
201   simgrid::kernel::activity::CommImpl::on_start.connect(&on_communication);
202   simgrid::kernel::activity::CommImpl::on_completion.connect(&on_communication);
203
204   simgrid::s4u::Link::on_state_change_cb([](simgrid::s4u::Link const& link) {
205     if (link.get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI) {
206       auto link_load = link.extension<LinkLoad>();
207       if (link_load->is_tracked())
208         link_load->update();
209     }
210   });
211   simgrid::s4u::Link::on_communication_state_change_cb([](simgrid::kernel::resource::NetworkAction const& action,
212                                                           simgrid::kernel::resource::Action::State /* previous */) {
213     for (auto const* link : action.get_links()) {
214       if (link != nullptr && link->get_sharing_policy() != simgrid::s4u::Link::SharingPolicy::WIFI) {
215         auto link_load = link->get_iface()->extension<LinkLoad>();
216         if (link_load->is_tracked())
217           link_load->update();
218       }
219     }
220   });
221 }
222
223 /**
224  * @ingroup plugin_link_load
225  * @brief Start the tracking of a link.
226  * @details This is required so the link cumulated load can be obtained later on.
227  * @pre The energy plugin should be initialized (cf. sg_link_load_plugin_init()).
228  * @pre The link should be in "untracked" state. In other words, do not call this function twice on the same link
229  * without a sg_link_load_untrack() call between them.
230  *
231  * @param link The link to track.
232  */
233 void sg_link_load_track(const_sg_link_t link)
234 {
235   xbt_assert(LinkLoad::EXTENSION_ID.valid(),
236              "Please call sg_link_load_plugin_init before sg_link_load_track. Aborting.");
237   link->extension<LinkLoad>()->track();
238 }
239
240 /**
241  * @ingroup plugin_link_load
242  * @brief Stop the tracking of a link.
243  * @details Once the tracking is stopped, the cumulated load of the link can no longer be obtained until
244  * sg_link_load_track() is called again on this link.
245  * @pre The energy plugin should be initialized (cf. sg_link_load_plugin_init()).
246  * @pre The link should be in "tracked" state. In other words, do not call this function twice on the same link without
247  * a sg_link_load_track() call between them.
248  *
249  * @param link The link to untrack.
250  */
251 void sg_link_load_untrack(const_sg_link_t link)
252 {
253   xbt_assert(LinkLoad::EXTENSION_ID.valid(),
254              "Please call sg_link_load_plugin_init before sg_link_load_untrack. Aborting.");
255   link->extension<LinkLoad>()->untrack();
256 }
257
258 /**
259  * @ingroup plugin_link_load
260  * @brief Resets the cumulated load counters of a link.
261  * @pre The energy plugin should be initialized (cf. sg_link_load_plugin_init()).
262  * @pre The link should be in "tracked" state (cf. sg_link_load_track()).
263  *
264  * @param link The link whose counters should be reset.
265  */
266 void sg_link_load_reset(const_sg_link_t link)
267 {
268   xbt_assert(LinkLoad::EXTENSION_ID.valid(),
269              "Please call sg_link_load_plugin_init before sg_link_load_reset. Aborting.");
270   link->extension<LinkLoad>()->reset();
271 }
272
273 /**
274  * @ingroup plugin_link_load
275  * @brief Get the cumulated load of a link (since the last call to sg_link_load_reset()).
276  * @pre The energy plugin should be initialized (cf. sg_link_load_plugin_init()).
277  * @pre The link should be in "tracked" state (cf. sg_link_load_track()).
278
279  * @param link The link whose cumulated load is requested.
280  * @return The load (in bytes) that passed through the given link since the last call to sg_link_load_reset.
281  */
282 double sg_link_get_cum_load(const_sg_link_t link)
283 {
284   xbt_assert(LinkLoad::EXTENSION_ID.valid(),
285              "Please call sg_link_load_plugin_init before sg_link_get_cum_load. Aborting.");
286   return link->extension<LinkLoad>()->get_cumulated_bytes();
287 }
288
289 /**
290  * @ingroup plugin_link_load
291  * @brief Get the average load of a link (since the last call to sg_link_load_reset()).
292  * @pre The energy plugin should be initialized (cf. sg_link_load_plugin_init()).
293  * @pre The link should be in "tracked" state (cf. sg_link_load_track()).
294
295  * @param link The link whose average load is requested.
296  * @return The average load (in bytes) that passed through the given link since the last call to sg_link_load_reset.
297  */
298 double sg_link_get_avg_load(const_sg_link_t link)
299 {
300   xbt_assert(LinkLoad::EXTENSION_ID.valid(),
301              "Please call sg_link_load_plugin_init before sg_link_get_avg_load. Aborting.");
302   return link->extension<LinkLoad>()->get_average_bytes();
303 }
304
305 /**
306  * @ingroup plugin_link_load
307  * @brief Get the minimum instantaneous load of a link (since the last call to sg_link_load_reset()).
308  * @pre The energy plugin should be initialized (cf. sg_link_load_plugin_init()).
309  * @pre The link should be in "tracked" state (cf. sg_link_load_track()).
310
311  * @param link The link whose average load is requested.
312  * @return The minimum load instantaneous load (in bytes per second) that passed through the given link since the last
313  call to sg_link_load_reset.
314  */
315 double sg_link_get_min_instantaneous_load(const_sg_link_t link)
316 {
317   xbt_assert(LinkLoad::EXTENSION_ID.valid(),
318              "Please call sg_link_load_plugin_init before sg_link_get_min_load. Aborting.");
319   return link->extension<LinkLoad>()->get_min_bytes_per_second();
320 }
321
322 /**
323  * @ingroup plugin_link_load
324  * @brief Get the maximum instantaneous load of a link (since the last call to sg_link_load_reset()).
325  * @pre The energy plugin should be initialized (cf. sg_link_load_plugin_init()).
326  * @pre The link should be in "tracked" state (cf. sg_link_load_track()).
327
328  * @param link The link whose average load is requested.
329  * @return The maximum load instantaneous load (in bytes per second) that passed through the given link since the last
330  call to sg_link_load_reset.
331  */
332 double sg_link_get_max_instantaneous_load(const_sg_link_t link)
333 {
334   xbt_assert(LinkLoad::EXTENSION_ID.valid(),
335              "Please call sg_link_load_plugin_init before sg_link_get_max_load. Aborting.");
336   return link->extension<LinkLoad>()->get_max_bytes_per_second();
337 }