Merge "profiling: Allow to profile all running processes."
diff --git a/Android.bp b/Android.bp
index 3881385..f548a75 100644
--- a/Android.bp
+++ b/Android.bp
@@ -3905,6 +3905,39 @@
   ],
 }
 
+// GN target: //protos/third_party/pprof:lite_gen
+genrule {
+  name: "perfetto_protos_third_party_pprof_lite_gen",
+  srcs: [
+    "protos/third_party/pprof/profile.proto",
+  ],
+  tools: [
+    "aprotoc",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
+  out: [
+    "external/perfetto/protos/third_party/pprof/profile.pb.cc",
+  ],
+}
+
+// GN target: //protos/third_party/pprof:lite_gen
+genrule {
+  name: "perfetto_protos_third_party_pprof_lite_gen_headers",
+  srcs: [
+    "protos/third_party/pprof/profile.proto",
+  ],
+  tools: [
+    "aprotoc",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos $(in)",
+  out: [
+    "external/perfetto/protos/third_party/pprof/profile.pb.h",
+  ],
+  export_include_dirs: [
+    "protos",
+  ],
+}
+
 // GN target: //src/ipc/protoc_plugin:ipc_plugin(//gn/standalone/toolchain:gcc_like_host)
 cc_binary_host {
   name: "perfetto_src_ipc_protoc_plugin_ipc_plugin___gn_standalone_toolchain_gcc_like_host_",
@@ -4678,10 +4711,24 @@
     ":perfetto_protos_perfetto_trace_profiling_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_lite_gen",
     ":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
+    ":perfetto_protos_third_party_pprof_lite_gen",
+    "src/base/event.cc",
+    "src/base/file_utils.cc",
+    "src/base/metatrace.cc",
+    "src/base/paged_memory.cc",
+    "src/base/string_splitter.cc",
+    "src/base/string_utils.cc",
+    "src/base/temp_file.cc",
+    "src/base/thread_checker.cc",
+    "src/base/time.cc",
+    "src/base/unix_task_runner.cc",
+    "src/base/virtual_destructors.cc",
+    "src/base/watchdog_posix.cc",
     "tools/trace_to_text/ftrace_event_formatter.cc",
     "tools/trace_to_text/ftrace_inode_handler.cc",
     "tools/trace_to_text/main.cc",
     "tools/trace_to_text/proto_full_utils.cc",
+    "tools/trace_to_text/trace_to_profile.cc",
     "tools/trace_to_text/trace_to_systrace.cc",
     "tools/trace_to_text/trace_to_text.cc",
     "tools/trace_to_text/utils.cc",
@@ -4705,6 +4752,7 @@
     "perfetto_protos_perfetto_trace_profiling_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_lite_gen_headers",
     "perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
+    "perfetto_protos_third_party_pprof_lite_gen_headers",
   ],
   defaults: [
     "perfetto_defaults",
diff --git a/protos/third_party/pprof/BUILD.gn b/protos/third_party/pprof/BUILD.gn
new file mode 100644
index 0000000..cb74152
--- /dev/null
+++ b/protos/third_party/pprof/BUILD.gn
@@ -0,0 +1,25 @@
+# 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.
+
+import("../../../gn/perfetto.gni")
+import("../../../gn/proto_library.gni")
+
+proto_library("lite") {
+  generate_python = false
+  sources = [
+    "profile.proto",
+  ]
+  proto_in_dir = "$perfetto_root_path/protos"
+  proto_out_dir = "$perfetto_root_path/protos"
+}
diff --git a/protos/third_party/pprof/profile.proto b/protos/third_party/pprof/profile.proto
new file mode 100644
index 0000000..9540940
--- /dev/null
+++ b/protos/third_party/pprof/profile.proto
@@ -0,0 +1,212 @@
+// 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.
+
+// Profile is a common stacktrace profile format.
+//
+// Measurements represented with this format should follow the
+// following conventions:
+//
+// - Consumers should treat unset optional fields as if they had been
+//   set with their default value.
+//
+// - When possible, measurements should be stored in "unsampled" form
+//   that is most useful to humans.  There should be enough
+//   information present to determine the original sampled values.
+//
+// - On-disk, the serialized proto must be gzip-compressed.
+//
+// - The profile is represented as a set of samples, where each sample
+//   references a sequence of locations, and where each location belongs
+//   to a mapping.
+// - There is a N->1 relationship from sample.location_id entries to
+//   locations. For every sample.location_id entry there must be a
+//   unique Location with that id.
+// - There is an optional N->1 relationship from locations to
+//   mappings. For every nonzero Location.mapping_id there must be a
+//   unique Mapping with that id.
+
+syntax = "proto3";
+
+package perftools.profiles;
+
+option java_package = "com.google.perftools.profiles";
+option java_outer_classname = "ProfileProto";
+
+message Profile {
+  // A description of the samples associated with each Sample.value.
+  // For a cpu profile this might be:
+  //   [["cpu","nanoseconds"]] or [["wall","seconds"]] or [["syscall","count"]]
+  // For a heap profile, this might be:
+  //   [["allocations","count"], ["space","bytes"]],
+  // If one of the values represents the number of events represented
+  // by the sample, by convention it should be at index 0 and use
+  // sample_type.unit == "count".
+  repeated ValueType sample_type = 1;
+  // The set of samples recorded in this profile.
+  repeated Sample sample = 2;
+  // Mapping from address ranges to the image/binary/library mapped
+  // into that address range.  mapping[0] will be the main binary.
+  repeated Mapping mapping = 3;
+  // Useful program location
+  repeated Location location = 4;
+  // Functions referenced by locations
+  repeated Function function = 5;
+  // A common table for strings referenced by various messages.
+  // string_table[0] must always be "".
+  repeated string string_table = 6;
+  // frames with Function.function_name fully matching the following
+  // regexp will be dropped from the samples, along with their successors.
+  int64 drop_frames = 7;  // Index into string table.
+  // frames with Function.function_name fully matching the following
+  // regexp will be kept, even if it matches drop_functions.
+  int64 keep_frames = 8;  // Index into string table.
+
+  // The following fields are informational, do not affect
+  // interpretation of results.
+
+  // Time of collection (UTC) represented as nanoseconds past the epoch.
+  int64 time_nanos = 9;
+  // Duration of the profile, if a duration makes sense.
+  int64 duration_nanos = 10;
+  // The kind of events between sampled ocurrences.
+  // e.g [ "cpu","cycles" ] or [ "heap","bytes" ]
+  ValueType period_type = 11;
+  // The number of events between sampled occurrences.
+  int64 period = 12;
+  // Freeform text associated to the profile.
+  repeated int64 comment = 13;  // Indices into string table.
+  // Index into the string table of the type of the preferred sample
+  // value. If unset, clients should default to the last sample value.
+  int64 default_sample_type = 14;
+}
+
+// ValueType describes the semantics and measurement units of a value.
+message ValueType {
+  int64 type = 1;  // Index into string table.
+  int64 unit = 2;  // Index into string table.
+}
+
+// Each Sample records values encountered in some program
+// context. The program context is typically a stack trace, perhaps
+// augmented with auxiliary information like the thread-id, some
+// indicator of a higher level request being handled etc.
+message Sample {
+  // The ids recorded here correspond to a Profile.location.id.
+  // The leaf is at location_id[0].
+  repeated uint64 location_id = 1;
+  // The type and unit of each value is defined by the corresponding
+  // entry in Profile.sample_type. All samples must have the same
+  // number of values, the same as the length of Profile.sample_type.
+  // When aggregating multiple samples into a single sample, the
+  // result has a list of values that is the elemntwise sum of the
+  // lists of the originals.
+  repeated int64 value = 2;
+  // label includes additional context for this sample. It can include
+  // things like a thread id, allocation size, etc
+  repeated Label label = 3;
+}
+
+message Label {
+  int64 key = 1;  // Index into string table
+
+  // At most one of the following must be present
+  int64 str = 2;  // Index into string table
+  int64 num = 3;
+
+  // Should only be present when num is present.
+  // Specifies the units of num.
+  // Use arbitrary string (for example, "requests") as a custom count unit.
+  // If no unit is specified, consumer may apply heuristic to deduce the unit.
+  // Consumers may also  interpret units like "bytes" and "kilobytes" as memory
+  // units and units like "seconds" and "nanoseconds" as time units,
+  // and apply appropriate unit conversions to these.
+  int64 num_unit = 4;  // Index into string table
+}
+
+message Mapping {
+  // Unique nonzero id for the mapping.
+  uint64 id = 1;
+  // Address at which the binary (or DLL) is loaded into memory.
+  uint64 memory_start = 2;
+  // The limit of the address range occupied by this mapping.
+  uint64 memory_limit = 3;
+  // Offset in the binary that corresponds to the first mapped address.
+  uint64 file_offset = 4;
+  // The object this entry is loaded from.  This can be a filename on
+  // disk for the main binary and shared libraries, or virtual
+  // abstractions like "[vdso]".
+  int64 filename = 5;  // Index into string table
+  // A string that uniquely identifies a particular program version
+  // with high probability. E.g., for binaries generated by GNU tools,
+  // it could be the contents of the .note.gnu.build-id field.
+  int64 build_id = 6;  // Index into string table
+
+  // The following fields indicate the resolution of symbolic info.
+  bool has_functions = 7;
+  bool has_filenames = 8;
+  bool has_line_numbers = 9;
+  bool has_inline_frames = 10;
+}
+
+// Describes function and line table debug information.
+message Location {
+  // Unique nonzero id for the location.  A profile could use
+  // instruction addresses or any integer sequence as ids.
+  uint64 id = 1;
+  // The id of the corresponding profile.Mapping for this location.
+  // It can be unset if the mapping is unknown or not applicable for
+  // this profile type.
+  uint64 mapping_id = 2;
+  // The instruction address for this location, if available.  It
+  // should be within [Mapping.memory_start...Mapping.memory_limit]
+  // for the corresponding mapping. A non-leaf address may be in the
+  // middle of a call instruction. It is up to display tools to find
+  // the beginning of the instruction if necessary.
+  uint64 address = 3;
+  // Multiple line indicates this location has inlined functions,
+  // where the last entry represents the caller into which the
+  // preceding entries were inlined.
+  //
+  // E.g., if memcpy() is inlined into printf:
+  //    line[0].function_name == "memcpy"
+  //    line[1].function_name == "printf"
+  repeated Line line = 4;
+  // Provides an indication that multiple symbols map to this location's
+  // address, for example due to identical code folding by the linker. In that
+  // case the line information above represents one of the multiple
+  // symbols. This field must be recomputed when the symbolization state of the
+  // profile changes.
+  bool is_folded = 5;
+}
+
+message Line {
+  // The id of the corresponding profile.Function for this line.
+  uint64 function_id = 1;
+  // Line number in source code.
+  int64 line = 2;
+}
+
+message Function {
+  // Unique nonzero id for the function.
+  uint64 id = 1;
+  // Name of the function, in human-readable form if available.
+  int64 name = 2;  // Index into string table
+  // Name of the function, as identified by the system.
+  // For instance, it can be a C++ mangled name.
+  int64 system_name = 3;  // Index into string table
+  // Source file containing the function.
+  int64 filename = 4;  // Index into string table
+  // Line number in source file.
+  int64 start_line = 5;
+}
diff --git a/src/profiling/memory/README.md b/src/profiling/memory/README.md
index 602fa1d..4ca4bbf 100644
--- a/src/profiling/memory/README.md
+++ b/src/profiling/memory/README.md
@@ -43,4 +43,18 @@
 adb pull /data/misc/perfetto-traces/trace /tmp/trace
 ```
 
-TODO(fmayer): Add instructions how to visualize the trace.
+While we work on UI support, you can convert the trace into pprof compatible
+heap dumps. To do so, run
+
+```
+trace_to_text profile /tmp/trace
+```
+
+This will create a directory in `/tmp/` containing the heap dumps. Run
+
+```
+gzip /tmp/heap_profile-XXXXXX/*.pb
+```
+
+to get gzipped protos, which tools handling pprof profile protos expect.
+Head to http://pprof/ and upload the gzipped protos to get a visualization.
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index cec37f4..019b244 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import("../../gn/perfetto.gni")
+import("../../gn/proto_library.gni")
 import("../../gn/wasm.gni")
 
 # The core source files that are used both by the "full" version (the host
@@ -24,6 +25,9 @@
     "../../include/perfetto/traced:sys_stats_counters",
     "../../protos/perfetto/trace:lite",
     "../../protos/perfetto/trace/ftrace:lite",
+    "../../protos/perfetto/trace/profiling:lite",
+    "../../protos/third_party/pprof:lite",
+    "../../src/base",
   ]
   sources = [
     "ftrace_event_formatter.cc",
@@ -32,6 +36,8 @@
     "ftrace_inode_handler.h",
     "main.cc",
     "process_formatter.h",
+    "trace_to_profile.cc",
+    "trace_to_profile.h",
     "trace_to_systrace.cc",
     "trace_to_systrace.h",
     "trace_to_text.h",
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index 68ca085..5ef76fd 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -18,6 +18,7 @@
 #include <iostream>
 
 #include "perfetto/base/logging.h"
+#include "tools/trace_to_text/trace_to_profile.h"
 #include "tools/trace_to_text/trace_to_systrace.h"
 #include "tools/trace_to_text/trace_to_text.h"
 
@@ -25,7 +26,7 @@
 
 int Usage(const char* argv0) {
   printf(
-      "Usage: %s systrace|json|text [trace.proto] "
+      "Usage: %s systrace|json|text|profile [trace.pb] "
       "[trace.txt]\n",
       argv0);
   return 1;
@@ -78,5 +79,8 @@
   if (format == "text")
     return perfetto::trace_to_text::TraceToText(input_stream, output_stream);
 
+  if (format == "profile")
+    return perfetto::trace_to_text::TraceToProfile(input_stream, output_stream);
+
   return Usage(argv[0]);
 }
diff --git a/tools/trace_to_text/trace_to_profile.cc b/tools/trace_to_text/trace_to_profile.cc
new file mode 100644
index 0000000..9849090
--- /dev/null
+++ b/tools/trace_to_text/trace_to_profile.cc
@@ -0,0 +1,186 @@
+/*
+ * 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.
+ */
+
+#include "tools/trace_to_text/trace_to_profile.h"
+
+#include <inttypes.h>
+
+#include <algorithm>
+#include <map>
+#include <vector>
+
+#include "tools/trace_to_text/utils.h"
+
+#include "perfetto/base/file_utils.h"
+#include "perfetto/base/logging.h"
+#include "perfetto/base/temp_file.h"
+
+#include "perfetto/trace/profiling/profile_packet.pb.h"
+#include "perfetto/trace/trace.pb.h"
+#include "perfetto/trace/trace_packet.pb.h"
+
+#include "third_party/pprof/profile.pb.h"
+
+namespace perfetto {
+namespace trace_to_text {
+
+namespace {
+
+using ::perfetto::protos::ProfilePacket;
+
+using GLine = ::perftools::profiles::Line;
+using GMapping = ::perftools::profiles::Mapping;
+using GLocation = ::perftools::profiles::Location;
+using GProfile = ::perftools::profiles::Profile;
+using GValueType = ::perftools::profiles::ValueType;
+using GFunction = ::perftools::profiles::Function;
+using GSample = ::perftools::profiles::Sample;
+
+void DumpProfilePacket(const ProfilePacket& packet,
+                       const std::string& file_prefix) {
+  std::map<uint64_t, std::string> string_lookup;
+  for (const ProfilePacket::InternedString& interned_string : packet.strings())
+    string_lookup.emplace(interned_string.id(), interned_string.str());
+
+  std::map<uint64_t, const std::vector<uint64_t>> callstack_lookup;
+  for (const ProfilePacket::Callstack& callstack : packet.callstacks()) {
+    std::vector<uint64_t> frame_ids(
+        static_cast<size_t>(callstack.frame_ids().size()));
+    std::reverse_copy(callstack.frame_ids().cbegin(),
+                      callstack.frame_ids().cend(), frame_ids.begin());
+    callstack_lookup.emplace(callstack.id(), std::move(frame_ids));
+  }
+
+  std::map<std::string, uint64_t> string_table;
+  string_table[""] = 0;
+  string_table["space"] = 1;
+  string_table["bytes"] = 2;
+
+  GProfile profile;
+  GValueType* value_type = profile.add_sample_type();
+  // ["space", "bytes"];
+  value_type->set_type(1);
+  value_type->set_type(2);
+
+  for (const ProfilePacket::Mapping& mapping : packet.mappings()) {
+    GMapping* gmapping = profile.add_mapping();
+    gmapping->set_id(mapping.id());
+    gmapping->set_memory_start(mapping.start());
+    gmapping->set_memory_limit(mapping.end());
+    gmapping->set_file_offset(mapping.offset());
+    std::string filename;
+    for (uint64_t str_id : mapping.path_string_ids()) {
+      auto it = string_lookup.find(str_id);
+      if (it == string_lookup.end()) {
+        PERFETTO_ELOG("Mapping %" PRIu64
+                      " referring to invalid string_id %" PRIu64 ".",
+                      static_cast<uint64_t>(mapping.id()), str_id);
+        continue;
+      }
+
+      filename += "/" + it->second;
+    }
+
+    decltype(string_table)::iterator it;
+    std::tie(it, std::ignore) =
+        string_table.emplace(filename, string_table.size());
+    gmapping->set_filename(static_cast<int64_t>(it->second));
+  }
+
+  std::set<uint64_t> functions_to_dump;
+  for (const ProfilePacket::Frame& frame : packet.frames()) {
+    GLocation* glocation = profile.add_location();
+    glocation->set_id(frame.id());
+    glocation->set_mapping_id(frame.mapping_id());
+    // TODO(fmayer): This is probably incorrect. Probably should be abs pc.
+    glocation->set_address(frame.rel_pc());
+    GLine* gline = glocation->add_line();
+    gline->set_function_id(frame.function_name_id());
+    functions_to_dump.emplace(frame.function_name_id());
+  }
+
+  for (uint64_t function_name_id : functions_to_dump) {
+    auto str_it = string_lookup.find(function_name_id);
+    if (str_it == string_lookup.end()) {
+      PERFETTO_ELOG("Function referring to invalid string id %" PRIu64,
+                    function_name_id);
+      continue;
+    }
+    decltype(string_table)::iterator it;
+    std::tie(it, std::ignore) =
+        string_table.emplace(str_it->second, string_table.size());
+    GFunction* gfunction = profile.add_function();
+    gfunction->set_id(function_name_id);
+    gfunction->set_name(static_cast<int64_t>(it->second));
+  }
+
+  // We keep the interning table as string -> uint64_t for fast and easy
+  // lookup. When dumping, we need to turn it into a uint64_t -> string
+  // table so we get it sorted by key order.
+  std::map<uint64_t, std::string> inverted_string_table;
+  for (const auto& p : string_table)
+    inverted_string_table[p.second] = p.first;
+  for (const auto& p : inverted_string_table)
+    profile.add_string_table(p.second);
+
+  for (const ProfilePacket::ProcessHeapSamples& samples :
+       packet.process_dumps()) {
+    GProfile cur_profile = profile;
+    for (const ProfilePacket::HeapSample& sample : samples.samples()) {
+      GSample* gsample = cur_profile.add_sample();
+      auto it = callstack_lookup.find(sample.callstack_id());
+      if (it == callstack_lookup.end()) {
+        PERFETTO_ELOG("Callstack referring to invalid callstack id %" PRIu64,
+                      static_cast<uint64_t>(sample.callstack_id()));
+        continue;
+      }
+      for (uint64_t frame_id : it->second)
+        gsample->add_location_id(frame_id);
+      gsample->add_value(static_cast<int64_t>(sample.cumulative_allocated() -
+                                              sample.cumulative_freed()));
+    }
+
+    std::string filename = file_prefix + std::to_string(samples.pid()) + ".pb";
+    base::ScopedFile fd(base::OpenFile(filename, O_CREAT | O_WRONLY, 0700));
+    if (!fd)
+      PERFETTO_FATAL("Failed to open %s", filename.c_str());
+    std::string serialized = cur_profile.SerializeAsString();
+    PERFETTO_CHECK(base::WriteAll(*fd, serialized.c_str(), serialized.size()) ==
+                   static_cast<ssize_t>(serialized.size()));
+  }
+}
+
+}  // namespace
+
+int TraceToProfile(std::istream* input, std::ostream* output) {
+  std::string temp_dir = "/tmp/heap_profile-XXXXXXX";
+  size_t itr = 0;
+  PERFETTO_CHECK(mkdtemp(&temp_dir[0]));
+  ForEachPacketInTrace(input, [&temp_dir,
+                               &itr](const protos::TracePacket& packet) {
+    if (!packet.has_profile_packet())
+      return;
+    DumpProfilePacket(packet.profile_packet(),
+                      temp_dir + "/heap_dump." + std::to_string(++itr) + ".");
+  });
+
+  *output << "Wrote profiles to " << temp_dir << std::endl;
+
+  return 0;
+}
+
+}  // namespace trace_to_text
+}  // namespace perfetto
diff --git a/tools/trace_to_text/trace_to_profile.h b/tools/trace_to_text/trace_to_profile.h
new file mode 100644
index 0000000..4e84f8e
--- /dev/null
+++ b/tools/trace_to_text/trace_to_profile.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef TOOLS_TRACE_TO_TEXT_TRACE_TO_PROFILE_H_
+#define TOOLS_TRACE_TO_TEXT_TRACE_TO_PROFILE_H_
+
+#include <iostream>
+
+namespace perfetto {
+namespace trace_to_text {
+
+int TraceToProfile(std::istream* input, std::ostream* output);
+
+}  // namespace trace_to_text
+}  // namespace perfetto
+
+#endif  // TOOLS_TRACE_TO_TEXT_TRACE_TO_PROFILE_H_