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