/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 | 49.0k | const char* const_basename(const char* filepath) { |
83 | 49.0k | const char* base = strrchr(filepath, '/'); |
84 | 49.0k | return base ? (base + 1)49.0k : filepath1 ; |
85 | 49.0k | } |
86 | | |
87 | | template <class Children> |
88 | | void DumpChildren( |
89 | 12.6k | std::ostream* out, int32_t tracing_depth, bool include_time_deltas, const Children* children) { |
90 | 12.6k | if (tracing_depth > GetAtomicFlag(&FLAGS_print_nesting_levels)) { |
91 | 0 | return; |
92 | 0 | } |
93 | 12.6k | for (auto &child_trace : *children) { |
94 | 13.3k | for (int i = 0; i < tracing_depth; i++8.42k ) { |
95 | 8.42k | *out << kNestedChildPrefix; |
96 | 8.42k | } |
97 | 4.92k | *out << "Related trace:" << std::endl; |
98 | 4.92k | *out << child_trace->DumpToString(tracing_depth, include_time_deltas); |
99 | 4.92k | } |
100 | 12.6k | } |
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 | 12.6k | const Entries& entries) { |
112 | 12.6k | if (entries.empty()) { |
113 | 1.77k | return; |
114 | 1.77k | } |
115 | | |
116 | 10.8k | auto time_usec = MonoDelta(entries.begin()->timestamp.time_since_epoch()).ToMicroseconds(); |
117 | 10.8k | const int64_t time_correction_usec = start - time_usec; |
118 | 10.8k | int64_t prev_usecs = time_usec; |
119 | 49.0k | for (const auto& e : entries) { |
120 | 49.0k | time_usec = MonoDelta(e.timestamp.time_since_epoch()).ToMicroseconds(); |
121 | 49.0k | const int64_t usecs_since_prev = time_usec - prev_usecs; |
122 | 49.0k | prev_usecs = time_usec; |
123 | | |
124 | 49.0k | const auto absolute_time_usec = time_usec + time_correction_usec; |
125 | 49.0k | const time_t secs_since_epoch = absolute_time_usec / 1000000; |
126 | 49.0k | const int usecs = absolute_time_usec % 1000000; |
127 | 49.0k | struct tm tm_time; |
128 | 49.0k | localtime_r(&secs_since_epoch, &tm_time); |
129 | | |
130 | 103k | for (int i = 0; i < tracing_depth; i++54.2k ) { |
131 | 54.2k | *out << kNestedChildPrefix; |
132 | 54.2k | } |
133 | | // Log format borrowed from glog/logging.cc |
134 | 49.0k | using std::setw; |
135 | 49.0k | out->fill('0'); |
136 | | |
137 | 49.0k | *out << setw(2) << (1 + tm_time.tm_mon) |
138 | 49.0k | << setw(2) << tm_time.tm_mday |
139 | 49.0k | << ' ' |
140 | 49.0k | << setw(2) << tm_time.tm_hour << ':' |
141 | 49.0k | << setw(2) << tm_time.tm_min << ':' |
142 | 49.0k | << setw(2) << tm_time.tm_sec << '.' |
143 | 49.0k | << setw(6) << usecs << ' '; |
144 | 49.0k | if (include_time_deltas) { |
145 | 49.0k | out->fill(' '); |
146 | 49.0k | *out << "(+" << setw(6) << usecs_since_prev << "us) "; |
147 | 49.0k | } |
148 | 49.0k | e.Dump(out); |
149 | 49.0k | *out << std::endl; |
150 | 49.0k | } |
151 | 10.8k | } trace.cc:void yb::(anonymous namespace)::DumpEntries<boost::range_detail::indirected_range<std::__1::vector<yb::TraceEntry*, std::__1::allocator<yb::TraceEntry*> > > >(std::__1::basic_ostream<char, std::__1::char_traits<char> >*, int, bool, long long, boost::range_detail::indirected_range<std::__1::vector<yb::TraceEntry*, std::__1::allocator<yb::TraceEntry*> > > const&) Line | Count | Source | 111 | 12.6k | const Entries& entries) { | 112 | 12.6k | if (entries.empty()) { | 113 | 1.77k | return; | 114 | 1.77k | } | 115 | | | 116 | 10.8k | auto time_usec = MonoDelta(entries.begin()->timestamp.time_since_epoch()).ToMicroseconds(); | 117 | 10.8k | const int64_t time_correction_usec = start - time_usec; | 118 | 10.8k | int64_t prev_usecs = time_usec; | 119 | 49.0k | for (const auto& e : entries) { | 120 | 49.0k | time_usec = MonoDelta(e.timestamp.time_since_epoch()).ToMicroseconds(); | 121 | 49.0k | const int64_t usecs_since_prev = time_usec - prev_usecs; | 122 | 49.0k | prev_usecs = time_usec; | 123 | | | 124 | 49.0k | const auto absolute_time_usec = time_usec + time_correction_usec; | 125 | 49.0k | const time_t secs_since_epoch = absolute_time_usec / 1000000; | 126 | 49.0k | const int usecs = absolute_time_usec % 1000000; | 127 | 49.0k | struct tm tm_time; | 128 | 49.0k | localtime_r(&secs_since_epoch, &tm_time); | 129 | | | 130 | 103k | for (int i = 0; i < tracing_depth; i++54.2k ) { | 131 | 54.2k | *out << kNestedChildPrefix; | 132 | 54.2k | } | 133 | | // Log format borrowed from glog/logging.cc | 134 | 49.0k | using std::setw; | 135 | 49.0k | out->fill('0'); | 136 | | | 137 | 49.0k | *out << setw(2) << (1 + tm_time.tm_mon) | 138 | 49.0k | << setw(2) << tm_time.tm_mday | 139 | 49.0k | << ' ' | 140 | 49.0k | << setw(2) << tm_time.tm_hour << ':' | 141 | 49.0k | << setw(2) << tm_time.tm_min << ':' | 142 | 49.0k | << setw(2) << tm_time.tm_sec << '.' | 143 | 49.0k | << setw(6) << usecs << ' '; | 144 | 49.0k | if (include_time_deltas) { | 145 | 49.0k | out->fill(' '); | 146 | 49.0k | *out << "(+" << setw(6) << usecs_since_prev << "us) "; | 147 | 49.0k | } | 148 | 49.0k | e.Dump(out); | 149 | 49.0k | *out << std::endl; | 150 | 49.0k | } | 151 | 10.8k | } |
Unexecuted instantiation: trace.cc:void yb::(anonymous namespace)::DumpEntries<boost::iterator_range<yb::PlainTrace::Entry const*> >(std::__1::basic_ostream<char, std::__1::char_traits<char> >*, int, bool, long long, boost::iterator_range<yb::PlainTrace::Entry const*> const&) |
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 | 12.6k | Children children) { |
161 | | // Save original flags. |
162 | 12.6k | std::ios::fmtflags save_flags(out->flags()); |
163 | | |
164 | 12.6k | DumpEntries(out, tracing_depth, include_time_deltas, start, entries); |
165 | 12.6k | DumpChildren(out, tracing_depth + 1, include_time_deltas, children); |
166 | | |
167 | | // Restore stream flags. |
168 | 12.6k | out->flags(save_flags); |
169 | 12.6k | } trace.cc:void yb::(anonymous namespace)::DoDump<boost::range_detail::indirected_range<std::__1::vector<yb::TraceEntry*, std::__1::allocator<yb::TraceEntry*> > >, std::__1::vector<scoped_refptr<yb::Trace>, std::__1::allocator<scoped_refptr<yb::Trace> > >*>(std::__1::basic_ostream<char, std::__1::char_traits<char> >*, int, bool, long long, boost::range_detail::indirected_range<std::__1::vector<yb::TraceEntry*, std::__1::allocator<yb::TraceEntry*> > > const&, std::__1::vector<scoped_refptr<yb::Trace>, std::__1::allocator<scoped_refptr<yb::Trace> > >*) Line | Count | Source | 160 | 12.6k | Children children) { | 161 | | // Save original flags. | 162 | 12.6k | std::ios::fmtflags save_flags(out->flags()); | 163 | | | 164 | 12.6k | DumpEntries(out, tracing_depth, include_time_deltas, start, entries); | 165 | 12.6k | DumpChildren(out, tracing_depth + 1, include_time_deltas, children); | 166 | | | 167 | | // Restore stream flags. | 168 | 12.6k | out->flags(save_flags); | 169 | 12.6k | } |
Unexecuted instantiation: trace.cc:void yb::(anonymous namespace)::DoDump<boost::iterator_range<yb::PlainTrace::Entry const*>, std::nullptr_t>(std::__1::basic_ostream<char, std::__1::char_traits<char> >*, int, bool, long long, boost::iterator_range<yb::PlainTrace::Entry const*> const&, std::nullptr_t) |
170 | | |
171 | | std::once_flag init_get_current_micros_fast_flag; |
172 | | int64_t initial_micros_offset; |
173 | | |
174 | 4.81k | void InitGetCurrentMicrosFast() { |
175 | 4.81k | auto before = CoarseMonoClock::Now(); |
176 | 4.81k | initial_micros_offset = GetCurrentTimeMicros(); |
177 | 4.81k | auto after = CoarseMonoClock::Now(); |
178 | 4.81k | auto mid = MonoDelta(after.time_since_epoch() + before.time_since_epoch()).ToMicroseconds() / 2; |
179 | 4.81k | initial_micros_offset -= mid; |
180 | 4.81k | } |
181 | | |
182 | 89.8M | int64_t GetCurrentMicrosFast(CoarseTimePoint now) { |
183 | 89.8M | std::call_once(init_get_current_micros_fast_flag, InitGetCurrentMicrosFast); |
184 | 89.8M | return initial_micros_offset + MonoDelta(now.time_since_epoch()).ToMicroseconds(); |
185 | 89.8M | } |
186 | | |
187 | | } // namespace |
188 | | |
189 | | ScopedAdoptTrace::ScopedAdoptTrace(Trace* t) |
190 | 293M | : old_trace_(Trace::threadlocal_trace_), is_enabled_(GetAtomicFlag(&FLAGS_enable_tracing)) { |
191 | 293M | if (is_enabled_) { |
192 | 78.4M | trace_ = t; |
193 | 78.4M | Trace::threadlocal_trace_ = t; |
194 | 78.4M | DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); |
195 | 78.4M | } |
196 | 293M | } |
197 | | |
198 | 293M | ScopedAdoptTrace::~ScopedAdoptTrace() { |
199 | 293M | if (is_enabled_) { |
200 | 78.4M | 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 | 78.4M | trace_.reset(); |
220 | 78.4M | DFAKE_SCOPED_LOCK_THREAD_LOCKED(ctor_dtor_); |
221 | 78.4M | } |
222 | 293M | } |
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 | 49.0k | void Dump(std::ostream* out) const { |
237 | 49.0k | *out << const_basename(file_path) << ':' << line_number |
238 | 49.0k | << "] "; |
239 | 49.0k | out->write(message, message_len); |
240 | 49.0k | } |
241 | | }; |
242 | | |
243 | 258M | Trace::Trace() { |
244 | 258M | } |
245 | | |
246 | 179M | ThreadSafeObjectPool<ThreadSafeArena>& ArenaPool() { |
247 | 179M | static ThreadSafeObjectPool<ThreadSafeArena> result([] { |
248 | 26.7M | return new ThreadSafeArena(8_KB, 128_KB); |
249 | 26.7M | }); |
250 | 179M | return result; |
251 | 179M | } |
252 | | |
253 | 258M | Trace::~Trace() { |
254 | 258M | auto* arena = arena_.load(std::memory_order_acquire); |
255 | 258M | if (arena) { |
256 | 89.7M | arena->Reset(); |
257 | 89.7M | ArenaPool().Release(arena); |
258 | 89.7M | } |
259 | 258M | } |
260 | | |
261 | 544M | ThreadSafeArena* Trace::GetAndInitArena() { |
262 | 544M | auto* arena = arena_.load(std::memory_order_acquire); |
263 | 544M | if (arena == nullptr) { |
264 | 89.7M | arena = ArenaPool().Take(); |
265 | 89.7M | ThreadSafeArena* existing_arena = nullptr; |
266 | 89.7M | if (arena_.compare_exchange_strong(existing_arena, arena, std::memory_order_release)89.7M ) { |
267 | 89.7M | return arena; |
268 | 18.4E | } else { |
269 | 18.4E | ArenaPool().Release(arena); |
270 | 18.4E | return existing_arena; |
271 | 18.4E | } |
272 | 89.7M | } |
273 | 454M | return arena; |
274 | 544M | } |
275 | | |
276 | | void Trace::SubstituteAndTrace( |
277 | 442M | const char* file_path, int line_number, CoarseTimePoint now, GStringPiece format) { |
278 | 442M | auto msg_len = format.size(); |
279 | 442M | DCHECK_NE(msg_len, 0) << "Bad format specification"0 ; |
280 | 442M | TraceEntry* entry = NewEntry(msg_len, file_path, line_number, now); |
281 | 442M | if (entry == nullptr) return0 ; |
282 | 442M | memcpy(entry->message, format.data(), msg_len); |
283 | 442M | AddEntry(entry); |
284 | 442M | } |
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 | 102M | const SubstituteArg& arg8, const SubstituteArg& arg9) { |
295 | 102M | const SubstituteArg* const args_array[] = { |
296 | 102M | &arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &arg6, &arg7, &arg8, &arg9, nullptr |
297 | 102M | }; |
298 | | |
299 | 102M | int msg_len = strings::internal::SubstitutedSize(format, args_array); |
300 | 102M | DCHECK_NE(msg_len, 0) << "Bad format specification"0 ; |
301 | 102M | TraceEntry* entry = NewEntry(msg_len, file_path, line_number, now); |
302 | 102M | if (entry == nullptr) return0 ; |
303 | 102M | SubstituteToBuffer(format, args_array, entry->message); |
304 | 102M | AddEntry(entry); |
305 | 102M | } |
306 | | |
307 | | TraceEntry* Trace::NewEntry( |
308 | 543M | size_t msg_len, const char* file_path, int line_number, CoarseTimePoint now) { |
309 | 543M | auto* arena = GetAndInitArena(); |
310 | 543M | size_t size = offsetof(TraceEntry, message) + msg_len; |
311 | 543M | void* dst = arena->AllocateBytesAligned(size, alignof(TraceEntry)); |
312 | 543M | 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 | 543M | TraceEntry* entry = new (dst) TraceEntry; |
318 | 543M | entry->timestamp = now; |
319 | 543M | entry->message_len = msg_len; |
320 | 543M | entry->file_path = file_path; |
321 | 543M | entry->line_number = line_number; |
322 | 543M | return entry; |
323 | 543M | } |
324 | | |
325 | 544M | void Trace::AddEntry(TraceEntry* entry) { |
326 | 544M | std::lock_guard<simple_spinlock> l(lock_); |
327 | 544M | entry->next = nullptr; |
328 | | |
329 | 544M | if (entries_tail_ != nullptr) { |
330 | 455M | entries_tail_->next = entry; |
331 | 455M | } else { |
332 | 89.1M | DCHECK(entries_head_ == nullptr); |
333 | 89.1M | entries_head_ = entry; |
334 | 89.1M | trace_start_time_usec_ = GetCurrentMicrosFast(entry->timestamp); |
335 | 89.1M | } |
336 | 544M | entries_tail_ = entry; |
337 | 544M | } |
338 | | |
339 | 275 | void Trace::Dump(std::ostream *out, bool include_time_deltas) const { |
340 | 275 | Dump(out, 0, include_time_deltas); |
341 | 275 | } |
342 | | |
343 | 12.6k | 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 | 12.6k | vector<TraceEntry*> entries; |
349 | 12.6k | vector<scoped_refptr<Trace> > child_traces; |
350 | 12.6k | decltype(trace_start_time_usec_) trace_start_time_usec; |
351 | 12.6k | { |
352 | 12.6k | std::lock_guard<simple_spinlock> l(lock_); |
353 | 12.6k | for (TraceEntry* cur = entries_head_; |
354 | 61.6k | cur != nullptr; |
355 | 49.0k | cur = cur->next) { |
356 | 49.0k | entries.push_back(cur); |
357 | 49.0k | } |
358 | | |
359 | 12.6k | child_traces = child_traces_; |
360 | 12.6k | trace_start_time_usec = trace_start_time_usec_; |
361 | 12.6k | } |
362 | | |
363 | 12.6k | DoDump( |
364 | 12.6k | out, tracing_depth, include_time_deltas, trace_start_time_usec, |
365 | 12.6k | entries | boost::adaptors::indirected, &child_traces); |
366 | 12.6k | } |
367 | | |
368 | 12.3k | string Trace::DumpToString(int32_t tracing_depth, bool include_time_deltas) const { |
369 | 12.3k | std::stringstream s; |
370 | 12.3k | Dump(&s, tracing_depth, include_time_deltas); |
371 | 12.3k | return s.str(); |
372 | 12.3k | } |
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 | 9.61M | void Trace::AddChildTrace(Trace* child_trace) { |
384 | 9.61M | CHECK_NOTNULL(child_trace); |
385 | 9.61M | { |
386 | 9.61M | std::lock_guard<simple_spinlock> l(lock_); |
387 | 9.61M | scoped_refptr<Trace> ptr(child_trace); |
388 | 9.61M | child_traces_.push_back(ptr); |
389 | 9.61M | } |
390 | 9.61M | CHECK(!child_trace->HasOneRef()); |
391 | 9.61M | } |
392 | | |
393 | 144M | size_t Trace::DynamicMemoryUsage() const { |
394 | 144M | auto arena = arena_.load(); |
395 | 144M | return arena ? arena->memory_footprint()79.2M : 064.8M ; |
396 | 144M | } |
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 |