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