diff --git a/Android.bp b/Android.bp index 11cd68223c0..420badbb242 100644 --- a/Android.bp +++ b/Android.bp @@ -47,6 +47,8 @@ cc_binary { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -83,6 +85,7 @@ cc_binary { ":perfetto_protos_perfetto_trace_generic_kernel_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -150,6 +153,8 @@ cc_binary { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -186,6 +191,7 @@ cc_binary { "perfetto_protos_perfetto_trace_generic_kernel_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -346,6 +352,8 @@ cc_library_shared { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -382,6 +390,7 @@ cc_library_shared { ":perfetto_protos_perfetto_trace_generic_kernel_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -457,6 +466,8 @@ cc_library_shared { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -493,6 +504,7 @@ cc_library_shared { "perfetto_protos_perfetto_trace_generic_kernel_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -581,6 +593,8 @@ cc_library_shared { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -617,6 +631,7 @@ cc_library_shared { ":perfetto_protos_perfetto_trace_generic_kernel_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -667,6 +682,7 @@ cc_library_shared { ":perfetto_src_traced_probes_ftrace_ftrace", ":perfetto_src_traced_probes_ftrace_tracefs", ":perfetto_src_traced_probes_initial_display_state_initial_display_state", + ":perfetto_src_traced_probes_journald_journald", ":perfetto_src_traced_probes_metatrace_metatrace", ":perfetto_src_traced_probes_packages_list_packages_list", ":perfetto_src_traced_probes_packages_list_packages_list_parser", @@ -710,6 +726,8 @@ cc_library_shared { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -746,6 +764,7 @@ cc_library_shared { "perfetto_protos_perfetto_trace_generic_kernel_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -873,6 +892,8 @@ cc_library { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -909,6 +930,7 @@ cc_library { ":perfetto_protos_perfetto_trace_generic_kernel_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -977,6 +999,8 @@ cc_library { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -1013,6 +1037,7 @@ cc_library { "perfetto_protos_perfetto_trace_generic_kernel_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -1091,6 +1116,8 @@ cc_library_static { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -1127,6 +1154,7 @@ cc_library_static { ":perfetto_protos_perfetto_trace_generic_kernel_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -1196,6 +1224,8 @@ cc_library_static { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -1232,6 +1262,7 @@ cc_library_static { "perfetto_protos_perfetto_trace_generic_kernel_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -1262,6 +1293,8 @@ cc_library_static { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -1298,6 +1331,7 @@ cc_library_static { "perfetto_protos_perfetto_trace_generic_kernel_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -1446,6 +1480,8 @@ cc_binary { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -1482,6 +1518,7 @@ cc_binary { ":perfetto_protos_perfetto_trace_generic_kernel_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -1540,6 +1577,8 @@ cc_binary { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -1576,6 +1615,7 @@ cc_binary { "perfetto_protos_perfetto_trace_generic_kernel_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -1667,6 +1707,7 @@ filegroup { "protos/perfetto/config/inode_file/inode_file_config.proto", "protos/perfetto/config/interceptor_config.proto", "protos/perfetto/config/interceptors/console_config.proto", + "protos/perfetto/config/linux/journald_config.proto", "protos/perfetto/config/power/android_power_config.proto", "protos/perfetto/config/priority_boost/priority_boost_config.proto", "protos/perfetto/config/process_stats/process_stats_config.proto", @@ -1746,6 +1787,7 @@ java_library { "protos/perfetto/config/inode_file/inode_file_config.proto", "protos/perfetto/config/interceptor_config.proto", "protos/perfetto/config/interceptors/console_config.proto", + "protos/perfetto/config/linux/journald_config.proto", "protos/perfetto/config/power/android_power_config.proto", "protos/perfetto/config/priority_boost/priority_boost_config.proto", "protos/perfetto/config/process_stats/process_stats_config.proto", @@ -1809,6 +1851,8 @@ cc_library_static { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -1855,6 +1899,8 @@ cc_library_static { ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_cpp_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_cpp_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_cpp_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_cpp_gen", @@ -1917,6 +1963,7 @@ cc_library_static { ":perfetto_src_traced_probes_ftrace_ftrace", ":perfetto_src_traced_probes_ftrace_tracefs", ":perfetto_src_traced_probes_initial_display_state_initial_display_state", + ":perfetto_src_traced_probes_journald_journald", ":perfetto_src_traced_probes_metatrace_metatrace", ":perfetto_src_traced_probes_packages_list_packages_list", ":perfetto_src_traced_probes_packages_list_packages_list_parser", @@ -1972,6 +2019,8 @@ cc_library_static { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -2018,6 +2067,8 @@ cc_library_static { "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_cpp_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers", @@ -2059,6 +2110,8 @@ cc_library_static { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -2105,6 +2158,8 @@ cc_library_static { "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_cpp_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers", @@ -2179,6 +2234,8 @@ cc_library_static { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -2225,6 +2282,8 @@ cc_library_static { ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_cpp_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_cpp_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_cpp_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_cpp_gen", @@ -2286,6 +2345,7 @@ cc_library_static { ":perfetto_src_traced_probes_ftrace_ftrace", ":perfetto_src_traced_probes_ftrace_tracefs", ":perfetto_src_traced_probes_initial_display_state_initial_display_state", + ":perfetto_src_traced_probes_journald_journald", ":perfetto_src_traced_probes_metatrace_metatrace", ":perfetto_src_traced_probes_packages_list_packages_list", ":perfetto_src_traced_probes_packages_list_packages_list_parser", @@ -2333,6 +2393,8 @@ cc_library_static { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -2379,6 +2441,8 @@ cc_library_static { "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_cpp_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers", @@ -2420,6 +2484,8 @@ cc_library_static { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -2466,6 +2532,8 @@ cc_library_static { "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_cpp_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers", @@ -2786,6 +2854,9 @@ cc_test { ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_lite_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_lite_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_lite_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_lite_gen", @@ -2858,6 +2929,9 @@ cc_test { ":perfetto_protos_perfetto_trace_interned_data_cpp_gen", ":perfetto_protos_perfetto_trace_interned_data_lite_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_cpp_gen", + ":perfetto_protos_perfetto_trace_linux_lite_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_cpp_gen", ":perfetto_protos_perfetto_trace_minimal_lite_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", @@ -3119,6 +3193,7 @@ cc_test { ":perfetto_src_traced_probes_ftrace_test_support", ":perfetto_src_traced_probes_ftrace_tracefs", ":perfetto_src_traced_probes_initial_display_state_initial_display_state", + ":perfetto_src_traced_probes_journald_journald", ":perfetto_src_traced_probes_metatrace_metatrace", ":perfetto_src_traced_probes_packages_list_packages_list", ":perfetto_src_traced_probes_packages_list_packages_list_parser", @@ -3200,6 +3275,9 @@ cc_test { "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_lite_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_lite_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_lite_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_lite_gen_headers", @@ -3272,6 +3350,9 @@ cc_test { "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers", "perfetto_protos_perfetto_trace_interned_data_lite_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_cpp_gen_headers", + "perfetto_protos_perfetto_trace_linux_lite_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers", "perfetto_protos_perfetto_trace_minimal_lite_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", @@ -3472,6 +3553,7 @@ filegroup { "protos/perfetto/config/inode_file/inode_file_config.proto", "protos/perfetto/config/interceptor_config.proto", "protos/perfetto/config/interceptors/console_config.proto", + "protos/perfetto/config/linux/journald_config.proto", "protos/perfetto/config/power/android_power_config.proto", "protos/perfetto/config/priority_boost/priority_boost_config.proto", "protos/perfetto/config/process_stats/process_stats_config.proto", @@ -3663,6 +3745,7 @@ filegroup { "protos/perfetto/trace/gpu/vulkan_api_event.proto", "protos/perfetto/trace/gpu/vulkan_memory_event.proto", "protos/perfetto/trace/interned_data/interned_data.proto", + "protos/perfetto/trace/linux/journald_event.proto", "protos/perfetto/trace/memory_graph.proto", "protos/perfetto/trace/perfetto/perfetto_metatrace.proto", "protos/perfetto/trace/perfetto/trace_provenance.proto", @@ -4562,6 +4645,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_cpp", ":perfetto_protos_perfetto_config_inode_file_cpp", ":perfetto_protos_perfetto_config_interceptors_cpp", + ":perfetto_protos_perfetto_config_linux_cpp", ":perfetto_protos_perfetto_config_power_cpp", ":perfetto_protos_perfetto_config_priority_boost_cpp", ":perfetto_protos_perfetto_config_process_stats_cpp", @@ -4606,6 +4690,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_cpp", ":perfetto_protos_perfetto_config_inode_file_cpp", ":perfetto_protos_perfetto_config_interceptors_cpp", + ":perfetto_protos_perfetto_config_linux_cpp", ":perfetto_protos_perfetto_config_power_cpp", ":perfetto_protos_perfetto_config_priority_boost_cpp", ":perfetto_protos_perfetto_config_process_stats_cpp", @@ -4700,6 +4785,7 @@ genrule { "protos/perfetto/config/inode_file/inode_file_config.proto", "protos/perfetto/config/interceptor_config.proto", "protos/perfetto/config/interceptors/console_config.proto", + "protos/perfetto/config/linux/journald_config.proto", "protos/perfetto/config/power/android_power_config.proto", "protos/perfetto/config/priority_boost/priority_boost_config.proto", "protos/perfetto/config/process_stats/process_stats_config.proto", @@ -5287,6 +5373,136 @@ genrule { ], } +// GN: //protos/perfetto/config/linux:cpp +filegroup { + name: "perfetto_protos_perfetto_config_linux_cpp", + srcs: [ + "protos/perfetto/config/linux/journald_config.proto", + ], +} + +// GN: //protos/perfetto/config/linux:cpp +genrule { + name: "perfetto_protos_perfetto_config_linux_cpp_gen", + srcs: [ + ":perfetto_protos_perfetto_config_linux_cpp", + ], + tools: [ + "aprotoc", + "perfetto_src_protozero_protoc_plugin_cppgen_plugin", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_linux_cpp)", + out: [ + "external/perfetto/protos/perfetto/config/linux/journald_config.gen.cc", + ], +} + +// GN: //protos/perfetto/config/linux:cpp +genrule { + name: "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + srcs: [ + ":perfetto_protos_perfetto_config_linux_cpp", + ], + tools: [ + "aprotoc", + "perfetto_src_protozero_protoc_plugin_cppgen_plugin", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_linux_cpp)", + out: [ + "external/perfetto/protos/perfetto/config/linux/journald_config.gen.h", + ], + export_include_dirs: [ + ".", + "protos", + ], +} + +// GN: //protos/perfetto/config/linux:lite +filegroup { + name: "perfetto_protos_perfetto_config_linux_lite", + srcs: [ + "protos/perfetto/config/linux/journald_config.proto", + ], +} + +// GN: //protos/perfetto/config/linux:lite +genrule { + name: "perfetto_protos_perfetto_config_linux_lite_gen", + srcs: [ + ":perfetto_protos_perfetto_config_linux_lite", + ], + tools: [ + "aprotoc", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_linux_lite)", + out: [ + "external/perfetto/protos/perfetto/config/linux/journald_config.pb.cc", + ], +} + +// GN: //protos/perfetto/config/linux:lite +genrule { + name: "perfetto_protos_perfetto_config_linux_lite_gen_headers", + srcs: [ + ":perfetto_protos_perfetto_config_linux_lite", + ], + tools: [ + "aprotoc", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_linux_lite)", + out: [ + "external/perfetto/protos/perfetto/config/linux/journald_config.pb.h", + ], + export_include_dirs: [ + ".", + "protos", + ], +} + +// GN: //protos/perfetto/config/linux:zero +filegroup { + name: "perfetto_protos_perfetto_config_linux_zero", + srcs: [ + "protos/perfetto/config/linux/journald_config.proto", + ], +} + +// GN: //protos/perfetto/config/linux:zero +genrule { + name: "perfetto_protos_perfetto_config_linux_zero_gen", + srcs: [ + ":perfetto_protos_perfetto_config_linux_zero", + ], + tools: [ + "aprotoc", + "protozero_plugin", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_linux_zero)", + out: [ + "external/perfetto/protos/perfetto/config/linux/journald_config.pbzero.cc", + ], +} + +// GN: //protos/perfetto/config/linux:zero +genrule { + name: "perfetto_protos_perfetto_config_linux_zero_gen_headers", + srcs: [ + ":perfetto_protos_perfetto_config_linux_zero", + ], + tools: [ + "aprotoc", + "protozero_plugin", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_config_linux_zero)", + out: [ + "external/perfetto/protos/perfetto/config/linux/journald_config.pbzero.h", + ], + export_include_dirs: [ + ".", + "protos", + ], +} + // GN: //protos/perfetto/config:lite filegroup { name: "perfetto_protos_perfetto_config_lite", @@ -5316,6 +5532,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_lite", ":perfetto_protos_perfetto_config_inode_file_lite", ":perfetto_protos_perfetto_config_interceptors_lite", + ":perfetto_protos_perfetto_config_linux_lite", ":perfetto_protos_perfetto_config_lite", ":perfetto_protos_perfetto_config_power_lite", ":perfetto_protos_perfetto_config_priority_boost_lite", @@ -5359,6 +5576,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_lite", ":perfetto_protos_perfetto_config_inode_file_lite", ":perfetto_protos_perfetto_config_interceptors_lite", + ":perfetto_protos_perfetto_config_linux_lite", ":perfetto_protos_perfetto_config_lite", ":perfetto_protos_perfetto_config_power_lite", ":perfetto_protos_perfetto_config_priority_boost_lite", @@ -6796,6 +7014,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_zero", ":perfetto_protos_perfetto_config_inode_file_zero", ":perfetto_protos_perfetto_config_interceptors_zero", + ":perfetto_protos_perfetto_config_linux_zero", ":perfetto_protos_perfetto_config_power_zero", ":perfetto_protos_perfetto_config_priority_boost_zero", ":perfetto_protos_perfetto_config_process_stats_zero", @@ -6840,6 +7059,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_zero", ":perfetto_protos_perfetto_config_inode_file_zero", ":perfetto_protos_perfetto_config_interceptors_zero", + ":perfetto_protos_perfetto_config_linux_zero", ":perfetto_protos_perfetto_config_power_zero", ":perfetto_protos_perfetto_config_priority_boost_zero", ":perfetto_protos_perfetto_config_process_stats_zero", @@ -6899,6 +7119,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_cpp", ":perfetto_protos_perfetto_config_inode_file_cpp", ":perfetto_protos_perfetto_config_interceptors_cpp", + ":perfetto_protos_perfetto_config_linux_cpp", ":perfetto_protos_perfetto_config_power_cpp", ":perfetto_protos_perfetto_config_priority_boost_cpp", ":perfetto_protos_perfetto_config_process_stats_cpp", @@ -6936,6 +7157,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_cpp", ":perfetto_protos_perfetto_config_inode_file_cpp", ":perfetto_protos_perfetto_config_interceptors_cpp", + ":perfetto_protos_perfetto_config_linux_cpp", ":perfetto_protos_perfetto_config_power_cpp", ":perfetto_protos_perfetto_config_priority_boost_cpp", ":perfetto_protos_perfetto_config_process_stats_cpp", @@ -6987,6 +7209,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_cpp", ":perfetto_protos_perfetto_config_inode_file_cpp", ":perfetto_protos_perfetto_config_interceptors_cpp", + ":perfetto_protos_perfetto_config_linux_cpp", ":perfetto_protos_perfetto_config_power_cpp", ":perfetto_protos_perfetto_config_priority_boost_cpp", ":perfetto_protos_perfetto_config_process_stats_cpp", @@ -7026,6 +7249,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_cpp", ":perfetto_protos_perfetto_config_inode_file_cpp", ":perfetto_protos_perfetto_config_interceptors_cpp", + ":perfetto_protos_perfetto_config_linux_cpp", ":perfetto_protos_perfetto_config_power_cpp", ":perfetto_protos_perfetto_config_priority_boost_cpp", ":perfetto_protos_perfetto_config_process_stats_cpp", @@ -8805,6 +9029,7 @@ genrule { "protos/perfetto/config/inode_file/inode_file_config.proto", "protos/perfetto/config/interceptor_config.proto", "protos/perfetto/config/interceptors/console_config.proto", + "protos/perfetto/config/linux/journald_config.proto", "protos/perfetto/config/power/android_power_config.proto", "protos/perfetto/config/priority_boost/priority_boost_config.proto", "protos/perfetto/config/process_stats/process_stats_config.proto", @@ -8955,6 +9180,7 @@ genrule { "protos/perfetto/trace/gpu/vulkan_api_event.proto", "protos/perfetto/trace/gpu/vulkan_memory_event.proto", "protos/perfetto/trace/interned_data/interned_data.proto", + "protos/perfetto/trace/linux/journald_event.proto", "protos/perfetto/trace/memory_graph.proto", "protos/perfetto/trace/perfetto/perfetto_metatrace.proto", "protos/perfetto/trace/perfetto/trace_provenance.proto", @@ -10955,6 +11181,136 @@ genrule { ], } +// GN: //protos/perfetto/trace/linux:cpp +filegroup { + name: "perfetto_protos_perfetto_trace_linux_cpp", + srcs: [ + "protos/perfetto/trace/linux/journald_event.proto", + ], +} + +// GN: //protos/perfetto/trace/linux:cpp +genrule { + name: "perfetto_protos_perfetto_trace_linux_cpp_gen", + srcs: [ + ":perfetto_protos_perfetto_trace_linux_cpp", + ], + tools: [ + "aprotoc", + "perfetto_src_protozero_protoc_plugin_cppgen_plugin", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_trace_linux_cpp)", + out: [ + "external/perfetto/protos/perfetto/trace/linux/journald_event.gen.cc", + ], +} + +// GN: //protos/perfetto/trace/linux:cpp +genrule { + name: "perfetto_protos_perfetto_trace_linux_cpp_gen_headers", + srcs: [ + ":perfetto_protos_perfetto_trace_linux_cpp", + ], + tools: [ + "aprotoc", + "perfetto_src_protozero_protoc_plugin_cppgen_plugin", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location perfetto_src_protozero_protoc_plugin_cppgen_plugin) --plugin_out=wrapper_namespace=gen:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_trace_linux_cpp)", + out: [ + "external/perfetto/protos/perfetto/trace/linux/journald_event.gen.h", + ], + export_include_dirs: [ + ".", + "protos", + ], +} + +// GN: //protos/perfetto/trace/linux:lite +filegroup { + name: "perfetto_protos_perfetto_trace_linux_lite", + srcs: [ + "protos/perfetto/trace/linux/journald_event.proto", + ], +} + +// GN: //protos/perfetto/trace/linux:lite +genrule { + name: "perfetto_protos_perfetto_trace_linux_lite_gen", + srcs: [ + ":perfetto_protos_perfetto_trace_linux_lite", + ], + tools: [ + "aprotoc", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_trace_linux_lite)", + out: [ + "external/perfetto/protos/perfetto/trace/linux/journald_event.pb.cc", + ], +} + +// GN: //protos/perfetto/trace/linux:lite +genrule { + name: "perfetto_protos_perfetto_trace_linux_lite_gen_headers", + srcs: [ + ":perfetto_protos_perfetto_trace_linux_lite", + ], + tools: [ + "aprotoc", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --cpp_out=lite=true:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_trace_linux_lite)", + out: [ + "external/perfetto/protos/perfetto/trace/linux/journald_event.pb.h", + ], + export_include_dirs: [ + ".", + "protos", + ], +} + +// GN: //protos/perfetto/trace/linux:zero +filegroup { + name: "perfetto_protos_perfetto_trace_linux_zero", + srcs: [ + "protos/perfetto/trace/linux/journald_event.proto", + ], +} + +// GN: //protos/perfetto/trace/linux:zero +genrule { + name: "perfetto_protos_perfetto_trace_linux_zero_gen", + srcs: [ + ":perfetto_protos_perfetto_trace_linux_zero", + ], + tools: [ + "aprotoc", + "protozero_plugin", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_trace_linux_zero)", + out: [ + "external/perfetto/protos/perfetto/trace/linux/journald_event.pbzero.cc", + ], +} + +// GN: //protos/perfetto/trace/linux:zero +genrule { + name: "perfetto_protos_perfetto_trace_linux_zero_gen_headers", + srcs: [ + ":perfetto_protos_perfetto_trace_linux_zero", + ], + tools: [ + "aprotoc", + "protozero_plugin", + ], + cmd: "mkdir -p $(genDir)/external/perfetto/ && $(location aprotoc) --proto_path=external/perfetto --plugin=protoc-gen-plugin=$(location protozero_plugin) --plugin_out=wrapper_namespace=pbzero:$(genDir)/external/perfetto/ $(locations :perfetto_protos_perfetto_trace_linux_zero)", + out: [ + "external/perfetto/protos/perfetto/trace/linux/journald_event.pbzero.h", + ], + export_include_dirs: [ + ".", + "protos", + ], +} + // GN: //protos/perfetto/trace:minimal_cpp filegroup { name: "perfetto_protos_perfetto_trace_minimal_cpp", @@ -10977,6 +11333,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_cpp", ":perfetto_protos_perfetto_config_inode_file_cpp", ":perfetto_protos_perfetto_config_interceptors_cpp", + ":perfetto_protos_perfetto_config_linux_cpp", ":perfetto_protos_perfetto_config_power_cpp", ":perfetto_protos_perfetto_config_priority_boost_cpp", ":perfetto_protos_perfetto_config_process_stats_cpp", @@ -11014,6 +11371,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_cpp", ":perfetto_protos_perfetto_config_inode_file_cpp", ":perfetto_protos_perfetto_config_interceptors_cpp", + ":perfetto_protos_perfetto_config_linux_cpp", ":perfetto_protos_perfetto_config_power_cpp", ":perfetto_protos_perfetto_config_priority_boost_cpp", ":perfetto_protos_perfetto_config_process_stats_cpp", @@ -11064,6 +11422,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_lite", ":perfetto_protos_perfetto_config_inode_file_lite", ":perfetto_protos_perfetto_config_interceptors_lite", + ":perfetto_protos_perfetto_config_linux_lite", ":perfetto_protos_perfetto_config_lite", ":perfetto_protos_perfetto_config_power_lite", ":perfetto_protos_perfetto_config_priority_boost_lite", @@ -11100,6 +11459,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_lite", ":perfetto_protos_perfetto_config_inode_file_lite", ":perfetto_protos_perfetto_config_interceptors_lite", + ":perfetto_protos_perfetto_config_linux_lite", ":perfetto_protos_perfetto_config_lite", ":perfetto_protos_perfetto_config_power_lite", ":perfetto_protos_perfetto_config_priority_boost_lite", @@ -11150,6 +11510,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_zero", ":perfetto_protos_perfetto_config_inode_file_zero", ":perfetto_protos_perfetto_config_interceptors_zero", + ":perfetto_protos_perfetto_config_linux_zero", ":perfetto_protos_perfetto_config_power_zero", ":perfetto_protos_perfetto_config_priority_boost_zero", ":perfetto_protos_perfetto_config_process_stats_zero", @@ -11187,6 +11548,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_zero", ":perfetto_protos_perfetto_config_inode_file_zero", ":perfetto_protos_perfetto_config_interceptors_zero", + ":perfetto_protos_perfetto_config_linux_zero", ":perfetto_protos_perfetto_config_power_zero", ":perfetto_protos_perfetto_config_priority_boost_zero", ":perfetto_protos_perfetto_config_process_stats_zero", @@ -11246,6 +11608,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_cpp", ":perfetto_protos_perfetto_config_inode_file_cpp", ":perfetto_protos_perfetto_config_interceptors_cpp", + ":perfetto_protos_perfetto_config_linux_cpp", ":perfetto_protos_perfetto_config_power_cpp", ":perfetto_protos_perfetto_config_priority_boost_cpp", ":perfetto_protos_perfetto_config_process_stats_cpp", @@ -11267,6 +11630,7 @@ genrule { ":perfetto_protos_perfetto_trace_generic_kernel_cpp", ":perfetto_protos_perfetto_trace_gpu_cpp", ":perfetto_protos_perfetto_trace_interned_data_cpp", + ":perfetto_protos_perfetto_trace_linux_cpp", ":perfetto_protos_perfetto_trace_minimal_cpp", ":perfetto_protos_perfetto_trace_non_minimal_cpp", ":perfetto_protos_perfetto_trace_perfetto_cpp", @@ -11310,6 +11674,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_cpp", ":perfetto_protos_perfetto_config_inode_file_cpp", ":perfetto_protos_perfetto_config_interceptors_cpp", + ":perfetto_protos_perfetto_config_linux_cpp", ":perfetto_protos_perfetto_config_power_cpp", ":perfetto_protos_perfetto_config_priority_boost_cpp", ":perfetto_protos_perfetto_config_process_stats_cpp", @@ -11331,6 +11696,7 @@ genrule { ":perfetto_protos_perfetto_trace_generic_kernel_cpp", ":perfetto_protos_perfetto_trace_gpu_cpp", ":perfetto_protos_perfetto_trace_interned_data_cpp", + ":perfetto_protos_perfetto_trace_linux_cpp", ":perfetto_protos_perfetto_trace_minimal_cpp", ":perfetto_protos_perfetto_trace_non_minimal_cpp", ":perfetto_protos_perfetto_trace_perfetto_cpp", @@ -11394,6 +11760,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_lite", ":perfetto_protos_perfetto_config_inode_file_lite", ":perfetto_protos_perfetto_config_interceptors_lite", + ":perfetto_protos_perfetto_config_linux_lite", ":perfetto_protos_perfetto_config_lite", ":perfetto_protos_perfetto_config_power_lite", ":perfetto_protos_perfetto_config_priority_boost_lite", @@ -11416,6 +11783,7 @@ genrule { ":perfetto_protos_perfetto_trace_generic_kernel_lite", ":perfetto_protos_perfetto_trace_gpu_lite", ":perfetto_protos_perfetto_trace_interned_data_lite", + ":perfetto_protos_perfetto_trace_linux_lite", ":perfetto_protos_perfetto_trace_minimal_lite", ":perfetto_protos_perfetto_trace_non_minimal_lite", ":perfetto_protos_perfetto_trace_perfetto_lite", @@ -11457,6 +11825,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_lite", ":perfetto_protos_perfetto_config_inode_file_lite", ":perfetto_protos_perfetto_config_interceptors_lite", + ":perfetto_protos_perfetto_config_linux_lite", ":perfetto_protos_perfetto_config_lite", ":perfetto_protos_perfetto_config_power_lite", ":perfetto_protos_perfetto_config_priority_boost_lite", @@ -11479,6 +11848,7 @@ genrule { ":perfetto_protos_perfetto_trace_generic_kernel_lite", ":perfetto_protos_perfetto_trace_gpu_lite", ":perfetto_protos_perfetto_trace_interned_data_lite", + ":perfetto_protos_perfetto_trace_linux_lite", ":perfetto_protos_perfetto_trace_minimal_lite", ":perfetto_protos_perfetto_trace_non_minimal_lite", ":perfetto_protos_perfetto_trace_perfetto_lite", @@ -11541,6 +11911,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_zero", ":perfetto_protos_perfetto_config_inode_file_zero", ":perfetto_protos_perfetto_config_interceptors_zero", + ":perfetto_protos_perfetto_config_linux_zero", ":perfetto_protos_perfetto_config_power_zero", ":perfetto_protos_perfetto_config_priority_boost_zero", ":perfetto_protos_perfetto_config_process_stats_zero", @@ -11563,6 +11934,7 @@ genrule { ":perfetto_protos_perfetto_trace_generic_kernel_zero", ":perfetto_protos_perfetto_trace_gpu_zero", ":perfetto_protos_perfetto_trace_interned_data_zero", + ":perfetto_protos_perfetto_trace_linux_zero", ":perfetto_protos_perfetto_trace_minimal_zero", ":perfetto_protos_perfetto_trace_non_minimal_zero", ":perfetto_protos_perfetto_trace_perfetto_zero", @@ -11605,6 +11977,7 @@ genrule { ":perfetto_protos_perfetto_config_gpu_zero", ":perfetto_protos_perfetto_config_inode_file_zero", ":perfetto_protos_perfetto_config_interceptors_zero", + ":perfetto_protos_perfetto_config_linux_zero", ":perfetto_protos_perfetto_config_power_zero", ":perfetto_protos_perfetto_config_priority_boost_zero", ":perfetto_protos_perfetto_config_process_stats_zero", @@ -11627,6 +12000,7 @@ genrule { ":perfetto_protos_perfetto_trace_generic_kernel_zero", ":perfetto_protos_perfetto_trace_gpu_zero", ":perfetto_protos_perfetto_trace_interned_data_zero", + ":perfetto_protos_perfetto_trace_linux_zero", ":perfetto_protos_perfetto_trace_minimal_zero", ":perfetto_protos_perfetto_trace_non_minimal_zero", ":perfetto_protos_perfetto_trace_perfetto_zero", @@ -16185,6 +16559,8 @@ filegroup { "src/trace_processor/importers/proto/heap_graph_module.cc", "src/trace_processor/importers/proto/heap_graph_tracker.cc", "src/trace_processor/importers/proto/jit_tracker.cc", + "src/trace_processor/importers/proto/linux_probes_module.cc", + "src/trace_processor/importers/proto/linux_probes_parser.cc", "src/trace_processor/importers/proto/metadata_module.cc", "src/trace_processor/importers/proto/pigweed_detokenizer.cc", "src/trace_processor/importers/proto/pixel_modem_module.cc", @@ -17028,6 +17404,7 @@ filegroup { "src/trace_processor/perfetto_sql/stdlib/linux/cpu/utilization/thread_cpu.sql", "src/trace_processor/perfetto_sql/stdlib/linux/devfreq.sql", "src/trace_processor/perfetto_sql/stdlib/linux/irqs.sql", + "src/trace_processor/perfetto_sql/stdlib/linux/journald.sql", "src/trace_processor/perfetto_sql/stdlib/linux/memory/general.sql", "src/trace_processor/perfetto_sql/stdlib/linux/memory/high_watermark.sql", "src/trace_processor/perfetto_sql/stdlib/linux/memory/process.sql", @@ -17339,6 +17716,7 @@ python_binary_host { "src/trace_processor/tables/etm_tables.py", "src/trace_processor/tables/flow_tables.py", "src/trace_processor/tables/jit_tables.py", + "src/trace_processor/tables/log_tables.py", "src/trace_processor/tables/memory_tables.py", "src/trace_processor/tables/metadata_tables.py", "src/trace_processor/tables/perf_tables.py", @@ -17424,6 +17802,7 @@ python_binary_host { "src/trace_processor/tables/etm_tables.py", "src/trace_processor/tables/flow_tables.py", "src/trace_processor/tables/jit_tables.py", + "src/trace_processor/tables/log_tables.py", "src/trace_processor/tables/memory_tables.py", "src/trace_processor/tables/metadata_tables.py", "src/trace_processor/tables/perf_tables.py", @@ -17717,6 +18096,7 @@ python_binary_host { "src/trace_processor/tables/etm_tables.py", "src/trace_processor/tables/flow_tables.py", "src/trace_processor/tables/jit_tables.py", + "src/trace_processor/tables/log_tables.py", "src/trace_processor/tables/memory_tables.py", "src/trace_processor/tables/metadata_tables.py", "src/trace_processor/tables/perf_tables.py", @@ -17779,6 +18159,7 @@ python_binary_host { "src/trace_processor/tables/etm_tables.py", "src/trace_processor/tables/flow_tables.py", "src/trace_processor/tables/jit_tables.py", + "src/trace_processor/tables/log_tables.py", "src/trace_processor/tables/memory_tables.py", "src/trace_processor/tables/metadata_tables.py", "src/trace_processor/tables/perf_tables.py", @@ -17840,6 +18221,7 @@ python_binary_host { "src/trace_processor/tables/etm_tables.py", "src/trace_processor/tables/flow_tables.py", "src/trace_processor/tables/jit_tables.py", + "src/trace_processor/tables/log_tables.py", "src/trace_processor/tables/memory_tables.py", "src/trace_processor/tables/metadata_tables.py", "src/trace_processor/tables/perf_tables.py", @@ -18593,6 +18975,7 @@ genrule { "src/trace_processor/tables/etm_tables.py", "src/trace_processor/tables/flow_tables.py", "src/trace_processor/tables/jit_tables.py", + "src/trace_processor/tables/log_tables.py", "src/trace_processor/tables/memory_tables.py", "src/trace_processor/tables/metadata_tables.py", "src/trace_processor/tables/perf_tables.py", @@ -18620,6 +19003,8 @@ genrule { "src/trace_processor/tables/flow_tables_py.h", "src/trace_processor/tables/jit_tables_fwd.h", "src/trace_processor/tables/jit_tables_py.h", + "src/trace_processor/tables/log_tables_fwd.h", + "src/trace_processor/tables/log_tables_py.h", "src/trace_processor/tables/memory_tables_fwd.h", "src/trace_processor/tables/memory_tables_py.h", "src/trace_processor/tables/metadata_tables_fwd.h", @@ -18655,6 +19040,7 @@ python_binary_host { "src/trace_processor/tables/etm_tables.py", "src/trace_processor/tables/flow_tables.py", "src/trace_processor/tables/jit_tables.py", + "src/trace_processor/tables/log_tables.py", "src/trace_processor/tables/memory_tables.py", "src/trace_processor/tables/metadata_tables.py", "src/trace_processor/tables/perf_tables.py", @@ -18715,6 +19101,7 @@ cc_library_static { ":perfetto_protos_perfetto_config_gpu_zero_gen", ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_zero_gen", ":perfetto_protos_perfetto_config_process_stats_zero_gen", @@ -18742,6 +19129,7 @@ cc_library_static { ":perfetto_protos_perfetto_trace_gpu_gpu_track_event_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -18957,6 +19345,7 @@ cc_library_static { "perfetto_protos_perfetto_config_gpu_zero_gen_headers", "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_zero_gen_headers", "perfetto_protos_perfetto_config_process_stats_zero_gen_headers", @@ -18984,6 +19373,7 @@ cc_library_static { "perfetto_protos_perfetto_trace_gpu_gpu_track_event_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -19051,6 +19441,7 @@ cc_library_static { "perfetto_protos_perfetto_config_gpu_zero_gen_headers", "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_zero_gen_headers", "perfetto_protos_perfetto_config_process_stats_zero_gen_headers", @@ -19078,6 +19469,7 @@ cc_library_static { "perfetto_protos_perfetto_trace_gpu_gpu_track_event_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -20047,6 +20439,22 @@ filegroup { ], } +// GN: //src/traced/probes/journald:journald +filegroup { + name: "perfetto_src_traced_probes_journald_journald", + srcs: [ + "src/traced/probes/journald/journald_data_source.cc", + ], +} + +// GN: //src/traced/probes/journald:unittests +filegroup { + name: "perfetto_src_traced_probes_journald_unittests", + srcs: [ + "src/traced/probes/journald/journald_data_source_unittest.cc", + ], +} + // GN: //src/traced/probes/metatrace:metatrace filegroup { name: "perfetto_src_traced_probes_metatrace_metatrace", @@ -20646,6 +21054,7 @@ java_library { "protos/perfetto/config/inode_file/inode_file_config.proto", "protos/perfetto/config/interceptor_config.proto", "protos/perfetto/config/interceptors/console_config.proto", + "protos/perfetto/config/linux/journald_config.proto", "protos/perfetto/config/power/android_power_config.proto", "protos/perfetto/config/priority_boost/priority_boost_config.proto", "protos/perfetto/config/process_stats/process_stats_config.proto", @@ -20796,6 +21205,7 @@ java_library { "protos/perfetto/trace/gpu/vulkan_api_event.proto", "protos/perfetto/trace/gpu/vulkan_memory_event.proto", "protos/perfetto/trace/interned_data/interned_data.proto", + "protos/perfetto/trace/linux/journald_event.proto", "protos/perfetto/trace/memory_graph.proto", "protos/perfetto/trace/perfetto/perfetto_metatrace.proto", "protos/perfetto/trace/perfetto/trace_provenance.proto", @@ -20918,6 +21328,7 @@ cc_library_static { ":perfetto_protos_perfetto_config_gpu_lite_gen", ":perfetto_protos_perfetto_config_inode_file_lite_gen", ":perfetto_protos_perfetto_config_interceptors_lite_gen", + ":perfetto_protos_perfetto_config_linux_lite_gen", ":perfetto_protos_perfetto_config_lite_gen", ":perfetto_protos_perfetto_config_power_lite_gen", ":perfetto_protos_perfetto_config_priority_boost_lite_gen", @@ -20940,6 +21351,7 @@ cc_library_static { ":perfetto_protos_perfetto_trace_generic_kernel_lite_gen", ":perfetto_protos_perfetto_trace_gpu_lite_gen", ":perfetto_protos_perfetto_trace_interned_data_lite_gen", + ":perfetto_protos_perfetto_trace_linux_lite_gen", ":perfetto_protos_perfetto_trace_minimal_lite_gen", ":perfetto_protos_perfetto_trace_non_minimal_lite_gen", ":perfetto_protos_perfetto_trace_perfetto_lite_gen", @@ -20965,6 +21377,7 @@ cc_library_static { "perfetto_protos_perfetto_config_gpu_lite_gen_headers", "perfetto_protos_perfetto_config_inode_file_lite_gen_headers", "perfetto_protos_perfetto_config_interceptors_lite_gen_headers", + "perfetto_protos_perfetto_config_linux_lite_gen_headers", "perfetto_protos_perfetto_config_lite_gen_headers", "perfetto_protos_perfetto_config_power_lite_gen_headers", "perfetto_protos_perfetto_config_priority_boost_lite_gen_headers", @@ -20987,6 +21400,7 @@ cc_library_static { "perfetto_protos_perfetto_trace_generic_kernel_lite_gen_headers", "perfetto_protos_perfetto_trace_gpu_lite_gen_headers", "perfetto_protos_perfetto_trace_interned_data_lite_gen_headers", + "perfetto_protos_perfetto_trace_linux_lite_gen_headers", "perfetto_protos_perfetto_trace_minimal_lite_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_lite_gen_headers", "perfetto_protos_perfetto_trace_perfetto_lite_gen_headers", @@ -21007,6 +21421,7 @@ cc_library_static { "perfetto_protos_perfetto_config_gpu_lite_gen_headers", "perfetto_protos_perfetto_config_inode_file_lite_gen_headers", "perfetto_protos_perfetto_config_interceptors_lite_gen_headers", + "perfetto_protos_perfetto_config_linux_lite_gen_headers", "perfetto_protos_perfetto_config_lite_gen_headers", "perfetto_protos_perfetto_config_power_lite_gen_headers", "perfetto_protos_perfetto_config_priority_boost_lite_gen_headers", @@ -21029,6 +21444,7 @@ cc_library_static { "perfetto_protos_perfetto_trace_generic_kernel_lite_gen_headers", "perfetto_protos_perfetto_trace_gpu_lite_gen_headers", "perfetto_protos_perfetto_trace_interned_data_lite_gen_headers", + "perfetto_protos_perfetto_trace_linux_lite_gen_headers", "perfetto_protos_perfetto_trace_minimal_lite_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_lite_gen_headers", "perfetto_protos_perfetto_trace_perfetto_lite_gen_headers", @@ -21124,6 +21540,9 @@ cc_test { ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_lite_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_lite_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_lite_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_lite_gen", @@ -21198,6 +21617,9 @@ cc_test { ":perfetto_protos_perfetto_trace_interned_data_cpp_gen", ":perfetto_protos_perfetto_trace_interned_data_lite_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_cpp_gen", + ":perfetto_protos_perfetto_trace_linux_lite_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_cpp_gen", ":perfetto_protos_perfetto_trace_minimal_lite_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", @@ -21567,6 +21989,8 @@ cc_test { ":perfetto_src_traced_probes_ftrace_unittests", ":perfetto_src_traced_probes_initial_display_state_initial_display_state", ":perfetto_src_traced_probes_initial_display_state_unittests", + ":perfetto_src_traced_probes_journald_journald", + ":perfetto_src_traced_probes_journald_unittests", ":perfetto_src_traced_probes_metatrace_metatrace", ":perfetto_src_traced_probes_packages_list_packages_list", ":perfetto_src_traced_probes_packages_list_packages_list_parser", @@ -21656,6 +22080,9 @@ cc_test { "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_lite_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_lite_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_lite_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_lite_gen_headers", @@ -21730,6 +22157,9 @@ cc_test { "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers", "perfetto_protos_perfetto_trace_interned_data_lite_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_cpp_gen_headers", + "perfetto_protos_perfetto_trace_linux_lite_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers", "perfetto_protos_perfetto_trace_minimal_lite_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", @@ -21903,6 +22333,8 @@ cc_library_static { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -21949,6 +22381,8 @@ cc_library_static { ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_cpp_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_cpp_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_cpp_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_cpp_gen", @@ -22010,6 +22444,7 @@ cc_library_static { ":perfetto_src_traced_probes_ftrace_ftrace", ":perfetto_src_traced_probes_ftrace_tracefs", ":perfetto_src_traced_probes_initial_display_state_initial_display_state", + ":perfetto_src_traced_probes_journald_journald", ":perfetto_src_traced_probes_metatrace_metatrace", ":perfetto_src_traced_probes_packages_list_packages_list", ":perfetto_src_traced_probes_packages_list_packages_list_parser", @@ -22057,6 +22492,8 @@ cc_library_static { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -22103,6 +22540,8 @@ cc_library_static { "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_cpp_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers", @@ -22144,6 +22583,8 @@ cc_library_static { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -22190,6 +22631,8 @@ cc_library_static { "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_cpp_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_cpp_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_cpp_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_cpp_gen_headers", @@ -22342,6 +22785,7 @@ cc_library_static { ":perfetto_protos_perfetto_config_gpu_zero_gen", ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_zero_gen", ":perfetto_protos_perfetto_config_process_stats_zero_gen", @@ -22369,6 +22813,7 @@ cc_library_static { ":perfetto_protos_perfetto_trace_gpu_gpu_track_event_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -22569,6 +23014,7 @@ cc_library_static { "perfetto_protos_perfetto_config_gpu_zero_gen_headers", "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_zero_gen_headers", "perfetto_protos_perfetto_config_process_stats_zero_gen_headers", @@ -22596,6 +23042,7 @@ cc_library_static { "perfetto_protos_perfetto_trace_gpu_gpu_track_event_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -22660,6 +23107,7 @@ cc_library_static { "perfetto_protos_perfetto_config_gpu_zero_gen_headers", "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_zero_gen_headers", "perfetto_protos_perfetto_config_process_stats_zero_gen_headers", @@ -22687,6 +23135,7 @@ cc_library_static { "perfetto_protos_perfetto_trace_gpu_gpu_track_event_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -22846,6 +23295,7 @@ cc_binary { ":perfetto_protos_perfetto_config_gpu_zero_gen", ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_zero_gen", ":perfetto_protos_perfetto_config_process_stats_zero_gen", @@ -22868,6 +23318,7 @@ cc_binary { ":perfetto_protos_perfetto_trace_generic_kernel_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -22900,6 +23351,7 @@ cc_binary { "perfetto_protos_perfetto_config_gpu_zero_gen_headers", "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_zero_gen_headers", "perfetto_protos_perfetto_config_process_stats_zero_gen_headers", @@ -22922,6 +23374,7 @@ cc_binary { "perfetto_protos_perfetto_trace_generic_kernel_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -22976,6 +23429,7 @@ cc_binary_host { ":perfetto_protos_perfetto_config_gpu_zero_gen", ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_zero_gen", ":perfetto_protos_perfetto_config_process_stats_zero_gen", @@ -23003,6 +23457,7 @@ cc_binary_host { ":perfetto_protos_perfetto_trace_gpu_gpu_track_event_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -23214,6 +23669,7 @@ cc_binary_host { "perfetto_protos_perfetto_config_gpu_zero_gen_headers", "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_zero_gen_headers", "perfetto_protos_perfetto_config_process_stats_zero_gen_headers", @@ -23241,6 +23697,7 @@ cc_binary_host { "perfetto_protos_perfetto_trace_gpu_gpu_track_event_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -23382,6 +23839,8 @@ cc_binary { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -23418,6 +23877,7 @@ cc_binary { ":perfetto_protos_perfetto_trace_generic_kernel_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -23502,6 +23962,8 @@ cc_binary { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -23538,6 +24000,7 @@ cc_binary { "perfetto_protos_perfetto_trace_generic_kernel_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -23617,6 +24080,8 @@ cc_binary { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -23653,6 +24118,7 @@ cc_binary { ":perfetto_protos_perfetto_trace_generic_kernel_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -23704,6 +24170,8 @@ cc_binary { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -23740,6 +24208,7 @@ cc_binary { "perfetto_protos_perfetto_trace_generic_kernel_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", @@ -23835,6 +24304,8 @@ cc_binary { ":perfetto_protos_perfetto_config_inode_file_zero_gen", ":perfetto_protos_perfetto_config_interceptors_cpp_gen", ":perfetto_protos_perfetto_config_interceptors_zero_gen", + ":perfetto_protos_perfetto_config_linux_cpp_gen", + ":perfetto_protos_perfetto_config_linux_zero_gen", ":perfetto_protos_perfetto_config_power_cpp_gen", ":perfetto_protos_perfetto_config_power_zero_gen", ":perfetto_protos_perfetto_config_priority_boost_cpp_gen", @@ -23871,6 +24342,7 @@ cc_binary { ":perfetto_protos_perfetto_trace_generic_kernel_zero_gen", ":perfetto_protos_perfetto_trace_gpu_zero_gen", ":perfetto_protos_perfetto_trace_interned_data_zero_gen", + ":perfetto_protos_perfetto_trace_linux_zero_gen", ":perfetto_protos_perfetto_trace_minimal_zero_gen", ":perfetto_protos_perfetto_trace_non_minimal_zero_gen", ":perfetto_protos_perfetto_trace_perfetto_zero_gen", @@ -23925,6 +24397,8 @@ cc_binary { "perfetto_protos_perfetto_config_inode_file_zero_gen_headers", "perfetto_protos_perfetto_config_interceptors_cpp_gen_headers", "perfetto_protos_perfetto_config_interceptors_zero_gen_headers", + "perfetto_protos_perfetto_config_linux_cpp_gen_headers", + "perfetto_protos_perfetto_config_linux_zero_gen_headers", "perfetto_protos_perfetto_config_power_cpp_gen_headers", "perfetto_protos_perfetto_config_power_zero_gen_headers", "perfetto_protos_perfetto_config_priority_boost_cpp_gen_headers", @@ -23961,6 +24435,7 @@ cc_binary { "perfetto_protos_perfetto_trace_generic_kernel_zero_gen_headers", "perfetto_protos_perfetto_trace_gpu_zero_gen_headers", "perfetto_protos_perfetto_trace_interned_data_zero_gen_headers", + "perfetto_protos_perfetto_trace_linux_zero_gen_headers", "perfetto_protos_perfetto_trace_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_non_minimal_zero_gen_headers", "perfetto_protos_perfetto_trace_perfetto_zero_gen_headers", diff --git a/BUILD b/BUILD index aa3437a0ab1..e6d40280240 100644 --- a/BUILD +++ b/BUILD @@ -223,6 +223,8 @@ perfetto_cc_library( ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_cpp", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_cpp", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_cpp", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_cpp", @@ -258,6 +260,7 @@ perfetto_cc_library( ":protos_perfetto_trace_generic_kernel_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -309,6 +312,7 @@ perfetto_cc_binary( ":protos_perfetto_config_gpu_cpp", ":protos_perfetto_config_inode_file_cpp", ":protos_perfetto_config_interceptors_cpp", + ":protos_perfetto_config_linux_cpp", ":protos_perfetto_config_power_cpp", ":protos_perfetto_config_priority_boost_cpp", ":protos_perfetto_config_process_stats_cpp", @@ -579,6 +583,7 @@ perfetto_cc_library( ":protos_perfetto_config_gpu_zero", ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_zero", ":protos_perfetto_config_process_stats_zero", @@ -606,6 +611,7 @@ perfetto_cc_library( ":protos_perfetto_trace_gpu_gpu_track_event_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -879,6 +885,7 @@ perfetto_cc_library( ":protos_perfetto_config_gpu_zero", ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_zero", ":protos_perfetto_config_process_stats_zero", @@ -906,6 +913,7 @@ perfetto_cc_library( ":protos_perfetto_trace_gpu_gpu_track_event_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -996,6 +1004,7 @@ perfetto_cc_library( ":protos_perfetto_config_gpu_zero", ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_zero", ":protos_perfetto_config_process_stats_zero", @@ -1018,6 +1027,7 @@ perfetto_cc_library( ":protos_perfetto_trace_generic_kernel_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -1068,6 +1078,8 @@ perfetto_cc_binary( ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_cpp", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_cpp", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_cpp", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_cpp", @@ -1101,6 +1113,7 @@ perfetto_cc_binary( ":protos_perfetto_trace_generic_kernel_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -1158,6 +1171,7 @@ perfetto_cc_library( ":src_traced_probes_ftrace_ftrace", ":src_traced_probes_ftrace_tracefs", ":src_traced_probes_initial_display_state_initial_display_state", + ":src_traced_probes_journald_journald", ":src_traced_probes_metatrace_metatrace", ":src_traced_probes_packages_list_packages_list", ":src_traced_probes_packages_list_packages_list_parser", @@ -1206,6 +1220,8 @@ perfetto_cc_library( ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_cpp", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_cpp", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_cpp", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_cpp", @@ -1241,6 +1257,7 @@ perfetto_cc_library( ":protos_perfetto_trace_generic_kernel_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -2956,6 +2973,10 @@ perfetto_filegroup( "src/trace_processor/importers/proto/heap_graph_tracker.h", "src/trace_processor/importers/proto/jit_tracker.cc", "src/trace_processor/importers/proto/jit_tracker.h", + "src/trace_processor/importers/proto/linux_probes_module.cc", + "src/trace_processor/importers/proto/linux_probes_module.h", + "src/trace_processor/importers/proto/linux_probes_parser.cc", + "src/trace_processor/importers/proto/linux_probes_parser.h", "src/trace_processor/importers/proto/metadata_module.cc", "src/trace_processor/importers/proto/metadata_module.h", "src/trace_processor/importers/proto/pigweed_detokenizer.cc", @@ -3974,6 +3995,7 @@ perfetto_filegroup( "src/trace_processor/perfetto_sql/stdlib/linux/block_io.sql", "src/trace_processor/perfetto_sql/stdlib/linux/devfreq.sql", "src/trace_processor/perfetto_sql/stdlib/linux/irqs.sql", + "src/trace_processor/perfetto_sql/stdlib/linux/journald.sql", "src/trace_processor/perfetto_sql/stdlib/linux/threads.sql", ], ) @@ -5291,6 +5313,7 @@ perfetto_cc_tp_tables( "src/trace_processor/tables/etm_tables.py", "src/trace_processor/tables/flow_tables.py", "src/trace_processor/tables/jit_tables.py", + "src/trace_processor/tables/log_tables.py", "src/trace_processor/tables/memory_tables.py", "src/trace_processor/tables/metadata_tables.py", "src/trace_processor/tables/perf_tables.py", @@ -5314,6 +5337,8 @@ perfetto_cc_tp_tables( "src/trace_processor/tables/flow_tables_py.h", "src/trace_processor/tables/jit_tables_fwd.h", "src/trace_processor/tables/jit_tables_py.h", + "src/trace_processor/tables/log_tables_fwd.h", + "src/trace_processor/tables/log_tables_py.h", "src/trace_processor/tables/memory_tables_fwd.h", "src/trace_processor/tables/memory_tables_py.h", "src/trace_processor/tables/metadata_tables_fwd.h", @@ -5407,6 +5432,7 @@ perfetto_cc_library( ":protos_perfetto_config_gpu_zero", ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_zero", ":protos_perfetto_config_process_stats_zero", @@ -5429,6 +5455,7 @@ perfetto_cc_library( ":protos_perfetto_trace_generic_kernel_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -5482,6 +5509,7 @@ perfetto_cc_library( ":protos_perfetto_config_gpu_zero", ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_zero", ":protos_perfetto_config_process_stats_zero", @@ -5504,6 +5532,7 @@ perfetto_cc_library( ":protos_perfetto_trace_generic_kernel_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -6151,6 +6180,15 @@ perfetto_filegroup( ], ) +# GN target: //src/traced/probes/journald:journald +perfetto_filegroup( + name = "src_traced_probes_journald_journald", + srcs = [ + "src/traced/probes/journald/journald_data_source.cc", + "src/traced/probes/journald/journald_data_source.h", + ], +) + # GN target: //src/traced/probes/metatrace:metatrace perfetto_filegroup( name = "src_traced_probes_metatrace_metatrace", @@ -6693,6 +6731,7 @@ perfetto_proto_library( ":protos_perfetto_config_gpu_protos", ":protos_perfetto_config_inode_file_protos", ":protos_perfetto_config_interceptors_protos", + ":protos_perfetto_config_linux_protos", ":protos_perfetto_config_power_protos", ":protos_perfetto_config_priority_boost_protos", ":protos_perfetto_config_process_stats_protos", @@ -6775,6 +6814,7 @@ perfetto_proto_library( ":protos_perfetto_config_gpu_protos", ":protos_perfetto_config_inode_file_protos", ":protos_perfetto_config_interceptors_protos", + ":protos_perfetto_config_linux_protos", ":protos_perfetto_config_power_protos", ":protos_perfetto_config_priority_boost_protos", ":protos_perfetto_config_process_stats_protos", @@ -6797,6 +6837,7 @@ perfetto_proto_library( ":protos_perfetto_trace_generic_kernel_protos", ":protos_perfetto_trace_gpu_protos", ":protos_perfetto_trace_interned_data_protos", + ":protos_perfetto_trace_linux_protos", ":protos_perfetto_trace_minimal_protos", ":protos_perfetto_trace_non_minimal_protos", ":protos_perfetto_trace_perfetto_protos", @@ -7328,6 +7369,7 @@ perfetto_cc_library( ":protos_perfetto_config_gpu_zero_h", ":protos_perfetto_config_inode_file_zero_h", ":protos_perfetto_config_interceptors_zero_h", + ":protos_perfetto_config_linux_zero_h", ":protos_perfetto_config_power_zero_h", ":protos_perfetto_config_priority_boost_zero_h", ":protos_perfetto_config_process_stats_zero_h", @@ -7350,6 +7392,7 @@ perfetto_cc_library( ":protos_perfetto_trace_generic_kernel_zero_h", ":protos_perfetto_trace_gpu_zero_h", ":protos_perfetto_trace_interned_data_zero_h", + ":protos_perfetto_trace_linux_zero_h", ":protos_perfetto_trace_minimal_zero_h", ":protos_perfetto_trace_non_minimal_zero_h", ":protos_perfetto_trace_perfetto_zero_h", @@ -7371,6 +7414,7 @@ perfetto_cc_library( ":protos_perfetto_config_gpu_zero", ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_zero", ":protos_perfetto_config_process_stats_zero", @@ -7393,6 +7437,7 @@ perfetto_cc_library( ":protos_perfetto_trace_generic_kernel_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -7557,6 +7602,7 @@ perfetto_cc_protocpp_library( ":protos_perfetto_config_gpu_cpp", ":protos_perfetto_config_inode_file_cpp", ":protos_perfetto_config_interceptors_cpp", + ":protos_perfetto_config_linux_cpp", ":protos_perfetto_config_power_cpp", ":protos_perfetto_config_priority_boost_cpp", ":protos_perfetto_config_process_stats_cpp", @@ -7702,6 +7748,33 @@ perfetto_cc_protozero_library( ], ) +# GN target: //protos/perfetto/config/linux:cpp +perfetto_cc_protocpp_library( + name = "protos_perfetto_config_linux_cpp", + deps = [ + ":protos_perfetto_config_linux_protos", + ], +) + +# GN target: //protos/perfetto/config/linux:source_set +perfetto_proto_library( + name = "protos_perfetto_config_linux_protos", + srcs = [ + "protos/perfetto/config/linux/journald_config.proto", + ], + visibility = [ + PERFETTO_CONFIG.proto_library_visibility, + ], +) + +# GN target: //protos/perfetto/config/linux:zero +perfetto_cc_protozero_library( + name = "protos_perfetto_config_linux_zero", + deps = [ + ":protos_perfetto_config_linux_protos", + ], +) + # GN target: //protos/perfetto/config/power:cpp perfetto_cc_protocpp_library( name = "protos_perfetto_config_power_cpp", @@ -7848,6 +7921,7 @@ perfetto_proto_library( ":protos_perfetto_config_gpu_protos", ":protos_perfetto_config_inode_file_protos", ":protos_perfetto_config_interceptors_protos", + ":protos_perfetto_config_linux_protos", ":protos_perfetto_config_power_protos", ":protos_perfetto_config_priority_boost_protos", ":protos_perfetto_config_process_stats_protos", @@ -8052,6 +8126,7 @@ perfetto_cc_protozero_library( ":protos_perfetto_config_gpu_zero", ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_zero", ":protos_perfetto_config_process_stats_zero", @@ -8079,6 +8154,7 @@ perfetto_cc_protocpp_library( ":protos_perfetto_config_gpu_cpp", ":protos_perfetto_config_inode_file_cpp", ":protos_perfetto_config_interceptors_cpp", + ":protos_perfetto_config_linux_cpp", ":protos_perfetto_config_power_cpp", ":protos_perfetto_config_priority_boost_cpp", ":protos_perfetto_config_process_stats_cpp", @@ -8106,6 +8182,7 @@ perfetto_cc_ipc_library( ":protos_perfetto_config_gpu_cpp", ":protos_perfetto_config_inode_file_cpp", ":protos_perfetto_config_interceptors_cpp", + ":protos_perfetto_config_linux_cpp", ":protos_perfetto_config_power_cpp", ":protos_perfetto_config_priority_boost_cpp", ":protos_perfetto_config_process_stats_cpp", @@ -8141,6 +8218,7 @@ perfetto_proto_library( ":protos_perfetto_config_gpu_protos", ":protos_perfetto_config_inode_file_protos", ":protos_perfetto_config_interceptors_protos", + ":protos_perfetto_config_linux_protos", ":protos_perfetto_config_power_protos", ":protos_perfetto_config_priority_boost_protos", ":protos_perfetto_config_process_stats_protos", @@ -9025,6 +9103,25 @@ perfetto_cc_protozero_library( ], ) +# GN target: //protos/perfetto/trace/linux:source_set +perfetto_proto_library( + name = "protos_perfetto_trace_linux_protos", + srcs = [ + "protos/perfetto/trace/linux/journald_event.proto", + ], + visibility = [ + PERFETTO_CONFIG.proto_library_visibility, + ], +) + +# GN target: //protos/perfetto/trace/linux:zero +perfetto_cc_protozero_library( + name = "protos_perfetto_trace_linux_zero", + deps = [ + ":protos_perfetto_trace_linux_protos", + ], +) + # GN target: //protos/perfetto/trace:minimal_source_set perfetto_proto_library( name = "protos_perfetto_trace_minimal_protos", @@ -9044,6 +9141,7 @@ perfetto_proto_library( ":protos_perfetto_config_gpu_protos", ":protos_perfetto_config_inode_file_protos", ":protos_perfetto_config_interceptors_protos", + ":protos_perfetto_config_linux_protos", ":protos_perfetto_config_power_protos", ":protos_perfetto_config_priority_boost_protos", ":protos_perfetto_config_process_stats_protos", @@ -9070,6 +9168,7 @@ perfetto_cc_protozero_library( ":protos_perfetto_config_gpu_zero", ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_zero", ":protos_perfetto_config_process_stats_zero", @@ -9112,6 +9211,7 @@ perfetto_proto_library( ":protos_perfetto_config_gpu_protos", ":protos_perfetto_config_inode_file_protos", ":protos_perfetto_config_interceptors_protos", + ":protos_perfetto_config_linux_protos", ":protos_perfetto_config_power_protos", ":protos_perfetto_config_priority_boost_protos", ":protos_perfetto_config_process_stats_protos", @@ -9134,6 +9234,7 @@ perfetto_proto_library( ":protos_perfetto_trace_generic_kernel_protos", ":protos_perfetto_trace_gpu_protos", ":protos_perfetto_trace_interned_data_protos", + ":protos_perfetto_trace_linux_protos", ":protos_perfetto_trace_minimal_protos", ":protos_perfetto_trace_perfetto_protos", ":protos_perfetto_trace_power_protos", @@ -9161,6 +9262,7 @@ perfetto_cc_protozero_library( ":protos_perfetto_config_gpu_zero", ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_zero", ":protos_perfetto_config_process_stats_zero", @@ -9183,6 +9285,7 @@ perfetto_cc_protozero_library( ":protos_perfetto_trace_generic_kernel_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_protos", ":protos_perfetto_trace_perfetto_zero", @@ -9341,6 +9444,7 @@ perfetto_proto_library( ":protos_perfetto_config_gpu_protos", ":protos_perfetto_config_inode_file_protos", ":protos_perfetto_config_interceptors_protos", + ":protos_perfetto_config_linux_protos", ":protos_perfetto_config_power_protos", ":protos_perfetto_config_priority_boost_protos", ":protos_perfetto_config_process_stats_protos", @@ -9363,6 +9467,7 @@ perfetto_proto_library( ":protos_perfetto_trace_generic_kernel_protos", ":protos_perfetto_trace_gpu_protos", ":protos_perfetto_trace_interned_data_protos", + ":protos_perfetto_trace_linux_protos", ":protos_perfetto_trace_minimal_protos", ":protos_perfetto_trace_non_minimal_protos", ":protos_perfetto_trace_perfetto_protos", @@ -9803,6 +9908,8 @@ perfetto_cc_library( ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_cpp", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_cpp", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_cpp", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_cpp", @@ -9838,6 +9945,7 @@ perfetto_cc_library( ":protos_perfetto_trace_generic_kernel_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -9912,6 +10020,8 @@ perfetto_cc_binary( ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_cpp", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_cpp", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_cpp", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_cpp", @@ -9947,6 +10057,7 @@ perfetto_cc_binary( ":protos_perfetto_trace_generic_kernel_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -10173,6 +10284,7 @@ perfetto_cc_library( ":protos_perfetto_config_gpu_zero", ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_zero", ":protos_perfetto_config_process_stats_zero", @@ -10200,6 +10312,7 @@ perfetto_cc_library( ":protos_perfetto_trace_gpu_gpu_track_event_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", @@ -10480,6 +10593,7 @@ perfetto_cc_binary( ":protos_perfetto_config_gpu_zero", ":protos_perfetto_config_inode_file_zero", ":protos_perfetto_config_interceptors_zero", + ":protos_perfetto_config_linux_zero", ":protos_perfetto_config_power_zero", ":protos_perfetto_config_priority_boost_zero", ":protos_perfetto_config_process_stats_zero", @@ -10507,6 +10621,7 @@ perfetto_cc_binary( ":protos_perfetto_trace_gpu_gpu_track_event_zero", ":protos_perfetto_trace_gpu_zero", ":protos_perfetto_trace_interned_data_zero", + ":protos_perfetto_trace_linux_zero", ":protos_perfetto_trace_minimal_zero", ":protos_perfetto_trace_non_minimal_zero", ":protos_perfetto_trace_perfetto_zero", diff --git a/protos/perfetto/config/BUILD.gn b/protos/perfetto/config/BUILD.gn index aba2b725183..69ec9441337 100644 --- a/protos/perfetto/config/BUILD.gn +++ b/protos/perfetto/config/BUILD.gn @@ -27,6 +27,7 @@ perfetto_proto_library("@TYPE@") { "gpu:@TYPE@", "inode_file:@TYPE@", "interceptors:@TYPE@", + "linux:@TYPE@", "power:@TYPE@", "priority_boost:@TYPE@", "process_stats:@TYPE@", diff --git a/protos/perfetto/config/data_source_config.proto b/protos/perfetto/config/data_source_config.proto index 0dceb7cb88f..b278ce2e378 100644 --- a/protos/perfetto/config/data_source_config.proto +++ b/protos/perfetto/config/data_source_config.proto @@ -61,6 +61,7 @@ import "protos/perfetto/config/test_config.proto"; import "protos/perfetto/config/track_event/track_event_config.proto"; import "protos/perfetto/config/system_info/system_info_config.proto"; import "protos/perfetto/config/chrome/histogram_samples.proto"; +import "protos/perfetto/config/linux/journald_config.proto"; import "protos/perfetto/config/qnx/qnx_config.proto"; // The configuration that is passed to each data source when starting tracing. @@ -305,6 +306,9 @@ message DataSourceConfig { // Data source name: android.aflags optional AndroidAflagsConfig android_aflags_config = 140 [lazy = true]; + // Data source name: linux.systemd_journald + optional SystemdJournaldConfig journald_config = 141 [lazy = true]; + // Data source name: qnx.kernel optional QnxConfig qnx_config = 150 [lazy = true]; diff --git a/protos/perfetto/config/linux/BUILD.gn b/protos/perfetto/config/linux/BUILD.gn new file mode 100644 index 00000000000..7a23f79036d --- /dev/null +++ b/protos/perfetto/config/linux/BUILD.gn @@ -0,0 +1,19 @@ +# Copyright (C) 2026 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/proto_library.gni") + +perfetto_proto_library("@TYPE@") { + sources = [ "journald_config.proto" ] +} diff --git a/protos/perfetto/config/linux/journald_config.proto b/protos/perfetto/config/linux/journald_config.proto new file mode 100644 index 00000000000..cfa4976da6a --- /dev/null +++ b/protos/perfetto/config/linux/journald_config.proto @@ -0,0 +1,33 @@ +// Copyright (C) 2026 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"; + +package perfetto.protos; + +// Configuration for the "linux.systemd_journald" data source. +// Next field id: 4 +message SystemdJournaldConfig { + // Minimum syslog priority level to capture (inclusive). + // 0=EMERG, 1=ALERT, 2=CRIT, 3=ERR, 4=WARNING, 5=NOTICE, 6=INFO, 7=DEBUG. + // Default (0 / unset): capture all priorities (equivalent to 7). + optional uint32 min_prio = 1; + + // If non-empty, only capture journal entries whose SYSLOG_IDENTIFIER + // matches one of these strings. + repeated string filter_identifiers = 2; + + // If non-empty, only capture journal entries from these systemd unit names. + repeated string filter_units = 3; +} diff --git a/protos/perfetto/trace/BUILD.gn b/protos/perfetto/trace/BUILD.gn index ce961d8b5f5..7ed9e988409 100644 --- a/protos/perfetto/trace/BUILD.gn +++ b/protos/perfetto/trace/BUILD.gn @@ -91,6 +91,7 @@ perfetto_proto_library("non_minimal_@TYPE@") { "generic_kernel:@TYPE@", "gpu:@TYPE@", "interned_data:@TYPE@", + "linux:@TYPE@", "perfetto:@TYPE@", "power:@TYPE@", "profiling:@TYPE@", diff --git a/protos/perfetto/trace/linux/BUILD.gn b/protos/perfetto/trace/linux/BUILD.gn new file mode 100644 index 00000000000..96957401767 --- /dev/null +++ b/protos/perfetto/trace/linux/BUILD.gn @@ -0,0 +1,19 @@ +# Copyright (C) 2026 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/proto_library.gni") + +perfetto_proto_library("@TYPE@") { + sources = [ "journald_event.proto" ] +} diff --git a/protos/perfetto/trace/linux/journald_event.proto b/protos/perfetto/trace/linux/journald_event.proto new file mode 100644 index 00000000000..737bfa957b8 --- /dev/null +++ b/protos/perfetto/trace/linux/journald_event.proto @@ -0,0 +1,66 @@ +// Copyright (C) 2026 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"; + +package perfetto.protos; + +// A journald log event packet, collected from the Linux systemd journal. +// Emitted by the "linux.systemd_journald" data source in traced_probes. +// Next field id: 13, statistics starting at 20 +message SystemdJournaldEvent { + // _PID: PID of the process that wrote the log entry (trusted). + optional uint32 pid = 1; + + // _TID: thread ID of the logging thread (trusted, set by journald for + // native journal protocol entries). Absent for syslog, stdout-piped, and + // kernel entries. + optional uint32 tid = 2; + + // _UID: user ID of the process (trusted). + optional uint32 uid = 3; + + // _GID: group ID of the process (trusted). + optional uint32 gid = 4; + + // PRIORITY: syslog priority level 0 (EMERG) .. 7 (DEBUG). + optional uint32 prio = 5; + + // SYSLOG_IDENTIFIER: program name / tag, set by the logging application. + optional string tag = 6; + + // MESSAGE: the human-readable log message text. + optional string message = 7; + + // _COMM: short process name as known to the kernel (trusted). + optional string comm = 8; + + // _EXE: full executable path (trusted). + optional string exe = 9; + + // _SYSTEMD_UNIT: the systemd unit that owns this process (trusted). + optional string systemd_unit = 10; + + // _HOSTNAME: hostname of the machine (trusted). + optional string hostname = 11; + + // _TRANSPORT: how the entry was received by journald (trusted). + // Values: "audit", "driver", "syslog", "journal", "stdout", "kernel". + optional string transport = 12; + + // These statistics emitted once on Flush() + optional uint64 num_total = 20; + optional uint64 num_failed = 21; + optional uint64 num_skipped = 22; +} diff --git a/protos/perfetto/trace/trace_packet.proto b/protos/perfetto/trace/trace_packet.proto index 3b463842e5c..d300afa334e 100644 --- a/protos/perfetto/trace/trace_packet.proto +++ b/protos/perfetto/trace/trace_packet.proto @@ -50,6 +50,7 @@ import "protos/perfetto/trace/chrome/chrome_trace_event.proto"; import "protos/perfetto/trace/chrome/chrome_trigger.proto"; import "protos/perfetto/trace/chrome/v8.proto"; import "protos/perfetto/trace/clock_snapshot.proto"; +import "protos/perfetto/trace/linux/journald_event.proto"; import "protos/perfetto/trace/etw/etw_event_bundle.proto"; import "protos/perfetto/trace/evdev.proto"; import "protos/perfetto/trace/filesystem/inode_file_map.proto"; @@ -120,7 +121,7 @@ package perfetto.protos; // See the [Buffers and Dataflow](/docs/concepts/buffers.md) doc for details. // // Next reserved id: 14 (up to 15). -// Next id: 132. +// Next id: 133. message TracePacket { // Encapsulates the state and configuration of the ProtoVM instances running // when the trace was snapshotted. This allows TP to re-instantiate the VMs @@ -313,6 +314,8 @@ message TracePacket { AndroidUserList user_list = 123; + SystemdJournaldEvent journald_event = 132; + // This field is only used for testing. // In previous versions of this proto this field had the id 268435455 // This caused many problems: diff --git a/src/trace_processor/importers/android_bugreport/android_log_event_parser.cc b/src/trace_processor/importers/android_bugreport/android_log_event_parser.cc index 1ed7cba9c9c..b4b2a5d64fb 100644 --- a/src/trace_processor/importers/android_bugreport/android_log_event_parser.cc +++ b/src/trace_processor/importers/android_bugreport/android_log_event_parser.cc @@ -17,11 +17,13 @@ #include "src/trace_processor/importers/android_bugreport/android_log_event_parser.h" #include +#include #include #include "src/trace_processor/importers/android_bugreport/android_log_event.h" #include "src/trace_processor/importers/common/process_tracker.h" -#include "src/trace_processor/tables/android_tables_py.h" +#include "src/trace_processor/storage/trace_storage.h" +#include "src/trace_processor/tables/log_tables_py.h" #include "src/trace_processor/types/trace_processor_context.h" namespace perfetto::trace_processor { @@ -29,13 +31,15 @@ namespace perfetto::trace_processor { AndroidLogEventParser::~AndroidLogEventParser() = default; void AndroidLogEventParser::Parse(int64_t ts, AndroidLogEvent event) { - tables::AndroidLogTable::Row row; + tables::LogTable::Row row; row.ts = ts; - row.utid = context_->process_tracker->UpdateThread(event.tid, event.pid); + row.utid = std::make_optional( + context_->process_tracker->UpdateThread(event.tid, event.pid)); row.prio = event.prio; - row.tag = event.tag; + row.log_source = context_->storage->InternString("android_logcat"); + row.tag = std::make_optional(event.tag); row.msg = event.msg; - context_->storage->mutable_android_log_table()->Insert(std::move(row)); + context_->storage->mutable_log_table()->Insert(std::move(row)); } } // namespace perfetto::trace_processor diff --git a/src/trace_processor/importers/common/args_tracker.h b/src/trace_processor/importers/common/args_tracker.h index bee19e05ae7..bd091bdba3c 100644 --- a/src/trace_processor/importers/common/args_tracker.h +++ b/src/trace_processor/importers/common/args_tracker.h @@ -30,6 +30,7 @@ #include "src/trace_processor/tables/android_tables_py.h" #include "src/trace_processor/tables/counter_tables_py.h" #include "src/trace_processor/tables/flow_tables_py.h" +#include "src/trace_processor/tables/log_tables_py.h" #include "src/trace_processor/tables/memory_tables_py.h" #include "src/trace_processor/tables/metadata_tables_py.h" #include "src/trace_processor/tables/profiler_tables_py.h" @@ -266,6 +267,10 @@ class ArgsTracker { return AddArgsTo(context_->storage->mutable_trace_import_logs_table(), id); } + BoundInserter AddArgsTo(tables::LogTable::Id id) { + return AddArgsTo(context_->storage->mutable_log_table(), id); + } + // Returns a CompactArgSet which contains the args inserted into this // ArgsTracker. Requires that every arg in this tracker was inserted for the // "arg_set_id" column given by |column| at the given |row_number|. diff --git a/src/trace_processor/importers/proto/BUILD.gn b/src/trace_processor/importers/proto/BUILD.gn index 47c0bf2e6d2..02a1839ded0 100644 --- a/src/trace_processor/importers/proto/BUILD.gn +++ b/src/trace_processor/importers/proto/BUILD.gn @@ -166,6 +166,10 @@ source_set("full") { "heap_graph_tracker.h", "jit_tracker.cc", "jit_tracker.h", + "linux_probes_module.cc", + "linux_probes_module.h", + "linux_probes_parser.cc", + "linux_probes_parser.h", "metadata_module.cc", "metadata_module.h", "pigweed_detokenizer.cc", @@ -211,6 +215,7 @@ source_set("full") { "../../../../protos/perfetto/trace/gpu:gpu_interned_data_zero", "../../../../protos/perfetto/trace/gpu:zero", "../../../../protos/perfetto/trace/interned_data:zero", + "../../../../protos/perfetto/trace/linux:zero", "../../../../protos/perfetto/trace/power:zero", "../../../../protos/perfetto/trace/profiling:zero", "../../../../protos/perfetto/trace/ps:zero", diff --git a/src/trace_processor/importers/proto/additional_modules.cc b/src/trace_processor/importers/proto/additional_modules.cc index de66bf3a69d..257ec568cd7 100644 --- a/src/trace_processor/importers/proto/additional_modules.cc +++ b/src/trace_processor/importers/proto/additional_modules.cc @@ -32,6 +32,7 @@ #include "src/trace_processor/importers/proto/deobfuscation_module.h" #include "src/trace_processor/importers/proto/graphics_event_module.h" #include "src/trace_processor/importers/proto/heap_graph_module.h" +#include "src/trace_processor/importers/proto/linux_probes_module.h" #include "src/trace_processor/importers/proto/metadata_module.h" #include "src/trace_processor/importers/proto/network_trace_module.h" #include "src/trace_processor/importers/proto/pixel_modem_module.h" @@ -57,6 +58,8 @@ void RegisterAdditionalModules(ProtoImporterModuleContext* module_context, new AndroidKernelWakelocksModule(module_context, context)); module_context->modules.emplace_back( new AndroidProbesModule(module_context, context)); + module_context->modules.emplace_back( + new LinuxProbesModule(module_context, context)); module_context->modules.emplace_back( new NetworkTraceModule(module_context, context)); module_context->modules.emplace_back( diff --git a/src/trace_processor/importers/proto/android_probes_parser.cc b/src/trace_processor/importers/proto/android_probes_parser.cc index ba59a868519..3fee7662900 100644 --- a/src/trace_processor/importers/proto/android_probes_parser.cc +++ b/src/trace_processor/importers/proto/android_probes_parser.cc @@ -44,6 +44,8 @@ #include "src/trace_processor/storage/metadata.h" #include "src/trace_processor/storage/stats.h" #include "src/trace_processor/storage/trace_storage.h" +#include "src/trace_processor/tables/android_tables_py.h" +#include "src/trace_processor/tables/log_tables_py.h" #include "src/trace_processor/types/trace_processor_context.h" #include "src/trace_processor/types/variadic.h" @@ -477,8 +479,14 @@ void AndroidProbesParser::ParseAndroidLogEvent(int64_t ts, // Log events are NOT required to be sorted by trace_time. The virtual table // will take care of sorting on-demand. - context_->storage->mutable_android_log_table()->Insert( - {ts, utid, prio, tag_id, msg_id}); + tables::LogTable::Row row; + row.ts = ts; + row.utid = tid ? std::make_optional(utid) : std::nullopt; + row.prio = static_cast(prio); + row.log_source = context_->storage->InternString("android_logcat"); + row.tag = evt.has_tag() ? std::make_optional(tag_id) : std::nullopt; + row.msg = msg_id; + context_->storage->mutable_log_table()->Insert(row); } void AndroidProbesParser::ParseAndroidLogStats(protozero::ConstBytes blob) { diff --git a/src/trace_processor/importers/proto/linux_probes_module.cc b/src/trace_processor/importers/proto/linux_probes_module.cc new file mode 100644 index 00000000000..6f6be78b1df --- /dev/null +++ b/src/trace_processor/importers/proto/linux_probes_module.cc @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "src/trace_processor/importers/proto/linux_probes_module.h" + +#include +#include + +#include "perfetto/base/logging.h" +#include "perfetto/protozero/field.h" +#include "perfetto/trace_processor/ref_counted.h" +#include "protos/perfetto/common/builtin_clock.pbzero.h" +#include "protos/perfetto/trace/linux/journald_event.pbzero.h" +#include "protos/perfetto/trace/trace_packet.pbzero.h" +#include "src/trace_processor/importers/common/clock_tracker.h" +#include "src/trace_processor/importers/common/parser_types.h" +#include "src/trace_processor/importers/proto/blob_packet_writer.h" +#include "src/trace_processor/importers/proto/packet_sequence_state_generation.h" +#include "src/trace_processor/importers/proto/proto_importer_module.h" +#include "src/trace_processor/sorter/trace_sorter.h" +#include "src/trace_processor/types/trace_processor_context.h" + +namespace perfetto::trace_processor { + +using perfetto::protos::pbzero::TracePacket; + +LinuxProbesModule::LinuxProbesModule(ProtoImporterModuleContext* module_context, + TraceProcessorContext* context) + : ProtoImporterModule(module_context), parser_(context) { + RegisterForField(TracePacket::kJournaldEventFieldNumber); +} + +void LinuxProbesModule::ParseTracePacketData( + const protos::pbzero::TracePacket_Decoder& decoder, + int64_t ts, + const TracePacketData&, + uint32_t field_id) { + switch (field_id) { + case TracePacket::kJournaldEventFieldNumber: + parser_.ParseSystemdJournaldEvent(ts, decoder.journald_event()); + return; + default: + PERFETTO_FATAL("Unexpected field_id in LinuxProbesModule"); + } +} + +} // namespace perfetto::trace_processor diff --git a/src/trace_processor/importers/proto/linux_probes_module.h b/src/trace_processor/importers/proto/linux_probes_module.h new file mode 100644 index 00000000000..e8206a06ff9 --- /dev/null +++ b/src/trace_processor/importers/proto/linux_probes_module.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_LINUX_PROBES_MODULE_H_ +#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_LINUX_PROBES_MODULE_H_ + +#include "src/trace_processor/importers/proto/linux_probes_parser.h" +#include "src/trace_processor/importers/proto/proto_importer_module.h" + +namespace perfetto::trace_processor { + +class TraceProcessorContext; +class LinuxProbesModule : public ProtoImporterModule { + public: + explicit LinuxProbesModule(ProtoImporterModuleContext* module_context, + TraceProcessorContext* context); + + void ParseTracePacketData(const protos::pbzero::TracePacket_Decoder& decoder, + int64_t ts, + const TracePacketData&, + uint32_t field_id) override; + + private: + LinuxProbesParser parser_; +}; + +} // namespace perfetto::trace_processor + +#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_LINUX_PROBES_MODULE_H_ diff --git a/src/trace_processor/importers/proto/linux_probes_parser.cc b/src/trace_processor/importers/proto/linux_probes_parser.cc new file mode 100644 index 00000000000..0895c174cf9 --- /dev/null +++ b/src/trace_processor/importers/proto/linux_probes_parser.cc @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "src/trace_processor/importers/proto/linux_probes_parser.h" + +#include +#include + +#include "perfetto/ext/base/string_view.h" +#include "protos/perfetto/trace/linux/journald_event.pbzero.h" +#include "src/trace_processor/importers/common/args_tracker.h" +#include "src/trace_processor/importers/common/process_tracker.h" +#include "src/trace_processor/storage/trace_storage.h" +#include "src/trace_processor/tables/log_tables_py.h" +#include "src/trace_processor/types/trace_processor_context.h" +#include "src/trace_processor/types/variadic.h" + +namespace perfetto::trace_processor { + +LinuxProbesParser::LinuxProbesParser(TraceProcessorContext* context) + : context_(context), + uid_key_id_(context->storage->InternString("uid")), + comm_key_id_(context->storage->InternString("comm")), + systemd_unit_key_id_(context->storage->InternString("systemd_unit")), + hostname_key_id_(context->storage->InternString("hostname")), + transport_key_id_(context->storage->InternString("transport")), + journald_source_id_(context->storage->InternString("journald")) {} + +void LinuxProbesParser::ParseSystemdJournaldEvent(int64_t ts, + protozero::ConstBytes blob) { + protos::pbzero::SystemdJournaldEvent::Decoder evt(blob); + + auto pid = evt.has_pid() ? static_cast(evt.pid()) : 0u; + auto tid = evt.has_tid() ? static_cast(evt.tid()) : pid; + + std::optional utid; + if (pid != 0) { + utid = context_->process_tracker->UpdateThread(tid, pid); + } + + auto prio = static_cast(evt.has_prio() ? evt.prio() : 0u); + + StringId tag_id = evt.has_tag() ? context_->storage->InternString(evt.tag()) + : kNullStringId; + StringId msg_id = context_->storage->InternString( + evt.has_message() ? evt.message() : base::StringView()); + + std::optional uid; + if (evt.has_uid()) + uid = static_cast(evt.uid()); + + StringId comm_id = evt.has_comm() + ? context_->storage->InternString(evt.comm()) + : kNullStringId; + StringId unit_id = evt.has_systemd_unit() + ? context_->storage->InternString(evt.systemd_unit()) + : kNullStringId; + StringId host_id = evt.has_hostname() + ? context_->storage->InternString(evt.hostname()) + : kNullStringId; + StringId transport_id = evt.has_transport() + ? context_->storage->InternString(evt.transport()) + : kNullStringId; + + tables::LogTable::Row row; + row.ts = ts; + row.utid = utid; + row.prio = prio; + row.log_source = journald_source_id_; + row.tag = evt.has_tag() ? std::make_optional(tag_id) : std::nullopt; + row.msg = msg_id; + auto id = context_->storage->mutable_log_table()->Insert(row).id; + + if (uid || evt.has_comm() || evt.has_systemd_unit() || evt.has_hostname() || + evt.has_transport()) { + ArgsTracker args_tracker(context_); + auto inserter = args_tracker.AddArgsTo(id); + if (uid) { + inserter.AddArg(uid_key_id_, uid_key_id_, + Variadic::Integer(static_cast(*uid))); + } + if (evt.has_comm()) { + inserter.AddArg(comm_key_id_, comm_key_id_, Variadic::String(comm_id)); + } + if (evt.has_systemd_unit()) { + inserter.AddArg(systemd_unit_key_id_, systemd_unit_key_id_, + Variadic::String(unit_id)); + } + if (evt.has_hostname()) { + inserter.AddArg(hostname_key_id_, hostname_key_id_, + Variadic::String(host_id)); + } + if (evt.has_transport()) { + inserter.AddArg(transport_key_id_, transport_key_id_, + Variadic::String(transport_id)); + } + } +} + +} // namespace perfetto::trace_processor diff --git a/src/trace_processor/importers/proto/linux_probes_parser.h b/src/trace_processor/importers/proto/linux_probes_parser.h new file mode 100644 index 00000000000..12e47b05c01 --- /dev/null +++ b/src/trace_processor/importers/proto/linux_probes_parser.h @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_LINUX_PROBES_PARSER_H_ +#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_LINUX_PROBES_PARSER_H_ + +#include "perfetto/protozero/field.h" +#include "src/trace_processor/containers/string_pool.h" + +namespace perfetto::trace_processor { + +class TraceProcessorContext; + +class LinuxProbesParser { + public: + explicit LinuxProbesParser(TraceProcessorContext* context); + + void ParseSystemdJournaldEvent(int64_t ts, protozero::ConstBytes blob); + + private: + TraceProcessorContext* context_; + + // Interned string ids for arg keys used when writing journald-specific + // metadata into the args table. + using StringId = StringPool::Id; + StringId uid_key_id_; + StringId comm_key_id_; + StringId systemd_unit_key_id_; + StringId hostname_key_id_; + StringId transport_key_id_; + StringId journald_source_id_; +}; + +} // namespace perfetto::trace_processor + +#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_LINUX_PROBES_PARSER_H_ diff --git a/src/trace_processor/importers/proto/proto_trace_parser_impl_unittest.cc b/src/trace_processor/importers/proto/proto_trace_parser_impl_unittest.cc index 99458071c85..771cccd6479 100644 --- a/src/trace_processor/importers/proto/proto_trace_parser_impl_unittest.cc +++ b/src/trace_processor/importers/proto/proto_trace_parser_impl_unittest.cc @@ -2326,10 +2326,11 @@ TEST_F(ProtoTraceParserTest, TrackEventWithLogMessage) { EXPECT_EQ(rr_0->ts(), 1010000); EXPECT_EQ(rr_0->track_id(), track); - EXPECT_GT(context_.storage->android_log_table().row_count(), 0u); - EXPECT_EQ(context_.storage->android_log_table()[0].ts(), 1010000); - EXPECT_EQ(context_.storage->android_log_table()[0].msg(), body_1); - EXPECT_EQ(context_.storage->android_log_table()[0].tag(), source_location_id); + EXPECT_GT(context_.storage->log_table().row_count(), 0u); + EXPECT_EQ(context_.storage->log_table()[0].ts(), 1010000); + EXPECT_EQ(context_.storage->log_table()[0].msg(), body_1); + EXPECT_EQ(context_.storage->log_table()[0].tag(), + std::make_optional(source_location_id)); } TEST_F(ProtoTraceParserTest, TrackEventParseLegacyEventIntoRawTable) { diff --git a/src/trace_processor/importers/proto/track_event_event_importer.h b/src/trace_processor/importers/proto/track_event_event_importer.h index de82e6ed3f8..3aa4f270e07 100644 --- a/src/trace_processor/importers/proto/track_event_event_importer.h +++ b/src/trace_processor/importers/proto/track_event_event_importer.h @@ -64,6 +64,8 @@ #include "src/trace_processor/importers/proto/track_event_tracker.h" #include "src/trace_processor/storage/stats.h" #include "src/trace_processor/storage/trace_storage.h" +#include "src/trace_processor/tables/android_tables_py.h" +#include "src/trace_processor/tables/log_tables_py.h" #include "src/trace_processor/tables/metadata_tables_py.h" #include "src/trace_processor/tables/slice_tables_py.h" #include "src/trace_processor/types/variadic.h" @@ -1492,10 +1494,18 @@ class TrackEventEventImporter { Variadic::Integer(priority)); } - storage_->mutable_android_log_table()->Insert( - {ts_, *utid_, - /*priority*/ static_cast(priority), - /*tag_id*/ source_location_id, log_message_id}); + { + tables::LogTable::Row log_row; + log_row.ts = ts_; + log_row.utid = utid_; + log_row.prio = static_cast(priority); + log_row.log_source = storage_->InternString("android_logcat"); + log_row.tag = source_location_id != kNullStringId + ? std::make_optional(source_location_id) + : std::nullopt; + log_row.msg = log_message_id; + storage_->mutable_log_table()->Insert(log_row); + } return base::OkStatus(); } diff --git a/src/trace_processor/perfetto_sql/stdlib/linux/BUILD.gn b/src/trace_processor/perfetto_sql/stdlib/linux/BUILD.gn index 49d9324ac59..207af2f82d6 100644 --- a/src/trace_processor/perfetto_sql/stdlib/linux/BUILD.gn +++ b/src/trace_processor/perfetto_sql/stdlib/linux/BUILD.gn @@ -19,6 +19,7 @@ perfetto_sql_source_set("linux") { "block_io.sql", "devfreq.sql", "irqs.sql", + "journald.sql", "threads.sql", ] deps = [ diff --git a/src/trace_processor/perfetto_sql/stdlib/linux/journald.sql b/src/trace_processor/perfetto_sql/stdlib/linux/journald.sql new file mode 100644 index 00000000000..6416e5d155c --- /dev/null +++ b/src/trace_processor/perfetto_sql/stdlib/linux/journald.sql @@ -0,0 +1,58 @@ +-- +-- Copyright 2026 The Android Open Source Project +-- +-- Licensed under the Apache License, Version 2.0 (the "License"); +-- you may not use this file except in compliance with the License. +-- You may obtain a copy of the License at +-- +-- https://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. + +-- Journald log entries from the linux.journald data source. +-- +-- NOTE: this table is not sorted by timestamp. +CREATE PERFETTO VIEW linux_journald_logs( + -- Which row in the table the log corresponds to. + id ID, + -- Timestamp in nanoseconds. + ts TIMESTAMP, + -- Thread id in the trace (nullable). + utid JOINID(thread.id), + -- Syslog priority (0=EMERG, 7=DEBUG). + prio LONG, + -- SYSLOG_IDENTIFIER (program name / tag, nullable). + tag STRING, + -- Log message text. + msg STRING, + -- User ID (nullable). + uid LONG, + -- Process comm name (nullable). + comm STRING, + -- Systemd unit name (nullable). + systemd_unit STRING, + -- Hostname (nullable). + hostname STRING, + -- Transport method (nullable). + transport STRING +) +AS +SELECT + l.id, + l.ts, + l.utid, + l.prio, + l.tag, + l.msg, + CAST(extract_arg(l.arg_set_id, 'uid') AS INTEGER) AS uid, + extract_arg(l.arg_set_id, 'comm') AS comm, + extract_arg(l.arg_set_id, 'systemd_unit') AS systemd_unit, + extract_arg(l.arg_set_id, 'hostname') AS hostname, + extract_arg(l.arg_set_id, 'transport') AS transport +FROM logs AS l +WHERE + l.log_source = 'journald'; diff --git a/src/trace_processor/perfetto_sql/stdlib/prelude/after_eof/views.sql b/src/trace_processor/perfetto_sql/stdlib/prelude/after_eof/views.sql index 0e4dfc45731..f15fe6e10dc 100644 --- a/src/trace_processor/perfetto_sql/stdlib/prelude/after_eof/views.sql +++ b/src/trace_processor/perfetto_sql/stdlib/prelude/after_eof/views.sql @@ -261,25 +261,53 @@ CREATE PERFETTO VIEW perf_session( AS SELECT *, id AS perf_session_id FROM __intrinsic_perf_session; --- Log entries from Android logcat. +-- Log entries from all sources (Android logcat, journald, etc.). +-- +-- NOTE: this table is not sorted by timestamp. +CREATE PERFETTO VIEW logs( + -- Which row in the table the log corresponds to. + id ID, + -- Timestamp of the log entry. + ts TIMESTAMP, + -- Thread writing the log entry (nullable). + utid JOINID(thread.id), + -- Priority. Android: 3=DEBUG..6=ERROR. Journald/syslog: 0=EMERG..7=DEBUG. + prio LONG, + -- Source of the log entry: 'android_logcat' or 'journald'. + log_source STRING, + -- Tag / SYSLOG_IDENTIFIER of the log entry. + tag STRING, + -- Content of the log entry. + msg STRING, + -- Args for source-specific metadata. + arg_set_id ARGSETID +) +AS +SELECT id, ts, utid, prio, log_source, tag, msg, arg_set_id +FROM __intrinsic_logs; + +-- Android log entries from logcat or bugreport. -- -- NOTE: this table is not sorted by timestamp. CREATE PERFETTO VIEW android_logs( -- Which row in the table the log corresponds to. id ID, - -- Timestamp of log entry. + -- Timestamp in nanoseconds. ts TIMESTAMP, - -- Thread writing the log entry. + -- Thread id in the trace (nullable). utid JOINID(thread.id), - -- Priority of the log. 3=DEBUG, 4=INFO, 5=WARN, 6=ERROR. + -- Android log priority (3=DEBUG, 4=INFO, 5=WARN, 6=ERROR, 7=FATAL). prio LONG, - -- Tag of the log entry. + -- Log tag. tag STRING, - -- Content of the log entry + -- Log message text. msg STRING ) AS -SELECT id, ts, utid, prio, tag, msg FROM __intrinsic_android_logs; +SELECT id, ts, utid, prio, tag, msg +FROM __intrinsic_logs +WHERE + log_source = 'android_logcat'; -- Contains flow events linking slices. CREATE PERFETTO VIEW flow( diff --git a/src/trace_processor/plugins/storage_tables/storage_tables.cc b/src/trace_processor/plugins/storage_tables/storage_tables.cc index f0919c17e66..7893b59a852 100644 --- a/src/trace_processor/plugins/storage_tables/storage_tables.cc +++ b/src/trace_processor/plugins/storage_tables/storage_tables.cc @@ -32,6 +32,7 @@ #include "src/trace_processor/tables/etm_tables_py.h" // IWYU pragma: keep #include "src/trace_processor/tables/flow_tables_py.h" // IWYU pragma: keep #include "src/trace_processor/tables/jit_tables_py.h" // IWYU pragma: keep +#include "src/trace_processor/tables/log_tables_py.h" // IWYU pragma: keep #include "src/trace_processor/tables/memory_tables_py.h" // IWYU pragma: keep #include "src/trace_processor/tables/metadata_tables_py.h" #include "src/trace_processor/tables/perf_tables_py.h" // IWYU pragma: keep @@ -98,7 +99,7 @@ class StorageTablesPlugin : public Plugin { AddDataframe(out, s->mutable_android_cpu_per_uid_track_table()); AddDataframe(out, s->mutable_android_dumpstate_table()); AddDataframe(out, s->mutable_android_game_intervenion_list_table()); - AddDataframe(out, s->mutable_android_log_table()); + AddDataframe(out, s->mutable_log_table()); AddDataframe(out, s->mutable_build_flags_table()); AddDataframe(out, s->mutable_modules_table()); AddDataframe(out, s->mutable_clock_snapshot_table()); @@ -209,8 +210,7 @@ class StorageTablesPlugin : public Plugin { s.sched_slice_table().mutations() + s.counter_table().mutations() + s.slice_table().mutations() + s.heap_profile_allocation_table().mutations() + - s.thread_state_table().mutations() + - s.android_log_table().mutations() + + s.thread_state_table().mutations() + s.log_table().mutations() + s.heap_graph_object_table().mutations() + s.perf_sample_table().mutations() + s.instruments_sample_table().mutations() + @@ -245,7 +245,7 @@ class StorageTablesPlugin : public Plugin { start_ns = std::min(it.ts(), start_ns); end_ns = std::max(it.ts() + it.dur(), end_ns); } - for (auto it = s.android_log_table().IterateRows(); it; ++it) { + for (auto it = s.log_table().IterateRows(); it; ++it) { start_ns = std::min(it.ts(), start_ns); end_ns = std::max(it.ts(), end_ns); } diff --git a/src/trace_processor/storage/trace_storage.cc b/src/trace_processor/storage/trace_storage.cc index efdb4102b6c..7d1e7dcf79f 100644 --- a/src/trace_processor/storage/trace_storage.cc +++ b/src/trace_processor/storage/trace_storage.cc @@ -33,6 +33,7 @@ #include "src/trace_processor/tables/etm_tables_py.h" // IWYU pragma: keep #include "src/trace_processor/tables/flow_tables_py.h" // IWYU pragma: keep #include "src/trace_processor/tables/jit_tables_py.h" // IWYU pragma: keep +#include "src/trace_processor/tables/log_tables_py.h" // IWYU pragma: keep #include "src/trace_processor/tables/memory_tables_py.h" // IWYU pragma: keep #include "src/trace_processor/tables/metadata_tables_py.h" // IWYU pragma: keep #include "src/trace_processor/tables/perf_tables_py.h" // IWYU pragma: keep diff --git a/src/trace_processor/storage/trace_storage.h b/src/trace_processor/storage/trace_storage.h index 036587935f8..9db889446c8 100644 --- a/src/trace_processor/storage/trace_storage.h +++ b/src/trace_processor/storage/trace_storage.h @@ -340,11 +340,11 @@ class TraceStorage { return mutable_table(); } - const tables::AndroidLogTable& android_log_table() const { - return table(); + const tables::LogTable& log_table() const { + return table(); } - tables::AndroidLogTable* mutable_android_log_table() { - return mutable_table(); + tables::LogTable* mutable_log_table() { + return mutable_table(); } const tables::AndroidDumpstateTable& android_dumpstate_table() const { diff --git a/src/trace_processor/tables/BUILD.gn b/src/trace_processor/tables/BUILD.gn index 39db846e43a..50c863bbbd5 100644 --- a/src/trace_processor/tables/BUILD.gn +++ b/src/trace_processor/tables/BUILD.gn @@ -31,6 +31,7 @@ perfetto_tp_tables("tables_python") { "etm_tables.py", "flow_tables.py", "jit_tables.py", + "log_tables.py", "memory_tables.py", "metadata_tables.py", "perf_tables.py", diff --git a/src/trace_processor/tables/android_tables.py b/src/trace_processor/tables/android_tables.py index 4e4ab331df8..478d4ebe989 100644 --- a/src/trace_processor/tables/android_tables.py +++ b/src/trace_processor/tables/android_tables.py @@ -29,38 +29,9 @@ from python.generators.trace_processor_table.public import TableDoc from python.generators.trace_processor_table.public import WrappingSqlView -from src.trace_processor.tables.metadata_tables import THREAD_TABLE +from src.trace_processor.tables.log_tables import LOG_TABLE from src.trace_processor.tables.track_tables import TRACK_TABLE -ANDROID_LOG_TABLE = Table( - python_module=__file__, - class_name="AndroidLogTable", - sql_name="__intrinsic_android_logs", - columns=[ - C("ts", CppInt64(), cpp_access=CppAccess.READ), - C("utid", CppTableId(THREAD_TABLE), cpp_access=CppAccess.READ), - C("prio", CppUint32(), cpp_access=CppAccess.READ), - C("tag", CppOptional(CppString()), cpp_access=CppAccess.READ), - C("msg", CppString(), cpp_access=CppAccess.READ), - ], - tabledoc=TableDoc( - doc=''' - Log entries from Android logcat. - - NOTE: this table is not sorted by timestamp. This is why we omit the - sorted flag on the ts column. - ''', - group='Android', - columns={ - 'ts': 'Timestamp of log entry.', - 'utid': 'Thread writing the log entry.', - 'prio': 'Priority of the log. 3=DEBUG, 4=INFO, 5=WARN, 6=ERROR.', - 'tag': 'Tag of the log entry.', - 'msg': 'Content of the log entry.' - }, - ), -) - ANDROID_CPU_PER_UID_TRACK_TABLE = Table( python_module=__file__, class_name="AndroidCpuPerUidTrackTable", @@ -406,7 +377,7 @@ ANDROID_GAME_INTERVENTION_LIST_TABLE, ANDROID_INPUT_EVENT_DISPATCH_TABLE, ANDROID_KEY_EVENTS_TABLE, - ANDROID_LOG_TABLE, + LOG_TABLE, ANDROID_MOTION_EVENTS_TABLE, ANDROID_USER_LIST_TABLE, ] diff --git a/src/trace_processor/tables/log_tables.py b/src/trace_processor/tables/log_tables.py new file mode 100644 index 00000000000..218cfb7f4ba --- /dev/null +++ b/src/trace_processor/tables/log_tables.py @@ -0,0 +1,77 @@ +# Copyright (C) 2022 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. +"""Contains tables for log entries from various sources.""" + +from python.generators.trace_processor_table.public import Column as C +from python.generators.trace_processor_table.public import ColumnDoc +from python.generators.trace_processor_table.public import CppAccess +from python.generators.trace_processor_table.public import CppInt64 +from python.generators.trace_processor_table.public import CppOptional +from python.generators.trace_processor_table.public import CppString +from python.generators.trace_processor_table.public import CppTableId +from python.generators.trace_processor_table.public import CppUint32 +from python.generators.trace_processor_table.public import Table +from python.generators.trace_processor_table.public import TableDoc + +from src.trace_processor.tables.metadata_tables import THREAD_TABLE + +LOG_TABLE = Table( + python_module=__file__, + class_name="LogTable", + sql_name="__intrinsic_logs", + columns=[ + C("ts", CppInt64(), cpp_access=CppAccess.READ), + C("utid", + CppOptional(CppTableId(THREAD_TABLE)), + cpp_access=CppAccess.READ), + C("prio", CppUint32(), cpp_access=CppAccess.READ), + C("log_source", CppString(), cpp_access=CppAccess.READ), + C("tag", CppOptional(CppString()), cpp_access=CppAccess.READ), + C("msg", CppString(), cpp_access=CppAccess.READ), + C("arg_set_id", + CppOptional(CppUint32()), + cpp_access=CppAccess.READ_AND_LOW_PERF_WRITE), + ], + tabledoc=TableDoc( + doc=''' + Log entries from all sources (Android logcat, journald, etc.). + + NOTE: this table is not sorted by timestamp. This is why we omit the + sorted flag on the ts column. + ''', + group='Android', + columns={ + 'ts': + 'Timestamp of the log entry.', + 'utid': + ColumnDoc( + doc='Thread writing the log entry (nullable).', + joinable='thread.id'), + 'prio': + 'Priority. Android: 3=DEBUG..6=ERROR. Journald/syslog: 0=EMERG..7=DEBUG.', + 'log_source': + "Source of the log entry: 'android_logcat' or 'journald'.", + 'tag': + 'Tag / SYSLOG_IDENTIFIER of the log entry.', + 'msg': + 'Content of the log entry.', + 'arg_set_id': + ColumnDoc( + doc='Args for source-specific metadata.', + joinable='args.arg_set_id'), + }, + ), +) + +ALL_TABLES = [LOG_TABLE] diff --git a/src/traced/probes/BUILD.gn b/src/traced/probes/BUILD.gn index ada538e4dff..341e5327063 100644 --- a/src/traced/probes/BUILD.gn +++ b/src/traced/probes/BUILD.gn @@ -76,6 +76,7 @@ source_set("probes_src") { "common", "filesystem", "initial_display_state", + "journald", "metatrace", "packages_list", "power", @@ -119,6 +120,7 @@ perfetto_unittest_source_set("unittests") { "filesystem:unittests", "ftrace:unittests", "initial_display_state:unittests", + "journald:unittests", "packages_list:unittests", "power:unittests", "ps:unittests", diff --git a/src/traced/probes/journald/BUILD.gn b/src/traced/probes/journald/BUILD.gn new file mode 100644 index 00000000000..45616002a6b --- /dev/null +++ b/src/traced/probes/journald/BUILD.gn @@ -0,0 +1,49 @@ +# Copyright (C) 2026 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("../../../../gn/perfetto.gni") +import("../../../../gn/test.gni") + +source_set("journald") { + public_deps = [ "../../../tracing/core" ] + deps = [ + "..:data_source", + "../../../../gn:default_deps", + "../../../../include/perfetto/ext/traced", + "../../../../protos/perfetto/common:zero", + "../../../../protos/perfetto/config/linux:zero", + "../../../../protos/perfetto/trace:zero", + "../../../../protos/perfetto/trace/linux:zero", + "../../../base", + ] + libs = [ "dl" ] + sources = [ + "journald_data_source.cc", + "journald_data_source.h", + ] +} + +perfetto_unittest_source_set("unittests") { + testonly = true + deps = [ + ":journald", + "../../../../gn:default_deps", + "../../../../gn:gtest_and_gmock", + "../../../../protos/perfetto/config/linux:cpp", + "../../../../protos/perfetto/trace/linux:cpp", + "../../../../src/base:test_support", + "../../../../src/tracing/test:test_support", + ] + sources = [ "journald_data_source_unittest.cc" ] +} diff --git a/src/traced/probes/journald/journald_data_source.cc b/src/traced/probes/journald/journald_data_source.cc new file mode 100644 index 00000000000..3d40bfeac9d --- /dev/null +++ b/src/traced/probes/journald/journald_data_source.cc @@ -0,0 +1,406 @@ +/* + * Copyright (C) 2026 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/journald/journald_data_source.h" + +#include +#include +#include + +#include + +#include "perfetto/base/logging.h" +#include "perfetto/base/task_runner.h" +#include "perfetto/base/time.h" +#include "perfetto/ext/tracing/core/trace_writer.h" +#include "perfetto/tracing/core/data_source_config.h" + +#include "protos/perfetto/common/builtin_clock.pbzero.h" +#include "protos/perfetto/config/linux/journald_config.pbzero.h" +#include "protos/perfetto/trace/linux/journald_event.pbzero.h" +#include "protos/perfetto/trace/trace_packet.pbzero.h" + +namespace perfetto { +namespace { + +constexpr uint32_t kMaxEventsPerRead = 500; + +// Locally defined to avoid needing at compile time. +struct sd_id128_t { + uint8_t bytes[16]; +}; + +// Function pointer typedefs matching the real libsystemd signatures. +using sd_journal_open_t = int (*)(sd_journal**, int); +using sd_journal_close_t = void (*)(sd_journal*); +using sd_journal_add_match_t = int (*)(sd_journal*, const void*, size_t); +using sd_journal_add_disjunction_t = int (*)(sd_journal*); +using sd_journal_add_conjunction_t = int (*)(sd_journal*); +using sd_journal_seek_tail_t = int (*)(sd_journal*); +using sd_journal_previous_t = int (*)(sd_journal*); +using sd_journal_next_t = int (*)(sd_journal*); +using sd_journal_get_fd_t = int (*)(sd_journal*); +using sd_journal_process_t = int (*)(sd_journal*); +using sd_journal_get_realtime_usec_t = int (*)(sd_journal*, uint64_t*); +using sd_journal_get_monotonic_usec_t = int (*)(sd_journal*, + uint64_t*, + sd_id128_t*); +using sd_journal_get_data_t = int (*)(sd_journal*, + const char*, + const void**, + size_t*); + +// Constants from — defined locally so we don't need +// the systemd headers at compile time. +static constexpr int kSdJournalLocalOnly = 1 << 0; +} // namespace + +struct JournaldDataSource::SdJournalApi { + sd_journal_open_t open; + sd_journal_close_t close; + sd_journal_add_match_t add_match; + sd_journal_add_disjunction_t add_disjunction; + sd_journal_add_conjunction_t add_conjunction; + sd_journal_seek_tail_t seek_tail; + sd_journal_previous_t previous; + sd_journal_next_t next; + sd_journal_get_fd_t get_fd; + sd_journal_process_t process; + sd_journal_get_realtime_usec_t get_realtime_usec; + sd_journal_get_monotonic_usec_t get_monotonic_usec; + sd_journal_get_data_t get_data; +}; + +std::unique_ptr +JournaldDataSource::LoadSdJournalApi() { + const char* libsystemd_soname = "libsystemd.so.0"; + void* handle = dlopen(libsystemd_soname, RTLD_NOW | RTLD_LOCAL); + if (!handle) { + PERFETTO_ELOG( + "linux.systemd_journald datasource unavailable, failed to load %s: %s", + libsystemd_soname, dlerror()); + return nullptr; + } + + auto api = std::make_unique(); + auto load_sym = [&](const char* name) -> void* { + void* sym = dlsym(handle, name); + if (!sym) + PERFETTO_ELOG("dlsym(\"%s\", %s) failed: %s", libsystemd_soname, name, + dlerror()); + return sym; + }; + + api->open = reinterpret_cast(load_sym("sd_journal_open")); + if (!api->open) + return nullptr; + + api->close = + reinterpret_cast(load_sym("sd_journal_close")); + if (!api->close) + return nullptr; + + api->add_match = reinterpret_cast( + load_sym("sd_journal_add_match")); + if (!api->add_match) + return nullptr; + + api->add_disjunction = reinterpret_cast( + load_sym("sd_journal_add_disjunction")); + if (!api->add_disjunction) + return nullptr; + + api->add_conjunction = reinterpret_cast( + load_sym("sd_journal_add_conjunction")); + if (!api->add_conjunction) + return nullptr; + + api->seek_tail = reinterpret_cast( + load_sym("sd_journal_seek_tail")); + if (!api->seek_tail) + return nullptr; + + api->previous = + reinterpret_cast(load_sym("sd_journal_previous")); + if (!api->previous) + return nullptr; + + api->next = reinterpret_cast(load_sym("sd_journal_next")); + if (!api->next) + return nullptr; + + api->get_fd = + reinterpret_cast(load_sym("sd_journal_get_fd")); + if (!api->get_fd) + return nullptr; + + api->process = + reinterpret_cast(load_sym("sd_journal_process")); + if (!api->process) + return nullptr; + + api->get_realtime_usec = reinterpret_cast( + load_sym("sd_journal_get_realtime_usec")); + if (!api->get_realtime_usec) + return nullptr; + + api->get_monotonic_usec = reinterpret_cast( + load_sym("sd_journal_get_monotonic_usec")); + if (!api->get_monotonic_usec) + return nullptr; + + api->get_data = + reinterpret_cast(load_sym("sd_journal_get_data")); + if (!api->get_data) + return nullptr; + + return api; +}; + +const ProbesDataSource::Descriptor JournaldDataSource::descriptor = { + /*name*/ "linux.systemd_journald", + /*flags*/ Descriptor::kFlagsNone, + /*fill_descriptor_func*/ nullptr, +}; + +JournaldDataSource::JournaldDataSource(DataSourceConfig ds_config, + base::TaskRunner* task_runner, + TracingSessionID session_id, + std::unique_ptr writer) + : ProbesDataSource(session_id, &descriptor), + task_runner_(task_runner), + writer_(std::move(writer)), + weak_factory_(this) { + protos::pbzero::SystemdJournaldConfig::Decoder cfg( + ds_config.journald_config_raw()); + if (cfg.has_min_prio()) + min_prio_ = cfg.min_prio(); + for (auto id = cfg.filter_identifiers(); id; ++id) + filter_identifiers_.push_back(id->as_std_string()); + for (auto u = cfg.filter_units(); u; ++u) + filter_units_.push_back(u->as_std_string()); +} + +JournaldDataSource::~JournaldDataSource() { + if (journal_ && sd_) { + task_runner_->RemoveFileDescriptorWatch(sd_->get_fd(journal_)); + sd_->close(journal_); + journal_ = nullptr; + } +} + +// Helper macro to check libsystemd return codes and raise up any errors +#define SD_CHECK(ex) \ + do { \ + int sd_res = (ex); \ + if (sd_res < 0) { \ + PERFETTO_ELOG(#ex " failed: %s", strerror(-sd_res)); \ + return; \ + } \ + } while (false) + +void JournaldDataSource::Start() { + sd_ = LoadSdJournalApi(); + if (!sd_) { + PERFETTO_ELOG("Failed to load libsystemd dynamically; journald disabled."); + return; + } + + int r = sd_->open(&journal_, kSdJournalLocalOnly); + if (r < 0) { + PERFETTO_ELOG("Failed to open journal: %d", -r); + return; + } + + // Add PRIORITY match filters. For each severity level <= min_prio_, + // add a match with OR (disjunction) logic between levels. + for (uint32_t p = 0; p <= min_prio_; ++p) { + std::string match = "PRIORITY=" + std::to_string(p); + SD_CHECK(sd_->add_match(journal_, match.c_str(), match.size())); + if (p < min_prio_) + SD_CHECK(sd_->add_disjunction(journal_)); + } + + // If identifier filters: add them conjuncted with the priority block. + if (!filter_identifiers_.empty()) { + SD_CHECK(sd_->add_conjunction(journal_)); + for (size_t i = 0; i < filter_identifiers_.size(); ++i) { + std::string match = "SYSLOG_IDENTIFIER=" + filter_identifiers_[i]; + SD_CHECK(sd_->add_match(journal_, match.c_str(), match.size())); + if (i + 1 < filter_identifiers_.size()) + SD_CHECK(sd_->add_disjunction(journal_)); + } + } + + // Unit filters similarly conjuncted. + if (!filter_units_.empty()) { + SD_CHECK(sd_->add_conjunction(journal_)); + for (size_t i = 0; i < filter_units_.size(); ++i) { + std::string match = "_SYSTEMD_UNIT=" + filter_units_[i]; + SD_CHECK(sd_->add_match(journal_, match.c_str(), match.size())); + if (i + 1 < filter_units_.size()) + SD_CHECK(sd_->add_disjunction(journal_)); + } + } + + // Seek to tail so only new entries are captured going forward. + SD_CHECK(sd_->seek_tail(journal_)); + SD_CHECK(sd_->previous(journal_)); + + int fd = sd_->get_fd(journal_); + if (fd < 0) { + PERFETTO_ELOG("sd_journal_get_fd failed: %d", -fd); + sd_->close(journal_); + journal_ = nullptr; + return; + } + + // Register the fd watch before draining so no wakeups are missed. + auto weak = weak_factory_.GetWeakPtr(); + task_runner_->AddFileDescriptorWatch(fd, [weak] { + if (weak) + weak->OnJournalReadable(); + }); + + // Drain once after seek_tail(). sd_journal_next() returns 0 immediately + // (no pre-existing entries are delivered), but this call is required to + // establish the per-file n_entries baseline in the sd_journal internals. + // Without it the EOF short-circuit in sd_journal_next() never clears on + // subsequent wakeups, so new entries written after Start() would never + // be seen. + while (sd_->next(journal_) > 0) { + } +} + +void JournaldDataSource::OnJournalReadable() { + sd_->process(journal_); + ReadJournalEntries(); +} + +void JournaldDataSource::ReadJournalEntries() { + uint32_t n = 0; + + while (sd_->next(journal_) > 0 && n < kMaxEventsPerRead) { + std::string prio_str = GetField("PRIORITY"); + uint32_t prio = prio_str.empty() ? min_prio_ + : static_cast(std::strtoul( + prio_str.c_str(), nullptr, 10)); + if (prio > min_prio_) { + stats_.num_skipped++; + continue; + } + + uint64_t monotonic_us = 0; + sd_id128_t boot_id{}; + if (sd_->get_monotonic_usec(journal_, &monotonic_us, &boot_id) < 0) { + PERFETTO_LOG("failed to get monotonic timestamp for journald event"); + stats_.num_failed++; + continue; + } + + auto packet = writer_->NewTracePacket(); + packet->set_timestamp(monotonic_us * 1000); + packet->set_timestamp_clock_id(protos::pbzero::BUILTIN_CLOCK_MONOTONIC); + auto* ev = packet->set_journald_event(); + ev->set_prio(prio); + + std::string msg = GetField("MESSAGE"); + if (!msg.empty()) + ev->set_message(msg); + + std::string tag = GetField("SYSLOG_IDENTIFIER"); + if (!tag.empty()) + ev->set_tag(tag); + + std::string comm = GetField("_COMM"); + if (!comm.empty()) + ev->set_comm(comm); + + std::string exe = GetField("_EXE"); + if (!exe.empty()) + ev->set_exe(exe); + + std::string unit = GetField("_SYSTEMD_UNIT"); + if (!unit.empty()) + ev->set_systemd_unit(unit); + + std::string host = GetField("_HOSTNAME"); + if (!host.empty()) + ev->set_hostname(host); + + std::string transport = GetField("_TRANSPORT"); + if (!transport.empty()) + ev->set_transport(transport); + + std::string pid_str = GetField("_PID"); + if (!pid_str.empty()) + ev->set_pid( + static_cast(std::strtoul(pid_str.c_str(), nullptr, 10))); + + std::string tid_str = GetField("_TID"); + if (!tid_str.empty()) + ev->set_tid( + static_cast(std::strtoul(tid_str.c_str(), nullptr, 10))); + + std::string uid_str = GetField("_UID"); + if (!uid_str.empty()) + ev->set_uid( + static_cast(std::strtoul(uid_str.c_str(), nullptr, 10))); + + std::string gid_str = GetField("_GID"); + if (!gid_str.empty()) + ev->set_gid( + static_cast(std::strtoul(gid_str.c_str(), nullptr, 10))); + + stats_.num_total++; + n++; + } +} + +std::string JournaldDataSource::GetField(const char* field) { + const void* data = nullptr; + size_t len = 0; + if (sd_->get_data(journal_, field, &data, &len) < 0) + return {}; + // sd_journal_get_data returns "FIELD=value"; skip past the '='. + const char* str = static_cast(data); + const char* eq = static_cast(memchr(str, '=', len)); + if (!eq) + return {}; + ++eq; // skip '=' + return std::string(eq, static_cast(str + len - eq)); +} + +void JournaldDataSource::Flush(FlushRequestID, std::function callback) { + if (journal_ && sd_) { + sd_->process(journal_); + ReadJournalEntries(); + } + + // Emit a stats packet. + { + auto packet = writer_->NewTracePacket(); + packet->set_timestamp(static_cast(base::GetBootTimeNs().count())); + auto* stats = packet->set_journald_event(); + stats->set_num_total(stats_.num_total); + stats->set_num_skipped(stats_.num_skipped); + stats->set_num_failed(stats_.num_failed); + } + + writer_->Flush(callback); +} + +} // namespace perfetto diff --git a/src/traced/probes/journald/journald_data_source.h b/src/traced/probes/journald/journald_data_source.h new file mode 100644 index 00000000000..22f93628db1 --- /dev/null +++ b/src/traced/probes/journald/journald_data_source.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2026 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_JOURNALD_JOURNALD_DATA_SOURCE_H_ +#define SRC_TRACED_PROBES_JOURNALD_JOURNALD_DATA_SOURCE_H_ + +#include +#include +#include +#include +#include + +#include "perfetto/ext/base/weak_ptr.h" +#include "perfetto/tracing/core/forward_decls.h" +#include "src/traced/probes/probes_data_source.h" + +// Forward-declare the C struct to avoid pulling in +// into every TU that includes this header. +struct sd_journal; + +namespace perfetto { + +class TraceWriter; +namespace base { +class TaskRunner; +} + +class JournaldDataSource : public ProbesDataSource { + public: + static const ProbesDataSource::Descriptor descriptor; + + struct Stats { + uint64_t num_total = 0; + uint64_t num_failed = 0; + uint64_t num_skipped = 0; + }; + + JournaldDataSource(DataSourceConfig ds_config, + base::TaskRunner* task_runner, + TracingSessionID session_id, + std::unique_ptr writer); + + ~JournaldDataSource() override; + + // ProbesDataSource implementation. + void Start() override; + void Flush(FlushRequestID, std::function callback) override; + + const Stats& stats() const { return stats_; } + + private: + void OnJournalReadable(); + void ReadJournalEntries(); + std::string GetField(const char* field); + + base::TaskRunner* const task_runner_; + std::unique_ptr writer_; + sd_journal* journal_ = nullptr; + + struct SdJournalApi; + std::unique_ptr sd_; + std::unique_ptr LoadSdJournalApi(); + + // Config parameters. + uint32_t min_prio_ = 7; // 7=DEBUG, capture everything by default. + std::vector filter_identifiers_; + std::vector filter_units_; + + Stats stats_; + base::WeakPtrFactory weak_factory_; // Keep last. +}; + +} // namespace perfetto + +#endif // SRC_TRACED_PROBES_JOURNALD_JOURNALD_DATA_SOURCE_H_ diff --git a/src/traced/probes/journald/journald_data_source_unittest.cc b/src/traced/probes/journald/journald_data_source_unittest.cc new file mode 100644 index 00000000000..481281dca67 --- /dev/null +++ b/src/traced/probes/journald/journald_data_source_unittest.cc @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2026 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/journald/journald_data_source.h" + +#include "perfetto/tracing/core/data_source_config.h" +#include "src/base/test/test_task_runner.h" +#include "src/tracing/core/trace_writer_for_testing.h" +#include "test/gtest_and_gmock.h" + +#include "protos/perfetto/config/linux/journald_config.gen.h" + +using ::perfetto::protos::gen::SystemdJournaldConfig; + +namespace perfetto { +namespace { + +class JournaldDataSourceTest : public ::testing::Test { + protected: + void CreateInstance(const DataSourceConfig& cfg) { + auto writer = std::make_unique(); + writer_raw_ = writer.get(); + data_source_ = std::make_unique( + cfg, &task_runner_, /*session_id=*/0, std::move(writer)); + } + + base::TestTaskRunner task_runner_; + std::unique_ptr data_source_; + TraceWriterForTesting* writer_raw_ = nullptr; +}; + +// Creating a data source without any config should not crash. +TEST_F(JournaldDataSourceTest, DefaultConfig) { + DataSourceConfig cfg; + CreateInstance(cfg); + EXPECT_EQ(data_source_->stats().num_total, 0u); + EXPECT_EQ(data_source_->stats().num_failed, 0u); + EXPECT_EQ(data_source_->stats().num_skipped, 0u); +} + +// Calling Flush() before Start() (journal_ == nullptr) must not crash and must +// invoke the callback. +TEST_F(JournaldDataSourceTest, FlushWithoutStart) { + DataSourceConfig cfg; + CreateInstance(cfg); + bool callback_called = false; + data_source_->Flush(0, [&callback_called] { callback_called = true; }); + EXPECT_TRUE(callback_called); +} + +// Verify that min_prio and filter fields from the config are accepted without +// error during construction. +TEST_F(JournaldDataSourceTest, ConfigParsing) { + SystemdJournaldConfig cfg_proto; + cfg_proto.set_min_prio(3); + cfg_proto.add_filter_identifiers("sshd"); + cfg_proto.add_filter_identifiers("kernel"); + cfg_proto.add_filter_units("nginx.service"); + + DataSourceConfig cfg; + cfg.set_journald_config_raw(cfg_proto.SerializeAsString()); + CreateInstance(cfg); + // Construction must succeed and stats start at zero. + EXPECT_EQ(data_source_->stats().num_total, 0u); +} + +// Verify the static descriptor has the correct name. +TEST_F(JournaldDataSourceTest, Descriptor) { + EXPECT_STREQ(JournaldDataSource::descriptor.name, "linux.systemd_journald"); +} + +} // namespace +} // namespace perfetto diff --git a/src/traced/probes/probes_producer.cc b/src/traced/probes/probes_producer.cc index a98fb823710..0925e2e73b0 100644 --- a/src/traced/probes/probes_producer.cc +++ b/src/traced/probes/probes_producer.cc @@ -59,6 +59,8 @@ #include "src/traced/probes/system_info/system_info_data_source.h" #include "src/traced/probes/user_list/user_list_data_source.h" +#include "src/traced/probes/journald/journald_data_source.h" + namespace perfetto { namespace { @@ -361,6 +363,17 @@ ProbesProducer::CreateDSInstance( endpoint_->CreateTraceWriter(buffer_id, BufferExhaustedPolicy::kStall)); } +template <> +std::unique_ptr +ProbesProducer::CreateDSInstance( + TracingSessionID session_id, + const DataSourceConfig& config) { + auto buffer_id = static_cast(config.target_buffer()); + return std::make_unique( + config, task_runner_, session_id, + endpoint_->CreateTraceWriter(buffer_id, BufferExhaustedPolicy::kStall)); +} + // Another anonymous namespace. This cannot be moved into the anonymous // namespace on top (it would fail to compile), because the CreateDSInstance // methods need to be fully declared before. @@ -391,6 +404,7 @@ constexpr const DataSourceTraits kAllDataSources[] = { Ds(), Ds(), Ds(), + Ds(), Ds(), Ds(), Ds(), diff --git a/test/trace_processor/diff_tests/include_index.py b/test/trace_processor/diff_tests/include_index.py index b66a216b722..5d0fcbc4ac5 100644 --- a/test/trace_processor/diff_tests/include_index.py +++ b/test/trace_processor/diff_tests/include_index.py @@ -89,6 +89,7 @@ from diff_tests.parser.gzip.tests import Gzip from diff_tests.parser.instruments.tests import Instruments from diff_tests.parser.json.tests import JsonParser +from diff_tests.parser.linux.tests import Linux from diff_tests.parser.memory.tests import MemoryParser from diff_tests.parser.network.tests import NetworkParser from diff_tests.parser.parsing.tests import Parsing @@ -226,6 +227,7 @@ def fetch_all_diff_tests( GraphicsParser, JsonParser, KernelTrackevent, + Linux, MemoryParser, NetworkParser, BatteryStats, diff --git a/test/trace_processor/diff_tests/parser/linux/__init__.py b/test/trace_processor/diff_tests/parser/linux/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/test/trace_processor/diff_tests/parser/linux/tests.py b/test/trace_processor/diff_tests/parser/linux/tests.py new file mode 100644 index 00000000000..7069bcd06a6 --- /dev/null +++ b/test/trace_processor/diff_tests/parser/linux/tests.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 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. + +from python.generators.diff_tests.testing import Csv, TextProto +from python.generators.diff_tests.testing import DiffTestBlueprint +from python.generators.diff_tests.testing import TestSuite + + +class Linux(TestSuite): + + def test_journald_basic(self): + return DiffTestBlueprint( + trace=TextProto(r""" + packet { + clock_snapshot { + clocks { + clock_id: 1 + timestamp: 1000000000 + } + clocks { + clock_id: 6 + timestamp: 1000000000 + } + primary_trace_clock: BUILTIN_CLOCK_BOOTTIME + } + } + packet { + timestamp: 1000000000 + journald_event { + pid: 42 + tid: 42 + prio: 6 + tag: "myapp" + message: "hello world" + comm: "myapp" + uid: 1000 + systemd_unit: "myapp.service" + hostname: "myhost" + transport: "journal" + } + } + """), + query=""" + INCLUDE PERFETTO MODULE linux.journald; + SELECT ts, prio, tag, msg, uid, comm, systemd_unit, hostname, transport + FROM linux_journald_logs; + """, + out=Csv(""" + "ts","prio","tag","msg","uid","comm","systemd_unit","hostname","transport" + 1000000000,6,"myapp","hello world",1000,"myapp","myapp.service","myhost","journal" + """)) diff --git a/test/trace_processor/diff_tests/parser/parsing/android_log_msgs_test.sql b/test/trace_processor/diff_tests/parser/parsing/android_log_msgs_test.sql index fde19d7850d..bd2e30577d2 100644 --- a/test/trace_processor/diff_tests/parser/parsing/android_log_msgs_test.sql +++ b/test/trace_processor/diff_tests/parser/parsing/android_log_msgs_test.sql @@ -20,7 +20,7 @@ CREATE VIEW v2 AS SELECT tag, count(*) FROM android_logs GROUP BY tag ORDER BY 2 CREATE VIEW v3 AS SELECT tag, count(*) FROM android_logs -WHERE msg GLOB '*wakelock*' OR msg GLOB '*Wakelock*' OR msg GLOB '*WakeLock*' OR msg GLOB '*wakeLock*' +WHERE (msg GLOB '*wakelock*' OR msg GLOB '*Wakelock*' OR msg GLOB '*WakeLock*' OR msg GLOB '*wakeLock*') GROUP BY tag; CREATE VIEW v4 AS SELECT msg, 1 FROM android_logs LIMIT 10; diff --git a/ui/src/components/tracks/log_track.ts b/ui/src/components/tracks/log_track.ts new file mode 100644 index 00000000000..c389837f2a2 --- /dev/null +++ b/ui/src/components/tracks/log_track.ts @@ -0,0 +1,137 @@ +// Copyright (C) 2024 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 m from 'mithril'; +import { + LONG, + NUM, + NUM_NULL, + STR_NULL, +} from '../../trace_processor/query_result'; +import type {Trace} from '../../public/trace'; +import {SliceTrack} from './slice_track'; +import {SourceDataset} from '../../trace_processor/dataset'; +import type {ColorScheme} from '../../base/color_scheme'; +import {Section} from '../../widgets/section'; +import {Tree, TreeNode} from '../../widgets/tree'; +import {Timestamp} from '../widgets/timestamp'; +import {Time} from '../../base/time'; +import {DetailsShell} from '../../widgets/details_shell'; +import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout'; +import {Spinner} from '../../widgets/spinner'; + +const EVT_PX = 6; // Width of an event tick in pixels. + +export interface LogTrackConfig { + /** Track / details panel title, e.g. "Android Log" or "Journald Log". */ + readonly title: string; + /** + * The SQL fragment that provides rows with columns: + * id, ts, prio, utid, tag, msg, depth + * It should already be wrapped in `select ... from ... order by ts`. + */ + readonly sqlSource: string; + /** Root table name forwarded to SliceTrack (e.g. 'logs', 'journald_logs'). */ + readonly rootTableName: string; + /** + * Color scheme per depth level; index 0 = depth 0 (most severe in context). + */ + readonly depthColors: ColorScheme[]; + /** Async callback to fetch the log message for the details panel. */ + readonly fetchMsg: (trace: Trace, id: number) => Promise; +} + +/** + * Creates a SliceTrack for log events given a LogTrackConfig. + * Both the AndroidLog and JournaldLog plugins use this factory. + */ +export function createLogTrack( + trace: Trace, + uri: string, + config: LogTrackConfig, +) { + return SliceTrack.create({ + trace, + uri, + rootTableName: config.rootTableName, + dataset: new SourceDataset({ + src: config.sqlSource, + schema: { + id: NUM, + ts: LONG, + prio: NUM, + utid: NUM_NULL, + depth: NUM, + tag: STR_NULL, + msg: STR_NULL, + }, + }), + initialMaxDepth: 4, + colorizer: (row) => config.depthColors[row.depth], + tooltip: (slice) => [m('', m('b', slice.row.tag)), m('', slice.row.msg)], + // All log events are instant events, render them as a little box rather + // than the default chevron. + instantStyle: { + width: EVT_PX, + render: (ctx, r) => ctx.fillRect(r.x, r.y, r.width, r.height), + }, + // Make rows a little more compact. + sliceLayout: { + padding: 2, + sliceHeight: 7, + }, + detailsPanel: (row) => { + // The msg is initially undefined; it'll be filled in when it loads. + let msg: string | undefined; + + config.fetchMsg(trace, row.id).then((result) => { + msg = result; + }); + + return { + render() { + return m( + DetailsShell, + {title: config.title}, + m( + GridLayout, + m( + GridLayoutColumn, + m( + Section, + {title: 'Details'}, + m( + Tree, + m(TreeNode, {left: 'ID', right: row.id}), + m(TreeNode, { + left: 'Timestamp', + right: m(Timestamp, {trace, ts: Time.fromRaw(row.ts)}), + }), + m(TreeNode, {left: 'Priority', right: row.prio}), + m(TreeNode, {left: 'Tag', right: row.tag}), + m(TreeNode, {left: 'Utid', right: row.utid}), + m(TreeNode, { + left: 'Message', + right: msg ? msg : m(Spinner), + }), + ), + ), + ), + ), + ); + }, + }; + }, + }); +} diff --git a/ui/src/components/widgets/log_panel/log_panel.ts b/ui/src/components/widgets/log_panel/log_panel.ts new file mode 100644 index 00000000000..3817765f527 --- /dev/null +++ b/ui/src/components/widgets/log_panel/log_panel.ts @@ -0,0 +1,664 @@ +// Copyright (C) 2024 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 m from 'mithril'; +import {type time, Time, type TimeSpan} from '../../../base/time'; +import {DetailsShell} from '../../../widgets/details_shell'; +import {Timestamp} from '../timestamp'; +import type {Engine} from '../../../trace_processor/engine'; +import { + LONG, + LONG_NULL, + NUM, + NUM_NULL, + type Row, + STR, +} from '../../../trace_processor/query_result'; +import { + escapeQuery, + escapeSearchQuery, +} from '../../../trace_processor/query_utils'; +import {Select} from '../../../widgets/select'; +import {Button} from '../../../widgets/button'; +import {TextInput} from '../../../widgets/text_input'; +import { + Grid, + type GridColumn, + type GridRow, + GridHeaderCell, + GridCell, +} from '../../../widgets/grid'; +import {classNames} from '../../../base/classnames'; +import {TagInput} from '../../../widgets/tag_input'; +import type {Store} from '../../../base/store'; +import type {Trace} from '../../../public/trace'; +import {Icons} from '../../../base/semantic_icons'; +import {SerialTaskQueue, QuerySlot} from '../../../base/query_slot'; +import {Anchor} from '../../../widgets/anchor'; +import {getThreadUriPrefix} from '../../../public/utils'; + +export const ROW_H = 24; + +// --------------------------------------------------------------------------- +// Shared filter state (base — each plugin may extend this) +// --------------------------------------------------------------------------- + +export interface BaseLogFilteringCriteria { + readonly minimumLevel: number; + readonly tags: string[]; + readonly isTagRegex?: boolean; + readonly textEntry: string; + readonly hideNonMatching: boolean; +} + +// --------------------------------------------------------------------------- +// Shared log entries produced by updateLogEntries +// --------------------------------------------------------------------------- + +export interface SharedLogEntries { + readonly offset: number; + readonly timestamps: time[]; + readonly pids: bigint[]; + readonly tids: bigint[]; + readonly priorities: number[]; + readonly tags: string[]; + readonly messages: string[]; + readonly isHighlighted: boolean[]; + readonly processName: string[]; + /** Resolved utid/upid for each row (null when not resolvable). */ + readonly utids: (number | null)[]; + readonly upids: (number | null)[]; + readonly totalEvents: number; + /** Plugin-specific extra arrays keyed by column name. */ + readonly extra: Record; +} + +// --------------------------------------------------------------------------- +// Config that each plugin provides to parameterise the shared panel +// --------------------------------------------------------------------------- + +export interface LogPanelConfig { + /** Displayed in the DetailsShell title bar. */ + readonly title: string; + /** Priority label array; index = numeric priority value. */ + readonly priorities: string[]; + /** + * How many leading entries in priorities[] to skip in the selector UI. + * Android = 2 (skips '-','-'), Journald = 0. + */ + readonly ignoredPriorityStates: number; + /** + * Name of the perfetto table created by buildSelectedRows, e.g. + * 'filtered_logs' or 'filtered_journald_logs'. + */ + readonly filteredTableName: string; + /** + * Builds the SQL SELECT expression used inside + * `create perfetto table as select * from () ...` + * The returned string should already include the globMatch columns. + */ + readonly buildSelectedRows: ( + filter: BaseLogFilteringCriteria, + globMatch: string, + ) => string; + /** + * SQL columns to fetch in addition to the common set. + * Each entry is ` as `. + */ + readonly extraSelectColumns?: string[]; + /** + * Schema entries for the extra columns (same keys as aliases above). + * Used with rowsResult.iter(). + */ + readonly extraSchema?: Row; + /** + * Called once per row to collect extra data into arrays stored in + * SharedLogEntries.extra[alias]. + */ + readonly collectExtraRow?: ( + it: Row, + extra: Record, + ) => void; + /** + * Renders additional GridColumn definitions (header cells). + * Called once when the entries are available. + */ + readonly extraColumns?: (entries: SharedLogEntries) => GridColumn[]; + /** + * Renders extra GridCells for row i. The returned cells are inserted + * after the Tag cell and before the Message cell. + */ + readonly renderExtraCells?: ( + entries: SharedLogEntries, + i: number, + className: string, + trace: Trace, + ) => m.Children[]; + /** CSS class prefix for grid and priority rows, e.g. 'pf-logs-panel'. */ + readonly classPrefix: string; + /** Maps numeric priority → CSS suffix string used for row colouring. */ + readonly classForPriority: (prio: number) => string | undefined; + /** + * Renders extra filter widgets appended after the standard ones. + * Return null/undefined if not needed. + */ + readonly renderExtraFilters?: ( + store: Store, + ) => m.Children; +} + +// --------------------------------------------------------------------------- +// Public attrs for the LogPanel component +// --------------------------------------------------------------------------- + +export interface LogPanelAttrs { + readonly config: LogPanelConfig; + readonly filterStore: Store; + readonly trace: Trace; +} + +// --------------------------------------------------------------------------- +// Internal helpers +// --------------------------------------------------------------------------- + +interface Pagination { + readonly offset: number; + readonly count: number; +} + +// --------------------------------------------------------------------------- +// Priority selector widget +// --------------------------------------------------------------------------- + +interface LogPriorityWidgetAttrs { + readonly options: string[]; + readonly ignoredStates: number; + readonly selectedIndex: number; + readonly onSelect: (id: number) => void; +} + +class LogPriorityWidget implements m.ClassComponent { + view(vnode: m.Vnode) { + const {options, ignoredStates, selectedIndex, onSelect} = vnode.attrs; + const optionComponents = []; + for (let i = ignoredStates; i < options.length; i++) { + optionComponents.push( + m('option', {value: i, selected: i === selectedIndex}, options[i]), + ); + } + return m( + Select, + { + onchange: (e: Event) => { + onSelect(Number((e.target as HTMLSelectElement).value)); + }, + }, + optionComponents, + ); + } +} + +// --------------------------------------------------------------------------- +// Text search widget +// --------------------------------------------------------------------------- + +interface LogTextWidgetAttrs { + readonly onChange: (value: string) => void; +} + +class LogTextWidget implements m.ClassComponent { + view({attrs}: m.CVnode) { + return m(TextInput, { + placeholder: 'Search logs...', + onkeyup: (e: KeyboardEvent) => { + attrs.onChange((e.target as HTMLInputElement).value); + }, + }); + } +} + +// --------------------------------------------------------------------------- +// Filter-by-text toggle +// --------------------------------------------------------------------------- + +interface FilterByTextWidgetAttrs { + readonly hideNonMatching: boolean; + readonly disabled: boolean; + readonly onClick: () => void; +} + +class FilterByTextWidget implements m.ClassComponent { + view({attrs}: m.Vnode) { + const icon = attrs.hideNonMatching ? Icons.Filter : Icons.FilterOff; + const tooltip = attrs.hideNonMatching + ? 'Show all logs and highlight matches' + : 'Show only matching logs'; + return m(Button, { + icon, + title: tooltip, + disabled: attrs.disabled, + onclick: attrs.onClick, + }); + } +} + +// --------------------------------------------------------------------------- +// Shared filter bar +// --------------------------------------------------------------------------- + +interface LogsFiltersAttrs { + readonly config: LogPanelConfig; + readonly store: Store; + readonly trace: Trace; +} + +class LogsFilters implements m.ClassComponent { + view({attrs}: m.CVnode) { + const {config, store} = attrs; + return [ + m('span', 'Log Level'), + m(LogPriorityWidget, { + options: config.priorities, + ignoredStates: config.ignoredPriorityStates, + selectedIndex: store.state.minimumLevel, + onSelect: (minimumLevel) => { + store.edit((draft) => { + draft.minimumLevel = minimumLevel; + }); + }, + }), + m(TagInput, { + placeholder: 'Filter by tag...', + tags: store.state.tags, + onTagAdd: (tag) => { + store.edit((draft) => { + draft.tags.push(tag); + }); + }, + onTagRemove: (index) => { + store.edit((draft) => { + draft.tags.splice(index, 1); + }); + }, + }), + m(Button, { + icon: 'regular_expression', + title: 'Use regular expression', + active: !!store.state.isTagRegex, + onclick: () => { + store.edit((draft) => { + draft.isTagRegex = !draft.isTagRegex; + }); + }, + }), + m(LogTextWidget, { + onChange: (text) => { + store.edit((draft) => { + draft.textEntry = text; + }); + }, + }), + m(FilterByTextWidget, { + hideNonMatching: store.state.hideNonMatching, + onClick: () => { + store.edit((draft) => { + draft.hideNonMatching = !draft.hideNonMatching; + }); + }, + disabled: store.state.textEntry === '', + }), + config.renderExtraFilters?.(store), + ]; + } +} + +// --------------------------------------------------------------------------- +// Shared LogPanel component +// --------------------------------------------------------------------------- + +export class LogPanel implements m.ClassComponent { + private readonly executor = new SerialTaskQueue(); + private readonly viewQuery = new QuerySlot(this.executor); + private readonly entriesQuery = new QuerySlot( + this.executor, + ); + private pagination: Pagination = {offset: 0, count: 0}; + + onremove() { + this.viewQuery.dispose(); + this.entriesQuery.dispose(); + } + + view({attrs}: m.CVnode) { + const {config, filterStore, trace} = attrs; + const visibleSpan = trace.timeline.visibleWindow.toTimeSpan(); + const filters = filterStore.state; + const pagination = this.pagination; + const engine = trace.engine; + + const viewResult = this.viewQuery.use({ + key: {filters}, + queryFn: () => updateLogView(engine, filters, config), + }); + + const entriesResult = this.entriesQuery.use({ + key: { + filters, + viewport: {start: visibleSpan.start, end: visibleSpan.end}, + pagination, + }, + retainOn: ['pagination', 'viewport'], + queryFn: () => updateLogEntries(engine, visibleSpan, pagination, config), + enabled: !!viewResult.data, + }); + + const entries = entriesResult.data; + const totalEvents = entries?.totalEvents ?? 0; + + return m( + DetailsShell, + { + title: config.title, + description: `Total messages: ${totalEvents}`, + buttons: m(LogsFilters, {config, store: filterStore, trace}), + }, + this.renderGrid(trace, config, entries), + ); + } + + private renderGrid( + trace: Trace, + config: LogPanelConfig, + entries: SharedLogEntries | undefined, + ) { + if (!entries) return null; + + const hasProcessNames = entries.processName.some((name) => name); + const extraCols = config.extraColumns?.(entries) ?? []; + + const columns: GridColumn[] = [ + {key: 'timestamp', header: m(GridHeaderCell, 'Timestamp')}, + {key: 'pid', header: m(GridHeaderCell, 'PID')}, + {key: 'tid', header: m(GridHeaderCell, 'TID')}, + {key: 'level', header: m(GridHeaderCell, 'Level')}, + ...(hasProcessNames + ? [{key: 'process', header: m(GridHeaderCell, 'Process')}] + : []), + {key: 'tag', header: m(GridHeaderCell, 'Tag')}, + ...extraCols, + { + key: 'message', + maxInitialWidthPx: Infinity, + header: m(GridHeaderCell, 'Message'), + }, + ]; + + return m(Grid, { + className: config.classPrefix, + columns, + rowData: { + data: this.renderRows(trace, config, entries, hasProcessNames), + total: entries.totalEvents, + offset: entries.offset, + onLoadData: (offset, count) => { + this.pagination = {offset, count}; + m.redraw(); + }, + }, + virtualization: {rowHeightPx: ROW_H}, + fillHeight: true, + onRowHover: (rowIndex) => { + const actualIndex = rowIndex - entries.offset; + const timestamp = entries.timestamps[actualIndex]; + if (timestamp !== undefined) { + trace.timeline.hoverCursorTimestamp = timestamp; + } + }, + onRowOut: () => { + trace.timeline.hoverCursorTimestamp = undefined; + }, + }); + } + + private renderRows( + trace: Trace, + config: LogPanelConfig, + entries: SharedLogEntries, + hasProcessNames: boolean, + ): ReadonlyArray { + const rows: GridRow[] = []; + for (let i = 0; i < entries.timestamps.length; i++) { + const priority = entries.priorities[i]; + const priorityLetter = config.priorities[priority]?.[0] ?? '?'; + const ts = entries.timestamps[i]; + const prioritySuffix = config.classForPriority(priority); + const priorityClass = prioritySuffix + ? `${config.classPrefix}__row--${prioritySuffix}` + : undefined; + const isHighlighted = entries.isHighlighted[i]; + const className = classNames( + priorityClass, + isHighlighted && `${config.classPrefix}__row--highlighted`, + ); + + const extraCells = + config.renderExtraCells?.(entries, i, className ?? '', trace) ?? []; + + const row = [ + m(GridCell, {className}, m(Timestamp, {trace, ts})), + m( + GridCell, + {className, align: 'right'}, + entries.upids[i] !== null + ? m( + Anchor, + { + onclick: () => + trace.scrollTo({ + track: { + uri: `/process_${entries.upids[i]}`, + expandGroup: true, + }, + }), + }, + String(entries.pids[i]), + ) + : String(entries.pids[i]), + ), + m( + GridCell, + {className, align: 'right'}, + entries.utids[i] !== null + ? m( + Anchor, + { + onclick: () => { + const uri = `${getThreadUriPrefix(entries.upids[i], entries.utids[i]!)}_state`; + trace.scrollTo({track: {uri, expandGroup: true}}); + }, + }, + String(entries.tids[i]), + ) + : String(entries.tids[i]), + ), + m(GridCell, {className}, priorityLetter), + hasProcessNames && m(GridCell, {className}, entries.processName[i]), + m(GridCell, {className}, entries.tags[i]), + ...extraCells, + m(GridCell, {className}, entries.messages[i]), + ].filter(Boolean); + + rows.push(row); + } + return rows; + } +} + +// --------------------------------------------------------------------------- +// Shared SQL helpers +// --------------------------------------------------------------------------- + +export function serializeTags(tags: string[]): string { + return tags.map((tag) => escapeQuery(tag)).join(); +} + +export function composeGlobMatch( + isCollapsed: boolean, + textEntry: string, +): string { + if (isCollapsed) { + return `msg glob ${escapeSearchQuery(textEntry)} as is_msg_chosen, + (process.name is not null and process.name glob ${escapeSearchQuery( + textEntry, + )}) as is_process_chosen, + 0 as is_msg_highlighted, + 0 as is_process_highlighted`; + } else if (!textEntry) { + return `1 as is_msg_chosen, + 1 as is_process_chosen, + 0 as is_msg_highlighted, + 0 as is_process_highlighted`; + } else { + return `1 as is_msg_chosen, + 1 as is_process_chosen, + msg glob ${escapeSearchQuery(textEntry)} as is_msg_highlighted, + (process.name is not null and process.name glob ${escapeSearchQuery( + textEntry, + )}) as is_process_highlighted`; + } +} + +// --------------------------------------------------------------------------- +// Shared query functions +// --------------------------------------------------------------------------- + +async function updateLogView( + engine: Engine, + filter: BaseLogFilteringCriteria, + config: LogPanelConfig, +): Promise { + const globMatch = composeGlobMatch(filter.hideNonMatching, filter.textEntry); + const selectedRows = config.buildSelectedRows(filter, globMatch); + const tableName = config.filteredTableName; + + await engine.query(`create perfetto table ${tableName} as select * + from (${selectedRows}) + where is_msg_chosen is 1 or is_process_chosen is 1`); + + return { + async [Symbol.asyncDispose]() { + await engine.query(`drop table ${tableName}`); + }, + }; +} + +async function updateLogEntries( + engine: Engine, + span: TimeSpan, + pagination: Pagination, + config: LogPanelConfig, +): Promise { + const tableName = config.filteredTableName; + const extraSelect = + config.extraSelectColumns && config.extraSelectColumns.length > 0 + ? ',\n ' + config.extraSelectColumns.join(',\n ') + : ''; + + const rowsResult = await engine.query(` + select + ts, + pid, + tid, + prio, + ifnull(tag, '[NULL]') as tag, + ifnull(msg, '[NULL]') as msg, + is_msg_highlighted as isMsgHighlighted, + is_process_highlighted as isProcessHighlighted, + ifnull(process_name, '') as processName, + utid, + upid${extraSelect} + from ${tableName} + where ts >= ${span.start} and ts <= ${span.end} + order by ts + limit ${pagination.offset}, ${pagination.count} + `); + + const timestamps: time[] = []; + const pids: bigint[] = []; + const tids: bigint[] = []; + const priorities: number[] = []; + const tags: string[] = []; + const messages: string[] = []; + const isHighlighted: boolean[] = []; + const processName: string[] = []; + const utids: (number | null)[] = []; + const upids: (number | null)[] = []; + const extra: Record = {}; + + const schema: Row = { + ts: LONG, + pid: LONG_NULL, + tid: LONG_NULL, + prio: NUM, + tag: STR, + msg: STR, + isMsgHighlighted: NUM_NULL, + isProcessHighlighted: NUM, + processName: STR, + utid: NUM_NULL, + upid: NUM_NULL, + ...(config.extraSchema ?? {}), + }; + + const it = rowsResult.iter(schema); + for (; it.valid(); it.next()) { + const row = it as Row; + timestamps.push(Time.fromRaw(row.ts as bigint)); + pids.push((row.pid as bigint | null) ?? 0n); + tids.push((row.tid as bigint | null) ?? 0n); + priorities.push(row.prio as number); + tags.push(row.tag as string); + messages.push(row.msg as string); + isHighlighted.push( + row.isMsgHighlighted === 1 || row.isProcessHighlighted === 1, + ); + processName.push(row.processName as string); + utids.push((row.utid as number | null) ?? null); + upids.push((row.upid as number | null) ?? null); + config.collectExtraRow?.(row, extra); + } + + const queryRes = await engine.query(` + select count(*) as totalEvents + from ${tableName} + where ts >= ${span.start} and ts <= ${span.end} + `); + const {totalEvents} = queryRes.firstRow({totalEvents: NUM}); + + return { + offset: pagination.offset, + timestamps, + pids, + tids, + priorities, + tags, + messages, + isHighlighted, + processName, + utids, + upids, + totalEvents, + extra, + }; +} diff --git a/ui/src/core/embedder/default_plugins.ts b/ui/src/core/embedder/default_plugins.ts index 7a42cb1a53e..8a36a9f34ba 100644 --- a/ui/src/core/embedder/default_plugins.ts +++ b/ui/src/core/embedder/default_plugins.ts @@ -69,6 +69,7 @@ export const defaultPlugins = [ 'dev.perfetto.Ftrace', 'dev.perfetto.GlobalGroups', 'dev.perfetto.Gpu', + 'dev.perfetto.JournaldLog', 'dev.perfetto.HeapProfile', 'dev.perfetto.InstrumentsSamplesProfile', 'dev.perfetto.KernelTrackEvent', diff --git a/ui/src/plugins/com.android.AndroidLog/logs_panel.ts b/ui/src/plugins/com.android.AndroidLog/logs_panel.ts index a070872bc58..20022f498ef 100644 --- a/ui/src/plugins/com.android.AndroidLog/logs_panel.ts +++ b/ui/src/plugins/com.android.AndroidLog/logs_panel.ts @@ -13,48 +13,25 @@ // limitations under the License. import m from 'mithril'; -import {type time, Time, type TimeSpan} from '../../base/time'; -import {DetailsShell} from '../../widgets/details_shell'; -import {Timestamp} from '../../components/widgets/timestamp'; -import type {Engine} from '../../trace_processor/engine'; -import {LONG, NUM, NUM_NULL, STR} from '../../trace_processor/query_result'; import { - escapeQuery, - escapeSearchQuery, - escapeRegexQuery, -} from '../../trace_processor/query_utils'; -import {Select} from '../../widgets/select'; + LogPanel as SharedLogPanel, + type LogPanelConfig, + type BaseLogFilteringCriteria, + type LogPanelAttrs as SharedLogPanelAttrs, + serializeTags, +} from '../../components/widgets/log_panel/log_panel'; +import type {Store} from '../../base/store'; +import type {Trace} from '../../public/trace'; +import {escapeRegexQuery} from '../../trace_processor/query_utils'; import { type MultiSelectDiff, type MultiSelectOption, PopupMultiSelect, } from '../../widgets/multiselect'; import {PopupPosition} from '../../widgets/popup'; -import {Button} from '../../widgets/button'; -import {TextInput} from '../../widgets/text_input'; -import { - Grid, - type GridColumn, - type GridRow, - GridHeaderCell, - GridCell, -} from '../../widgets/grid'; -import {classNames} from '../../base/classnames'; -import {TagInput} from '../../widgets/tag_input'; -import type {Store} from '../../base/store'; -import type {Trace} from '../../public/trace'; import {Icons} from '../../base/semantic_icons'; -import {MenuItem} from '../../widgets/menu'; -import {SerialTaskQueue, QuerySlot} from '../../base/query_slot'; - -const ROW_H = 24; -export interface LogFilteringCriteria { - readonly minimumLevel: number; - readonly tags: string[]; - readonly isTagRegex?: boolean; - readonly textEntry: string; - readonly hideNonMatching: boolean; +export interface LogFilteringCriteria extends BaseLogFilteringCriteria { readonly machineExcludeList: number[]; } @@ -68,216 +45,20 @@ export interface LogPanelAttrs { readonly trace: Trace; } -interface Pagination { - readonly offset: number; - readonly count: number; -} - -interface LogEntries { - readonly offset: number; - readonly ids: number[]; - readonly machineIds: number[]; - readonly timestamps: time[]; - readonly pids: bigint[]; - readonly tids: bigint[]; - readonly priorities: number[]; - readonly tags: string[]; - readonly messages: string[]; - readonly isHighlighted: boolean[]; - readonly processName: string[]; - readonly totalEvents: number; // Count of the total number of events within this window -} - -export class LogPanel implements m.ClassComponent { - private readonly trace: Trace; - private readonly executor = new SerialTaskQueue(); - private readonly viewQuery = new QuerySlot(this.executor); - private readonly entriesQuery = new QuerySlot(this.executor); - private pagination: Pagination = { - offset: 0, - count: 0, - }; - - constructor({attrs}: m.CVnode) { - this.trace = attrs.trace; - } - - onremove() { - this.viewQuery.dispose(); - this.entriesQuery.dispose(); - } - - view({attrs}: m.CVnode) { - const visibleSpan = attrs.trace.timeline.visibleWindow.toTimeSpan(); - const filters = attrs.filterStore.state; - const pagination = this.pagination; - const engine = attrs.trace.engine; - - // Query 1: Create the filtered_logs table (no staleOn = always-fresh) - const viewResult = this.viewQuery.use({ - key: {filters}, - queryFn: () => updateLogView(engine, filters), - }); - - // Query 2: Read from the table (staleOn=['pagination'] for smooth scrolling) - const entriesResult = this.entriesQuery.use({ - key: { - filters, - viewport: {start: visibleSpan.start, end: visibleSpan.end}, - pagination, - }, - retainOn: ['pagination', 'viewport'], - queryFn: () => updateLogEntries(engine, visibleSpan, pagination), - enabled: !!viewResult.data, - }); - - const entries = entriesResult.data; - const totalEvents = entries?.totalEvents ?? 0; - - return m( - DetailsShell, - { - title: 'Android Logs', - description: `Total messages: ${totalEvents}`, - buttons: m(LogsFilters, { - trace: attrs.trace, - cache: attrs.cache, - store: attrs.filterStore, - }), - }, - this.renderGrid(attrs.trace, entries, attrs.cache), - ); - } - - private renderGrid( - trace: Trace, - entries: LogEntries | undefined, - cache: LogPanelCache, - ) { - if (entries) { - const hasMachineIds = cache.uniqueMachineIds.length > 1; - const hasProcessNames = - entries.processName.filter((name) => name).length > 0; - - const columns: GridColumn[] = [ - ...(hasMachineIds - ? [{key: 'machine', header: m(GridHeaderCell, 'Machine')}] - : []), - {key: 'timestamp', header: m(GridHeaderCell, 'Timestamp')}, - {key: 'pid', header: m(GridHeaderCell, 'PID')}, - {key: 'tid', header: m(GridHeaderCell, 'TID')}, - {key: 'level', header: m(GridHeaderCell, 'Level')}, - ...(hasProcessNames - ? [{key: 'process', header: m(GridHeaderCell, 'Process')}] - : []), - {key: 'tag', header: m(GridHeaderCell, 'Tag')}, - { - key: 'message', - // Allow the initial width of the message column to expand as needed. - maxInitialWidthPx: Infinity, - header: m(GridHeaderCell, 'Message'), - }, - ]; - - return m(Grid, { - className: 'pf-logs-panel', - columns, - rowData: { - data: this.renderRows(entries, hasMachineIds, hasProcessNames), - total: entries?.totalEvents ?? 0, - offset: entries?.offset ?? 0, - onLoadData: (offset, count) => { - this.pagination = {offset, count}; - m.redraw(); - }, - }, - virtualization: { - rowHeightPx: ROW_H, - }, - fillHeight: true, - onRowHover: (rowIndex) => { - // Calculate the actual row index from virtualization offset - const actualIndex = rowIndex - (entries?.offset ?? 0); - const timestamp = entries?.timestamps[actualIndex]; - if (timestamp !== undefined) { - trace.timeline.hoverCursorTimestamp = timestamp; - } - }, - onRowOut: () => { - trace.timeline.hoverCursorTimestamp = undefined; - }, - }); - } else { - return null; - } - } - - private renderRows( - entries: LogEntries, - hasMachineIds: boolean | undefined, - hasProcessNames: boolean | undefined, - ): ReadonlyArray { - const trace = this.trace; - const ids = entries.ids; - const machineIds = entries.machineIds; - const timestamps = entries.timestamps; - const pids = entries.pids; - const tids = entries.tids; - const priorities = entries.priorities; - const tags = entries.tags; - const messages = entries.messages; - const processNames = entries.processName; - - const rows: GridRow[] = []; - for (let i = 0; i < entries.timestamps.length; i++) { - const priority = priorities[i]; - const priorityLetter = LOG_PRIORITIES[priority][0]; - const ts = timestamps[i]; - const eventId = ids[i]; - const priorityClass = `pf-logs-panel__row--${classForPriority(priority)}`; - const isHighlighted = entries.isHighlighted[i]; - const className = classNames( - priorityClass, - isHighlighted && 'pf-logs-panel__row--highlighted', - ); - - const row = [ - hasMachineIds && - m(GridCell, {className, align: 'right'}, machineIds[i]), - m( - GridCell, - { - className, - menuItems: m(MenuItem, { - label: 'Go to event on timeline', - icon: Icons.UpdateSelection, - onclick: () => { - trace.selection.selectSqlEvent('android_logs', eventId, { - scrollToSelection: true, - switchToCurrentSelectionTab: true, - }); - }, - }), - }, - m(Timestamp, {trace, ts}), - ), - m(GridCell, {className, align: 'right'}, String(pids[i])), - m(GridCell, {className, align: 'right'}, String(tids[i])), - m(GridCell, {className}, priorityLetter || '?'), - hasProcessNames && m(GridCell, {className}, processNames[i]), - m(GridCell, {className}, tags[i]), - m(GridCell, {className}, messages[i]), - ].filter(Boolean); - - rows.push(row); - } - - return rows; - } -} +// Android log priorities: index = numeric priority value. +const ANDROID_PRIORITIES = [ + '-', + '-', + 'Verbose', + 'Debug', + 'Info', + 'Warn', + 'Error', + 'Fatal', +]; -function classForPriority(priority: number) { - switch (priority) { +function classForPriority(p: number): string | undefined { + switch (p) { case 2: return 'verbose'; case 3: @@ -295,342 +76,86 @@ function classForPriority(priority: number) { } } -export const LOG_PRIORITIES = [ - '-', - '-', - 'Verbose', - 'Debug', - 'Info', - 'Warn', - 'Error', - 'Fatal', -]; -const IGNORED_STATES = 2; - -interface LogPriorityWidgetAttrs { - readonly trace: Trace; - readonly options: string[]; - readonly selectedIndex: number; - readonly onSelect: (id: number) => void; -} +function buildAndroidConfig( + cache: LogPanelCache, + filterStore: Store, +): LogPanelConfig { + return { + title: 'Android Logs', + priorities: ANDROID_PRIORITIES, + ignoredPriorityStates: 2, + filteredTableName: 'filtered_logs', + classPrefix: 'pf-logs-panel', + classForPriority, + buildSelectedRows: ( + filter: BaseLogFilteringCriteria, + globMatch: string, + ) => { + const androidFilter = filter as LogFilteringCriteria; + let sql = `select android_logs.id, prio, ts, pid, tid, tag, msg, + process.name as process_name, + thread.utid as utid, thread.upid as upid, + ${globMatch} + from android_logs + left join thread using(utid) + left join process using(upid) + where prio >= ${filter.minimumLevel}`; + if (filter.tags.length) { + sql += filter.isTagRegex + ? ` and (${filter.tags.map((p) => `tag glob ${escapeRegexQuery(p)}`).join(' OR ')})` + : ` and tag in (${serializeTags(filter.tags)})`; + } + if (androidFilter.machineExcludeList?.length) { + sql += ` and process.machine_id not in (${androidFilter.machineExcludeList.join(',')})`; + } + return sql; + }, + renderExtraFilters: (store: Store) => { + const hasMachineIds = cache.uniqueMachineIds.length > 1; + if (!hasMachineIds) return null; -class LogPriorityWidget implements m.ClassComponent { - view(vnode: m.Vnode) { - const attrs = vnode.attrs; - const optionComponents = []; - for (let i = IGNORED_STATES; i < attrs.options.length; i++) { - const selected = i === attrs.selectedIndex; - optionComponents.push( - m('option', {value: i, selected}, attrs.options[i]), + const androidStore = store as Store; + const machineExcludeList = androidStore.state.machineExcludeList ?? []; + const options: MultiSelectOption[] = cache.uniqueMachineIds.map( + (uMachineId) => ({ + id: String(uMachineId), + name: `Machine ${uMachineId}`, + checked: !machineExcludeList.some((x) => x === uMachineId), + }), ); - } - return m( - Select, - { - onchange: (e: Event) => { - const selectionValue = (e.target as HTMLSelectElement).value; - attrs.onSelect(Number(selectionValue)); - }, - }, - optionComponents, - ); - } -} - -interface LogTextWidgetAttrs { - readonly trace: Trace; - readonly onChange: (value: string) => void; -} - -class LogTextWidget implements m.ClassComponent { - view({attrs}: m.CVnode) { - return m(TextInput, { - leftIcon: 'search', - placeholder: 'Search logs...', - onkeyup: (e: KeyboardEvent) => { - // We want to use the value of the input field after it has been - // updated with the latest key (onkeyup). - const htmlElement = e.target as HTMLInputElement; - attrs.onChange(htmlElement.value); - }, - }); - } -} -interface FilterByTextWidgetAttrs { - readonly hideNonMatching: boolean; - readonly onClick: () => void; -} - -class FilterByTextWidget implements m.ClassComponent { - view({attrs}: m.Vnode) { - const icon = attrs.hideNonMatching ? Icons.Filter : Icons.FilterOff; - const tooltip = attrs.hideNonMatching - ? 'Show all logs and highlight matches' - : 'Show only matching logs'; - return m(Button, { - icon, - tooltip, - onclick: attrs.onClick, - }); - } -} - -interface LogsFiltersAttrs { - readonly trace: Trace; - readonly cache: LogPanelCache; - readonly store: Store; -} - -export class LogsFilters implements m.ClassComponent { - view({attrs}: m.CVnode) { - const hasMachineIds = attrs.cache.uniqueMachineIds.length > 1; - - return [ - m('span', 'Log Level'), - m(LogPriorityWidget, { - trace: attrs.trace, - options: LOG_PRIORITIES, - selectedIndex: attrs.store.state.minimumLevel, - onSelect: (minimumLevel) => { - attrs.store.edit((draft) => { - draft.minimumLevel = minimumLevel; - }); - }, - }), - m(TagInput, { - leftIcon: 'label', - placeholder: 'Filter by tag...', - tags: attrs.store.state.tags, - onTagAdd: (tag) => { - attrs.store.edit((draft) => { - draft.tags.push(tag); - }); - }, - onTagRemove: (index) => { - attrs.store.edit((draft) => { - draft.tags.splice(index, 1); - }); - }, - }), - m(Button, { - icon: 'regular_expression', - tooltip: 'Use regex', - active: !!attrs.store.state.isTagRegex, - onclick: () => { - attrs.store.edit((draft) => { - draft.isTagRegex = !draft.isTagRegex; - }); - }, - }), - m(LogTextWidget, { - trace: attrs.trace, - onChange: (text) => { - attrs.store.edit((draft) => { - draft.textEntry = text; + return m(PopupMultiSelect, { + label: 'Filter by machine', + icon: Icons.Filter, + position: PopupPosition.Top, + options, + onChange: (diffs: MultiSelectDiff[]) => { + const newList = new Set(machineExcludeList); + diffs.forEach(({checked, id}) => { + const machineId = Number(id); + if (checked) { + newList.delete(machineId); + } else { + newList.add(machineId); + } }); - }, - }), - m(FilterByTextWidget, { - hideNonMatching: attrs.store.state.hideNonMatching, - onClick: () => { - attrs.store.edit((draft) => { - draft.hideNonMatching = !draft.hideNonMatching; + filterStore.edit((draft) => { + draft.machineExcludeList = Array.from(newList); }); }, - }), - hasMachineIds && this.renderFilterPanel(attrs), - ]; - } - - private renderFilterPanel(attrs: LogsFiltersAttrs) { - const machineExcludeList = attrs.store.state.machineExcludeList; - const options: MultiSelectOption[] = attrs.cache.uniqueMachineIds.map( - (uMachineId) => { - return { - id: String(uMachineId), - name: `Machine ${uMachineId}`, - checked: !machineExcludeList.some( - (excluded: number) => excluded === uMachineId, - ), - }; - }, - ); - - return m(PopupMultiSelect, { - label: 'Filter by machine', - icon: Icons.Filter, - position: PopupPosition.Top, - options, - onChange: (diffs: MultiSelectDiff[]) => { - const newList = new Set(machineExcludeList); - diffs.forEach(({checked, id}) => { - const machineId = Number(id); - if (checked) { - newList.delete(machineId); - } else { - newList.add(machineId); - } - }); - attrs.store.edit((draft) => { - draft.machineExcludeList = Array.from(newList); - }); - }, - }); - } -} - -async function updateLogEntries( - engine: Engine, - span: TimeSpan, - pagination: Pagination, -): Promise { - const rowsResult = await engine.query(` - select - id, - ts, - pid, - tid, - prio, - ifnull(tag, '[NULL]') as tag, - ifnull(msg, '[NULL]') as msg, - is_msg_highlighted as isMsgHighlighted, - is_process_highlighted as isProcessHighlighted, - ifnull(process_name, '') as processName, - machine_id as machineId - from filtered_logs - where ts >= ${span.start} and ts <= ${span.end} - order by ts - limit ${pagination.offset}, ${pagination.count} - `); - - const ids: number[] = []; - const machineIds = []; - const timestamps: time[] = []; - const pids = []; - const tids = []; - const priorities = []; - const tags = []; - const messages = []; - const isHighlighted = []; - const processName = []; - - const it = rowsResult.iter({ - id: NUM, - ts: LONG, - pid: LONG, - tid: LONG, - prio: NUM, - tag: STR, - msg: STR, - isMsgHighlighted: NUM_NULL, - isProcessHighlighted: NUM, - processName: STR, - machineId: NUM, - }); - for (; it.valid(); it.next()) { - ids.push(it.id); - timestamps.push(Time.fromRaw(it.ts)); - pids.push(it.pid); - tids.push(it.tid); - priorities.push(it.prio); - tags.push(it.tag); - messages.push(it.msg); - isHighlighted.push( - it.isMsgHighlighted === 1 || it.isProcessHighlighted === 1, - ); - processName.push(it.processName); - machineIds.push(it.machineId); - } - - const queryRes = await engine.query(` - select - count(*) as totalEvents - from filtered_logs - where ts >= ${span.start} and ts <= ${span.end} - `); - const {totalEvents} = queryRes.firstRow({totalEvents: NUM}); - - return { - offset: pagination.offset, - ids, - machineIds, - timestamps, - pids, - tids, - priorities, - tags, - messages, - isHighlighted, - processName, - totalEvents, - }; -} - -async function updateLogView( - engine: Engine, - filter: LogFilteringCriteria, -): Promise { - const globMatch = composeGlobMatch(filter.hideNonMatching, filter.textEntry); - let selectedRows = `select android_logs.id, prio, ts, pid, tid, tag, msg, - process.name as process_name, - process.machine_id as machine_id, ${globMatch} - from android_logs - left join thread using(utid) - left join process using(upid) - where prio >= ${filter.minimumLevel}`; - if (filter.tags.length) { - if (filter.isTagRegex) { - const tagGlobClauses = filter.tags.map( - (pattern) => `tag glob ${escapeRegexQuery(pattern)}`, - ); - selectedRows += ` and (${tagGlobClauses.join(' OR ')})`; - } else { - selectedRows += ` and tag in (${serializeTags(filter.tags)})`; - } - } - if (filter.machineExcludeList.length) { - selectedRows += ` and process.machine_id not in (${filter.machineExcludeList.join(',')})`; - } - - // We extract only the rows which will be visible. - await engine.query(`create perfetto table filtered_logs as select * - from (${selectedRows}) - where is_msg_chosen is 1 or is_process_chosen is 1`); - - return { - async [Symbol.asyncDispose]() { - await engine.query('drop table filtered_logs'); + }); }, }; } -function serializeTags(tags: string[]) { - return tags.map((tag) => escapeQuery(tag)).join(); -} - -function composeGlobMatch(isCollaped: boolean, textEntry: string) { - if (isCollaped) { - // If the entries are collapsed, we won't highlight any lines. - return `msg glob ${escapeSearchQuery(textEntry)} as is_msg_chosen, - (process.name is not null and process.name glob ${escapeSearchQuery( - textEntry, - )}) as is_process_chosen, - 0 as is_msg_highlighted, - 0 as is_process_highlighted`; - } else if (!textEntry) { - // If there is no text entry, we will show all lines, but won't highlight. - // any. - return `1 as is_msg_chosen, - 1 as is_process_chosen, - 0 as is_msg_highlighted, - 0 as is_process_highlighted`; - } else { - return `1 as is_msg_chosen, - 1 as is_process_chosen, - msg glob ${escapeSearchQuery(textEntry)} as is_msg_highlighted, - (process.name is not null and process.name glob ${escapeSearchQuery( - textEntry, - )}) as is_process_highlighted`; +export class LogPanel implements m.ClassComponent { + view({attrs}: m.CVnode): m.Children { + const config = buildAndroidConfig(attrs.cache, attrs.filterStore); + const panelAttrs: SharedLogPanelAttrs = { + config, + filterStore: attrs.filterStore as Store, + trace: attrs.trace, + }; + return m(SharedLogPanel, panelAttrs); } } diff --git a/ui/src/plugins/com.android.AndroidLog/logs_track.ts b/ui/src/plugins/com.android.AndroidLog/logs_track.ts index f226fc470e3..fc6148fd63e 100644 --- a/ui/src/plugins/com.android.AndroidLog/logs_track.ts +++ b/ui/src/plugins/com.android.AndroidLog/logs_track.ts @@ -13,32 +13,28 @@ // limitations under the License. import m from 'mithril'; -import {LONG, NUM, STR, STR_NULL} from '../../trace_processor/query_result'; +import {makeColorScheme} from '../../components/colorizer'; +import {HSLColor} from '../../base/color'; import type {Trace} from '../../public/trace'; +import { + LONG, + NUM, + NUM_NULL, + STR, + STR_NULL, +} from '../../trace_processor/query_result'; +import {createLogTrack} from '../../components/tracks/log_track'; import {SliceTrack} from '../../components/tracks/slice_track'; import {SourceDataset} from '../../trace_processor/dataset'; -import {makeColorScheme} from '../../components/colorizer'; -import {HSLColor} from '../../base/color'; -import {Section} from '../../widgets/section'; -import {Tree, TreeNode} from '../../widgets/tree'; -import {Timestamp} from '../../components/widgets/timestamp'; -import {Time} from '../../base/time'; -import {DetailsShell} from '../../widgets/details_shell'; -import {GridLayout, GridLayoutColumn} from '../../widgets/grid_layout'; -import {Spinner} from '../../widgets/spinner'; -import type {ColorScheme} from '../../base/color_scheme'; -const PRIO_TO_COLOR: Record = { - 2: makeColorScheme(new HSLColor({h: 0, s: 0, l: 60})), // V - grey - 3: makeColorScheme(new HSLColor({h: 217, s: 100, l: 60})), // D - blue - 4: makeColorScheme(new HSLColor({h: 120, s: 62, l: 55})), // I - green - 5: makeColorScheme(new HSLColor({h: 38, s: 100, l: 58})), // W - amber - 6: makeColorScheme(new HSLColor({h: 0, s: 86, l: 60})), // E - red - 7: makeColorScheme(new HSLColor({h: 268, s: 100, l: 65})), // F - magenta -}; -const DEFAULT_PRIO_COLOR = makeColorScheme(new HSLColor({h: 0, s: 0, l: 60})); - -const EVT_PX = 6; // Width of an event tick in pixels. +// Android priority depths: prio<=3 -> 0, 4 -> 1, 5 -> 2, 6 -> 3, 7 -> 4. +const DEPTH_TO_COLOR = [ + makeColorScheme(new HSLColor({h: 0, s: 0, l: 60})), // prio<=3 V/D — grey + makeColorScheme(new HSLColor({h: 120, s: 62, l: 55})), // prio=4 I — green + makeColorScheme(new HSLColor({h: 38, s: 100, l: 58})), // prio=5 W — amber + makeColorScheme(new HSLColor({h: 0, s: 86, l: 60})), // prio=6 E — red + makeColorScheme(new HSLColor({h: 268, s: 100, l: 65})), // prio=7 F — magenta +]; const LOGS_SQL = ` select @@ -64,113 +60,26 @@ const LOGS_SCHEMA = { id: NUM, ts: LONG, prio: NUM, - utid: NUM, + utid: NUM_NULL, depth: NUM, tag: STR_NULL, msg: STR_NULL, }; -const PRIO_LABELS: Record = { - 2: 'Verbose', - 3: 'Debug', - 4: 'Info', - 5: 'Warn', - 6: 'Error', - 7: 'Fatal', -}; - -function makeDetailsPanel( - trace: Trace, - row: { - id: number; - ts: bigint; - prio: number; - tag: string | null; - utid: number; - }, -) { - // The msg is initially undefined, it'll be filled in when it loads - let msg: string | undefined; - let threadInfo: string | undefined; - - // Quickly load the log message - trace.engine - .query(`select msg from android_logs where id = ${row.id}`) - .then((result) => { - msg = result.maybeFirstRow({msg: STR})?.msg; - }); - - trace.engine - .query(`select tid, name from thread where utid = ${row.utid}`) - .then((result) => { - const r = result.maybeFirstRow({tid: NUM, name: STR_NULL}); - threadInfo = r - ? r.name - ? `${r.name} [${r.tid}]` - : `${r.tid}` - : `utid=${row.utid}`; - }); - - const prioLabel = PRIO_LABELS[row.prio] ?? `${row.prio}`; - - return { - render() { - return m( - DetailsShell, - {title: `Android Log`}, - m( - GridLayout, - m( - GridLayoutColumn, - m( - Section, - {title: 'Details'}, - m( - Tree, - m(TreeNode, {left: 'ID', right: row.id}), - m(TreeNode, { - left: 'Timestamp', - right: m(Timestamp, {trace, ts: Time.fromRaw(row.ts)}), - }), - m(TreeNode, {left: 'Priority', right: prioLabel}), - m(TreeNode, {left: 'Tag', right: row.tag}), - m(TreeNode, { - left: 'Thread', - right: threadInfo ?? m(Spinner), - }), - m(TreeNode, { - left: 'Message', - right: msg !== undefined ? msg : m(Spinner), - }), - ), - ), - ), - ), - ); - }, - }; -} +const EVT_PX = 6; export function createAndroidLogTrack(trace: Trace, uri: string) { - return SliceTrack.create({ - trace, - uri, - dataset: new SourceDataset({src: LOGS_SQL, schema: LOGS_SCHEMA}), - initialMaxDepth: 4, - colorizer: (row) => PRIO_TO_COLOR[row.prio] ?? DEFAULT_PRIO_COLOR, - tooltip: (slice) => [m('', m('b', slice.row.tag)), m('', slice.row.msg)], - // All log events are instant events, render them as a little box rather - // than the default chevron. - instantStyle: { - width: EVT_PX, - render: (ctx, r) => ctx.fillRect(r.x, r.y, r.width, r.height), - }, - // Make rows a little more compact. - sliceLayout: { - padding: 2, - sliceHeight: 7, + return createLogTrack(trace, uri, { + title: 'Android Log', + rootTableName: 'android_logs', + sqlSource: LOGS_SQL, + depthColors: DEPTH_TO_COLOR, + fetchMsg: async (t, id) => { + const result = await t.engine.query( + `select msg from android_logs where id = ${id}`, + ); + return result.maybeFirstRow({msg: STR})?.msg; }, - detailsPanel: (row) => makeDetailsPanel(trace, row), }); } @@ -182,23 +91,20 @@ export function createPerProcessLogTrack( return SliceTrack.create({ trace, uri, + rootTableName: 'android_logs', dataset: new SourceDataset({ src: LOGS_SQL, schema: LOGS_SCHEMA, filter: {col: 'utid', in: utids}, }), initialMaxDepth: 4, - colorizer: (row) => PRIO_TO_COLOR[row.prio] ?? DEFAULT_PRIO_COLOR, + colorizer: (row) => DEPTH_TO_COLOR[row.depth], tooltip: (slice) => [m('', m('b', slice.row.tag)), m('', slice.row.msg)], instantStyle: { width: EVT_PX, render: (ctx, r) => ctx.fillRect(r.x, r.y, r.width, r.height), }, - sliceLayout: { - padding: 2, - sliceHeight: 7, - }, - detailsPanel: (row) => makeDetailsPanel(trace, row), + sliceLayout: {padding: 2, sliceHeight: 7}, }); } @@ -217,16 +123,12 @@ export function createPerThreadLogTrack( filter: {col: 'utid', eq: utid}, }), initialMaxDepth: 4, - colorizer: (row) => PRIO_TO_COLOR[row.prio] ?? DEFAULT_PRIO_COLOR, + colorizer: (row) => DEPTH_TO_COLOR[row.depth], tooltip: (slice) => [m('', m('b', slice.row.tag)), m('', slice.row.msg)], instantStyle: { width: EVT_PX, render: (ctx, r) => ctx.fillRect(r.x, r.y, r.width, r.height), }, - sliceLayout: { - padding: 2, - sliceHeight: 7, - }, - detailsPanel: (row) => makeDetailsPanel(trace, row), + sliceLayout: {padding: 2, sliceHeight: 7}, }); } diff --git a/ui/src/plugins/dev.perfetto.JournaldLog/index.ts b/ui/src/plugins/dev.perfetto.JournaldLog/index.ts new file mode 100644 index 00000000000..e8d2c765f7d --- /dev/null +++ b/ui/src/plugins/dev.perfetto.JournaldLog/index.ts @@ -0,0 +1,129 @@ +// Copyright (C) 2024 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 m from 'mithril'; +import { + type JournaldLogFilteringCriteria, + JournaldLogPanel, + type JournaldLogPanelCache, +} from './logs_panel'; +import {JOURNALD_LOGS_TRACK_KIND} from '../../public/track_kinds'; +import type {Trace} from '../../public/trace'; +import type {PerfettoPlugin} from '../../public/plugin'; +import {NUM, STR_NULL} from '../../trace_processor/query_result'; +import {createJournaldLogTrack} from './logs_track'; +import {exists} from '../../base/utils'; +import {TrackNode} from '../../public/workspace'; +import {escapeSearchQuery} from '../../trace_processor/query_utils'; + +const VERSION = 1; + +const DEFAULT_STATE: JournaldLogPluginState = { + version: VERSION, + filter: { + // Show all levels by default (7 = DEBUG, the least severe). + minimumLevel: 7, + tags: [], + isTagRegex: false, + textEntry: '', + hideNonMatching: true, + }, +}; + +interface JournaldLogPluginState { + version: number; + filter: JournaldLogFilteringCriteria; +} + +export default class implements PerfettoPlugin { + static readonly id = 'dev.perfetto.JournaldLog'; + async onTraceLoad(ctx: Trace): Promise { + const store = ctx.mountStore( + 'dev.perfetto.JournaldLogFilterState', + (init) => { + return exists(init) && (init as {version: unknown}).version === VERSION + ? (init as JournaldLogPluginState) + : DEFAULT_STATE; + }, + ); + + const result = await ctx.engine.query(` + INCLUDE PERFETTO MODULE linux.journald; + select count(1) as cnt from linux_journald_logs; + `); + const logCount = result.firstRow({cnt: NUM}).cnt; + const uri = 'perfetto.JournaldLog'; + if (logCount > 0) { + ctx.tracks.registerTrack({ + uri, + tags: {kinds: [JOURNALD_LOGS_TRACK_KIND]}, + renderer: createJournaldLogTrack(ctx, uri), + }); + const track = new TrackNode({ + name: 'Journald logs', + uri, + }); + ctx.defaultWorkspace.addChildInOrder(track); + } + + const journaldLogsTabUri = 'perfetto.JournaldLog#tab'; + + const filterStore = store.createSubStore( + ['filter'], + (x) => x as JournaldLogFilteringCriteria, + ); + + const cache: JournaldLogPanelCache = {}; + + ctx.tabs.registerTab({ + isEphemeral: false, + uri: journaldLogsTabUri, + content: { + render: () => m(JournaldLogPanel, {filterStore, cache, trace: ctx}), + getTitle: () => 'Journald Logs', + }, + }); + + if (logCount > 0) { + ctx.tabs.addDefaultTab(journaldLogsTabUri); + } + + ctx.commands.registerCommand({ + id: 'dev.perfetto.ShowJournaldLogsTab', + name: 'Show journald logs tab', + callback: () => { + ctx.tabs.showTab(journaldLogsTabUri); + }, + }); + + ctx.search.registerSearchProvider({ + name: 'Journald logs', + selectTracks(tracks) { + return tracks + .filter((track) => + track.tags?.kinds?.includes(JOURNALD_LOGS_TRACK_KIND), + ) + .filter((t) => + t.renderer.getDataset?.()?.implements({msg: STR_NULL}), + ); + }, + async getSearchFilter(searchTerm) { + return { + where: `msg GLOB ${escapeSearchQuery(searchTerm)}`, + columns: {msg: STR_NULL}, + }; + }, + }); + } +} diff --git a/ui/src/plugins/dev.perfetto.JournaldLog/logs_panel.scss b/ui/src/plugins/dev.perfetto.JournaldLog/logs_panel.scss new file mode 100644 index 00000000000..bed701127bf --- /dev/null +++ b/ui/src/plugins/dev.perfetto.JournaldLog/logs_panel.scss @@ -0,0 +1,48 @@ +// Copyright (C) 2024 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. + +@function log-level-color($color, $mix) { + @return color-mix(in srgb, $color, var(--pf-color-text) $mix); +} + +.pf-journald-logs-panel { + font-size: 11px; + font-family: var(--pf-font-monospace); + + &__row { + &--debug { + color: log-level-color(hsl(155, 79%, 51%), 30%); + } + + &--info { + color: var(--pf-color-text); + } + + &--warn { + color: log-level-color(hsl(45, 73%, 50%), 20%); + } + + &--error { + color: log-level-color(hsl(4, 100%, 50%), 20%); + } + + &--fatal { + color: log-level-color(hsl(291, 100%, 50%), 20%); + } + + &--highlighted { + background: var(--pf-color-highlight); + } + } +} diff --git a/ui/src/plugins/dev.perfetto.JournaldLog/logs_panel.ts b/ui/src/plugins/dev.perfetto.JournaldLog/logs_panel.ts new file mode 100644 index 00000000000..01c9c946f11 --- /dev/null +++ b/ui/src/plugins/dev.perfetto.JournaldLog/logs_panel.ts @@ -0,0 +1,118 @@ +// Copyright (C) 2024 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 m from 'mithril'; +import { + LogPanel, + type LogPanelAttrs, + type LogPanelConfig, + type BaseLogFilteringCriteria, + serializeTags, +} from '../../components/widgets/log_panel/log_panel'; +import type {Store} from '../../base/store'; +import type {Trace} from '../../public/trace'; +import {GridCell, GridHeaderCell} from '../../widgets/grid'; +import {escapeRegexQuery} from '../../trace_processor/query_utils'; +import {STR} from '../../trace_processor/query_result'; + +export interface JournaldLogFilteringCriteria + extends BaseLogFilteringCriteria {} + +// JournaldLogPanelCache is intentionally empty (no machine IDs needed), +// but kept for API consistency with the Android log panel. +export interface JournaldLogPanelCache {} + +export interface JournaldLogPanelAttrs { + readonly cache: JournaldLogPanelCache; + readonly filterStore: Store; + readonly trace: Trace; +} + +// Journald priorities: index = numeric syslog priority value. +export const JOURNALD_PRIORITIES = [ + 'Emergency', + 'Alert', + 'Critical', + 'Error', + 'Warning', + 'Notice', + 'Info', + 'Debug', +]; + +function classForPriority(p: number): string | undefined { + if (p <= 2) return 'fatal'; // EMERG, ALERT, CRIT + if (p === 3) return 'error'; // ERR + if (p === 4) return 'warn'; // WARNING + if (p <= 6) return 'info'; // NOTICE, INFO + if (p === 7) return 'debug'; + return undefined; +} + +const JOURNALD_CONFIG: LogPanelConfig = { + title: 'Journald Logs', + priorities: JOURNALD_PRIORITIES, + ignoredPriorityStates: 0, + filteredTableName: 'filtered_journald_logs', + classPrefix: 'pf-journald-logs-panel', + classForPriority, + buildSelectedRows: (filter, globMatch) => { + let sql = `select prio, ts, pid, tid, tag, msg, + process.name as process_name, + ifnull(linux_journald_logs.systemd_unit, '') as systemd_unit, + thread.utid as utid, thread.upid as upid, + ${globMatch} + from linux_journald_logs + left join thread using(utid) left join process using(upid) + where prio <= ${filter.minimumLevel}`; + if (filter.tags.length) { + sql += filter.isTagRegex + ? ` and (${filter.tags.map((p) => `tag glob ${escapeRegexQuery(p)}`).join(' OR ')})` + : ` and tag in (${serializeTags(filter.tags)})`; + } + return sql; + }, + extraSelectColumns: ["ifnull(systemd_unit, '') as systemdUnit"], + extraSchema: {systemdUnit: STR}, + collectExtraRow: (it, extra) => { + if (extra['systemdUnits'] === undefined) extra['systemdUnits'] = []; + extra['systemdUnits'].push(it.systemdUnit as string); + }, + extraColumns: (entries) => { + const units = entries.extra['systemdUnits'] as string[] | undefined; + if (!units?.some((u) => u)) return []; + return [{key: 'unit', header: m(GridHeaderCell, 'Unit')}]; + }, + renderExtraCells: (entries, i, className, _trace) => { + const units = entries.extra['systemdUnits'] as string[] | undefined; + if (!units?.some((u) => u)) return []; + return [m(GridCell, {className}, units[i])]; + }, +}; + +export class JournaldLogPanel + implements m.ClassComponent +{ + view({attrs}: m.CVnode): m.Children { + const panelAttrs: LogPanelAttrs = { + config: JOURNALD_CONFIG, + filterStore: attrs.filterStore as Store, + trace: attrs.trace, + }; + return m(LogPanel, panelAttrs); + } +} + +// Keep the old export name for backwards compatibility with index.ts +export {JournaldLogPanel as LogPanel}; diff --git a/ui/src/plugins/dev.perfetto.JournaldLog/logs_track.ts b/ui/src/plugins/dev.perfetto.JournaldLog/logs_track.ts new file mode 100644 index 00000000000..1aa6cc9d893 --- /dev/null +++ b/ui/src/plugins/dev.perfetto.JournaldLog/logs_track.ts @@ -0,0 +1,65 @@ +// Copyright (C) 2024 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 {makeColorScheme} from '../../components/colorizer'; +import {HSLColor} from '../../base/color'; +import type {Trace} from '../../public/trace'; +import {STR} from '../../trace_processor/query_result'; +import {createLogTrack} from '../../components/tracks/log_track'; + +// Journald priority: 0=EMERG … 7=DEBUG (lower number = worse severity). +// Depth 0 = most severe (EMERG/ALERT/CRIT), depth 4 = least (DEBUG). +const DEPTH_TO_COLOR = [ + makeColorScheme(new HSLColor({h: 291, s: 64, l: 42})), // EMERG/ALERT/CRIT — purple + makeColorScheme(new HSLColor({h: 4, s: 90, l: 58})), // ERR — red + makeColorScheme(new HSLColor({h: 45, s: 100, l: 51})), // WARNING — orange + makeColorScheme(new HSLColor({h: 0, s: 0, l: 70})), // NOTICE/INFO — grey + makeColorScheme(new HSLColor({h: 122, s: 39, l: 49})), // DEBUG — green +]; + +export function createJournaldLogTrack(trace: Trace, uri: string) { + return createLogTrack(trace, uri, { + title: 'Journald Log', + rootTableName: 'linux_journald_logs', + sqlSource: ` + select + id, + ts, + prio, + utid, + tag, + msg, + CASE + WHEN prio <= 2 THEN 0 + WHEN prio = 3 THEN 1 + WHEN prio = 4 THEN 2 + WHEN prio >= 5 AND prio <= 6 THEN 3 + WHEN prio = 7 THEN 4 + ELSE -1 + END as depth + from linux_journald_logs + order by ts + -- linux_journald_logs aren't guaranteed to be ordered by ts, but this is a + -- requirement for SliceTrack's mipmap operator to work + -- correctly, so we must explicitly sort them above. + `, + depthColors: DEPTH_TO_COLOR, + fetchMsg: async (t, id) => { + const result = await t.engine.query( + `select msg from journald_logs where id = ${id}`, + ); + return result.maybeFirstRow({msg: STR})?.msg; + }, + }); +} diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/index.ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/index.ts index e085fa73c62..6b1b0b38e91 100644 --- a/ui/src/plugins/dev.perfetto.RecordTraceV2/index.ts +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/index.ts @@ -25,6 +25,7 @@ import {chromeRecordSection} from './pages/chrome'; import {cpuRecordSection} from './pages/cpu'; import {gpuRecordSection} from './pages/gpu'; import {instructionsPage} from './pages/instructions_page'; +import {linuxRecordSection} from './pages/linux'; import {memoryRecordSection} from './pages/memory'; import {powerRecordSection} from './pages/power'; import {RecordPageV2} from './pages/record_page'; @@ -118,6 +119,7 @@ export default class implements PerfettoPlugin { gpuRecordSection(), powerRecordSection(), memoryRecordSection(), + linuxRecordSection(), androidRecordSection(), perfettoSDKRecordSection(), stackSamplingRecordSection(), diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/linux.ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/linux.ts new file mode 100644 index 00000000000..555f05fe18e --- /dev/null +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/linux.ts @@ -0,0 +1,89 @@ +// Copyright (C) 2026 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 type {RecordProbe, RecordSubpage} from '../config/config_interfaces'; +import type {TraceConfigBuilder} from '../config/trace_config_builder'; +import {Dropdown} from './widgets/dropdown'; +import {Textarea} from './widgets/textarea'; + +const JOURNALD_DS = 'linux.journald'; + +const MIN_PRIO_OPTIONS = [ + {value: 0, label: 'EMERG'}, + {value: 1, label: 'ALERT'}, + {value: 2, label: 'CRIT'}, + {value: 3, label: 'ERR'}, + {value: 4, label: 'WARNING'}, + {value: 5, label: 'NOTICE'}, + {value: 6, label: 'INFO'}, + {value: 7, label: 'DEBUG'}, +] as const; + +export function linuxRecordSection(): RecordSubpage { + return { + kind: 'PROBES_PAGE', + id: 'linux', + title: 'Linux', + subtitle: 'Linux-specific data sources', + icon: 'dns', + probes: [journald()], + }; +} + +function journald(): RecordProbe { + const settings = { + minPrio: new Dropdown({ + title: 'Minimum syslog priority', + options: MIN_PRIO_OPTIONS, + defaultValue: 7, + }), + identifiers: new Textarea({ + title: 'Process name (SYSLOG_IDENTIFIER) to record', + placeholder: 'One identifier per line\nsshd\nmy-service', + }), + units: new Textarea({ + title: 'systemd units to record', + placeholder: + 'One unit per line\nsystemd-journald.service\nuser@1000.service', + }), + }; + + return { + id: 'journald', + title: 'Journald log messages', + description: 'Records log messages from systemd-journald.', + supportedPlatforms: ['LINUX'], + settings, + genConfig(tc: TraceConfigBuilder) { + const ds = tc.addDataSource(JOURNALD_DS); + const journaldConfig = (ds.journaldConfig ??= {}); + journaldConfig.minPrio = settings.minPrio.value; + + const identifiers = settings.identifiers.text + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.length > 0); + if (identifiers.length > 0) { + journaldConfig.filterIdentifiers = identifiers; + } + const units = settings.units.text + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.length > 0); + if (units.length > 0) { + journaldConfig.filterUnits = units; + } + }, + }; +} diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/record_page.ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/record_page.ts index 0981d777832..69c44884fb9 100644 --- a/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/record_page.ts +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/record_page.ts @@ -158,6 +158,7 @@ export class RecordPageV2 implements m.ClassComponent { memory: 40, android: 50, network: 60, + linux: 65, chrome: 70, stack_sampling: 80, perfetto_sdk: 90, diff --git a/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/widgets/dropdown.ts b/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/widgets/dropdown.ts new file mode 100644 index 00000000000..64175746a11 --- /dev/null +++ b/ui/src/plugins/dev.perfetto.RecordTraceV2/pages/widgets/dropdown.ts @@ -0,0 +1,82 @@ +// Copyright (C) 2026 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 m from 'mithril'; +import {assertTrue} from '../../../../base/assert'; +import type {ProbeSetting} from '../../config/config_interfaces'; +import {Select} from '../../../../widgets/select'; + +export interface DropdownOption { + readonly value: T; + readonly label: string; +} + +export interface DropdownAttrs { + readonly title: string; + readonly options: ReadonlyArray>; + readonly defaultValue?: T; +} + +export class Dropdown implements ProbeSetting { + private _value: T; + + constructor(readonly attrs: DropdownAttrs) { + assertTrue(attrs.options.length > 0); + this._value = attrs.defaultValue ?? attrs.options[0].value; + } + + serialize() { + return this._value; + } + + deserialize(state: unknown): void { + if (typeof state !== 'string' && typeof state !== 'number') { + return; + } + const option = this.attrs.options.find( + (candidate) => candidate.value === state, + ); + if (option) { + this._value = option.value; + } + } + + get value(): T { + return this._value; + } + + render() { + return m('.textarea-holder', [ + m('header', this.attrs.title), + m( + Select, + { + value: String(this._value), + onchange: (e: Event) => { + const selectedValue = (e.target as HTMLSelectElement).value; + const option = this.attrs.options.find( + (candidate) => String(candidate.value) === selectedValue, + ); + if (option) { + this._value = option.value; + } + }, + }, + this.attrs.options.map((option) => + m('option', {value: String(option.value)}, option.label), + ), + ), + ]); + } +} diff --git a/ui/src/public/track_kinds.ts b/ui/src/public/track_kinds.ts index b200918cee3..a95bd8e4dc4 100644 --- a/ui/src/public/track_kinds.ts +++ b/ui/src/public/track_kinds.ts @@ -20,3 +20,4 @@ export const THREAD_STATE_TRACK_KIND = 'ThreadStateTrack'; export const SLICE_TRACK_KIND = 'SliceTrack'; export const COUNTER_TRACK_KIND = 'CounterTrack'; export const ANDROID_LOGS_TRACK_KIND = 'AndroidLogTrack'; +export const JOURNALD_LOGS_TRACK_KIND = 'JournaldLogTrack';