Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(256)

Side by Side Diff: base/win/wait_chain_unittest.cc

Issue 1834463002: Identify the hung thread using the Wait Chain Traversal API (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebase Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/win/wait_chain.h"
6
7 #include <memory>
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/command_line.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_piece.h"
14 #include "base/test/multiprocess_test.h"
15 #include "base/threading/simple_thread.h"
16 #include "base/win/win_util.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 #include "testing/multiprocess_func_list.h"
19
20 namespace base {
21 namespace win {
22
23 namespace {
24
25 // Appends |handle| as a command line switch.
26 void AppendSwitchHandle(CommandLine* command_line,
27 StringPiece switch_name,
28 HANDLE handle) {
29 command_line->AppendSwitchASCII(switch_name.as_string(),
30 UintToString(HandleToUint32(handle)));
31 }
32
33 // Retrieves the |handle| associated to |switch_name| from the command line.
34 ScopedHandle GetSwitchValueHandle(CommandLine* command_line,
35 StringPiece switch_name) {
36 std::string switch_string =
37 command_line->GetSwitchValueASCII(switch_name.as_string());
38 unsigned int switch_uint = 0;
39 if (switch_string.empty() || !StringToUint(switch_string, &switch_uint)) {
40 DLOG(ERROR) << "Missing or invalid " << switch_name << " argument.";
41 return ScopedHandle();
42 }
43 return ScopedHandle(reinterpret_cast<HANDLE>(switch_uint));
44 }
45
46 // Helper function to create a mutex.
47 ScopedHandle CreateMutex(bool inheritable) {
48 SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
49 nullptr, inheritable};
50 return ScopedHandle(::CreateMutex(&security_attributes, FALSE, NULL));
51 }
52
53 // Helper function to create an event.
54 ScopedHandle CreateEvent(bool inheritable) {
55 SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES),
56 nullptr, inheritable};
57 return ScopedHandle(
58 ::CreateEvent(&security_attributes, FALSE, FALSE, nullptr));
59 }
60
61 class SimpleThreadWithHandle : public SimpleThread {
62 public:
63 explicit SimpleThreadWithHandle(const std::string& name_prefix)
64 : SimpleThread(name_prefix) {}
65
66 // Returns a handle to the thread so that other threads can wait on it.
67 HANDLE get_thread_handle() {
68 if (!thread_handle_.Get()) {
69 thread_handle_.Set(
70 ::OpenThread(SYNCHRONIZE | THREAD_TERMINATE, FALSE, tid()));
71 }
72 return thread_handle_.Get();
73 }
74
75 private:
76 ScopedHandle thread_handle_;
77
78 DISALLOW_COPY_AND_ASSIGN(SimpleThreadWithHandle);
79 };
80
81 // Helper thread class that runs the callback then stops.
82 class SingleTaskThread : public SimpleThreadWithHandle {
83 public:
84 explicit SingleTaskThread(const Closure& task)
85 : SimpleThreadWithHandle("WaitChainTest SingleTaskThread"), task_(task) {}
86
87 void Run() override { task_.Run(); }
88
89 private:
90 Closure task_;
91
92 DISALLOW_COPY_AND_ASSIGN(SingleTaskThread);
93 };
94
95 // Helper thread to cause a deadlock by acquiring 2 mutexes in a given order.
96 class DeadlockThread : public SimpleThreadWithHandle {
97 public:
98 DeadlockThread(HANDLE mutex_1, HANDLE mutex_2)
99 : SimpleThreadWithHandle("WaitChainTest SingleTaskThread"),
100 wait_event_(CreateEvent(false)),
101 mutex_acquired_event_(CreateEvent(false)),
102 mutex_1_(mutex_1),
103 mutex_2_(mutex_2) {}
104
105 void Run() override {
106 // Acquire the mutex then signal the main thread.
107 EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(mutex_1_, INFINITE));
108 EXPECT_TRUE(::SetEvent(mutex_acquired_event_.Get()));
109
110 // Wait until both threads are holding their mutex before trying to acquire
111 // the other one.
112 EXPECT_EQ(WAIT_OBJECT_0,
113 ::WaitForSingleObject(wait_event_.Get(), INFINITE));
114
115 // To unblock the deadlock, one of the threads will get terminated (via
116 // TerminateThread()) without releasing the mutex. This causes the other
117 // thread to wake up with WAIT_ABANDONED.
118 EXPECT_EQ(WAIT_ABANDONED, ::WaitForSingleObject(mutex_2_, INFINITE));
119 }
120
121 // Blocks until a mutex is acquired.
122 void WaitForMutexAcquired() {
123 EXPECT_EQ(WAIT_OBJECT_0,
124 ::WaitForSingleObject(mutex_acquired_event_.Get(), INFINITE));
125 }
126
127 // Signal the thread to acquire the second mutex.
128 void SignalToAcquireMutex() { EXPECT_TRUE(::SetEvent(wait_event_.Get())); }
129
130 private:
131 ScopedHandle wait_event_;
132 ScopedHandle mutex_acquired_event_;
133
134 // The 2 mutex to acquire.
135 HANDLE mutex_1_;
136 HANDLE mutex_2_;
137
138 DISALLOW_COPY_AND_ASSIGN(DeadlockThread);
139 };
140
141 // Creates a thread that calls WaitForSingleObject() on the handle and then
142 // terminates when it unblocks.
143 std::unique_ptr<SingleTaskThread> CreateWaitingThread(HANDLE handle) {
144 std::unique_ptr<SingleTaskThread> thread(new SingleTaskThread(
145 Bind(IgnoreResult(&::WaitForSingleObject), handle, INFINITE)));
146 thread->Start();
147
148 return std::move(thread);
149 }
150
151 // Creates a thread that calls WaitForSingleObject() on the handle and then
152 // terminates when it unblocks.
153 std::unique_ptr<DeadlockThread> CreateDeadlockThread(HANDLE mutex_1,
154 HANDLE mutex_2) {
155 std::unique_ptr<DeadlockThread> thread(new DeadlockThread(mutex_1, mutex_2));
156 thread->Start();
157
158 // Wait until the first mutex is acquired before returning.
159 thread->WaitForMutexAcquired();
160
161 return std::move(thread);
162 }
163
164 // Child process to test the cross-process capability of the WCT api.
165 // This process will simulate a hang while holding a mutex that the parent
166 // process is waiting on.
167 MULTIPROCESS_TEST_MAIN(WaitChainTestProc) {
168 CommandLine* command_line = CommandLine::ForCurrentProcess();
169
170 ScopedHandle mutex = GetSwitchValueHandle(command_line, "mutex");
171 CHECK(mutex.IsValid());
172
173 ScopedHandle sync_event(GetSwitchValueHandle(command_line, "sync_event"));
174 CHECK(sync_event.IsValid());
175
176 // Acquire mutex.
177 CHECK(::WaitForSingleObject(mutex.Get(), INFINITE) == WAIT_OBJECT_0);
178
179 // Signal back to the parent process that the mutex is hold.
180 CHECK(::SetEvent(sync_event.Get()));
181
182 // Wait on a signal from the parent process before terminating.
183 CHECK(::WaitForSingleObject(sync_event.Get(), INFINITE) == WAIT_OBJECT_0);
184
185 return 0;
186 }
187
188 // Start a child process and passes the |mutex| and the |sync_event| to the
189 // command line.
190 Process StartChildProcess(HANDLE mutex, HANDLE sync_event) {
191 CommandLine command_line = GetMultiProcessTestChildBaseCommandLine();
192
193 AppendSwitchHandle(&command_line, "mutex", mutex);
194 AppendSwitchHandle(&command_line, "sync_event", sync_event);
195
196 LaunchOptions options;
197 HandlesToInheritVector handle_vector;
198 handle_vector.push_back(mutex);
199 handle_vector.push_back(sync_event);
200 options.handles_to_inherit = &handle_vector;
201 return SpawnMultiProcessTestChild("WaitChainTestProc", command_line, options);
202 }
203
204 // Returns true if the |wait_chain| is an alternating sequence of thread objects
205 // and synchronization objects.
206 bool WaitChainStructureIsCorrect(const WaitChainNodeVector& wait_chain) {
207 // Checks thread objects.
208 for (size_t i = 0; i < wait_chain.size(); i += 2) {
209 if (wait_chain[i].ObjectType != WctThreadType)
210 return false;
211 }
212
213 // Check synchronization objects.
214 for (size_t i = 1; i < wait_chain.size(); i += 2) {
215 if (wait_chain[i].ObjectType == WctThreadType)
216 return false;
217 }
218 return true;
219 }
220
221 // Returns true if the |wait_chain| goes through more than 1 process.
222 bool WaitChainIsCrossProcess(const WaitChainNodeVector& wait_chain) {
223 if (wait_chain.size() == 0)
224 return false;
225
226 // Just check that the process id changes somewhere in the chain.
227 // Note: ThreadObjects are every 2 nodes.
228 DWORD first_process = wait_chain[0].ThreadObject.ProcessId;
229 for (size_t i = 2; i < wait_chain.size(); i += 2) {
230 if (first_process != wait_chain[i].ThreadObject.ProcessId)
231 return true;
232 }
233 return false;
234 }
235
236 } // namespace
237
238 // Creates 2 threads that acquire their designated mutex and then try to
239 // acquire each others' mutex to cause a deadlock.
240 TEST(WaitChainTest, Deadlock) {
241 // 2 mutexes are needed to get a deadlock.
242 ScopedHandle mutex_1 = CreateMutex(false);
243 ASSERT_TRUE(mutex_1.IsValid());
244 ScopedHandle mutex_2 = CreateMutex(false);
245 ASSERT_TRUE(mutex_2.IsValid());
246
247 std::unique_ptr<DeadlockThread> deadlock_thread_1 =
248 CreateDeadlockThread(mutex_1.Get(), mutex_2.Get());
249 std::unique_ptr<DeadlockThread> deadlock_thread_2 =
250 CreateDeadlockThread(mutex_2.Get(), mutex_1.Get());
251
252 // Signal the threads to try to acquire the other mutex.
253 deadlock_thread_1->SignalToAcquireMutex();
254 deadlock_thread_2->SignalToAcquireMutex();
255 // Sleep to make sure the 2 threads got a chance to execute.
256 Sleep(10);
257
258 // Create a few waiting threads to get a longer wait chain.
259 std::unique_ptr<SingleTaskThread> waiting_thread_1 =
260 CreateWaitingThread(deadlock_thread_1->get_thread_handle());
261 std::unique_ptr<SingleTaskThread> waiting_thread_2 =
262 CreateWaitingThread(waiting_thread_1->get_thread_handle());
263
264 WaitChainNodeVector wait_chain;
265 bool is_deadlock;
266 ASSERT_TRUE(
267 GetThreadWaitChain(waiting_thread_2->tid(), &wait_chain, &is_deadlock));
268
269 EXPECT_EQ(9U, wait_chain.size());
270 EXPECT_TRUE(is_deadlock);
271 EXPECT_TRUE(WaitChainStructureIsCorrect(wait_chain));
272 EXPECT_FALSE(WaitChainIsCrossProcess(wait_chain));
273
274 ASSERT_TRUE(::TerminateThread(deadlock_thread_1->get_thread_handle(), 0));
275 }
276
277 // Creates a child process that acquires a mutex and then blocks. A chain of
278 // threads then blocks on that mutex.
279 TEST(WaitChainTest, CrossProcess) {
280 ScopedHandle mutex = CreateMutex(true);
281 ASSERT_TRUE(mutex.IsValid());
282 ScopedHandle sync_event = CreateEvent(true);
283 ASSERT_TRUE(sync_event.IsValid());
284
285 Process child_process = StartChildProcess(mutex.Get(), sync_event.Get());
286 ASSERT_TRUE(child_process.IsValid());
287
288 // Wait for the child process to signal when it's holding the mutex.
289 EXPECT_EQ(WAIT_OBJECT_0, ::WaitForSingleObject(sync_event.Get(), INFINITE));
290
291 // Create a few waiting threads to get a longer wait chain.
292 std::unique_ptr<SingleTaskThread> waiting_thread_1 =
293 CreateWaitingThread(mutex.Get());
294 std::unique_ptr<SingleTaskThread> waiting_thread_2 =
295 CreateWaitingThread(waiting_thread_1->get_thread_handle());
296 std::unique_ptr<SingleTaskThread> waiting_thread_3 =
297 CreateWaitingThread(waiting_thread_2->get_thread_handle());
298
299 WaitChainNodeVector wait_chain;
300 bool is_deadlock;
301 ASSERT_TRUE(
302 GetThreadWaitChain(waiting_thread_3->tid(), &wait_chain, &is_deadlock));
303
304 EXPECT_EQ(7U, wait_chain.size());
305 EXPECT_FALSE(is_deadlock);
306 EXPECT_TRUE(WaitChainStructureIsCorrect(wait_chain));
307 EXPECT_TRUE(WaitChainIsCrossProcess(wait_chain));
308
309 // Unblock child process and wait for it to terminate.
310 ASSERT_TRUE(::SetEvent(sync_event.Get()));
311 ASSERT_TRUE(child_process.WaitForExit(nullptr));
312 }
313
314 } // namespace win
315 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698