1 : // Copyright 2015 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include <windows.h> // NOLINT
16 : #include <tlhelp32.h>
17 :
18 : #include "base/base_switches.h"
19 : #include "base/bind.h"
20 : #include "base/bind_helpers.h"
21 : #include "base/callback_helpers.h"
22 : #include "base/command_line.h"
23 : #include "base/logging.h"
24 : #include "base/macros.h"
25 : #include "base/files/file_path.h"
26 : #include "base/files/file_util.h"
27 : #include "base/files/scoped_temp_dir.h"
28 : #include "base/process/kill.h"
29 : #include "base/process/launch.h"
30 : #include "base/process/process_handle.h"
31 : #include "base/strings/string16.h"
32 : #include "base/strings/string_number_conversions.h"
33 : #include "base/strings/utf_string_conversions.h"
34 : #include "base/synchronization/waitable_event.h"
35 : #include "base/test/multiprocess_test.h"
36 : #include "base/win/scoped_comptr.h"
37 : #include "base/win/scoped_handle.h"
38 : #include "syzygy/kasko/api/client.h"
39 : #include "syzygy/kasko/api/reporter.h"
40 : #include "syzygy/kasko/testing/minidump_unittest_helpers.h"
41 : #include "syzygy/kasko/testing/test_server.h"
42 : #include "syzygy/kasko/testing/upload_observer.h"
43 : #include "testing/multiprocess_func_list.h"
44 : #include "testing/gtest/include/gtest/gtest.h"
45 :
46 m : namespace kasko {
47 m : namespace api {
48 :
49 m : namespace {
50 :
51 m : const char kGlobalString[] = "a global string";
52 :
53 m : const char kClientProcessIdSwitch[] = "client-process-id";
54 m : const char kExpectGlobalSwitch[] = "expect-global";
55 m : const char kExpectRegisteredKeys[] = "expect-registered-keys";
56 m : const char kSynthesizeException[] = "synthesize-exception";
57 :
58 m : const base::char16 kExitEventNamePrefix[] = L"kasko_api_test_exit_event_";
59 m : const base::char16 kReadyEventNamePrefix[] = L"kasko_api_test_ready_event_";
60 m : const base::char16 kEndpointPrefix[] = L"kasko_api_test_endpoint_";
61 :
62 : // Verifies the minidump contents and sets the bool pointed to by |context| to
63 : // true.
64 m : void OnUploadProc(void* context,
65 m : const base::char16* report_id,
66 m : const base::char16* minidump_path,
67 m : const base::char16* const* keys,
68 m : const base::char16* const* values) {
69 m : CHECK(report_id);
70 m : CHECK(report_id[0] != 0);
71 m : CHECK(minidump_path);
72 m : CHECK(minidump_path[0] != 0);
73 m : CHECK(keys[0]);
74 m : CHECK(values[0]);
75 m : bool found_hello_world = false;
76 m : for (int i = 0; keys[i] != nullptr; ++i) {
77 m : if (keys[i] == base::string16(L"hello")) {
78 m : CHECK_EQ(base::string16(L"world"), base::string16(values[i]));
79 m : found_hello_world = true;
80 m : }
81 : // Make sure that the ""="bar" key was dropped along the way.
82 m : CHECK_NE(values[i], base::string16(L"bar"));
83 m : CHECK_NE(keys[i], base::string16());
84 m : }
85 m : CHECK(found_hello_world);
86 m : *reinterpret_cast<bool*>(context) = true;
87 m : }
88 :
89 : // Implements the setup and teardown of a child process that runs a Kasko
90 : // reporter.
91 m : class ChildProcess {
92 m : public:
93 m : ChildProcess() : client_process_id_(0), on_upload_invoked_(false) {
94 m : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
95 m : base::string16 client_process_id_string = base::ASCIIToUTF16(
96 m : cmd_line->GetSwitchValueASCII(kClientProcessIdSwitch));
97 m : unsigned int client_process_id_uint = 0;
98 m : CHECK(
99 m : base::StringToUint(client_process_id_string, &client_process_id_uint));
100 m : client_process_id_ = client_process_id_uint;
101 m : CHECK(permanent_failure_directory_.CreateUniqueTempDir());
102 :
103 : // Set up a directory for the Reporter to generate and store crash dumps.
104 m : CHECK(data_directory_.CreateUniqueTempDir());
105 :
106 : // Start up a test server to receive uploads.
107 m : CHECK(server_.Start());
108 m : }
109 :
110 : // Initializes the reporter, invokes the subclass-implemented OnInitialized,
111 : // shuts down the reporter (waiting for an upload to complete), then invokes
112 : // the subclass-implemented OnComplete().
113 m : void Run() {
114 m : base::string16 url =
115 m : L"http://127.0.0.1:" + base::UintToString16(server_.port()) + L"/crash";
116 m : base::string16 endpoint =
117 m : kEndpointPrefix + base::UintToString16(client_process_id_);
118 :
119 : // Initialize the Reporter process
120 m : InitializeReporter(endpoint.c_str(), url.c_str(),
121 m : data_directory_.path().value().c_str(),
122 m : permanent_failure_directory_.path().value().c_str(),
123 m : &OnUploadProc, &on_upload_invoked_);
124 m : observer_.reset(new testing::UploadObserver(
125 m : server_.incoming_directory(), permanent_failure_directory_.path()));
126 m : OnInitialized();
127 :
128 : // Shut down the Reporter process. This will block on upload completion.
129 m : ShutdownReporter();
130 :
131 m : base::FilePath minidump_path;
132 m : std::map<std::string, std::string> crash_keys;
133 m : bool success = false;
134 m : observer_->WaitForUpload(&minidump_path, &crash_keys, &success);
135 m : CHECK_EQ(success, on_upload_invoked_);
136 :
137 m : OnComplete(success, minidump_path, crash_keys);
138 m : }
139 :
140 m : protected:
141 m : base::ProcessId client_process_id() { return client_process_id_; }
142 :
143 m : private:
144 : // Invoked once the reporter is initialized. The reporter will be shut down
145 : // when this method returns.
146 m : virtual void OnInitialized() = 0;
147 :
148 : // Invoked when the minidump upload has been received by the test server.
149 m : virtual void OnComplete(
150 m : bool success,
151 m : const base::FilePath& minidump_path,
152 m : const std::map<std::string, std::string>& crash_keys) = 0;
153 :
154 m : base::ProcessId client_process_id_;
155 m : base::ScopedTempDir permanent_failure_directory_;
156 m : base::ScopedTempDir data_directory_;
157 m : testing::TestServer server_;
158 m : bool on_upload_invoked_;
159 m : std::unique_ptr<testing::UploadObserver> observer_;
160 :
161 m : DISALLOW_COPY_AND_ASSIGN(ChildProcess);
162 m : };
163 :
164 : // Takes a snapshot of all threads in the system and returns the first one from
165 : // |process|.
166 m : base::PlatformThreadId GetMainThreadFromProcess(const base::Process& process) {
167 m : base::win::ScopedHandle thread_snapshot(
168 m : CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
169 m : if (!thread_snapshot.IsValid())
170 m : return 0;
171 :
172 m : THREADENTRY32 te32 = {0};
173 m : te32.dwSize = sizeof(THREADENTRY32);
174 :
175 m : if (!Thread32First(thread_snapshot.Get(), &te32))
176 m : return 0;
177 :
178 m : do {
179 m : if (te32.th32OwnerProcessID == process.Pid())
180 m : return te32.th32ThreadID;
181 m : } while (Thread32Next(thread_snapshot.Get(), &te32));
182 :
183 m : return 0;
184 m : }
185 :
186 : // Use an event to tell the client process that it is ready to serve requests.
187 : // Expects to receive a single invocation of SendReport followed by
188 : // the exit of the client process.
189 : // Verifies that kGlobalString is in the dump if and only if kExpectGlobalSwitch
190 : // is used.
191 m : MULTIPROCESS_TEST_MAIN(WaitForClientInvocation) {
192 m : class DoWaitForClientInvocation : public ChildProcess {
193 m : private:
194 m : void OnInitialized() override {
195 m : base::string16 client_process_id_string =
196 m : base::UintToString16(client_process_id());
197 :
198 m : base::Process client_process = base::Process::Open(client_process_id());
199 m : CHECK(client_process.IsValid());
200 :
201 : // Tell the client process that we are active.
202 m : base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
203 m : NULL, FALSE, FALSE,
204 m : (kReadyEventNamePrefix + client_process_id_string).c_str())));
205 m : ready_event.Signal();
206 :
207 : // The client will exit when it has finished invoking
208 : // SendReport.
209 m : int exit_code = 0;
210 m : CHECK(client_process.WaitForExit(&exit_code));
211 m : }
212 :
213 m : void OnComplete(
214 m : bool success,
215 m : const base::FilePath& minidump_path,
216 m : const std::map<std::string, std::string>& crash_keys) override {
217 m : CHECK(success);
218 m : CHECK(crash_keys.end() != crash_keys.find("hello"));
219 m : CHECK_EQ("world", crash_keys.find("hello")->second);
220 : // Make sure that the ""="bar" key was dropped along the way.
221 m : for (auto& entry : crash_keys) {
222 m : CHECK_NE("", entry.first);
223 m : CHECK_NE("bar", entry.first);
224 m : }
225 :
226 m : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
227 m : std::string dump;
228 m : CHECK(base::ReadFileToString(minidump_path, &dump));
229 m : if (cmd_line->HasSwitch(kExpectGlobalSwitch)) {
230 m : CHECK_NE(std::string::npos, dump.find(kGlobalString));
231 m : } else {
232 m : CHECK_EQ(std::string::npos, dump.find(kGlobalString));
233 m : }
234 m : }
235 m : };
236 :
237 m : DoWaitForClientInvocation().Run();
238 :
239 m : return 0;
240 m : }
241 :
242 : // Invokes SendReportForProcess on the client (parent) process and verifies that
243 : // the dump is correctly taken and uploaded.
244 m : MULTIPROCESS_TEST_MAIN(SendReportForProcess) {
245 m : class DoSendReportForProcess : public ChildProcess {
246 m : private:
247 m : static void MinidumpVisitor(IDebugClient4* client,
248 m : IDebugControl* control,
249 m : IDebugSymbols* symbols) {
250 m : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
251 m : bool synthesize_exception = cmd_line->HasSwitch(kSynthesizeException);
252 m : base::win::ScopedComPtr<IDebugAdvanced2> debug_advanced_2;
253 m : HRESULT result = client->QueryInterface(__uuidof(IDebugAdvanced2),
254 m : debug_advanced_2.ReceiveVoid());
255 m : CHECK(SUCCEEDED(result)) << "QueryInterface(IDebugAdvanced2)";
256 m : EXCEPTION_RECORD64 exception_record = {0};
257 m : result = debug_advanced_2->Request(DEBUG_REQUEST_TARGET_EXCEPTION_RECORD,
258 m : nullptr, 0, &exception_record,
259 m : sizeof(exception_record), nullptr);
260 : // If and only if kSynthesizeException, there should be an exception
261 : // record.
262 m : CHECK(!synthesize_exception || SUCCEEDED(result))
263 m : << "IDebugAdvanced2::Request";
264 m : }
265 m : void OnInitialized() override {
266 : // Request a dump of the client process.
267 m : base::char16* keys[] = {L"hello", L"", nullptr};
268 m : base::char16* values[] = {L"world", L"bar", nullptr};
269 : // Open with minimal access as SendReportForProcess() will reopen the
270 : // process with the access it needs.
271 m : base::Process client_process = base::Process::OpenWithAccess(
272 m : client_process_id(), PROCESS_QUERY_LIMITED_INFORMATION);
273 m : CHECK(client_process.IsValid());
274 :
275 m : CONTEXT ctx = {};
276 m : EXCEPTION_RECORD exc_rec = {};
277 m : exc_rec.ExceptionAddress = 0;
278 m : exc_rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
279 m : EXCEPTION_POINTERS exc_ptrs = {&exc_rec, &ctx};
280 :
281 m : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
282 m : bool synthesize_exception = cmd_line->HasSwitch(kSynthesizeException);
283 m : SendReportForProcess(
284 m : client_process.Handle(),
285 m : synthesize_exception ? GetMainThreadFromProcess(client_process) : 0,
286 m : synthesize_exception ? &exc_ptrs : nullptr, SMALL_DUMP_TYPE, keys,
287 m : values);
288 m : }
289 :
290 m : void OnComplete(
291 m : bool success,
292 m : const base::FilePath& minidump_path,
293 m : const std::map<std::string, std::string>& crash_keys) override {
294 m : CHECK(success);
295 m : CHECK(crash_keys.end() != crash_keys.find("hello"));
296 m : CHECK_EQ("world", crash_keys.find("hello")->second);
297 : // Make sure that the ""="bar" key was dropped along the way.
298 m : for (auto& entry : crash_keys) {
299 m : CHECK_NE("", entry.first);
300 m : CHECK_NE("bar", entry.first);
301 m : }
302 m : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
303 m : if (cmd_line->HasSwitch(kExpectRegisteredKeys)) {
304 m : CHECK(crash_keys.end() != crash_keys.find("largest"));
305 m : CHECK_EQ("Jupiter", crash_keys.find("largest")->second);
306 m : CHECK(crash_keys.end() != crash_keys.find("inhabitable"));
307 m : CHECK_EQ("Earth", crash_keys.find("inhabitable")->second);
308 m : }
309 m : CHECK(SUCCEEDED(
310 m : testing::VisitMinidump(minidump_path, base::Bind(&MinidumpVisitor))));
311 m : }
312 m : };
313 :
314 m : DoSendReportForProcess().Run();
315 :
316 m : return 0;
317 m : }
318 :
319 m : MemoryRange GetMemoryRange() {
320 m : MemoryRange memory_range = {reinterpret_cast<const void*>(kGlobalString),
321 m : sizeof(kGlobalString)};
322 m : return memory_range;
323 m : }
324 :
325 : // Waits for the reporter process to signal readiness, invokes SendReport, then
326 : // exits.
327 m : MULTIPROCESS_TEST_MAIN(ClientProcess) {
328 m : base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
329 m : nullptr, false, false,
330 m : (kReadyEventNamePrefix + base::UintToString16(base::GetCurrentProcId()))
331 m : .c_str())));
332 m : ready_event.Wait();
333 : // Initialize the Client process.
334 m : InitializeClient(
335 m : (kEndpointPrefix + base::UintToString16(base::GetCurrentProcId()))
336 m : .c_str());
337 :
338 : // Send up a crash report.
339 m : CONTEXT ctx = {};
340 m : ::RtlCaptureContext(&ctx);
341 m : EXCEPTION_RECORD exc_rec = {};
342 m : exc_rec.ExceptionAddress = reinterpret_cast<void*>(ctx.Eip);
343 m : exc_rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
344 m : EXCEPTION_POINTERS exc_ptrs = {&exc_rec, &ctx};
345 :
346 m : CrashKey crash_keys[] = {{L"hello", L"world"}, {L"", L"bar"}};
347 :
348 m : std::vector<MemoryRange> memory_ranges;
349 : // GetMemoryRange is extracted to prevent kGlobalString from unintentionally
350 : // being on the stack and potentially being included for that reason.
351 m : if (base::CommandLine::ForCurrentProcess()->HasSwitch(kExpectGlobalSwitch))
352 m : memory_ranges.push_back(GetMemoryRange());
353 :
354 m : SendReport(&exc_ptrs, SMALL_DUMP_TYPE, NULL, 0, crash_keys,
355 m : arraysize(crash_keys), memory_ranges.data(), memory_ranges.size());
356 :
357 m : ShutdownClient();
358 m : return 0;
359 m : }
360 :
361 : // Starts up child client and reporter processes. The client will request a
362 : // report, and the reporter will generate, upload, and then verify the report.
363 : // If |request_memory_range| is true, inclusion of kGlobalString will be
364 : // requested (and verified).
365 m : void DoInvokeSendReport(bool request_memory_range) {
366 : // Start building the Client process command line.
367 m : base::CommandLine client_command_line =
368 m : base::GetMultiProcessTestChildBaseCommandLine();
369 m : client_command_line.AppendSwitchASCII(switches::kTestChildProcess,
370 m : "ClientProcess");
371 m : if (request_memory_range)
372 m : client_command_line.AppendSwitch(kExpectGlobalSwitch);
373 : // Launch the Client process.
374 m : base::Process client_process =
375 m : base::LaunchProcess(client_command_line, base::LaunchOptions());
376 m : ASSERT_TRUE(client_process.IsValid());
377 : // Make sure that we terminate the client process, even if we ASSERT out of
378 : // here.
379 m : base::ScopedClosureRunner terminate_client_process(
380 m : base::Bind(base::IgnoreResult(&base::Process::Terminate),
381 m : base::Unretained(&client_process), 0, true));
382 :
383 : // Start building the Reporter process command line.
384 m : base::CommandLine reporter_command_line =
385 m : base::GetMultiProcessTestChildBaseCommandLine();
386 m : reporter_command_line.AppendSwitchASCII(switches::kTestChildProcess,
387 m : "WaitForClientInvocation");
388 :
389 : // Pass the client process ID, used to share event and RPC endpoint names.
390 m : reporter_command_line.AppendSwitchASCII(
391 m : kClientProcessIdSwitch, base::UintToString(client_process.Pid()));
392 :
393 m : if (request_memory_range)
394 m : reporter_command_line.AppendSwitch(kExpectGlobalSwitch);
395 :
396 : // Launch the Reporter process and wait until it is fully initialized.
397 m : base::Process reporter_process =
398 m : base::LaunchProcess(reporter_command_line, base::LaunchOptions());
399 m : ASSERT_TRUE(reporter_process.IsValid());
400 : // Make sure that we terminate the reporter process, even if we ASSERT out of
401 : // here.
402 m : base::ScopedClosureRunner terminate_reporter_process(
403 m : base::Bind(base::IgnoreResult(&base::Process::Terminate),
404 m : base::Unretained(&reporter_process), 0, true));
405 :
406 : // The client will wait for the reporter to signal a "ready" event.
407 : // The client will then invoke SendReport and exit.
408 : // The reporter process will exit after the generated report has been uploaded
409 : // and its contents verified.
410 :
411 : // Wait for the reporter process to exit and verify its status code.
412 m : int exit_code = 0;
413 m : reporter_process.WaitForExit(&exit_code);
414 m : ASSERT_EQ(0, exit_code);
415 m : }
416 :
417 m : } // namespace
418 :
419 m : TEST(ApiTest, ExportedConstants) {
420 : // Verify that these constants are exported.
421 m : base::string16 crash_keys_extension(
422 m : kasko::api::kPermanentFailureCrashKeysExtension);
423 m : base::string16 minidump_extension(
424 m : kasko::api::kPermanentFailureMinidumpExtension);
425 m : }
426 :
427 m : TEST(ApiTest, SendReport) {
428 m : DoInvokeSendReport(false); // Without explicit memory ranges.
429 m : DoInvokeSendReport(true); // With explicit memory ranges.
430 m : }
431 :
432 : // Signals when it is done registering crash keys and then sleeps indefinitely.
433 : // The parent process is responsible for terminating this child after
434 : // SendReportForProcess has successfully been invoked on it.
435 m : MULTIPROCESS_TEST_MAIN(RegisterCrashKeysClient) {
436 m : CrashKey crash_keys[] = {
437 m : {L"largest", L"Jupiter"}, {L"inhabitable", L"Earth"}, {L"", L""}};
438 :
439 m : RegisterCrashKeys(crash_keys, arraysize(crash_keys));
440 :
441 m : base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
442 m : nullptr, false, false,
443 m : (kReadyEventNamePrefix + base::UintToString16(base::GetCurrentProcId()))
444 m : .c_str())));
445 m : ready_event.Signal();
446 m : ::Sleep(INFINITE);
447 m : return 0;
448 m : }
449 :
450 m : TEST(ApiTest, CrashKeyRegistrationTest) {
451 m : base::CommandLine client_command_line =
452 m : base::GetMultiProcessTestChildBaseCommandLine();
453 m : client_command_line.AppendSwitchASCII(switches::kTestChildProcess,
454 m : "RegisterCrashKeysClient");
455 :
456 : // Launch the Client process.
457 m : base::Process client_process =
458 m : base::LaunchProcess(client_command_line, base::LaunchOptions());
459 m : ASSERT_TRUE(client_process.IsValid());
460 : // Make sure that we terminate the client process, even if we ASSERT out of
461 : // here.
462 m : base::ScopedClosureRunner terminate_client_process(
463 m : base::Bind(base::IgnoreResult(&base::Process::Terminate),
464 m : base::Unretained(&client_process), 0, true));
465 :
466 : // Wait for the child to be initialized.
467 m : base::WaitableEvent child_ready_event(base::win::ScopedHandle(::CreateEvent(
468 m : nullptr, false, false,
469 m : (kReadyEventNamePrefix + base::UintToString16(client_process.Pid()))
470 m : .c_str())));
471 m : child_ready_event.Wait();
472 :
473 : // Launch a Reporter process that will call SendReportForProcess and then
474 : // verify that the registered keys are included.
475 :
476 : // Start building the Reporter process command line.
477 m : base::CommandLine reporter_command_line =
478 m : base::GetMultiProcessTestChildBaseCommandLine();
479 m : reporter_command_line.AppendSwitchASCII(switches::kTestChildProcess,
480 m : "SendReportForProcess");
481 m : reporter_command_line.AppendSwitch(kExpectRegisteredKeys);
482 :
483 : // Pass the client process ID, used to call SendReportForProcess.
484 m : reporter_command_line.AppendSwitchASCII(
485 m : kClientProcessIdSwitch, base::UintToString(client_process.Pid()));
486 :
487 : // Launch the Reporter process.
488 m : base::Process reporter_process =
489 m : base::LaunchProcess(reporter_command_line, base::LaunchOptions());
490 m : ASSERT_TRUE(reporter_process.IsValid());
491 :
492 : // The Reporter process will exit after taking a dump of us and verifying its
493 : // contents.
494 :
495 : // Wait for the reporter process to exit and verify its status code.
496 m : int exit_code = 0;
497 m : reporter_process.WaitForExit(&exit_code);
498 m : ASSERT_EQ(0, exit_code);
499 m : }
500 :
501 : // Signals when it is executing and then sleeps indefinitely. The parent process
502 : // is responsible for terminating this child after SendReportForProcess has
503 : // successfully been invoked on it.
504 m : MULTIPROCESS_TEST_MAIN(IdleChildProcess) {
505 m : base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
506 m : nullptr, false, false,
507 m : (kReadyEventNamePrefix + base::UintToString16(base::GetCurrentProcId()))
508 m : .c_str())));
509 m : ready_event.Signal();
510 m : ::Sleep(INFINITE);
511 m : return 0;
512 m : }
513 :
514 : // Starts up a child process to be reported on, and then instantiates a reporter
515 : // process that generates and verifies the report.
516 m : TEST(ApiTest, SendReportForProcessTest) {
517 m : base::CommandLine client_command_line =
518 m : base::GetMultiProcessTestChildBaseCommandLine();
519 m : client_command_line.AppendSwitchASCII(switches::kTestChildProcess,
520 m : "IdleChildProcess");
521 :
522 : // Launch the Client process.
523 m : base::Process client_process =
524 m : base::LaunchProcess(client_command_line, base::LaunchOptions());
525 m : ASSERT_TRUE(client_process.IsValid());
526 : // Make sure that we terminate the client process, even if we ASSERT out of
527 : // here.
528 m : base::ScopedClosureRunner terminate_client_process(
529 m : base::Bind(base::IgnoreResult(&base::Process::Terminate),
530 m : base::Unretained(&client_process), 0, true));
531 :
532 : // Wait for the child to be initialized.
533 m : base::WaitableEvent child_ready_event(base::win::ScopedHandle(::CreateEvent(
534 m : nullptr, false, false,
535 m : (kReadyEventNamePrefix + base::UintToString16(client_process.Pid()))
536 m : .c_str())));
537 m : child_ready_event.Wait();
538 :
539 : // Start building the Reporter process command line.
540 m : base::CommandLine reporter_command_line =
541 m : base::GetMultiProcessTestChildBaseCommandLine();
542 m : reporter_command_line.AppendSwitchASCII(switches::kTestChildProcess,
543 m : "SendReportForProcess");
544 :
545 : // Pass the client process ID, used to call SendReportForProcess.
546 m : reporter_command_line.AppendSwitchASCII(
547 m : kClientProcessIdSwitch, base::UintToString(client_process.Pid()));
548 :
549 : // Launch the Reporter process.
550 m : base::Process reporter_process =
551 m : base::LaunchProcess(reporter_command_line, base::LaunchOptions());
552 m : ASSERT_TRUE(reporter_process.IsValid());
553 :
554 : // The Reporter process will exit after taking a dump of the client and
555 : // verifying its contents.
556 :
557 : // Wait for the reporter process to exit and verify its status code.
558 m : int exit_code = 0;
559 m : reporter_process.WaitForExit(&exit_code);
560 m : ASSERT_EQ(0, exit_code);
561 :
562 : // Do it again, with kSynthesizeException.
563 m : reporter_command_line.AppendSwitch(kSynthesizeException);
564 :
565 : // Launch the Reporter process.
566 m : reporter_process =
567 m : base::LaunchProcess(reporter_command_line, base::LaunchOptions());
568 m : ASSERT_TRUE(reporter_process.IsValid());
569 :
570 : // Wait for the reporter process to exit and verify its status code.
571 m : exit_code = 0;
572 m : reporter_process.WaitForExit(&exit_code);
573 m : ASSERT_EQ(0, exit_code);
574 m : }
575 :
576 m : } // namespace api
577 m : } // namespace kasko
|