blob: 88253fe6d3ebac8eb0b385667d6627cebe98e664 [file] [log] [blame]
#
# Copyright (C) 2018 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import datetime
import logging
import operator
import os
import time
from vts.runners.host import logger
from vts.utils.python.instrumentation import test_framework_instrumentation_categories as tfic
from vts.utils.python.instrumentation import test_framework_instrumentation_event as tfie
# global category listing
categories = tfic.TestFrameworkInstrumentationCategories()
# TODO(yuexima): use data class
counts = {}
DEFAULT_CATEGORY = 'Misc'
DEFAULT_FILE_NAME_TEXT_RESULT = 'instrumentation_data.txt'
def Begin(name, category=DEFAULT_CATEGORY, enable_logging=None, disable_subevent_logging=False):
"""Marks the beginning of an event.
Params:
name: string, name of the event.
category: string, category of the event. Default category will be used if not specified.
enable_logging: bool or None. Whether to put the event in logging.
Should be set to False when timing small pieces of code that could take
very short time to run.
If not specified or is None, global configuration will be used.
disable_subevent_logging: bool, whether to disable logging for events created after this
event begins and before this event ends. This will overwrite
subevent's logging setting if set to True.
Returns:
Event object representing the event
"""
event = tfie.TestFrameworkInstrumentationEvent(name, category)
event.Begin(enable_logging=enable_logging, disable_subevent_logging=disable_subevent_logging)
return event
def End(name, category=DEFAULT_CATEGORY):
"""Marks the end of an event.
This function tries to find an event in internal event stack by calling FindEvent
method with the given category and name.
Will log error and return None if no match is found.
If multiple event with the same category and name are found, the last one will be used.
Use this function with caution if there are multiple events began with the same name and
category. It is highly recommended to call End() method from the Event object directly.
Params:
name: string, name of the event.
category: string, category of the event. Default category will be used if not specified.
Returns:
Event object representing the event. None if cannot find an active matching event
"""
event = FindEvent(name, category)
if not event:
logging.error('Event with category %s and name %s either does not '
'exists or has already ended. Skipping...', name, category)
return None
event.End()
return event
def FindEvent(name, category=DEFAULT_CATEGORY):
"""Finds an existing event that has started given the names.
Use this function with caution if there are multiple events began with the same name and
category. It is highly recommended to call End() method from the Event object directly.
Params:
name: string, name of the event.
category: string, category of the event. Default category will be used if not specified.
Returns:
TestFrameworkInstrumentationEvent object if found; None otherwise.
"""
for event in reversed(tfie.event_stack):
if event.Match(name, category):
return event
return None
def Count(name, category=DEFAULT_CATEGORY):
"""Counts the occurrence of an event.
Events will be mapped using name and category as key.
Params:
name: string, name of the event.
category: string, category of the event. Default category will be used if not specified.
"""
name, category = tfie.NormalizeNameCategory(name, category)
# TODO(yuexima): give warning when there's illegal char, but only once for each combination.'
if (name, category) not in counts:
counts[(name, category)] = [time.time()]
else:
counts[name, category].append(time.time())
def GenerateTextReport():
"""Compile instrumentation results into a simple text output format for visualization.
Returns:
a string containing result text.
"""
class EventItem:
"""Temporary data storage class for instrumentation text result generation.
Attributes:
name: string, event name
category: string, event category
time_cpu: float, CPU time of the event (can be begin or end)
time_wall: float, wall time of the event (can be begin or end)
type: string, begin or end
"""
name = ''
category = ''
time_cpu = -1
time_wall = -1
type = ''
duration = -1
results = []
for event in tfie.event_data:
ei = EventItem()
ei.name = event.name
ei.category = event.category
ei.type = 'begin'
ei.time_cpu = event.timestamp_begin_cpu
ei.time_wall = event.timestamp_begin_wall
results.append(ei)
for event in tfie.event_data:
ei = EventItem()
ei.name = event.name
ei.category = event.category
ei.type = 'end'
ei.time_cpu = event.timestamp_end_cpu
ei.time_wall = event.timestamp_end_wall
ei.duration = event.timestamp_end_wall - event.timestamp_begin_wall
results.append(ei)
results.sort(key=operator.attrgetter('time_cpu'))
result_text = []
level = 0
for e in results:
if e.type == 'begin':
s = ('Begin [%s @ %s] ' % (e.name, e.category) +
datetime.datetime.fromtimestamp(e.time_wall).strftime('%Y-%m-%d %H:%M:%S_%f'))
result_text.append(' '*level + s)
level += 1
else:
s = ('End [%s @ %s] ' % (e.name, e.category) +
datetime.datetime.fromtimestamp(e.time_wall).strftime('%Y-%m-%d %H:%M:%S_%f'))
level -= 1
result_text.append(' '*level + s)
result_text.append('\n')
result_text.append(' '*level + "%.4f" % e.duration)
result_text.append('\n')
return ''.join(result_text)
def CompileResults(directory=None, filename=DEFAULT_FILE_NAME_TEXT_RESULT):
"""Writes instrumentation results into a simple text output format for visualization.
Args:
directory: string, parent path of result
filename: string, result file name
"""
if not directory:
directory = logging.log_path
with open(os.path.join(directory, filename), 'w') as f:
f.write(GenerateTextReport())