trace_processor: add support for importing ctrace files

This adds a GZIP parser as ctrace is just systrace compressed with
deflate and the TRACE: header added.

This parser just forwards to the systrace parser after decompression.

Change-Id: I8a0118f7ff8c083f7e6f3bed1eda7de36ea701dc
diff --git a/Android.bp b/Android.bp
index 71646b3..23f38db 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3545,6 +3545,7 @@
     "src/trace_processor/fuchsia_trace_parser.cc",
     "src/trace_processor/fuchsia_trace_tokenizer.cc",
     "src/trace_processor/fuchsia_trace_utils.cc",
+    "src/trace_processor/gzip_trace_parser.cc",
     "src/trace_processor/heap_profile_allocation_table.cc",
     "src/trace_processor/heap_profile_callsite_table.cc",
     "src/trace_processor/heap_profile_frame_table.cc",
@@ -3595,6 +3596,7 @@
     "liblog",
     "libprotobuf-cpp-full",
     "libprotobuf-cpp-lite",
+    "libz",
   ],
   static_libs: [
     "libsqlite",
@@ -3641,7 +3643,10 @@
   cflags: [
     "-DGOOGLE_PROTOBUF_NO_RTTI",
     "-DGOOGLE_PROTOBUF_NO_STATIC_INITIALIZER",
+    "-DHAVE_HIDDEN",
     "-DPERFETTO_BUILD_WITH_ANDROID",
+    "-DUSE_MMAP",
+    "-DZLIB_CONST",
   ],
 }
 
diff --git a/BUILD b/BUILD
index 3a624d7..bc1aea2 100644
--- a/BUILD
+++ b/BUILD
@@ -171,6 +171,8 @@
         "src/trace_processor/fuchsia_trace_tokenizer.h",
         "src/trace_processor/fuchsia_trace_utils.cc",
         "src/trace_processor/fuchsia_trace_utils.h",
+        "src/trace_processor/gzip_trace_parser.cc",
+        "src/trace_processor/gzip_trace_parser.h",
         "src/trace_processor/heap_profile_allocation_table.cc",
         "src/trace_processor/heap_profile_allocation_table.h",
         "src/trace_processor/heap_profile_callsite_table.cc",
@@ -342,6 +344,7 @@
         "//third_party/perfetto/protos:trace_zero_cc_proto",
         "//third_party/sqlite",
         "//third_party/sqlite:sqlite_ext_percentile",
+        "//third_party/zlib",
     ],
 )
 
@@ -448,6 +451,8 @@
         "src/trace_processor/fuchsia_trace_tokenizer.h",
         "src/trace_processor/fuchsia_trace_utils.cc",
         "src/trace_processor/fuchsia_trace_utils.h",
+        "src/trace_processor/gzip_trace_parser.cc",
+        "src/trace_processor/gzip_trace_parser.h",
         "src/trace_processor/heap_profile_allocation_table.cc",
         "src/trace_processor/heap_profile_allocation_table.h",
         "src/trace_processor/heap_profile_callsite_table.cc",
@@ -575,6 +580,7 @@
         "//third_party/protobuf:libprotoc",
         "//third_party/sqlite",
         "//third_party/sqlite:sqlite_ext_percentile",
+        "//third_party/zlib",
     ],
 )
 
@@ -681,6 +687,8 @@
         "src/trace_processor/fuchsia_trace_tokenizer.h",
         "src/trace_processor/fuchsia_trace_utils.cc",
         "src/trace_processor/fuchsia_trace_utils.h",
+        "src/trace_processor/gzip_trace_parser.cc",
+        "src/trace_processor/gzip_trace_parser.h",
         "src/trace_processor/heap_profile_allocation_table.cc",
         "src/trace_processor/heap_profile_allocation_table.h",
         "src/trace_processor/heap_profile_callsite_table.cc",
@@ -833,6 +841,7 @@
         "//third_party/protobuf:libprotoc",
         "//third_party/sqlite",
         "//third_party/sqlite:sqlite_ext_percentile",
+        "//third_party/zlib",
     ],
 )
 
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 8950cee..bd8c3e4 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -75,6 +75,8 @@
     "fuchsia_trace_tokenizer.h",
     "fuchsia_trace_utils.cc",
     "fuchsia_trace_utils.h",
+    "gzip_trace_parser.cc",
+    "gzip_trace_parser.h",
     "heap_profile_allocation_table.cc",
     "heap_profile_allocation_table.h",
     "heap_profile_callsite_table.cc",
@@ -165,6 +167,7 @@
 
   deps = [
     ":sqlite",
+    "../../buildtools:zlib",
     "../../gn:default_deps",
     "../../include/perfetto/ext/traced:sys_stats_counters",
     "../../protos/perfetto/common:zero",
diff --git a/src/trace_processor/gzip_trace_parser.cc b/src/trace_processor/gzip_trace_parser.cc
new file mode 100644
index 0000000..39f407d
--- /dev/null
+++ b/src/trace_processor/gzip_trace_parser.cc
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2019 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 "src/trace_processor/gzip_trace_parser.h"
+
+#include <zlib.h>
+
+#include "src/trace_processor/systrace_trace_parser.h"
+
+namespace perfetto {
+namespace trace_processor {
+
+GzipTraceParser::GzipTraceParser(TraceProcessorContext* context)
+    : context_(context), z_stream_(new z_stream()) {
+  z_stream_->zalloc = Z_NULL;
+  z_stream_->zfree = Z_NULL;
+  z_stream_->opaque = Z_NULL;
+  inflateInit(z_stream_.get());
+}
+
+GzipTraceParser::~GzipTraceParser() = default;
+
+util::Status GzipTraceParser::Parse(std::unique_ptr<uint8_t[]> data,
+                                    size_t size) {
+  uint8_t* start = data.get();
+  size_t len = size;
+
+  static const char kSystraceFilerHeader[] = "TRACE:\n";
+  if (!inner_) {
+    inner_.reset(new SystraceTraceParser(context_));
+
+    // Strip the header by ignoring the associated bytes.
+    start += strlen(kSystraceFilerHeader);
+    len -= strlen(kSystraceFilerHeader);
+  }
+
+  z_stream_->next_in = start;
+  z_stream_->avail_in = static_cast<uInt>(len);
+
+  // Our default uncompressed buffer size is 32MB as it allows for good
+  // throughput.
+  constexpr size_t kUncompressedBufferSize = 32 * 1024 * 1024;
+  int ret = Z_OK;
+  for (; ret != Z_STREAM_END && z_stream_->avail_in != 0;) {
+    std::unique_ptr<uint8_t[]> buffer(new uint8_t[kUncompressedBufferSize]);
+    z_stream_->next_out = buffer.get();
+    z_stream_->avail_out = static_cast<uInt>(kUncompressedBufferSize);
+
+    ret = inflate(z_stream_.get(), Z_NO_FLUSH);
+    switch (ret) {
+      case Z_NEED_DICT:
+      case Z_DATA_ERROR:
+      case Z_MEM_ERROR:
+        // Ignore inflateEnd error as we will error out anyway.
+        inflateEnd(z_stream_.get());
+        return util::ErrStatus("Error decompressing ctrace file");
+    }
+
+    size_t read = size * 64 - z_stream_->avail_out;
+    util::Status status = inner_->Parse(std::move(buffer), read);
+    if (!status.ok())
+      return status;
+  }
+  if (ret == Z_STREAM_END) {
+    ret = inflateEnd(z_stream_.get());
+    if (ret == Z_STREAM_ERROR)
+      return util::ErrStatus("Error finishing decompression");
+  }
+  return util::OkStatus();
+}
+
+}  // namespace trace_processor
+}  // namespace perfetto
diff --git a/src/trace_processor/gzip_trace_parser.h b/src/trace_processor/gzip_trace_parser.h
new file mode 100644
index 0000000..a129c4b
--- /dev/null
+++ b/src/trace_processor/gzip_trace_parser.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 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 SRC_TRACE_PROCESSOR_GZIP_TRACE_PARSER_H_
+#define SRC_TRACE_PROCESSOR_GZIP_TRACE_PARSER_H_
+
+#include "src/trace_processor/chunked_trace_reader.h"
+
+struct z_stream_s;
+
+namespace perfetto {
+namespace trace_processor {
+
+class TraceProcessorContext;
+
+class GzipTraceParser : public ChunkedTraceReader {
+ public:
+  explicit GzipTraceParser(TraceProcessorContext*);
+  ~GzipTraceParser() override;
+
+  // ChunkedTraceReader implementation
+  util::Status Parse(std::unique_ptr<uint8_t[]>, size_t) override;
+
+ private:
+  TraceProcessorContext* const context_;
+  std::unique_ptr<z_stream_s> z_stream_;
+  std::unique_ptr<ChunkedTraceReader> inner_;
+};
+
+}  // namespace trace_processor
+}  // namespace perfetto
+
+#endif  // SRC_TRACE_PROCESSOR_GZIP_TRACE_PARSER_H_
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 9e34dd3..f6d657d 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -35,6 +35,7 @@
 #include "src/trace_processor/event_tracker.h"
 #include "src/trace_processor/fuchsia_trace_parser.h"
 #include "src/trace_processor/fuchsia_trace_tokenizer.h"
+#include "src/trace_processor/gzip_trace_parser.h"
 #include "src/trace_processor/heap_profile_allocation_table.h"
 #include "src/trace_processor/heap_profile_callsite_table.h"
 #include "src/trace_processor/heap_profile_frame_table.h"
@@ -294,6 +295,10 @@
   if (base::StartsWith(start, " "))
     return kSystraceTraceType;
 
+  // Ctrace is GZIPed systrace with no headers.
+  if (base::StartsWith(start, "TRACE:"))
+    return kCtraceTraceType;
+
   return kProtoTraceType;
 }
 
@@ -400,6 +405,9 @@
       case kSystraceTraceType:
         context_.chunk_reader.reset(new SystraceTraceParser(&context_));
         break;
+      case kCtraceTraceType:
+        context_.chunk_reader.reset(new GzipTraceParser(&context_));
+        break;
       case kUnknownTraceType:
         return util::ErrStatus("Unknown trace type provided");
     }
diff --git a/src/trace_processor/trace_processor_impl.h b/src/trace_processor/trace_processor_impl.h
index 8673928..e9c3435 100644
--- a/src/trace_processor/trace_processor_impl.h
+++ b/src/trace_processor/trace_processor_impl.h
@@ -42,6 +42,7 @@
   kJsonTraceType,
   kFuchsiaTraceType,
   kSystraceTraceType,
+  kCtraceTraceType,
 };
 
 TraceType GuessTraceType(const uint8_t* data, size_t size);
diff --git a/tools/gen_bazel b/tools/gen_bazel
index c430fbe..032a78b 100755
--- a/tools/gen_bazel
+++ b/tools/gen_bazel
@@ -103,6 +103,10 @@
   module.deps.add(Label('//third_party/perfetto/google:perfetto_version'))
 
 
+def enable_zlib(module):
+  module.deps.add(Label('//third_party/zlib'))
+
+
 def disable_module(module):
   pass
 
@@ -116,6 +120,7 @@
     '//buildtools:protobuf_full': enable_protobuf_full,
     '//buildtools:protoc': disable_module,
     '//buildtools:sqlite': enable_sqlite,
+    '//buildtools:zlib': enable_zlib,
     '//gn:default_deps': disable_module,
     '//gn:protoc_lib_deps': enable_protobuf_full,
     '//gn/standalone:gen_git_revision': enable_perfetto_version,