drd_load_store.c revision 86562bd89ac23ce795d19c71fabcb9d1c8f956d3
1/*
2  This file is part of drd, a thread error detector.
3
4  Copyright (C) 2006-2009 Bart Van Assche <bart.vanassche@gmail.com>.
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_bitmap.h"
26#include "drd_thread_bitmap.h"
27#include "drd_vc.h"            /* DRD_(vc_snprint)() */
28
29/* Include several source files here in order to allow the compiler to */
30/* do more inlining.                                                   */
31#include "drd_bitmap.c"
32#include "drd_load_store.h"
33#include "drd_segment.c"
34#include "drd_thread.c"
35#include "drd_vc.c"
36#include "libvex_guest_offsets.h"
37
38
39/* STACK_POINTER_OFFSET: VEX register offset for the stack pointer register. */
40#if defined(VGA_x86)
41#define STACK_POINTER_OFFSET OFFSET_x86_ESP
42#elif defined(VGA_amd64)
43#define STACK_POINTER_OFFSET OFFSET_amd64_RSP
44#elif defined(VGA_ppc32)
45#define STACK_POINTER_OFFSET ((OFFSET_ppc32_GPR0 + OFFSET_ppc32_GPR2) / 2)
46#elif defined(VGA_ppc64)
47#define STACK_POINTER_OFFSET ((OFFSET_ppc64_GPR0 + OFFSET_ppc64_GPR2) / 2)
48#else
49#error Unknown architecture.
50#endif
51
52
53/* Local variables. */
54
55static Bool DRD_(s_check_stack_accesses) = False;
56
57
58/* Function definitions. */
59
60Bool DRD_(get_check_stack_accesses)()
61{
62  return DRD_(s_check_stack_accesses);
63}
64
65void DRD_(set_check_stack_accesses)(const Bool c)
66{
67  tl_assert(c == False || c == True);
68  DRD_(s_check_stack_accesses) = c;
69}
70
71void DRD_(trace_mem_access)(const Addr addr, const SizeT size,
72                          const BmAccessTypeT access_type)
73{
74  if (DRD_(is_any_traced)(addr, addr + size))
75  {
76    char vc[80];
77    DRD_(vc_snprint)(vc, sizeof(vc),
78                     DRD_(thread_get_vc)(DRD_(thread_get_running_tid)()));
79    VG_(message)(Vg_UserMsg,
80                 "%s 0x%lx size %ld (vg %d / drd %d / vc %s)",
81                 access_type == eLoad
82                 ? "load "
83                 : access_type == eStore
84                 ? "store"
85                 : access_type == eStart
86                 ? "start"
87                 : access_type == eEnd
88                 ? "end  "
89                 : "????",
90                 addr,
91                 size,
92                 VG_(get_running_tid)(),
93                 DRD_(thread_get_running_tid)(),
94                 vc);
95    VG_(get_and_pp_StackTrace)(VG_(get_running_tid)(),
96                               VG_(clo_backtrace_size));
97    tl_assert(DRD_(DrdThreadIdToVgThreadId)(DRD_(thread_get_running_tid)())
98              == VG_(get_running_tid)());
99  }
100}
101
102static VG_REGPARM(2) void drd_trace_mem_load(const Addr addr, const SizeT size)
103{
104  return DRD_(trace_mem_access)(addr, size, eLoad);
105}
106
107static VG_REGPARM(2) void drd_trace_mem_store(const Addr addr,const SizeT size)
108{
109  return DRD_(trace_mem_access)(addr, size, eStore);
110}
111
112static void drd_report_race(const Addr addr, const SizeT size,
113                            const BmAccessTypeT access_type)
114{
115  DataRaceErrInfo drei;
116
117  drei.tid  = DRD_(thread_get_running_tid)();
118  drei.addr = addr;
119  drei.size = size;
120  drei.access_type = access_type;
121  VG_(maybe_record_error)(VG_(get_running_tid)(),
122                          DataRaceErr,
123                          VG_(get_IP)(VG_(get_running_tid)()),
124                          "Conflicting accesses",
125                          &drei);
126}
127
128VG_REGPARM(2) void DRD_(trace_load)(Addr addr, SizeT size)
129{
130#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
131  /* The assert below has been commented out because of performance reasons.*/
132  tl_assert(thread_get_running_tid()
133            == VgThreadIdToDrdThreadId(VG_(get_running_tid())));
134#endif
135
136  if (DRD_(running_thread_is_recording)()
137      && (DRD_(s_check_stack_accesses)
138          || ! DRD_(thread_address_on_stack)(addr))
139      && bm_access_load_triggers_conflict(addr, addr + size)
140      && ! DRD_(is_suppressed)(addr, addr + size))
141  {
142    drd_report_race(addr, size, eLoad);
143  }
144}
145
146static VG_REGPARM(1) void drd_trace_load_1(Addr addr)
147{
148  if (DRD_(running_thread_is_recording)()
149      && (DRD_(s_check_stack_accesses)
150          || ! DRD_(thread_address_on_stack)(addr))
151      && bm_access_load_1_triggers_conflict(addr)
152      && ! DRD_(is_suppressed)(addr, addr + 1))
153  {
154    drd_report_race(addr, 1, eLoad);
155  }
156}
157
158static VG_REGPARM(1) void drd_trace_load_2(Addr addr)
159{
160  if (DRD_(running_thread_is_recording)()
161      && (DRD_(s_check_stack_accesses)
162          || ! DRD_(thread_address_on_stack)(addr))
163      && bm_access_load_2_triggers_conflict(addr)
164      && ! DRD_(is_suppressed)(addr, addr + 2))
165  {
166    drd_report_race(addr, 2, eLoad);
167  }
168}
169
170static VG_REGPARM(1) void drd_trace_load_4(Addr addr)
171{
172  if (DRD_(running_thread_is_recording)()
173      && (DRD_(s_check_stack_accesses)
174          || ! DRD_(thread_address_on_stack)(addr))
175      && bm_access_load_4_triggers_conflict(addr)
176      && ! DRD_(is_suppressed)(addr, addr + 4))
177  {
178    drd_report_race(addr, 4, eLoad);
179  }
180}
181
182static VG_REGPARM(1) void drd_trace_load_8(Addr addr)
183{
184  if (DRD_(running_thread_is_recording)()
185      && (DRD_(s_check_stack_accesses)
186          || ! DRD_(thread_address_on_stack)(addr))
187      && bm_access_load_8_triggers_conflict(addr)
188      && ! DRD_(is_suppressed)(addr, addr + 8))
189  {
190    drd_report_race(addr, 8, eLoad);
191  }
192}
193
194VG_REGPARM(2) void DRD_(trace_store)(Addr addr, SizeT size)
195{
196#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
197  /* The assert below has been commented out because of performance reasons.*/
198  tl_assert(thread_get_running_tid()
199            == VgThreadIdToDrdThreadId(VG_(get_running_tid())));
200#endif
201
202  if (DRD_(running_thread_is_recording)()
203      && (DRD_(s_check_stack_accesses)
204          || ! DRD_(thread_address_on_stack)(addr))
205      && bm_access_store_triggers_conflict(addr, addr + size)
206      && ! DRD_(is_suppressed)(addr, addr + size))
207  {
208    drd_report_race(addr, size, eStore);
209  }
210}
211
212static VG_REGPARM(1) void drd_trace_store_1(Addr addr)
213{
214  if (DRD_(running_thread_is_recording)()
215      && (DRD_(s_check_stack_accesses)
216          || ! DRD_(thread_address_on_stack)(addr))
217      && bm_access_store_1_triggers_conflict(addr)
218      && ! DRD_(is_suppressed)(addr, addr + 1))
219  {
220    drd_report_race(addr, 1, eStore);
221  }
222}
223
224static VG_REGPARM(1) void drd_trace_store_2(Addr addr)
225{
226  if (DRD_(running_thread_is_recording)()
227      && (DRD_(s_check_stack_accesses)
228          || ! DRD_(thread_address_on_stack)(addr))
229      && bm_access_store_2_triggers_conflict(addr)
230      && ! DRD_(is_suppressed)(addr, addr + 2))
231  {
232    drd_report_race(addr, 2, eStore);
233  }
234}
235
236static VG_REGPARM(1) void drd_trace_store_4(Addr addr)
237{
238  if (DRD_(running_thread_is_recording)()
239      && (DRD_(s_check_stack_accesses)
240          || ! DRD_(thread_address_on_stack)(addr))
241      && bm_access_store_4_triggers_conflict(addr)
242      && ! DRD_(is_suppressed)(addr, addr + 4))
243  {
244    drd_report_race(addr, 4, eStore);
245  }
246}
247
248static VG_REGPARM(1) void drd_trace_store_8(Addr addr)
249{
250  if (DRD_(running_thread_is_recording)()
251      && (DRD_(s_check_stack_accesses)
252          || ! DRD_(thread_address_on_stack)(addr))
253      && bm_access_store_8_triggers_conflict(addr)
254      && ! DRD_(is_suppressed)(addr, addr + 8))
255  {
256    drd_report_race(addr, 8, eStore);
257  }
258}
259
260/**
261 * Return true if and only if addr_expr matches the pattern (SP) or
262 * <offset>(SP).
263 */
264static Bool is_stack_access(IRSB* const bb, IRExpr* const addr_expr)
265{
266  Bool result = False;
267
268  if (addr_expr->tag == Iex_RdTmp)
269  {
270    int i;
271    for (i = 0; i < bb->stmts_size; i++)
272    {
273      if (bb->stmts[i]
274          && bb->stmts[i]->tag == Ist_WrTmp
275          && bb->stmts[i]->Ist.WrTmp.tmp == addr_expr->Iex.RdTmp.tmp)
276      {
277        IRExpr* e = bb->stmts[i]->Ist.WrTmp.data;
278        if (e->tag == Iex_Get && e->Iex.Get.offset == STACK_POINTER_OFFSET)
279        {
280          result = True;
281        }
282
283        //ppIRExpr(e);
284        //VG_(printf)(" (%s)\n", result ? "True" : "False");
285        break;
286      }
287    }
288  }
289  return result;
290}
291
292static void instrument_load(IRSB* const bb,
293                            IRExpr* const addr_expr,
294                            const HWord size)
295{
296  IRExpr* size_expr;
297  IRExpr** argv;
298  IRDirty* di;
299
300  if (UNLIKELY(DRD_(any_address_is_traced)()))
301  {
302    addStmtToIRSB(bb,
303		  IRStmt_Dirty(
304		    unsafeIRDirty_0_N(/*regparms*/2,
305				      "drd_trace_load",
306				      VG_(fnptr_to_fnentry)
307				      (drd_trace_mem_load),
308				      mkIRExprVec_2(addr_expr,
309						    mkIRExpr_HWord(size)))));
310  }
311
312  if (! DRD_(s_check_stack_accesses) && is_stack_access(bb, addr_expr))
313    return;
314
315  switch (size)
316  {
317  case 1:
318    argv = mkIRExprVec_1(addr_expr);
319    di = unsafeIRDirty_0_N(/*regparms*/1,
320                           "drd_trace_load_1",
321                           VG_(fnptr_to_fnentry)(drd_trace_load_1),
322                           argv);
323    break;
324  case 2:
325    argv = mkIRExprVec_1(addr_expr);
326    di = unsafeIRDirty_0_N(/*regparms*/1,
327                           "drd_trace_load_2",
328                           VG_(fnptr_to_fnentry)(drd_trace_load_2),
329                           argv);
330    break;
331  case 4:
332    argv = mkIRExprVec_1(addr_expr);
333    di = unsafeIRDirty_0_N(/*regparms*/1,
334                           "drd_trace_load_4",
335                           VG_(fnptr_to_fnentry)(drd_trace_load_4),
336                           argv);
337    break;
338  case 8:
339    argv = mkIRExprVec_1(addr_expr);
340    di = unsafeIRDirty_0_N(/*regparms*/1,
341                           "drd_trace_load_8",
342                           VG_(fnptr_to_fnentry)(drd_trace_load_8),
343                           argv);
344    break;
345  default:
346    size_expr = mkIRExpr_HWord(size);
347    argv = mkIRExprVec_2(addr_expr, size_expr);
348    di = unsafeIRDirty_0_N(/*regparms*/2,
349                           "drd_trace_load",
350                           VG_(fnptr_to_fnentry)(DRD_(trace_load)),
351                           argv);
352    break;
353  }
354  addStmtToIRSB(bb, IRStmt_Dirty(di));
355}
356
357static void instrument_store(IRSB* const bb,
358                             IRExpr* const addr_expr,
359                             const HWord size)
360{
361  IRExpr* size_expr;
362  IRExpr** argv;
363  IRDirty* di;
364
365  if (UNLIKELY(DRD_(any_address_is_traced)()))
366  {
367    addStmtToIRSB(bb,
368		  IRStmt_Dirty(
369		    unsafeIRDirty_0_N(/*regparms*/2,
370				      "drd_trace_store",
371				      VG_(fnptr_to_fnentry)
372				      (drd_trace_mem_store),
373				      mkIRExprVec_2(addr_expr,
374						    mkIRExpr_HWord(size)))));
375  }
376
377  if (! DRD_(s_check_stack_accesses) && is_stack_access(bb, addr_expr))
378    return;
379
380  switch (size)
381  {
382  case 1:
383    argv = mkIRExprVec_1(addr_expr);
384    di = unsafeIRDirty_0_N(/*regparms*/1,
385                           "drd_trace_store_1",
386                           VG_(fnptr_to_fnentry)(drd_trace_store_1),
387                           argv);
388    break;
389  case 2:
390    argv = mkIRExprVec_1(addr_expr);
391    di = unsafeIRDirty_0_N(/*regparms*/1,
392                           "drd_trace_store_2",
393                           VG_(fnptr_to_fnentry)(drd_trace_store_2),
394                           argv);
395    break;
396  case 4:
397    argv = mkIRExprVec_1(addr_expr);
398    di = unsafeIRDirty_0_N(/*regparms*/1,
399                           "drd_trace_store_4",
400                           VG_(fnptr_to_fnentry)(drd_trace_store_4),
401                           argv);
402    break;
403  case 8:
404    argv = mkIRExprVec_1(addr_expr);
405    di = unsafeIRDirty_0_N(/*regparms*/1,
406                           "drd_trace_store_8",
407                           VG_(fnptr_to_fnentry)(drd_trace_store_8),
408                           argv);
409    break;
410  default:
411    size_expr = mkIRExpr_HWord(size);
412    argv = mkIRExprVec_2(addr_expr, size_expr);
413    di = unsafeIRDirty_0_N(/*regparms*/2,
414                           "drd_trace_store",
415                           VG_(fnptr_to_fnentry)(DRD_(trace_store)),
416                           argv);
417    break;
418  }
419  addStmtToIRSB(bb, IRStmt_Dirty(di));
420}
421
422IRSB* DRD_(instrument)(VgCallbackClosure* const closure,
423                     IRSB* const bb_in,
424                     VexGuestLayout* const layout,
425                     VexGuestExtents* const vge,
426                     IRType const gWordTy,
427                     IRType const hWordTy)
428{
429  IRDirty* di;
430  Int      i;
431  IRSB*    bb;
432  IRExpr** argv;
433  Bool     instrument = True;
434  Bool     bus_locked = False;
435
436  /* Set up BB */
437  bb           = emptyIRSB();
438  bb->tyenv    = deepCopyIRTypeEnv(bb_in->tyenv);
439  bb->next     = deepCopyIRExpr(bb_in->next);
440  bb->jumpkind = bb_in->jumpkind;
441
442  for (i = 0; i < bb_in->stmts_used; i++)
443  {
444    IRStmt* const st = bb_in->stmts[i];
445    tl_assert(st);
446    if (st->tag == Ist_NoOp)
447      continue;
448
449    switch (st->tag)
450    {
451    /* Note: the code for not instrumenting the code in .plt          */
452    /* sections is only necessary on CentOS 3.0 x86 (kernel 2.4.21    */
453    /* + glibc 2.3.2 + NPTL 0.60 + binutils 2.14.90.0.4).             */
454    /* This is because on this platform dynamic library symbols are   */
455    /* relocated in another way than by later binutils versions. The  */
456    /* linker e.g. does not generate .got.plt sections on CentOS 3.0. */
457    case Ist_IMark:
458      instrument = VG_(seginfo_sect_kind)(NULL, 0, st->Ist.IMark.addr)
459        != Vg_SectPLT;
460      addStmtToIRSB(bb, st);
461      break;
462
463    case Ist_MBE:
464      switch (st->Ist.MBE.event)
465      {
466      case Imbe_Fence:
467        break; /* not interesting */
468      case Imbe_BusLock:
469      case Imbe_SnoopedStoreBegin:
470        tl_assert(! bus_locked);
471        bus_locked = True;
472        break;
473      case Imbe_BusUnlock:
474      case Imbe_SnoopedStoreEnd:
475        tl_assert(bus_locked);
476        bus_locked = False;
477        break;
478      default:
479        tl_assert(0);
480      }
481      addStmtToIRSB(bb, st);
482      break;
483
484    case Ist_Store:
485      if (instrument && ! bus_locked)
486      {
487        instrument_store(bb,
488                         st->Ist.Store.addr,
489                         sizeofIRType(typeOfIRExpr(bb->tyenv,
490                                                   st->Ist.Store.data)));
491      }
492      addStmtToIRSB(bb, st);
493      break;
494
495    case Ist_WrTmp:
496      if (instrument)
497      {
498        const IRExpr* const data = st->Ist.WrTmp.data;
499        if (data->tag == Iex_Load)
500        {
501          instrument_load(bb,
502                          data->Iex.Load.addr,
503                          sizeofIRType(data->Iex.Load.ty));
504        }
505      }
506      addStmtToIRSB(bb, st);
507      break;
508
509    case Ist_Dirty:
510      if (instrument)
511      {
512        IRDirty* d = st->Ist.Dirty.details;
513        IREffect const mFx = d->mFx;
514        switch (mFx) {
515        case Ifx_None:
516          break;
517        case Ifx_Read:
518        case Ifx_Write:
519        case Ifx_Modify:
520          tl_assert(d->mAddr);
521          tl_assert(d->mSize > 0);
522          argv = mkIRExprVec_2(d->mAddr, mkIRExpr_HWord(d->mSize));
523          if (mFx == Ifx_Read || mFx == Ifx_Modify) {
524            di = unsafeIRDirty_0_N(
525                                   /*regparms*/2,
526                                   "drd_trace_load",
527                                   VG_(fnptr_to_fnentry)(DRD_(trace_load)),
528                                   argv);
529            addStmtToIRSB(bb, IRStmt_Dirty(di));
530          }
531          if ((mFx == Ifx_Write || mFx == Ifx_Modify)
532              && ! bus_locked)
533          {
534            di = unsafeIRDirty_0_N(
535                                   /*regparms*/2,
536                                   "drd_trace_store",
537                                   VG_(fnptr_to_fnentry)(DRD_(trace_store)),
538                                   argv);
539            addStmtToIRSB(bb, IRStmt_Dirty(di));
540          }
541          break;
542        default:
543          tl_assert(0);
544        }
545      }
546      addStmtToIRSB(bb, st);
547      break;
548
549    default:
550      addStmtToIRSB(bb, st);
551      break;
552    }
553  }
554
555  tl_assert(! bus_locked);
556
557  return bb;
558}
559
560