| /* |
| This file is part of drd, a data race detector. |
| |
| Copyright (C) 2006-2007 Bart Van Assche |
| bart.vanassche@gmail.com |
| |
| This program is free software; you can redistribute it and/or |
| modify it under the terms of the GNU General Public License as |
| published by the Free Software Foundation; either version 2 of the |
| License, or (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program; if not, write to the Free Software |
| Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA |
| 02111-1307, USA. |
| |
| The GNU General Public License is contained in the file COPYING. |
| */ |
| |
| |
| #include "drd_error.h" |
| #include "drd_malloc_wrappers.h" |
| #include "drd_mutex.h" // struct mutex_info |
| #include "drd_suppression.h" // drd_start_suppression() |
| #include "pub_drd_bitmap.h" // LHS_W, ... |
| #include "pub_tool_vki.h" |
| #include "pub_tool_basics.h" |
| #include "pub_tool_libcassert.h" // tl_assert() |
| #include "pub_tool_libcbase.h" // strlen() |
| #include "pub_tool_libcfile.h" // VG_(get_startup_wd)() |
| #include "pub_tool_libcprint.h" // VG_(printf)() |
| #include "pub_tool_machine.h" |
| #include "pub_tool_threadstate.h" // VG_(get_pthread_id)() |
| #include "pub_tool_tooliface.h" // VG_(needs_tool_errors)() |
| |
| |
| typedef enum { |
| ConflictingAccessSupp |
| } DRD_SuppKind; |
| |
| |
| static void make_path_relative(Char* const path) |
| { |
| int offset = 0; |
| Char cwd[512]; |
| |
| if (! VG_(get_startup_wd)(cwd, sizeof(cwd))) |
| tl_assert(False); |
| if (VG_(strncmp)(path + offset, cwd, VG_(strlen)(cwd)) == 0) |
| { |
| offset += VG_(strlen)(cwd); |
| if (path[offset] == '/') |
| { |
| offset++; |
| } |
| } |
| VG_(memmove)(path, path + offset, VG_(strlen)(path + offset) + 1); |
| } |
| |
| |
| /* Describe a data address range [a,a+len[ as good as you can, for error */ |
| /* messages, putting the result in ai. */ |
| void describe_addr(Addr const a, SizeT const len, AddrInfo* const ai) |
| { |
| Addr stack_min, stack_max; |
| SegInfo* sg; |
| |
| /* Perhaps it's on a thread's stack? */ |
| ai->stack_tid = thread_lookup_stackaddr(a, &stack_min, &stack_max); |
| if (ai->stack_tid != DRD_INVALID_THREADID) |
| { |
| ai->akind = eStack; |
| ai->size = len; |
| ai->rwoffset = a - stack_max; |
| tl_assert(a + ai->size <= stack_max); |
| tl_assert(ai->rwoffset < 0); |
| return; |
| } |
| |
| /* Perhaps it's in a mapped segment ? */ |
| sg = VG_(find_seginfo)(a); |
| if (sg) |
| { |
| int i, n; |
| |
| ai->akind = eSegment; |
| ai->seginfo = sg; |
| ai->name[0] = 0; |
| ai->size = 1; |
| ai->rwoffset = 0; |
| |
| n = VG_(seginfo_syms_howmany)(sg); |
| for (i = 0; i < n; i++) |
| { |
| Addr addr; |
| Addr tocptr; |
| UInt size; |
| HChar* name; |
| Char filename[256]; |
| Int linenum; |
| |
| VG_(seginfo_syms_getidx)(sg, i, &addr, &tocptr, &size, &name); |
| if (addr <= a && a < addr + size) |
| { |
| ai->size = size; |
| ai->rwoffset = a - addr; |
| tl_assert(name && name[0]); |
| VG_(snprintf)(ai->name, sizeof(ai->name), "%s", name); |
| if (VG_(get_filename_linenum)(addr, |
| filename, sizeof(filename), |
| 0, 0, 0, |
| &linenum)) |
| { |
| make_path_relative(filename); |
| VG_(snprintf)(ai->descr, sizeof(ai->descr), |
| " in %s:%d", filename, linenum); |
| } |
| else |
| { |
| i = n; |
| } |
| break; |
| } |
| } |
| if (i == n) |
| { |
| Char filename[512]; |
| Char soname[512]; |
| Char sect_kind_name[16]; |
| |
| VG_(seginfo_sect_kind_name)(a, sect_kind_name, |
| sizeof(sect_kind_name)); |
| VG_(strncpy)(filename, VG_(seginfo_filename)(sg), sizeof(filename)); |
| filename[sizeof(filename) - 1] = 0; |
| make_path_relative(filename); |
| VG_(strncpy)(soname, VG_(seginfo_soname)(sg), sizeof(soname)); |
| soname[sizeof(soname) - 1] = 0; |
| make_path_relative(soname); |
| VG_(snprintf)(ai->descr, sizeof(ai->descr), |
| "%s, %s:%s", |
| filename, |
| soname, |
| sect_kind_name); |
| } |
| return; |
| } |
| |
| /* Search for a currently malloc'd block which might bracket it. */ |
| { |
| Addr data; |
| if (drd_heap_addrinfo(a, &data, &ai->size, &ai->lastchange)) |
| { |
| ai->akind = eMallocd; |
| ai->rwoffset = a - data; |
| return; |
| } |
| } |
| |
| /* Clueless ... */ |
| ai->akind = eUnknown; |
| return; |
| } |
| |
| /** |
| * Generate a description string for the data residing at address a. |
| */ |
| Char* describe_addr_text(Addr const a, SizeT const len, AddrInfo* const ai, |
| Char* const buf, UInt const n_buf) |
| { |
| tl_assert(a); |
| tl_assert(ai); |
| tl_assert(buf); |
| |
| describe_addr(a, len, ai); |
| |
| switch (ai->akind) |
| { |
| case eStack: { |
| VG_(snprintf)(buf, n_buf, |
| "stack of %s, offset %d", |
| thread_get_name(ai->stack_tid), ai->rwoffset); |
| break; |
| } |
| case eSegment: { |
| if (ai->name[0]) |
| { |
| VG_(snprintf)(buf, n_buf, |
| "%s (offset %ld, size %ld) in %s", |
| ai->name, ai->rwoffset, ai->size, ai->descr); |
| } |
| else |
| { |
| VG_(snprintf)(buf, n_buf, |
| "%s", |
| ai->descr); |
| } |
| break; |
| } |
| case eMallocd: { |
| VG_(snprintf)(buf, n_buf, "heap"); |
| VG_(snprintf)(buf + VG_(strlen)(buf), n_buf - VG_(strlen)(buf), |
| ", offset %ld in block at 0x%lx of size %ld", |
| ai->rwoffset, a - ai->rwoffset, ai->size); |
| break; |
| } |
| case eUnknown: |
| VG_(snprintf)(buf, n_buf, "unknown"); |
| break; |
| default: |
| tl_assert(0); |
| } |
| return buf; |
| } |
| |
| #ifdef OLD_RACE_DETECTION_ALGORITHM |
| void drd_report_data_race(const DataRaceInfo* const dri) |
| { |
| AddrInfo ai; |
| Char descr[256]; |
| |
| tl_assert(dri); |
| tl_assert(dri->range_begin < dri->range_end); |
| describe_addr_text(dri->range_begin, dri->range_end - dri->range_begin, |
| &ai, descr, sizeof(descr)); |
| VG_(message)(Vg_UserMsg, |
| "0x%08lx sz %ld %c %c (%s)", |
| dri->range_begin, |
| dri->range_end - dri->range_begin, |
| dri->range_access & LHS_W ? 'W' : 'R', |
| dri->range_access & RHS_W ? 'W' : 'R', |
| descr); |
| if (ai.akind == eMallocd && ai.lastchange) |
| { |
| VG_(message)(Vg_UserMsg, "Allocation context:"); |
| VG_(pp_ExeContext)(ai.lastchange); |
| } |
| // Note: for stack and heap variables suppression should be |
| // stopped automatically as soon as the specified memory |
| // range has been freed. |
| tl_assert(dri->range_begin < dri->range_end); |
| drd_start_suppression(dri->range_begin, dri->range_end, "detected race"); |
| } |
| #endif |
| |
| static |
| void drd_report_data_race2(Error* const err, const DataRaceErrInfo* const dri) |
| { |
| AddrInfo ai; |
| Char descr[256]; |
| |
| tl_assert(dri); |
| tl_assert(dri->addr); |
| tl_assert(dri->size > 0); |
| describe_addr_text(dri->addr, dri->size, |
| &ai, descr, sizeof(descr)); |
| VG_(message)(Vg_UserMsg, |
| "Conflicting %s by %s at 0x%08lx size %ld", |
| dri->access_type == eStore ? "store" : "load", |
| thread_get_name(VgThreadIdToDrdThreadId(dri->tid)), |
| dri->addr, |
| dri->size); |
| VG_(pp_ExeContext)(VG_(get_error_where)(err)); |
| VG_(message)(Vg_UserMsg, "Allocation context: %s", descr); |
| if (ai.akind == eMallocd && ai.lastchange) |
| { |
| VG_(pp_ExeContext)(ai.lastchange); |
| } |
| thread_report_conflicting_segments(VgThreadIdToDrdThreadId(dri->tid), |
| dri->addr, dri->size, dri->access_type); |
| } |
| |
| static Bool drd_tool_error_eq(VgRes res, Error* e1, Error* e2) |
| { |
| return False; |
| } |
| |
| static void drd_tool_error_pp(Error* const e) |
| { |
| switch (VG_(get_error_kind)(e)) |
| { |
| case DataRaceErr: { |
| drd_report_data_race2(e, VG_(get_error_extra)(e)); |
| break; |
| } |
| case MutexErr: { |
| MutexErrInfo* p = (MutexErrInfo*)(VG_(get_error_extra)(e)); |
| VG_(message)(Vg_UserMsg, |
| "%s / mutex 0x%lx (recursion count %d, owner %d)", |
| VG_(get_error_string)(e), |
| p->mutex, |
| p->recursion_count, |
| p->owner); |
| VG_(pp_ExeContext)(VG_(get_error_where)(e)); |
| break; |
| } |
| case CondRaceErr: { |
| CondRaceErrInfo* cei = (CondRaceErrInfo*)(VG_(get_error_extra)(e)); |
| VG_(message)(Vg_UserMsg, |
| "Race condition: condition variable 0x%lx has been signalled" |
| " but the associated mutex 0x%lx is not locked by the" |
| " signalling thread", |
| cei->cond, cei->mutex); |
| VG_(pp_ExeContext)(VG_(get_error_where)(e)); |
| break; |
| } |
| case CondErr: { |
| CondErrInfo* cdei =(CondErrInfo*)(VG_(get_error_extra)(e)); |
| VG_(message)(Vg_UserMsg, |
| "cond 0x%lx: %s", |
| cdei->cond, |
| VG_(get_error_string)(e)); |
| VG_(pp_ExeContext)(VG_(get_error_where)(e)); |
| break; |
| } |
| default: |
| VG_(message)(Vg_UserMsg, |
| "%s", |
| VG_(get_error_string)(e)); |
| VG_(pp_ExeContext)(VG_(get_error_where)(e)); |
| break; |
| } |
| } |
| |
| static UInt drd_tool_error_update_extra(Error* e) |
| { |
| switch (VG_(get_error_kind)(e)) |
| { |
| case DataRaceErr: |
| return sizeof(DataRaceErrInfo); |
| case MutexErr: |
| return sizeof(MutexErrInfo); |
| case CondRaceErr: |
| return sizeof(CondRaceErrInfo); |
| case CondErr: |
| return sizeof(CondErrInfo); |
| default: |
| tl_assert(False); |
| break; |
| } |
| } |
| |
| static Bool drd_tool_error_recog(Char* const name, Supp* const supp) |
| { |
| SuppKind skind; |
| |
| if (VG_(strcmp)(name, "ConflictingAccess") == 0) |
| skind = ConflictingAccessSupp; |
| else |
| return False; |
| |
| VG_(set_supp_kind)(supp, skind); |
| return True; |
| } |
| |
| static Bool drd_tool_error_read_extra(Int fd, Char* buf, Int nBuf, Supp* supp) |
| { |
| return True; |
| } |
| |
| static Bool drd_tool_error_matches(Error* const e, Supp* const supp) |
| { |
| switch (VG_(get_supp_kind)(supp)) |
| { |
| } |
| return True; |
| } |
| |
| static Char* drd_tool_error_name(Error* e) |
| { |
| switch (VG_(get_error_kind)(e)) |
| { |
| case DataRaceErr: return "ConflictingAccess"; |
| case MutexErr: return "MutexErr"; |
| case CondRaceErr: return "CondRaceErr"; |
| default: |
| tl_assert(0); |
| } |
| return 0; |
| } |
| |
| static void drd_tool_error_print_extra(Error* e) |
| { |
| switch (VG_(get_error_kind)(e)) |
| { |
| // VG_(printf)(" %s\n", VG_(get_error_string)(err)); |
| } |
| } |
| |
| void drd_register_error_handlers(void) |
| { |
| // Tool error reporting. |
| VG_(needs_tool_errors)(drd_tool_error_eq, |
| drd_tool_error_pp, |
| True, |
| drd_tool_error_update_extra, |
| drd_tool_error_recog, |
| drd_tool_error_read_extra, |
| drd_tool_error_matches, |
| drd_tool_error_name, |
| drd_tool_error_print_extra); |
| } |
| |
| /* |
| * Local variables: |
| * c-basic-offset: 3 |
| * End: |
| */ |