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 Romain::PThreadLockObserver*
18 Romain::PThreadLockObserver::Create()
19 { return new PThreadLock_priv(); }
22 void Romain::PThreadLock_priv::status() const
24 INFO() << "LOCK.lock = " << det_lock_count;
25 INFO() << "LOCK.unlock = " << det_unlock_count;
26 INFO() << "MTX.lock = " << mtx_lock_count;
27 INFO() << "MTX.unlock = " << mtx_unlock_count;
28 INFO() << "pt.lock = " << pt_lock_count;
29 INFO() << "pt.unlock = " << pt_unlock_count;
30 INFO() << "# ignored = " << ignore_count;
31 INFO() << "Total count = " << total_count;
35 Romain::Observer::ObserverReturnVal
36 Romain::PThreadLock_priv::notify(Romain::App_instance* inst,
37 Romain::App_thread* thread,
38 Romain::Thread_group* group,
39 Romain::App_model* model)
41 if (!entry_reason_is_int3(thread->vcpu(), inst, model)) {
42 return Romain::Observer::Ignored;
45 DEBUG() << "LOCK observer";
46 l4util_inc32(&total_count);
49 * HACK: intercept notifications coming from the replication-
50 * aware pthread library.
52 if (thread->vcpu()->r()->ip == 0xA021) {
53 l4util_inc32(&det_lock_count);
54 det_lock(inst, thread, group, model);
55 //thread->vcpu()->r()->flags |= TrapFlag;
56 return Romain::Observer::Replicatable;
57 } else if (thread->vcpu()->r()->ip == 0xA041) {
58 l4util_inc32(&det_unlock_count);
59 det_unlock(inst, thread, group, model);
60 return Romain::Observer::Replicatable;
63 #define HANDLE_BP(var, handler) do { \
64 if(_functions[(var)].orig_address == thread->vcpu()->r()->ip - 1) { \
65 (handler)(inst, thread, group, model); \
66 return Romain::Observer::Replicatable; \
70 HANDLE_BP(mutex_lock_id, mutex_lock);
71 HANDLE_BP(mutex_unlock_id, mutex_unlock);
72 //HANDLE_BP(mutex_init_id, mutex_init);
73 HANDLE_BP(pt_lock_id, lock);
74 HANDLE_BP(pt_unlock_id, unlock);
78 l4util_inc32(&ignore_count);
79 return Romain::Observer::Ignored;
83 void Romain::PThreadLock_priv::attach_lock_info_page(Romain::App_model *am)
85 _lip_ds = am->alloc_ds(L4_PAGESIZE);
86 _lip_local = am->local_attach_ds(_lip_ds, L4_PAGESIZE, 0);
87 INFO() << "Local LIP address: " << std::hex << _lip_local;
88 void* remote__lip = (void*)am->prog_attach_ds(Romain::LOCK_INFO_PAGE,
89 L4_PAGESIZE, _lip_ds, 0, 0,
92 _check(reinterpret_cast<l4_umword_t>(remote__lip) != Romain::LOCK_INFO_PAGE,
93 "LIP did not attach to proper remote location");
95 am->lockinfo_local(_lip_local);
96 am->lockinfo_remote(Romain::LOCK_INFO_PAGE);
98 memset((void*)_lip_local, 0, L4_PAGESIZE);
102 void Romain::PThreadLock_priv::startup_notify(Romain::App_instance *inst,
103 Romain::App_thread *,
104 Romain::Thread_group *,
105 Romain::App_model *am)
107 static unsigned callCount = 0;
108 lock_info *lip = reinterpret_cast<lock_info*>(_lip_local);
110 #if INTERNAL_DETERMINISM
113 * For internal determinism, we make sure that we only attach the
114 * lock info page once (for the first replica), because the LIP
115 * is shared across all replicas.
117 attach_lock_info_page(am);
118 lip = reinterpret_cast<lock_info*>(_lip_local);
119 lip->locks[0].lockdesc = 0xFAFAFAFA;
120 lip->locks[0].owner = 0xDEADBEEF;
123 //DEBUG() << "Replica LIP address: " << std::hex << remote_lock_info;
126 * Breakpoints / patching of function entries is only done once,
127 * because code is shared across all replicas.
129 for (unsigned idx = 0; idx < pt_max_wrappers; ++idx) {
131 _functions[idx].activate(inst, am);
133 #if INTERNAL_DETERMINISM
136 lip->replica_count += 1;
143 * pthread_mutex_init()
144 * - mutex address @ ESP + 4
145 * - mutex attrib ptr @ ESP + 8
146 * - returns int in EAX
148 void Romain::PThreadLock_priv::mutex_init(Romain::App_instance *inst,
149 Romain::App_thread *t,
150 Romain::Thread_group *tg,
151 Romain::App_model *am)
153 l4_addr_t stack = am->rm()->remote_to_local(t->vcpu()->r()->sp, inst->id());
154 l4_umword_t ret = *(l4_umword_t*)stack;
155 l4_umword_t lock = *(l4_umword_t*)(stack + 1*sizeof(l4_umword_t));
156 l4_umword_t attr = *(l4_umword_t*)(stack + 2*sizeof(l4_umword_t));
158 DEBUG() << "INIT: lock @ " << std::hex << lock << ", attr " << attr;
160 enter_kdebug("init with attributes");
163 _locks[lock] = new PThreadMutex(false /*XXX*/);
165 t->vcpu()->r()->ax = 0;
166 t->vcpu()->r()->sp += sizeof(l4_umword_t);
167 t->vcpu()->r()->ip = ret;
173 * - called with lock pointer in EAX
176 void Romain::PThreadLock_priv::lock(Romain::App_instance *inst,
177 Romain::App_thread *t,
178 Romain::Thread_group *tg,
179 Romain::App_model *am)
181 l4util_inc32(&pt_lock_count);
182 l4_addr_t stack = am->rm()->remote_to_local(t->vcpu()->r()->sp, inst->id());
183 l4_umword_t ret = *(l4_umword_t*)stack;
184 l4_umword_t lock = t->vcpu()->r()->ax;
186 DEBUG() << "Stack ptr " << std::hex << t->vcpu()->r()->sp << " => "
188 DEBUG() << "Lock @ " << std::hex << lock;
189 DEBUG() << "Return addr " << std::hex << ret;
191 lookup_or_create(lock)->lock(tg);
193 t->vcpu()->r()->sp += sizeof(l4_umword_t); // RET: inc. ESP
194 t->vcpu()->r()->ip = ret; // RET: return addr
199 * __pthread_unlock():
200 * - called with lock pointer as single stack argument
203 void Romain::PThreadLock_priv::unlock(Romain::App_instance *inst,
204 Romain::App_thread *t,
205 Romain::Thread_group *tg,
206 Romain::App_model *am)
208 l4util_inc32(&pt_unlock_count);
209 l4_addr_t stack = am->rm()->remote_to_local(t->vcpu()->r()->sp, inst->id());
210 l4_umword_t retaddr = *(l4_umword_t*)stack;
211 l4_umword_t lock = *(l4_umword_t*)(stack + 1*sizeof(l4_umword_t));
213 DEBUG() << "Return addr " << std::hex << retaddr;
214 DEBUG() << "Lock @ " << std::hex << lock;
216 //enter_kdebug("unlock");
218 int ret = lookup_or_fail(lock)->unlock();
220 t->vcpu()->r()->ax = ret;
221 t->vcpu()->r()->sp += sizeof(l4_umword_t); // RET: inc. ESP
222 t->vcpu()->r()->ip = retaddr; // RET: return addr
226 * pthread_mutex_lock()
230 void Romain::PThreadLock_priv::mutex_lock(Romain::App_instance* inst, Romain::App_thread* t,
231 Romain::Thread_group* group, Romain::App_model* model)
233 l4util_inc32(&mtx_lock_count);
235 l4_addr_t stack = model->rm()->remote_to_local(t->vcpu()->r()->sp, inst->id());
236 l4_umword_t retaddr = *(l4_umword_t*)stack;
237 l4_umword_t lock = *(l4_umword_t*)(stack + 1*sizeof(l4_umword_t));
239 DEBUG() << "lock @ " << std::hex << lock << " ESP.local = " << stack;
240 PThreadMutex* mtx = _locks[lock];
243 * Can't use the shortcut here. We found a yet unknown lock. Now we need to
244 * figure out whether it should be recursive. The respective member in the
245 * pthread data structure is at offset 12 (dec) from the mutex pointer
246 * we received as the argument.
248 l4_umword_t mtx_kind_ptr = model->rm()->remote_to_local(lock + 12, inst->id());
250 DEBUG() << "log kind: " << *(l4_umword_t*)mtx_kind_ptr
251 << " RECURSIVE: " << PTHREAD_MUTEX_RECURSIVE_NP;
254 mtx = new PThreadMutex(*(l4_umword_t*)mtx_kind_ptr == PTHREAD_MUTEX_RECURSIVE_NP);
258 int ret = mtx->lock(group);
260 t->vcpu()->r()->ax = ret;
261 t->vcpu()->r()->sp += sizeof(l4_umword_t); // RET: inc. ESP
262 t->vcpu()->r()->ip = retaddr; // RET: return addr
266 void Romain::PThreadLock_priv::mutex_unlock(Romain::App_instance* inst, Romain::App_thread* t,
267 Romain::Thread_group* group, Romain::App_model* model)
269 l4util_inc32(&mtx_unlock_count);
271 l4_addr_t stack = model->rm()->remote_to_local(t->vcpu()->r()->sp, inst->id());
272 l4_umword_t retaddr = *(l4_umword_t*)stack;
273 l4_umword_t lock = *(l4_umword_t*)(stack + 1*sizeof(l4_umword_t));
275 int ret = lookup_or_fail(lock)->unlock();
276 DEBUG() << "unlock @ " << std::hex << lock << " = " << ret;
278 t->vcpu()->r()->ax = ret;
279 t->vcpu()->r()->sp += sizeof(l4_umword_t); // RET: inc. ESP
280 t->vcpu()->r()->ip = retaddr; // RET: return addr