trace_processor: implement first android mem metric!

We fill the LMK field for now: more metrics will be filled in following
CLs.

Context: go/perfetto-metrics
Bug: 129747127
Change-Id: If3d7de375e16bbfd15a8b19bda05d3162ae30308
diff --git a/Android.bp b/Android.bp
index 0b5991d..515e736 100644
--- a/Android.bp
+++ b/Android.bp
@@ -14,6 +14,20 @@
 //
 // This file is automatically generated by tools/gen_android_bp. Do not edit.
 
+genrule {
+  name: "gen_merged_sql_metrics",
+  srcs: [
+    "src/trace_processor/metrics/android/android_mem.sql",
+  ],
+  cmd: "$(location tools/gen_merged_sql_metrics) --cpp_out=$(out) $(in)",
+  out: [
+    "src/trace_processor/metrics/sql_metrics.h",
+  ],
+  tool_files: [
+    "tools/gen_merged_sql_metrics",
+  ],
+}
+
 // GN target: //:heapprofd
 cc_binary {
   name: "heapprofd",
@@ -1034,6 +1048,76 @@
   ],
 }
 
+// GN target: //protos/perfetto/metrics/android:zero_gen
+genrule {
+  name: "perfetto_protos_perfetto_metrics_android_zero_gen",
+  srcs: [
+    "protos/perfetto/metrics/android/mem_metric.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/metrics/android/mem_metric.pbzero.cc",
+  ],
+}
+
+// GN target: //protos/perfetto/metrics/android:zero_gen
+genrule {
+  name: "perfetto_protos_perfetto_metrics_android_zero_gen_headers",
+  srcs: [
+    "protos/perfetto/metrics/android/mem_metric.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/metrics/android/mem_metric.pbzero.h",
+  ],
+  export_include_dirs: [
+    "protos",
+  ],
+}
+
+// GN target: //protos/perfetto/metrics:zero_gen
+genrule {
+  name: "perfetto_protos_perfetto_metrics_zero_gen",
+  srcs: [
+    "protos/perfetto/metrics/metrics.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/metrics/metrics.pbzero.cc",
+  ],
+}
+
+// GN target: //protos/perfetto/metrics:zero_gen
+genrule {
+  name: "perfetto_protos_perfetto_metrics_zero_gen_headers",
+  srcs: [
+    "protos/perfetto/metrics/metrics.proto",
+  ],
+  tools: [
+    "aprotoc",
+    "perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_",
+  ],
+  cmd: "mkdir -p $(genDir)/external/perfetto/protos && $(location aprotoc) --cpp_out=$(genDir)/external/perfetto/protos --proto_path=external/perfetto/protos --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_protoc_plugin___gn_standalone_toolchain_gcc_like_host_) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/protos $(in)",
+  out: [
+    "external/perfetto/protos/perfetto/metrics/metrics.pbzero.h",
+  ],
+  export_include_dirs: [
+    "protos",
+  ],
+}
+
 // GN target: //protos/perfetto/trace/android:lite_gen
 genrule {
   name: "perfetto_protos_perfetto_trace_android_lite_gen",
@@ -3069,6 +3153,8 @@
     ":perfetto_protos_perfetto_common_zero_gen",
     ":perfetto_protos_perfetto_config_lite_gen",
     ":perfetto_protos_perfetto_config_zero_gen",
+    ":perfetto_protos_perfetto_metrics_android_zero_gen",
+    ":perfetto_protos_perfetto_metrics_zero_gen",
     ":perfetto_protos_perfetto_trace_android_lite_gen",
     ":perfetto_protos_perfetto_trace_android_zero_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
@@ -3175,10 +3261,13 @@
     "libsqlite",
   ],
   generated_headers: [
+    "gen_merged_sql_metrics",
     "perfetto_protos_perfetto_common_lite_gen_headers",
     "perfetto_protos_perfetto_common_zero_gen_headers",
     "perfetto_protos_perfetto_config_lite_gen_headers",
     "perfetto_protos_perfetto_config_zero_gen_headers",
+    "perfetto_protos_perfetto_metrics_android_zero_gen_headers",
+    "perfetto_protos_perfetto_metrics_zero_gen_headers",
     "perfetto_protos_perfetto_trace_android_lite_gen_headers",
     "perfetto_protos_perfetto_trace_android_zero_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
diff --git a/BUILD b/BUILD
index 502d219..bf70a25 100644
--- a/BUILD
+++ b/BUILD
@@ -20,6 +20,21 @@
 
 exports_files(["LICENSE"])
 
+# GN target: //src/trace_processor/metrics:gen_merged_sql_metrics
+genrule(
+    name = "gen_merged_sql_metrics",
+    srcs = [
+        "src/trace_processor/metrics/android/android_mem.sql",
+    ],
+    cmd = "$(location gen_merged_sql_metrics_py) --cpp_out=$@ $SRCS",
+    outs = [
+        "src/trace_processor/metrics/sql_metrics.h",
+    ],
+    tools = [
+        "gen_merged_sql_metrics_py",
+    ],
+)
+
 # GN target: //src/protozero:libprotozero
 cc_library(
     name = "libprotozero",
@@ -286,10 +301,13 @@
         "include/perfetto/traced/sys_stats_counters.h",
     ],
     deps = [
+        "//third_party/perfetto:gen_merged_sql_metrics",
         "//third_party/perfetto/google:gtest_prod",
         "//third_party/perfetto/google:jsoncpp",
         "//third_party/perfetto/protos:common_zero_cc_proto",
         "//third_party/perfetto/protos:config_zero_cc_proto",
+        "//third_party/perfetto/protos:metrics_android_zero_cc_proto",
+        "//third_party/perfetto/protos:metrics_zero_cc_proto",
         "//third_party/perfetto/protos:trace_android_zero_cc_proto",
         "//third_party/perfetto/protos:trace_chrome_zero_cc_proto",
         "//third_party/perfetto/protos:trace_filesystem_zero_cc_proto",
@@ -482,12 +500,15 @@
         "src/trace_processor/window_operator_table.h",
     ],
     deps = [
+        "//third_party/perfetto:gen_merged_sql_metrics",
         "//third_party/perfetto/google:gtest_prod",
         "//third_party/perfetto/google:jsoncpp",
         "//third_party/perfetto/google:linenoise",
         "//third_party/perfetto/google:perfetto_version",
         "//third_party/perfetto/protos:common_zero_cc_proto",
         "//third_party/perfetto/protos:config_zero_cc_proto",
+        "//third_party/perfetto/protos:metrics_android_zero_cc_proto",
+        "//third_party/perfetto/protos:metrics_zero_cc_proto",
         "//third_party/perfetto/protos:trace_android_zero_cc_proto",
         "//third_party/perfetto/protos:trace_chrome_zero_cc_proto",
         "//third_party/perfetto/protos:trace_filesystem_zero_cc_proto",
@@ -690,6 +711,7 @@
         "tools/trace_to_text/utils.h",
     ],
     deps = [
+        "//third_party/perfetto:gen_merged_sql_metrics",
         "//third_party/perfetto/google:gtest_prod",
         "//third_party/perfetto/google:jsoncpp",
         "//third_party/perfetto/google:perfetto_version",
@@ -697,6 +719,8 @@
         "//third_party/perfetto/protos:common_zero_cc_proto",
         "//third_party/perfetto/protos:config_cc_proto",
         "//third_party/perfetto/protos:config_zero_cc_proto",
+        "//third_party/perfetto/protos:metrics_android_zero_cc_proto",
+        "//third_party/perfetto/protos:metrics_zero_cc_proto",
         "//third_party/perfetto/protos:protos_third_party_pprof_cc_proto",
         "//third_party/perfetto/protos:trace_android_cc_proto",
         "//third_party/perfetto/protos:trace_android_zero_cc_proto",
@@ -741,3 +765,10 @@
         ":trace_to_text",
     ],
 )
+
+py_binary(
+    name = "gen_merged_sql_metrics_py"
+    srcs = [
+      "tools/gen_merged_sql_metrics"
+    ]
+)
diff --git a/BUILD.extras b/BUILD.extras
index a1ca2c3..1139c44 100644
--- a/BUILD.extras
+++ b/BUILD.extras
@@ -11,3 +11,10 @@
         ":trace_to_text",
     ],
 )
+
+py_binary(
+    name = "gen_merged_sql_metrics_py"
+    srcs = [
+      "tools/gen_merged_sql_metrics"
+    ]
+)
diff --git a/include/perfetto/base/string_utils.h b/include/perfetto/base/string_utils.h
index 2df82f5..d2f9a36 100644
--- a/include/perfetto/base/string_utils.h
+++ b/include/perfetto/base/string_utils.h
@@ -28,6 +28,8 @@
 bool Contains(const std::string& haystack, const std::string& needle);
 std::string Join(const std::vector<std::string>& parts,
                  const std::string& delim);
+std::vector<std::string> SplitString(const std::string& text,
+                                     const std::string& delimiter);
 
 }  // namespace base
 }  // namespace perfetto
diff --git a/protos/BUILD b/protos/BUILD
index b41edf1..ed2ce1f 100644
--- a/protos/BUILD
+++ b/protos/BUILD
@@ -170,6 +170,45 @@
     ],
 )
 
+# GN target: //protos/perfetto/metrics/android:zero_gen
+proto_library(
+    name = "metrics_android_zero",
+    srcs = [
+        "perfetto/metrics/android/mem_metric.proto",
+    ],
+)
+
+# GN target: //protos/perfetto/metrics/android:zero_gen
+pbzero_cc_proto_library(
+    name = "metrics_android_zero_cc_proto",
+    src_proto_library = "//third_party/perfetto/protos:metrics_android_zero",
+    deps = [
+        "//third_party/perfetto:libprotozero",
+        "//third_party/perfetto/google:gtest_prod",
+    ],
+)
+
+# GN target: //protos/perfetto/metrics:zero_gen
+proto_library(
+    name = "metrics_zero",
+    srcs = [
+        "perfetto/metrics/metrics.proto",
+    ],
+    deps = [
+        "//third_party/perfetto/protos:metrics_android_zero",
+    ],
+)
+
+# GN target: //protos/perfetto/metrics:zero_gen
+pbzero_cc_proto_library(
+    name = "metrics_zero_cc_proto",
+    src_proto_library = "//third_party/perfetto/protos:metrics_zero",
+    deps = [
+        "//third_party/perfetto:libprotozero",
+        "//third_party/perfetto/google:gtest_prod",
+    ],
+)
+
 # GN target: //protos/third_party/pprof:lite_gen
 proto_library(
     name = "protos_third_party_pprof",
diff --git a/src/base/string_utils.cc b/src/base/string_utils.cc
index ad5c620..267952b 100644
--- a/src/base/string_utils.cc
+++ b/src/base/string_utils.cc
@@ -16,6 +16,8 @@
 
 #include "perfetto/base/string_utils.h"
 
+#include "perfetto/base/logging.h"
+
 namespace perfetto {
 namespace base {
 
@@ -45,5 +47,22 @@
   return acc;
 }
 
+std::vector<std::string> SplitString(const std::string& text,
+                                     const std::string& delimiter) {
+  PERFETTO_CHECK(!delimiter.empty());
+
+  std::vector<std::string> output;
+  size_t start = 0;
+  size_t next;
+  for (;;) {
+    next = std::min(text.find(delimiter, start), text.size());
+    output.emplace_back(&text[start], next - start);
+    start = next + delimiter.size();
+    if (start >= text.size())
+      break;
+  }
+  return output;
+}
+
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/base/string_utils_unittest.cc b/src/base/string_utils_unittest.cc
index c5737a4..bed511f 100644
--- a/src/base/string_utils_unittest.cc
+++ b/src/base/string_utils_unittest.cc
@@ -23,6 +23,8 @@
 namespace base {
 namespace {
 
+using testing::ElementsAre;
+
 TEST(StringUtilsTest, StartsWith) {
   EXPECT_TRUE(StartsWith("", ""));
   EXPECT_TRUE(StartsWith("abc", ""));
@@ -45,6 +47,16 @@
   EXPECT_FALSE(EndsWith("", "c"));
 }
 
+TEST(StringUtilsTest, SplitString) {
+  EXPECT_THAT(SplitString("", ":"), ElementsAre(""));
+  EXPECT_THAT(SplitString("a:b:c", ":"), ElementsAre("a", "b", "c"));
+  EXPECT_THAT(SplitString("a::b::c", "::"), ElementsAre("a", "b", "c"));
+  EXPECT_THAT(SplitString("abc", ":"), ElementsAre("abc"));
+  EXPECT_THAT(SplitString("abc", "::"), ElementsAre("abc"));
+  EXPECT_THAT(SplitString("abc", ":"), ElementsAre("abc"));
+  EXPECT_THAT(SplitString("abc", "::"), ElementsAre("abc"));
+}
+
 }  // namespace
 }  // namespace base
 }  // namespace perfetto
diff --git a/src/trace_processor/BUILD.gn b/src/trace_processor/BUILD.gn
index 3a44cb0..720d2ae 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -147,6 +147,8 @@
     "../../gn:default_deps",
     "../../include/perfetto/traced:sys_stats_counters",
     "../../protos/perfetto/common:zero",
+    "../../protos/perfetto/metrics:zero",
+    "../../protos/perfetto/metrics/android:zero",
     "../../protos/perfetto/trace:zero",
     "../../protos/perfetto/trace/android:zero",
     "../../protos/perfetto/trace/ftrace:zero",
@@ -158,6 +160,7 @@
     "../../protos/perfetto/trace/track_event:zero",
     "../base",
     "../protozero",
+    "metrics:gen_merged_sql_metrics",
   ]
   public_deps = [
     "../../include/perfetto/trace_processor",
diff --git a/src/trace_processor/metrics/BUILD.gn b/src/trace_processor/metrics/BUILD.gn
new file mode 100644
index 0000000..779cdf7
--- /dev/null
+++ b/src/trace_processor/metrics/BUILD.gn
@@ -0,0 +1,35 @@
+# 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")
+
+sql_files = [ "android/android_mem.sql" ]
+
+config("gen_config") {
+  include_dirs = [ "${root_gen_dir}/${perfetto_root_path}" ]
+}
+
+action("gen_merged_sql_metrics") {
+  script = "../../../tools/gen_merged_sql_metrics"
+  generated_header = "${target_gen_dir}/sql_metrics.h"
+  args = rebase_path(sql_files, root_build_dir) + [
+           "--cpp_out",
+           rebase_path(generated_header, root_build_dir),
+         ]
+  inputs = sql_files
+  outputs = [
+    generated_header,
+  ]
+  public_configs = [ ":gen_config" ]
+}
diff --git a/src/trace_processor/metrics/android/android_mem.sql b/src/trace_processor/metrics/android/android_mem.sql
new file mode 100644
index 0000000..80daf9c
--- /dev/null
+++ b/src/trace_processor/metrics/android/android_mem.sql
@@ -0,0 +1,42 @@
+--
+-- Copyright 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
+--
+--     https://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.
+--
+
+-- Create all the views used to generate the Android Memory metrics proto.
+CREATE TABLE last_oom_adj(upid BIG INT PRIMARY KEY, ts BIG INT, score INT);
+
+INSERT INTO last_oom_adj
+SELECT upid, ts, score
+FROM (
+  SELECT ref AS upid,
+         ts,
+         CAST(value AS INT) AS score,
+         row_number() OVER (PARTITION BY counter_id ORDER BY ts DESC) AS rank
+  FROM counter_definitions JOIN counter_values USING(counter_id)
+  WHERE name = 'oom_score_adj'
+  AND ref_type = 'upid')
+WHERE rank = 1;
+
+CREATE VIEW lmk_events AS
+SELECT ref AS upid
+FROM instants
+WHERE name = 'mem.lmk' AND ref_type = 'upid';
+
+CREATE VIEW lmk_by_score AS
+SELECT process.name, last_oom_adj.score
+FROM lmk_events
+LEFT JOIN process ON lmk_events.upid = process.upid
+LEFT JOIN last_oom_adj ON lmk_events.upid = last_oom_adj.upid
+ORDER BY lmk_events.upid;
diff --git a/src/trace_processor/trace_processor_impl.cc b/src/trace_processor/trace_processor_impl.cc
index 1d235db..e06fd56 100644
--- a/src/trace_processor/trace_processor_impl.cc
+++ b/src/trace_processor/trace_processor_impl.cc
@@ -21,7 +21,9 @@
 #include <functional>
 
 #include "perfetto/base/logging.h"
+#include "perfetto/base/string_utils.h"
 #include "perfetto/base/time.h"
+#include "perfetto/protozero/scattered_heap_buffer.h"
 #include "src/trace_processor/android_logs_table.h"
 #include "src/trace_processor/args_table.h"
 #include "src/trace_processor/args_tracker.h"
@@ -32,6 +34,7 @@
 #include "src/trace_processor/fuchsia_trace_parser.h"
 #include "src/trace_processor/fuchsia_trace_tokenizer.h"
 #include "src/trace_processor/instants_table.h"
+#include "src/trace_processor/metrics/sql_metrics.h"
 #include "src/trace_processor/process_table.h"
 #include "src/trace_processor/process_tracker.h"
 #include "src/trace_processor/proto_trace_parser.h"
@@ -52,6 +55,9 @@
 #include "src/trace_processor/trace_sorter.h"
 #include "src/trace_processor/window_operator_table.h"
 
+#include "perfetto/metrics/android/mem_metric.pbzero.h"
+#include "perfetto/metrics/metrics.pbzero.h"
+
 // JSON parsing is only supported in the standalone build.
 #if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
 #include "src/trace_processor/json_trace_parser.h"
@@ -71,6 +77,12 @@
 namespace trace_processor {
 namespace {
 
+std::string RemoveWhitespace(const std::string& input) {
+  std::string str(input);
+  str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end());
+  return str;
+}
+
 void InitializeSqlite(sqlite3* db) {
   char* error = nullptr;
   sqlite3_exec(db, "PRAGMA temp_store=2", 0, 0, &error);
@@ -169,16 +181,6 @@
   }
 }
 
-bool IsPrefix(const std::string& a, const std::string& b) {
-  return a.size() <= b.size() && b.substr(0, a.size()) == a;
-}
-
-std::string RemoveWhitespace(const std::string& input) {
-  std::string str(input);
-  str.erase(std::remove_if(str.begin(), str.end(), ::isspace), str.end());
-  return str;
-}
-
 // Fuchsia traces have a magic number as documented here:
 // https://fuchsia.googlesource.com/fuchsia/+/HEAD/docs/development/tracing/trace-format/README.md#magic-number-record-trace-info-type-0
 constexpr uint64_t kFuchsiaMagicNumber = 0x0016547846040010;
@@ -191,9 +193,9 @@
   std::string start(reinterpret_cast<const char*>(data),
                     std::min<size_t>(size, 20));
   std::string start_minus_white_space = RemoveWhitespace(start);
-  if (IsPrefix("{\"traceEvents\":[", start_minus_white_space))
+  if (base::StartsWith(start_minus_white_space, "{\"traceEvents\":["))
     return kJsonTraceType;
-  if (IsPrefix("[{", start_minus_white_space))
+  if (base::StartsWith(start_minus_white_space, "[{"))
     return kJsonTraceType;
   if (size >= 8) {
     uint64_t first_word = *reinterpret_cast<const uint64_t*>(data);
@@ -321,7 +323,53 @@
 int TraceProcessorImpl::ComputeMetric(
     const std::vector<std::string>& metric_names,
     std::vector<uint8_t>* metrics_proto) {
-  perfetto::base::ignore_result(metric_names, metrics_proto);
+  // TODO(lalitm): stop hardcoding android.mem metric and read the proto
+  // descriptor for this logic instead.
+  if (metric_names.size() != 1 || metric_names[0] != "android.mem") {
+    PERFETTO_ELOG("Only android.mem metric is currently supported");
+    return 1;
+  }
+
+  auto queries = base::SplitString(metrics::kAndroidMem, ";\n\n");
+  for (const auto& query : queries) {
+    PERFETTO_DLOG("Executing query: %s", query.c_str());
+    auto prep_it = ExecuteQuery(query);
+    auto prep_has_next = prep_it.Next();
+    if (auto opt_error = prep_it.GetLastError()) {
+      PERFETTO_ELOG("SQLite error: %s", opt_error->c_str());
+      return 1;
+    }
+    PERFETTO_DCHECK(!prep_has_next);
+  }
+
+  protozero::ScatteredHeapBuffer delegate;
+  protozero::ScatteredStreamWriter writer(&delegate);
+  delegate.set_writer(&writer);
+
+  protos::pbzero::TraceMetrics metrics;
+  metrics.Reset(&writer);
+
+  // TODO(lalitm): all the below is temporary hardcoded queries and proto
+  // filling to ensure that the code above works.
+  auto it = ExecuteQuery("SELECT COUNT(*) from lmk_by_score;");
+  auto has_next = it.Next();
+  if (auto opt_error = it.GetLastError()) {
+    PERFETTO_ELOG("SQLite error: %s", opt_error->c_str());
+    return 1;
+  }
+  PERFETTO_CHECK(has_next);
+  PERFETTO_CHECK(it.Get(0).type == SqlValue::Type::kLong);
+
+  auto* memory = metrics.set_android_mem();
+  memory->set_system_metrics()->set_lmks()->set_total_count(
+      static_cast<int32_t>(it.Get(0).long_value));
+  metrics.Finalize();
+
+  *metrics_proto = delegate.StitchSlices();
+
+  has_next = it.Next();
+  PERFETTO_DCHECK(!has_next);
+
   return 0;
 }
 
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index 05215e4..a75d934 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -17,6 +17,7 @@
 #include <aio.h>
 #include <fcntl.h>
 #include <inttypes.h>
+#include <stdio.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
@@ -248,6 +249,7 @@
     PERFETTO_ELOG("Error when computing metrics");
     return 1;
   }
+  fwrite(metric_result.data(), sizeof(uint8_t), metric_result.size(), stdout);
   return 0;
 }
 
diff --git a/tools/gen_android_bp b/tools/gen_android_bp
index e9ca6ab..b6b5eda 100755
--- a/tools/gen_android_bp
+++ b/tools/gen_android_bp
@@ -213,6 +213,7 @@
         self.include_dirs = []
         self.required = []
         self.user_debug_flag = False
+        self.tool_files = None
 
     def to_string(self, output):
         if self.comment:
@@ -235,6 +236,7 @@
         self._output_field(output, 'local_include_dirs')
         self._output_field(output, 'include_dirs')
         self._output_field(output, 'required')
+        self._output_field(output, 'tool_files')
 
         disable_pdk = any(name in library_not_in_pdk for name in self.shared_libs)
         if self.user_debug_flag or disable_pdk:
@@ -365,12 +367,16 @@
 
     type = target['type']
     if type == 'action':
-        create_modules_from_target(blueprint, desc, dep_name)
-        # Depend both on the generated sources and headers -- see
-        # make_genrules_for_action.
-        module.srcs.append(':' + label_to_module_name(dep_name))
-        module.generated_headers.append(
-            label_to_module_name(dep_name) + '_headers')
+        if "gen_merged_sql_metrics" in dep_name:
+          dep_mod = create_merged_sql_metrics_target(blueprint, desc, dep_name)
+          module.generated_headers.append(dep_mod.name)
+        else:
+          create_modules_from_target(blueprint, desc, dep_name)
+          # Depend both on the generated sources and headers -- see
+          # make_genrules_for_action.
+          module.srcs.append(':' + label_to_module_name(dep_name))
+          module.generated_headers.append(
+              label_to_module_name(dep_name) + '_headers')
     elif type == 'static_library' and label_to_module_name(
             dep_name) != module.name:
         create_modules_from_target(blueprint, desc, dep_name)
@@ -506,6 +512,26 @@
         ]
     return source_module, header_module
 
+def create_merged_sql_metrics_target(blueprint, desc, gn_target_name):
+    target_desc = desc[gn_target_name]
+    module = Module(
+        'genrule',
+        'gen_merged_sql_metrics',
+    )
+    module.tool_files = [
+        'tools/gen_merged_sql_metrics',
+    ]
+    module.cmd = '$(location tools/gen_merged_sql_metrics) --cpp_out=$(out) $(in)'
+    module.out = set(
+        src[src.index('gen/') + len('gen/'):]
+        for src in target_desc.get('outputs', [])
+    )
+    module.srcs.extend(
+      label_to_path(src)
+      for src in target_desc.get('inputs', [])
+    )
+    blueprint.add_module(module)
+    return module
 
 def _get_cflags(target):
     cflags = set(flag for flag in target.get('cflags', [])
diff --git a/tools/gen_build b/tools/gen_build
index 307d693..ade417f 100755
--- a/tools/gen_build
+++ b/tools/gen_build
@@ -314,7 +314,7 @@
   def __init__(self, type, name, gn_name=None, **kwargs):
     assert type in ('cc_binary', 'cc_library', 'cc_proto_library',
                     'proto_library', 'filegroup', 'alias',
-                    'pbzero_cc_proto_library',)
+                    'pbzero_cc_proto_library', 'genrule', )
     self.type = type
     self.name = name
     self.srcs = set()
@@ -459,13 +459,21 @@
 
       self.apply_module_sources_to_target(target, dep_desc)
     elif dep_desc['type'] == 'action':
-      (proto_target, cc_target) = self.create_target(dep_name)
-      if target.type == 'proto_library':
-        dep_target_name = proto_target.name
+      args = dep_desc['args']
+      if "gen_merged_sql_metrics" in dep_name:
+        dep_target = self.create_merged_sql_metrics_target(dep_name)
+        target.deps.add(Label("//third_party/perfetto:" + dep_target.name))
+      elif args[0].endswith('/protoc'):
+        (proto_target, cc_target) = self.create_proto_target(dep_name)
+        if target.type == 'proto_library':
+          dep_target_name = proto_target.name
+        else:
+          dep_target_name = cc_target.name
+        target.deps.add(
+            Label("//third_party/perfetto/protos:" + dep_target_name))
       else:
-        dep_target_name = cc_target.name
-      target.deps.add(
-          Label("//third_party/perfetto/protos:" + dep_target_name))
+        raise Error('Unsupported action in target %s: %s' % (dep_target_name,
+                                                            args))
     elif dep_desc['type'] == 'static_library':
       dep_target = self.create_target(dep_name)
       target.deps.add(Label("//third_party/perfetto:" + dep_target.name))
@@ -480,13 +488,33 @@
       raise Error('Unknown target name %s with type: %s' %
                   (dep_name, dep_desc['type']))
 
+  def create_merged_sql_metrics_target(self, gn_target_name):
+    target_desc = self.desc[gn_target_name]
+    gn_target_name_no_toolchain = label_without_toolchain(gn_target_name)
+    target = Target(
+      'genrule',
+      'gen_merged_sql_metrics',
+      gn_name=gn_target_name_no_toolchain,
+      outs=set(
+        Label(src[src.index('gen/') + len('gen/'):])
+        for src in target_desc.get('outputs', [])
+      ),
+      cmd = '$(location gen_merged_sql_metrics_py) --cpp_out=$@ $SRCS',
+      tools=[
+        'gen_merged_sql_metrics_py',
+      ],
+    )
+    target.srcs.update(
+      Label(label_to_path(src))
+      for src in target_desc.get('inputs', [])
+      if src not in self.action_generated_files
+    )
+    self.build.add_target(target)
+    return target
 
-  def create_action_target(self, gn_target_name):
+  def create_proto_target(self, gn_target_name):
     target_desc = self.desc[gn_target_name]
     args = target_desc['args']
-    if not args[0].endswith('/protoc'):
-      raise Error('Unsupported action in target %s: %s' % (gn_target_name,
-                                                          target_desc['args']))
 
     is_pbzero = any("pbzero" in arg for arg in args)
     gn_target_name_no_toolchain = label_without_toolchain(gn_target_name)
@@ -496,14 +524,14 @@
     pretty_target_name = pretty_target_name.replace("_zero_gen", "_zero")
 
     proto_target = Target(
-        'proto_library',
-        pretty_target_name,
-        gn_name=gn_target_name_no_toolchain,
-        is_pbzero=is_pbzero
+      'proto_library',
+      pretty_target_name,
+      gn_name=gn_target_name_no_toolchain,
+      is_pbzero=is_pbzero
     )
     proto_target.srcs.update([
-        Label(label_to_path(src).replace('protos/', ''))
-        for src in target_desc.get('sources', [])
+      Label(label_to_path(src).replace('protos/', ''))
+      for src in target_desc.get('sources', [])
     ])
     if not is_pbzero:
       proto_target.visibility.add("//visibility:public")
@@ -563,7 +591,12 @@
 
     target_desc = self.desc[gn_target_name]
     if target_desc['type'] == 'action':
-      return self.create_action_target(gn_target_name)
+      args = target_desc['args']
+      if args[0].endswith('/protoc'):
+        return self.create_proto_target(gn_target_name)
+      else:
+        raise Error('Unsupported action in target %s: %s' % (gn_target_name,
+                                                            args))
     elif target_desc['type'] == 'executable':
       target_type = 'cc_binary'
     elif target_desc['type'] == 'static_library':
diff --git a/tools/gen_merged_sql_metrics b/tools/gen_merged_sql_metrics
new file mode 100755
index 0000000..6ad35ea
--- /dev/null
+++ b/tools/gen_merged_sql_metrics
@@ -0,0 +1,107 @@
+#!/usr/bin/env python
+# 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.
+
+import argparse
+import os
+import sys
+
+# Converts the SQL metrics for trace processor into a C++ header with the SQL
+# as a string constant to allow trace processor to exectue the metrics.
+
+REPLACEMENT_HEADER = '''/*
+ * 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.
+ */
+
+/*
+ *******************************************************************************
+ * AUTOGENERATED BY tools/gen_merged_sql_metrics - DO NOT EDIT
+ *******************************************************************************
+ */
+'''
+
+NAMESPACE_BEGIN = '''
+namespace perfetto {
+namespace trace_processor {
+namespace metrics {
+'''
+
+FILE_TO_SQL_STRUCT = '''
+struct FileToSql {
+  const char* filename;
+  const char* sql;
+};
+'''
+
+NAMESPACE_END = '''
+}  // namespace metrics
+}  // namespace trace_processor
+}  // namsepace perfetto
+'''
+
+def filename_to_variable(filename):
+  return "k" + "".join([x.capitalize() for x in filename.split("_")])
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('--cpp_out', required=True)
+  parser.add_argument('sql_files', action='append')
+  args = parser.parse_args()
+
+  # Extract the SQL output from each file.
+  escaped_sql_outputs = {}
+  for file_name in args.sql_files:
+    with open(file_name, 'r') as f:
+      basename = os.path.basename(file_name)
+
+      # Escape any quote characters.
+      escaped_sql_outputs[basename] = "".join(f.readlines())
+
+  with open(args.cpp_out, 'w+') as output:
+    output.write(REPLACEMENT_HEADER)
+    output.write(NAMESPACE_BEGIN)
+
+    # Create the C++ variable for each SQL file.
+    for name, sql in escaped_sql_outputs.items():
+      variable = filename_to_variable(os.path.splitext(name)[0])
+      output.write('\nconst char {}[] = R"gendelimiter(\n{})gendelimiter";\n'
+        .format(variable, sql))
+
+    output.write(FILE_TO_SQL_STRUCT)
+
+    # Create mapping of filename to variable name for each variable.
+    output.write("\nconst FileToSql kFileToSql[] = {")
+    for name in escaped_sql_outputs.keys():
+      variable = filename_to_variable(os.path.splitext(name)[0])
+      output.write('\n  {{"{}", {}}},\n'.format(name, variable))
+    output.write("};\n")
+
+    output.write(NAMESPACE_END)
+
+  return 0
+
+if __name__ == '__main__':
+  sys.exit(main())