// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/disk_cache/trace.h" #include <stdio.h> #if defined(OS_WIN) #include <windows.h> #endif #include "base/lazy_instance.h" #include "base/logging.h" #include "base/synchronization/lock.h" #include "net/disk_cache/stress_support.h" // Change this value to 1 to enable tracing on a release build. By default, // tracing is enabled only on debug builds. #define ENABLE_TRACING 0 #ifndef NDEBUG #undef ENABLE_TRACING #define ENABLE_TRACING 1 #endif namespace { const int kEntrySize = 12 * sizeof(size_t); #if defined(NET_BUILD_STRESS_CACHE) const int kNumberOfEntries = 500000; #else const int kNumberOfEntries = 5000; // 240 KB on 32bit, 480 KB on 64bit #endif bool s_trace_enabled = false; base::LazyInstance<base::Lock>::Leaky s_lock = LAZY_INSTANCE_INITIALIZER; struct TraceBuffer { int num_traces; int current; char buffer[kNumberOfEntries][kEntrySize]; }; #if ENABLE_TRACING void DebugOutput(const char* msg) { #if defined(OS_WIN) OutputDebugStringA(msg); #else NOTIMPLEMENTED(); #endif } #endif // ENABLE_TRACING } // namespace namespace disk_cache { // s_trace_buffer and s_trace_object are not singletons because I want the // buffer to be destroyed and re-created when the last user goes away, and it // must be straightforward to access the buffer from the debugger. static TraceObject* s_trace_object = NULL; // Static. TraceObject* TraceObject::GetTraceObject() { base::AutoLock lock(s_lock.Get()); if (s_trace_object) return s_trace_object; s_trace_object = new TraceObject(); return s_trace_object; } TraceObject::TraceObject() { InitTrace(); } TraceObject::~TraceObject() { DestroyTrace(); } void TraceObject::EnableTracing(bool enable) { base::AutoLock lock(s_lock.Get()); s_trace_enabled = enable; } #if ENABLE_TRACING static TraceBuffer* s_trace_buffer = NULL; void InitTrace(void) { s_trace_enabled = true; if (s_trace_buffer) return; s_trace_buffer = new TraceBuffer; memset(s_trace_buffer, 0, sizeof(*s_trace_buffer)); } void DestroyTrace(void) { base::AutoLock lock(s_lock.Get()); delete s_trace_buffer; s_trace_buffer = NULL; s_trace_object = NULL; } void Trace(const char* format, ...) { if (!s_trace_buffer || !s_trace_enabled) return; va_list ap; va_start(ap, format); char line[kEntrySize + 2]; #if defined(OS_WIN) vsprintf_s(line, format, ap); #else vsnprintf(line, kEntrySize, format, ap); #endif #if defined(DISK_CACHE_TRACE_TO_LOG) line[kEntrySize] = '\0'; LOG(INFO) << line; #endif va_end(ap); { base::AutoLock lock(s_lock.Get()); if (!s_trace_buffer || !s_trace_enabled) return; memcpy(s_trace_buffer->buffer[s_trace_buffer->current], line, kEntrySize); s_trace_buffer->num_traces++; s_trace_buffer->current++; if (s_trace_buffer->current == kNumberOfEntries) s_trace_buffer->current = 0; } } // Writes the last num_traces to the debugger output. void DumpTrace(int num_traces) { DCHECK(s_trace_buffer); DebugOutput("Last traces:\n"); if (num_traces > kNumberOfEntries || num_traces < 0) num_traces = kNumberOfEntries; if (s_trace_buffer->num_traces) { char line[kEntrySize + 2]; int current = s_trace_buffer->current - num_traces; if (current < 0) current += kNumberOfEntries; for (int i = 0; i < num_traces; i++) { memcpy(line, s_trace_buffer->buffer[current], kEntrySize); line[kEntrySize] = '\0'; size_t length = strlen(line); if (length) { line[length] = '\n'; line[length + 1] = '\0'; DebugOutput(line); } current++; if (current == kNumberOfEntries) current = 0; } } DebugOutput("End of Traces\n"); } #else // ENABLE_TRACING void InitTrace(void) { return; } void DestroyTrace(void) { s_trace_object = NULL; } void Trace(const char* format, ...) { } #endif // ENABLE_TRACING } // namespace disk_cache