aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLalit Maganti <lalitm@google.com>2021-09-03 16:13:39 +0100
committerLalit Maganti <lalitm@google.com>2021-09-03 16:13:39 +0100
commit0639e91232e12e5e39c7281cd14648cea0b19bc7 (patch)
tree2a86b0b7a79c23481f542c9d6030eb9e7254a391
parentc069e0130087606a5703005ca7a06e5b9c4bd4c4 (diff)
downloadperfetto-0639e91232e12e5e39c7281cd14648cea0b19bc7.tar.gz
tp: fix textproto and JSON escaping for metrics output
JSON and textproto do not have the same escaping rules so split the functions and fix both implementations to be more correct. Change-Id: I355f8680e817e9b934b729718d0b214e449606d9 Bug: 198692199
-rw-r--r--include/perfetto/ext/base/string_utils.h2
-rw-r--r--src/base/string_utils.cc36
-rw-r--r--src/trace_processor/util/proto_to_json.cc30
-rw-r--r--src/trace_processor/util/protozero_to_text.cc59
4 files changed, 86 insertions, 41 deletions
diff --git a/include/perfetto/ext/base/string_utils.h b/include/perfetto/ext/base/string_utils.h
index a44ea735b..5b687eb7f 100644
--- a/include/perfetto/ext/base/string_utils.h
+++ b/include/perfetto/ext/base/string_utils.h
@@ -29,8 +29,6 @@
namespace perfetto {
namespace base {
-std::string QuoteAndEscapeControlCodes(const std::string& raw);
-
inline char Lowercase(char c) {
return ('A' <= c && c <= 'Z') ? static_cast<char>(c - ('A' - 'a')) : c;
}
diff --git a/src/base/string_utils.cc b/src/base/string_utils.cc
index a6ee8335e..9d5ae249a 100644
--- a/src/base/string_utils.cc
+++ b/src/base/string_utils.cc
@@ -48,42 +48,6 @@ double StrToD(const char* nptr, char** endptr) {
#endif
}
-std::string QuoteAndEscapeControlCodes(const std::string& raw) {
- std::string ret;
- for (auto it = raw.cbegin(); it != raw.cend(); it++) {
- switch (*it) {
- case '\\':
- ret += "\\\\";
- break;
- case '"':
- ret += "\\\"";
- break;
- case '/':
- ret += "\\/";
- break;
- case '\b':
- ret += "\\b";
- break;
- case '\f':
- ret += "\\f";
- break;
- case '\n':
- ret += "\\n";
- break;
- case '\r':
- ret += "\\r";
- break;
- case '\t':
- ret += "\\t";
- break;
- default:
- ret += *it;
- break;
- }
- }
- return '"' + ret + '"';
-}
-
bool StartsWith(const std::string& str, const std::string& prefix) {
return str.compare(0, prefix.length(), prefix) == 0;
}
diff --git a/src/trace_processor/util/proto_to_json.cc b/src/trace_processor/util/proto_to_json.cc
index ff430c79f..ace8e35bc 100644
--- a/src/trace_processor/util/proto_to_json.cc
+++ b/src/trace_processor/util/proto_to_json.cc
@@ -30,6 +30,32 @@ namespace proto_to_json {
namespace {
+std::string QuoteAndEscapeJsonString(const std::string& raw) {
+ std::string ret;
+ for (auto it = raw.cbegin(); it != raw.cend(); it++) {
+ char c = *it;
+ if (c < 0x20) {
+ // All 32 ASCII control codes need to be escaped. Instead of using the
+ // short forms, we just always use \u escape sequences instead to make
+ // things simpler.
+ ret += "\\u00";
+
+ // Print |c| as a hex character. We reserve 3 bytes of space: 2 for the
+ // hex code and one for the null terminator.
+ char buf[3];
+ snprintf(buf, sizeof(buf), "%02X", c);
+ ret += buf;
+ } else if (c == '"') {
+ // Double quote also needs to be escaped.
+ ret += "\\\"";
+ } else {
+ // Everything else can be passed through directly.
+ ret += c;
+ }
+ }
+ return '"' + ret + '"';
+}
+
std::string FieldToJson(const google::protobuf::Message& message,
const google::protobuf::FieldDescriptor* field_desc,
int idx,
@@ -44,7 +70,7 @@ std::string FieldToJson(const google::protobuf::Message& message,
? ref->GetRepeatedBool(message, field_desc, idx)
: ref->GetBool(message, field_desc));
case FieldDescriptor::CppType::CPPTYPE_ENUM:
- return base::QuoteAndEscapeControlCodes(
+ return QuoteAndEscapeJsonString(
is_repeated ? ref->GetRepeatedEnum(message, field_desc, idx)->name()
: ref->GetEnum(message, field_desc)->name());
case FieldDescriptor::CppType::CPPTYPE_FLOAT:
@@ -66,7 +92,7 @@ std::string FieldToJson(const google::protobuf::Message& message,
is_repeated ? ref->GetRepeatedDouble(message, field_desc, idx)
: ref->GetDouble(message, field_desc));
case FieldDescriptor::CppType::CPPTYPE_STRING:
- return base::QuoteAndEscapeControlCodes(
+ return QuoteAndEscapeJsonString(
is_repeated ? ref->GetRepeatedString(message, field_desc, idx)
: ref->GetString(message, field_desc));
case FieldDescriptor::CppType::CPPTYPE_UINT32:
diff --git a/src/trace_processor/util/protozero_to_text.cc b/src/trace_processor/util/protozero_to_text.cc
index a070a721c..ce6b9737a 100644
--- a/src/trace_processor/util/protozero_to_text.cc
+++ b/src/trace_processor/util/protozero_to_text.cc
@@ -30,6 +30,63 @@ std::string BytesToHexEncodedString(const std::string& bytes) {
return value;
}
+// This function matches the implementation of TextFormatEscaper.escapeBytes
+// from the Java protobuf library.
+std::string QuoteAndEscapeTextProtoString(const std::string& raw) {
+ std::string ret;
+ for (auto it = raw.cbegin(); it != raw.cend(); it++) {
+ switch (*it) {
+ case '\a':
+ ret += "\\a";
+ break;
+ case '\b':
+ ret += "\\b";
+ break;
+ case '\f':
+ ret += "\\f";
+ break;
+ case '\n':
+ ret += "\\n";
+ break;
+ case '\r':
+ ret += "\\r";
+ break;
+ case '\t':
+ ret += "\\t";
+ break;
+ case '\v':
+ ret += "\\v";
+ break;
+ case '\\':
+ ret += "\\\\";
+ break;
+ case '\'':
+ ret += "\\\'";
+ break;
+ case '"':
+ ret += "\\\"";
+ break;
+ default:
+ // Only ASCII characters between 0x20 (space) and 0x7e (tilde) are
+ // printable; other byte values are escaped with 3-character octal
+ // codes.
+ if (*it >= 0x20 && *it <= 0x7e) {
+ ret += *it;
+ } else {
+ ret += '\\';
+
+ // Cast to unsigned char to make the right shift unsigned as well.
+ unsigned char c = static_cast<unsigned char>(*it);
+ ret += ('0' + ((c >> 6) & 3));
+ ret += ('0' + ((c >> 3) & 7));
+ ret += ('0' + (c & 7));
+ }
+ break;
+ }
+ }
+ return '"' + ret + '"';
+}
+
// Recursively determine the size of all the string like things passed in the
// parameter pack |rest|.
size_t SizeOfStr() {
@@ -115,7 +172,7 @@ void ConvertProtoTypeToFieldAndValueString(const FieldDescriptor& fd,
std::to_string(field.as_float()));
return;
case FieldDescriptorProto::TYPE_STRING: {
- auto s = base::QuoteAndEscapeControlCodes(field.as_std_string());
+ auto s = QuoteAndEscapeTextProtoString(field.as_std_string());
StrAppend(out, separator, indent, fd.name(), ": ", s);
return;
}