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/service_bridge.h"
16 :
17 : #include <Windows.h> // NOLINT
18 : #include <Rpc.h>
19 :
20 : #include <vector>
21 :
22 : #include "base/bind.h"
23 : #include "base/callback.h"
24 : #include "base/callback_helpers.h"
25 : #include "base/location.h"
26 : #include "base/macros.h"
27 : #include "base/message_loop/message_loop.h"
28 : #include "base/process/process_handle.h"
29 : #include "base/strings/string16.h"
30 : #include "base/strings/string_number_conversions.h"
31 : #include "base/synchronization/waitable_event.h"
32 : #include "base/threading/thread.h"
33 : #include "gtest/gtest.h"
34 : #include "syzygy/common/rpc/helpers.h"
35 : #include "syzygy/kasko/kasko_rpc.h"
36 : #include "syzygy/kasko/service.h"
37 : #include "syzygy/kasko/testing/mock_service.h"
38 :
39 : namespace kasko {
40 :
41 : namespace {
42 :
43 : const base::char16* const kValidRpcProtocol = L"ncalrpc";
44 : const base::char16* const kTestRpcEndpointPrefix = L"syzygy-kasko-test-svc";
45 :
46 E : base::string16 GetTestEndpoint() {
47 E : return kTestRpcEndpointPrefix + base::UintToString16(::GetCurrentProcessId());
48 E : }
49 :
50 : class BlockingService : public Service {
51 : public:
52 : BlockingService(base::WaitableEvent* release_call,
53 : base::WaitableEvent* blocking);
54 : virtual ~BlockingService();
55 :
56 : // Service implementation
57 : virtual void SendDiagnosticReport(
58 : base::ProcessId client_process_id,
59 : base::PlatformThreadId thread_id,
60 : const MinidumpRequest& request) override;
61 :
62 : private:
63 : base::WaitableEvent* release_call_;
64 : base::WaitableEvent* blocking_;
65 : DISALLOW_COPY_AND_ASSIGN(BlockingService);
66 : };
67 :
68 : BlockingService::BlockingService(base::WaitableEvent* release_call,
69 : base::WaitableEvent* blocking)
70 E : : release_call_(release_call), blocking_(blocking) {}
71 :
72 E : BlockingService::~BlockingService() {}
73 :
74 : void BlockingService::SendDiagnosticReport(
75 : base::ProcessId client_process_id,
76 : base::PlatformThreadId thread_id,
77 E : const MinidumpRequest& request) {
78 E : blocking_->Signal();
79 E : release_call_->Wait();
80 E : }
81 :
82 : void InvokeAndCheckRpcStatus(const base::Callback<RPC_STATUS(void)>& callback) {
83 : ASSERT_EQ(RPC_S_OK, callback.Run());
84 : }
85 :
86 : base::Closure WrapRpcStatusCallback(
87 : const base::Callback<RPC_STATUS(void)>& callback) {
88 : return base::Bind(InvokeAndCheckRpcStatus, callback);
89 : }
90 :
91 : void DoInvokeService(const base::string16& protocol,
92 : const base::string16& endpoint,
93 : bool* complete,
94 : long exception_info_address,
95 : long thread_id,
96 : DumpType dump_type,
97 : size_t memory_ranges_length,
98 : const MemoryRange* memory_ranges,
99 : size_t crash_keys_length,
100 : const CrashKey* crash_keys,
101 : size_t custom_streams_length,
102 E : const CustomStream* custom_streams) {
103 E : common::rpc::ScopedRpcBinding rpc_binding;
104 E : ASSERT_TRUE(rpc_binding.Open(protocol, endpoint));
105 :
106 : ::MinidumpRequest rpc_request = {exception_info_address,
107 : thread_id,
108 : dump_type,
109 : memory_ranges_length,
110 : memory_ranges,
111 : crash_keys_length,
112 : crash_keys,
113 : custom_streams_length,
114 E : custom_streams};
115 :
116 : common::rpc::RpcStatus status = common::rpc::InvokeRpc(
117 E : KaskoClient_SendDiagnosticReport, rpc_binding.Get(), rpc_request);
118 E : ASSERT_FALSE(status.exception_occurred);
119 E : ASSERT_TRUE(status.succeeded());
120 E : *complete = true;
121 E : }
122 :
123 : } // namespace
124 :
125 E : TEST(KaskoServiceBridgeTest, ConstructDestruct) {
126 E : std::vector<testing::MockService::CallRecord> call_log;
127 : {
128 : ServiceBridge instance(
129 : L"aaa", L"bbb",
130 E : scoped_ptr<Service>(new testing::MockService(&call_log)));
131 E : }
132 : {
133 : ServiceBridge instance(
134 : L"aaa", L"bbb",
135 E : scoped_ptr<Service>(new testing::MockService(&call_log)));
136 E : }
137 E : }
138 :
139 E : TEST(KaskoServiceBridgeTest, StopNonRunningInstance) {
140 E : std::vector<testing::MockService::CallRecord> call_log;
141 : ServiceBridge instance(
142 E : L"aaa", L"bbb", scoped_ptr<Service>(new testing::MockService(&call_log)));
143 E : instance.Stop();
144 E : }
145 :
146 E : TEST(KaskoServiceBridgeTest, FailToRunWithBadProtocol) {
147 E : std::vector<testing::MockService::CallRecord> call_log;
148 : {
149 : ServiceBridge instance(
150 : L"aaa", GetTestEndpoint(),
151 E : scoped_ptr<Service>(new testing::MockService(&call_log)));
152 E : ASSERT_FALSE(instance.Run());
153 : // Stop should not crash in this case.
154 E : instance.Stop();
155 E : }
156 E : }
157 :
158 E : TEST(KaskoServiceBridgeTest, RunSuccessfully) {
159 E : std::vector<testing::MockService::CallRecord> call_log;
160 :
161 : {
162 : ServiceBridge instance(
163 : kValidRpcProtocol, GetTestEndpoint(),
164 E : scoped_ptr<Service>(new testing::MockService(&call_log)));
165 E : ASSERT_TRUE(instance.Run());
166 E : instance.Stop();
167 :
168 : // Second run, same instance.
169 E : ASSERT_TRUE(instance.Run());
170 E : instance.Stop();
171 E : }
172 : {
173 : // Second instance.
174 : ServiceBridge instance(
175 : kValidRpcProtocol, GetTestEndpoint(),
176 E : scoped_ptr<Service>(new testing::MockService(&call_log)));
177 E : ASSERT_TRUE(instance.Run());
178 E : instance.Stop();
179 E : }
180 E : }
181 :
182 E : TEST(KaskoServiceBridgeTest, InvokeService) {
183 E : std::vector<testing::MockService::CallRecord> call_log;
184 :
185 E : base::string16 protocol = kValidRpcProtocol;
186 E : base::string16 endpoint = GetTestEndpoint();
187 : ServiceBridge instance(
188 : protocol, endpoint,
189 E : scoped_ptr<Service>(new testing::MockService(&call_log)));
190 E : ASSERT_TRUE(instance.Run());
191 :
192 : base::ScopedClosureRunner stop_service_bridge(
193 E : base::Bind(&ServiceBridge::Stop, base::Unretained(&instance)));
194 :
195 :
196 E : std::string stream_data = "hello world";
197 E : uint32_t kStreamType = 987;
198 : CustomStream custom_streams[] = {
199 : {kStreamType, stream_data.length(),
200 E : reinterpret_cast<const signed char*>(stream_data.data())}};
201 E : bool complete = false;
202 : CrashKey crash_keys[] = {{reinterpret_cast<const wchar_t*>(L"foo"),
203 : reinterpret_cast<const wchar_t*>(L"bar")},
204 : {reinterpret_cast<const wchar_t*>(L"hello"),
205 E : reinterpret_cast<const wchar_t*>(L"world")}};
206 :
207 E : MemoryRange memory_ranges[] = {{0xdeadbeef, 123}};
208 :
209 : DoInvokeService(protocol, endpoint, &complete, 0, 0, SMALL_DUMP,
210 : arraysize(memory_ranges), memory_ranges,
211 : arraysize(crash_keys), crash_keys, arraysize(custom_streams),
212 E : custom_streams);
213 E : ASSERT_TRUE(complete);
214 E : complete = false;
215 : DoInvokeService(protocol, endpoint, &complete, 1122, 3, LARGER_DUMP, 0,
216 E : nullptr, 0, nullptr, 0, nullptr);
217 E : ASSERT_TRUE(complete);
218 :
219 E : ASSERT_EQ(2u, call_log.size());
220 :
221 : // First request
222 E : ASSERT_EQ(::GetCurrentProcessId(), call_log[0].client_process_id);
223 E : ASSERT_EQ(0, call_log[0].exception_info_address);
224 E : ASSERT_EQ(0, call_log[0].thread_id);
225 :
226 E : ASSERT_EQ(1u, call_log[0].user_selected_memory_ranges.size());
227 : ASSERT_EQ(memory_ranges[0].base_address,
228 E : call_log[0].user_selected_memory_ranges[0].start());
229 : ASSERT_EQ(memory_ranges[0].length,
230 E : call_log[0].user_selected_memory_ranges[0].size());
231 :
232 E : ASSERT_EQ(1u, call_log[0].custom_streams.size());
233 E : auto custom_streams_entry = call_log[0].custom_streams.find(kStreamType);
234 E : ASSERT_NE(call_log[0].custom_streams.end(), custom_streams_entry);
235 E : ASSERT_EQ(stream_data, custom_streams_entry->second);
236 :
237 E : ASSERT_EQ(2u, call_log[0].crash_keys.size());
238 E : auto crash_keys_entry = call_log[0].crash_keys.find(L"foo");
239 E : ASSERT_NE(call_log[0].crash_keys.end(), crash_keys_entry);
240 E : ASSERT_EQ(L"bar", crash_keys_entry->second);
241 E : crash_keys_entry = call_log[0].crash_keys.find(L"hello");
242 E : ASSERT_NE(call_log[0].crash_keys.end(), crash_keys_entry);
243 E : ASSERT_EQ(L"world", crash_keys_entry->second);
244 :
245 : // Second request
246 E : ASSERT_EQ(::GetCurrentProcessId(), call_log[1].client_process_id);
247 E : ASSERT_EQ(1122, call_log[1].exception_info_address);
248 E : ASSERT_EQ(3, call_log[1].thread_id);
249 E : ASSERT_EQ(0u, call_log[1].custom_streams.size());
250 E : ASSERT_EQ(0u, call_log[1].crash_keys.size());
251 E : }
252 :
253 :
254 E : TEST(KaskoServiceBridgeTest, StopBlocksUntilCallsComplete) {
255 E : base::WaitableEvent release_call(false, false);
256 E : base::WaitableEvent blocking(false, false);
257 :
258 E : base::string16 protocol = kValidRpcProtocol;
259 E : base::string16 endpoint = GetTestEndpoint();
260 : ServiceBridge instance(
261 : protocol, endpoint,
262 E : scoped_ptr<Service>(new BlockingService(&release_call, &blocking)));
263 E : ASSERT_TRUE(instance.Run());
264 :
265 : base::ScopedClosureRunner stop_service_bridge(
266 E : base::Bind(&ServiceBridge::Stop, base::Unretained(&instance)));
267 : // In case an assertion fails, make sure that we will not block.
268 : base::ScopedClosureRunner signal_release_call(base::Bind(
269 E : &base::WaitableEvent::Signal, base::Unretained(&release_call)));
270 :
271 E : bool complete = false;
272 : CrashKey crash_keys[] = {{reinterpret_cast<const wchar_t*>(L"foo"),
273 : reinterpret_cast<const wchar_t*>(L"bar")},
274 : {reinterpret_cast<const wchar_t*>(L"hello"),
275 E : reinterpret_cast<const wchar_t*>(L"world")}};
276 :
277 E : base::Thread client_thread("client thread");
278 E : ASSERT_TRUE(client_thread.Start());
279 : client_thread.message_loop()->PostTask(
280 : FROM_HERE, base::Bind(&DoInvokeService, protocol, endpoint,
281 : base::Unretained(&complete), 0, 0, SMALL_DUMP, 0,
282 : nullptr, arraysize(crash_keys),
283 E : base::Unretained(crash_keys), 0, nullptr));
284 : // In case the DoInvokeService fails, let's make sure we unblock ourselves.
285 : client_thread.message_loop()->PostTask(
286 : FROM_HERE,
287 E : base::Bind(&base::WaitableEvent::Signal, base::Unretained(&blocking)));
288 E : blocking.Wait();
289 :
290 : // Either DoInvokeService failed (complete == true), or we are blocking in
291 : // BlockingService::SendDiagnosticReport (complete == false).
292 E : ASSERT_FALSE(complete);
293 :
294 : // Reduce the chance of false positives by giving the service call a chance to
295 : // complete. (It shouldn't.)
296 E : ::Sleep(100);
297 :
298 E : base::Thread stop_thread("stop thread");
299 E : ASSERT_TRUE(stop_thread.Start());
300 : stop_thread.message_loop()->PostTask(
301 E : FROM_HERE, base::Bind(&ServiceBridge::Stop, base::Unretained(&instance)));
302 E : ASSERT_FALSE(complete);
303 :
304 : // Stop is waiting for the pending call to complete. Let's unblock it now.
305 E : release_call.Signal();
306 :
307 : // This will not return until the ServiceBridge::Stop has completed.
308 E : stop_thread.Stop();
309 E : ASSERT_TRUE(complete);
310 E : }
311 :
312 : } // namespace kasko
|