ThreadStart(tid, GetTid());
SetCurrentThread(tid);
- if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit)
- Atexit(DoLeakCheck);
+ if (common_flags()->detect_leaks) {
+ MaybeStartBackgroudLeakCheckingThread();
+ if (common_flags()->leak_check_at_exit) Atexit(DoLeakCheck);
+ }
InitializeCoverage(common_flags()->coverage, common_flags()->coverage_dir);
}
param.leak_report.ApplySuppressions();
uptr unsuppressed_count = param.leak_report.UnsuppressedLeakCount();
+ CommonSanitizerReportMutex.Lock();
if (unsuppressed_count > 0) {
Decorator d;
Printf("\n"
param.leak_report.PrintSummary();
if (common_flags()->print_cmdline)
PrintCmdline();
+ CommonSanitizerReportMutex.Unlock();
lsan_check_in_progress = false;
return true;
}
+ CommonSanitizerReportMutex.Unlock();
lsan_check_in_progress = false;
return false;
}
static bool already_done;
if (already_done) return;
already_done = true;
+ MaybeRestoreLogPath();
bool have_leaks = CheckForLeaks();
if (!have_leaks) {
return;
return nullptr;
}
+void MaybeRestoreLogPath() {
+ if (common_flags()->leak_check_interval_s == kLeaksIntervalNotSet)
+ return;
+ __sanitizer_set_report_path(common_flags()->log_path);
+}
+
+void MaybeAppendToLogPath(const char *suffix) {
+ if (common_flags()->leak_check_interval_s == kLeaksIntervalNotSet)
+ return;
+ const char *log_path = common_flags()->log_path;
+ if (internal_strcmp(log_path, "stderr") == 0 ||
+ internal_strcmp(log_path, "stdout") == 0 || !log_path)
+ return;
+ static char new_log_path[kMaxPathLength] = {'\0'};
+ if (UNLIKELY(new_log_path[0] == '\0')) {
+ internal_snprintf(new_log_path, kMaxPathLength, "%s.%s", log_path, suffix);
+ __sanitizer_set_report_path(new_log_path);
+ }
+}
+
///// LeakReport implementation. /////
// A hard limit on the number of distinct leaks, to avoid quadratic complexity
void DoLeakCheck();
void DoRecoverableLeakCheckVoid();
bool DisabledInThisThread();
+void MaybeRestoreLogPath();
+void MaybeAppendToLogPath(const char *suffix);
+
+static const uptr kLeaksIntervalNotSet = 0;
+void MaybeStartBackgroudLeakCheckingThread();
// Used to implement __lsan::ScopedDisabler.
void DisableInThisThread();
dl_iterate_phdr(DoStopTheWorldCallback, ¶m);
}
+void BackgroudLeakCheckingThread(void *arg) {
+ ScopedInterceptorDisabler disabler;
+ uptr interval_s = common_flags()->leak_check_interval_s;
+ MaybeAppendToLogPath("in-progress");
+ while (true) {
+ SleepForSeconds(interval_s);
+ report_file.Truncate();
+ DoRecoverableLeakCheckVoid();
+ }
+ MaybeRestoreLogPath();
+}
+
+void MaybeStartBackgroudLeakCheckingThread() {
+ if (common_flags()->leak_check_interval_s == kLeaksIntervalNotSet) return;
+ internal_start_thread(BackgroudLeakCheckingThread, nullptr);
+}
+
} // namespace __lsan
#endif // CAN_SANITIZE_LEAKS && SANITIZER_LINUX
#include <stddef.h>
#include <sys/mman.h>
+#if SANITIZER_POSIX
+#include "sanitizer_common/sanitizer_posix.h"
+#endif
+
using namespace __lsan;
extern "C" {
return res;
}
+DEFINE_REAL_PTHREAD_FUNCTIONS
+
static inline bool isSharedMmap(void *addr, int flags) {
return (flags & MAP_SHARED) && (addr == NULL);
}
}
}
+void ReportFile::Truncate() {
+ if (fd == kStdoutFd || fd == kStderrFd) return;
+ if (!full_path) return;
+ SpinMutexLock l(mu);
+ if (fd != kInvalidFd)
+ CloseFile(fd);
+ fd = OpenFile(full_path, WrTrunc);
+}
+
// PID of the tracer task in StopTheWorld. It shares the address space with the
// main process, but has a different PID and thus requires special handling.
uptr stoptheworld_tracer_pid = 0;
void Write(const char *buffer, uptr length);
bool SupportsColors();
void SetReportPath(const char *path);
+ void Truncate();
// Don't use fields directly. They are only declared public to allow
// aggregate initialization.
enum FileAccessMode {
RdOnly,
WrOnly,
- RdWr
+ RdWr,
+ WrTrunc
};
// Returns kInvalidFd on error.
"Invoke leak checking in an atexit handler. Has no effect if "
"detect_leaks=false, or if __lsan_do_leak_check() is called before the "
"handler has a chance to run.")
+COMMON_FLAG(uptr, leak_check_interval_s, 0,
+ "Experimental. If set, creates background thread that performs "
+ "recoverable leak checking every leak_check_interval_s seconds. "
+ "Disabled if zero or detect_leaks=false.\n")
COMMON_FLAG(bool, allocator_may_return_null, false,
"If false, the allocator will crash instead of returning 0 on "
"out-of-memory.")
case RdOnly: flags = O_RDONLY; break;
case WrOnly: flags = O_WRONLY | O_CREAT; break;
case RdWr: flags = O_RDWR | O_CREAT; break;
+ case WrTrunc: flags = O_WRONLY | O_CREAT | O_TRUNC; break;
}
fd_t res = internal_open(filename, flags, 0660);
if (internal_iserror(res, errno_p))