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

Side by Side Diff: components/browser_watcher/postmortem_unittest.cc

Issue 2128683002: Collect unclean shutdown debug information (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@tracker
Patch Set: Minimal collection to proto Created 4 years, 4 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 "components/browser_watcher/postmortem.h"
6
7 #include <stdint.h>
8
9 #include <memory>
10 #include <set>
11 #include <string>
12 #include <utility>
13 #include <vector>
14
15 #include "base/debug/activity_tracker.h"
16 #include "base/files/file.h"
17 #include "base/files/file_path.h"
18 #include "base/files/file_util.h"
19 #include "base/files/memory_mapped_file.h"
20 #include "base/files/scoped_file.h"
21 #include "base/files/scoped_temp_dir.h"
22 #include "base/memory/ptr_util.h"
23 #include "base/metrics/persistent_memory_allocator.h"
24 #include "base/threading/platform_thread.h"
25 #include "testing/gmock/include/gmock/gmock.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27
28 namespace browser_watcher {
29
30 using base::debug::GlobalActivityTracker;
31 using base::debug::ThreadActivityTracker;
32 using base::File;
33 using base::FilePersistentMemoryAllocator;
34 using base::MemoryMappedFile;
35 using base::PersistentMemoryAllocator;
36 using base::WrapUnique;
37 using testing::_;
38 using testing::Return;
39
40 namespace {
41
42 class MockReporterDelegate
43 : public PostmortemReportCollector::ReporterDelegate {
44 public:
45 MockReporterDelegate() {}
46 MOCK_METHOD1(SubmitReportForUpload, void(const base::FilePath& minidump));
47 };
48
49 // Exposes GetDebugStateFilePaths for unit testing.
50 class TestPostmortemReportCollector : public PostmortemReportCollector {
51 public:
52 TestPostmortemReportCollector(
53 const base::FilePath& debug_dir,
54 const base::FilePath::StringType& debug_file_pattern,
55 ReporterDelegate* delegate)
56 : PostmortemReportCollector(debug_dir, debug_file_pattern, delegate) {}
57 TestPostmortemReportCollector() : PostmortemReportCollector() {}
58
59 using PostmortemReportCollector::GetDebugStateFilePaths;
60 using PostmortemReportCollector::Collect;
61 };
62
63 // Used for testing CollectAndSubmitForUpload.
64 class MockPostmortemReportCollector : public PostmortemReportCollector {
65 public:
66 MockPostmortemReportCollector(
67 const base::FilePath& debug_dir,
68 const base::FilePath::StringType& debug_file_pattern,
69 ReporterDelegate* delegate)
70 : PostmortemReportCollector(debug_dir, debug_file_pattern, delegate) {}
71
72 // A function that returns a unique_ptr cannot be mocked, so mock a function
73 // that returns a raw pointer instead.
74 std::unique_ptr<StabilityReport> Collect(
75 const base::FilePath& debug_state_file) override {
76 return std::unique_ptr<StabilityReport>(CollectRaw(debug_state_file));
77 }
78
79 MOCK_METHOD1(GetDebugStateFilePaths,
80 std::vector<base::FilePath>(const std::set<base::FilePath>&));
81 MOCK_METHOD1(CollectRaw, StabilityReport*(const base::FilePath&));
82 MOCK_METHOD2(CreateReport,
83 bool(const StabilityReport& report,
84 const base::FilePath& minidump_path));
85 };
86
87 // Checks if two proto messages are the same.
88 MATCHER_P(EqualsProto, message, "") {
89 std::string expected_serialized;
90 std::string actual_serialized;
91 message.SerializeToString(&expected_serialized);
92 arg.SerializeToString(&actual_serialized);
93 return expected_serialized == actual_serialized;
94 }
95
96 } // namespace
97
98 TEST(PostmortemReportCollectorTest, CollectAndSubmitForUpload) {
99 // Create a dummy debug file, to validate deletion.
100 base::ScopedTempDir temp_dir;
101 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
102 base::FilePath debug_file = temp_dir.path().AppendASCII("foo-1.pma");
103 {
104 base::ScopedFILE file(base::OpenFile(debug_file, "w"));
105 ASSERT_NE(file.get(), nullptr);
106 }
107 ASSERT_TRUE(base::PathExists(debug_file));
108
109 // Create mocks.
110 MockReporterDelegate reporter;
111 MockPostmortemReportCollector collector(
112 debug_file.DirName(), FILE_PATH_LITERAL("foo-*.pma"), &reporter);
113
114 // Set up expectations.
115 std::set<base::FilePath> no_excluded_files;
116 std::vector<base::FilePath> debug_files{debug_file};
117 EXPECT_CALL(collector, GetDebugStateFilePaths(no_excluded_files))
118 .Times(1)
119 .WillOnce(Return(debug_files));
120
121 // Note: caller takes ownership.
122 StabilityReport* stability_report = new StabilityReport();
123 EXPECT_CALL(collector, CollectRaw(debug_file))
124 .Times(1)
125 .WillOnce(Return(stability_report));
126
127 base::FilePath expected_minidump_path =
128 temp_dir.path().AppendASCII("foo-1.dmp");
129 EXPECT_CALL(collector, CreateReport(EqualsProto(*stability_report),
130 expected_minidump_path))
131 .Times(1)
132 .WillOnce(Return(true));
133
134 EXPECT_CALL(reporter, SubmitReportForUpload(expected_minidump_path)).Times(1);
135
136 collector.CollectAndSubmitForUpload(no_excluded_files);
137 ASSERT_FALSE(base::PathExists(debug_file));
138 }
139
140 TEST(PostmortemReportCollectorTest, GetDebugStateFilePaths) {
141 base::ScopedTempDir temp_dir;
142 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
143
144 // Create files.
145 std::vector<base::FilePath> expected_paths;
146 std::set<base::FilePath> excluded_paths;
147 {
148 // Matches the pattern.
149 base::FilePath path = temp_dir.path().AppendASCII("foo1.pma");
150 base::ScopedFILE file(base::OpenFile(path, "w"));
151 ASSERT_NE(file.get(), nullptr);
152 expected_paths.push_back(path);
153
154 // Matches the pattern, but is excluded.
155 path = temp_dir.path().AppendASCII("foo2.pma");
156 file.reset(base::OpenFile(path, "w"));
157 ASSERT_NE(file.get(), nullptr);
158 ASSERT_TRUE(excluded_paths.insert(path).second);
159
160 // Matches the pattern.
161 path = temp_dir.path().AppendASCII("foo3.pma");
162 file.reset(base::OpenFile(path, "w"));
163 ASSERT_NE(file.get(), nullptr);
164 expected_paths.push_back(path);
165
166 // Does not match the pattern.
167 path = temp_dir.path().AppendASCII("bar.baz");
168 file.reset(base::OpenFile(path, "w"));
169 ASSERT_NE(file.get(), nullptr);
170 }
171
172 MockReporterDelegate reporter;
173 TestPostmortemReportCollector collector(
174 temp_dir.path(), FILE_PATH_LITERAL("foo*.pma"), &reporter);
175
176 EXPECT_THAT(collector.GetDebugStateFilePaths(excluded_paths),
177 testing::UnorderedElementsAreArray(expected_paths));
178 }
179
180 TEST(PostmortemReportCollectorTest, CollectEmptyFile) {
181 // Create an empty file.
182 base::ScopedTempDir temp_dir;
183 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
184 base::FilePath file_path = temp_dir.path().AppendASCII("empty.pma");
185 {
186 base::ScopedFILE file(base::OpenFile(file_path, "w"));
187 ASSERT_NE(file.get(), nullptr);
188 }
189 ASSERT_TRUE(PathExists(file_path));
190
191 // Validate collection returns nullptr.
192 TestPostmortemReportCollector collector;
193 ASSERT_EQ(nullptr, collector.Collect(file_path));
194 }
195
196 TEST(PostmortemReportCollectorTest, CollectRandomFile) {
197 // Create a file with content we don't expect to be valid for a debug file.
198 base::ScopedTempDir temp_dir;
199 ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
200 base::FilePath file_path = temp_dir.path().AppendASCII("invalid_content.pma");
201 {
202 base::ScopedFILE file(base::OpenFile(file_path, "w"));
203 ASSERT_NE(file.get(), nullptr);
204 // Assuming this size is greater than the minimum size of a debug file.
205 std::vector<uint8_t> data(1024);
206 for (size_t i = 0; i < data.size(); ++i)
207 data[i] = i % UINT8_MAX;
208 ASSERT_EQ(data.size(),
209 fwrite(&data.at(0), sizeof(uint8_t), data.size(), file.get()));
210 }
211 ASSERT_TRUE(PathExists(file_path));
212
213 // Validate collection returns nullptr.
214 TestPostmortemReportCollector collector;
215 ASSERT_EQ(nullptr, collector.Collect(file_path));
216 }
217
218 namespace {
219
220 // Parameters for the activity tracking.
221 const size_t kFileSize = 2 * 1024;
222 const int kStackDepth = 4;
223 const uint64_t kAllocatorId = 0;
224 const char kAllocatorName[] = "PostmortemReportCollectorCollectionTest";
225
226 } // namespace
227
228 class PostmortemReportCollectorCollectionTest : public testing::Test {
229 public:
230 // Create a proper debug file.
231 void SetUp() override {
232 testing::Test::SetUp();
233
234 // Create a file backed allocator.
235 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
236 debug_file_path_ = temp_dir_.path().AppendASCII("debug_file.pma");
237 std::unique_ptr<PersistentMemoryAllocator> allocator = CreateAllocator();
238 ASSERT_NE(nullptr, allocator);
239
240 size_t tracker_mem_size =
241 ThreadActivityTracker::SizeForStackDepth(kStackDepth);
242 ASSERT_GT(kFileSize, tracker_mem_size);
243
244 // Create some debug data using trackers.
245 std::unique_ptr<ThreadActivityTracker> tracker =
246 CreateTracker(allocator.get(), tracker_mem_size);
247 ASSERT_NE(nullptr, tracker);
248 ASSERT_TRUE(tracker->IsValid());
249
250 const void* dummy_task_origin = reinterpret_cast<void*>(0xCAFE);
251 const int dummy_task_sequence_num = 42;
252 tracker->PushActivity(
253 dummy_task_origin, ThreadActivityTracker::ACT_TASK_RUN,
254 ThreadActivityTracker::ActivityData::ForTask(dummy_task_sequence_num));
255
256 // TODO(manzagop): flesh out the data (more trackers and content).
257 }
258
259 std::unique_ptr<PersistentMemoryAllocator> CreateAllocator() {
260 // Create the memory mapped file.
261 std::unique_ptr<MemoryMappedFile> mmfile(new MemoryMappedFile());
262 bool success = mmfile->Initialize(
263 File(debug_file_path_, File::FLAG_CREATE | File::FLAG_READ |
264 File::FLAG_WRITE | File::FLAG_SHARE_DELETE),
265 {0, static_cast<int64_t>(kFileSize)},
266 MemoryMappedFile::READ_WRITE_EXTEND);
267 if (!success || !mmfile->IsValid())
268 return nullptr;
269
270 // Create a persistent memory allocator.
271 if (!FilePersistentMemoryAllocator::IsFileAcceptable(*mmfile, true))
272 return nullptr;
273 return WrapUnique(new FilePersistentMemoryAllocator(
274 std::move(mmfile), kFileSize, kAllocatorId, kAllocatorName, false));
275 }
276
277 std::unique_ptr<ThreadActivityTracker> CreateTracker(
278 PersistentMemoryAllocator* allocator,
279 size_t tracker_mem_size) {
280 // Allocate a block of memory for the tracker to use.
281 PersistentMemoryAllocator::Reference mem_reference = allocator->Allocate(
282 tracker_mem_size, GlobalActivityTracker::kTypeIdActivityTracker);
283 if (mem_reference == 0U)
284 return nullptr;
285
286 // Get the memory's base address.
287 void* mem_base = allocator->GetAsObject<char>(
288 mem_reference, GlobalActivityTracker::kTypeIdActivityTracker);
289 if (mem_base == nullptr)
290 return nullptr;
291
292 // Make the allocation iterable so it can be found by other processes.
293 allocator->MakeIterable(mem_reference);
294
295 return WrapUnique(new ThreadActivityTracker(mem_base, tracker_mem_size));
296 }
297
298 const base::FilePath& debug_file_path() const { return debug_file_path_; }
299
300 private:
301 base::ScopedTempDir temp_dir_;
302 base::FilePath debug_file_path_;
303 };
304
305 TEST_F(PostmortemReportCollectorCollectionTest, CollectSuccess) {
306 // Validate collection returns the expected report.
307 TestPostmortemReportCollector collector;
308 std::unique_ptr<StabilityReport> report =
309 collector.Collect(debug_file_path());
310 ASSERT_NE(nullptr, report);
311
312 // Build the expected report.
313 StabilityReport expected_report;
314 ProcessState* process_state = expected_report.add_process_states();
315 ThreadState* thread_state = process_state->add_threads();
316 thread_state->set_thread_name(base::PlatformThread::GetName());
317
318 ASSERT_EQ(expected_report.SerializeAsString(), report->SerializeAsString());
319 }
320
321 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698