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