Add format parser
$ cat tools/ftrace_proto_gen/ion_alloc_buffer_end.format | ./out/linux/ftrace_proto_gen /dev/stdin /dev/stdout
// Autogenerated, do not edit.
syntax = "proto3";
message ion_alloc_buffer_end {
string client_name = 1;
string heap_name = 2;
uint32 len = 3;
uint32 mask = 4;
uint32 flags = 5;
}
To test on a large corpus:
$ wget https://hjd.users.x20web.corp.google.com/www/2017-10-10-perfetto-format-files/testdata.tar.gz
$ tar -zxvf testdata.tar.gz
$ rm testdata.tar.gz
$ find testdata -type f | grep 'format' | while read f; do echo $f; python testdata/test_proto_gen.py out/linux/ftrace_proto_gen out/linux/gcc_like_host/protoc "$f"; done
Change-Id: I8afa0097e681e5bbf4cbbf33501be006e14dc973
diff --git a/BUILD.gn b/BUILD.gn
index 28a22f4..9a222ed 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -17,6 +17,7 @@
deps = [
":tests",
"//buildtools:protobuf_lite",
+ "//tools/ftrace_proto_gen:ftrace_proto_gen",
]
deps += [ "//buildtools:protoc($host_toolchain)" ]
}
@@ -27,5 +28,6 @@
"//libtracing:libtracing_benchmarks",
"//libtracing:libtracing_unittests",
"//libtracing:sanitizers_unittests",
+ "//tools/ftrace_proto_gen:ftrace_proto_gen_unittests",
]
}
diff --git a/testdata/.gitignore b/testdata/.gitignore
new file mode 100644
index 0000000..77c3bb2
--- /dev/null
+++ b/testdata/.gitignore
@@ -0,0 +1,2 @@
+androidone/
+linux/
diff --git a/testdata/test_proto_gen.py b/testdata/test_proto_gen.py
new file mode 100755
index 0000000..104a3a2
--- /dev/null
+++ b/testdata/test_proto_gen.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+# Copyright (C) 2017 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.
+
+from __future__ import print_function
+import sys
+import os
+import subprocess
+import tempfile
+
+def test_command(*args):
+ subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
+
+if __name__ == '__main__':
+ if len(sys.argv) != 4:
+ print('Usage: test_proto_gen.py to/ftrace_proto_gen to/protoc to/format')
+ exit(1)
+ ftrace_proto_gen_path = sys.argv[1]
+ protoc_path = sys.argv[2]
+ format_path = sys.argv[3]
+ tmpdir = tempfile.mkdtemp()
+ proto_path = os.path.join(tmpdir, 'format.proto')
+ test_command(ftrace_proto_gen_path, format_path, proto_path)
+ test_command(
+ protoc_path,
+ proto_path,
+ '--proto_path='+tmpdir,
+ '--cpp_out='+tmpdir)
diff --git a/tools/ftrace_proto_gen/BUILD.gn b/tools/ftrace_proto_gen/BUILD.gn
new file mode 100644
index 0000000..ed6c246
--- /dev/null
+++ b/tools/ftrace_proto_gen/BUILD.gn
@@ -0,0 +1,43 @@
+# Copyright (C) 2017 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.
+
+executable("ftrace_proto_gen_unittests") {
+ testonly = true
+ deps += [
+ ":lib",
+ "//buildtools:gmock",
+ "//buildtools:gtest",
+ "//buildtools:gtest_main",
+ ]
+ sources = [
+ "format_parser_unittest.cc",
+ "ftrace_to_proto_unittest.cc",
+ ]
+}
+
+executable("ftrace_proto_gen") {
+ sources = [
+ "main.cc",
+ ]
+ deps += [ ":lib" ]
+}
+
+source_set("lib") {
+ sources = [
+ "format_parser.cc",
+ "format_parser.h",
+ "ftrace_to_proto.cc",
+ "ftrace_to_proto.h",
+ ]
+}
diff --git a/tools/ftrace_proto_gen/format_parser.cc b/tools/ftrace_proto_gen/format_parser.cc
new file mode 100644
index 0000000..0ce1519
--- /dev/null
+++ b/tools/ftrace_proto_gen/format_parser.cc
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 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 "tools/ftrace_proto_gen/format_parser.h"
+
+#include <string.h>
+
+#include <iosfwd>
+#include <iostream>
+#include <memory>
+#include <vector>
+
+#include "tools/ftrace_proto_gen/ftrace_to_proto.h"
+
+namespace perfetto {
+namespace {
+
+#define MAX_FIELD_LENGTH 127
+#define STRINGIFY(x) STRINGIFY2(x)
+#define STRINGIFY2(x) #x
+
+const char* kCommonFieldPrefix = "common_";
+
+} // namespace
+
+bool ParseFtraceEvent(const std::string& input, FtraceEvent* output) {
+ std::unique_ptr<char[]> input_copy(strdup(input.c_str()));
+ char* s = input_copy.get();
+
+ char buffer[MAX_FIELD_LENGTH + 1];
+
+ bool has_id = false;
+ bool has_name = false;
+
+ int id = 0;
+ std::string name;
+ std::vector<FtraceEvent::Field> fields;
+
+ for (char* line = strtok(s, "\n"); line; line = strtok(nullptr, "\n")) {
+ if (!has_id && sscanf(line, "ID: %d", &id) == 1) {
+ has_id = true;
+ continue;
+ }
+
+ if (!has_name &&
+ sscanf(line, "name: %" STRINGIFY(MAX_FIELD_LENGTH) "s", buffer) == 1) {
+ name = std::string(buffer);
+ has_name = true;
+ continue;
+ }
+
+ if (strcmp("format:", line) == 0) {
+ continue;
+ }
+
+ int offset = 0;
+ int size = 0;
+ int is_signed = 0;
+ if (sscanf(
+ line,
+ "\tfield:%" STRINGIFY(
+ MAX_FIELD_LENGTH) "[^;];\toffset: %d;\tsize: %d;\tsigned: %d;",
+ buffer, &offset, &size, &is_signed) == 4) {
+ std::string type_and_name(buffer);
+
+ // Don't add common fields.
+ if (GetNameFromTypeAndName(type_and_name)
+ .compare(0, strlen(kCommonFieldPrefix), kCommonFieldPrefix) == 0)
+ continue;
+
+ FtraceEvent::Field field{type_and_name, offset, size, is_signed == 1};
+ fields.push_back(field);
+ continue;
+ }
+
+ if (strncmp(line, "print fmt:", 10) == 0) {
+ break;
+ }
+
+ if (output)
+ fprintf(stderr, "Cannot parse line: \"%s\"\n", line);
+ return false;
+ }
+
+ if (!has_id || !has_name || fields.size() == 0) {
+ if (output)
+ fprintf(stderr, "Could not parse format file: %s.\n",
+ !has_id ? "no ID found"
+ : !has_name ? "no name found" : "no fields found");
+ return false;
+ }
+
+ if (!output)
+ return true;
+
+ output->id = id;
+ output->name = name;
+ output->fields = std::move(fields);
+
+ return true;
+}
+
+::std::ostream& operator<<(::std::ostream& os,
+ const FtraceEvent::Field& field) {
+ PrintTo(field, &os);
+ return os;
+}
+
+// Allow gtest to pretty print FtraceEvent::Field.
+void PrintTo(const FtraceEvent::Field& field, ::std::ostream* os) {
+ *os << "FtraceEvent::Field(" << field.type_and_name << ", " << field.offset
+ << ", " << field.size << ", " << field.is_signed << ")";
+}
+
+} // namespace perfetto
diff --git a/tools/ftrace_proto_gen/format_parser.h b/tools/ftrace_proto_gen/format_parser.h
new file mode 100644
index 0000000..317c30a
--- /dev/null
+++ b/tools/ftrace_proto_gen/format_parser.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef TOOLS_FTRACE_PROTO_GEN_FORMAT_PARSER_H_
+#define TOOLS_FTRACE_PROTO_GEN_FORMAT_PARSER_H_
+
+#include <string>
+
+namespace perfetto {
+
+struct FtraceEvent;
+
+bool ParseFtraceEvent(const std::string& input, FtraceEvent* output = nullptr);
+
+} // namespace perfetto
+
+#endif // TOOLS_FTRACE_PROTO_GEN_FORMAT_PARSER_H_
diff --git a/tools/ftrace_proto_gen/format_parser_unittest.cc b/tools/ftrace_proto_gen/format_parser_unittest.cc
new file mode 100644
index 0000000..4238ece
--- /dev/null
+++ b/tools/ftrace_proto_gen/format_parser_unittest.cc
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2017 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 "tools/ftrace_proto_gen/format_parser.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "tools/ftrace_proto_gen/ftrace_to_proto.h"
+
+namespace perfetto {
+namespace {
+
+using testing::ElementsAre;
+using testing::Eq;
+
+TEST(FtraceEventParser, HappyPath) {
+ const std::string input = R"(name: the_name
+ID: 42
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:char client_name[64]; offset:8; size:64; signed:0;
+ field:const char * heap_name; offset:72; size:4; signed:1;
+
+print fmt: "client_name=%s heap_name=%s len=%zu mask=0x%x flags=0x%x", REC->client_name, REC->heap_name, REC->len, REC->mask, REC->flags
+)";
+
+ FtraceEvent output;
+ EXPECT_TRUE(ParseFtraceEvent(input));
+ EXPECT_TRUE(ParseFtraceEvent(input, &output));
+ EXPECT_EQ(output.name, "the_name");
+ EXPECT_EQ(output.id, 42);
+ EXPECT_THAT(
+ output.fields,
+ ElementsAre(
+ Eq(FtraceEvent::Field{"char client_name[64]", 8, 64, false}),
+ Eq(FtraceEvent::Field{"const char * heap_name", 72, 4, true})));
+}
+
+TEST(FtraceEventParser, MissingName) {
+ const std::string input = R"(ID: 42
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+print fmt: "client_name=%s heap_name=%s len=%zu mask=0x%x flags=0x%x", REC->client_name, REC->heap_name, REC->len, REC->mask, REC->flags
+)";
+
+ EXPECT_FALSE(ParseFtraceEvent(input));
+}
+
+TEST(FtraceEventParser, MissingID) {
+ const std::string input = R"(name: the_name
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+print fmt: "client_name=%s heap_name=%s len=%zu mask=0x%x flags=0x%x", REC->client_name, REC->heap_name, REC->len, REC->mask, REC->flags
+)";
+
+ EXPECT_FALSE(ParseFtraceEvent(input));
+}
+
+TEST(FtraceEventParser, NoFeilds) {
+ const std::string input = R"(name: the_name
+ID: 10
+print fmt: "client_name=%s heap_name=%s len=%zu mask=0x%x flags=0x%x", REC->client_name, REC->heap_name, REC->len, REC->mask, REC->flags
+)";
+
+ EXPECT_FALSE(ParseFtraceEvent(input));
+}
+
+TEST(FtraceEventParser, BasicFuzzing) {
+ const std::string input = R"(name: the_name
+ID: 42
+format:
+ field:unsigned short common_type; offset:0; size:2; signed:0;
+ field:unsigned char common_flags; offset:2; size:1; signed:0;
+ field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
+ field:int common_pid; offset:4; size:4; signed:1;
+
+ field:char client_name[64]; offset:8; size:64; signed:0;
+ field:const char * heap_name; offset:72; size:4; signed:0;
+ field:size_t len; offset:76; size:4; signed:0;
+ field:unsigned int mask; offset:80; size:4; signed:0;
+ field:unsigned int flags; offset:84; size:4; signed:0;
+
+print fmt: "client_name=%s heap_name=%s len=%zu mask=0x%x flags=0x%x", REC->client_name, REC->heap_name, REC->len, REC->mask, REC->flags
+)";
+
+ for (size_t i = 0; i < input.length(); i++) {
+ for (size_t j = 1; j < 10 && i + j < input.length(); j++) {
+ std::string copy = input;
+ copy.erase(i, j);
+ ParseFtraceEvent(copy);
+ }
+ }
+}
+
+} // namespace
+} // namespace perfetto
diff --git a/tools/ftrace_proto_gen/ftrace_to_proto.cc b/tools/ftrace_proto_gen/ftrace_to_proto.cc
new file mode 100644
index 0000000..0d1c81c
--- /dev/null
+++ b/tools/ftrace_proto_gen/ftrace_to_proto.cc
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2017 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 "tools/ftrace_proto_gen/ftrace_to_proto.h"
+
+#include <set>
+#include <sstream>
+#include <string>
+#include <vector>
+
+namespace perfetto {
+namespace {
+
+bool IsCIdentifier(const std::string& s) {
+ for (const char c : s) {
+ if (!(std::isalnum(c) || c == '_'))
+ return false;
+ }
+ return s.size() > 0 && !std::isdigit(s[0]);
+}
+
+} // namespace
+
+// For example:
+// "int foo" -> "foo"
+// "u8 foo[(int)sizeof(struct blah)]" -> "foo"
+// "char[] foo[16]" -> "foo"
+// "something_went_wrong" -> ""
+// "" -> ""
+std::string GetNameFromTypeAndName(const std::string& type_and_name) {
+ size_t right = type_and_name.size();
+ if (right == 0)
+ return "";
+
+ if (type_and_name[type_and_name.size() - 1] == ']') {
+ right = type_and_name.rfind('[');
+ if (right == std::string::npos)
+ return "";
+ }
+
+ size_t left = type_and_name.rfind(' ', right);
+ if (left == std::string::npos)
+ return "";
+ left++;
+
+ std::string result = type_and_name.substr(left, right - left);
+ if (!IsCIdentifier(result))
+ return "";
+
+ return result;
+}
+
+std::string InferProtoType(const FtraceEvent::Field& field) {
+ if (field.type_and_name.find("char *") != std::string::npos)
+ return "string";
+ if (field.size <= 4 && field.is_signed)
+ return "int32";
+ if (field.size <= 4 && !field.is_signed)
+ return "uint32";
+ if (field.size <= 8 && field.is_signed)
+ return "int64";
+ if (field.size <= 8 && !field.is_signed)
+ return "uint64";
+ return "";
+}
+
+bool GenerateProto(const FtraceEvent& format, Proto* proto_out) {
+ proto_out->name = format.name;
+ proto_out->fields.reserve(format.fields.size());
+ std::set<std::string> seen;
+ // TODO(hjd): We should be cleverer about id assignment.
+ uint32_t i = 1;
+ for (const FtraceEvent::Field& field : format.fields) {
+ std::string name = GetNameFromTypeAndName(field.type_and_name);
+ // TODO(hjd): Handle dup names.
+ if (name == "" || seen.count(name))
+ continue;
+ seen.insert(name);
+ std::string type = InferProtoType(field);
+ // Check we managed to infer a type.
+ if (type == "")
+ continue;
+ proto_out->fields.emplace_back(Proto::Field{type, name, i});
+ i++;
+ }
+
+ return true;
+}
+
+std::string Proto::ToString() {
+ std::stringstream s;
+ s << "// Autogenerated by " << __FILE__ << " do not edit.\n";
+ s << "syntax = \"proto3\";\n";
+ s << "message " << name << " {\n";
+ for (const Proto::Field& field : fields) {
+ s << " " << field.type << " " << field.name << " = " << field.number
+ << ";\n";
+ }
+ s << "}\n";
+ return s.str();
+}
+
+} // namespace perfetto
diff --git a/tools/ftrace_proto_gen/ftrace_to_proto.h b/tools/ftrace_proto_gen/ftrace_to_proto.h
new file mode 100644
index 0000000..db5a72f
--- /dev/null
+++ b/tools/ftrace_proto_gen/ftrace_to_proto.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef TOOLS_FTRACE_PROTO_GEN_FTRACE_GEN_H_
+#define TOOLS_FTRACE_PROTO_GEN_FTRACE_GEN_H_
+
+#include <stdint.h>
+
+#include <iosfwd>
+#include <iostream>
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace perfetto {
+
+struct FtraceEvent {
+ struct Field {
+ std::string type_and_name;
+ int offset;
+ int size;
+ bool is_signed;
+
+ bool operator==(const Field& other) const {
+ return std::tie(type_and_name, offset, size, is_signed) ==
+ std::tie(other.type_and_name, other.offset, other.size,
+ other.is_signed);
+ }
+ };
+
+ std::string name;
+ int id;
+ std::vector<Field> fields;
+};
+
+struct Proto {
+ struct Field {
+ std::string type;
+ std::string name;
+ uint32_t number;
+ };
+ std::string name;
+ std::vector<Field> fields;
+
+ std::string ToString();
+};
+
+bool GenerateProto(const FtraceEvent& format, Proto* proto_out);
+std::string InferProtoType(const FtraceEvent::Field& field);
+std::string GetNameFromTypeAndName(const std::string& type_and_name);
+
+// Allow gtest to pretty print FtraceEvent::Field.
+::std::ostream& operator<<(::std::ostream& os, const FtraceEvent::Field&);
+void PrintTo(const FtraceEvent::Field& args, ::std::ostream* os);
+
+} // namespace perfetto
+
+#endif // TOOLS_FTRACE_PROTO_GEN_FTRACE_GEN_H_
diff --git a/tools/ftrace_proto_gen/ftrace_to_proto_unittest.cc b/tools/ftrace_proto_gen/ftrace_to_proto_unittest.cc
new file mode 100644
index 0000000..df592e8
--- /dev/null
+++ b/tools/ftrace_proto_gen/ftrace_to_proto_unittest.cc
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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 "tools/ftrace_proto_gen/ftrace_to_proto.h"
+#include "gtest/gtest.h"
+
+namespace perfetto {
+namespace {
+
+TEST(FtraceEventParser, GetNameFromTypeAndName) {
+ EXPECT_EQ(GetNameFromTypeAndName("int foo"), "foo");
+ EXPECT_EQ(GetNameFromTypeAndName("int foo_bar"), "foo_bar");
+ EXPECT_EQ(GetNameFromTypeAndName("const char * foo"), "foo");
+ EXPECT_EQ(GetNameFromTypeAndName("const char foo[64]"), "foo");
+ EXPECT_EQ(GetNameFromTypeAndName("char[] foo[16]"), "foo");
+ EXPECT_EQ(GetNameFromTypeAndName("u8 foo[(int)sizeof(struct blah)]"), "foo");
+
+ EXPECT_EQ(GetNameFromTypeAndName(""), "");
+ EXPECT_EQ(GetNameFromTypeAndName("]"), "");
+ EXPECT_EQ(GetNameFromTypeAndName("["), "");
+ EXPECT_EQ(GetNameFromTypeAndName(" "), "");
+ EXPECT_EQ(GetNameFromTypeAndName(" []"), "");
+ EXPECT_EQ(GetNameFromTypeAndName(" ]["), "");
+ EXPECT_EQ(GetNameFromTypeAndName("char"), "");
+ EXPECT_EQ(GetNameFromTypeAndName("char *"), "");
+ EXPECT_EQ(GetNameFromTypeAndName("char 42"), "");
+}
+
+TEST(FtraceEventParser, InferProtoType) {
+ EXPECT_EQ(InferProtoType(FtraceEvent::Field{"char * foo", 2, 0, false}),
+ "string");
+}
+
+} // namespace
+} // namespace perfetto
diff --git a/tools/ftrace_proto_gen/main.cc b/tools/ftrace_proto_gen/main.cc
new file mode 100644
index 0000000..e965cd4
--- /dev/null
+++ b/tools/ftrace_proto_gen/main.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2017 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 <fstream>
+#include <memory>
+#include <sstream>
+#include <string>
+
+#include "tools/ftrace_proto_gen/format_parser.h"
+#include "tools/ftrace_proto_gen/ftrace_to_proto.h"
+
+int main(int argc, const char** argv) {
+ if (argc != 3) {
+ printf("Usage: ./%s in.format out.proto\n", argv[0]);
+ return 1;
+ }
+
+ const char* input_path = argv[1];
+ const char* output_path = argv[2];
+
+ std::ifstream fin(input_path, std::ios::in);
+ if (!fin) {
+ fprintf(stderr, "Failed to open %s\n", input_path);
+ return 1;
+ }
+ std::ostringstream stream;
+ stream << fin.rdbuf();
+ fin.close();
+ std::string contents = stream.str();
+
+ perfetto::FtraceEvent format;
+ if (!perfetto::ParseFtraceEvent(contents, &format)) {
+ fprintf(stderr, "Could not parse file %s.\n", input_path);
+ return 1;
+ }
+
+ perfetto::Proto proto;
+ if (!perfetto::GenerateProto(format, &proto)) {
+ fprintf(stderr, "Could not generate proto for file %s\n", input_path);
+ return 1;
+ }
+
+ std::ofstream fout(output_path, std::ios::out);
+ if (!fout) {
+ fprintf(stderr, "Failed to open %s\n", output_path);
+ return 1;
+ }
+
+ fout << proto.ToString();
+ fout.close();
+}