4 * Deterministic lock acquisition
6 * (c) 2012-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
7 * economic rights: Technische Universität Dresden (Germany)
8 * This file is part of TUD:OS and distributed under the terms of the
9 * GNU General Public License 2.
10 * Please see the COPYING-GPL-2 file for details.
13 #include "lock_observer.h"
15 #define DEBUGt(t) DEBUG() << "[" << t->vcpu() << "] "
17 #define EVENT(event, ptr) \
19 Measurements::GenericEvent* ev = Romain::_the_instance_manager->logbuf()->next(); \
20 ev->header.tsc = Romain::_the_instance_manager->logbuf()->getTime(Log::logLocalTSC); \
21 ev->header.vcpu = (l4_uint32_t)t->vcpu(); \
22 ev->header.type = Measurements::Locking; \
23 ev->data.lock.eventType = event; \
24 ev->data.lock.lockPtr = ptr; \
27 Romain::PThreadLockObserver*
28 Romain::PThreadLockObserver::Create()
29 { return new PThreadLock_priv(); }
32 extern InstanceManager* _the_instance_manager;
35 void Romain::PThreadLock_priv::status() const
37 INFO() << "[lock] LOCK.lock = " << det_lock_count;
38 INFO() << "[lock] LOCK.unlock = " << det_unlock_count;
39 INFO() << "[lock] MTX.lock = " << mtx_lock_count;
40 INFO() << "[lock] MTX.unlock = " << mtx_unlock_count;
41 INFO() << "[lock] pt.lock = " << pt_lock_count;
42 INFO() << "[lock] pt.unlock = " << pt_unlock_count;
43 INFO() << "[lock] # ignored = " << ignore_count;
44 INFO() << "[lock] Total count = " << total_count;
48 Romain::Observer::ObserverReturnVal
49 Romain::PThreadLock_priv::notify(Romain::App_instance* inst,
50 Romain::App_thread* thread,
51 Romain::Thread_group* group,
52 Romain::App_model* model)
54 if (!entry_reason_is_int3(thread->vcpu(), inst, model)) {
55 return Romain::Observer::Ignored;
58 DEBUG() << "LOCK observer";
59 l4util_inc32(&total_count);
62 * HACK: intercept notifications coming from the replication-
63 * aware pthread library.
65 if (thread->vcpu()->r()->ip == 0xA021) {
66 l4util_inc32(&det_lock_count);
67 det_lock(inst, thread, group, model);
68 //thread->vcpu()->r()->flags |= TrapFlag;
69 return Romain::Observer::Replicatable;
70 } else if (thread->vcpu()->r()->ip == 0xA041) {
71 l4util_inc32(&det_unlock_count);
72 det_unlock(inst, thread, group, model);
73 return Romain::Observer::Replicatable;
76 #define HANDLE_BP(var, handler) do { \
77 if(_functions[(var)].orig_address == thread->vcpu()->r()->ip - 1) { \
78 (handler)(inst, thread, group, model); \
79 return Romain::Observer::Replicatable; \
83 HANDLE_BP(mutex_lock_id, mutex_lock);
84 HANDLE_BP(mutex_unlock_id, mutex_unlock);
85 //HANDLE_BP(mutex_init_id, mutex_init);
86 HANDLE_BP(pt_lock_id, lock);
87 HANDLE_BP(pt_unlock_id, unlock);
91 l4util_inc32(&ignore_count);
92 return Romain::Observer::Ignored;
96 void Romain::PThreadLock_priv::attach_lock_info_page(Romain::App_model *am)
98 _lip_ds = am->alloc_ds(L4_PAGESIZE);
99 _lip_local = am->local_attach_ds(_lip_ds, L4_PAGESIZE, 0);
100 INFO() << "Local LIP address: " << std::hex << _lip_local;
101 void* remote__lip = (void*)am->prog_attach_ds(Romain::LOCK_INFO_PAGE,
102 L4_PAGESIZE, _lip_ds, 0, 0,
105 _check(reinterpret_cast<l4_umword_t>(remote__lip) != Romain::LOCK_INFO_PAGE,
106 "LIP did not attach to proper remote location");
108 am->lockinfo_local(_lip_local);
109 am->lockinfo_remote(Romain::LOCK_INFO_PAGE);
111 memset((void*)_lip_local, 0, L4_PAGESIZE);
115 void Romain::PThreadLock_priv::startup_notify(Romain::App_instance *inst,
116 Romain::App_thread *,
117 Romain::Thread_group *,
118 Romain::App_model *am)
120 static unsigned callCount = 0;
121 lock_info *lip = reinterpret_cast<lock_info*>(_lip_local);
123 #if INTERNAL_DETERMINISM
126 * For internal determinism, we make sure that we only attach the
127 * lock info page once (for the first replica), because the LIP
128 * is shared across all replicas.
130 attach_lock_info_page(am);
131 lip = reinterpret_cast<lock_info*>(_lip_local);
132 lip->locks[0].lockdesc = 0xFAFAFAFA;
133 lip->locks[0].owner = 0xDEADBEEF;
136 //DEBUG() << "Replica LIP address: " << std::hex << remote_lock_info;
139 * Breakpoints / patching of function entries is only done once,
140 * because code is shared across all replicas.
142 for (unsigned idx = 0; idx < pt_max_wrappers; ++idx) {
144 _functions[idx].activate(inst, am);
146 #if INTERNAL_DETERMINISM
149 lip->replica_count += 1;
156 * pthread_mutex_init()
157 * - mutex address @ ESP + 4
158 * - mutex attrib ptr @ ESP + 8
159 * - returns int in EAX
161 void Romain::PThreadLock_priv::mutex_init(Romain::App_instance *inst,
162 Romain::App_thread *t,
163 Romain::Thread_group *tg,
164 Romain::App_model *am)
166 l4_addr_t stack = am->rm()->remote_to_local(t->vcpu()->r()->sp, inst->id());
167 l4_umword_t ret = *(l4_umword_t*)stack;
168 l4_umword_t lock = *(l4_umword_t*)(stack + 1*sizeof(l4_umword_t));
169 l4_umword_t attr = *(l4_umword_t*)(stack + 2*sizeof(l4_umword_t));
171 DEBUG() << "INIT: lock @ " << std::hex << lock << ", attr " << attr;
173 enter_kdebug("init with attributes");
176 _locks[lock] = new PThreadMutex(false /*XXX*/);
178 t->vcpu()->r()->ax = 0;
185 * - called with lock pointer in EAX
188 void Romain::PThreadLock_priv::lock(Romain::App_instance *inst,
189 Romain::App_thread *t,
190 Romain::Thread_group *tg,
191 Romain::App_model *am)
193 l4util_inc32(&pt_lock_count);
194 l4_addr_t stack = am->rm()->remote_to_local(t->vcpu()->r()->sp, inst->id());
195 l4_umword_t ret = *(l4_umword_t*)stack;
196 l4_umword_t lock = t->vcpu()->r()->ax;
198 EVENT(Measurements::LockEvent::lock, lock);
200 DEBUG() << "Stack ptr " << std::hex << t->vcpu()->r()->sp << " => "
202 DEBUG() << "Lock @ " << std::hex << lock;
203 DEBUG() << "Return addr " << std::hex << ret;
205 lookup_or_create(lock)->lock(tg);
212 * __pthread_unlock():
213 * - called with lock pointer as single stack argument
216 void Romain::PThreadLock_priv::unlock(Romain::App_instance *inst,
217 Romain::App_thread *t,
218 Romain::Thread_group *tg,
219 Romain::App_model *am)
221 l4util_inc32(&pt_unlock_count);
222 l4_addr_t stack = am->rm()->remote_to_local(t->vcpu()->r()->sp, inst->id());
223 l4_umword_t retaddr = *(l4_umword_t*)stack;
224 l4_umword_t lock = *(l4_umword_t*)(stack + 1*sizeof(l4_umword_t));
226 EVENT(Measurements::LockEvent::unlock, lock);
228 DEBUG() << "Return addr " << std::hex << retaddr;
229 DEBUG() << "Lock @ " << std::hex << lock;
231 //enter_kdebug("unlock");
233 int ret = lookup_or_fail(lock)->unlock();
235 t->vcpu()->r()->ax = ret;
236 t->return_to(retaddr);
240 * pthread_mutex_lock()
244 void Romain::PThreadLock_priv::mutex_lock(Romain::App_instance* inst, Romain::App_thread* t,
245 Romain::Thread_group* group, Romain::App_model* model)
247 l4util_inc32(&mtx_lock_count);
249 l4_addr_t stack = model->rm()->remote_to_local(t->vcpu()->r()->sp, inst->id());
250 l4_umword_t retaddr = *(l4_umword_t*)stack;
251 l4_umword_t lock = *(l4_umword_t*)(stack + 1*sizeof(l4_umword_t));
253 EVENT(Measurements::LockEvent::mtx_lock, lock);
255 DEBUG() << "lock @ " << std::hex << lock << " ESP.local = " << stack;
256 PThreadMutex* mtx = _locks[lock];
259 * Can't use the shortcut here. We found a yet unknown lock. Now we need to
260 * figure out whether it should be recursive. The respective member in the
261 * pthread data structure is at offset 12 (dec) from the mutex pointer
262 * we received as the argument.
264 l4_umword_t mtx_kind_ptr = model->rm()->remote_to_local(lock + 12, inst->id());
266 DEBUG() << "log kind: " << *(l4_umword_t*)mtx_kind_ptr
267 << " RECURSIVE: " << PTHREAD_MUTEX_RECURSIVE_NP;
270 mtx = new PThreadMutex(*(l4_umword_t*)mtx_kind_ptr == PTHREAD_MUTEX_RECURSIVE_NP);
274 int ret = mtx->lock(group);
276 t->vcpu()->r()->ax = ret;
277 t->return_to(retaddr);
281 void Romain::PThreadLock_priv::mutex_unlock(Romain::App_instance* inst, Romain::App_thread* t,
282 Romain::Thread_group* group, Romain::App_model* model)
284 l4util_inc32(&mtx_unlock_count);
286 l4_addr_t stack = model->rm()->remote_to_local(t->vcpu()->r()->sp, inst->id());
287 l4_umword_t retaddr = *(l4_umword_t*)stack;
288 l4_umword_t lock = *(l4_umword_t*)(stack + 1*sizeof(l4_umword_t));
290 EVENT(Measurements::LockEvent::mtx_unlock, lock);
292 int ret = lookup_or_fail(lock)->unlock();
293 DEBUG() << "unlock @ " << std::hex << lock << " = " << ret;
295 t->vcpu()->r()->ax = ret;
296 t->return_to(retaddr);