1 /*
2   This file is part of drd, a thread error detector.
3 
4   Copyright (C) 2006-2015 Bart Van Assche <bvanassche@acm.org>.
5 
6   This program is free software; you can redistribute it and/or
7   modify it under the terms of the GNU General Public License as
8   published by the Free Software Foundation; either version 2 of the
9   License, or (at your option) any later version.
10 
11   This program is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   General Public License for more details.
15 
16   You should have received a copy of the GNU General Public License
17   along with this program; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
19   02111-1307, USA.
20 
21   The GNU General Public License is contained in the file COPYING.
22 */
23 
24 
25 #include "drd_clientobj.h"
26 #include "drd_hb.h"
27 #include "drd_error.h"
28 #include "pub_tool_errormgr.h"    /* VG_(maybe_record_error)() */
29 #include "pub_tool_libcassert.h"  /* tl_assert()               */
30 #include "pub_tool_libcprint.h"   /* VG_(printf)()             */
31 #include "pub_tool_machine.h"     /* VG_(get_IP)()             */
32 #include "pub_tool_mallocfree.h"  /* VG_(malloc)(), VG_(free)()*/
33 #include "pub_tool_threadstate.h" /* VG_(get_running_tid)()    */
34 
35 
36 /* Type definitions. */
37 
38 /** Per-thread hb information. */
39 struct hb_thread_info
40 {
41    UWord       tid; // A DrdThreadId declared as UWord because
42                     // this member variable is the key of an OSet.
43    Segment*    sg;  // Segment created before most recent
44                     // ANNOTATE_HAPPENS_BEFORE().
45 };
46 
47 
48 /* Local functions. */
49 
50 static void DRD_(hb_cleanup)(struct hb_info* p);
51 
52 
53 /* Local variables. */
54 
55 static Bool DRD_(s_trace_hb);
56 
57 
58 /* Function definitions. */
59 
DRD_(hb_set_trace)60 void DRD_(hb_set_trace)(const Bool trace_hb)
61 {
62    DRD_(s_trace_hb) = trace_hb;
63 }
64 
65 /**
66  * Initialize the structure *p with the specified thread ID.
67  */
68 static
DRD_(hb_thread_initialize)69 void DRD_(hb_thread_initialize)(struct hb_thread_info* const p,
70                                   const DrdThreadId tid)
71 {
72    p->tid  = tid;
73    p->sg   = 0;
74 }
75 
76 /**
77  * Deallocate the memory that is owned by members of struct hb_thread_info.
78  */
DRD_(hb_thread_destroy)79 static void DRD_(hb_thread_destroy)(struct hb_thread_info* const p)
80 {
81    tl_assert(p);
82    DRD_(sg_put)(p->sg);
83 }
84 
85 static
DRD_(hb_initialize)86 void DRD_(hb_initialize)(struct hb_info* const p, const Addr hb)
87 {
88    tl_assert(hb != 0);
89    tl_assert(p->a1   == hb);
90    tl_assert(p->type == ClientHbvar);
91 
92    p->cleanup       = (void(*)(DrdClientobj*))(DRD_(hb_cleanup));
93    p->delete_thread = 0;
94    p->oset          = VG_(OSetGen_Create)(0, 0, VG_(malloc), "drd.hb",
95                                           VG_(free));
96 }
97 
98 /**
99  * Free the memory that was allocated by hb_initialize(). Called by
100  * DRD_(clientobj_remove)().
101  */
DRD_(hb_cleanup)102 static void DRD_(hb_cleanup)(struct hb_info* p)
103 {
104    struct hb_thread_info* r;
105 
106    tl_assert(p);
107    VG_(OSetGen_ResetIter)(p->oset);
108    for ( ; (r = VG_(OSetGen_Next)(p->oset)) != 0; )
109       DRD_(hb_thread_destroy)(r);
110    VG_(OSetGen_Destroy)(p->oset);
111 }
112 
113 /**
114  * Report that the synchronization object at address 'addr' is of the
115  * wrong type.
116  */
wrong_type(const Addr addr)117 static void wrong_type(const Addr addr)
118 {
119    GenericErrInfo gei = {
120       .tid  = DRD_(thread_get_running_tid)(),
121       .addr = addr,
122    };
123    VG_(maybe_record_error)(VG_(get_running_tid)(),
124                            GenericErr,
125                            VG_(get_IP)(VG_(get_running_tid)()),
126                            "wrong type of synchronization object",
127                            &gei);
128 }
129 
DRD_(hb_get_or_allocate)130 struct hb_info* DRD_(hb_get_or_allocate)(const Addr hb)
131 {
132    struct hb_info *p;
133 
134    tl_assert(offsetof(DrdClientobj, hb) == 0);
135    p = &(DRD_(clientobj_get)(hb, ClientHbvar)->hb);
136    if (p)
137       return p;
138 
139    if (DRD_(clientobj_present)(hb, hb + 1))
140    {
141       wrong_type(hb);
142       return 0;
143    }
144 
145    p = &(DRD_(clientobj_add)(hb, ClientHbvar)->hb);
146    DRD_(hb_initialize)(p, hb);
147    return p;
148 }
149 
DRD_(hb_get)150 struct hb_info* DRD_(hb_get)(const Addr hb)
151 {
152    tl_assert(offsetof(DrdClientobj, hb) == 0);
153    return &(DRD_(clientobj_get)(hb, ClientHbvar)->hb);
154 }
155 
156 /** Called because of a happens-before annotation. */
DRD_(hb_happens_before)157 void DRD_(hb_happens_before)(const DrdThreadId tid, Addr const hb)
158 {
159    const ThreadId vg_tid = VG_(get_running_tid)();
160    const DrdThreadId drd_tid = DRD_(VgThreadIdToDrdThreadId)(vg_tid);
161    const UWord word_tid = tid;
162    struct hb_info* p;
163    struct hb_thread_info* q;
164 
165    p = DRD_(hb_get_or_allocate)(hb);
166    if (DRD_(s_trace_hb))
167       DRD_(trace_msg)("[%u] happens_before 0x%lx",
168                       DRD_(thread_get_running_tid)(), hb);
169 
170    if (!p)
171       return;
172 
173    /* Allocate the per-thread data structure if necessary. */
174    q = VG_(OSetGen_Lookup)(p->oset, &word_tid);
175    if (!q)
176    {
177       q = VG_(OSetGen_AllocNode)(p->oset, sizeof(*q));
178       DRD_(hb_thread_initialize)(q, tid);
179       VG_(OSetGen_Insert)(p->oset, q);
180       tl_assert(VG_(OSetGen_Lookup)(p->oset, &word_tid) == q);
181    }
182 
183    /*
184     * Store a pointer to the latest segment of the current thread in the
185     * per-thread data structure.
186     */
187    DRD_(thread_get_latest_segment)(&q->sg, tid);
188    DRD_(thread_new_segment)(drd_tid);
189 }
190 
191 /** Called because of a happens-after annotation. */
DRD_(hb_happens_after)192 void DRD_(hb_happens_after)(const DrdThreadId tid, const Addr hb)
193 {
194    struct hb_info* p;
195    struct hb_thread_info* q;
196    VectorClock old_vc;
197 
198    p = DRD_(hb_get_or_allocate)(hb);
199 
200    if (DRD_(s_trace_hb))
201       DRD_(trace_msg)("[%u] happens_after  0x%lx",
202                       DRD_(thread_get_running_tid)(), hb);
203 
204    if (!p)
205       return;
206 
207    DRD_(thread_new_segment)(tid);
208 
209    /*
210     * Combine all vector clocks that were stored because of happens-before
211     * annotations with the vector clock of the current thread.
212     */
213    DRD_(vc_copy)(&old_vc, DRD_(thread_get_vc)(tid));
214    VG_(OSetGen_ResetIter)(p->oset);
215    for ( ; (q = VG_(OSetGen_Next)(p->oset)) != 0; )
216    {
217       if (q->tid != tid)
218       {
219          tl_assert(q->sg);
220          DRD_(vc_combine)(DRD_(thread_get_vc)(tid), &q->sg->vc);
221       }
222    }
223    DRD_(thread_update_conflict_set)(tid, &old_vc);
224    DRD_(vc_cleanup)(&old_vc);
225 }
226 
227 /** Called because of a happens-done annotation. */
DRD_(hb_happens_done)228 void DRD_(hb_happens_done)(const DrdThreadId tid, const Addr hb)
229 {
230    struct hb_info* p;
231 
232    if (DRD_(s_trace_hb))
233       DRD_(trace_msg)("[%u] happens_done  0x%lx",
234                       DRD_(thread_get_running_tid)(), hb);
235 
236    p = DRD_(hb_get)(hb);
237    if (!p)
238    {
239       GenericErrInfo gei = {
240 	 .tid = DRD_(thread_get_running_tid)(),
241 	 .addr = hb,
242       };
243       VG_(maybe_record_error)(VG_(get_running_tid)(),
244                               GenericErr,
245                               VG_(get_IP)(VG_(get_running_tid)()),
246                               "missing happens-before annotation",
247                               &gei);
248       return;
249    }
250 
251    DRD_(clientobj_remove)(p->a1, ClientHbvar);
252 }
253