1 // Copyright (c) 2012 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.
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.
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.
13 // A regular build should never crash.
14 // To test that the disk cache doesn't generate critical errors with regular
15 // application level crashes, edit stress_support.h.
20 #include "base/at_exit.h"
21 #include "base/bind.h"
22 #include "base/command_line.h"
23 #include "base/debug/debugger.h"
24 #include "base/files/file_path.h"
25 #include "base/logging.h"
26 #include "base/message_loop/message_loop.h"
27 #include "base/path_service.h"
28 #include "base/process/kill.h"
29 #include "base/process/launch.h"
30 #include "base/process/process_handle.h"
31 #include "base/strings/string_number_conversions.h"
32 #include "base/strings/string_util.h"
33 #include "base/strings/utf_string_conversions.h"
34 #include "base/threading/platform_thread.h"
35 #include "base/threading/thread.h"
36 #include "net/base/io_buffer.h"
37 #include "net/base/net_errors.h"
38 #include "net/base/test_completion_callback.h"
39 #include "net/disk_cache/backend_impl.h"
40 #include "net/disk_cache/disk_cache.h"
41 #include "net/disk_cache/disk_cache_test_util.h"
42 #include "net/disk_cache/stress_support.h"
43 #include "net/disk_cache/trace.h"
46 #include "base/logging_win.h"
51 const int kError
= -1;
52 const int kExpectedCrash
= 100;
54 // Starts a new process.
55 int RunSlave(int iteration
) {
57 PathService::Get(base::FILE_EXE
, &exe
);
59 CommandLine
cmdline(exe
);
60 cmdline
.AppendArg(base::IntToString(iteration
));
62 base::ProcessHandle handle
;
63 if (!base::LaunchProcess(cmdline
, base::LaunchOptions(), &handle
)) {
64 printf("Unable to run test\n");
69 if (!base::WaitForExitCode(handle
, &exit_code
)) {
70 printf("Unable to get return code\n");
76 // Main loop for the master process.
78 for (int i
= 0; i
< 100000; i
++) {
79 int ret
= RunSlave(i
);
80 if (kExpectedCrash
!= ret
)
84 printf("More than enough...\n");
89 // -----------------------------------------------------------------------
91 std::string
GenerateStressKey() {
93 size_t size
= 50 + rand() % 20000;
94 CacheTestFillBuffer(key
, size
, true);
97 return std::string(key
);
100 // This thread will loop forever, adding and removing entries from the cache.
101 // iteration is the current crash cycle, so the entries on the cache are marked
102 // to know which instance of the application wrote them.
103 void StressTheCache(int iteration
) {
104 int cache_size
= 0x2000000; // 32MB.
105 uint32 mask
= 0xfff; // 4096 entries.
108 PathService::Get(base::DIR_TEMP
, &path
);
109 path
= path
.AppendASCII("cache_test_stress");
111 base::Thread
cache_thread("CacheThread");
112 if (!cache_thread
.StartWithOptions(
113 base::Thread::Options(base::MessageLoop::TYPE_IO
, 0)))
116 disk_cache::BackendImpl
* cache
=
117 new disk_cache::BackendImpl(path
, mask
,
118 cache_thread
.message_loop_proxy().get(),
120 cache
->SetMaxSize(cache_size
);
121 cache
->SetFlags(disk_cache::kNoLoadProtection
);
123 net::TestCompletionCallback cb
;
124 int rv
= cache
->Init(cb
.callback());
126 if (cb
.GetResult(rv
) != net::OK
) {
127 printf("Unable to initialize cache.\n");
130 printf("Iteration %d, initial entries: %d\n", iteration
,
131 cache
->GetEntryCount());
133 int seed
= static_cast<int>(Time::Now().ToInternalValue());
136 // kNumKeys is meant to be enough to have about 3x or 4x iterations before
137 // the process crashes.
139 const int kNumKeys
= 4000;
141 const int kNumKeys
= 1200;
143 const int kNumEntries
= 30;
144 std::string keys
[kNumKeys
];
145 disk_cache::Entry
* entries
[kNumEntries
] = {0};
147 for (int i
= 0; i
< kNumKeys
; i
++) {
148 keys
[i
] = GenerateStressKey();
151 const int kSize
= 20000;
152 scoped_refptr
<net::IOBuffer
> buffer(new net::IOBuffer(kSize
));
153 memset(buffer
->data(), 'k', kSize
);
155 for (int i
= 0;; i
++) {
156 int slot
= rand() % kNumEntries
;
157 int key
= rand() % kNumKeys
;
158 bool truncate
= (rand() % 2 == 0);
159 int size
= kSize
- (rand() % 20) * kSize
/ 20;
162 entries
[slot
]->Close();
164 net::TestCompletionCallback cb
;
165 rv
= cache
->OpenEntry(keys
[key
], &entries
[slot
], cb
.callback());
166 if (cb
.GetResult(rv
) != net::OK
) {
167 rv
= cache
->CreateEntry(keys
[key
], &entries
[slot
], cb
.callback());
168 CHECK_EQ(net::OK
, cb
.GetResult(rv
));
171 base::snprintf(buffer
->data(), kSize
,
172 "i: %d iter: %d, size: %d, truncate: %d ", i
, iteration
,
173 size
, truncate
? 1 : 0);
174 rv
= entries
[slot
]->WriteData(0, 0, buffer
.get(), size
, cb
.callback(),
176 CHECK_EQ(size
, cb
.GetResult(rv
));
178 if (rand() % 100 > 80) {
179 key
= rand() % kNumKeys
;
180 net::TestCompletionCallback cb2
;
181 rv
= cache
->DoomEntry(keys
[key
], cb2
.callback());
186 printf("Entries: %d \r", i
);
190 // We want to prevent the timer thread from killing the process while we are
191 // waiting for the debugger to attach.
192 bool g_crashing
= false;
194 // RunSoon() and CrashCallback() reference each other, unfortunately.
195 void RunSoon(base::MessageLoop
* target_loop
);
197 void CrashCallback() {
198 // Keep trying to run.
199 RunSoon(base::MessageLoop::current());
204 if (rand() % 100 > 30) {
205 printf("sweet death...\n");
207 // Windows does more work on _exit() that we would like, so we use Kill.
208 base::KillProcessById(base::GetCurrentProcId(), kExpectedCrash
, false);
209 #elif defined(OS_POSIX)
210 // On POSIX, _exit() will terminate the process with minimal cleanup,
211 // and it is cleaner than killing.
212 _exit(kExpectedCrash
);
217 void RunSoon(base::MessageLoop
* target_loop
) {
218 const base::TimeDelta kTaskDelay
= base::TimeDelta::FromSeconds(10);
219 target_loop
->PostDelayedTask(
220 FROM_HERE
, base::Bind(&CrashCallback
), kTaskDelay
);
223 // We leak everything here :)
224 bool StartCrashThread() {
225 base::Thread
* thread
= new base::Thread("party_crasher");
226 if (!thread
->Start())
229 RunSoon(thread
->message_loop());
233 void CrashHandler(const std::string
& str
) {
235 base::debug::BreakDebugger();
238 bool MessageHandler(int severity
, const char* file
, int line
,
239 size_t message_start
, const std::string
& str
) {
240 const size_t kMaxMessageLen
= 48;
241 char message
[kMaxMessageLen
];
242 size_t len
= std::min(str
.length() - message_start
, kMaxMessageLen
- 1);
244 memcpy(message
, str
.c_str() + message_start
, len
);
246 #if !defined(DISK_CACHE_TRACE_TO_LOG)
247 disk_cache::Trace("%s", message
);
252 // -----------------------------------------------------------------------
255 // {B9A153D4-31C3-48e4-9ABF-D54383F14A0D}
256 const GUID kStressCacheTraceProviderName
= {
257 0xb9a153d4, 0x31c3, 0x48e4,
258 { 0x9a, 0xbf, 0xd5, 0x43, 0x83, 0xf1, 0x4a, 0xd } };
261 int main(int argc
, const char* argv
[]) {
262 // Setup an AtExitManager so Singleton objects will be destructed.
263 base::AtExitManager at_exit_manager
;
268 logging::SetLogAssertHandler(CrashHandler
);
269 logging::SetLogMessageHandler(MessageHandler
);
272 logging::LogEventProvider::Initialize(kStressCacheTraceProviderName
);
274 CommandLine::Init(argc
, argv
);
275 logging::LoggingSettings settings
;
276 settings
.logging_dest
= logging::LOG_TO_SYSTEM_DEBUG_LOG
;
277 logging::InitLogging(settings
);
280 // Some time for the memory manager to flush stuff.
281 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(3));
282 base::MessageLoop
message_loop(base::MessageLoop::TYPE_IO
);
285 long int iteration
= strtol(argv
[1], &end
, 0);
287 if (!StartCrashThread()) {
288 printf("failed to start thread\n");
292 StressTheCache(iteration
);