From f735d050c4873b7497506b635b266ea304b3922f Mon Sep 17 00:00:00 2001 From: Mark Mentovai Date: Thu, 27 Oct 2016 17:02:48 -0400 Subject: [PATCH] Port the util library to Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this change, it is possible to build crashpad_util on Linux. I built with clang 3.8.1 and GCC 6.2.0. - For per-OS “exception code” metrics, Android and Linux are broken out distinctly. - Because Linux provides no standard UUID generator, base::RandBytes() is used to generate random UUIDs for the InitializeWithNew() form. - Multiple fixes for CloseMultipleNowOrOnExec(): - readdir_r() is deprecated in glibc 2.24. Use readdir() on Linux. - Linux does not have OPEN_MAX. Use the fs.nr_open sysctl (via /proc/sys) to determine the maximum (currently-configured) possible number of file descriptors per process. - Use the {CTL_KERN, KERN_MAXFILESPERPROC} sysctl on Mac to determine the maximum (currently-configured) possible number of file descriptors per process. This is an improvement over using OPEN_MAX, which is still consulted. - ThreadLogMessages’ use of DCHECK_EQ() needs an address-of operator on function pointers to avoid confusing GCC. One problem remains: - util/misc/pdb_structures.h produces -Wmultichar errors. -Wmultichar is enabled by default with GCC (but not clang). It is impossible to disable this warning with #pragma GCC diagnostic ignored. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431 This has not been tested beyond building the crashpad_util target. BUG=crashpad:30 Change-Id: I02e7a05da512ca312806d825b3fc9b2c5bf1a990 Reviewed-on: https://chromium-review.googlesource.com/404009 Reviewed-by: Robert Sesek --- util/misc/metrics.cc | 29 +++++------ util/misc/uuid.cc | 11 ++++ util/posix/close_multiple.cc | 84 +++++++++++++++++++++++++++--- util/thread/thread_log_messages.cc | 4 +- 4 files changed, 103 insertions(+), 25 deletions(-) diff --git a/util/misc/metrics.cc b/util/misc/metrics.cc index f5ab07b6..48be7ecd 100644 --- a/util/misc/metrics.cc +++ b/util/misc/metrics.cc @@ -18,6 +18,16 @@ #include "base/metrics/sparse_histogram.h" #include "build/build_config.h" +#if defined(OS_MACOSX) +#define METRICS_OS_NAME "Mac" +#elif defined(OS_WIN) +#define METRICS_OS_NAME "Win" +#elif defined(OS_ANDROID) +#define METRICS_OS_NAME "Android" +#elif defined(OS_LINUX) +#define METRICS_OS_NAME "Linux" +#endif + namespace crashpad { namespace { @@ -79,12 +89,7 @@ void Metrics::ExceptionCaptureResult(CaptureResult result) { // static void Metrics::ExceptionCode(uint32_t exception_code) { -#if defined(OS_WIN) - static const char kExceptionCodeString[] = "Crashpad.ExceptionCode.Win"; -#elif defined(OS_MACOSX) - static const char kExceptionCodeString[] = "Crashpad.ExceptionCode.Mac"; -#endif - UMA_HISTOGRAM_SPARSE_SLOWLY(kExceptionCodeString, + UMA_HISTOGRAM_SPARSE_SLOWLY("Crashpad.ExceptionCode." METRICS_OS_NAME, static_cast(exception_code)); } @@ -94,15 +99,9 @@ void Metrics::ExceptionEncountered() { } void Metrics::HandlerCrashed(uint32_t exception_code) { -#if defined(OS_WIN) - static const char kExceptionCodeString[] = - "Crashpad.HandlerCrash.ExceptionCode.Win"; -#elif defined(OS_MACOSX) - static const char kExceptionCodeString[] = - "Crashpad.HandlerCrash.ExceptionCode.Mac"; -#endif - UMA_HISTOGRAM_SPARSE_SLOWLY(kExceptionCodeString, - static_cast(exception_code)); + UMA_HISTOGRAM_SPARSE_SLOWLY( + "Crashpad.HandlerCrash.ExceptionCode." METRICS_OS_NAME, + static_cast(exception_code)); } } // namespace crashpad diff --git a/util/misc/uuid.cc b/util/misc/uuid.cc index 9ef55a65..21581cf3 100644 --- a/util/misc/uuid.cc +++ b/util/misc/uuid.cc @@ -23,6 +23,7 @@ #include #include "base/logging.h" +#include "base/rand_util.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" #include "base/sys_byteorder.h" @@ -109,6 +110,16 @@ bool UUID::InitializeWithNew() { return false; } InitializeFromSystemUUID(&system_uuid); + return true; +#elif defined(OS_LINUX) + // Linux does not provide a UUID generator in a widely-available system + // library. uuid_generate() from libuuid is not available everywhere. + base::RandBytes(this, sizeof(*this)); + + // Set six bits per RFC 4122 §4.4 to identify this as a pseudo-random UUID. + data_3 = (4 << 12) | (data_3 & 0x0fff); // §4.1.3 + data_4[0] = 0x80 | (data_4[0] & 0x3f); // §4.1.1 + return true; #else #error Port. diff --git a/util/posix/close_multiple.cc b/util/posix/close_multiple.cc index fea7ca03..d46cd9db 100644 --- a/util/posix/close_multiple.cc +++ b/util/posix/close_multiple.cc @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -25,12 +26,17 @@ #include #include +#include "base/files/scoped_file.h" #include "base/logging.h" #include "base/posix/eintr_wrapper.h" #include "build/build_config.h" #include "util/misc/implicit_cast.h" #include "util/numeric/safe_assignment.h" +#if defined(OS_MACOSX) +#include +#endif + // Everything in this file is expected to execute between fork() and exec(), // so everything called here must be acceptable in this context. However, // logging code that is not expected to execute under normal circumstances is @@ -100,10 +106,19 @@ bool CloseMultipleNowOrOnExecUsingFDDir(int fd, int preserve_fd) { return false; } - dirent entry; dirent* result; - int rv; - while ((rv = readdir_r(dir, &entry, &result)) == 0 && result != nullptr) { +#if defined(OS_LINUX) + // readdir_r() is deprecated as of glibc 2.24. See + // https://sourceware.org/bugzilla/show_bug.cgi?id=19056 and + // https://git.kernel.org/cgit/docs/man-pages/man-pages.git/commit?id=0c52f6d623636a61eacd0f7b7a3bb942793a2a05. + const char kReaddirName[] = "readdir"; + while ((errno = 0, result = readdir(dir)) != nullptr) +#else + const char kReaddirName[] = "readdir_r"; + dirent entry; + while ((errno = readdir_r(dir, &entry, &result)) == 0 && result != nullptr) +#endif + { const char* entry_name = &(*result->d_name); if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) { continue; @@ -127,6 +142,11 @@ bool CloseMultipleNowOrOnExecUsingFDDir(int fd, int preserve_fd) { } } + if (errno != 0) { + PLOG(WARNING) << kReaddirName; + return false; + } + return true; } @@ -141,13 +161,61 @@ void CloseMultipleNowOrOnExec(int fd, int preserve_fd) { // system’s file descriptor limit. Check a few values and use the highest as // the limit, because these may be based on the file descriptor limit set by // setrlimit(), and higher-numbered file descriptors may have been opened - // prior to the limit being lowered. For Mac OS X, see 10.9.2 - // Libc-997.90.3/gen/FreeBSD/sysconf.c sysconf() and 10.9.4 - // xnu-2422.110.17/bsd/kern/kern_descrip.c getdtablesize(), which both return - // the current RLIMIT_NOFILE value, not the maximum possible file descriptor. - int max_fd = std::max(implicit_cast(sysconf(_SC_OPEN_MAX)), OPEN_MAX); + // prior to the limit being lowered. On both macOS and Linux glibc, both + // sysconf() and getdtablesize() return the current RLIMIT_NOFILE value, not + // the maximum possible file descriptor. For macOS, see 10.11.5 + // Libc-1082.50.1/gen/FreeBSD/sysconf.c sysconf() and 10.11.6 + // xnu-3248.60.10/bsd/kern/kern_descrip.c getdtablesize(). For Linux glibc, + // see glibc-2.24/sysdeps/posix/sysconf.c __sysconf() and + // glibc-2.24/sysdeps/posix/getdtsz.c __getdtablesize(). + int max_fd = implicit_cast(sysconf(_SC_OPEN_MAX)); max_fd = std::max(max_fd, getdtablesize()); +#if !defined(OS_LINUX) || defined(OPEN_MAX) + // Linux does not provide OPEN_MAX. See + // https://git.kernel.org/cgit/linux/kernel/git/stable/linux-stable.git/commit/include/linux/limits.h?id=77293034696e3e0b6c8b8fc1f96be091104b3d2b. + max_fd = std::max(max_fd, OPEN_MAX); +#endif + + // Consult a sysctl to determine the system-wide limit on the maximum number + // of open files per process. Note that it is possible to change this limit + // while the system is running, but it’s still a better upper bound than the + // current RLIMIT_NOFILE value. + +#if defined(OS_MACOSX) + // See 10.11.6 xnu-3248.60.10/bsd/kern/kern_resource.c maxfilesperproc, + // referenced by dosetrlimit(). + int oid[] = {CTL_KERN, KERN_MAXFILESPERPROC}; + int maxfilesperproc; + size_t maxfilesperproc_size = sizeof(maxfilesperproc); + if (sysctl(oid, + arraysize(oid), + &maxfilesperproc, + &maxfilesperproc_size, + nullptr, + 0) == 0) { + max_fd = std::max(max_fd, maxfilesperproc); + } else { + PLOG(WARNING) << "sysctl"; + } +#elif defined(OS_LINUX) + // See linux-4.4.27/fs/file.c sysctl_nr_open, referenced by kernel/sys.c + // do_prlimit() and kernel/sysctl.c fs_table. Inability to open this file is + // not considered an error, because /proc may not be available or usable. + { + base::ScopedFILE nr_open_file(fopen("/proc/sys/fs/nr_open", "r")); + if (nr_open_file.get() != nullptr) { + int nr_open; + if (fscanf(nr_open_file.get(), "%d\n", &nr_open) == 1 && + feof(nr_open_file.get())) { + max_fd = std::max(max_fd, nr_open); + } else { + LOG(WARNING) << "/proc/sys/fs/nr_open format error"; + } + } + } +#endif + for (int entry_fd = fd; entry_fd < max_fd; ++entry_fd) { if (entry_fd != preserve_fd) { CloseNowOrOnExec(entry_fd, true); diff --git a/util/thread/thread_log_messages.cc b/util/thread/thread_log_messages.cc index 7f67e458..dec111e7 100644 --- a/util/thread/thread_log_messages.cc +++ b/util/thread/thread_log_messages.cc @@ -46,14 +46,14 @@ class ThreadLogMessagesMaster { } ~ThreadLogMessagesMaster() { - DCHECK_EQ(logging::GetLogMessageHandler(), LogMessageHandler); + DCHECK_EQ(logging::GetLogMessageHandler(), &LogMessageHandler); logging::SetLogMessageHandler(nullptr); tls_.Free(); } void SetThreadMessageList(std::vector* message_list) { - DCHECK_EQ(logging::GetLogMessageHandler(), LogMessageHandler); + DCHECK_EQ(logging::GetLogMessageHandler(), &LogMessageHandler); DCHECK_NE(tls_.Get() != nullptr, message_list != nullptr); tls_.Set(message_list); }