Home | History | Annotate | Download | only in disk_cache
      1 // Copyright (c) 2006-2008 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 is a simple application that stress-tests the crash recovery of the disk
      6 // cache. The main application starts a copy of itself on a loop, checking the
      7 // exit code of the child process. When the child dies in an unexpected way,
      8 // the main application quits.
      9 
     10 // The child application has two threads: one to exercise the cache in an
     11 // infinite loop, and another one to asynchronously kill the process.
     12 
     13 #include <string>
     14 #include <vector>
     15 
     16 #include "base/at_exit.h"
     17 #include "base/command_line.h"
     18 #include "base/debug_util.h"
     19 #include "base/file_path.h"
     20 #include "base/logging.h"
     21 #include "base/message_loop.h"
     22 #include "base/path_service.h"
     23 #include "base/platform_thread.h"
     24 #include "base/process_util.h"
     25 #include "base/string_util.h"
     26 #include "base/thread.h"
     27 #include "net/base/io_buffer.h"
     28 #include "net/disk_cache/backend_impl.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 const int kError = -1;
     35 const int kExpectedCrash = 100;
     36 
     37 // Starts a new process.
     38 int RunSlave(int iteration) {
     39   FilePath exe;
     40   PathService::Get(base::FILE_EXE, &exe);
     41 
     42   CommandLine cmdline(exe);
     43   cmdline.AppendLooseValue(ASCIIToWide(IntToString(iteration)));
     44 
     45   base::ProcessHandle handle;
     46   if (!base::LaunchApp(cmdline, false, false, &handle)) {
     47     printf("Unable to run test\n");
     48     return kError;
     49   }
     50 
     51   int exit_code;
     52   if (!base::WaitForExitCode(handle, &exit_code)) {
     53     printf("Unable to get return code\n");
     54     return kError;
     55   }
     56   return exit_code;
     57 }
     58 
     59 // Main loop for the master process.
     60 int MasterCode() {
     61   for (int i = 0; i < 100000; i++) {
     62     int ret = RunSlave(i);
     63     if (kExpectedCrash != ret)
     64       return ret;
     65   }
     66 
     67   printf("More than enough...\n");
     68 
     69   return 0;
     70 }
     71 
     72 // -----------------------------------------------------------------------
     73 
     74 // This thread will loop forever, adding and removing entries from the cache.
     75 // iteration is the current crash cycle, so the entries on the cache are marked
     76 // to know which instance of the application wrote them.
     77 void StressTheCache(int iteration) {
     78   int cache_size = 0x800000;  // 8MB
     79   FilePath path = GetCacheFilePath().AppendASCII("_stress");
     80   disk_cache::BackendImpl* cache = new disk_cache::BackendImpl(path);
     81   cache->SetFlags(disk_cache::kNoLoadProtection | disk_cache::kNoRandom);
     82   cache->SetMaxSize(cache_size);
     83   cache->SetType(net::DISK_CACHE);
     84   if (!cache->Init()) {
     85     printf("Unable to initialize cache.\n");
     86     return;
     87   }
     88   printf("Iteration %d, initial entries: %d\n", iteration,
     89          cache->GetEntryCount());
     90 
     91   int seed = static_cast<int>(Time::Now().ToInternalValue());
     92   srand(seed);
     93 
     94   const int kNumKeys = 5000;
     95   const int kNumEntries = 30;
     96   std::string keys[kNumKeys];
     97   disk_cache::Entry* entries[kNumEntries] = {0};
     98 
     99   for (int i = 0; i < kNumKeys; i++) {
    100     keys[i] = GenerateKey(true);
    101   }
    102 
    103   const int kSize = 4000;
    104   scoped_refptr<net::IOBuffer> buffer = new net::IOBuffer(kSize);
    105   memset(buffer->data(), 'k', kSize);
    106 
    107   for (int i = 0;; i++) {
    108     int slot = rand() % kNumEntries;
    109     int key = rand() % kNumKeys;
    110 
    111     if (entries[slot])
    112       entries[slot]->Close();
    113 
    114     if (!cache->OpenEntry(keys[key], &entries[slot]))
    115       CHECK(cache->CreateEntry(keys[key], &entries[slot]));
    116 
    117     base::snprintf(buffer->data(), kSize, "%d %d", iteration, i);
    118     CHECK(kSize == entries[slot]->WriteData(0, 0, buffer, kSize, NULL, false));
    119 
    120     if (rand() % 100 > 80) {
    121       key = rand() % kNumKeys;
    122       cache->DoomEntry(keys[key]);
    123     }
    124 
    125     if (!(i % 100))
    126       printf("Entries: %d    \r", i);
    127     MessageLoop::current()->RunAllPending();
    128   }
    129 }
    130 
    131 // We want to prevent the timer thread from killing the process while we are
    132 // waiting for the debugger to attach.
    133 bool g_crashing = false;
    134 
    135 class CrashTask : public Task {
    136  public:
    137   CrashTask() {}
    138   ~CrashTask() {}
    139 
    140   virtual void Run() {
    141     // Keep trying to run.
    142     RunSoon(MessageLoop::current());
    143 
    144     if (g_crashing)
    145       return;
    146 
    147     if (rand() % 100 > 1) {
    148       printf("sweet death...\n");
    149 #if defined(OS_WIN)
    150       // Windows does more work on _exit() that we would like, so we use Kill.
    151       base::KillProcessById(base::GetCurrentProcId(), kExpectedCrash, false);
    152 #elif defined(OS_POSIX)
    153       // On POSIX, _exit() will terminate the process with minimal cleanup,
    154       // and it is cleaner than killing.
    155       _exit(kExpectedCrash);
    156 #endif
    157     }
    158   }
    159 
    160   static void RunSoon(MessageLoop* target_loop) {
    161     int task_delay = 10000;  // 10 seconds
    162     CrashTask* task = new CrashTask();
    163     target_loop->PostDelayedTask(FROM_HERE, task, task_delay);
    164   }
    165 };
    166 
    167 // We leak everything here :)
    168 bool StartCrashThread() {
    169   base::Thread* thread = new base::Thread("party_crasher");
    170   if (!thread->Start())
    171     return false;
    172 
    173   CrashTask::RunSoon(thread->message_loop());
    174   return true;
    175 }
    176 
    177 void CrashHandler(const std::string& str) {
    178   g_crashing = true;
    179   DebugUtil::BreakDebugger();
    180 }
    181 
    182 // -----------------------------------------------------------------------
    183 
    184 int main(int argc, const char* argv[]) {
    185   // Setup an AtExitManager so Singleton objects will be destructed.
    186   base::AtExitManager at_exit_manager;
    187 
    188   if (argc < 2)
    189     return MasterCode();
    190 
    191   logging::SetLogAssertHandler(CrashHandler);
    192 
    193   // Some time for the memory manager to flush stuff.
    194   PlatformThread::Sleep(3000);
    195   MessageLoop message_loop(MessageLoop::TYPE_IO);
    196 
    197   char* end;
    198   long int iteration = strtol(argv[1], &end, 0);
    199 
    200   if (!StartCrashThread()) {
    201     printf("failed to start thread\n");
    202     return kError;
    203   }
    204 
    205   StressTheCache(iteration);
    206   return 0;
    207 }
    208