blob: 55aade4a1022bef9b66ea97b68c7dcd93ec1683e [file] [log] [blame]
# Copyright 2014 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.
"""Print prettier and more detailed exceptions."""
from __future__ import print_function
import logging
import math
import os
import sys
import traceback
from telemetry.core import exceptions
def PrintFormattedException(exception_class=None, exception=None, tb=None,
msg=None):
logging.info('Try printing formatted exception: %s %s %s' %
(exception_class, exception, tb))
assert bool(exception_class) == bool(exception) == bool(tb), (
'Must specify all or none of exception_class, exception, and tb')
if not exception_class:
exception_class, exception, tb = sys.exc_info()
if exception_class == exceptions.IntentionalException:
return
def _GetFinalFrame(tb_level):
while tb_level.tb_next:
tb_level = tb_level.tb_next
return tb_level.tb_frame
processed_tb = traceback.extract_tb(tb)
frame = _GetFinalFrame(tb)
exception_list = traceback.format_exception_only(exception_class, exception)
exception_string = '\n'.join(l.strip() for l in exception_list)
if msg:
print(file=sys.stderr)
print(msg, file=sys.stderr)
_PrintFormattedTrace(processed_tb, frame, exception_string)
def PrintFormattedFrame(frame, exception_string=None):
_PrintFormattedTrace(traceback.extract_stack(frame), frame, exception_string)
def _PrintFormattedTrace(processed_tb, frame, exception_string=None):
"""Prints an Exception in a more useful format than the default.
TODO(tonyg): Consider further enhancements. For instance:
- Report stacks to maintainers like depot_tools does.
- Add a debug flag to automatically start pdb upon exception.
"""
print(file=sys.stderr)
# Format the traceback.
print('Traceback (most recent call last):', file=sys.stderr)
for filename, line, function, text in processed_tb:
filename = os.path.abspath(filename)
print(' %s at %s:%d' % (function, filename, line), file=sys.stderr)
print(' %s' % text, file=sys.stderr)
# Format the exception.
if exception_string:
print(exception_string, file=sys.stderr)
# Format the locals.
local_variables = [(variable, value) for variable, value in
frame.f_locals.iteritems() if variable != 'self']
print(file=sys.stderr)
print('Locals:', file=sys.stderr)
if local_variables:
longest_variable = max(len(v) for v, _ in local_variables)
for variable, value in sorted(local_variables):
value = repr(value)
possibly_truncated_value = _AbbreviateMiddleOfString(value, ' ... ', 1024)
truncation_indication = ''
if len(possibly_truncated_value) != len(value):
truncation_indication = ' (truncated)'
print(' %s: %s%s' % (variable.ljust(longest_variable + 1),
possibly_truncated_value,
truncation_indication),
file=sys.stderr)
else:
print(' No locals!', file=sys.stderr)
print(file=sys.stderr)
sys.stderr.flush()
def _AbbreviateMiddleOfString(target, middle, max_length):
if max_length < 0:
raise ValueError('Must provide positive max_length')
if len(middle) > max_length:
raise ValueError('middle must not be greater than max_length')
if len(target) <= max_length:
return target
half_length = (max_length - len(middle)) / 2.
return (target[:int(math.floor(half_length))] + middle +
target[-int(math.ceil(half_length)):])