/*
 * Copyright (C) 2019 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef SRC_TRACE_PROCESSOR_STORAGE_STATS_H_
#define SRC_TRACE_PROCESSOR_STORAGE_STATS_H_

#include <cstddef>

namespace perfetto::trace_processor::stats {

// Compile time list of parsing and processing stats.
// clang-format off
#define PERFETTO_TP_STATS(F)                                                   \
  /* TODO(b/512786856): Restore severity to kError after flag rollout. */    \
  F(android_aflags_errors,               kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,          \
       "Errors occurred during the collection of Android aconfig flags by the "\
       "android.aflags data source. This typically happens if the aflags tool "\
       "fails or its output is malformed."),                                   \
  F(android_br_parse_errors,              kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(android_log_num_failed,               kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(android_log_format_invalid,           kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(android_log_num_skipped,              kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(android_log_num_total,                kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(deobfuscate_location_parse_error,     kSingle,  kError,    kAnalysis, Scope::kGlobal,          ""), \
  F(energy_breakdown_missing_values,      kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(energy_descriptor_invalid,            kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(entity_state_descriptor_invalid,      kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(entity_state_residency_invalid,       kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(entity_state_residency_lookup_failed, kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(energy_uid_breakdown_missing_values,  kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(frame_timeline_event_parser_errors,   kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(frame_timeline_unpaired_end_event,    kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(ftrace_bundle_tokenizer_errors,       kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(ftrace_cpu_bytes_begin,               kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_bytes_end,                 kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_bytes_delta,               kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_commit_overrun_begin,      kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_commit_overrun_end,        kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_commit_overrun_delta,      kIndexed, kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_dropped_events_begin,      kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_dropped_events_end,        kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_dropped_events_delta,      kIndexed, kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_entries_begin,             kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_entries_end,               kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_entries_delta,             kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_now_ts_begin,              kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_now_ts_end,                kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_oldest_event_ts_begin,     kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_oldest_event_ts_end,       kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_overrun_begin,             kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_overrun_end,               kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_overrun_delta,             kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_read_events_begin,         kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_read_events_end,           kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_read_events_delta,         kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(ftrace_cpu_has_data_loss,             kIndexed, kDataLoss, kTrace, Scope::kMachineAndTrace,         \
       "Ftrace data for the given cpu has data losses and is therefore "       \
       "unreliable. The kernel buffer overwrote events between our reads "     \
       "in userspace. Try re-recording the trace with a bigger buffer "        \
       "(ftrace_config.buffer_size_kb), or with fewer enabled ftrace events."),\
  F(ftrace_kprobe_hits_begin,             kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "The number of kretprobe hits at the beginning of the trace."),         \
  F(ftrace_kprobe_hits_end,               kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "The number of kretprobe hits at the end of the trace."),               \
  F(ftrace_kprobe_hits_delta,             kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "The number of kprobe hits encountered during the collection of the"    \
       "trace."),                                                              \
  F(ftrace_kprobe_misses_begin,           kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "The number of kretprobe missed events at the beginning of the trace."),\
  F(ftrace_kprobe_misses_end,             kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "The number of kretprobe missed events at the end of the trace."),      \
  F(ftrace_kprobe_misses_delta,           kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
       "The number of kretprobe missed events encountered during the "         \
       "collection of the trace. A value greater than zero is due to the "     \
       "maxactive parameter for the kretprobe being too small"),               \
  F(ftrace_setup_errors,                  kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "One or more atrace/ftrace categories were not found or failed to "     \
       "enable. See ftrace_setup_errors in the metadata table for details."),  \
  F(ftrace_abi_errors_skipped_zero_data_length,                                \
                                          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(ftrace_generic_descriptor_errors,     kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
       "The config is setting denser_generic_event_encoding, but there are "   \
       "issues with parsing or matching up the in-trace proto descriptors."),  \
  F(ftrace_thermal_exynos_acpm_unknown_tz_id,                                  \
                                          kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(fuchsia_non_numeric_counters,         kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(fuchsia_timestamp_overflow,           kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(fuchsia_record_read_error,            kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(fuchsia_invalid_event,                kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(fuchsia_invalid_event_arg_type,       kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(fuchsia_invalid_event_arg_name,       kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(fuchsia_unknown_event_arg,            kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(fuchsia_invalid_string_ref,           kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(generic_task_state_invalid_order,     kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
       "Invalid order of generic task state events. Should never happen."),    \
  F(gpu_counters_invalid_spec,            kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(gpu_counters_missing_spec,            kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(gpu_render_stage_parser_errors,       kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(graphics_frame_event_parser_errors,   kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(guess_trace_type_duration_ns,         kSingle,  kInfo,     kAnalysis, Scope::kGlobal, ""), \
  F(interned_data_tokenizer_errors,       kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(invalid_clock_snapshots,              kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(invalid_cpu_times,                    kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(kernel_wakelock_reused_id,            kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
       "Duplicated interning ID seen. Should never happen."),                  \
  F(kernel_wakelock_unknown_id,           kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
       "Interning ID not found. Should never happen."),                        \
  F(kernel_wakelock_zero_value_reported,  kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
       "Zero value received from SuspendControlService. Indicates a transient "\
       "error in SuspendControlService."),                                     \
  F(kernel_wakelock_non_monotonic_value_reported,                              \
                                          kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
       "Decreased value received from SuspendControlService. Indicates a "     \
       "transient error in SuspendControlService."),                           \
  F(kernel_wakelock_implausibly_large_value_reported,                          \
                                          kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
       "Implausibly large increment to value received from "                   \
       "SuspendControlService. Indicates a transient error in "                \
       "SuspendControlService."),                                              \
  F(kernel_trackevent_format_error,       kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
       "Ftrace event payloads did not match the format file while being "      \
       "parsed as kernel track events."),                                      \
  F(app_wakelock_parse_error,             kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
       "Parsing packed repeated field. Should never happen."),                 \
  F(app_wakelock_unknown_id,              kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
       "Interning ID not found. Should never happen."),                        \
  F(meminfo_unknown_keys,                 kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(missing_disk_io_event_name,           kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
       "ETW Disk IO tracker encountered an event with an opcode for which it " \
        "didn't have a name."),                                                \
  F(mismatched_sched_switch_tids,         kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(mm_unknown_type,                      kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(parse_trace_duration_ns,              kSingle,  kInfo,     kAnalysis, Scope::kGlobal, ""), \
  F(power_rail_empty_packet,              kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(power_rail_unknown_index,             kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(proc_stat_unknown_counters,           kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(rss_stat_unknown_keys,                kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(rss_stat_negative_size,               kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(rss_stat_unknown_thread_for_mm_id,    kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(filter_input_bytes,                   kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Number of bytes pre-TraceFilter. The trace file would have been this " \
       "many bytes big if the TraceConfig didn't specify any TraceFilter. "    \
       "This affects the actual buffer usage, as filtering happens only "      \
       "when writing into the trace file (or over IPC)."),                     \
  F(filter_input_packets,                 kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Number of packets pre-TraceFilter. The trace file would have had so "  \
       "many packets if the TraceConfig didn't specify any TraceFilter."),     \
  F(filter_output_bytes,                  kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Number of bytes that made it through the TraceFilter, before the "     \
       "(optional) Zlib compression stage."),                                  \
  F(filter_time_taken_ns,                 kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Time cumulatively spent running the TraceFilter throughout the "       \
       "tracing session by MaybeFilterPackets()."),                            \
  F(filter_errors,                        kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(flow_duplicate_id,                    kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(flow_no_enclosing_slice,              kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(flow_step_without_start,              kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(flow_end_without_start,               kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(flow_invalid_id,                      kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(flow_without_direction,               kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(stackprofile_empty_callstack,         kSingle,  kError,    kTrace, Scope::kMachineAndTrace,         \
      "Callstack had no frames. Ignored"),                                     \
  F(stackprofile_invalid_string_id,       kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(stackprofile_invalid_mapping_id,      kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(stackprofile_invalid_frame_id,        kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(stackprofile_invalid_callstack_id,    kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(stackprofile_parser_error,            kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(systemd_journal_num_failed,           kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(systemd_journal_num_total,            kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(systrace_parse_failure,               kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(task_state_invalid,                   kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(traced_buf_abi_violations,            kIndexed, kDataLoss, kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_buffer_size,               kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_bytes_overwritten,         kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_bytes_read,                kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_bytes_filtered_out,        kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Number of bytes discarded (input - output) by the TraceFilter for "    \
       "each buffer. It is a subset of, but does not add up perfectly to, "    \
       "(filter_input_bytes - filter_output_bytes) because of the synthetic "  \
       "metadata and stats packets generated by the tracing service itself."), \
  F(traced_buf_bytes_written,             kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_clone_done_timestamp_ns,   kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
    "The timestamp when the clone snapshot operation for this buffer "         \
    "finished"),                                                               \
  F(traced_buf_chunks_discarded,          kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_chunks_overwritten,        kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_chunks_read,               kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_chunks_rewritten,          kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_chunks_written,            kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_chunks_committed_out_of_order,                                  \
                                          kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_padding_bytes_cleared,     kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_padding_bytes_written,     kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_patches_failed,            kIndexed, kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "The tracing service potentially lost data from one of the data sources "\
      "writing into the given target_buffer. This entry can be ignored "       \
      "if you're using DISCARD buffers and traced_buf_chunks_discarded is "    \
      "nonzero, meaning that the buffer was filled."),                         \
  F(traced_buf_patches_succeeded,         kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_readaheads_failed,         kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_readaheads_succeeded,      kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_trace_writer_packet_loss,  kIndexed, kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "The tracing service observed packet loss for this buffer during this "  \
      "tracing session. This also counts packet loss that happened before "    \
      "the RING_BUFFER start or after the DISCARD buffer end."),               \
  F(traced_buf_sequence_packet_loss,      kIndexed, kDataLoss, kAnalysis, Scope::kMachineAndTrace,      \
      "The number of groups of consecutive packets lost in each sequence for " \
      "this buffer"),                                                          \
  F(traced_buf_incremental_sequences_dropped, kIndexed, kDataLoss, kAnalysis, Scope::kMachineAndTrace,  \
      "For a given buffer, indicates the number of sequences where all the "   \
      "packets on that sequence were dropped due to lack of a valid "          \
      "incremental state (i.e. interned data). This is usually a strong sign " \
      "that either: "                                                          \
      "1) incremental state invalidation is disabled. "                        \
      "2) the incremental state invalidation interval is too low. "            \
      "In either case, see "                                                   \
      "https://perfetto.dev/docs/concepts/buffers"                             \
      "#incremental-state-in-trace-packets"),                                  \
  F(traced_buf_write_wrap_count,          kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_buf_v2s_packets_seen,          kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Shadow mode: total packets read."),                                    \
  F(traced_buf_v2s_packets_in_both,       kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Shadow mode: packets found in both V1 and V2 buffers."),               \
  F(traced_buf_v2s_packets_only_v1,       kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Shadow mode: packets found only in V1 buffer."),                       \
  F(traced_buf_v2s_packets_only_v2,       kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Shadow mode: packets found only in V2 buffer."),                       \
  F(traced_buf_v2s_patches_attempted,     kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Shadow mode: patch operations attempted."),                            \
  F(traced_buf_v2s_v1_patches_succeeded,  kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Shadow mode: patches that succeeded on V1 buffer."),                   \
  F(traced_buf_v2s_v2_patches_succeeded,  kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Shadow mode: patches that succeeded on V2 buffer."),                   \
  F(traced_buf_v2s_stats_version,         kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Shadow mode: version of the comparison stats."),                       \
  F(traced_clone_started_timestamp_ns,    kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
    "The timestamp when the clone snapshot operation for this trace started"), \
  F(traced_clone_trigger_timestamp_ns,    kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
    "The timestamp when trigger for the clone snapshot operation for this "    \
    "trace was received"), \
  F(traced_chunks_discarded,              kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_data_sources_registered,       kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_data_sources_seen,             kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_final_flush_failed,            kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_final_flush_succeeded,         kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_flushes_failed,                kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_flushes_requested,             kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_flushes_succeeded,             kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_patches_discarded,             kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_producers_connected,           kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_producers_seen,                kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_total_buffers,                 kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(traced_tracing_sessions,              kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(track_event_parser_errors,            kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(track_event_dropped_packets_outside_of_range_of_interest,                  \
                                          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "The number of TrackEvent packets dropped by trace processor due to "    \
      "being outside of the range of interest. This happens if a trace has a " \
      "TrackEventRangeOfInterest packet, and track event dropping is "         \
      "enabled."),                                                             \
  F(track_event_tokenizer_errors,         kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(track_hierarchy_missing_uuid,         kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Upper bound on the number of events dropped due to track hierarchy "    \
      "validation failures where a parent UUID was not found. This stat is "   \
      "incremented each time trace processor attempts to resolve a track "     \
      "with an invalid hierarchy, which can happen in multiple contexts, "     \
      "making it an upper bound rather than an exact count of dropped events. "\
      "This is a bug in the trace producer."),                                 \
  F(track_event_thread_invalid_end,       kSingle,  kError,    kTrace, Scope::kMachineAndTrace,         \
      "The end event for a thread track does not match a track event "         \
      "begin event. This can happen on mixed atrace/track_event traces "       \
      "and is usually caused by data loss or bugs when the events are "        \
      "emitted. The outcome of this is that slices can appear to be closed "   \
      "before they were closed in reality"),                                   \
  F(tokenizer_skipped_packets,            kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(vmstat_unknown_keys,                  kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(psi_unknown_resource,                 kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(vulkan_allocations_invalid_string_id,                                      \
                                          kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(clock_sync_failure,                   kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(clock_sync_failure_unknown_source_clock, kSingle, kError, kAnalysis, Scope::kMachineAndTrace,      \
      "A packet with a timestamp could not be converted to trace time "        \
      "because the source clock ID has never appeared in any ClockSnapshot. "  \
      "This indicates the trace producer emitted packets with a clock_id but " \
      "never provided a ClockSnapshot for that clock. Check that "             \
      "ClockSnapshots are emitted before any packets using that clock_id. "    \
      "For sequence-scoped clocks (64-128), ensure each packet sequence "      \
      "emits its own ClockSnapshot."),                                         \
  F(clock_sync_failure_unknown_target_clock, kSingle, kError, kAnalysis, Scope::kMachineAndTrace,      \
      "A packet timestamp could not be converted because the target trace "    \
      "time clock has never appeared in any ClockSnapshot. This is an "        \
      "internal error that should not occur."),                                \
  F(clock_sync_failure_no_path, kSingle, kError, kAnalysis, Scope::kMachineAndTrace,                   \
      "A packet timestamp could not be converted to trace time because no "    \
      "ClockSnapshot path exists connecting the source clock to the trace "    \
      "time clock. Both clocks exist in snapshots, but never together or "     \
      "via a common intermediate clock. Ensure ClockSnapshots link all used "  \
      "clocks to the trace time clock."),                                      \
  F(clock_sync_mixed_clock_sources,         kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "A non-primary trace file used both the primary trace's clock "          \
      "snapshots and its own for timestamp conversion. Timestamps "            \
      "converted before the first own clock snapshot used the primary "        \
      "trace's clocks which may differ."),                                     \
  F(clock_sync_failure_undeferrable_packet_loss, kSingle, kDataLoss,          \
      kAnalysis, Scope::kMachineAndTrace,                                                               \
      "A packet had a timestamp with a clock ID that could not be resolved "   \
      "to trace time and the packet could not be deferred for later "          \
      "resolution (the trace sorter was unable to switch to full-sort mode). " \
      "The packet was dropped and its data will be missing from query "        \
      "results. This can happen when a sequence-scoped clock (64-127) is "    \
      "used before the ClockSnapshot defining it arrives, and the sorter "     \
      "has already started flushing."),                                        \
  F(clock_sync_cache_miss,                kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(process_tracker_errors,               kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(namespaced_thread_missing_process,    kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "A namespaced thread association was received but the corresponding "    \
      "process association was not found. This can happen due to data losses " \
      "during trace collection. The trace will be missing namespace "          \
      "associations for some threads, which may affect analysis. To address "  \
      "this issue, address the underlying data losses."),                      \
  F(json_tokenizer_failure,               kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(json_parser_failure,                  kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(json_display_time_unit,               kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
      "The displayTimeUnit key was set in the JSON trace. In some prior "      \
      "versions of trace processor this key could effect how the trace "       \
      "processor parsed timestamps and durations. In this version the key is " \
      "ignored which more closely matches the bavahiour of catapult."),        \
  /* HeapGraphTracker is owned globally (one instance shared across all       \
     machines/traces), so it can't trivially attribute its writes to a       \
     specific (machine, trace). Until the tracker itself is reworked to be   \
     per-context, the heap_graph_* stats below are kGlobal — heap-graph      \
     errors are aggregated rather than broken out per machine. */            \
  F(heap_graph_invalid_string_id,         kIndexed, kError,    kTrace, Scope::kGlobal,             ""), \
  F(heap_graph_malformed_packet,          kIndexed, kError,    kTrace, Scope::kGlobal,             ""), \
  F(heap_graph_missing_packet,            kIndexed, kError,    kTrace, Scope::kGlobal,             ""), \
  F(heapprofd_buffer_corrupted,           kIndexed, kError,    kTrace, Scope::kMachineAndTrace,         \
      "Shared memory buffer corrupted. This is a bug or memory corruption "    \
      "in the target. Indexed by target upid."),                               \
  F(heapprofd_hit_guardrail,              kIndexed, kError,    kTrace, Scope::kMachineAndTrace,         \
      "HeapprofdConfig specified a CPU or Memory Guardrail that was hit. "     \
      "Indexed by target upid."),                                              \
  F(heapprofd_buffer_overran,             kIndexed, kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "The shared memory buffer between the target and heapprofd overran. "    \
      "The profile was truncated early. Indexed by target upid."),             \
  F(heapprofd_client_error,               kIndexed, kError,    kTrace, Scope::kMachineAndTrace,         \
      "The heapprofd client ran into a problem and disconnected. "             \
      "See profile_packet.proto  for error codes."),                           \
  F(heapprofd_client_disconnected,        kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(heapprofd_malformed_packet,           kIndexed, kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(heapprofd_missing_packet,             kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(heapprofd_rejected_concurrent,        kIndexed, kError,    kTrace, Scope::kMachineAndTrace,         \
      "The target was already profiled by another tracing session, so the "    \
      "profile was not taken. Indexed by target upid."),                       \
  F(heapprofd_non_finalized_profile,      kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(heapprofd_sampling_interval_adjusted,                                      \
    kIndexed, kInfo,    kTrace, Scope::kMachineAndTrace,                                                \
      "By how many byes the interval for PID was increased "                   \
      "by adaptive sampling."),                                                \
  F(heapprofd_unwind_time_us,             kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
      "Time spent unwinding callstacks."),                                     \
  F(heapprofd_unwind_samples,             kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
      "Number of samples unwound."),                                           \
  F(heapprofd_client_spinlock_blocked,    kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "Time (us) the heapprofd client was blocked on the spinlock."),         \
  F(heapprofd_last_profile_timestamp,     kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "The timestamp (in trace time) for the last dump for a process"),       \
  F(symbolization_tmp_build_id_not_found,     kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,  \
       "Number of file mappings in /data/local/tmp without a build id. "       \
       "Symbolization doesn't work for executables in /data/local/tmp "        \
       "because of SELinux. Please use /data/local/tests"),                    \
  F(metatrace_overruns,                   kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(packages_list_has_parse_errors,       kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(packages_list_has_read_errors,        kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(user_list_errors,       kSingle,  kError,    kTrace, Scope::kMachineAndTrace,                       \
        "Errors occurred while reading or parsing the user.list file."),       \
  F(game_intervention_has_parse_errors,   kSingle,  kError,    kTrace, Scope::kMachineAndTrace,         \
       "One or more parsing errors occurred. This could result from "          \
       "unknown game more or intervention added to the file to be parsed."),   \
  F(game_intervention_has_read_errors,    kSingle,  kError,    kTrace, Scope::kMachineAndTrace,         \
       "The file to be parsed can't be opened. This can happend when "         \
       "the file name is not found or no permission to access the file"),      \
  F(compact_sched_has_parse_errors,       kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(misplaced_end_event,                  kSingle,  kDataLoss, kAnalysis, Scope::kMachineAndTrace, ""), \
  F(truncated_sys_write_duration,         kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Count of sys_write slices that have a truncated duration to resolve "   \
      "nesting incompatibilities with atrace slices. Real durations "          \
      "can be recovered via the |raw| table."),                                \
  F(compact_sched_switch_skipped,         kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(compact_sched_waking_skipped,         kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(empty_chrome_metadata,                kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(ninja_parse_errors,                   kSingle,  kError,    kTrace, Scope::kMachineAndTrace,    ""), \
  F(perf_cpu_lost_records,                kIndexed, kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "Count of perf samples lost due to kernel buffer overruns. The trace "   \
      "is missing information, but it's not known which processes are "        \
      "affected. Consider lowering the sampling frequency or raising "         \
      "the ring_buffer_pages config option."),                                 \
  F(perf_process_shard_count,             kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(perf_chosen_process_shard,            kIndexed, kInfo,     kTrace, Scope::kMachineAndTrace,    ""), \
  F(perf_guardrail_stop_ts,               kIndexed, kDataLoss, kTrace, Scope::kMachineAndTrace,    ""), \
  F(perf_unknown_record_type,             kIndexed, kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(perf_record_skipped,                  kIndexed, kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(perf_samples_skipped,                 kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "Count of skipped perf samples that otherwise matched the tracing "      \
      "config. This will cause a process to be completely absent from the "    \
      "trace, but does *not* imply data loss for processes that do have "      \
      "samples in this trace."),                                               \
  F(perf_features_skipped,                kIndexed, kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(perf_samples_cpu_mode_unknown,        kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(perf_samples_skipped_dataloss,        kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "Count of perf samples lost within the profiler (traced_perf), likely "  \
      "due to load shedding. This may impact any traced processes. The trace " \
      "protobuf needs to be inspected manually to confirm which processes "    \
      "are affected."),                                                        \
  F(perf_dummy_mapping_used,              kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(perf_aux_missing,                     kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "Number of bytes missing in AUX data streams due to missing "            \
      "PREF_RECORD_AUX messages."),                                            \
  F(perf_aux_ignored,                     kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
       "AUX data was ignored because the proper parser is not implemented."), \
  F(perf_aux_lost,                        kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "Gaps in the AUX data stream pased to the tokenizer."), \
  F(perf_aux_truncated,                   kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "Data was truncated when being written to the AUX stream at the "        \
      "source."),\
  F(perf_aux_partial,                     kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "The PERF_RECORD_AUX contained partial data."), \
  F(perf_aux_collision,                   kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "The collection of a sample colliden with another. You should reduce "   \
      "the rate at which samples are collected."),                             \
  F(perf_auxtrace_missing,                kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "Number of bytes missing in AUX data streams due to missing "            \
      "PREF_RECORD_AUXTRACE messages."),                                       \
  F(perf_unknown_aux_data,                kIndexed, kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "AUX data type encountered for which there is no known parser."),        \
  F(perf_no_tsc_data,                     kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
      "TSC data unavailable. Will be unable to translate HW clocks."),         \
  F(spe_no_timestamp,                     kSingle,  kInfo,     kTrace, Scope::kMachineAndTrace,         \
      "SPE record with no timestamp. Will try our best to assign a "           \
      "timestamp."),                                                           \
  F(etm_no_importer,                      kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "Unable to parse ETM data because TraceProcessor was not compiled to  "  \
      " support it. Make sure you enable the `enable_perfetto_etm_importer` "  \
      " GN flag."),                                                            \
  F(memory_snapshot_parser_failure,       kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(thread_time_in_state_unknown_cpu_freq,                                     \
                                          kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(ftrace_packet_before_tracing_start,   kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "An ftrace packet was seen before the tracing start timestamp from "     \
      "the tracing service. This happens if the ftrace buffers were not "      \
      "cleared properly. These packets are silently dropped by trace "         \
      "processor."),                                                           \
  F(sorter_push_event_out_of_order,       kSingle, kError,     kTrace, Scope::kGlobal,         \
      "Trace events are out of order event after sorting. This can happen "    \
      "due to many factors including clock sync drift, producers emitting "    \
      "events out of order or a bug in trace processor's logic of sorting."),  \
  F(unknown_extension_fields,             kSingle,  kError,    kTrace, Scope::kMachineAndTrace,         \
      "TraceEvent had unknown extension fields, which might result in "        \
      "missing some arguments. You may need a newer version of trace "         \
      "processor to parse them."),                                             \
  F(network_trace_intern_errors,          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(network_trace_parse_errors,           kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace, ""), \
  F(atom_timestamp_missing,               kSingle,  kError,    kTrace, Scope::kMachineAndTrace,         \
      "The corresponding timestamp_nanos entry for a StatsdAtom was "          \
      "missing. Defaulted to inaccurate packet timestamp."),                   \
  F(atom_unknown,                         kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Unknown statsd atom. Atom descriptor may need to be updated"),          \
  F(v8_intern_errors,                                                          \
                                          kSingle,  kDataLoss, kAnalysis, Scope::kMachineAndTrace,      \
      "Failed to resolve V8 interned data."),                                  \
  F(v8_isolate_has_no_code_range,                                              \
                                          kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "V8 isolate had no code range. THis is currently no supported and means" \
      "we will be unable to parse JS code events for this isolate."),          \
  F(v8_no_defaults,                                                            \
                                          kSingle,  kDataLoss, kAnalysis, Scope::kMachineAndTrace,      \
      "Failed to resolve V8 default data."),                                   \
  F(v8_no_code_range,                                                          \
                                          kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "V8 isolate had no code range."),                                        \
  F(v8_unknown_code_type,                 kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace, ""), \
  F(v8_code_load_missing_code_range,      kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "V8 load had no code range or an empty one. Event ignored."),            \
  F(winscope_inputmethod_clients_parse_errors,                                 \
                                          kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "InputMethod clients packet has unknown fields, which results in "       \
      "some arguments missing. You may need a newer version of trace "         \
      "processor to parse them."),                                             \
  F(winscope_inputmethod_manager_service_parse_errors,                         \
                                          kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "InputMethod manager service packet has unknown fields, which results "  \
      "in some arguments missing. You may need a newer version of trace "      \
      "processor to parse them."),                                             \
  F(winscope_inputmethod_service_parse_errors,                                 \
                                          kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "InputMethod service packet has unknown fields, which results in "       \
      "some arguments missing. You may need a newer version of trace "         \
      "processor to parse them."),                                             \
  F(winscope_sf_layers_parse_errors,      kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "SurfaceFlinger layers snapshot has unknown fields, which results in "   \
      "some arguments missing. You may need a newer version of trace "         \
      "processor to parse them."),                                             \
  F(winscope_sf_transactions_parse_errors,                                     \
                                          kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "SurfaceFlinger transactions packet has unknown fields, which results "  \
      "in some arguments missing. You may need a newer version of trace "      \
      "processor to parse them."),                                             \
  F(winscope_shell_transitions_parse_errors,                                   \
                                          kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "Shell transition packet has unknown fields, which results "             \
      "in some arguments missing. You may need a newer version of trace "      \
      "processor to parse them."),                                             \
  F(winscope_protolog_invalid_interpolation_parse_errors,                      \
                                          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "ProtoLog message string has invalid interplation parameter."),          \
  F(winscope_protolog_missing_interned_arg_parse_errors,                       \
                                          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Failed to find interned ProtoLog argument."),                           \
  F(winscope_protolog_missing_interned_stacktrace_parse_errors,                \
                                          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Failed to find interned ProtoLog stacktrace."),                         \
  F(winscope_protolog_message_decoding_failed,                                 \
                                          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Failed to decode ProtoLog message."),                                   \
  F(winscope_protolog_message_collision,                                       \
                                          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Got a ProtoLog message collision!"),                                    \
  F(winscope_protolog_message_collision_resolved,                              \
                                          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Got a ProtoLog message collision resolved!"),                           \
  F(winscope_protolog_group_tag_collision,                                     \
                                          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Got a ProtoLog group tag collision!"),                                  \
  F(winscope_protolog_group_tag_missing,                                       \
                                          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Got a ProtoLog group tag missing!"),                                    \
  F(winscope_protolog_param_mismatch,                                          \
                                          kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Message had mismatching parameters!"),                                  \
  F(winscope_viewcapture_parse_errors,                                         \
                                          kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "ViewCapture packet has unknown fields, which results in some "          \
      "arguments missing. You may need a newer version of trace processor "    \
      "to parse them."),                                                       \
  F(winscope_viewcapture_missing_interned_string_parse_errors,                 \
                                          kSingle,  kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "Failed to find interned ViewCapture string."),                          \
  F(winscope_windowmanager_parse_errors, kSingle,   kError,    kAnalysis, Scope::kMachineAndTrace,      \
      "WindowManager state packet has unknown fields, which results "          \
      "in some arguments missing. You may need a newer version of trace "      \
      "processor to parse them."),                                             \
  F(jit_unknown_frame,                    kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,         \
      "Indicates that we were unable to determine the function for a frame in "\
      "a jitted memory region"),                                               \
  F(ftrace_missing_event_id,              kSingle,  kInfo,    kAnalysis, Scope::kMachineAndTrace,       \
      "Indicates that the ftrace event was dropped because the event id was "  \
      "missing. This is an 'info' stat rather than an error stat because "     \
      "this can be legitimately missing due to proto filtering."),             \
  F(android_input_event_parse_errors,     kSingle,  kInfo,     kAnalysis, Scope::kMachineAndTrace,      \
      "Android input event packet has unknown fields, which results "          \
      "in some arguments missing. You may need a newer version of trace "      \
      "processor to parse them."),                                             \
  F(mali_unknown_mcu_state_id,            kSingle,  kError,   kAnalysis, Scope::kMachineAndTrace,       \
      "An invalid Mali GPU MCU state ID was detected."),                       \
  F(pixel_modem_negative_timestamp,       kSingle,  kError,   kAnalysis, Scope::kMachineAndTrace,       \
      "A negative timestamp was received from a Pixel modem event."),          \
  F(legacy_v8_cpu_profile_invalid_callsite, kSingle, kError,  kTrace, Scope::kMachineAndTrace,          \
      "Indicates a callsite in legacy v8 CPU profiling is invalid. This is "   \
      "a sign that the trace is malformed."),                                  \
  F(legacy_v8_cpu_profile_invalid_sample, kSingle,  kError,  kTrace, Scope::kMachineAndTrace,           \
      "Indicates a sample in legacy v8 CPU profile is invalid. This will "     \
      "cause CPU samples to be missing in the UI. This is a sign that the "    \
      "trace is malformed."),                                                  \
  F(config_write_into_file_no_flush,      kSingle,  kError,  kTrace, Scope::kMachineAndTrace,           \
      "The trace was collected with the `write_into_file` option set but "     \
      "*without* `flush_period_ms` being set. This will cause the trace to "   \
      "be fully loaded into memory and use significantly more memory than "    \
      "necessary."),                                                           \
  F(config_write_into_file_discard,        kIndexed,  kDataLoss,  kTrace, Scope::kMachineAndTrace,      \
      "The trace was collected with the `write_into_file` option set but "     \
      "uses a `DISCARD` buffer. This configuration is strongly discouraged "   \
      "and can cause mysterious data loss in the trace. Please use "           \
      "`RING_BUFFER` buffers instead."),                                       \
   F(hprof_string_counter,                 kSingle,  kInfo,   kAnalysis, Scope::kMachineAndTrace,       \
         "Number of strings encountered."),                                    \
   F(hprof_class_counter,                  kSingle,  kInfo,   kAnalysis, Scope::kMachineAndTrace,       \
         "Number of classes encountered."),                                    \
   F(hprof_heap_dump_counter,              kSingle,  kInfo,   kAnalysis, Scope::kMachineAndTrace,       \
         "Number of heap dumps encountered."),                                 \
   F(hprof_instance_counter,               kSingle,  kInfo,   kAnalysis, Scope::kMachineAndTrace,       \
         "Number of instances encountered."),                                  \
   F(hprof_object_array_counter,           kSingle,  kInfo,   kAnalysis, Scope::kMachineAndTrace,       \
         "Number of object arrays encountered."),                              \
  F(hprof_primitive_array_counter,         kSingle,  kInfo,   kAnalysis, Scope::kMachineAndTrace,       \
        "Number of primitive arrays encountered."),                            \
  F(hprof_root_counter,                    kSingle,  kInfo,   kAnalysis, Scope::kMachineAndTrace,       \
        "Number of roots encountered."),                                       \
  F(hprof_reference_counter,               kSingle,  kInfo,   kAnalysis, Scope::kMachineAndTrace,       \
        "Number of references encountered."),                                  \
  F(hprof_record_counter,                  kSingle,  kInfo,   kAnalysis, Scope::kMachineAndTrace,       \
        "Total number of records parsed."),                                    \
  F(hprof_segment_overshoot_counter,       kSingle,  kInfo,   kAnalysis, Scope::kMachineAndTrace,       \
        "Number of HEAP_DUMP_SEGMENTs whose last sub-record extended past "    \
        "the declared segment end. Benign: the sub-record's own length is "    \
        "authoritative and the parser keeps going. Reported for visibility "   \
        "into how a producer framed the dump."),                               \
  F(hprof_field_value_errors,              kSingle,  kError,   kAnalysis, Scope::kMachineAndTrace,      \
      "Number of field value parsing errors. This indicates a malformed "      \
      "hprof file. Check if the hprof opens correctly in a tool like "         \
      "AHAT. Missing values could yield incorrect native object sizes."),      \
  F(hprof_class_errors,                    kSingle,  kError,   kAnalysis, Scope::kMachineAndTrace,      \
      "Number of class parsing errors encountered. This indicates a "          \
      "malformed hprof file. Check if the hprof opens correctly in a tool "    \
      "like AHAT. Missing classes could cause missing references, thus "       \
      "affecting the overall size of the heap graph."),                    \
  F(hprof_header_errors,                   kSingle,  kError,   kAnalysis, Scope::kMachineAndTrace,      \
      "Number of header parsing errors. This indicates a malformed hprof "     \
      "file with invalid or missing header information. The file may be "      \
      "corrupted or might not be a valid hprof file. There may not be any "    \
      "heap graph data parsed."),                                              \
  F(hprof_heap_dump_errors,                kSingle,  kError,   kAnalysis, Scope::kMachineAndTrace,      \
      "Number of heap dump parsing errors. This indicates a malformed "        \
      "hprof file with corrupted heap segments. Check if the hprof opens "     \
      "correctly in a tool like AHAT. Missing heap dump sections can lead to " \
      "huge clusters of the heap graph missing, thus affecting the overall "   \
      "size of the graph"),                                                    \
  F(hprof_primitive_array_parsing_errors,  kSingle,  kError,   kAnalysis, Scope::kMachineAndTrace,      \
      "Number of primitive array parsing errors. This indicates a "            \
      "malformed hprof file. Check if the hprof opens correctly in a tool "    \
      "like AHAT. Primitive arrays like bytes[] missing can dramatically "     \
      "affect the overall size of the heap graph."),                           \
  F(hprof_reference_errors,                kSingle,  kError,   kAnalysis, Scope::kMachineAndTrace,      \
      "Number of object reference errors encountered. This indicates a "       \
      "malformed hprof file. Check if the hprof opens correctly in a tool "    \
      "like AHAT. Missing references will affect the overall size of the "     \
      "heap graph."),                                                          \
  F(trace_sorter_negative_timestamp_dropped,       kSingle,  kError,   kTrace, Scope::kGlobal, \
      "A negative timestamp was received by the TraceSorter and was dropped. " \
      "Negative timestamps are not supported by trace processor and "          \
      "the presence of one is usually a sign that something went wrong while " \
      "recording a trace. Common causes of this include incorrect "            \
      "incremental timestamps, bad clock synchronization or kernel bugs in "   \
      "drivers emitting timestamps"),                                          \
  F(slice_drop_overlapping_complete_event,        kSingle,  kError,  kTrace, Scope::kMachineAndTrace,   \
      "A complete slice was dropped because it overlaps with another "         \
      "slice. This can happen e.g. in JSON traces using X events or in other " \
      "cases where a duration is part of the trace. To solve this problem "    \
      "make sure that your X events do not overlap on the same track (e.g. "   \
      "thread/process)"),                                                      \
  F(perf_text_importer_sample_no_frames,        kSingle,  kError,  kTrace, Scope::kMachineAndTrace,     \
      "A perf sample was encountered that has no frames. This can happen "     \
      "if the kernel is unable to unwind the stack while sampling. Check "     \
      "Linux kernel documentation for causes of this and potential fixes."),   \
  F(simpleperf_missing_file_mapping,            kSingle,  kDataLoss, kTrace, Scope::kMachineAndTrace,   \
      "One or more simpleperf samples were dropped because their callchain "   \
      "entries referenced a file_id that has no corresponding File record in " \
      "the simpleperf proto. This typically happens when the simpleperf data " \
      "is incomplete or truncated, or due to a bug in simpleperf. Try "        \
      "re-recording the profile and ensure the file is not truncated. If "     \
      "this occurs consistently, please report it to the simpleperf team."),   \
  F(slice_negative_duration,                    kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "Number of slices dropped due to negative duration. This usually "       \
      "indicates incorrect timestamps in the trace data."),                    \
  F(gpu_work_period_negative_duration,          kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "Number of GPU work period events with negative duration (end < start). "\
      "Check the GPU driver for timestamp bugs."),                             \
  F(track_descriptor_missing_uuid,              kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A TrackDescriptor packet was received without a uuid field. The uuid "  \
      "is required to uniquely identify tracks and associate events with "     \
      "them. Without it, the track cannot be created and any events "          \
      "referencing this track will be dropped. This indicates a bug in the "   \
      "trace producer or trace conversion tool. Ensure your trace producer "   \
      "or conversion tool assigns a unique uuid to each TrackDescriptor."),    \
  F(track_descriptor_thread_missing_pid_tid,    kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A ThreadDescriptor within a TrackDescriptor packet is missing "         \
      "required pid or tid fields. Both fields are necessary to associate "    \
      "the track with a specific thread. The track will be ignored and any "   \
      "events on it will be dropped. This indicates a bug in the trace "       \
      "producer or trace conversion tool. Ensure ThreadDescriptor packets "    \
      "include both pid and tid fields."),                                     \
  F(track_descriptor_process_missing_pid,       kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A ProcessDescriptor within a TrackDescriptor packet is missing the "    \
      "required pid field. The pid is necessary to associate the track with "  \
      "a specific process. The track will be ignored and any events on it "    \
      "will be dropped. This indicates a bug in the trace producer or trace "  \
      "conversion tool. Ensure ProcessDescriptor packets include a valid "     \
      "pid field."),                                                           \
  F(track_event_missing_sequence_id,            kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A TrackEvent packet was received without a trusted_packet_sequence_id " \
      "field. This field is required for tracking incremental state (interned "\
      "data like strings, track associations, etc.). Without it, the event "   \
      "cannot be properly decoded and will be dropped. This indicates a bug "  \
      "in the trace producer or trace conversion tool. Ensure each packet "    \
      "sequence has a unique trusted_packet_sequence_id."),                    \
  F(track_event_missing_timestamp,              kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A TrackEvent packet was received without a valid timestamp. Events "    \
      "must have either a timestamp_delta_us, timestamp_absolute_us, or the "  \
      "containing TracePacket must have a timestamp field. Without a "         \
      "timestamp, the event cannot be placed in the trace timeline and will "  \
      "be dropped. This indicates a bug in the trace producer or trace "       \
      "conversion tool, or data corruption. Ensure all events have valid "     \
      "timestamps."),                                                          \
  F(thread_descriptor_missing_sequence_id,      kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A ThreadDescriptor packet was received without a "                      \
      "trusted_packet_sequence_id field. This field is required to associate " \
      "the thread descriptor with the correct packet sequence for incremental "\
      "state tracking. Without it, delta-encoded timestamps and other "        \
      "incremental data cannot be properly decoded. The descriptor will be "   \
      "ignored. This indicates a bug in the trace producer or trace "          \
      "conversion tool. Ensure ThreadDescriptor packets include "              \
      "trusted_packet_sequence_id on all packets."),                           \
  F(incremental_state_cleared_missing_sequence_id, kSingle, kError, kAnalysis, Scope::kMachineAndTrace, \
      "A packet with incremental_state_cleared set was received without "      \
      "trusted_packet_sequence_id. This field is required to identify which "  \
      "packet sequence's incremental state should be cleared. Without it, "    \
      "incremental state cannot be properly managed and subsequent packets "   \
      "may fail to decode. This indicates a bug in the trace producer or "     \
      "trace conversion tool."),                                               \
  F(previous_packet_dropped_missing_sequence_id, kSingle, kError, kAnalysis, Scope::kMachineAndTrace,   \
      "A packet with previous_packet_dropped set was received without "        \
      "trusted_packet_sequence_id. This field is required to identify which "  \
      "packet sequence experienced packet loss. Without it, packet loss "      \
      "cannot be properly tracked and incremental state may become "           \
      "corrupted. This indicates a bug in the trace producer or trace "        \
      "conversion tool."),                                                     \
  F(trace_packet_defaults_missing_sequence_id, kSingle, kError, kAnalysis, Scope::kMachineAndTrace,     \
      "A TracePacketDefaults packet was received without "                     \
      "trusted_packet_sequence_id. This field is required to associate the "   \
      "defaults with the correct packet sequence. Without it, the defaults "   \
      "cannot be applied and subsequent packets may not decode correctly. "    \
      "This indicates a bug in the trace producer or trace conversion tool."), \
  F(interned_data_missing_sequence_id, kSingle, kError, kAnalysis, Scope::kMachineAndTrace,             \
      "An InternedData packet was received without "                           \
      "trusted_packet_sequence_id. This field is required to associate "       \
      "interned data (strings, tracks, etc.) with the correct packet "         \
      "sequence. Without it, the interned data cannot be used and subsequent " \
      "packets will fail to decode. This indicates a bug in the trace "        \
      "producer or trace conversion tool."),                                   \
  F(track_event_track_hierarchy_loop,           kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A track hierarchy loop was detected where a track is its own ancestor " \
      "through parent_uuid references. This creates a circular dependency "    \
      "that cannot be resolved. The track will not be properly associated "    \
      "with its ancestors. This indicates corrupted or malformed track "       \
      "descriptors in the trace. Check that parent_uuid references form a "    \
      "valid tree structure."),                                                \
  F(track_event_track_hierarchy_too_deep,       kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A track hierarchy exceeds the maximum depth of 100 ancestors. This "    \
      "either indicates an extremely deep (and likely incorrect) track "       \
      "structure, or a loop that wasn't detected. The track will not be "      \
      "properly resolved. This indicates corrupted or malformed track "        \
      "descriptors. Review the track parent_uuid relationships."),             \
  F(track_descriptor_default_track_with_parent, kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A TrackDescriptor for the default track (uuid=0) specified a "          \
      "parent_uuid. The default track is the root of the track hierarchy and " \
      "cannot have a parent. The descriptor is ignored. This is a bug in the " \
      "trace producer."),                                                      \
  F(track_descriptor_conflicting_reservation,   kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A TrackDescriptor was received that conflicts with an earlier "         \
      "descriptor for the same uuid. Track descriptors for the same uuid "     \
      "must be consistent (same type, same parent, etc.). The conflicting "    \
      "descriptor is ignored. This is a bug in the trace producer."),          \
  F(track_event_range_of_interest_missing_start_us, kSingle, kError, kAnalysis, Scope::kMachineAndTrace,\
      "A TrackEventRangeOfInterest packet was received without the required "  \
      "start_us field. The packet is ignored. This is a bug in the trace "     \
      "producer."),                                                            \
  F(track_descriptor_invalid_child_ordering,    kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A TrackDescriptor specified an invalid or unrecognized value for "      \
      "child_tracks_ordering. The descriptor is ignored. This is a bug in "    \
      "the trace producer."),                                                  \
  F(track_event_counter_missing_track_uuid,     kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A TrackEvent with TYPE_COUNTER was received without a track_uuid "      \
      "(neither in the event nor in TrackEventDefaults). Counter events "      \
      "require a track_uuid to identify which counter track to use. The "      \
      "event is dropped. This is a bug in the trace producer."),               \
  F(track_event_counter_missing_value,          kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A TrackEvent with TYPE_COUNTER was received without a counter value "   \
      "(neither counter_value nor double_counter_value). Counter events "      \
      "must specify a value. The event is dropped. This is a bug in the "      \
      "trace producer."),                                                      \
  F(track_event_counter_invalid_track_uuid,     kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A TrackEvent with TYPE_COUNTER specified a track_uuid that could not "  \
      "be resolved or converted to an absolute counter value. The event is "   \
      "dropped. This is a bug in the trace producer."),                        \
  F(track_event_extra_counter_missing_track_uuids, kSingle, kError, kAnalysis, Scope::kMachineAndTrace, \
      "A TrackEvent specified extra counter values but did not provide the "   \
      "corresponding extra_counter_track_uuids (neither in the event nor in "  \
      "TrackEventDefaults). The event is dropped. This is a bug in the trace " \
      "producer."),                                                            \
  F(track_event_extra_counter_track_uuid_mismatch, kSingle, kError, kAnalysis, Scope::kMachineAndTrace, \
      "A TrackEvent provided more extra counter values than "                  \
      "extra_counter_track_uuids. Arrays must have matching lengths. The "     \
      "event is dropped. This is a bug in the trace producer."),               \
  F(track_event_extra_counter_exceeds_max,      kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A TrackEvent provided more extra counter values than the maximum "      \
      "supported (TrackEventData::kMaxNumExtraCounters). Excess counters are " \
      "ignored. This is a bug in the trace producer."),                        \
  F(track_event_extra_counter_invalid_track,    kSingle,  kError,  kAnalysis, Scope::kMachineAndTrace,  \
      "A TrackEvent specified an invalid track_uuid in "                       \
      "extra_counter_track_uuids that could not be resolved. The event is "    \
      "dropped. This is a bug in the trace producer."),                        \
  F(thread_descriptor_skipped_incremental_state_invalid, kSingle, kError,      \
      kAnalysis, Scope::kMachineAndTrace,                                                               \
      "A ThreadDescriptor packet was skipped because incremental state is "    \
      "invalid. ThreadDescriptors are ignored until incremental state is "     \
      "cleared to prevent incorrect delta-encoded timestamp calculations. "    \
      "Root cause: packet loss in the trace (check packet loss stats) or a "   \
      "bug in the trace producer (missing incremental_state_cleared)."),       \
  F(track_event_skipped_timestamp_delta_without_valid_state, kSingle, kError,  \
      kAnalysis, Scope::kMachineAndTrace,                                                               \
      "A TrackEvent with timestamp_delta_us was skipped because no valid "     \
      "ThreadDescriptor baseline was available. Delta timestamps require a "   \
      "ThreadDescriptor to establish a baseline. Root cause: packet loss in "  \
      "the trace (check packet loss stats) or a bug in the trace producer "    \
      "(missing ThreadDescriptor)."),                                          \
  F(track_event_skipped_thread_time_delta_without_valid_state, kSingle,        \
      kError, kAnalysis, Scope::kMachineAndTrace,                                                       \
      "A TrackEvent with thread_time_delta_us was skipped because no valid "   \
      "ThreadDescriptor baseline was available. Delta timestamps require a "   \
      "ThreadDescriptor to establish a baseline. Root cause: packet loss in "  \
      "the trace (check packet loss stats) or a bug in the trace producer "    \
      "(missing ThreadDescriptor)."),                                          \
  F(track_event_skipped_thread_instruction_delta_without_valid_state, kSingle, \
      kError, kAnalysis, Scope::kMachineAndTrace,                                                       \
      "A TrackEvent with thread_instruction_count_delta was skipped because "  \
      "no valid ThreadDescriptor baseline was available. Delta counts "        \
      "require a ThreadDescriptor to establish a baseline. Root cause: "       \
      "packet loss in the trace (check packet loss stats) or a bug in the "    \
      "trace producer (missing ThreadDescriptor)."),                           \
  F(packet_skipped_seq_needs_incremental_state_invalid, kSingle, kInfo,        \
      kAnalysis, Scope::kMachineAndTrace,                                                               \
      "A packet with SEQ_NEEDS_INCREMENTAL_STATE flag was skipped because "    \
      "incremental state is invalid. Packets that depend on incremental "      \
      "state cannot be processed until state is reestablished. Root cause: "   \
      "packet loss in the trace (check packet loss stats) or a bug in the "    \
      "trace producer (missing incremental_state_cleared packet)."),           \
  F(interned_data_skipped_incremental_state_invalid, kSingle, kInfo,           \
      kAnalysis, Scope::kMachineAndTrace,                                                               \
      "An InternedData packet was skipped because incremental state is "       \
      "invalid. InternedData must be associated with the correct state "       \
      "generation, so it's ignored until incremental state is cleared. Root "  \
      "cause: packet loss in the trace (check packet loss stats) or a bug "    \
      "in the trace producer (missing incremental_state_cleared packet)."),    \
  F(heap_graph_non_finalized_graph,             kSingle,  kDataLoss, kTrace, Scope::kGlobal,            \
      "Heap graph profile was not finalized before the trace ended. Packet "   \
      "loss was detected (missing packet indices) which means the profile is " \
      "incomplete. Some objects and references may be missing from the heap "  \
      "graph. This typically occurs when the profiled process crashes or is "  \
      "killed before completing the profile. To get complete profiles, ensure "\
      "the process finishes cleanly or increase the profiling timeout."),      \
  F(primes_unknown_edge_type, kSingle, kDataLoss, kAnalysis, Scope::kMachineAndTrace,                   \
    "A Primes TraceEdge was received which did not contain a known slice type "\
    "(Begin, End, Mark)."),                                                    \
  F(primes_executor_not_found, kSingle, kDataLoss, kAnalysis, Scope::kMachineAndTrace,                  \
    "A valid executor_id was not found for the given edge's parent_id, so the "\
    "slice was dropped."),                                                     \
  F(primes_end_without_matching_begin, kSingle, kInfo, kAnalysis, Scope::kMachineAndTrace,              \
    "A SliceEnd event was seen without its corresponding SliceBegin."),        \
  F(primes_missing_entity_details, kSingle, kInfo, kAnalysis, Scope::kMachineAndTrace,                  \
    "The entity_details field was missing from an edge that requires it."),    \
  F(primes_missing_parent_id, kSingle, kInfo, kAnalysis, Scope::kMachineAndTrace,                       \
    "The parent_id field was missing from an edge that requires it."),         \
  F(primes_malformed_timestamp, kSingle, kDataLoss, kAnalysis, Scope::kMachineAndTrace,                 \
    "The timestamp for an edge or trace was not able to be parsed"),           \
  F(protovm_abort, kSingle,  kError, kAnalysis, Scope::kMachineAndTrace,                                \
      "A ProtoVM instance aborted the execution while applying the patch. "    \
      "This might be due to inconsistencies between VM program logic and "     \
      "actual patch format."),                                                 \
  F(protovm_registration_error, kSingle,  kError, kAnalysis, Scope::kMachineAndTrace,                   \
    "Failed to find the sequence IDs corresponding to a ProtoVM's producer "   \
    "ID. Such mapping should be provided by the TraceProvenance packet.")
// clang-format on

enum Type {
  kSingle,  // Single-value property, one value per key.
  kIndexed  // Indexed property, multiple value per key (e.g. cpu_stats[1]).
};

enum Severity {
  kInfo,      // Diagnostic counters
  kDataLoss,  // Correct operation that still resulted in data loss
  kError,     // If any kError counter is > 0 trace_processor_shell will
              // raise an error. This is also surfaced in the web UI.
  kNumSeverities,
};

enum Source {
  // The counter is collected when recording the trace on-device and is just
  // being reflected in the stats table.
  kTrace,

  // The counter is generated when importing / processing the trace in the trace
  // processor.
  kAnalysis,

  kNumSources,
};

// Scope of a stat defines its association within a potentially
// multi-machine/multi-trace merged session.
//
// Classification Reasoning:
// - kGlobal: Properties intrinsic to the TP process (e.g. parse timing).
// - kTrace: Properties of a recording session or trace file, not tied to a
//   specific machine (e.g. traced service stats, buffer stats).
// - kMachine: Static properties of a device. Currently unused for stats.
// - kMachineAndTrace: Per-machine execution data within a specific session
//   (e.g. ftrace stats, parser errors, clock sync).
#define PERFETTO_TP_STATS_SCOPES(F)                                 \
  F(kGlobal, "global"), F(kTrace, "trace"), F(kMachine, "machine"), \
      F(kMachineAndTrace, "machine_and_trace")

#define PERFETTO_TP_STATS_SCOPE_ENUM(varname, name) varname
enum class Scope : size_t {
  PERFETTO_TP_STATS_SCOPES(PERFETTO_TP_STATS_SCOPE_ENUM),
  kNumScopes,
};

#define PERFETTO_TP_STATS_SCOPE_NAME(varname, name) name
constexpr char const* kScopeNames[] = {
    PERFETTO_TP_STATS_SCOPES(PERFETTO_TP_STATS_SCOPE_NAME)};

#if defined(__GNUC__) || defined(__clang__)
#if defined(__clang__)
#pragma clang diagnostic push
// Fix 'error: #pragma system_header ignored in main file' for clang in Google3.
#pragma clang diagnostic ignored "-Wpragma-system-header-outside-header"
#endif

// Ignore GCC warning about a missing argument for a variadic macro parameter.
#pragma GCC system_header

#if defined(__clang__)
#pragma clang diagnostic pop
#endif
#endif

// Declares an enum of literals (one for each stat). The enum values of each
// literal corresponds to the string index in the arrays below.
#define PERFETTO_TP_STATS_ENUM(name, ...) name
enum KeyIDs : size_t { PERFETTO_TP_STATS(PERFETTO_TP_STATS_ENUM), kNumKeys };

// The code below declares an array for each property (name, type, ...).

#define PERFETTO_TP_STATS_NAME(name, ...) #name
constexpr char const* kNames[] = {PERFETTO_TP_STATS(PERFETTO_TP_STATS_NAME)};

#define PERFETTO_TP_STATS_TYPE(_, type, ...) type
constexpr Type kTypes[] = {PERFETTO_TP_STATS(PERFETTO_TP_STATS_TYPE)};

#define PERFETTO_TP_STATS_SEVERITY(_, __, severity, ...) severity
constexpr Severity kSeverities[] = {
    PERFETTO_TP_STATS(PERFETTO_TP_STATS_SEVERITY)};

#define PERFETTO_TP_STATS_SOURCE(_, __, ___, source, ...) source
constexpr Source kSources[] = {PERFETTO_TP_STATS(PERFETTO_TP_STATS_SOURCE)};

#define PERFETTO_TP_STATS_SCOPE(_, __, ___, ____, scope, ...) scope
constexpr Scope kScopes[] = {PERFETTO_TP_STATS(PERFETTO_TP_STATS_SCOPE)};

#define PERFETTO_TP_STATS_DESCRIPTION(_, __, ___, ____, _____, descr, ...) descr
constexpr char const* kDescriptions[] = {
    PERFETTO_TP_STATS(PERFETTO_TP_STATS_DESCRIPTION)};

}  // namespace perfetto::trace_processor::stats

#endif  // SRC_TRACE_PROCESSOR_STORAGE_STATS_H_
