blob: 926e8edce320cc1b09278181049b46e51ec78860 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "perfetto/tracing/traced_value.h"
#include <array>
#include <deque>
#include <forward_list>
#include <map>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <unordered_map>
#include <unordered_set>
#include "perfetto/protozero/scattered_heap_buffer.h"
#include "perfetto/tracing/debug_annotation.h"
#include "perfetto/tracing/track_event.h"
#include "protos/perfetto/trace/track_event/debug_annotation.gen.h"
#include "protos/perfetto/trace/track_event/debug_annotation.pb.h"
#include "test/gtest_and_gmock.h"
namespace perfetto {
namespace {
void WriteAsJSON(const protos::DebugAnnotation::NestedValue& value,
std::stringstream& ss) {
if (value.nested_type() ==
protos::DebugAnnotation_NestedValue_NestedType_DICT) {
ss << "{";
for (int i = 0; i < value.dict_keys_size() && i < value.dict_values_size();
++i) {
if (i > 0)
ss << ",";
ss << value.dict_keys(i);
ss << ":";
WriteAsJSON(value.dict_values(i), ss);
}
ss << "}";
return;
} else if (value.nested_type() ==
protos::DebugAnnotation_NestedValue_NestedType_ARRAY) {
ss << "[";
for (int i = 0; i < value.array_values_size(); ++i) {
if (i > 0)
ss << ",";
WriteAsJSON(value.array_values(i), ss);
}
ss << "]";
return;
} else if (value.has_int_value()) {
ss << value.int_value();
return;
} else if (value.has_double_value()) {
ss << value.double_value();
return;
} else if (value.has_bool_value()) {
ss << static_cast<bool>(value.bool_value());
return;
} else if (value.has_string_value()) {
ss << value.string_value();
return;
}
}
void WriteAsJSON(const protos::DebugAnnotation& value, std::stringstream& ss) {
if (value.has_bool_value()) {
ss << static_cast<bool>(value.bool_value());
return;
} else if (value.has_uint_value()) {
ss << value.uint_value();
return;
} else if (value.has_int_value()) {
ss << value.int_value();
return;
} else if (value.has_double_value()) {
ss << value.double_value();
return;
} else if (value.has_string_value()) {
ss << value.string_value();
return;
} else if (value.has_pointer_value()) {
ss << value.pointer_value();
return;
} else if (value.has_nested_value()) {
WriteAsJSON(value.nested_value(), ss);
return;
} else if (value.has_legacy_json_value()) {
ss << value.legacy_json_value();
return;
}
}
std::string MessageToJSON(const std::string& data) {
std::stringstream ss;
protos::DebugAnnotation result;
result.ParseFromString(data);
WriteAsJSON(result, ss);
return ss.str();
}
} // namespace
TEST(TracedValueTest, FlatDictionary_Explicit) {
protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
{
auto dict = TracedValue::CreateForTest(message.get()).WriteDictionary();
dict.AddItem("bool").WriteBoolean(true);
dict.AddItem("double").WriteDouble(0.0);
dict.AddItem("int").WriteInt64(2014);
dict.AddItem("string").WriteString("string");
dict.AddItem("ptr").WritePointer(reinterpret_cast<void*>(0x1234));
}
EXPECT_EQ("{bool:1,double:0,int:2014,string:string,ptr:4660}",
MessageToJSON(message.SerializeAsString()));
}
TEST(TracedValueTest, Hierarchy_Explicit) {
protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
{
auto root_dict =
TracedValue::CreateForTest(message.get()).WriteDictionary();
{
auto array = root_dict.AddItem("a1").WriteArray();
array.AppendItem().WriteInt64(1);
array.AppendItem().WriteBoolean(true);
{
auto dict = array.AppendItem().WriteDictionary();
dict.AddItem("i2").WriteInt64(3);
}
}
root_dict.AddItem("b0").WriteBoolean(true);
root_dict.AddItem("d0").WriteDouble(0.0);
{
auto dict1 = root_dict.AddItem("dict1").WriteDictionary();
{
auto dict2 = dict1.AddItem("dict2").WriteDictionary();
dict2.AddItem("b2").WriteBoolean(false);
}
dict1.AddItem("i1").WriteInt64(2014);
dict1.AddItem("s1").WriteString("foo");
}
root_dict.AddItem("i0").WriteInt64(2014);
root_dict.AddItem("s0").WriteString("foo");
}
EXPECT_EQ(
"{"
"a1:[1,1,{i2:3}],"
"b0:1,"
"d0:0,"
"dict1:{dict2:{b2:0},i1:2014,s1:foo},"
"i0:2014,"
"s0:foo}",
MessageToJSON(message.SerializeAsString()));
}
#if PERFETTO_DCHECK_IS_ON()
// This death test makes sense only when dchecks are enabled.
TEST(TracedValueTest, FailOnIncorrectUsage) {
// A new call to AddItem is not allowed before the previous result is
// consumed.
EXPECT_DEATH(
{
protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
auto dict = TracedValue::CreateForTest(message.get()).WriteDictionary();
auto scope1 = dict.AddItem("key1");
auto scope2 = dict.AddItem("key2");
std::move(scope1).WriteInt64(1);
std::move(scope2).WriteInt64(2);
},
"");
// A new call to AppendItem is not allowed before the previous result is
// consumed.
EXPECT_DEATH(
{
protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
auto array = TracedValue::CreateForTest(message.get()).WriteArray();
auto scope1 = array.AppendItem();
auto scope2 = array.AppendItem();
std::move(scope1).WriteInt64(1);
std::move(scope2).WriteInt64(2);
},
"");
// Writing to parent scope is not allowed.
EXPECT_DEATH(
{
protozero::HeapBuffered<protos::pbzero::DebugAnnotation> message;
auto outer_dict =
TracedValue::CreateForTest(message.get()).WriteDictionary();
{
auto inner_dict = outer_dict.AddDictionary("inner");
outer_dict.AddItem("key").WriteString("value");
}
},
"");
}
#endif // PERFETTO_DCHECK_IS_ON()
} // namespace perfetto