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/location.h"
26 #include "base/logging.h"
27 #include "base/message_loop/message_loop.h"
28 #include "base/path_service.h"
29 #include "base/process/launch.h"
30 #include "base/process/process.h"
31 #include "base/single_thread_task_runner.h"
32 #include "base/strings/string_number_conversions.h"
33 #include "base/strings/string_util.h"
34 #include "base/strings/utf_string_conversions.h"
35 #include "base/threading/platform_thread.h"
36 #include "base/threading/thread.h"
37 #include "net/base/io_buffer.h"
38 #include "net/base/net_errors.h"
39 #include "net/base/test_completion_callback.h"
40 #include "net/disk_cache/blockfile/backend_impl.h"
41 #include "net/disk_cache/blockfile/stress_support.h"
42 #include "net/disk_cache/blockfile/trace.h"
43 #include "net/disk_cache/disk_cache.h"
44 #include "net/disk_cache/disk_cache_test_util.h"
47 #include "base/logging_win.h"
52 const int kError
= -1;
53 const int kExpectedCrash
= 100;
55 // Starts a new process.
56 int RunSlave(int iteration
) {
58 PathService::Get(base::FILE_EXE
, &exe
);
60 base::CommandLine
cmdline(exe
);
61 cmdline
.AppendArg(base::IntToString(iteration
));
63 base::Process process
= base::LaunchProcess(cmdline
, base::LaunchOptions());
64 if (!process
.IsValid()) {
65 printf("Unable to run test\n");
70 if (!process
.WaitForExit(&exit_code
)) {
71 printf("Unable to get return code\n");
77 // Main loop for the master process.
79 for (int i
= 0; i
< 100000; i
++) {
80 int ret
= RunSlave(i
);
81 if (kExpectedCrash
!= ret
)
85 printf("More than enough...\n");
90 // -----------------------------------------------------------------------
92 std::string
GenerateStressKey() {
94 size_t size
= 50 + rand() % 20000;
95 CacheTestFillBuffer(key
, size
, true);
98 return std::string(key
);
101 // This thread will loop forever, adding and removing entries from the cache.
102 // iteration is the current crash cycle, so the entries on the cache are marked
103 // to know which instance of the application wrote them.
104 void StressTheCache(int iteration
) {
105 int cache_size
= 0x2000000; // 32MB.
106 uint32 mask
= 0xfff; // 4096 entries.
109 PathService::Get(base::DIR_TEMP
, &path
);
110 path
= path
.AppendASCII("cache_test_stress");
112 base::Thread
cache_thread("CacheThread");
113 if (!cache_thread
.StartWithOptions(
114 base::Thread::Options(base::MessageLoop::TYPE_IO
, 0)))
117 disk_cache::BackendImpl
* cache
= new disk_cache::BackendImpl(
118 path
, mask
, cache_thread
.task_runner().get(), NULL
);
119 cache
->SetMaxSize(cache_size
);
120 cache
->SetFlags(disk_cache::kNoLoadProtection
);
122 net::TestCompletionCallback cb
;
123 int rv
= cache
->Init(cb
.callback());
125 if (cb
.GetResult(rv
) != net::OK
) {
126 printf("Unable to initialize cache.\n");
129 printf("Iteration %d, initial entries: %d\n", iteration
,
130 cache
->GetEntryCount());
132 int seed
= static_cast<int>(Time::Now().ToInternalValue());
135 // kNumKeys is meant to be enough to have about 3x or 4x iterations before
136 // the process crashes.
138 const int kNumKeys
= 4000;
140 const int kNumKeys
= 1200;
142 const int kNumEntries
= 30;
143 std::string keys
[kNumKeys
];
144 disk_cache::Entry
* entries
[kNumEntries
] = {0};
146 for (int i
= 0; i
< kNumKeys
; i
++) {
147 keys
[i
] = GenerateStressKey();
150 const int kSize
= 20000;
151 scoped_refptr
<net::IOBuffer
> buffer(new net::IOBuffer(kSize
));
152 memset(buffer
->data(), 'k', kSize
);
154 for (int i
= 0;; i
++) {
155 int slot
= rand() % kNumEntries
;
156 int key
= rand() % kNumKeys
;
157 bool truncate
= (rand() % 2 == 0);
158 int size
= kSize
- (rand() % 20) * kSize
/ 20;
161 entries
[slot
]->Close();
163 net::TestCompletionCallback cb
;
164 rv
= cache
->OpenEntry(keys
[key
], &entries
[slot
], cb
.callback());
165 if (cb
.GetResult(rv
) != net::OK
) {
166 rv
= cache
->CreateEntry(keys
[key
], &entries
[slot
], cb
.callback());
167 CHECK_EQ(net::OK
, cb
.GetResult(rv
));
170 base::snprintf(buffer
->data(), kSize
,
171 "i: %d iter: %d, size: %d, truncate: %d ", i
, iteration
,
172 size
, truncate
? 1 : 0);
173 rv
= entries
[slot
]->WriteData(0, 0, buffer
.get(), size
, cb
.callback(),
175 CHECK_EQ(size
, cb
.GetResult(rv
));
177 if (rand() % 100 > 80) {
178 key
= rand() % kNumKeys
;
179 net::TestCompletionCallback cb2
;
180 rv
= cache
->DoomEntry(keys
[key
], cb2
.callback());
185 printf("Entries: %d \r", i
);
189 // We want to prevent the timer thread from killing the process while we are
190 // waiting for the debugger to attach.
191 bool g_crashing
= false;
193 // RunSoon() and CrashCallback() reference each other, unfortunately.
194 void RunSoon(base::MessageLoop
* target_loop
);
196 void CrashCallback() {
197 // Keep trying to run.
198 RunSoon(base::MessageLoop::current());
203 if (rand() % 100 > 30) {
204 printf("sweet death...\n");
206 // Windows does more work on _exit() than we would like.
207 base::Process::Current().Terminate(kExpectedCrash
, false);
208 #elif defined(OS_POSIX)
209 // On POSIX, _exit() will terminate the process with minimal cleanup,
210 // and it is cleaner than killing.
211 _exit(kExpectedCrash
);
216 void RunSoon(base::MessageLoop
* target_loop
) {
217 const base::TimeDelta kTaskDelay
= base::TimeDelta::FromSeconds(10);
218 target_loop
->task_runner()->PostDelayedTask(
219 FROM_HERE
, base::Bind(&CrashCallback
), kTaskDelay
);
222 // We leak everything here :)
223 bool StartCrashThread() {
224 base::Thread
* thread
= new base::Thread("party_crasher");
225 if (!thread
->Start())
228 RunSoon(thread
->message_loop());
232 void CrashHandler(const std::string
& str
) {
234 base::debug::BreakDebugger();
237 bool MessageHandler(int severity
, const char* file
, int line
,
238 size_t message_start
, const std::string
& str
) {
239 const size_t kMaxMessageLen
= 48;
240 char message
[kMaxMessageLen
];
241 size_t len
= std::min(str
.length() - message_start
, kMaxMessageLen
- 1);
243 memcpy(message
, str
.c_str() + message_start
, len
);
245 #if !defined(DISK_CACHE_TRACE_TO_LOG)
246 disk_cache::Trace("%s", message
);
251 // -----------------------------------------------------------------------
254 // {B9A153D4-31C3-48e4-9ABF-D54383F14A0D}
255 const GUID kStressCacheTraceProviderName
= {
256 0xb9a153d4, 0x31c3, 0x48e4,
257 { 0x9a, 0xbf, 0xd5, 0x43, 0x83, 0xf1, 0x4a, 0xd } };
260 int main(int argc
, const char* argv
[]) {
261 // Setup an AtExitManager so Singleton objects will be destructed.
262 base::AtExitManager at_exit_manager
;
267 logging::SetLogAssertHandler(CrashHandler
);
268 logging::SetLogMessageHandler(MessageHandler
);
271 logging::LogEventProvider::Initialize(kStressCacheTraceProviderName
);
273 base::CommandLine::Init(argc
, argv
);
274 logging::LoggingSettings settings
;
275 settings
.logging_dest
= logging::LOG_TO_SYSTEM_DEBUG_LOG
;
276 logging::InitLogging(settings
);
279 // Some time for the memory manager to flush stuff.
280 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(3));
281 base::MessageLoopForIO message_loop
;
284 long int iteration
= strtol(argv
[1], &end
, 0);
286 if (!StartCrashThread()) {
287 printf("failed to start thread\n");
291 StressTheCache(iteration
);