OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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 // This command-line program generates the set of files needed for the crash- | |
6 // cache unit tests (DiskCacheTest,CacheBackend_Recover*). This program only | |
7 // works properly on debug mode, because the crash functionality is not compiled | |
8 // on release builds of the cache. | |
9 | |
10 #include <string> | |
11 | |
12 #include "base/at_exit.h" | |
13 #include "base/command_line.h" | |
14 #include "base/files/file_util.h" | |
15 #include "base/logging.h" | |
16 #include "base/message_loop/message_loop.h" | |
17 #include "base/path_service.h" | |
18 #include "base/process/kill.h" | |
19 #include "base/process/launch.h" | |
20 #include "base/strings/string_number_conversions.h" | |
21 #include "base/strings/string_util.h" | |
22 #include "base/strings/utf_string_conversions.h" | |
23 #include "base/threading/thread.h" | |
24 #include "net/base/net_errors.h" | |
25 #include "net/base/net_export.h" | |
26 #include "net/base/test_completion_callback.h" | |
27 #include "net/disk_cache/blockfile/backend_impl.h" | |
28 #include "net/disk_cache/blockfile/rankings.h" | |
29 #include "net/disk_cache/disk_cache.h" | |
30 #include "net/disk_cache/disk_cache_test_util.h" | |
31 | |
32 using base::Time; | |
33 | |
34 enum Errors { | |
35 GENERIC = -1, | |
36 ALL_GOOD = 0, | |
37 INVALID_ARGUMENT = 1, | |
38 CRASH_OVERWRITE, | |
39 NOT_REACHED | |
40 }; | |
41 | |
42 using disk_cache::RankCrashes; | |
43 | |
44 // Starts a new process, to generate the files. | |
45 int RunSlave(RankCrashes action) { | |
46 base::FilePath exe; | |
47 PathService::Get(base::FILE_EXE, &exe); | |
48 | |
49 base::CommandLine cmdline(exe); | |
50 cmdline.AppendArg(base::IntToString(action)); | |
51 | |
52 base::Process process = base::LaunchProcess(cmdline, base::LaunchOptions()); | |
53 if (!process.IsValid()) { | |
54 printf("Unable to run test %d\n", action); | |
55 return GENERIC; | |
56 } | |
57 | |
58 int exit_code; | |
59 | |
60 if (!process.WaitForExit(&exit_code)) { | |
61 printf("Unable to get return code, test %d\n", action); | |
62 return GENERIC; | |
63 } | |
64 if (ALL_GOOD != exit_code) | |
65 printf("Test %d failed, code %d\n", action, exit_code); | |
66 | |
67 return exit_code; | |
68 } | |
69 | |
70 // Main loop for the master process. | |
71 int MasterCode() { | |
72 for (int i = disk_cache::NO_CRASH + 1; i < disk_cache::MAX_CRASH; i++) { | |
73 int ret = RunSlave(static_cast<RankCrashes>(i)); | |
74 if (ALL_GOOD != ret) | |
75 return ret; | |
76 } | |
77 | |
78 return ALL_GOOD; | |
79 } | |
80 | |
81 // ----------------------------------------------------------------------- | |
82 | |
83 namespace disk_cache { | |
84 NET_EXPORT_PRIVATE extern RankCrashes g_rankings_crash; | |
85 } | |
86 | |
87 const char kCrashEntryName[] = "the first key"; | |
88 | |
89 // Creates the destinaton folder for this run, and returns it on full_path. | |
90 bool CreateTargetFolder(const base::FilePath& path, RankCrashes action, | |
91 base::FilePath* full_path) { | |
92 const char* const folders[] = { | |
93 "", | |
94 "insert_empty1", | |
95 "insert_empty2", | |
96 "insert_empty3", | |
97 "insert_one1", | |
98 "insert_one2", | |
99 "insert_one3", | |
100 "insert_load1", | |
101 "insert_load2", | |
102 "remove_one1", | |
103 "remove_one2", | |
104 "remove_one3", | |
105 "remove_one4", | |
106 "remove_head1", | |
107 "remove_head2", | |
108 "remove_head3", | |
109 "remove_head4", | |
110 "remove_tail1", | |
111 "remove_tail2", | |
112 "remove_tail3", | |
113 "remove_load1", | |
114 "remove_load2", | |
115 "remove_load3" | |
116 }; | |
117 static_assert(arraysize(folders) == disk_cache::MAX_CRASH, "sync folders"); | |
118 DCHECK(action > disk_cache::NO_CRASH && action < disk_cache::MAX_CRASH); | |
119 | |
120 *full_path = path.AppendASCII(folders[action]); | |
121 | |
122 if (base::PathExists(*full_path)) | |
123 return false; | |
124 | |
125 return base::CreateDirectory(*full_path); | |
126 } | |
127 | |
128 // Makes sure that any pending task is processed. | |
129 void FlushQueue(disk_cache::Backend* cache) { | |
130 net::TestCompletionCallback cb; | |
131 int rv = | |
132 reinterpret_cast<disk_cache::BackendImpl*>(cache)->FlushQueueForTest( | |
133 cb.callback()); | |
134 cb.GetResult(rv); // Ignore the result; | |
135 } | |
136 | |
137 bool CreateCache(const base::FilePath& path, | |
138 base::Thread* thread, | |
139 disk_cache::Backend** cache, | |
140 net::TestCompletionCallback* cb) { | |
141 int size = 1024 * 1024; | |
142 disk_cache::BackendImpl* backend = new disk_cache::BackendImpl( | |
143 path, thread->message_loop_proxy().get(), NULL); | |
144 backend->SetMaxSize(size); | |
145 backend->SetType(net::DISK_CACHE); | |
146 backend->SetFlags(disk_cache::kNoRandom); | |
147 int rv = backend->Init(cb->callback()); | |
148 *cache = backend; | |
149 return (cb->GetResult(rv) == net::OK && !(*cache)->GetEntryCount()); | |
150 } | |
151 | |
152 // Generates the files for an empty and one item cache. | |
153 int SimpleInsert(const base::FilePath& path, RankCrashes action, | |
154 base::Thread* cache_thread) { | |
155 net::TestCompletionCallback cb; | |
156 disk_cache::Backend* cache; | |
157 if (!CreateCache(path, cache_thread, &cache, &cb)) | |
158 return GENERIC; | |
159 | |
160 const char* test_name = "some other key"; | |
161 | |
162 if (action <= disk_cache::INSERT_EMPTY_3) { | |
163 test_name = kCrashEntryName; | |
164 disk_cache::g_rankings_crash = action; | |
165 } | |
166 | |
167 disk_cache::Entry* entry; | |
168 int rv = cache->CreateEntry(test_name, &entry, cb.callback()); | |
169 if (cb.GetResult(rv) != net::OK) | |
170 return GENERIC; | |
171 | |
172 entry->Close(); | |
173 FlushQueue(cache); | |
174 | |
175 DCHECK(action <= disk_cache::INSERT_ONE_3); | |
176 disk_cache::g_rankings_crash = action; | |
177 test_name = kCrashEntryName; | |
178 | |
179 rv = cache->CreateEntry(test_name, &entry, cb.callback()); | |
180 if (cb.GetResult(rv) != net::OK) | |
181 return GENERIC; | |
182 | |
183 return NOT_REACHED; | |
184 } | |
185 | |
186 // Generates the files for a one item cache, and removing the head. | |
187 int SimpleRemove(const base::FilePath& path, RankCrashes action, | |
188 base::Thread* cache_thread) { | |
189 DCHECK(action >= disk_cache::REMOVE_ONE_1); | |
190 DCHECK(action <= disk_cache::REMOVE_TAIL_3); | |
191 | |
192 net::TestCompletionCallback cb; | |
193 disk_cache::Backend* cache; | |
194 if (!CreateCache(path, cache_thread, &cache, &cb)) | |
195 return GENERIC; | |
196 | |
197 disk_cache::Entry* entry; | |
198 int rv = cache->CreateEntry(kCrashEntryName, &entry, cb.callback()); | |
199 if (cb.GetResult(rv) != net::OK) | |
200 return GENERIC; | |
201 | |
202 entry->Close(); | |
203 FlushQueue(cache); | |
204 | |
205 if (action >= disk_cache::REMOVE_TAIL_1) { | |
206 rv = cache->CreateEntry("some other key", &entry, cb.callback()); | |
207 if (cb.GetResult(rv) != net::OK) | |
208 return GENERIC; | |
209 | |
210 entry->Close(); | |
211 FlushQueue(cache); | |
212 } | |
213 | |
214 rv = cache->OpenEntry(kCrashEntryName, &entry, cb.callback()); | |
215 if (cb.GetResult(rv) != net::OK) | |
216 return GENERIC; | |
217 | |
218 disk_cache::g_rankings_crash = action; | |
219 entry->Doom(); | |
220 entry->Close(); | |
221 FlushQueue(cache); | |
222 | |
223 return NOT_REACHED; | |
224 } | |
225 | |
226 int HeadRemove(const base::FilePath& path, RankCrashes action, | |
227 base::Thread* cache_thread) { | |
228 DCHECK(action >= disk_cache::REMOVE_HEAD_1); | |
229 DCHECK(action <= disk_cache::REMOVE_HEAD_4); | |
230 | |
231 net::TestCompletionCallback cb; | |
232 disk_cache::Backend* cache; | |
233 if (!CreateCache(path, cache_thread, &cache, &cb)) | |
234 return GENERIC; | |
235 | |
236 disk_cache::Entry* entry; | |
237 int rv = cache->CreateEntry("some other key", &entry, cb.callback()); | |
238 if (cb.GetResult(rv) != net::OK) | |
239 return GENERIC; | |
240 | |
241 entry->Close(); | |
242 FlushQueue(cache); | |
243 rv = cache->CreateEntry(kCrashEntryName, &entry, cb.callback()); | |
244 if (cb.GetResult(rv) != net::OK) | |
245 return GENERIC; | |
246 | |
247 entry->Close(); | |
248 FlushQueue(cache); | |
249 | |
250 rv = cache->OpenEntry(kCrashEntryName, &entry, cb.callback()); | |
251 if (cb.GetResult(rv) != net::OK) | |
252 return GENERIC; | |
253 | |
254 disk_cache::g_rankings_crash = action; | |
255 entry->Doom(); | |
256 entry->Close(); | |
257 FlushQueue(cache); | |
258 | |
259 return NOT_REACHED; | |
260 } | |
261 | |
262 // Generates the files for insertion and removals on heavy loaded caches. | |
263 int LoadOperations(const base::FilePath& path, RankCrashes action, | |
264 base::Thread* cache_thread) { | |
265 DCHECK(action >= disk_cache::INSERT_LOAD_1); | |
266 | |
267 // Work with a tiny index table (16 entries). | |
268 disk_cache::BackendImpl* cache = new disk_cache::BackendImpl( | |
269 path, 0xf, cache_thread->message_loop_proxy().get(), NULL); | |
270 if (!cache->SetMaxSize(0x100000)) | |
271 return GENERIC; | |
272 | |
273 // No experiments and use a simple LRU. | |
274 cache->SetFlags(disk_cache::kNoRandom); | |
275 net::TestCompletionCallback cb; | |
276 int rv = cache->Init(cb.callback()); | |
277 if (cb.GetResult(rv) != net::OK || cache->GetEntryCount()) | |
278 return GENERIC; | |
279 | |
280 int seed = static_cast<int>(Time::Now().ToInternalValue()); | |
281 srand(seed); | |
282 | |
283 disk_cache::Entry* entry; | |
284 for (int i = 0; i < 100; i++) { | |
285 std::string key = GenerateKey(true); | |
286 rv = cache->CreateEntry(key, &entry, cb.callback()); | |
287 if (cb.GetResult(rv) != net::OK) | |
288 return GENERIC; | |
289 entry->Close(); | |
290 FlushQueue(cache); | |
291 if (50 == i && action >= disk_cache::REMOVE_LOAD_1) { | |
292 rv = cache->CreateEntry(kCrashEntryName, &entry, cb.callback()); | |
293 if (cb.GetResult(rv) != net::OK) | |
294 return GENERIC; | |
295 entry->Close(); | |
296 FlushQueue(cache); | |
297 } | |
298 } | |
299 | |
300 if (action <= disk_cache::INSERT_LOAD_2) { | |
301 disk_cache::g_rankings_crash = action; | |
302 | |
303 rv = cache->CreateEntry(kCrashEntryName, &entry, cb.callback()); | |
304 if (cb.GetResult(rv) != net::OK) | |
305 return GENERIC; | |
306 } | |
307 | |
308 rv = cache->OpenEntry(kCrashEntryName, &entry, cb.callback()); | |
309 if (cb.GetResult(rv) != net::OK) | |
310 return GENERIC; | |
311 | |
312 disk_cache::g_rankings_crash = action; | |
313 | |
314 entry->Doom(); | |
315 entry->Close(); | |
316 FlushQueue(cache); | |
317 | |
318 return NOT_REACHED; | |
319 } | |
320 | |
321 // Main function on the child process. | |
322 int SlaveCode(const base::FilePath& path, RankCrashes action) { | |
323 base::MessageLoopForIO message_loop; | |
324 | |
325 base::FilePath full_path; | |
326 if (!CreateTargetFolder(path, action, &full_path)) { | |
327 printf("Destination folder found, please remove it.\n"); | |
328 return CRASH_OVERWRITE; | |
329 } | |
330 | |
331 base::Thread cache_thread("CacheThread"); | |
332 if (!cache_thread.StartWithOptions( | |
333 base::Thread::Options(base::MessageLoop::TYPE_IO, 0))) | |
334 return GENERIC; | |
335 | |
336 if (action <= disk_cache::INSERT_ONE_3) | |
337 return SimpleInsert(full_path, action, &cache_thread); | |
338 | |
339 if (action <= disk_cache::INSERT_LOAD_2) | |
340 return LoadOperations(full_path, action, &cache_thread); | |
341 | |
342 if (action <= disk_cache::REMOVE_ONE_4) | |
343 return SimpleRemove(full_path, action, &cache_thread); | |
344 | |
345 if (action <= disk_cache::REMOVE_HEAD_4) | |
346 return HeadRemove(full_path, action, &cache_thread); | |
347 | |
348 if (action <= disk_cache::REMOVE_TAIL_3) | |
349 return SimpleRemove(full_path, action, &cache_thread); | |
350 | |
351 if (action <= disk_cache::REMOVE_LOAD_3) | |
352 return LoadOperations(full_path, action, &cache_thread); | |
353 | |
354 return NOT_REACHED; | |
355 } | |
356 | |
357 // ----------------------------------------------------------------------- | |
358 | |
359 int main(int argc, const char* argv[]) { | |
360 // Setup an AtExitManager so Singleton objects will be destructed. | |
361 base::AtExitManager at_exit_manager; | |
362 | |
363 if (argc < 2) | |
364 return MasterCode(); | |
365 | |
366 char* end; | |
367 RankCrashes action = static_cast<RankCrashes>(strtol(argv[1], &end, 0)); | |
368 if (action <= disk_cache::NO_CRASH || action >= disk_cache::MAX_CRASH) { | |
369 printf("Invalid action\n"); | |
370 return INVALID_ARGUMENT; | |
371 } | |
372 | |
373 base::FilePath path; | |
374 PathService::Get(base::DIR_SOURCE_ROOT, &path); | |
375 path = path.AppendASCII("net"); | |
376 path = path.AppendASCII("data"); | |
377 path = path.AppendASCII("cache_tests"); | |
378 path = path.AppendASCII("new_crashes"); | |
379 | |
380 return SlaveCode(path, action); | |
381 } | |
OLD | NEW |