# Copyright 2015 The Chromium Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. import json import time from google.appengine.api import urlfetch import webapp2 from base import bigquery from base import constants from common import query_filter class Query(webapp2.RequestHandler): def get(self): urlfetch.set_default_fetch_deadline(60) try: filters = query_filter.Filters(self.request) except ValueError as e: self.response.headers['Content-Type'] = 'application/json' self.response.out.write({'error': str(e)}) return query_results = _QueryEvents(bigquery.BigQuery(), **filters) trace_events = list(_ConvertQueryEventsToTraceEvents(query_results)) self.response.headers['Content-Type'] = 'application/json' self.response.out.write(json.dumps(trace_events, separators=(',', ':'))) def _QueryEvents(bq, **filters): start_time = filters.get( 'start_time', time.time() - constants.DEFAULT_HISTORY_DURATION_SECONDS) query_start_time_ms = int(start_time * 1000) query_start_time_us = int(start_time * 1000000) fields = ( 'name', 'GREATEST(INTEGER(start_time), %d) AS start_time_us' % query_start_time_us, 'INTEGER(end_time) AS end_time_us', 'builder', 'configuration', 'hostname', 'status', 'url', ) tables = (constants.BUILDS_TABLE, constants.CURRENT_BUILDS_TABLE) tables = ['[%s.%s@%d-]' % (constants.DATASET, table, query_start_time_ms) for table in tables] conditions = [] conditions.append('NOT LOWER(name) CONTAINS "trigger"') conditions.append('end_time - start_time >= 1000000') conditions.append('end_time > %d' % query_start_time_us) for filter_name, filter_values in filters.iteritems(): if not isinstance(filter_values, list): continue if isinstance(filter_values[0], int): filter_values = map(str, filter_values) elif isinstance(filter_values[0], basestring): # QueryFilter handles string validation. Assume no quotes in string. filter_values = ['"%s"' % v for v in filter_values] else: raise NotImplementedError() conditions.append('%s IN (%s)' % (filter_name, ','.join(filter_values))) query = ('SELECT %s ' % ','.join(fields) + 'FROM %s ' % ','.join(tables) + 'WHERE %s ' % ' AND '.join(conditions) + 'ORDER BY builder, start_time') return bq.QuerySync(query) def _ConvertQueryEventsToTraceEvents(events): for row in events: event_start_time_us = int(row['f'][1]['v']) event_end_time_us = int(row['f'][2]['v']) status = row['f'][6]['v'] if status: status = int(status) # TODO: Use constants from update/common/buildbot/__init__.py. if status == 0: color_name = 'cq_build_passed' elif status == 1: color_name = 'cq_build_warning' elif status == 2: color_name = 'cq_build_failed' elif status == 4: color_name = 'cq_build_exception' elif status == 5: color_name = 'cq_build_abandoned' else: color_name = 'cq_build_running' yield { 'name': row['f'][0]['v'], 'pid': row['f'][4]['v'], 'tid': '%s [%s]' % (row['f'][3]['v'], row['f'][5]['v']), 'ph': 'X', 'ts': event_start_time_us, 'dur': event_end_time_us - event_start_time_us, 'cname': color_name, 'args': { 'url': row['f'][7]['v'], }, }