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

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

Powered by Google App Engine
This is Rietveld 408576698