aboutsummaryrefslogtreecommitdiff
path: root/catapult/telemetry/telemetry/internal/util/exception_formatter.py
diff options
context:
space:
mode:
Diffstat (limited to 'catapult/telemetry/telemetry/internal/util/exception_formatter.py')
-rw-r--r--catapult/telemetry/telemetry/internal/util/exception_formatter.py103
1 files changed, 103 insertions, 0 deletions
diff --git a/catapult/telemetry/telemetry/internal/util/exception_formatter.py b/catapult/telemetry/telemetry/internal/util/exception_formatter.py
new file mode 100644
index 00000000..fe036c84
--- /dev/null
+++ b/catapult/telemetry/telemetry/internal/util/exception_formatter.py
@@ -0,0 +1,103 @@
+# 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
+
+
+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.
+ print >> sys.stderr, 'Traceback (most recent call last):'
+ for filename, line, function, text in processed_tb:
+ filename = os.path.abspath(filename)
+ 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)):])