blob: 5c3f86e738e74679b973870331916dc62a00069c [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."""
import logging
import math
import os
import sys
import traceback
from telemetry.core import exceptions
from telemetry.core import util
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 >> sys.stderr
print >> sys.stderr, msg
_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 >> sys.stderr
# Format the traceback.
base_dir = os.path.abspath(util.GetChromiumSrcDir())
print >> sys.stderr, 'Traceback (most recent call last):'
for filename, line, function, text in processed_tb:
filename = os.path.abspath(filename)
if filename.startswith(base_dir):
filename = filename[len(base_dir)+1:]
print >> sys.stderr, ' %s at %s:%d' % (function, filename, line)
print >> sys.stderr, ' %s' % text
# Format the exception.
if exception_string:
print >> sys.stderr, exception_string
# Format the locals.
local_variables = [(variable, value) for variable, value in
frame.f_locals.iteritems() if variable != 'self']
print >> sys.stderr
print >> sys.stderr, 'Locals:'
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 >> sys.stderr, ' %s: %s%s' % (variable.ljust(longest_variable + 1),
possibly_truncated_value,
truncation_indication)
else:
print >> sys.stderr, ' No locals!'
print >> 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)):])