chromium/tools/metrics/histograms/populate_enums.py

# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""Functions for populating enums with ukm events."""

from collections import namedtuple
import os
import sys
import xml.dom.minidom

import xml_utils

sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common'))
import codegen_shared


EventDetails = namedtuple("EventDetails", "name hash is_obsolete")


def _GetEventDetails(event):
  """Returns a simple struct containing the event details.

  Args:
    event: An event description as defined in ukm.xml.

  Returns:
    A struct containing the event name, truncated hash, and whether the event is
    considered obsolete.
  """
  name = event.getAttribute('name')
  # The value is UKM event name hash truncated to 31 bits. This is recorded in
  # https://cs.chromium.org/chromium/src/components/ukm/ukm_recorder_impl.cc?q=LogEventHashasUmaHistogram
  hash = codegen_shared.HashName(name) & 0x7fffffff

  def _HasDirectObsoleteTag(node):
    return any(
        isinstance(child, xml.dom.minidom.Element)
        and child.tagName == 'obsolete' for child in node.childNodes)

  # The UKM event is considered obsolete if the event itself is marked as
  # obsolete with a tag or all of its metrics are marked as obsolete.
  is_event_obsolete = _HasDirectObsoleteTag(event)
  are_all_metrics_obsolete = all(
      _HasDirectObsoleteTag(metric)
      for metric in event.getElementsByTagName('metric'))

  return EventDetails(name=name,
                      hash=hash,
                      is_obsolete=is_event_obsolete or are_all_metrics_obsolete)


def PopulateEnumWithUkmEvents(doc, enum, ukm_events):
  """Populates the enum node with a list of ukm events.

  Args:
    doc: The document to create the node in.
    enum: The enum node needed to be populated.
    ukm_events: A list of ukm event nodes.
  """
  event_details = [_GetEventDetails(event) for event in ukm_events]
  event_details.sort(key=lambda event: event.hash)

  for event in event_details:
    node = doc.createElement('int')
    node.attributes['value'] = str(event.hash)
    label = event.name
    # If the event is obsolete, mark it in the int's label.
    if event.is_obsolete:
      label += ' (Obsolete)'
    node.attributes['label'] = label
    enum.appendChild(node)


def PopulateEnumsWithUkmEvents(doc, enums, ukm_events):
  """Populates enum nodes in the enums with a list of ukm events

  Args:
    doc: The document to create the node in.
    enums: The enums node to be iterated.
    ukm_events: A list of ukm event nodes.
  """
  for enum in xml_utils.IterElementsWithTag(enums, 'enum', 1):
    # We only special case 'UkmEventNameHash' currently.
    if enum.getAttribute('name') == 'UkmEventNameHash':
      PopulateEnumWithUkmEvents(doc, enum, ukm_events)