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