Logo AND Algorithmique Numérique Distribuée

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