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