普通文本  |  769行  |  19.62 KB

/* Copyright (c) 2008-2010, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// This file is part of ThreadSanitizer, a dynamic data race detector.
// Author: Konstantin Serebryany.
// Author: Timur Iskhodzhanov.
//
// See ts_util.h for mode details.

#include "common_util.h"
#include "thread_sanitizer.h"
#include "ts_stats.h"
#include "ts_lock.h"
#include <stdarg.h>

FLAGS *G_flags = NULL;

#if defined(_MSC_VER)

#pragma comment(lib, "winmm.lib")

# ifdef TS_PIN
#  include "pin.H"
# endif
namespace WINDOWS
{
// This is the way of including winows.h recommended by PIN docs.
#include<Windows.h>
}
int getpid() { return WINDOWS::GetCurrentProcessId(); }
#endif

#if defined(TS_VALGRIND)
size_t TimeInMilliSeconds() {
  return VG_(read_millisecond_timer)();
}
#else
// TODO(kcc): implement this.
size_t TimeInMilliSeconds() {
#ifdef __GNUC__
  return time(0) * 1000;
#else
  return WINDOWS::timeGetTime();
#endif
}
#endif

Stats *G_stats;

#ifndef TS_LLVM
bool GetNameAndOffsetOfGlobalObject(uintptr_t addr,
                                    string *name, uintptr_t *offset) {
# ifdef TS_VALGRIND
    const int kBufLen = 1023;
    char buff[kBufLen+1];
    PtrdiffT off;
    if (VG_(get_datasym_and_offset)(addr, reinterpret_cast<Char*>(buff),
                                    kBufLen, &off)) {
      *name = buff;
      *offset = off;
      return true;
    }
    return false;
# else
  return false;
# endif  // TS_VALGRIND
}
#endif  // TS_LLVM


#ifndef TS_VALGRIND
void GetThreadStack(int tid, uintptr_t *min_addr, uintptr_t *max_addr) {
  *min_addr = 0xfffa;
  *max_addr = 0xfffb;
}
#endif

static int n_errs_found;

void SetNumberOfFoundErrors(int n_errs) {
  n_errs_found = n_errs;
}

int GetNumberOfFoundErrors() {
  return n_errs_found;
}


#if !defined(TS_VALGRIND) && !defined(TS_LLVM)
FILE *G_out = stderr;
#endif

#ifdef TS_LLVM
FILE *G_out;
#endif

static string RemoveUnsupportedFormat(const char *str) {
#ifdef _MSC_VER
  // replace "%'" with "%"
  string res;
  size_t n = strlen(str);
  if (n == 0) {
    return "";
  }
  res.reserve(n);
  res.push_back(str[0]);
  for (size_t i = 1; i < n; i++) {
    if (str[i] == '\'' && *res.rbegin() == '%') continue;
    res.push_back(str[i]);
  }
  return res;
#else
  return str;
#endif
}

void Printf(const char *format, ...) {
#ifdef TS_VALGRIND
  va_list args;
  va_start(args, format);
  VG_(vprintf)(format, args);
  va_end(args);
#else
  va_list args;
  va_start(args, format);
  vfprintf(G_out, RemoveUnsupportedFormat(format).c_str(), args);
  fflush(G_out);
  va_end(args);
#endif
}

// Like Print(), but prepend each line with ==XXXXX==,
// where XXXXX is the pid.
void Report(const char *format, ...) {
  int buff_size = 1024*16;
  char *buff = new char[buff_size];
  CHECK(buff);
  DCHECK(G_flags);

  va_list args;

  while (1) {
    va_start(args, format);
    int ret = vsnprintf(buff, buff_size,
                        RemoveUnsupportedFormat(format).c_str(), args);
    va_end(args);
    if (ret < buff_size) break;
    delete [] buff;
    buff_size *= 2;
    buff = new char[buff_size];
    CHECK(buff);
    // Printf("Resized buff: %d\n", buff_size);
  }

  char pid_buff[100];
  snprintf(pid_buff, sizeof(pid_buff), "==%d== ", getpid());

  string res;
#ifndef TS_LLVM
  int len = strlen(buff);
#else
  int len = __real_strlen(buff);
#endif
  bool last_was_new_line = true;
  for (int i = 0; i < len; i++) {
    if (G_flags->show_pid && last_was_new_line)
      res += pid_buff;
    last_was_new_line = (buff[i] == '\n');
    res += buff[i];
  }

  delete [] buff;

  Printf("%s", res.c_str());
}

long my_strtol(const char *str, char **end, int base) {
#ifdef TS_VALGRIND
  if (base == 16 || (base == 0 && str && str[0] == '0' && str[1] == 'x')) {
    return VG_(strtoll16)((Char*)str, (Char**)end);
  }
  return VG_(strtoll10)((Char*)str, (Char**)end);
#else
  return strtoll(str, end, base);
#endif
}

// Not thread-safe. Need to make it thread-local if we allow
// malloc to be called concurrently.
MallocCostCenterStack g_malloc_stack;

size_t GetVmSizeInMb() {
#ifdef VGO_linux
  const char *path ="/proc/self/statm";  // see 'man proc'
  uintptr_t counter = G_stats->read_proc_self_stats++;
  if (counter >= 1024 && ((counter & (counter - 1)) == 0))
    Report("INFO: reading %s for %ld'th time\n", path, counter);
  int  fd = OpenFileReadOnly(path, false);
  if (fd < 0) return 0;
  char buff[128];
  int n_read = read(fd, buff, sizeof(buff) - 1);
  buff[n_read] = 0;
  close(fd);
  char *end;
  size_t vm_size_in_pages = my_strtol(buff, &end, 10);
  return vm_size_in_pages >> 8;
#else
  return 0;
#endif
}

static string StripTemplatesFromFunctionName(const string &fname) {
  // Returns "" in case of error.

  string ret;
  size_t read_pointer = 0, braces_depth = 0;

  while (read_pointer < fname.size()) {
    size_t next_brace = fname.find_first_of("<>", read_pointer);
    if (next_brace == fname.npos) {
      if (braces_depth > 0) {
        // This can happen on Visual Studio if we reach the ~2000 char limit.
        CHECK(fname.size() > 256);
        return "";
      }
      ret += (fname.c_str() + read_pointer);
      break;
    }

    if (braces_depth == 0) {
      ret.append(fname, read_pointer, next_brace - read_pointer);
    }

    if (next_brace > 0) {
      // We could have found one of the following operators.
      const char *OP[] = {">>=", "<<=",
                          ">>", "<<",
                          ">=", "<=",
                          "->", "->*",
                          "<", ">"};

      bool operator_name = false;
      for (size_t i = 0; i < sizeof(OP)/sizeof(*OP); i++) {
        size_t op_offset = ((string)OP[i]).find(fname[next_brace]);
        if (op_offset == string::npos)
          continue;
        if (next_brace >= 8 + op_offset &&  // 8 == strlen("operator");
            "operator" == fname.substr(next_brace - (8 + op_offset), 8) &&
            OP[i] == fname.substr(next_brace - op_offset, strlen(OP[i]))) {
          operator_name = true;
          ret += OP[i] + op_offset;
          next_brace += strlen(OP[i] + op_offset);
          read_pointer = next_brace;
          break;
        }
      }

      if (operator_name)
        continue;
    }

    if (fname[next_brace] == '<') {
      braces_depth++;
      read_pointer = next_brace + 1;
    } else if (fname[next_brace] == '>') {
      if (braces_depth == 0) {
        // Going to `braces_depth == -1` IS possible at least for this function on Windows:
        // "std::operator<<char,std::char_traits<char>,std::allocator<char> >".
        // Oh, well... Return an empty string and let the caller decide.
        return "";
      }
      braces_depth--;
      read_pointer = next_brace + 1;
    } else
      CHECK(0);
  }
  if (braces_depth != 0) {
    CHECK(fname.size() > 256);
    return "";
  }
  return ret;
}

static string StripParametersFromFunctionName(const string &demangled) {
  // Returns "" in case of error.

  string fname = demangled;

  // Strip stuff like "(***)" and "(anonymous namespace)" -> they are tricky.
  size_t found = fname.npos;
  while ((found = fname.find(", ")) != fname.npos)
    fname.erase(found+1, 1);
  while ((found = fname.find("(**")) != fname.npos)
    fname.erase(found+2, 1);
  while ((found = fname.find("(*)")) != fname.npos)
    fname.erase(found, 3);
  while ((found = fname.find("const()")) != fname.npos)
    fname.erase(found+5, 2);
  while ((found = fname.find("const volatile")) != fname.npos &&
         found > 1 && found + 14 == fname.size())
    fname.erase(found-1);
  while ((found = fname.find("(anonymous namespace)")) != fname.npos)
    fname.erase(found, 21);

  if (fname.find_first_of("(") == fname.npos)
    return fname;
  DCHECK(std::count(fname.begin(), fname.end(), '(') ==
         std::count(fname.begin(), fname.end(), ')'));

  string ret;
  bool returns_fun_ptr = false;
  size_t braces_depth = 0, read_pointer = 0;

  size_t first_parenthesis = fname.find("(");
  if (first_parenthesis != fname.npos) {
    DCHECK(fname.find_first_of(")") != fname.npos);
    DCHECK(fname.find_first_of(")") > first_parenthesis);
    DCHECK(fname[first_parenthesis] == '(');
    if (first_parenthesis + 2 < fname.size() &&
        fname[first_parenthesis - 1] == ' ' &&
        fname[first_parenthesis + 1] == '*' &&
        fname[first_parenthesis + 2] != ' ') {
      // Return value type is a function pointer
      read_pointer = first_parenthesis + 2;
      while (fname[read_pointer] == '*' || fname[read_pointer] == '&')
        read_pointer++;
      braces_depth = 1;
      returns_fun_ptr = true;
    }
  }

  while (read_pointer < fname.size()) {
    size_t next_brace = fname.find_first_of("()", read_pointer);
    if (next_brace == fname.npos) {
      if (braces_depth != 0) {
        // Overflow?
        return "";
      }
      size_t _const = fname.find(" const", read_pointer);
      if (_const == fname.npos) {
        ret += (fname.c_str() + read_pointer);
      } else {
        CHECK(_const + 6 == fname.size());
        ret.append(fname, read_pointer, _const - read_pointer);
      }
      break;
    }

    if (braces_depth == (returns_fun_ptr ? 1 : 0)) {
      ret.append(fname, read_pointer, next_brace - read_pointer);
      returns_fun_ptr = false;
    }

    if (fname[next_brace] == '(') {
      if (next_brace >= 8 && fname[next_brace+1] == ')' &&
          "operator" == fname.substr(next_brace - 8, 8)) {
        ret += "()";
        read_pointer = next_brace + 2;
      } else {
        braces_depth++;
        read_pointer = next_brace + 1;
      }
    } else if (fname[next_brace] == ')') {
      CHECK(braces_depth > 0);
      braces_depth--;
      read_pointer = next_brace + 1;
    } else
      CHECK(0);
  }
  if (braces_depth != 0)
    return "";

  // Special case: on Linux, Valgrind prepends the return type for template
  // functions. And on Windows we may see `scalar deleting destructor'.
  // And we may see "operaror new" etc.
  // And some STL code inserts const& between the return type and the function
  // name.
  // Oh, well...
  size_t space_or_tick;
  while (ret != "") {
    space_or_tick = ret.find_first_of("` ");
    if (space_or_tick != ret.npos && ret[space_or_tick] == ' ' &&
        ret.substr(0, space_or_tick).find("operator") == string::npos) {
      ret = ret.substr(space_or_tick + 1);
    } else if (space_or_tick != ret.npos && space_or_tick + 1 == ret.size()) {
      ret = ret.substr(0, space_or_tick);
    } else {
      break;
    }
  }
  return ret;
}

string NormalizeFunctionName(const string &demangled) {
  if (demangled[1] == '[' && strchr("+-=", demangled[0]) != NULL) {
    // Objective-C function
    return demangled;
  }

  if (demangled.find_first_of("<>()") == demangled.npos) {
    // C function or a well-formatted function name.
    return demangled;
  }

  if (demangled == "(below main)" || demangled == "(no symbols)")
    return demangled;

  const char* const MALFORMED = "(malformed frame)";

  string fname = StripTemplatesFromFunctionName(demangled);
  if (fname.size() == 0) {
    if (DEBUG_MODE)
      Printf("PANIC: `%s`\n", demangled.c_str());
    return MALFORMED;
  }

  fname = StripParametersFromFunctionName(fname);
  if (fname.size() == 0) {
    CHECK(demangled.size() >= 256);
    if (DEBUG_MODE)
      Printf("PANIC: `%s`\n", demangled.c_str());
    return MALFORMED;
  }

  return fname;
}

void OpenFileWriteStringAndClose(const string &file_name, const string &str) {
#ifdef TS_VALGRIND
  SysRes sres = VG_(open)((const Char*)file_name.c_str(),
                          VKI_O_WRONLY|VKI_O_CREAT|VKI_O_TRUNC,
                          VKI_S_IRUSR|VKI_S_IWUSR);
  if (sr_isError(sres)) {
    Report("WARNING: can not open file %s\n", file_name.c_str());
    exit(1);
  }
  int fd = sr_Res(sres);
  write(fd, str.c_str(), str.size());
  close(fd);
#else
  CHECK(0);
#endif
}

//--------- Sockets ------------------ {{{1
#if defined(TS_PIN) && defined(__GNUC__)
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
FILE *OpenSocketForWriting(const string &host_and_port) {
  size_t col = host_and_port.find(":");
  if (col == string::npos) return NULL;
  string host = host_and_port.substr(0, col);
  string port_str = host_and_port.substr(col + 1);
  int sockfd;
  struct sockaddr_in serv_addr;
  struct hostent *server;
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0) return NULL;
  server = gethostbyname(host.c_str());
  if (server == 0) return NULL;
  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  memcpy((char *)&serv_addr.sin_addr.s_addr,
         (char *)server->h_addr,
         server->h_length);
  serv_addr.sin_port = htons(atoi(port_str.c_str()));
  if (connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) < 0)
    return NULL;
  return fdopen(sockfd, "w");
}
#else
FILE *OpenSocketForWriting(const string &host_and_port) {
  return NULL;  // unimplemented.
}
#endif
//--------- TSLock ------------------ {{{1
#ifdef _MSC_VER
//# define TS_LOCK_PIPE
# define TS_LOCK_PIN
#else
# define TS_LOCK_FUTEX
#endif

#if defined(TS_LOCK_PIPE) && defined(TS_PIN)
#ifdef __GNUC__
#include <unistd.h>
// Lock based on pipe's send/receive. The idea (but not the code) 
// is shamelessly stolen from valgrind's /coregrind/m_scheduler/sema.c
struct TSLock::Rep {
  bool held;
  char pipe_char;
  int pipe_fd[2];

  void Write() {
    char buf[2];
    buf[0] = pipe_char;
    buf[1] = 0;
    int res = write(pipe_fd[1], buf, 1);
    CHECK(res == 1);
  }
  bool Read() {
    char buf[2];
    buf[0] = 0;
    buf[1] = 0;
    int res = read(pipe_fd[0], buf, 1);
    if (res != 1)
      return false;
    //Printf("rep::Read: %c\n", buf[0]);

    pipe_char++;
    if (pipe_char == 'Z' + 1) pipe_char = 'A';
    return true;
  }
  void Open() {
    CHECK(0 == pipe(pipe_fd));
    CHECK(pipe_fd[0] != pipe_fd[1]);
    pipe_char = 'A';
  }
  void Close() {
    close(pipe_fd[0]);
    close(pipe_fd[1]);
  }
};
#elif defined(_MSC_VER)
struct TSLock::Rep {
  bool held;
  char pipe_char;
  WINDOWS::HANDLE pipe_fd[2];
  void Write() {
    char buf[2];
    buf[0] = pipe_char;
    buf[1] = 0;
    WINDOWS::DWORD n_written = 0;
    int res = WINDOWS::WriteFile(pipe_fd[1], buf, 1, &n_written, NULL);
    CHECK(res != 0 && n_written == 1);
  }
  bool Read() {
    char buf[2];
    buf[0] = 0;
    buf[1] = 0;
    WINDOWS::DWORD n_read  = 0;
    int res = WINDOWS::ReadFile(pipe_fd[0], buf, 1, &n_read, NULL);
    if (res == 0 && n_read == 0)
      return false;
    //Printf("rep::Read: %c\n", buf[0]);

    pipe_char++;
    if (pipe_char == 'Z' + 1) pipe_char = 'A';
    return true;
  }
  void Open() {
    CHECK(WINDOWS::CreatePipe(&pipe_fd[0], &pipe_fd[1], NULL, 0));
    CHECK(pipe_fd[0] != pipe_fd[1]);
    pipe_char = 'A';
  }
  void Close() {
    WINDOWS::CloseHandle(pipe_fd[0]);
    WINDOWS::CloseHandle(pipe_fd[1]);
  }
};
#endif

TSLock::TSLock() {
  rep_ = new Rep;
  rep_->held = false;
  rep_->Open();
  rep_->Write();
}
TSLock::~TSLock() {
  rep_->Close();
}
void TSLock::Lock() {
  while(rep_->Read() == false)
    ;
  rep_->held = true;
}
void TSLock::Unlock() {
  rep_->held = false;
  rep_->Write();
}
void TSLock::AssertHeld() {
  DCHECK(rep_->held);
}
#endif  // __GNUC__ & TS_LOCK_PIPE

#if defined(TS_LOCK_PIN) && defined(TS_PIN)
#include "pin.H"
struct TSLock::Rep {
  PIN_LOCK lock;
  bool held;
};

TSLock::TSLock() {
  rep_ = new Rep();
  rep_->held = false;
  InitLock(&rep_->lock);
}
TSLock::~TSLock() {
  delete rep_;
}
void TSLock::Lock() {
  GetLock(&rep_->lock, __LINE__);
  rep_->held = true;
}
void TSLock::Unlock() {
  rep_->held = false;
  ReleaseLock(&rep_->lock);
}
void TSLock::AssertHeld() {
  DCHECK(rep_->held);
}
#endif  // TS_LOCK_PIN

#if defined(TS_WRAP_PTHREAD_LOCK)
#include "tsan_rtl_wrap.h"

struct TSLock::Rep {
  pthread_mutex_t lock;
  bool held;
};
TSLock::TSLock() {
  rep_ = new Rep();
  rep_->held = false;
  __real_pthread_mutex_init(&rep_->lock, NULL);
}
TSLock::~TSLock() {
  __real_pthread_mutex_destroy(&rep_->lock);
  delete rep_;
}
void TSLock::Lock() {
  __real_pthread_mutex_lock(&rep_->lock);
  rep_->held = true;
}
void TSLock::Unlock() {
  rep_->held = false;
  __real_pthread_mutex_unlock(&rep_->lock);
}
void TSLock::AssertHeld() {
  DCHECK(rep_->held);
}
#endif  // TS_LLVM

#if defined(TS_LOCK_FUTEX) && defined(__GNUC__) && \
 (defined (TS_PIN) || defined (TS_LLVM))
#include <linux/futex.h>
#include <sys/time.h>
#include <syscall.h>

// Simple futex-based lock.
// The idea is taken from "Futexes Are Tricky" by Ulrich Drepper

TSLock::TSLock() {
  rep_ = 0;
  ANNOTATE_BENIGN_RACE(&rep_, "Benign race on TSLock::rep_");
  ANNOTATE_RWLOCK_CREATE(this);
}
TSLock::~TSLock() {
  ANNOTATE_RWLOCK_DESTROY(this);
  DCHECK(rep_ == 0);
}
void TSLock::Lock() {
  int *p = (int*)&rep_;
  const int kSpinCount = 100;
  DCHECK(kSpinCount > 0);
  int c;
  for (int i = 0; i < kSpinCount; i++) {
    c = __sync_val_compare_and_swap(p, 0, 1);
    if (c == 0) break;
  }
  if (c == 0) {
    // The mutex was unlocked. Now it's ours. Done.
    ANNOTATE_RWLOCK_ACQUIRED(this, /*is_w*/true);
    return;
  }
  DCHECK(c == 1 || c == 2);
  // We are going to block on this lock. Make sure others know that.
  if (c != 2) {
    c = __sync_lock_test_and_set(p, 2);
  }
  // Block.
  int n_waits = 0;
  while (c != 0) {
    syscall(SYS_futex, p, FUTEX_WAIT, 2, 0, 0, 0);
    n_waits++;
    c = __sync_lock_test_and_set(p, 2);
  }
  ANNOTATE_RWLOCK_ACQUIRED(this, /*is_w*/true);
  G_stats->futex_wait += n_waits;
}
void TSLock::Unlock() {
  ANNOTATE_RWLOCK_RELEASED(this, /*is_w*/true);
  int *p = (int*)&rep_;
  DCHECK(*p == 1 || *p == 2);
  int c = __sync_sub_and_fetch(p, 1);
  DCHECK(c == 0 || c == 1);
  if (c == 1) {
    *p = 0;
    syscall(SYS_futex, p, FUTEX_WAKE, 1, 0, 0, 0);
  }
}
void TSLock::AssertHeld() {
  DCHECK(rep_);
}
#endif


//--------------- Atomics ----------------- {{{1
#if defined (_MSC_VER) && TS_SERIALIZED == 0
uintptr_t AtomicExchange(uintptr_t *ptr, uintptr_t new_value) {
  return _InterlockedExchange((volatile WINDOWS::LONG*)ptr, new_value);
}

void ReleaseStore(uintptr_t *ptr, uintptr_t value) {
  *(volatile uintptr_t*)ptr = value;
  // TODO(kcc): anything to add here?
}

int32_t NoBarrier_AtomicIncrement(int32_t* ptr) {
  return _InterlockedIncrement((volatile WINDOWS::LONG *)ptr);
}

int32_t NoBarrier_AtomicDecrement(int32_t* ptr) {
  return _InterlockedDecrement((volatile WINDOWS::LONG *)ptr);
}
#endif  // _MSC_VER && TS_SERIALIZED
//--------------- YIELD ----------------- {{{1
#if defined (_MSC_VER)
void YIELD() {
  WINDOWS::Sleep(0);
}
#elif defined(TS_VALGRIND)
void YIELD() {
}
#elif defined(__GNUC__)
void YIELD() {
  sched_yield();
}
#else
#error "Unknown config"
#endif

// end. {{{1
// vim:shiftwidth=2:softtabstop=2:expandtab:tw=80