Merge "Chrome trace events: bind_id can be 64bit"
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/buildtools/.gitignore b/buildtools/.gitignore
index 83e0217..1dd47b9 100644
--- a/buildtools/.gitignore
+++ b/buildtools/.gitignore
@@ -1,6 +1,7 @@
android_sdk/
aosp-*/
benchmark/
+bionic/
clang/
clang_format/
emulator/
@@ -10,6 +11,7 @@
libcxx/
libcxxabi/
libunwind/
+linenoise/
linux/
linux64/
mac/
diff --git a/buildtools/BUILD.gn b/buildtools/BUILD.gn
index 9a16d42..0415b3e 100644
--- a/buildtools/BUILD.gn
+++ b/buildtools/BUILD.gn
@@ -597,6 +597,7 @@
"-DSQLITE_CORE",
"-DSQLITE_TEMP_STORE=3",
"-DSQLITE_OMIT_LOAD_EXTENSION",
+ "-DSQLITE_OMIT_RANDOMNESS",
]
}
@@ -750,3 +751,22 @@
configs -= [ "//gn/standalone:extra_warnings" ]
public_configs = [ ":jsoncpp_config" ]
}
+
+config("linenoise_config") {
+ cflags = [
+ # Using -isystem instead of include_dirs (-I), so we don't need to suppress
+ # warnings coming from third-party headers. Doing so would mask warnings in
+ # our own code.
+ "-isystem",
+ rebase_path("linenoise", root_build_dir),
+ ]
+}
+
+source_set("linenoise") {
+ sources = [
+ "linenoise/linenoise.c",
+ "linenoise/linenoise.h",
+ ]
+ configs -= [ "//gn/standalone:extra_warnings" ]
+ public_configs = [ ":linenoise_config" ]
+}
diff --git a/gn/standalone/wasm.gni b/gn/standalone/wasm.gni
index 0304007..055af6a 100644
--- a/gn/standalone/wasm.gni
+++ b/gn/standalone/wasm.gni
@@ -64,7 +64,9 @@
"-s",
"EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'cwrap', 'addFunction']",
]
- if (!is_debug) {
+ if (is_debug) {
+ _target_ldflags += [ "-g4" ]
+ } else {
_target_ldflags += [ "-O3" ]
}
@@ -106,6 +108,9 @@
outputs = [
"$root_out_dir/$_lib_name.wasm",
]
+ if (is_debug) {
+ outputs += [ "$root_out_dir/$_lib_name.wasm.map" ]
+ }
args = [ "--noop" ]
script = "//gn/standalone/build_tool_wrapper.py"
}
diff --git a/include/perfetto/base/build_config.h b/include/perfetto/base/build_config.h
index e083415..ef00b16 100644
--- a/include/perfetto/base/build_config.h
+++ b/include/perfetto/base/build_config.h
@@ -70,6 +70,13 @@
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_CHROMIUM_BUILD() 0
#endif
+#if !defined(PERFETTO_BUILD_WITH_CHROMIUM) && \
+ !defined(PERFETTO_BUILD_WITH_ANDROID)
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_STANDALONE_BUILD() 1
+#else
+#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_STANDALONE_BUILD() 0
+#endif
+
#if defined(PERFETTO_START_DAEMONS_FOR_TESTING)
#define PERFETTO_BUILDFLAG_DEFINE_PERFETTO_START_DAEMONS() 1
#else
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/BUILD.gn b/src/trace_processor/BUILD.gn
index 811443f..092c6bf 100644
--- a/src/trace_processor/BUILD.gn
+++ b/src/trace_processor/BUILD.gn
@@ -108,6 +108,9 @@
"../../protos/perfetto/trace_processor:lite",
"../base",
]
+ if (build_standalone) {
+ deps += [ "../../buildtools:linenoise" ]
+ }
sources = [
"trace_processor_shell.cc",
]
diff --git a/src/trace_processor/counters_table.cc b/src/trace_processor/counters_table.cc
index 86cf70c..1cc4026 100644
--- a/src/trace_processor/counters_table.cc
+++ b/src/trace_processor/counters_table.cc
@@ -42,8 +42,9 @@
"name text, "
"value UNSIGNED BIG INT, "
"dur UNSIGNED BIG INT, "
+ "value_delta UNSIGNED BIG INT, "
"ref UNSIGNED INT, "
- "reftype TEXT, "
+ "ref_type TEXT, "
"PRIMARY KEY(name, ts, ref)"
") WITHOUT ROWID;";
}
@@ -52,54 +53,65 @@
return std::unique_ptr<Table::Cursor>(new Cursor(storage_));
}
-int CountersTable::BestIndex(const QueryConstraints& qc, BestIndexInfo* info) {
- info->estimated_cost = 10;
-
- // If the query has a constraint on the |kRef| field, return a reduced cost
- // because we can do that filter efficiently.
- const auto& constraints = qc.constraints();
- if (constraints.size() == 1 && constraints.front().iColumn == Column::kRef) {
- info->estimated_cost = IsOpEq(constraints.front().op) ? 1 : 10;
- }
-
+int CountersTable::BestIndex(const QueryConstraints&, BestIndexInfo* info) {
+ // TODO(taylori): Work out cost dependant on constraints.
+ info->estimated_cost =
+ static_cast<uint32_t>(storage_->counters().counter_count());
return SQLITE_OK;
}
-CountersTable::Cursor::Cursor(const TraceStorage* storage)
- : storage_(storage) {}
+CountersTable::Cursor::Cursor(const TraceStorage* storage) : storage_(storage) {
+ num_rows_ = storage->counters().counter_count();
+}
int CountersTable::Cursor::Column(sqlite3_context* context, int N) {
switch (N) {
case Column::kTimestamp: {
- const auto& freq = storage_->GetFreqForCpu(current_cpu_);
- sqlite3_result_int64(context,
- static_cast<int64_t>(freq[index_in_cpu_].first));
+ sqlite3_result_int64(
+ context,
+ static_cast<int64_t>(storage_->counters().timestamps()[row_]));
break;
}
case Column::kValue: {
- const auto& freq = storage_->GetFreqForCpu(current_cpu_);
- sqlite3_result_int64(context, freq[index_in_cpu_].second);
+ sqlite3_result_int64(
+ context, static_cast<int64_t>(storage_->counters().values()[row_]));
break;
}
case Column::kName: {
- sqlite3_result_text(context, "cpufreq", -1, nullptr);
+ sqlite3_result_text(
+ context,
+ storage_->GetString(storage_->counters().name_ids()[row_]).c_str(),
+ -1, nullptr);
break;
}
case Column::kRef: {
- sqlite3_result_int64(context, current_cpu_);
+ sqlite3_result_int64(
+ context, static_cast<int64_t>(storage_->counters().refs()[row_]));
break;
}
case Column::kRefType: {
- sqlite3_result_text(context, "cpu", -1, nullptr);
+ switch (storage_->counters().types()[row_]) {
+ case RefType::kCPU_ID: {
+ sqlite3_result_text(context, "cpu", -1, nullptr);
+ break;
+ }
+ case RefType::kUTID: {
+ sqlite3_result_text(context, "utid", -1, nullptr);
+ break;
+ }
+ }
break;
}
case Column::kDuration: {
- const auto& freq = storage_->GetFreqForCpu(current_cpu_);
- uint64_t duration = 0;
- if (index_in_cpu_ + 1 < freq.size()) {
- duration = freq[index_in_cpu_ + 1].first - freq[index_in_cpu_].first;
- }
- sqlite3_result_int64(context, static_cast<int64_t>(duration));
+ sqlite3_result_int64(
+ context,
+ static_cast<int64_t>(storage_->counters().durations()[row_]));
+ break;
+ }
+ case Column::kValueDelta: {
+ sqlite3_result_int64(
+ context,
+ static_cast<int64_t>(storage_->counters().value_deltas()[row_]));
break;
}
default:
@@ -109,47 +121,17 @@
return SQLITE_OK;
}
-int CountersTable::Cursor::Filter(const QueryConstraints& qc,
- sqlite3_value** argv) {
- for (size_t j = 0; j < qc.constraints().size(); j++) {
- const auto& cs = qc.constraints()[j];
- if (cs.iColumn == Column::kRef) {
- auto constraint_cpu = static_cast<uint32_t>(sqlite3_value_int(argv[j]));
- if (IsOpEq(cs.op)) {
- filter_by_cpu_ = true;
- filter_cpu_ = constraint_cpu;
- }
- }
- }
-
+int CountersTable::Cursor::Filter(const QueryConstraints&, sqlite3_value**) {
return SQLITE_OK;
}
int CountersTable::Cursor::Next() {
- if (filter_by_cpu_) {
- current_cpu_ = filter_cpu_;
- ++index_in_cpu_;
- } else {
- if (index_in_cpu_ < storage_->GetFreqForCpu(current_cpu_).size() - 1) {
- index_in_cpu_++;
- } else if (current_cpu_ < storage_->GetMaxCpu()) {
- ++current_cpu_;
- index_in_cpu_ = 0;
- }
- // If the cpu is has no freq events, move to the next one.
- while (current_cpu_ != storage_->GetMaxCpu() &&
- storage_->GetFreqForCpu(current_cpu_).size() == 0) {
- ++current_cpu_;
- }
- }
+ row_++;
return SQLITE_OK;
}
int CountersTable::Cursor::Eof() {
- if (filter_by_cpu_) {
- return index_in_cpu_ == storage_->GetFreqForCpu(current_cpu_).size();
- }
- return current_cpu_ == storage_->GetMaxCpu();
+ return row_ >= num_rows_;
}
} // namespace trace_processor
diff --git a/src/trace_processor/counters_table.h b/src/trace_processor/counters_table.h
index a28f366..5ef21ba 100644
--- a/src/trace_processor/counters_table.h
+++ b/src/trace_processor/counters_table.h
@@ -33,8 +33,9 @@
kName = 1,
kValue = 2,
kDuration = 3,
- kRef = 4,
- kRefType = 5,
+ kValueDelta = 4,
+ kRef = 5,
+ kRefType = 6,
};
static void RegisterTable(sqlite3* db, const TraceStorage* storage);
@@ -58,10 +59,8 @@
int Column(sqlite3_context*, int N) override;
private:
- bool filter_by_cpu_ = false;
- uint32_t current_cpu_ = 0;
- size_t index_in_cpu_ = 0;
- uint32_t filter_cpu_ = 0;
+ size_t num_rows_;
+ size_t row_ = 0;
const TraceStorage* const storage_;
};
diff --git a/src/trace_processor/counters_table_unittest.cc b/src/trace_processor/counters_table_unittest.cc
index 4ec5792..211cb78 100644
--- a/src/trace_processor/counters_table_unittest.cc
+++ b/src/trace_processor/counters_table_unittest.cc
@@ -15,6 +15,7 @@
*/
#include "src/trace_processor/counters_table.h"
+#include "src/trace_processor/sched_tracker.h"
#include "src/trace_processor/scoped_db.h"
#include "src/trace_processor/trace_processor_context.h"
@@ -33,6 +34,7 @@
db_.reset(db);
context_.storage.reset(new TraceStorage());
+ context_.sched_tracker.reset(new SchedTracker(&context_));
CountersTable::RegisterTable(db_.get(), context_.storage.get());
}
@@ -60,20 +62,23 @@
TEST_F(CountersTableUnittest, SelectWhereCpu) {
uint64_t timestamp = 1000;
uint32_t freq = 3000;
- context_.storage->PushCpuFreq(timestamp, 1 /* cpu */, freq);
- context_.storage->PushCpuFreq(timestamp + 1, 1 /* cpu */, freq + 1000);
- context_.storage->PushCpuFreq(timestamp + 2, 2 /* cpu */, freq + 2000);
+ context_.storage->mutable_counters()->AddCounter(
+ timestamp, 0, 1, freq, 0, 1 /* cpu */, RefType::kCPU_ID);
+ context_.storage->mutable_counters()->AddCounter(
+ timestamp + 1, 1, 1, freq + 1000, 1000, 1 /* cpu */, RefType::kCPU_ID);
+ context_.storage->mutable_counters()->AddCounter(
+ timestamp + 2, 1, 1, freq + 2000, 1000, 2 /* cpu */, RefType::kCPU_ID);
PrepareValidStatement("SELECT ts, dur, value FROM counters where ref = 1");
ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
ASSERT_EQ(sqlite3_column_int(*stmt_, 0), timestamp);
- ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 1);
+ ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 0);
ASSERT_EQ(sqlite3_column_int(*stmt_, 2), freq);
ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
ASSERT_EQ(sqlite3_column_int(*stmt_, 0), timestamp + 1);
- ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 0);
+ ASSERT_EQ(sqlite3_column_int(*stmt_, 1), 1);
ASSERT_EQ(sqlite3_column_int(*stmt_, 2), freq + 1000);
ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
@@ -82,9 +87,16 @@
TEST_F(CountersTableUnittest, GroupByFreq) {
uint64_t timestamp = 1000;
uint32_t freq = 3000;
- context_.storage->PushCpuFreq(timestamp, 1 /* cpu */, freq);
- context_.storage->PushCpuFreq(timestamp + 1, 1 /* cpu */, freq + 1000);
- context_.storage->PushCpuFreq(timestamp + 3, 1 /* cpu */, freq);
+ uint32_t name_id = 1;
+ context_.storage->mutable_counters()->AddCounter(
+ timestamp, 1 /* dur */, name_id, freq, 0 /* value delta */, 1 /* cpu */,
+ RefType::kCPU_ID);
+ context_.storage->mutable_counters()->AddCounter(
+ timestamp + 1, 2 /* dur */, name_id, freq + 1000, 1000 /* value delta */,
+ 1 /* cpu */, RefType::kCPU_ID);
+ context_.storage->mutable_counters()->AddCounter(
+ timestamp + 3, 0 /* dur */, name_id, freq, -1000 /* value delta */,
+ 1 /* cpu */, RefType::kCPU_ID);
PrepareValidStatement(
"SELECT value, sum(dur) as dur_sum FROM counters where value > 0 group "
diff --git a/src/trace_processor/process_tracker.cc b/src/trace_processor/process_tracker.cc
index 295cf5a..2da8994 100644
--- a/src/trace_processor/process_tracker.cc
+++ b/src/trace_processor/process_tracker.cc
@@ -42,7 +42,8 @@
auto prev_utid = std::prev(pair_it.second)->second;
TraceStorage::Thread* thread =
context_->storage->GetMutableThread(prev_utid);
- thread->name_id = thread_name_id;
+ if (thread_name_id)
+ thread->name_id = thread_name_id;
return prev_utid;
}
diff --git a/src/trace_processor/proto_trace_parser.cc b/src/trace_processor/proto_trace_parser.cc
index 8cf1101..ac1b65c 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"
@@ -43,38 +45,53 @@
const char* s = str.data();
size_t len = str.size();
- // If str matches '[BEC]\|[0-9]+[\|\n]' set pid_length to the length of
+ // If str matches '[BEC]\|[0-9]+[\|\n]' set tid_length to the length of
// the number. Otherwise return false.
if (len < 3 || s[1] != '|')
return false;
if (s[0] != 'B' && s[0] != 'E' && s[0] != 'C')
return false;
- size_t pid_length;
+ size_t tid_length;
for (size_t i = 2;; i++) {
if (i >= len)
return false;
if (s[i] == '|' || s[i] == '\n') {
- pid_length = i - 2;
+ tid_length = i - 2;
break;
}
if (s[i] < '0' || s[i] > '9')
return false;
}
- std::string pid_str(s + 2, pid_length);
- out->pid = static_cast<uint32_t>(std::stoi(pid_str.c_str()));
+ std::string tid_str(s + 2, tid_length);
+ out->tid = static_cast<uint32_t>(std::stoi(tid_str.c_str()));
out->phase = s[0];
switch (s[0]) {
case 'B': {
- size_t name_index = 2 + pid_length + 1;
+ size_t name_index = 2 + tid_length + 1;
out->name = base::StringView(s + name_index, len - name_index);
return true;
}
- case 'E':
+ case 'E': {
return true;
- case 'C':
+ }
+ case 'C': {
+ size_t name_index = 2 + tid_length + 1;
+ size_t name_length = 0;
+ for (size_t i = name_index; i < len; i++) {
+ if (s[i] == '|' || s[i] == '\n') {
+ name_length = i - name_index;
+ break;
+ }
+ }
+ out->name = base::StringView(s + name_index, name_length);
+ size_t value_index = name_index + name_length + 1;
+ char value_str[32];
+ strcpy(value_str, s + value_index);
+ out->value = std::stod(value_str);
return true;
+ }
default:
return false;
}
@@ -84,7 +101,8 @@
using protozero::proto_utils::kFieldTypeLengthDelimited;
ProtoTraceParser::ProtoTraceParser(TraceProcessorContext* context)
- : context_(context) {}
+ : context_(context),
+ cpu_freq_name_id_(context->storage->InternString("cpufreq")) {}
ProtoTraceParser::~ProtoTraceParser() = default;
@@ -204,20 +222,20 @@
void ProtoTraceParser::ParseCpuFreq(uint64_t timestamp, TraceBlobView view) {
ProtoDecoder decoder(view.data(), view.length());
- uint32_t cpu = 0;
+ uint32_t cpu_affected = 0;
uint32_t new_freq = 0;
for (auto fld = decoder.ReadField(); fld.id != 0; fld = decoder.ReadField()) {
switch (fld.id) {
case protos::CpuFrequencyFtraceEvent::kCpuIdFieldNumber:
- cpu = fld.as_uint32();
+ cpu_affected = fld.as_uint32();
break;
case protos::CpuFrequencyFtraceEvent::kStateFieldNumber:
new_freq = fld.as_uint32();
break;
}
}
-
- context_->storage->PushCpuFreq(timestamp, cpu, new_freq);
+ context_->sched_tracker->PushCounter(timestamp, new_freq, cpu_freq_name_id_,
+ cpu_affected, RefType::kCPU_ID);
PERFETTO_DCHECK(decoder.IsEndOfBuffer());
}
@@ -271,19 +289,26 @@
if (!ParseSystraceTracePoint(buf, &point))
return;
- UniquePid upid = context_->process_tracker->UpdateProcess(point.pid);
+ UniqueTid utid =
+ context_->process_tracker->UpdateThread(timestamp, point.tid, 0);
switch (point.phase) {
case 'B': {
StringId name_id = context_->storage->InternString(point.name);
- context_->slice_tracker->Begin(timestamp, upid, 0 /*cat_id*/, name_id);
+ context_->slice_tracker->Begin(timestamp, utid, 0 /*cat_id*/, name_id);
break;
}
case 'E': {
- context_->slice_tracker->End(timestamp, upid);
+ context_->slice_tracker->End(timestamp, utid);
break;
}
+
+ case 'C': {
+ StringId name_id = context_->storage->InternString(point.name);
+ context_->sched_tracker->PushCounter(timestamp, point.value, name_id,
+ utid, RefType::kUTID);
+ }
}
PERFETTO_DCHECK(decoder.IsEndOfBuffer());
}
diff --git a/src/trace_processor/proto_trace_parser.h b/src/trace_processor/proto_trace_parser.h
index be3bc40..9da91e4 100644
--- a/src/trace_processor/proto_trace_parser.h
+++ b/src/trace_processor/proto_trace_parser.h
@@ -31,19 +31,19 @@
struct SystraceTracePoint {
char phase;
- uint32_t pid;
+ uint32_t tid;
// For phase = 'B' and phase = 'C' only.
base::StringView name;
// For phase = 'C' only.
- int64_t value;
+ double value;
};
inline bool operator==(const SystraceTracePoint& x,
const SystraceTracePoint& y) {
- return std::tie(x.phase, x.pid, x.name, x.value) ==
- std::tie(y.phase, y.pid, y.name, y.value);
+ return std::tie(x.phase, x.tid, x.name, x.value) ==
+ std::tie(y.phase, y.tid, y.name, y.value);
}
bool ParseSystraceTracePoint(base::StringView, SystraceTracePoint* out);
@@ -67,6 +67,7 @@
private:
TraceProcessorContext* context_;
+ const StringId cpu_freq_name_id_;
};
} // namespace trace_processor
diff --git a/src/trace_processor/proto_trace_parser_unittest.cc b/src/trace_processor/proto_trace_parser_unittest.cc
index 68efcdd..c0881c8 100644
--- a/src/trace_processor/proto_trace_parser_unittest.cc
+++ b/src/trace_processor/proto_trace_parser_unittest.cc
@@ -49,6 +49,13 @@
uint32_t prev_state,
base::StringView prev_comm,
uint32_t next_pid));
+
+ MOCK_METHOD5(PushCounter,
+ void(uint64_t timestamp,
+ double value,
+ StringId name_id,
+ uint64_t ref,
+ RefType ref_type));
};
class MockProcessTracker : public ProcessTracker {
@@ -66,8 +73,7 @@
public:
MockTraceStorage() : TraceStorage() {}
- MOCK_METHOD3(PushCpuFreq,
- void(uint64_t timestamp, uint32_t cpu, uint32_t new_freq));
+ MOCK_METHOD1(InternString, StringId(base::StringView));
};
class ProtoTraceParserTest : public ::testing::Test {
@@ -236,7 +242,7 @@
cpu_freq->set_cpu_id(10);
cpu_freq->set_state(2000);
- EXPECT_CALL(*storage_, PushCpuFreq(1000, 10, 2000));
+ EXPECT_CALL(*sched_, PushCounter(1000, 2000, 0, 10, RefType::kCPU_ID));
Tokenize(trace_1);
}
@@ -291,6 +297,10 @@
ASSERT_TRUE(ParseSystraceTracePoint(base::StringView("B|42|Bar"), &result));
EXPECT_EQ(result, (SystraceTracePoint{'B', 42, base::StringView("Bar"), 0}));
+
+ ASSERT_TRUE(
+ ParseSystraceTracePoint(base::StringView("C|543|foo|8"), &result));
+ EXPECT_EQ(result, (SystraceTracePoint{'C', 543, base::StringView("foo"), 8}));
}
} // namespace
diff --git a/src/trace_processor/sched_slice_table.cc b/src/trace_processor/sched_slice_table.cc
index 352d244..dfdde24 100644
--- a/src/trace_processor/sched_slice_table.cc
+++ b/src/trace_processor/sched_slice_table.cc
@@ -103,7 +103,6 @@
"dur UNSIGNED BIG INT, "
"quantized_group UNSIGNED BIG INT, "
"utid UNSIGNED INT, "
- "cycles UNSIGNED BIG INT, "
"quantum HIDDEN BIG INT, "
"ts_lower_bound HIDDEN BIG INT, "
"ts_clip HIDDEN BOOLEAN, "
@@ -275,11 +274,6 @@
sqlite3_result_int64(context, slices.utids()[row]);
break;
}
- case Column::kCycles: {
- sqlite3_result_int64(context,
- static_cast<sqlite3_int64>(slices.cycles()[row]));
- break;
- }
}
return SQLITE_OK;
}
@@ -463,8 +457,6 @@
return Compare(f_cpu, s_cpu, ob.desc);
case SchedSliceTable::Column::kUtid:
return Compare(f_sl.utids()[f_idx], s_sl.utids()[s_idx], ob.desc);
- case SchedSliceTable::Column::kCycles:
- return Compare(f_sl.cycles()[f_idx], s_sl.cycles()[s_idx], ob.desc);
case SchedSliceTable::Column::kQuantizedGroup: {
// We don't support sorting in descending order on quantized group when
// we have a non-zero quantum.
diff --git a/src/trace_processor/sched_slice_table.h b/src/trace_processor/sched_slice_table.h
index 1cfca45..4e86336 100644
--- a/src/trace_processor/sched_slice_table.h
+++ b/src/trace_processor/sched_slice_table.h
@@ -39,12 +39,11 @@
kDuration = 2,
kQuantizedGroup = 3,
kUtid = 4,
- kCycles = 5,
// Hidden columns.
- kQuantum = 6,
- kTimestampLowerBound = 7,
- kClipTimestamp = 8,
+ kQuantum = 5,
+ kTimestampLowerBound = 6,
+ kClipTimestamp = 7,
};
SchedSliceTable(sqlite3*, const TraceStorage* storage);
diff --git a/src/trace_processor/sched_slice_table_unittest.cc b/src/trace_processor/sched_slice_table_unittest.cc
index fbc84ff..31c215f 100644
--- a/src/trace_processor/sched_slice_table_unittest.cc
+++ b/src/trace_processor/sched_slice_table_unittest.cc
@@ -366,42 +366,6 @@
ElementsAre(71));
}
-TEST_F(SchedSliceTableTest, CyclesOrdering) {
- uint32_t cpu = 3;
- uint64_t timestamp = 100;
- uint32_t pid_1 = 2;
- uint32_t prev_state = 32;
- static const char kCommProc1[] = "process1";
- static const char kCommProc2[] = "process2";
- uint32_t pid_2 = 4;
- context_.sched_tracker->PushSchedSwitch(cpu, timestamp, pid_1, prev_state,
- kCommProc1, pid_2);
- context_.storage->PushCpuFreq(timestamp + 1, cpu, 1e9);
- context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 2, pid_2, prev_state,
- kCommProc2, pid_1);
- context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 4, pid_1, prev_state,
- kCommProc1, pid_2);
- context_.storage->PushCpuFreq(timestamp + 5, cpu, 2e9);
- context_.sched_tracker->PushSchedSwitch(cpu, timestamp + 7, pid_2, prev_state,
- kCommProc2, pid_1);
-
- PrepareValidStatement("SELECT cycles, ts FROM sched ORDER BY cycles desc");
-
- ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
- ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 5000 /* cycles */);
- ASSERT_EQ(sqlite3_column_int64(*stmt_, 1), timestamp + 4);
-
- ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
- ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 2000 /* cycles */);
- ASSERT_EQ(sqlite3_column_int64(*stmt_, 1), timestamp + 2);
-
- ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_ROW);
- ASSERT_EQ(sqlite3_column_int64(*stmt_, 0), 1000 /* cycles */);
- ASSERT_EQ(sqlite3_column_int64(*stmt_, 1), timestamp);
-
- ASSERT_EQ(sqlite3_step(*stmt_), SQLITE_DONE);
-}
-
} // namespace
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/sched_tracker.cc b/src/trace_processor/sched_tracker.cc
index e1e1854..4dcc097 100644
--- a/src/trace_processor/sched_tracker.cc
+++ b/src/trace_processor/sched_tracker.cc
@@ -54,9 +54,7 @@
: context_->storage->InternString(prev_comm);
UniqueTid utid = context_->process_tracker->UpdateThread(
prev->timestamp, prev->next_pid /* == prev_pid */, prev_thread_name_id);
- uint64_t cycles = CalculateCycles(cpu, prev->timestamp, timestamp);
- context_->storage->AddSliceToCpu(cpu, prev->timestamp, duration, utid,
- cycles);
+ context_->storage->AddSliceToCpu(cpu, prev->timestamp, duration, utid);
}
// If the this events previous pid does not match the previous event's next
@@ -72,42 +70,36 @@
prev->next_pid = next_pid;
};
-uint64_t SchedTracker::CalculateCycles(uint32_t cpu,
- uint64_t start_ns,
- uint64_t end_ns) {
- const auto& frequencies = context_->storage->GetFreqForCpu(cpu);
- auto lower_index = lower_index_per_cpu_[cpu];
- if (frequencies.empty())
- return 0;
+void SchedTracker::PushCounter(uint64_t timestamp,
+ double value,
+ StringId name_id,
+ uint64_t ref,
+ RefType ref_type) {
+ if (timestamp < prev_timestamp_) {
+ PERFETTO_ELOG("counter event out of order by %.4f ms, skipping",
+ (prev_timestamp_ - timestamp) / 1e6);
+ return;
+ }
+ prev_timestamp_ = timestamp;
- long double cycles = 0;
+ // The previous counter with the same ref and name_id.
+ Counter& prev = prev_counters_[CounterKey{ref, name_id}];
- // Move the lower index up to the first cpu_freq event before start_ns.
- while (lower_index + 1 < frequencies.size()) {
- if (frequencies[lower_index + 1].first >= start_ns)
- break;
- ++lower_index;
- };
+ uint64_t duration = 0;
+ double value_delta = 0;
- // Since events are processed in timestamp order, we don't have any cpu_freq
- // events with a timestamp larger than end_ns. Therefore we care about all
- // freq events from lower_index (first event before start_ns) to the last
- // cpu_freq event.
- for (size_t i = lower_index; i < frequencies.size(); ++i) {
- // Using max handles the special case for the first cpu_freq event.
- uint64_t cycle_start = std::max(frequencies[i].first, start_ns);
- // If there are no more freq_events we compute cycles until |end_ns|.
- uint64_t cycle_end = end_ns;
- if (i + 1 < frequencies.size())
- cycle_end = frequencies[i + 1].first;
+ if (prev.timestamp != 0) {
+ duration = timestamp - prev.timestamp;
+ value_delta = value - prev.value;
- uint32_t freq_khz = frequencies[i].second;
- cycles += ((cycle_end - cycle_start) / 1E6L) * freq_khz;
+ context_->storage->mutable_counters()->AddCounter(
+ prev.timestamp, duration, name_id, prev.value, value_delta,
+ static_cast<int64_t>(ref), ref_type);
}
- lower_index_per_cpu_[cpu] = frequencies.size() - 1;
- return static_cast<uint64_t>(round(cycles));
-}
+ prev.timestamp = timestamp;
+ prev.value = value;
+};
} // namespace trace_processor
} // namespace perfetto
diff --git a/src/trace_processor/sched_tracker.h b/src/trace_processor/sched_tracker.h
index 53855a3..b2c5791 100644
--- a/src/trace_processor/sched_tracker.h
+++ b/src/trace_processor/sched_tracker.h
@@ -46,6 +46,33 @@
bool valid() const { return timestamp != 0; }
};
+ // A Counter is a trace event that has a value attached to a timestamp.
+ // These include CPU frequency ftrace events and systrace trace_marker
+ // counter events.
+ struct Counter {
+ uint64_t timestamp = 0;
+ double value = 0;
+ };
+
+ // Used as the key in |prev_counters_| to find the previous counter with the
+ // same ref and name_id.
+ struct CounterKey {
+ uint64_t ref; // cpu, utid, ...
+ StringId name_id; // "cpufreq"
+
+ bool operator==(const CounterKey& other) const {
+ return (ref == other.ref && name_id == other.name_id);
+ }
+
+ struct Hasher {
+ size_t operator()(const CounterKey& c) const {
+ size_t const h1(std::hash<uint64_t>{}(c.ref));
+ size_t const h2(std::hash<size_t>{}(c.name_id));
+ return h1 ^ (h2 << 1);
+ }
+ };
+ };
+
// This method is called when a sched switch event is seen in the trace.
virtual void PushSchedSwitch(uint32_t cpu,
uint64_t timestamp,
@@ -54,16 +81,24 @@
base::StringView prev_comm,
uint32_t next_pid);
- private:
- // Based on the cpu frequencies stored in trace_storage, the number of cycles
- // between start_ns and end_ns on |cpu| is calculated.
- uint64_t CalculateCycles(uint32_t cpu, uint64_t start_ns, uint64_t end_ns);
+ // This method is called when a cpu freq event is seen in the trace.
+ // TODO(taylori): Move to a more appropriate class or rename class.
+ virtual void PushCounter(uint64_t timestamp,
+ double value,
+ StringId name_id,
+ uint64_t ref,
+ RefType ref_type);
+ private:
// Store the previous sched event to calculate the duration before storing it.
std::array<SchedSwitchEvent, base::kMaxCpus> last_sched_per_cpu_;
- std::array<size_t, base::kMaxCpus> lower_index_per_cpu_{};
+ // Store the previous counter event to calculate the duration and value delta
+ // before storing it in trace storage.
+ std::unordered_map<CounterKey, Counter, CounterKey::Hasher> prev_counters_;
+ // Timestamp of the previous event. Used to discard events arriving out
+ // of order.
uint64_t prev_timestamp_ = 0;
StringId const idle_string_id_;
diff --git a/src/trace_processor/sched_tracker_unittest.cc b/src/trace_processor/sched_tracker_unittest.cc
index b50f135..dbd94e6 100644
--- a/src/trace_processor/sched_tracker_unittest.cc
+++ b/src/trace_processor/sched_tracker_unittest.cc
@@ -97,32 +97,60 @@
ASSERT_EQ(context.storage->SlicesForCpu(cpu).durations().at(2), 31u - 11u);
ASSERT_EQ(context.storage->SlicesForCpu(cpu).utids().at(0),
context.storage->SlicesForCpu(cpu).utids().at(2));
- ASSERT_EQ(context.storage->SlicesForCpu(cpu).cycles().at(0), 0);
}
-TEST_F(SchedTrackerTest, TestCyclesCalculation) {
+TEST_F(SchedTrackerTest, CounterDuration) {
uint32_t cpu = 3;
- uint64_t timestamp = 1e9;
- context.storage->PushCpuFreq(timestamp, cpu, 1e6);
+ uint64_t timestamp = 100;
+ StringId name_id = 0;
+ context.sched_tracker->PushCounter(timestamp, 1000, name_id, cpu,
+ RefType::kCPU_ID);
+ context.sched_tracker->PushCounter(timestamp + 1, 4000, name_id, cpu,
+ RefType::kCPU_ID);
+ context.sched_tracker->PushCounter(timestamp + 3, 5000, name_id, cpu,
+ RefType::kCPU_ID);
+ context.sched_tracker->PushCounter(timestamp + 9, 1000, name_id, cpu,
+ RefType::kCPU_ID);
- uint32_t prev_state = 32;
- static const char kCommProc1[] = "process1";
- static const char kCommProc2[] = "process2";
+ ASSERT_EQ(context.storage->counters().counter_count(), 3ul);
+ ASSERT_EQ(context.storage->counters().timestamps().at(0), timestamp);
+ ASSERT_EQ(context.storage->counters().durations().at(0), 1);
+ ASSERT_EQ(context.storage->counters().values().at(0), 1000);
- context.sched_tracker->PushSchedSwitch(
- cpu, static_cast<uint64_t>(timestamp + 1e7L), /*tid=*/2, prev_state,
- kCommProc1,
- /*tid=*/4);
+ ASSERT_EQ(context.storage->counters().timestamps().at(1), timestamp + 1);
+ ASSERT_EQ(context.storage->counters().durations().at(1), 2);
+ ASSERT_EQ(context.storage->counters().values().at(1), 4000);
- context.storage->PushCpuFreq(static_cast<uint64_t>(timestamp + 1e8L), cpu,
- 2e6);
- context.storage->PushCpuFreq(static_cast<uint64_t>(timestamp + 2e8L), cpu,
- 3e6);
- context.sched_tracker->PushSchedSwitch(
- cpu, static_cast<uint64_t>(timestamp + 3e8L), /*tid=*/4, prev_state,
- kCommProc2,
- /*tid=*/2);
- ASSERT_EQ(context.storage->SlicesForCpu(cpu).cycles().at(0), 590000000);
+ ASSERT_EQ(context.storage->counters().timestamps().at(2), timestamp + 3);
+ ASSERT_EQ(context.storage->counters().durations().at(2), 6);
+ ASSERT_EQ(context.storage->counters().values().at(2), 5000);
+}
+
+TEST_F(SchedTrackerTest, MixedEventsValueDelta) {
+ uint32_t cpu = 3;
+ uint64_t timestamp = 100;
+ StringId name_id_cpu = 0;
+ StringId name_id_upid = 0;
+ UniquePid upid = 12;
+ context.sched_tracker->PushCounter(timestamp, 1000, name_id_cpu, cpu,
+ RefType::kCPU_ID);
+ context.sched_tracker->PushCounter(timestamp + 1, 0, name_id_upid, upid,
+ RefType::kUTID);
+ context.sched_tracker->PushCounter(timestamp + 3, 5000, name_id_cpu, cpu,
+ RefType::kCPU_ID);
+ context.sched_tracker->PushCounter(timestamp + 9, 1, name_id_upid, upid,
+ RefType::kUTID);
+
+ ASSERT_EQ(context.storage->counters().counter_count(), 2ul);
+ ASSERT_EQ(context.storage->counters().timestamps().at(0), timestamp);
+ ASSERT_EQ(context.storage->counters().durations().at(0), 3);
+ ASSERT_EQ(context.storage->counters().values().at(0), 1000);
+ ASSERT_EQ(context.storage->counters().value_deltas().at(0), 4000);
+
+ ASSERT_EQ(context.storage->counters().timestamps().at(1), timestamp + 1);
+ ASSERT_EQ(context.storage->counters().durations().at(1), 8);
+ ASSERT_EQ(context.storage->counters().values().at(1), 0);
+ ASSERT_EQ(context.storage->counters().value_deltas().at(1), 1);
}
} // namespace
diff --git a/src/trace_processor/slice_tracker.cc b/src/trace_processor/slice_tracker.cc
index 1d0e8d1..16dbc3c 100644
--- a/src/trace_processor/slice_tracker.cc
+++ b/src/trace_processor/slice_tracker.cc
@@ -56,7 +56,9 @@
StringId name) {
auto& stack = threads_[utid];
MaybeCloseStack(timestamp, stack);
- PERFETTO_CHECK(!stack.empty());
+ if (stack.empty()) {
+ return;
+ }
PERFETTO_CHECK(cat == 0 || stack.back().cat_id == cat);
PERFETTO_CHECK(name == 0 || stack.back().name_id == name);
diff --git a/src/trace_processor/trace_processor_shell.cc b/src/trace_processor/trace_processor_shell.cc
index c485934..fd8af99 100644
--- a/src/trace_processor/trace_processor_shell.cc
+++ b/src/trace_processor/trace_processor_shell.cc
@@ -36,6 +36,10 @@
#define PERFETTO_HAS_SIGNAL_H() 0
#endif
+#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
+#include <linenoise.h>
+#endif
+
#if PERFETTO_HAS_SIGNAL_H()
#include <signal.h>
#endif
@@ -46,11 +50,45 @@
namespace {
TraceProcessor* g_tp;
-void PrintPrompt() {
- printf("\r%80s\r> ", "");
- fflush(stdout);
+#if PERFETTO_BUILDFLAG(PERFETTO_STANDALONE_BUILD)
+
+void SetupLineEditor() {
+ linenoiseSetMultiLine(true);
+ linenoiseHistorySetMaxLen(1000);
}
+void FreeLine(char* line) {
+ linenoiseHistoryAdd(line);
+ linenoiseFree(line);
+}
+
+char* GetLine(const char* prompt) {
+ return linenoise(prompt);
+}
+
+#else
+
+void SetupLineEditor() {}
+
+void FreeLine(char* line) {
+ free(line);
+}
+
+char* GetLine(const char* prompt) {
+ printf("\r%80s\r%s", "", prompt);
+ fflush(stdout);
+ char* line = new char[1024];
+ if (!fgets(line, 1024 - 1, stdin)) {
+ FreeLine(line);
+ return nullptr;
+ }
+ if (strlen(line) > 0)
+ line[strlen(line) - 1] = 0;
+ return line;
+}
+
+#endif
+
void OnQueryResult(base::TimeNanos t_start, const protos::RawQueryResult& res) {
if (res.has_error()) {
PERFETTO_ELOG("SQLite error: %s", res.error().c_str());
@@ -175,12 +213,13 @@
signal(SIGINT, [](int) { g_tp->InterruptQuery(); });
#endif
+ SetupLineEditor();
+
for (;;) {
- PrintPrompt();
- char line[1024];
- if (!fgets(line, sizeof(line) - 1, stdin) || strcmp(line, "q\n") == 0)
- return 0;
- if (strcmp(line, "\n") == 0)
+ char* line = GetLine("> ");
+ if (!line || strcmp(line, "q\n") == 0)
+ break;
+ if (strcmp(line, "") == 0)
continue;
protos::RawQueryArgs query;
query.set_sql_query(line);
@@ -188,5 +227,9 @@
g_tp->ExecuteQuery(query, [t_start](const protos::RawQueryResult& res) {
OnQueryResult(t_start, res);
});
+
+ FreeLine(line);
}
+
+ return 0;
}
diff --git a/src/trace_processor/trace_sorter_unittest.cc b/src/trace_processor/trace_sorter_unittest.cc
index db1a8b6..8716fbb 100644
--- a/src/trace_processor/trace_sorter_unittest.cc
+++ b/src/trace_processor/trace_sorter_unittest.cc
@@ -52,10 +52,19 @@
}
};
+class MockTraceStorage : public TraceStorage {
+ public:
+ MockTraceStorage() : TraceStorage() {}
+
+ MOCK_METHOD1(InternString, StringId(base::StringView view));
+};
+
class TraceSorterTest : public ::testing::TestWithParam<OptimizationMode> {
public:
TraceSorterTest()
: test_buffer_(std::unique_ptr<uint8_t[]>(new uint8_t[8]), 0, 8) {
+ storage_ = new MockTraceStorage();
+ context_.storage.reset(storage_);
context_.sorter.reset(
new TraceSorter(&context_, GetParam(), 0 /*window_size*/));
parser_ = new MockTraceParser(&context_);
@@ -65,6 +74,7 @@
protected:
TraceProcessorContext context_;
MockTraceParser* parser_;
+ MockTraceStorage* storage_;
TraceBlobView test_buffer_;
};
diff --git a/src/trace_processor/trace_storage.cc b/src/trace_processor/trace_storage.cc
index ce5fae1..43c5944 100644
--- a/src/trace_processor/trace_storage.cc
+++ b/src/trace_processor/trace_storage.cc
@@ -29,9 +29,6 @@
// Reserve string ID 0 for the empty string.
InternString("");
- // Initialize all CPUs @ freq 0Hz.
- for (size_t cpu = 0; cpu < base::kMaxCpus; cpu++)
- cpu_freq_[cpu].emplace_back(0, 0);
}
TraceStorage::~TraceStorage() {}
@@ -39,9 +36,8 @@
void TraceStorage::AddSliceToCpu(uint32_t cpu,
uint64_t start_ns,
uint64_t duration_ns,
- UniqueTid utid,
- uint64_t cycles) {
- cpu_events_[cpu].AddSlice(start_ns, duration_ns, utid, cycles);
+ UniqueTid utid) {
+ cpu_events_[cpu].AddSlice(start_ns, duration_ns, utid);
};
StringId TraceStorage::InternString(base::StringView str) {
diff --git a/src/trace_processor/trace_storage.h b/src/trace_processor/trace_storage.h
index 79b1492..47ccbf7 100644
--- a/src/trace_processor/trace_storage.h
+++ b/src/trace_processor/trace_storage.h
@@ -43,9 +43,7 @@
// StringId is an offset into |string_pool_|.
using StringId = size_t;
-// A map containing timestamps and the cpu frequency set at that time.
-using CpuFreq =
- std::deque<std::pair<uint64_t /*timestamp*/, uint32_t /*freq*/>>;
+enum RefType { kUTID = 0, kCPU_ID = 1 };
// Stores a data inside a trace file in a columnar form. This makes it efficient
// to read or search across a single field of the trace (e.g. all the thread
@@ -84,12 +82,10 @@
public:
inline void AddSlice(uint64_t start_ns,
uint64_t duration_ns,
- UniqueTid utid,
- uint64_t cycles) {
+ UniqueTid utid) {
start_ns_.emplace_back(start_ns);
durations_.emplace_back(duration_ns);
utids_.emplace_back(utid);
- cycles_.emplace_back(cycles);
}
size_t slice_count() const { return start_ns_.size(); }
@@ -100,15 +96,12 @@
const std::deque<UniqueTid>& utids() const { return utids_; }
- const std::deque<uint64_t>& cycles() const { return cycles_; }
-
private:
// Each deque below has the same number of entries (the number of slices
// in the trace for the CPU).
std::deque<uint64_t> start_ns_;
std::deque<uint64_t> durations_;
std::deque<UniqueTid> utids_;
- std::deque<uint64_t> cycles_;
};
class NestableSlices {
@@ -154,13 +147,55 @@
std::deque<uint64_t> parent_stack_ids_;
};
+ class Counters {
+ public:
+ inline void AddCounter(uint64_t timestamp,
+ uint64_t duration,
+ StringId name_id,
+ double value,
+ double value_delta,
+ int64_t ref,
+ RefType type) {
+ timestamps_.emplace_back(timestamp);
+ durations_.emplace_back(duration);
+ name_ids_.emplace_back(name_id);
+ values_.emplace_back(value);
+ value_deltas_.emplace_back(value_delta);
+ refs_.emplace_back(ref);
+ types_.emplace_back(type);
+ }
+ size_t counter_count() const { return timestamps_.size(); }
+
+ const std::deque<uint64_t>& timestamps() const { return timestamps_; }
+
+ const std::deque<uint64_t>& durations() const { return durations_; }
+
+ const std::deque<StringId>& name_ids() const { return name_ids_; }
+
+ const std::deque<double>& values() const { return values_; }
+
+ const std::deque<double>& value_deltas() const { return value_deltas_; }
+
+ const std::deque<int64_t>& refs() const { return refs_; }
+
+ const std::deque<RefType>& types() const { return types_; }
+
+ private:
+ std::deque<uint64_t> timestamps_;
+ std::deque<uint64_t> durations_;
+ std::deque<StringId> name_ids_;
+ std::deque<double> values_;
+ std::deque<double> value_deltas_;
+ std::deque<int64_t> refs_;
+ std::deque<RefType> types_;
+ };
+
void ResetStorage();
void AddSliceToCpu(uint32_t cpu,
uint64_t start_ns,
uint64_t duration_ns,
- UniqueTid utid,
- uint64_t cycles);
+ UniqueTid utid);
UniqueTid AddEmptyThread(uint32_t tid) {
unique_threads_.emplace_back(tid);
@@ -176,7 +211,8 @@
// Return an unqiue identifier for the contents of each string.
// The string is copied internally and can be destroyed after this called.
- StringId InternString(base::StringView);
+ // Virtual for testing.
+ virtual StringId InternString(base::StringView);
Process* GetMutableProcess(UniquePid upid) {
PERFETTO_DCHECK(upid > 0 && upid < unique_processes_.size());
@@ -184,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];
}
@@ -206,31 +242,15 @@
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];
}
const NestableSlices& nestable_slices() const { return nestable_slices_; }
NestableSlices* mutable_nestable_slices() { return &nestable_slices_; }
- // Virtual for testing.
- virtual void PushCpuFreq(uint64_t timestamp,
- uint32_t cpu,
- uint32_t new_freq) {
- auto& freqs = cpu_freq_[cpu];
- if (!freqs.empty() && timestamp < freqs.back().first) {
- PERFETTO_ELOG("cpufreq out of order by %.4f ms, skipping",
- (freqs.back().first - timestamp) / 1e6);
- return;
- }
- freqs.emplace_back(timestamp, new_freq);
- }
-
- const CpuFreq& GetFreqForCpu(uint32_t cpu) const { return cpu_freq_[cpu]; }
-
- uint32_t GetMaxCpu() const {
- return static_cast<uint32_t>(cpu_freq_.size() - 1);
- }
+ const Counters& counters() const { return counters_; }
+ Counters* mutable_counters() { return &counters_; }
// |unique_processes_| always contains at least 1 element becuase the 0th ID
// is reserved to indicate an invalid process.
@@ -254,10 +274,6 @@
// One entry for each CPU in the trace.
std::array<SlicesPerCpu, base::kMaxCpus> cpu_events_;
- // One map containing frequencies for every CPU in the trace. The map contains
- // timestamps and the cpu frequency value at that time.
- std::array<CpuFreq, base::kMaxCpus> cpu_freq_;
-
// One entry for each unique string in the trace.
std::deque<std::string> string_pool_;
@@ -272,6 +288,10 @@
// Slices coming from userspace events (e.g. Chromium TRACE_EVENT macros).
NestableSlices nestable_slices_;
+
+ // Counter events from the trace. This includes CPU frequency events as well
+ // systrace trace_marker counter events.
+ Counters counters_;
};
} // namespace trace_processor
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/install-build-deps b/tools/install-build-deps
index 5368905..d9512b5 100755
--- a/tools/install-build-deps
+++ b/tools/install-build-deps
@@ -178,6 +178,13 @@
'4adec454b60a943dd58603d4be80d42b2db62cbd',
'all',
),
+
+ # Linenoise
+ ('buildtools/linenoise',
+ 'https://github.com/antirez/linenoise.git',
+ '4a961c0108720741e2683868eb10495f015ee422',
+ 'all'
+ ),
]
# Dependencies required to build Android code.
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));
+ }
}
});
diff --git a/ui/BUILD.gn b/ui/BUILD.gn
index 104b6e8..8783c9f 100644
--- a/ui/BUILD.gn
+++ b/ui/BUILD.gn
@@ -260,6 +260,7 @@
main_css,
"src/assets/sidebar.scss",
"src/assets/topbar.scss",
+ "src/assets/record.scss",
]
outputs = [
"$ui_dir/perfetto.css",
@@ -347,6 +348,9 @@
"$root_build_dir/wasm/trace_processor.js",
"$root_build_dir/wasm/trace_processor.wasm",
]
+ if (is_debug) {
+ sources += [ "$root_build_dir/wasm/trace_processor.wasm.map" ]
+ }
outputs = [
"$ui_gen_dir/{{source_file_part}}",
]
diff --git a/ui/src/assets/perfetto.scss b/ui/src/assets/perfetto.scss
index 95daabd..cac3d75 100644
--- a/ui/src/assets/perfetto.scss
+++ b/ui/src/assets/perfetto.scss
@@ -143,6 +143,8 @@
@import 'sidebar';
@import 'topbar';
+@import 'record';
+
.home-page {
text-align: center;
padding-top: 20vh;
@@ -309,3 +311,77 @@
header {
height: 25px;
}
+
+.text-column {
+ font-size: 115%;
+ // 2-3 alphabets per line is comfortable for reading.
+ // https://practicaltypography.com/line-length.html
+ max-width: calc(26ch*2.34);
+ margin: 3rem auto;
+ user-select: text;
+ word-break: break-word;
+}
+
+.debug-panel-border {
+ position: absolute;
+ top: 0px;
+ height: 100%;
+ width: 100%;
+ border: 1px solid rgba(69, 187, 73, 0.5);
+ pointer-events: none;
+}
+
+.perf-stats {
+ --perfetto-orange: hsl(45, 100%, 48%);
+ --perfetto-red: hsl(6, 70%, 53%);
+ --stroke-color: hsl(217, 39%, 94%);
+ position: fixed;
+ bottom: 0;
+ color: var(--stroke-color);
+ font-family: monospace;
+ padding: 2px 0px;
+ z-index: 100;
+ button:hover {
+ color: var(--perfetto-red);
+ }
+ &[expanded=true] {
+ width: 600px;
+ background-color: rgba(27, 28, 29, 0.95);
+ button {
+ color: var(--perfetto-orange);
+ &:hover {
+ color: var(--perfetto-red);
+ }
+ }
+ }
+ &[expanded=false] {
+ width: var(--sidebar-width);
+ background-color: transparent;
+ }
+ i {
+ margin: 0px 24px;
+ font-size: 30px;
+ }
+ .perf-stats-content {
+ margin: 10px 24px;
+ & > section {
+ padding: 5px;
+ border-bottom: 1px solid var(--stroke-color);
+ }
+ button {
+ text-decoration: underline;
+ }
+ div {
+ margin: 2px 0px;
+ }
+ table, td, th {
+ border: 1px solid var(--stroke-color);
+ text-align: center;
+ padding: 4px;
+ margin: 4px 0px;
+ }
+ table {
+ border-collapse: collapse;
+ }
+ }
+}
diff --git a/ui/src/assets/record.scss b/ui/src/assets/record.scss
new file mode 100644
index 0000000..e46456a
--- /dev/null
+++ b/ui/src/assets/record.scss
@@ -0,0 +1,50 @@
+// 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.
+
+.example-code {
+ display: block;
+ padding: 1rem;
+ background-color: black;
+ color: white;
+ margin: 1rem 0;
+ margin-top: calc(20px + 1rem);
+ border-radius: 3px;
+ position: relative;
+ border-top-right-radius: 4px;
+ overflow: initial;
+ user-select: text;
+
+ ::before {
+ height: 20px;
+ content: "";
+ display: block;
+ width: 100%;
+ background-color: rgb(87%, 87%, 87%);
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: -18px;
+ border-top-left-radius: 4px;
+ border-top-right-radius: 4px;
+ }
+
+ button {
+ margin-left: auto;
+ display: block;
+ font-style: italic;
+ font-size: 75%;
+ }
+}
+
+
diff --git a/ui/src/assets/topbar.scss b/ui/src/assets/topbar.scss
index 2ecd4ae..81a4a1b 100644
--- a/ui/src/assets/topbar.scss
+++ b/ui/src/assets/topbar.scss
@@ -180,4 +180,4 @@
right: -90%;
}
}
-}
\ No newline at end of file
+}
diff --git a/ui/src/common/actions.ts b/ui/src/common/actions.ts
index b06a44a..9da59d9 100644
--- a/ui/src/common/actions.ts
+++ b/ui/src/common/actions.ts
@@ -34,12 +34,14 @@
}
// TODO(hjd): Remove CPU and add a generic way to handle track specific state.
-export function addTrack(engineId: string, trackKind: string, cpu: number) {
+export function addTrack(
+ engineId: string, trackKind: string, name: string, config: {}) {
return {
type: 'ADD_TRACK',
engineId,
trackKind,
- cpu,
+ name,
+ config,
};
}
@@ -52,25 +54,6 @@
return {type: 'CLEAR_TRACK_DATA_REQ', trackId};
}
-// TODO: There should be merged with addTrack above.
-export function addChromeSliceTrack(
- engineId: string,
- trackKind: string,
- upid: number,
- utid: number,
- threadName: string,
- maxDepth: number) {
- return {
- type: 'ADD_CHROME_TRACK',
- engineId,
- trackKind,
- upid,
- utid,
- threadName,
- maxDepth,
- };
-}
-
export function executeQuery(engineId: string, queryId: string, query: string) {
return {
type: 'EXECUTE_QUERY',
diff --git a/ui/src/common/state.ts b/ui/src/common/state.ts
index c1fe8b4..c3c9e21 100644
--- a/ui/src/common/state.ts
+++ b/ui/src/common/state.ts
@@ -22,16 +22,10 @@
export interface TrackState {
id: string;
engineId: string;
- maxDepth: number;
kind: string;
name: string;
- // TODO: These need to be nested into track kind spesific state.
- // cpu slice state:
- cpu: number;
- // chrome slice state:
- upid?: number;
- utid?: number;
dataReq?: TrackDataRequest;
+ config: {};
}
export interface TrackDataRequest {
diff --git a/ui/src/controller/engine.ts b/ui/src/controller/engine.ts
index 81c3872..47f06e4 100644
--- a/ui/src/controller/engine.ts
+++ b/ui/src/controller/engine.ts
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import {IRawQueryArgs, RawQueryResult, TraceProcessor} from '../common/protos';
+import {RawQueryResult, TraceProcessor} from '../common/protos';
import {TimeSpan} from '../common/time';
/**
@@ -24,7 +24,7 @@
* the given service.
*
* Engine also defines helpers for the most common service methods
- * (e.g. rawQuery).
+ * (e.g. query).
*/
export abstract class Engine {
abstract readonly id: string;
@@ -46,14 +46,15 @@
abstract get rpc(): TraceProcessor;
/**
- * Send a raw SQL query to the engine.
+ * Shorthand for sending a SQL query to the engine.
+ * Exactly the same as engine.rpc.rawQuery({rawQuery});
*/
- rawQuery(args: IRawQueryArgs): Promise<RawQueryResult> {
- return this.rpc.rawQuery(args);
+ query(sqlQuery: string): Promise<RawQueryResult> {
+ return this.rpc.rawQuery({sqlQuery});
}
- async rawQueryOneRow(sqlQuery: string): Promise<number[]> {
- const result = await this.rawQuery({sqlQuery});
+ async queryOneRow(query: string): Promise<number[]> {
+ const result = await this.query(query);
const res: number[] = [];
result.columns.map(c => res.push(+c.longValues![0]));
return res;
@@ -62,18 +63,16 @@
// TODO(hjd): Maybe we should cache result? But then Engine must be
// streaming aware.
async getNumberOfCpus(): Promise<number> {
- const result = await this.rawQuery({
- sqlQuery: 'select count(distinct(cpu)) as cpuCount from sched;',
- });
+ const result =
+ await this.query('select count(distinct(cpu)) as cpuCount from sched;');
return +result.columns[0].longValues![0];
}
// TODO: This should live in code that's more specific to chrome, instead of
// in engine.
async getNumberOfProcesses(): Promise<number> {
- const result = await this.rawQuery({
- sqlQuery: 'select count(distinct(upid)) from thread;',
- });
+ const result =
+ await this.query('select count(distinct(upid)) from thread;');
return +result.columns[0].longValues![0];
}
@@ -82,13 +81,8 @@
'union all select max(ts) as ts from slices)';
const minQuery = 'select min(ts) from (select min(ts) as ts from sched ' +
'union all select min(ts) as ts from slices)';
- const start = (await this.rawQueryOneRow(minQuery))[0];
- const end = (await this.rawQueryOneRow(maxQuery))[0];
+ const start = (await this.queryOneRow(minQuery))[0];
+ const end = (await this.queryOneRow(maxQuery))[0];
return new TimeSpan(start / 1e9, end / 1e9);
}
}
-
-export interface EnginePortAndId {
- id: string;
- port: MessagePort;
-}
diff --git a/ui/src/controller/globals.ts b/ui/src/controller/globals.ts
index 950b678..8a336fa 100644
--- a/ui/src/controller/globals.ts
+++ b/ui/src/controller/globals.ts
@@ -17,9 +17,13 @@
import {Action} from '../common/actions';
import {createEmptyState, State} from '../common/state';
import {ControllerAny} from './controller';
-import {Engine, EnginePortAndId} from './engine';
+import {Engine} from './engine';
import {rootReducer} from './reducer';
import {WasmEngineProxy} from './wasm_engine_proxy';
+import {
+ createWasmEngine,
+ destroyWasmEngine,
+} from './wasm_engine_proxy';
/**
* Global accessors for state/dispatch in the controller.
@@ -79,14 +83,14 @@
console.timeEnd(summary);
}
- async createEngine(): Promise<Engine> {
- const portAndId = await assertExists(this._frontend)
- .send<EnginePortAndId>('createEngine', []);
- return WasmEngineProxy.create(portAndId);
+ createEngine(): Engine {
+ const id = new Date().toUTCString();
+ const portAndId = {id, worker: createWasmEngine(id)};
+ return new WasmEngineProxy(portAndId);
}
- async destroyEngine(id: string): Promise<void> {
- await assertExists(this._frontend).send<void>('destroyEngine', [id]);
+ destroyEngine(id: string): void {
+ destroyWasmEngine(id);
}
// TODO: this needs to be cleaned up.
diff --git a/ui/src/controller/index.ts b/ui/src/controller/index.ts
index 43d9535..28e095c 100644
--- a/ui/src/controller/index.ts
+++ b/ui/src/controller/index.ts
@@ -18,18 +18,21 @@
import {AppController} from './app_controller';
import {globals} from './globals';
+import {warmupWasmEngine} from './wasm_engine_proxy';
function main(port: MessagePort) {
+ warmupWasmEngine();
let receivedFrontendPort = false;
port.onmessage = ({data}) => {
- if (!receivedFrontendPort) {
- const frontendPort = data as MessagePort;
- const frontend = new Remote(frontendPort);
- globals.initialize(new AppController(), frontend);
- receivedFrontendPort = true;
- } else {
+ if (receivedFrontendPort) {
globals.dispatch(data);
+ return;
}
+
+ const frontendPort = data as MessagePort;
+ const frontend = new Remote(frontendPort);
+ globals.initialize(new AppController(), frontend);
+ receivedFrontendPort = true;
};
}
diff --git a/ui/src/controller/query_controller.ts b/ui/src/controller/query_controller.ts
index c268d3a..a700f71 100644
--- a/ui/src/controller/query_controller.ts
+++ b/ui/src/controller/query_controller.ts
@@ -54,7 +54,7 @@
private async runQuery(sqlQuery: string) {
const startMs = performance.now();
- const rawResult = await this.args.engine.rawQuery({sqlQuery});
+ const rawResult = await this.args.engine.query(sqlQuery);
const durationMs = performance.now() - startMs;
const columns = rawQueryResultColumns(rawResult);
const rows =
diff --git a/ui/src/controller/reducer.ts b/ui/src/controller/reducer.ts
index 5988b6f..a969bc0 100644
--- a/ui/src/controller/reducer.ts
+++ b/ui/src/controller/reducer.ts
@@ -56,18 +56,19 @@
id,
engineId: action.engineId,
kind: action.trackKind,
- name: `Cpu Track ${id}`,
- maxDepth: 1,
- cpu: action.cpu,
+ name: action.name,
+ config: action.config,
};
nextState.scrollingTracks.push(id);
return nextState;
}
case 'REQ_TRACK_DATA': {
+ const id = action.trackId;
const nextState = {...state};
- nextState.tracks = {...state.tracks};
- nextState.tracks[action.trackId].dataReq = {
+ const nextTracks = nextState.tracks = {...state.tracks};
+ const nextTrack = nextTracks[id] = {...nextTracks[id]};
+ nextTrack.dataReq = {
start: action.start,
end: action.end,
resolution: action.resolution
@@ -76,29 +77,11 @@
}
case 'CLEAR_TRACK_DATA_REQ': {
+ const id = action.trackId;
const nextState = {...state};
- nextState.tracks = {...state.tracks};
- nextState.tracks[action.trackId].dataReq = undefined;
- return nextState;
- }
-
- // TODO: 'ADD_CHROME_TRACK' string should be a shared const.
- case 'ADD_CHROME_TRACK': {
- const nextState = {...state};
- nextState.tracks = {...state.tracks};
- const id = `${nextState.nextId++}`;
- nextState.tracks[id] = {
- id,
- engineId: action.engineId,
- kind: action.trackKind,
- name: `${action.threadName}`,
- // TODO(dproy): This should be part of published information.
- maxDepth: action.maxDepth,
- cpu: 0, // TODO: Remove this after we have kind specific state.
- upid: action.upid,
- utid: action.utid,
- };
- nextState.scrollingTracks.push(id);
+ const nextTracks = nextState.tracks = {...state.tracks};
+ const nextTrack = nextTracks[id] = {...nextTracks[id]};
+ nextTrack.dataReq = undefined;
return nextState;
}
diff --git a/ui/src/controller/reducer_unittest.ts b/ui/src/controller/reducer_unittest.ts
index b297ee3..4abf173 100644
--- a/ui/src/controller/reducer_unittest.ts
+++ b/ui/src/controller/reducer_unittest.ts
@@ -21,10 +21,9 @@
const track: TrackState = {
id,
engineId: '1',
- maxDepth: 0,
kind: 'SOME_TRACK_KIND',
name: 'A track',
- cpu: 0,
+ config: {},
};
state.tracks[id] = track;
return track;
@@ -60,13 +59,13 @@
type: 'ADD_TRACK',
engineId: '1',
trackKind: 'cpu',
- cpu: '1',
+ config: {},
});
const before = rootReducer(step1, {
type: 'ADD_TRACK',
engineId: '2',
trackKind: 'cpu',
- cpu: '2',
+ config: {},
});
const firstTrackId = before.scrollingTracks[0];
diff --git a/ui/src/controller/trace_controller.ts b/ui/src/controller/trace_controller.ts
index a7119c3..06f570f 100644
--- a/ui/src/controller/trace_controller.ts
+++ b/ui/src/controller/trace_controller.ts
@@ -17,7 +17,6 @@
import {assertExists, assertTrue} from '../base/logging';
import {
Action,
- addChromeSliceTrack,
addTrack,
navigate,
setEngineReady,
@@ -113,7 +112,7 @@
private async loadTrace() {
globals.dispatch(updateStatus('Creating trace processor'));
const engineCfg = assertExists(globals.state.engines[this.engineId]);
- this.engine = await globals.createEngine();
+ this.engine = globals.createEngine();
const statusHeader = 'Opening trace';
if (engineCfg.source instanceof File) {
@@ -185,13 +184,14 @@
const numCpus = await engine.getNumberOfCpus();
for (let cpu = 0; cpu < numCpus; cpu++) {
addToTrackActions.push(
- addTrack(this.engineId, CPU_SLICE_TRACK_KIND, cpu));
+ addTrack(this.engineId, CPU_SLICE_TRACK_KIND, `Cpu ${cpu}`, {
+ cpu,
+ }));
}
- const threadQuery = await engine.rawQuery({
- sqlQuery: 'select upid, utid, tid, thread.name, max(slices.depth) ' +
- 'from thread inner join slices using(utid) group by utid'
- });
+ const threadQuery = await engine.query(
+ 'select upid, utid, tid, thread.name, max(slices.depth) ' +
+ 'from thread inner join slices using(utid) group by utid');
for (let i = 0; i < threadQuery.numRecords; i++) {
const upid = threadQuery.columns[0].longValues![i];
const utid = threadQuery.columns[1].longValues![i];
@@ -199,13 +199,12 @@
let threadName = threadQuery.columns[3].stringValues![i];
threadName += `[${threadId}]`;
const maxDepth = threadQuery.columns[4].longValues![i];
- addToTrackActions.push(addChromeSliceTrack(
- this.engineId,
- SLICE_TRACK_KIND,
- upid as number,
- utid as number,
- threadName,
- maxDepth as number));
+ addToTrackActions.push(
+ addTrack(this.engineId, SLICE_TRACK_KIND, threadName, {
+ upid: upid as number,
+ utid: utid as number,
+ maxDepth: maxDepth as number,
+ }));
}
globals.dispatchMultiple(addToTrackActions);
}
@@ -214,7 +213,7 @@
globals.dispatch(updateStatus('Reading thread list'));
const sqlQuery = 'select utid, tid, pid, thread.name, process.name ' +
'from thread inner join process using(upid)';
- const threadRows = await assertExists(this.engine).rawQuery({sqlQuery});
+ const threadRows = await assertExists(this.engine).query(sqlQuery);
const threads: ThreadDesc[] = [];
for (let i = 0; i < threadRows.numRecords; i++) {
const utid = threadRows.columns[0].longValues![i] as number;
@@ -241,11 +240,10 @@
const endNs = Math.ceil(endSec * 1e9);
// Sched overview.
- const schedRows = await engine.rawQuery({
- sqlQuery: `select sum(dur)/${stepSec}/1e9, cpu from sched ` +
- `where ts >= ${startNs} and ts < ${endNs} and utid != 0 ` +
- 'group by cpu order by cpu'
- });
+ const schedRows = await engine.query(
+ `select sum(dur)/${stepSec}/1e9, cpu from sched ` +
+ `where ts >= ${startNs} and ts < ${endNs} and utid != 0 ` +
+ 'group by cpu order by cpu');
const schedData: {[key: string]: QuantizedLoad} = {};
for (let i = 0; i < schedRows.numRecords; i++) {
const load = schedRows.columns[0].doubleValues![i];
@@ -255,14 +253,12 @@
globals.publish('OverviewData', schedData);
// Slices overview.
- const slicesRows = await engine.rawQuery({
- sqlQuery:
- `select sum(dur)/${stepSec}/1e9, process.name, process.pid, upid ` +
- 'from slices inner join thread using(utid) ' +
- 'inner join process using(upid) where depth = 0 ' +
- `and ts >= ${startNs} and ts < ${endNs} ` +
- 'group by upid'
- });
+ const slicesRows = await engine.query(
+ `select sum(dur)/${stepSec}/1e9, process.name, process.pid, upid ` +
+ 'from slices inner join thread using(utid) ' +
+ 'inner join process using(upid) where depth = 0 ' +
+ `and ts >= ${startNs} and ts < ${endNs} ` +
+ 'group by upid');
const slicesData: {[key: string]: QuantizedLoad} = {};
for (let i = 0; i < slicesRows.numRecords; i++) {
const load = slicesRows.columns[0].doubleValues![i];
diff --git a/ui/src/controller/track_controller.ts b/ui/src/controller/track_controller.ts
index b18c8f9..cbf444c 100644
--- a/ui/src/controller/track_controller.ts
+++ b/ui/src/controller/track_controller.ts
@@ -15,6 +15,8 @@
import {assertExists} from '../base/logging';
import {clearTrackDataRequest} from '../common/actions';
import {Registry} from '../common/registry';
+import {TrackState} from '../common/state';
+
import {Controller} from './controller';
import {ControllerFactory} from './controller';
import {Engine} from './engine';
@@ -22,7 +24,8 @@
// TrackController is a base class overridden by track implementations (e.g.,
// sched slices, nestable slices, counters).
-export abstract class TrackController extends Controller<'main'> {
+export abstract class TrackController<Config = {}, Data = {}> extends
+ Controller<'main'> {
readonly trackId: string;
readonly engine: Engine;
@@ -37,10 +40,18 @@
// to publish new track data in response to this call.
abstract onBoundsChange(start: number, end: number, resolution: number): void;
- get trackState() {
+ get trackState(): TrackState {
return assertExists(globals.state.tracks[this.trackId]);
}
+ get config(): Config {
+ return this.trackState.config as Config;
+ }
+
+ publish(data: Data): void {
+ globals.publish('TrackData', {id: this.trackId, data});
+ }
+
run() {
const dataReq = this.trackState.dataReq;
if (dataReq === undefined) return;
diff --git a/ui/src/controller/wasm_engine_proxy.ts b/ui/src/controller/wasm_engine_proxy.ts
index aff7503..80375b9 100644
--- a/ui/src/controller/wasm_engine_proxy.ts
+++ b/ui/src/controller/wasm_engine_proxy.ts
@@ -18,27 +18,18 @@
import {TraceProcessor} from '../common/protos';
import {WasmBridgeRequest, WasmBridgeResponse} from '../engine/wasm_bridge';
-import {Engine, EnginePortAndId} from './engine';
+import {Engine} from './engine';
-interface WorkerAndPort {
- worker: Worker;
- port: MessagePort;
-}
+const activeWorkers = new Map<string, Worker>();
+let warmWorker: null|Worker = null;
-const activeWorkers = new Map<string, WorkerAndPort>();
-let warmWorker: null|WorkerAndPort = null;
-
-function createWorker(): WorkerAndPort {
- const channel = new MessageChannel();
- const worker = new Worker('engine_bundle.js');
- // tslint:disable-next-line deprecation
- worker.postMessage(channel.port1, [channel.port1]);
- return {worker, port: channel.port2};
+function createWorker(): Worker {
+ return new Worker('engine_bundle.js');
}
// Take the warm engine and start creating a new WASM engine in the background
// for the next call.
-export function createWasmEngine(id: string): MessagePort {
+export function createWasmEngine(id: string): Worker {
if (warmWorker === null) {
throw new Error('warmupWasmEngine() not called');
}
@@ -48,14 +39,14 @@
const activeWorker = warmWorker;
warmWorker = createWorker();
activeWorkers.set(id, activeWorker);
- return activeWorker.port;
+ return activeWorker;
}
export function destroyWasmEngine(id: string) {
if (!activeWorkers.has(id)) {
throw new Error(`Cannot find worker ID ${id}`);
}
- activeWorkers.get(id)!.worker.terminate();
+ activeWorkers.get(id)!.terminate();
activeWorkers.delete(id);
}
@@ -79,23 +70,19 @@
* worker thread.
*/
export class WasmEngineProxy extends Engine {
- private readonly port: MessagePort;
+ private readonly worker: Worker;
private readonly traceProcessor_: TraceProcessor;
private pendingCallbacks: Map<number, protobufjs.RPCImplCallback>;
private nextRequestId: number;
readonly id: string;
- static create(args: EnginePortAndId): Engine {
- return new WasmEngineProxy(args);
- }
-
- constructor(args: EnginePortAndId) {
+ constructor(args: {id: string, worker: Worker}) {
super();
this.nextRequestId = 0;
this.pendingCallbacks = new Map();
- this.port = args.port;
this.id = args.id;
- this.port.onmessage = this.onMessage.bind(this);
+ this.worker = args.worker;
+ this.worker.onmessage = this.onMessage.bind(this);
this.traceProcessor_ =
TraceProcessor.create(this.rpcImpl.bind(this, 'trace_processor'));
}
@@ -110,7 +97,7 @@
{id, serviceName: 'trace_processor', methodName: 'parse', data};
const promise = defer<void>();
this.pendingCallbacks.set(id, () => promise.resolve());
- this.port.postMessage(request);
+ this.worker.postMessage(request);
return promise;
}
@@ -121,7 +108,7 @@
{id, serviceName: 'trace_processor', methodName: 'notifyEof', data};
const promise = defer<void>();
this.pendingCallbacks.set(id, () => promise.resolve());
- this.port.postMessage(request);
+ this.worker.postMessage(request);
return promise;
}
@@ -147,6 +134,6 @@
methodName,
data: requestData,
};
- this.port.postMessage(request);
+ this.worker.postMessage(request);
}
}
diff --git a/ui/src/engine/index.ts b/ui/src/engine/index.ts
index fbf2a3d..7891a38 100644
--- a/ui/src/engine/index.ts
+++ b/ui/src/engine/index.ts
@@ -17,23 +17,13 @@
import {WasmBridge, WasmBridgeRequest} from './wasm_bridge';
// tslint:disable no-any
-
-// We expect to get exactly one message from the creator of the worker:
-// a MessagePort we should listen to for future messages.
-// This indirection is due to workers not being able create workers in Chrome
-// which is tracked at: crbug.com/31666
-// TODO(hjd): Remove this once the fix has landed.
-// Once we have the MessagePort we proxy all messages to WasmBridge#callWasm.
+// Proxy all messages to WasmBridge#callWasm.
const anySelf = (self as any);
+const boundPostMessage = anySelf.postMessage.bind(anySelf);
+const bridge = new WasmBridge(init_trace_processor, boundPostMessage);
+bridge.initialize();
+
anySelf.onmessage = (msg: MessageEvent) => {
- const port: MessagePort = msg.data;
-
- const bridge =
- new WasmBridge(init_trace_processor, port.postMessage.bind(port));
- bridge.initialize();
-
- port.onmessage = (msg: MessageEvent) => {
- const request: WasmBridgeRequest = msg.data;
- bridge.callWasm(request);
- };
+ const request: WasmBridgeRequest = msg.data;
+ bridge.callWasm(request);
};
diff --git a/ui/src/frontend/frontend_local_state.ts b/ui/src/frontend/frontend_local_state.ts
index c295c43..b9aa740 100644
--- a/ui/src/frontend/frontend_local_state.ts
+++ b/ui/src/frontend/frontend_local_state.ts
@@ -27,6 +27,7 @@
timeScale = new TimeScale(this.visibleWindowTime, [0, 0]);
private _visibleTimeLastUpdate = 0;
private pendingGlobalTimeUpdate?: TimeSpan;
+ perfDebug = false;
// TODO: there is some redundancy in the fact that both |visibleWindowTime|
// and a |timeScale| have a notion of time range. That should live in one
@@ -52,4 +53,9 @@
get visibleTimeLastUpdate() {
return this._visibleTimeLastUpdate;
}
+
+ togglePerfDebug() {
+ this.perfDebug = !this.perfDebug;
+ globals.rafScheduler.scheduleFullRedraw();
+ }
}
diff --git a/ui/src/frontend/globals.ts b/ui/src/frontend/globals.ts
index 054df61..e370127 100644
--- a/ui/src/frontend/globals.ts
+++ b/ui/src/frontend/globals.ts
@@ -45,20 +45,24 @@
class Globals {
private _dispatch?: Dispatch = undefined;
private _state?: State = undefined;
- private _trackDataStore?: TrackDataStore = undefined;
- private _queryResults?: QueryResultsStore = undefined;
private _frontendLocalState?: FrontendLocalState = undefined;
private _rafScheduler?: RafScheduler = undefined;
+
+ // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
+ private _trackDataStore?: TrackDataStore = undefined;
+ private _queryResults?: QueryResultsStore = undefined;
private _overviewStore?: OverviewStore = undefined;
private _threadMap?: ThreadMap = undefined;
initialize(dispatch?: Dispatch) {
this._dispatch = dispatch;
this._state = createEmptyState();
- this._trackDataStore = new Map<string, {}>();
- this._queryResults = new Map<string, {}>();
this._frontendLocalState = new FrontendLocalState();
this._rafScheduler = new RafScheduler();
+
+ // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
+ this._trackDataStore = new Map<string, {}>();
+ this._queryResults = new Map<string, {}>();
this._overviewStore = new Map<string, QuantizedLoad[]>();
this._threadMap = new Map<number, ThreadDesc>();
}
@@ -75,6 +79,15 @@
return assertExists(this._dispatch);
}
+ get frontendLocalState() {
+ return assertExists(this._frontendLocalState);
+ }
+
+ get rafScheduler() {
+ return assertExists(this._rafScheduler);
+ }
+
+ // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
get overviewStore(): OverviewStore {
return assertExists(this._overviewStore);
}
@@ -87,14 +100,6 @@
return assertExists(this._queryResults);
}
- get frontendLocalState() {
- return assertExists(this._frontendLocalState);
- }
-
- get rafScheduler() {
- return assertExists(this._rafScheduler);
- }
-
get threads() {
return assertExists(this._threadMap);
}
@@ -102,11 +107,14 @@
resetForTesting() {
this._dispatch = undefined;
this._state = undefined;
- this._trackDataStore = undefined;
- this._queryResults = undefined;
this._frontendLocalState = undefined;
this._rafScheduler = undefined;
+
+ // TODO(hjd): Unify trackDataStore, queryResults, overviewStore, threads.
+ this._trackDataStore = undefined;
+ this._queryResults = undefined;
this._overviewStore = undefined;
+ this._threadMap = undefined;
}
}
diff --git a/ui/src/frontend/index.ts b/ui/src/frontend/index.ts
index d331899..0b92765 100644
--- a/ui/src/frontend/index.ts
+++ b/ui/src/frontend/index.ts
@@ -20,15 +20,9 @@
import {loadPermalink} from '../common/actions';
import {State} from '../common/state';
import {TimeSpan} from '../common/time';
-import {EnginePortAndId} from '../controller/engine';
-import {
- createWasmEngine,
- destroyWasmEngine,
- warmupWasmEngine,
-} from '../controller/wasm_engine_proxy';
-
import {globals, QuantizedLoad, ThreadDesc} from './globals';
import {HomePage} from './home_page';
+import {RecordPage} from './record_page';
import {Router} from './router';
import {ViewerPage} from './viewer_page';
@@ -86,22 +80,6 @@
this.redraw();
}
- /**
- * Creates a new trace processor wasm engine (backed by a worker running
- * engine_bundle.js) and returns a MessagePort for talking to it.
- * This indirection is due to workers not being able create workers in
- * Chrome which is tracked at: crbug.com/31666
- * TODO(hjd): Remove this once the fix has landed.
- */
- createEngine(): EnginePortAndId {
- const id = new Date().toUTCString();
- return {id, port: createWasmEngine(id)};
- }
-
- destroyEngine(id: string) {
- destroyWasmEngine(id);
- }
-
private redraw(): void {
if (globals.state.route &&
globals.state.route !== this.router.getRouteFromHash()) {
@@ -125,6 +103,7 @@
{
'/': HomePage,
'/viewer': ViewerPage,
+ '/record': RecordPage,
},
dispatch);
forwardRemoteCalls(channel.port2, new FrontendApi(router));
@@ -133,7 +112,6 @@
globals.rafScheduler.domRedraw = () =>
m.render(document.body, m(router.resolve(globals.state.route)));
- warmupWasmEngine();
// Put these variables in the global scope for better debugging.
(window as {} as {m: {}}).m = m;
diff --git a/ui/src/frontend/pages.ts b/ui/src/frontend/pages.ts
index d258656..a964952 100644
--- a/ui/src/frontend/pages.ts
+++ b/ui/src/frontend/pages.ts
@@ -27,10 +27,37 @@
hash ? ['Permalink: ', m(`a[href=${url}]`, url)] : 'Uploading...');
}
-const Alerts: m.Component = {
+class Alerts implements m.ClassComponent {
view() {
return m('.alerts', renderPermalink());
- },
+ }
+}
+
+const TogglePerfDebugButton = {
+ view() {
+ return m(
+ '.perf-monitor-button',
+ m('button',
+ {
+ onclick: () => globals.frontendLocalState.togglePerfDebug(),
+ },
+ m('i.material-icons',
+ {
+ title: 'Toggle Perf Debug Mode',
+ },
+ 'assessment')));
+ }
+};
+
+const PerfStats: m.Component = {
+ view() {
+ const perfDebug = globals.frontendLocalState.perfDebug;
+ const children = [m(TogglePerfDebugButton)];
+ if (perfDebug) {
+ children.unshift(m('.perf-stats-content'));
+ }
+ return m(`.perf-stats[expanded=${perfDebug}]`, children);
+ }
};
/**
@@ -44,6 +71,7 @@
m(Topbar),
m(component),
m(Alerts),
+ m(PerfStats),
];
},
};
diff --git a/ui/src/frontend/panel_container.ts b/ui/src/frontend/panel_container.ts
index a3866bb..4fb18f5 100644
--- a/ui/src/frontend/panel_container.ts
+++ b/ui/src/frontend/panel_container.ts
@@ -17,7 +17,14 @@
import {assertExists, assertTrue} from '../base/logging';
import {globals} from './globals';
-import {isPanelVNode} from './panel';
+import {isPanelVNode, Panel, PanelSize} from './panel';
+import {
+ debugNow,
+ perfDebug,
+ perfDisplay,
+ RunningStatistics,
+ runningStatStr
+} from './perf';
/**
* If the panel container scrolls, the backing canvas height is
@@ -41,6 +48,13 @@
private totalPanelHeight = 0;
private canvasHeight = 0;
+ private panelPerfStats = new WeakMap<Panel, RunningStatistics>();
+ private perfStats = {
+ totalPanels: 0,
+ panelsOnCanvas: 0,
+ renderStats: new RunningStatistics(10),
+ };
+
// attrs received in the most recent mithril redraw.
private attrs?: Attrs;
@@ -56,6 +70,7 @@
vnode.attrs.doesScroll ? SCROLLING_CANVAS_OVERDRAW_FACTOR : 1;
this.canvasRedrawer = () => this.redrawCanvas();
globals.rafScheduler.addRedrawCallback(this.canvasRedrawer);
+ perfDisplay.addContainer(this);
}
oncreate(vnodeDom: m.CVnodeDOM<Attrs>) {
@@ -113,16 +128,21 @@
if (attrs.doesScroll) {
dom.parentElement!.removeEventListener('scroll', this.parentOnScroll);
}
+ perfDisplay.removeContainer(this);
}
view({attrs}: m.CVnode<Attrs>) {
// We receive a new vnode object with new attrs on every mithril redraw. We
// store the latest attrs so redrawCanvas can use it.
this.attrs = attrs;
+ const renderPanel = (panel: m.Vnode) => perfDebug() ?
+ m('.panel', panel, m('.debug-panel-border')) :
+ m('.panel', panel);
+
return m(
'.scroll-limiter',
m('canvas.main-canvas'),
- attrs.panels.map(panel => m('.panel', panel)));
+ attrs.panels.map(renderPanel));
}
onupdate(vnodeDom: m.CVnodeDOM<Attrs>) {
@@ -186,6 +206,7 @@
}
private redrawCanvas() {
+ const redrawStart = debugNow();
if (!this.ctx) return;
this.ctx.clearRect(0, 0, this.parentWidth, this.canvasHeight);
const canvasYStart = this.scrollTop - this.getCanvasOverdrawHeightPerSide();
@@ -193,6 +214,7 @@
let panelYStart = 0;
const panels = assertExists(this.attrs).panels;
assertTrue(panels.length === this.panelHeights.length);
+ let totalOnCanvas = 0;
for (let i = 0; i < panels.length; i++) {
const panel = panels[i];
const panelHeight = this.panelHeights[i];
@@ -203,6 +225,8 @@
continue;
}
+ totalOnCanvas++;
+
if (!isPanelVNode(panel)) {
throw Error('Vnode passed to panel container is not a panel');
}
@@ -213,10 +237,53 @@
const size = {width: this.parentWidth, height: panelHeight};
clipRect.rect(0, 0, size.width, size.height);
this.ctx.clip(clipRect);
+ const beforeRender = debugNow();
panel.state.renderCanvas(this.ctx, size, panel);
+ this.updatePanelStats(
+ i, panel.state, debugNow() - beforeRender, this.ctx, size);
this.ctx.restore();
panelYStart += panelHeight;
}
+ const redrawDur = debugNow() - redrawStart;
+ this.updatePerfStats(redrawDur, panels.length, totalOnCanvas);
+ }
+
+ private updatePanelStats(
+ panelIndex: number, panel: Panel, renderTime: number,
+ ctx: CanvasRenderingContext2D, size: PanelSize) {
+ if (!perfDebug()) return;
+ let renderStats = this.panelPerfStats.get(panel);
+ if (renderStats === undefined) {
+ renderStats = new RunningStatistics();
+ this.panelPerfStats.set(panel, renderStats);
+ }
+ renderStats.addValue(renderTime);
+
+ const statW = 300;
+ ctx.fillStyle = 'hsl(97, 100%, 96%)';
+ ctx.fillRect(size.width - statW, size.height - 20, statW, 20);
+ ctx.fillStyle = 'hsla(122, 77%, 22%)';
+ const statStr = `Panel ${panelIndex + 1} | ` + runningStatStr(renderStats);
+ ctx.fillText(statStr, size.width - statW, size.height - 10);
+ }
+
+ private updatePerfStats(
+ renderTime: number, totalPanels: number, panelsOnCanvas: number) {
+ if (!perfDebug()) return;
+ this.perfStats.renderStats.addValue(renderTime);
+ this.perfStats.totalPanels = totalPanels;
+ this.perfStats.panelsOnCanvas = panelsOnCanvas;
+ }
+
+ renderPerfStats(index: number) {
+ assertTrue(perfDebug());
+ return [m(
+ 'section',
+ m('div', `Panel Container ${index + 1}`),
+ m('div',
+ `${this.perfStats.totalPanels} panels, ` +
+ `${this.perfStats.panelsOnCanvas} on canvas.`),
+ m('div', runningStatStr(this.perfStats.renderStats)), )];
}
private getCanvasOverdrawHeightPerSide() {
diff --git a/ui/src/frontend/perf.ts b/ui/src/frontend/perf.ts
new file mode 100644
index 0000000..6e4093b
--- /dev/null
+++ b/ui/src/frontend/perf.ts
@@ -0,0 +1,123 @@
+
+// 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 * as m from 'mithril';
+
+import {globals} from './globals';
+import {PanelContainer} from './panel_container';
+
+/**
+ * Shorthand for if globals perf debug mode is on.
+ */
+export const perfDebug = () => globals.frontendLocalState.perfDebug;
+
+/**
+ * Returns performance.now() if perfDebug is enabled, otherwise 0.
+ * This is needed because calling performance.now is generally expensive
+ * and should not be done for every frame.
+ */
+export const debugNow = () => perfDebug() ? performance.now() : 0;
+
+/**
+ * Returns execution time of |fn| if perf debug mode is on. Returns 0 otherwise.
+ */
+export function measure(fn: () => void): number {
+ const start = debugNow();
+ fn();
+ return debugNow() - start;
+}
+
+/**
+ * Stores statistics about samples, and keeps a fixed size buffer of most recent
+ * samples.
+ */
+export class RunningStatistics {
+ private _count = 0;
+ private _mean = 0;
+ private _lastValue = 0;
+
+ private buffer: number[] = [];
+
+ constructor(private _maxBufferSize = 10) {}
+
+ addValue(value: number) {
+ this._lastValue = value;
+ this.buffer.push(value);
+ if (this.buffer.length > this._maxBufferSize) {
+ this.buffer.shift();
+ }
+ this._mean = (this._mean * this._count + value) / (this._count + 1);
+ this._count++;
+ }
+
+ get mean() {
+ return this._mean;
+ }
+ get count() {
+ return this._count;
+ }
+ get bufferMean() {
+ return this.buffer.reduce((sum, v) => sum + v, 0) / this.buffer.length;
+ }
+ get bufferSize() {
+ return this.buffer.length;
+ }
+ get maxBufferSize() {
+ return this._maxBufferSize;
+ }
+ get last() {
+ return this._lastValue;
+ }
+}
+
+/**
+ * Returns a summary string representation of a RunningStatistics object.
+ */
+export function runningStatStr(stat: RunningStatistics) {
+ return `Last: ${stat.last.toFixed(2)}ms | ` +
+ `Avg: ${stat.mean.toFixed(2)}ms | ` +
+ `Avg${stat.maxBufferSize}: ${stat.bufferMean.toFixed(2)}ms`;
+}
+
+/**
+ * Globals singleton class that renders performance stats for the whole app.
+ */
+class PerfDisplay {
+ private containers: PanelContainer[] = [];
+ addContainer(container: PanelContainer) {
+ this.containers.push(container);
+ }
+
+ removeContainer(container: PanelContainer) {
+ const i = this.containers.indexOf(container);
+ this.containers.splice(i, 1);
+ }
+
+ renderPerfStats() {
+ if (!perfDebug()) return;
+ const perfDisplayEl = this.getPerfDisplayEl();
+ if (!perfDisplayEl) return;
+ m.render(perfDisplayEl, [
+ m('section', globals.rafScheduler.renderPerfStats()),
+ this.containers.map((c, i) => m('section', c.renderPerfStats(i)))
+ ]);
+ }
+
+ getPerfDisplayEl() {
+ return document.querySelector('.perf-stats-content');
+ }
+}
+
+export const perfDisplay = new PerfDisplay();
diff --git a/ui/src/frontend/raf_scheduler.ts b/ui/src/frontend/raf_scheduler.ts
index 577ccca..bfe2017 100644
--- a/ui/src/frontend/raf_scheduler.ts
+++ b/ui/src/frontend/raf_scheduler.ts
@@ -12,6 +12,37 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+import * as m from 'mithril';
+
+import {assertTrue} from '../base/logging';
+
+import {
+ debugNow,
+ measure,
+ perfDebug,
+ perfDisplay,
+ RunningStatistics,
+ runningStatStr
+} from './perf';
+
+function statTableHeader() {
+ return m(
+ 'tr',
+ m('th', ''),
+ m('th', 'Last (ms)'),
+ m('th', 'Avg (ms)'),
+ m('th', 'Avg-10 (ms)'), );
+}
+
+function statTableRow(title: string, stat: RunningStatistics) {
+ return m(
+ 'tr',
+ m('td', title),
+ m('td', stat.last.toFixed(2)),
+ m('td', stat.mean.toFixed(2)),
+ m('td', stat.bufferMean.toFixed(2)), );
+}
+
export type ActionCallback = (nowMs: number) => void;
export type RedrawCallback = (nowMs: number) => void;
@@ -31,6 +62,14 @@
private requestedFullRedraw = false;
private isRedrawing = false;
+ private perfStats = {
+ rafActions: new RunningStatistics(),
+ rafCanvas: new RunningStatistics(),
+ rafDom: new RunningStatistics(),
+ rafTotal: new RunningStatistics(),
+ domRedraw: new RunningStatistics(),
+ };
+
start(cb: ActionCallback) {
this.actionCallbacks.add(cb);
this.maybeScheduleAnimationFrame();
@@ -61,11 +100,23 @@
this.maybeScheduleAnimationFrame(true);
}
+ syncDomRedraw(nowMs: number) {
+ const redrawStart = debugNow();
+ this._syncDomRedraw(nowMs);
+ if (perfDebug()) {
+ this.perfStats.domRedraw.addValue(debugNow() - redrawStart);
+ }
+ }
+
private syncCanvasRedraw(nowMs: number) {
+ const redrawStart = debugNow();
if (this.isRedrawing) return;
this.isRedrawing = true;
for (const redraw of this.canvasRedrawCallbacks) redraw(nowMs);
this.isRedrawing = false;
+ if (perfDebug()) {
+ this.perfStats.rafCanvas.addValue(debugNow() - redrawStart);
+ }
}
private maybeScheduleAnimationFrame(force = false) {
@@ -77,15 +128,64 @@
}
private onAnimationFrame(nowMs: number) {
+ const rafStart = debugNow();
this.hasScheduledNextFrame = false;
const doFullRedraw = this.requestedFullRedraw;
this.requestedFullRedraw = false;
- for (const action of this.actionCallbacks) action(nowMs);
- if (doFullRedraw) this._syncDomRedraw(nowMs);
- this.syncCanvasRedraw(nowMs);
+ const actionTime = measure(() => {
+ for (const action of this.actionCallbacks) action(nowMs);
+ });
+
+ const domTime = measure(() => {
+ if (doFullRedraw) this.syncDomRedraw(nowMs);
+ });
+ const canvasTime = measure(() => this.syncCanvasRedraw(nowMs));
+
+ const totalRafTime = debugNow() - rafStart;
+ this.updatePerfStats(actionTime, domTime, canvasTime, totalRafTime);
+ perfDisplay.renderPerfStats();
this.maybeScheduleAnimationFrame();
}
+
+ private updatePerfStats(
+ actionsTime: number, domTime: number, canvasTime: number,
+ totalRafTime: number) {
+ if (!perfDebug()) return;
+ this.perfStats.rafActions.addValue(actionsTime);
+ this.perfStats.rafDom.addValue(domTime);
+ this.perfStats.rafCanvas.addValue(canvasTime);
+ this.perfStats.rafTotal.addValue(totalRafTime);
+ }
+
+ renderPerfStats() {
+ assertTrue(perfDebug());
+ return m(
+ 'div',
+ m('div',
+ [
+ m('button',
+ {onclick: () => this.scheduleRedraw()},
+ 'Do Canvas Redraw'),
+ ' | ',
+ m('button',
+ {onclick: () => this.scheduleFullRedraw()},
+ 'Do Full Redraw'),
+ ]),
+ m('div',
+ 'Raf Timing ' +
+ '(Total may not add up due to imprecision)'),
+ m('table',
+ statTableHeader(),
+ statTableRow('Actions', this.perfStats.rafActions),
+ statTableRow('Dom', this.perfStats.rafDom),
+ statTableRow('Canvas', this.perfStats.rafCanvas),
+ statTableRow('Total', this.perfStats.rafTotal), ),
+ m('div',
+ 'Dom redraw: ' +
+ `Count: ${this.perfStats.domRedraw.count} | ` +
+ runningStatStr(this.perfStats.domRedraw)), );
+ }
}
diff --git a/ui/src/frontend/record_page.ts b/ui/src/frontend/record_page.ts
new file mode 100644
index 0000000..79d8417
--- /dev/null
+++ b/ui/src/frontend/record_page.ts
@@ -0,0 +1,59 @@
+// 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 * as m from 'mithril';
+
+import {createPage} from './pages';
+
+const RECORD_COMMAND_LINE =
+ 'echo CgYIgKAGIAESIwohCgxsaW51eC5mdHJhY2UQAKIGDhIFc2NoZWQSBWlucHV0GJBOMh0KFnBlcmZldHRvLnRyYWNlZF9wcm9iZXMQgCAYBEAASAA= | base64 --decode | adb shell "perfetto -c - -o /data/misc/perfetto-traces/trace" && adb pull /data/misc/perfetto-traces/trace /tmp/trace';
+
+async function copyToClipboard(text: string): Promise<void> {
+ try {
+ // TODO(hjd): Fix typescript type for navigator.
+ // tslint:disable-next-line no-any
+ await(navigator as any).clipboard.writeText(text);
+ } catch (err) {
+ console.error(`Failed to copy "${text}" to clipboard: ${err}`);
+ }
+}
+
+interface CodeSampleAttrs {
+ text: string;
+}
+
+class CodeSample implements m.ClassComponent<CodeSampleAttrs> {
+ view({attrs}: m.CVnode<CodeSampleAttrs>) {
+ return m(
+ '.example-code',
+ m('code', attrs.text),
+ m('button',
+ {
+ onclick: () => copyToClipboard(attrs.text),
+ },
+ 'Copy to clipboard'), );
+ }
+}
+
+export const RecordPage = createPage({
+ view() {
+ return m(
+ '.text-column',
+ 'To collect a 10 second Perfetto trace from an Android phone run this',
+ ' command:',
+ m(CodeSample, {text: RECORD_COMMAND_LINE}),
+ 'Then click "Open trace file" in the menu to the left and select',
+ ' "/tmp/trace".');
+ }
+});
diff --git a/ui/src/frontend/router.ts b/ui/src/frontend/router.ts
index 425bb33..98bf553 100644
--- a/ui/src/frontend/router.ts
+++ b/ui/src/frontend/router.ts
@@ -55,7 +55,8 @@
history.pushState(undefined, undefined, ROUTE_PREFIX + route);
if (!(route in this.routes)) {
- // Redirect to default route.
+ console.info(
+ `Route ${route} not known redirecting to ${this.defaultRoute}.`);
this.dispatch(navigate(this.defaultRoute));
}
}
diff --git a/ui/src/frontend/sidebar.ts b/ui/src/frontend/sidebar.ts
index da204ab..6f8e5d2 100644
--- a/ui/src/frontend/sidebar.ts
+++ b/ui/src/frontend/sidebar.ts
@@ -64,9 +64,12 @@
};
}
-const EXAMPLE_TRACE_URL =
+const EXAMPLE_ANDROID_TRACE_URL =
'https://storage.googleapis.com/perfetto-misc/example_trace_30s';
+const EXAMPLE_CHROME_TRACE_URL =
+ 'https://storage.googleapis.com/perfetto-misc/example_chrome_trace_10s.json';
+
const SECTIONS = [
{
title: 'Traces',
@@ -74,8 +77,17 @@
expanded: true,
items: [
{t: 'Open trace file', a: popupFileSelectionDialog, i: 'folder_open'},
- {t: 'Open example trace', a: handleOpenTraceUrl, i: 'description'},
- {t: 'Record new trace', a: navigateHome, i: 'fiber_smart_record'},
+ {
+ t: 'Open Android example',
+ a: openTraceUrl(EXAMPLE_ANDROID_TRACE_URL),
+ i: 'description'
+ },
+ {
+ t: 'Open Chrome example',
+ a: openTraceUrl(EXAMPLE_CHROME_TRACE_URL),
+ i: 'description'
+ },
+ {t: 'Record new trace', a: navigateRecord, i: 'fiber_smart_record'},
{t: 'Share current trace', a: dispatchCreatePermalink, i: 'share'},
],
},
@@ -133,9 +145,11 @@
(document.querySelector('input[type=file]')! as HTMLInputElement).click();
}
-function handleOpenTraceUrl(e: Event) {
- e.preventDefault();
- globals.dispatch(openTraceFromUrl(EXAMPLE_TRACE_URL));
+function openTraceUrl(url: string): (e: Event) => void {
+ return e => {
+ e.preventDefault();
+ globals.dispatch(openTraceFromUrl(url));
+ };
}
function onInputElementFileSelectionChanged(e: Event) {
@@ -146,16 +160,22 @@
globals.dispatch(openTraceFromFile(e.target.files[0]));
}
-function navigateHome(_: Event) {
+function navigateHome(e: Event) {
+ e.preventDefault();
globals.dispatch(navigate('/'));
}
+function navigateRecord(e: Event) {
+ e.preventDefault();
+ globals.dispatch(navigate('/record'));
+}
+
function dispatchCreatePermalink(e: Event) {
e.preventDefault();
globals.dispatch(createPermalink());
}
-export const Sidebar: m.Component = {
+export class Sidebar implements m.ClassComponent {
view() {
const vdomSections = [];
for (const section of SECTIONS) {
@@ -186,5 +206,5 @@
m('header', 'Perfetto'),
m('input[type=file]', {onchange: onInputElementFileSelectionChanged}),
...vdomSections);
- },
-};
+ }
+}
diff --git a/ui/src/frontend/topbar.ts b/ui/src/frontend/topbar.ts
index a04d2c6..03b142a 100644
--- a/ui/src/frontend/topbar.ts
+++ b/ui/src/frontend/topbar.ts
@@ -89,13 +89,14 @@
}
-const Omnibox: m.Component = {
- oncreate(vnode) {
+class Omnibox implements m.ClassComponent {
+ oncreate(vnode: m.VnodeDOM) {
const txt = vnode.dom.querySelector('input') as HTMLInputElement;
txt.addEventListener('blur', clearOmniboxResults);
txt.addEventListener('keydown', onKeyDown);
txt.addEventListener('keyup', onKeyUp);
- },
+ }
+
view() {
const msgTTL = globals.state.status.timestamp + 3 - Date.now() / 1e3;
let enginesAreBusy = false;
@@ -131,10 +132,10 @@
`.omnibox${commandMode ? '.command-mode' : ''}`,
m(`input[placeholder=${placeholder[mode]}]`),
m('.omnibox-results', results));
- },
-};
+ }
+}
-export const Topbar: m.Component = {
+export class Topbar implements m.ClassComponent {
view() {
const progBar = [];
const engine: EngineConfig = globals.state.engines['0'];
@@ -142,7 +143,6 @@
(engine !== undefined && !engine.ready)) {
progBar.push(m('.progress'));
}
-
return m('.topbar', m(Omnibox), ...progBar);
- },
-};
+ }
+}
diff --git a/ui/src/frontend/track.ts b/ui/src/frontend/track.ts
index 724041f..c543ad8 100644
--- a/ui/src/frontend/track.ts
+++ b/ui/src/frontend/track.ts
@@ -13,6 +13,7 @@
// limitations under the License.
import {TrackState} from '../common/state';
+import {globals} from './globals';
/**
* This interface forces track implementations to have some static properties.
@@ -32,15 +33,26 @@
/**
* The abstract class that needs to be implemented by all tracks.
*/
-export abstract class Track {
+export abstract class Track<Config = {}, Data = {}> {
/**
* Receive data published by the TrackController of this track.
*/
constructor(protected trackState: TrackState) {}
abstract renderCanvas(ctx: CanvasRenderingContext2D): void;
+
+ get config(): Config {
+ return this.trackState.config as Config;
+ }
+
+ data(): Data {
+ return globals.trackDataStore.get(this.trackState.id) as Data;
+ }
+
getHeight(): number {
return 40;
}
+
onMouseMove(_position: {x: number, y: number}) {}
+
onMouseOut() {}
}
diff --git a/ui/src/frontend/track_panel.ts b/ui/src/frontend/track_panel.ts
index ccf6041..a12024d 100644
--- a/ui/src/frontend/track_panel.ts
+++ b/ui/src/frontend/track_panel.ts
@@ -32,8 +32,11 @@
return globals.state.pinnedTracks.indexOf(id) !== -1;
}
-const TrackShell = {
- view({attrs}) {
+interface TrackShellAttrs {
+ trackState: TrackState;
+}
+class TrackShell implements m.ClassComponent<TrackShellAttrs> {
+ view({attrs}: m.CVnode<TrackShellAttrs>) {
return m(
'.track-shell',
m('h1', attrs.trackState.name),
@@ -49,11 +52,14 @@
action: toggleTrackPinned(attrs.trackState.id),
i: isPinned(attrs.trackState.id) ? 'star' : 'star_border',
}));
- },
-} as m.Component<{trackState: TrackState}>;
+ }
+}
-const TrackContent = {
- view({attrs}) {
+interface TrackContentAttrs {
+ track: Track;
+}
+class TrackContent implements m.ClassComponent<TrackContentAttrs> {
+ view({attrs}: m.CVnode<TrackContentAttrs>) {
return m('.track-content', {
onmousemove: (e: MouseEvent) => {
attrs.track.onMouseMove({x: e.layerX, y: e.layerY});
@@ -65,19 +71,27 @@
},
}, );
}
-} as m.Component<{track: Track}>;
+}
-const TrackComponent = {
- view({attrs}) {
+interface TrackComponentAttrs {
+ trackState: TrackState;
+ track: Track;
+}
+class TrackComponent implements m.ClassComponent<TrackComponentAttrs> {
+ view({attrs}: m.CVnode<TrackComponentAttrs>) {
return m('.track', [
m(TrackShell, {trackState: attrs.trackState}),
m(TrackContent, {track: attrs.track})
]);
}
-} as m.Component<{trackState: TrackState, track: Track}>;
+}
-const TrackButton = {
- view({attrs}) {
+interface TrackButtonAttrs {
+ action: Action;
+ i: string;
+}
+class TrackButton implements m.ClassComponent<TrackButtonAttrs> {
+ view({attrs}: m.CVnode<TrackButtonAttrs>) {
return m(
'i.material-icons.track-button',
{
@@ -85,11 +99,7 @@
},
attrs.i);
}
-} as m.Component<{
- action: Action,
- i: string,
-},
- {}>;
+}
interface TrackPanelAttrs {
id: string;
@@ -121,6 +131,7 @@
}
renderCanvas(ctx: CanvasRenderingContext2D, size: PanelSize) {
+ ctx.save();
ctx.translate(TRACK_SHELL_WIDTH, 0);
drawGridLines(
ctx,
@@ -129,5 +140,6 @@
size.height);
this.track.renderCanvas(ctx);
+ ctx.restore();
}
}
diff --git a/ui/src/frontend/viewer_page.ts b/ui/src/frontend/viewer_page.ts
index 94ee184..e801992 100644
--- a/ui/src/frontend/viewer_page.ts
+++ b/ui/src/frontend/viewer_page.ts
@@ -30,7 +30,7 @@
const MAX_ZOOM_SPAN_SEC = 1e-4; // 0.1 ms.
-const QueryTable: m.Component<{}, {}> = {
+class QueryTable implements m.ClassComponent {
view() {
const resp = globals.queryResults.get('command') as QueryResponse;
if (resp === undefined) {
@@ -58,25 +58,23 @@
resp.error ?
m('.query-error', `SQL error: ${resp.error}`) :
m('table.query-table', m('thead', header), m('tbody', rows)));
- },
-};
+ }
+}
/**
* Top-most level component for the viewer page. Holds tracks, brush timeline,
* panels, and everything else that's part of the main trace viewer page.
*/
-const TraceViewer = {
- oninit() {
- this.width = 0;
- },
+class TraceViewer implements m.ClassComponent {
+ private onResize: () => void = () => {};
+ private zoomContent?: PanAndZoomHandler;
- oncreate(vnode) {
+ oncreate(vnode: m.CVnodeDOM) {
const frontendLocalState = globals.frontendLocalState;
const updateDimensions = () => {
const rect = vnode.dom.getBoundingClientRect();
- this.width = rect.width;
frontendLocalState.timeScale.setLimitsPx(
- 0, this.width - TRACK_SHELL_WIDTH);
+ 0, rect.width - TRACK_SHELL_WIDTH);
};
updateDimensions();
@@ -124,12 +122,12 @@
new TimeSpan(newStartSec, newEndSec));
}
});
- },
+ }
onremove() {
window.removeEventListener('resize', this.onResize);
- this.zoomContent.shutdown();
- },
+ if (this.zoomContent) this.zoomContent.shutdown();
+ }
view() {
const scrollingPanels = globals.state.scrollingTracks.length > 0 ?
@@ -158,15 +156,8 @@
doesScroll: true,
panels: scrollingPanels,
}))));
- },
-
-} as m.Component<{}, {
- onResize: () => void,
- width: number,
- zoomContent: PanAndZoomHandler,
- overviewQueryExecuted: boolean,
- overviewQueryResponse: QueryResponse,
-}>;
+ }
+}
export const ViewerPage = createPage({
view() {
diff --git a/ui/src/tracks/chrome_slices/common.ts b/ui/src/tracks/chrome_slices/common.ts
index ca09741..1256dc7 100644
--- a/ui/src/tracks/chrome_slices/common.ts
+++ b/ui/src/tracks/chrome_slices/common.ts
@@ -14,7 +14,13 @@
export const SLICE_TRACK_KIND = 'ChromeSliceTrack';
-export interface ChromeSliceTrackData {
+export interface Config {
+ maxDepth: number;
+ upid: number;
+ utid: number;
+}
+
+export interface Data {
start: number;
end: number;
resolution: number;
diff --git a/ui/src/tracks/chrome_slices/controller.ts b/ui/src/tracks/chrome_slices/controller.ts
index 15d33c9..186491a 100644
--- a/ui/src/tracks/chrome_slices/controller.ts
+++ b/ui/src/tracks/chrome_slices/controller.ts
@@ -13,15 +13,14 @@
// limitations under the License.
import {fromNs} from '../../common/time';
-import {globals} from '../../controller/globals';
import {
TrackController,
trackControllerRegistry
} from '../../controller/track_controller';
-import {ChromeSliceTrackData, SLICE_TRACK_KIND} from './common';
+import {Config, Data, SLICE_TRACK_KIND} from './common';
-class ChromeSliceTrackController extends TrackController {
+class ChromeSliceTrackController extends TrackController<Config, Data> {
static readonly kind = SLICE_TRACK_KIND;
private busy = false;
@@ -34,7 +33,7 @@
// any index. We need to introduce ts_lower_bound also for the slices table
// (see sched table).
const query = `select ts,dur,depth,cat,name from slices ` +
- `where utid = ${this.trackState.utid} ` +
+ `where utid = ${this.config.utid} ` +
`and ts >= ${Math.round(start * 1e9)} - dur ` +
`and ts <= ${Math.round(end * 1e9)} ` +
`and dur >= ${Math.round(resolution * 1e9)} ` +
@@ -42,8 +41,7 @@
`limit ${LIMIT};`;
this.busy = true;
- console.log(query);
- this.engine.rawQuery({'sqlQuery': query}).then(rawResult => {
+ this.engine.query(query).then(rawResult => {
this.busy = false;
if (rawResult.error) {
throw new Error(`Query error "${query}": ${rawResult.error}`);
@@ -51,7 +49,7 @@
const numRows = +rawResult.numRecords;
- const slices: ChromeSliceTrackData = {
+ const slices: Data = {
start,
end,
resolution,
@@ -85,9 +83,10 @@
if (numRows === LIMIT) {
slices.end = slices.ends[slices.ends.length - 1];
}
- globals.publish('TrackData', {id: this.trackId, data: slices});
+ this.publish(slices);
});
}
}
+
trackControllerRegistry.register(ChromeSliceTrackController);
diff --git a/ui/src/tracks/chrome_slices/frontend.ts b/ui/src/tracks/chrome_slices/frontend.ts
index fd22613..aba1e7f 100644
--- a/ui/src/tracks/chrome_slices/frontend.ts
+++ b/ui/src/tracks/chrome_slices/frontend.ts
@@ -18,10 +18,7 @@
import {Track} from '../../frontend/track';
import {trackRegistry} from '../../frontend/track_registry';
-import {
- ChromeSliceTrackData,
- SLICE_TRACK_KIND,
-} from './common';
+import {Config, Data, SLICE_TRACK_KIND} from './common';
const SLICE_HEIGHT = 30;
const TRACK_PADDING = 5;
@@ -41,7 +38,7 @@
return Math.pow(10, Math.floor(Math.log10(resolution)));
}
-class ChromeSliceTrack extends Track {
+class ChromeSliceTrack extends Track<Config, Data> {
static readonly kind = SLICE_TRACK_KIND;
static create(trackState: TrackState): ChromeSliceTrack {
return new ChromeSliceTrack(trackState);
@@ -54,7 +51,6 @@
super(trackState);
}
-
reqDataDeferred() {
const {visibleWindowTime} = globals.frontendLocalState;
const reqStart = visibleWindowTime.start - visibleWindowTime.duration;
@@ -69,27 +65,27 @@
// TODO: fonts and colors should come from the CSS and not hardcoded here.
const {timeScale, visibleWindowTime} = globals.frontendLocalState;
- const trackData = this.trackData;
+ const data = this.data();
- // If there aren't enough cached slices data in |trackData| request more to
+ // If there aren't enough cached slices data in |data| request more to
// the controller.
- const inRange = trackData !== undefined &&
- (visibleWindowTime.start >= trackData.start &&
- visibleWindowTime.end <= trackData.end);
- if (!inRange || trackData.resolution > getCurResolution()) {
+ const inRange = data !== undefined &&
+ (visibleWindowTime.start >= data.start &&
+ visibleWindowTime.end <= data.end);
+ if (!inRange || data.resolution > getCurResolution()) {
if (!this.reqPending) {
this.reqPending = true;
setTimeout(() => this.reqDataDeferred(), 50);
}
- if (trackData === undefined) return; // Can't possibly draw anything.
+ if (data === undefined) return; // Can't possibly draw anything.
}
// If the cached trace slices don't fully cover the visible time range,
// show a gray rectangle with a "Loading..." label.
ctx.font = '12px Google Sans';
- if (trackData.start > visibleWindowTime.start) {
+ if (data.start > visibleWindowTime.start) {
const rectWidth =
- timeScale.timeToPx(Math.min(trackData.start, visibleWindowTime.end));
+ timeScale.timeToPx(Math.min(data.start, visibleWindowTime.end));
ctx.fillStyle = '#eee';
ctx.fillRect(0, TRACK_PADDING, rectWidth, SLICE_HEIGHT);
ctx.fillStyle = '#666';
@@ -99,9 +95,9 @@
TRACK_PADDING + SLICE_HEIGHT / 2,
rectWidth);
}
- if (trackData.end < visibleWindowTime.end) {
+ if (data.end < visibleWindowTime.end) {
const rectX =
- timeScale.timeToPx(Math.max(trackData.end, visibleWindowTime.start));
+ timeScale.timeToPx(Math.max(data.end, visibleWindowTime.start));
const rectWidth = timeScale.timeToPx(visibleWindowTime.end) - rectX;
ctx.fillStyle = '#eee';
ctx.fillRect(rectX, TRACK_PADDING, rectWidth, SLICE_HEIGHT);
@@ -120,13 +116,13 @@
const charWidth = ctx.measureText('abcdefghij').width / 10;
const pxEnd = timeScale.timeToPx(visibleWindowTime.end);
- for (let i = 0; i < trackData.starts.length; i++) {
- const tStart = trackData.starts[i];
- const tEnd = trackData.ends[i];
- const depth = trackData.depths[i];
- const cat = trackData.strings[trackData.categories[i]];
- const titleId = trackData.titles[i];
- const title = trackData.strings[titleId];
+ for (let i = 0; i < data.starts.length; i++) {
+ const tStart = data.starts[i];
+ const tEnd = data.ends[i];
+ const depth = data.depths[i];
+ const cat = data.strings[data.categories[i]];
+ const titleId = data.titles[i];
+ const title = data.strings[titleId];
if (tEnd <= visibleWindowTime.start || tStart >= visibleWindowTime.end) {
continue;
}
@@ -162,18 +158,18 @@
}
onMouseMove({x, y}: {x: number, y: number}) {
- const trackData = this.trackData;
+ const data = this.data();
this.hoveredTitleId = -1;
- if (trackData === undefined) return;
+ if (data === undefined) return;
const {timeScale} = globals.frontendLocalState;
if (y < TRACK_PADDING) return;
const t = timeScale.pxToTime(x);
const depth = Math.floor(y / SLICE_HEIGHT);
- for (let i = 0; i < trackData.starts.length; i++) {
- const tStart = trackData.starts[i];
- const tEnd = trackData.ends[i];
- const titleId = trackData.titles[i];
- if (tStart <= t && t <= tEnd && depth === trackData.depths[i]) {
+ for (let i = 0; i < data.starts.length; i++) {
+ const tStart = data.starts[i];
+ const tEnd = data.ends[i];
+ const titleId = data.titles[i];
+ if (tStart <= t && t <= tEnd && depth === data.depths[i]) {
this.hoveredTitleId = titleId;
break;
}
@@ -185,12 +181,7 @@
}
getHeight() {
- return SLICE_HEIGHT * (this.trackState.maxDepth + 1) + 2 * TRACK_PADDING;
- }
-
- private get trackData(): ChromeSliceTrackData {
- return globals.trackDataStore.get(this.trackState.id) as
- ChromeSliceTrackData;
+ return SLICE_HEIGHT * (this.config.maxDepth + 1) + 2 * TRACK_PADDING;
}
}
diff --git a/ui/src/tracks/cpu_slices/common.ts b/ui/src/tracks/cpu_slices/common.ts
index e7d2003..38844f1 100644
--- a/ui/src/tracks/cpu_slices/common.ts
+++ b/ui/src/tracks/cpu_slices/common.ts
@@ -14,7 +14,7 @@
export const CPU_SLICE_TRACK_KIND = 'CpuSliceTrack';
-export interface CpuSliceTrackData {
+export interface Data {
start: number;
end: number;
resolution: number;
@@ -24,3 +24,5 @@
ends: Float64Array;
utids: Uint32Array;
}
+
+export interface Config { cpu: number; }
diff --git a/ui/src/tracks/cpu_slices/controller.ts b/ui/src/tracks/cpu_slices/controller.ts
index f2243a1..17a1b0f 100644
--- a/ui/src/tracks/cpu_slices/controller.ts
+++ b/ui/src/tracks/cpu_slices/controller.ts
@@ -13,15 +13,14 @@
// limitations under the License.
import {fromNs} from '../../common/time';
-import {globals} from '../../controller/globals';
import {
TrackController,
trackControllerRegistry
} from '../../controller/track_controller';
-import {CPU_SLICE_TRACK_KIND, CpuSliceTrackData} from './common';
+import {Config, CPU_SLICE_TRACK_KIND, Data} from './common';
-class CpuSliceTrackController extends TrackController {
+class CpuSliceTrackController extends TrackController<Config, Data> {
static readonly kind = CPU_SLICE_TRACK_KIND;
private busy = false;
@@ -30,7 +29,7 @@
if (this.busy) return;
const LIMIT = 10000;
const query = 'select ts,dur,utid from sched ' +
- `where cpu = ${this.trackState.cpu} ` +
+ `where cpu = ${this.config.cpu} ` +
`and ts_lower_bound = ${Math.round(start * 1e9)} ` +
`and ts <= ${Math.round(end * 1e9)} ` +
`and dur >= ${Math.round(resolution * 1e9)} ` +
@@ -38,19 +37,15 @@
`order by ts ` +
`limit ${LIMIT};`;
- if (this.trackState.cpu === 0) console.log('QUERY', query);
-
this.busy = true;
- this.engine.rawQuery({'sqlQuery': query}).then(rawResult => {
+ this.engine.query(query).then(rawResult => {
this.busy = false;
if (rawResult.error) {
throw new Error(`Query error "${query}": ${rawResult.error}`);
}
- if (this.trackState.cpu === 0) console.log('QUERY DONE', query);
-
const numRows = +rawResult.numRecords;
- const slices: CpuSliceTrackData = {
+ const slices: Data = {
start,
end,
resolution,
@@ -69,7 +64,7 @@
if (numRows === LIMIT) {
slices.end = slices.ends[slices.ends.length - 1];
}
- globals.publish('TrackData', {id: this.trackId, data: slices});
+ this.publish(slices);
});
}
}
diff --git a/ui/src/tracks/cpu_slices/frontend.ts b/ui/src/tracks/cpu_slices/frontend.ts
index 0e0bddb..34b84d5 100644
--- a/ui/src/tracks/cpu_slices/frontend.ts
+++ b/ui/src/tracks/cpu_slices/frontend.ts
@@ -19,7 +19,7 @@
import {Track} from '../../frontend/track';
import {trackRegistry} from '../../frontend/track_registry';
-import {CPU_SLICE_TRACK_KIND, CpuSliceTrackData} from './common';
+import {Config, CPU_SLICE_TRACK_KIND, Data} from './common';
const MARGIN_TOP = 5;
const RECT_HEIGHT = 30;
@@ -46,7 +46,7 @@
return Math.pow(10, Math.floor(Math.log10(resolution)));
}
-class CpuSliceTrack extends Track {
+class CpuSliceTrack extends Track<Config, Data> {
static readonly kind = CPU_SLICE_TRACK_KIND;
static create(trackState: TrackState): CpuSliceTrack {
return new CpuSliceTrack(trackState);
@@ -74,19 +74,19 @@
// TODO: fonts and colors should come from the CSS and not hardcoded here.
const {timeScale, visibleWindowTime} = globals.frontendLocalState;
- const trackData = this.trackData;
+ const data = this.data();
- // If there aren't enough cached slices data in |trackData| request more to
+ // If there aren't enough cached slices data in |data| request more to
// the controller.
- const inRange = trackData !== undefined &&
- (visibleWindowTime.start >= trackData.start &&
- visibleWindowTime.end <= trackData.end);
- if (!inRange || trackData.resolution > getCurResolution()) {
+ const inRange = data !== undefined &&
+ (visibleWindowTime.start >= data.start &&
+ visibleWindowTime.end <= data.end);
+ if (!inRange || data.resolution > getCurResolution()) {
if (!this.reqPending) {
this.reqPending = true;
setTimeout(() => this.reqDataDeferred(), 50);
}
- if (trackData === undefined) return; // Can't possibly draw anything.
+ if (data === undefined) return; // Can't possibly draw anything.
}
ctx.textAlign = 'center';
ctx.font = '12px Google Sans';
@@ -94,23 +94,23 @@
// TODO: this needs to be kept in sync with the hue generation algorithm
// of overview_timeline_panel.ts
- const hue = (128 + (32 * this.trackState.cpu)) % 256;
+ const hue = (128 + (32 * this.config.cpu)) % 256;
// If the cached trace slices don't fully cover the visible time range,
// show a gray rectangle with a "Loading..." label.
ctx.font = '12px Google Sans';
- if (trackData.start > visibleWindowTime.start) {
+ if (data.start > visibleWindowTime.start) {
const rectWidth =
- timeScale.timeToPx(Math.min(trackData.start, visibleWindowTime.end));
+ timeScale.timeToPx(Math.min(data.start, visibleWindowTime.end));
ctx.fillStyle = '#eee';
ctx.fillRect(0, MARGIN_TOP, rectWidth, RECT_HEIGHT);
ctx.fillStyle = '#666';
ctx.fillText(
'loading...', rectWidth / 2, MARGIN_TOP + RECT_HEIGHT / 2, rectWidth);
}
- if (trackData.end < visibleWindowTime.end) {
+ if (data.end < visibleWindowTime.end) {
const rectX =
- timeScale.timeToPx(Math.max(trackData.end, visibleWindowTime.start));
+ timeScale.timeToPx(Math.max(data.end, visibleWindowTime.start));
const rectWidth = timeScale.timeToPx(visibleWindowTime.end) - rectX;
ctx.fillStyle = '#eee';
ctx.fillRect(rectX, MARGIN_TOP, rectWidth, RECT_HEIGHT);
@@ -122,12 +122,12 @@
rectWidth);
}
- assertTrue(trackData.starts.length === trackData.ends.length);
- assertTrue(trackData.starts.length === trackData.utids.length);
- for (let i = 0; i < trackData.starts.length; i++) {
- const tStart = trackData.starts[i];
- const tEnd = trackData.ends[i];
- const utid = trackData.utids[i];
+ assertTrue(data.starts.length === data.ends.length);
+ assertTrue(data.starts.length === data.utids.length);
+ for (let i = 0; i < data.starts.length; i++) {
+ const tStart = data.starts[i];
+ const tEnd = data.ends[i];
+ const utid = data.utids[i];
if (tEnd <= visibleWindowTime.start || tStart >= visibleWindowTime.end) {
continue;
}
@@ -185,9 +185,9 @@
}
onMouseMove({x, y}: {x: number, y: number}) {
- const trackData = this.trackData;
+ const data = this.data();
this.mouseXpos = x;
- if (trackData === undefined) return;
+ if (data === undefined) return;
const {timeScale} = globals.frontendLocalState;
if (y < MARGIN_TOP || y > MARGIN_TOP + RECT_HEIGHT) {
this.hoveredUtid = -1;
@@ -196,10 +196,10 @@
const t = timeScale.pxToTime(x);
this.hoveredUtid = -1;
- for (let i = 0; i < trackData.starts.length; i++) {
- const tStart = trackData.starts[i];
- const tEnd = trackData.ends[i];
- const utid = trackData.utids[i];
+ for (let i = 0; i < data.starts.length; i++) {
+ const tStart = data.starts[i];
+ const tEnd = data.ends[i];
+ const utid = data.utids[i];
if (tStart <= t && t <= tEnd) {
this.hoveredUtid = utid;
break;
@@ -211,10 +211,6 @@
this.hoveredUtid = -1;
this.mouseXpos = 0;
}
-
- private get trackData(): CpuSliceTrackData {
- return globals.trackDataStore.get(this.trackState.id) as CpuSliceTrackData;
- }
}
trackRegistry.register(CpuSliceTrack);