OLD | NEW |
(Empty) | |
| 1 """SCons.Debug |
| 2 |
| 3 Code for debugging SCons internal things. Shouldn't be |
| 4 needed by most users. |
| 5 |
| 6 """ |
| 7 |
| 8 # |
| 9 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The S
Cons Foundation |
| 10 # |
| 11 # Permission is hereby granted, free of charge, to any person obtaining |
| 12 # a copy of this software and associated documentation files (the |
| 13 # "Software"), to deal in the Software without restriction, including |
| 14 # without limitation the rights to use, copy, modify, merge, publish, |
| 15 # distribute, sublicense, and/or sell copies of the Software, and to |
| 16 # permit persons to whom the Software is furnished to do so, subject to |
| 17 # the following conditions: |
| 18 # |
| 19 # The above copyright notice and this permission notice shall be included |
| 20 # in all copies or substantial portions of the Software. |
| 21 # |
| 22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
| 23 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
| 24 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 25 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| 26 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 27 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 28 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 29 # |
| 30 |
| 31 __revision__ = "src/engine/SCons/Debug.py 5134 2010/08/16 23:02:40 bdeegan" |
| 32 |
| 33 import os |
| 34 import sys |
| 35 import time |
| 36 import weakref |
| 37 |
| 38 tracked_classes = {} |
| 39 |
| 40 def logInstanceCreation(instance, name=None): |
| 41 if name is None: |
| 42 name = instance.__class__.__name__ |
| 43 if name not in tracked_classes: |
| 44 tracked_classes[name] = [] |
| 45 tracked_classes[name].append(weakref.ref(instance)) |
| 46 |
| 47 def string_to_classes(s): |
| 48 if s == '*': |
| 49 return sorted(tracked_classes.keys()) |
| 50 else: |
| 51 return s.split() |
| 52 |
| 53 def fetchLoggedInstances(classes="*"): |
| 54 classnames = string_to_classes(classes) |
| 55 return [(cn, len(tracked_classes[cn])) for cn in classnames] |
| 56 |
| 57 def countLoggedInstances(classes, file=sys.stdout): |
| 58 for classname in string_to_classes(classes): |
| 59 file.write("%s: %d\n" % (classname, len(tracked_classes[classname]))) |
| 60 |
| 61 def listLoggedInstances(classes, file=sys.stdout): |
| 62 for classname in string_to_classes(classes): |
| 63 file.write('\n%s:\n' % classname) |
| 64 for ref in tracked_classes[classname]: |
| 65 obj = ref() |
| 66 if obj is not None: |
| 67 file.write(' %s\n' % repr(obj)) |
| 68 |
| 69 def dumpLoggedInstances(classes, file=sys.stdout): |
| 70 for classname in string_to_classes(classes): |
| 71 file.write('\n%s:\n' % classname) |
| 72 for ref in tracked_classes[classname]: |
| 73 obj = ref() |
| 74 if obj is not None: |
| 75 file.write(' %s:\n' % obj) |
| 76 for key, value in obj.__dict__.items(): |
| 77 file.write(' %20s : %s\n' % (key, value)) |
| 78 |
| 79 |
| 80 |
| 81 if sys.platform[:5] == "linux": |
| 82 # Linux doesn't actually support memory usage stats from getrusage(). |
| 83 def memory(): |
| 84 mstr = open('/proc/self/stat').read() |
| 85 mstr = mstr.split()[22] |
| 86 return int(mstr) |
| 87 elif sys.platform[:6] == 'darwin': |
| 88 #TODO really get memory stats for OS X |
| 89 def memory(): |
| 90 return 0 |
| 91 else: |
| 92 try: |
| 93 import resource |
| 94 except ImportError: |
| 95 try: |
| 96 import win32process |
| 97 import win32api |
| 98 except ImportError: |
| 99 def memory(): |
| 100 return 0 |
| 101 else: |
| 102 def memory(): |
| 103 process_handle = win32api.GetCurrentProcess() |
| 104 memory_info = win32process.GetProcessMemoryInfo( process_handle
) |
| 105 return memory_info['PeakWorkingSetSize'] |
| 106 else: |
| 107 def memory(): |
| 108 res = resource.getrusage(resource.RUSAGE_SELF) |
| 109 return res[4] |
| 110 |
| 111 # returns caller's stack |
| 112 def caller_stack(*backlist): |
| 113 import traceback |
| 114 if not backlist: |
| 115 backlist = [0] |
| 116 result = [] |
| 117 for back in backlist: |
| 118 tb = traceback.extract_stack(limit=3+back) |
| 119 key = tb[0][:3] |
| 120 result.append('%s:%d(%s)' % func_shorten(key)) |
| 121 return result |
| 122 |
| 123 caller_bases = {} |
| 124 caller_dicts = {} |
| 125 |
| 126 # trace a caller's stack |
| 127 def caller_trace(back=0): |
| 128 import traceback |
| 129 tb = traceback.extract_stack(limit=3+back) |
| 130 tb.reverse() |
| 131 callee = tb[1][:3] |
| 132 caller_bases[callee] = caller_bases.get(callee, 0) + 1 |
| 133 for caller in tb[2:]: |
| 134 caller = callee + caller[:3] |
| 135 try: |
| 136 entry = caller_dicts[callee] |
| 137 except KeyError: |
| 138 caller_dicts[callee] = entry = {} |
| 139 entry[caller] = entry.get(caller, 0) + 1 |
| 140 callee = caller |
| 141 |
| 142 # print a single caller and its callers, if any |
| 143 def _dump_one_caller(key, file, level=0): |
| 144 leader = ' '*level |
| 145 for v,c in sorted([(-v,c) for c,v in caller_dicts[key].items()]): |
| 146 file.write("%s %6d %s:%d(%s)\n" % ((leader,-v) + func_shorten(c[-3:]))) |
| 147 if c in caller_dicts: |
| 148 _dump_one_caller(c, file, level+1) |
| 149 |
| 150 # print each call tree |
| 151 def dump_caller_counts(file=sys.stdout): |
| 152 for k in sorted(caller_bases.keys()): |
| 153 file.write("Callers of %s:%d(%s), %d calls:\n" |
| 154 % (func_shorten(k) + (caller_bases[k],))) |
| 155 _dump_one_caller(k, file) |
| 156 |
| 157 shorten_list = [ |
| 158 ( '/scons/SCons/', 1), |
| 159 ( '/src/engine/SCons/', 1), |
| 160 ( '/usr/lib/python', 0), |
| 161 ] |
| 162 |
| 163 if os.sep != '/': |
| 164 shorten_list = [(t[0].replace('/', os.sep), t[1]) for t in shorten_list] |
| 165 |
| 166 def func_shorten(func_tuple): |
| 167 f = func_tuple[0] |
| 168 for t in shorten_list: |
| 169 i = f.find(t[0]) |
| 170 if i >= 0: |
| 171 if t[1]: |
| 172 i = i + len(t[0]) |
| 173 return (f[i:],)+func_tuple[1:] |
| 174 return func_tuple |
| 175 |
| 176 |
| 177 TraceFP = {} |
| 178 if sys.platform == 'win32': |
| 179 TraceDefault = 'con' |
| 180 else: |
| 181 TraceDefault = '/dev/tty' |
| 182 |
| 183 TimeStampDefault = None |
| 184 StartTime = time.time() |
| 185 PreviousTime = StartTime |
| 186 |
| 187 def Trace(msg, file=None, mode='w', tstamp=None): |
| 188 """Write a trace message to a file. Whenever a file is specified, |
| 189 it becomes the default for the next call to Trace().""" |
| 190 global TraceDefault |
| 191 global TimeStampDefault |
| 192 global PreviousTime |
| 193 if file is None: |
| 194 file = TraceDefault |
| 195 else: |
| 196 TraceDefault = file |
| 197 if tstamp is None: |
| 198 tstamp = TimeStampDefault |
| 199 else: |
| 200 TimeStampDefault = tstamp |
| 201 try: |
| 202 fp = TraceFP[file] |
| 203 except KeyError: |
| 204 try: |
| 205 fp = TraceFP[file] = open(file, mode) |
| 206 except TypeError: |
| 207 # Assume we were passed an open file pointer. |
| 208 fp = file |
| 209 if tstamp: |
| 210 now = time.time() |
| 211 fp.write('%8.4f %8.4f: ' % (now - StartTime, now - PreviousTime)) |
| 212 PreviousTime = now |
| 213 fp.write(msg) |
| 214 fp.flush() |
| 215 |
| 216 # Local Variables: |
| 217 # tab-width:4 |
| 218 # indent-tabs-mode:nil |
| 219 # End: |
| 220 # vim: set expandtab tabstop=4 shiftwidth=4: |
OLD | NEW |