Skip to content
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
9302f1c
initial state -> taking as template arinc.ml
denis631 Dec 6, 2020
0ab7ed0
compilation fixes and PID -> TID
denis631 Dec 8, 2020
d71e25b
fixed coding errors, but having cyclic dependency
denis631 Dec 10, 2020
72a18d5
first codegen success
denis631 Dec 19, 2020
553690f
Mutexes and Functions + Refactoring
denis631 Dec 21, 2020
7c0b0c2
conditional vars support + refactoring
denis631 Dec 24, 2020
a4f55dd
minor refactoring
denis631 Dec 24, 2020
4909a64
[threads] threading logic as conditional vars
denis631 Dec 25, 2020
6ef52d4
[promela] base.pml initial draft
denis631 Dec 25, 2020
08ee82b
[promela] pthread.base.pml refinement
denis631 Jan 3, 2021
bc31ade
[promela] condition var implementation + refactoring
denis631 Jan 3, 2021
6f3ed6c
[promela] [analysis] other function analysis fix + promela fixes
denis631 Jan 16, 2021
ab41071
[analysis] assign and branch implementation
denis631 Feb 4, 2021
fb4666c
[promela][analysis] base.pml fix and analysis improvements
denis631 Feb 25, 2021
1c08207
[analysis] locals and globals assignment
denis631 Feb 26, 2021
6790982
[analysis] promela body generation for multiple threads with same ini…
denis631 Mar 8, 2021
deba179
[pml][analysis] base.pml refactoring and analysis refactoring
denis631 Mar 11, 2021
fc1f2a0
[analysis] evaluation refactoring
denis631 Mar 17, 2021
db9d892
[analysis] handling assignment of special functions
denis631 Mar 18, 2021
e920ba7
[analysis] small refactoring
denis631 Mar 18, 2021
47a8b01
[analysis] assume_success flag
denis631 Mar 19, 2021
980b62f
[analysis] exit function support
denis631 Mar 19, 2021
36df1c3
[analysis] TOP assignment
denis631 Mar 22, 2021
ec76880
[analysis] TOP handling refactoring
denis631 Mar 23, 2021
2d69cc2
[pml] POSIX implementation fix
denis631 Mar 23, 2021
7fe5e88
[analysis] pthread_exit() support
denis631 Mar 30, 2021
99308f0
[analysis] IgnoreAssigns flag introduced + bug fix
denis631 Apr 1, 2021
8176dd3
[analysis] custom printer implementation
denis631 Apr 1, 2021
5bc883b
[analysis] printer, special assign extraction fix
May 4, 2021
cc69775
[analysis] child thread spawn is not allowed
May 5, 2021
459f7ce
Merge remote-tracking branch 'origin/master' into pthread_analysis
michael-schwarz May 10, 2021
0628579
Update src/cdomains/pthreadDomain.ml
denis631 May 24, 2021
2ac1746
Update src/analyses/pthreadAnalysis.ml
denis631 May 24, 2021
0ec111a
suggestions addressed
May 24, 2021
e5c799e
Merge branch 'master' into pthread_analysis
michael-schwarz Aug 4, 2022
b855cd6
Merge branch 'pthread_analysis' of github.com:denis631/analyzer into …
michael-schwarz Aug 4, 2022
c56d878
Merge branch 'master' into pthread_analysis
michael-schwarz Aug 4, 2022
b7356a0
address semgrep complaint
michael-schwarz Aug 4, 2022
4f609a7
Rename analysis to `extract-pthread`
michael-schwarz Aug 4, 2022
4fad65a
Fix path that is written to
michael-schwarz Aug 4, 2022
2115453
Remove pragmas that promella can not handle
michael-schwarz Aug 4, 2022
727380d
Repair some broken things
michael-schwarz Aug 4, 2022
178b019
Fix to string
michael-schwarz Aug 4, 2022
8f63bf1
Fix promela output
michael-schwarz Aug 4, 2022
0dcabd1
Gitignore promela result
michael-schwarz Aug 4, 2022
9148b24
Add regression-y tests for `extract-pthread`
michael-schwarz Aug 4, 2022
d75d664
Add -a that is needed for promela
michael-schwarz Aug 4, 2022
11891d4
Add comment why `MutexUnlock` makes unrealistically strong assumptions.
michael-schwarz Aug 4, 2022
6e16172
Merge branch 'master' into pthread_analysis
michael-schwarz Aug 4, 2022
e7eaba1
Minimal automatic testing
michael-schwarz Aug 4, 2022
c5d7343
Add missing steps to extraction workflow
michael-schwarz Aug 4, 2022
14a6a37
Fix duplicate definition of domain
michael-schwarz Aug 4, 2022
da4de6c
typoy
michael-schwarz Aug 4, 2022
3676784
Remove duplication of library functions
michael-schwarz Aug 8, 2022
be566cf
Fix nonsensical locked extraction run
michael-schwarz Aug 8, 2022
1e8f12a
add `mkdir -p` in case directory does not exist
michael-schwarz Aug 8, 2022
334c00d
Make `is_bot` more robust
michael-schwarz Aug 10, 2022
716d65b
Rm polymorphic comparison of varinfos
michael-schwarz Aug 10, 2022
748e22c
Cleanup `test.sh`
michael-schwarz Aug 10, 2022
3b1c451
Merge branch 'master' into pthread_analysis
michael-schwarz Aug 10, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
302 changes: 302 additions & 0 deletions spin/pthread.base.pml
Original file line number Diff line number Diff line change
@@ -0,0 +1,302 @@
// configuration defaults
#ifndef mutex_count
#define mutex_count 0
#endif

#ifndef cond_var_count
#define cond_var_count 0
#endif

// resources
mtype = {NONE, THREAD, MUTEX, COND_VAR}
typedef Resource {
mtype type = NONE;
byte id;
};

// threads
mtype = {NOTCREATED, READY, WAITING, DONE} // possible thread states

typedef Thread {
mtype state = NOTCREATED;
chan waitQueue = [thread_count] of { byte } // threads waiting on thread_join
Resource waitingOnResource;
};
Thread threads[thread_count];

#if mutex_count
mtype = {UNLOCKED, LOCKED}
typedef Mutex {
mtype state = UNLOCKED;
byte tid;
chan blockedQueue = [thread_count] of { byte, byte };
};
Mutex mutexes[mutex_count];
#endif

#if cond_var_count
typedef CondVar {
byte mid;
chan waitQueue = [thread_count] of { byte, byte };
};
CondVar cond_vars[cond_var_count];
#endif

// manage function calls at runtime to avoid inlining
// each proctype has its own: stack, sp
inline mark(pc) {
sp++;
stack[sp] = pc;
}

// handle exit call in C programs
inline exit() {
atomic {
for (___i in threads) {
threads[___i].state = DONE;
}
}
}

// helpers for scheduling etc.
#define notStarving(i) \
(always(threads[i].state == READY implies always eventually(threads[i].state == READY || \
threads[i].state == DONE)))

// LTL formulas
ltl not_starving { allTasks(notStarving) }

#define canRun(thread_id) (threads[thread_id].state == READY)

#define isWaiting(thread_id, resource_type, resource_id) \
(threads[thread_id].state == WAITING && \
threads[thread_id].waitingOnResource.type == resource_type && \
threads[thread_id].waitingOnResource.id == resource_id)

#pragma mark - waiting/signaling

inline setWaiting(thread_id, resource_type, resource_id) {
printf("setWaiting: thread %d will wait for %e %d\n", thread_id, resource_type,
resource_id);

// thread should not be waiting for anything at this point
assert(threads[thread_id].waitingOnResource.type == NONE);

threads[thread_id].waitingOnResource.type = resource_type;
threads[thread_id].waitingOnResource.id = resource_id;

// update process threads[tid].state (provided clause will block immediately)
threads[thread_id].state = WAITING;
}

inline setReady(thread_id) {
printf("setReady: thread %d will be ready (was waiting for %e %d)\n",
thread_id, threads[thread_id].waitingOnResource.type, threads[thread_id].waitingOnResource.id);

threads[thread_id].waitingOnResource.type = NONE;
threads[thread_id].state = READY;
}

#pragma mark - Helper globals
byte ___i;
byte ___rand_num;

#pragma mark - Thread logic

inline ThreadCreate(thread_id) {
atomic {
printf("ThreadCreate: id %d\n", thread_id);

setReady(thread_id);
}
}

// pthread_join
inline ThreadWait(thread_id) {
atomic {
printf("ThreadWait: id %d\n", thread_id);

if
:: threads[thread_id].state != DONE ->
threads[thread_id].waitQueue!tid; // should block here
setWaiting(tid, THREAD, thread_id);
:: threads[thread_id].state == DONE -> skip
fi
}
}

// called by the functions invoked by pthread_create
// notify the caller, that the thread is done computing
// similar to conditional var broadcast
inline ThreadExit() {
atomic {
printf("ThreadBroadcast: id %d\n", tid);
assert(threads[tid].waitingOnResource.type == NONE); // thread should not be waiting for anything at this point

do
:: nempty(threads[tid].waitQueue) ->
threads[tid].waitQueue?___i; // consume msg from queue

assert(isWaiting(___i, THREAD, tid));
printf("ThreadBroadcast: thread %d is waking up thread %d\n", tid, ___i);

setReady(___i);

:: empty(threads[tid].waitQueue) -> break
od

threads[tid].state = DONE;
}
}

#pragma mark - Mutex logic

inline MutexInit(mid) {
atomic {
printf("MutexInit: id %d\n", mid);
}
}

inline MutexLock(mid) {
__MutexLock(tid, mid)
}

inline __MutexLock(thread_id, x) {
atomic {
if
:: mutexes[x].state == LOCKED -> // TODO: reentrant mutex?
printf("__MutexLock will block: mutexes[%d]\n", x);

if
:: full(mutexes[x].blockedQueue) -> // TODO can this happen?
printf("FAIL: __MutexLock: queue is full\n");
assert(false);
:: nfull(mutexes[x].blockedQueue) ->
printf("Thread %d put into queue for mutex %d\n", thread_id, x);

select (___rand_num : 0..(thread_count - 1));

// put random number and tid in queue in a sorted way
// we do this in order to preserve POSIX semantics
// of random mutex awakaning on mutex_unlock
mutexes[x].blockedQueue!!___rand_num,thread_id;
fi;

setWaiting(thread_id, MUTEX, x); // blocks this process instantly
// doc says: if stmt in atomic blocks, the rest will still remain atomic once it becomes executable.
// atomicity is lost if one jumps out of the sequence (which might be the case with provided (...)).
// revised: atomicity is broken if a statement inside the atomic blocks, but can continue as non-atomic
// so, atomic is broken after setWaiting, but that's ok since we're done with WaitSemaphore anyway

:: mutexes[x].state == UNLOCKED ->
printf("__MutexLock locked: mutexes[%d] = LOCKED by %d\n", x, thread_id);
mutexes[x].state = LOCKED;
mutexes[x].tid = thread_id;
fi
}
}

inline MutexUnlock(x) {
atomic {
if
:: mutexes[x].tid == tid ->
printf("MutexUnlock unlocked: mutexes[%d] = UNLOCKED by %d\n", x, tid);
mutexes[x].state = UNLOCKED;

if
// wake up waiting process and reacquire the mutex
:: nempty(mutexes[x].blockedQueue) ->
printf("MutexUnlock: %d threads in queue for mutex %d\n",
len(mutexes[x].blockedQueue), x);

mutexes[x].blockedQueue?_,___i;
printf("MutexUnlock: thread %d is waking up thread %d\n", tid, ___i);

__MutexLock(___i, x);
setReady(___i);

:: empty(mutexes[x].blockedQueue) -> skip
fi;
:: mutexes[x].tid != tid -> skip // undefined behavior
fi;
}
}

#pragma mark - conditional vars

inline CondVarInit(cond_var_id) {
atomic {
printf("CondVarInit: id %d\n", cond_var_id);
}
}

inline CondVarWait(cond_var_id, mut_id) {
atomic {
printf("CondVarWait: id %d\n", cond_var_id);

// if (me->tid != tid) failure("illegal wait");
assert(mutexes[mut_id].tid == tid)

cond_vars[cond_var_id].mid = mut_id

// enqueue(cv->wq, tid);

select (___rand_num : 0..(thread_count - 1));

// put random number and tid in queue in a sorted way
// we do this in order to preserve POSIX semantics
// of random cond var awakaning on signaling

cond_vars[cond_var_id].waitQueue!!___rand_num,tid

// unlock(me);
MutexUnlock(mut_id)
setWaiting(tid, COND_VAR, cond_var_id)
}
}

inline CondVarSignal(cond_var_id) {
atomic {
if
// if there is a waiting process -> wake it up
:: nempty(cond_vars[cond_var_id].waitQueue) ->
printf("CondVarSignal: %d threads in queue for condition var %d\n",
len(cond_vars[cond_var_id].waitQueue), cond_var_id);

cond_vars[cond_var_id].waitQueue?_,___i;

assert(isWaiting(___i, COND_VAR, cond_var_id));
printf("CondVarSignal: thread %d is waking up thread %d\n", tid, ___i);

setReady(___i);

// reacquire the mutex lock
__MutexLock(___i, cond_vars[cond_var_id].mid)
:: empty(cond_vars[cond_var_id].waitQueue) -> skip
fi;
}
}

inline CondVarBroadcast(cond_var_id) {
atomic {
printf("CondVarSignal: %d threads in queue for condition var %d\n",
len(cond_vars[cond_var_id].waitQueue), cond_var_id);

do
:: nempty(cond_vars[cond_var_id].waitQueue) ->
// consume random msg from queue
cond_vars[cond_var_id].waitQueue?_,___i;

assert(isWaiting(___i, COND_VAR, cond_var_id));
printf("CondVarSignal: thread %d is waking up thread %d\n", tid, ___i);

setReady(___i);
// reacquire the mutex lock
__MutexLock(___i, cond_vars[cond_var_id].mid);

:: empty(cond_vars[cond_var_id].waitQueue) -> break
od

// All waititng processes/threads are now waiting for the mutex to be released
}
}
Loading