Skip to content

Commit bce9f92

Browse files
authored
Merge pull request #1561 from goblint/threadid-history-may_create
Improve history thread ID `may_create`
2 parents ea7ee83 + 3b2df23 commit bce9f92

File tree

11 files changed

+311
-35
lines changed

11 files changed

+311
-35
lines changed

src/cdomain/value/cdomains/threadIdDomain.ml

Lines changed: 38 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ sig
1212
val is_main: t -> bool
1313
val is_unique: t -> bool
1414

15-
(** Overapproximates whether the first TID can be involved in the creation fo the second TID*)
16-
val may_create: t -> t -> bool
15+
(** Overapproximates whether the first TID can be involved in the creation of the second TID*)
16+
val may_be_ancestor: t -> t -> bool
1717

18-
(** Is the first TID a must parent of the second thread. Always false if the first TID is not unique *)
19-
val is_must_parent: t -> t -> bool
18+
(** Is the first TID a must ancestor of the second thread. Always false if the first TID is not unique *)
19+
val must_be_ancestor: t -> t -> bool
2020
end
2121

2222
module type Stateless =
@@ -87,8 +87,8 @@ struct
8787
| _ -> false
8888

8989
let is_unique = is_main
90-
let may_create _ _ = true
91-
let is_must_parent _ _ = false
90+
let may_be_ancestor _ _ = true
91+
let must_be_ancestor _ _ = false
9292
end
9393

9494

@@ -140,18 +140,34 @@ struct
140140
let is_unique (_, s) =
141141
S.is_empty s
142142

143-
let is_must_parent (p,s) (p',s') =
144-
if not (S.is_empty s) then
143+
let must_be_ancestor ((p, s) as t) ((p', s') as t') =
144+
if not (is_unique t) then
145145
false
146-
else if P.equal p' p && S.is_empty s' then (* s is already empty *)
147-
(* We do not consider a thread its own parent *)
148-
false
149-
else
150-
let cdef_ancestor = P.common_suffix p p' in
151-
P.equal p cdef_ancestor
146+
else if is_unique t' && P.equal p p' then (* t is already unique, so no need to compare sets *)
147+
false (* thread is not its own parent *)
148+
else ( (* t is already unique, so no need to check sets *)
149+
match GobList.remove_common_prefix Base.equal (List.rev p) (List.rev p') with (* prefixes are stored reversed *)
150+
| [], _ -> true (* p is prefix of p' *)
151+
| _ :: _, _ -> false
152+
)
152153

153-
let may_create (p,s) (p',s') =
154-
S.subset (S.union (S.of_list p) s) (S.union (S.of_list p') s')
154+
let may_be_ancestor ((p, s) as t) ((p', s') as t') =
155+
if is_unique t' then
156+
must_be_ancestor t t' (* unique must be created by something unique (that's a prefix) *)
157+
else ( (* t' is already non-unique (but doesn't matter) *)
158+
match GobList.remove_common_prefix Base.equal (List.rev p) (List.rev p') with (* prefixes are stored reversed *)
159+
| [], dp when is_unique t -> (* p is prefix of p' *)
160+
(* dp = elements added to prefix (reversed, but doesn't matter) *)
161+
true (* all elements are contained: p is prefix of p' and s is empty (due to uniqueness) *)
162+
| dp', [] -> (* p' is prefix of p *)
163+
(* dp' = elements removed from prefix (reversed, but doesn't matter) *)
164+
S.subset (S.of_list dp') s' && (* removed elements become part of set, must be contained, because compose can only add them *)
165+
S.subset s s' (* set elements must be contained, because compose can only add them *)
166+
| [], _ :: _ -> (* p is strict prefix of p' and t is non-unique *)
167+
false (* composing to non-unique cannot lengthen prefix *)
168+
| _ :: _, _ :: _ -> (* prefixes are incompatible *)
169+
false (* composing cannot fix incompatibility there *)
170+
)
155171

156172
let compose ((p, s) as current) ni =
157173
if BatList.mem_cmp Base.compare ni p then (
@@ -242,8 +258,8 @@ struct
242258

243259
let is_main = unop H.is_main P.is_main
244260
let is_unique = unop H.is_unique P.is_unique
245-
let may_create = binop H.may_create P.may_create
246-
let is_must_parent = binop H.is_must_parent P.is_must_parent
261+
let may_be_ancestor = binop H.may_be_ancestor P.may_be_ancestor
262+
let must_be_ancestor = binop H.must_be_ancestor P.must_be_ancestor
247263

248264
let created x d =
249265
let lifth x' d' =
@@ -339,14 +355,14 @@ struct
339355
| Thread tid -> FlagConfiguredTID.is_unique tid
340356
| UnknownThread -> false
341357

342-
let may_create t1 t2 =
358+
let may_be_ancestor t1 t2 =
343359
match t1, t2 with
344-
| Thread tid1, Thread tid2 -> FlagConfiguredTID.may_create tid1 tid2
360+
| Thread tid1, Thread tid2 -> FlagConfiguredTID.may_be_ancestor tid1 tid2
345361
| _, _ -> true
346362

347-
let is_must_parent t1 t2 =
363+
let must_be_ancestor t1 t2 =
348364
match t1, t2 with
349-
| Thread tid1, Thread tid2 -> FlagConfiguredTID.is_must_parent tid1 tid2
365+
| Thread tid1, Thread tid2 -> FlagConfiguredTID.must_be_ancestor tid1 tid2
350366
| _, _ -> false
351367

352368
module D = FlagConfiguredTID.D

src/cdomains/mHP.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ let current (ask:Queries.ask) =
2121
}
2222

2323
let pretty () {tid; created; must_joined} =
24-
let tid_doc =
24+
let tid_doc =
2525
if GobConfig.get_bool "dbg.full-output" then
2626
Some (Pretty.dprintf "tid=%a" ThreadIdDomain.ThreadLifted.pretty tid)
2727
else
@@ -53,10 +53,10 @@ include Printable.SimplePretty (
5353
(** Can it be excluded that the thread tid2 is running at a program point where *)
5454
(* thread tid1 has created the threads in created1 *)
5555
let definitely_not_started (current, created) other =
56-
if (not (TID.is_must_parent current other)) then
56+
if (not (TID.must_be_ancestor current other)) then
5757
false
5858
else
59-
let ident_or_may_be_created creator = TID.equal creator other || TID.may_create creator other in
59+
let ident_or_may_be_created creator = TID.equal creator other || TID.may_be_ancestor creator other in
6060
if ConcDomain.ThreadSet.is_top created then
6161
false
6262
else

src/common/domains/printable.ml

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -631,16 +631,6 @@ struct
631631
BatPrintf.fprintf f "<value>\n<map>\n";
632632
loop 0 xs;
633633
BatPrintf.fprintf f "</map>\n</value>\n"
634-
635-
let common_prefix x y =
636-
let rec helper acc x y =
637-
match x,y with
638-
| x::xs, y::ys when Base.equal x y-> helper (x::acc) xs ys
639-
| _ -> acc
640-
in
641-
List.rev (helper [] x y)
642-
643-
let common_suffix x y = List.rev (common_prefix (List.rev x) (List.rev y))
644634
end
645635

646636
module type ChainParams = sig

src/util/std/gobList.ml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,22 @@ let rec fold_while_some (f : 'a -> 'b -> 'a option) (acc: 'a) (xs: 'b list): 'a
3030

3131
let equal = List.eq
3232

33+
(** [remove_common_prefix eq l1 l2] removes the common prefix ([p]) of [l1] and [l2] and
34+
returns the rest of both lists a pair [(l1', l2')].
35+
Formally, [p @ l1' = l1] and [p @ l2' = l2] such that [p] has maximal length.
36+
37+
This can be used to check being a prefix in both directions simultaneously:
38+
- if [l1' = []], then [l1] is a prefix of [l2],
39+
- if [l2' = []], then [l2] is a prefix of [l1].
40+
41+
In other cases, the common prefix is not returned (i.e. reconstructed) for efficiency reasons.
42+
43+
@param eq equality predicate for elements *)
44+
let rec remove_common_prefix eq l1 l2 =
45+
match l1, l2 with
46+
| x1 :: l1', x2 :: l2' when eq x1 x2 -> remove_common_prefix eq l1' l2'
47+
| _, _ -> (l1, l2)
48+
3349
(** Given a predicate and a list, returns two lists [(l1, l2)].
3450
[l1] contains the prefix of the list until the last element that satisfies the predicate, [l2] contains all subsequent elements. The order of elements is preserved. *)
3551
let until_last_with (pred: 'a -> bool) (xs: 'a list) =
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// PARAM: --set ana.activated[+] mhp --disable ana.thread.include-node
2+
#include <pthread.h>
3+
4+
int g;
5+
6+
void *b(void *arg) {
7+
int *gp = arg;
8+
if (gp)
9+
(*gp)++; // NORACE
10+
return NULL;
11+
}
12+
13+
void *a(void *arg) {
14+
pthread_t id;
15+
pthread_create(&id, NULL, b, arg);
16+
return NULL;
17+
}
18+
19+
int main() {
20+
pthread_t id, id2;
21+
pthread_create(&id, NULL, b, NULL);
22+
g++; // NORACE
23+
pthread_create(&id2, NULL, a, &g);
24+
return 0;
25+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// PARAM: --set ana.activated[+] mhp --disable ana.thread.include-node
2+
#include <pthread.h>
3+
4+
int g;
5+
6+
void *a(void *arg) {
7+
int *gp = arg;
8+
if (gp)
9+
(*gp)++; // RACE (self-race in non-unique thread)
10+
return NULL;
11+
}
12+
13+
void *b(void *arg) {
14+
pthread_t id, id2;
15+
pthread_create(&id, NULL, a, NULL);
16+
pthread_create(&id2, NULL, a, &g);
17+
return NULL;
18+
}
19+
20+
21+
int main() {
22+
pthread_t id, id2;
23+
pthread_create(&id, NULL, a, NULL);
24+
g++; // NORACE
25+
pthread_create(&id2, NULL, b, NULL);
26+
return 0;
27+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// PARAM: --set ana.activated[+] mhp --disable ana.thread.include-node
2+
#include <pthread.h>
3+
4+
int g;
5+
6+
void *d(void *arg) {
7+
int *gp = arg;
8+
if (gp)
9+
(*gp)++; // RACE (self-race in non-unique thread)
10+
return NULL;
11+
}
12+
13+
void *c(void *arg) {
14+
pthread_t id, id2;
15+
pthread_create(&id, NULL, d, NULL);
16+
pthread_create(&id2, NULL, d, &g);
17+
return NULL;
18+
}
19+
20+
void *b(void *arg) {
21+
return NULL;
22+
}
23+
24+
void *a(void *arg) {
25+
pthread_t id, id2;
26+
pthread_create(&id, NULL, b, NULL);
27+
g++; // NORACE
28+
pthread_create(&id2, NULL, c, NULL);
29+
return NULL;
30+
}
31+
32+
int main() {
33+
pthread_t id;
34+
pthread_create(&id, NULL, a, NULL);
35+
return 0;
36+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// PARAM: --set ana.activated[+] mhp --disable ana.thread.include-node
2+
#include <pthread.h>
3+
4+
int g;
5+
6+
void *a(void *arg) {
7+
int *gp = arg;
8+
if (gp)
9+
(*gp)++; // RACE (self-race in non-unique thread)
10+
return NULL;
11+
}
12+
13+
void *b(void *arg) {
14+
pthread_t id, id2;
15+
pthread_create(&id, NULL, a, NULL);
16+
pthread_create(&id2, NULL, a, &g);
17+
return NULL;
18+
}
19+
20+
int main() {
21+
pthread_t id, id2, id3;
22+
pthread_create(&id, NULL, a, NULL);
23+
pthread_create(&id, NULL, a, NULL);
24+
g++; // NORACE
25+
pthread_create(&id, NULL, b, NULL);
26+
return 0;
27+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// PARAM: --set ana.activated[+] mhp --disable ana.thread.include-node
2+
#include <pthread.h>
3+
4+
int g;
5+
6+
void *b(void *arg) {
7+
return NULL;
8+
}
9+
10+
void *c(void *arg) {
11+
int *gp = arg;
12+
if (gp)
13+
(*gp)++; // RACE (self-race in non-unique thread)
14+
return NULL;
15+
}
16+
17+
void *a(void *arg) {
18+
pthread_t id, id2, id3, id4;
19+
pthread_create(&id, NULL, b, NULL);
20+
pthread_create(&id2, NULL, b, NULL);
21+
g++; // NORACE
22+
pthread_create(&id, NULL, c, NULL);
23+
pthread_create(&id2, NULL, c, &g);
24+
return NULL;
25+
}
26+
27+
int main() {
28+
pthread_t id;
29+
pthread_create(&id, NULL, a, NULL);
30+
return 0;
31+
}

0 commit comments

Comments
 (0)