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#include "net/disk_cache/blockfile/trace.h"
6
7#include <stdio.h>
8#if defined(OS_WIN)
9#include <windows.h>
10#endif
11
12#include "base/lazy_instance.h"
13#include "base/logging.h"
14#include "base/synchronization/lock.h"
15#include "net/disk_cache/blockfile/stress_support.h"
16
17// Change this value to 1 to enable tracing on a release build. By default,
18// tracing is enabled only on debug builds.
19#define ENABLE_TRACING 0
20
21#ifndef NDEBUG
22#undef ENABLE_TRACING
23#define ENABLE_TRACING 1
24#endif
25
26namespace {
27
28const int kEntrySize = 12 * sizeof(size_t);
29#if defined(NET_BUILD_STRESS_CACHE)
30const int kNumberOfEntries = 500000;
31#else
32const int kNumberOfEntries = 5000;  // 240 KB on 32bit, 480 KB on 64bit
33#endif
34
35bool s_trace_enabled = false;
36base::LazyInstance<base::Lock>::Leaky s_lock = LAZY_INSTANCE_INITIALIZER;
37
38struct TraceBuffer {
39  int num_traces;
40  int current;
41  char buffer[kNumberOfEntries][kEntrySize];
42};
43
44#if ENABLE_TRACING
45void DebugOutput(const char* msg) {
46#if defined(OS_WIN)
47  OutputDebugStringA(msg);
48#else
49  NOTIMPLEMENTED();
50#endif
51}
52#endif  // ENABLE_TRACING
53
54}  // namespace
55
56namespace disk_cache {
57
58// s_trace_buffer and s_trace_object are not singletons because I want the
59// buffer to be destroyed and re-created when the last user goes away, and it
60// must be straightforward to access the buffer from the debugger.
61static TraceObject* s_trace_object = NULL;
62
63// Static.
64TraceObject* TraceObject::GetTraceObject() {
65  base::AutoLock lock(s_lock.Get());
66
67  if (s_trace_object)
68    return s_trace_object;
69
70  s_trace_object = new TraceObject();
71  return s_trace_object;
72}
73
74TraceObject::TraceObject() {
75  InitTrace();
76}
77
78TraceObject::~TraceObject() {
79  DestroyTrace();
80}
81
82void TraceObject::EnableTracing(bool enable) {
83  base::AutoLock lock(s_lock.Get());
84  s_trace_enabled = enable;
85}
86
87#if ENABLE_TRACING
88
89static TraceBuffer* s_trace_buffer = NULL;
90
91void InitTrace(void) {
92  s_trace_enabled = true;
93  if (s_trace_buffer)
94    return;
95
96  s_trace_buffer = new TraceBuffer;
97  memset(s_trace_buffer, 0, sizeof(*s_trace_buffer));
98}
99
100void DestroyTrace(void) {
101  base::AutoLock lock(s_lock.Get());
102
103  delete s_trace_buffer;
104  s_trace_buffer = NULL;
105  s_trace_object = NULL;
106}
107
108void Trace(const char* format, ...) {
109  if (!s_trace_buffer || !s_trace_enabled)
110    return;
111
112  va_list ap;
113  va_start(ap, format);
114  char line[kEntrySize + 2];
115
116#if defined(OS_WIN)
117  vsprintf_s(line, format, ap);
118#else
119  vsnprintf(line, kEntrySize, format, ap);
120#endif
121
122#if defined(DISK_CACHE_TRACE_TO_LOG)
123  line[kEntrySize] = '\0';
124  LOG(INFO) << line;
125#endif
126
127  va_end(ap);
128
129  {
130    base::AutoLock lock(s_lock.Get());
131    if (!s_trace_buffer || !s_trace_enabled)
132      return;
133
134    memcpy(s_trace_buffer->buffer[s_trace_buffer->current], line, kEntrySize);
135
136    s_trace_buffer->num_traces++;
137    s_trace_buffer->current++;
138    if (s_trace_buffer->current == kNumberOfEntries)
139      s_trace_buffer->current = 0;
140  }
141}
142
143// Writes the last num_traces to the debugger output.
144void DumpTrace(int num_traces) {
145  DCHECK(s_trace_buffer);
146  DebugOutput("Last traces:\n");
147
148  if (num_traces > kNumberOfEntries || num_traces < 0)
149    num_traces = kNumberOfEntries;
150
151  if (s_trace_buffer->num_traces) {
152    char line[kEntrySize + 2];
153
154    int current = s_trace_buffer->current - num_traces;
155    if (current < 0)
156      current += kNumberOfEntries;
157
158    for (int i = 0; i < num_traces; i++) {
159      memcpy(line, s_trace_buffer->buffer[current], kEntrySize);
160      line[kEntrySize] = '\0';
161      size_t length = strlen(line);
162      if (length) {
163        line[length] = '\n';
164        line[length + 1] = '\0';
165        DebugOutput(line);
166      }
167
168      current++;
169      if (current ==  kNumberOfEntries)
170        current = 0;
171    }
172  }
173
174  DebugOutput("End of Traces\n");
175}
176
177#else  // ENABLE_TRACING
178
179void InitTrace(void) {
180  return;
181}
182
183void DestroyTrace(void) {
184  s_trace_object = NULL;
185}
186
187void Trace(const char* format, ...) {
188}
189
190#endif  // ENABLE_TRACING
191
192}  // namespace disk_cache
193