diff --git a/util/mach/scoped_task_suspend.cc b/util/mach/scoped_task_suspend.cc new file mode 100644 index 00000000..abca77e3 --- /dev/null +++ b/util/mach/scoped_task_suspend.cc @@ -0,0 +1,39 @@ +// Copyright 2014 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/mach/scoped_task_suspend.h" + +#include "base/logging.h" +#include "base/mac/mach_logging.h" + +namespace crashpad { + +ScopedTaskSuspend::ScopedTaskSuspend(task_t task) : task_(task) { + DCHECK_NE(task_, mach_task_self()); + + kern_return_t kr = task_suspend(task_); + if (kr != KERN_SUCCESS) { + task_ = TASK_NULL; + MACH_LOG(ERROR, kr) << "task_suspend"; + } +} + +ScopedTaskSuspend::~ScopedTaskSuspend() { + if (task_ != TASK_NULL) { + kern_return_t kr = task_resume(task_); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "task_resume"; + } +} + +} // namespace crashpad diff --git a/util/mach/scoped_task_suspend.h b/util/mach/scoped_task_suspend.h new file mode 100644 index 00000000..4b90aba0 --- /dev/null +++ b/util/mach/scoped_task_suspend.h @@ -0,0 +1,45 @@ +// Copyright 2014 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_MACH_SCOPED_TASK_SUSPEND_H_ +#define CRASHPAD_UTIL_MACH_SCOPED_TASK_SUSPEND_H_ + +#include + +#include "base/basictypes.h" + +namespace crashpad { + +//! \brief Manages the suspension of another task. +//! +//! While an object of this class exists, the other task will be suspended. Once +//! the object is destroyed, the other task will become eligible for resumption. +//! Note that suspensions are counted, and the task will not actually resume +//! unless its suspend count drops to 0. +//! +//! Callers should not attempt to suspend the current task (`mach_task_self()`). +class ScopedTaskSuspend { + public: + explicit ScopedTaskSuspend(task_t task); + ~ScopedTaskSuspend(); + + private: + task_t task_; + + DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspend); +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_MACH_SCOPED_TASK_SUSPEND_H_ diff --git a/util/mach/scoped_task_suspend_test.cc b/util/mach/scoped_task_suspend_test.cc new file mode 100644 index 00000000..c8606a0f --- /dev/null +++ b/util/mach/scoped_task_suspend_test.cc @@ -0,0 +1,87 @@ +// Copyright 2014 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/mach/scoped_task_suspend.h" + +#include + +#include "gtest/gtest.h" +#include "util/file/fd_io.h" +#include "util/test/mac/mach_errors.h" +#include "util/test/mac/mach_multiprocess.h" + +namespace crashpad { +namespace test { +namespace { + +int SuspendCount(task_t task) { + // As of the 10.8 SDK, the preferred routine is MACH_TASK_BASIC_INFO. + // TASK_BASIC_INFO_64 is equivalent and works on earlier systems. + task_basic_info_64 task_basic_info; + mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT; + kern_return_t kr = task_info(task, + TASK_BASIC_INFO_64, + reinterpret_cast(&task_basic_info), + &task_basic_info_count); + if (kr != KERN_SUCCESS) { + ADD_FAILURE() << MachErrorMessage(kr, "task_info"); + return -1; + } + + return task_basic_info.suspend_count; +} + +class ScopedTaskSuspendTest final : public MachMultiprocess { + public: + ScopedTaskSuspendTest() : MachMultiprocess() {} + ~ScopedTaskSuspendTest() {} + + private: + // MachMultiprocess: + + virtual void MachMultiprocessParent() override { + task_t child_task = ChildTask(); + + EXPECT_EQ(0, SuspendCount(child_task)); + + { + ScopedTaskSuspend suspend(child_task); + EXPECT_EQ(1, SuspendCount(child_task)); + + { + ScopedTaskSuspend suspend_again(child_task); + EXPECT_EQ(2, SuspendCount(child_task)); + } + + EXPECT_EQ(1, SuspendCount(child_task)); + } + + EXPECT_EQ(0, SuspendCount(child_task)); + } + + virtual void MachMultiprocessChild() override { + CheckedReadFDAtEOF(ReadPipeFD()); + } + + DISALLOW_COPY_AND_ASSIGN(ScopedTaskSuspendTest); +}; + +TEST(ScopedTaskSuspend, ScopedTaskSuspend) { + ScopedTaskSuspendTest scoped_task_suspend_test; + scoped_task_suspend_test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/util/util.gyp b/util/util.gyp index 80df8584..c0fb13b3 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -71,6 +71,8 @@ 'mach/mach_extensions.h', 'mach/mach_message_server.cc', 'mach/mach_message_server.h', + 'mach/scoped_task_suspend.cc', + 'mach/scoped_task_suspend.h', 'mach/symbolic_constants_mach.cc', 'mach/symbolic_constants_mach.h', 'mach/task_memory.cc', @@ -225,6 +227,7 @@ 'mach/exception_ports_test.cc', 'mach/mach_extensions_test.cc', 'mach/mach_message_server_test.cc', + 'mach/scoped_task_suspend_test.cc', 'mach/symbolic_constants_mach_test.cc', 'mach/task_memory_test.cc', 'misc/clock_test.cc',