1 : // Copyright 2014 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 "syzygy/kasko/reporter.h"
16 :
17 : #include <Windows.h> // NOLINT
18 : #include <Dbgeng.h>
19 : #include <Rpc.h>
20 :
21 : #include <string>
22 :
23 : #include "base/base_switches.h"
24 : #include "base/bind.h"
25 : #include "base/command_line.h"
26 : #include "base/files/file_path.h"
27 : #include "base/files/scoped_temp_dir.h"
28 : #include "base/memory/scoped_ptr.h"
29 : #include "base/process/kill.h"
30 : #include "base/process/launch.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/time/time.h"
37 : #include "gtest/gtest.h"
38 : #include "syzygy/common/rpc/helpers.h"
39 : #include "syzygy/kasko/kasko_rpc.h"
40 : #include "syzygy/kasko/minidump_request.h"
41 : #include "syzygy/kasko/version.h"
42 : #include "syzygy/kasko/testing/minidump_unittest_helpers.h"
43 : #include "syzygy/kasko/testing/test_server.h"
44 : #include "syzygy/kasko/testing/upload_observer.h"
45 : #include "testing/multiprocess_func_list.h"
46 :
47 : // The test server will respond to POSTs to /crash by writing all parameters to
48 : // a report directory. Each file in the directory has the name of a parameter
49 : // and the parameter value as its contents.
50 : //
51 : // This test instantiates a reporter process, points it at a test server, and
52 : // then monitors the server's "incoming" director for new files named
53 : // Reporter::kMinidumpUploadFilePart.
54 : //
55 : // These tests are flaky on the bots. They appear to occasionally hang.
56 : // Presumably there is some kind of race condition.
57 : // TODO(erikwright): Debug these on the bots, add additional tracing, or do
58 : // whatever's necessary to diagnose and deflake these tests.
59 : namespace kasko {
60 :
61 : namespace {
62 :
63 : const base::char16 kCrashKey1Name[] = L"foo";
64 : const base::char16 kCrashKey1Value[] = L"bar";
65 : const base::char16 kCrashKey2Name[] = L"hello";
66 : const base::char16 kCrashKey2Value[] = L"world";
67 :
68 : const char kEndpointSwitch[] = "endpoint";
69 : const char kReadyEventSwitch[] = "ready-event";
70 :
71 : // Signals an event named by kReadyEventSwitch, then blocks indefinitely.
72 E : MULTIPROCESS_TEST_MAIN(ReporterTestBlockingProcess) {
73 : // Read the caller-supplied parameters.
74 E : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
75 : base::string16 ready_event_name =
76 E : base::ASCIIToUTF16(cmd_line->GetSwitchValueASCII(kReadyEventSwitch));
77 : base::WaitableEvent ready_event(base::win::ScopedHandle(
78 E : ::OpenEvent(EVENT_MODIFY_STATE, FALSE, ready_event_name.c_str())));
79 E : ready_event.Signal();
80 E : ::Sleep(INFINITE);
81 E : return 0;
82 E : }
83 :
84 : // Invokes SendDiagnosticReport via the RPC endpoint named by kEndpointSwitch.
85 E : MULTIPROCESS_TEST_MAIN(ReporterTestClientProcess) {
86 : // Read the caller-supplied parameters.
87 E : base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
88 : base::string16 endpoint =
89 E : base::ASCIIToUTF16(cmd_line->GetSwitchValueASCII(kEndpointSwitch));
90 E : common::rpc::ScopedRpcBinding rpc_binding;
91 E : if (!rpc_binding.Open(L"ncalrpc", endpoint)) {
92 i : PLOG(ERROR) << "ScopedRpcBinding::Open";
93 i : return 1;
94 : }
95 :
96 : CrashKey crash_keys[] = {{kCrashKey1Name, kCrashKey1Value},
97 E : {kCrashKey2Name, kCrashKey2Value}};
98 : ::MinidumpRequest rpc_request = {0, 0, SMALL_DUMP,
99 : 0, nullptr, arraysize(crash_keys),
100 E : crash_keys, 0, nullptr};
101 :
102 : common::rpc::RpcStatus status = common::rpc::InvokeRpc(
103 E : KaskoClient_SendDiagnosticReport, rpc_binding.Get(), rpc_request);
104 E : if (status.exception_occurred || !status.succeeded()) {
105 i : PLOG(ERROR) << "InvokeRpc";
106 i : return 1;
107 : }
108 E : return 0;
109 E : }
110 :
111 : // Invokes |instance|->SendReportForProcess() using |child_process|.
112 : void InvokeSendReportForProcess(Reporter* instance,
113 E : base::ProcessHandle child_process) {
114 E : MinidumpRequest request;
115 : request.crash_keys.push_back(
116 E : MinidumpRequest::CrashKey(kCrashKey1Name, kCrashKey1Value));
117 : request.crash_keys.push_back(
118 E : MinidumpRequest::CrashKey(kCrashKey2Name, kCrashKey2Value));
119 :
120 E : instance->SendReportForProcess(child_process, 0, request);
121 E : }
122 :
123 : // Verifies that the uploaded minidump is plausibly a dump of this test process.
124 : void ValidateMinidump(IDebugClient4* debug_client,
125 : IDebugControl* debug_control,
126 E : IDebugSymbols* debug_symbols) {
127 : ASSERT_HRESULT_SUCCEEDED(
128 E : debug_symbols->GetModuleByModuleName("kasko_unittests", 0, NULL, NULL));
129 E : }
130 :
131 : void OnUpload(base::string16* report_id_out,
132 : const base::string16& report_id,
133 : const base::FilePath& minidump_path,
134 E : const std::map<base::string16, base::string16>& crash_keys) {
135 E : *report_id_out = report_id;
136 E : ASSERT_FALSE(report_id.empty());
137 E : ASSERT_FALSE(minidump_path.empty());
138 E : }
139 :
140 : } // namespace
141 :
142 : class ReporterTest : public ::testing::Test {
143 : public:
144 E : ReporterTest()
145 : : test_instance_key_(base::UintToString(base::GetCurrentProcId())) {}
146 :
147 E : virtual void SetUp() override {
148 E : ASSERT_TRUE(server_.Start());
149 E : ASSERT_TRUE(temp_directory_.CreateUniqueTempDir());
150 E : }
151 :
152 : protected:
153 : // Launches a child process that will invoke SendDiagnosticReport using the
154 : // RPC endpoint returned by endpoint().
155 E : void InvokeRpcFromChildProcess() {
156 : base::CommandLine client_command_line =
157 E : base::GetMultiProcessTestChildBaseCommandLine();
158 : client_command_line.AppendSwitchASCII(switches::kTestChildProcess,
159 E : "ReporterTestClientProcess");
160 : client_command_line.AppendSwitchASCII(kEndpointSwitch,
161 E : base::UTF16ToASCII(endpoint()));
162 : base::Process client_process = base::LaunchProcess(client_command_line,
163 E : base::LaunchOptions());
164 E : ASSERT_TRUE(client_process.IsValid());
165 :
166 E : int exit_code = 0;
167 E : ASSERT_TRUE(client_process.WaitForExit(&exit_code));
168 E : ASSERT_EQ(0, exit_code);
169 E : }
170 :
171 : // Launches a child process and passes its handle to |callback|. Then kills
172 : // the child process.
173 : void DoWithChildProcess(
174 E : const base::Callback<void(base::ProcessHandle)>& callback) {
175 E : std::string ready_event_name = "reporter_test_ready_" + test_instance_key_;
176 : base::WaitableEvent ready_event(base::win::ScopedHandle(::CreateEvent(
177 E : NULL, FALSE, FALSE, base::ASCIIToUTF16(ready_event_name).c_str())));
178 :
179 : base::CommandLine child_command_line =
180 E : base::GetMultiProcessTestChildBaseCommandLine();
181 : child_command_line.AppendSwitchASCII(switches::kTestChildProcess,
182 E : "ReporterTestBlockingProcess");
183 E : child_command_line.AppendSwitchASCII(kReadyEventSwitch, ready_event_name);
184 : base::Process child_process = base::LaunchProcess(child_command_line,
185 E : base::LaunchOptions());
186 E : ASSERT_TRUE(child_process.IsValid());
187 E : ready_event.Wait();
188 E : callback.Run(child_process.Handle());
189 E : ASSERT_TRUE(child_process.Terminate(0, true));
190 E : }
191 :
192 E : uint16_t server_port() { return server_.port(); }
193 :
194 E : base::string16 endpoint() {
195 E : return base::ASCIIToUTF16("reporter_test_endpoint_" + test_instance_key_);
196 E : }
197 :
198 : // This directory is intentionally non-existant to verify that the reporter
199 : // creates the target directory as needed.
200 E : base::FilePath data_directory() {
201 E : return temp_directory_.path().Append(L"Crash Reports");
202 E : }
203 :
204 : // This directory is intentionally non-existant to verify that the reporter
205 : // creates the target directory as needed.
206 E : base::FilePath permanent_failure_directory() {
207 E : return temp_directory_.path().Append(L"Permanent Failure");
208 E : }
209 :
210 E : base::FilePath upload_directory() { return server_.incoming_directory(); }
211 :
212 : private:
213 : testing::TestServer server_;
214 : base::ScopedTempDir temp_directory_;
215 : std::string test_instance_key_;
216 :
217 : DISALLOW_COPY_AND_ASSIGN(ReporterTest);
218 : };
219 :
220 E : TEST_F(ReporterTest, BasicTest) {
221 E : base::string16 report_id;
222 : scoped_ptr<Reporter> instance(Reporter::Create(
223 : endpoint(),
224 : L"http://127.0.0.1:" + base::UintToString16(server_port()) + L"/crash",
225 : data_directory(), permanent_failure_directory(),
226 : base::TimeDelta::FromMilliseconds(1),
227 : base::TimeDelta::FromMilliseconds(1),
228 E : base::Bind(&OnUpload, base::Unretained(&report_id))));
229 :
230 E : ASSERT_TRUE(instance);
231 :
232 : testing::UploadObserver upload_observer(upload_directory(),
233 E : permanent_failure_directory());
234 :
235 E : ASSERT_NO_FATAL_FAILURE(InvokeRpcFromChildProcess());
236 :
237 E : base::FilePath minidump_path;
238 E : std::map<std::string, std::string> crash_keys;
239 E : bool upload_success = false;
240 :
241 E : upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
242 :
243 E : EXPECT_TRUE(upload_success);
244 : EXPECT_HRESULT_SUCCEEDED(
245 E : testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
246 E : Reporter::Shutdown(instance.Pass());
247 :
248 E : auto entry = crash_keys.find(base::UTF16ToASCII(kCrashKey1Name));
249 E : ASSERT_NE(entry, crash_keys.end());
250 E : ASSERT_EQ(base::UTF16ToASCII(kCrashKey1Value), entry->second);
251 E : entry = crash_keys.find(base::UTF16ToASCII(kCrashKey2Name));
252 E : ASSERT_NE(entry, crash_keys.end());
253 E : ASSERT_EQ(base::UTF16ToASCII(kCrashKey2Value), entry->second);
254 : entry =
255 E : crash_keys.find(base::UTF16ToASCII(Reporter::kKaskoUploadedByVersion));
256 E : ASSERT_NE(entry, crash_keys.end());
257 E : ASSERT_EQ(KASKO_VERSION_STRING, entry->second);
258 : entry =
259 E : crash_keys.find(base::UTF16ToASCII(Reporter::kKaskoGeneratedByVersion));
260 E : ASSERT_NE(entry, crash_keys.end());
261 E : ASSERT_EQ(KASKO_VERSION_STRING, entry->second);
262 :
263 E : ASSERT_FALSE(report_id.empty());
264 E : }
265 :
266 E : TEST_F(ReporterTest, NoCallback) {
267 : scoped_ptr<Reporter> instance(Reporter::Create(
268 : endpoint(),
269 : L"http://127.0.0.1:" + base::UintToString16(server_port()) + L"/crash",
270 : data_directory(), permanent_failure_directory(),
271 : base::TimeDelta::FromMilliseconds(1),
272 E : base::TimeDelta::FromMilliseconds(1), Reporter::OnUploadCallback()));
273 :
274 E : ASSERT_TRUE(instance);
275 :
276 : testing::UploadObserver upload_observer(upload_directory(),
277 E : permanent_failure_directory());
278 :
279 E : ASSERT_NO_FATAL_FAILURE(InvokeRpcFromChildProcess());
280 :
281 E : base::FilePath minidump_path;
282 E : std::map<std::string, std::string> crash_keys;
283 E : bool upload_success = false;
284 :
285 E : upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
286 :
287 E : EXPECT_TRUE(upload_success);
288 : EXPECT_HRESULT_SUCCEEDED(
289 E : testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
290 :
291 E : Reporter::Shutdown(instance.Pass());
292 E : }
293 :
294 E : TEST_F(ReporterTest, SendReportForProcessTest) {
295 E : base::string16 report_id;
296 : scoped_ptr<Reporter> instance(Reporter::Create(
297 : endpoint(),
298 : L"http://127.0.0.1:" + base::UintToString16(server_port()) + L"/crash",
299 : data_directory(), permanent_failure_directory(),
300 : base::TimeDelta::FromMilliseconds(1),
301 : base::TimeDelta::FromMilliseconds(1),
302 E : base::Bind(&OnUpload, base::Unretained(&report_id))));
303 :
304 E : ASSERT_TRUE(instance);
305 :
306 : testing::UploadObserver upload_observer(upload_directory(),
307 E : permanent_failure_directory());
308 :
309 : ASSERT_NO_FATAL_FAILURE(DoWithChildProcess(base::Bind(
310 E : &InvokeSendReportForProcess, base::Unretained(instance.get()))));
311 :
312 E : base::FilePath minidump_path;
313 E : std::map<std::string, std::string> crash_keys;
314 E : bool upload_success = false;
315 E : upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
316 :
317 E : EXPECT_TRUE(upload_success);
318 : EXPECT_HRESULT_SUCCEEDED(
319 E : testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
320 :
321 E : Reporter::Shutdown(instance.Pass());
322 :
323 E : ASSERT_FALSE(report_id.empty());
324 E : }
325 :
326 E : TEST_F(ReporterTest, PermanentFailureTest) {
327 E : base::string16 report_id;
328 : scoped_ptr<Reporter> instance(Reporter::Create(
329 : endpoint(), L"http://127.0.0.1:" + base::UintToString16(server_port()) +
330 : L"/crash_failure",
331 : data_directory(), permanent_failure_directory(),
332 : base::TimeDelta::FromMilliseconds(1),
333 : base::TimeDelta::FromMilliseconds(1),
334 E : base::Bind(&OnUpload, base::Unretained(&report_id))));
335 :
336 E : ASSERT_TRUE(instance);
337 :
338 : testing::UploadObserver upload_observer(upload_directory(),
339 E : permanent_failure_directory());
340 :
341 E : ASSERT_NO_FATAL_FAILURE(InvokeRpcFromChildProcess());
342 :
343 E : base::FilePath minidump_path;
344 E : std::map<std::string, std::string> crash_keys;
345 E : bool upload_success = false;
346 :
347 E : upload_observer.WaitForUpload(&minidump_path, &crash_keys, &upload_success);
348 :
349 E : EXPECT_FALSE(upload_success);
350 : EXPECT_HRESULT_SUCCEEDED(
351 E : testing::VisitMinidump(minidump_path, base::Bind(&ValidateMinidump)));
352 :
353 E : Reporter::Shutdown(instance.Pass());
354 :
355 E : ASSERT_TRUE(report_id.empty());
356 E : }
357 :
358 : } // namespace kasko
|