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 : namespace kasko {
47 : namespace api {
48 :
49 : namespace {
50 :
51 : const char kGlobalString[] = "a global string";
52 :
53 : const char kClientProcessIdSwitch[] = "client-process-id";
54 : const char kExpectGlobalSwitch[] = "expect-global";
55 : const char kExpectRegisteredKeys[] = "expect-registered-keys";
56 : const char kSynthesizeException[] = "synthesize-exception";
57 :
58 : const base::char16 kExitEventNamePrefix[] = L"kasko_api_test_exit_event_";
59 : const base::char16 kReadyEventNamePrefix[] = L"kasko_api_test_ready_event_";
60 : 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 : void OnUploadProc(void* context,
65 : const base::char16* report_id,
66 : const base::char16* minidump_path,
67 : const base::char16* const* keys,
68 E : const base::char16* const* values) {
69 E : CHECK(report_id);
70 E : CHECK(report_id[0] != 0);
71 E : CHECK(minidump_path);
72 E : CHECK(minidump_path[0] != 0);
73 E : CHECK(keys[0]);
74 E : CHECK(values[0]);
75 E : bool found_hello_world = false;
76 E : for (int i = 0; keys[i] != nullptr; ++i) {
77 E : if (keys[i] == base::string16(L"hello")) {
78 E : CHECK_EQ(base::string16(L"world"), base::string16(values[i]));
79 E : found_hello_world = true;
80 : }
81 : // Make sure that the ""="bar" key was dropped along the way.
82 E : CHECK_NE(values[i], base::string16(L"bar"));
83 E : CHECK_NE(keys[i], base::string16());
84 E : }
85 E : CHECK(found_hello_world);
86 E : *reinterpret_cast<bool*>(context) = true;
87 E : }
88 :
89 : // Implements the setup and teardown of a child process that runs a Kasko
90 : // reporter.
91 : class ChildProcess {
92 : public:
93 E : ChildProcess() : client_process_id_(0), on_upload_invoked_(false) {
94 E : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
95 : base::string16 client_process_id_string = base::ASCIIToUTF16(
96 E : cmd_line->GetSwitchValueASCII(kClientProcessIdSwitch));
97 E : unsigned int client_process_id_uint = 0;
98 E : CHECK(
99 : base::StringToUint(client_process_id_string, &client_process_id_uint));
100 E : client_process_id_ = client_process_id_uint;
101 E : CHECK(permanent_failure_directory_.CreateUniqueTempDir());
102 :
103 : // Set up a directory for the Reporter to generate and store crash dumps.
104 E : CHECK(data_directory_.CreateUniqueTempDir());
105 :
106 : // Start up a test server to receive uploads.
107 E : CHECK(server_.Start());
108 E : }
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 E : void Run() {
114 : base::string16 url =
115 E : L"http://127.0.0.1:" + base::UintToString16(server_.port()) + L"/crash";
116 : base::string16 endpoint =
117 E : kEndpointPrefix + base::UintToString16(client_process_id_);
118 :
119 : // Initialize the Reporter process
120 : InitializeReporter(endpoint.c_str(), url.c_str(),
121 : data_directory_.path().value().c_str(),
122 : permanent_failure_directory_.path().value().c_str(),
123 E : &OnUploadProc, &on_upload_invoked_);
124 : observer_.reset(new testing::UploadObserver(
125 E : server_.incoming_directory(), permanent_failure_directory_.path()));
126 E : OnInitialized();
127 :
128 : // Shut down the Reporter process. This will block on upload completion.
129 E : ShutdownReporter();
130 :
131 E : base::FilePath minidump_path;
132 E : std::map<std::string, std::string> crash_keys;
133 E : bool success = false;
134 E : observer_->WaitForUpload(&minidump_path, &crash_keys, &success);
135 E : CHECK_EQ(success, on_upload_invoked_);
136 :
137 E : OnComplete(success, minidump_path, crash_keys);
138 E : }
139 :
140 : protected:
141 E : base::ProcessId client_process_id() { return client_process_id_; }
142 :
143 : private:
144 : // Invoked once the reporter is initialized. The reporter will be shut down
145 : // when this method returns.
146 : virtual void OnInitialized() = 0;
147 :
148 : // Invoked when the minidump upload has been received by the test server.
149 : virtual void OnComplete(
150 : bool success,
151 : const base::FilePath& minidump_path,
152 : const std::map<std::string, std::string>& crash_keys) = 0;
153 :
154 : base::ProcessId client_process_id_;
155 : base::ScopedTempDir permanent_failure_directory_;
156 : base::ScopedTempDir data_directory_;
157 : testing::TestServer server_;
158 : bool on_upload_invoked_;
159 : scoped_ptr<testing::UploadObserver> observer_;
160 :
161 : DISALLOW_COPY_AND_ASSIGN(ChildProcess);
162 : };
163 :
164 : // Takes a snapshot of all threads in the system and returns the first one from
165 : // |process|.
166 E : base::PlatformThreadId GetMainThreadFromProcess(const base::Process& process) {
167 : base::win::ScopedHandle thread_snapshot(
168 E : CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
169 E : if (!thread_snapshot.IsValid())
170 i : return 0;
171 :
172 E : THREADENTRY32 te32 = {0};
173 E : te32.dwSize = sizeof(THREADENTRY32);
174 :
175 E : if (!Thread32First(thread_snapshot.Get(), &te32))
176 i : return 0;
177 :
178 : do {
179 E : if (te32.th32OwnerProcessID == process.Pid())
180 E : return te32.th32ThreadID;
181 E : } while (Thread32Next(thread_snapshot.Get(), &te32));
182 :
183 i : return 0;
184 E : }
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 E : MULTIPROCESS_TEST_MAIN(WaitForClientInvocation) {
192 : class DoWaitForClientInvocation : public ChildProcess {
193 : private:
194 E : void OnInitialized() override {
195 : base::string16 client_process_id_string =
196 E : base::UintToString16(client_process_id());
197 :
198 E : base::Process client_process = base::Process::Open(client_process_id());
199 E : CHECK(client_process.IsValid());
200 :
201 : // Tell the client process that we are active.
202 : base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
203 : NULL, FALSE, FALSE,
204 E : (kReadyEventNamePrefix + client_process_id_string).c_str())));
205 E : ready_event.Signal();
206 :
207 : // The client will exit when it has finished invoking
208 : // SendReport.
209 E : int exit_code = 0;
210 E : CHECK(client_process.WaitForExit(&exit_code));
211 E : }
212 :
213 : void OnComplete(
214 : bool success,
215 : const base::FilePath& minidump_path,
216 E : const std::map<std::string, std::string>& crash_keys) override {
217 E : CHECK(success);
218 E : CHECK(crash_keys.end() != crash_keys.find("hello"));
219 E : CHECK_EQ("world", crash_keys.find("hello")->second);
220 : // Make sure that the ""="bar" key was dropped along the way.
221 E : for (auto& entry : crash_keys) {
222 E : CHECK_NE("", entry.first);
223 E : CHECK_NE("bar", entry.first);
224 : }
225 :
226 E : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
227 E : std::string dump;
228 E : CHECK(base::ReadFileToString(minidump_path, &dump));
229 E : if (cmd_line->HasSwitch(kExpectGlobalSwitch)) {
230 E : CHECK_NE(std::string::npos, dump.find(kGlobalString));
231 E : } else {
232 E : CHECK_EQ(std::string::npos, dump.find(kGlobalString));
233 : }
234 E : }
235 : };
236 :
237 E : DoWaitForClientInvocation().Run();
238 :
239 E : return 0;
240 E : }
241 :
242 : // Invokes SendReportForProcess on the client (parent) process and verifies that
243 : // the dump is correctly taken and uploaded.
244 E : MULTIPROCESS_TEST_MAIN(SendReportForProcess) {
245 : class DoSendReportForProcess : public ChildProcess {
246 : private:
247 : static void MinidumpVisitor(IDebugClient4* client,
248 : IDebugControl* control,
249 E : IDebugSymbols* symbols) {
250 E : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
251 E : bool synthesize_exception = cmd_line->HasSwitch(kSynthesizeException);
252 E : base::win::ScopedComPtr<IDebugAdvanced2> debug_advanced_2;
253 : HRESULT result = client->QueryInterface(__uuidof(IDebugAdvanced2),
254 E : debug_advanced_2.ReceiveVoid());
255 E : CHECK(SUCCEEDED(result)) << "QueryInterface(IDebugAdvanced2)";
256 E : EXCEPTION_RECORD64 exception_record = {0};
257 : result = debug_advanced_2->Request(DEBUG_REQUEST_TARGET_EXCEPTION_RECORD,
258 : nullptr, 0, &exception_record,
259 E : sizeof(exception_record), nullptr);
260 : // If and only if kSynthesizeException, there should be an exception
261 : // record.
262 E : CHECK(!synthesize_exception || SUCCEEDED(result))
263 : << "IDebugAdvanced2::Request";
264 E : }
265 E : void OnInitialized() override {
266 : // Request a dump of the client process.
267 E : base::char16* keys[] = {L"hello", L"", nullptr};
268 E : 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 : base::Process client_process = base::Process::OpenWithAccess(
272 E : client_process_id(), PROCESS_QUERY_LIMITED_INFORMATION);
273 E : CHECK(client_process.IsValid());
274 :
275 E : CONTEXT ctx = {};
276 E : EXCEPTION_RECORD exc_rec = {};
277 E : exc_rec.ExceptionAddress = 0;
278 E : exc_rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
279 E : EXCEPTION_POINTERS exc_ptrs = {&exc_rec, &ctx};
280 :
281 E : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
282 E : bool synthesize_exception = cmd_line->HasSwitch(kSynthesizeException);
283 : SendReportForProcess(
284 : client_process.Handle(),
285 : synthesize_exception ? GetMainThreadFromProcess(client_process) : 0,
286 : synthesize_exception ? &exc_ptrs : nullptr, SMALL_DUMP_TYPE, keys,
287 E : values);
288 E : }
289 :
290 : void OnComplete(
291 : bool success,
292 : const base::FilePath& minidump_path,
293 E : const std::map<std::string, std::string>& crash_keys) override {
294 E : CHECK(success);
295 E : CHECK(crash_keys.end() != crash_keys.find("hello"));
296 E : CHECK_EQ("world", crash_keys.find("hello")->second);
297 : // Make sure that the ""="bar" key was dropped along the way.
298 E : for (auto& entry : crash_keys) {
299 E : CHECK_NE("", entry.first);
300 E : CHECK_NE("bar", entry.first);
301 : }
302 E : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
303 E : if (cmd_line->HasSwitch(kExpectRegisteredKeys)) {
304 E : CHECK(crash_keys.end() != crash_keys.find("largest"));
305 E : CHECK_EQ("Jupiter", crash_keys.find("largest")->second);
306 E : CHECK(crash_keys.end() != crash_keys.find("inhabitable"));
307 E : CHECK_EQ("Earth", crash_keys.find("inhabitable")->second);
308 : }
309 E : CHECK(SUCCEEDED(
310 : testing::VisitMinidump(minidump_path, base::Bind(&MinidumpVisitor))));
311 E : }
312 : };
313 :
314 E : DoSendReportForProcess().Run();
315 :
316 E : return 0;
317 E : }
318 :
319 E : MemoryRange GetMemoryRange() {
320 : MemoryRange memory_range = {reinterpret_cast<const void*>(kGlobalString),
321 E : sizeof(kGlobalString)};
322 E : return memory_range;
323 E : }
324 :
325 : // Waits for the reporter process to signal readiness, invokes SendReport, then
326 : // exits.
327 E : MULTIPROCESS_TEST_MAIN(ClientProcess) {
328 : base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
329 : nullptr, false, false,
330 : (kReadyEventNamePrefix + base::UintToString16(base::GetCurrentProcId()))
331 E : .c_str())));
332 E : ready_event.Wait();
333 : // Initialize the Client process.
334 : InitializeClient(
335 : (kEndpointPrefix + base::UintToString16(base::GetCurrentProcId()))
336 E : .c_str());
337 :
338 : // Send up a crash report.
339 E : CONTEXT ctx = {};
340 E : ::RtlCaptureContext(&ctx);
341 E : EXCEPTION_RECORD exc_rec = {};
342 E : exc_rec.ExceptionAddress = reinterpret_cast<void*>(ctx.Eip);
343 E : exc_rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
344 E : EXCEPTION_POINTERS exc_ptrs = {&exc_rec, &ctx};
345 :
346 E : CrashKey crash_keys[] = {{L"hello", L"world"}, {L"", L"bar"}};
347 :
348 E : 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 E : if (base::CommandLine::ForCurrentProcess()->HasSwitch(kExpectGlobalSwitch))
352 E : memory_ranges.push_back(GetMemoryRange());
353 :
354 : SendReport(&exc_ptrs, SMALL_DUMP_TYPE, NULL, 0, crash_keys,
355 E : arraysize(crash_keys), memory_ranges.data(), memory_ranges.size());
356 :
357 E : ShutdownClient();
358 E : return 0;
359 E : }
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 E : void DoInvokeSendReport(bool request_memory_range) {
366 : // Start building the Client process command line.
367 : base::CommandLine client_command_line =
368 E : base::GetMultiProcessTestChildBaseCommandLine();
369 : client_command_line.AppendSwitchASCII(switches::kTestChildProcess,
370 E : "ClientProcess");
371 E : if (request_memory_range)
372 E : client_command_line.AppendSwitch(kExpectGlobalSwitch);
373 : // Launch the Client process.
374 : base::Process client_process =
375 E : base::LaunchProcess(client_command_line, base::LaunchOptions());
376 E : ASSERT_TRUE(client_process.IsValid());
377 : // Make sure that we terminate the client process, even if we ASSERT out of
378 : // here.
379 : base::ScopedClosureRunner terminate_client_process(
380 : base::Bind(base::IgnoreResult(&base::Process::Terminate),
381 E : base::Unretained(&client_process), 0, true));
382 :
383 : // Start building the Reporter process command line.
384 : base::CommandLine reporter_command_line =
385 E : base::GetMultiProcessTestChildBaseCommandLine();
386 : reporter_command_line.AppendSwitchASCII(switches::kTestChildProcess,
387 E : "WaitForClientInvocation");
388 :
389 : // Pass the client process ID, used to share event and RPC endpoint names.
390 : reporter_command_line.AppendSwitchASCII(
391 E : kClientProcessIdSwitch, base::UintToString(client_process.Pid()));
392 :
393 E : if (request_memory_range)
394 E : reporter_command_line.AppendSwitch(kExpectGlobalSwitch);
395 :
396 : // Launch the Reporter process and wait until it is fully initialized.
397 : base::Process reporter_process =
398 E : base::LaunchProcess(reporter_command_line, base::LaunchOptions());
399 E : ASSERT_TRUE(reporter_process.IsValid());
400 : // Make sure that we terminate the reporter process, even if we ASSERT out of
401 : // here.
402 : base::ScopedClosureRunner terminate_reporter_process(
403 : base::Bind(base::IgnoreResult(&base::Process::Terminate),
404 E : 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 E : int exit_code = 0;
413 E : reporter_process.WaitForExit(&exit_code);
414 E : ASSERT_EQ(0, exit_code);
415 E : }
416 :
417 : } // namespace
418 :
419 E : TEST(ApiTest, ExportedConstants) {
420 : // Verify that these constants are exported.
421 : base::string16 crash_keys_extension(
422 E : kasko::api::kPermanentFailureCrashKeysExtension);
423 : base::string16 minidump_extension(
424 E : kasko::api::kPermanentFailureMinidumpExtension);
425 E : }
426 :
427 E : TEST(ApiTest, SendReport) {
428 E : DoInvokeSendReport(false); // Without explicit memory ranges.
429 E : DoInvokeSendReport(true); // With explicit memory ranges.
430 E : }
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 E : MULTIPROCESS_TEST_MAIN(RegisterCrashKeysClient) {
436 : CrashKey crash_keys[] = {
437 E : {L"largest", L"Jupiter"}, {L"inhabitable", L"Earth"}, {L"", L""}};
438 :
439 E : RegisterCrashKeys(crash_keys, arraysize(crash_keys));
440 :
441 : base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
442 : nullptr, false, false,
443 : (kReadyEventNamePrefix + base::UintToString16(base::GetCurrentProcId()))
444 E : .c_str())));
445 E : ready_event.Signal();
446 E : ::Sleep(INFINITE);
447 E : return 0;
448 E : }
449 :
450 E : TEST(ApiTest, CrashKeyRegistrationTest) {
451 : base::CommandLine client_command_line =
452 E : base::GetMultiProcessTestChildBaseCommandLine();
453 : client_command_line.AppendSwitchASCII(switches::kTestChildProcess,
454 E : "RegisterCrashKeysClient");
455 :
456 : // Launch the Client process.
457 : base::Process client_process =
458 E : base::LaunchProcess(client_command_line, base::LaunchOptions());
459 E : ASSERT_TRUE(client_process.IsValid());
460 : // Make sure that we terminate the client process, even if we ASSERT out of
461 : // here.
462 : base::ScopedClosureRunner terminate_client_process(
463 : base::Bind(base::IgnoreResult(&base::Process::Terminate),
464 E : base::Unretained(&client_process), 0, true));
465 :
466 : // Wait for the child to be initialized.
467 : base::WaitableEvent child_ready_event(base::win::ScopedHandle(::CreateEvent(
468 : nullptr, false, false,
469 : (kReadyEventNamePrefix + base::UintToString16(client_process.Pid()))
470 E : .c_str())));
471 E : 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 : base::CommandLine reporter_command_line =
478 E : base::GetMultiProcessTestChildBaseCommandLine();
479 : reporter_command_line.AppendSwitchASCII(switches::kTestChildProcess,
480 E : "SendReportForProcess");
481 E : reporter_command_line.AppendSwitch(kExpectRegisteredKeys);
482 :
483 : // Pass the client process ID, used to call SendReportForProcess.
484 : reporter_command_line.AppendSwitchASCII(
485 E : kClientProcessIdSwitch, base::UintToString(client_process.Pid()));
486 :
487 : // Launch the Reporter process.
488 : base::Process reporter_process =
489 E : base::LaunchProcess(reporter_command_line, base::LaunchOptions());
490 E : 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 E : int exit_code = 0;
497 E : reporter_process.WaitForExit(&exit_code);
498 E : ASSERT_EQ(0, exit_code);
499 E : }
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 E : MULTIPROCESS_TEST_MAIN(IdleChildProcess) {
505 : base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
506 : nullptr, false, false,
507 : (kReadyEventNamePrefix + base::UintToString16(base::GetCurrentProcId()))
508 E : .c_str())));
509 E : ready_event.Signal();
510 E : ::Sleep(INFINITE);
511 E : return 0;
512 E : }
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 E : TEST(ApiTest, SendReportForProcessTest) {
517 : base::CommandLine client_command_line =
518 E : base::GetMultiProcessTestChildBaseCommandLine();
519 : client_command_line.AppendSwitchASCII(switches::kTestChildProcess,
520 E : "IdleChildProcess");
521 :
522 : // Launch the Client process.
523 : base::Process client_process =
524 E : base::LaunchProcess(client_command_line, base::LaunchOptions());
525 E : ASSERT_TRUE(client_process.IsValid());
526 : // Make sure that we terminate the client process, even if we ASSERT out of
527 : // here.
528 : base::ScopedClosureRunner terminate_client_process(
529 : base::Bind(base::IgnoreResult(&base::Process::Terminate),
530 E : base::Unretained(&client_process), 0, true));
531 :
532 : // Wait for the child to be initialized.
533 : base::WaitableEvent child_ready_event(base::win::ScopedHandle(::CreateEvent(
534 : nullptr, false, false,
535 : (kReadyEventNamePrefix + base::UintToString16(client_process.Pid()))
536 E : .c_str())));
537 E : child_ready_event.Wait();
538 :
539 : // Start building the Reporter process command line.
540 : base::CommandLine reporter_command_line =
541 E : base::GetMultiProcessTestChildBaseCommandLine();
542 : reporter_command_line.AppendSwitchASCII(switches::kTestChildProcess,
543 E : "SendReportForProcess");
544 :
545 : // Pass the client process ID, used to call SendReportForProcess.
546 : reporter_command_line.AppendSwitchASCII(
547 E : kClientProcessIdSwitch, base::UintToString(client_process.Pid()));
548 :
549 : // Launch the Reporter process.
550 : base::Process reporter_process =
551 E : base::LaunchProcess(reporter_command_line, base::LaunchOptions());
552 E : 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 E : int exit_code = 0;
559 E : reporter_process.WaitForExit(&exit_code);
560 E : ASSERT_EQ(0, exit_code);
561 :
562 : // Do it again, with kSynthesizeException.
563 E : reporter_command_line.AppendSwitch(kSynthesizeException);
564 :
565 : // Launch the Reporter process.
566 : reporter_process =
567 E : base::LaunchProcess(reporter_command_line, base::LaunchOptions());
568 E : ASSERT_TRUE(reporter_process.IsValid());
569 :
570 : // Wait for the reporter process to exit and verify its status code.
571 E : exit_code = 0;
572 E : reporter_process.WaitForExit(&exit_code);
573 E : ASSERT_EQ(0, exit_code);
574 E : }
575 :
576 : } // namespace api
577 : } // namespace kasko
|