diff --git a/handler/crash_report_upload_thread.cc b/handler/crash_report_upload_thread.cc index c13bbf4b..d77cdbad 100644 --- a/handler/crash_report_upload_thread.cc +++ b/handler/crash_report_upload_thread.cc @@ -35,6 +35,10 @@ #include "util/net/http_transport.h" #include "util/stdlib/map_insert.h" +#if defined(OS_MACOSX) +#include "handler/mac/file_limit_annotation.h" +#endif // OS_MACOSX + namespace crashpad { namespace { @@ -228,6 +232,10 @@ void CrashReportUploadThread::ProcessPendingReports() { void CrashReportUploadThread::ProcessPendingReport( const CrashReportDatabase::Report& report) { +#if defined(OS_MACOSX) + RecordFileLimitAnnotation(); +#endif // OS_MACOSX + Settings* const settings = database_->GetSettings(); bool uploads_enabled; diff --git a/handler/handler.gyp b/handler/handler.gyp index d6e4c271..50479828 100644 --- a/handler/handler.gyp +++ b/handler/handler.gyp @@ -43,6 +43,8 @@ 'mac/crash_report_exception_handler.h', 'mac/exception_handler_server.cc', 'mac/exception_handler_server.h', + 'mac/file_limit_annotation.cc', + 'mac/file_limit_annotation.h', 'prune_crash_reports_thread.cc', 'prune_crash_reports_thread.h', 'user_stream_data_source.cc', diff --git a/handler/handler_main.cc b/handler/handler_main.cc index b4a08c51..e6d05e22 100644 --- a/handler/handler_main.cc +++ b/handler/handler_main.cc @@ -61,6 +61,7 @@ #include "base/mac/scoped_mach_port.h" #include "handler/mac/crash_report_exception_handler.h" #include "handler/mac/exception_handler_server.h" +#include "handler/mac/file_limit_annotation.h" #include "util/mach/child_port_handshake.h" #include "util/mach/mach_extensions.h" #include "util/posix/close_stdio.h" @@ -697,6 +698,8 @@ int HandlerMain(int argc, reset_sigterm.reset(&old_sigterm_action); } } + + RecordFileLimitAnnotation(); #elif defined(OS_WIN) // Shut down as late as possible relative to programs we're watching. if (!SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY)) diff --git a/handler/mac/crash_report_exception_handler.cc b/handler/mac/crash_report_exception_handler.cc index 7d3d6297..6f9cdbe6 100644 --- a/handler/mac/crash_report_exception_handler.cc +++ b/handler/mac/crash_report_exception_handler.cc @@ -21,6 +21,7 @@ #include "base/mac/scoped_mach_port.h" #include "base/strings/stringprintf.h" #include "client/settings.h" +#include "handler/mac/file_limit_annotation.h" #include "minidump/minidump_file_writer.h" #include "minidump/minidump_user_extension_stream_data_source.h" #include "snapshot/crashpad_info_client_options.h" @@ -67,6 +68,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException( mach_msg_type_number_t* new_state_count, const mach_msg_trailer_t* trailer, bool* destroy_complex_request) { + RecordFileLimitAnnotation(); Metrics::ExceptionEncountered(); Metrics::ExceptionCode(ExceptionCodeForMetrics(exception, code[0])); *destroy_complex_request = true; diff --git a/handler/mac/file_limit_annotation.cc b/handler/mac/file_limit_annotation.cc new file mode 100644 index 00000000..359ed8e0 --- /dev/null +++ b/handler/mac/file_limit_annotation.cc @@ -0,0 +1,93 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed 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 "handler/mac/file_limit_annotation.h" + +#include +#include +#include +#include + +#include + +#include "base/format_macros.h" +#include "base/macros.h" +#include "base/strings/stringprintf.h" +#include "client/crashpad_info.h" +#include "client/simple_string_dictionary.h" + +namespace { + +// rv is the return value from sysctl() or sysctlbyname(), and value and size +// are the pointers passed as oldp and oldlenp. If sysctl() failed, the returned +// string will be "E" followed by the error number. If there was a size +// mismatch, the returned string will be "Z" followed by the size indicated by +// sysctl(). Normally, a string representation of *value will be returned. +std::string FormatFromSysctl(int rv, const int* value, const size_t* size) { + if (rv != 0) { + return base::StringPrintf("E%d", errno); + } + if (*size != sizeof(*value)) { + return base::StringPrintf("Z%zu", *size); + } + return base::StringPrintf("%d", *value); +} + +// Returns a string for |limit|, or "inf" if |limit| is RLIM_INFINITY. +std::string StringForRLim(rlim_t limit) { + if (limit == RLIM_INFINITY) { + return std::string("inf"); + } + + return base::StringPrintf("%" PRIu64, limit); +} + +} // namespace + +namespace crashpad { + +void RecordFileLimitAnnotation() { + CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); + SimpleStringDictionary* simple_annotations = + crashpad_info->simple_annotations(); + if (!simple_annotations) { + simple_annotations = new SimpleStringDictionary(); + crashpad_info->set_simple_annotations(simple_annotations); + } + + int value; + size_t size = sizeof(value); + std::string num_files = FormatFromSysctl( + sysctlbyname("kern.num_files", &value, &size, nullptr, 0), &value, &size); + + int mib[] = {CTL_KERN, KERN_MAXFILES}; + size = sizeof(value); + std::string max_files = FormatFromSysctl( + sysctl(mib, arraysize(mib), &value, &size, nullptr, 0), &value, &size); + + rlimit limit; + std::string nofile; + if (getrlimit(RLIMIT_NOFILE, &limit) != 0) { + nofile = base::StringPrintf("E%d,E%d", errno, errno); + } else { + nofile = + StringForRLim(limit.rlim_cur) + "," + StringForRLim(limit.rlim_max); + } + + std::string annotation = base::StringPrintf( + "%s,%s,%s", num_files.c_str(), max_files.c_str(), nofile.c_str()); + simple_annotations->SetKeyValue("file-limits", annotation.c_str()); +} + +} // namespace crashpad diff --git a/handler/mac/file_limit_annotation.h b/handler/mac/file_limit_annotation.h new file mode 100644 index 00000000..1131986e --- /dev/null +++ b/handler/mac/file_limit_annotation.h @@ -0,0 +1,38 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// Licensed 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 CRASHPAD_HANDLER_MAC_FILE_LIMIT_ANNOTATION_H_ +#define CRASHPAD_HANDLER_MAC_FILE_LIMIT_ANNOTATION_H_ + +namespace crashpad { + +//! \brief Records a `"file-limits"` simple annotation for the process. +//! +//! This annotation will be used to confirm the theory that certain crashes are +//! caused by systems at or near their file descriptor table size limits. +//! +//! The format of the annotation is four comma-separated values: the system-wide +//! `kern.num_files` and `kern.maxfiles` values from `sysctl()`, and the +//! process-specific current and maximum file descriptor limits from +//! `getrlimit(RLIMIT_NOFILE, …)`. +//! +//! See https://crashpad.chromium.org/bug/180. +//! +//! TODO(mark): Remove this annotation after sufficient data has been collected +//! for analysis. +void RecordFileLimitAnnotation(); + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_MAC_FILE_LIMIT_ANNOTATION_H_