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