perfetto_cmd: Teach perfetto_cmd to parse pbtxt configs

Adds the ability for perfetto_cmd to understand pbtxt configs
(this is controlled by the -t/--txt flag). This is somewhat complicated
by perfetto_cmd only having access to protobuf lite.

- Add a cut down version of descriptor.proto
- Use this to generate a (lite) descriptor.pb.h/descriptor.pb.cc
- Convert perfetto_config.proto into a FileSetDescriptor
  (perfetto_config.descriptor)
- Encode perfetto_config.descriptor as a C++ constant named
  kPerfettoConfigDescriptor (perfetto_config.descriptor.h)
- Use descriptor.pb.h to parse the kPerfettoConfigDescriptor
- Manually parse the incoming config based on the descriptor and
  use protozero to write the proto encoding into a buffer which is then
  parsed as normal.

Converting perfetto_config.proto to a descriptor happens at commit time
to avoid having to teach google3 and Android build systems how to
convert a proto descriptor into a .h file of the correct format.

This is not a stable API. Backwards and forwards compatible proto format changes
such as renaming a field or removing an optional field are breaking changes to
the proto text format (we can't distinguish between a typo'ed field and an
optional field) so this format is only really designed for local testing.

Several smaller clean ups:
- Move scattered_stream_memory_delegate.{h,cc} to be part of protozero
  proper
- Remove the (now empty) src/protozero:test_support target
- Remove unused code in tools/gen_merged_protos
- Remove trailing whitespace in PRESUBMIT.py

Change-Id: Ic59b1338bd83421f68d4e12b4e01b9aaf5aa99be
diff --git a/tools/gen_binary_descriptors b/tools/gen_binary_descriptors
new file mode 100755
index 0000000..513ef95
--- /dev/null
+++ b/tools/gen_binary_descriptors
@@ -0,0 +1,135 @@
+#!/usr/bin/env python
+# Copyright (C) 2018 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 absolute_import
+from __future__ import division
+from __future__ import print_function
+import os
+import re
+import sys
+import argparse
+import tempfile
+import subprocess
+import hashlib
+import textwrap
+
+SOURCE_TARGET = {
+    'protos/perfetto/config/perfetto_config.proto':
+            'src/perfetto_cmd/perfetto_config.descriptor.h',
+}
+
+ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+
+SCRIPT_PATH = 'tools/gen_binary_descriptors'
+
+def hash_path(path):
+  hash = hashlib.sha1()
+  with open(os.path.join(ROOT_DIR, path)) as f:
+    hash.update(f.read())
+  return hash.hexdigest()
+
+
+def check(source, target):
+  assert os.path.exists(os.path.join(ROOT_DIR, target))
+
+  with open(target, 'rb') as f:
+    s = f.read()
+
+  hashes = re.findall(r'// SHA1\((.*)\)\n// (.*)\n', s)
+  assert sorted([SCRIPT_PATH, source]) == sorted([key for key, _ in hashes])
+  for path, expected_sha1 in hashes:
+    actual_sha1 = hash_path(os.path.join(ROOT_DIR, path))
+    assert actual_sha1 == expected_sha1, \
+        'Hash for path {} did not match'.format(path)
+
+
+def generate(source, target, protoc_path):
+  _, source_name = os.path.split(source)
+  _, target_name = os.path.split(target)
+  assert source_name.replace('.proto', '.descriptor.h') == target_name
+
+  with tempfile.NamedTemporaryFile() as fdescriptor:
+    subprocess.check_call([
+        protoc_path,
+        '--proto_path=protos',
+        '-o{}'.format(fdescriptor.name),
+        source,
+    ], cwd=ROOT_DIR)
+
+    s = fdescriptor.read()
+    constant_name = 'k' + target_name.title()[:-2].translate(None, '._')
+    binary = '{' + ', '.join('{0:#04x}'.format(ord(c)) for c in s) + '}'
+    binary = textwrap.fill(binary,
+        width=80,
+        initial_indent='    ',
+        subsequent_indent='     ')
+    include_guard = target.replace('/', '_').replace('.', '_').upper() + '_'
+
+    with open(os.path.join(ROOT_DIR, target), 'wb') as f:
+      f.write("""
+#ifndef {include_guard}
+#define {include_guard}
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <array>
+
+// This file was autogenerated by tools/gen_binary_descriptors. Do not edit.
+
+// SHA1({script_path})
+// {script_hash}
+// SHA1({source_path})
+// {source_hash}
+
+// This is the proto {{proto_name}} encoded as a ProtoFileDescriptor to allow
+// for reflection without libprotobuf full/non-lite protos.
+
+namespace perfetto {{
+
+constexpr std::array<uint8_t, {size}> {constant_name}{{
+{binary}}};
+
+}}  // namespace perfetto
+
+#endif  // {include_guard}
+""".format(**{
+        'size': len(s),
+        'constant_name': constant_name,
+        'binary': binary,
+        'include_guard': include_guard,
+        'script_path': SCRIPT_PATH,
+        'script_hash': hash_path(__file__),
+        'source_path': source,
+        'source_hash': hash_path(os.path.join(source)),
+      }))
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--check-only', action='store_true')
+  parser.add_argument('--protoc')
+  args = parser.parse_args()
+
+  for source, target in SOURCE_TARGET.iteritems():
+    if args.check_only:
+        check(source, target)
+    else:
+      protoc = args.protoc
+      assert os.path.exists(protoc)
+      generate(source, target, args.protoc)
+
+if __name__ == '__main__':
+  exit(main())