Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
Pass the depends() from the observer to the mc::Transition for CommWait, Send and...
[simgrid.git] / src / kernel / actor / SimcallObserver.cpp
1 /* Copyright (c) 2019-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 "src/kernel/actor/SimcallObserver.hpp"
7 #include "simgrid/s4u/Host.hpp"
8 #include "src/kernel/activity/CommImpl.hpp"
9 #include "src/kernel/activity/MailboxImpl.hpp"
10 #include "src/kernel/activity/MutexImpl.hpp"
11 #include "src/kernel/actor/ActorImpl.hpp"
12 #include "src/mc/mc_config.hpp"
13
14 #include <sstream>
15
16 XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_observer, mc, "Logging specific to MC simcall observation");
17
18 namespace simgrid {
19 namespace kernel {
20 namespace actor {
21
22 bool SimcallObserver::depends(SimcallObserver* other)
23 {
24   THROW_UNIMPLEMENTED;
25 }
26 /* Random is only dependent when issued by the same actor (ie, always independent) */
27 bool RandomSimcall::depends(SimcallObserver* other)
28 {
29   return get_issuer() == other->get_issuer();
30 }
31 bool MutexSimcall::depends(SimcallObserver* other)
32 {
33   if (dynamic_cast<RandomSimcall*>(other) != nullptr)
34     return other->depends(this); /* Other is random, that is very permissive. Use that relation instead. */
35
36 #if 0 /* This code is currently broken and shouldn't be used. We must implement asynchronous locks before */
37   MutexSimcall* that = dynamic_cast<MutexSimcall*>(other);
38   if (that == nullptr)
39     return true; // Depends on anything we don't know
40
41   /* Theorem 4.4.7: Any pair of synchronization actions of distinct actors concerning distinct mutexes are independent */
42   if (this->get_issuer() != that->get_issuer() && this->get_mutex() != that->get_mutex())
43     return false;
44
45   /* Theorem 4.4.8 An AsyncMutexLock is independent with a MutexUnlock of another actor */
46   if (((dynamic_cast<MutexLockSimcall*>(this) != nullptr && dynamic_cast<MutexUnlockSimcall*>(that)) ||
47        (dynamic_cast<MutexLockSimcall*>(that) != nullptr && dynamic_cast<MutexUnlockSimcall*>(this))) &&
48       get_issuer() != other->get_issuer())
49     return false;
50 #endif
51   return true; // Depend on things we don't know for sure that they are independent
52 }
53
54 /*
55 std::string SimcallObserver::to_string(int) const
56 {
57   return simgrid::xbt::string_printf("[(%ld)%s (%s)] ", issuer_->get_pid(), issuer_->get_host()->get_cname(),
58                                      issuer_->get_cname());
59 }*/
60
61 std::string SimcallObserver::dot_label(int /*times_considered*/) const
62 {
63   if (issuer_->get_host())
64     return xbt::string_printf("[(%ld)%s] ", issuer_->get_pid(), issuer_->get_host()->get_cname());
65   return xbt::string_printf("[(%ld)] ", issuer_->get_pid());
66 }
67
68 std::string RandomSimcall::dot_label(int times_considered) const
69 {
70   return SimcallObserver::dot_label(times_considered) + "MC_RANDOM(" + std::to_string(next_value_) + ")";
71 }
72
73 void RandomSimcall::prepare(int times_considered)
74 {
75   next_value_ = min_ + times_considered;
76   XBT_DEBUG("MC_RANDOM(%d, %d) will return %d after %d times", min_, max_, next_value_, times_considered);
77 }
78
79 int RandomSimcall::get_max_consider() const
80 {
81   return max_ - min_ + 1;
82 }
83
84 std::string MutexUnlockSimcall::dot_label(int times_considered) const
85 {
86   return SimcallObserver::dot_label(times_considered) + "Mutex UNLOCK";
87 }
88
89 /*
90 std::string MutexLockSimcall::to_string(int times_considered) const
91 {
92   auto mutex      = get_mutex();
93   std::string res = SimcallObserver::to_string(times_considered) + (blocking_ ? "Mutex LOCK" : "Mutex TRYLOCK");
94   res += "(locked = " + std::to_string(mutex->is_locked());
95   res += ", owner = " + std::to_string(mutex->get_owner() ? mutex->get_owner()->get_pid() : -1);
96   res += ", sleeping = n/a)";
97   return res;
98 }*/
99
100 std::string MutexLockSimcall::dot_label(int times_considered) const
101 {
102   return SimcallObserver::dot_label(times_considered) + (blocking_ ? "Mutex LOCK" : "Mutex TRYLOCK");
103 }
104
105 bool MutexLockSimcall::is_enabled() const
106 {
107   return not blocking_ || get_mutex()->get_owner() == nullptr || get_mutex()->get_owner() == get_issuer();
108 }
109
110 std::string ConditionWaitSimcall::dot_label(int times_considered) const
111 {
112   return SimcallObserver::dot_label(times_considered) + "Condition WAIT";
113 }
114
115 bool ConditionWaitSimcall::is_enabled() const
116 {
117   static bool warned = false;
118   if (not warned) {
119     XBT_INFO("Using condition variables in model-checked code is still experimental. Use at your own risk");
120     warned = true;
121   }
122   return true;
123 }
124
125 std::string SemAcquireSimcall::dot_label(int times_considered) const
126 {
127   return SimcallObserver::dot_label(times_considered) + "Sem ACQUIRE";
128 }
129
130 bool SemAcquireSimcall::is_enabled() const
131 {
132   static bool warned = false;
133   if (not warned) {
134     XBT_INFO("Using semaphore in model-checked code is still experimental. Use at your own risk");
135     warned = true;
136   }
137   return true;
138 }
139
140 int ActivityTestanySimcall::get_max_consider() const
141 {
142   // Only Comms are of interest to MC for now. When all types of activities can be consider, this function can simply
143   // return the size of activities_.
144   int count = 0;
145   for (const auto& act : activities_)
146     if (dynamic_cast<activity::CommImpl*>(act) != nullptr)
147       count++;
148   return count;
149 }
150
151 void ActivityTestanySimcall::prepare(int times_considered)
152 {
153   next_value_ = times_considered;
154 }
155
156 /*
157 std::string ActivityTestanySimcall::to_string(int times_considered) const
158 {
159   std::string res = SimcallObserver::to_string(times_considered);
160   if (times_considered == -1) {
161     res += "TestAny FALSE(-)";
162   } else {
163     res += "TestAny(" + xbt::string_printf("(%d of %zu)", times_considered + 1, activities_.size());
164   }
165
166   return res;
167 }*/
168
169 std::string ActivityTestanySimcall::dot_label(int times_considered) const
170 {
171   std::string res = SimcallObserver::dot_label(times_considered) + "TestAny ";
172   if (times_considered == -1) {
173     res += "FALSE";
174   } else {
175     res += xbt::string_printf("TRUE [%d of %zu]", times_considered + 1, activities_.size());
176   }
177   return res;
178 }
179
180 bool ActivityTestSimcall::depends(SimcallObserver* other)
181 {
182   if (get_issuer() == other->get_issuer())
183     return false;
184
185   if (dynamic_cast<ActivityTestSimcall*>(other))
186     return true;
187
188   const auto* comm1 = dynamic_cast<activity::CommImpl*>(activity_);
189   if (comm1 == nullptr)
190     return false;
191
192   if (dynamic_cast<ActivityWaitSimcall*>(other) != nullptr &&
193       (comm1->src_actor_.get() == nullptr || comm1->dst_actor_.get() == nullptr))
194     return false;
195
196   if (comm1->src_buff_ == nullptr || comm1->dst_buff_ == nullptr)
197     return false;
198
199   if (const auto* test = dynamic_cast<ActivityTestSimcall*>(other)) {
200     const auto* comm2 = dynamic_cast<activity::CommImpl*>(test->get_activity());
201     if (comm2 == nullptr)
202       return false;
203     else if (comm2->src_buff_ == nullptr || comm2->dst_buff_ == nullptr)
204       return false;
205   }
206
207   if (auto* wait = dynamic_cast<ActivityWaitSimcall*>(other)) {
208     auto* comm2 = dynamic_cast<activity::CommImpl*>(wait->get_activity());
209     if (comm2 == nullptr)
210       return false;
211     if (comm1->src_buff_ == comm2->src_buff_ && comm1->dst_buff_ == comm2->dst_buff_)
212       return false;
213     if (comm1->src_buff_ != nullptr && comm1->dst_buff_ != nullptr && comm2->src_buff_ != nullptr &&
214         comm2->dst_buff_ != nullptr && comm1->dst_buff_ != comm2->src_buff_ && comm1->dst_buff_ != comm2->dst_buff_ &&
215         comm2->dst_buff_ != comm1->src_buff_)
216       return false;
217   }
218
219   return true;
220 }
221 void ActivityWaitSimcall::serialize(Simcall& type, char* buffer)
222 {
223   std::stringstream stream;
224   if (auto* comm = dynamic_cast<activity::CommImpl*>(activity_)) {
225     type = Simcall::COMM_WAIT;
226     stream << timeout_ << ' ' << comm;
227     stream << ' ' << (comm->src_actor_ != nullptr ? comm->src_actor_->get_pid() : -1);
228     stream << ' ' << (comm->dst_actor_ != nullptr ? comm->dst_actor_->get_pid() : -1);
229     stream << ' ' << (comm->get_mailbox() != nullptr ? comm->get_mailbox()->get_id() : 666);
230     stream << ' ' << (void*)comm->src_buff_ << ' ' << (void*)comm->dst_buff_ << ' ' << comm->src_buff_size_;
231     strcpy(buffer, stream.str().c_str());
232   } else {
233     type = Simcall::UNKNOWN;
234     strcpy(buffer, stream.str().c_str());
235   }
236 }
237
238 /*
239 std::string ActivityTestSimcall::to_string(int times_considered) const
240 {
241   std::string res = SimcallObserver::to_string(times_considered) + "Test ";
242   if (const auto* comm = dynamic_cast<activity::CommImpl*>(activity_)) {
243     if (comm->src_actor_.get() == nullptr || comm->dst_actor_.get() == nullptr) {
244       res += "FALSE(comm=";
245       res += XBT_LOG_ISENABLED(mc_observer, xbt_log_priority_verbose) ? xbt::string_printf("%p)", comm)
246                                                                       : "(verbose only))";
247     } else {
248       res += "TRUE(comm=";
249
250       auto src = comm->src_actor_;
251       auto dst = comm->dst_actor_;
252       res +=
253           XBT_LOG_ISENABLED(mc_observer, xbt_log_priority_verbose) ? xbt::string_printf("%p", comm) : "(verbose only) ";
254       res += xbt::string_printf("[(%ld)%s (%s) ", src->get_pid(), src->get_host()->get_cname(), src->get_cname()) +
255              "-> " +
256              xbt::string_printf("(%ld)%s (%s)])", dst->get_pid(), dst->get_host()->get_cname(), dst->get_cname());
257     }
258   } else
259     xbt_die("Only Comms are supported here for now");
260   return res;
261 }*/
262
263 std::string ActivityTestSimcall::dot_label(int times_considered) const
264 {
265   std::string res  = SimcallObserver::dot_label(times_considered) + "Test ";
266   const auto* comm = dynamic_cast<activity::CommImpl*>(activity_);
267   if (comm && (comm->src_actor_.get() == nullptr || comm->dst_actor_.get() == nullptr)) {
268     res += "FALSE";
269   } else {
270     res += "TRUE";
271   }
272   return res;
273 }
274
275 bool ActivityWaitSimcall::is_enabled() const
276 {
277   /* FIXME: check also that src and dst processes are not suspended */
278   const auto* comm = dynamic_cast<activity::CommImpl*>(activity_);
279   if (comm == nullptr)
280     xbt_die("Only Comms are supported here for now");
281
282   if (comm->src_timeout_ || comm->dst_timeout_) {
283     /* If it has a timeout it will be always be enabled (regardless of who declared the timeout),
284      * because even if the communication is not ready, it can timeout and won't block. */
285     if (_sg_mc_timeout == 1)
286       return true;
287   }
288   /* On the other hand if it hasn't a timeout, check if the comm is ready.*/
289   else if (comm->detached() && comm->src_actor_ == nullptr && comm->get_state() == activity::State::READY)
290     return (comm->dst_actor_ != nullptr);
291   return (comm->src_actor_ && comm->dst_actor_);
292 }
293
294 std::string ActivityWaitSimcall::dot_label(int times_considered) const
295 {
296   std::string res = SimcallObserver::dot_label(times_considered);
297   res += (times_considered == -1) ? "WaitTimeout " : "Wait ";
298
299   const auto* comm = dynamic_cast<activity::CommImpl*>(activity_);
300   if (comm) {
301     auto src = comm->src_actor_;
302     auto dst = comm->dst_actor_;
303     res += " [(" + std::to_string(src ? src->get_pid() : 0) + ")";
304     res += "->(" + std::to_string(dst ? dst->get_pid() : 0) + ")]";
305   } else
306     xbt_die("Only Comms are supported here for now");
307   return res;
308 }
309
310 std::string ActivityWaitanySimcall::dot_label(int times_considered) const
311 {
312   return SimcallObserver::dot_label(times_considered) +
313          xbt::string_printf("WaitAny [%d of %zu]", times_considered + 1, activities_.size());
314 }
315
316 bool ActivityWaitanySimcall::is_enabled() const
317 {
318   // FIXME: deal with other kind of activities (Exec and I/Os)
319   // FIXME: Can be factored with ActivityWaitSimcall::is_enabled()
320   const auto* comm = dynamic_cast<activity::CommImpl*>(activities_[next_value_]);
321   if (comm == nullptr)
322     xbt_die("Only Comms are supported here for now");
323   if (comm->src_timeout_ || comm->dst_timeout_) {
324     /* If it has a timeout it will be always be enabled (regardless of who declared the timeout),
325      * because even if the communication is not ready, it can timeout and won't block. */
326     if (_sg_mc_timeout == 1)
327       return true;
328   }
329   /* On the other hand if it hasn't a timeout, check if the comm is ready.*/
330   else if (comm->detached() && comm->src_actor_ == nullptr && comm->get_state() == activity::State::READY)
331     return (comm->dst_actor_ != nullptr);
332   return (comm->src_actor_ && comm->dst_actor_);
333 }
334
335 int ActivityWaitanySimcall::get_max_consider() const
336 {
337   return static_cast<int>(activities_.size());
338 }
339
340 void ActivityWaitanySimcall::prepare(int times_considered)
341 {
342   next_value_ = times_considered;
343 }
344
345 void CommIsendSimcall::serialize(Simcall& type, char* buffer)
346 {
347   type = Simcall::ISEND;
348   std::stringstream stream;
349   stream << mbox_->get_id() << ' ' << (void*)src_buff_ << ' ' << src_buff_size_;
350   strcpy(buffer, stream.str().c_str());
351 }
352
353 void CommIrecvSimcall::serialize(Simcall& type, char* buffer)
354 {
355   type = Simcall::IRECV;
356   std::stringstream stream;
357   stream << mbox_->get_id() << dst_buff_;
358   strcpy(buffer, stream.str().c_str());
359 }
360
361
362 /*
363 std::string CommIrecvSimcall::to_string(int times_considered) const
364 {
365   std::string res = SimcallObserver::to_string(times_considered) + "iRecv(";
366   res += xbt::string_printf("dst=(%ld)%s (%s)", get_issuer()->get_pid(), get_issuer()->get_host()->get_cname(),
367                             get_issuer()->get_cname());
368   res += ", buff=" + (XBT_LOG_ISENABLED(mc_observer, xbt_log_priority_verbose) ? xbt::string_printf("%p", dst_buff_)
369                                                                                : "(verbose only)");
370   res += ", size=" + (XBT_LOG_ISENABLED(mc_observer, xbt_log_priority_verbose) ? std::to_string(*dst_buff_size_)
371                                                                                : "(verbose only)");
372   res += ")";
373   return res;
374 }
375 */
376
377 } // namespace actor
378 } // namespace kernel
379 } // namespace simgrid