http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/d6abb29d/be/src/kudu/util/debug-util.cc
----------------------------------------------------------------------
diff --git a/be/src/kudu/util/debug-util.cc b/be/src/kudu/util/debug-util.cc
new file mode 100644
index 0000000..a9e0dc6
--- /dev/null
+++ b/be/src/kudu/util/debug-util.cc
@@ -0,0 +1,428 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#include "kudu/util/debug-util.h"
+
+#include <execinfo.h>
+#include <dirent.h>
+#include <glog/logging.h>
+#include <signal.h>
+#include <string>
+#include <sys/syscall.h>
+
+#include "kudu/gutil/hash/city.h"
+#include "kudu/gutil/macros.h"
+#include "kudu/gutil/spinlock.h"
+#include "kudu/gutil/stringprintf.h"
+#include "kudu/gutil/strings/numbers.h"
+#include "kudu/util/debug/sanitizer_scopes.h"
+#include "kudu/util/env.h"
+#include "kudu/util/errno.h"
+#include "kudu/util/monotime.h"
+#include "kudu/util/thread.h"
+
+#if defined(__APPLE__)
+typedef sig_t sighandler_t;
+#endif
+
+// In coverage builds, this symbol will be defined and allows us to flush 
coverage info
+// to disk before exiting.
+#if defined(__APPLE__)
+  // OS X does not support weak linking at compile time properly.
+  #if defined(COVERAGE_BUILD)
+extern "C" void __gcov_flush() __attribute__((weak_import));
+  #else
+extern "C" void (*__gcov_flush)() = nullptr;
+  #endif
+#else
+extern "C" {
+__attribute__((weak))
+void __gcov_flush();
+}
+#endif
+
+// Evil hack to grab a few useful functions from glog
+namespace google {
+
+extern int GetStackTrace(void** result, int max_depth, int skip_count);
+
+// Symbolizes a program counter.  On success, returns true and write the
+// symbol name to "out".  The symbol name is demangled if possible
+// (supports symbols generated by GCC 3.x or newer).  Otherwise,
+// returns false.
+bool Symbolize(void *pc, char *out, int out_size);
+
+namespace glog_internal_namespace_ {
+extern void DumpStackTraceToString(std::string *s);
+} // namespace glog_internal_namespace_
+} // namespace google
+
+// The %p field width for printf() functions is two characters per byte.
+// For some environments, add two extra bytes for the leading "0x".
+static const int kPrintfPointerFieldWidth = 2 + 2 * sizeof(void*);
+
+// The signal that we'll use to communicate with our other threads.
+// This can't be in used by other libraries in the process.
+static int g_stack_trace_signum = SIGUSR2;
+
+// We only allow a single dumper thread to run at a time. This simplifies the 
synchronization
+// between the dumper and the target thread.
+//
+// This lock also protects changes to the signal handler.
+static base::SpinLock g_dumper_thread_lock(base::LINKER_INITIALIZED);
+
+namespace kudu {
+
+bool IsCoverageBuild() {
+  return __gcov_flush != nullptr;
+}
+
+void TryFlushCoverage() {
+  static base::SpinLock lock(base::LINKER_INITIALIZED);
+
+  // Flushing coverage is not reentrant or thread-safe.
+  if (!__gcov_flush || !lock.TryLock()) {
+    return;
+  }
+
+  __gcov_flush();
+
+  lock.Unlock();
+}
+
+
+
+namespace {
+
+// Global structure used to communicate between the signal handler
+// and a dumping thread.
+struct SignalCommunication {
+  // The actual stack trace collected from the target thread.
+  StackTrace stack;
+
+  // The current target. Signals can be delivered asynchronously, so the
+  // dumper thread sets this variable first before sending a signal. If
+  // a signal is received on a thread that doesn't match 'target_tid', it is
+  // ignored.
+  pid_t target_tid;
+
+  // Set to 1 when the target thread has successfully collected its stack.
+  // The dumper thread spins waiting for this to become true.
+  Atomic32 result_ready;
+
+  // Lock protecting the other members. We use a bare atomic here and a custom
+  // lock guard below instead of existing spinlock implementaitons because 
futex()
+  // is not signal-safe.
+  Atomic32 lock;
+
+  struct Lock;
+};
+SignalCommunication g_comm;
+
+// Pared-down SpinLock for SignalCommunication::lock. This doesn't rely on 
futex
+// so it is async-signal safe.
+struct SignalCommunication::Lock {
+  Lock() {
+    while (base::subtle::Acquire_CompareAndSwap(&g_comm.lock, 0, 1) != 0) {
+      sched_yield();
+    }
+  }
+  ~Lock() {
+    base::subtle::Release_Store(&g_comm.lock, 0);
+  }
+};
+
+// Signal handler for our stack trace signal.
+// We expect that the signal is only sent from DumpThreadStack() -- not by a 
user.
+void HandleStackTraceSignal(int signum) {
+  SignalCommunication::Lock l;
+
+  // Check that the dumper thread is still interested in our stack trace.
+  // It's possible for signal delivery to be artificially delayed, in which
+  // case the dumper thread would have already timed out and moved on with
+  // its life. In that case, we don't want to race with some other thread's
+  // dump.
+  int64_t my_tid = Thread::CurrentThreadId();
+  if (g_comm.target_tid != my_tid) {
+    return;
+  }
+
+  g_comm.stack.Collect(2);
+  base::subtle::Release_Store(&g_comm.result_ready, 1);
+}
+
+bool InitSignalHandlerUnlocked(int signum) {
+  enum InitState {
+    UNINITIALIZED,
+    INIT_ERROR,
+    INITIALIZED
+  };
+  static InitState state = UNINITIALIZED;
+
+  // If we've already registered a handler, but we're being asked to
+  // change our signal, unregister the old one.
+  if (signum != g_stack_trace_signum && state == INITIALIZED) {
+    struct sigaction old_act;
+    PCHECK(sigaction(g_stack_trace_signum, nullptr, &old_act) == 0);
+    if (old_act.sa_handler == &HandleStackTraceSignal) {
+      signal(g_stack_trace_signum, SIG_DFL);
+    }
+  }
+
+  // If we'd previously had an error, but the signal number
+  // is changing, we should mark ourselves uninitialized.
+  if (signum != g_stack_trace_signum) {
+    g_stack_trace_signum = signum;
+    state = UNINITIALIZED;
+  }
+
+  if (state == UNINITIALIZED) {
+    struct sigaction old_act;
+    PCHECK(sigaction(g_stack_trace_signum, nullptr, &old_act) == 0);
+    if (old_act.sa_handler != SIG_DFL &&
+        old_act.sa_handler != SIG_IGN) {
+      state = INIT_ERROR;
+      LOG(WARNING) << "signal handler for stack trace signal "
+                   << g_stack_trace_signum
+                   << " is already in use: "
+                   << "Kudu will not produce thread stack traces.";
+    } else {
+      // No one appears to be using the signal. This is racy, but there is no
+      // atomic swap capability.
+      sighandler_t old_handler = signal(g_stack_trace_signum, 
HandleStackTraceSignal);
+      if (old_handler != SIG_IGN &&
+          old_handler != SIG_DFL) {
+        LOG(FATAL) << "raced against another thread installing a signal 
handler";
+      }
+      state = INITIALIZED;
+    }
+  }
+  return state == INITIALIZED;
+}
+
+} // namespace
+
+Status SetStackTraceSignal(int signum) {
+  base::SpinLockHolder h(&g_dumper_thread_lock);
+  if (!InitSignalHandlerUnlocked(signum)) {
+    return Status::InvalidArgument("unable to install signal handler");
+  }
+  return Status::OK();
+}
+
+std::string DumpThreadStack(int64_t tid) {
+#if defined(__linux__)
+  base::SpinLockHolder h(&g_dumper_thread_lock);
+
+  // Ensure that our signal handler is installed. We don't need any fancy 
GoogleOnce here
+  // because of the mutex above.
+  if (!InitSignalHandlerUnlocked(g_stack_trace_signum)) {
+    return "<unable to take thread stack: signal handler unavailable>";
+  }
+
+  // Set the target TID in our communication structure, so if we end up with 
any
+  // delayed signal reaching some other thread, it will know to ignore it.
+  {
+    SignalCommunication::Lock l;
+    CHECK_EQ(0, g_comm.target_tid);
+    g_comm.target_tid = tid;
+  }
+
+  // We use the raw syscall here instead of kill() to ensure that we don't 
accidentally
+  // send a signal to some other process in the case that the thread has 
exited and
+  // the TID been recycled.
+  if (syscall(SYS_tgkill, getpid(), tid, g_stack_trace_signum) != 0) {
+    {
+      SignalCommunication::Lock l;
+      g_comm.target_tid = 0;
+    }
+    return "(unable to deliver signal: process may have exited)";
+  }
+
+  // We give the thread ~1s to respond. In testing, threads typically respond 
within
+  // a few iterations of the loop, so this timeout is very conservative.
+  //
+  // The main reason that a thread would not respond is that it has blocked 
signals. For
+  // example, glibc's timer_thread doesn't respond to our signal, so we always 
time out
+  // on that one.
+  string ret;
+  int i = 0;
+  while (!base::subtle::Acquire_Load(&g_comm.result_ready) &&
+         i++ < 100) {
+    SleepFor(MonoDelta::FromMilliseconds(10));
+  }
+
+  {
+    SignalCommunication::Lock l;
+    CHECK_EQ(tid, g_comm.target_tid);
+
+    if (!g_comm.result_ready) {
+      ret = "(thread did not respond: maybe it is blocking signals)";
+    } else {
+      ret = g_comm.stack.Symbolize();
+    }
+
+    g_comm.target_tid = 0;
+    g_comm.result_ready = 0;
+  }
+  return ret;
+#else // defined(__linux__)
+  return "(unsupported platform)";
+#endif
+}
+
+Status ListThreads(vector<pid_t> *tids) {
+#if defined(__linux__)
+  DIR *dir = opendir("/proc/self/task/");
+  if (dir == NULL) {
+    return Status::IOError("failed to open task dir", ErrnoToString(errno), 
errno);
+  }
+  struct dirent *d;
+  while ((d = readdir(dir)) != NULL) {
+    if (d->d_name[0] != '.') {
+      uint32_t tid;
+      if (!safe_strtou32(d->d_name, &tid)) {
+        LOG(WARNING) << "bad tid found in procfs: " << d->d_name;
+        continue;
+      }
+      tids->push_back(tid);
+    }
+  }
+  closedir(dir);
+#endif // defined(__linux__)
+  return Status::OK();
+}
+
+std::string GetStackTrace() {
+  std::string s;
+  google::glog_internal_namespace_::DumpStackTraceToString(&s);
+  return s;
+}
+
+std::string GetStackTraceHex() {
+  char buf[1024];
+  HexStackTraceToString(buf, 1024);
+  return std::string(buf);
+}
+
+void HexStackTraceToString(char* buf, size_t size) {
+  StackTrace trace;
+  trace.Collect(1);
+  trace.StringifyToHex(buf, size);
+}
+
+string GetLogFormatStackTraceHex() {
+  StackTrace trace;
+  trace.Collect(1);
+  return trace.ToLogFormatHexString();
+}
+
+void StackTrace::Collect(int skip_frames) {
+  // google::GetStackTrace has a data race. This is called frequently, so 
better
+  // to ignore it with an annotation rather than use a suppression.
+  debug::ScopedTSANIgnoreReadsAndWrites ignore_tsan;
+  num_frames_ = google::GetStackTrace(frames_, arraysize(frames_), 
skip_frames);
+}
+
+void StackTrace::StringifyToHex(char* buf, size_t size, int flags) const {
+  char* dst = buf;
+
+  // Reserve kHexEntryLength for the first iteration of the loop, 1 byte for a
+  // space (which we may not need if there's just one frame), and 1 for a nul
+  // terminator.
+  char* limit = dst + size - kHexEntryLength - 2;
+  for (int i = 0; i < num_frames_ && dst < limit; i++) {
+    if (i != 0) {
+      *dst++ = ' ';
+    }
+    // See note in Symbolize() below about why we subtract 1 from each address 
here.
+    uintptr_t addr = reinterpret_cast<uintptr_t>(frames_[i]);
+    if (!(flags & NO_FIX_CALLER_ADDRESSES)) {
+      addr--;
+    }
+    FastHex64ToBuffer(addr, dst);
+    dst += kHexEntryLength;
+  }
+  *dst = '\0';
+}
+
+string StackTrace::ToHexString(int flags) const {
+  // Each frame requires kHexEntryLength, plus a space
+  // We also need one more byte at the end for '\0'
+  char buf[kMaxFrames * (kHexEntryLength + 1) + 1];
+  StringifyToHex(buf, arraysize(buf), flags);
+  return string(buf);
+}
+
+// Symbolization function borrowed from glog.
+string StackTrace::Symbolize() const {
+  string ret;
+  for (int i = 0; i < num_frames_; i++) {
+    void* pc = frames_[i];
+
+    char tmp[1024];
+    const char* symbol = "(unknown)";
+
+    // The return address 'pc' on the stack is the address of the instruction
+    // following the 'call' instruction. In the case of calling a function 
annotated
+    // 'noreturn', this address may actually be the first instruction of the 
next
+    // function, because the function we care about ends with the 'call'.
+    // So, we subtract 1 from 'pc' so that we're pointing at the 'call' instead
+    // of the return address.
+    //
+    // For example, compiling a C program with -O2 that simply calls 'abort()' 
yields
+    // the following disassembly:
+    //     Disassembly of section .text:
+    //
+    //     0000000000400440 <main>:
+    //       400440:   48 83 ec 08             sub    $0x8,%rsp
+    //       400444:   e8 c7 ff ff ff          callq  400410 <abort@plt>
+    //
+    //     0000000000400449 <_start>:
+    //       400449:   31 ed                   xor    %ebp,%ebp
+    //       ...
+    //
+    // If we were to take a stack trace while inside 'abort', the return 
pointer
+    // on the stack would be 0x400449 (the first instruction of '_start'). By 
subtracting
+    // 1, we end up with 0x400448, which is still within 'main'.
+    //
+    // This also ensures that we point at the correct line number when using 
addr2line
+    // on logged stacks.
+    if (google::Symbolize(
+            reinterpret_cast<char *>(pc) - 1, tmp, sizeof(tmp))) {
+      symbol = tmp;
+    }
+    StringAppendF(&ret, "    @ %*p  %s\n", kPrintfPointerFieldWidth, pc, 
symbol);
+  }
+  return ret;
+}
+
+string StackTrace::ToLogFormatHexString() const {
+  string ret;
+  for (int i = 0; i < num_frames_; i++) {
+    void* pc = frames_[i];
+    StringAppendF(&ret, "    @ %*p\n", kPrintfPointerFieldWidth, pc);
+  }
+  return ret;
+}
+
+uint64_t StackTrace::HashCode() const {
+  return util_hash::CityHash64(reinterpret_cast<const char*>(frames_),
+                               sizeof(frames_[0]) * num_frames_);
+}
+
+}  // namespace kudu

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/d6abb29d/be/src/kudu/util/debug-util.h
----------------------------------------------------------------------
diff --git a/be/src/kudu/util/debug-util.h b/be/src/kudu/util/debug-util.h
new file mode 100644
index 0000000..2617520
--- /dev/null
+++ b/be/src/kudu/util/debug-util.h
@@ -0,0 +1,171 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+#ifndef KUDU_UTIL_DEBUG_UTIL_H
+#define KUDU_UTIL_DEBUG_UTIL_H
+
+#include <sys/types.h>
+
+#include <string>
+#include <vector>
+
+#include "kudu/gutil/strings/fastmem.h"
+#include "kudu/util/status.h"
+
+namespace kudu {
+
+// Return true if coverage is enabled.
+bool IsCoverageBuild();
+
+// Try to flush coverage info. If another thread is already flushing
+// coverage, this returns without doing anything, since flushing coverage
+// is not thread-safe or re-entrant.
+void TryFlushCoverage();
+
+// Return a list of all of the thread IDs currently running in this process.
+// Not async-safe.
+Status ListThreads(std::vector<pid_t>* tids);
+
+// Set which POSIX signal number should be used internally for triggering
+// stack traces. If the specified signal handler is already in use, this
+// returns an error, and stack traces will be disabled.
+Status SetStackTraceSignal(int signum);
+
+// Return the stack trace of the given thread, stringified and symbolized.
+//
+// Note that the symbolization happens on the calling thread, not the target
+// thread, so this is relatively low-impact on the target.
+//
+// This is safe to use against the current thread, the main thread, or any 
other
+// thread. It requires that the target thread has not blocked POSIX signals. If
+// it has, an error message will be returned.
+//
+// This function is thread-safe but coarsely synchronized: only one "dumper" 
thread
+// may be active at a time.
+std::string DumpThreadStack(int64_t tid);
+
+// Return the current stack trace, stringified.
+std::string GetStackTrace();
+
+// Return the current stack trace, in hex form. This is significantly
+// faster than GetStackTrace() above, so should be used in performance-critical
+// places like TRACE() calls. If you really need blazing-fast speed, though,
+// use HexStackTraceToString() into a stack-allocated buffer instead --
+// this call causes a heap allocation for the std::string.
+//
+// Note that this is much more useful in the context of a static binary,
+// since addr2line wouldn't know where shared libraries were mapped at
+// runtime.
+//
+// NOTE: This inherits the same async-safety issue as HexStackTraceToString()
+std::string GetStackTraceHex();
+
+// This is the same as GetStackTraceHex(), except multi-line in a format that
+// looks very similar to GetStackTrace() but without symbols. Because it's in
+// that format, the tool stacktrace_addr2line.pl in the kudu build-support
+// directory can symbolize it automatically (to the extent that addr2line(1)
+// is able to find the symbols).
+std::string GetLogFormatStackTraceHex();
+
+// Collect the current stack trace in hex form into the given buffer.
+//
+// The resulting trace just includes the hex addresses, space-separated. This 
is suitable
+// for later stringification by pasting into 'addr2line' for example.
+//
+// This function is not async-safe, since it uses the libc backtrace() 
function which
+// may invoke the dynamic loader.
+void HexStackTraceToString(char* buf, size_t size);
+
+// Efficient class for collecting and later stringifying a stack trace.
+//
+// Requires external synchronization.
+class StackTrace {
+ public:
+  StackTrace()
+    : num_frames_(0) {
+  }
+
+  void Reset() {
+    num_frames_ = 0;
+  }
+
+  void CopyFrom(const StackTrace& s) {
+    memcpy(this, &s, sizeof(s));
+  }
+
+  bool Equals(const StackTrace& s) {
+    return s.num_frames_ == num_frames_ &&
+      strings::memeq(frames_, s.frames_,
+                     num_frames_ * sizeof(frames_[0]));
+  }
+
+  // Collect and store the current stack trace. Skips the top 'skip_frames' 
frames
+  // from the stack. For example, a value of '1' will skip the 'Collect()' 
function
+  // call itself.
+  //
+  // This function is technically not async-safe. However, according to
+  // 
http://lists.nongnu.org/archive/html/libunwind-devel/2011-08/msg00054.html it 
is "largely
+  // async safe" and it would only deadlock in the case that you call it while 
a dynamic library
+  // load is in progress. We assume that dynamic library loads would almost 
always be completed
+  // very early in the application lifecycle, so for now, this is considered 
"async safe" until
+  // it proves to be a problem.
+  void Collect(int skip_frames = 1);
+
+
+  enum Flags {
+    // Do not fix up the addresses on the stack to try to point to the 'call'
+    // instructions instead of the return address. This is necessary when 
dumping
+    // addresses to be interpreted by 'pprof', which does this fix-up itself.
+    NO_FIX_CALLER_ADDRESSES = 1
+  };
+
+  // Stringify the trace into the given buffer.
+  // The resulting output is hex addresses suitable for passing into 
'addr2line'
+  // later.
+  void StringifyToHex(char* buf, size_t size, int flags = 0) const;
+
+  // Same as above, but returning a std::string.
+  // This is not async-safe.
+  std::string ToHexString(int flags = 0) const;
+
+  // Return a string with a symbolized backtrace in a format suitable for
+  // printing to a log file.
+  // This is not async-safe.
+  std::string Symbolize() const;
+
+  // Return a string with a hex-only backtrace in the format typically used in
+  // log files. Similar to the format given by Symbolize(), but symbols are not
+  // resolved (only the hex addresses are given).
+  std::string ToLogFormatHexString() const;
+
+  uint64_t HashCode() const;
+
+ private:
+  enum {
+    // The maximum number of stack frames to collect.
+    kMaxFrames = 16,
+
+    // The max number of characters any frame requires in string form.
+    kHexEntryLength = 16
+  };
+
+  int num_frames_;
+  void* frames_[kMaxFrames];
+};
+
+} // namespace kudu
+
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/d6abb29d/be/src/kudu/util/debug/leak_annotations.h
----------------------------------------------------------------------
diff --git a/be/src/kudu/util/debug/leak_annotations.h 
b/be/src/kudu/util/debug/leak_annotations.h
new file mode 100644
index 0000000..2bfc3d8
--- /dev/null
+++ b/be/src/kudu/util/debug/leak_annotations.h
@@ -0,0 +1,84 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+#ifndef KUDU_UTIL_DEBUG_LEAK_ANNOTATIONS_H_
+#define KUDU_UTIL_DEBUG_LEAK_ANNOTATIONS_H_
+
+// Ignore a single leaked object, given its pointer.
+// Does nothing if LeakSanitizer is not enabled.
+#define ANNOTATE_LEAKING_OBJECT_PTR(p)
+
+#if defined(__has_feature)
+#  if __has_feature(address_sanitizer)
+#    if defined(__linux__)
+
+#undef ANNOTATE_LEAKING_OBJECT_PTR
+#define ANNOTATE_LEAKING_OBJECT_PTR(p) __lsan_ignore_object(p);
+
+#    endif
+#  endif
+#endif
+
+// API definitions from LLVM lsan_interface.h
+
+extern "C" {
+  // Allocations made between calls to __lsan_disable() and __lsan_enable() 
will
+  // be treated as non-leaks. Disable/enable pairs may be nested.
+  void __lsan_disable();
+  void __lsan_enable();
+
+  // The heap object into which p points will be treated as a non-leak.
+  void __lsan_ignore_object(const void *p);
+
+  // The user may optionally provide this function to disallow leak checking
+  // for the program it is linked into (if the return value is non-zero). This
+  // function must be defined as returning a constant value; any behavior 
beyond
+  // that is unsupported.
+  int __lsan_is_turned_off();
+
+  // Check for leaks now. This function behaves identically to the default
+  // end-of-process leak check. In particular, it will terminate the process if
+  // leaks are found and the exitcode runtime flag is non-zero.
+  // Subsequent calls to this function will have no effect and end-of-process
+  // leak check will not run. Effectively, end-of-process leak check is moved 
to
+  // the time of first invocation of this function.
+  // By calling this function early during process shutdown, you can instruct
+  // LSan to ignore shutdown-only leaks which happen later on.
+  void __lsan_do_leak_check();
+
+  // Check for leaks now. Returns zero if no leaks have been found or if leak
+  // detection is disabled, non-zero otherwise.
+  // This function may be called repeatedly, e.g. to periodically check a
+  // long-running process. It prints a leak report if appropriate, but does not
+  // terminate the process. It does not affect the behavior of
+  // __lsan_do_leak_check() or the end-of-process leak check, and is not
+  // affected by them.
+  int __lsan_do_recoverable_leak_check();
+} // extern "C"
+
+namespace kudu {
+namespace debug {
+
+class ScopedLSANDisabler {
+ public:
+  ScopedLSANDisabler() { __lsan_disable(); }
+  ~ScopedLSANDisabler() { __lsan_enable(); }
+};
+
+} // namespace debug
+} // namespace kudu
+
+#endif  // KUDU_UTIL_DEBUG_LEAK_ANNOTATIONS_H_

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/d6abb29d/be/src/kudu/util/debug/leakcheck_disabler.h
----------------------------------------------------------------------
diff --git a/be/src/kudu/util/debug/leakcheck_disabler.h 
b/be/src/kudu/util/debug/leakcheck_disabler.h
new file mode 100644
index 0000000..815f818
--- /dev/null
+++ b/be/src/kudu/util/debug/leakcheck_disabler.h
@@ -0,0 +1,48 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+#ifndef KUDU_UTIL_DEBUG_LEAKCHECK_DISABLER_H_
+#define KUDU_UTIL_DEBUG_LEAKCHECK_DISABLER_H_
+
+#include "kudu/gutil/macros.h"
+#include "kudu/util/debug/leak_annotations.h"
+
+namespace kudu {
+namespace debug {
+
+// Scoped object that generically disables LSAN leak checking in a given scope.
+// While this object is alive, calls to "new" will not be checked for leaks.
+class ScopedLeakCheckDisabler {
+ public:
+  ScopedLeakCheckDisabler() {}
+
+ private:
+
+#if defined(__has_feature)
+#  if __has_feature(address_sanitizer)
+#    if defined(__linux__)
+  ScopedLSANDisabler lsan_disabler;
+#    endif
+#  endif
+#endif
+
+  DISALLOW_COPY_AND_ASSIGN(ScopedLeakCheckDisabler);
+};
+
+} // namespace debug
+} // namespace kudu
+
+#endif // KUDU_UTIL_DEBUG_LEAKCHECK_DISABLER_H_

http://git-wip-us.apache.org/repos/asf/incubator-impala/blob/d6abb29d/be/src/kudu/util/debug/sanitizer_scopes.h
----------------------------------------------------------------------
diff --git a/be/src/kudu/util/debug/sanitizer_scopes.h 
b/be/src/kudu/util/debug/sanitizer_scopes.h
new file mode 100644
index 0000000..2f8a557
--- /dev/null
+++ b/be/src/kudu/util/debug/sanitizer_scopes.h
@@ -0,0 +1,47 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+//
+// Wrappers around the annotations from gutil/dynamic_annotations.h,
+// provided as C++-style scope guards.
+#ifndef KUDU_UTIL_DEBUG_SANITIZER_SCOPES_H_
+#define KUDU_UTIL_DEBUG_SANITIZER_SCOPES_H_
+
+#include "kudu/gutil/dynamic_annotations.h"
+#include "kudu/gutil/macros.h"
+
+namespace kudu {
+namespace debug {
+
+// Scope guard which instructs TSAN to ignore all reads and writes
+// on the current thread as long as it is alive. These may be safely
+// nested.
+class ScopedTSANIgnoreReadsAndWrites {
+ public:
+  ScopedTSANIgnoreReadsAndWrites() {
+    ANNOTATE_IGNORE_READS_AND_WRITES_BEGIN();
+  }
+  ~ScopedTSANIgnoreReadsAndWrites() {
+    ANNOTATE_IGNORE_READS_AND_WRITES_END();
+  }
+ private:
+  DISALLOW_COPY_AND_ASSIGN(ScopedTSANIgnoreReadsAndWrites);
+};
+
+} // namespace debug
+} // namespace kudu
+
+#endif  // KUDU_UTIL_DEBUG_SANITIZER_SCOPES_H_

Reply via email to