1 // Copyright 2016 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/tracing/traced-value.h"
6
7 #include "src/base/platform/platform.h"
8 #include "src/numbers/conversions.h"
9 #include "src/utils/vector.h"
10
11 #ifdef V8_USE_PERFETTO
12 #include "protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
13 #endif
14
15 namespace v8 {
16 namespace tracing {
17
18 namespace {
19
20 #define DCHECK_CURRENT_CONTAINER_IS(x) DCHECK_EQ(x, nesting_stack_.back())
21 #define DCHECK_CONTAINER_STACK_DEPTH_EQ(x) DCHECK_EQ(x, nesting_stack_.size())
22 #ifdef DEBUG
23 const bool kStackTypeDict = false;
24 const bool kStackTypeArray = true;
25 #define DEBUG_PUSH_CONTAINER(x) nesting_stack_.push_back(x)
26 #define DEBUG_POP_CONTAINER() nesting_stack_.pop_back()
27 #else
28 #define DEBUG_PUSH_CONTAINER(x) ((void)0)
29 #define DEBUG_POP_CONTAINER() ((void)0)
30 #endif
31
EscapeAndAppendString(const char * value,std::string * result)32 void EscapeAndAppendString(const char* value, std::string* result) {
33 *result += '"';
34 while (*value) {
35 unsigned char c = *value++;
36 switch (c) {
37 case '\b':
38 *result += "\\b";
39 break;
40 case '\f':
41 *result += "\\f";
42 break;
43 case '\n':
44 *result += "\\n";
45 break;
46 case '\r':
47 *result += "\\r";
48 break;
49 case '\t':
50 *result += "\\t";
51 break;
52 case '\"':
53 *result += "\\\"";
54 break;
55 case '\\':
56 *result += "\\\\";
57 break;
58 default:
59 if (c < '\x20' || c == '\x7F') {
60 char number_buffer[8];
61 base::OS::SNPrintF(number_buffer, arraysize(number_buffer), "\\u%04X",
62 static_cast<unsigned>(c));
63 *result += number_buffer;
64 } else {
65 *result += c;
66 }
67 }
68 }
69 *result += '"';
70 }
71
72 } // namespace
73
74 // static
Create()75 std::unique_ptr<TracedValue> TracedValue::Create() {
76 return std::unique_ptr<TracedValue>(new TracedValue());
77 }
78
TracedValue()79 TracedValue::TracedValue() : first_item_(true) {
80 DEBUG_PUSH_CONTAINER(kStackTypeDict);
81 }
82
~TracedValue()83 TracedValue::~TracedValue() {
84 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
85 DEBUG_POP_CONTAINER();
86 DCHECK_CONTAINER_STACK_DEPTH_EQ(0u);
87 }
88
SetInteger(const char * name,int value)89 void TracedValue::SetInteger(const char* name, int value) {
90 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
91 WriteName(name);
92 data_ += std::to_string(value);
93 }
94
SetDouble(const char * name,double value)95 void TracedValue::SetDouble(const char* name, double value) {
96 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
97 WriteName(name);
98 i::EmbeddedVector<char, 100> buffer;
99 data_ += DoubleToCString(value, buffer);
100 }
101
SetBoolean(const char * name,bool value)102 void TracedValue::SetBoolean(const char* name, bool value) {
103 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
104 WriteName(name);
105 data_ += value ? "true" : "false";
106 }
107
SetString(const char * name,const char * value)108 void TracedValue::SetString(const char* name, const char* value) {
109 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
110 WriteName(name);
111 EscapeAndAppendString(value, &data_);
112 }
113
SetValue(const char * name,TracedValue * value)114 void TracedValue::SetValue(const char* name, TracedValue* value) {
115 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
116 WriteName(name);
117 std::string tmp;
118 value->AppendAsTraceFormat(&tmp);
119 data_ += tmp;
120 }
121
BeginDictionary(const char * name)122 void TracedValue::BeginDictionary(const char* name) {
123 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
124 DEBUG_PUSH_CONTAINER(kStackTypeDict);
125 WriteName(name);
126 data_ += '{';
127 first_item_ = true;
128 }
129
BeginArray(const char * name)130 void TracedValue::BeginArray(const char* name) {
131 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
132 DEBUG_PUSH_CONTAINER(kStackTypeArray);
133 WriteName(name);
134 data_ += '[';
135 first_item_ = true;
136 }
137
AppendInteger(int value)138 void TracedValue::AppendInteger(int value) {
139 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
140 WriteComma();
141 data_ += std::to_string(value);
142 }
143
AppendDouble(double value)144 void TracedValue::AppendDouble(double value) {
145 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
146 WriteComma();
147 i::EmbeddedVector<char, 100> buffer;
148 data_ += DoubleToCString(value, buffer);
149 }
150
AppendBoolean(bool value)151 void TracedValue::AppendBoolean(bool value) {
152 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
153 WriteComma();
154 data_ += value ? "true" : "false";
155 }
156
AppendString(const char * value)157 void TracedValue::AppendString(const char* value) {
158 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
159 WriteComma();
160 EscapeAndAppendString(value, &data_);
161 }
162
BeginDictionary()163 void TracedValue::BeginDictionary() {
164 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
165 DEBUG_PUSH_CONTAINER(kStackTypeDict);
166 WriteComma();
167 data_ += '{';
168 first_item_ = true;
169 }
170
BeginArray()171 void TracedValue::BeginArray() {
172 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
173 DEBUG_PUSH_CONTAINER(kStackTypeArray);
174 WriteComma();
175 data_ += '[';
176 first_item_ = true;
177 }
178
EndDictionary()179 void TracedValue::EndDictionary() {
180 DCHECK_CURRENT_CONTAINER_IS(kStackTypeDict);
181 DEBUG_POP_CONTAINER();
182 data_ += '}';
183 first_item_ = false;
184 }
185
EndArray()186 void TracedValue::EndArray() {
187 DCHECK_CURRENT_CONTAINER_IS(kStackTypeArray);
188 DEBUG_POP_CONTAINER();
189 data_ += ']';
190 first_item_ = false;
191 }
192
WriteComma()193 void TracedValue::WriteComma() {
194 if (first_item_) {
195 first_item_ = false;
196 } else {
197 data_ += ',';
198 }
199 }
200
WriteName(const char * name)201 void TracedValue::WriteName(const char* name) {
202 WriteComma();
203 data_ += '"';
204 data_ += name;
205 data_ += "\":";
206 }
207
AppendAsTraceFormat(std::string * out) const208 void TracedValue::AppendAsTraceFormat(std::string* out) const {
209 *out += '{';
210 *out += data_;
211 *out += '}';
212 }
213
214 #ifdef V8_USE_PERFETTO
Add(perfetto::protos::pbzero::DebugAnnotation * annotation) const215 void TracedValue::Add(
216 perfetto::protos::pbzero::DebugAnnotation* annotation) const {
217 std::string json;
218 json += "{";
219 json += data_;
220 json += "}";
221 annotation->set_legacy_json_value(json);
222 }
223 #endif // V8_USE_PERFETTO
224
225 } // namespace tracing
226 } // namespace v8
227