Add a portage_debug module and python-trace feature for --debug mode.
authorZac Medico <zmedico@gentoo.org>
Thu, 23 Mar 2006 08:22:58 +0000 (08:22 -0000)
committerZac Medico <zmedico@gentoo.org>
Thu, 23 Mar 2006 08:22:58 +0000 (08:22 -0000)
svn path=/main/trunk/; revision=2979

bin/ebuild
bin/emerge
pym/portage_debug.py [new file with mode: 0644]

index fafdbbccc203c91f6cbefac0bf6e3396e8b7b9d2..a62585a5eaf77fc6e25606e4c422a38235753a4a 100755 (executable)
@@ -25,6 +25,11 @@ sys.path = ["/usr/lib/portage/pym"]+sys.path
 
 import portage, portage_util, portage_const
 
+# do this _after_ 'import portage' to prevent unnecessary tracing
+if debug and "python-trace" in portage.features:
+       import portage_debug
+       portage_debug.set_trace(True)
+
 if portage.settings["NOCOLOR"] in ("yes","true") or not sys.stdout.isatty():
        import output
        output.nocolor()
index b17a2a6b20a53acba9989a0df111121bbe39534d..55e9420d1ea1d251caf6e59c5c344fb4f3fa1412 100755 (executable)
@@ -401,6 +401,9 @@ if ("--debug" in myopts):
        portage.settings.backup_changes("PORTAGE_DEBUG")
        portage.debug=1
        portage.settings.lock()
+       if "python-trace" in portage.features:
+               import portage_debug
+               portage_debug.set_trace(True)
 
 if ("--resume" in myopts):
        if "--tree" in myopts:
diff --git a/pym/portage_debug.py b/pym/portage_debug.py
new file mode 100644 (file)
index 0000000..318754d
--- /dev/null
@@ -0,0 +1,116 @@
+# Copyright 1999-2006 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: $
+
+import os, sys, threading
+
+import portage_const
+from portage_util import writemsg
+
+def set_trace(on=True):
+       if on:
+               t = trace_handler()
+               threading.settrace(t.event_handler)
+               sys.settrace(t.event_handler)
+       else:
+               sys.settrace(None)
+               threading.settrace(None)
+
+class trace_handler(object):
+
+       def __init__(self):
+               python_base = None
+               for x in sys.path:
+                       if os.path.basename(x).startswith("python2."):
+                               python_base = x
+                               break
+
+               self.ignore_prefixes = []
+               if python_base is not None:
+                       self.ignore_prefixes.append(python_base + os.sep)
+
+               self.trim_filename = prefix_trimmer(os.path.join(portage_const.PORTAGE_BASE_PATH, "pym") + os.sep).trim
+               self.show_local_lines = False
+               self.max_repr_length = 200
+
+       def event_handler(self, *args):
+               frame, event, arg = args
+               if "line" == event:
+                       if self.show_local_lines:
+                               self.trace_line(*args)
+               else:
+                       if not self.ignore_filename(frame.f_code.co_filename):
+                               self.trace_event(*args)
+                               return self.event_handler
+
+       def trace_event(self, frame, event, arg):
+               writemsg("%s line=%d name=%s event=%s %slocals=%s\n" % \
+               (self.trim_filename(frame.f_code.co_filename),
+               frame.f_lineno,
+               frame.f_code.co_name,
+               event,
+               self.arg_repr(frame, event, arg),
+               self.locals_repr(frame, event, arg)))
+
+       def arg_repr(self, frame, event, arg):
+               my_repr = None
+               if "return" == event:
+                       my_repr = repr(arg)
+                       if len(my_repr) > self.max_repr_length:
+                               my_repr = "'omitted'"
+                       return "value=%s " % my_repr
+               elif "exception" == event:
+                       my_repr = repr(arg[1])
+                       if len(my_repr) > self.max_repr_length:
+                               my_repr = "'omitted'"
+                       return "type=%s value=%s " % (arg[0], my_repr)
+
+               return ""
+
+       def trace_line(self, frame, event, arg):
+               writemsg("%s line=%d\n" % (self.trim_filename(frame.f_code.co_filename), frame.f_lineno))
+
+       def ignore_filename(self, filename):
+               if filename:
+                       for x in self.ignore_prefixes:
+                               if filename.startswith(x):
+                                       return True
+               return False
+
+       def locals_repr(self, frame, event, arg):
+               """Create a representation of the locals dict that is suitable for
+               tracing output."""
+
+               my_locals = frame.f_locals.copy()
+
+               # prevent unsafe  __repr__ call on self when __init__ is called
+               # (method calls aren't safe until after __init__  has completed).
+               if frame.f_code.co_name == "__init__" and "self" in my_locals:
+                       my_locals["self"] = "omitted"
+
+               # We omit items that will lead to unreasonable bloat of the trace
+               # output (and resulting log file).
+               for k, v in my_locals.iteritems():
+                       my_repr = repr(v)
+                       if len(my_repr) > self.max_repr_length:
+                               my_locals[k] = "omitted"
+               return my_locals
+
+class prefix_trimmer(object):
+       def __init__(self, prefix):
+               self.prefix = prefix
+               self.cut_index = len(prefix)
+               self.previous = None
+               self.previous_trimmed = None
+
+       def trim(self, s):
+               """Remove a prefix from the string and return the result.
+               The previous result is automatically cached."""
+               if s == self.previous:
+                       return self.previous_trimmed
+               else:
+                       if s.startswith(self.prefix):
+                               self.previous_trimmed = s[self.cut_index:]
+                       else:
+                               self.previous_trimmed = s
+                       return self.previous_trimmed