Abstract
We construct novel threadmodular analyses that track relational information for potentially overlapping clusters of global variables – given that they are protected by common mutexes. We provide a framework to systematically increase the precision of clustered relational analyses by splitting control locations based on abstractions of local traces. As one instance, we obtain an analysis of dynamic thread creation and joining. Interestingly, tracking less relational information for globals may result in higher precision. We consider the class of 2decomposable domains that encompasses many weakly relational domains (e.g., Octagons). For these domains, we prove that maximal precision is attained already for clusters of globals of sizes at most 2.
You have full access to this open access chapter, Download conference paper PDF
Similar content being viewed by others
Keywords
 threadmodular relational abstract interpretation
 collecting local trace semantics
 clusters
 dynamic thread creation
 concurrency
1 Introduction
Tracking relationships between program variables is indispensable for proving properties of programs or verifying the absence of certain programming errors [14, 16, 33]. Inferring relational properties is particularly challenging for multithreaded programs as all interferences by other threads that may happen in parallel, must be taken into account. In such an environment, only relational properties between globals protected by common mutexes are likely to persist throughout program execution. Generally, relations on clusters consisting of fewer variables are less brittle than those on larger clusters. Moreover, monolithic relational analyses employing, e.g., the polyhedral abstract domain are known to be notoriously expensive [36, 54]. Tracking smaller clusters may even be more precise than tracking larger clusters [19].
Example 1
Consider the following program. All accesses to globals g, h, and i are protected by the mutex a.
In this program, the main thread creates two new threads, starting at \(t_1\) and \(t_2\), respectively. Then it locks the mutex a to set all globals nondeterministically to some value and unlocks a again. After having joined the thread \(t_2\), it locks a again and sets all globals to the same unknown value and unlocks a again. Thread \(t_1\) sets i to the value of h. Thread \(t_2\) sets g and h to (potentially different) unknown values. Assume we are interested in equalities between globals. In order to succeed in showing assertion (1), it is necessary to detect that the main thread is unique and thus cannot read its past writes since these have been overwritten. Additionally, the analysis needs to certify that thread \(t_2\) also is unique, has been joined before the assertion, and that its writes must also have been overwritten.
For an analysis to prove assertion (2), propagating a joint abstraction of the values of all globals protected by a does not suffice: At the unlock of a in \(t_1\), \(g{=}h\) need not hold. If this monolithic relation is propagated to the last lock of a in main, (2) cannot be shown — despite \(t_1\) modifying neither g nor h. \(\square \)
Here we show, that the loss of precision indicated in the example can be remedied by replacing the monolithic abstraction of all globals protected by a mutex with suitably chosen subclusters. In the example, we propose to instead consider the subclusters \(\{g,h\}\) and \(\{h,i\}\) separately. As \(t_1\) does not write any values to the cluster \(\{g,h\}\), the imprecise relation \(\top \) is not propagated to the main thread and assertion (2) can be shown.
To finetune the analysis, we rely on weakly relational domains. A variety of weakly relational domains have been proposed in the literature such as Two Variables Per Inequality [53], Octagons [36, 37], or simplifications thereof [33, 35]. The technical property of interest which all these domains have in common is that each abstract relation can be reconstructed from its projections onto subclusters of variables of size at most 2. We call such domains 2decomposable. Beyond the numerical 2decomposable domains, also nonnumerical 2decomposable domains can be constructed such as a domain relating string names and function pointers.
Based on 2decomposable domains, we design threadmodular relational analyses of globals which may attain additional precision by taking local knowledge of threads into account. Therefore, we do not rely on a global trace semantics, but on a local trace semantics which formalizes for each thread that part of the computational past it can observe [48]. Abstract values for program points describe the set of all reaching local traces. Likewise, values recorded for observable actions are abstractions of all local traces ending in the corresponding action. Such observable actions are, e.g., unlock operations for mutexes. The abstract values are then refined by taking finite abstractions of local traces into account. To this end, we propose a generic framework that reuses the components of any base analysis as black boxes. Our contributions can be summarized as follows:

We provide new relational analyses of globals as abstractions of the local trace semantics based on overlapping variable clusters (Sections 3, 4, and 8).

Our analysis deals with dynamically created and joined threads, whose thread ids may, e.g., be communicated to other threads via variables and which may synchronize via mutexes (Section 3).

We provide a generic scheme to incorporate historybased arguments into the analysis by taking finite abstractions of local traces into account (Section 5).

We give an analysis of dynamically created thread ids as an instance of our generic scheme. We apply this to exclude selfinfluences or reads from threads that cannot possibly run in parallel (Sections 6 and 7).

We prove that some loss of precision of relational analyses can be avoided by tracking all subclusters of variables. For the class of 2decomposable relational domains, we prove that tracking variable clusters of size greater than 2 can be abandoned without precision loss (Section 8).
The analyses in this paper have all been implemented, a report of a practical evaluation is included in Section 9, whereas Section 10 details related work.
2 Relational Domains
First, we define the notion of relational domain employed in the description of our analysis. Let \({\mathcal{V}\! ars }\) be a set of variables, potentially of different types. We assume all configurations and assignments to be welltyped, i.e., the type of the (abstract) value matches the one specified for a variable. For each type \(\tau \) of values, we assume a complete lattice \(\mathcal{V}_\tau ^\sharp \) of abstract values abstracting the respective concrete values from \(\mathcal{V}_\tau \). Let \({\mathcal{V}^\sharp }\) denote the collection of these lattices, and \({\mathcal{V}\! ars }\rightarrow _\bot {\mathcal{V}^\sharp }\) denote the set of all typeconsistent assignments \(\sigma \) from variables to non\(\bot \) abstract values, extended with a dedicated least element (also denoted by \(\bot \)), and equipped with the induced ordering. A relational domain \(\mathcal{R}\) then is a complete lattice which provides the following operations
The operations to the left provide the abstract state transformers for the basic operation of programs (with nondeterministic assignments expressed as restrictions), while \(\textsf {lift}\) and \(\textsf {unlift}\) allow casting from abstract variable assignments to the relational domain as well as extracting singlevariable information. We assume that \(\textsf {lift}\,\bot = \bot \) and \(\textsf {unlift}\,\bot = \bot \), and require that \(\textsf {unlift}\circ \textsf {lift}\sqsupseteq \textsf {id}\) where \(\sqsupseteq \) refers to the ordering of \(({\mathcal{V}\! ars }\rightarrow _\bot {\mathcal{V}^\sharp })\). Moreover, we require that the meet operations \(\sqcap \) of \({\mathcal{V}^\sharp }\) and \(\mathcal{R}\) safely approximate the intersection of the concretizations of the respective arguments. Restricting a relation r to a subset Y of variables amounts to forgetting all information about variables not in Y. Thus, we demand , , when \(Y_1 \subseteq Y_2\), , and
Restriction thus is idempotent. For convenience, we also define a shorthand for assignment of abstract values^{Footnote 1}: . In order to construct an abstract interpretation, we further require monotonic concretization functions \(\gamma _{\mathcal{V}^\sharp }:{\mathcal{V}^\sharp }\rightarrow 2^\mathcal{V}\) and \(\gamma _\mathcal{R}:\mathcal{R}\rightarrow 2^{{\mathcal{V}\! ars }\rightarrow \mathcal{V}}\) satisfying the requirements presented in Fig. 1.
Example 2
As a value domain \(\mathcal{V}^\sharp _\tau \), consider the flat lattice over the sets of values of appropriate type \(\tau \). A relational domain \(\mathcal{R}_1\) is obtained by collecting satisfiable conjunctions of equalities between variables or variables and constants where the ordering is logical implication, extended with \({\textsf {False}}\) as least element. The greatest element in this complete lattice is given by \({\textsf {True}}\). The operations \(\textsf {lift}\) and \(\textsf {unlift}\) for non\(\bot \) arguments then can be defined as
The restriction of r to a subset Y of variables is given by the conjunction of all equalities implied by r which only contain variables from Y or constants. \(\square \)
In line of Example 2, also nonnumerical relational domains may be constructed.
A variable clustering \(\mathcal{S}\subseteq 2^{{\mathcal{V}\! ars }}\) is a set of subsets (clusters) of variables. For any cluster \(Y\subseteq {\mathcal{V}\! ars }\), let ; this set collects all abstract values from \(\mathcal{R}\) containing information on variables in Y only. Given an arbitrary clustering \(\mathcal{S}\subseteq 2^{{\mathcal{V}\! ars }}\), any relation \(r\in \mathcal{R}\) can be approximated by a meet of relations from \(\mathcal{R}^Y\) (\(Y\in \mathcal S\)) since for every \(r\in \mathcal{R}\), holds.
Some relational domains, however, can be fully recovered from their restrictions to specific subsets of clusters. We consider for \(k\ge 1\), the set \(\mathcal{S}_k\) of all nonempty subsets \(Y\subseteq {\mathcal{V}\! ars }\) of cardinality at most k. We call a relational domain \(\mathcal{R}\) kdecomposable if each abstract value from \(\mathcal{R}\) can be precisely expressed as the meet of its restrictions to clusters of \(\mathcal{S}_k\) and when all least upper bounds can be recovered by computing with clusters of \(\mathcal{S}_k\) only; that is,
holds for each abstract relation \(r\in \mathcal{R}\) and each set of abstract relations \(R\subseteq \mathcal{R}\).
Example 3
The domain \(\mathcal{R}_1\) from the previous example is 2decomposable. This also holds for the octagon domain [36] and many other weakly relational numeric domains (pentagons [33], weighted hexagons [21], logahedra [28], TVPI [53], dDBM [46], and AVO [11]). The affine equalities or affine inequalities domains [16, 30], however, are not. The relational string domains proposed by Arceri et al. [6, Sec. 5.1  5.3], are examples of nonnumeric 2decomposable domains.
3 A Local Trace Semantics
We build upon the semantic framework for local traces, introduced by Schwarz et al. [48]. A local trace records all past events that have affected the present configuration of a specific thread, referred to as the ego thread. In [48], the local trace semantics is proven equivalent to the global trace semantics which itself is equivalent to a global interleaving semantics. In particular, any analysis that is sound w.r.t. the local trace semantics also is w.r.t. the interleaving semantics.
While the framework of Schwarz et al. [48] allows for different formalizations of traces, thread synchronization happens only via locking/unlocking and thread creation. Generalizing their semantics, we identify certain actions as observable by other threads when executing corresponding observing actions (see Table 1). When the ego thread executes an observing action, a local trace ending in the corresponding observable action is incorporated. Here, we consider as observable/observing actions locking/unlocking mutexes and creating/joining threads.
Consider, e.g., the program in Fig. 2a and a corresponding local trace (Fig. 2b). This trace consists of one swim lane for each thread representing the sequence of steps it executed where each node in the graph represents a configuration attained by it. Additionally, the trace records the create and join orders as well as for each mutex a, the locking order for a (\(\rightarrow _c,\rightarrow _j\), and \(\rightarrow _a\), respectively). These orders introduce extra relationships between thread configurations. The unique start node of each local trace is an initial configuration of the main thread.
We distinguish between the sets \(\mathcal{X}\) and \(\mathcal{G}\) of local and global variables. We assume that \(\mathcal{X}\) contains a special variable \(\textsf {self}\) within which the thread id of the current thread, drawn from the set \(\mathcal{I}\), is maintained. A (local) thread configuration is a pair \((u,\sigma )\) where u is a program point and the typeconsistent map \(\sigma : \mathcal{X}\rightarrow \mathcal{V}\) provides values for the local variables. The values of globals are not explicitly represented in a thread configuration, but can be recovered by consulting the (unique) last write to this global within the local trace. To model weak memory effects, weaker notions of last writes are conceivable. As in [48], we consider a set of actions \(\mathcal {A} ct \) that consists of locking and unlocking a (nonreentrant) mutex from a set \(\textsf {M}\), copying values of globals into locals and viceversa, creating a new thread, as well as assignments with and branching on local variables. We extend \(\mathcal {A} ct \) with actions for returning from and joining with threads. We assume that writes to and reads from globals are atomic (or more precisely, we assume copying values of integral type to be atomic). This is enforced for each global g by a dedicated mutex \(m_g\) acquired just before accessing g and released immediately after. For simplicity, we associate traces corresponding to a write of g to this dedicated mutex \(m_g\), and thus do not need to consider writing and reading of globals as observable/observing actions. In examples, we omit explicitly locking and unlocking these mutexes. By convention, at program start all globals have value 0, while local variables may initially have any value.
Each thread is represented by a controlflow graph with edges \(e\in \mathcal{E}\) of the form \(e=(u,\textsf {act},u')\) for some action \(\textsf {act}\in \mathcal {A} ct \) and program points u and \(u'\) where the start point of the main thread is \(u_0\). Let \(\mathcal {T}\) denote the set of all local traces of a given program. A formalism for local traces must, for each edge e of the controlflow graph, provide a transformation \(\llbracket e\rrbracket :\mathcal {T}^k\rightarrow 2^\mathcal {T}\) so that \(\llbracket e\rrbracket (t_0,\ldots ,t_{k1})\) extends the local trace \(t_0\), possibly incorporating other local traces. For the operations \(\textsf {lock}(a),a\in \textsf {M}\), or \(x{=}\textsf {join}(x'), x,x'\in \mathcal{X}\), the arity of \(\llbracket e\rrbracket \) is two, another local trace, namely, with last operation \(\textsf {unlock}(a)\) or \(\textsf {return}\,x''\), respectively, is incorporated. The remaining edge transformations have arity one. In all cases, the set of resulting local traces may be empty when the operation is not applicable to its argument(s). We write \(\llbracket e\rrbracket (T_0,\ldots ,T_{k1})\) for the set \(\bigcup _{t_0\in T_0,\ldots ,t_{k1}\in T_{k1}}\llbracket e\rrbracket (t_0,\ldots ,t_{k1})\).
Given definitions of \(\llbracket e\rrbracket \), the set \(\mathcal {T}\) can be inductively defined starting from a set \(\textsf {init}\) of initial local traces consisting of initial configurations of the main thread. To develop efficient threadmodular abstractions, we are interested in subsets \(\mathcal {T}[u], \mathcal {T}[a],\mathcal {T}[i]\) of local traces ending at some program point u, ending with an unlock operation for mutexes a (or from \(\textsf {init}\)), or ending with a return statement of thread i, respectively. Schwarz et al. [48] showed that such subsets can be described as the least solution of a sideeffecting constraint system [5]. There, each righthand side may, besides its contribution to the unknown on the left, also provide contributions to other unknowns (the sideeffects). This allows expressing analyses that accumulate flowinsensitive information about globals during a flowsensitive analysis of local states with dynamic control flow [51]. Here, in the presence of dynamic thread creation, we use sideeffects to express that an observable action, unlock or return, should also contribute to the sets \(\mathcal {T}[a]\) or \(\mathcal {T}[i]\), such that they can be incorporated at the corresponding observing action. The sideeffecting formulation of our concrete semantics takes the form:
where the ordering \(\sqsupseteq \) is induced by the superset ordering and righthand sides are defined in Fig. 3. A righthand side takes an assignment \(\eta \) of the unknowns of the system and returns a pair \((\eta ',T)\) where T is the contribution to the unknown occurring on the left (as in ordinary constraint systems). The first component collects the sideeffects as the assignment \(\eta '\). If the righthand sides are monotonic, Eq. (3) has a unique least solution.
We only detail the righthand sides for the creation of threads as well as the new actions join and return; the rest remain the same as defined by Schwarz et al. [48]. For thread creation, they provide the action \(x {=} \textsf {create}(u_1)\). Here, \(u_1\) is the program point at which the created thread should start. We assume that all locals from the creator are passed to the created thread, except for the variable \(\textsf {self}\). The variables \(\textsf {self}\) in the created thread and x in the creating thread receive a fresh thread id. Here, \(\textsf {new}\,u\,u_1\,t\) computes the local trace at the start point \(u_1\) from the local trace t of the creating thread. To handle returning and joining of threads we introduce the following two actions:

return x; – terminating a thread and returning the value of the local variable x to a thread waiting for the given thread to terminate.

\(x {=} \textsf {join}(x');\) where \(x'\) is a local variable holding a thread id – blocks the ego thread, until the thread with the given thread id has terminated. As in pthreads, at most one thread may call join for a given thread id. The value provided to return by the joined thread is assigned to the local variable x.
For returning results and realization of join, we employ the unknown [i] for the thread id i of the returning thread, as shown in Fig. 3.
4 Relational Analyses as Abstractions of Local Traces
Subsequently, we give relational analyses of the values of globals which we base on the local trace semantics. They are generic in the relational domain \(\mathcal{R}\), with 2decomposable domains being particularly wellsuited, as the concept of clusters is central to the analyses. We focus on relations between globals that are jointly writeprotected by some mutex. We assume we are given for each global g, a set \(\mathcal {M}[g]\) of (write) protecting mutexes, i.e., mutexes that are always held when g is written. Let \(\mathcal {G}[a]=\{g\in \mathcal{G}\mid a\in \mathcal {M}[g]\}\) denote the set of globals protected by a mutex a. Let \(\emptyset \ne \mathcal {Q}_a \subseteq 2^{\mathcal {G}[a]}\) the set of clusters of these globals we associate with a. For technical reasons, we require at least one cluster per mutex a, which may be the empty cluster \(\emptyset \), thus not associating any information with a.
Our basic idea is to store at the unknown \([a,Q]\) (for each mutex a and cluster \(Q\in \mathcal {Q}_a\)) an abstraction of the relations only between globals in \(Q\). By construction, all globals in \(Q\) are protected by a. Whenever it is locked, the relational information stored at all \([a,Q]\) is incorporated into the local state by the lattice operation meet, i.e., the local state now maintains relations between locals as well as globals which no other thread can access at this program point. Whenever a is unlocked, the new relation between globals in all corresponding clusters \(Q\in \mathcal {Q}_a\) is sideeffected to the respective unknowns \([a,Q]\). Simultaneously, all information on globals no longer protected, is forgotten to obtain the new local state. In this way, the analysis is fully relational in the local state, while only keeping relations within clusters of globals jointly protected by some mutex.
For clarity of presentation, we perform controlpoint splitting on the set of held mutexes when reaching program points. Apart from this, the constraint system and righthand sides for the analysis closely follow those of the concrete semantics (Section 3) — with the exception that unknowns now take values from \(\mathcal{R}\) and that unknowns [a] are replaced with unknowns \([a,Q]\) for \(Q\in \mathcal {Q}_a\).
All righthand sides are given in detail in Fig. 4. For the start point of the program and the empty lockset, the righthand side \(\textsf {init}^\sharp \) returns the \(\top \) relation updated such that the variable \(\textsf {self}\) holds the abstract thread id \(i_{0}\) of the main thread. Additionally, \(\textsf {init}^\sharp \) produces a sideeffect for each mutex a and cluster \(Q\) that initializes all globals from the cluster with the value 0.
For a thread creating edge starting in program point u with lockset S, the righthand side \(\llbracket [u,S],x {=} \textsf {create}(u_1)\rrbracket ^\sharp \) first generates a new abstract thread id, which we assume can be computed using function \(\nu ^\sharp \). The new id is assigned to the variable x in the local state of the current thread. Additionally, the start state \(r'\) for the newly created thread is constructed and sideeffected to the thread’s start point with empty lockset \([u_1,\emptyset ]\). Since threads start with empty lockset, the state \(r'\) is obtained by removing all information about globals from the local state of the creator and assigning the new abstract thread id to the variable \(\textsf {self}\).
When locking a mutex a, the states stored at unknowns \([a,Q]\) with \(Q\in \mathcal {Q}_a\) are combined with the local state by meet. This is sound because the value stored at any \([a,Q]\) only maintains relationships between variables writeprotected by a, and these values soundly account for the program state at every \(\textsf {unlock}(a)\) and at program start. When unlocking a, on the other hand, the local state restricted to the appropriate clusters \(Q\in \mathcal {Q}_a\) is sideeffected to the respective unknowns \([a,Q]\), so that the changes made to variables in the cluster become visible to other threads. Also, the local state is restricted to the local variables and only those globals for which at least one protecting mutex is still held.
As special mutexes \(m_g\) immediately surrounding accesses to g are used to ensure atomicity, and information about g is associated with them, all reads and writes refer to the local copy of g. Guards and assignments (which may only involve local variables) are defined analogously. For a return edge, the abstract value to be returned is looked up in the local state and then sideeffected to the abstract thread id of the current thread (as the value of the dedicated variable \(\textsf {ret}\)). For join, the least upper bound of all return values of all possibly joined threads is assigned to the lefthand side of the join statement in the local state.
Example 4
Consider the program^{Footnote 2} where \(\mathcal {M}[g] = \{a,b,m_g\}\), \(\mathcal {M}[h] = \{a,b,m_h\}\), \(\mathcal {Q}_a = \{\{g,h\}\}\), \(\mathcal {Q}_b = \{\{g,h\}\}\).
Our analysis succeeds in proving all assertions here. Thread \(t_2\) is of particular interest: When locking b only \(g\le h\) is known to hold, and locking the additional mutex a means that the better information \(g=h\) becomes available. The analysis by Mukherjee et al. [42] on the other hand only succeeds in proving assertion (2) — even when all globals are put in the same region. It cannot establish (1) because all correlations between locals and globals are forgotten when the mix operation is applied at the second \(\textsf {lock}(b)\) in the main thread. (3) cannot be established because, at \(\textsf {lock}(b)\) in \(t_1\), the mix operation also incorporates the state after the first \(\textsf {unlock}(b)\) in the main thread, where \(g=h\) does not hold. Similarly, for (4). The same applies for assertion (3) and the analysis using lock invariants proposed by Miné [39]. This analysis also falls short of showing (1), as at the \(\textsf {lock}(b)\) in the main thread, the lock invariant associated with b is joined into the local state. (4) is similarly out of reach. The same reasoning also applies to [39, 42, 48] after equipping the analyses with thread ids. \(\square \)
Theorem 1
Any solution of the constraint system is sound w.r.t. the local trace semantics.
Proof
The proof is by fixpoint induction, the details are given in Appendix B of the extended version [49] of this paper.
We remark that, instead of relying on \(\mathcal {M}[g]\) being precomputed, an analysis can also infer this information on the fly [58].
The analysis however still has some deficiencies. All writes to a global are accumulated regardless of the writing thread. As a consequence, a thread does, e.g., not only read its latest local writes but also all earlier local writes, even if those are definitely overwritten. Excluding some threads’ writes is an instance of the more general idea of excluding writes that cannot be last writes. Instead of giving ad hoc remedies for this specific shortcoming, we propose a general mechanism to improve the precision of any threadmodular analysis in the next section, and later instantiate it to the issue highlighted here.
5 Refinement via Finite Abstractions of Local Traces
To improve precision of threadmodular analyses we take additional abstractions of local traces into account. Our approach is generic, building on the righthand sides of a base analysis and using them as black boxes. We will instantiate this framework to exclude writes based on thread ids from the analysis in Section 4. Other instantiations are conceivable as well. To make it widely applicable, the framework allows base analyses that already perform some splitting of unknowns at program points (e.g., locksets in Section 4). We denote by \([\hat{u}]\) such (possibly) extended unknowns for a program point u. A (base) analysis is defined by its righthand sides, and a collection of domains: (1) \(\mathcal{D}_S\) for abstract values stored at unknowns for program points; (2) \(\mathcal{D}_\textsf {act}\) for abstract values stored at observable actions \(\textsf {act}\) (e.g., in Section 4, \(\mathcal{D}_M\) for unlocks and \(\mathcal{D}_T\) for thread returns).
Let \(\mathcal {A}\) be a set of finite information that can be extracted from a local trace by a function \(\alpha _\mathcal {A}{:}\mathcal {T}{\rightarrow } \mathcal {A}\). We call \(\alpha _\mathcal {A}\,t{\in }\mathcal {A}\) the digest of some local trace t. Let \(\llbracket u,\textsf {act}\rrbracket ^\sharp _\mathcal {A}{:} \mathcal {A}^k{\rightarrow }2^\mathcal {A}\) be the effect on the digest when performing a kary action \(\textsf {act}\in \mathcal {A} ct \) for a control flow edge originating at u. We require for \(e{=}(u,\textsf {act},v){\in }\mathcal{E}\),
where \(\alpha _\mathcal {A}\) is lifted elementwise to sets. While the first restriction ensures determinism, the second intuitively ensures that \(\llbracket u,\textsf {act}\rrbracket ^\sharp _\mathcal {A}\) soundly abstracts \(\llbracket e\rrbracket \).
For thread creation, we additionally require a helper function \(\textsf {new}^\sharp _\mathcal {A}: \mathcal{N}\rightarrow \mathcal{N}\rightarrow \mathcal {A}\rightarrow \mathcal {A}\) that returns for a thread created at an edge originating from u and starting execution at program point \(u_1\) the new digest. The same requirements are imposed for edges \((u,x {=}\textsf {create}(u_1),v)\in \mathcal{E}\),
Also, we define for the initial digest at the start of the program
Under these assumptions, we can perform controlpoint splitting according to \(\mathcal {A}\). This means that unknowns \([\hat{u}]\) for program points u are replaced with new unknowns \([\hat{u},A]\), \(A\in \mathcal {A}\). Analogously, unknowns for observable actions \([\textsf {act}]\) are replaced with unknowns \([\textsf {act},A]\) for \(A\in \mathcal {A}\). Consider a single constraint from an abstract constraint system of the last section, which soundly abstracts the collecting local trace semantics of a program.
The corresponding constraints of the refined system with controlpoint splitting differ based on whether the action \(\textsf {act}\) is observing, observable, or neither.

When \(\textsf {act}\) is observing, the new righthand side additionally gets the digest \(A_1\) associated with the local traces that are to be incorporated:
$$ \begin{array}{lll} (\eta , \eta \,\left[ \hat{v}, A'\right] )\sqsupseteq & {} \llbracket [\hat{u},A_0],\textsf {act},A_1\rrbracket ^\sharp \,\eta \qquad \text {for } A_0,A_1\in \mathcal {A}, A'\in \llbracket u,\textsf {act}\rrbracket ^\sharp _\mathcal {A}\,(A_0,A_1) \end{array} $$ 
When \(\textsf {act}\) is \(observable \), the digest \(A'\) of the resulting local trace is passed, so the sideeffect can be redirected to the appropriate unknown:
$$ \begin{array}{lll} (\eta , \eta \,\left[ \hat{v}, A'\right] )\sqsupseteq & {} \llbracket [\hat{u},A_0],\textsf {act},A'\rrbracket ^\sharp \,\eta \qquad \text {for } A_0\in \mathcal {A}, A'\in \llbracket u,\textsf {act}\rrbracket ^\sharp _\mathcal {A}\,(A_0) \end{array} $$ 
When \(\textsf {act}\) is neither, no additional digest is passed:
$$ \begin{array}{lll} (\eta , \eta \,\left[ \hat{v}, A'\right] )\sqsupseteq & {} \llbracket [\hat{u},A_0],\textsf {act}\rrbracket ^\sharp \,\eta \qquad \;\;\;\;\; \text {for } A_0\in \mathcal {A}, A'\in \llbracket u,\textsf {act}\rrbracket ^\sharp _\mathcal {A}\,(A_0) \end{array} $$
The new righthand sides are defined in terms of the righthand side of the base analysis which are used as black boxes (Fig. 5). They act as wrappers, mapping any unknown consulted or sideeffected to by the original analysis to the appropriate unknown of the refined system. Thus, the refined analysis automatically benefits from the extra information the digests provide. It may, e.g., exploit that \(\llbracket u,\textsf {act}\rrbracket ^\sharp _\mathcal {A}(A_0,A_1) = \emptyset \) meaning that, no local traces with digests \(A_0,A_1\) can be combined into a valid local trace ending with action \(\textsf {act}\). The complete definition of the refined constraint system instantiated to the actions considered here and unknowns for program points enriched with locksets is given in [49, Fig. 14].
Enriching program points with locksets can in fact be seen as a first application of this framework. The righthand sides are given in Fig. 6.
Example 5
As a further instance, consider tracking which mutexes have been locked at least once in the local trace. At \(\textsf {lock}(a)\) traces in which a thread has performed a \(\textsf {lock}(a)\) can not be combined with traces that contain no \(\textsf {lock}(a)\). The corresponding righthand sides are given in Fig. 7. When refining the analysis from Section 4 accordingly (assuming a protects g and h), it succeeds in proving the assert in this program as the initial values of 0 for g and h can be excluded.
This naturally generalizes to counting how often some action (e.g., a write to a global g) occurred, stopping exact bookkeeping at a constant (1 in this case). \(\square \)
To prove soundness of localtracebased refinement of our analysis from Section 4, we first construct a corresponding refined collecting local trace semantics. Then we verify that the refined analysis is sound w.r.t. this refined semantics – which, in turn, is proven sound w.r.t. the original collecting local trace semantics.
Theorem 2
Assume that \(\alpha _\mathcal {A}\), \(\textsf {new}^\sharp _\mathcal {A}\), and \(\llbracket u,\textsf {act}\rrbracket ^\sharp _\mathcal {A}\) fulfill requirements (4), (5), and (6). Then any solution of the refined constraint system is sound relative to the collecting local trace semantics.
Proof
A proof sketch instantiated with the actions considered here and unknowns enriched with locksets is provided in [49, Appendix D].
6 Analysis of Thread Ids and Uniqueness
We instantiate the scheme from the previous section to compute abstract thread ids and their uniqueness. That refinement of the base analysis enhances precision of the analysis by excluding reads, e.g., from threads that have not yet been started. For that, we identify threads by their thread creation history, i.e., by sequences of create edges. As these sequences may grow arbitrarily, we collect all creates occurring after the first repetition into a set to obtain finite abstractions.
Example 6
In the program from Fig. 8, the first thread created by main receives the abstract thread id \((\textsf {main}\cdot \langle u_1,t_1\rangle ,\emptyset )\). It creates a thread with abstract thread id \((\textsf {main}\cdot \langle u_1,t_1\rangle \cdot \langle u_3,t_1\rangle ,\emptyset )\). At program point \(u_3\), the latter creates a thread starting at \(t_1\) and receiving the abstract thread id \((\textsf {main}\cdot \langle u_1,t_1\rangle ,\{\langle u_3,t_1\rangle \})\) – as do all threads subsequently created at this edge. \(\square \)
Create edges, however, may also be repeatedly encountered within the creating thread, in a loop. To deal with this, we track for each thread, the set C of possibly already encountered create edges. As soon as a create edge is encountered again, the created thread receives a nonunique thread id.
Example 7
The first time the main thread reaches program point \(u_2\) in the program from Fig. 8, the created thread is assigned the unique abstract thread id \((\textsf {main}\cdot \langle u_2,t_1\rangle ,\emptyset )\). In subsequent loop iterations, the created threads are no longer kept separate, and thus receive the nonunique id \((\textsf {main},\{\langle u_2,t_1\rangle \})\). \(\square \)
Formally, let \(\mathcal{N}_C,\mathcal{N}_S\) denote the subsets of program points with outgoing edge labeled \(x {=} \textsf {create}(...)\), and of starting points of threads, respectively. Let \(\mathcal P \subseteq \mathcal{N}_C \times \mathcal{N}_S\) denote sets of pairs relating thread creation nodes with the starting points of the created threads. The set \(\mathcal{I}^\sharp \) of abstract thread ids then consists of all pairs \((i,s)\in (\textsf {main}\cdot \mathcal{P}^*)\times 2^\mathcal{P}\) in which each pair \(\langle u,f\rangle \) occurs at most once. Given the set \(\mathcal{I}^\sharp \), we require that there is a concretization \(\gamma :\mathcal{I}^\sharp \rightarrow 2^\mathcal{I}\) and a function \(\textsf {single}:\mathcal{I}^\sharp \rightarrow \mathcal{V}^\sharp _\mathcal{I}\) with \(\gamma \,i^\sharp \subseteq \gamma _{\mathcal{V}^\sharp }\,(\textsf {single}\,i^\sharp )\). The abstract thread id of the main thread is given by \((\textsf {main},\emptyset )\). Therein, the elements in \((\textsf {main}\cdot \mathcal{P}^*)\times \{\emptyset \}\) represent the unique thread ids representing at most one concrete thread id, while the elements (i, s), \(s\ne \emptyset \), are ambiguous, i.e., may represent multiple concrete thread ids. Moreover, we maintain the understanding that the concretizations of distinct abstract thread ids from \(\mathcal{I}^\sharp \) all are disjoint.
As refining information \(\mathcal {A}\) we consider not only abstract thread ids – but additionally track sets of executed thread creations within the current thread. Accordingly, we set \(\mathcal {A}= \mathcal{I}^\sharp \times 2^P\) and define the righthand sides as seen in Fig. 9, where \(\bar{i}\) denotes the set of pairs occurring in the sequence i.
Example 8
Consider again the program from Fig. 8 with righthand sides from Fig. 9, and assume that the missing righthand for join returns its first argument. The initial thread has the abstract thread id \(i_0 = (\textsf {main},\emptyset )\). At its start point, the digest thus is \((i_0,\emptyset )\). At the create edge originating at \(u_1\), a new thread with id \((\textsf {main}\cdot \langle u_1,t_1\rangle ,\emptyset )\) is created. The digest for this thread then is \(((\textsf {main}\cdot \langle u_1,t_1\rangle ,\emptyset ),\emptyset )\). For the main thread, the encountered create edge \(\langle u_1,t_1\rangle \) is added to the second component of the digest, making it \((i_0,\{\langle u_1,t_1\rangle \})\).
When \(u_2\) is reached with \((i_0,\{\langle u_1,t_1\rangle \})\), a unique thread with id \((\textsf {main}\cdot \langle u_2,t_1\rangle ,\emptyset )\) is created. The new digest of the creating thread then is \((i_0,\{\langle u_1,t_1\rangle ,\) \(\langle u_2,t_1\rangle \})\). In subsequent iterations of the loop, for which \(u_2\) is reached with \((i_0,\{\langle u_1,t_1\rangle , \langle u_2,t_1\rangle \})\), a nonunique thread with id \((\textsf {main},\{\langle u_2,t_1\rangle \})\) is created.
When reaching \(u_3\) with id \((\textsf {main},\{\langle u_2,t_1\rangle \})\), a thread with id \((\textsf {main},\{\langle u_2,t_1\rangle ,\) \(\langle u_3,t_1\rangle \})\) is created as the id of the creating thread was already not unique. When reaching it with the id \((\textsf {main}\cdot \langle u_1,t_1\rangle ,\emptyset )\), a new thread with id \((\textsf {main}\cdot \langle u_1,t_1\rangle \cdot \langle u_3,t_1\rangle ,\emptyset )\) is created. When the newly created thread reaches this program point, the threads created there have the nonunique id \((\textsf {main}\cdot \langle u_1,t_1\rangle ,\{\langle u_3,t_1\rangle \})\), as \(\langle u_3,t_1\rangle \) already appears in the id of the creating thread. \(\square \)
Abstract thread ids should provide us with functions

\(\textsf {unique}: \mathcal{I}^\sharp {\rightarrow } {\textbf {bool}}\) tells whether a thread id is unique.

\(\textsf {lcu\_anc}: \mathcal{I}^\sharp {\rightarrow } \mathcal{I}^\sharp {\rightarrow } \mathcal{I}^\sharp \) returns the last common unique ancestor of two threads.

\(\textsf {may\_create}: \mathcal{I}^\sharp {\rightarrow } \mathcal{I}^\sharp {\rightarrow } {\textbf {bool}}\) checks whether a thread may (transitively) create another.
For our domain \(\mathcal{I}^\sharp \), these can be defined as \(\textsf {unique}\,(i,s) = (s = \emptyset )\) and
We use this extra information to enhance the definitions of \(\llbracket u,\textsf {lock}(a)\rrbracket ^\sharp _\mathcal {A}\) and \(\llbracket u,x' {=} \textsf {join}(x)\rrbracket ^\sharp _\mathcal {A}\) to take into account that the ego thread cannot acquire a mutex from another thread or join a thread that has definitely not yet been created. This is the case for a thread \(t'\)

(1)
that is directly created by the unique ego thread, but the ego thread has not yet reached the program point where \(t'\) is created;

(2)
whose thread id indicates that a thread that has not yet been created according to (1), is part of the creation history of \(t'\).
Accordingly, we introduce the predicate \(\textsf {may\_run}\,(i,C)\,(i',C')\) defined as
which is false whenever thread \(i'\) is definitely not yet started. We then set
This analysis of thread ids and uniqueness can be considered as a MayHappenInParallel (or, more precisely, MustNotHappenInParallel) analysis. MHP information is useful in a variety of scenarios: a threadmodular analysis of data races or deadlocks, e.g., that does not consider thread ids and joining, can be refined with this analysis to exclude more data races or deadlocks. Subsequently, we outline how the analysis from Section 4 may benefit from MHP information.
7 Exploiting Thread IDs to Improve Relational Analyses
We subsequently exploit abstract thread ids and their uniqueness to limit the amount of reading performed by the analysis from Section 4.
 I1:

from other threads that have not yet been created.
 I2:

the ego thread’s past writes, if its thread id is unique.
 I3:

past writes from threads that have already been joined.
Improvements I1 and I3 have, e.g., been realized in a setting where thread ids and which thread is joined where can be read off from controlflow graphs [31]. Here, however, this information is computed during analysis. In our framework, I1 is already achieved by refining the base analysis according to Section 6.
Example 9
Consider the program below where \(\mathcal {M}[g] = \{a,b,m_g\}\), \(\mathcal {M}[h] = \{a,b,m_h\}\), \(\mathcal {M}[i] = \{m_i\}\) and assume \(\mathcal {Q}_a =\{\{g,h\}\}\).
The analysis succeeds in proving (1), as the thread (starting at) \(t_3\) that breaks the invariant \(g{=}h\) has definitely not been started yet at this program point. Without refinement, the analysis from Section 4 could not prove (1). However, this does not suffice to prove (2). At this program point, \(t_2\) may already be started. At the \(\textsf {lock}(a)\) in \(t_2\), \(t_3\) may also be started; thus, the violation of the invariant \(g{=}h\) by \(t_3\) is incorporated into the local state of \(t_2\) at lock. At \(\textsf {unlock}(a)\), despite \(t_2\) only reading g, the imprecise abstract relation violating \(g{=}h\), is sideeffected to \([a,\{g,h\},t_2]\) and is incorporated at the second \(\textsf {lock}(a)\) of the main thread. The final shortcoming is that each thread reads all its own past (and future!) writes – even when it is known to be unique. This means that (3) cannot be proven. \(\square \)
To achieve I2, some effort is required as our analysis forgets values of globals when they become unprotected. This is in contrast, e.g., to [39, 42]. We thus restrict sideeffecting to mutexes to cases where the ego thread has possibly written a protected global since acquiring it. This is in contrast to Section 4, where a sideeffect is performed at every unlock, i.e., everything a thread reads is treated as if it was written by that thread.
Technically, we locally track a map \(L: (\textsf {M}\times \mathcal {Q}) \rightarrow \mathcal{R}\), where \(L\,(a,Q)\) maintains for a mutex a, an abstract relation between the globals in cluster \(Q\in \mathcal {Q}_a\). More specifically, the abstract relation on the globals from \(Q\) recorded in \(L\,(a,Q)\) is the one that held when a was unlocked joinlocally for the first time after the last joinlocal write to a global in \(\mathcal {G}\,[a]\). If there is no such \(\textsf {unlock}(a)\), the relation at program start is recorded. We call an operation in a local trace joinlocal to the ego thread, if it is (a) threadlocal, i.e., performed by the ego thread, or (b) is executed by a thread that is (transitively) joined into the ego thread, or (c) is joinlocal to the parent thread at the node at which the ego thread is created. This notion will also be crucial for realizing I3. Joinlocality is illustrated in Fig. 10, where the joinlocal part of a local trace is highlighted.
For joinlocal contributions, it suffices to consult \(L\,a\) instead of unknowns \([a,Q,i]\). Such contributions are accounted for. To check whether a contribution from some thread id is accounted for, we introduce a function \(\textsf {acc}: (\mathcal {A}\times \mathcal{D}_S) {\rightarrow } \mathcal {A}{\rightarrow } \textsf {bool}\) (see definition (7) below). Besides an abstract value from \(\mathcal{R}\), the local state \(\mathcal{D}_S\) now contains two additional components:

The map \(L: (\textsf {M}\times \mathcal {Q}) \rightarrow \mathcal{R}\) for which the join is given componentwise;

The set \(W: 2^\mathcal{G}\) (ordered by \(\subseteq \)) of globals that may have been written since one of its protecting mutexes has been locked, and not all protecting mutexes have been unlocked since.
Just like r, L and W are abstractions of the reaching local traces. \(\mathcal{D}_T\) is also enhanced with an L component, while \(\mathcal{D}_M\) remains unmodified. We sketch the righthand sides here, definitions are given in Fig. 11. For program start \(\textsf {init}^\sharp \), in contrast to the analysis from Section 4, there is no initial sideeffect to the unknowns for mutexes. The initial values of globals are joinlocal, and thus accounted for in the L component also passed to any subsequently created thread.
The righthand sides for thread creation and return differ from the analysis from Section 4 enhanced with thread ids only in the handling of additional data structures L and W. As the thread ids are tracked precisely in the \(\mathcal {A}\) component, this information is directly used when determining which unknown to sideeffect to and unknowns [(i, C)] replace unknowns \([i',(i,C)]\).
For join, if the return value of the thread is not accounted for, it is assigned to the variable on the lefthand side and the L information from the ego thread and the joined thread is joined. If, on the other hand, it is accounted for, the thread has already been joined and cannot be joined again. There is a separate constraint for each \((i',C')\), so that all threads that could be joined are considered.
For locking of mutexes, upon lock, if \((i',C')\) is not accounted for, its information on the globals protected by a is joined with the joinlocal information for a maintained in \(L\,(a,Q)\), \(Q\in \mathcal {Q}_a\). This information about the globals protected by a is then incorporated into the local state by \(\sqcap \). For unlocking of mutexes, if there may have been a write to a protected global since the mutex was locked (according to W), the joinlocal information is updated and the local state restricted to \(Q\) is sideeffected to the appropriate unknown \([a,Q,(i,C)]\) for \(Q\in \mathcal {Q}_a\). Just like in Section 4, r is then restricted to only maintain relationships between locals and those globals for which at least one protecting mutex is still held. Reading from and writing to globals once more are purely local operations. To exclude self writes, we set
The resulting analysis thus takes I1 (via \(\llbracket ...\rrbracket ^\sharp _\mathcal {A}\) defined in Section 6), as well as I2 (via \(\textsf {acc}\)) into account. In Example 9, it is now able to show all assertions.
Theorem 3
This analysis is sound w.r.t. to the local trace semantics.
Proof
The proof relies on the following observations:

When \(\mathcal {G}[a]\cap W = \emptyset \), no sideeffect is required.

Exclusions based on \(\textsf {acc}\) are sound, i.e., it only excludes joinlocal writes.
The detailed proof is a simplification of a proof for an enhanced analysis from the extended version [49, Appendix F], which we outline in Appendix G there. \(\square \)
The analysis does not make use of components C at unknowns \([a,Q,(i,C)]\) and [i, C]. In [49, Appendix E], we detail how this information can be exploited to exclude a further class of writes – namely, those that are performed by an ancestor of the ego thread before the ego thread was created. Alternatively, an implementation may abandon controlpoint splitting according to C at mutexes and thread ids, replacing \([a,Q,(i,C)], [i,C]\) with \([a,Q,i]\) and [i], respectively.
When turning to improvement I3, we observe that after joining a thread t with a unique thread id, t cannot perform further writes. As all writes of joined threads are joinlocal to the ego thread, it is not necessary to read from the corresponding global unknowns. We therefore enhance the analysis to also track in the local state, the set J of thread ids for which join has definitely been called in the joinlocal part of the local trace and refine \(\textsf {acc}\) to take J into account:
The extended version [49, Appendix F] gives details on this enhancement.
8 Exploiting Clustered Relational Domains
Naïvely, one might assume that tracking relations among a larger set of globals is necessarily more precise than between smaller sets. Interestingly, this is no longer true for our analyses, e.g., in presence of thread ids. A similar effect where relating more globals can deteriorate precision has also been observed in the context of an analysis using a dataflow graph to model interferences [19].
Example 10
Consider again Example 1 in the introduction with \(\mathcal {Q}_a = \{\{g,h,i\}\}\). For this program, the constraint system of the analysis has a unique least solution. It verifies that assertion (1) holds. It assures for \([a,\{g,h,i\},t_1]\) that \(h{=}i\) holds, while for the main thread and the program point before each assertion, \(L\,(a,\{g,h,i\}) = \{ g{=}h, h{=}i \}\) holds, while for \([a,\{g,h,i\},\textsf {main}]\) and \([a,\{g,h,i\},t_2]\) only \(\top \) is recorded, as is for any relation associated with \(m_g\), \(m_h\), or \(m_i\). Assertion (2), however, will not succeed, as the sideeffect from \(t_1\) causes the older values from the first write in the main thread to be propagated to the assertions as well, implying that while \(h{=}i\) is proven, \(g{=}h\) is not. \(\square \)
Intuitively, the analysis loses precision because, at an unlock of mutex a, the current relationships between all clusters protected by a are sideeffected. As soon as one global is written to, the analysis behaves as if all protected globals had been written. By limiting publishing to those clusters for which at least one global has been written, more precise information may remain at others.
In the improved analysis, when unlocking a mutex a, sideeffects are only produced to clusters \(Q\in \mathcal {Q}_a\) containing at least one global that was written to since the last \(\textsf {lock}(a)\). Definitions for locking and unlocking are given in Fig. 12.
For locking the mutex a, the abstract value to be incorporated into the local state is assembled from the contributions of different threads to the clusters. For that, the separate constraints for each admitted digest from Section 5 are combined into one for the set \(\textbf{I} = \{ (i',C') \mid (i,C) \in \llbracket \textsf {lock}(a)\rrbracket ^\sharp _\mathcal {A}((i,C),(i',C')) \}\) of all admitted digests. This is necessary as sideeffects to unaffected clusters at \(\textsf {unlock}(a)\) have been abandoned and thus the meet with the values for clusters of one thread at a time is unsound. For each \(Q\), the joinlocal information \(L\,(a,Q)\) is joined with all contributions to \(Q\) by threads that are not yet accounted for, but admitted for \(Q\) by the digests. Here, the contributions of threads that do not write \(Q\) is \(\bot \), and thus do not affect the value for \(Q\). Finally, the resulting value is used to improve the local state by meet. The righthand side for \(\textsf {lock}(a)\) thus exploits the finegrained, percluster MHP information provided by the digests and the predicate \(\textsf {acc}\). We obtain:
Theorem 4
Given domains \(\mathcal{R}\) and \({\mathcal{V}^\sharp }\) fulfilling the requirements from Fig. 1, any solution of the constraint system is sound w.r.t. the local trace semantics. Maximum precision is obtained with \(\mathcal {Q}_a = 2^{\mathcal {G}[a]}\). \(\square \)
For Example 1, with \(\mathcal {Q}_a = 2^{\mathcal {G}[a]}\), both assertions are verified. Performing the analysis with all subclusters simultaneously can be expensive when sets \(\mathcal {G}[a]\) are large. The choice of subclustering thus generally involves a tradeoff between precision and runtime. This is different for kdecomposable relational domains:
Theorem 5
Provided the relational domain is kdecomposable (Equation (2)), the clustered analysis using all subclusters of sizes at most k only, is equally precise as the clustered analysis using all subclusters \(\mathcal {Q}_a = 2^{\mathcal {G}[a]}\) at mutexes a.
Proof
Consider a solution \(\eta \) of the constraint system with \(\mathcal {Q}_a = 2^{\mathcal {G}[a]}\). Then for unknowns [a, Q, (i, C)] and \([a,Q',(i,C)]\) with \(Q\subseteq Q'\) and \(Q \le k\), and values \(r{=}\eta \,[a,Q,(i,C)]\), \(r'{=}\eta \,[a,Q',(i,C)]\), we have that (whenever the smaller cluster receives a sideeffect, so does the larger one). Thus, by kdecomposability, the additional larger clusters \(Q'\), do not improve the meet over the clusters of size at most k for individual thread ids as well as the meet of their joins over all thread ids. The same also applies to the clustered information stored in L. \(\square \)
Example 11
Consider again Example 1. If the analysis is performed with clusters \(\mathcal {Q}_a = \{\{h,i\},\{g,h\},\{g,i\},\{g\},\{i\},\{h\}\}\) both assertions can be proven. \(\square \)
The one element clusters, on the other hand, cannot be abandoned – as indicated by the example from Appendix H in the extended version [49].
9 Experimental Evaluation
We implemented [50] the analyses extending the contextsensitive static analyzer Goblint which provides the set of protecting mutexes for each global. The implementation tracks information about integral variables using either the Interval or the Octagon domains from Apron [29]. A comparison with other tools is difficult, for details see [49, Appendix I]:

Duet [19] — Its benchmarks are only available as binary gotoprograms which neither its current version nor any other tool considered here can consume. Since Duet does not support function calls, it could only be run on some of the benchmarks considered here.

AstréeA [39] — A public version is available but not licensed for evaluation.

Watts [31] — Since we were unable to run the tool on any program, we compared with the numbers reported by the authors.

NRGoblint [48] — Goblint with the nonrelational analyses from [48].
We considered four different configurations, namely, Interval: the analysis from Section 4 with Intervals; Octagon: the same analysis with Octagons; TIDs: the analysis from Section 7 with enhancement [49, Appendix F] with Octagons; Clusters: TIDs using clusters of size at most 2 only. All benchmarks were run in a virtual machine on an AMD EPYC 7742 64Core processor^{Footnote 3} running Ubuntu 20.04. The results of our evaluation are summarized in Table 2.
Our benchmarks. To capture particular challenges for multithreaded relational analysis, we collected a set of small benchmarks (including the examples from this paper) and added assertions. On these, we evaluated our analyzer, NRGoblint, and Duet. Our analysis in the Clusters configuration is capable of verifying all the programs. The other tools could only prove a handful of relational assertions.
Goblint benchmarks [48]. These benchmarks do not contain assertions. To still relate the precision of our analyzer to the nonrelational NRGoblint and to Duet, we used our tool in the Clusters setting to automatically derive invariants at each locking operation. Perhaps surprisingly, NRGoblint could verify 95% of the invariants despite being nonrelational and not using thread ids.
Watts benchmarks [31]. These programs were instrumented with asserts and significantly changed by the authors. Our analyses can verify all but 7 out of over 1000 assertions. Due to necessary fixes to programs and our inability to run their tool, numbers are not directly comparable. Nevertheless, for their scalability tests, reported runtimes for Watts are up to two orders of magnitude worse than ours. See [49, Appendix I] for a more detailed discussion.
Ratcop benchmarks [42]. These were Java programs. After manual translation to C, our analyzer succeeded in proving all assertions any configuration of Ratcop could with Octagons, while Ratcop required polyhedra in one case.
Internal comparison We evaluated our analyses in more detail on the Goblint benchmark set [48]. Fig. 13a shows sizes of the programs (in Logical LoC) and the number of thread ids found by the analysis from Section 6. The high number of threads identified as unique is encouraging. To evaluate precision, we compared the abstract values at each program point (joined over contexts). Fig. 13a shows for what proportion of program points tracking thread ids increases precision. There were no program points where precision decreased or values became incomparable, while for some programs gains of over 50% were observed. Fig. 13b illustrates runtimes. In 9 of 12 cases, performance differences between our relational analyses are negligible. In all cases, using clusters incurs no additional cost. Thus, the more precise analysis with clusters of size \(\le 2\) seems to be the method of choice for threadmodular relational abstract interpretation.
10 Related Work
Since its introduction by Miné [36, 37], the weakly relational numerical domain of Octagons has found widespread application for the analysis and verification of programs [8, 14]. Since tracking relations between all variables may be expensive, preanalyses have been suggested to identify clusters of numerical variables whose relationships may be of interest [8, 14, 26, 45]. A dynamic approach to decompose relational domains into nonoverlapping clusters based on learning is proposed by Singh et al. [55]. While these approaches trade (unnecessary) precision for efficiency, others try to partition the variables into clusters without compromising precision [15, 23, 24, 44, 54, 56]. These types of clustering are orthogonal to our approach and could, perhaps, be combined with it.
The integration of relational domains into threadmodular abstract interpretation was pioneered by Miné [39]. His analysis is based on lock invariants determining for each mutex a relation which holds whenever the mutex is not held. Weak interferences are used to account for asynchronous variable accesses. For practical analyses, a relational abstraction only for lock invariants is proposed, while using a coarse, nonrelational abstraction for the weak interferences. This framework closely follows the framework for nonrelational analysis [38], while abandoning background locksets. Our relational analysis, on the other hand, maintains at each mutex a only relations between variables writeprotected by a. For these relations more precise results can be obtained, since they are incorporated into the local state at locks by meet (while [39] uses join).
Miné [40] present an analysis framework which is orthogonal to our approach. It is tailored to the verification of algorithms that do not rely on explicit synchronization via mutexes such as the Bakery algorithm. Miné [57] extend [40] to handle weak memory effects (PSO, TSO) by incorporating memory buffers into the threadlocal semantics. The notion of interferences is also used by Sharma and Sharma [52] for the analysis of programs under the Release/Acquire Memory Model of C11 by additionally tracking abstractions of modification sequences for global variables. They consider fixed finite sets of threads only, and do not deal with thread creation or joining.
Earlier works on threadmodular relational analysis rely on Datalog rules to model interferences in the sense of Miné in combination with abstract interpretation applied to the DataFlow Graph [19] or the ControlFlow Graph [31] (later extended to weak memory [32]), respectively. Botbol et al. [10] give a nonthreadmodular analysis of multithreaded programs with messagepassing concurrency by encoding the program semantics as a symbolic transducer.
In all these approaches clusters of variables, if there are any, are predefined and not treated specially by the analysis. This is different in the threadmodular analysis proposed by Mukherjee et al. [42]. It propagates information from unlocks to locks. It is relational for the locals of each thread, and within disjoint subsets of globals, called regions. These regions must be determined beforehand and must satisfy regionrace freedom. In contrast, the only extra a priori information required by our analysis, are the sets of (write) protecting mutexes of globals – which can be computed during the analysis itself. The closest concept within our approach to a region is the set of globals jointly protected by mutexes. These sets may overlap – which the analysis explicitly exploits. Like ours, their proof of correctness refers to a threadlocal semantics. Unlike ours, it is based on interleavings and thus overly detailed. The concrete semantics on which our analyses are based, is a collecting local trace semantics extending the semantics of Schwarz et al. [48] by additionally taking thread termination and joins into account. The analyses in [48], however, are nonrelational. No refinement via further finite abstractions of local traces, such as thread ids is provided.
The thread id analysis perhaps most closely related to ours, is by Feret [20] who computes ids for agents in the \(\pi \)calculus as abstractions of sequences of encountered create edges. Another line of analysis of concurrent programs deals with determining which critical events may happen in parallel (MHP) [1,2,3,4, 7, 17, 43, 59] to detect programming errors like, e.g., data races, or identifying opportunities for optimization. Mostly, MHP analyses are obtained as abstractions of a global trace semantics [18]. We apply related techniques for improving threadmodular analyses – but based on a local trace semantics. Like MHP analyses, we take thread creation and joining histories as well as sets of held mutexes into account. Additionally, we also consider crucial aspects of the modification history of globals and provide a general framework for further refinements.
In a sequential setting, splitting control locations according to some abstraction of reaching traces is a common technique for improving the precision of dataflow analyses [9, 27] or abstract interpretation [25, 34, 41, 47]. Control point splitting can be understood as an instance of the reduced cardinal power domain [12, 13, 22]. For the analysis of multithreaded programs, Miné [39] applies the techniques of Mauborgne and Rival [34] to single threads, i.e., independently of the actions of all other threads. Our approach, on the other hand, may take arbitrary properties of local traces into account, and thus is more general.
11 Conclusion and Future Work
We have presented threadmodular relational analyses of global variables tailored to decomposable domains. In some cases, more precise results can be obtained by considering smaller clusters. For kdecomposable domains, however, we proved that the optimal result can already be obtained by considering clusters of size at most k. We have provided a framework to incorporate finite abstractions of local traces into the analysis. Here, we have applied this framework to take creation as well as joining of threads into account, but believe that it paves the way to seamlessly enhance the precision of threadmodular abstract interpretation. The evaluation of our analyses on benchmarks proposed in the literature indicates that our implementation is competitive both w.r.t. precision and efficiency. In future work, we would like to experiment with further abstractions of local traces, perhaps tailored to particular programming idioms, and also explore the potential of nonnumerical 2decomposable domains.
Notes
 1.
We use \(\sigma \oplus \{x_i\mapsto v_i\mid i=1,\ldots ,m\}\) to denote the variable assignment obtained from \(\sigma \) by replacing the values for \(x_i\) with \(v_i\) (\(i=1,\ldots ,m\)).
 2.
In all examples, g, h, and i are globals, whereas x, y, and z are locals, and the clusters at special mutexes \(m_g\) contain only g: \(\mathcal {Q}_{m_g} = \{\{g\}\}\). Unless explicitly stated otherwise, domain \(\mathcal{R}_1\) from Example 2, enhanced with variable inequalities is used.
 3.
The analyzer is singlethreaded, so it only used one (virtual) core per analysis job.
References
Agarwal, S., Barik, R., Sarkar, V., Shyamasundar, R.K.: Mayhappeninparallel analysis of x10 programs. In: PPoPP ’07, p. 183–193, ACM (2007), https://doi.org/10.1145/1229428.1229471
Albert, E., FloresMontoya, A., Genaim, S.: Analysis of mayhappeninparallel in concurrent objects. In: Giese, H., Rosu, G. (eds.) Formal Techniques for Distributed Systems  Joint 14th IFIP WG 6.1 International Conference, FMOODS 2012 and 32nd IFIP WG 6.1 International Conference, FORTE 2012, Stockholm, Sweden, June 1316, 2012. Proceedings, Lecture Notes in Computer Science, vol. 7273, pp. 35–51, Springer (2012), https://doi.org/10.1007/9783642307935_3
Albert, E., Genaim, S., Gordillo, P.: Mayhappeninparallel analysis for asynchronous programs with interprocedural synchronization. In: Blazy, S., Jensen, T.P. (eds.) Static Analysis  22nd International Symposium, SAS 2015, SaintMalo, France, September 911, 2015, Proceedings, Lecture Notes in Computer Science, vol. 9291, pp. 72–89, Springer (2015), https://doi.org/10.1007/9783662482889_5
Albert, E., Genaim, S., Gordillo, P.: Mayhappeninparallel analysis with returned futures. In: D’Souza, D., Kumar, K.N. (eds.) Automated Technology for Verification and Analysis  15th International Symposium, ATVA 2017, Pune, India, October 36, 2017, Proceedings, Lecture Notes in Computer Science, vol. 10482, pp. 42–58, Springer (2017), https://doi.org/10.1007/9783319681672_3
Apinis, K., Seidl, H., Vojdani, V.: Sideeffecting constraint systems: a swiss army knife for program analysis. In: APLAS ’12, pp. 157–172, Springer (2012), https://doi.org/10.1007/9783642351822_12
Arceri, V., Olliaro, M., Cortesi, A., Ferrara, P.: Relational string abstract domains. In: Finkbeiner, B., Wies, T. (eds.) Verification, Model Checking, and Abstract Interpretation  23rd International Conference, VMCAI 2022, Philadelphia, PA, USA, January 1618, 2022, Proceedings, Lecture Notes in Computer Science, vol. 13182, pp. 20–42, Springer (2022), https://doi.org/10.1007/9783030945831_2
Barik, R.: Efficient computation of mayhappeninparallel information for concurrent Java programs. In: LCPC ’06, vol. 4339 LNCS, pp. 152–169, Springer (2006), https://doi.org/10.1007/9783540693307_11
Blanchet, B., Cousot, P., Cousot, R., Feret, J., Mauborgne, L., Miné, A., Monniaux, D., Rival, X.: A static analyzer for large safetycritical software. In: Proceedings of the ACM SIGPLAN 2003 Conference on Programming Language Design and Implementation, p. 196–207, PLDI ’03, Association for Computing Machinery, New York, NY, USA (2003), ISBN 1581136625, https://doi.org/10.1145/781131.781153
Bodík, R., Gupta, R., Soffa, M.L.: Refining data flow information using infeasible paths. SIGSOFT Softw. Eng. Notes 22(6), 361–377 (Nov 1997), ISSN 01635948, https://doi.org/10.1145/267896.267921
Botbol, V., Chailloux, E., Gall, T.L.: Static analysis of communicating processes using symbolic transducers. In: Bouajjani, A., Monniaux, D. (eds.) Verification, Model Checking, and Abstract Interpretation  18th International Conference, VMCAI 2017, Paris, France, January 1517, 2017, Proceedings, Lecture Notes in Computer Science, vol. 10145, pp. 73–90, Springer (2017), https://doi.org/10.1007/9783319522340_5
Chen, L., Liu, J., Miné, A., Kapur, D., Wang, J.: An abstract domain to infer octagonal constraints with absolute value. In: MüllerOlm, M., Seidl, H. (eds.) Static Analysis  21st International Symposium, SAS 2014, Munich, Germany, September 1113, 2014. Proceedings, Lecture Notes in Computer Science, vol. 8723, pp. 101–117, Springer (2014), https://doi.org/10.1007/9783319109367_7
Cortesi, A., Costantini, G., Ferrara, P.: A survey on product operators in abstract interpretation. In: Banerjee, A., Danvy, O., Doh, K., Hatcliff, J. (eds.) Semantics, Abstract Interpretation, and Reasoning about Programs: Essays Dedicated to David A. Schmidt on the Occasion of his Sixtieth Birthday, Manhattan, Kansas, USA, 1920th September 2013, EPTCS, vol. 129, pp. 325–336 (2013), https://doi.org/10.4204/EPTCS.129.19
Cousot, P., Cousot, R.: Systematic design of program analysis frameworks. In: Aho, A.V., Zilles, S.N., Rosen, B.K. (eds.) Conference Record of the Sixth Annual ACM Symposium on Principles of Programming Languages, San Antonio, Texas, USA, January 1979, pp. 269–282, ACM Press (1979), https://doi.org/10.1145/567752.567778
Cousot, P., Cousot, R., Feret, J., Mauborgne, L., Miné, A., Rival, X.: Why does astrée scale up? Form. Methods Syst. Des. 35(3), 229–264 (dec 2009), ISSN 09259856, https://doi.org/10.1007/s1070300900896
Cousot, P., Giacobazzi, R., Ranzato, F.: \(a^2i\): Abstract\(^2\) interpretation. Proc. ACM Program. Lang. 3(POPL) (Jan 2019), https://doi.org/10.1145/3290355
Cousot, P., Halbwachs, N.: Automatic discovery of linear restraints among variables of a program. In: Aho, A.V., Zilles, S.N., Szymanski, T.G. (eds.) Conference Record of the Fifth Annual ACM Symposium on Principles of Programming Languages, Tucson, Arizona, USA, January 1978, pp. 84–96, ACM Press (1978), https://doi.org/10.1145/512760.512770
Di, P., Sui, Y., Ye, D., Xue, J.: Regionbased mayhappeninparallel analysis for C programs. In: ICPP, pp. 889–898, IEEE (2015), ISBN 9781467375870, https://doi.org/10.1109/ICPP.2015.98
Dwyer, M.B., Clarke, L.A.: Data flow analysis for verifying properties of concurrent programs. ACM SIGSOFT Software Engineering Notes 19(5), 62–75 (dec 1994), https://doi.org/10.1145/195274.195295
Farzan, A., Kincaid, Z.: Verification of parameterized concurrent programs by modular reasoning about data and control. In: Proceedings of the 39th Annual ACM SIGPLANSIGACT Symposium on Principles of Programming Languages, p. 297–308, POPL ’12, Association for Computing Machinery, New York, NY, USA (2012), ISBN 9781450310833, https://doi.org/10.1145/2103656.2103693
Feret, J.: Abstract interpretation of mobile systems. J. Log. Algebraic Methods Program. 63(1), 59–130 (2005), https://doi.org/10.1016/j.jlap.2004.01.005
Fulara, J., Durnoga, K., Jakubczyk, K., Schubert, A.: Relational abstract domain of weighted hexagons. Electron. Notes Theor. Comput. Sci. 267(1), 59–72 (2010), https://doi.org/10.1016/j.entcs.2010.09.006
Giacobazzi, R., Ranzato, F.: The reduced relative power operation on abstract domains. Theor. Comput. Sci. 216(12), 159–211 (1999), https://doi.org/10.1016/S03043975(98)001947
Halbwachs, N., Merchat, D., Gonnord, L.: Some ways to reduce the space dimension in polyhedra computations. Formal Methods in System Design 29(1), 79–95 (Jul 2006), ISSN 15728102, https://doi.org/10.1007/s1070300600132
Halbwachs, N., Merchat, D., ParentVigouroux, C.: Cartesian factoring of polyhedra in linear relation analysis. In: Cousot, R. (ed.) Static Analysis, pp. 355–365, Springer Berlin Heidelberg, Berlin, Heidelberg (2003), ISBN 9783540448983
Handjieva, M., Tzolovski, S.: Refining static analyses by tracebased partitioning using control flow. In: Levi, G. (ed.) Static Analysis, pp. 200–214, Springer Berlin Heidelberg, Berlin, Heidelberg (1998), ISBN 9783540497271
Heo, K., Oh, H., Yang, H.: Learning a variableclustering strategy for octagon from labeled data generated by a static analysis. In: Rival, X. (ed.) Static Analysis, pp. 237–256, Springer Berlin Heidelberg, Berlin, Heidelberg (2016), ISBN 9783662534137
Holley, L.H., Rosen, B.K.: Qualified data flow problems. In: Proceedings of the 7th ACM SIGPLANSIGACT Symposium on Principles of Programming Languages, p. 68–82, POPL ’80, Association for Computing Machinery, New York, NY, USA (1980), ISBN 0897910117, https://doi.org/10.1145/567446.567454
Howe, J.M., King, A.: Logahedra: A new weakly relational domain. In: Liu, Z., Ravn, A.P. (eds.) Automated Technology for Verification and Analysis, 7th International Symposium, ATVA 2009, Macao, China, October 1416, 2009. Proceedings, Lecture Notes in Computer Science, vol. 5799, pp. 306–320, Springer (2009), https://doi.org/10.1007/9783642047619_23
Jeannet, B., Miné, A.: Apron: A library of numerical abstract domains for static analysis. In: Bouajjani, A., Maler, O. (eds.) Computer Aided Verification, 21st International Conference, CAV 2009, Grenoble, France, June 26  July 2, 2009. Proceedings, LNCS, vol. 5643, pp. 661–667, Springer (2009), https://doi.org/10.1007/9783642026584_52
Karr, M.: Affine relationships among variables of a program. Acta Informatica 6, 133–151 (1976), https://doi.org/10.1007/BF00268497
Kusano, M., Wang, C.: Flowsensitive composition of threadmodular abstract interpretation. In: Proceedings of the 2016 24th ACM SIGSOFT International Symposium on Foundations of Software Engineering, p. 799–809, FSE 2016, Association for Computing Machinery, New York, NY, USA (2016), ISBN 9781450342186, https://doi.org/10.1145/2950290.2950291
Kusano, M., Wang, C.: Threadmodular static analysis for relaxed memory models. In: Proceedings of the 2017 11th Joint Meeting on Foundations of Software Engineering, p. 337–348, ESEC/FSE 2017, Association for Computing Machinery, New York, NY, USA (2017), ISBN 9781450351058, https://doi.org/10.1145/3106237.3106243
Logozzo, F., Fähndrich, M.: Pentagons: A weakly relational abstract domain for the efficient validation of array accesses. In: Proceedings of the 2008 ACM Symposium on Applied Computing, p. 184–188, SAC ’08, Association for Computing Machinery, New York, NY, USA (2008), ISBN 9781595937537, https://doi.org/10.1145/1363686.1363736
Mauborgne, L., Rival, X.: Trace partitioning in abstract interpretation based static analyzers. In: Sagiv, M. (ed.) Programming Languages and Systems, pp. 5–20, Springer Berlin Heidelberg, Berlin, Heidelberg (2005), ISBN 9783540319870
Miné, A.: A new numerical abstract domain based on differencebound matrices. In: Danvy, O., Filinski, A. (eds.) Programs as Data Objects, Second Symposium, PADO 2001, Aarhus, Denmark, May 2123, 2001, Proceedings, LNCS, vol. 2053, pp. 155–172, Springer (2001), https://doi.org/10.1007/3540449787_10
Miné, A.: The octagon abstract domain. In: WCRE’ 01, p. 310, IEEE Computer Society (2001), https://doi.org/10.1109/WCRE.2001.957836
Miné, A.: The octagon abstract domain. Higher Order Symbol. Comput. 19(1), 31–100 (mar 2006), ISSN 13883690, https://doi.org/10.1007/s1099000686091
Miné, A.: Static analysis of runtime errors in embedded realtime parallel C programs. Logical Methods in Computer Science 8(1), 1–63 (mar 2012), DOI: 10.2168/LMCS8(1:26)2012.
Miné, A.: Static analysis of runtime errors in embedded realtime parallel C programs. Logical Methods in Computer Science 8(1), 1–63 (mar 2012), https://doi.org/10.2168/LMCS8(1:26)2012
Monat, R., Miné, A.: Precise threadmodular abstract interpretation of concurrent programs using relational interference abstractions. In: VMCAI ’17, vol. 10145 LNCS, pp. 386–404, Springer (2017), https://doi.org/10.1007/9783319522340_21
Montagu, B., Jensen, T.: Tracebased controlflow analysis. In: PLDI ’21, p. 482–496, ACM (2021), https://doi.org/10.1145/3453483.3454057
Mukherjee, S., Padon, O., Shoham, S., D’Souza, D., Rinetzky, N.: Threadlocal semantics and its efficient sequential abstractions for racefree programs. In: SAS ’17, vol. LNCS 10422, pp. 253–276, Springer (2017), https://doi.org/10.1007/9783319667065_13
Naumovich, G., Avrunin, G.S., Clarke, L.A.: An efficient algorithm for computing mhp information for concurrent Java programs. In: ESEC/FSE ’99, vol. 1687 LNCS, pp. 338–354, Springer (1999), https://doi.org/10.1007/3540481664_21
Oh, H., Heo, K., Lee, W., Lee, W., Park, D., Kang, J., Yi, K.: Global sparse analysis framework. ACM Trans. Program. Lang. Syst. 36(3) (sep 2014), ISSN 01640925, https://doi.org/10.1145/2590811
Oh, H., Lee, W., Heo, K., Yang, H., Yi, K.: Selective xsensitive analysis guided by impact preanalysis. ACM Trans. Program. Lang. Syst. 38(2) (Dec 2015), ISSN 01640925, https://doi.org/10.1145/2821504
Péron, M., Halbwachs, N.: An abstract domain extending differencebound matrices with disequality constraints. In: Cook, B., Podelski, A. (eds.) Verification, Model Checking, and Abstract Interpretation, 8th International Conference, VMCAI 2007, Nice, France, January 1416, 2007, Proceedings, Lecture Notes in Computer Science, vol. 4349, pp. 268–282, Springer (2007), https://doi.org/10.1007/9783540697381_20
Rival, X., Mauborgne, L.: The trace partitioning abstract domain. ACM Trans. Program. Lang. Syst. 29(5), 26–es (Aug 2007), ISSN 01640925, https://doi.org/10.1145/1275497.1275501
Schwarz, M., Saan, S., Seidl, H., Apinis, K., Erhard, J., Vojdani, V.: Improving threadmodular abstract interpretation. In: SAS ’21, vol. 12913 LNCS, pp. 359–383, Springer (2021), https://doi.org/10.1007/9783030888060_18
Schwarz, M., Saan, S., Seidl, H., Erhard, J., Vojdani, V.: Clustered relational threadmodular abstract interpretation with local traces. CoRR abs/2301.06439 (2023), URL https://arxiv.org/abs/2301.06439
Schwarz, M., Saan, S., Seidl, H., Erhard, J., Vojdani, V.: Clustered Relational ThreadModular Abstract Interpretation with Local Traces (Jan 2023), https://doi.org/10.5281/zenodo.7505428
Seidl, H., Vogler, R.: Three improvements to the topdown solver. Math. Struct. Comput. Sci. 31(9), 1090–1134 (2021), https://doi.org/10.1017/S0960129521000499
Sharma, D., Sharma, S.: Threadmodular analysis of releaseacquire concurrency. In: Dragoi, C., Mukherjee, S., Namjoshi, K.S. (eds.) Static Analysis  28th International Symposium, SAS 2021, Chicago, IL, USA, October 1719, 2021, Proceedings, LNCS, vol. 12913, pp. 384–404, Springer (2021), https://doi.org/10.1007/9783030888060_19
Simon, A., King, A., Howe, J.M.: Two variables per linear inequality as an abstract domain. In: Leuschel, M. (ed.) Logic Based Program Synthesis and Tranformation, 12th International Workshop, LOPSTR 2002, Madrid, Spain, September 1720,2002, Revised Selected Papers, LNCS, vol. 2664, pp. 71–89, Springer (2002), https://doi.org/10.1007/3540450130_7
Singh, G., Püschel, M., Vechev, M.: Fast polyhedra abstract domain. In: Proceedings of the 44th ACM SIGPLAN Symposium on Principles of Programming Languages, p. 46–59, POPL 2017, Association for Computing Machinery, New York, NY, USA (2017), ISBN 9781450346603, https://doi.org/10.1145/3009837.3009885
Singh, G., Püschel, M., Vechev, M.: Fast numerical program analysis with reinforcement learning. In: Chockler, H., Weissenbacher, G. (eds.) Computer Aided Verification, pp. 211–229, Springer International Publishing, Cham (2018), ISBN 9783319961453
Singh, G., Püschel, M., Vechev, M.: A practical construction for decomposing numerical abstract domains. Proc. ACM Program. Lang. 2(POPL) (dec 2018), https://doi.org/10.1145/3158143
Suzanne, T., Miné, A.: Relational threadmodular abstract interpretation under relaxed memory models. In: APLAS ’18, vol. LNCS 11275, pp. 109–128, Springer (dec 2018), https://doi.org/10.1007/9783030027681_6
Vojdani, V., Apinis, K., Rõtov, V., Seidl, H., Vene, V., Vogler, R.: Static Race Detection for Device Drivers: The Goblint Approach. In: ASE ’16, pp. 391–402, ACM (2016), https://doi.org/10.1145/2970276.2970337
Zhou, Q., Li, L., Wang, L., Xue, J., Feng, X.: Mayhappeninparallel analysis with static vector clocks. In: CGO ’18, pp. 228–240, ACM (2018), https://doi.org/10.1145/3168813
Acknowledgements
This work was supported by Deutsche Forschungsgemeinschaft (DFG)  378803395/2428 ConVeY and the Estonian Centre of Excellence in IT (EXCITE), funded by the European Regional Development Fund.
Author information
Authors and Affiliations
Corresponding author
Editor information
Editors and Affiliations
Rights and permissions
Open Access This chapter is licensed under the terms of the Creative Commons Attribution 4.0 International License (http://creativecommons.org/licenses/by/4.0/), which permits use, sharing, adaptation, distribution and reproduction in any medium or format, as long as you give appropriate credit to the original author(s) and the source, provide a link to the Creative Commons license and indicate if changes were made.
The images or other third party material in this chapter are included in the chapter's Creative Commons license, unless indicated otherwise in a credit line to the material. If material is not included in the chapter's Creative Commons license and your intended use is not permitted by statutory regulation or exceeds the permitted use, you will need to obtain permission directly from the copyright holder.
Copyright information
© 2023 The Author(s)
About this paper
Cite this paper
Schwarz, M., Saan, S., Seidl, H., Erhard, J., Vojdani, V. (2023). Clustered Relational ThreadModular Abstract Interpretation with Local Traces. In: Wies, T. (eds) Programming Languages and Systems. ESOP 2023. Lecture Notes in Computer Science, vol 13990. Springer, Cham. https://doi.org/10.1007/9783031300448_2
Download citation
DOI: https://doi.org/10.1007/9783031300448_2
Published:
Publisher Name: Springer, Cham
Print ISBN: 9783031300431
Online ISBN: 9783031300448
eBook Packages: Computer ScienceComputer Science (R0)