mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-27 19:12:27 +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':
|
'crashpad/third_party/mini_chromium/mini_chromium':
|
||||||
Var('chromium_git') + '/chromium/mini_chromium@' +
|
Var('chromium_git') + '/chromium/mini_chromium@' +
|
||||||
'62e6015f633dd4acb1610db15a064889315cadaa',
|
'dc3d480305b27a5a1fb57f51a997529e00fed00b',
|
||||||
'crashpad/third_party/zlib/zlib':
|
'crashpad/third_party/zlib/zlib':
|
||||||
Var('chromium_git') + '/chromium/src/third_party/zlib@' +
|
Var('chromium_git') + '/chromium/src/third_party/zlib@' +
|
||||||
'13dc246a58e4b72104d35f9b1809af95221ebda7',
|
'13dc246a58e4b72104d35f9b1809af95221ebda7',
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -29,12 +30,15 @@
|
|||||||
#include "snapshot/module_snapshot.h"
|
#include "snapshot/module_snapshot.h"
|
||||||
#include "util/file/file_reader.h"
|
#include "util/file/file_reader.h"
|
||||||
#include "util/misc/metrics.h"
|
#include "util/misc/metrics.h"
|
||||||
#include "util/misc/uuid.h"
|
|
||||||
#include "util/net/http_body.h"
|
#include "util/net/http_body.h"
|
||||||
#include "util/net/http_multipart_builder.h"
|
#include "util/net/http_multipart_builder.h"
|
||||||
#include "util/net/http_transport.h"
|
#include "util/net/http_transport.h"
|
||||||
#include "util/stdlib/map_insert.h"
|
#include "util/stdlib/map_insert.h"
|
||||||
|
|
||||||
|
#if defined(OS_MACOSX)
|
||||||
|
#include "handler/mac/file_limit_annotation.h"
|
||||||
|
#endif // OS_MACOSX
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
@ -139,15 +143,19 @@ class CallRecordUploadAttempt {
|
|||||||
|
|
||||||
CrashReportUploadThread::CrashReportUploadThread(CrashReportDatabase* database,
|
CrashReportUploadThread::CrashReportUploadThread(CrashReportDatabase* database,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
|
bool watch_pending_reports,
|
||||||
bool rate_limit,
|
bool rate_limit,
|
||||||
bool upload_gzip)
|
bool upload_gzip)
|
||||||
: url_(url),
|
: url_(url),
|
||||||
// Check for pending reports every 15 minutes, even in the absence of a
|
// When watching for pending reports, check every 15 minutes, even in the
|
||||||
// signal from the handler thread. This allows for failed uploads to be
|
// absence of a signal from the handler thread. This allows for failed
|
||||||
// retried periodically, and for pending reports written by other
|
// uploads to be retried periodically, and for pending reports written by
|
||||||
// processes to be recognized.
|
// other processes to be recognized.
|
||||||
thread_(15 * 60, this),
|
thread_(watch_pending_reports ? 15 * 60.0 : WorkerThread::kIndefiniteWait,
|
||||||
|
this),
|
||||||
|
known_pending_report_uuids_(),
|
||||||
database_(database),
|
database_(database),
|
||||||
|
watch_pending_reports_(watch_pending_reports),
|
||||||
rate_limit_(rate_limit),
|
rate_limit_(rate_limit),
|
||||||
upload_gzip_(upload_gzip) {
|
upload_gzip_(upload_gzip) {
|
||||||
}
|
}
|
||||||
@ -156,18 +164,43 @@ CrashReportUploadThread::~CrashReportUploadThread() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CrashReportUploadThread::Start() {
|
void CrashReportUploadThread::Start() {
|
||||||
thread_.Start(0);
|
thread_.Start(watch_pending_reports_ ? 0.0 : WorkerThread::kIndefiniteWait);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashReportUploadThread::Stop() {
|
void CrashReportUploadThread::Stop() {
|
||||||
thread_.Stop();
|
thread_.Stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashReportUploadThread::ReportPending() {
|
void CrashReportUploadThread::ReportPending(const UUID& report_uuid) {
|
||||||
|
known_pending_report_uuids_.PushBack(report_uuid);
|
||||||
thread_.DoWorkNow();
|
thread_.DoWorkNow();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashReportUploadThread::ProcessPendingReports() {
|
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;
|
std::vector<CrashReportDatabase::Report> reports;
|
||||||
if (database_->GetPendingReports(&reports) != CrashReportDatabase::kNoError) {
|
if (database_->GetPendingReports(&reports) != CrashReportDatabase::kNoError) {
|
||||||
// The database is sick. It might be prudent to stop trying to poke it from
|
// 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) {
|
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);
|
ProcessPendingReport(report);
|
||||||
|
|
||||||
// Respect Stop() being called after at least one attempt to process a
|
// Respect Stop() being called after at least one attempt to process a
|
||||||
@ -190,6 +232,10 @@ void CrashReportUploadThread::ProcessPendingReports() {
|
|||||||
|
|
||||||
void CrashReportUploadThread::ProcessPendingReport(
|
void CrashReportUploadThread::ProcessPendingReport(
|
||||||
const CrashReportDatabase::Report& report) {
|
const CrashReportDatabase::Report& report) {
|
||||||
|
#if defined(OS_MACOSX)
|
||||||
|
RecordFileLimitAnnotation();
|
||||||
|
#endif // OS_MACOSX
|
||||||
|
|
||||||
Settings* const settings = database_->GetSettings();
|
Settings* const settings = database_->GetSettings();
|
||||||
|
|
||||||
bool uploads_enabled;
|
bool uploads_enabled;
|
||||||
|
@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
#include "client/crash_report_database.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"
|
#include "util/thread/worker_thread.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
@ -32,22 +34,28 @@ namespace crashpad {
|
|||||||
//! report has been added to the database by calling ReportPending().
|
//! report has been added to the database by calling ReportPending().
|
||||||
//!
|
//!
|
||||||
//! Independently of being triggered by ReportPending(), objects of this class
|
//! Independently of being triggered by ReportPending(), objects of this class
|
||||||
//! periodically examine the database for pending reports. This allows failed
|
//! can periodically examine the database for pending reports. This allows
|
||||||
//! upload attempts for reports left in the pending state to be retried. It also
|
//! failed upload attempts for reports left in the pending state to be retried.
|
||||||
//! catches reports that are added without a ReportPending() signal being
|
//! It also catches reports that are added without a ReportPending() signal
|
||||||
//! caught. This may happen if crash reports are added to the database by other
|
//! being caught. This may happen if crash reports are added to the database by
|
||||||
//! processes.
|
//! other processes.
|
||||||
class CrashReportUploadThread : public WorkerThread::Delegate {
|
class CrashReportUploadThread : public WorkerThread::Delegate {
|
||||||
public:
|
public:
|
||||||
//! \brief Constructs a new object.
|
//! \brief Constructs a new object.
|
||||||
//!
|
//!
|
||||||
//! \param[in] database The database to upload crash reports from.
|
//! \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] 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
|
//! \param[in] rate_limit Whether uploads should be throttled to a (currently
|
||||||
//! hardcoded) rate.
|
//! hardcoded) rate.
|
||||||
//! \param[in] upload_gzip Whether uploads should use `gzip` compression.
|
//! \param[in] upload_gzip Whether uploads should use `gzip` compression.
|
||||||
CrashReportUploadThread(CrashReportDatabase* database,
|
CrashReportUploadThread(CrashReportDatabase* database,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
|
bool watch_pending_reports,
|
||||||
bool rate_limit,
|
bool rate_limit,
|
||||||
bool upload_gzip);
|
bool upload_gzip);
|
||||||
~CrashReportUploadThread();
|
~CrashReportUploadThread();
|
||||||
@ -75,8 +83,11 @@ class CrashReportUploadThread : public WorkerThread::Delegate {
|
|||||||
//! \brief Informs the upload thread that a new pending report has been added
|
//! \brief Informs the upload thread that a new pending report has been added
|
||||||
//! to the database.
|
//! to the database.
|
||||||
//!
|
//!
|
||||||
|
//! \param[in] report_uuid The unique identifier of the newly added pending
|
||||||
|
//! report.
|
||||||
|
//!
|
||||||
//! This method may be called from any thread.
|
//! This method may be called from any thread.
|
||||||
void ReportPending();
|
void ReportPending(const UUID& report_uuid);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//! \brief The result code from UploadReport().
|
//! \brief The result code from UploadReport().
|
||||||
@ -99,8 +110,13 @@ class CrashReportUploadThread : public WorkerThread::Delegate {
|
|||||||
kRetry,
|
kRetry,
|
||||||
};
|
};
|
||||||
|
|
||||||
//! \brief Obtains all pending reports from the database, and calls
|
//! \brief Calls ProcessPendingReport() on pending reports.
|
||||||
//! ProcessPendingReport() to process each one.
|
//!
|
||||||
|
//! 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();
|
void ProcessPendingReports();
|
||||||
|
|
||||||
//! \brief Processes a single pending report from the database.
|
//! \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.
|
//! been called on any thread, as well as periodically on a timer.
|
||||||
void DoWork(const WorkerThread* thread) override;
|
void DoWork(const WorkerThread* thread) override;
|
||||||
|
|
||||||
std::string url_;
|
const std::string url_;
|
||||||
WorkerThread thread_;
|
WorkerThread thread_;
|
||||||
|
ThreadSafeVector<UUID> known_pending_report_uuids_;
|
||||||
CrashReportDatabase* database_; // weak
|
CrashReportDatabase* database_; // weak
|
||||||
bool rate_limit_;
|
const bool watch_pending_reports_;
|
||||||
bool upload_gzip_;
|
const bool rate_limit_;
|
||||||
|
const bool upload_gzip_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(CrashReportUploadThread);
|
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
|
become a client of the second one. The second instance will be started with
|
||||||
the same **--annotation**, **--database**, **--monitor-self-annotation**,
|
the same **--annotation**, **--database**, **--monitor-self-annotation**,
|
||||||
**--no-rate-limit**, **--no-upload-gzip**, and **--url** arguments as the
|
**--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.
|
**--metrics-dir** argument even if the original instance was.
|
||||||
|
|
||||||
Where supported by the underlying operating system, the second instance will
|
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
|
To prevent excessive accumulation of handler processes, _ARGUMENT_ must not
|
||||||
be `--monitor-self`.
|
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**
|
* **--no-rate-limit**
|
||||||
|
|
||||||
Do not rate limit the upload of crash reports. By default uploads are
|
Do not rate limit the upload of crash reports. By default uploads are
|
||||||
|
@ -43,6 +43,8 @@
|
|||||||
'mac/crash_report_exception_handler.h',
|
'mac/crash_report_exception_handler.h',
|
||||||
'mac/exception_handler_server.cc',
|
'mac/exception_handler_server.cc',
|
||||||
'mac/exception_handler_server.h',
|
'mac/exception_handler_server.h',
|
||||||
|
'mac/file_limit_annotation.cc',
|
||||||
|
'mac/file_limit_annotation.h',
|
||||||
'prune_crash_reports_thread.cc',
|
'prune_crash_reports_thread.cc',
|
||||||
'prune_crash_reports_thread.h',
|
'prune_crash_reports_thread.h',
|
||||||
'user_stream_data_source.cc',
|
'user_stream_data_source.cc',
|
||||||
|
@ -61,6 +61,7 @@
|
|||||||
#include "base/mac/scoped_mach_port.h"
|
#include "base/mac/scoped_mach_port.h"
|
||||||
#include "handler/mac/crash_report_exception_handler.h"
|
#include "handler/mac/crash_report_exception_handler.h"
|
||||||
#include "handler/mac/exception_handler_server.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/child_port_handshake.h"
|
||||||
#include "util/mach/mach_extensions.h"
|
#include "util/mach/mach_extensions.h"
|
||||||
#include "util/posix/close_stdio.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"
|
" set a module annotation in the handler\n"
|
||||||
" --monitor-self-argument=ARGUMENT\n"
|
" --monitor-self-argument=ARGUMENT\n"
|
||||||
" provide additional arguments to the second handler\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-rate-limit don't rate limit crash uploads\n"
|
||||||
" --no-upload-gzip don't use gzip compression when uploading\n"
|
" --no-upload-gzip don't use gzip compression when uploading\n"
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
@ -142,6 +144,7 @@ struct Options {
|
|||||||
InitialClientData initial_client_data;
|
InitialClientData initial_client_data;
|
||||||
#endif // OS_MACOSX
|
#endif // OS_MACOSX
|
||||||
bool monitor_self;
|
bool monitor_self;
|
||||||
|
bool periodic_tasks;
|
||||||
bool rate_limit;
|
bool rate_limit;
|
||||||
bool upload_gzip;
|
bool upload_gzip;
|
||||||
};
|
};
|
||||||
@ -353,6 +356,7 @@ void MonitorSelf(const Options& options) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
std::vector<std::string> extra_arguments(options.monitor_self_arguments);
|
std::vector<std::string> extra_arguments(options.monitor_self_arguments);
|
||||||
|
extra_arguments.push_back("--no-periodic-tasks");
|
||||||
if (!options.rate_limit) {
|
if (!options.rate_limit) {
|
||||||
extra_arguments.push_back("--no-rate-limit");
|
extra_arguments.push_back("--no-rate-limit");
|
||||||
}
|
}
|
||||||
@ -416,6 +420,7 @@ int HandlerMain(int argc,
|
|||||||
kOptionMonitorSelf,
|
kOptionMonitorSelf,
|
||||||
kOptionMonitorSelfAnnotation,
|
kOptionMonitorSelfAnnotation,
|
||||||
kOptionMonitorSelfArgument,
|
kOptionMonitorSelfArgument,
|
||||||
|
kOptionNoPeriodicTasks,
|
||||||
kOptionNoRateLimit,
|
kOptionNoRateLimit,
|
||||||
kOptionNoUploadGzip,
|
kOptionNoUploadGzip,
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
@ -456,6 +461,7 @@ int HandlerMain(int argc,
|
|||||||
required_argument,
|
required_argument,
|
||||||
nullptr,
|
nullptr,
|
||||||
kOptionMonitorSelfArgument},
|
kOptionMonitorSelfArgument},
|
||||||
|
{"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks},
|
||||||
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
|
{"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit},
|
||||||
{"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip},
|
{"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip},
|
||||||
#if defined(OS_WIN)
|
#if defined(OS_WIN)
|
||||||
@ -477,6 +483,7 @@ int HandlerMain(int argc,
|
|||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
options.handshake_fd = -1;
|
options.handshake_fd = -1;
|
||||||
#endif
|
#endif
|
||||||
|
options.periodic_tasks = true;
|
||||||
options.rate_limit = true;
|
options.rate_limit = true;
|
||||||
options.upload_gzip = true;
|
options.upload_gzip = true;
|
||||||
|
|
||||||
@ -540,6 +547,10 @@ int HandlerMain(int argc,
|
|||||||
options.monitor_self_arguments.push_back(optarg);
|
options.monitor_self_arguments.push_back(optarg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case kOptionNoPeriodicTasks: {
|
||||||
|
options.periodic_tasks = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
case kOptionNoRateLimit: {
|
case kOptionNoRateLimit: {
|
||||||
options.rate_limit = false;
|
options.rate_limit = false;
|
||||||
break;
|
break;
|
||||||
@ -687,6 +698,8 @@ int HandlerMain(int argc,
|
|||||||
reset_sigterm.reset(&old_sigterm_action);
|
reset_sigterm.reset(&old_sigterm_action);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RecordFileLimitAnnotation();
|
||||||
#elif defined(OS_WIN)
|
#elif defined(OS_WIN)
|
||||||
// Shut down as late as possible relative to programs we're watching.
|
// Shut down as late as possible relative to programs we're watching.
|
||||||
if (!SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY))
|
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
|
// TODO(scottmg): options.rate_limit should be removed when we have a
|
||||||
// configurable database setting to control upload limiting.
|
// configurable database setting to control upload limiting.
|
||||||
// See https://crashpad.chromium.org/bug/23.
|
// See https://crashpad.chromium.org/bug/23.
|
||||||
CrashReportUploadThread upload_thread(
|
CrashReportUploadThread upload_thread(database.get(),
|
||||||
database.get(), options.url, options.rate_limit, options.upload_gzip);
|
options.url,
|
||||||
|
options.periodic_tasks,
|
||||||
|
options.rate_limit,
|
||||||
|
options.upload_gzip);
|
||||||
upload_thread.Start();
|
upload_thread.Start();
|
||||||
|
|
||||||
PruneCrashReportThread prune_thread(database.get(),
|
std::unique_ptr<PruneCrashReportThread> prune_thread;
|
||||||
PruneCondition::GetDefault());
|
if (options.periodic_tasks) {
|
||||||
prune_thread.Start();
|
prune_thread.reset(new PruneCrashReportThread(
|
||||||
|
database.get(), PruneCondition::GetDefault()));
|
||||||
|
prune_thread->Start();
|
||||||
|
}
|
||||||
|
|
||||||
CrashReportExceptionHandler exception_handler(database.get(),
|
CrashReportExceptionHandler exception_handler(database.get(),
|
||||||
&upload_thread,
|
&upload_thread,
|
||||||
@ -744,7 +763,9 @@ int HandlerMain(int argc,
|
|||||||
exception_handler_server.Run(&exception_handler);
|
exception_handler_server.Run(&exception_handler);
|
||||||
|
|
||||||
upload_thread.Stop();
|
upload_thread.Stop();
|
||||||
prune_thread.Stop();
|
if (prune_thread) {
|
||||||
|
prune_thread->Stop();
|
||||||
|
}
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "base/mac/scoped_mach_port.h"
|
#include "base/mac/scoped_mach_port.h"
|
||||||
#include "base/strings/stringprintf.h"
|
#include "base/strings/stringprintf.h"
|
||||||
#include "client/settings.h"
|
#include "client/settings.h"
|
||||||
|
#include "handler/mac/file_limit_annotation.h"
|
||||||
#include "minidump/minidump_file_writer.h"
|
#include "minidump/minidump_file_writer.h"
|
||||||
#include "minidump/minidump_user_extension_stream_data_source.h"
|
#include "minidump/minidump_user_extension_stream_data_source.h"
|
||||||
#include "snapshot/crashpad_info_client_options.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,
|
mach_msg_type_number_t* new_state_count,
|
||||||
const mach_msg_trailer_t* trailer,
|
const mach_msg_trailer_t* trailer,
|
||||||
bool* destroy_complex_request) {
|
bool* destroy_complex_request) {
|
||||||
|
RecordFileLimitAnnotation();
|
||||||
Metrics::ExceptionEncountered();
|
Metrics::ExceptionEncountered();
|
||||||
Metrics::ExceptionCode(ExceptionCodeForMetrics(exception, code[0]));
|
Metrics::ExceptionCode(ExceptionCodeForMetrics(exception, code[0]));
|
||||||
*destroy_complex_request = true;
|
*destroy_complex_request = true;
|
||||||
@ -190,7 +192,7 @@ kern_return_t CrashReportExceptionHandler::CatchMachException(
|
|||||||
return KERN_FAILURE;
|
return KERN_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
upload_thread_->ReportPending();
|
upload_thread_->ReportPending(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (client_options.system_crash_reporter_forwarding != TriState::kDisabled &&
|
if (client_options.system_crash_reporter_forwarding != TriState::kDisabled &&
|
||||||
|
@ -105,7 +105,14 @@ class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface,
|
|||||||
MachMessageServer::kOneShot,
|
MachMessageServer::kOneShot,
|
||||||
MachMessageServer::kReceiveLargeIgnore,
|
MachMessageServer::kReceiveLargeIgnore,
|
||||||
kMachMessageTimeoutWaitIndefinitely);
|
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;
|
return termination_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
upload_thread_->ReportPending();
|
upload_thread_->ReportPending(uuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
|
Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess);
|
||||||
|
@ -162,7 +162,11 @@ class MachMessageServer {
|
|||||||
//! timeout_ms is not #kMachMessageTimeoutWaitIndefinitely). This function
|
//! timeout_ms is not #kMachMessageTimeoutWaitIndefinitely). This function
|
||||||
//! has no successful return value when \a persistent is #kPersistent and
|
//! has no successful return value when \a persistent is #kPersistent and
|
||||||
//! \a timeout_ms is #kMachMessageTimeoutWaitIndefinitely. On failure,
|
//! \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,
|
static mach_msg_return_t Run(Interface* interface,
|
||||||
mach_port_t receive_port,
|
mach_port_t receive_port,
|
||||||
mach_msg_options_t options,
|
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_
|
#ifndef CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_
|
||||||
#define CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_
|
#define CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
#include "build/build_config.h"
|
#include "build/build_config.h"
|
||||||
|
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
@ -30,6 +32,10 @@ namespace crashpad {
|
|||||||
//! \brief An anonymous in-process counting sempahore.
|
//! \brief An anonymous in-process counting sempahore.
|
||||||
class Semaphore {
|
class Semaphore {
|
||||||
public:
|
public:
|
||||||
|
//! \brief A TimedWait() argument that causes an indefinite wait.
|
||||||
|
static constexpr double kIndefiniteWait =
|
||||||
|
std::numeric_limits<double>::infinity();
|
||||||
|
|
||||||
//! \brief Initializes the semaphore.
|
//! \brief Initializes the semaphore.
|
||||||
//!
|
//!
|
||||||
//! \param[in] value The initial value of 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.
|
//! \brief Performs a timed wait (or “procure”) operation on the semaphore.
|
||||||
//!
|
//!
|
||||||
//! \param[in] seconds The maximum number of seconds to wait for the operation
|
//! \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.
|
//! \return `false` if the wait timed out, `true` otherwise.
|
||||||
//!
|
//!
|
||||||
|
@ -14,6 +14,8 @@
|
|||||||
|
|
||||||
#include "util/synchronization/semaphore.h"
|
#include "util/synchronization/semaphore.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
@ -33,6 +35,12 @@ void Semaphore::Wait() {
|
|||||||
|
|
||||||
bool Semaphore::TimedWait(double seconds) {
|
bool Semaphore::TimedWait(double seconds) {
|
||||||
DCHECK_GE(seconds, 0.0);
|
DCHECK_GE(seconds, 0.0);
|
||||||
|
|
||||||
|
if (std::isinf(seconds)) {
|
||||||
|
Wait();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
const dispatch_time_t timeout =
|
const dispatch_time_t timeout =
|
||||||
dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC);
|
dispatch_time(DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC);
|
||||||
return dispatch_semaphore_wait(semaphore_, timeout) == 0;
|
return dispatch_semaphore_wait(semaphore_, timeout) == 0;
|
||||||
|
@ -39,6 +39,12 @@ void Semaphore::Wait() {
|
|||||||
|
|
||||||
bool Semaphore::TimedWait(double seconds) {
|
bool Semaphore::TimedWait(double seconds) {
|
||||||
DCHECK_GE(seconds, 0.0);
|
DCHECK_GE(seconds, 0.0);
|
||||||
|
|
||||||
|
if (std::isinf(seconds)) {
|
||||||
|
Wait();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
timespec timeout;
|
timespec timeout;
|
||||||
timeout.tv_sec = seconds;
|
timeout.tv_sec = seconds;
|
||||||
timeout.tv_nsec = (seconds - trunc(seconds)) * 1E9;
|
timeout.tv_nsec = (seconds - trunc(seconds)) * 1E9;
|
||||||
|
@ -43,6 +43,18 @@ TEST(Semaphore, TimedWaitTimeout) {
|
|||||||
EXPECT_FALSE(semaphore.TimedWait(0.01)); // 10ms
|
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 {
|
struct ThreadMainInfo {
|
||||||
#if defined(OS_POSIX)
|
#if defined(OS_POSIX)
|
||||||
pthread_t pthread;
|
pthread_t pthread;
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
|
|
||||||
#include "util/synchronization/semaphore.h"
|
#include "util/synchronization/semaphore.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
@ -38,6 +39,12 @@ void Semaphore::Wait() {
|
|||||||
|
|
||||||
bool Semaphore::TimedWait(double seconds) {
|
bool Semaphore::TimedWait(double seconds) {
|
||||||
DCHECK_GE(seconds, 0.0);
|
DCHECK_GE(seconds, 0.0);
|
||||||
|
|
||||||
|
if (std::isinf(seconds)) {
|
||||||
|
Wait();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
DWORD rv = WaitForSingleObject(semaphore_, static_cast<DWORD>(seconds * 1E3));
|
DWORD rv = WaitForSingleObject(semaphore_, static_cast<DWORD>(seconds * 1E3));
|
||||||
PCHECK(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT) << "WaitForSingleObject";
|
PCHECK(rv == WAIT_OBJECT_0 || rv == WAIT_TIMEOUT) << "WaitForSingleObject";
|
||||||
return rv == WAIT_OBJECT_0;
|
return rv == WAIT_OBJECT_0;
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
#include "util/thread/worker_thread.h"
|
#include "util/thread/worker_thread.h"
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/logging.h"
|
||||||
#include "util/synchronization/semaphore.h"
|
|
||||||
#include "util/thread/thread.h"
|
#include "util/thread/thread.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
|
#include "util/synchronization/semaphore.h"
|
||||||
|
|
||||||
namespace crashpad {
|
namespace crashpad {
|
||||||
|
|
||||||
@ -40,11 +41,16 @@ class WorkerThread {
|
|||||||
virtual ~Delegate() {}
|
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.
|
//! \brief Creates a new WorkerThread that is not yet running.
|
||||||
//!
|
//!
|
||||||
//! \param[in] work_interval The time interval in seconds at which the \a
|
//! \param[in] work_interval The time interval in seconds at which the \a
|
||||||
//! delegate runs. The interval counts from the completion of
|
//! 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.
|
//! \param[in] delegate The work delegate to invoke every interval.
|
||||||
WorkerThread(double work_interval, Delegate* delegate);
|
WorkerThread(double work_interval, Delegate* delegate);
|
||||||
~WorkerThread();
|
~WorkerThread();
|
||||||
@ -55,7 +61,8 @@ class WorkerThread {
|
|||||||
//!
|
//!
|
||||||
//! \param[in] initial_work_delay The amount of time in seconds to wait
|
//! \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
|
//! 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);
|
void Start(double initial_work_delay);
|
||||||
|
|
||||||
//! \brief Stops the worker thread from running.
|
//! \brief Stops the worker thread from running.
|
||||||
|
@ -168,6 +168,7 @@
|
|||||||
'stdlib/strlcpy.h',
|
'stdlib/strlcpy.h',
|
||||||
'stdlib/strnlen.cc',
|
'stdlib/strnlen.cc',
|
||||||
'stdlib/strnlen.h',
|
'stdlib/strnlen.h',
|
||||||
|
'stdlib/thread_safe_vector.h',
|
||||||
'string/split_string.cc',
|
'string/split_string.cc',
|
||||||
'string/split_string.h',
|
'string/split_string.h',
|
||||||
'synchronization/semaphore_mac.cc',
|
'synchronization/semaphore_mac.cc',
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
'stdlib/string_number_conversion_test.cc',
|
'stdlib/string_number_conversion_test.cc',
|
||||||
'stdlib/strlcpy_test.cc',
|
'stdlib/strlcpy_test.cc',
|
||||||
'stdlib/strnlen_test.cc',
|
'stdlib/strnlen_test.cc',
|
||||||
|
'stdlib/thread_safe_vector_test.cc',
|
||||||
'string/split_string_test.cc',
|
'string/split_string_test.cc',
|
||||||
'synchronization/semaphore_test.cc',
|
'synchronization/semaphore_test.cc',
|
||||||
'thread/thread_log_messages_test.cc',
|
'thread/thread_log_messages_test.cc',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user