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_
