Home | History | Annotate | Download | only in crash_cache
      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.
     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.
     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.
     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.
    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.
    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.
    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 
    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.
    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.
    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 
    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