Index: third_party/crashpad/crashpad/test/win/win_multiprocess_with_temp_dir.cc |
diff --git a/third_party/crashpad/crashpad/test/win/win_multiprocess_with_temp_dir.cc b/third_party/crashpad/crashpad/test/win/win_multiprocess_with_temp_dir.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..13e38a5bea4e8ac70c0fb09a155d7bfedde317f1 |
--- /dev/null |
+++ b/third_party/crashpad/crashpad/test/win/win_multiprocess_with_temp_dir.cc |
@@ -0,0 +1,188 @@ |
+// 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 "test/win/win_multiprocess_with_temp_dir.h" |
+ |
+#include <tlhelp32.h> |
+ |
+#include "base/memory/ptr_util.h" |
+#include "test/errors.h" |
+#include "util/win/process_info.h" |
+ |
+namespace crashpad { |
+namespace test { |
+ |
+namespace { |
+ |
+constexpr wchar_t kTempDirEnvName[] = L"CRASHPAD_TEST_TEMP_DIR"; |
+ |
+// Returns the process IDs of all processes that have |parent_pid| as |
+// parent process ID. |
+std::vector<pid_t> GetPotentialChildProcessesOf(pid_t parent_pid) { |
+ ScopedFileHANDLE snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); |
+ if (!snapshot.is_valid()) { |
+ ADD_FAILURE() << ErrorMessage("CreateToolhelp32Snapshot"); |
+ return std::vector<pid_t>(); |
+ } |
+ |
+ PROCESSENTRY32 entry = {sizeof(entry)}; |
+ if (!Process32First(snapshot.get(), &entry)) { |
+ ADD_FAILURE() << ErrorMessage("Process32First"); |
+ return std::vector<pid_t>(); |
+ } |
+ |
+ std::vector<pid_t> child_pids; |
+ do { |
+ if (entry.th32ParentProcessID == parent_pid) |
+ child_pids.push_back(entry.th32ProcessID); |
+ } while (Process32Next(snapshot.get(), &entry)); |
+ |
+ return child_pids; |
+} |
+ |
+ULARGE_INTEGER GetProcessCreationTime(HANDLE process) { |
+ ULARGE_INTEGER ret = {}; |
+ FILETIME creation_time; |
+ FILETIME dummy; |
+ if (GetProcessTimes(process, &creation_time, &dummy, &dummy, &dummy)) { |
+ ret.LowPart = creation_time.dwLowDateTime; |
+ ret.HighPart = creation_time.dwHighDateTime; |
+ } else { |
+ ADD_FAILURE() << ErrorMessage("GetProcessTimes"); |
+ } |
+ |
+ return ret; |
+} |
+ |
+// Waits for the processes directly created by |parent| - and specifically |
+// not their offspring. For this to work without race, |parent| has to be |
+// suspended or have exited. |
+void WaitForAllChildProcessesOf(HANDLE parent) { |
+ pid_t parent_pid = GetProcessId(parent); |
+ std::vector<pid_t> child_pids = GetPotentialChildProcessesOf(parent_pid); |
+ |
+ ULARGE_INTEGER parent_creationtime = GetProcessCreationTime(parent); |
+ for (pid_t child_pid : child_pids) { |
+ // Try and open the process. This may fail for reasons such as: |
+ // 1. The process isn't |parent|'s child process, but rather a |
+ // higher-privilege sub-process of an earlier process that had |
+ // |parent|'s PID. |
+ // 2. The process no longer exists, e.g. it exited after enumeration. |
+ ScopedKernelHANDLE child_process( |
+ OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | SYNCHRONIZE, |
+ false, |
+ child_pid)); |
+ if (!child_process.is_valid()) |
+ continue; |
+ |
+ // Check that the child now has the right parent PID, as its PID may have |
+ // been reused after the enumeration above. |
+ ProcessInfo child_info; |
+ if (!child_info.Initialize(child_process.get())) { |
+ // This can happen if child_process has exited after the handle is opened. |
+ LOG(ERROR) << "ProcessInfo::Initialize, pid: " << child_pid; |
+ continue; |
+ } |
+ |
+ if (parent_pid != child_info.ParentProcessID()) { |
+ // The child's process ID was reused after enumeration. |
+ continue; |
+ } |
+ |
+ // We successfully opened |child_process| and it has |parent|'s PID for |
+ // parent process ID. However, this could still be a sub-process of another |
+ // process that earlier had |parent|'s PID. To make sure, check that |
+ // |child_process| was created after |parent_process|. |
+ ULARGE_INTEGER process_creationtime = |
+ GetProcessCreationTime(child_process.get()); |
+ if (process_creationtime.QuadPart < parent_creationtime.QuadPart) |
+ continue; |
+ |
+ DWORD err = WaitForSingleObject(child_process.get(), INFINITE); |
+ if (err == WAIT_FAILED) { |
+ ADD_FAILURE() << ErrorMessage("WaitForSingleObject"); |
+ } else if (err != WAIT_OBJECT_0) { |
+ ADD_FAILURE() << "WaitForSingleObject returned " << err; |
+ } |
+ } |
+} |
+ |
+} // namespace |
+ |
+WinMultiprocessWithTempDir::WinMultiprocessWithTempDir() |
+ : WinMultiprocess(), temp_dir_env_(kTempDirEnvName) {} |
+ |
+void WinMultiprocessWithTempDir::WinMultiprocessParentBeforeChild() { |
+ temp_dir_ = base::WrapUnique(new ScopedTempDir); |
+ temp_dir_env_.SetValue(temp_dir_->path().value().c_str()); |
+} |
+ |
+void WinMultiprocessWithTempDir::WinMultiprocessParentAfterChild(HANDLE child) { |
+ WaitForAllChildProcessesOf(child); |
+ temp_dir_.reset(); |
+} |
+ |
+base::FilePath WinMultiprocessWithTempDir::GetTempDirPath() const { |
+ return base::FilePath(temp_dir_env_.GetValue()); |
+} |
+ |
+WinMultiprocessWithTempDir::ScopedEnvironmentVariable:: |
+ ScopedEnvironmentVariable(const wchar_t* name) |
+ : name_(name) { |
+ original_value_ = GetValueImpl(&was_defined_); |
+} |
+ |
+WinMultiprocessWithTempDir::ScopedEnvironmentVariable:: |
+ ~ScopedEnvironmentVariable() { |
+ if (was_defined_) |
+ SetValue(original_value_.data()); |
+ else |
+ SetValue(nullptr); |
+} |
+ |
+std::wstring WinMultiprocessWithTempDir::ScopedEnvironmentVariable::GetValue() |
+ const { |
+ bool dummy; |
+ return GetValueImpl(&dummy); |
+} |
+ |
+std::wstring |
+WinMultiprocessWithTempDir::ScopedEnvironmentVariable::GetValueImpl( |
+ bool* is_defined) const { |
+ // The length returned is inclusive of the terminating zero, except |
+ // if the variable doesn't exist, in which case the return value is zero. |
+ DWORD len = GetEnvironmentVariable(name_, nullptr, 0); |
+ if (len == 0) { |
+ *is_defined = false; |
+ return L""; |
+ } |
+ |
+ *is_defined = true; |
+ |
+ std::wstring ret; |
+ ret.resize(len); |
+ // The length returned on success is exclusive of the terminating zero. |
+ len = GetEnvironmentVariable(name_, &ret[0], len); |
+ ret.resize(len); |
+ |
+ return ret; |
+} |
+ |
+void WinMultiprocessWithTempDir::ScopedEnvironmentVariable::SetValue( |
+ const wchar_t* new_value) const { |
+ SetEnvironmentVariable(name_, new_value); |
+} |
+ |
+} // namespace test |
+} // namespace crashpad |