/Users/deen/code/yugabyte-db/src/yb/util/trace.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // |
18 | | // The following only applies to changes made to this file as part of YugaByte development. |
19 | | // |
20 | | // Portions Copyright (c) YugaByte, Inc. |
21 | | // |
22 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
23 | | // in compliance with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
28 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
29 | | // or implied. See the License for the specific language governing permissions and limitations |
30 | | // under the License. |
31 | | // |
32 | | |
33 | | #include "yb/util/trace.h" |
34 | | |
35 | | #include <iomanip> |
36 | | #include <iostream> |
37 | | #include <string> |
38 | | #include <vector> |
39 | | |
40 | | #include <boost/range/adaptor/indirected.hpp> |
41 | | |
42 | | #include "yb/gutil/strings/substitute.h" |
43 | | #include "yb/gutil/walltime.h" |
44 | | |
45 | | #include "yb/util/flag_tags.h" |
46 | | #include "yb/util/memory/arena.h" |
47 | | #include "yb/util/memory/memory.h" |
48 | | #include "yb/util/object_pool.h" |
49 | | #include "yb/util/size_literals.h" |
50 | | |
51 | | DEFINE_bool(enable_tracing, false, "Flag to enable/disable tracing across the code."); |
52 | | TAG_FLAG(enable_tracing, advanced); |
53 | | TAG_FLAG(enable_tracing, runtime); |
54 | | |
55 | | DEFINE_bool(use_monotime_for_traces, false, "Flag to enable use of MonoTime::Now() instead of " |
56 | | "CoarseMonoClock::Now(). CoarseMonoClock is much cheaper so it is better to use it. However " |
57 | | "if we need more accurate sub-millisecond level breakdown, we could use MonoTime."); |
58 | | TAG_FLAG(use_monotime_for_traces, advanced); |
59 | | TAG_FLAG(use_monotime_for_traces, runtime); |
60 | | |
61 | | DEFINE_int32(tracing_level, 0, "verbosity levels (like --v) up to which tracing is enabled."); |
62 | | TAG_FLAG(tracing_level, advanced); |
63 | | TAG_FLAG(tracing_level, runtime); |
64 | | |
65 | | DEFINE_int32(print_nesting_levels, 5, "controls the depth of the child traces to be printed."); |
66 | | TAG_FLAG(print_nesting_levels, advanced); |
67 | | TAG_FLAG(print_nesting_levels, runtime); |
68 | | |
69 | | namespace yb { |
70 | | |
71 | | using strings::internal::SubstituteArg; |
72 | | |
73 | | __thread Trace* Trace::threadlocal_trace_; |
74 | | |
75 | | namespace { |
76 | | |
77 | | const char* kNestedChildPrefix = ".. "; |
78 | | |
79 | | // Get the part of filepath after the last path separator. |
80 | | // (Doesn't modify filepath, contrary to basename() in libgen.h.) |
81 | | // Borrowed from glog. |
82 | 26.6k | const char* const_basename(const char* filepath) { |
83 | 26.6k | const char* base = strrchr(filepath, '/'); |
84 | 26.6k | return base ? (base + 1) : filepath; |
85 | 26.6k | } |
86 | | |
87 | | template <class Children> |
88 | | void DumpChildren( |
89 | 7.96k | std::ostream* out, int32_t tracing_depth, bool include_time_deltas, const Children* children) { |
90 | 7.96k | if (tracing_depth > GetAtomicFlag(&FLAGS_print_nesting_levels)) { |
91 | 0 | return; |
92 | 0 | } |
93 | 7.96k | for (auto &child_trace : *children) { |
94 | 17.0k | for (int i = 0; i < tracing_depth; i++) { |
95 | 11.5k | *out << kNestedChildPrefix; |
96 | 11.5k | } |
97 | 5.42k | *out << "Related trace:" << std::endl; |
98 | 5.42k | *out << child_trace->DumpToString(tracing_depth, include_time_deltas); |
99 | 5.42k | } |
100 | 7.96k | } |
101 | | |
102 | | void DumpChildren( |
103 | 0 | std::ostream* out, int32_t tracing_depth, bool include_time_deltas, std::nullptr_t children) {} |
104 | | |
105 | | template <class Entries> |
106 | | void DumpEntries( |
107 | | std::ostream* out, |
108 | | int32_t tracing_depth, |
109 | | bool include_time_deltas, |
110 | | int64_t start, |
111 | 7.96k | const Entries& entries) { |
112 | 7.96k | if (entries.empty()) { |
113 | 228 | return; |
114 | 228 | } |
115 | | |
116 | 7.73k | auto time_usec = MonoDelta(entries.begin()->timestamp.time_since_epoch()).ToMicroseconds(); |
117 | 7.73k | const int64_t time_correction_usec = start - time_usec; |
118 | 7.73k | int64_t prev_usecs = time_usec; |
119 | 26.6k | for (const auto& e : entries) { |
120 | 26.6k | time_usec = MonoDelta(e.timestamp.time_since_epoch()).ToMicroseconds(); |
121 | 26.6k | const int64_t usecs_since_prev = time_usec - prev_usecs; |
122 | 26.6k | prev_usecs = time_usec; |
123 | | |
124 | 26.6k | const auto absolute_time_usec = time_usec + time_correction_usec; |
125 | 26.6k | const time_t secs_since_epoch = absolute_time_usec / 1000000; |
126 | 26.6k | const int usecs = absolute_time_usec % 1000000; |
127 | 26.6k | struct tm tm_time; |
128 | 26.6k | localtime_r(&secs_since_epoch, &tm_time); |
129 | | |
130 | 65.5k | for (int i = 0; i < tracing_depth; i++) { |
131 | 38.9k | *out << kNestedChildPrefix; |
132 | 38.9k | } |
133 | | // Log format borrowed from glog/logging.cc |
134 | 26.6k | using std::setw; |
135 | 26.6k | out->fill('0'); |
136 | | |
137 | 26.6k | *out << setw(2) << (1 + tm_time.tm_mon) |
138 | 26.6k | << setw(2) << tm_time.tm_mday |
139 | 26.6k | << ' ' |
140 | 26.6k | << setw(2) << tm_time.tm_hour << ':' |
141 | 26.6k | << setw(2) << tm_time.tm_min << ':' |
142 | 26.6k | << setw(2) << tm_time.tm_sec << '.' |
143 | 26.6k | << setw(6) << usecs << ' '; |
144 | 26.6k | if (include_time_deltas) { |
145 | 26.6k | out->fill(' '); |
146 | 26.6k | *out << "(+" << setw(6) << usecs_since_prev << "us) "; |
147 | 26.6k | } |
148 | 26.6k | e.Dump(out); |
149 | 26.6k | *out << std::endl; |
150 | 26.6k | } |
151 | 7.73k | } trace.cc:_ZN2yb12_GLOBAL__N_111DumpEntriesIN5boost12range_detail16indirected_rangeINSt3__16vectorIPNS_10TraceEntryENS5_9allocatorIS8_EEEEEEEEvPNS5_13basic_ostreamIcNS5_11char_traitsIcEEEEibxRKT_ Line | Count | Source | 111 | 7.96k | const Entries& entries) { | 112 | 7.96k | if (entries.empty()) { | 113 | 228 | return; | 114 | 228 | } | 115 | | | 116 | 7.73k | auto time_usec = MonoDelta(entries.begin()->timestamp.time_since_epoch()).ToMicroseconds(); | 117 | 7.73k | const int64_t time_correction_usec = start - time_usec; | 118 | 7.73k | int64_t prev_usecs = time_usec; | 119 | 26.6k | for (const auto& e : entries) { | 120 | 26.6k | time_usec = MonoDelta(e.timestamp.time_since_epoch()).ToMicroseconds(); | 121 | 26.6k | const int64_t usecs_since_prev = time_usec - prev_usecs; | 122 | 26.6k | prev_usecs = time_usec; | 123 | | | 124 | 26.6k | const auto absolute_time_usec = time_usec + time_correction_usec; | 125 | 26.6k | const time_t secs_since_epoch = absolute_time_usec / 1000000; | 126 | 26.6k | const int usecs = absolute_time_usec % 1000000; | 127 | 26.6k | struct tm tm_time; | 128 | 26.6k | localtime_r(&secs_since_epoch, &tm_time); | 129 | | | 130 | 65.5k | for (int i = 0; i < tracing_depth; i++) { | 131 | 38.9k | *out << kNestedChildPrefix; | 132 | 38.9k | } | 133 | | // Log format borrowed from glog/logging.cc | 134 | 26.6k | using std::setw; | 135 | 26.6k | out->fill('0'); | 136 | | | 137 | 26.6k | *out << setw(2) << (1 + tm_time.tm_mon) | 138 | 26.6k | << setw(2) << tm_time.tm_mday | 139 | 26.6k | << ' ' | 140 | 26.6k | << setw(2) << tm_time.tm_hour << ':' | 141 | 26.6k | << setw(2) << tm_time.tm_min << ':' | 142 | 26.6k | << setw(2) << tm_time.tm_sec << '.' | 143 | 26.6k | << setw(6) << usecs << ' '; | 144 | 26.6k | if (include_time_deltas) { | 145 | 26.6k | out->fill(' '); | 146 | 26.6k | *out << "(+" << setw(6) << usecs_since_prev << "us) "; | 147 | 26.6k | } | 148 | 26.6k | e.Dump(out); | 149 | 26.6k | *out << std::endl; | 150 | 26.6k | } | 151 | 7.73k | } |
Unexecuted instantiation: trace.cc:_ZN2yb12_GLOBAL__N_111DumpEntriesIN5boost14iterator_rangeIPKNS_10PlainTrace5EntryEEEEEvPNSt3__113basic_ostreamIcNS9_11char_traitsIcEEEEibxRKT_ |
152 | | |
153 | | template <class Entries, class Children> |
154 | | void DoDump( |
155 | | std::ostream* out, |
156 | | int32_t tracing_depth, |
157 | | bool include_time_deltas, |
158 | | int64_t start, |
159 | | const Entries& entries, |
160 | 7.96k | Children children) { |
161 | | // Save original flags. |
162 | 7.96k | std::ios::fmtflags save_flags(out->flags()); |
163 | | |
164 | 7.96k | DumpEntries(out, tracing_depth, include_time_deltas, start, entries); |
165 | 7.96k | DumpChildren(out, tracing_depth + 1, include_time_deltas, children); |
166 | | |
167 | | // Restore stream flags. |
168 | 7.96k | out->flags(save_flags); |
169 | 7.96k | } trace.cc:_ZN2yb12_GLOBAL__N_16DoDumpIN5boost12range_detail16indirected_rangeINSt3__16vectorIPNS_10TraceEntryENS5_9allocatorIS8_EEEEEEPNS6_I13scoped_refptrINS_5TraceEENS9_ISF_EEEEEEvPNS5_13basic_ostreamIcNS5_11char_traitsIcEEEEibxRKT_T0_ Line | Count | Source | 160 | 7.96k | Children children) { | 161 | | // Save original flags. | 162 | 7.96k | std::ios::fmtflags save_flags(out->flags()); | 163 | | | 164 | 7.96k | DumpEntries(out, tracing_depth, include_time_deltas, start, entries); | 165 | 7.96k | DumpChildren(out, tracing_depth + 1, include_time_deltas, children); | 166 | | | 167 | | // Restore stream flags. | 168 | 7.96k | out->flags(save_flags); | 169 | 7.96k | } |
Unexecuted instantiation: trace.cc:_ZN2yb12_GLOBAL__N_16DoDumpIN5boost14iterator_rangeIPKNS_10PlainTrace5EntryEEEDnEEvPNSt3__113basic_ostreamIcNS9_11char_traitsIcEEEEibxRKT_T0_ |
170 | | |
171 | | std::once_flag init_get_current_micros_fast_flag; |
172 | | int64_t initial_micros_offset; |
173 | | |
174 | 1.87k | void InitGetCurrentMicrosFast() { |
175 | 1.87k | auto before = CoarseMonoClock::Now(); |
176 | 1.87k | initial_micros_offset = GetCurrentTimeMicros(); |
177 | 1.87k | auto after = CoarseMonoClock::Now(); |
178 | 1.87k | auto mid = MonoDelta(after.time_since_epoch() + before.time_since_epoch()).ToMicroseconds() / 2; |
179 | 1.87k | initial_micros_offset -= mid; |
180 | 1.87k | } |
181 | | |
182 | 8.33M | int64_t GetCurrentMicrosFast(CoarseTimePoint now) { |
183 | 8.33M | std::call_once(init_get_current_micros_fast_flag, InitGetCurrentMicrosFast); |
184 | 8.33M | return initial_micros_offset + MonoDelta(now.time_since_epoch()).ToMicroseconds(); |
185 | 8.33M | } |
186 | | |
187 | | } // namespace |
188 | | |
189 | | ScopedAdoptTrace::ScopedAdoptTrace(Trace* t) |
190 | 121M | : old_trace_(Trace::threadlocal_trace_), is_enabled_(GetAtomicFlag(&FLAGS_enable_tracing)) { |
191 | 121M | if (is_enabled_) { |
192 | 13.5M | trace_ = t; |
193 | 13.5M | Trace::threadlocal_trace_ = t; |
194 | 13.5M | DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); |
195 | 13.5M | } |
196 | 121M | } |
197 | | |
198 | 121M | ScopedAdoptTrace::~ScopedAdoptTrace() { |
199 | 121M | if (is_enabled_) { |
200 | 13.5M | Trace::threadlocal_trace_ = old_trace_; |
201 | | // It's critical that we Release() the reference count on 't' only |
202 | | // after we've unset the thread-local variable. Otherwise, we can hit |
203 | | // a nasty interaction with tcmalloc contention profiling. Consider |
204 | | // the following sequence: |
205 | | // |
206 | | // 1. threadlocal_trace_ has refcount = 1 |
207 | | // 2. we call threadlocal_trace_->Release() which decrements refcount to 0 |
208 | | // 3. this calls 'delete' on the Trace object |
209 | | // 3a. this calls tcmalloc free() on the Trace and various sub-objects |
210 | | // 3b. the free() calls may end up experiencing contention in tcmalloc |
211 | | // 3c. we try to account the contention in threadlocal_trace_'s TraceMetrics, |
212 | | // but it has already been freed. |
213 | | // |
214 | | // In the best case, we just scribble into some free tcmalloc memory. In the |
215 | | // worst case, tcmalloc would have already re-used this memory for a new |
216 | | // allocation on another thread, and we end up overwriting someone else's memory. |
217 | | // |
218 | | // Waiting to Release() only after 'unpublishing' the trace solves this. |
219 | 13.5M | trace_.reset(); |
220 | 13.5M | DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); |
221 | 13.5M | } |
222 | 121M | } |
223 | | |
224 | | // Struct which precedes each entry in the trace. |
225 | | struct TraceEntry { |
226 | | CoarseTimePoint timestamp; |
227 | | |
228 | | // The source file and line number which generated the trace message. |
229 | | const char* file_path; |
230 | | int line_number; |
231 | | |
232 | | size_t message_len; |
233 | | TraceEntry* next; |
234 | | char message[0]; |
235 | | |
236 | 26.6k | void Dump(std::ostream* out) const { |
237 | 26.6k | *out << const_basename(file_path) << ':' << line_number |
238 | 26.6k | << "] "; |
239 | 26.6k | out->write(message, message_len); |
240 | 26.6k | } |
241 | | }; |
242 | | |
243 | 81.7M | Trace::Trace() { |
244 | 81.7M | } |
245 | | |
246 | 16.6M | ThreadSafeObjectPool<ThreadSafeArena>& ArenaPool() { |
247 | 2.27M | static ThreadSafeObjectPool<ThreadSafeArena> result([] { |
248 | 2.27M | return new ThreadSafeArena(8_KB, 128_KB); |
249 | 2.27M | }); |
250 | 16.6M | return result; |
251 | 16.6M | } |
252 | | |
253 | 81.7M | Trace::~Trace() { |
254 | 81.7M | auto* arena = arena_.load(std::memory_order_acquire); |
255 | 81.7M | if (arena) { |
256 | 8.30M | arena->Reset(); |
257 | 8.30M | ArenaPool().Release(arena); |
258 | 8.30M | } |
259 | 81.7M | } |
260 | | |
261 | 51.6M | ThreadSafeArena* Trace::GetAndInitArena() { |
262 | 51.6M | auto* arena = arena_.load(std::memory_order_acquire); |
263 | 51.6M | if (arena == nullptr) { |
264 | 8.31M | arena = ArenaPool().Take(); |
265 | 8.31M | ThreadSafeArena* existing_arena = nullptr; |
266 | 8.32M | if (arena_.compare_exchange_strong(existing_arena, arena, std::memory_order_release)) { |
267 | 8.32M | return arena; |
268 | 18.4E | } else { |
269 | 18.4E | ArenaPool().Release(arena); |
270 | 18.4E | return existing_arena; |
271 | 18.4E | } |
272 | 43.3M | } |
273 | 43.3M | return arena; |
274 | 43.3M | } |
275 | | |
276 | | void Trace::SubstituteAndTrace( |
277 | 41.0M | const char* file_path, int line_number, CoarseTimePoint now, GStringPiece format) { |
278 | 41.0M | auto msg_len = format.size(); |
279 | 0 | DCHECK_NE(msg_len, 0) << "Bad format specification"; |
280 | 41.0M | TraceEntry* entry = NewEntry(msg_len, file_path, line_number, now); |
281 | 41.0M | if (entry == nullptr) return; |
282 | 41.0M | memcpy(entry->message, format.data(), msg_len); |
283 | 41.0M | AddEntry(entry); |
284 | 41.0M | } |
285 | | |
286 | | void Trace::SubstituteAndTrace(const char* file_path, |
287 | | int line_number, |
288 | | CoarseTimePoint now, |
289 | | GStringPiece format, |
290 | | const SubstituteArg& arg0, const SubstituteArg& arg1, |
291 | | const SubstituteArg& arg2, const SubstituteArg& arg3, |
292 | | const SubstituteArg& arg4, const SubstituteArg& arg5, |
293 | | const SubstituteArg& arg6, const SubstituteArg& arg7, |
294 | 10.6M | const SubstituteArg& arg8, const SubstituteArg& arg9) { |
295 | 10.6M | const SubstituteArg* const args_array[] = { |
296 | 10.6M | &arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arg7, &arg8, &arg9, nullptr |
297 | 10.6M | }; |
298 | | |
299 | 10.6M | int msg_len = strings::internal::SubstitutedSize(format, args_array); |
300 | 0 | DCHECK_NE(msg_len, 0) << "Bad format specification"; |
301 | 10.6M | TraceEntry* entry = NewEntry(msg_len, file_path, line_number, now); |
302 | 10.6M | if (entry == nullptr) return; |
303 | 10.6M | SubstituteToBuffer(format, args_array, entry->message); |
304 | 10.6M | AddEntry(entry); |
305 | 10.6M | } |
306 | | |
307 | | TraceEntry* Trace::NewEntry( |
308 | 51.6M | size_t msg_len, const char* file_path, int line_number, CoarseTimePoint now) { |
309 | 51.6M | auto* arena = GetAndInitArena(); |
310 | 51.6M | size_t size = offsetof(TraceEntry, message) + msg_len; |
311 | 51.6M | void* dst = arena->AllocateBytesAligned(size, alignof(TraceEntry)); |
312 | 51.6M | if (dst == nullptr) { |
313 | 0 | LOG(ERROR) << "NewEntry(msg_len, " << file_path << ", " << line_number |
314 | 0 | << ") received nullptr from AllocateBytes.\n So far:" << DumpToString(true); |
315 | 0 | return nullptr; |
316 | 0 | } |
317 | 51.6M | TraceEntry* entry = new (dst) TraceEntry; |
318 | 51.6M | entry->timestamp = now; |
319 | 51.6M | entry->message_len = msg_len; |
320 | 51.6M | entry->file_path = file_path; |
321 | 51.6M | entry->line_number = line_number; |
322 | 51.6M | return entry; |
323 | 51.6M | } |
324 | | |
325 | 51.7M | void Trace::AddEntry(TraceEntry* entry) { |
326 | 51.7M | std::lock_guard<simple_spinlock> l(lock_); |
327 | 51.7M | entry->next = nullptr; |
328 | | |
329 | 51.7M | if (entries_tail_ != nullptr) { |
330 | 43.4M | entries_tail_->next = entry; |
331 | 8.25M | } else { |
332 | 8.25M | DCHECK(entries_head_ == nullptr); |
333 | 8.25M | entries_head_ = entry; |
334 | 8.25M | trace_start_time_usec_ = GetCurrentMicrosFast(entry->timestamp); |
335 | 8.25M | } |
336 | 51.7M | entries_tail_ = entry; |
337 | 51.7M | } |
338 | | |
339 | 124 | void Trace::Dump(std::ostream *out, bool include_time_deltas) const { |
340 | 124 | Dump(out, 0, include_time_deltas); |
341 | 124 | } |
342 | | |
343 | 7.96k | void Trace::Dump(std::ostream* out, int32_t tracing_depth, bool include_time_deltas) const { |
344 | | // Gather a copy of the list of entries under the lock. This is fast |
345 | | // enough that we aren't worried about stalling concurrent tracers |
346 | | // (whereas doing the logging itself while holding the lock might be |
347 | | // too slow, if the output stream is a file, for example). |
348 | 7.96k | vector<TraceEntry*> entries; |
349 | 7.96k | vector<scoped_refptr<Trace> > child_traces; |
350 | 7.96k | decltype(trace_start_time_usec_) trace_start_time_usec; |
351 | 7.96k | { |
352 | 7.96k | std::lock_guard<simple_spinlock> l(lock_); |
353 | 7.96k | for (TraceEntry* cur = entries_head_; |
354 | 34.6k | cur != nullptr; |
355 | 26.6k | cur = cur->next) { |
356 | 26.6k | entries.push_back(cur); |
357 | 26.6k | } |
358 | | |
359 | 7.96k | child_traces = child_traces_; |
360 | 7.96k | trace_start_time_usec = trace_start_time_usec_; |
361 | 7.96k | } |
362 | | |
363 | 7.96k | DoDump( |
364 | 7.96k | out, tracing_depth, include_time_deltas, trace_start_time_usec, |
365 | 7.96k | entries | boost::adaptors::indirected, &child_traces); |
366 | 7.96k | } |
367 | | |
368 | 7.84k | string Trace::DumpToString(int32_t tracing_depth, bool include_time_deltas) const { |
369 | 7.84k | std::stringstream s; |
370 | 7.84k | Dump(&s, tracing_depth, include_time_deltas); |
371 | 7.84k | return s.str(); |
372 | 7.84k | } |
373 | | |
374 | 0 | void Trace::DumpCurrentTrace() { |
375 | 0 | Trace* t = CurrentTrace(); |
376 | 0 | if (t == nullptr) { |
377 | 0 | LOG(INFO) << "No trace is currently active."; |
378 | 0 | return; |
379 | 0 | } |
380 | 0 | t->Dump(&std::cerr, true); |
381 | 0 | } |
382 | | |
383 | 2.78M | void Trace::AddChildTrace(Trace* child_trace) { |
384 | 2.78M | CHECK_NOTNULL(child_trace); |
385 | 2.78M | { |
386 | 2.78M | std::lock_guard<simple_spinlock> l(lock_); |
387 | 2.78M | scoped_refptr<Trace> ptr(child_trace); |
388 | 2.78M | child_traces_.push_back(ptr); |
389 | 2.78M | } |
390 | 2.78M | CHECK(!child_trace->HasOneRef()); |
391 | 2.78M | } |
392 | | |
393 | 39.2M | size_t Trace::DynamicMemoryUsage() const { |
394 | 39.2M | auto arena = arena_.load(); |
395 | 33.1M | return arena ? arena->memory_footprint() : 0; |
396 | 39.2M | } |
397 | | |
398 | 0 | PlainTrace::PlainTrace() { |
399 | 0 | } |
400 | | |
401 | 0 | void PlainTrace::Trace(const char *file_path, int line_number, const char *message) { |
402 | 0 | auto timestamp = CoarseMonoClock::Now(); |
403 | 0 | { |
404 | 0 | std::lock_guard<decltype(mutex_)> lock(mutex_); |
405 | 0 | if (size_ < kMaxEntries) { |
406 | 0 | if (size_ == 0) { |
407 | 0 | trace_start_time_usec_ = GetCurrentMicrosFast(timestamp); |
408 | 0 | } |
409 | 0 | entries_[size_] = {file_path, line_number, message, timestamp}; |
410 | 0 | ++size_; |
411 | 0 | } |
412 | 0 | } |
413 | 0 | } |
414 | | |
415 | 0 | void PlainTrace::Dump(std::ostream *out, bool include_time_deltas) const { |
416 | 0 | Dump(out, 0, include_time_deltas); |
417 | 0 | } |
418 | | |
419 | 0 | void PlainTrace::Dump(std::ostream* out, int32_t tracing_depth, bool include_time_deltas) const { |
420 | 0 | size_t size; |
421 | 0 | decltype(trace_start_time_usec_) trace_start_time_usec; |
422 | 0 | { |
423 | 0 | std::lock_guard<decltype(mutex_)> lock(mutex_); |
424 | 0 | size = size_; |
425 | 0 | trace_start_time_usec = trace_start_time_usec_; |
426 | 0 | } |
427 | 0 | auto entries = boost::make_iterator_range(entries_, entries_ + size); |
428 | 0 | DoDump( |
429 | 0 | out, tracing_depth, include_time_deltas, trace_start_time_usec, entries, |
430 | 0 | /* children */ nullptr); |
431 | 0 | } |
432 | | |
433 | 0 | std::string PlainTrace::DumpToString(int32_t tracing_depth, bool include_time_deltas) const { |
434 | 0 | std::stringstream s; |
435 | 0 | Dump(&s, tracing_depth, include_time_deltas); |
436 | 0 | return s.str(); |
437 | 0 | } |
438 | | |
439 | 0 | void PlainTrace::Entry::Dump(std::ostream *out) const { |
440 | 0 | *out << const_basename(file_path) << ':' << line_number << "] " << message; |
441 | 0 | } |
442 | | |
443 | | } // namespace yb |