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 "base/base_switches.h"
16 : #include "base/bind.h"
17 : #include "base/command_line.h"
18 : #include "base/logging.h"
19 : #include "base/files/file_path.h"
20 : #include "base/files/scoped_temp_dir.h"
21 : #include "base/process/kill.h"
22 : #include "base/process/launch.h"
23 : #include "base/process/process_handle.h"
24 : #include "base/strings/string16.h"
25 : #include "base/strings/string_number_conversions.h"
26 : #include "base/strings/utf_string_conversions.h"
27 : #include "base/synchronization/waitable_event.h"
28 : #include "base/test/multiprocess_test.h"
29 : #include "base/win/scoped_handle.h"
30 : #include "syzygy/kasko/api/client.h"
31 : #include "syzygy/kasko/api/reporter.h"
32 : #include "syzygy/kasko/testing/minidump_unittest_helpers.h"
33 : #include "syzygy/kasko/testing/test_server.h"
34 : #include "testing/multiprocess_func_list.h"
35 : #include "testing/gtest/include/gtest/gtest.h"
36 :
37 : namespace kasko {
38 : namespace api {
39 :
40 : namespace {
41 :
42 : const char kTestInstanceKeySwitch[] = "test-instance-key";
43 : const char kClientProcessIdSwitch[] = "client-process-id";
44 : const base::char16 kExitEventNamePrefix[] = L"kasko_api_test_exit_event_";
45 : const base::char16 kReadyEventNamePrefix[] = L"kasko_api_test_ready_event_";
46 : const base::char16 kEndpointPrefix[] = L"kasko_api_test_endpoint_";
47 :
48 E : MULTIPROCESS_TEST_MAIN(ApiTestReporterProcess) {
49 : // Read the client-supplied parameters.
50 E : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
51 E : unsigned int client_process_id_uint = 0;
52 : CHECK(
53 : base::StringToUint(cmd_line->GetSwitchValueASCII(kClientProcessIdSwitch),
54 E : &client_process_id_uint));
55 E : base::ProcessId client_process_id = client_process_id_uint;
56 : base::string16 test_instance_key =
57 E : base::ASCIIToUTF16(cmd_line->GetSwitchValueASCII(kTestInstanceKeySwitch));
58 E : base::string16 endpoint = kEndpointPrefix + test_instance_key;
59 E : base::ScopedTempDir permanent_failure_directory;
60 E : CHECK(permanent_failure_directory.CreateUniqueTempDir());
61 :
62 : // Set up a directory for the Reporter to generate and store crash dumps.
63 E : base::ScopedTempDir data_directory;
64 E : CHECK(data_directory.CreateUniqueTempDir());
65 :
66 : // Create the events used for inter-process synchronization.
67 : base::WaitableEvent exit_event(::CreateEvent(
68 E : NULL, FALSE, FALSE, (kExitEventNamePrefix + test_instance_key).c_str()));
69 : base::WaitableEvent ready_event(::CreateEvent(
70 E : NULL, FALSE, FALSE, (kReadyEventNamePrefix + test_instance_key).c_str()));
71 :
72 : // Start up a test server to receive uploads.
73 E : testing::TestServer server;
74 E : CHECK(server.Start());
75 : base::string16 url =
76 E : L"http://127.0.0.1:" + base::UintToString16(server.port()) + L"/crash";
77 :
78 :
79 : // Initialize the Reporter process
80 : InitializeReporter(endpoint.c_str(), url.c_str(),
81 : data_directory.path().value().c_str(),
82 E : permanent_failure_directory.path().value().c_str());
83 :
84 : // Request a dump of the client process.
85 E : base::char16* keys[] = {L"hello", nullptr};
86 E : base::char16* values[] = {L"world", nullptr};
87 : base::win::ScopedHandle client_process(
88 E : ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, client_process_id));
89 E : CHECK(client_process.IsValid());
90 E : SendReportForProcess(client_process.Get(), SMALL_DUMP_TYPE, keys, values);
91 :
92 : // Tell the client process that we are active.
93 E : ready_event.Signal();
94 :
95 : // Wait until the client signals us to shut down.
96 E : exit_event.Wait();
97 :
98 : // Shut down the Reporter process.
99 E : ShutdownReporter();
100 :
101 E : return 0;
102 E : }
103 :
104 : } // namespace
105 :
106 : // This test exercises all of the API methods of Kasko.dll. It does not do an
107 : // end-to-end test (i.e. by verifying that the requested crash report is
108 : // properly uploaded). The primary reason is that the delay before uploading is
109 : // not configurable via the public API and this test would therefore need to
110 : // wait 3 minutes before observing an upload.
111 E : TEST(ApiTest, BasicTest) {
112 : {
113 : // Verify that these constants are exported.
114 : base::string16 crash_keys_extension(
115 E : kasko::api::kPermanentFailureCrashKeysExtension);
116 : base::string16 minidump_extension(
117 E : kasko::api::kPermanentFailureMinidumpExtension);
118 E : }
119 :
120 : // Pick an ID used to avoid global namespace collisions.
121 : base::string16 test_instance_key =
122 E : base::UintToString16(base::GetCurrentProcId());
123 :
124 : // Create the events used for inter-process synchronization.
125 : base::win::ScopedHandle exit_event_handle(::CreateEvent(
126 E : NULL, FALSE, FALSE, (kExitEventNamePrefix + test_instance_key).c_str()));
127 E : ASSERT_TRUE(exit_event_handle.IsValid());
128 E : base::WaitableEvent exit_event(exit_event_handle.Take());
129 :
130 : base::win::ScopedHandle ready_event_handle(::CreateEvent(
131 E : NULL, FALSE, FALSE, (kReadyEventNamePrefix + test_instance_key).c_str()));
132 E : ASSERT_TRUE(ready_event_handle.IsValid());
133 E : base::WaitableEvent ready_event(ready_event_handle.Take());
134 :
135 : // Start building the Reporter process command line.
136 : base::CommandLine reporter_command_line =
137 E : base::GetMultiProcessTestChildBaseCommandLine();
138 : reporter_command_line.AppendSwitchASCII(switches::kTestChildProcess,
139 E : "ApiTestReporterProcess");
140 :
141 : // Pass the test instance ID.
142 : reporter_command_line.AppendSwitchASCII(
143 E : kTestInstanceKeySwitch, base::UTF16ToASCII(test_instance_key));
144 :
145 : // Pass the client process ID, used by the reporter to invoke
146 : // SendReportForProcess.
147 : reporter_command_line.AppendSwitchASCII(
148 E : kClientProcessIdSwitch, base::UintToString(base::GetCurrentProcId()));
149 :
150 : // Launch the Reporter process and wait until it is fully initialized.
151 : base::ProcessHandle reporter_process;
152 : ASSERT_TRUE(base::LaunchProcess(reporter_command_line, base::LaunchOptions(),
153 E : &reporter_process));
154 E : ready_event.Wait();
155 :
156 : // Initialize the Client process.
157 E : InitializeClient((kEndpointPrefix + test_instance_key).c_str());
158 :
159 : // Send up a crash report.
160 E : CONTEXT ctx = {};
161 E : ::RtlCaptureContext(&ctx);
162 E : EXCEPTION_RECORD exc_rec = {};
163 E : exc_rec.ExceptionAddress = reinterpret_cast<void*>(ctx.Eip);
164 E : exc_rec.ExceptionCode = EXCEPTION_ARRAY_BOUNDS_EXCEEDED;
165 E : EXCEPTION_POINTERS exc_ptrs = { &exc_rec, &ctx };
166 :
167 E : CrashKey crash_keys[] = {{L"hello", L"world"}, {L"", L"bar"}};
168 : SendReport(&exc_ptrs, SMALL_DUMP_TYPE, NULL, 0, crash_keys,
169 E : arraysize(crash_keys));
170 :
171 : // TODO(erikwright): Wait for the upload and verify the report contents.
172 :
173 : // Shut down the client.
174 E : ShutdownClient();
175 :
176 : // Tell the reporter process that we are done.
177 E : exit_event.Signal();
178 :
179 : // Wait for the reporter process to exit and verify its status code.
180 E : int exit_code = 0;
181 E : base::WaitForExitCode(reporter_process, &exit_code);
182 E : ASSERT_EQ(0, exit_code);
183 E : }
184 :
185 : } // namespace api
186 : } // namespace kasko
|