Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "proto_zero_generator.h" | 5 #include "proto_zero_generator.h" |
| 6 | 6 |
| 7 #include <map> | 7 #include <map> |
| 8 #include <memory> | 8 #include <memory> |
| 9 #include <set> | |
| 9 #include <string> | 10 #include <string> |
| 10 | 11 |
| 11 #include "third_party/protobuf/src/google/protobuf/descriptor.h" | 12 #include "third_party/protobuf/src/google/protobuf/descriptor.h" |
| 12 #include "third_party/protobuf/src/google/protobuf/io/printer.h" | 13 #include "third_party/protobuf/src/google/protobuf/io/printer.h" |
| 13 #include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h" | 14 #include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h" |
| 14 #include "third_party/protobuf/src/google/protobuf/stubs/strutil.h" | 15 #include "third_party/protobuf/src/google/protobuf/stubs/strutil.h" |
| 15 | 16 |
| 16 namespace tracing { | 17 namespace tracing { |
| 17 namespace proto { | 18 namespace proto { |
| 18 | 19 |
| 19 using google::protobuf::Descriptor; // Message descriptor. | 20 using google::protobuf::Descriptor; // Message descriptor. |
| 20 using google::protobuf::EnumDescriptor; | 21 using google::protobuf::EnumDescriptor; |
| 21 using google::protobuf::EnumValueDescriptor; | 22 using google::protobuf::EnumValueDescriptor; |
| 22 using google::protobuf::FieldDescriptor; | 23 using google::protobuf::FieldDescriptor; |
| 23 using google::protobuf::FileDescriptor; | 24 using google::protobuf::FileDescriptor; |
| 24 using google::protobuf::compiler::GeneratorContext; | 25 using google::protobuf::compiler::GeneratorContext; |
| 25 using google::protobuf::io::Printer; | 26 using google::protobuf::io::Printer; |
| 26 using google::protobuf::io::ZeroCopyOutputStream; | 27 using google::protobuf::io::ZeroCopyOutputStream; |
| 27 | 28 |
| 28 using google::protobuf::Split; | 29 using google::protobuf::Split; |
| 29 using google::protobuf::StripPrefixString; | 30 using google::protobuf::StripPrefixString; |
| 30 using google::protobuf::StripString; | 31 using google::protobuf::StripString; |
| 31 using google::protobuf::StripSuffixString; | 32 using google::protobuf::StripSuffixString; |
| 32 using google::protobuf::UpperString; | 33 using google::protobuf::UpperString; |
| 33 | 34 |
| 34 namespace { | 35 namespace { |
| 35 | 36 |
| 37 inline std::string ProtoStubName(const FileDescriptor* proto) { | |
| 38 return StripSuffixString(proto->name(), ".proto") + ".pbzero"; | |
| 39 } | |
| 40 | |
| 36 class GeneratorJob { | 41 class GeneratorJob { |
| 37 public: | 42 public: |
| 38 GeneratorJob(const FileDescriptor *file, | 43 GeneratorJob(const FileDescriptor *file, |
| 39 Printer* stub_h_printer, | 44 Printer* stub_h_printer, |
| 40 Printer* stub_cc_printer) | 45 Printer* stub_cc_printer) |
| 41 : source_(file), | 46 : source_(file), |
| 42 stub_h_(stub_h_printer), | 47 stub_h_(stub_h_printer), |
| 43 stub_cc_(stub_cc_printer) {} | 48 stub_cc_(stub_cc_printer) {} |
| 44 | 49 |
| 45 bool GenerateStubs() { | 50 bool GenerateStubs() { |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 95 const EnumDescriptor* enumeration = field->enum_type(); | 100 const EnumDescriptor* enumeration = field->enum_type(); |
| 96 | 101 |
| 97 for (int i = 0; i < enumeration->value_count(); ++i) { | 102 for (int i = 0; i < enumeration->value_count(); ++i) { |
| 98 int32_t value = enumeration->value(i)->number(); | 103 int32_t value = enumeration->value(i)->number(); |
| 99 if (value < 0 || value > 0x7F) | 104 if (value < 0 || value > 0x7F) |
| 100 return false; | 105 return false; |
| 101 } | 106 } |
| 102 return true; | 107 return true; |
| 103 } | 108 } |
| 104 | 109 |
| 105 void Preprocess() { | 110 void CollectDescriptors() { |
| 106 // Package name maps to a series of namespaces. | |
| 107 package_ = source_->package(); | |
| 108 namespaces_ = Split(package_, "."); | |
| 109 full_namespace_prefix_ = "::"; | |
| 110 for (const std::string& ns : namespaces_) | |
| 111 full_namespace_prefix_ += ns + "::"; | |
| 112 | |
| 113 // Collect message descriptors in DFS order. | 111 // Collect message descriptors in DFS order. |
| 114 std::vector<const Descriptor*> stack; | 112 std::vector<const Descriptor*> stack; |
| 115 for (int i = 0; i < source_->message_type_count(); ++i) | 113 for (int i = 0; i < source_->message_type_count(); ++i) |
| 116 stack.push_back(source_->message_type(i)); | 114 stack.push_back(source_->message_type(i)); |
| 117 | 115 |
| 118 while (!stack.empty()) { | 116 while (!stack.empty()) { |
| 119 const Descriptor* message = stack.back(); | 117 const Descriptor* message = stack.back(); |
| 120 stack.pop_back(); | 118 stack.pop_back(); |
| 121 messages_.push_back(message); | 119 messages_.push_back(message); |
| 122 for (int i = 0; i < message->nested_type_count(); ++i) { | 120 for (int i = 0; i < message->nested_type_count(); ++i) { |
| 123 stack.push_back(message->nested_type(i)); | 121 stack.push_back(message->nested_type(i)); |
| 124 } | 122 } |
| 125 } | 123 } |
| 126 | 124 |
| 127 // Collect enums. | 125 // Collect enums. |
| 128 for (int i = 0; i < source_->enum_type_count(); ++i) | 126 for (int i = 0; i < source_->enum_type_count(); ++i) |
| 129 enums_.push_back(source_->enum_type(i)); | 127 enums_.push_back(source_->enum_type(i)); |
| 130 | 128 |
| 131 for (const Descriptor* message : messages_) { | 129 for (const Descriptor* message : messages_) { |
| 132 for (int i = 0; i < message->enum_type_count(); ++i) { | 130 for (int i = 0; i < message->enum_type_count(); ++i) { |
| 133 enums_.push_back(message->enum_type(i)); | 131 enums_.push_back(message->enum_type(i)); |
| 134 } | 132 } |
| 135 } | 133 } |
| 136 } | 134 } |
| 137 | 135 |
| 136 void CollectDependencies() { | |
| 137 // Public import basically means that callers only need to import this | |
| 138 // proto in order to use the stuff publicly imported by this proto. | |
| 139 for (int i = 0; i < source_->public_dependency_count(); ++i) | |
| 140 public_imports_.insert(source_->public_dependency(i)); | |
| 141 | |
| 142 if (source_->weak_dependency_count() > 0) | |
| 143 Abort("Weak imports are not supported."); | |
| 144 | |
| 145 // Sanity check. Collect public imports (of collected imports) in DFS order. | |
| 146 // Visibilty for current proto: | |
| 147 // - all imports listed in current proto, | |
| 148 // - public imports of everything imported (recursive). | |
| 149 std::vector<const FileDescriptor*> stack; | |
|
Primiano Tucci (use gerrit)
2016/08/04 13:32:04
in general you want to name variables according to
kraynov
2016/08/04 13:42:48
It think it's okay since it's part of standard DFS
| |
| 150 for (int i = 0; i < source_->dependency_count(); ++i) { | |
| 151 const FileDescriptor* proto = source_->dependency(i); | |
| 152 stack.push_back(proto); | |
| 153 if (public_imports_.find(proto) == public_imports_.end()) { | |
| 154 private_imports_.insert(proto); | |
| 155 } | |
| 156 } | |
| 157 | |
| 158 while (!stack.empty()) { | |
| 159 const FileDescriptor* proto = stack.back(); | |
| 160 stack.pop_back(); | |
| 161 // Having imports under different packages leads to unnecessary | |
| 162 // complexity with namespaces. | |
| 163 if (proto->package() != package_) | |
| 164 Abort("Imported proto must be in the same package."); | |
| 165 | |
| 166 for (int i = 0; i < proto->public_dependency_count(); ++i) { | |
| 167 stack.push_back(proto->public_dependency(i)); | |
| 168 } | |
| 169 } | |
| 170 | |
| 171 // Collect descriptors of messages and enums used in current proto. | |
| 172 // It will be used to generate necessary forward declarations and performed | |
| 173 // sanity check guarantees that everything lays in the same namespace. | |
| 174 for (const Descriptor* message : messages_) { | |
| 175 for (int i = 0; i < message->field_count(); ++i) { | |
| 176 const FieldDescriptor* field = message->field(i); | |
| 177 | |
| 178 if (field->type() == FieldDescriptor::TYPE_MESSAGE) { | |
| 179 if (public_imports_.find(field->message_type()->file()) == | |
|
Primiano Tucci (use gerrit)
2016/08/04 13:32:04
.count(...) == 0
kraynov
2016/08/04 13:42:48
Acknowledged.
| |
| 180 public_imports_.end()) { | |
| 181 // Avoid multiple forward declarations. | |
|
Primiano Tucci (use gerrit)
2016/08/04 13:32:04
not sure this comment adds anything useful. same b
kraynov
2016/08/04 13:42:48
Ok, will change the wording. I meant that definiti
| |
| 182 referenced_messages_.insert(field->message_type()); | |
| 183 } | |
| 184 } else if (field->type() == FieldDescriptor::TYPE_ENUM) { | |
| 185 if (public_imports_.find(field->enum_type()->file()) == | |
| 186 public_imports_.end()) { | |
| 187 // Avoid multiple forward declarations. | |
| 188 referenced_enums_.insert(field->enum_type()); | |
| 189 } | |
| 190 } | |
| 191 } | |
| 192 } | |
| 193 } | |
| 194 | |
| 195 void Preprocess() { | |
| 196 // Package name maps to a series of namespaces. | |
| 197 package_ = source_->package(); | |
| 198 namespaces_ = Split(package_, "."); | |
| 199 full_namespace_prefix_ = "::"; | |
| 200 for (const std::string& ns : namespaces_) | |
| 201 full_namespace_prefix_ += ns + "::"; | |
| 202 CollectDescriptors(); | |
| 203 CollectDependencies(); | |
| 204 } | |
| 205 | |
| 138 // Print top header, namespaces and forward declarations. | 206 // Print top header, namespaces and forward declarations. |
| 139 void GeneratePrologue() { | 207 void GeneratePrologue() { |
| 140 std::string greeting = | 208 std::string greeting = |
| 141 "// Autogenerated. DO NOT EDIT.\n" | 209 "// Autogenerated. DO NOT EDIT.\n" |
| 142 "// Protobuf compiler (protoc) has generated these stubs with\n" | 210 "// Protobuf compiler (protoc) has generated these stubs with\n" |
| 143 "// //components/tracing/tools/proto_zero_plugin.\n"; | 211 "// //components/tracing/tools/proto_zero_plugin.\n"; |
| 144 std::string guard = package_ + "_" + source_->name() + "_H_"; | 212 std::string guard = package_ + "_" + source_->name() + "_H_"; |
| 145 UpperString(&guard); | 213 UpperString(&guard); |
| 146 StripString(&guard, ".-", '_'); | 214 StripString(&guard, ".-", '_'); |
| 147 | 215 |
| 148 stub_h_->Print( | 216 stub_h_->Print( |
| 149 "$greeting$\n" | 217 "$greeting$\n" |
| 150 "#ifndef $guard$\n" | 218 "#ifndef $guard$\n" |
| 151 "#define $guard$\n\n" | 219 "#define $guard$\n\n" |
| 152 "#include <stddef.h>\n" | 220 "#include <stddef.h>\n" |
| 153 "#include <stdint.h>\n\n" | 221 "#include <stdint.h>\n\n" |
| 154 "#include \"components/tracing/core/proto_zero_message.h\"\n\n", | 222 "#include \"components/tracing/core/proto_zero_message.h\"\n", |
| 155 "greeting", greeting, | 223 "greeting", greeting, |
| 156 "guard", guard); | 224 "guard", guard); |
| 157 stub_cc_->Print( | 225 stub_cc_->Print( |
| 158 "$greeting$\n" | 226 "$greeting$\n" |
| 159 "// This file intentionally left blank.\n", | 227 "#include \"$name$.h\"\n", |
| 160 "greeting", greeting); | 228 "greeting", greeting, |
| 229 "name", ProtoStubName(source_)); | |
| 230 | |
| 231 // Print includes for public imports. | |
| 232 for (const FileDescriptor* dependency : public_imports_) { | |
| 233 // Dependency name could contatin slashes but importing from upper-level | |
| 234 // directories is not possible anyway since build system process each | |
| 235 // proto file individually. Hence proto lookup path always equal to the | |
| 236 // directory where particular proto file is located and protoc does not | |
| 237 // allow reference to upper directory (aka ..) in import path. | |
| 238 // | |
| 239 // Laconically said: | |
| 240 // - source_->name() may never have slashes, | |
| 241 // - dependency->name() may have slashes but always reffers to inner path. | |
| 242 stub_h_->Print( | |
| 243 "#include \"$name$.h\"\n", | |
| 244 "name", ProtoStubName(dependency)); | |
| 245 } | |
| 246 stub_h_->Print("\n"); | |
| 247 | |
| 248 // Print includes for private imports to .cc file. | |
| 249 for (const FileDescriptor* dependency : private_imports_) { | |
| 250 stub_cc_->Print( | |
| 251 "#include \"$name$.h\"\n", | |
| 252 "name", ProtoStubName(dependency)); | |
| 253 } | |
| 254 stub_cc_->Print("\n"); | |
| 161 | 255 |
| 162 // Print namespaces. | 256 // Print namespaces. |
| 163 for (const std::string& ns : namespaces_) | 257 for (const std::string& ns : namespaces_) { |
| 164 stub_h_->Print("namespace $ns$ {\n", "ns", ns); | 258 stub_h_->Print("namespace $ns$ {\n", "ns", ns); |
| 259 stub_cc_->Print("namespace $ns$ {\n", "ns", ns); | |
| 260 } | |
| 165 stub_h_->Print("\n"); | 261 stub_h_->Print("\n"); |
| 262 stub_cc_->Print("\n"); | |
| 263 | |
| 166 // Print forward declarations. | 264 // Print forward declarations. |
| 167 for (const Descriptor* message : messages_) { | 265 for (const Descriptor* message : referenced_messages_) { |
| 168 stub_h_->Print( | 266 stub_h_->Print( |
| 169 "class $class$;\n", | 267 "class $class$;\n", |
| 170 "class", GetCppClassName(message)); | 268 "class", GetCppClassName(message)); |
| 171 } | 269 } |
| 270 for (const EnumDescriptor* enumeration : referenced_enums_) { | |
| 271 stub_h_->Print( | |
| 272 "enum $class$ : int32_t;\n", | |
| 273 "class", GetCppClassName(enumeration)); | |
| 274 } | |
| 172 stub_h_->Print("\n"); | 275 stub_h_->Print("\n"); |
| 173 } | 276 } |
| 174 | 277 |
| 175 void GenerateEnumDescriptor(const EnumDescriptor* enumeration) { | 278 void GenerateEnumDescriptor(const EnumDescriptor* enumeration) { |
| 176 stub_h_->Print( | 279 stub_h_->Print( |
| 177 "enum $class$ {\n", | 280 "enum $class$ : int32_t {\n", |
| 178 "class", GetCppClassName(enumeration)); | 281 "class", GetCppClassName(enumeration)); |
| 179 stub_h_->Indent(); | 282 stub_h_->Indent(); |
| 180 | 283 |
| 181 std::string value_name_prefix; | 284 std::string value_name_prefix; |
| 182 if (enumeration->containing_type() != nullptr) | 285 if (enumeration->containing_type() != nullptr) |
| 183 value_name_prefix = GetCppClassName(enumeration) + "_"; | 286 value_name_prefix = GetCppClassName(enumeration) + "_"; |
| 184 | 287 |
| 185 for (int i = 0; i < enumeration->value_count(); ++i) { | 288 for (int i = 0; i < enumeration->value_count(); ++i) { |
| 186 const EnumValueDescriptor* value = enumeration->value(i); | 289 const EnumValueDescriptor* value = enumeration->value(i); |
| 187 stub_h_->Print( | 290 stub_h_->Print( |
| (...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 295 setter["appender"] = appender; | 398 setter["appender"] = appender; |
| 296 setter["cpp_type"] = cpp_type; | 399 setter["cpp_type"] = cpp_type; |
| 297 stub_h_->Print( | 400 stub_h_->Print( |
| 298 setter, | 401 setter, |
| 299 "void $action$_$name$($cpp_type$ value) {\n" | 402 "void $action$_$name$($cpp_type$ value) {\n" |
| 300 " // $appender$($id$, value);\n" | 403 " // $appender$($id$, value);\n" |
| 301 "}\n"); | 404 "}\n"); |
| 302 } | 405 } |
| 303 | 406 |
| 304 void GenerateNestedMessageFieldDescriptor(const FieldDescriptor* field) { | 407 void GenerateNestedMessageFieldDescriptor(const FieldDescriptor* field) { |
| 408 std::string action = field->is_repeated() ? "add" : "set"; | |
| 409 std::string inner_class = GetCppClassName(field->message_type()); | |
| 410 std::string outer_class = GetCppClassName(field->containing_type()); | |
| 411 | |
| 305 stub_h_->Print( | 412 stub_h_->Print( |
| 306 "$class$* $action$_$name$() {\n" | 413 "$inner_class$* $action$_$name$();\n", |
| 307 " return BeginNestedMessage<$class$>($id$);\n" | 414 "name", field->name(), |
| 308 "}\n", | 415 "action", action, |
| 416 "inner_class", inner_class); | |
| 417 stub_cc_->Print( | |
| 418 "$inner_class$* $outer_class$::$action$_$name$() {\n" | |
| 419 " return BeginNestedMessage<$inner_class$>($id$);\n" | |
| 420 "}\n\n", | |
| 309 "id", std::to_string(field->number()), | 421 "id", std::to_string(field->number()), |
| 310 "name", field->name(), | 422 "name", field->name(), |
| 311 "action", field->is_repeated() ? "add" : "set", | 423 "action", action, |
| 312 "class", GetCppClassName(field->message_type())); | 424 "inner_class", inner_class, |
| 425 "outer_class", outer_class); | |
| 313 } | 426 } |
| 314 | 427 |
| 315 void GenerateMessageDescriptor(const Descriptor* message) { | 428 void GenerateMessageDescriptor(const Descriptor* message) { |
| 316 stub_h_->Print( | 429 stub_h_->Print( |
| 317 "class $name$ : public ::tracing::v2::ProtoZeroMessage {\n" | 430 "class $name$ : public ::tracing::v2::ProtoZeroMessage {\n" |
| 318 " public:\n", | 431 " public:\n", |
| 319 "name", GetCppClassName(message)); | 432 "name", GetCppClassName(message)); |
| 320 stub_h_->Indent(); | 433 stub_h_->Indent(); |
| 321 | 434 |
| 322 // Using statements for nested messages. | 435 // Using statements for nested messages. |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 366 } | 479 } |
| 367 } | 480 } |
| 368 | 481 |
| 369 stub_h_->Outdent(); | 482 stub_h_->Outdent(); |
| 370 stub_h_->Print("};\n\n"); | 483 stub_h_->Print("};\n\n"); |
| 371 } | 484 } |
| 372 | 485 |
| 373 void GenerateEpilogue() { | 486 void GenerateEpilogue() { |
| 374 for (unsigned i = 0; i < namespaces_.size(); ++i) { | 487 for (unsigned i = 0; i < namespaces_.size(); ++i) { |
| 375 stub_h_->Print("} // Namespace.\n"); | 488 stub_h_->Print("} // Namespace.\n"); |
| 489 stub_cc_->Print("} // Namespace.\n"); | |
| 376 } | 490 } |
| 377 stub_h_->Print("#endif // Include guard.\n"); | 491 stub_h_->Print("#endif // Include guard.\n"); |
| 378 } | 492 } |
| 379 | 493 |
| 380 const FileDescriptor* const source_; | 494 const FileDescriptor* const source_; |
| 381 Printer* const stub_h_; | 495 Printer* const stub_h_; |
| 382 Printer* const stub_cc_; | 496 Printer* const stub_cc_; |
| 383 std::string error_; | 497 std::string error_; |
| 384 | 498 |
| 385 std::string package_; | 499 std::string package_; |
| 386 std::vector<std::string> namespaces_; | 500 std::vector<std::string> namespaces_; |
| 387 std::string full_namespace_prefix_; | 501 std::string full_namespace_prefix_; |
| 388 std::vector<const Descriptor*> messages_; | 502 std::vector<const Descriptor*> messages_; |
| 389 std::vector<const EnumDescriptor*> enums_; | 503 std::vector<const EnumDescriptor*> enums_; |
| 504 | |
| 505 std::set<const FileDescriptor*> public_imports_; | |
| 506 std::set<const FileDescriptor*> private_imports_; | |
| 507 std::set<const Descriptor*> referenced_messages_; | |
| 508 std::set<const EnumDescriptor*> referenced_enums_; | |
| 390 }; | 509 }; |
| 391 | 510 |
| 392 } // namespace | 511 } // namespace |
| 393 | 512 |
| 394 ProtoZeroGenerator::ProtoZeroGenerator() { | 513 ProtoZeroGenerator::ProtoZeroGenerator() { |
| 395 } | 514 } |
| 396 | 515 |
| 397 ProtoZeroGenerator::~ProtoZeroGenerator() { | 516 ProtoZeroGenerator::~ProtoZeroGenerator() { |
| 398 } | 517 } |
| 399 | 518 |
| 400 bool ProtoZeroGenerator::Generate(const FileDescriptor* file, | 519 bool ProtoZeroGenerator::Generate(const FileDescriptor* file, |
| 401 const std::string& options, | 520 const std::string& options, |
| 402 GeneratorContext* context, | 521 GeneratorContext* context, |
| 403 std::string* error) const { | 522 std::string* error) const { |
| 404 | 523 |
| 405 const std::string proto_stubs_name = | |
| 406 StripSuffixString(file->name(), ".proto") + ".pbzero"; | |
| 407 | |
| 408 const std::unique_ptr<ZeroCopyOutputStream> stub_h_file_stream( | 524 const std::unique_ptr<ZeroCopyOutputStream> stub_h_file_stream( |
| 409 context->Open(proto_stubs_name + ".h")); | 525 context->Open(ProtoStubName(file) + ".h")); |
| 410 const std::unique_ptr<ZeroCopyOutputStream> stub_cc_file_stream( | 526 const std::unique_ptr<ZeroCopyOutputStream> stub_cc_file_stream( |
| 411 context->Open(proto_stubs_name + ".cc")); | 527 context->Open(ProtoStubName(file) + ".cc")); |
| 412 | 528 |
| 413 // Variables are delimited by $. | 529 // Variables are delimited by $. |
| 414 Printer stub_h_printer(stub_h_file_stream.get(), '$'); | 530 Printer stub_h_printer(stub_h_file_stream.get(), '$'); |
| 415 Printer stub_cc_printer(stub_cc_file_stream.get(), '$'); | 531 Printer stub_cc_printer(stub_cc_file_stream.get(), '$'); |
| 416 | 532 |
| 417 GeneratorJob job(file, &stub_h_printer, &stub_cc_printer); | 533 GeneratorJob job(file, &stub_h_printer, &stub_cc_printer); |
| 418 if (!job.GenerateStubs()) { | 534 if (!job.GenerateStubs()) { |
| 419 *error = job.GetFirstError(); | 535 *error = job.GetFirstError(); |
| 420 return false; | 536 return false; |
| 421 } | 537 } |
| 422 return true; | 538 return true; |
| 423 } | 539 } |
| 424 | 540 |
| 425 } // namespace proto | 541 } // namespace proto |
| 426 } // namespace tracing | 542 } // namespace tracing |
| OLD | NEW |