blob: 26771e954a951c5d3f7abb67a839904b11f7c357 [file] [log] [blame]
# -*- coding: utf-8 -*-
# Copyright 2018 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Utility functions for recording CL's action metrics."""
from __future__ import print_function
from infra_libs import ts_mon
from chromite.lib import clactions
from chromite.lib import constants
from chromite.lib import cros_logging as logging
from chromite.lib import metrics
def RecordSubmissionMetrics(action_history, submitted_change_strategies):
"""Record submission metrics in monarch.
Args:
action_history: A CLActionHistory instance for all cl actions for all
changes in submitted_change_strategies.
submitted_change_strategies: A dictionary from GerritPatchTuples to
submission strategy strings. These changes will have their handling
times recorded in monarch.
"""
# TODO(phobbs) move to top level after crbug.com/755415
handling_time_metric = metrics.CumulativeSecondsDistribution(
constants.MON_CL_HANDLE_TIME)
wall_clock_time_metric = metrics.CumulativeSecondsDistribution(
constants.MON_CL_WALL_CLOCK_TIME)
precq_time_metric = metrics.CumulativeSecondsDistribution(
constants.MON_CL_PRECQ_TIME)
wait_time_metric = metrics.CumulativeSecondsDistribution(
constants.MON_CL_WAIT_TIME)
cq_run_time_metric = metrics.CumulativeSecondsDistribution(
constants.MON_CL_CQRUN_TIME)
cq_tries_metric = metrics.CumulativeSmallIntegerDistribution(
constants.MON_CL_CQ_TRIES)
# These 3 false rejection metrics are different in subtle but important ways.
# false_rejections: distribution of the number of times a CL was rejected,
# broken down by what it was rejected by (cq vs. pre-cq). Every CL will emit
# two data points to this distribution.
false_rejection_metric = metrics.CumulativeSmallIntegerDistribution(
constants.MON_CL_FALSE_REJ)
# false_rejections_total: distribution of the total number of times a CL
# was rejected (not broken down by phase). Note that there is no way to
# independently calculate this from |false_rejections| distribution above,
# since one cannot reconstruct after the fact which pre-cq and cq data points
# (for the same underlying CL) belong together.
false_rejection_total_metric = metrics.CumulativeSmallIntegerDistribution(
constants.MON_CL_FALSE_REJ_TOTAL)
# false_rejection_count: counter of the total number of false rejections that
# have occurred (broken down by phase)
false_rejection_count_metric = metrics.Counter(
constants.MON_CL_FALSE_REJ_COUNT)
# This metric excludes rejections which were forgiven by the CL-exonerator
# service.
false_rejections_minus_exonerations_metric = \
metrics.CumulativeSmallIntegerDistribution(
constants.MON_CQ_FALSE_REJ_MINUS_EXONERATIONS,
description='The number of rejections - exonerations per CL.',
field_spec=[ts_mon.StringField('submission_strategy')],
)
precq_false_rejections = action_history.GetFalseRejections(
bot_type=constants.PRE_CQ)
cq_false_rejections = action_history.GetFalseRejections(
bot_type=constants.CQ)
exonerations = action_history.GetExonerations()
for change, strategy in submitted_change_strategies.iteritems():
strategy = strategy or ''
handling_time = clactions.GetCLHandlingTime(change, action_history)
wall_clock_time = clactions.GetCLWallClockTime(change, action_history)
precq_time = clactions.GetPreCQTime(change, action_history)
wait_time = clactions.GetCQWaitTime(change, action_history)
run_time = clactions.GetCQRunTime(change, action_history)
pickups = clactions.GetCQAttemptsCount(change, action_history)
fields = {'submission_strategy': strategy}
handling_time_metric.add(handling_time, fields=fields)
wall_clock_time_metric.add(wall_clock_time, fields=fields)
precq_time_metric.add(precq_time, fields=fields)
wait_time_metric.add(wait_time, fields=fields)
cq_run_time_metric.add(run_time, fields=fields)
cq_tries_metric.add(pickups, fields=fields)
rejection_types = (
(constants.PRE_CQ, precq_false_rejections),
(constants.CQ, cq_false_rejections),
)
total_rejections = 0
for by, rej in rejection_types:
c = len(rej.get(change, []))
f = dict(fields, rejected_by=by)
false_rejection_metric.add(c, fields=f)
false_rejection_count_metric.increment_by(c, fields=f)
total_rejections += c
false_rejection_total_metric.add(total_rejections, fields=fields)
n_exonerations = len(exonerations.get(change, []))
# TODO(crbug.com/804900) max(0, ...) is required because of an accounting
# bug where sometimes this quantity is negative.
net_rejections = total_rejections - n_exonerations
if net_rejections < 0:
logging.error(
'Exonerations is larger than total rejections for CL %s.'
' See crbug.com/804900', change)
false_rejections_minus_exonerations_metric.add(
max(0, net_rejections),
fields=fields)