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

Side by Side Diff: chromecast/crash/linux/synchronized_minidump_manager_unittest.cc

Issue 1154383006: Adding crash utilities to chromecast/crash. (Closed) Base URL: https://eureka-internal.googlesource.com/chromium/src@master
Patch Set: cast_shell_unittests not built for android Created 5 years, 6 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
« no previous file with comments | « chromecast/crash/linux/synchronized_minidump_manager.cc ('k') | chromecast/media/BUILD.gn » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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 <fcntl.h>
6 #include <stdlib.h>
7 #include <sys/file.h>
8 #include <sys/stat.h> // mkdir
9 #include <sys/types.h> //
10 #include <stdio.h> // perror
11 #include <time.h>
12
13 #include <fstream>
14
15 #include "base/base_paths.h"
16 #include "base/bind.h"
17 #include "base/files/file.h"
18 #include "base/files/file_util.h"
19 #include "base/memory/scoped_ptr.h"
20 #include "base/memory/scoped_vector.h"
21 #include "base/process/launch.h"
22 #include "base/test/scoped_path_override.h"
23 #include "base/threading/platform_thread.h"
24 #include "base/threading/thread.h"
25 #include "chromecast/crash/linux/dump_info.h"
26 #include "chromecast/crash/linux/synchronized_minidump_manager.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28
29 namespace chromecast {
30 namespace {
31
32 const char kLockfileName[] = "lockfile";
33 const char kMinidumpSubdir[] = "minidumps";
34
35 ScopedVector<DumpInfo> GetCurrentDumps(const std::string& logfile_path) {
36 ScopedVector<DumpInfo> dumps;
37 std::string entry;
38
39 std::ifstream in(logfile_path);
40 DCHECK(in.is_open());
41 while (std::getline(in, entry)) {
42 scoped_ptr<DumpInfo> info(new DumpInfo(entry));
43 dumps.push_back(info.Pass());
44 }
45 return dumps.Pass();
46 }
47
48 // A trivial implementation of SynchronizedMinidumpManager, which does no work
49 // to the
50 // minidump and exposes its protected members for testing.
51 class SynchronizedMinidumpManagerSimple : public SynchronizedMinidumpManager {
52 public:
53 SynchronizedMinidumpManagerSimple()
54 : SynchronizedMinidumpManager(),
55 work_done_(false),
56 add_entry_return_code_(-1),
57 lockfile_path_(dump_path_.Append(kLockfileName).value()) {}
58 ~SynchronizedMinidumpManagerSimple() override {}
59
60 void SetDumpInfoToWrite(scoped_ptr<DumpInfo> dump_info) {
61 dump_info_ = dump_info.Pass();
62 }
63
64 int DoWorkLocked() { return AcquireLockAndDoWork(); }
65
66 // SynchronizedMinidumpManager implementation:
67 int DoWork() override {
68 if (dump_info_)
69 add_entry_return_code_ = AddEntryToLockFile(*dump_info_);
70 work_done_ = true;
71 return 0;
72 }
73
74 // Accessors for testing.
75 const std::string& dump_path() { return dump_path_.value(); }
76 const std::string& lockfile_path() { return lockfile_path_; }
77 bool work_done() { return work_done_; }
78 int add_entry_return_code() { return add_entry_return_code_; }
79
80 private:
81 bool work_done_;
82 int add_entry_return_code_;
83 std::string lockfile_path_;
84 scoped_ptr<DumpInfo> dump_info_;
85 };
86
87 void DoWorkLockedTask(SynchronizedMinidumpManagerSimple* manager) {
88 manager->DoWorkLocked();
89 }
90
91 class SleepySynchronizedMinidumpManagerSimple
92 : public SynchronizedMinidumpManagerSimple {
93 public:
94 SleepySynchronizedMinidumpManagerSimple(int sleep_duration_ms)
95 : SynchronizedMinidumpManagerSimple(),
96 sleep_duration_ms_(sleep_duration_ms) {}
97 ~SleepySynchronizedMinidumpManagerSimple() override {}
98
99 // SynchronizedMinidumpManager implementation:
100 int DoWork() override {
101 // The lock has been acquired. Fall asleep for |kSleepDurationMs|, then
102 // write the file.
103 base::PlatformThread::Sleep(
104 base::TimeDelta::FromMilliseconds(sleep_duration_ms_));
105 return SynchronizedMinidumpManagerSimple::DoWork();
106 }
107
108 private:
109 const int sleep_duration_ms_;
110 };
111
112 class SynchronizedMinidumpManagerTest : public testing::Test {
113 public:
114 SynchronizedMinidumpManagerTest() {}
115 ~SynchronizedMinidumpManagerTest() override {}
116
117 void SetUp() override {
118 // Set up a temporary directory which will be used as our fake home dir.
119 ASSERT_TRUE(base::CreateNewTempDirectory("", &fake_home_dir_));
120 path_override_.reset(
121 new base::ScopedPathOverride(base::DIR_HOME, fake_home_dir_));
122 minidump_dir_ = fake_home_dir_.Append(kMinidumpSubdir);
123 lockfile_ = minidump_dir_.Append(kLockfileName);
124
125 // Create a minidump directory.
126 ASSERT_TRUE(base::CreateDirectory(minidump_dir_));
127 ASSERT_TRUE(base::IsDirectoryEmpty(minidump_dir_));
128
129 // Create a lockfile in that directory.
130 base::File lockfile(
131 lockfile_, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
132 ASSERT_TRUE(lockfile.IsValid());
133 }
134
135 void TearDown() override {
136 // Remove the temp directory.
137 path_override_.reset();
138 ASSERT_TRUE(base::DeleteFile(fake_home_dir_, true));
139 }
140
141 protected:
142 base::FilePath fake_home_dir_; // Path to the test home directory.
143 base::FilePath minidump_dir_; // Path the the minidump directory.
144 base::FilePath lockfile_; // Path to the lockfile in |minidump_dir_|.
145
146 private:
147 scoped_ptr<base::ScopedPathOverride> path_override_;
148 };
149
150 } // namespace
151
152 TEST_F(SynchronizedMinidumpManagerTest, FilePathsAreCorrect) {
153 SynchronizedMinidumpManagerSimple manager;
154
155 // Verify file paths for directory and lock file.
156 ASSERT_EQ(minidump_dir_.value(), manager.dump_path());
157 ASSERT_EQ(lockfile_.value(), manager.lockfile_path());
158 }
159
160 TEST_F(SynchronizedMinidumpManagerTest, AcquireLockOnNonExistentDirectory) {
161 // The directory was created in SetUp(). Delete it and its contents.
162 ASSERT_TRUE(base::DeleteFile(minidump_dir_, true));
163 ASSERT_FALSE(base::PathExists(minidump_dir_));
164
165 SynchronizedMinidumpManagerSimple manager;
166 ASSERT_EQ(0, manager.DoWorkLocked());
167 ASSERT_TRUE(manager.work_done());
168
169 // Verify the directory and the lockfile both exist.
170 ASSERT_TRUE(base::DirectoryExists(minidump_dir_));
171 ASSERT_TRUE(base::PathExists(lockfile_));
172 }
173
174 TEST_F(SynchronizedMinidumpManagerTest, AcquireLockOnExistingEmptyDirectory) {
175 // The lockfile was created in SetUp(). Delete it.
176 ASSERT_TRUE(base::DeleteFile(lockfile_, false));
177 ASSERT_FALSE(base::PathExists(lockfile_));
178
179 SynchronizedMinidumpManagerSimple manager;
180 ASSERT_EQ(0, manager.DoWorkLocked());
181 ASSERT_TRUE(manager.work_done());
182
183 // Verify the directory and the lockfile both exist.
184 ASSERT_TRUE(base::DirectoryExists(minidump_dir_));
185 ASSERT_TRUE(base::PathExists(lockfile_));
186 }
187
188 TEST_F(SynchronizedMinidumpManagerTest,
189 AcquireLockOnExistingDirectoryWithLockfile) {
190 SynchronizedMinidumpManagerSimple manager;
191 ASSERT_EQ(0, manager.DoWorkLocked());
192 ASSERT_TRUE(manager.work_done());
193
194 // Verify the directory and the lockfile both exist.
195 ASSERT_TRUE(base::DirectoryExists(minidump_dir_));
196 ASSERT_TRUE(base::PathExists(lockfile_));
197 }
198
199 TEST_F(SynchronizedMinidumpManagerTest,
200 AddEntryToLockFile_FailsWithInvalidEntry) {
201 // Test that the manager tried to log the entry and failed.
202 SynchronizedMinidumpManagerSimple manager;
203 manager.SetDumpInfoToWrite(make_scoped_ptr(new DumpInfo("")));
204 ASSERT_EQ(0, manager.DoWorkLocked());
205 ASSERT_EQ(-1, manager.add_entry_return_code());
206
207 // Verify the lockfile is untouched.
208 ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
209 ASSERT_EQ(0u, dumps.size());
210 }
211
212 TEST_F(SynchronizedMinidumpManagerTest,
213 AddEntryToLockFile_SucceedsWithValidEntries) {
214 // Sample parameters.
215 time_t now = time(0);
216 MinidumpParams params;
217 params.process_name = "process";
218
219 // Write the first entry.
220 SynchronizedMinidumpManagerSimple manager;
221 manager.SetDumpInfoToWrite(
222 make_scoped_ptr(new DumpInfo("dump1", "log1", now, params)));
223 ASSERT_EQ(0, manager.DoWorkLocked());
224 ASSERT_EQ(0, manager.add_entry_return_code());
225
226 // Test that the manager was successful in logging the entry.
227 ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
228 ASSERT_EQ(1u, dumps.size());
229
230 // Write the second entry.
231 manager.SetDumpInfoToWrite(
232 make_scoped_ptr(new DumpInfo("dump2", "log2", now, params)));
233 ASSERT_EQ(0, manager.DoWorkLocked());
234 ASSERT_EQ(0, manager.add_entry_return_code());
235
236 // Test that the second entry is also valid.
237 dumps = GetCurrentDumps(lockfile_.value());
238 ASSERT_EQ(2u, dumps.size());
239
240 // TODO(slan): Weird time incosistencies making this fail.
241 // ASSERT_EQ(dumps[0]->entry(), DumpInfo("dump", "log", now, params).entry());
242 }
243
244 TEST_F(SynchronizedMinidumpManagerTest,
245 AcquireLockFile_FailsWhenNonBlockingAndFileLocked) {
246 // Lock the lockfile here. Note that the Chromium base::File tools permit
247 // multiple locks on the same process to succeed, so we must use POSIX system
248 // calls to accomplish this.
249 int fd = open(lockfile_.value().c_str(), O_RDWR | O_CREAT, 0660);
250 ASSERT_GE(fd, 0);
251 ASSERT_EQ(0, flock(fd, LOCK_EX));
252
253 SynchronizedMinidumpManagerSimple manager;
254 manager.set_non_blocking(true);
255 ASSERT_EQ(-1, manager.DoWorkLocked());
256 ASSERT_FALSE(manager.work_done());
257
258 // Test that the manager was not able to log the crash dump.
259 ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
260 ASSERT_EQ(0u, dumps.size());
261 }
262
263 TEST_F(SynchronizedMinidumpManagerTest,
264 AcquireLockFile_WaitsForOtherThreadWhenBlocking) {
265 // Create some parameters for a minidump.
266 time_t now = time(0);
267 MinidumpParams params;
268 params.process_name = "process";
269
270 // Create a manager that grabs the lock then sleeps. Post a DoWork task to
271 // another thread. |sleepy_manager| will grab the lock and hold it for
272 // |sleep_time_ms|. It will then write a dump and release the lock.
273 const int sleep_time_ms = 100;
274 SleepySynchronizedMinidumpManagerSimple sleepy_manager(sleep_time_ms);
275 sleepy_manager.SetDumpInfoToWrite(
276 make_scoped_ptr(new DumpInfo("dump", "log", now, params)));
277 base::Thread sleepy_thread("sleepy");
278 sleepy_thread.Start();
279 sleepy_thread.task_runner()->PostTask(
280 FROM_HERE,
281 base::Bind(&DoWorkLockedTask, base::Unretained(&sleepy_manager)));
282
283 // Meanwhile, this thread should wait brielfy to allow the other thread to
284 // grab the lock.
285 const int concurrency_delay = 50;
286 base::PlatformThread::Sleep(
287 base::TimeDelta::FromMilliseconds(concurrency_delay));
288
289 // |sleepy_manager| has the lock by now, but has not released it. Attempt to
290 // grab it. DoWorkLocked() should block until |manager| has a chance to write
291 // the dump.
292 SynchronizedMinidumpManagerSimple manager;
293 manager.SetDumpInfoToWrite(
294 make_scoped_ptr(new DumpInfo("dump", "log", now, params)));
295 manager.set_non_blocking(false);
296
297 EXPECT_EQ(0, manager.DoWorkLocked());
298 EXPECT_EQ(0, manager.add_entry_return_code());
299 EXPECT_TRUE(manager.work_done());
300
301 // Check that the other manager was also successful.
302 EXPECT_EQ(0, sleepy_manager.add_entry_return_code());
303 EXPECT_TRUE(sleepy_manager.work_done());
304
305 // Test that both entries were logged.
306 ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
307 EXPECT_EQ(2u, dumps.size());
308 }
309
310 // TODO(slan): These tests are passing but forking them is creating duplicates
311 // of all tests in this thread. Figure out how to lock the file more cleanly
312 // from another process.
313 TEST_F(SynchronizedMinidumpManagerTest,
314 DISABLED_AcquireLockFile_FailsWhenNonBlockingAndLockedFromOtherProcess) {
315 // Fork the process.
316 pid_t pid = base::ForkWithFlags(0u, nullptr, nullptr);
317 if (pid != 0) {
318 // The child process should instantiate a manager which immediately grabs
319 // the lock, and falls aleep for some period of time, then writes a dump,
320 // and finally releases the lock.
321 SleepySynchronizedMinidumpManagerSimple sleepy_manager(100);
322 ASSERT_EQ(0, sleepy_manager.DoWorkLocked());
323 ASSERT_TRUE(sleepy_manager.work_done());
324 return;
325 }
326
327 // Meanwhile, this process should wait brielfy to allow the other thread to
328 // grab the lock.
329 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(50));
330
331 SynchronizedMinidumpManagerSimple manager;
332 manager.set_non_blocking(true);
333 ASSERT_EQ(-1, manager.DoWorkLocked());
334 ASSERT_FALSE(manager.work_done());
335
336 // Test that the manager was not able to log the crash dump.
337 ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
338 ASSERT_EQ(0u, dumps.size());
339 }
340
341 // TODO(slan): These tests are passing but forking them is creating duplicates
342 // of all tests in this thread. Figure out how to lock the file more cleanly
343 // from another process.
344 TEST_F(SynchronizedMinidumpManagerTest,
345 DISABLED_AcquireLockFile_WaitsForOtherProcessWhenBlocking) {
346 // Create some parameters for a minidump.
347 time_t now = time(0);
348 MinidumpParams params;
349 params.process_name = "process";
350
351 // Fork the process.
352 pid_t pid = base::ForkWithFlags(0u, nullptr, nullptr);
353 if (pid != 0) {
354 // The child process should instantiate a manager which immediately grabs
355 // the lock, and falls aleep for some period of time, then writes a dump,
356 // and finally releases the lock.
357 SleepySynchronizedMinidumpManagerSimple sleepy_manager(100);
358 sleepy_manager.SetDumpInfoToWrite(
359 make_scoped_ptr(new DumpInfo("dump", "log", now, params)));
360 ASSERT_EQ(0, sleepy_manager.DoWorkLocked());
361 ASSERT_TRUE(sleepy_manager.work_done());
362 return;
363 }
364
365 // Meanwhile, this process should wait brielfy to allow the other thread to
366 // grab the lock.
367 const int concurrency_delay = 50;
368 base::PlatformThread::Sleep(
369 base::TimeDelta::FromMilliseconds(concurrency_delay));
370
371 // |sleepy_manager| has the lock by now, but has not released it. Attempt to
372 // grab it. DoWorkLocked() should block until |manager| has a chance to write
373 // the dump.
374 SynchronizedMinidumpManagerSimple manager;
375 manager.SetDumpInfoToWrite(
376 make_scoped_ptr(new DumpInfo("dump", "log", now, params)));
377 manager.set_non_blocking(false);
378
379 EXPECT_EQ(0, manager.DoWorkLocked());
380 EXPECT_EQ(0, manager.add_entry_return_code());
381 EXPECT_TRUE(manager.work_done());
382
383 // Test that both entries were logged.
384 ScopedVector<DumpInfo> dumps = GetCurrentDumps(lockfile_.value());
385 EXPECT_EQ(2u, dumps.size());
386 }
387
388 } // namespace chromecast
OLDNEW
« no previous file with comments | « chromecast/crash/linux/synchronized_minidump_manager.cc ('k') | chromecast/media/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698