Merge "perfetto-ui: Remove engine worker proxy hacks"
diff --git a/Android.bp b/Android.bp
index 0915ccf..9365d34 100644
--- a/Android.bp
+++ b/Android.bp
@@ -18,15 +18,17 @@
 cc_library_shared {
   name: "libtraced_shared",
   srcs: [
-    ":perfetto_protos_perfetto_common_common_gen",
-    ":perfetto_protos_perfetto_config_config_gen",
-    ":perfetto_protos_perfetto_config_config_zero_gen",
+    ":perfetto_protos_perfetto_common_lite_gen",
+    ":perfetto_protos_perfetto_common_zero_gen",
+    ":perfetto_protos_perfetto_config_lite_gen",
+    ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
     ":perfetto_protos_perfetto_trace_chrome_zero_gen",
     ":perfetto_protos_perfetto_trace_filesystem_zero_gen",
     ":perfetto_protos_perfetto_trace_ftrace_zero_gen",
     ":perfetto_protos_perfetto_trace_minimal_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
     ":perfetto_protos_perfetto_trace_trusted_lite_gen",
     ":perfetto_protos_perfetto_trace_zero_gen",
     ":perfetto_src_ipc_wire_protocol_gen",
@@ -79,6 +81,7 @@
     "src/traced/probes/probes_data_source.cc",
     "src/traced/probes/probes_producer.cc",
     "src/traced/probes/ps/process_stats_data_source.cc",
+    "src/traced/probes/sys_stats/sys_stats_data_source.cc",
     "src/traced/service/service.cc",
     "src/tracing/core/chrome_config.cc",
     "src/tracing/core/commit_data_request.cc",
@@ -93,6 +96,7 @@
     "src/tracing/core/shared_memory_abi.cc",
     "src/tracing/core/shared_memory_arbiter_impl.cc",
     "src/tracing/core/sliced_protobuf_input_stream.cc",
+    "src/tracing/core/sys_stats_config.cc",
     "src/tracing/core/test_config.cc",
     "src/tracing/core/trace_buffer.cc",
     "src/tracing/core/trace_config.cc",
@@ -110,15 +114,17 @@
     "perfetto_src_tracing_ipc",
   ],
   generated_headers: [
-    "perfetto_protos_perfetto_common_common_gen_headers",
-    "perfetto_protos_perfetto_config_config_gen_headers",
-    "perfetto_protos_perfetto_config_config_zero_gen_headers",
+    "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_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
     "perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
     "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
     "perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
     "perfetto_protos_perfetto_trace_zero_gen_headers",
     "perfetto_src_ipc_wire_protocol_gen_headers",
@@ -136,15 +142,17 @@
 cc_binary {
   name: "perfetto",
   srcs: [
-    ":perfetto_protos_perfetto_common_common_gen",
-    ":perfetto_protos_perfetto_config_config_gen",
-    ":perfetto_protos_perfetto_config_config_zero_gen",
+    ":perfetto_protos_perfetto_common_lite_gen",
+    ":perfetto_protos_perfetto_common_zero_gen",
+    ":perfetto_protos_perfetto_config_lite_gen",
+    ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
     ":perfetto_protos_perfetto_trace_chrome_zero_gen",
     ":perfetto_protos_perfetto_trace_filesystem_zero_gen",
     ":perfetto_protos_perfetto_trace_ftrace_zero_gen",
     ":perfetto_protos_perfetto_trace_minimal_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
     ":perfetto_protos_perfetto_trace_trusted_lite_gen",
     ":perfetto_protos_perfetto_trace_zero_gen",
     ":perfetto_src_ipc_wire_protocol_gen",
@@ -191,6 +199,7 @@
     "src/tracing/core/shared_memory_abi.cc",
     "src/tracing/core/shared_memory_arbiter_impl.cc",
     "src/tracing/core/sliced_protobuf_input_stream.cc",
+    "src/tracing/core/sys_stats_config.cc",
     "src/tracing/core/test_config.cc",
     "src/tracing/core/trace_buffer.cc",
     "src/tracing/core/trace_config.cc",
@@ -214,15 +223,17 @@
     "libgtest_prod",
   ],
   generated_headers: [
-    "perfetto_protos_perfetto_common_common_gen_headers",
-    "perfetto_protos_perfetto_config_config_gen_headers",
-    "perfetto_protos_perfetto_config_config_zero_gen_headers",
+    "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_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
     "perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
     "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
     "perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
     "perfetto_protos_perfetto_trace_zero_gen_headers",
     "perfetto_src_ipc_wire_protocol_gen_headers",
@@ -267,9 +278,10 @@
 cc_test {
   name: "perfetto_integrationtests",
   srcs: [
-    ":perfetto_protos_perfetto_common_common_gen",
-    ":perfetto_protos_perfetto_config_config_gen",
-    ":perfetto_protos_perfetto_config_config_zero_gen",
+    ":perfetto_protos_perfetto_common_lite_gen",
+    ":perfetto_protos_perfetto_common_zero_gen",
+    ":perfetto_protos_perfetto_config_lite_gen",
+    ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
     ":perfetto_protos_perfetto_trace_chrome_zero_gen",
@@ -281,6 +293,8 @@
     ":perfetto_protos_perfetto_trace_minimal_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
     ":perfetto_protos_perfetto_trace_trusted_lite_gen",
     ":perfetto_protos_perfetto_trace_zero_gen",
     ":perfetto_src_ipc_wire_protocol_gen",
@@ -339,6 +353,7 @@
     "src/traced/probes/probes_data_source.cc",
     "src/traced/probes/probes_producer.cc",
     "src/traced/probes/ps/process_stats_data_source.cc",
+    "src/traced/probes/sys_stats/sys_stats_data_source.cc",
     "src/tracing/core/chrome_config.cc",
     "src/tracing/core/commit_data_request.cc",
     "src/tracing/core/data_source_config.cc",
@@ -352,6 +367,7 @@
     "src/tracing/core/shared_memory_abi.cc",
     "src/tracing/core/shared_memory_arbiter_impl.cc",
     "src/tracing/core/sliced_protobuf_input_stream.cc",
+    "src/tracing/core/sys_stats_config.cc",
     "src/tracing/core/test_config.cc",
     "src/tracing/core/trace_buffer.cc",
     "src/tracing/core/trace_config.cc",
@@ -375,9 +391,10 @@
     "perfetto_src_tracing_ipc",
   ],
   generated_headers: [
-    "perfetto_protos_perfetto_common_common_gen_headers",
-    "perfetto_protos_perfetto_config_config_gen_headers",
-    "perfetto_protos_perfetto_config_config_zero_gen_headers",
+    "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_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
@@ -389,6 +406,8 @@
     "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
     "perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
     "perfetto_protos_perfetto_trace_zero_gen_headers",
     "perfetto_src_ipc_wire_protocol_gen_headers",
@@ -408,11 +427,12 @@
   },
 }
 
-// GN target: //protos/perfetto/common:common_gen
+// GN target: //protos/perfetto/common:lite_gen
 genrule {
-  name: "perfetto_protos_perfetto_common_common_gen",
+  name: "perfetto_protos_perfetto_common_lite_gen",
   srcs: [
     "protos/perfetto/common/commit_data_request.proto",
+    "protos/perfetto/common/sys_stats_counters.proto",
   ],
   tools: [
     "aprotoc",
@@ -420,14 +440,16 @@
   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/perfetto/common/commit_data_request.pb.cc",
+    "external/perfetto/protos/perfetto/common/sys_stats_counters.pb.cc",
   ],
 }
 
-// GN target: //protos/perfetto/common:common_gen
+// GN target: //protos/perfetto/common:lite_gen
 genrule {
-  name: "perfetto_protos_perfetto_common_common_gen_headers",
+  name: "perfetto_protos_perfetto_common_lite_gen_headers",
   srcs: [
     "protos/perfetto/common/commit_data_request.proto",
+    "protos/perfetto/common/sys_stats_counters.proto",
   ],
   tools: [
     "aprotoc",
@@ -435,15 +457,55 @@
   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/perfetto/common/commit_data_request.pb.h",
+    "external/perfetto/protos/perfetto/common/sys_stats_counters.pb.h",
   ],
   export_include_dirs: [
     "protos",
   ],
 }
 
-// GN target: //protos/perfetto/config:config_gen
+// GN target: //protos/perfetto/common:zero_gen
 genrule {
-  name: "perfetto_protos_perfetto_config_config_gen",
+  name: "perfetto_protos_perfetto_common_zero_gen",
+  srcs: [
+    "protos/perfetto/common/commit_data_request.proto",
+    "protos/perfetto/common/sys_stats_counters.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/common/commit_data_request.pbzero.cc",
+    "external/perfetto/protos/perfetto/common/sys_stats_counters.pbzero.cc",
+  ],
+}
+
+// GN target: //protos/perfetto/common:zero_gen
+genrule {
+  name: "perfetto_protos_perfetto_common_zero_gen_headers",
+  srcs: [
+    "protos/perfetto/common/commit_data_request.proto",
+    "protos/perfetto/common/sys_stats_counters.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/common/commit_data_request.pbzero.h",
+    "external/perfetto/protos/perfetto/common/sys_stats_counters.pbzero.h",
+  ],
+  export_include_dirs: [
+    "protos",
+  ],
+}
+
+// GN target: //protos/perfetto/config:lite_gen
+genrule {
+  name: "perfetto_protos_perfetto_config_lite_gen",
   srcs: [
     "protos/perfetto/config/chrome/chrome_config.proto",
     "protos/perfetto/config/data_source_config.proto",
@@ -451,6 +513,7 @@
     "protos/perfetto/config/ftrace/ftrace_config.proto",
     "protos/perfetto/config/inode_file/inode_file_config.proto",
     "protos/perfetto/config/process_stats/process_stats_config.proto",
+    "protos/perfetto/config/sys_stats/sys_stats_config.proto",
     "protos/perfetto/config/test_config.proto",
     "protos/perfetto/config/trace_config.proto",
   ],
@@ -465,14 +528,15 @@
     "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pb.cc",
     "external/perfetto/protos/perfetto/config/inode_file/inode_file_config.pb.cc",
     "external/perfetto/protos/perfetto/config/process_stats/process_stats_config.pb.cc",
+    "external/perfetto/protos/perfetto/config/sys_stats/sys_stats_config.pb.cc",
     "external/perfetto/protos/perfetto/config/test_config.pb.cc",
     "external/perfetto/protos/perfetto/config/trace_config.pb.cc",
   ],
 }
 
-// GN target: //protos/perfetto/config:config_gen
+// GN target: //protos/perfetto/config:lite_gen
 genrule {
-  name: "perfetto_protos_perfetto_config_config_gen_headers",
+  name: "perfetto_protos_perfetto_config_lite_gen_headers",
   srcs: [
     "protos/perfetto/config/chrome/chrome_config.proto",
     "protos/perfetto/config/data_source_config.proto",
@@ -480,6 +544,7 @@
     "protos/perfetto/config/ftrace/ftrace_config.proto",
     "protos/perfetto/config/inode_file/inode_file_config.proto",
     "protos/perfetto/config/process_stats/process_stats_config.proto",
+    "protos/perfetto/config/sys_stats/sys_stats_config.proto",
     "protos/perfetto/config/test_config.proto",
     "protos/perfetto/config/trace_config.proto",
   ],
@@ -494,6 +559,7 @@
     "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pb.h",
     "external/perfetto/protos/perfetto/config/inode_file/inode_file_config.pb.h",
     "external/perfetto/protos/perfetto/config/process_stats/process_stats_config.pb.h",
+    "external/perfetto/protos/perfetto/config/sys_stats/sys_stats_config.pb.h",
     "external/perfetto/protos/perfetto/config/test_config.pb.h",
     "external/perfetto/protos/perfetto/config/trace_config.pb.h",
   ],
@@ -502,9 +568,9 @@
   ],
 }
 
-// GN target: //protos/perfetto/config:config_zero_gen
+// GN target: //protos/perfetto/config:zero_gen
 genrule {
-  name: "perfetto_protos_perfetto_config_config_zero_gen",
+  name: "perfetto_protos_perfetto_config_zero_gen",
   srcs: [
     "protos/perfetto/config/chrome/chrome_config.proto",
     "protos/perfetto/config/data_source_config.proto",
@@ -512,6 +578,7 @@
     "protos/perfetto/config/ftrace/ftrace_config.proto",
     "protos/perfetto/config/inode_file/inode_file_config.proto",
     "protos/perfetto/config/process_stats/process_stats_config.proto",
+    "protos/perfetto/config/sys_stats/sys_stats_config.proto",
     "protos/perfetto/config/test_config.proto",
     "protos/perfetto/config/trace_config.proto",
   ],
@@ -527,14 +594,15 @@
     "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pbzero.cc",
     "external/perfetto/protos/perfetto/config/inode_file/inode_file_config.pbzero.cc",
     "external/perfetto/protos/perfetto/config/process_stats/process_stats_config.pbzero.cc",
+    "external/perfetto/protos/perfetto/config/sys_stats/sys_stats_config.pbzero.cc",
     "external/perfetto/protos/perfetto/config/test_config.pbzero.cc",
     "external/perfetto/protos/perfetto/config/trace_config.pbzero.cc",
   ],
 }
 
-// GN target: //protos/perfetto/config:config_zero_gen
+// GN target: //protos/perfetto/config:zero_gen
 genrule {
-  name: "perfetto_protos_perfetto_config_config_zero_gen_headers",
+  name: "perfetto_protos_perfetto_config_zero_gen_headers",
   srcs: [
     "protos/perfetto/config/chrome/chrome_config.proto",
     "protos/perfetto/config/data_source_config.proto",
@@ -542,6 +610,7 @@
     "protos/perfetto/config/ftrace/ftrace_config.proto",
     "protos/perfetto/config/inode_file/inode_file_config.proto",
     "protos/perfetto/config/process_stats/process_stats_config.proto",
+    "protos/perfetto/config/sys_stats/sys_stats_config.proto",
     "protos/perfetto/config/test_config.proto",
     "protos/perfetto/config/trace_config.proto",
   ],
@@ -557,6 +626,7 @@
     "external/perfetto/protos/perfetto/config/ftrace/ftrace_config.pbzero.h",
     "external/perfetto/protos/perfetto/config/inode_file/inode_file_config.pbzero.h",
     "external/perfetto/protos/perfetto/config/process_stats/process_stats_config.pbzero.h",
+    "external/perfetto/protos/perfetto/config/sys_stats/sys_stats_config.pbzero.h",
     "external/perfetto/protos/perfetto/config/test_config.pbzero.h",
     "external/perfetto/protos/perfetto/config/trace_config.pbzero.h",
   ],
@@ -3062,6 +3132,74 @@
   ],
 }
 
+// GN target: //protos/perfetto/trace/sys_stats:lite_gen
+genrule {
+  name: "perfetto_protos_perfetto_trace_sys_stats_lite_gen",
+  srcs: [
+    "protos/perfetto/trace/sys_stats/sys_stats.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/perfetto/trace/sys_stats/sys_stats.pb.cc",
+  ],
+}
+
+// GN target: //protos/perfetto/trace/sys_stats:lite_gen
+genrule {
+  name: "perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
+  srcs: [
+    "protos/perfetto/trace/sys_stats/sys_stats.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/perfetto/trace/sys_stats/sys_stats.pb.h",
+  ],
+  export_include_dirs: [
+    "protos",
+  ],
+}
+
+// GN target: //protos/perfetto/trace/sys_stats:zero_gen
+genrule {
+  name: "perfetto_protos_perfetto_trace_sys_stats_zero_gen",
+  srcs: [
+    "protos/perfetto/trace/sys_stats/sys_stats.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/trace/sys_stats/sys_stats.pbzero.cc",
+  ],
+}
+
+// GN target: //protos/perfetto/trace/sys_stats:zero_gen
+genrule {
+  name: "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
+  srcs: [
+    "protos/perfetto/trace/sys_stats/sys_stats.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/trace/sys_stats/sys_stats.pbzero.h",
+  ],
+  export_include_dirs: [
+    "protos",
+  ],
+}
+
 // GN target: //protos/perfetto/trace:trusted_lite_gen
 genrule {
   name: "perfetto_protos_perfetto_trace_trusted_lite_gen",
@@ -3463,15 +3601,17 @@
 cc_library_static {
   name: "perfetto_src_tracing_ipc",
   srcs: [
-    ":perfetto_protos_perfetto_common_common_gen",
-    ":perfetto_protos_perfetto_config_config_gen",
-    ":perfetto_protos_perfetto_config_config_zero_gen",
+    ":perfetto_protos_perfetto_common_lite_gen",
+    ":perfetto_protos_perfetto_common_zero_gen",
+    ":perfetto_protos_perfetto_config_lite_gen",
+    ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
     ":perfetto_protos_perfetto_trace_chrome_zero_gen",
     ":perfetto_protos_perfetto_trace_filesystem_zero_gen",
     ":perfetto_protos_perfetto_trace_ftrace_zero_gen",
     ":perfetto_protos_perfetto_trace_minimal_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
     ":perfetto_protos_perfetto_trace_trusted_lite_gen",
     ":perfetto_protos_perfetto_trace_zero_gen",
     ":perfetto_src_ipc_wire_protocol_gen",
@@ -3513,6 +3653,7 @@
     "src/tracing/core/shared_memory_abi.cc",
     "src/tracing/core/shared_memory_arbiter_impl.cc",
     "src/tracing/core/sliced_protobuf_input_stream.cc",
+    "src/tracing/core/sys_stats_config.cc",
     "src/tracing/core/test_config.cc",
     "src/tracing/core/trace_buffer.cc",
     "src/tracing/core/trace_config.cc",
@@ -3539,29 +3680,33 @@
     "include",
   ],
   generated_headers: [
-    "perfetto_protos_perfetto_common_common_gen_headers",
-    "perfetto_protos_perfetto_config_config_gen_headers",
-    "perfetto_protos_perfetto_config_config_zero_gen_headers",
+    "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_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
     "perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
     "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
     "perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
     "perfetto_protos_perfetto_trace_zero_gen_headers",
     "perfetto_src_ipc_wire_protocol_gen_headers",
   ],
   export_generated_headers: [
-    "perfetto_protos_perfetto_common_common_gen_headers",
-    "perfetto_protos_perfetto_config_config_gen_headers",
-    "perfetto_protos_perfetto_config_config_zero_gen_headers",
+    "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_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
     "perfetto_protos_perfetto_trace_filesystem_zero_gen_headers",
     "perfetto_protos_perfetto_trace_ftrace_zero_gen_headers",
     "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
     "perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
     "perfetto_protos_perfetto_trace_zero_gen_headers",
     "perfetto_src_ipc_wire_protocol_gen_headers",
@@ -3579,13 +3724,15 @@
 cc_library_static {
   name: "perfetto_trace_protos",
   srcs: [
-    ":perfetto_protos_perfetto_config_config_gen",
+    ":perfetto_protos_perfetto_common_lite_gen",
+    ":perfetto_protos_perfetto_config_lite_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
     ":perfetto_protos_perfetto_trace_filesystem_lite_gen",
     ":perfetto_protos_perfetto_trace_ftrace_lite_gen",
     ":perfetto_protos_perfetto_trace_lite_gen",
     ":perfetto_protos_perfetto_trace_minimal_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_lite_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
   ],
   shared_libs: [
     "liblog",
@@ -3596,22 +3743,26 @@
     "include",
   ],
   generated_headers: [
-    "perfetto_protos_perfetto_config_config_gen_headers",
+    "perfetto_protos_perfetto_common_lite_gen_headers",
+    "perfetto_protos_perfetto_config_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_filesystem_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ftrace_lite_gen_headers",
     "perfetto_protos_perfetto_trace_lite_gen_headers",
     "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
   ],
   export_generated_headers: [
-    "perfetto_protos_perfetto_config_config_gen_headers",
+    "perfetto_protos_perfetto_common_lite_gen_headers",
+    "perfetto_protos_perfetto_config_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_filesystem_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ftrace_lite_gen_headers",
     "perfetto_protos_perfetto_trace_lite_gen_headers",
     "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
   ],
   defaults: [
     "perfetto_defaults",
@@ -3626,9 +3777,10 @@
 cc_test {
   name: "perfetto_unittests",
   srcs: [
-    ":perfetto_protos_perfetto_common_common_gen",
-    ":perfetto_protos_perfetto_config_config_gen",
-    ":perfetto_protos_perfetto_config_config_zero_gen",
+    ":perfetto_protos_perfetto_common_lite_gen",
+    ":perfetto_protos_perfetto_common_zero_gen",
+    ":perfetto_protos_perfetto_config_lite_gen",
+    ":perfetto_protos_perfetto_config_zero_gen",
     ":perfetto_protos_perfetto_ipc_ipc_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
     ":perfetto_protos_perfetto_trace_chrome_zero_gen",
@@ -3640,6 +3792,8 @@
     ":perfetto_protos_perfetto_trace_minimal_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_zero_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_zero_gen",
     ":perfetto_protos_perfetto_trace_trusted_lite_gen",
     ":perfetto_protos_perfetto_trace_zero_gen",
     ":perfetto_src_ipc_test_messages_gen",
@@ -3759,6 +3913,8 @@
     "src/traced/probes/probes_producer.cc",
     "src/traced/probes/ps/process_stats_data_source.cc",
     "src/traced/probes/ps/process_stats_data_source_unittest.cc",
+    "src/traced/probes/sys_stats/sys_stats_data_source.cc",
+    "src/traced/probes/sys_stats/sys_stats_data_source_unittest.cc",
     "src/tracing/core/chrome_config.cc",
     "src/tracing/core/commit_data_request.cc",
     "src/tracing/core/data_source_config.cc",
@@ -3780,6 +3936,7 @@
     "src/tracing/core/shared_memory_arbiter_impl_unittest.cc",
     "src/tracing/core/sliced_protobuf_input_stream.cc",
     "src/tracing/core/sliced_protobuf_input_stream_unittest.cc",
+    "src/tracing/core/sys_stats_config.cc",
     "src/tracing/core/test_config.cc",
     "src/tracing/core/trace_buffer.cc",
     "src/tracing/core/trace_buffer_unittest.cc",
@@ -3820,9 +3977,10 @@
     "perfetto_src_tracing_ipc",
   ],
   generated_headers: [
-    "perfetto_protos_perfetto_common_common_gen_headers",
-    "perfetto_protos_perfetto_config_config_gen_headers",
-    "perfetto_protos_perfetto_config_config_zero_gen_headers",
+    "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_ipc_ipc_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_zero_gen_headers",
@@ -3834,6 +3992,8 @@
     "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_zero_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_zero_gen_headers",
     "perfetto_protos_perfetto_trace_trusted_lite_gen_headers",
     "perfetto_protos_perfetto_trace_zero_gen_headers",
     "perfetto_src_ipc_test_messages_gen_headers",
@@ -3862,13 +4022,33 @@
 cc_binary_host {
   name: "trace_to_text",
   srcs: [
-    ":perfetto_protos_perfetto_config_config_gen",
+    ":perfetto_protos_perfetto_common_lite_gen",
+    ":perfetto_protos_perfetto_common_zero_gen",
+    ":perfetto_protos_perfetto_config_lite_gen",
     ":perfetto_protos_perfetto_trace_chrome_lite_gen",
     ":perfetto_protos_perfetto_trace_filesystem_lite_gen",
     ":perfetto_protos_perfetto_trace_ftrace_lite_gen",
     ":perfetto_protos_perfetto_trace_lite_gen",
     ":perfetto_protos_perfetto_trace_minimal_lite_gen",
     ":perfetto_protos_perfetto_trace_ps_lite_gen",
+    ":perfetto_protos_perfetto_trace_sys_stats_lite_gen",
+    "src/base/file_utils.cc",
+    "src/base/metatrace.cc",
+    "src/base/page_allocator.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",
+    "src/protozero/message.cc",
+    "src/protozero/message_handle.cc",
+    "src/protozero/proto_decoder.cc",
+    "src/protozero/proto_field_descriptor.cc",
+    "src/protozero/scattered_stream_null_delegate.cc",
+    "src/protozero/scattered_stream_writer.cc",
     "tools/trace_to_text/ftrace_event_formatter.cc",
     "tools/trace_to_text/ftrace_inode_handler.cc",
     "tools/trace_to_text/main.cc",
@@ -3878,14 +4058,20 @@
     "libprotobuf-cpp-full",
     "libprotobuf-cpp-lite",
   ],
+  static_libs: [
+    "libgtest_prod",
+  ],
   generated_headers: [
-    "perfetto_protos_perfetto_config_config_gen_headers",
+    "perfetto_protos_perfetto_common_lite_gen_headers",
+    "perfetto_protos_perfetto_common_zero_gen_headers",
+    "perfetto_protos_perfetto_config_lite_gen_headers",
     "perfetto_protos_perfetto_trace_chrome_lite_gen_headers",
     "perfetto_protos_perfetto_trace_filesystem_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ftrace_lite_gen_headers",
     "perfetto_protos_perfetto_trace_lite_gen_headers",
     "perfetto_protos_perfetto_trace_minimal_lite_gen_headers",
     "perfetto_protos_perfetto_trace_ps_lite_gen_headers",
+    "perfetto_protos_perfetto_trace_sys_stats_lite_gen_headers",
   ],
   defaults: [
     "perfetto_defaults",
diff --git a/include/perfetto/base/time.h b/include/perfetto/base/time.h
index d1895c1..760d494 100644
--- a/include/perfetto/base/time.h
+++ b/include/perfetto/base/time.h
@@ -53,6 +53,11 @@
 TimeNanos GetWallTimeNs();
 TimeNanos GetThreadCPUTimeNs();
 
+// TODO: Clock that counts time during suspend is not implemented on Windows.
+inline TimeNanos GetBootTimeNs() {
+  return GetWallTimeNs();
+}
+
 #elif PERFETTO_BUILDFLAG(PERFETTO_OS_MACOSX)
 
 inline TimeNanos GetWallTimeNs() {
@@ -66,6 +71,11 @@
   return TimeNanos(mach_absolute_time() * monotonic_timebase_factor);
 }
 
+// TODO: Clock that counts time during suspend is not implemented on Mac.
+inline TimeNanos GetBootTimeNs() {
+  return GetWallTimeNs();
+}
+
 inline TimeNanos GetThreadCPUTimeNs() {
   mach_port_t this_thread = mach_thread_self();
   mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
@@ -95,6 +105,11 @@
   return TimeNanos(0);
 }
 
+// TODO: Clock that counts time during suspend is not implemented on WASM.
+inline TimeNanos GetBootTimeNs() {
+  return GetWallTimeNs();
+}
+
 #else
 
 constexpr clockid_t kWallTimeClockSource = CLOCK_MONOTONIC;
@@ -105,6 +120,18 @@
   return FromPosixTimespec(ts);
 }
 
+// Return ns from boot. Conversely to GetWallTimeNs, this clock counts also time
+// during suspend (when supported).
+inline TimeNanos GetBootTimeNs() {
+  // Determine if CLOCK_BOOTTIME is available on the first call.
+  static const clockid_t kBootTimeClockSource = [] {
+    struct timespec ts = {};
+    int res = clock_gettime(CLOCK_BOOTTIME, &ts);
+    return res == 0 ? CLOCK_BOOTTIME : kWallTimeClockSource;
+  }();
+  return GetTimeInternalNs(kBootTimeClockSource);
+}
+
 inline TimeNanos GetWallTimeNs() {
   return GetTimeInternalNs(kWallTimeClockSource);
 }
diff --git a/include/perfetto/traced/BUILD.gn b/include/perfetto/traced/BUILD.gn
index fefc5fa..f7dc66f 100644
--- a/include/perfetto/traced/BUILD.gn
+++ b/include/perfetto/traced/BUILD.gn
@@ -18,3 +18,16 @@
     "traced.h",
   ]
 }
+
+source_set("sys_stats_counters") {
+  deps = [
+    "../../../gn:default_deps",
+  ]
+  public_deps = [
+    "../../../protos/perfetto/common:zero",
+    "../base",
+  ]
+  sources = [
+    "sys_stats_counters.h",
+  ]
+}
diff --git a/include/perfetto/traced/sys_stats_counters.h b/include/perfetto/traced/sys_stats_counters.h
new file mode 100644
index 0000000..5bb0974
--- /dev/null
+++ b/include/perfetto/traced/sys_stats_counters.h
@@ -0,0 +1,233 @@
+/*
+ * 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 INCLUDE_PERFETTO_TRACED_SYS_STATS_COUNTERS_H_
+#define INCLUDE_PERFETTO_TRACED_SYS_STATS_COUNTERS_H_
+
+#include "perfetto/base/utils.h"
+#include "perfetto/common/sys_stats_counters.pbzero.h"
+
+#include <vector>
+
+namespace perfetto {
+
+struct KeyAndId {
+  const char* str;
+  int id;
+};
+
+constexpr KeyAndId kMeminfoKeys[] = {
+    {"MemTotal", protos::pbzero::MeminfoCounters::MEMINFO_MEM_TOTAL},
+    {"MemFree", protos::pbzero::MeminfoCounters::MEMINFO_MEM_FREE},
+    {"MemAvailable", protos::pbzero::MeminfoCounters::MEMINFO_MEM_AVAILABLE},
+    {"Buffers", protos::pbzero::MeminfoCounters::MEMINFO_BUFFERS},
+    {"Cached", protos::pbzero::MeminfoCounters::MEMINFO_CACHED},
+    {"SwapCached", protos::pbzero::MeminfoCounters::MEMINFO_SWAP_CACHED},
+    {"Active", protos::pbzero::MeminfoCounters::MEMINFO_ACTIVE},
+    {"Inactive", protos::pbzero::MeminfoCounters::MEMINFO_INACTIVE},
+    {"Active(anon)", protos::pbzero::MeminfoCounters::MEMINFO_ACTIVE_ANON},
+    {"Inactive(anon)", protos::pbzero::MeminfoCounters::MEMINFO_INACTIVE_ANON},
+    {"Active(file)", protos::pbzero::MeminfoCounters::MEMINFO_ACTIVE_FILE},
+    {"Inactive(file)", protos::pbzero::MeminfoCounters::MEMINFO_INACTIVE_FILE},
+    {"Unevictable", protos::pbzero::MeminfoCounters::MEMINFO_UNEVICTABLE},
+    {"Mlocked", protos::pbzero::MeminfoCounters::MEMINFO_MLOCKED},
+    {"SwapTotal", protos::pbzero::MeminfoCounters::MEMINFO_SWAP_TOTAL},
+    {"SwapFree", protos::pbzero::MeminfoCounters::MEMINFO_SWAP_FREE},
+    {"Dirty", protos::pbzero::MeminfoCounters::MEMINFO_DIRTY},
+    {"Writeback", protos::pbzero::MeminfoCounters::MEMINFO_WRITEBACK},
+    {"AnonPages", protos::pbzero::MeminfoCounters::MEMINFO_ANON_PAGES},
+    {"Mapped", protos::pbzero::MeminfoCounters::MEMINFO_MAPPED},
+    {"Shmem", protos::pbzero::MeminfoCounters::MEMINFO_SHMEM},
+    {"Slab", protos::pbzero::MeminfoCounters::MEMINFO_SLAB},
+    {"SReclaimable", protos::pbzero::MeminfoCounters::MEMINFO_SLAB_RECLAIMABLE},
+    {"SUnreclaim", protos::pbzero::MeminfoCounters::MEMINFO_SLAB_UNRECLAIMABLE},
+    {"KernelStack", protos::pbzero::MeminfoCounters::MEMINFO_KERNEL_STACK},
+    {"PageTables", protos::pbzero::MeminfoCounters::MEMINFO_PAGE_TABLES},
+    {"CommitLimit", protos::pbzero::MeminfoCounters::MEMINFO_COMMIT_LIMIT},
+    {"Committed_AS", protos::pbzero::MeminfoCounters::MEMINFO_COMMITED_AS},
+    {"VmallocTotal", protos::pbzero::MeminfoCounters::MEMINFO_VMALLOC_TOTAL},
+    {"VmallocUsed", protos::pbzero::MeminfoCounters::MEMINFO_VMALLOC_USED},
+    {"VmallocChunk", protos::pbzero::MeminfoCounters::MEMINFO_VMALLOC_CHUNK},
+    {"CmaTotal", protos::pbzero::MeminfoCounters::MEMINFO_CMA_TOTAL},
+    {"CmaFree", protos::pbzero::MeminfoCounters::MEMINFO_CMA_FREE},
+};
+
+const KeyAndId kVmstatKeys[] = {
+    {"nr_free_pages", protos::pbzero::VmstatCounters::VMSTAT_NR_FREE_PAGES},
+    {"nr_alloc_batch", protos::pbzero::VmstatCounters::VMSTAT_NR_ALLOC_BATCH},
+    {"nr_inactive_anon",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_INACTIVE_ANON},
+    {"nr_active_anon", protos::pbzero::VmstatCounters::VMSTAT_NR_ACTIVE_ANON},
+    {"nr_inactive_file",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_INACTIVE_FILE},
+    {"nr_active_file", protos::pbzero::VmstatCounters::VMSTAT_NR_ACTIVE_FILE},
+    {"nr_unevictable", protos::pbzero::VmstatCounters::VMSTAT_NR_UNEVICTABLE},
+    {"nr_mlock", protos::pbzero::VmstatCounters::VMSTAT_NR_MLOCK},
+    {"nr_anon_pages", protos::pbzero::VmstatCounters::VMSTAT_NR_ANON_PAGES},
+    {"nr_mapped", protos::pbzero::VmstatCounters::VMSTAT_NR_MAPPED},
+    {"nr_file_pages", protos::pbzero::VmstatCounters::VMSTAT_NR_FILE_PAGES},
+    {"nr_dirty", protos::pbzero::VmstatCounters::VMSTAT_NR_DIRTY},
+    {"nr_writeback", protos::pbzero::VmstatCounters::VMSTAT_NR_WRITEBACK},
+    {"nr_slab_reclaimable",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_SLAB_RECLAIMABLE},
+    {"nr_slab_unreclaimable",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_SLAB_UNRECLAIMABLE},
+    {"nr_page_table_pages",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_PAGE_TABLE_PAGES},
+    {"nr_kernel_stack", protos::pbzero::VmstatCounters::VMSTAT_NR_KERNEL_STACK},
+    {"nr_overhead", protos::pbzero::VmstatCounters::VMSTAT_NR_OVERHEAD},
+    {"nr_unstable", protos::pbzero::VmstatCounters::VMSTAT_NR_UNSTABLE},
+    {"nr_bounce", protos::pbzero::VmstatCounters::VMSTAT_NR_BOUNCE},
+    {"nr_vmscan_write", protos::pbzero::VmstatCounters::VMSTAT_NR_VMSCAN_WRITE},
+    {"nr_vmscan_immediate_reclaim",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_VMSCAN_IMMEDIATE_RECLAIM},
+    {"nr_writeback_temp",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_WRITEBACK_TEMP},
+    {"nr_isolated_anon",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_ISOLATED_ANON},
+    {"nr_isolated_file",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_ISOLATED_FILE},
+    {"nr_shmem", protos::pbzero::VmstatCounters::VMSTAT_NR_SHMEM},
+    {"nr_dirtied", protos::pbzero::VmstatCounters::VMSTAT_NR_DIRTIED},
+    {"nr_written", protos::pbzero::VmstatCounters::VMSTAT_NR_WRITTEN},
+    {"nr_pages_scanned",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_PAGES_SCANNED},
+    {"workingset_refault",
+     protos::pbzero::VmstatCounters::VMSTAT_WORKINGSET_REFAULT},
+    {"workingset_activate",
+     protos::pbzero::VmstatCounters::VMSTAT_WORKINGSET_ACTIVATE},
+    {"workingset_nodereclaim",
+     protos::pbzero::VmstatCounters::VMSTAT_WORKINGSET_NODERECLAIM},
+    {"nr_anon_transparent_hugepages",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_ANON_TRANSPARENT_HUGEPAGES},
+    {"nr_free_cma", protos::pbzero::VmstatCounters::VMSTAT_NR_FREE_CMA},
+    {"nr_swapcache", protos::pbzero::VmstatCounters::VMSTAT_NR_SWAPCACHE},
+    {"nr_dirty_threshold",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_DIRTY_THRESHOLD},
+    {"nr_dirty_background_threshold",
+     protos::pbzero::VmstatCounters::VMSTAT_NR_DIRTY_BACKGROUND_THRESHOLD},
+    {"pgpgin", protos::pbzero::VmstatCounters::VMSTAT_PGPGIN},
+    {"pgpgout", protos::pbzero::VmstatCounters::VMSTAT_PGPGOUT},
+    {"pgpgoutclean", protos::pbzero::VmstatCounters::VMSTAT_PGPGOUTCLEAN},
+    {"pswpin", protos::pbzero::VmstatCounters::VMSTAT_PSWPIN},
+    {"pswpout", protos::pbzero::VmstatCounters::VMSTAT_PSWPOUT},
+    {"pgalloc_dma", protos::pbzero::VmstatCounters::VMSTAT_PGALLOC_DMA},
+    {"pgalloc_normal", protos::pbzero::VmstatCounters::VMSTAT_PGALLOC_NORMAL},
+    {"pgalloc_movable", protos::pbzero::VmstatCounters::VMSTAT_PGALLOC_MOVABLE},
+    {"pgfree", protos::pbzero::VmstatCounters::VMSTAT_PGFREE},
+    {"pgactivate", protos::pbzero::VmstatCounters::VMSTAT_PGACTIVATE},
+    {"pgdeactivate", protos::pbzero::VmstatCounters::VMSTAT_PGDEACTIVATE},
+    {"pgfault", protos::pbzero::VmstatCounters::VMSTAT_PGFAULT},
+    {"pgmajfault", protos::pbzero::VmstatCounters::VMSTAT_PGMAJFAULT},
+    {"pgrefill_dma", protos::pbzero::VmstatCounters::VMSTAT_PGREFILL_DMA},
+    {"pgrefill_normal", protos::pbzero::VmstatCounters::VMSTAT_PGREFILL_NORMAL},
+    {"pgrefill_movable",
+     protos::pbzero::VmstatCounters::VMSTAT_PGREFILL_MOVABLE},
+    {"pgsteal_kswapd_dma",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSTEAL_KSWAPD_DMA},
+    {"pgsteal_kswapd_normal",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSTEAL_KSWAPD_NORMAL},
+    {"pgsteal_kswapd_movable",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSTEAL_KSWAPD_MOVABLE},
+    {"pgsteal_direct_dma",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSTEAL_DIRECT_DMA},
+    {"pgsteal_direct_normal",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSTEAL_DIRECT_NORMAL},
+    {"pgsteal_direct_movable",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSTEAL_DIRECT_MOVABLE},
+    {"pgscan_kswapd_dma",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSCAN_KSWAPD_DMA},
+    {"pgscan_kswapd_normal",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSCAN_KSWAPD_NORMAL},
+    {"pgscan_kswapd_movable",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSCAN_KSWAPD_MOVABLE},
+    {"pgscan_direct_dma",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSCAN_DIRECT_DMA},
+    {"pgscan_direct_normal",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSCAN_DIRECT_NORMAL},
+    {"pgscan_direct_movable",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSCAN_DIRECT_MOVABLE},
+    {"pgscan_direct_throttle",
+     protos::pbzero::VmstatCounters::VMSTAT_PGSCAN_DIRECT_THROTTLE},
+    {"pginodesteal", protos::pbzero::VmstatCounters::VMSTAT_PGINODESTEAL},
+    {"slabs_scanned", protos::pbzero::VmstatCounters::VMSTAT_SLABS_SCANNED},
+    {"kswapd_inodesteal",
+     protos::pbzero::VmstatCounters::VMSTAT_KSWAPD_INODESTEAL},
+    {"kswapd_low_wmark_hit_quickly",
+     protos::pbzero::VmstatCounters::VMSTAT_KSWAPD_LOW_WMARK_HIT_QUICKLY},
+    {"kswapd_high_wmark_hit_quickly",
+     protos::pbzero::VmstatCounters::VMSTAT_KSWAPD_HIGH_WMARK_HIT_QUICKLY},
+    {"pageoutrun", protos::pbzero::VmstatCounters::VMSTAT_PAGEOUTRUN},
+    {"allocstall", protos::pbzero::VmstatCounters::VMSTAT_ALLOCSTALL},
+    {"pgrotated", protos::pbzero::VmstatCounters::VMSTAT_PGROTATED},
+    {"drop_pagecache", protos::pbzero::VmstatCounters::VMSTAT_DROP_PAGECACHE},
+    {"drop_slab", protos::pbzero::VmstatCounters::VMSTAT_DROP_SLAB},
+    {"pgmigrate_success",
+     protos::pbzero::VmstatCounters::VMSTAT_PGMIGRATE_SUCCESS},
+    {"pgmigrate_fail", protos::pbzero::VmstatCounters::VMSTAT_PGMIGRATE_FAIL},
+    {"compact_migrate_scanned",
+     protos::pbzero::VmstatCounters::VMSTAT_COMPACT_MIGRATE_SCANNED},
+    {"compact_free_scanned",
+     protos::pbzero::VmstatCounters::VMSTAT_COMPACT_FREE_SCANNED},
+    {"compact_isolated",
+     protos::pbzero::VmstatCounters::VMSTAT_COMPACT_ISOLATED},
+    {"compact_stall", protos::pbzero::VmstatCounters::VMSTAT_COMPACT_STALL},
+    {"compact_fail", protos::pbzero::VmstatCounters::VMSTAT_COMPACT_FAIL},
+    {"compact_success", protos::pbzero::VmstatCounters::VMSTAT_COMPACT_SUCCESS},
+    {"compact_daemon_wake",
+     protos::pbzero::VmstatCounters::VMSTAT_COMPACT_DAEMON_WAKE},
+    {"unevictable_pgs_culled",
+     protos::pbzero::VmstatCounters::VMSTAT_UNEVICTABLE_PGS_CULLED},
+    {"unevictable_pgs_scanned",
+     protos::pbzero::VmstatCounters::VMSTAT_UNEVICTABLE_PGS_SCANNED},
+    {"unevictable_pgs_rescued",
+     protos::pbzero::VmstatCounters::VMSTAT_UNEVICTABLE_PGS_RESCUED},
+    {"unevictable_pgs_mlocked",
+     protos::pbzero::VmstatCounters::VMSTAT_UNEVICTABLE_PGS_MLOCKED},
+    {"unevictable_pgs_munlocked",
+     protos::pbzero::VmstatCounters::VMSTAT_UNEVICTABLE_PGS_MUNLOCKED},
+    {"unevictable_pgs_cleared",
+     protos::pbzero::VmstatCounters::VMSTAT_UNEVICTABLE_PGS_CLEARED},
+    {"unevictable_pgs_stranded",
+     protos::pbzero::VmstatCounters::VMSTAT_UNEVICTABLE_PGS_STRANDED},
+};
+
+// Returns a lookup table of meminfo counter names addressable by counter id.
+inline std::vector<const char*> BuildMeminfoCounterNames() {
+  int max_id = 0;
+  for (size_t i = 0; i < base::ArraySize(kMeminfoKeys); i++)
+    max_id = std::max(max_id, kMeminfoKeys[i].id);
+  std::vector<const char*> v;
+  v.resize(static_cast<size_t>(max_id) + 1);
+  for (size_t i = 0; i < base::ArraySize(kMeminfoKeys); i++)
+    v[static_cast<size_t>(kMeminfoKeys[i].id)] = kMeminfoKeys[i].str;
+  return v;
+}
+
+inline std::vector<const char*> BuildVmstatCounterNames() {
+  int max_id = 0;
+  for (size_t i = 0; i < base::ArraySize(kVmstatKeys); i++)
+    max_id = std::max(max_id, kVmstatKeys[i].id);
+  std::vector<const char*> v;
+  v.resize(static_cast<size_t>(max_id) + 1);
+  for (size_t i = 0; i < base::ArraySize(kVmstatKeys); i++)
+    v[static_cast<size_t>(kVmstatKeys[i].id)] = kVmstatKeys[i].str;
+  return v;
+}
+
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACED_SYS_STATS_COUNTERS_H_
diff --git a/include/perfetto/tracing/core/chrome_config.h b/include/perfetto/tracing/core/chrome_config.h
index ceac1a6..693546d 100644
--- a/include/perfetto/tracing/core/chrome_config.h
+++ b/include/perfetto/tracing/core/chrome_config.h
@@ -69,4 +69,5 @@
 };
 
 }  // namespace perfetto
+
 #endif  // INCLUDE_PERFETTO_TRACING_CORE_CHROME_CONFIG_H_
diff --git a/include/perfetto/tracing/core/commit_data_request.h b/include/perfetto/tracing/core/commit_data_request.h
index 3f0042f..d112d01 100644
--- a/include/perfetto/tracing/core/commit_data_request.h
+++ b/include/perfetto/tracing/core/commit_data_request.h
@@ -205,4 +205,5 @@
 };
 
 }  // namespace perfetto
+
 #endif  // INCLUDE_PERFETTO_TRACING_CORE_COMMIT_DATA_REQUEST_H_
diff --git a/include/perfetto/tracing/core/data_source_config.h b/include/perfetto/tracing/core/data_source_config.h
index fda2fb0..e816948 100644
--- a/include/perfetto/tracing/core/data_source_config.h
+++ b/include/perfetto/tracing/core/data_source_config.h
@@ -39,6 +39,7 @@
 #include "perfetto/tracing/core/ftrace_config.h"
 #include "perfetto/tracing/core/inode_file_config.h"
 #include "perfetto/tracing/core/process_stats_config.h"
+#include "perfetto/tracing/core/sys_stats_config.h"
 #include "perfetto/tracing/core/test_config.h"
 
 // Forward declarations for protobuf types.
@@ -50,6 +51,7 @@
 class InodeFileConfig;
 class InodeFileConfig_MountPointMappingEntry;
 class ProcessStatsConfig;
+class SysStatsConfig;
 class TestConfig;
 }  // namespace protos
 }  // namespace perfetto
@@ -99,6 +101,9 @@
     return &process_stats_config_;
   }
 
+  const SysStatsConfig& sys_stats_config() const { return sys_stats_config_; }
+  SysStatsConfig* mutable_sys_stats_config() { return &sys_stats_config_; }
+
   const std::string& legacy_config() const { return legacy_config_; }
   void set_legacy_config(const std::string& value) { legacy_config_ = value; }
 
@@ -114,6 +119,7 @@
   ChromeConfig chrome_config_ = {};
   InodeFileConfig inode_file_config_ = {};
   ProcessStatsConfig process_stats_config_ = {};
+  SysStatsConfig sys_stats_config_ = {};
   std::string legacy_config_ = {};
   TestConfig for_testing_ = {};
 
@@ -123,4 +129,5 @@
 };
 
 }  // namespace perfetto
+
 #endif  // INCLUDE_PERFETTO_TRACING_CORE_DATA_SOURCE_CONFIG_H_
diff --git a/include/perfetto/tracing/core/data_source_descriptor.h b/include/perfetto/tracing/core/data_source_descriptor.h
index 1678e79..db4e0de 100644
--- a/include/perfetto/tracing/core/data_source_descriptor.h
+++ b/include/perfetto/tracing/core/data_source_descriptor.h
@@ -73,4 +73,5 @@
 };
 
 }  // namespace perfetto
+
 #endif  // INCLUDE_PERFETTO_TRACING_CORE_DATA_SOURCE_DESCRIPTOR_H_
diff --git a/include/perfetto/tracing/core/ftrace_config.h b/include/perfetto/tracing/core/ftrace_config.h
index 5ed5715..794314a 100644
--- a/include/perfetto/tracing/core/ftrace_config.h
+++ b/include/perfetto/tracing/core/ftrace_config.h
@@ -105,4 +105,5 @@
 };
 
 }  // namespace perfetto
+
 #endif  // INCLUDE_PERFETTO_TRACING_CORE_FTRACE_CONFIG_H_
diff --git a/include/perfetto/tracing/core/inode_file_config.h b/include/perfetto/tracing/core/inode_file_config.h
index e376624..34f3e0a 100644
--- a/include/perfetto/tracing/core/inode_file_config.h
+++ b/include/perfetto/tracing/core/inode_file_config.h
@@ -140,4 +140,5 @@
 };
 
 }  // namespace perfetto
+
 #endif  // INCLUDE_PERFETTO_TRACING_CORE_INODE_FILE_CONFIG_H_
diff --git a/include/perfetto/tracing/core/process_stats_config.h b/include/perfetto/tracing/core/process_stats_config.h
index c89ee86..7adf410 100644
--- a/include/perfetto/tracing/core/process_stats_config.h
+++ b/include/perfetto/tracing/core/process_stats_config.h
@@ -90,4 +90,5 @@
 };
 
 }  // namespace perfetto
+
 #endif  // INCLUDE_PERFETTO_TRACING_CORE_PROCESS_STATS_CONFIG_H_
diff --git a/include/perfetto/tracing/core/sys_stats_config.h b/include/perfetto/tracing/core/sys_stats_config.h
new file mode 100644
index 0000000..a5deb9b
--- /dev/null
+++ b/include/perfetto/tracing/core/sys_stats_config.h
@@ -0,0 +1,257 @@
+/*
+ * 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.
+ */
+
+/*******************************************************************************
+ * AUTOGENERATED - DO NOT EDIT
+ *******************************************************************************
+ * This file has been generated from the protobuf message
+ * perfetto/config/sys_stats/sys_stats_config.proto
+ * by
+ * ../../tools/proto_to_cpp/proto_to_cpp.cc.
+ * If you need to make changes here, change the .proto file and then run
+ * ./tools/gen_tracing_cpp_headers_from_protos.py
+ */
+
+#ifndef INCLUDE_PERFETTO_TRACING_CORE_SYS_STATS_CONFIG_H_
+#define INCLUDE_PERFETTO_TRACING_CORE_SYS_STATS_CONFIG_H_
+
+#include <stdint.h>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "perfetto/base/export.h"
+
+#include "perfetto/tracing/core/sys_stats_counters.h"
+
+// Forward declarations for protobuf types.
+namespace perfetto {
+namespace protos {
+class SysStatsConfig;
+}
+}  // namespace perfetto
+
+namespace perfetto {
+
+class PERFETTO_EXPORT SysStatsConfig {
+ public:
+  enum MeminfoCounters {
+    MEMINFO_UNSPECIFIED = 0,
+    MEMINFO_MEM_TOTAL = 1,
+    MEMINFO_MEM_FREE = 2,
+    MEMINFO_MEM_AVAILABLE = 3,
+    MEMINFO_BUFFERS = 4,
+    MEMINFO_CACHED = 5,
+    MEMINFO_SWAP_CACHED = 6,
+    MEMINFO_ACTIVE = 7,
+    MEMINFO_INACTIVE = 8,
+    MEMINFO_ACTIVE_ANON = 9,
+    MEMINFO_INACTIVE_ANON = 10,
+    MEMINFO_ACTIVE_FILE = 11,
+    MEMINFO_INACTIVE_FILE = 12,
+    MEMINFO_UNEVICTABLE = 13,
+    MEMINFO_MLOCKED = 14,
+    MEMINFO_SWAP_TOTAL = 15,
+    MEMINFO_SWAP_FREE = 16,
+    MEMINFO_DIRTY = 17,
+    MEMINFO_WRITEBACK = 18,
+    MEMINFO_ANON_PAGES = 19,
+    MEMINFO_MAPPED = 20,
+    MEMINFO_SHMEM = 21,
+    MEMINFO_SLAB = 22,
+    MEMINFO_SLAB_RECLAIMABLE = 23,
+    MEMINFO_SLAB_UNRECLAIMABLE = 24,
+    MEMINFO_KERNEL_STACK = 25,
+    MEMINFO_PAGE_TABLES = 26,
+    MEMINFO_COMMIT_LIMIT = 27,
+    MEMINFO_COMMITED_AS = 28,
+    MEMINFO_VMALLOC_TOTAL = 29,
+    MEMINFO_VMALLOC_USED = 30,
+    MEMINFO_VMALLOC_CHUNK = 31,
+    MEMINFO_CMA_TOTAL = 32,
+    MEMINFO_CMA_FREE = 33,
+  };
+  enum VmstatCounters {
+    VMSTAT_UNSPECIFIED = 0,
+    VMSTAT_NR_FREE_PAGES = 1,
+    VMSTAT_NR_ALLOC_BATCH = 2,
+    VMSTAT_NR_INACTIVE_ANON = 3,
+    VMSTAT_NR_ACTIVE_ANON = 4,
+    VMSTAT_NR_INACTIVE_FILE = 5,
+    VMSTAT_NR_ACTIVE_FILE = 6,
+    VMSTAT_NR_UNEVICTABLE = 7,
+    VMSTAT_NR_MLOCK = 8,
+    VMSTAT_NR_ANON_PAGES = 9,
+    VMSTAT_NR_MAPPED = 10,
+    VMSTAT_NR_FILE_PAGES = 11,
+    VMSTAT_NR_DIRTY = 12,
+    VMSTAT_NR_WRITEBACK = 13,
+    VMSTAT_NR_SLAB_RECLAIMABLE = 14,
+    VMSTAT_NR_SLAB_UNRECLAIMABLE = 15,
+    VMSTAT_NR_PAGE_TABLE_PAGES = 16,
+    VMSTAT_NR_KERNEL_STACK = 17,
+    VMSTAT_NR_OVERHEAD = 18,
+    VMSTAT_NR_UNSTABLE = 19,
+    VMSTAT_NR_BOUNCE = 20,
+    VMSTAT_NR_VMSCAN_WRITE = 21,
+    VMSTAT_NR_VMSCAN_IMMEDIATE_RECLAIM = 22,
+    VMSTAT_NR_WRITEBACK_TEMP = 23,
+    VMSTAT_NR_ISOLATED_ANON = 24,
+    VMSTAT_NR_ISOLATED_FILE = 25,
+    VMSTAT_NR_SHMEM = 26,
+    VMSTAT_NR_DIRTIED = 27,
+    VMSTAT_NR_WRITTEN = 28,
+    VMSTAT_NR_PAGES_SCANNED = 29,
+    VMSTAT_WORKINGSET_REFAULT = 30,
+    VMSTAT_WORKINGSET_ACTIVATE = 31,
+    VMSTAT_WORKINGSET_NODERECLAIM = 32,
+    VMSTAT_NR_ANON_TRANSPARENT_HUGEPAGES = 33,
+    VMSTAT_NR_FREE_CMA = 34,
+    VMSTAT_NR_SWAPCACHE = 35,
+    VMSTAT_NR_DIRTY_THRESHOLD = 36,
+    VMSTAT_NR_DIRTY_BACKGROUND_THRESHOLD = 37,
+    VMSTAT_PGPGIN = 38,
+    VMSTAT_PGPGOUT = 39,
+    VMSTAT_PGPGOUTCLEAN = 40,
+    VMSTAT_PSWPIN = 41,
+    VMSTAT_PSWPOUT = 42,
+    VMSTAT_PGALLOC_DMA = 43,
+    VMSTAT_PGALLOC_NORMAL = 44,
+    VMSTAT_PGALLOC_MOVABLE = 45,
+    VMSTAT_PGFREE = 46,
+    VMSTAT_PGACTIVATE = 47,
+    VMSTAT_PGDEACTIVATE = 48,
+    VMSTAT_PGFAULT = 49,
+    VMSTAT_PGMAJFAULT = 50,
+    VMSTAT_PGREFILL_DMA = 51,
+    VMSTAT_PGREFILL_NORMAL = 52,
+    VMSTAT_PGREFILL_MOVABLE = 53,
+    VMSTAT_PGSTEAL_KSWAPD_DMA = 54,
+    VMSTAT_PGSTEAL_KSWAPD_NORMAL = 55,
+    VMSTAT_PGSTEAL_KSWAPD_MOVABLE = 56,
+    VMSTAT_PGSTEAL_DIRECT_DMA = 57,
+    VMSTAT_PGSTEAL_DIRECT_NORMAL = 58,
+    VMSTAT_PGSTEAL_DIRECT_MOVABLE = 59,
+    VMSTAT_PGSCAN_KSWAPD_DMA = 60,
+    VMSTAT_PGSCAN_KSWAPD_NORMAL = 61,
+    VMSTAT_PGSCAN_KSWAPD_MOVABLE = 62,
+    VMSTAT_PGSCAN_DIRECT_DMA = 63,
+    VMSTAT_PGSCAN_DIRECT_NORMAL = 64,
+    VMSTAT_PGSCAN_DIRECT_MOVABLE = 65,
+    VMSTAT_PGSCAN_DIRECT_THROTTLE = 66,
+    VMSTAT_PGINODESTEAL = 67,
+    VMSTAT_SLABS_SCANNED = 68,
+    VMSTAT_KSWAPD_INODESTEAL = 69,
+    VMSTAT_KSWAPD_LOW_WMARK_HIT_QUICKLY = 70,
+    VMSTAT_KSWAPD_HIGH_WMARK_HIT_QUICKLY = 71,
+    VMSTAT_PAGEOUTRUN = 72,
+    VMSTAT_ALLOCSTALL = 73,
+    VMSTAT_PGROTATED = 74,
+    VMSTAT_DROP_PAGECACHE = 75,
+    VMSTAT_DROP_SLAB = 76,
+    VMSTAT_PGMIGRATE_SUCCESS = 77,
+    VMSTAT_PGMIGRATE_FAIL = 78,
+    VMSTAT_COMPACT_MIGRATE_SCANNED = 79,
+    VMSTAT_COMPACT_FREE_SCANNED = 80,
+    VMSTAT_COMPACT_ISOLATED = 81,
+    VMSTAT_COMPACT_STALL = 82,
+    VMSTAT_COMPACT_FAIL = 83,
+    VMSTAT_COMPACT_SUCCESS = 84,
+    VMSTAT_COMPACT_DAEMON_WAKE = 85,
+    VMSTAT_UNEVICTABLE_PGS_CULLED = 86,
+    VMSTAT_UNEVICTABLE_PGS_SCANNED = 87,
+    VMSTAT_UNEVICTABLE_PGS_RESCUED = 88,
+    VMSTAT_UNEVICTABLE_PGS_MLOCKED = 89,
+    VMSTAT_UNEVICTABLE_PGS_MUNLOCKED = 90,
+    VMSTAT_UNEVICTABLE_PGS_CLEARED = 91,
+    VMSTAT_UNEVICTABLE_PGS_STRANDED = 92,
+  };
+  enum StatCounters {
+    STAT_UNSPECIFIED = 0,
+    STAT_CPU_TIMES = 1,
+    STAT_IRQ_COUNTS = 2,
+    STAT_SOFTIRQ_COUNTS = 3,
+    STAT_FORK_COUNT = 4,
+  };
+  SysStatsConfig();
+  ~SysStatsConfig();
+  SysStatsConfig(SysStatsConfig&&) noexcept;
+  SysStatsConfig& operator=(SysStatsConfig&&);
+  SysStatsConfig(const SysStatsConfig&);
+  SysStatsConfig& operator=(const SysStatsConfig&);
+
+  // Conversion methods from/to the corresponding protobuf types.
+  void FromProto(const perfetto::protos::SysStatsConfig&);
+  void ToProto(perfetto::protos::SysStatsConfig*) const;
+
+  uint32_t meminfo_period_ms() const { return meminfo_period_ms_; }
+  void set_meminfo_period_ms(uint32_t value) { meminfo_period_ms_ = value; }
+
+  int meminfo_counters_size() const {
+    return static_cast<int>(meminfo_counters_.size());
+  }
+  const std::vector<MeminfoCounters>& meminfo_counters() const {
+    return meminfo_counters_;
+  }
+  MeminfoCounters* add_meminfo_counters() {
+    meminfo_counters_.emplace_back();
+    return &meminfo_counters_.back();
+  }
+
+  uint32_t vmstat_period_ms() const { return vmstat_period_ms_; }
+  void set_vmstat_period_ms(uint32_t value) { vmstat_period_ms_ = value; }
+
+  int vmstat_counters_size() const {
+    return static_cast<int>(vmstat_counters_.size());
+  }
+  const std::vector<VmstatCounters>& vmstat_counters() const {
+    return vmstat_counters_;
+  }
+  VmstatCounters* add_vmstat_counters() {
+    vmstat_counters_.emplace_back();
+    return &vmstat_counters_.back();
+  }
+
+  uint32_t stat_period_ms() const { return stat_period_ms_; }
+  void set_stat_period_ms(uint32_t value) { stat_period_ms_ = value; }
+
+  int stat_counters_size() const {
+    return static_cast<int>(stat_counters_.size());
+  }
+  const std::vector<StatCounters>& stat_counters() const {
+    return stat_counters_;
+  }
+  StatCounters* add_stat_counters() {
+    stat_counters_.emplace_back();
+    return &stat_counters_.back();
+  }
+
+ private:
+  uint32_t meminfo_period_ms_ = {};
+  std::vector<MeminfoCounters> meminfo_counters_;
+  uint32_t vmstat_period_ms_ = {};
+  std::vector<VmstatCounters> vmstat_counters_;
+  uint32_t stat_period_ms_ = {};
+  std::vector<StatCounters> stat_counters_;
+
+  // Allows to preserve unknown protobuf fields for compatibility
+  // with future versions of .proto files.
+  std::string unknown_fields_;
+};
+
+}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACING_CORE_SYS_STATS_CONFIG_H_
diff --git a/include/perfetto/tracing/core/sys_stats_counters.h b/include/perfetto/tracing/core/sys_stats_counters.h
new file mode 100644
index 0000000..e27b5f2
--- /dev/null
+++ b/include/perfetto/tracing/core/sys_stats_counters.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+/*******************************************************************************
+ * AUTOGENERATED - DO NOT EDIT
+ *******************************************************************************
+ * This file has been generated from the protobuf message
+ * perfetto/common/sys_stats_counters.proto
+ * by
+ * ../../tools/proto_to_cpp/proto_to_cpp.cc.
+ * If you need to make changes here, change the .proto file and then run
+ * ./tools/gen_tracing_cpp_headers_from_protos.py
+ */
+
+#ifndef INCLUDE_PERFETTO_TRACING_CORE_SYS_STATS_COUNTERS_H_
+#define INCLUDE_PERFETTO_TRACING_CORE_SYS_STATS_COUNTERS_H_
+
+#include <stdint.h>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "perfetto/base/export.h"
+
+// Forward declarations for protobuf types.
+namespace perfetto {
+namespace protos {}
+}  // namespace perfetto
+
+namespace perfetto {}  // namespace perfetto
+
+#endif  // INCLUDE_PERFETTO_TRACING_CORE_SYS_STATS_COUNTERS_H_
diff --git a/include/perfetto/tracing/core/test_config.h b/include/perfetto/tracing/core/test_config.h
index 4fdfe8e..24e272d 100644
--- a/include/perfetto/tracing/core/test_config.h
+++ b/include/perfetto/tracing/core/test_config.h
@@ -89,4 +89,5 @@
 };
 
 }  // namespace perfetto
+
 #endif  // INCLUDE_PERFETTO_TRACING_CORE_TEST_CONFIG_H_
diff --git a/include/perfetto/tracing/core/trace_config.h b/include/perfetto/tracing/core/trace_config.h
index d94b72a..df0a89e 100644
--- a/include/perfetto/tracing/core/trace_config.h
+++ b/include/perfetto/tracing/core/trace_config.h
@@ -49,6 +49,7 @@
 class InodeFileConfig;
 class InodeFileConfig_MountPointMappingEntry;
 class ProcessStatsConfig;
+class SysStatsConfig;
 class TestConfig;
 class TraceConfig_ProducerConfig;
 class TraceConfig_StatsdMetadata;
@@ -319,4 +320,5 @@
 };
 
 }  // namespace perfetto
+
 #endif  // INCLUDE_PERFETTO_TRACING_CORE_TRACE_CONFIG_H_
diff --git a/protos/perfetto/common/BUILD.gn b/protos/perfetto/common/BUILD.gn
index 9a07e5a..38603fd 100644
--- a/protos/perfetto/common/BUILD.gn
+++ b/protos/perfetto/common/BUILD.gn
@@ -14,14 +14,25 @@
 
 import("../../../gn/perfetto.gni")
 import("../../../gn/proto_library.gni")
+import("../../../src/protozero/protozero_library.gni")
+
+common_sources = [
+  "commit_data_request.proto",
+  "sys_stats_counters.proto",
+]
 
 # Proto messages that are required by the IPC service definitions but have also
 # a C++ counterpart in tracing/core (i.e. are used also for the non-IPC cases).
-proto_library("common") {
+proto_library("lite") {
   generate_python = false
   proto_in_dir = "$perfetto_root_path/protos"
   proto_out_dir = "$perfetto_root_path/protos"
-  sources = [
-    "commit_data_request.proto",
-  ]
+  sources = common_sources
+}
+
+protozero_library("zero") {
+  proto_in_dir = "$perfetto_root_path/protos"
+  proto_out_dir = "$perfetto_root_path/protos"
+  sources = common_sources
+  generator_plugin_options = "wrapper_namespace=pbzero"
 }
diff --git a/protos/perfetto/common/sys_stats_counters.proto b/protos/perfetto/common/sys_stats_counters.proto
new file mode 100644
index 0000000..2e482e7
--- /dev/null
+++ b/protos/perfetto/common/sys_stats_counters.proto
@@ -0,0 +1,158 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package perfetto.protos;
+
+// When editing entries here remember also to update "sys_stats_counters.h" with
+// the corresponding string definitions for the actual /proc files parser.
+
+// Counter definitions for Linux's /proc/meminfo.
+enum MeminfoCounters {
+  MEMINFO_UNSPECIFIED = 0;
+  MEMINFO_MEM_TOTAL = 1;
+  MEMINFO_MEM_FREE = 2;
+  MEMINFO_MEM_AVAILABLE = 3;
+  MEMINFO_BUFFERS = 4;
+  MEMINFO_CACHED = 5;
+  MEMINFO_SWAP_CACHED = 6;
+  MEMINFO_ACTIVE = 7;
+  MEMINFO_INACTIVE = 8;
+  MEMINFO_ACTIVE_ANON = 9;
+  MEMINFO_INACTIVE_ANON = 10;
+  MEMINFO_ACTIVE_FILE = 11;
+  MEMINFO_INACTIVE_FILE = 12;
+  MEMINFO_UNEVICTABLE = 13;
+  MEMINFO_MLOCKED = 14;
+  MEMINFO_SWAP_TOTAL = 15;
+  MEMINFO_SWAP_FREE = 16;
+  MEMINFO_DIRTY = 17;
+  MEMINFO_WRITEBACK = 18;
+  MEMINFO_ANON_PAGES = 19;
+  MEMINFO_MAPPED = 20;
+  MEMINFO_SHMEM = 21;
+  MEMINFO_SLAB = 22;
+  MEMINFO_SLAB_RECLAIMABLE = 23;
+  MEMINFO_SLAB_UNRECLAIMABLE = 24;
+  MEMINFO_KERNEL_STACK = 25;
+  MEMINFO_PAGE_TABLES = 26;
+  MEMINFO_COMMIT_LIMIT = 27;
+  MEMINFO_COMMITED_AS = 28;
+  MEMINFO_VMALLOC_TOTAL = 29;
+  MEMINFO_VMALLOC_USED = 30;
+  MEMINFO_VMALLOC_CHUNK = 31;
+  MEMINFO_CMA_TOTAL = 32;
+  MEMINFO_CMA_FREE = 33;
+}
+
+// Counter definitions for Linux's /proc/vmstat.
+enum VmstatCounters {
+  VMSTAT_UNSPECIFIED = 0;
+  VMSTAT_NR_FREE_PAGES = 1;
+  VMSTAT_NR_ALLOC_BATCH = 2;
+  VMSTAT_NR_INACTIVE_ANON = 3;
+  VMSTAT_NR_ACTIVE_ANON = 4;
+  VMSTAT_NR_INACTIVE_FILE = 5;
+  VMSTAT_NR_ACTIVE_FILE = 6;
+  VMSTAT_NR_UNEVICTABLE = 7;
+  VMSTAT_NR_MLOCK = 8;
+  VMSTAT_NR_ANON_PAGES = 9;
+  VMSTAT_NR_MAPPED = 10;
+  VMSTAT_NR_FILE_PAGES = 11;
+  VMSTAT_NR_DIRTY = 12;
+  VMSTAT_NR_WRITEBACK = 13;
+  VMSTAT_NR_SLAB_RECLAIMABLE = 14;
+  VMSTAT_NR_SLAB_UNRECLAIMABLE = 15;
+  VMSTAT_NR_PAGE_TABLE_PAGES = 16;
+  VMSTAT_NR_KERNEL_STACK = 17;
+  VMSTAT_NR_OVERHEAD = 18;
+  VMSTAT_NR_UNSTABLE = 19;
+  VMSTAT_NR_BOUNCE = 20;
+  VMSTAT_NR_VMSCAN_WRITE = 21;
+  VMSTAT_NR_VMSCAN_IMMEDIATE_RECLAIM = 22;
+  VMSTAT_NR_WRITEBACK_TEMP = 23;
+  VMSTAT_NR_ISOLATED_ANON = 24;
+  VMSTAT_NR_ISOLATED_FILE = 25;
+  VMSTAT_NR_SHMEM = 26;
+  VMSTAT_NR_DIRTIED = 27;
+  VMSTAT_NR_WRITTEN = 28;
+  VMSTAT_NR_PAGES_SCANNED = 29;
+  VMSTAT_WORKINGSET_REFAULT = 30;
+  VMSTAT_WORKINGSET_ACTIVATE = 31;
+  VMSTAT_WORKINGSET_NODERECLAIM = 32;
+  VMSTAT_NR_ANON_TRANSPARENT_HUGEPAGES = 33;
+  VMSTAT_NR_FREE_CMA = 34;
+  VMSTAT_NR_SWAPCACHE = 35;
+  VMSTAT_NR_DIRTY_THRESHOLD = 36;
+  VMSTAT_NR_DIRTY_BACKGROUND_THRESHOLD = 37;
+  VMSTAT_PGPGIN = 38;
+  VMSTAT_PGPGOUT = 39;
+  VMSTAT_PGPGOUTCLEAN = 40;
+  VMSTAT_PSWPIN = 41;
+  VMSTAT_PSWPOUT = 42;
+  VMSTAT_PGALLOC_DMA = 43;
+  VMSTAT_PGALLOC_NORMAL = 44;
+  VMSTAT_PGALLOC_MOVABLE = 45;
+  VMSTAT_PGFREE = 46;
+  VMSTAT_PGACTIVATE = 47;
+  VMSTAT_PGDEACTIVATE = 48;
+  VMSTAT_PGFAULT = 49;
+  VMSTAT_PGMAJFAULT = 50;
+  VMSTAT_PGREFILL_DMA = 51;
+  VMSTAT_PGREFILL_NORMAL = 52;
+  VMSTAT_PGREFILL_MOVABLE = 53;
+  VMSTAT_PGSTEAL_KSWAPD_DMA = 54;
+  VMSTAT_PGSTEAL_KSWAPD_NORMAL = 55;
+  VMSTAT_PGSTEAL_KSWAPD_MOVABLE = 56;
+  VMSTAT_PGSTEAL_DIRECT_DMA = 57;
+  VMSTAT_PGSTEAL_DIRECT_NORMAL = 58;
+  VMSTAT_PGSTEAL_DIRECT_MOVABLE = 59;
+  VMSTAT_PGSCAN_KSWAPD_DMA = 60;
+  VMSTAT_PGSCAN_KSWAPD_NORMAL = 61;
+  VMSTAT_PGSCAN_KSWAPD_MOVABLE = 62;
+  VMSTAT_PGSCAN_DIRECT_DMA = 63;
+  VMSTAT_PGSCAN_DIRECT_NORMAL = 64;
+  VMSTAT_PGSCAN_DIRECT_MOVABLE = 65;
+  VMSTAT_PGSCAN_DIRECT_THROTTLE = 66;
+  VMSTAT_PGINODESTEAL = 67;
+  VMSTAT_SLABS_SCANNED = 68;
+  VMSTAT_KSWAPD_INODESTEAL = 69;
+  VMSTAT_KSWAPD_LOW_WMARK_HIT_QUICKLY = 70;
+  VMSTAT_KSWAPD_HIGH_WMARK_HIT_QUICKLY = 71;
+  VMSTAT_PAGEOUTRUN = 72;
+  VMSTAT_ALLOCSTALL = 73;
+  VMSTAT_PGROTATED = 74;
+  VMSTAT_DROP_PAGECACHE = 75;
+  VMSTAT_DROP_SLAB = 76;
+  VMSTAT_PGMIGRATE_SUCCESS = 77;
+  VMSTAT_PGMIGRATE_FAIL = 78;
+  VMSTAT_COMPACT_MIGRATE_SCANNED = 79;
+  VMSTAT_COMPACT_FREE_SCANNED = 80;
+  VMSTAT_COMPACT_ISOLATED = 81;
+  VMSTAT_COMPACT_STALL = 82;
+  VMSTAT_COMPACT_FAIL = 83;
+  VMSTAT_COMPACT_SUCCESS = 84;
+  VMSTAT_COMPACT_DAEMON_WAKE = 85;
+  VMSTAT_UNEVICTABLE_PGS_CULLED = 86;
+  VMSTAT_UNEVICTABLE_PGS_SCANNED = 87;
+  VMSTAT_UNEVICTABLE_PGS_RESCUED = 88;
+  VMSTAT_UNEVICTABLE_PGS_MLOCKED = 89;
+  VMSTAT_UNEVICTABLE_PGS_MUNLOCKED = 90;
+  VMSTAT_UNEVICTABLE_PGS_CLEARED = 91;
+  VMSTAT_UNEVICTABLE_PGS_STRANDED = 92;
+}
\ No newline at end of file
diff --git a/protos/perfetto/config/BUILD.gn b/protos/perfetto/config/BUILD.gn
index 74fa6c1..db89822 100644
--- a/protos/perfetto/config/BUILD.gn
+++ b/protos/perfetto/config/BUILD.gn
@@ -16,10 +16,13 @@
 import("../../../gn/proto_library.gni")
 import("../../../src/protozero/protozero_library.gni")
 
-proto_library("config") {
+proto_library("lite") {
   generate_python = false
   proto_in_dir = "$perfetto_root_path/protos"
   proto_out_dir = "$perfetto_root_path/protos"
+  deps = [
+    "../common:lite",
+  ]
   sources = [
     "chrome/chrome_config.proto",
     "data_source_config.proto",
@@ -27,14 +30,18 @@
     "ftrace/ftrace_config.proto",
     "inode_file/inode_file_config.proto",
     "process_stats/process_stats_config.proto",
+    "sys_stats/sys_stats_config.proto",
     "test_config.proto",
     "trace_config.proto",
   ]
 }
 
-protozero_library("config_zero") {
+protozero_library("zero") {
   proto_in_dir = "$perfetto_root_path/protos"
   proto_out_dir = "$perfetto_root_path/protos"
+  deps = [
+    "../common:zero",
+  ]
   sources = [
     "chrome/chrome_config.proto",
     "data_source_config.proto",
@@ -42,6 +49,7 @@
     "ftrace/ftrace_config.proto",
     "inode_file/inode_file_config.proto",
     "process_stats/process_stats_config.proto",
+    "sys_stats/sys_stats_config.proto",
     "test_config.proto",
     "trace_config.proto",
   ]
diff --git a/protos/perfetto/config/data_source_config.proto b/protos/perfetto/config/data_source_config.proto
index 35b8fe1..1688179 100644
--- a/protos/perfetto/config/data_source_config.proto
+++ b/protos/perfetto/config/data_source_config.proto
@@ -23,7 +23,9 @@
 import "perfetto/config/ftrace/ftrace_config.proto";
 import "perfetto/config/inode_file/inode_file_config.proto";
 import "perfetto/config/process_stats/process_stats_config.proto";
+import "perfetto/config/sys_stats/sys_stats_config.proto";
 import "perfetto/config/test_config.proto";
+
 // When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
 // to reflect changes in the corresponding C++ headers.
 
@@ -60,6 +62,7 @@
   optional ChromeConfig chrome_config = 101;
   optional InodeFileConfig inode_file_config = 102;
   optional ProcessStatsConfig process_stats_config = 103;
+  optional SysStatsConfig sys_stats_config = 104;
 
   // This is a fallback mechanism to send a free-form text config to the
   // producer. In theory this should never be needed. All the code that
diff --git a/protos/perfetto/config/perfetto_config.proto b/protos/perfetto/config/perfetto_config.proto
index e99a626..91f4aad 100644
--- a/protos/perfetto/config/perfetto_config.proto
+++ b/protos/perfetto/config/perfetto_config.proto
@@ -11,6 +11,147 @@
 
 package perfetto.protos;
 
+// Begin of protos/perfetto/common/sys_stats_counters.proto
+
+// When editing entries here remember also to update "sys_stats_counters.h" with
+// the corresponding string definitions for the actual /proc files parser.
+
+// Counter definitions for Linux's /proc/meminfo.
+enum MeminfoCounters {
+  MEMINFO_UNSPECIFIED = 0;
+  MEMINFO_MEM_TOTAL = 1;
+  MEMINFO_MEM_FREE = 2;
+  MEMINFO_MEM_AVAILABLE = 3;
+  MEMINFO_BUFFERS = 4;
+  MEMINFO_CACHED = 5;
+  MEMINFO_SWAP_CACHED = 6;
+  MEMINFO_ACTIVE = 7;
+  MEMINFO_INACTIVE = 8;
+  MEMINFO_ACTIVE_ANON = 9;
+  MEMINFO_INACTIVE_ANON = 10;
+  MEMINFO_ACTIVE_FILE = 11;
+  MEMINFO_INACTIVE_FILE = 12;
+  MEMINFO_UNEVICTABLE = 13;
+  MEMINFO_MLOCKED = 14;
+  MEMINFO_SWAP_TOTAL = 15;
+  MEMINFO_SWAP_FREE = 16;
+  MEMINFO_DIRTY = 17;
+  MEMINFO_WRITEBACK = 18;
+  MEMINFO_ANON_PAGES = 19;
+  MEMINFO_MAPPED = 20;
+  MEMINFO_SHMEM = 21;
+  MEMINFO_SLAB = 22;
+  MEMINFO_SLAB_RECLAIMABLE = 23;
+  MEMINFO_SLAB_UNRECLAIMABLE = 24;
+  MEMINFO_KERNEL_STACK = 25;
+  MEMINFO_PAGE_TABLES = 26;
+  MEMINFO_COMMIT_LIMIT = 27;
+  MEMINFO_COMMITED_AS = 28;
+  MEMINFO_VMALLOC_TOTAL = 29;
+  MEMINFO_VMALLOC_USED = 30;
+  MEMINFO_VMALLOC_CHUNK = 31;
+  MEMINFO_CMA_TOTAL = 32;
+  MEMINFO_CMA_FREE = 33;
+}
+
+// Counter definitions for Linux's /proc/vmstat.
+enum VmstatCounters {
+  VMSTAT_UNSPECIFIED = 0;
+  VMSTAT_NR_FREE_PAGES = 1;
+  VMSTAT_NR_ALLOC_BATCH = 2;
+  VMSTAT_NR_INACTIVE_ANON = 3;
+  VMSTAT_NR_ACTIVE_ANON = 4;
+  VMSTAT_NR_INACTIVE_FILE = 5;
+  VMSTAT_NR_ACTIVE_FILE = 6;
+  VMSTAT_NR_UNEVICTABLE = 7;
+  VMSTAT_NR_MLOCK = 8;
+  VMSTAT_NR_ANON_PAGES = 9;
+  VMSTAT_NR_MAPPED = 10;
+  VMSTAT_NR_FILE_PAGES = 11;
+  VMSTAT_NR_DIRTY = 12;
+  VMSTAT_NR_WRITEBACK = 13;
+  VMSTAT_NR_SLAB_RECLAIMABLE = 14;
+  VMSTAT_NR_SLAB_UNRECLAIMABLE = 15;
+  VMSTAT_NR_PAGE_TABLE_PAGES = 16;
+  VMSTAT_NR_KERNEL_STACK = 17;
+  VMSTAT_NR_OVERHEAD = 18;
+  VMSTAT_NR_UNSTABLE = 19;
+  VMSTAT_NR_BOUNCE = 20;
+  VMSTAT_NR_VMSCAN_WRITE = 21;
+  VMSTAT_NR_VMSCAN_IMMEDIATE_RECLAIM = 22;
+  VMSTAT_NR_WRITEBACK_TEMP = 23;
+  VMSTAT_NR_ISOLATED_ANON = 24;
+  VMSTAT_NR_ISOLATED_FILE = 25;
+  VMSTAT_NR_SHMEM = 26;
+  VMSTAT_NR_DIRTIED = 27;
+  VMSTAT_NR_WRITTEN = 28;
+  VMSTAT_NR_PAGES_SCANNED = 29;
+  VMSTAT_WORKINGSET_REFAULT = 30;
+  VMSTAT_WORKINGSET_ACTIVATE = 31;
+  VMSTAT_WORKINGSET_NODERECLAIM = 32;
+  VMSTAT_NR_ANON_TRANSPARENT_HUGEPAGES = 33;
+  VMSTAT_NR_FREE_CMA = 34;
+  VMSTAT_NR_SWAPCACHE = 35;
+  VMSTAT_NR_DIRTY_THRESHOLD = 36;
+  VMSTAT_NR_DIRTY_BACKGROUND_THRESHOLD = 37;
+  VMSTAT_PGPGIN = 38;
+  VMSTAT_PGPGOUT = 39;
+  VMSTAT_PGPGOUTCLEAN = 40;
+  VMSTAT_PSWPIN = 41;
+  VMSTAT_PSWPOUT = 42;
+  VMSTAT_PGALLOC_DMA = 43;
+  VMSTAT_PGALLOC_NORMAL = 44;
+  VMSTAT_PGALLOC_MOVABLE = 45;
+  VMSTAT_PGFREE = 46;
+  VMSTAT_PGACTIVATE = 47;
+  VMSTAT_PGDEACTIVATE = 48;
+  VMSTAT_PGFAULT = 49;
+  VMSTAT_PGMAJFAULT = 50;
+  VMSTAT_PGREFILL_DMA = 51;
+  VMSTAT_PGREFILL_NORMAL = 52;
+  VMSTAT_PGREFILL_MOVABLE = 53;
+  VMSTAT_PGSTEAL_KSWAPD_DMA = 54;
+  VMSTAT_PGSTEAL_KSWAPD_NORMAL = 55;
+  VMSTAT_PGSTEAL_KSWAPD_MOVABLE = 56;
+  VMSTAT_PGSTEAL_DIRECT_DMA = 57;
+  VMSTAT_PGSTEAL_DIRECT_NORMAL = 58;
+  VMSTAT_PGSTEAL_DIRECT_MOVABLE = 59;
+  VMSTAT_PGSCAN_KSWAPD_DMA = 60;
+  VMSTAT_PGSCAN_KSWAPD_NORMAL = 61;
+  VMSTAT_PGSCAN_KSWAPD_MOVABLE = 62;
+  VMSTAT_PGSCAN_DIRECT_DMA = 63;
+  VMSTAT_PGSCAN_DIRECT_NORMAL = 64;
+  VMSTAT_PGSCAN_DIRECT_MOVABLE = 65;
+  VMSTAT_PGSCAN_DIRECT_THROTTLE = 66;
+  VMSTAT_PGINODESTEAL = 67;
+  VMSTAT_SLABS_SCANNED = 68;
+  VMSTAT_KSWAPD_INODESTEAL = 69;
+  VMSTAT_KSWAPD_LOW_WMARK_HIT_QUICKLY = 70;
+  VMSTAT_KSWAPD_HIGH_WMARK_HIT_QUICKLY = 71;
+  VMSTAT_PAGEOUTRUN = 72;
+  VMSTAT_ALLOCSTALL = 73;
+  VMSTAT_PGROTATED = 74;
+  VMSTAT_DROP_PAGECACHE = 75;
+  VMSTAT_DROP_SLAB = 76;
+  VMSTAT_PGMIGRATE_SUCCESS = 77;
+  VMSTAT_PGMIGRATE_FAIL = 78;
+  VMSTAT_COMPACT_MIGRATE_SCANNED = 79;
+  VMSTAT_COMPACT_FREE_SCANNED = 80;
+  VMSTAT_COMPACT_ISOLATED = 81;
+  VMSTAT_COMPACT_STALL = 82;
+  VMSTAT_COMPACT_FAIL = 83;
+  VMSTAT_COMPACT_SUCCESS = 84;
+  VMSTAT_COMPACT_DAEMON_WAKE = 85;
+  VMSTAT_UNEVICTABLE_PGS_CULLED = 86;
+  VMSTAT_UNEVICTABLE_PGS_SCANNED = 87;
+  VMSTAT_UNEVICTABLE_PGS_RESCUED = 88;
+  VMSTAT_UNEVICTABLE_PGS_MLOCKED = 89;
+  VMSTAT_UNEVICTABLE_PGS_MUNLOCKED = 90;
+  VMSTAT_UNEVICTABLE_PGS_CLEARED = 91;
+  VMSTAT_UNEVICTABLE_PGS_STRANDED = 92;
+}
+// End of protos/perfetto/common/sys_stats_counters.proto
+
 // Begin of protos/perfetto/config/chrome/chrome_config.proto
 
 // When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
@@ -22,6 +163,79 @@
 
 // End of protos/perfetto/config/chrome/chrome_config.proto
 
+// Begin of protos/perfetto/config/data_source_config.proto
+
+
+// When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
+// to reflect changes in the corresponding C++ headers.
+
+// The configuration that is passed to each data source when starting tracing.
+message DataSourceConfig {
+  // Data source unique name, e.g., "linux.ftrace". This must match
+  // the name passed by the data source when it registers (see
+  // RegisterDataSource()).
+  optional string name = 1;
+
+  // The index of the logging buffer where TracePacket(s) will be stored.
+  // This field doesn't make a major difference for the Producer(s). The final
+  // logging buffers, in fact, are completely owned by the Service. We just ask
+  // the Producer to copy this number into the chunk headers it emits, so that
+  // the Service can quickly identify the buffer where to move the chunks into
+  // without expensive lookups on its fastpath.
+  optional uint32 target_buffer = 2;
+
+  // Set by the service to indicate the duration of the trace.
+  // DO NOT SET in consumer as this will be overridden by the service.
+  optional uint32 trace_duration_ms = 3;
+
+  // Set by the service to indicate which tracing session the data source
+  // belongs to. The intended use case for this is checking if two data sources,
+  // one of which produces metadata for the other one, belong to the same trace
+  // session and hence should be linked together.
+  // This field was introduced in Aug 2018 after Android P.
+  optional uint64 tracing_session_id = 4;
+
+  // Keeep the lower IDs (up to 99) for fields that are *not* specific to
+  // data-sources and needs to be processed by the traced daemon.
+
+  optional FtraceConfig ftrace_config = 100;
+  optional ChromeConfig chrome_config = 101;
+  optional InodeFileConfig inode_file_config = 102;
+  optional ProcessStatsConfig process_stats_config = 103;
+  optional SysStatsConfig sys_stats_config = 104;
+
+  // This is a fallback mechanism to send a free-form text config to the
+  // producer. In theory this should never be needed. All the code that
+  // is part of the platform (i.e. traced service) is supposed to *not* truncate
+  // the trace config proto and propagate unknown fields. However, if anything
+  // in the pipeline (client or backend) ends up breaking this forward compat
+  // plan, this field will become the escape hatch to allow future data sources
+  // to get some meaningful configuration.
+  optional string legacy_config = 1000;
+
+  // This field is only used for testing.
+  optional TestConfig for_testing =
+      536870911;  // 2^29 - 1, max field id for protos.
+}
+
+// End of protos/perfetto/config/data_source_config.proto
+
+// Begin of protos/perfetto/config/ftrace/ftrace_config.proto
+
+// When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
+// to reflect changes in the corresponding C++ headers.
+
+message FtraceConfig {
+  repeated string ftrace_events = 1;
+  repeated string atrace_categories = 2;
+  repeated string atrace_apps = 3;
+  // *Per-CPU* buffer size.
+  optional uint32 buffer_size_kb = 10;
+  optional uint32 drain_period_ms = 11;
+}
+
+// End of protos/perfetto/config/ftrace/ftrace_config.proto
+
 // Begin of protos/perfetto/config/inode_file/inode_file_config.proto
 
 // When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
@@ -85,76 +299,45 @@
 
 // End of protos/perfetto/config/process_stats/process_stats_config.proto
 
-// Begin of protos/perfetto/config/data_source_config.proto
+// Begin of protos/perfetto/config/sys_stats/sys_stats_config.proto
+
 
 // When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
 // to reflect changes in the corresponding C++ headers.
 
-// The configuration that is passed to each data source when starting tracing.
-message DataSourceConfig {
-  // Data source unique name, e.g., "linux.ftrace". This must match
-  // the name passed by the data source when it registers (see
-  // RegisterDataSource()).
-  optional string name = 1;
+// This file defines the configuration for the Linux /proc poller data source,
+// which injects counters in the trace.
+// Counters that are needed in the trace must be explicitly listed in the
+// *_counters fields. This is to avoid spamming the trace with all counters
+// at all times.
+// The sampling rate is configurable. All polling rates (*_period_ms) need
+// to be integer multiples of each other.
+// OK:     [10ms, 10ms, 10ms],  [10ms, 20ms, 10ms],  [10ms, 20ms, 60ms]
+// Not OK: [10ms, 10ms, 11ms],  [10ms, 15ms, 20ms]
+message SysStatsConfig {
+  // Polls /proc/meminfo every X ms, if non-zero.
+  optional uint32 meminfo_period_ms = 1;
 
-  // The index of the logging buffer where TracePacket(s) will be stored.
-  // This field doesn't make a major difference for the Producer(s). The final
-  // logging buffers, in fact, are completely owned by the Service. We just ask
-  // the Producer to copy this number into the chunk headers it emits, so that
-  // the Service can quickly identify the buffer where to move the chunks into
-  // without expensive lookups on its fastpath.
-  optional uint32 target_buffer = 2;
+  // Only the counters specified below are reported.
+  repeated MeminfoCounters meminfo_counters = 2;
 
-  // Set by the service to indicate the duration of the trace.
-  // DO NOT SET in consumer as this will be overridden by the service.
-  optional uint32 trace_duration_ms = 3;
+  // Polls /proc/vmstat every X ms, if non-zero.
+  optional uint32 vmstat_period_ms = 3;
+  repeated VmstatCounters vmstat_counters = 4;
 
-  // Set by the service to indicate which tracing session the data source
-  // belongs to. The intended use case for this is checking if two data sources,
-  // one of which produces metadata for the other one, belong to the same trace
-  // session and hence should be linked together.
-  // This field was introduced in Aug 2018 after Android P.
-  optional uint64 tracing_session_id = 4;
-
-  // Keeep the lower IDs (up to 99) for fields that are *not* specific to
-  // data-sources and needs to be processed by the traced daemon.
-
-  optional FtraceConfig ftrace_config = 100;
-  optional ChromeConfig chrome_config = 101;
-  optional InodeFileConfig inode_file_config = 102;
-  optional ProcessStatsConfig process_stats_config = 103;
-
-  // This is a fallback mechanism to send a free-form text config to the
-  // producer. In theory this should never be needed. All the code that
-  // is part of the platform (i.e. traced service) is supposed to *not* truncate
-  // the trace config proto and propagate unknown fields. However, if anything
-  // in the pipeline (client or backend) ends up breaking this forward compat
-  // plan, this field will become the escape hatch to allow future data sources
-  // to get some meaningful configuration.
-  optional string legacy_config = 1000;
-
-  // This field is only used for testing.
-  optional TestConfig for_testing =
-      536870911;  // 2^29 - 1, max field id for protos.
+  // Pols /proc/stat every X ms, if non-zero.
+  optional uint32 stat_period_ms = 5;
+  enum StatCounters {
+    STAT_UNSPECIFIED = 0;
+    STAT_CPU_TIMES = 1;
+    STAT_IRQ_COUNTS = 2;
+    STAT_SOFTIRQ_COUNTS = 3;
+    STAT_FORK_COUNT = 4;
+  }
+  repeated StatCounters stat_counters = 6;
 }
 
-// End of protos/perfetto/config/data_source_config.proto
-
-// Begin of protos/perfetto/config/ftrace/ftrace_config.proto
-
-// When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
-// to reflect changes in the corresponding C++ headers.
-
-message FtraceConfig {
-  repeated string ftrace_events = 1;
-  repeated string atrace_categories = 2;
-  repeated string atrace_apps = 3;
-  // *Per-CPU* buffer size.
-  optional uint32 buffer_size_kb = 10;
-  optional uint32 drain_period_ms = 11;
-}
-
-// End of protos/perfetto/config/ftrace/ftrace_config.proto
+// End of protos/perfetto/config/sys_stats/sys_stats_config.proto
 
 // Begin of protos/perfetto/config/test_config.proto
 
diff --git a/protos/perfetto/config/sys_stats/sys_stats_config.proto b/protos/perfetto/config/sys_stats/sys_stats_config.proto
new file mode 100644
index 0000000..ef958b3
--- /dev/null
+++ b/protos/perfetto/config/sys_stats/sys_stats_config.proto
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+
+package perfetto.protos;
+
+import "perfetto/common/sys_stats_counters.proto";
+
+// When editing this file run ./tools/gen_tracing_cpp_headers_from_protos.py
+// to reflect changes in the corresponding C++ headers.
+
+// This file defines the configuration for the Linux /proc poller data source,
+// which injects counters in the trace.
+// Counters that are needed in the trace must be explicitly listed in the
+// *_counters fields. This is to avoid spamming the trace with all counters
+// at all times.
+// The sampling rate is configurable. All polling rates (*_period_ms) need
+// to be integer multiples of each other.
+// OK:     [10ms, 10ms, 10ms],  [10ms, 20ms, 10ms],  [10ms, 20ms, 60ms]
+// Not OK: [10ms, 10ms, 11ms],  [10ms, 15ms, 20ms]
+message SysStatsConfig {
+  // Polls /proc/meminfo every X ms, if non-zero.
+  optional uint32 meminfo_period_ms = 1;
+
+  // Only the counters specified below are reported.
+  repeated MeminfoCounters meminfo_counters = 2;
+
+  // Polls /proc/vmstat every X ms, if non-zero.
+  optional uint32 vmstat_period_ms = 3;
+  repeated VmstatCounters vmstat_counters = 4;
+
+  // Pols /proc/stat every X ms, if non-zero.
+  optional uint32 stat_period_ms = 5;
+  enum StatCounters {
+    STAT_UNSPECIFIED = 0;
+    STAT_CPU_TIMES = 1;
+    STAT_IRQ_COUNTS = 2;
+    STAT_SOFTIRQ_COUNTS = 3;
+    STAT_FORK_COUNT = 4;
+  }
+  repeated StatCounters stat_counters = 6;
+}
diff --git a/protos/perfetto/ipc/BUILD.gn b/protos/perfetto/ipc/BUILD.gn
index e9055be..cd78bc1 100644
--- a/protos/perfetto/ipc/BUILD.gn
+++ b/protos/perfetto/ipc/BUILD.gn
@@ -18,8 +18,8 @@
 # IPC service definitions.
 ipc_library("ipc") {
   deps = [
-    "../common",
-    "../config",
+    "../common:lite",
+    "../config:lite",
   ]
   proto_in_dir = "$perfetto_root_path/protos"
   proto_out_dir = "$perfetto_root_path/protos"
diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn
index 60d8259..aa1fe9a 100644
--- a/protos/perfetto/trace/BUILD.gn
+++ b/protos/perfetto/trace/BUILD.gn
@@ -34,11 +34,12 @@
 # Protozero generated stubs, for writers.
 protozero_library("zero") {
   deps = [
-    "../config:config_zero",
+    "../config:zero",
     "chrome:zero",
     "filesystem:zero",
     "ftrace:zero",
     "ps:zero",
+    "sys_stats:zero",
   ]
   sources = proto_sources_minimal + proto_sources
   proto_in_dir = "$perfetto_root_path/protos"
@@ -51,11 +52,12 @@
   generate_python = false
   deps = [
     ":minimal_lite",
-    "../config:config",
+    "../config:lite",
     "chrome:lite",
     "filesystem:lite",
     "ftrace:lite",
     "ps:lite",
+    "sys_stats:lite",
   ]
   if (!build_with_chromium) {
     generate_descriptor = "$perfetto_root_path/protos/trace/trace.descriptor"
@@ -69,7 +71,7 @@
 proto_library("minimal_lite") {
   generate_python = false
   deps = [
-    "../config:config",
+    "../config:lite",
   ]
   sources = proto_sources_minimal
   proto_in_dir = "$perfetto_root_path/protos"
@@ -81,7 +83,7 @@
   generate_python = false
   deps = [
     ":minimal_lite",
-    "../config:config",
+    "../config:lite",
   ]
   sources = proto_sources_trusted
   proto_in_dir = "$perfetto_root_path/protos"
diff --git a/protos/perfetto/trace/chrome/BUILD.gn b/protos/perfetto/trace/chrome/BUILD.gn
index 4a6735f..b5191a6 100644
--- a/protos/perfetto/trace/chrome/BUILD.gn
+++ b/protos/perfetto/trace/chrome/BUILD.gn
@@ -37,7 +37,7 @@
   deps = [
     ":lite",
     "../:minimal_lite",
-    "../../config:config",
+    "../../config:lite",
   ]
   sources = minimal_chrome_proto_names
   proto_in_dir = "$perfetto_root_path/protos"
diff --git a/protos/perfetto/trace/perfetto_trace.proto b/protos/perfetto/trace/perfetto_trace.proto
index b83ecf0..7fb6490 100644
--- a/protos/perfetto/trace/perfetto_trace.proto
+++ b/protos/perfetto/trace/perfetto_trace.proto
@@ -11,6 +11,147 @@
 
 package perfetto.protos;
 
+// Begin of protos/perfetto/common/sys_stats_counters.proto
+
+// When editing entries here remember also to update "sys_stats_counters.h" with
+// the corresponding string definitions for the actual /proc files parser.
+
+// Counter definitions for Linux's /proc/meminfo.
+enum MeminfoCounters {
+  MEMINFO_UNSPECIFIED = 0;
+  MEMINFO_MEM_TOTAL = 1;
+  MEMINFO_MEM_FREE = 2;
+  MEMINFO_MEM_AVAILABLE = 3;
+  MEMINFO_BUFFERS = 4;
+  MEMINFO_CACHED = 5;
+  MEMINFO_SWAP_CACHED = 6;
+  MEMINFO_ACTIVE = 7;
+  MEMINFO_INACTIVE = 8;
+  MEMINFO_ACTIVE_ANON = 9;
+  MEMINFO_INACTIVE_ANON = 10;
+  MEMINFO_ACTIVE_FILE = 11;
+  MEMINFO_INACTIVE_FILE = 12;
+  MEMINFO_UNEVICTABLE = 13;
+  MEMINFO_MLOCKED = 14;
+  MEMINFO_SWAP_TOTAL = 15;
+  MEMINFO_SWAP_FREE = 16;
+  MEMINFO_DIRTY = 17;
+  MEMINFO_WRITEBACK = 18;
+  MEMINFO_ANON_PAGES = 19;
+  MEMINFO_MAPPED = 20;
+  MEMINFO_SHMEM = 21;
+  MEMINFO_SLAB = 22;
+  MEMINFO_SLAB_RECLAIMABLE = 23;
+  MEMINFO_SLAB_UNRECLAIMABLE = 24;
+  MEMINFO_KERNEL_STACK = 25;
+  MEMINFO_PAGE_TABLES = 26;
+  MEMINFO_COMMIT_LIMIT = 27;
+  MEMINFO_COMMITED_AS = 28;
+  MEMINFO_VMALLOC_TOTAL = 29;
+  MEMINFO_VMALLOC_USED = 30;
+  MEMINFO_VMALLOC_CHUNK = 31;
+  MEMINFO_CMA_TOTAL = 32;
+  MEMINFO_CMA_FREE = 33;
+}
+
+// Counter definitions for Linux's /proc/vmstat.
+enum VmstatCounters {
+  VMSTAT_UNSPECIFIED = 0;
+  VMSTAT_NR_FREE_PAGES = 1;
+  VMSTAT_NR_ALLOC_BATCH = 2;
+  VMSTAT_NR_INACTIVE_ANON = 3;
+  VMSTAT_NR_ACTIVE_ANON = 4;
+  VMSTAT_NR_INACTIVE_FILE = 5;
+  VMSTAT_NR_ACTIVE_FILE = 6;
+  VMSTAT_NR_UNEVICTABLE = 7;
+  VMSTAT_NR_MLOCK = 8;
+  VMSTAT_NR_ANON_PAGES = 9;
+  VMSTAT_NR_MAPPED = 10;
+  VMSTAT_NR_FILE_PAGES = 11;
+  VMSTAT_NR_DIRTY = 12;
+  VMSTAT_NR_WRITEBACK = 13;
+  VMSTAT_NR_SLAB_RECLAIMABLE = 14;
+  VMSTAT_NR_SLAB_UNRECLAIMABLE = 15;
+  VMSTAT_NR_PAGE_TABLE_PAGES = 16;
+  VMSTAT_NR_KERNEL_STACK = 17;
+  VMSTAT_NR_OVERHEAD = 18;
+  VMSTAT_NR_UNSTABLE = 19;
+  VMSTAT_NR_BOUNCE = 20;
+  VMSTAT_NR_VMSCAN_WRITE = 21;
+  VMSTAT_NR_VMSCAN_IMMEDIATE_RECLAIM = 22;
+  VMSTAT_NR_WRITEBACK_TEMP = 23;
+  VMSTAT_NR_ISOLATED_ANON = 24;
+  VMSTAT_NR_ISOLATED_FILE = 25;
+  VMSTAT_NR_SHMEM = 26;
+  VMSTAT_NR_DIRTIED = 27;
+  VMSTAT_NR_WRITTEN = 28;
+  VMSTAT_NR_PAGES_SCANNED = 29;
+  VMSTAT_WORKINGSET_REFAULT = 30;
+  VMSTAT_WORKINGSET_ACTIVATE = 31;
+  VMSTAT_WORKINGSET_NODERECLAIM = 32;
+  VMSTAT_NR_ANON_TRANSPARENT_HUGEPAGES = 33;
+  VMSTAT_NR_FREE_CMA = 34;
+  VMSTAT_NR_SWAPCACHE = 35;
+  VMSTAT_NR_DIRTY_THRESHOLD = 36;
+  VMSTAT_NR_DIRTY_BACKGROUND_THRESHOLD = 37;
+  VMSTAT_PGPGIN = 38;
+  VMSTAT_PGPGOUT = 39;
+  VMSTAT_PGPGOUTCLEAN = 40;
+  VMSTAT_PSWPIN = 41;
+  VMSTAT_PSWPOUT = 42;
+  VMSTAT_PGALLOC_DMA = 43;
+  VMSTAT_PGALLOC_NORMAL = 44;
+  VMSTAT_PGALLOC_MOVABLE = 45;
+  VMSTAT_PGFREE = 46;
+  VMSTAT_PGACTIVATE = 47;
+  VMSTAT_PGDEACTIVATE = 48;
+  VMSTAT_PGFAULT = 49;
+  VMSTAT_PGMAJFAULT = 50;
+  VMSTAT_PGREFILL_DMA = 51;
+  VMSTAT_PGREFILL_NORMAL = 52;
+  VMSTAT_PGREFILL_MOVABLE = 53;
+  VMSTAT_PGSTEAL_KSWAPD_DMA = 54;
+  VMSTAT_PGSTEAL_KSWAPD_NORMAL = 55;
+  VMSTAT_PGSTEAL_KSWAPD_MOVABLE = 56;
+  VMSTAT_PGSTEAL_DIRECT_DMA = 57;
+  VMSTAT_PGSTEAL_DIRECT_NORMAL = 58;
+  VMSTAT_PGSTEAL_DIRECT_MOVABLE = 59;
+  VMSTAT_PGSCAN_KSWAPD_DMA = 60;
+  VMSTAT_PGSCAN_KSWAPD_NORMAL = 61;
+  VMSTAT_PGSCAN_KSWAPD_MOVABLE = 62;
+  VMSTAT_PGSCAN_DIRECT_DMA = 63;
+  VMSTAT_PGSCAN_DIRECT_NORMAL = 64;
+  VMSTAT_PGSCAN_DIRECT_MOVABLE = 65;
+  VMSTAT_PGSCAN_DIRECT_THROTTLE = 66;
+  VMSTAT_PGINODESTEAL = 67;
+  VMSTAT_SLABS_SCANNED = 68;
+  VMSTAT_KSWAPD_INODESTEAL = 69;
+  VMSTAT_KSWAPD_LOW_WMARK_HIT_QUICKLY = 70;
+  VMSTAT_KSWAPD_HIGH_WMARK_HIT_QUICKLY = 71;
+  VMSTAT_PAGEOUTRUN = 72;
+  VMSTAT_ALLOCSTALL = 73;
+  VMSTAT_PGROTATED = 74;
+  VMSTAT_DROP_PAGECACHE = 75;
+  VMSTAT_DROP_SLAB = 76;
+  VMSTAT_PGMIGRATE_SUCCESS = 77;
+  VMSTAT_PGMIGRATE_FAIL = 78;
+  VMSTAT_COMPACT_MIGRATE_SCANNED = 79;
+  VMSTAT_COMPACT_FREE_SCANNED = 80;
+  VMSTAT_COMPACT_ISOLATED = 81;
+  VMSTAT_COMPACT_STALL = 82;
+  VMSTAT_COMPACT_FAIL = 83;
+  VMSTAT_COMPACT_SUCCESS = 84;
+  VMSTAT_COMPACT_DAEMON_WAKE = 85;
+  VMSTAT_UNEVICTABLE_PGS_CULLED = 86;
+  VMSTAT_UNEVICTABLE_PGS_SCANNED = 87;
+  VMSTAT_UNEVICTABLE_PGS_RESCUED = 88;
+  VMSTAT_UNEVICTABLE_PGS_MLOCKED = 89;
+  VMSTAT_UNEVICTABLE_PGS_MUNLOCKED = 90;
+  VMSTAT_UNEVICTABLE_PGS_CLEARED = 91;
+  VMSTAT_UNEVICTABLE_PGS_STRANDED = 92;
+}
+// End of protos/perfetto/common/sys_stats_counters.proto
+
 // Begin of protos/perfetto/trace/trace.proto
 
 message Trace {
@@ -30,14 +171,19 @@
 // The root object emitted by Perfetto. A perfetto trace is just a stream of
 // TracePacket(s).
 //
-// Next id: 7.
+// Next id: 9.
 message TracePacket {
+  // TODO: in future we should add a timestamp_clock_domain field to
+  // allow mixing timestamps from different clock domains.
+  optional uint64 timestamp = 8;  // Timestamp [ns].
+
   oneof data {
     FtraceEventBundle ftrace_events = 1;
     ProcessTree process_tree = 2;
     InodeFileMap inode_file_map = 4;
     // removed field with id 5
-    // removed field with id 6
+    ClockSnapshot clock_snapshot = 6;
+    SysStats sys_stats = 7;
 
     // IDs up to 32 are reserved for events that are quite frequent because they
     // take only one byte to encode their preamble.
@@ -55,6 +201,7 @@
     // This field is only used for testing.
     // removed field with id 536870911  // 2^29 - 1, max field id for protos.
   }
+
   // Trusted user id of the producer which generated this packet. Keep in sync
   // with TrustedPacket.trusted_uid.
   oneof optional_trusted_uid { int32 trusted_uid = 3; };
@@ -443,6 +590,86 @@
 
 // End of protos/perfetto/trace/ps/process_tree.proto
 
+// Begin of protos/perfetto/trace/clock_snapshot.proto
+
+// A snapshot of clock readings to allow for trace alignment.
+message ClockSnapshot {
+  message Clock {
+    enum Type {
+      UNKNOWN = 0;
+      REALTIME = 1;
+      REALTIME_COARSE = 2;
+      MONOTONIC = 3;
+      MONOTONIC_COARSE = 4;
+      MONOTONIC_RAW = 5;
+      BOOTTIME = 6;
+      PROCESS_CPUTIME = 7;
+      THREAD_CPUTIME = 8;
+    }
+    optional Type type = 1;
+    optional uint64 timestamp = 2;
+  }
+  repeated Clock clocks = 1;
+}
+
+// End of protos/perfetto/trace/clock_snapshot.proto
+
+// Begin of protos/perfetto/trace/sys_stats/sys_stats.proto
+
+
+// Various Linux system stat counters from /proc.
+// The fields in this message can be reported at different rates and with
+// different granularity. See sys_stats_config.proto.
+message SysStats {
+  // Counters from /proc/meminfo. Values are in KB.
+  message MeminfoValue {
+    optional MeminfoCounters key = 1;
+    optional uint64 value = 2;
+  };
+  repeated MeminfoValue meminfo = 1;
+
+  // Counter from /proc/vmstat. Units are often pages, not KB.
+  message VmstatValue {
+    optional VmstatCounters key = 1;
+    optional uint64 value = 2;
+  };
+  repeated VmstatValue vmstat = 2;
+
+  // Times in each mode, since boot. Unit: nanoseconds.
+  message CpuTimes {
+    optional uint32 cpu_id = 1;
+    optional uint64 user_ns = 2;         // Time spent in user mode.
+    optional uint64 user_ice_ns = 3;     // Time spent in user mode (low prio).
+    optional uint64 system_mode_ns = 4;  // Time spent in system mode.
+    optional uint64 idle_ns = 5;         // Time spent in the idle task.
+    optional uint64 io_wait_ns = 6;      // Time spent waiting for I/O.
+    optional uint64 irq_ns = 7;          // Time spent servicing interrupts.
+    optional uint64 softirq_ns = 8;      // Time spent servicing softirqs.
+  }
+  repeated CpuTimes cpu_stat = 3;  // One entry per cpu.
+
+  // Num processes forked since boot.
+  // Populated only if FORK_COUNT in config.stat_counters.
+  optional uint64 num_forks = 4;
+
+  message InterruptCount {
+    optional int32 irq = 1;
+    optional uint64 count = 2;
+  }
+
+  // Number of interrupts, broken by IRQ number.
+  // Populated only if IRQ_COUNTS in config.stat_counters.
+  optional uint64 num_irq_total = 5;  // Total num of irqs serviced since boot.
+  repeated InterruptCount num_irq = 6;
+
+  // Number of softirqs, broken by softirq number.
+  // Populated only if SOFTIRQ_COUNTS in config.stat_counters.
+  optional uint64 num_softirq_total = 7;    // Total num of softirqs since boot.
+  repeated InterruptCount num_softirq = 8;  // Per-softirq count.
+}
+
+// End of protos/perfetto/trace/sys_stats/sys_stats.proto
+
 // Begin of protos/perfetto/trace/ftrace/print.proto
 
 message PrintFtraceEvent {
diff --git a/protos/perfetto/trace/sys_stats/BUILD.gn b/protos/perfetto/trace/sys_stats/BUILD.gn
new file mode 100644
index 0000000..bb7f93b
--- /dev/null
+++ b/protos/perfetto/trace/sys_stats/BUILD.gn
@@ -0,0 +1,38 @@
+# 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("../../../../src/protozero/protozero_library.gni")
+
+sys_stats_proto_names = [ "sys_stats.proto" ]
+
+proto_library("lite") {
+  generate_python = false
+  deps = [
+    "../../common:lite",
+  ]
+  sources = sys_stats_proto_names
+  proto_in_dir = "$perfetto_root_path/protos"
+  proto_out_dir = "$perfetto_root_path/protos"
+}
+
+protozero_library("zero") {
+  deps = [
+    "../../common:zero",
+  ]
+  sources = sys_stats_proto_names
+  proto_in_dir = "$perfetto_root_path/protos"
+  proto_out_dir = "$perfetto_root_path/protos"
+  generator_plugin_options = "wrapper_namespace=pbzero"
+}
diff --git a/protos/perfetto/trace/sys_stats/sys_stats.proto b/protos/perfetto/trace/sys_stats/sys_stats.proto
new file mode 100644
index 0000000..7b298bf
--- /dev/null
+++ b/protos/perfetto/trace/sys_stats/sys_stats.proto
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+syntax = "proto2";
+option optimize_for = LITE_RUNTIME;
+package perfetto.protos;
+
+import "perfetto/common/sys_stats_counters.proto";
+
+// Various Linux system stat counters from /proc.
+// The fields in this message can be reported at different rates and with
+// different granularity. See sys_stats_config.proto.
+message SysStats {
+  // Counters from /proc/meminfo. Values are in KB.
+  message MeminfoValue {
+    optional MeminfoCounters key = 1;
+    optional uint64 value = 2;
+  };
+  repeated MeminfoValue meminfo = 1;
+
+  // Counter from /proc/vmstat. Units are often pages, not KB.
+  message VmstatValue {
+    optional VmstatCounters key = 1;
+    optional uint64 value = 2;
+  };
+  repeated VmstatValue vmstat = 2;
+
+  // Times in each mode, since boot. Unit: nanoseconds.
+  message CpuTimes {
+    optional uint32 cpu_id = 1;
+    optional uint64 user_ns = 2;         // Time spent in user mode.
+    optional uint64 user_ice_ns = 3;     // Time spent in user mode (low prio).
+    optional uint64 system_mode_ns = 4;  // Time spent in system mode.
+    optional uint64 idle_ns = 5;         // Time spent in the idle task.
+    optional uint64 io_wait_ns = 6;      // Time spent waiting for I/O.
+    optional uint64 irq_ns = 7;          // Time spent servicing interrupts.
+    optional uint64 softirq_ns = 8;      // Time spent servicing softirqs.
+  }
+  repeated CpuTimes cpu_stat = 3;  // One entry per cpu.
+
+  // Num processes forked since boot.
+  // Populated only if FORK_COUNT in config.stat_counters.
+  optional uint64 num_forks = 4;
+
+  message InterruptCount {
+    optional int32 irq = 1;
+    optional uint64 count = 2;
+  }
+
+  // Number of interrupts, broken by IRQ number.
+  // Populated only if IRQ_COUNTS in config.stat_counters.
+  optional uint64 num_irq_total = 5;  // Total num of irqs serviced since boot.
+  repeated InterruptCount num_irq = 6;
+
+  // Number of softirqs, broken by softirq number.
+  // Populated only if SOFTIRQ_COUNTS in config.stat_counters.
+  optional uint64 num_softirq_total = 7;    // Total num of softirqs since boot.
+  repeated InterruptCount num_softirq = 8;  // Per-softirq count.
+}
diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto
index 553ade2..e6fe149 100644
--- a/protos/perfetto/trace/trace_packet.proto
+++ b/protos/perfetto/trace/trace_packet.proto
@@ -24,6 +24,7 @@
 import "perfetto/trace/ftrace/ftrace_event_bundle.proto";
 import "perfetto/trace/ftrace/ftrace_stats.proto";
 import "perfetto/trace/ps/process_tree.proto";
+import "perfetto/trace/sys_stats/sys_stats.proto";
 import "perfetto/trace/test_event.proto";
 import "perfetto/trace/trace_stats.proto";
 
@@ -32,14 +33,19 @@
 // The root object emitted by Perfetto. A perfetto trace is just a stream of
 // TracePacket(s).
 //
-// Next id: 7.
+// Next id: 9.
 message TracePacket {
+  // TODO(primiano): in future we should add a timestamp_clock_domain field to
+  // allow mixing timestamps from different clock domains.
+  optional uint64 timestamp = 8;  // Timestamp [ns].
+
   oneof data {
     FtraceEventBundle ftrace_events = 1;
     ProcessTree process_tree = 2;
     InodeFileMap inode_file_map = 4;
     ChromeEventBundle chrome_events = 5;
     ClockSnapshot clock_snapshot = 6;
+    SysStats sys_stats = 7;
 
     // IDs up to 32 are reserved for events that are quite frequent because they
     // take only one byte to encode their preamble.
@@ -57,6 +63,7 @@
     // This field is only used for testing.
     TestEvent for_testing = 536870911;  // 2^29 - 1, max field id for protos.
   }
+
   // Trusted user id of the producer which generated this packet. Keep in sync
   // with TrustedPacket.trusted_uid.
   oneof optional_trusted_uid { int32 trusted_uid = 3; };
diff --git a/src/perfetto_cmd/BUILD.gn b/src/perfetto_cmd/BUILD.gn
index 9e893e4..d71b1ed 100644
--- a/src/perfetto_cmd/BUILD.gn
+++ b/src/perfetto_cmd/BUILD.gn
@@ -22,7 +22,7 @@
   ]
   deps = [
     "../../gn:default_deps",
-    "../../protos/perfetto/config",
+    "../../protos/perfetto/config:lite",
     "../base",
     "../protozero",
     "../tracing:ipc_consumer",
diff --git a/src/protozero/scattered_stream_delegate_for_testing.cc b/src/protozero/scattered_stream_delegate_for_testing.cc
index 0409a06..ce474ff 100644
--- a/src/protozero/scattered_stream_delegate_for_testing.cc
+++ b/src/protozero/scattered_stream_delegate_for_testing.cc
@@ -38,23 +38,17 @@
   return {begin, begin + chunk_size_};
 }
 
-std::unique_ptr<uint8_t[]> ScatteredStreamDelegateForTesting::StitchChunks(
-    size_t size) {
-  std::unique_ptr<uint8_t[]> buffer =
-      std::unique_ptr<uint8_t[]>(new uint8_t[size]);
-  size_t remaining = size;
+std::vector<uint8_t> ScatteredStreamDelegateForTesting::StitchChunks() {
+  std::vector<uint8_t> buffer;
   size_t i = 0;
   for (const auto& chunk : chunks_) {
-    size_t chunk_size = remaining;
-    if (i < chunks_used_size_.size()) {
-      chunk_size = chunks_used_size_[i];
-    }
+    size_t chunk_size = (i < chunks_used_size_.size())
+                            ? chunks_used_size_[i]
+                            : (chunk_size_ - writer_->bytes_available());
     PERFETTO_CHECK(chunk_size <= chunk_size_);
-    memcpy(buffer.get() + size - remaining, chunk.get(), chunk_size);
-    remaining -= chunk_size;
+    buffer.insert(buffer.end(), chunk.get(), chunk.get() + chunk_size);
     i++;
   }
-
   return buffer;
 }
 
diff --git a/src/protozero/scattered_stream_delegate_for_testing.h b/src/protozero/scattered_stream_delegate_for_testing.h
index 93b6874..365ad43 100644
--- a/src/protozero/scattered_stream_delegate_for_testing.h
+++ b/src/protozero/scattered_stream_delegate_for_testing.h
@@ -35,7 +35,7 @@
   protozero::ContiguousMemoryRange GetNewBuffer() override;
 
   // Stitch all the chunks into a single contiguous buffer.
-  std::unique_ptr<uint8_t[]> StitchChunks(size_t size);
+  std::vector<uint8_t> StitchChunks();
 
   const std::vector<std::unique_ptr<uint8_t[]>>& chunks() const {
     return chunks_;
diff --git a/src/trace_processor/proto_trace_parser.cc b/src/trace_processor/proto_trace_parser.cc
index 8593a26..bf42b3f 100644
--- a/src/trace_processor/proto_trace_parser.cc
+++ b/src/trace_processor/proto_trace_parser.cc
@@ -16,6 +16,8 @@
 
 #include "src/trace_processor/proto_trace_parser.h"
 
+#include <string.h>
+
 #include <string>
 
 #include "perfetto/base/logging.h"
@@ -86,7 +88,7 @@
       out->name = base::StringView(s + name_index, name_length);
       size_t value_index = name_index + name_length + 1;
       char value_str[32];
-      std::strcpy(value_str, s + value_index);
+      strcpy(value_str, s + value_index);
       out->value = std::stod(value_str);
       return true;
     }
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index fe2651b..c462d19 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -220,7 +220,7 @@
   }
 
   Thread* GetMutableThread(UniqueTid utid) {
-    PERFETTO_DCHECK(utid >= 0 && utid < unique_threads_.size());
+    PERFETTO_DCHECK(utid < unique_threads_.size());
     return &unique_threads_[utid];
   }
 
@@ -242,7 +242,7 @@
 
   const Thread& GetThread(UniqueTid utid) const {
     // Allow utid == 0 for idle thread retrieval.
-    PERFETTO_DCHECK(utid >= 0 && utid < unique_threads_.size());
+    PERFETTO_DCHECK(utid < unique_threads_.size());
     return unique_threads_[utid];
   }
 
diff --git a/src/traced/probes/BUILD.gn b/src/traced/probes/BUILD.gn
index babc591..fd01252 100644
--- a/src/traced/probes/BUILD.gn
+++ b/src/traced/probes/BUILD.gn
@@ -40,6 +40,7 @@
     "../../tracing:tracing",
     "filesystem",
     "ps",
+    "sys_stats",
   ]
   sources = [
     "probes_producer.cc",
@@ -69,5 +70,6 @@
     "../../tracing:test_support",
     "filesystem:unittests",
     "ps:unittests",
+    "sys_stats:unittests",
   ]
 }
diff --git a/src/traced/probes/ftrace/cpu_reader_unittest.cc b/src/traced/probes/ftrace/cpu_reader_unittest.cc
index 6938547..048eb41 100644
--- a/src/traced/probes/ftrace/cpu_reader_unittest.cc
+++ b/src/traced/probes/ftrace/cpu_reader_unittest.cc
@@ -88,10 +88,8 @@
   // on success and nullptr on failure.
   std::unique_ptr<ProtoT> ParseProto() {
     auto bundle = std::unique_ptr<ProtoT>(new ProtoT());
-    size_t msg_size =
-        delegate_.chunks().size() * chunk_size_ - stream_.bytes_available();
-    std::unique_ptr<uint8_t[]> buffer = delegate_.StitchChunks(msg_size);
-    if (!bundle->ParseFromArray(buffer.get(), static_cast<int>(msg_size)))
+    std::vector<uint8_t> buffer = delegate_.StitchChunks();
+    if (!bundle->ParseFromArray(buffer.data(), static_cast<int>(buffer.size())))
       return nullptr;
     return bundle;
   }
diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc
index e56eb73..cc90242 100644
--- a/src/traced/probes/probes_producer.cc
+++ b/src/traced/probes/probes_producer.cc
@@ -50,6 +50,7 @@
 constexpr char kFtraceSourceName[] = "linux.ftrace";
 constexpr char kProcessStatsSourceName[] = "linux.process_stats";
 constexpr char kInodeMapSourceName[] = "linux.inode_file_map";
+constexpr char kSysStatsSourceName[] = "linux.sys_stats";
 
 }  // namespace.
 
@@ -74,17 +75,29 @@
   ResetConnectionBackoff();
   PERFETTO_LOG("Connected to the service");
 
-  DataSourceDescriptor ftrace_descriptor;
-  ftrace_descriptor.set_name(kFtraceSourceName);
-  endpoint_->RegisterDataSource(ftrace_descriptor);
+  {
+    DataSourceDescriptor desc;
+    desc.set_name(kFtraceSourceName);
+    endpoint_->RegisterDataSource(desc);
+  }
 
-  DataSourceDescriptor process_stats_descriptor;
-  process_stats_descriptor.set_name(kProcessStatsSourceName);
-  endpoint_->RegisterDataSource(process_stats_descriptor);
+  {
+    DataSourceDescriptor desc;
+    desc.set_name(kProcessStatsSourceName);
+    endpoint_->RegisterDataSource(desc);
+  }
 
-  DataSourceDescriptor inode_map_descriptor;
-  inode_map_descriptor.set_name(kInodeMapSourceName);
-  endpoint_->RegisterDataSource(inode_map_descriptor);
+  {
+    DataSourceDescriptor desc;
+    desc.set_name(kInodeMapSourceName);
+    endpoint_->RegisterDataSource(desc);
+  }
+
+  {
+    DataSourceDescriptor desc;
+    desc.set_name(kSysStatsSourceName);
+    endpoint_->RegisterDataSource(desc);
+  }
 }
 
 void ProbesProducer::OnDisconnect() {
@@ -129,6 +142,8 @@
     data_source = CreateInodeFileDataSource(session_id, instance_id, config);
   } else if (config.name() == kProcessStatsSourceName) {
     data_source = CreateProcessStatsDataSource(session_id, instance_id, config);
+  } else if (config.name() == kSysStatsSourceName) {
+    data_source = CreateSysStatsDataSource(session_id, instance_id, config);
   }
 
   if (!data_source) {
@@ -214,6 +229,18 @@
   return std::move(data_source);
 }
 
+std::unique_ptr<SysStatsDataSource> ProbesProducer::CreateSysStatsDataSource(
+    TracingSessionID session_id,
+    DataSourceInstanceID id,
+    const DataSourceConfig& config) {
+  base::ignore_result(id);
+  auto buffer_id = static_cast<BufferID>(config.target_buffer());
+  auto data_source = std::unique_ptr<SysStatsDataSource>(
+      new SysStatsDataSource(task_runner_, session_id,
+                             endpoint_->CreateTraceWriter(buffer_id), config));
+  return data_source;
+}
+
 void ProbesProducer::TearDownDataSourceInstance(DataSourceInstanceID id) {
   PERFETTO_LOG("Producer stop (id=%" PRIu64 ")", id);
   auto it = data_sources_.find(id);
@@ -290,6 +317,8 @@
       case ProcessStatsDataSource::kTypeId:
         ps_data_source = static_cast<ProcessStatsDataSource*>(ds);
         break;
+      case SysStatsDataSource::kTypeId:
+        break;
       default:
         PERFETTO_DCHECK(false);
     }  // switch (type_id)
diff --git a/src/traced/probes/probes_producer.h b/src/traced/probes/probes_producer.h
index 46e0f57..c3670ce 100644
--- a/src/traced/probes/probes_producer.h
+++ b/src/traced/probes/probes_producer.h
@@ -31,6 +31,7 @@
 #include "src/traced/probes/ftrace/ftrace_controller.h"
 #include "src/traced/probes/ftrace/ftrace_metadata.h"
 #include "src/traced/probes/ps/process_stats_data_source.h"
+#include "src/traced/probes/sys_stats/sys_stats_data_source.h"
 
 #include "perfetto/trace/filesystem/inode_file_map.pbzero.h"
 
@@ -74,6 +75,10 @@
       TracingSessionID session_id,
       DataSourceInstanceID id,
       DataSourceConfig config);
+  std::unique_ptr<SysStatsDataSource> CreateSysStatsDataSource(
+      TracingSessionID session_id,
+      DataSourceInstanceID id,
+      const DataSourceConfig& config);
 
  private:
   enum State {
diff --git a/src/traced/probes/sys_stats/BUILD.gn b/src/traced/probes/sys_stats/BUILD.gn
new file mode 100644
index 0000000..16ae4e0
--- /dev/null
+++ b/src/traced/probes/sys_stats/BUILD.gn
@@ -0,0 +1,48 @@
+# 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.
+
+source_set("sys_stats") {
+  public_deps = [
+    "../../../tracing",
+  ]
+  deps = [
+    "..:data_source",
+    "../../../../gn:default_deps",
+    "../../../../include/perfetto/traced",
+    "../../../../include/perfetto/traced:sys_stats_counters",
+    "../../../../protos/perfetto/config:lite",
+    "../../../../protos/perfetto/trace/sys_stats:zero",
+    "../../../base",
+  ]
+  sources = [
+    "sys_stats_data_source.cc",
+    "sys_stats_data_source.h",
+  ]
+}
+
+source_set("unittests") {
+  testonly = true
+  deps = [
+    ":sys_stats",
+    "../../../../gn:default_deps",
+    "../../../../gn:gtest_deps",
+    "../../../../protos/perfetto/config:lite",
+    "../../../../protos/perfetto/trace:lite",
+    "../../../../src/base:test_support",
+    "../../../../src/tracing:test_support",
+  ]
+  sources = [
+    "sys_stats_data_source_unittest.cc",
+  ]
+}
diff --git a/src/traced/probes/sys_stats/sys_stats_data_source.cc b/src/traced/probes/sys_stats/sys_stats_data_source.cc
new file mode 100644
index 0000000..ebc3733
--- /dev/null
+++ b/src/traced/probes/sys_stats/sys_stats_data_source.cc
@@ -0,0 +1,304 @@
+/*
+ * 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 "src/traced/probes/sys_stats/sys_stats_data_source.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <algorithm>
+#include <array>
+#include <limits>
+#include <utility>
+
+#include "perfetto/base/file_utils.h"
+#include "perfetto/base/metatrace.h"
+#include "perfetto/base/scoped_file.h"
+#include "perfetto/base/string_splitter.h"
+#include "perfetto/base/task_runner.h"
+#include "perfetto/base/time.h"
+#include "perfetto/base/utils.h"
+#include "perfetto/traced/sys_stats_counters.h"
+#include "perfetto/tracing/core/sys_stats_config.h"
+
+#include "perfetto/common/sys_stats_counters.pbzero.h"
+#include "perfetto/config/sys_stats/sys_stats_config.pb.h"
+#include "perfetto/trace/sys_stats/sys_stats.pbzero.h"
+#include "perfetto/trace/trace_packet.pbzero.h"
+
+namespace perfetto {
+
+namespace {
+constexpr size_t kReadBufSize = 1024 * 16;
+
+base::ScopedFile OpenReadOnly(const char* path) {
+  base::ScopedFile fd;
+  fd.reset(open(path, O_RDONLY | O_CLOEXEC));
+  if (!fd)
+    PERFETTO_PLOG("Failed opening %s", path);
+  return fd;
+}
+
+}  // namespace
+
+// static
+constexpr int SysStatsDataSource::kTypeId;
+
+SysStatsDataSource::SysStatsDataSource(base::TaskRunner* task_runner,
+                                       TracingSessionID session_id,
+                                       std::unique_ptr<TraceWriter> writer,
+                                       const DataSourceConfig& ds_config,
+                                       OpenFunction open_fn)
+    : ProbesDataSource(session_id, kTypeId),
+      task_runner_(task_runner),
+      writer_(std::move(writer)),
+      weak_factory_(this) {
+  const auto& config = ds_config.sys_stats_config();
+
+  ns_per_user_hz_ = 1000000000ull / static_cast<uint64_t>(sysconf(_SC_CLK_TCK));
+
+  open_fn = open_fn ? open_fn : OpenReadOnly;
+  meminfo_fd_ = open_fn("/proc/meminfo");
+  vmstat_fd_ = open_fn("/proc/vmstat");
+  stat_fd_ = open_fn("/proc/stat");
+
+  read_buf_ = base::PageAllocator::Allocate(kReadBufSize);
+
+  // Build a lookup map that allows to quickly translate strings like "MemTotal"
+  // into the corresponding enum value, only for the counters enabled in the
+  // config.
+  for (const auto& counter_id : config.meminfo_counters()) {
+    for (size_t i = 0; i < base::ArraySize(kMeminfoKeys); i++) {
+      const auto& k = kMeminfoKeys[i];
+      if (static_cast<int>(k.id) == static_cast<int>(counter_id))
+        meminfo_counters_.emplace(k.str, k.id);
+    }
+  }
+
+  for (const auto& counter_id : config.vmstat_counters()) {
+    for (size_t i = 0; i < base::ArraySize(kVmstatKeys); i++) {
+      const auto& k = kVmstatKeys[i];
+      if (static_cast<int>(k.id) == static_cast<int>(counter_id))
+        vmstat_counters_.emplace(k.str, k.id);
+    }
+  }
+
+  for (const auto& counter_id : config.stat_counters()) {
+    stat_enabled_fields_ |= 1 << counter_id;
+  }
+
+  std::array<uint32_t, 3> periods_ms{};
+  std::array<uint32_t, 3> ticks{};
+  static_assert(periods_ms.size() == ticks.size(), "must have same size");
+  periods_ms[0] = config.meminfo_period_ms();
+  periods_ms[1] = config.vmstat_period_ms();
+  periods_ms[2] = config.stat_period_ms();
+  tick_period_ms_ = 0;
+  for (uint32_t ms : periods_ms) {
+    if (ms && (ms < tick_period_ms_ || tick_period_ms_ == 0))
+      tick_period_ms_ = ms;
+  }
+  if (tick_period_ms_ == 0)
+    return;  // No polling configured.
+
+  for (size_t i = 0; i < periods_ms.size(); i++) {
+    auto ms = periods_ms[i];
+    if (ms && ms % tick_period_ms_ != 0) {
+      PERFETTO_ELOG("SysStat periods are not integer multiples of each other");
+      return;
+    }
+    ticks[i] = ms / tick_period_ms_;
+  }
+  meminfo_ticks_ = ticks[0];
+  vmstat_ticks_ = ticks[1];
+  stat_ticks_ = ticks[2];
+  auto weak_this = GetWeakPtr();
+  task_runner_->PostTask(std::bind(&SysStatsDataSource::Tick, weak_this));
+}
+
+// static
+void SysStatsDataSource::Tick(base::WeakPtr<SysStatsDataSource> weak_this) {
+  if (!weak_this)
+    return;
+  SysStatsDataSource& thiz = *weak_this;
+
+  uint32_t period_ms = thiz.tick_period_ms_;
+  uint32_t delay_ms = period_ms - (base::GetWallTimeMs().count() % period_ms);
+  thiz.task_runner_->PostDelayedTask(
+      std::bind(&SysStatsDataSource::Tick, weak_this), delay_ms);
+  thiz.ReadSysStats();
+}
+
+SysStatsDataSource::~SysStatsDataSource() = default;
+
+void SysStatsDataSource::ReadSysStats() {
+  PERFETTO_METATRACE("ReadSysStats", 0);
+  auto packet = writer_->NewTracePacket();
+
+  packet->set_timestamp(static_cast<uint64_t>(base::GetBootTimeNs().count()));
+  auto* sys_stats = packet->set_sys_stats();
+
+  if (meminfo_ticks_ && tick_ % meminfo_ticks_ == 0)
+    ReadMeminfo(sys_stats);
+
+  if (vmstat_ticks_ && tick_ % vmstat_ticks_ == 0)
+    ReadVmstat(sys_stats);
+
+  if (stat_ticks_ && tick_ % stat_ticks_ == 0)
+    ReadStat(sys_stats);
+
+  tick_++;
+}
+
+void SysStatsDataSource::ReadMeminfo(protos::pbzero::SysStats* sys_stats) {
+  size_t rsize = ReadFile(&meminfo_fd_, "/proc/meminfo");
+  if (!rsize)
+    return;
+  char* buf = static_cast<char*>(read_buf_.get());
+  for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
+    base::StringSplitter words(&lines, ' ');
+    if (!words.Next())
+      continue;
+    // Extract the meminfo key, dropping trailing ':' (e.g., "MemTotal: NN KB").
+    words.cur_token()[words.cur_token_size() - 1] = '\0';
+    auto it = meminfo_counters_.find(words.cur_token());
+    if (it == meminfo_counters_.end())
+      continue;
+    int counter_id = it->second;
+    if (!words.Next())
+      continue;
+    auto value = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
+    auto* meminfo = sys_stats->add_meminfo();
+    meminfo->set_key(static_cast<protos::pbzero::MeminfoCounters>(counter_id));
+    meminfo->set_value(value);
+  }
+}
+
+void SysStatsDataSource::ReadVmstat(protos::pbzero::SysStats* sys_stats) {
+  size_t rsize = ReadFile(&vmstat_fd_, "/proc/vmstat");
+  if (!rsize)
+    return;
+  char* buf = static_cast<char*>(read_buf_.get());
+  for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
+    base::StringSplitter words(&lines, ' ');
+    if (!words.Next())
+      continue;
+    auto it = vmstat_counters_.find(words.cur_token());
+    if (it == vmstat_counters_.end())
+      continue;
+    int counter_id = it->second;
+    if (!words.Next())
+      continue;
+    auto value = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
+    auto* vmstat = sys_stats->add_vmstat();
+    vmstat->set_key(static_cast<protos::pbzero::VmstatCounters>(counter_id));
+    vmstat->set_value(value);
+  }
+}
+
+void SysStatsDataSource::ReadStat(protos::pbzero::SysStats* sys_stats) {
+  size_t rsize = ReadFile(&stat_fd_, "/proc/stat");
+  if (!rsize)
+    return;
+  char* buf = static_cast<char*>(read_buf_.get());
+  for (base::StringSplitter lines(buf, rsize, '\n'); lines.Next();) {
+    base::StringSplitter words(&lines, ' ');
+    if (!words.Next())
+      continue;
+
+    // Per-CPU stats.
+    if ((stat_enabled_fields_ & (1 << SysStatsConfig::STAT_CPU_TIMES)) &&
+        words.cur_token_size() > 3 && !strncmp(words.cur_token(), "cpu", 3)) {
+      long cpu_id = strtol(words.cur_token() + 3, nullptr, 10);
+      std::array<uint64_t, 7> cpu_times{};
+      for (size_t i = 0; i < cpu_times.size() && words.Next(); i++) {
+        cpu_times[i] =
+            static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
+      }
+      auto* cpu_stat = sys_stats->add_cpu_stat();
+      cpu_stat->set_cpu_id(static_cast<uint32_t>(cpu_id));
+      cpu_stat->set_user_ns(cpu_times[0] * ns_per_user_hz_);
+      cpu_stat->set_user_ice_ns(cpu_times[1] * ns_per_user_hz_);
+      cpu_stat->set_system_mode_ns(cpu_times[2] * ns_per_user_hz_);
+      cpu_stat->set_idle_ns(cpu_times[3] * ns_per_user_hz_);
+      cpu_stat->set_io_wait_ns(cpu_times[4] * ns_per_user_hz_);
+      cpu_stat->set_irq_ns(cpu_times[5] * ns_per_user_hz_);
+      cpu_stat->set_softirq_ns(cpu_times[6] * ns_per_user_hz_);
+    }
+    // IRQ counters
+    else if ((stat_enabled_fields_ & (1 << SysStatsConfig::STAT_IRQ_COUNTS)) &&
+             !strcmp(words.cur_token(), "intr")) {
+      for (size_t i = 0; words.Next(); i++) {
+        auto v = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
+        if (i == 0) {
+          sys_stats->set_num_irq_total(v);
+        } else {
+          auto* irq_stat = sys_stats->add_num_irq();
+          irq_stat->set_irq(static_cast<int32_t>(i - 1));
+          irq_stat->set_count(v);
+        }
+      }
+    }
+    // Softirq counters.
+    else if ((stat_enabled_fields_ &
+              (1 << SysStatsConfig::STAT_SOFTIRQ_COUNTS)) &&
+             !strcmp(words.cur_token(), "softirq")) {
+      for (size_t i = 0; words.Next(); i++) {
+        auto v = static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10));
+        if (i == 0) {
+          sys_stats->set_num_softirq_total(v);
+        } else {
+          auto* softirq_stat = sys_stats->add_num_softirq();
+          softirq_stat->set_irq(static_cast<int32_t>(i - 1));
+          softirq_stat->set_count(v);
+        }
+      }
+    }
+    // Number of forked processes since boot.
+    else if ((stat_enabled_fields_ & (1 << SysStatsConfig::STAT_FORK_COUNT)) &&
+             !strcmp(words.cur_token(), "processes")) {
+      if (words.Next()) {
+        sys_stats->set_num_forks(
+            static_cast<uint64_t>(strtoll(words.cur_token(), nullptr, 10)));
+      }
+    }
+
+  }  // for (line)
+}
+
+base::WeakPtr<SysStatsDataSource> SysStatsDataSource::GetWeakPtr() const {
+  return weak_factory_.GetWeakPtr();
+}
+
+void SysStatsDataSource::Flush() {
+  writer_->Flush();
+}
+
+size_t SysStatsDataSource::ReadFile(base::ScopedFile* fd, const char* path) {
+  if (!*fd)
+    return 0;
+  ssize_t res = pread(**fd, read_buf_.get(), kReadBufSize - 1, 0);
+  if (res <= 0) {
+    PERFETTO_PLOG("Failed reading %s", path);
+    fd->reset();
+    return 0;
+  }
+  size_t rsize = static_cast<size_t>(res);
+  static_cast<char*>(read_buf_.get())[rsize] = '\0';
+  return rsize + 1;  // Include null terminator in the count.
+}
+
+}  // namespace perfetto
diff --git a/src/traced/probes/sys_stats/sys_stats_data_source.h b/src/traced/probes/sys_stats/sys_stats_data_source.h
new file mode 100644
index 0000000..a5ea4f7
--- /dev/null
+++ b/src/traced/probes/sys_stats/sys_stats_data_source.h
@@ -0,0 +1,104 @@
+/*
+ * 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 SRC_TRACED_PROBES_SYS_STATS_SYS_STATS_DATA_SOURCE_H_
+#define SRC_TRACED_PROBES_SYS_STATS_SYS_STATS_DATA_SOURCE_H_
+
+#include <string.h>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "perfetto/base/page_allocator.h"
+#include "perfetto/base/scoped_file.h"
+#include "perfetto/base/weak_ptr.h"
+#include "perfetto/tracing/core/basic_types.h"
+#include "perfetto/tracing/core/data_source_config.h"
+#include "perfetto/tracing/core/trace_writer.h"
+#include "src/traced/probes/probes_data_source.h"
+
+namespace perfetto {
+
+namespace base {
+class TaskRunner;
+}
+
+namespace protos {
+namespace pbzero {
+class SysStats;
+}
+}  // namespace protos
+
+class SysStatsDataSource : public ProbesDataSource {
+ public:
+  static constexpr int kTypeId = 4;
+  using OpenFunction = base::ScopedFile (*)(const char*);
+  SysStatsDataSource(base::TaskRunner*,
+                     TracingSessionID,
+                     std::unique_ptr<TraceWriter> writer,
+                     const DataSourceConfig&,
+                     OpenFunction = nullptr);
+  ~SysStatsDataSource() override;
+
+  // ProbesDataSource implementation.
+  void Flush() override;
+
+  base::WeakPtr<SysStatsDataSource> GetWeakPtr() const;
+
+  void set_ns_per_user_hz_for_testing(uint64_t ns) { ns_per_user_hz_ = ns; }
+  uint32_t tick_for_testing() const { return tick_; }
+
+ private:
+  struct CStrCmp {
+    bool operator()(const char* a, const char* b) const {
+      return strcmp(a, b) < 0;
+    }
+  };
+
+  static void Tick(base::WeakPtr<SysStatsDataSource>);
+
+  SysStatsDataSource(const SysStatsDataSource&) = delete;
+  SysStatsDataSource& operator=(const SysStatsDataSource&) = delete;
+  void ReadSysStats();  // Virtual for testing.
+  void ReadMeminfo(protos::pbzero::SysStats* sys_stats);
+  void ReadVmstat(protos::pbzero::SysStats* sys_stats);
+  void ReadStat(protos::pbzero::SysStats* sys_stats);
+  size_t ReadFile(base::ScopedFile*, const char* path);
+
+  base::TaskRunner* const task_runner_;
+  std::unique_ptr<TraceWriter> writer_;
+  base::ScopedFile meminfo_fd_;
+  base::ScopedFile vmstat_fd_;
+  base::ScopedFile stat_fd_;
+  base::PageAllocator::UniquePtr read_buf_;
+  TraceWriter::TracePacketHandle cur_packet_;
+  std::map<const char*, int, CStrCmp> meminfo_counters_;
+  std::map<const char*, int, CStrCmp> vmstat_counters_;
+  uint64_t ns_per_user_hz_ = 0;
+  uint32_t tick_ = 0;
+  uint32_t tick_period_ms_ = 0;
+  uint32_t meminfo_ticks_ = 0;
+  uint32_t vmstat_ticks_ = 0;
+  uint32_t stat_ticks_ = 0;
+  uint32_t stat_enabled_fields_ = 0;
+
+  base::WeakPtrFactory<SysStatsDataSource> weak_factory_;  // Keep last.
+};
+
+}  // namespace perfetto
+
+#endif  // SRC_TRACED_PROBES_SYS_STATS_SYS_STATS_DATA_SOURCE_H_
diff --git a/src/traced/probes/sys_stats/sys_stats_data_source_unittest.cc b/src/traced/probes/sys_stats/sys_stats_data_source_unittest.cc
new file mode 100644
index 0000000..e2c18d9
--- /dev/null
+++ b/src/traced/probes/sys_stats/sys_stats_data_source_unittest.cc
@@ -0,0 +1,370 @@
+/*
+ * 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 <unistd.h>
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "perfetto/base/temp_file.h"
+#include "src/base/test/test_task_runner.h"
+#include "src/traced/probes/sys_stats/sys_stats_data_source.h"
+#include "src/tracing/core/trace_writer_for_testing.h"
+
+#include "perfetto/config/sys_stats/sys_stats_config.pb.h"
+#include "perfetto/trace/trace_packet.pb.h"
+#include "perfetto/trace/trace_packet.pbzero.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::UnorderedElementsAre;
+
+namespace perfetto {
+namespace {
+
+const char kMockMeminfo[] = R"(
+MemTotal:        3744240 kB
+MemFree:           73328 kB
+MemAvailable:     629896 kB
+Buffers:           19296 kB
+Cached:           731032 kB
+SwapCached:         4936 kB
+Active:          1616348 kB
+Inactive:         745492 kB
+Active(anon):    1322636 kB
+Inactive(anon):   449172 kB
+Active(file):     293712 kB
+Inactive(file):   296320 kB
+Unevictable:      142152 kB
+Mlocked:          142152 kB
+SwapTotal:        524284 kB
+SwapFree:            128 kB
+Dirty:                 0 kB
+Writeback:             0 kB
+AnonPages:       1751140 kB
+Mapped:           508372 kB
+Shmem:             18604 kB
+Slab:             240352 kB
+SReclaimable:      64684 kB
+SUnreclaim:       175668 kB
+KernelStack:       62672 kB
+PageTables:        70108 kB
+NFS_Unstable:          0 kB
+Bounce:                0 kB
+WritebackTmp:          0 kB
+CommitLimit:     2396404 kB
+Committed_AS:   81911488 kB
+VmallocTotal:   258867136 kB
+VmallocUsed:           0 kB
+VmallocChunk:          0 kB
+CmaTotal:         196608 kB
+CmaFree:              60 kB)";
+
+const char kMockVmstat[] = R"(
+nr_free_pages 16449
+nr_alloc_batch 79
+nr_inactive_anon 112545
+nr_active_anon 322027
+nr_inactive_file 75904
+nr_active_file 87939
+nr_unevictable 35538
+nr_mlock 35538
+nr_anon_pages 429005
+nr_mapped 125844
+nr_file_pages 205523
+nr_dirty 23
+nr_writeback 0
+nr_slab_reclaimable 15840
+nr_slab_unreclaimable 43912
+nr_page_table_pages 17158
+nr_kernel_stack 3822
+nr_overhead 0
+nr_unstable 0
+nr_bounce 0
+nr_vmscan_write 558690
+nr_vmscan_immediate_reclaim 14853
+nr_writeback_temp 0
+nr_isolated_anon 0
+nr_isolated_file 0
+nr_shmem 5027
+nr_dirtied 6732417
+nr_written 6945513
+nr_pages_scanned 0
+workingset_refault 32784684
+workingset_activate 8200928
+workingset_nodereclaim 0
+nr_anon_transparent_hugepages 0
+nr_free_cma 0
+nr_swapcache 1254
+nr_dirty_threshold 33922
+nr_dirty_background_threshold 8449
+pgpgin 161257156
+pgpgout 35973852
+pgpgoutclean 37181384
+pswpin 185308
+pswpout 557662
+pgalloc_dma 79259070
+pgalloc_normal 88265512
+pgalloc_movable 0
+pgfree 175051592
+pgactivate 11897892
+pgdeactivate 20412230
+pgfault 181696234
+pgmajfault 1060871
+pgrefill_dma 12970047
+pgrefill_normal 14391564
+pgrefill_movable 0
+pgsteal_kswapd_dma 19471476
+pgsteal_kswapd_normal 21138380
+pgsteal_kswapd_movable 0
+pgsteal_direct_dma 40625
+pgsteal_direct_normal 50912
+pgsteal_direct_movable 0
+pgscan_kswapd_dma 23544417
+pgscan_kswapd_normal 25623715
+pgscan_kswapd_movable 0
+pgscan_direct_dma 50369
+pgscan_direct_normal 66284
+pgscan_direct_movable 0
+pgscan_direct_throttle 0
+pginodesteal 0
+slabs_scanned 39582828
+kswapd_inodesteal 110199
+kswapd_low_wmark_hit_quickly 21321
+kswapd_high_wmark_hit_quickly 4112
+pageoutrun 37666
+allocstall 1587
+pgrotated 12086
+drop_pagecache 0
+drop_slab 0
+pgmigrate_success 5923482
+pgmigrate_fail 3439
+compact_migrate_scanned 92906456
+compact_free_scanned 467077168
+compact_isolated 13456528
+compact_stall 197
+compact_fail 42
+compact_success 155
+compact_daemon_wake 2131
+unevictable_pgs_culled 50170
+unevictable_pgs_scanned 0
+unevictable_pgs_rescued 14640
+unevictable_pgs_mlocked 52520
+unevictable_pgs_munlocked 14640
+unevictable_pgs_cleared 2342
+unevictable_pgs_stranded 2342)";
+
+const char kMockStat[] = R"(
+cpu  2655987 822682 2352153 8801203 41917 322733 175055 0 0 0
+cpu0 762178 198125 902284 8678856 41716 152974 68262 0 0 0
+cpu1 613833 243394 504323 15194 96 60625 28785 0 0 0
+cpu2 207349 95060 248856 17351 42 32148 26108 0 0 0
+cpu3 138474 92158 174852 17537 48 25076 25035 0 0 0
+cpu4 278720 34689 141048 18117 1 20782 5873 0 0 0
+cpu5 235376 33907 85098 18278 2 10049 3774 0 0 0
+cpu6 239568 67149 155814 17890 5 11518 3807 0 0 0
+cpu7 180484 58196 139874 17975 3 9556 13407 0 0 0
+intr 238128517 0 0 0 63500984 0 6253792 6 4 5 0 0 0 0 0 0 0 160331 0 0 14 0 0 0 0 0 0 0 0 0 0 0 20430 2279 11 11 83272 0 0 0 0 0 0 0 5754 220829 0 154753 908545 1824602 7314228 0 0 0 6898259 0 0 10 0 0 2 0 0 0 0 0 0 0 42 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 575816 1447531 134022 0 0 0 0 0 435008 319921 2755476 0 0 0 0 91 310212 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 4 0 0 545 901 554 9 3377 4184 12 10 588851 0 2 1109045 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 8 0 0 0 0 0 0 0 0 0 0 0 0 497 0 0 0 0 0 26172 0 0 0 0 0 0 0 1362 0 0 0 0 0 0 0 424 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 23427 0 0 0 0 1 1298 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 108 0 0 0 0 86 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1784935 407979 2140 10562241 52374 74699 6976 84926 222 169088 0 0 0 0 174 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2789 51543 0 83 0 0 0 0 0 0 0 0 0 0 0 0 0 0 8 8 0 13 11 17 1393 0 0 0 0 0 0 0 0 0 0 26 0 0 2 106 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 11150 0 13 0 1 390 6 0 6 4 0 0 0 0 352 284743 2 0 0 24 3 0 3 0 0 0 12 0 668788 2 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 680 0 0
+ctxt 373122860
+btime 1536912218
+processes 243320
+procs_running 1
+procs_blocked 0
+softirq 84611084 10220177 28299167 155083 3035679 6390543 66234 4396819 15604187 0 16443195)";
+
+base::ScopedFile MockOpenReadOnly(const char* path) {
+  base::TempFile tmp_ = base::TempFile::CreateUnlinked();
+  if (!strcmp(path, "/proc/meminfo")) {
+    EXPECT_GT(pwrite(tmp_.fd(), kMockMeminfo, strlen(kMockMeminfo), 0), 0);
+  } else if (!strcmp(path, "/proc/vmstat")) {
+    EXPECT_GT(pwrite(tmp_.fd(), kMockVmstat, strlen(kMockVmstat), 0), 0);
+  } else if (!strcmp(path, "/proc/stat")) {
+    EXPECT_GT(pwrite(tmp_.fd(), kMockStat, strlen(kMockStat), 0), 0);
+  } else {
+    PERFETTO_FATAL("Unexpected file opened %s", path);
+  }
+  return tmp_.ReleaseFD();
+}
+
+class SysStatsDataSourceTest : public ::testing::Test {
+ protected:
+  std::unique_ptr<SysStatsDataSource> GetSysStatsDataSource(
+      const DataSourceConfig& cfg) {
+    auto writer =
+        std::unique_ptr<TraceWriterForTesting>(new TraceWriterForTesting());
+    writer_raw_ = writer.get();
+    auto instance = std::unique_ptr<SysStatsDataSource>(new SysStatsDataSource(
+        &task_runner_, 0, std::move(writer), cfg, MockOpenReadOnly));
+    instance->set_ns_per_user_hz_for_testing(1000000000ull / 100);  // 100 Hz.
+    return instance;
+  }
+
+  void Poller(SysStatsDataSource* ds, std::function<void()> checkpoint) {
+    if (ds->tick_for_testing())
+      checkpoint();
+    else
+      task_runner_.PostDelayedTask(
+          [ds, checkpoint, this] { Poller(ds, checkpoint); }, 1);
+  }
+
+  void WaitTick(SysStatsDataSource* data_source) {
+    auto checkpoint = task_runner_.CreateCheckpoint("on_tick");
+    Poller(data_source, checkpoint);
+    task_runner_.RunUntilCheckpoint("on_tick");
+  }
+
+  TraceWriterForTesting* writer_raw_ = nullptr;
+  base::TestTaskRunner task_runner_;
+};
+
+TEST_F(SysStatsDataSourceTest, Meminfo) {
+  using C = protos::MeminfoCounters;
+  protos::DataSourceConfig config;
+  config.mutable_sys_stats_config()->set_meminfo_period_ms(1);
+  config.mutable_sys_stats_config()->add_meminfo_counters(C::MEMINFO_MEM_TOTAL);
+  config.mutable_sys_stats_config()->add_meminfo_counters(C::MEMINFO_MEM_FREE);
+  config.mutable_sys_stats_config()->add_meminfo_counters(
+      C::MEMINFO_ACTIVE_ANON);
+  config.mutable_sys_stats_config()->add_meminfo_counters(
+      C::MEMINFO_INACTIVE_FILE);
+  config.mutable_sys_stats_config()->add_meminfo_counters(C::MEMINFO_CMA_FREE);
+  DataSourceConfig config_obj;
+  config_obj.FromProto(config);
+  auto data_source = GetSysStatsDataSource(config_obj);
+
+  WaitTick(data_source.get());
+
+  std::unique_ptr<protos::TracePacket> packet = writer_raw_->ParseProto();
+  ASSERT_TRUE(packet->has_sys_stats());
+  const auto& sys_stats = packet->sys_stats();
+  EXPECT_EQ(sys_stats.vmstat_size(), 0);
+  EXPECT_EQ(sys_stats.cpu_stat_size(), 0);
+
+  using KV = std::pair<int, uint64_t>;
+  std::vector<KV> kvs;
+  for (const auto& kv : sys_stats.meminfo())
+    kvs.push_back({kv.key(), kv.value()});
+
+  EXPECT_THAT(kvs,
+              UnorderedElementsAre(KV{C::MEMINFO_MEM_TOTAL, 3744240},     //
+                                   KV{C::MEMINFO_MEM_FREE, 73328},        //
+                                   KV{C::MEMINFO_ACTIVE_ANON, 1322636},   //
+                                   KV{C::MEMINFO_INACTIVE_FILE, 296320},  //
+                                   KV{C::MEMINFO_CMA_FREE, 60}));
+}
+
+TEST_F(SysStatsDataSourceTest, Vmstat) {
+  using C = protos::VmstatCounters;
+  protos::DataSourceConfig config;
+  config.mutable_sys_stats_config()->set_vmstat_period_ms(1);
+  config.mutable_sys_stats_config()->add_vmstat_counters(
+      C::VMSTAT_NR_FREE_PAGES);
+  config.mutable_sys_stats_config()->add_vmstat_counters(C::VMSTAT_PGACTIVATE);
+  config.mutable_sys_stats_config()->add_vmstat_counters(
+      C::VMSTAT_PGMIGRATE_FAIL);
+  DataSourceConfig config_obj;
+  config_obj.FromProto(config);
+  auto data_source = GetSysStatsDataSource(config_obj);
+
+  WaitTick(data_source.get());
+
+  std::unique_ptr<protos::TracePacket> packet = writer_raw_->ParseProto();
+  ASSERT_TRUE(packet->has_sys_stats());
+  const auto& sys_stats = packet->sys_stats();
+  EXPECT_EQ(sys_stats.meminfo_size(), 0);
+  EXPECT_EQ(sys_stats.cpu_stat_size(), 0);
+
+  using KV = std::pair<int, uint64_t>;
+  std::vector<KV> kvs;
+  for (const auto& kv : sys_stats.vmstat())
+    kvs.push_back({kv.key(), kv.value()});
+
+  EXPECT_THAT(kvs, UnorderedElementsAre(KV{C::VMSTAT_NR_FREE_PAGES, 16449},  //
+                                        KV{C::VMSTAT_PGACTIVATE, 11897892},  //
+                                        KV{C::VMSTAT_PGMIGRATE_FAIL, 3439}));
+}
+
+TEST_F(SysStatsDataSourceTest, StatAll) {
+  using C = protos::SysStatsConfig;
+  protos::DataSourceConfig config;
+  config.mutable_sys_stats_config()->set_stat_period_ms(1);
+  config.mutable_sys_stats_config()->add_stat_counters(C::STAT_CPU_TIMES);
+  config.mutable_sys_stats_config()->add_stat_counters(C::STAT_IRQ_COUNTS);
+  config.mutable_sys_stats_config()->add_stat_counters(C::STAT_SOFTIRQ_COUNTS);
+  config.mutable_sys_stats_config()->add_stat_counters(C::STAT_FORK_COUNT);
+  DataSourceConfig config_obj;
+  config_obj.FromProto(config);
+  auto data_source = GetSysStatsDataSource(config_obj);
+
+  WaitTick(data_source.get());
+
+  std::unique_ptr<protos::TracePacket> packet = writer_raw_->ParseProto();
+  ASSERT_TRUE(packet);
+  ASSERT_TRUE(packet->has_sys_stats());
+  const auto& sys_stats = packet->sys_stats();
+  EXPECT_EQ(sys_stats.meminfo_size(), 0);
+  EXPECT_EQ(sys_stats.vmstat_size(), 0);
+
+  ASSERT_EQ(sys_stats.cpu_stat_size(), 8);
+  EXPECT_EQ(sys_stats.cpu_stat(0).user_ns(), 762178 * 10000000ull);
+  EXPECT_EQ(sys_stats.cpu_stat(0).system_mode_ns(), 902284 * 10000000ull);
+  EXPECT_EQ(sys_stats.cpu_stat(0).softirq_ns(), 68262 * 10000000ull);
+  EXPECT_EQ(sys_stats.cpu_stat(7).user_ns(), 180484 * 10000000ull);
+  EXPECT_EQ(sys_stats.cpu_stat(7).system_mode_ns(), 139874 * 10000000ull);
+  EXPECT_EQ(sys_stats.cpu_stat(7).softirq_ns(), 13407 * 10000000ull);
+
+  EXPECT_EQ(sys_stats.num_forks(), 243320);
+
+  EXPECT_EQ(sys_stats.num_irq_total(), 238128517);
+  ASSERT_EQ(sys_stats.num_irq_size(), 793);
+  EXPECT_EQ(sys_stats.num_irq(0).count(), 0);
+  EXPECT_EQ(sys_stats.num_irq(3).count(), 63500984);
+  EXPECT_EQ(sys_stats.num_irq(790).count(), 680);
+
+  EXPECT_EQ(sys_stats.num_softirq_total(), 84611084);
+  ASSERT_EQ(sys_stats.num_softirq_size(), 10);
+  EXPECT_EQ(sys_stats.num_softirq(0).count(), 10220177);
+  EXPECT_EQ(sys_stats.num_softirq(9).count(), 16443195);
+
+  EXPECT_EQ(sys_stats.num_softirq_total(), 84611084);
+}
+
+TEST_F(SysStatsDataSourceTest, StatForksOnly) {
+  using C = protos::SysStatsConfig;
+  protos::DataSourceConfig config;
+  config.mutable_sys_stats_config()->set_stat_period_ms(1);
+  config.mutable_sys_stats_config()->add_stat_counters(C::STAT_FORK_COUNT);
+  DataSourceConfig config_obj;
+  config_obj.FromProto(config);
+  auto data_source = GetSysStatsDataSource(config_obj);
+
+  WaitTick(data_source.get());
+
+  std::unique_ptr<protos::TracePacket> packet = writer_raw_->ParseProto();
+  ASSERT_TRUE(packet->has_sys_stats());
+  const auto& sys_stats = packet->sys_stats();
+  EXPECT_EQ(sys_stats.meminfo_size(), 0);
+  EXPECT_EQ(sys_stats.vmstat_size(), 0);
+  ASSERT_EQ(sys_stats.cpu_stat_size(), 0);
+  EXPECT_EQ(sys_stats.num_forks(), 243320);
+  EXPECT_EQ(sys_stats.num_irq_total(), 0);
+  ASSERT_EQ(sys_stats.num_irq_size(), 0);
+  EXPECT_EQ(sys_stats.num_softirq_total(), 0);
+  ASSERT_EQ(sys_stats.num_softirq_size(), 0);
+}
+
+}  // namespace
+}  // namespace perfetto
diff --git a/src/tracing/BUILD.gn b/src/tracing/BUILD.gn
index 2e8831f..4455bfb 100644
--- a/src/tracing/BUILD.gn
+++ b/src/tracing/BUILD.gn
@@ -19,7 +19,7 @@
 source_set("tracing") {
   public_deps = [
     "../../include/perfetto/tracing/core",
-    "../../protos/perfetto/common",
+    "../../protos/perfetto/common:lite",
     "../../protos/perfetto/trace:minimal_lite",
     "../../protos/perfetto/trace:trusted_lite",
     "../../protos/perfetto/trace:zero",
@@ -27,7 +27,7 @@
   deps = [
     "../../gn:default_deps",
     "../../gn:gtest_prod_config",
-    "../../protos/perfetto/config",
+    "../../protos/perfetto/config:lite",
     "../base",
     "../protozero",
   ]
@@ -51,6 +51,7 @@
     "core/shared_memory_arbiter_impl.h",
     "core/sliced_protobuf_input_stream.cc",
     "core/sliced_protobuf_input_stream.h",
+    "core/sys_stats_config.cc",
     "core/test_config.cc",
     "core/trace_buffer.cc",
     "core/trace_buffer.h",
@@ -71,7 +72,7 @@
     ":tracing",
     "../../gn:default_deps",
     "../../gn:gtest_deps",
-    "../../protos/perfetto/config",
+    "../../protos/perfetto/config:lite",
     "../../protos/perfetto/trace:lite",
     "../../protos/perfetto/trace:zero",
     "../base",
diff --git a/src/tracing/core/data_source_config.cc b/src/tracing/core/data_source_config.cc
index 0addd0e..405b430 100644
--- a/src/tracing/core/data_source_config.cc
+++ b/src/tracing/core/data_source_config.cc
@@ -32,6 +32,7 @@
 #include "perfetto/config/ftrace/ftrace_config.pb.h"
 #include "perfetto/config/inode_file/inode_file_config.pb.h"
 #include "perfetto/config/process_stats/process_stats_config.pb.h"
+#include "perfetto/config/sys_stats/sys_stats_config.pb.h"
 #include "perfetto/config/test_config.pb.h"
 
 namespace perfetto {
@@ -72,6 +73,8 @@
 
   process_stats_config_.FromProto(proto.process_stats_config());
 
+  sys_stats_config_.FromProto(proto.sys_stats_config());
+
   static_assert(sizeof(legacy_config_) == sizeof(proto.legacy_config()),
                 "size mismatch");
   legacy_config_ = static_cast<decltype(legacy_config_)>(proto.legacy_config());
@@ -112,6 +115,8 @@
 
   process_stats_config_.ToProto(proto->mutable_process_stats_config());
 
+  sys_stats_config_.ToProto(proto->mutable_sys_stats_config());
+
   static_assert(sizeof(legacy_config_) == sizeof(proto->legacy_config()),
                 "size mismatch");
   proto->set_legacy_config(
diff --git a/src/tracing/core/sys_stats_config.cc b/src/tracing/core/sys_stats_config.cc
new file mode 100644
index 0000000..444fc84
--- /dev/null
+++ b/src/tracing/core/sys_stats_config.cc
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+/*******************************************************************************
+ * AUTOGENERATED - DO NOT EDIT
+ *******************************************************************************
+ * This file has been generated from the protobuf message
+ * perfetto/config/sys_stats/sys_stats_config.proto
+ * by
+ * ../../tools/proto_to_cpp/proto_to_cpp.cc.
+ * If you need to make changes here, change the .proto file and then run
+ * ./tools/gen_tracing_cpp_headers_from_protos.py
+ */
+
+#include "perfetto/tracing/core/sys_stats_config.h"
+
+#include "perfetto/common/sys_stats_counters.pb.h"
+#include "perfetto/config/sys_stats/sys_stats_config.pb.h"
+
+namespace perfetto {
+
+SysStatsConfig::SysStatsConfig() = default;
+SysStatsConfig::~SysStatsConfig() = default;
+SysStatsConfig::SysStatsConfig(const SysStatsConfig&) = default;
+SysStatsConfig& SysStatsConfig::operator=(const SysStatsConfig&) = default;
+SysStatsConfig::SysStatsConfig(SysStatsConfig&&) noexcept = default;
+SysStatsConfig& SysStatsConfig::operator=(SysStatsConfig&&) = default;
+
+void SysStatsConfig::FromProto(const perfetto::protos::SysStatsConfig& proto) {
+  static_assert(sizeof(meminfo_period_ms_) == sizeof(proto.meminfo_period_ms()),
+                "size mismatch");
+  meminfo_period_ms_ =
+      static_cast<decltype(meminfo_period_ms_)>(proto.meminfo_period_ms());
+
+  meminfo_counters_.clear();
+  for (const auto& field : proto.meminfo_counters()) {
+    meminfo_counters_.emplace_back();
+    static_assert(
+        sizeof(meminfo_counters_.back()) == sizeof(proto.meminfo_counters(0)),
+        "size mismatch");
+    meminfo_counters_.back() =
+        static_cast<decltype(meminfo_counters_)::value_type>(field);
+  }
+
+  static_assert(sizeof(vmstat_period_ms_) == sizeof(proto.vmstat_period_ms()),
+                "size mismatch");
+  vmstat_period_ms_ =
+      static_cast<decltype(vmstat_period_ms_)>(proto.vmstat_period_ms());
+
+  vmstat_counters_.clear();
+  for (const auto& field : proto.vmstat_counters()) {
+    vmstat_counters_.emplace_back();
+    static_assert(
+        sizeof(vmstat_counters_.back()) == sizeof(proto.vmstat_counters(0)),
+        "size mismatch");
+    vmstat_counters_.back() =
+        static_cast<decltype(vmstat_counters_)::value_type>(field);
+  }
+
+  static_assert(sizeof(stat_period_ms_) == sizeof(proto.stat_period_ms()),
+                "size mismatch");
+  stat_period_ms_ =
+      static_cast<decltype(stat_period_ms_)>(proto.stat_period_ms());
+
+  stat_counters_.clear();
+  for (const auto& field : proto.stat_counters()) {
+    stat_counters_.emplace_back();
+    static_assert(
+        sizeof(stat_counters_.back()) == sizeof(proto.stat_counters(0)),
+        "size mismatch");
+    stat_counters_.back() =
+        static_cast<decltype(stat_counters_)::value_type>(field);
+  }
+  unknown_fields_ = proto.unknown_fields();
+}
+
+void SysStatsConfig::ToProto(perfetto::protos::SysStatsConfig* proto) const {
+  proto->Clear();
+
+  static_assert(
+      sizeof(meminfo_period_ms_) == sizeof(proto->meminfo_period_ms()),
+      "size mismatch");
+  proto->set_meminfo_period_ms(
+      static_cast<decltype(proto->meminfo_period_ms())>(meminfo_period_ms_));
+
+  for (const auto& it : meminfo_counters_) {
+    proto->add_meminfo_counters(
+        static_cast<decltype(proto->meminfo_counters(0))>(it));
+    static_assert(sizeof(it) == sizeof(proto->meminfo_counters(0)),
+                  "size mismatch");
+  }
+
+  static_assert(sizeof(vmstat_period_ms_) == sizeof(proto->vmstat_period_ms()),
+                "size mismatch");
+  proto->set_vmstat_period_ms(
+      static_cast<decltype(proto->vmstat_period_ms())>(vmstat_period_ms_));
+
+  for (const auto& it : vmstat_counters_) {
+    proto->add_vmstat_counters(
+        static_cast<decltype(proto->vmstat_counters(0))>(it));
+    static_assert(sizeof(it) == sizeof(proto->vmstat_counters(0)),
+                  "size mismatch");
+  }
+
+  static_assert(sizeof(stat_period_ms_) == sizeof(proto->stat_period_ms()),
+                "size mismatch");
+  proto->set_stat_period_ms(
+      static_cast<decltype(proto->stat_period_ms())>(stat_period_ms_));
+
+  for (const auto& it : stat_counters_) {
+    proto->add_stat_counters(
+        static_cast<decltype(proto->stat_counters(0))>(it));
+    static_assert(sizeof(it) == sizeof(proto->stat_counters(0)),
+                  "size mismatch");
+  }
+  *(proto->mutable_unknown_fields()) = unknown_fields_;
+}
+
+}  // namespace perfetto
diff --git a/src/tracing/core/sys_stats_counters.cc b/src/tracing/core/sys_stats_counters.cc
new file mode 100644
index 0000000..6edb5b1
--- /dev/null
+++ b/src/tracing/core/sys_stats_counters.cc
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+/*******************************************************************************
+ * AUTOGENERATED - DO NOT EDIT
+ *******************************************************************************
+ * This file has been generated from the protobuf message
+ * perfetto/common/sys_stats_counters.proto
+ * by
+ * ../../tools/proto_to_cpp/proto_to_cpp.cc.
+ * If you need to make changes here, change the .proto file and then run
+ * ./tools/gen_tracing_cpp_headers_from_protos.py
+ */
+
+#include "perfetto/tracing/core/sys_stats_counters.h"
+
+#include "perfetto/common/sys_stats_counters.pb.h"
+
+namespace perfetto {}  // namespace perfetto
diff --git a/src/tracing/core/trace_writer_for_testing.cc b/src/tracing/core/trace_writer_for_testing.cc
index 1b7e531..3f95c0f 100644
--- a/src/tracing/core/trace_writer_for_testing.cc
+++ b/src/tracing/core/trace_writer_for_testing.cc
@@ -44,12 +44,9 @@
 
 std::unique_ptr<protos::TracePacket> TraceWriterForTesting::ParseProto() {
   PERFETTO_CHECK(cur_packet_->is_finalized());
-  size_t chunk_size_ = base::kPageSize;
   auto packet = std::unique_ptr<protos::TracePacket>(new protos::TracePacket());
-  size_t msg_size =
-      delegate_.chunks().size() * chunk_size_ - stream_.bytes_available();
-  std::unique_ptr<uint8_t[]> buffer = delegate_.StitchChunks(msg_size);
-  if (!packet->ParseFromArray(buffer.get(), static_cast<int>(msg_size)))
+  std::vector<uint8_t> buffer = delegate_.StitchChunks();
+  if (!packet->ParseFromArray(buffer.data(), static_cast<int>(buffer.size())))
     return nullptr;
   return packet;
 }
diff --git a/test/configs/BUILD.gn b/test/configs/BUILD.gn
index 1505529..cbc0970 100644
--- a/test/configs/BUILD.gn
+++ b/test/configs/BUILD.gn
@@ -30,6 +30,7 @@
       "long_trace.cfg",
       "processes.cfg",
       "summary.cfg",
+      "sys_stats.cfg",
     ]
 
     outputs = [
diff --git a/test/configs/sys_stats.cfg b/test/configs/sys_stats.cfg
new file mode 100644
index 0000000..fd4f6ea
--- /dev/null
+++ b/test/configs/sys_stats.cfg
@@ -0,0 +1,89 @@
+# Example config for a trace that polls system counters.
+
+duration_ms: 2000
+
+buffers {
+  size_kb: 16384
+  fill_policy: RING_BUFFER
+}
+
+buffers {
+  size_kb: 16384
+  fill_policy: RING_BUFFER
+}
+
+# Enable various data sources as usual.
+data_sources {
+  config {
+    name: "linux.ftrace"
+    target_buffer: 0
+    ftrace_config {
+      # These parameters affect only the kernel trace buffer size and how
+      # frequently it gets moved into the userspace buffer defined above.
+      buffer_size_kb: 16384
+      drain_period_ms: 250
+      ftrace_events: "cpu_frequency"
+      ftrace_events: "cpu_idle"
+      ftrace_events: "sched_switch"
+      ftrace_events: "tracing_mark_write"
+    }
+  }
+}
+
+data_sources {
+  config {
+    name: "linux.process_stats"
+    target_buffer: 0
+  }
+}
+
+data_sources {
+  config {
+    name: "linux.sys_stats"
+    target_buffer: 1
+    sys_stats_config {
+      meminfo_period_ms: 100
+      meminfo_counters: MEMINFO_MEM_AVAILABLE
+      meminfo_counters: MEMINFO_BUFFERS
+      meminfo_counters: MEMINFO_CACHED
+      meminfo_counters: MEMINFO_SWAP_CACHED
+      meminfo_counters: MEMINFO_ACTIVE
+      meminfo_counters: MEMINFO_INACTIVE
+      meminfo_counters: MEMINFO_ACTIVE_ANON
+      meminfo_counters: MEMINFO_INACTIVE_ANON
+      meminfo_counters: MEMINFO_ACTIVE_FILE
+      meminfo_counters: MEMINFO_INACTIVE_FILE
+      meminfo_counters: MEMINFO_UNEVICTABLE
+
+      vmstat_period_ms: 100
+      vmstat_counters: VMSTAT_NR_FREE_PAGES
+      vmstat_counters: VMSTAT_NR_ALLOC_BATCH
+      vmstat_counters: VMSTAT_NR_INACTIVE_ANON
+      vmstat_counters: VMSTAT_NR_ACTIVE_ANON
+      vmstat_counters: VMSTAT_NR_INACTIVE_FILE
+      vmstat_counters: VMSTAT_NR_ACTIVE_FILE
+      vmstat_counters: VMSTAT_NR_UNEVICTABLE
+      vmstat_counters: VMSTAT_NR_MLOCK
+      vmstat_counters: VMSTAT_NR_ANON_PAGES
+      vmstat_counters: VMSTAT_NR_MAPPED
+      vmstat_counters: VMSTAT_NR_FILE_PAGES
+      vmstat_counters: VMSTAT_NR_DIRTY
+      vmstat_counters: VMSTAT_NR_WRITEBACK
+      vmstat_counters: VMSTAT_NR_SLAB_RECLAIMABLE
+      vmstat_counters: VMSTAT_NR_SLAB_UNRECLAIMABLE
+      vmstat_counters: VMSTAT_NR_PAGE_TABLE_PAGES
+      vmstat_counters: VMSTAT_NR_KERNEL_STACK
+      vmstat_counters: VMSTAT_NR_OVERHEAD
+      vmstat_counters: VMSTAT_NR_UNSTABLE
+      vmstat_counters: VMSTAT_NR_BOUNCE
+      vmstat_counters: VMSTAT_NR_VMSCAN_WRITE
+      vmstat_counters: VMSTAT_NR_VMSCAN_IMMEDIATE_RECLAIM
+      vmstat_counters: VMSTAT_NR_WRITEBACK_TEMP
+
+      stat_period_ms: 100
+      stat_counters: STAT_CPU_TIMES
+      stat_counters: STAT_IRQ_COUNTS
+      stat_counters: STAT_FORK_COUNT
+    }
+  }
+}
\ No newline at end of file
diff --git a/tools/gen_merged_protos b/tools/gen_merged_protos
index 77bab2b..8d42fcd 100755
--- a/tools/gen_merged_protos
+++ b/tools/gen_merged_protos
@@ -22,11 +22,13 @@
 import sys
 
 CONFIG_PROTOS = (
+  'protos/perfetto/common/sys_stats_counters.proto',
   'protos/perfetto/config/chrome/chrome_config.proto',
-  'protos/perfetto/config/inode_file/inode_file_config.proto',
-  'protos/perfetto/config/process_stats/process_stats_config.proto',
   'protos/perfetto/config/data_source_config.proto',
   'protos/perfetto/config/ftrace/ftrace_config.proto',
+  'protos/perfetto/config/inode_file/inode_file_config.proto',
+  'protos/perfetto/config/process_stats/process_stats_config.proto',
+  'protos/perfetto/config/sys_stats/sys_stats_config.proto',
   'protos/perfetto/config/test_config.proto',
   'protos/perfetto/config/trace_config.proto',
 )
@@ -34,12 +36,15 @@
 MERGED_CONFIG_PROTO = 'protos/perfetto/config/perfetto_config.proto'
 
 TRACE_PROTOS = (
+  'protos/perfetto/common/sys_stats_counters.proto',
   'protos/perfetto/trace/trace.proto',
   'protos/perfetto/trace/trace_packet.proto',
   'protos/perfetto/trace/ftrace/ftrace_event_bundle.proto',
   'protos/perfetto/trace/ftrace/ftrace_event.proto',
   'protos/perfetto/trace/filesystem/inode_file_map.proto',
   'protos/perfetto/trace/ps/process_tree.proto',
+  'protos/perfetto/trace/clock_snapshot.proto',
+  'protos/perfetto/trace/sys_stats/sys_stats.proto',
 
   # Print proto is special: it doesn't have a enable file so is
   # not present in genfs_contexts.
diff --git a/tools/gen_tracing_cpp_headers_from_protos.py b/tools/gen_tracing_cpp_headers_from_protos.py
index ce7fdb6..901d73e 100755
--- a/tools/gen_tracing_cpp_headers_from_protos.py
+++ b/tools/gen_tracing_cpp_headers_from_protos.py
@@ -22,11 +22,13 @@
   'perfetto/config/data_source_config.proto',
   'perfetto/config/inode_file/inode_file_config.proto',
   'perfetto/config/process_stats/process_stats_config.proto',
+  'perfetto/config/sys_stats/sys_stats_config.proto',
   'perfetto/config/data_source_descriptor.proto',
   'perfetto/config/ftrace/ftrace_config.proto',
   'perfetto/config/trace_config.proto',
   'perfetto/config/test_config.proto',
   'perfetto/common/commit_data_request.proto',
+  'perfetto/common/sys_stats_counters.proto',
 )
 
 HEADER_PATH = 'include/perfetto/tracing/core'
diff --git a/tools/proto_to_cpp/proto_to_cpp.cc b/tools/proto_to_cpp/proto_to_cpp.cc
index 2a77542..ac773ea 100644
--- a/tools/proto_to_cpp/proto_to_cpp.cc
+++ b/tools/proto_to_cpp/proto_to_cpp.cc
@@ -270,7 +270,7 @@
 
   cpp_printer.Print("}  // namespace perfetto\n");
   header_printer.Print("}  // namespace perfetto\n");
-  header_printer.Print("#endif  // $g$\n", "g", include_guard);
+  header_printer.Print("\n#endif  // $g$\n", "g", include_guard);
 }
 
 void ProtoToCpp::GenHeader(const Descriptor* msg, Printer* p) {
diff --git a/tools/trace_to_text/BUILD.gn b/tools/trace_to_text/BUILD.gn
index 56b24bc..ba7cd9f 100644
--- a/tools/trace_to_text/BUILD.gn
+++ b/tools/trace_to_text/BUILD.gn
@@ -20,6 +20,7 @@
     "../../gn:default_deps",
     "../../gn:protobuf_full_deps",
     "../../include/perfetto/base",
+    "../../include/perfetto/traced:sys_stats_counters",
     "../../protos/perfetto/trace:lite",
     "../../protos/perfetto/trace/ftrace:lite",
   ]
diff --git a/tools/trace_to_text/main.cc b/tools/trace_to_text/main.cc
index b775326..709d139 100644
--- a/tools/trace_to_text/main.cc
+++ b/tools/trace_to_text/main.cc
@@ -43,6 +43,7 @@
 #include "perfetto/trace/ftrace/ftrace_stats.pb.h"
 #include "perfetto/trace/trace.pb.h"
 #include "perfetto/trace/trace_packet.pb.h"
+#include "perfetto/traced/sys_stats_counters.h"
 #include "tools/trace_to_text/ftrace_event_formatter.h"
 #include "tools/trace_to_text/ftrace_inode_handler.h"
 
@@ -92,6 +93,7 @@
 using protos::FtraceStats;
 using protos::FtraceStats_Phase_START_OF_TRACE;
 using protos::FtraceStats_Phase_END_OF_TRACE;
+using protos::SysStats;
 using Entry = protos::InodeFileMap::Entry;
 using Process = protos::ProcessTree::Process;
 
@@ -176,17 +178,46 @@
                     bool wrap_in_json) {
   std::multimap<uint64_t, std::string> sorted;
 
-  ForEachPacketInTrace(input, [&sorted](const protos::TracePacket& packet) {
-    if (!packet.has_ftrace_events())
-      return;
+  std::vector<const char*> meminfo_strs = BuildMeminfoCounterNames();
+  std::vector<const char*> vmstat_strs = BuildVmstatCounterNames();
 
-    const FtraceEventBundle& bundle = packet.ftrace_events();
-    for (const FtraceEvent& event : bundle.event()) {
-      std::string line =
-          FormatFtraceEvent(event.timestamp(), bundle.cpu(), event);
-      if (line == "")
-        continue;
-      sorted.emplace(event.timestamp(), line);
+  ForEachPacketInTrace(input, [&sorted, &meminfo_strs, &vmstat_strs](
+                                  const protos::TracePacket& packet) {
+    if (packet.has_ftrace_events()) {
+      const FtraceEventBundle& bundle = packet.ftrace_events();
+      for (const FtraceEvent& event : bundle.event()) {
+        std::string line =
+            FormatFtraceEvent(event.timestamp(), bundle.cpu(), event);
+        if (line == "")
+          continue;
+        sorted.emplace(event.timestamp(), line);
+      }
+    }  // packet.has_ftrace_events
+
+    if (packet.has_sys_stats()) {
+      const SysStats& sys_stats = packet.sys_stats();
+      for (const auto& meminfo : sys_stats.meminfo()) {
+        FtraceEvent event;
+        uint64_t ts = static_cast<uint64_t>(packet.timestamp());
+        char str[256];
+        event.set_timestamp(ts);
+        event.set_pid(1);
+        sprintf(str, "C|1|%s|%" PRIu64, meminfo_strs[meminfo.key()],
+                static_cast<uint64_t>(meminfo.value()));
+        event.mutable_print()->set_buf(str);
+        sorted.emplace(ts, FormatFtraceEvent(ts, 0, event));
+      }
+      for (const auto& vmstat : sys_stats.vmstat()) {
+        FtraceEvent event;
+        uint64_t ts = static_cast<uint64_t>(packet.timestamp());
+        char str[256];
+        event.set_timestamp(ts);
+        event.set_pid(1);
+        sprintf(str, "C|1|%s|%" PRIu64, vmstat_strs[vmstat.key()],
+                static_cast<uint64_t>(vmstat.value()));
+        event.mutable_print()->set_buf(str);
+        sorted.emplace(ts, FormatFtraceEvent(ts, 0, event));
+      }
     }
   });