/Users/deen/code/yugabyte-db/src/yb/util/trace-test.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 <string> |
34 | | |
35 | | #include <gtest/gtest.h> |
36 | | #include <rapidjson/document.h> |
37 | | #include <rapidjson/rapidjson.h> // NOLINT |
38 | | |
39 | | #include "yb/gutil/casts.h" |
40 | | |
41 | | #include "yb/util/debug/trace_event.h" |
42 | | #include "yb/util/debug/trace_event_synthetic_delay.h" |
43 | | #include "yb/util/debug/trace_logging.h" |
44 | | #include "yb/util/status_log.h" |
45 | | #include "yb/util/stopwatch.h" |
46 | | #include "yb/util/test_macros.h" |
47 | | #include "yb/util/test_util.h" |
48 | | #include "yb/util/thread.h" |
49 | | #include "yb/util/trace.h" |
50 | | |
51 | | // Need to add rapidjson.h to the list of recognized third-party libraries in our linter. |
52 | | |
53 | | |
54 | | using yb::debug::TraceLog; |
55 | | using yb::debug::TraceResultBuffer; |
56 | | using yb::debug::CategoryFilter; |
57 | | using rapidjson::Document; |
58 | | using rapidjson::Value; |
59 | | using std::string; |
60 | | using std::vector; |
61 | | |
62 | | namespace yb { |
63 | | |
64 | | class TraceTest : public YBTest { |
65 | | }; |
66 | | |
67 | | // Replace all digits in 's' with the character 'X'. |
68 | 4 | static string XOutDigits(const string& s) { |
69 | 4 | string ret; |
70 | 4 | ret.reserve(s.size()); |
71 | 379 | for (char c : s) { |
72 | 379 | if (isdigit(c)) { |
73 | 122 | ret.push_back('X'); |
74 | 257 | } else { |
75 | 257 | ret.push_back(c); |
76 | 257 | } |
77 | 379 | } |
78 | 4 | return ret; |
79 | 4 | } |
80 | | |
81 | 1 | TEST_F(TraceTest, TestBasic) { |
82 | 1 | scoped_refptr<Trace> t(new Trace); |
83 | 1 | TRACE_TO(t, "hello $0, $1", "world", 12345); |
84 | 1 | TRACE_TO(t, "goodbye $0, $1", "cruel world", 54321); |
85 | | |
86 | 1 | string result = XOutDigits(t->DumpToString(false)); |
87 | 1 | ASSERT_EQ("XXXX XX:XX:XX.XXXXXX trace-test.cc:XX] hello world, XXXXX\n" |
88 | 1 | "XXXX XX:XX:XX.XXXXXX trace-test.cc:XX] goodbye cruel world, XXXXX\n", |
89 | 1 | result); |
90 | 1 | } |
91 | | |
92 | 1 | TEST_F(TraceTest, TestAttach) { |
93 | 1 | scoped_refptr<Trace> traceA(new Trace); |
94 | 1 | scoped_refptr<Trace> traceB(new Trace); |
95 | 1 | { |
96 | 1 | ADOPT_TRACE(traceA.get()); |
97 | 1 | EXPECT_EQ(traceA.get(), Trace::CurrentTrace()); |
98 | 1 | { |
99 | 1 | ADOPT_TRACE(traceB.get()); |
100 | 1 | EXPECT_EQ(traceB.get(), Trace::CurrentTrace()); |
101 | 1 | TRACE("hello from traceB"); |
102 | 1 | } |
103 | 1 | EXPECT_EQ(traceA.get(), Trace::CurrentTrace()); |
104 | 1 | TRACE("hello from traceA"); |
105 | 1 | } |
106 | 1 | EXPECT_TRUE(Trace::CurrentTrace() == nullptr); |
107 | 1 | TRACE("this goes nowhere"); |
108 | | |
109 | 1 | EXPECT_EQ(XOutDigits(traceA->DumpToString(false)), |
110 | 1 | "XXXX XX:XX:XX.XXXXXX trace-test.cc:XXX] hello from traceA\n"); |
111 | 1 | EXPECT_EQ(XOutDigits(traceB->DumpToString(false)), |
112 | 1 | "XXXX XX:XX:XX.XXXXXX trace-test.cc:XXX] hello from traceB\n"); |
113 | 1 | } |
114 | | |
115 | 1 | TEST_F(TraceTest, TestChildTrace) { |
116 | 1 | scoped_refptr<Trace> traceA(new Trace); |
117 | 1 | scoped_refptr<Trace> traceB(new Trace); |
118 | 1 | ADOPT_TRACE(traceA.get()); |
119 | 1 | traceA->AddChildTrace(traceB.get()); |
120 | 1 | TRACE("hello from traceA"); |
121 | 1 | { |
122 | 1 | ADOPT_TRACE(traceB.get()); |
123 | 1 | TRACE("hello from traceB"); |
124 | 1 | } |
125 | 1 | EXPECT_EQ("XXXX XX:XX:XX.XXXXXX trace-test.cc:XXX] hello from traceA\n" |
126 | 1 | ".. Related trace:\n" |
127 | 1 | ".. XXXX XX:XX:XX.XXXXXX trace-test.cc:XXX] hello from traceB\n", |
128 | 1 | XOutDigits(traceA->DumpToString(false))); |
129 | 1 | } |
130 | | |
131 | | static void GenerateTraceEvents(int thread_id, |
132 | 5 | int num_events) { |
133 | 33.8k | for (int i = 0; i < num_events; i++) { |
134 | 33.8k | TRACE_EVENT1("test", "foo", "thread_id", thread_id); |
135 | 33.8k | } |
136 | 5 | } |
137 | | |
138 | | // Parse the dumped trace data and return the number of events |
139 | | // found within, including only those with the "test" category. |
140 | 8 | int ParseAndReturnEventCount(const string& trace_json) { |
141 | 8 | Document d; |
142 | 8 | d.Parse<0>(trace_json.c_str()); |
143 | 0 | CHECK(d.IsObject()) << "bad json: " << trace_json; |
144 | 8 | const Value& events_json = d["traceEvents"]; |
145 | 0 | CHECK(events_json.IsArray()) << "bad json: " << trace_json; |
146 | | |
147 | | // Count how many of our events were seen. We have to filter out |
148 | | // the metadata events. |
149 | 8 | int seen_real_events = 0; |
150 | 58.0k | for (rapidjson::SizeType i = 0; i < events_json.Size(); i++) { |
151 | 58.0k | if (events_json[i]["cat"].GetString() == string("test")) { |
152 | 58.0k | seen_real_events++; |
153 | 58.0k | } |
154 | 58.0k | } |
155 | | |
156 | 8 | return seen_real_events; |
157 | 8 | } |
158 | | |
159 | 1 | TEST_F(TraceTest, TestChromeTracing) { |
160 | 1 | const int kNumThreads = 4; |
161 | 1 | const int kEventsPerThread = AllowSlowTests() ? 1000000 : 10000; |
162 | | |
163 | 1 | TraceLog* tl = TraceLog::GetInstance(); |
164 | 1 | tl->SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString), |
165 | 1 | TraceLog::RECORDING_MODE, |
166 | 1 | TraceLog::RECORD_CONTINUOUSLY); |
167 | | |
168 | 1 | vector<scoped_refptr<Thread> > threads(kNumThreads); |
169 | | |
170 | 1 | Stopwatch s; |
171 | 1 | s.start(); |
172 | 5 | for (int i = 0; i < kNumThreads; i++) { |
173 | 4 | CHECK_OK(Thread::Create("test", "gen-traces", &GenerateTraceEvents, i, kEventsPerThread, |
174 | 4 | &threads[i])); |
175 | 4 | } |
176 | | |
177 | 5 | for (int i = 0; i < kNumThreads; i++) { |
178 | 4 | threads[i]->Join(); |
179 | 4 | } |
180 | 1 | tl->SetDisabled(); |
181 | | |
182 | 1 | int total_events = kNumThreads * kEventsPerThread; |
183 | 1 | double elapsed = s.elapsed().wall_seconds(); |
184 | | |
185 | 1 | LOG(INFO) << "Trace performance: " << static_cast<int>(total_events / elapsed) << " traces/sec"; |
186 | | |
187 | 1 | string trace_json = TraceResultBuffer::FlushTraceLogToString(); |
188 | | |
189 | | // Verify that the JSON contains events. It won't have exactly |
190 | | // kEventsPerThread * kNumThreads because the trace buffer isn't large enough |
191 | | // for that. |
192 | 1 | ASSERT_GE(ParseAndReturnEventCount(trace_json), 100); |
193 | 1 | } |
194 | | |
195 | | // Test that, if a thread exits before filling a full trace buffer, we still |
196 | | // see its results. This is a regression test for a bug in the earlier integration |
197 | | // of Chromium tracing into YB. |
198 | 1 | TEST_F(TraceTest, TestTraceFromExitedThread) { |
199 | 1 | TraceLog* tl = TraceLog::GetInstance(); |
200 | 1 | tl->SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString), |
201 | 1 | TraceLog::RECORDING_MODE, |
202 | 1 | TraceLog::RECORD_CONTINUOUSLY); |
203 | | |
204 | | // Generate 10 trace events in a separate thread. |
205 | 1 | int kNumEvents = 10; |
206 | 1 | scoped_refptr<Thread> t; |
207 | 1 | CHECK_OK(Thread::Create("test", "gen-traces", &GenerateTraceEvents, 1, kNumEvents, |
208 | 1 | &t)); |
209 | 1 | t->Join(); |
210 | 1 | tl->SetDisabled(); |
211 | 1 | string trace_json = TraceResultBuffer::FlushTraceLogToString(); |
212 | 1 | LOG(INFO) << trace_json; |
213 | | |
214 | | // Verify that the buffer contains 10 trace events |
215 | 1 | ASSERT_EQ(10, ParseAndReturnEventCount(trace_json)); |
216 | 1 | } |
217 | | |
218 | 1 | static void GenerateWideSpan() { |
219 | 1 | TRACE_EVENT0("test", "GenerateWideSpan"); |
220 | 1.00k | for (int i = 0; i < 1000; i++) { |
221 | 1.00k | TRACE_EVENT0("test", "InnerLoop"); |
222 | 1.00k | } |
223 | 1 | } |
224 | | |
225 | | // Test creating a trace event which contains many other trace events. |
226 | | // This ensures that we can go back and update a TraceEvent which fell in |
227 | | // a different trace chunk. |
228 | 1 | TEST_F(TraceTest, TestWideSpan) { |
229 | 1 | TraceLog* tl = TraceLog::GetInstance(); |
230 | 1 | tl->SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString), |
231 | 1 | TraceLog::RECORDING_MODE, |
232 | 1 | TraceLog::RECORD_CONTINUOUSLY); |
233 | | |
234 | 1 | scoped_refptr<Thread> t; |
235 | 1 | CHECK_OK(Thread::Create("test", "gen-traces", &GenerateWideSpan, &t)); |
236 | 1 | t->Join(); |
237 | 1 | tl->SetDisabled(); |
238 | | |
239 | 1 | string trace_json = TraceResultBuffer::FlushTraceLogToString(); |
240 | 1 | ASSERT_EQ(1001, ParseAndReturnEventCount(trace_json)); |
241 | 1 | } |
242 | | |
243 | | // Regression test for KUDU-753: faulty JSON escaping when dealing with |
244 | | // single quote characters. |
245 | 1 | TEST_F(TraceTest, TestJsonEncodingString) { |
246 | 1 | TraceLog* tl = TraceLog::GetInstance(); |
247 | 1 | tl->SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString), |
248 | 1 | TraceLog::RECORDING_MODE, |
249 | 1 | TraceLog::RECORD_CONTINUOUSLY); |
250 | 1 | { |
251 | 1 | TRACE_EVENT1("test", "test", "arg", "this is a test with \"'\"' and characters\nand new lines"); |
252 | 1 | } |
253 | 1 | tl->SetDisabled(); |
254 | 1 | string trace_json = TraceResultBuffer::FlushTraceLogToString(); |
255 | 1 | ASSERT_EQ(1, ParseAndReturnEventCount(trace_json)); |
256 | 1 | } |
257 | | |
258 | | // Generate trace events continuously until 'latch' fires. |
259 | | // Increment *num_events_generated for each event generated. |
260 | | void GenerateTracesUntilLatch(AtomicInt<int64_t>* num_events_generated, |
261 | 1 | CountDownLatch* latch) { |
262 | 4.16M | while (latch->count()) { |
263 | 4.16M | { |
264 | | // This goes in its own scope so that the event is fully generated (with |
265 | | // both its START and END times) before we do the counter increment below. |
266 | 4.16M | TRACE_EVENT0("test", "GenerateTracesUntilLatch"); |
267 | 4.16M | } |
268 | 4.16M | num_events_generated->Increment(); |
269 | 4.16M | } |
270 | 1 | } |
271 | | |
272 | | // Test starting and stopping tracing while a thread is running. |
273 | | // This is a regression test for bugs in earlier versions of the imported |
274 | | // trace code. |
275 | 1 | TEST_F(TraceTest, TestStartAndStopCollection) { |
276 | 1 | TraceLog* tl = TraceLog::GetInstance(); |
277 | | |
278 | 1 | CountDownLatch latch(1); |
279 | 1 | AtomicInt<int64_t> num_events_generated(0); |
280 | 1 | scoped_refptr<Thread> t; |
281 | 1 | CHECK_OK(Thread::Create("test", "gen-traces", &GenerateTracesUntilLatch, |
282 | 1 | &num_events_generated, &latch, &t)); |
283 | | |
284 | 1 | const int num_flushes = AllowSlowTests() ? 50 : 3; |
285 | 4 | for (int i = 0; i < num_flushes; i++) { |
286 | 3 | tl->SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString), |
287 | 3 | TraceLog::RECORDING_MODE, |
288 | 3 | TraceLog::RECORD_CONTINUOUSLY); |
289 | | |
290 | 3 | const int64_t num_events_before = num_events_generated.Load(); |
291 | 3 | SleepFor(MonoDelta::FromMilliseconds(10)); |
292 | 3 | const int64_t num_events_after = num_events_generated.Load(); |
293 | 3 | tl->SetDisabled(); |
294 | | |
295 | 3 | string trace_json = TraceResultBuffer::FlushTraceLogToString(); |
296 | | // We might under-count the number of events, since we only measure the sleep, |
297 | | // and tracing is enabled before and disabled after we start counting. |
298 | | // We might also over-count by at most 1, because we could enable tracing |
299 | | // right in between creating a trace event and incrementing the counter. |
300 | | // But, we should never over-count by more than 1. |
301 | 3 | auto expected_events_lowerbound = num_events_after - num_events_before - 1; |
302 | 3 | int captured_events = ParseAndReturnEventCount(trace_json); |
303 | 3 | ASSERT_GE(captured_events, expected_events_lowerbound); |
304 | 3 | } |
305 | | |
306 | 1 | latch.CountDown(); |
307 | 1 | t->Join(); |
308 | 1 | } |
309 | | |
310 | 1 | TEST_F(TraceTest, TestChromeSampling) { |
311 | 1 | TraceLog* tl = TraceLog::GetInstance(); |
312 | 1 | tl->SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString), |
313 | 1 | TraceLog::RECORDING_MODE, |
314 | 1 | static_cast<TraceLog::Options>(TraceLog::RECORD_CONTINUOUSLY | |
315 | 1 | TraceLog::ENABLE_SAMPLING)); |
316 | | |
317 | 101 | for (int i = 0; i < 100; i++) { |
318 | 100 | switch (i % 3) { |
319 | 34 | case 0: |
320 | 34 | TRACE_EVENT_SET_SAMPLING_STATE("test", "state-0"); |
321 | 34 | break; |
322 | 33 | case 1: |
323 | 33 | TRACE_EVENT_SET_SAMPLING_STATE("test", "state-1"); |
324 | 33 | break; |
325 | 33 | case 2: |
326 | 33 | TRACE_EVENT_SET_SAMPLING_STATE("test", "state-2"); |
327 | 33 | break; |
328 | 100 | } |
329 | 100 | SleepFor(MonoDelta::FromMilliseconds(1)); |
330 | 100 | } |
331 | 1 | tl->SetDisabled(); |
332 | 1 | string trace_json = TraceResultBuffer::FlushTraceLogToString(); |
333 | 1 | ASSERT_GT(ParseAndReturnEventCount(trace_json), 0); |
334 | 1 | } |
335 | | |
336 | | class TraceEventCallbackTest : public YBTest { |
337 | | public: |
338 | 7 | void SetUp() override { |
339 | 7 | YBTest::SetUp(); |
340 | 7 | ASSERT_EQ(nullptr, s_instance); |
341 | 7 | s_instance = this; |
342 | 7 | } |
343 | 7 | void TearDown() override { |
344 | 7 | TraceLog::GetInstance()->SetDisabled(); |
345 | | |
346 | | // Flush the buffer so that one test doesn't end up leaving any |
347 | | // extra results for the next test. |
348 | 7 | TraceResultBuffer::FlushTraceLogToString(); |
349 | | |
350 | 7 | ASSERT_TRUE(!!s_instance); |
351 | 7 | s_instance = nullptr; |
352 | 7 | YBTest::TearDown(); |
353 | | |
354 | 7 | } |
355 | | |
356 | | protected: |
357 | 5 | void EndTraceAndFlush() { |
358 | 5 | TraceLog::GetInstance()->SetDisabled(); |
359 | 5 | string trace_json = TraceResultBuffer::FlushTraceLogToString(); |
360 | 5 | trace_doc_.Parse<0>(trace_json.c_str()); |
361 | 5 | LOG(INFO) << trace_json; |
362 | 5 | ASSERT_TRUE(trace_doc_.IsObject()); |
363 | 5 | trace_parsed_ = trace_doc_["traceEvents"]; |
364 | 5 | ASSERT_TRUE(trace_parsed_.IsArray()); |
365 | 5 | } |
366 | | |
367 | 4 | void DropTracedMetadataRecords() { |
368 | | // NB: rapidjson has move-semantics, like auto_ptr. |
369 | 4 | Value old_trace_parsed; |
370 | 4 | old_trace_parsed = trace_parsed_; |
371 | 4 | trace_parsed_.SetArray(); |
372 | 4 | auto old_trace_parsed_size = old_trace_parsed.Size(); |
373 | | |
374 | 16 | for (rapidjson::SizeType i = 0; i < old_trace_parsed_size; i++) { |
375 | 12 | Value value; |
376 | 12 | value = old_trace_parsed[i]; |
377 | 12 | if (value.GetType() != rapidjson::kObjectType) { |
378 | 0 | trace_parsed_.PushBack(value, trace_doc_.GetAllocator()); |
379 | 0 | continue; |
380 | 0 | } |
381 | 12 | string tmp; |
382 | 12 | if (value.HasMember("ph") && strcmp(value["ph"].GetString(), "M") == 0) { |
383 | 4 | continue; |
384 | 4 | } |
385 | | |
386 | 8 | trace_parsed_.PushBack(value, trace_doc_.GetAllocator()); |
387 | 8 | } |
388 | 4 | } |
389 | | |
390 | | // Search through the given array for any dictionary which has a key |
391 | | // or value which has 'string_to_match' as a substring. |
392 | | // Returns the matching dictionary, or NULL. |
393 | | static const Value* FindTraceEntry( |
394 | | const Value& trace_parsed, |
395 | 16 | const char* string_to_match) { |
396 | | // Scan all items |
397 | 16 | auto trace_parsed_count = trace_parsed.Size(); |
398 | 32 | for (rapidjson::SizeType i = 0; i < trace_parsed_count; i++) { |
399 | 24 | const Value& value = trace_parsed[i]; |
400 | 24 | if (value.GetType() != rapidjson::kObjectType) { |
401 | 0 | continue; |
402 | 0 | } |
403 | | |
404 | 24 | for (Value::ConstMemberIterator it = value.MemberBegin(); |
405 | 188 | it != value.MemberEnd(); |
406 | 172 | ++it) { |
407 | 172 | if (it->name.IsString() && strstr(it->name.GetString(), string_to_match) != nullptr) { |
408 | 0 | return &value; |
409 | 0 | } |
410 | 172 | if (it->value.IsString() && strstr(it->value.GetString(), string_to_match) != nullptr) { |
411 | 8 | return &value; |
412 | 8 | } |
413 | 172 | } |
414 | 24 | } |
415 | 8 | return nullptr; |
416 | 16 | } |
417 | | |
418 | | // For TraceEventCallbackAndRecordingX tests. |
419 | | void VerifyCallbackAndRecordedEvents(size_t expected_callback_count, |
420 | 4 | size_t expected_recorded_count) { |
421 | | // Callback events. |
422 | 4 | EXPECT_EQ(expected_callback_count, collected_events_names_.size()); |
423 | 12 | for (size_t i = 0; i < collected_events_names_.size(); ++i) { |
424 | 8 | EXPECT_EQ("callback", collected_events_categories_[i]); |
425 | 8 | EXPECT_EQ("yes", collected_events_names_[i]); |
426 | 8 | } |
427 | | |
428 | | // Recorded events. |
429 | 4 | EXPECT_EQ(expected_recorded_count, trace_parsed_.Size()); |
430 | 4 | EXPECT_TRUE(FindTraceEntry(trace_parsed_, "recording")); |
431 | 4 | EXPECT_FALSE(FindTraceEntry(trace_parsed_, "callback")); |
432 | 4 | EXPECT_TRUE(FindTraceEntry(trace_parsed_, "yes")); |
433 | 4 | EXPECT_FALSE(FindTraceEntry(trace_parsed_, "no")); |
434 | 4 | } |
435 | | |
436 | | void VerifyCollectedEvent(size_t i, |
437 | | unsigned phase, |
438 | | const string& category, |
439 | 6 | const string& name) { |
440 | 6 | EXPECT_EQ(phase, collected_events_phases_[i]); |
441 | 6 | EXPECT_EQ(category, collected_events_categories_[i]); |
442 | 6 | EXPECT_EQ(name, collected_events_names_[i]); |
443 | 6 | } |
444 | | |
445 | | Document trace_doc_; |
446 | | Value trace_parsed_; |
447 | | |
448 | | vector<string> collected_events_categories_; |
449 | | vector<string> collected_events_names_; |
450 | | vector<unsigned char> collected_events_phases_; |
451 | | vector<MicrosecondsInt64> collected_events_timestamps_; |
452 | | |
453 | | static TraceEventCallbackTest* s_instance; |
454 | | static void Callback(MicrosecondsInt64 timestamp, |
455 | | char phase, |
456 | | const unsigned char* category_group_enabled, |
457 | | const char* name, |
458 | | uint64_t id, |
459 | | int num_args, |
460 | | const char* const arg_names[], |
461 | | const unsigned char arg_types[], |
462 | | const uint64_t arg_values[], |
463 | 20 | unsigned char flags) { |
464 | 20 | s_instance->collected_events_phases_.push_back(phase); |
465 | 20 | s_instance->collected_events_categories_.push_back( |
466 | 20 | TraceLog::GetCategoryGroupName(category_group_enabled)); |
467 | 20 | s_instance->collected_events_names_.push_back(name); |
468 | 20 | s_instance->collected_events_timestamps_.push_back(timestamp); |
469 | 20 | } |
470 | | }; |
471 | | |
472 | | TraceEventCallbackTest* TraceEventCallbackTest::s_instance; |
473 | | |
474 | 1 | TEST_F(TraceEventCallbackTest, TraceEventCallback) { |
475 | 1 | TRACE_EVENT_INSTANT0("all", "before enable", TRACE_EVENT_SCOPE_THREAD); |
476 | 1 | TraceLog::GetInstance()->SetEventCallbackEnabled( |
477 | 1 | CategoryFilter("*"), Callback); |
478 | 1 | TRACE_EVENT_INSTANT0("all", "event1", TRACE_EVENT_SCOPE_GLOBAL); |
479 | 1 | TRACE_EVENT_INSTANT0("all", "event2", TRACE_EVENT_SCOPE_GLOBAL); |
480 | 1 | { |
481 | 1 | TRACE_EVENT0("all", "duration"); |
482 | 1 | TRACE_EVENT_INSTANT0("all", "event3", TRACE_EVENT_SCOPE_GLOBAL); |
483 | 1 | } |
484 | 1 | TraceLog::GetInstance()->SetEventCallbackDisabled(); |
485 | 1 | TRACE_EVENT_INSTANT0("all", "after callback removed", |
486 | 1 | TRACE_EVENT_SCOPE_GLOBAL); |
487 | 1 | const auto n = std::min(collected_events_names_.size(), collected_events_phases_.size()); |
488 | 6 | for (size_t i = 0; i < n; ++i) { |
489 | 5 | const auto& name = collected_events_names_[i]; |
490 | 5 | const auto phase = collected_events_phases_[i]; |
491 | 5 | LOG(INFO) << "Collected event #" << i << ": name=" << name << ", phase=" << phase; |
492 | 5 | } |
493 | | |
494 | 1 | ASSERT_EQ(5u, collected_events_names_.size()); |
495 | | |
496 | 1 | EXPECT_EQ("event1", collected_events_names_[0]); |
497 | 1 | EXPECT_EQ(TRACE_EVENT_PHASE_INSTANT, collected_events_phases_[0]); |
498 | | |
499 | 1 | EXPECT_EQ("event2", collected_events_names_[1]); |
500 | 1 | EXPECT_EQ(TRACE_EVENT_PHASE_INSTANT, collected_events_phases_[1]); |
501 | | |
502 | 1 | EXPECT_EQ("duration", collected_events_names_[2]); |
503 | 1 | EXPECT_EQ(TRACE_EVENT_PHASE_BEGIN, collected_events_phases_[2]); |
504 | | |
505 | 1 | EXPECT_EQ("event3", collected_events_names_[3]); |
506 | 1 | EXPECT_EQ(TRACE_EVENT_PHASE_INSTANT, collected_events_phases_[3]); |
507 | | |
508 | 1 | EXPECT_EQ("duration", collected_events_names_[4]); |
509 | 1 | EXPECT_EQ(TRACE_EVENT_PHASE_END, collected_events_phases_[4]); |
510 | | |
511 | 5 | for (size_t i = 1; i < collected_events_timestamps_.size(); i++) { |
512 | 4 | EXPECT_LE(collected_events_timestamps_[i - 1], |
513 | 4 | collected_events_timestamps_[i]); |
514 | 4 | } |
515 | 1 | } |
516 | | |
517 | 1 | TEST_F(TraceEventCallbackTest, TraceEventCallbackWhileFull) { |
518 | 1 | TraceLog::GetInstance()->SetEnabled( |
519 | 1 | CategoryFilter("*"), |
520 | 1 | TraceLog::RECORDING_MODE, |
521 | 1 | TraceLog::RECORD_UNTIL_FULL); |
522 | 255k | do { |
523 | 255k | TRACE_EVENT_INSTANT0("all", "badger badger", TRACE_EVENT_SCOPE_GLOBAL); |
524 | 255k | } while (!TraceLog::GetInstance()->BufferIsFull()); |
525 | 1 | TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("*"), |
526 | 1 | Callback); |
527 | 1 | TRACE_EVENT_INSTANT0("all", "a snake", TRACE_EVENT_SCOPE_GLOBAL); |
528 | 1 | TraceLog::GetInstance()->SetEventCallbackDisabled(); |
529 | 1 | ASSERT_EQ(1u, collected_events_names_.size()); |
530 | 1 | EXPECT_EQ("a snake", collected_events_names_[0]); |
531 | 1 | } |
532 | | |
533 | | // 1: Enable callback, enable recording, disable callback, disable recording. |
534 | 1 | TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording1) { |
535 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
536 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
537 | 1 | TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"), |
538 | 1 | Callback); |
539 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
540 | 1 | TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
541 | 1 | TraceLog::GetInstance()->SetEnabled( |
542 | 1 | CategoryFilter("recording"), |
543 | 1 | TraceLog::RECORDING_MODE, |
544 | 1 | TraceLog::RECORD_UNTIL_FULL); |
545 | 1 | TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
546 | 1 | TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
547 | 1 | TraceLog::GetInstance()->SetEventCallbackDisabled(); |
548 | 1 | TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
549 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
550 | 1 | EndTraceAndFlush(); |
551 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
552 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
553 | | |
554 | 1 | DropTracedMetadataRecords(); |
555 | 1 | ASSERT_NO_FATALS(); |
556 | 1 | VerifyCallbackAndRecordedEvents(2, 2); |
557 | 1 | } |
558 | | |
559 | | // 2: Enable callback, enable recording, disable recording, disable callback. |
560 | 1 | TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording2) { |
561 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
562 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
563 | 1 | TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"), |
564 | 1 | Callback); |
565 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
566 | 1 | TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
567 | 1 | TraceLog::GetInstance()->SetEnabled( |
568 | 1 | CategoryFilter("recording"), |
569 | 1 | TraceLog::RECORDING_MODE, |
570 | 1 | TraceLog::RECORD_UNTIL_FULL); |
571 | 1 | TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
572 | 1 | TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
573 | 1 | EndTraceAndFlush(); |
574 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
575 | 1 | TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
576 | 1 | TraceLog::GetInstance()->SetEventCallbackDisabled(); |
577 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
578 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
579 | | |
580 | 1 | DropTracedMetadataRecords(); |
581 | 1 | VerifyCallbackAndRecordedEvents(3, 1); |
582 | 1 | } |
583 | | |
584 | | // 3: Enable recording, enable callback, disable callback, disable recording. |
585 | 1 | TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording3) { |
586 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
587 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
588 | 1 | TraceLog::GetInstance()->SetEnabled( |
589 | 1 | CategoryFilter("recording"), |
590 | 1 | TraceLog::RECORDING_MODE, |
591 | 1 | TraceLog::RECORD_UNTIL_FULL); |
592 | 1 | TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
593 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
594 | 1 | TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"), |
595 | 1 | Callback); |
596 | 1 | TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
597 | 1 | TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
598 | 1 | TraceLog::GetInstance()->SetEventCallbackDisabled(); |
599 | 1 | TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
600 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
601 | 1 | EndTraceAndFlush(); |
602 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
603 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
604 | | |
605 | 1 | DropTracedMetadataRecords(); |
606 | 1 | VerifyCallbackAndRecordedEvents(1, 3); |
607 | 1 | } |
608 | | |
609 | | // 4: Enable recording, enable callback, disable recording, disable callback. |
610 | 1 | TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecording4) { |
611 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
612 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
613 | 1 | TraceLog::GetInstance()->SetEnabled( |
614 | 1 | CategoryFilter("recording"), |
615 | 1 | TraceLog::RECORDING_MODE, |
616 | 1 | TraceLog::RECORD_UNTIL_FULL); |
617 | 1 | TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
618 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
619 | 1 | TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("callback"), |
620 | 1 | Callback); |
621 | 1 | TRACE_EVENT_INSTANT0("recording", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
622 | 1 | TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
623 | 1 | EndTraceAndFlush(); |
624 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
625 | 1 | TRACE_EVENT_INSTANT0("callback", "yes", TRACE_EVENT_SCOPE_GLOBAL); |
626 | 1 | TraceLog::GetInstance()->SetEventCallbackDisabled(); |
627 | 1 | TRACE_EVENT_INSTANT0("recording", "no", TRACE_EVENT_SCOPE_GLOBAL); |
628 | 1 | TRACE_EVENT_INSTANT0("callback", "no", TRACE_EVENT_SCOPE_GLOBAL); |
629 | | |
630 | 1 | DropTracedMetadataRecords(); |
631 | 1 | VerifyCallbackAndRecordedEvents(2, 2); |
632 | 1 | } |
633 | | |
634 | 1 | TEST_F(TraceEventCallbackTest, TraceEventCallbackAndRecordingDuration) { |
635 | 1 | TraceLog::GetInstance()->SetEventCallbackEnabled(CategoryFilter("*"), |
636 | 1 | Callback); |
637 | 1 | { |
638 | 1 | TRACE_EVENT0("callback", "duration1"); |
639 | 1 | TraceLog::GetInstance()->SetEnabled( |
640 | 1 | CategoryFilter("*"), |
641 | 1 | TraceLog::RECORDING_MODE, |
642 | 1 | TraceLog::RECORD_UNTIL_FULL); |
643 | 1 | TRACE_EVENT0("callback", "duration2"); |
644 | 1 | EndTraceAndFlush(); |
645 | 1 | TRACE_EVENT0("callback", "duration3"); |
646 | 1 | } |
647 | 1 | TraceLog::GetInstance()->SetEventCallbackDisabled(); |
648 | | |
649 | 1 | ASSERT_EQ(6u, collected_events_names_.size()); |
650 | 1 | VerifyCollectedEvent(0, TRACE_EVENT_PHASE_BEGIN, "callback", "duration1"); |
651 | 1 | VerifyCollectedEvent(1, TRACE_EVENT_PHASE_BEGIN, "callback", "duration2"); |
652 | 1 | VerifyCollectedEvent(2, TRACE_EVENT_PHASE_BEGIN, "callback", "duration3"); |
653 | 1 | VerifyCollectedEvent(3, TRACE_EVENT_PHASE_END, "callback", "duration3"); |
654 | 1 | VerifyCollectedEvent(4, TRACE_EVENT_PHASE_END, "callback", "duration2"); |
655 | 1 | VerifyCollectedEvent(5, TRACE_EVENT_PHASE_END, "callback", "duration1"); |
656 | 1 | } |
657 | | |
658 | | //////////////////////////////////////////////////////////// |
659 | | // Tests for synthetic delay |
660 | | // (from chromium-base/debug/trace_event_synthetic_delay_unittest.cc) |
661 | | //////////////////////////////////////////////////////////// |
662 | | |
663 | | namespace { |
664 | | |
665 | | const int kTargetDurationMs = 100; |
666 | | // Allow some leeway in timings to make it possible to run these tests with a |
667 | | // wall clock time source too. |
668 | | const int kShortDurationMs = 10; |
669 | | |
670 | | } // namespace |
671 | | |
672 | | namespace debug { |
673 | | |
674 | | class TraceEventSyntheticDelayTest : public YBTest, |
675 | | public TraceEventSyntheticDelayClock { |
676 | | public: |
677 | 10 | TraceEventSyntheticDelayTest() { |
678 | 10 | now_ = MonoTime::Min(); |
679 | 10 | } |
680 | | |
681 | 10 | virtual ~TraceEventSyntheticDelayTest() { |
682 | 10 | ResetTraceEventSyntheticDelays(); |
683 | 10 | } |
684 | | |
685 | | // TraceEventSyntheticDelayClock implementation. |
686 | 1.05k | MonoTime Now() override { |
687 | 1.05k | AdvanceTime(MonoDelta::FromMilliseconds(kShortDurationMs / 10)); |
688 | 1.05k | return now_; |
689 | 1.05k | } |
690 | | |
691 | 10 | TraceEventSyntheticDelay* ConfigureDelay(const char* name) { |
692 | 10 | TraceEventSyntheticDelay* delay = TraceEventSyntheticDelay::Lookup(name); |
693 | 10 | delay->SetClock(this); |
694 | 10 | delay->SetTargetDuration( |
695 | 10 | MonoDelta::FromMilliseconds(kTargetDurationMs)); |
696 | 10 | return delay; |
697 | 10 | } |
698 | | |
699 | 1.05k | void AdvanceTime(MonoDelta delta) { now_.AddDelta(delta); } |
700 | | |
701 | 9 | int TestFunction() { |
702 | 9 | MonoTime start = Now(); |
703 | 9 | { TRACE_EVENT_SYNTHETIC_DELAY("test.Delay"); } |
704 | 9 | MonoTime end = Now(); |
705 | 9 | return narrow_cast<int>(end.GetDeltaSince(start).ToMilliseconds()); |
706 | 9 | } |
707 | | |
708 | 6 | int AsyncTestFunctionBegin() { |
709 | 6 | MonoTime start = Now(); |
710 | 6 | { TRACE_EVENT_SYNTHETIC_DELAY_BEGIN("test.AsyncDelay"); } |
711 | 6 | MonoTime end = Now(); |
712 | 6 | return narrow_cast<int>(end.GetDeltaSince(start).ToMilliseconds()); |
713 | 6 | } |
714 | | |
715 | 8 | int AsyncTestFunctionEnd() { |
716 | 8 | MonoTime start = Now(); |
717 | 8 | { TRACE_EVENT_SYNTHETIC_DELAY_END("test.AsyncDelay"); } |
718 | 8 | MonoTime end = Now(); |
719 | 8 | return narrow_cast<int>(end.GetDeltaSince(start).ToMilliseconds()); |
720 | 8 | } |
721 | | |
722 | | private: |
723 | | MonoTime now_; |
724 | | |
725 | | DISALLOW_COPY_AND_ASSIGN(TraceEventSyntheticDelayTest); |
726 | | }; |
727 | | |
728 | 1 | TEST_F(TraceEventSyntheticDelayTest, StaticDelay) { |
729 | 1 | TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay"); |
730 | 1 | delay->SetMode(TraceEventSyntheticDelay::STATIC); |
731 | 1 | EXPECT_GE(TestFunction(), kTargetDurationMs); |
732 | 1 | } |
733 | | |
734 | 1 | TEST_F(TraceEventSyntheticDelayTest, OneShotDelay) { |
735 | 1 | TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay"); |
736 | 1 | delay->SetMode(TraceEventSyntheticDelay::ONE_SHOT); |
737 | 1 | EXPECT_GE(TestFunction(), kTargetDurationMs); |
738 | 1 | EXPECT_LT(TestFunction(), kShortDurationMs); |
739 | | |
740 | 1 | delay->SetTargetDuration( |
741 | 1 | MonoDelta::FromMilliseconds(kTargetDurationMs)); |
742 | 1 | EXPECT_GE(TestFunction(), kTargetDurationMs); |
743 | 1 | } |
744 | | |
745 | 1 | TEST_F(TraceEventSyntheticDelayTest, AlternatingDelay) { |
746 | 1 | TraceEventSyntheticDelay* delay = ConfigureDelay("test.Delay"); |
747 | 1 | delay->SetMode(TraceEventSyntheticDelay::ALTERNATING); |
748 | 1 | EXPECT_GE(TestFunction(), kTargetDurationMs); |
749 | 1 | EXPECT_LT(TestFunction(), kShortDurationMs); |
750 | 1 | EXPECT_GE(TestFunction(), kTargetDurationMs); |
751 | 1 | EXPECT_LT(TestFunction(), kShortDurationMs); |
752 | 1 | } |
753 | | |
754 | 1 | TEST_F(TraceEventSyntheticDelayTest, AsyncDelay) { |
755 | 1 | ConfigureDelay("test.AsyncDelay"); |
756 | 1 | EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); |
757 | 1 | EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); |
758 | 1 | } |
759 | | |
760 | 1 | TEST_F(TraceEventSyntheticDelayTest, AsyncDelayExceeded) { |
761 | 1 | ConfigureDelay("test.AsyncDelay"); |
762 | 1 | EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); |
763 | 1 | AdvanceTime(MonoDelta::FromMilliseconds(kTargetDurationMs)); |
764 | 1 | EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); |
765 | 1 | } |
766 | | |
767 | 1 | TEST_F(TraceEventSyntheticDelayTest, AsyncDelayNoActivation) { |
768 | 1 | ConfigureDelay("test.AsyncDelay"); |
769 | 1 | EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); |
770 | 1 | } |
771 | | |
772 | 1 | TEST_F(TraceEventSyntheticDelayTest, AsyncDelayNested) { |
773 | 1 | ConfigureDelay("test.AsyncDelay"); |
774 | 1 | EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); |
775 | 1 | EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); |
776 | 1 | EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); |
777 | 1 | EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); |
778 | 1 | } |
779 | | |
780 | 1 | TEST_F(TraceEventSyntheticDelayTest, AsyncDelayUnbalanced) { |
781 | 1 | ConfigureDelay("test.AsyncDelay"); |
782 | 1 | EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); |
783 | 1 | EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); |
784 | 1 | EXPECT_LT(AsyncTestFunctionEnd(), kShortDurationMs); |
785 | | |
786 | 1 | EXPECT_LT(AsyncTestFunctionBegin(), kShortDurationMs); |
787 | 1 | EXPECT_GE(AsyncTestFunctionEnd(), kTargetDurationMs / 2); |
788 | 1 | } |
789 | | |
790 | 1 | TEST_F(TraceEventSyntheticDelayTest, ResetDelays) { |
791 | 1 | ConfigureDelay("test.Delay"); |
792 | 1 | ResetTraceEventSyntheticDelays(); |
793 | 1 | EXPECT_LT(TestFunction(), kShortDurationMs); |
794 | 1 | } |
795 | | |
796 | 1 | TEST_F(TraceEventSyntheticDelayTest, BeginParallel) { |
797 | 1 | TraceEventSyntheticDelay* delay = ConfigureDelay("test.AsyncDelay"); |
798 | 1 | MonoTime end_times[2]; |
799 | 1 | MonoTime start_time = Now(); |
800 | | |
801 | 1 | delay->BeginParallel(&end_times[0]); |
802 | 1 | EXPECT_FALSE(!end_times[0].Initialized()); |
803 | | |
804 | 1 | delay->BeginParallel(&end_times[1]); |
805 | 1 | EXPECT_FALSE(!end_times[1].Initialized()); |
806 | | |
807 | 1 | delay->EndParallel(end_times[0]); |
808 | 1 | EXPECT_GE(Now().GetDeltaSince(start_time).ToMilliseconds(), kTargetDurationMs); |
809 | | |
810 | 1 | start_time = Now(); |
811 | 1 | delay->EndParallel(end_times[1]); |
812 | 1 | EXPECT_LT(Now().GetDeltaSince(start_time).ToMilliseconds(), kShortDurationMs); |
813 | 1 | } |
814 | | |
815 | 1 | TEST_F(TraceTest, TestVLogTrace) { |
816 | 3 | for (FLAGS_v = 0; FLAGS_v <= 1; FLAGS_v++) { |
817 | 2 | TraceLog* tl = TraceLog::GetInstance(); |
818 | 2 | tl->SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString), |
819 | 2 | TraceLog::RECORDING_MODE, |
820 | 2 | TraceLog::RECORD_CONTINUOUSLY); |
821 | 2 | VLOG_AND_TRACE("test", 1) << "hello world"; |
822 | 2 | tl->SetDisabled(); |
823 | 2 | string trace_json = TraceResultBuffer::FlushTraceLogToString(); |
824 | 2 | ASSERT_STR_CONTAINS(trace_json, "hello world"); |
825 | 2 | ASSERT_STR_CONTAINS(trace_json, "trace-test.cc"); |
826 | 2 | } |
827 | 1 | } |
828 | | |
829 | | namespace { |
830 | 1 | string FunctionWithSideEffect(bool* b) { |
831 | 1 | *b = true; |
832 | 1 | return "function-result"; |
833 | 1 | } |
834 | | } // anonymous namespace |
835 | | |
836 | | // Test that, if tracing is not enabled, a VLOG_AND_TRACE doesn't evaluate its |
837 | | // arguments. |
838 | 1 | TEST_F(TraceTest, TestVLogTraceLazyEvaluation) { |
839 | 1 | FLAGS_v = 0; |
840 | 1 | bool function_run = false; |
841 | 1 | VLOG_AND_TRACE("test", 1) << FunctionWithSideEffect(&function_run); |
842 | 1 | ASSERT_FALSE(function_run); |
843 | | |
844 | | // If we enable verbose logging, we should run the side effect even though |
845 | | // trace logging is disabled. |
846 | 1 | FLAGS_v = 1; |
847 | 1 | VLOG_AND_TRACE("test", 1) << FunctionWithSideEffect(&function_run); |
848 | 1 | ASSERT_TRUE(function_run); |
849 | 1 | } |
850 | | |
851 | 1 | TEST_F(TraceTest, TestVLogAndEchoToConsole) { |
852 | 1 | TraceLog* tl = TraceLog::GetInstance(); |
853 | 1 | tl->SetEnabled(CategoryFilter(CategoryFilter::kDefaultCategoryFilterString), |
854 | 1 | TraceLog::RECORDING_MODE, |
855 | 1 | TraceLog::ECHO_TO_CONSOLE); |
856 | 1 | FLAGS_v = 1; |
857 | 1 | VLOG_AND_TRACE("test", 1) << "hello world"; |
858 | 1 | tl->SetDisabled(); |
859 | 1 | } |
860 | | |
861 | | } // namespace debug |
862 | | } // namespace yb |