1 /*
2 * Copyright (C) 2021 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "perfetto/tracing/traced_value.h"
18
19 #include <array>
20 #include <deque>
21 #include <forward_list>
22 #include <list>
23 #include <map>
24 #include <queue>
25 #include <set>
26 #include <sstream>
27 #include <stack>
28 #include <unordered_map>
29 #include <unordered_set>
30
31 #include "perfetto/base/template_util.h"
32 #include "perfetto/protozero/scattered_heap_buffer.h"
33 #include "perfetto/test/traced_value_test_support.h"
34 #include "perfetto/tracing/debug_annotation.h"
35 #include "perfetto/tracing/track_event.h"
36 #include "protos/perfetto/trace/test_event.pb.h"
37 #include "protos/perfetto/trace/test_event.pbzero.h"
38 #include "protos/perfetto/trace/track_event/debug_annotation.gen.h"
39 #include "protos/perfetto/trace/track_event/debug_annotation.pb.h"
40 #include "test/gtest_and_gmock.h"
41
42 namespace perfetto {
43
44 // static asserts checking for conversion support for known types.
45
46 #define ASSERT_TYPE_SUPPORTED(T) \
47 static_assert(check_traced_value_support<T>::value, ""); \
48 static_assert(internal::has_traced_value_support<T>::value, "")
49
50 #define ASSERT_TYPE_NOT_SUPPORTED(T) \
51 static_assert(!internal::has_traced_value_support<T>::value, "")
52
53 struct NonSupportedType {};
54
55 ASSERT_TYPE_SUPPORTED(bool);
56
57 ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType);
58
59 // Integer types.
60 ASSERT_TYPE_SUPPORTED(short int);
61 ASSERT_TYPE_SUPPORTED(unsigned short int);
62 ASSERT_TYPE_SUPPORTED(int);
63 ASSERT_TYPE_SUPPORTED(unsigned int);
64 ASSERT_TYPE_SUPPORTED(long int);
65 ASSERT_TYPE_SUPPORTED(unsigned long int);
66 ASSERT_TYPE_SUPPORTED(long long int);
67 ASSERT_TYPE_SUPPORTED(unsigned long long int);
68
69 // References and const references types.
70 ASSERT_TYPE_SUPPORTED(int&);
71 ASSERT_TYPE_SUPPORTED(const int&);
72 ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType&);
73 ASSERT_TYPE_NOT_SUPPORTED(const NonSupportedType&);
74
75 // Character types.
76 ASSERT_TYPE_SUPPORTED(signed char);
77 ASSERT_TYPE_SUPPORTED(unsigned char);
78 ASSERT_TYPE_SUPPORTED(char);
79 ASSERT_TYPE_SUPPORTED(wchar_t);
80
81 // Float types.
82 ASSERT_TYPE_SUPPORTED(float);
83 ASSERT_TYPE_SUPPORTED(double);
84 ASSERT_TYPE_SUPPORTED(long double);
85
86 // Strings.
87 ASSERT_TYPE_SUPPORTED(const char*);
88 ASSERT_TYPE_SUPPORTED(const char[]);
89 ASSERT_TYPE_SUPPORTED(const char[2]);
90 ASSERT_TYPE_SUPPORTED(std::string);
91
92 // Pointers.
93 ASSERT_TYPE_SUPPORTED(int*);
94 ASSERT_TYPE_SUPPORTED(const int*);
95 ASSERT_TYPE_SUPPORTED(void*);
96 ASSERT_TYPE_SUPPORTED(const void*);
97 ASSERT_TYPE_SUPPORTED(std::nullptr_t);
98 ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType*);
99 ASSERT_TYPE_NOT_SUPPORTED(const NonSupportedType*);
100
101 // Arrays.
102 ASSERT_TYPE_NOT_SUPPORTED(int[]);
103 ASSERT_TYPE_NOT_SUPPORTED(const int[]);
104 ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType[]);
105 ASSERT_TYPE_NOT_SUPPORTED(const NonSupportedType[]);
106 ASSERT_TYPE_SUPPORTED(int (&)[3]);
107 ASSERT_TYPE_SUPPORTED(const int (&)[3]);
108 ASSERT_TYPE_NOT_SUPPORTED(NonSupportedType (&)[3]);
109 ASSERT_TYPE_NOT_SUPPORTED(const NonSupportedType (&)[3]);
110
111 // STL containers.
112 ASSERT_TYPE_SUPPORTED(std::vector<int>);
113 ASSERT_TYPE_NOT_SUPPORTED(std::vector<NonSupportedType>);
114
115 using array_int_t = std::array<int, 4>;
116 ASSERT_TYPE_SUPPORTED(array_int_t);
117 ASSERT_TYPE_SUPPORTED(std::deque<int>);
118 ASSERT_TYPE_SUPPORTED(std::forward_list<int>);
119 ASSERT_TYPE_SUPPORTED(std::list<int>);
120 ASSERT_TYPE_NOT_SUPPORTED(std::stack<int>);
121 ASSERT_TYPE_NOT_SUPPORTED(std::queue<int>);
122 ASSERT_TYPE_NOT_SUPPORTED(std::priority_queue<int>);
123 ASSERT_TYPE_SUPPORTED(std::set<int>);
124 ASSERT_TYPE_SUPPORTED(std::multiset<int>);
125 using map_int_int_t = std::map<int, int>;
126 ASSERT_TYPE_NOT_SUPPORTED(map_int_int_t);
127 using multimap_int_int_t = std::multimap<int, int>;
128 ASSERT_TYPE_NOT_SUPPORTED(multimap_int_int_t);
129 ASSERT_TYPE_SUPPORTED(std::unordered_set<int>);
130 ASSERT_TYPE_SUPPORTED(std::unordered_multiset<int>);
131 using unordered_map_int_int_t = std::unordered_map<int, int>;
132 ASSERT_TYPE_NOT_SUPPORTED(unordered_map_int_int_t);
133 using unordered_multimap_int_int_t = std::unordered_multimap<int, int>;
134 ASSERT_TYPE_NOT_SUPPORTED(unordered_multimap_int_int_t);
135
136 // unique_ptr.
137 ASSERT_TYPE_SUPPORTED(std::unique_ptr<int>);
138 ASSERT_TYPE_NOT_SUPPORTED(std::unique_ptr<NonSupportedType>);
139
TEST(TracedValueTest,FlatDictionary_Explicit)140 TEST(TracedValueTest, FlatDictionary_Explicit) {
141 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
142 {
143 auto dict =
144 internal::CreateTracedValueFromProto(message.get()).WriteDictionary();
145 dict.AddItem("bool").WriteBoolean(true);
146 dict.AddItem("double").WriteDouble(0.0);
147 dict.AddItem("int").WriteInt64(2014);
148 dict.AddItem("string").WriteString("string");
149 dict.AddItem("truncated_string").WriteString("truncated_string", 9);
150 dict.AddItem("ptr").WritePointer(reinterpret_cast<void*>(0x1234));
151 }
152 EXPECT_EQ(
153 "{bool:true,double:0,int:2014,string:string,truncated_string:truncated,"
154 "ptr:0x1234}",
155 internal::DebugAnnotationToString(message.SerializeAsString()));
156 }
157
TEST(TracedValueTest,FlatDictionary_Short)158 TEST(TracedValueTest, FlatDictionary_Short) {
159 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
160 {
161 auto dict =
162 internal::CreateTracedValueFromProto(message.get()).WriteDictionary();
163 dict.Add("bool", true);
164 dict.Add("double", 0.0);
165 dict.Add("int", 2014);
166 dict.Add("string", "string");
167 dict.Add("ptr", reinterpret_cast<void*>(0x1234));
168 }
169 EXPECT_EQ("{bool:true,double:0,int:2014,string:string,ptr:0x1234}",
170 internal::DebugAnnotationToString(message.SerializeAsString()));
171 }
172
TEST(TracedValueTest,Hierarchy_Explicit)173 TEST(TracedValueTest, Hierarchy_Explicit) {
174 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
175 {
176 auto root_dict =
177 internal::CreateTracedValueFromProto(message.get()).WriteDictionary();
178 {
179 auto array = root_dict.AddItem("a1").WriteArray();
180 array.AppendItem().WriteInt64(1);
181 array.AppendItem().WriteBoolean(true);
182 {
183 auto dict = array.AppendItem().WriteDictionary();
184 dict.AddItem("i2").WriteInt64(3);
185 }
186 }
187 root_dict.AddItem("b0").WriteBoolean(true);
188 root_dict.AddItem("d0").WriteDouble(0.0);
189 {
190 auto dict1 = root_dict.AddItem("dict1").WriteDictionary();
191 {
192 auto dict2 = dict1.AddItem("dict2").WriteDictionary();
193 dict2.AddItem("b2").WriteBoolean(false);
194 }
195 dict1.AddItem("i1").WriteInt64(2014);
196 dict1.AddItem("s1").WriteString("foo");
197 }
198 root_dict.AddItem("i0").WriteInt64(2014);
199 root_dict.AddItem("s0").WriteString("foo");
200 }
201
202 EXPECT_EQ(
203 "{"
204 "a1:[1,true,{i2:3}],"
205 "b0:true,"
206 "d0:0,"
207 "dict1:{dict2:{b2:false},i1:2014,s1:foo},"
208 "i0:2014,"
209 "s0:foo}",
210 internal::DebugAnnotationToString(message.SerializeAsString()));
211 }
212
TEST(TracedValueTest,Hierarchy_Short)213 TEST(TracedValueTest, Hierarchy_Short) {
214 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
215 {
216 auto root_dict =
217 internal::CreateTracedValueFromProto(message.get()).WriteDictionary();
218 {
219 auto array = root_dict.AddArray("a1");
220 array.Append(1);
221 array.Append(true);
222 {
223 auto dict = array.AppendDictionary();
224 dict.Add("i2", 3);
225 }
226 }
227 root_dict.Add("b0", true);
228 root_dict.Add("d0", 0.0);
229 {
230 auto dict1 = root_dict.AddDictionary("dict1");
231 {
232 auto dict2 = dict1.AddDictionary("dict2");
233 dict2.Add("b2", false);
234 }
235 dict1.Add("i1", 2014);
236 dict1.Add("s1", "foo");
237 }
238 root_dict.Add("i0", 2014);
239 root_dict.Add("s0", "foo");
240 }
241
242 EXPECT_EQ(
243 "{"
244 "a1:[1,true,{i2:3}],"
245 "b0:true,"
246 "d0:0,"
247 "dict1:{dict2:{b2:false},i1:2014,s1:foo},"
248 "i0:2014,"
249 "s0:foo}",
250 internal::DebugAnnotationToString(message.SerializeAsString()));
251 }
252
253 namespace {
254
255 class HasWriteIntoTracedValueConvertorMember {
256 public:
WriteIntoTracedValue(TracedValue context) const257 void WriteIntoTracedValue(TracedValue context) const {
258 auto dict = std::move(context).WriteDictionary();
259 dict.Add("int", 42);
260 dict.Add("bool", false);
261 }
262 };
263
264 class HasWriteIntoTraceConvertorMember {
265 public:
WriteIntoTrace(TracedValue context) const266 void WriteIntoTrace(TracedValue context) const {
267 auto dict = std::move(context).WriteDictionary();
268 dict.Add("int", 42);
269 dict.Add("bool", false);
270 }
271 };
272
273 class HasExternalWriteIntoTraceConvertor {};
274 class HasExternalWriteIntoTracedValueConvertor {};
275
276 class HasAllConversionMethods {
277 public:
WriteIntoTracedValue(TracedValue context) const278 void WriteIntoTracedValue(TracedValue context) const {
279 std::move(context).WriteString("T::WriteIntoTracedValue");
280 }
281
operator ()(TracedValue context) const282 void operator()(TracedValue context) const {
283 std::move(context).WriteString("T::()");
284 }
285 };
286
287 class NoConversions {};
288
289 class HasConstWriteMember {
290 public:
WriteIntoTracedValue(TracedValue context) const291 void WriteIntoTracedValue(TracedValue context) const {
292 std::move(context).WriteString("T::WriteIntoTracedValue const");
293 }
294 };
295
296 class HasNonConstWriteMember {
297 public:
WriteIntoTracedValue(TracedValue context)298 void WriteIntoTracedValue(TracedValue context) {
299 std::move(context).WriteString("T::WriteIntoTracedValue");
300 }
301 };
302
303 class HasConstAndNonConstWriteMember {
304 public:
WriteIntoTracedValue(TracedValue context)305 void WriteIntoTracedValue(TracedValue context) {
306 std::move(context).WriteString("T::WriteIntoTracedValue");
307 }
308
WriteIntoTracedValue(TracedValue context) const309 void WriteIntoTracedValue(TracedValue context) const {
310 std::move(context).WriteString("T::WriteIntoTracedValue const");
311 }
312 };
313
314 } // namespace
315
316 template <>
317 struct TraceFormatTraits<HasExternalWriteIntoTraceConvertor> {
WriteIntoTraceperfetto::TraceFormatTraits318 static void WriteIntoTrace(TracedValue context,
319 const HasExternalWriteIntoTraceConvertor&) {
320 std::move(context).WriteString("TraceFormatTraits::WriteIntoTrace");
321 }
322 };
323
324 template <>
325 struct TraceFormatTraits<HasExternalWriteIntoTracedValueConvertor> {
WriteIntoTracedValueperfetto::TraceFormatTraits326 static void WriteIntoTracedValue(
327 TracedValue context,
328 const HasExternalWriteIntoTracedValueConvertor&) {
329 std::move(context).WriteString("TraceFormatTraits::WriteIntoTracedValue");
330 }
331 };
332
333 template <>
334 struct TraceFormatTraits<HasAllConversionMethods> {
WriteIntoTracedValueperfetto::TraceFormatTraits335 static void WriteIntoTracedValue(TracedValue context,
336 const HasAllConversionMethods&) {
337 std::move(context).WriteString("TraceFormatTraits::WriteIntoTracedValue");
338 }
339 };
340
341 template <typename T>
ToStringWithFallback(T && value,const std::string & fallback)342 std::string ToStringWithFallback(T&& value, const std::string& fallback) {
343 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
344 WriteIntoTracedValueWithFallback(
345 internal::CreateTracedValueFromProto(message.get()),
346 std::forward<T>(value), fallback);
347 return internal::DebugAnnotationToString(message.SerializeAsString());
348 }
349
350 ASSERT_TYPE_SUPPORTED(HasWriteIntoTraceConvertorMember);
351 ASSERT_TYPE_SUPPORTED(HasWriteIntoTracedValueConvertorMember);
352 ASSERT_TYPE_SUPPORTED(HasExternalWriteIntoTraceConvertor);
353 ASSERT_TYPE_SUPPORTED(HasExternalWriteIntoTracedValueConvertor);
354 ASSERT_TYPE_SUPPORTED(HasAllConversionMethods);
355
356 ASSERT_TYPE_SUPPORTED(HasConstWriteMember);
357 ASSERT_TYPE_SUPPORTED(HasConstWriteMember&);
358 ASSERT_TYPE_SUPPORTED(HasConstWriteMember*);
359 ASSERT_TYPE_SUPPORTED(std::unique_ptr<HasConstWriteMember>);
360 ASSERT_TYPE_SUPPORTED(std::vector<HasConstWriteMember>);
361 ASSERT_TYPE_SUPPORTED(const HasConstWriteMember);
362 ASSERT_TYPE_SUPPORTED(const HasConstWriteMember&);
363 ASSERT_TYPE_SUPPORTED(const HasConstWriteMember*);
364 ASSERT_TYPE_SUPPORTED(std::unique_ptr<const HasConstWriteMember>);
365 ASSERT_TYPE_SUPPORTED(const std::vector<HasConstWriteMember>);
366 ASSERT_TYPE_SUPPORTED(std::vector<const HasConstWriteMember*>);
367
368 ASSERT_TYPE_SUPPORTED(HasNonConstWriteMember);
369 ASSERT_TYPE_SUPPORTED(HasNonConstWriteMember&);
370 ASSERT_TYPE_SUPPORTED(HasNonConstWriteMember*);
371 ASSERT_TYPE_SUPPORTED(std::unique_ptr<HasNonConstWriteMember>);
372 ASSERT_TYPE_SUPPORTED(std::vector<HasNonConstWriteMember>);
373 ASSERT_TYPE_NOT_SUPPORTED(const HasNonConstWriteMember);
374 ASSERT_TYPE_NOT_SUPPORTED(const HasNonConstWriteMember&);
375 ASSERT_TYPE_NOT_SUPPORTED(const HasNonConstWriteMember*);
376 ASSERT_TYPE_NOT_SUPPORTED(std::unique_ptr<const HasNonConstWriteMember>);
377 ASSERT_TYPE_NOT_SUPPORTED(const std::vector<HasNonConstWriteMember>);
378 ASSERT_TYPE_NOT_SUPPORTED(std::vector<const HasNonConstWriteMember*>);
379
380 ASSERT_TYPE_SUPPORTED(HasConstAndNonConstWriteMember);
381 ASSERT_TYPE_SUPPORTED(HasConstAndNonConstWriteMember&);
382 ASSERT_TYPE_SUPPORTED(HasConstAndNonConstWriteMember*);
383 ASSERT_TYPE_SUPPORTED(std::unique_ptr<HasConstAndNonConstWriteMember>);
384 ASSERT_TYPE_SUPPORTED(const HasConstAndNonConstWriteMember);
385 ASSERT_TYPE_SUPPORTED(const HasConstAndNonConstWriteMember&);
386 ASSERT_TYPE_SUPPORTED(const HasConstAndNonConstWriteMember*);
387 ASSERT_TYPE_SUPPORTED(std::unique_ptr<const HasConstAndNonConstWriteMember*>);
388
TEST(TracedValueTest,UserDefinedConvertors)389 TEST(TracedValueTest, UserDefinedConvertors) {
390 HasWriteIntoTraceConvertorMember value1;
391 EXPECT_EQ(TracedValueToString(value1), "{int:42,bool:false}");
392 EXPECT_EQ(TracedValueToString(&value1), "{int:42,bool:false}");
393
394 HasWriteIntoTracedValueConvertorMember value2;
395 EXPECT_EQ(TracedValueToString(value2), "{int:42,bool:false}");
396 EXPECT_EQ(TracedValueToString(&value2), "{int:42,bool:false}");
397
398 HasExternalWriteIntoTracedValueConvertor value3;
399 EXPECT_EQ(TracedValueToString(value3),
400 "TraceFormatTraits::WriteIntoTracedValue");
401 EXPECT_EQ(TracedValueToString(&value3),
402 "TraceFormatTraits::WriteIntoTracedValue");
403
404 HasExternalWriteIntoTraceConvertor value4;
405 EXPECT_EQ(TracedValueToString(value4), "TraceFormatTraits::WriteIntoTrace");
406 EXPECT_EQ(TracedValueToString(&value4), "TraceFormatTraits::WriteIntoTrace");
407
408 HasAllConversionMethods value5;
409 EXPECT_EQ(TracedValueToString(value5), "T::WriteIntoTracedValue");
410 EXPECT_EQ(TracedValueToString(&value5), "T::WriteIntoTracedValue");
411 }
412
TEST(TracedValueTest,WriteAsLambda)413 TEST(TracedValueTest, WriteAsLambda) {
414 EXPECT_EQ("42", TracedValueToString([&](TracedValue context) {
415 std::move(context).WriteInt64(42);
416 }));
417 }
418
419 #if PERFETTO_DCHECK_IS_ON()
420 // This death test makes sense only when dchecks are enabled.
TEST(TracedValueTest,FailOnIncorrectUsage)421 TEST(TracedValueTest, FailOnIncorrectUsage) {
422 // A new call to AddItem is not allowed before the previous result is
423 // consumed.
424 EXPECT_DEATH(
425
426 {
427 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
428 auto dict = internal::CreateTracedValueFromProto(message.get())
429 .WriteDictionary();
430 auto scope1 = dict.AddItem("key1");
431 auto scope2 = dict.AddItem("key2");
432 std::move(scope1).WriteInt64(1);
433 std::move(scope2).WriteInt64(2);
434 },
435 "");
436
437 // A new call to AppendItem is not allowed before the previous result is
438 // consumed.
439 EXPECT_DEATH(
440 {
441 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
442 auto array =
443 internal::CreateTracedValueFromProto(message.get()).WriteArray();
444 auto scope1 = array.AppendItem();
445 auto scope2 = array.AppendItem();
446 std::move(scope1).WriteInt64(1);
447 std::move(scope2).WriteInt64(2);
448 },
449 "");
450
451 // Writing to parent scope is not allowed.
452 EXPECT_DEATH(
453 {
454 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
455 auto outer_dict = internal::CreateTracedValueFromProto(message.get())
456 .WriteDictionary();
457 {
458 auto inner_dict = outer_dict.AddDictionary("inner");
459 outer_dict.Add("key", "value");
460 }
461 },
462 "");
463 }
464 #endif // PERFETTO_DCHECK_IS_ON()
465
TEST(TracedValueTest,PrimitiveTypesSupport)466 TEST(TracedValueTest, PrimitiveTypesSupport) {
467 EXPECT_EQ("0x0", TracedValueToString(nullptr));
468 EXPECT_EQ("0x1", TracedValueToString(reinterpret_cast<void*>(1)));
469
470 const int int_value = 1;
471 EXPECT_EQ("1", TracedValueToString(int_value));
472 EXPECT_EQ("1", TracedValueToString(&int_value));
473
474 EXPECT_EQ("1.5", TracedValueToString(1.5));
475 EXPECT_EQ("true", TracedValueToString(true));
476 EXPECT_EQ("foo", TracedValueToString("foo"));
477 EXPECT_EQ("bar", TracedValueToString(std::string("bar")));
478 }
479
TEST(TracedValueTest,UniquePtrSupport)480 TEST(TracedValueTest, UniquePtrSupport) {
481 std::unique_ptr<int> value1;
482 EXPECT_EQ("0x0", TracedValueToString(value1));
483
484 std::unique_ptr<int> value2(new int(4));
485 EXPECT_EQ("4", TracedValueToString(value2));
486 }
487
488 namespace {
489
490 enum OldStyleEnum { kFoo, kBar };
491
492 enum class NewStyleEnum { kValue1, kValue2 };
493
494 enum class EnumWithPrettyPrint { kValue1, kValue2 };
495
496 } // namespace
497
498 template <>
499 struct TraceFormatTraits<EnumWithPrettyPrint> {
WriteIntoTracedValueperfetto::TraceFormatTraits500 static void WriteIntoTracedValue(TracedValue context,
501 EnumWithPrettyPrint value) {
502 switch (value) {
503 case EnumWithPrettyPrint::kValue1:
504 std::move(context).WriteString("value1");
505 return;
506 case EnumWithPrettyPrint::kValue2:
507 std::move(context).WriteString("value2");
508 return;
509 }
510 }
511 };
512
TEST(TracedValueTest,EnumSupport)513 TEST(TracedValueTest, EnumSupport) {
514 EXPECT_EQ(TracedValueToString(kFoo), "0");
515 EXPECT_EQ(TracedValueToString(NewStyleEnum::kValue2), "1");
516 EXPECT_EQ(TracedValueToString(EnumWithPrettyPrint::kValue2), "value2");
517 }
518
TEST(TracedValueTest,ContainerSupport)519 TEST(TracedValueTest, ContainerSupport) {
520 std::vector<std::list<int>> value1{{1, 2}, {3, 4}};
521 EXPECT_EQ("[[1,2],[3,4]]", TracedValueToString(value1));
522 }
523
TEST(TracedValueTest,WriteWithFallback)524 TEST(TracedValueTest, WriteWithFallback) {
525 EXPECT_EQ("1", ToStringWithFallback(1, "fallback"));
526 EXPECT_EQ("true", ToStringWithFallback(true, "fallback"));
527 EXPECT_EQ("fallback", ToStringWithFallback(NonSupportedType(), "fallback"));
528 }
529
TEST(TracedValueTest,ConstAndNotConstSupport)530 TEST(TracedValueTest, ConstAndNotConstSupport) {
531 {
532 HasConstWriteMember value;
533 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(value));
534 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(&value));
535
536 std::vector<HasConstWriteMember> arr(1, value);
537 EXPECT_EQ("[T::WriteIntoTracedValue const]", TracedValueToString(arr));
538 }
539
540 {
541 const HasConstWriteMember value;
542 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(value));
543 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(&value));
544
545 const std::vector<HasConstWriteMember> arr(1, value);
546 EXPECT_EQ("[T::WriteIntoTracedValue const]", TracedValueToString(arr));
547 }
548
549 {
550 HasNonConstWriteMember value;
551 EXPECT_EQ("T::WriteIntoTracedValue", TracedValueToString(value));
552 EXPECT_EQ("T::WriteIntoTracedValue", TracedValueToString(&value));
553
554 std::vector<HasNonConstWriteMember> arr(1, value);
555 EXPECT_EQ("[T::WriteIntoTracedValue]", TracedValueToString(arr));
556 }
557
558 {
559 HasConstAndNonConstWriteMember value;
560 EXPECT_EQ("T::WriteIntoTracedValue", TracedValueToString(value));
561 EXPECT_EQ("T::WriteIntoTracedValue", TracedValueToString(&value));
562
563 std::vector<HasConstAndNonConstWriteMember> arr(1, value);
564 EXPECT_EQ("[T::WriteIntoTracedValue]", TracedValueToString(arr));
565 }
566
567 {
568 const HasConstAndNonConstWriteMember value;
569 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(value));
570 EXPECT_EQ("T::WriteIntoTracedValue const", TracedValueToString(&value));
571
572 const std::vector<HasConstAndNonConstWriteMember> arr(1, value);
573 EXPECT_EQ("[T::WriteIntoTracedValue const]", TracedValueToString(arr));
574 }
575 }
576
577 // Note: interning of the dictionary keys is not implemented yet, so there is no
578 // difference in behaviour for StaticString and DynamicString yet.
TEST(TracedValueTest,DictionaryKeys)579 TEST(TracedValueTest, DictionaryKeys) {
580 EXPECT_EQ("{literal:1}", TracedValueToString([&](TracedValue context) {
581 auto dict = std::move(context).WriteDictionary();
582 dict.Add("literal", 1);
583 }));
584
585 EXPECT_EQ("{static:1}", TracedValueToString([&](TracedValue context) {
586 auto dict = std::move(context).WriteDictionary();
587 const char* key = "static";
588 dict.Add(StaticString{key}, 1);
589 }));
590
591 EXPECT_EQ("{dynamic:1}", TracedValueToString([&](TracedValue context) {
592 auto dict = std::move(context).WriteDictionary();
593 std::string key = "dynamic";
594 dict.Add(DynamicString{key.data()}, 1);
595 }));
596
597 EXPECT_EQ("{dynamic:1}", TracedValueToString([&](TracedValue context) {
598 auto dict = std::move(context).WriteDictionary();
599 std::string key = "dynamic";
600 dict.Add(DynamicString{key.data(), key.length()}, 1);
601 }));
602
603 EXPECT_EQ("{dynamic:1}", TracedValueToString([&](TracedValue context) {
604 auto dict = std::move(context).WriteDictionary();
605 std::string key = "dynamic";
606 dict.Add(DynamicString{key}, 1);
607 }));
608 }
609
TEST(TracedValueTest,EmptyDict)610 TEST(TracedValueTest, EmptyDict) {
611 EXPECT_EQ("{}", TracedValueToString([&](TracedValue context) {
612 auto dict = std::move(context).WriteDictionary();
613 }));
614 }
615
TEST(TracedValueTest,EmptyArray)616 TEST(TracedValueTest, EmptyArray) {
617 // For now we do not distinguish between empty arrays and empty dicts on proto
618 // level as trace processor ignores them anyway.
619 EXPECT_EQ("{}", TracedValueToString([&](TracedValue context) {
620 auto array = std::move(context).WriteArray();
621 }));
622 }
623
TEST(TracedValueTest,WriteTypedProto_Explicit)624 TEST(TracedValueTest, WriteTypedProto_Explicit) {
625 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
626 WriteIntoTracedValue(
627 internal::CreateTracedValueFromProto(message.get()),
628 [](perfetto::TracedValue context) {
629 perfetto::TracedProto<protos::pbzero::TestEvent::TestPayload> proto =
630 std::move(context)
631 .WriteProto<protos::pbzero::TestEvent::TestPayload>();
632 proto->set_single_string("payload");
633 });
634
635 protos::DebugAnnotation annotation;
636 annotation.ParseFromString(message.SerializeAsString());
637 EXPECT_EQ(annotation.proto_type_name(),
638 ".perfetto.protos.TestEvent.TestPayload");
639
640 protos::TestEvent::TestPayload payload;
641 payload.ParseFromString(annotation.proto_value());
642 EXPECT_EQ(payload.single_string(), "payload");
643 }
644
TEST(TracedValueTest,WriteTypedProto_Implicit)645 TEST(TracedValueTest, WriteTypedProto_Implicit) {
646 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
647 WriteIntoTracedValue(
648 internal::CreateTracedValueFromProto(message.get()),
649 [](perfetto::TracedProto<protos::pbzero::TestEvent::TestPayload> proto) {
650 proto->set_single_string("payload");
651 });
652
653 protos::DebugAnnotation annotation;
654 annotation.ParseFromString(message.SerializeAsString());
655 EXPECT_EQ(annotation.proto_type_name(),
656 ".perfetto.protos.TestEvent.TestPayload");
657
658 protos::TestEvent::TestPayload payload;
659 payload.ParseFromString(annotation.proto_value());
660 EXPECT_EQ(payload.single_string(), "payload");
661 }
662
TEST(TracedValueTest,ImplicitTracedDictionary)663 TEST(TracedValueTest, ImplicitTracedDictionary) {
664 EXPECT_EQ("{key:value}", TracedValueToString([&](TracedDictionary dict) {
665 dict.Add("key", "value");
666 }));
667 }
668
TEST(TracedValueTest,ImplicitTracedArray)669 TEST(TracedValueTest, ImplicitTracedArray) {
670 EXPECT_EQ("[1]",
671 TracedValueToString([&](TracedArray array) { array.Append(1); }));
672 }
673
TEST(TracedValueTest,TracedProtoInDict)674 TEST(TracedValueTest, TracedProtoInDict) {
675 struct Foo {
676 void WriteIntoTrace(
677 perfetto::TracedProto<protos::pbzero::TestEvent::TestPayload> message) {
678 message->set_single_int(42);
679 }
680 };
681 Foo foo;
682 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
683 WriteIntoTracedValue(internal::CreateTracedValueFromProto(message.get()),
684 [&](TracedDictionary dict) { dict.Add("foo", foo); });
685 protos::DebugAnnotation annotation;
686 annotation.ParseFromString(message.SerializeAsString());
687 EXPECT_EQ(annotation.dict_entries_size(), 1);
688 EXPECT_EQ(annotation.dict_entries(0).name(), "foo");
689 EXPECT_EQ(annotation.dict_entries(0).proto_type_name(),
690 ".perfetto.protos.TestEvent.TestPayload");
691
692 protos::TestEvent::TestPayload payload;
693 payload.ParseFromString(annotation.dict_entries(0).proto_value());
694 EXPECT_EQ(payload.single_int(), 42);
695 }
696
TEST(TracedValueTest,PointerToTracedProtoInDict)697 TEST(TracedValueTest, PointerToTracedProtoInDict) {
698 struct Foo {
699 void WriteIntoTrace(
700 perfetto::TracedProto<protos::pbzero::TestEvent::TestPayload> message) {
701 message->set_single_int(42);
702 }
703 };
704 Foo foo;
705 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
706 WriteIntoTracedValue(internal::CreateTracedValueFromProto(message.get()),
707 [&](TracedDictionary dict) { dict.Add("foo", &foo); });
708 protos::DebugAnnotation annotation;
709 annotation.ParseFromString(message.SerializeAsString());
710 EXPECT_EQ(annotation.dict_entries_size(), 1);
711 EXPECT_EQ(annotation.dict_entries(0).name(), "foo");
712 EXPECT_EQ(annotation.dict_entries(0).proto_type_name(),
713 ".perfetto.protos.TestEvent.TestPayload");
714
715 protos::TestEvent::TestPayload payload;
716 payload.ParseFromString(annotation.dict_entries(0).proto_value());
717 EXPECT_EQ(payload.single_int(), 42);
718 }
719
TEST(TracedValueTest,UniquePointerToTracedProtoInDict)720 TEST(TracedValueTest, UniquePointerToTracedProtoInDict) {
721 struct Foo {
722 void WriteIntoTrace(
723 perfetto::TracedProto<protos::pbzero::TestEvent::TestPayload> message) {
724 message->set_single_int(42);
725 }
726 };
727 std::unique_ptr<Foo> foo(new Foo());
728 protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
729 WriteIntoTracedValue(internal::CreateTracedValueFromProto(message.get()),
730 [&](TracedDictionary dict) { dict.Add("foo", foo); });
731 protos::DebugAnnotation annotation;
732 annotation.ParseFromString(message.SerializeAsString());
733 EXPECT_EQ(annotation.dict_entries_size(), 1);
734 EXPECT_EQ(annotation.dict_entries(0).name(), "foo");
735 EXPECT_EQ(annotation.dict_entries(0).proto_type_name(),
736 ".perfetto.protos.TestEvent.TestPayload");
737
738 protos::TestEvent::TestPayload payload;
739 payload.ParseFromString(annotation.dict_entries(0).proto_value());
740 EXPECT_EQ(payload.single_int(), 42);
741 }
742
743 } // namespace perfetto
744