mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-20 18:53:47 +00:00
Merge master b8aaa2290530 into doc
This commit is contained in:
commit
4bc4b53382
2
DEPS
2
DEPS
@ -38,7 +38,7 @@ deps = {
|
||||
|
||||
'crashpad/third_party/mini_chromium/mini_chromium':
|
||||
Var('chromium_git') + '/chromium/mini_chromium@' +
|
||||
'62e6015f633dd4acb1610db15a064889315cadaa',
|
||||
'dc3d480305b27a5a1fb57f51a997529e00fed00b',
|
||||
'crashpad/third_party/zlib/zlib':
|
||||
Var('chromium_git') + '/chromium/src/third_party/zlib@' +
|
||||
'13dc246a58e4b72104d35f9b1809af95221ebda7',
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
@ -29,12 +30,15 @@
|
||||
#include "snapshot/module_snapshot.h"
|
||||
#include "util/file/file_reader.h"
|
||||
#include "util/misc/metrics.h"
|
||||
#include "util/misc/uuid.h"
|
||||
#include "util/net/http_body.h"
|
||||
#include "util/net/http_multipart_builder.h"
|
||||
#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 {
|
||||
@ -139,15 +143,19 @@ class CallRecordUploadAttempt {
|
||||
|
||||
CrashReportUploadThread::CrashReportUploadThread(CrashReportDatabase* database,
|
||||
const std::string& url,
|
||||
bool watch_pending_reports,
|
||||
bool rate_limit,
|
||||
bool upload_gzip)
|
||||
: url_(url),
|
||||
// Check for pending reports every 15 minutes, even in the absence of a
|
||||
// signal from the handler thread. This allows for failed uploads to be
|
||||
// retried periodically, and for pending reports written by other
|
||||
// processes to be recognized.
|
||||
thread_(15 * 60, this),
|
||||
// When watching for pending reports, check every 15 minutes, even in the
|
||||
// absence of a signal from the handler thread. This allows for failed
|
||||
// uploads to be retried periodically, and for pending reports written by
|
||||
// other processes to be recognized.
|
||||
thread_(watch_pending_reports ? 15 * 60.0 : WorkerThread::kIndefiniteWait,
|
||||
this),
|
||||
known_pending_report_uuids_(),
|
||||
database_(database),
|
||||
watch_pending_reports_(watch_pending_reports),
|
||||
rate_limit_(rate_limit),
|
||||
upload_gzip_(upload_gzip) {
|
||||
}
|
||||
@ -156,18 +164,43 @@ CrashReportUploadThread::~CrashReportUploadThread() {
|
||||
}
|
||||
|
||||
void CrashReportUploadThread::Start() {
|
||||
thread_.Start(0);
|
||||
thread_.Start(watch_pending_reports_ ? 0.0 : WorkerThread::kIndefiniteWait);
|
||||
}
|
||||
|
||||
void CrashReportUploadThread::Stop() {
|
||||
thread_.Stop();
|
||||
}
|
||||
|
||||
void CrashReportUploadThread::ReportPending() {
|
||||
void CrashReportUploadThread::ReportPending(const UUID& report_uuid) {
|
||||
known_pending_report_uuids_.PushBack(report_uuid);
|
||||
thread_.DoWorkNow();
|
||||
}
|
||||
|
||||
void CrashReportUploadThread::ProcessPendingReports() {
|
||||
std::vector<UUID> known_report_uuids = known_pending_report_uuids_.Drain();
|
||||
for (const UUID& report_uuid : known_report_uuids) {
|
||||
CrashReportDatabase::Report report;
|
||||
if (database_->LookUpCrashReport(report_uuid, &report) !=
|
||||
CrashReportDatabase::kNoError) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ProcessPendingReport(report);
|
||||
|
||||
// Respect Stop() being called after at least one attempt to process a
|
||||
// report.
|
||||
if (!thread_.is_running()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Known pending reports are always processed (above). The rest of this
|
||||
// function is concerned with scanning for pending reports not already known
|
||||
// to this thread.
|
||||
if (!watch_pending_reports_) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<CrashReportDatabase::Report> reports;
|
||||
if (database_->GetPendingReports(&reports) != CrashReportDatabase::kNoError) {
|
||||
// The database is sick. It might be prudent to stop trying to poke it from
|
||||
@ -178,6 +211,15 @@ void CrashReportUploadThread::ProcessPendingReports() {
|
||||
}
|
||||
|
||||
for (const CrashReportDatabase::Report& report : reports) {
|
||||
if (std::find(known_report_uuids.begin(),
|
||||
known_report_uuids.end(),
|
||||
report.uuid) != known_report_uuids.end()) {
|
||||
// An attempt to process the report already occurred above. The report is
|
||||
// still pending, so upload must have failed. Don’t retry it immediately,
|
||||
// it can wait until at least the next pass through this method.
|
||||
continue;
|
||||
}
|
||||
|
||||
ProcessPendingReport(report);
|
||||
|
||||
// Respect Stop() being called after at least one attempt to process a
|
||||
@ -190,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;
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "client/crash_report_database.h"
|
||||
#include "util/misc/uuid.h"
|
||||
#include "util/stdlib/thread_safe_vector.h"
|
||||
#include "util/thread/worker_thread.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -32,22 +34,28 @@ namespace crashpad {
|
||||
//! report has been added to the database by calling ReportPending().
|
||||
//!
|
||||
//! Independently of being triggered by ReportPending(), objects of this class
|
||||
//! periodically examine the database for pending reports. This allows failed
|
||||
//! upload attempts for reports left in the pending state to be retried. It also
|
||||
//! catches reports that are added without a ReportPending() signal being
|
||||
//! caught. This may happen if crash reports are added to the database by other
|
||||
//! processes.
|
||||
//! can periodically examine the database for pending reports. This allows
|
||||
//! failed upload attempts for reports left in the pending state to be retried.
|
||||
//! It also catches reports that are added without a ReportPending() signal
|
||||
//! being caught. This may happen if crash reports are added to the database by
|
||||
//! other processes.
|
||||
class CrashReportUploadThread : public WorkerThread::Delegate {
|
||||
public:
|
||||
//! \brief Constructs a new object.
|
||||
//!
|
||||
//! \param[in] database The database to upload crash reports from.
|
||||
//! \param[in] url The URL of the server to upload crash reports to.
|
||||
//! \param[in] watch_pending_reports Whether to periodically check for new
|
||||
//! pending reports not already known to exist. When `false`, only an
|
||||
//! initial upload attempt will be made for reports known to exist by
|
||||
//! having been added by the ReportPending() method. No scans for new
|
||||
//! pending reports will be conducted.
|
||||
//! \param[in] rate_limit Whether uploads should be throttled to a (currently
|
||||
//! hardcoded) rate.
|
||||
//! \param[in] upload_gzip Whether uploads should use `gzip` compression.
|
||||
CrashReportUploadThread(CrashReportDatabase* database,
|
||||
const std::string& url,
|
||||
bool watch_pending_reports,
|
||||
bool rate_limit,
|
||||
bool upload_gzip);
|
||||
~CrashReportUploadThread();
|
||||
@ -75,8 +83,11 @@ class CrashReportUploadThread : public WorkerThread::Delegate {
|
||||
//! \brief Informs the upload thread that a new pending report has been added
|
||||
//! to the database.
|
||||
//!
|
||||
//! \param[in] report_uuid The unique identifier of the newly added pending
|
||||
//! report.
|
||||
//!
|
||||
//! This method may be called from any thread.
|
||||
void ReportPending();
|
||||
void ReportPending(const UUID& report_uuid);
|
||||
|
||||
private:
|
||||
//! \brief The result code from UploadReport().
|
||||
@ -99,8 +110,13 @@ class CrashReportUploadThread : public WorkerThread::Delegate {
|
||||
kRetry,
|
||||
};
|
||||
|
||||
//! \brief Obtains all pending reports from the database, and calls
|
||||
//! ProcessPendingReport() to process each one.
|
||||
//! \brief Calls ProcessPendingReport() on pending reports.
|
||||
//!
|
||||
//! Assuming Stop() has not been called, this will process reports that the
|
||||
//! object has been made aware of in ReportPending(). Additionally, if the
|
||||
//! object was constructed with \a watch_pending_reports, it will also scan
|
||||
//! the crash report database for other pending reports, and process those as
|
||||
//! well.
|
||||
void ProcessPendingReports();
|
||||
|
||||
//! \brief Processes a single pending report from the database.
|
||||
@ -137,11 +153,13 @@ class CrashReportUploadThread : public WorkerThread::Delegate {
|
||||
//! been called on any thread, as well as periodically on a timer.
|
||||
void DoWork(const WorkerThread* thread) override;
|
||||
|
||||
std::string url_;
|
||||
const std::string url_;
|
||||
WorkerThread thread_;
|
||||
ThreadSafeVector<UUID> known_pending_report_uuids_;
|
||||
CrashReportDatabase* database_; // weak
|
||||
bool rate_limit_;
|
||||
bool upload_gzip_;
|
||||
const bool watch_pending_reports_;
|
||||
const bool rate_limit_;
|
||||
const bool upload_gzip_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(CrashReportUploadThread);
|
||||
};
|
||||
|
@ -151,7 +151,8 @@ establish the Crashpad client environment before running a program.
|
||||
become a client of the second one. The second instance will be started with
|
||||
the same **--annotation**, **--database**, **--monitor-self-annotation**,
|
||||
**--no-rate-limit**, **--no-upload-gzip**, and **--url** arguments as the
|
||||
original one. The second instance will not be started with a
|
||||
original one. The second instance will always be started with a
|
||||
**--no-periodic-tasks** argument, and will not be started with a
|
||||
**--metrics-dir** argument even if the original instance was.
|
||||
|
||||
Where supported by the underlying operating system, the second instance will
|
||||
@ -183,6 +184,20 @@ establish the Crashpad client environment before running a program.
|
||||
To prevent excessive accumulation of handler processes, _ARGUMENT_ must not
|
||||
be `--monitor-self`.
|
||||
|
||||
* **--no-periodic-tasks**
|
||||
|
||||
Do not scan for new pending crash reports or prune the crash report database.
|
||||
Only crash reports recorded by this instance of the Crashpad handler will
|
||||
become eligible for upload in this instance, and only a single initial upload
|
||||
attempt will be made.
|
||||
|
||||
This option is not intended for general use. It is provided to prevent
|
||||
multiple instances of the Crashpad handler from duplicating the effort of
|
||||
performing the same periodic tasks. In normal use, the first instance of the
|
||||
Crashpad handler will assume the responsibility for performing these tasks,
|
||||
and will provide this argument to any second instance. See
|
||||
**--monitor-self**.
|
||||
|
||||
* **--no-rate-limit**
|
||||
|
||||
Do not rate limit the upload of crash reports. By default uploads are
|
||||
|
@ -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',
|
||||
|
@ -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"
|
||||
@ -109,6 +110,7 @@ void Usage(const base::FilePath& me) {
|
||||
" set a module annotation in the handler\n"
|
||||
" --monitor-self-argument=ARGUMENT\n"
|
||||
" provide additional arguments to the second handler\n"
|
||||
" --no-periodic-tasks don't scan for new reports or prune the database\n"
|
||||
" --no-rate-limit don't rate limit crash uploads\n"
|
||||
" --no-upload-gzip don't use gzip compression when uploading\n"
|
||||
#if defined(OS_WIN)
|
||||
@ -142,6 +144,7 @@ struct Options {
|
||||
InitialClientData initial_client_data;
|
||||
#endif // OS_MACOSX
|
||||
bool monitor_self;
|
||||
bool periodic_tasks;
|
||||
bool rate_limit;
|
||||
bool upload_gzip;
|
||||
};
|
||||
@ -353,6 +356,7 @@ void MonitorSelf(const Options& options) {
|
||||
return;
|
||||
}
|
||||
std::vector<std::string> extra_arguments(options.monitor_self_arguments);
|
||||
extra_arguments.push_back("--no-periodic-tasks");
|
||||
if (!options.rate_limit) {
|
||||
extra_arguments.push_back("--no-rate-limit");
|
||||
}
|
||||
@ -416,6 +420,7 @@ int HandlerMain(int argc,
|
||||
kOptionMonitorSelf,
|
||||
kOptionMonitorSelfAnnotation,
|
||||
kOptionMonitorSelfArgument,
|
||||
kOptionNoPeriodicTasks,
|
||||
kOptionNoRateLimit,
|
||||
kOptionNoUploadGzip,
|
||||
#if defined(OS_WIN)
|
||||
@ -456,6 +461,7 @@ int HandlerMain(int argc,
|
||||
required_argument,
|
||||
nullptr,
|
||||
kOptionMonitorSelfArgument},
|
||||
{"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks},
|
||||
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
|
||||
{"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip},
|
||||
#if defined(OS_WIN)
|
||||
@ -477,6 +483,7 @@ int HandlerMain(int argc,
|
||||
#if defined(OS_MACOSX)
|
||||
options.handshake_fd = -1;
|
||||
#endif
|
||||
options.periodic_tasks = true;
|
||||
options.rate_limit = true;
|
||||
options.upload_gzip = true;
|
||||
|
||||
@ -540,6 +547,10 @@ int HandlerMain(int argc,
|
||||
options.monitor_self_arguments.push_back(optarg);
|
||||
break;
|
||||
}
|
||||
case kOptionNoPeriodicTasks: {
|
||||
options.periodic_tasks = false;
|
||||
break;
|
||||
}
|
||||
case kOptionNoRateLimit: {
|
||||
options.rate_limit = false;
|
||||
break;
|
||||
@ -687,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))
|
||||
@ -721,13 +734,19 @@ int HandlerMain(int argc,
|
||||
// TODO(scottmg): options.rate_limit should be removed when we have a
|
||||
// configurable database setting to control upload limiting.
|
||||
// See https://crashpad.chromium.org/bug/23.
|
||||
CrashReportUploadThread upload_thread(
|
||||
database.get(), options.url, options.rate_limit, options.upload_gzip);
|
||||
CrashReportUploadThread upload_thread(database.get(),
|
||||
options.url,
|
||||
options.periodic_tasks,
|
||||
options.rate_limit,
|
||||
options.upload_gzip);
|
||||
upload_thread.Start();
|
||||
|
||||
PruneCrashReportThread prune_thread(database.get(),
|
||||
PruneCondition::GetDefault());
|
||||
prune_thread.Start();
|
||||
std::unique_ptr<PruneCrashReportThread> prune_thread;
|
||||
if (options.periodic_tasks) {
|
||||
prune_thread.reset(new PruneCrashReportThread(
|
||||
database.get(), PruneCondition::GetDefault()));
|
||||
prune_thread->Start();
|
||||
}
|
||||
|
||||
CrashReportExceptionHandler exception_handler(database.get(),
|
||||
&upload_thread,
|
||||
@ -744,7 +763,9 @@ int HandlerMain(int argc,
|
||||
exception_handler_server.Run(&exception_handler);
|
||||
|
||||
upload_thread.Stop();
|
||||
prune_thread.Stop();
|
||||
if (prune_thread) {
|
||||
prune_thread->Stop();
|
||||
}
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
@ -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;
|
||||
@ -190,7 +192,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
|
||||
return KERN_FAILURE;
|
||||
}
|
||||
|
||||
upload_thread_->ReportPending();
|
||||
upload_thread_->ReportPending(uuid);
|
||||
}
|
||||
|
||||
if (client_options.system_crash_reporter_forwarding != TriState::kDisabled &&
|
||||
|
@ -105,7 +105,14 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
|
||||
MachMessageServer::kOneShot,
|
||||
MachMessageServer::kReceiveLargeIgnore,
|
||||
kMachMessageTimeoutWaitIndefinitely);
|
||||
MACH_CHECK(mr == MACH_MSG_SUCCESS, mr) << "MachMessageServer::Run";
|
||||
|
||||
// MACH_SEND_INVALID_DEST occurs when attempting to reply to a dead name.
|
||||
// This can happen if a mach_exc or exc client disappears before a reply
|
||||
// can be sent to it. That’s unusal for kernel-generated requests, but can
|
||||
// easily happen if a task sends its own exception request (as
|
||||
// SimulateCrash() does) and dies before the reply is sent.
|
||||
MACH_CHECK(mr == MACH_MSG_SUCCESS || mr == MACH_SEND_INVALID_DEST, mr)
|
||||
<< "MachMessageServer::Run";
|
||||
}
|
||||
}
|
||||
|
||||
|
93
handler/mac/file_limit_annotation.cc
Normal file
93
handler/mac/file_limit_annotation.cc
Normal file
@ -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 <errno.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <string>
|
||||
|
||||
#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
|
38
handler/mac/file_limit_annotation.h
Normal file
38
handler/mac/file_limit_annotation.h
Normal file
@ -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_
|
@ -130,7 +130,7 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(
|
||||
return termination_code;
|
||||
}
|
||||
|
||||
upload_thread_->ReportPending();
|
||||
upload_thread_->ReportPending(uuid);
|
||||
}
|
||||
|
||||
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
|
||||
|
@ -162,7 +162,11 @@ class MachMessageServer {
|
||||
//! timeout_ms is not #kMachMessageTimeoutWaitIndefinitely). This function
|
||||
//! has no successful return value when \a persistent is #kPersistent and
|
||||
//! \a timeout_ms is #kMachMessageTimeoutWaitIndefinitely. On failure,
|
||||
//! returns a value identifying the nature of the error.
|
||||
//! returns a value identifying the nature of the error. A request
|
||||
//! received with a reply port that is (or becomes) a dead name before the
|
||||
//! reply is sent will result in `MACH_SEND_INVALID_DEST` as a return
|
||||
//! value, which may or may not be considered an error from the caller’s
|
||||
//! perspective.
|
||||
static mach_msg_return_t Run(Interface* interface,
|
||||
mach_port_t receive_port,
|
||||
mach_msg_options_t options,
|
||||
|
63
util/stdlib/thread_safe_vector.h
Normal file
63
util/stdlib/thread_safe_vector.h
Normal file
@ -0,0 +1,63 @@
|
||||
// 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_UTIL_STDLIB_THREAD_SAFE_VECTOR_H_
|
||||
#define CRASHPAD_UTIL_STDLIB_THREAD_SAFE_VECTOR_H_
|
||||
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
//! \brief A wrapper for a `std::vector<>` that can be accessed safely from
|
||||
//! multiple threads.
|
||||
//!
|
||||
//! This is not a drop-in replacement for `std::vector<>`. Only necessary
|
||||
//! operations are defined.
|
||||
template <typename T>
|
||||
class ThreadSafeVector {
|
||||
public:
|
||||
ThreadSafeVector() : vector_(), lock_() {}
|
||||
~ThreadSafeVector() {}
|
||||
|
||||
//! \brief Wraps `std::vector<>::%push_back()`.
|
||||
void PushBack(const T& element) {
|
||||
base::AutoLock lock_owner(lock_);
|
||||
vector_.push_back(element);
|
||||
}
|
||||
|
||||
//! \brief Atomically clears the underlying vector and returns its previous
|
||||
//! contents.
|
||||
std::vector<T> Drain() {
|
||||
std::vector<T> contents;
|
||||
{
|
||||
base::AutoLock lock_owner(lock_);
|
||||
std::swap(vector_, contents);
|
||||
}
|
||||
return contents;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<T> vector_;
|
||||
base::Lock lock_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ThreadSafeVector);
|
||||
};
|
||||
|
||||
} // namespace crashpad
|
||||
|
||||
#endif // CRASHPAD_UTIL_STDLIB_THREAD_SAFE_VECTOR_H_
|
91
util/stdlib/thread_safe_vector_test.cc
Normal file
91
util/stdlib/thread_safe_vector_test.cc
Normal file
@ -0,0 +1,91 @@
|
||||
// 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 "util/stdlib/thread_safe_vector.h"
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "util/thread/thread.h"
|
||||
|
||||
namespace crashpad {
|
||||
namespace test {
|
||||
namespace {
|
||||
|
||||
constexpr int kElementsPerThread = 100;
|
||||
|
||||
class ThreadSafeVectorTestThread : public Thread {
|
||||
public:
|
||||
ThreadSafeVectorTestThread() : thread_safe_vector_(nullptr), start_(0) {}
|
||||
~ThreadSafeVectorTestThread() {}
|
||||
|
||||
void SetTestParameters(ThreadSafeVector<int>* thread_safe_vector, int start) {
|
||||
thread_safe_vector_ = thread_safe_vector;
|
||||
start_ = start;
|
||||
}
|
||||
|
||||
// Thread:
|
||||
void ThreadMain() override {
|
||||
for (int i = start_; i < start_ + kElementsPerThread; ++i) {
|
||||
thread_safe_vector_->PushBack(i);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
ThreadSafeVector<int>* thread_safe_vector_;
|
||||
int start_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ThreadSafeVectorTestThread);
|
||||
};
|
||||
|
||||
TEST(ThreadSafeVector, ThreadSafeVector) {
|
||||
ThreadSafeVector<int> thread_safe_vector;
|
||||
std::vector<int> vector = thread_safe_vector.Drain();
|
||||
EXPECT_TRUE(vector.empty());
|
||||
|
||||
ThreadSafeVectorTestThread threads[100];
|
||||
for (size_t index = 0; index < arraysize(threads); ++index) {
|
||||
threads[index].SetTestParameters(
|
||||
&thread_safe_vector, static_cast<int>(index * kElementsPerThread));
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < arraysize(threads); ++index) {
|
||||
threads[index].Start();
|
||||
|
||||
if (index % 10 == 0) {
|
||||
// Drain the vector periodically to test that simultaneous Drain() and
|
||||
// PushBack() operations work properly.
|
||||
std::vector<int> drained = thread_safe_vector.Drain();
|
||||
vector.insert(vector.end(), drained.begin(), drained.end());
|
||||
}
|
||||
}
|
||||
|
||||
for (ThreadSafeVectorTestThread& thread : threads) {
|
||||
thread.Join();
|
||||
}
|
||||
|
||||
std::vector<int> drained = thread_safe_vector.Drain();
|
||||
vector.insert(vector.end(), drained.begin(), drained.end());
|
||||
bool found[arraysize(threads) * kElementsPerThread] = {};
|
||||
EXPECT_EQ(vector.size(), arraysize(found));
|
||||
for (int element : vector) {
|
||||
EXPECT_FALSE(found[element]) << element;
|
||||
found[element] = true;
|
||||
}
|
||||
|
||||
vector = thread_safe_vector.Drain();
|
||||
EXPECT_TRUE(vector.empty());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace test
|
||||
} // namespace crashpad
|
@ -15,6 +15,8 @@
|
||||
#ifndef CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_
|
||||
#define CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
@ -30,6 +32,10 @@ namespace crashpad {
|
||||
//! \brief An anonymous in-process counting sempahore.
|
||||
class Semaphore {
|
||||
public:
|
||||
//! \brief A TimedWait() argument that causes an indefinite wait.
|
||||
static constexpr double kIndefiniteWait =
|
||||
std::numeric_limits<double>::infinity();
|
||||
|
||||
//! \brief Initializes the semaphore.
|
||||
//!
|
||||
//! \param[in] value The initial value of the semaphore.
|
||||
@ -51,7 +57,8 @@ class Semaphore {
|
||||
//! \brief Performs a timed wait (or “procure”) operation on the semaphore.
|
||||
//!
|
||||
//! \param[in] seconds The maximum number of seconds to wait for the operation
|
||||
//! to complete.
|
||||
//! to complete. If \a seconds is #kIndefiniteWait, this method behaves as
|
||||
//! Wait(), and will not time out.
|
||||
//!
|
||||
//! \return `false` if the wait timed out, `true` otherwise.
|
||||
//!
|
||||
|
@ -14,6 +14,8 @@
|
||||
|
||||
#include "util/synchronization/semaphore.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "base/logging.h"
|
||||
|
||||
namespace crashpad {
|
||||
@ -33,6 +35,12 @@ void Semaphore::Wait() {
|
||||
|
||||
bool Semaphore::TimedWait(double seconds) {
|
||||
DCHECK_GE(seconds, 0.0);
|
||||
|
||||
if (std::isinf(seconds)) {
|
||||
Wait();
|
||||
return true;
|
||||
}
|
||||
|
||||
const dispatch_time_t timeout =
|
||||
dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC);
|
||||
return dispatch_semaphore_wait(semaphore_, timeout) == 0;
|
||||
|
@ -39,6 +39,12 @@ void Semaphore::Wait() {
|
||||
|
||||
bool Semaphore::TimedWait(double seconds) {
|
||||
DCHECK_GE(seconds, 0.0);
|
||||
|
||||
if (std::isinf(seconds)) {
|
||||
Wait();
|
||||
return true;
|
||||
}
|
||||
|
||||
timespec timeout;
|
||||
timeout.tv_sec = seconds;
|
||||
timeout.tv_nsec = (seconds - trunc(seconds)) * 1E9;
|
||||
|
@ -43,6 +43,18 @@ TEST(Semaphore, TimedWaitTimeout) {
|
||||
EXPECT_FALSE(semaphore.TimedWait(0.01)); // 10ms
|
||||
}
|
||||
|
||||
TEST(Semaphore, TimedWaitInfinite_0) {
|
||||
Semaphore semaphore(0);
|
||||
semaphore.Signal();
|
||||
EXPECT_TRUE(semaphore.TimedWait(std::numeric_limits<double>::infinity()));
|
||||
}
|
||||
|
||||
TEST(Semaphore, TimedWaitInfinite_1) {
|
||||
Semaphore semaphore(1);
|
||||
EXPECT_TRUE(semaphore.TimedWait(std::numeric_limits<double>::infinity()));
|
||||
semaphore.Signal();
|
||||
}
|
||||
|
||||
struct ThreadMainInfo {
|
||||
#if defined(OS_POSIX)
|
||||
pthread_t pthread;
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "util/synchronization/semaphore.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
#include "base/logging.h"
|
||||
@ -38,6 +39,12 @@ void Semaphore::Wait() {
|
||||
|
||||
bool Semaphore::TimedWait(double seconds) {
|
||||
DCHECK_GE(seconds, 0.0);
|
||||
|
||||
if (std::isinf(seconds)) {
|
||||
Wait();
|
||||
return true;
|
||||
}
|
||||
|
||||
DWORD rv = WaitForSingleObject(semaphore_, static_cast<DWORD>(seconds * 1E3));
|
||||
PCHECK(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT) << "WaitForSingleObject";
|
||||
return rv == WAIT_OBJECT_0;
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include "util/thread/worker_thread.h"
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "util/synchronization/semaphore.h"
|
||||
#include "util/thread/thread.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <memory>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "util/synchronization/semaphore.h"
|
||||
|
||||
namespace crashpad {
|
||||
|
||||
@ -40,11 +41,16 @@ class WorkerThread {
|
||||
virtual ~Delegate() {}
|
||||
};
|
||||
|
||||
//! \brief A delay or interval argument that causes an indefinite wait.
|
||||
static constexpr double kIndefiniteWait = Semaphore::kIndefiniteWait;
|
||||
|
||||
//! \brief Creates a new WorkerThread that is not yet running.
|
||||
//!
|
||||
//! \param[in] work_interval The time interval in seconds at which the \a
|
||||
//! delegate runs. The interval counts from the completion of
|
||||
//! Delegate::DoWork() to the next invocation.
|
||||
//! Delegate::DoWork() to the next invocation. This can be
|
||||
//! #kIndefiniteWait if work should only be done when DoWorkNow() is
|
||||
//! called.
|
||||
//! \param[in] delegate The work delegate to invoke every interval.
|
||||
WorkerThread(double work_interval, Delegate* delegate);
|
||||
~WorkerThread();
|
||||
@ -55,7 +61,8 @@ class WorkerThread {
|
||||
//!
|
||||
//! \param[in] initial_work_delay The amount of time in seconds to wait
|
||||
//! before invoking the \a delegate for the first time. Pass `0` for
|
||||
//! no delay.
|
||||
//! no delay. This can be #kIndefiniteWait if work should not be done
|
||||
//! until DoWorkNow() is called.
|
||||
void Start(double initial_work_delay);
|
||||
|
||||
//! \brief Stops the worker thread from running.
|
||||
|
@ -168,6 +168,7 @@
|
||||
'stdlib/strlcpy.h',
|
||||
'stdlib/strnlen.cc',
|
||||
'stdlib/strnlen.h',
|
||||
'stdlib/thread_safe_vector.h',
|
||||
'string/split_string.cc',
|
||||
'string/split_string.h',
|
||||
'synchronization/semaphore_mac.cc',
|
||||
|
@ -87,6 +87,7 @@
|
||||
'stdlib/string_number_conversion_test.cc',
|
||||
'stdlib/strlcpy_test.cc',
|
||||
'stdlib/strnlen_test.cc',
|
||||
'stdlib/thread_safe_vector_test.cc',
|
||||
'string/split_string_test.cc',
|
||||
'synchronization/semaphore_test.cc',
|
||||
'thread/thread_log_messages_test.cc',
|
||||
|
Loading…
x
Reference in New Issue
Block a user