| 1 |
|
|---|
| 2 |
|
|---|
| 3 |
|
|---|
| 4 |
|
|---|
| 5 |
|
|---|
| 6 |
|
|---|
| 7 |
|
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 |
|
|---|
| 14 |
|
|---|
| 15 |
|
|---|
| 16 |
|
|---|
| 17 |
|
|---|
| 18 |
|
|---|
| 19 |
|
|---|
| 20 |
|
|---|
| 21 |
|
|---|
| 22 |
|
|---|
| 23 |
|
|---|
| 24 |
|
|---|
| 25 |
|
|---|
| 26 |
|
|---|
| 27 |
|
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
|
|---|
| 31 |
|
|---|
| 32 |
|
|---|
| 33 |
|
|---|
| 34 |
__revision__ = "/home/scons/scons/branch.0/branch.96/baseline/src/script/scons-time.py 0.96.95.D001 2007/02/12 21:41:50 knight" |
|---|
| 35 |
|
|---|
| 36 |
import getopt |
|---|
| 37 |
import glob |
|---|
| 38 |
import os |
|---|
| 39 |
import re |
|---|
| 40 |
import shutil |
|---|
| 41 |
import string |
|---|
| 42 |
import sys |
|---|
| 43 |
import tempfile |
|---|
| 44 |
import time |
|---|
| 45 |
|
|---|
| 46 |
class Plotter: |
|---|
| 47 |
def increment_size(self, largest): |
|---|
| 48 |
""" |
|---|
| 49 |
Return the size of each horizontal increment line for a specified |
|---|
| 50 |
maximum value. This returns a value that will provide somewhere |
|---|
| 51 |
between 5 and 9 horizontal lines on the graph, on some set of |
|---|
| 52 |
boundaries that are multiples of 10/100/1000/etc. |
|---|
| 53 |
""" |
|---|
| 54 |
i = largest / 5 |
|---|
| 55 |
if not i: |
|---|
| 56 |
return largest |
|---|
| 57 |
multiplier = 1 |
|---|
| 58 |
while i >= 10: |
|---|
| 59 |
i = i / 10 |
|---|
| 60 |
multiplier = multiplier * 10 |
|---|
| 61 |
return i * multiplier |
|---|
| 62 |
|
|---|
| 63 |
def max_graph_value(self, largest): |
|---|
| 64 |
|
|---|
| 65 |
largest = int(largest) + 1 |
|---|
| 66 |
increment = self.increment_size(largest) |
|---|
| 67 |
return ((largest + increment - 1) / increment) * increment |
|---|
| 68 |
|
|---|
| 69 |
class Line: |
|---|
| 70 |
def __init__(self, points, type, title, label, comment, fmt="%s %s"): |
|---|
| 71 |
self.points = points |
|---|
| 72 |
self.type = type |
|---|
| 73 |
self.title = title |
|---|
| 74 |
self.label = label |
|---|
| 75 |
self.comment = comment |
|---|
| 76 |
self.fmt = fmt |
|---|
| 77 |
|
|---|
| 78 |
def print_label(self, inx, x, y): |
|---|
| 79 |
if self.label: |
|---|
| 80 |
print 'set label %s "%s" at %s,%s right' % (inx, self.label, x, y) |
|---|
| 81 |
|
|---|
| 82 |
def plot_string(self): |
|---|
| 83 |
if self.title: |
|---|
| 84 |
title_string = 'title "%s"' % self.title |
|---|
| 85 |
else: |
|---|
| 86 |
title_string = 'notitle' |
|---|
| 87 |
return "'-' %s with lines lt %s" % (title_string, self.type) |
|---|
| 88 |
|
|---|
| 89 |
def print_points(self, fmt=None): |
|---|
| 90 |
if fmt is None: |
|---|
| 91 |
fmt = self.fmt |
|---|
| 92 |
if self.comment: |
|---|
| 93 |
print '# %s' % self.comment |
|---|
| 94 |
for x, y in self.points: |
|---|
| 95 |
print fmt % (x, y) |
|---|
| 96 |
print 'e' |
|---|
| 97 |
|
|---|
| 98 |
def get_x_values(self): |
|---|
| 99 |
return [ p[0] for p in self.points ] |
|---|
| 100 |
|
|---|
| 101 |
def get_y_values(self): |
|---|
| 102 |
return [ p[1] for p in self.points ] |
|---|
| 103 |
|
|---|
| 104 |
class Gnuplotter(Plotter): |
|---|
| 105 |
|
|---|
| 106 |
def __init__(self, title, key_location): |
|---|
| 107 |
self.lines = [] |
|---|
| 108 |
self.title = title |
|---|
| 109 |
self.key_location = key_location |
|---|
| 110 |
|
|---|
| 111 |
def line(self, points, type, title=None, label=None, comment=None, fmt='%s %s'): |
|---|
| 112 |
if points: |
|---|
| 113 |
line = Line(points, type, title, label, comment, fmt) |
|---|
| 114 |
self.lines.append(line) |
|---|
| 115 |
|
|---|
| 116 |
def plot_string(self, line): |
|---|
| 117 |
return line.plot_string() |
|---|
| 118 |
|
|---|
| 119 |
def vertical_bar(self, x, type, label, comment): |
|---|
| 120 |
if self.get_min_x() <= x and x <= self.get_max_x(): |
|---|
| 121 |
points = [(x, 0), (x, self.get_max_x())] |
|---|
| 122 |
self.line(points, type, label, comment) |
|---|
| 123 |
|
|---|
| 124 |
def get_all_x_values(self): |
|---|
| 125 |
result = [] |
|---|
| 126 |
for line in self.lines: |
|---|
| 127 |
result.extend(line.get_x_values()) |
|---|
| 128 |
return result |
|---|
| 129 |
|
|---|
| 130 |
def get_all_y_values(self): |
|---|
| 131 |
result = [] |
|---|
| 132 |
for line in self.lines: |
|---|
| 133 |
result.extend(line.get_y_values()) |
|---|
| 134 |
return result |
|---|
| 135 |
|
|---|
| 136 |
def get_min_x(self): |
|---|
| 137 |
try: |
|---|
| 138 |
return self.min_x |
|---|
| 139 |
except AttributeError: |
|---|
| 140 |
self.min_x = min(self.get_all_x_values()) |
|---|
| 141 |
return self.min_x |
|---|
| 142 |
|
|---|
| 143 |
def get_max_x(self): |
|---|
| 144 |
try: |
|---|
| 145 |
return self.max_x |
|---|
| 146 |
except AttributeError: |
|---|
| 147 |
self.max_x = max(self.get_all_x_values()) |
|---|
| 148 |
return self.max_x |
|---|
| 149 |
|
|---|
| 150 |
def get_min_y(self): |
|---|
| 151 |
try: |
|---|
| 152 |
return self.min_y |
|---|
| 153 |
except AttributeError: |
|---|
| 154 |
self.min_y = min(self.get_all_y_values()) |
|---|
| 155 |
return self.min_y |
|---|
| 156 |
|
|---|
| 157 |
def get_max_y(self): |
|---|
| 158 |
try: |
|---|
| 159 |
return self.max_y |
|---|
| 160 |
except AttributeError: |
|---|
| 161 |
self.max_y = max(self.get_all_y_values()) |
|---|
| 162 |
return self.max_y |
|---|
| 163 |
|
|---|
| 164 |
def draw(self): |
|---|
| 165 |
|
|---|
| 166 |
if not self.lines: |
|---|
| 167 |
return |
|---|
| 168 |
|
|---|
| 169 |
if self.title: |
|---|
| 170 |
print 'set title "%s"' % self.title |
|---|
| 171 |
print 'set key %s' % self.key_location |
|---|
| 172 |
|
|---|
| 173 |
inx = 1 |
|---|
| 174 |
max_y = self.max_graph_value(self.get_max_y())/2 |
|---|
| 175 |
for line in self.lines: |
|---|
| 176 |
line.print_label(inx, line.points[0][0]-1, max_y) |
|---|
| 177 |
inx += 1 |
|---|
| 178 |
|
|---|
| 179 |
plot_strings = [ self.plot_string(l) for l in self.lines ] |
|---|
| 180 |
print 'plot ' + ', \\\n '.join(plot_strings) |
|---|
| 181 |
|
|---|
| 182 |
for line in self.lines: |
|---|
| 183 |
line.print_points() |
|---|
| 184 |
|
|---|
| 185 |
|
|---|
| 186 |
|
|---|
| 187 |
def untar(fname): |
|---|
| 188 |
import tarfile |
|---|
| 189 |
tar = tarfile.open(name=fname, mode='r') |
|---|
| 190 |
for tarinfo in tar: |
|---|
| 191 |
tar.extract(tarinfo) |
|---|
| 192 |
tar.close() |
|---|
| 193 |
|
|---|
| 194 |
def unzip(fname): |
|---|
| 195 |
import zipfile |
|---|
| 196 |
zf = zipfile.ZipFile(fname, 'r') |
|---|
| 197 |
for name in zf.namelist(): |
|---|
| 198 |
dir = os.path.dirname(name) |
|---|
| 199 |
try: |
|---|
| 200 |
os.makedirs(dir) |
|---|
| 201 |
except: |
|---|
| 202 |
pass |
|---|
| 203 |
open(name, 'w').write(zf.read(name)) |
|---|
| 204 |
|
|---|
| 205 |
def read_tree(dir): |
|---|
| 206 |
def read_files(arg, dirname, fnames): |
|---|
| 207 |
for fn in fnames: |
|---|
| 208 |
fn = os.path.join(dirname, fn) |
|---|
| 209 |
if os.path.isfile(fn): |
|---|
| 210 |
open(fn, 'rb').read() |
|---|
| 211 |
os.path.walk('.', read_files, None) |
|---|
| 212 |
|
|---|
| 213 |
def redirect_to_file(command, log): |
|---|
| 214 |
return '%s > %s 2>&1' % (command, log) |
|---|
| 215 |
|
|---|
| 216 |
def tee_to_file(command, log): |
|---|
| 217 |
return '%s 2>&1 | tee %s' % (command, log) |
|---|
| 218 |
|
|---|
| 219 |
|
|---|
| 220 |
|
|---|
| 221 |
class SConsTimer: |
|---|
| 222 |
""" |
|---|
| 223 |
Usage: scons-time SUBCOMMAND [ARGUMENTS] |
|---|
| 224 |
Type "scons-time help SUBCOMMAND" for help on a specific subcommand. |
|---|
| 225 |
|
|---|
| 226 |
Available subcommands: |
|---|
| 227 |
func Extract test-run data for a function |
|---|
| 228 |
help Provides help |
|---|
| 229 |
mem Extract --debug=memory data from test runs |
|---|
| 230 |
obj Extract --debug=count data from test runs |
|---|
| 231 |
time Extract --debug=time data from test runs |
|---|
| 232 |
run Runs a test configuration |
|---|
| 233 |
""" |
|---|
| 234 |
|
|---|
| 235 |
name = 'scons-time' |
|---|
| 236 |
name_spaces = ' '*len(name) |
|---|
| 237 |
|
|---|
| 238 |
def makedict(**kw): |
|---|
| 239 |
return kw |
|---|
| 240 |
|
|---|
| 241 |
default_settings = makedict( |
|---|
| 242 |
aegis = 'aegis', |
|---|
| 243 |
aegis_project = None, |
|---|
| 244 |
chdir = None, |
|---|
| 245 |
config_file = None, |
|---|
| 246 |
initial_commands = [], |
|---|
| 247 |
key_location = 'bottom left', |
|---|
| 248 |
orig_cwd = os.getcwd(), |
|---|
| 249 |
outdir = None, |
|---|
| 250 |
prefix = '', |
|---|
| 251 |
python = '"%s"' % sys.executable, |
|---|
| 252 |
redirect = redirect_to_file, |
|---|
| 253 |
scons = None, |
|---|
| 254 |
scons_flags = '--debug=count --debug=memory --debug=time --debug=memoizer', |
|---|
| 255 |
scons_lib_dir = None, |
|---|
| 256 |
scons_wrapper = None, |
|---|
| 257 |
startup_targets = '--help', |
|---|
| 258 |
subdir = None, |
|---|
| 259 |
subversion_url = None, |
|---|
| 260 |
svn = 'svn', |
|---|
| 261 |
svn_co_flag = '-q', |
|---|
| 262 |
tar = 'tar', |
|---|
| 263 |
targets = '', |
|---|
| 264 |
targets0 = None, |
|---|
| 265 |
targets1 = None, |
|---|
| 266 |
targets2 = None, |
|---|
| 267 |
title = None, |
|---|
| 268 |
unzip = 'unzip', |
|---|
| 269 |
verbose = False, |
|---|
| 270 |
vertical_bars = [], |
|---|
| 271 |
|
|---|
| 272 |
unpack_map = { |
|---|
| 273 |
'.tar.gz' : (untar, '%(tar)s xzf %%s'), |
|---|
| 274 |
'.tgz' : (untar, '%(tar)s xzf %%s'), |
|---|
| 275 |
'.tar' : (untar, '%(tar)s xf %%s'), |
|---|
| 276 |
'.zip' : (unzip, '%(unzip)s %%s'), |
|---|
| 277 |
}, |
|---|
| 278 |
) |
|---|
| 279 |
|
|---|
| 280 |
run_titles = [ |
|---|
| 281 |
'Startup', |
|---|
| 282 |
'Full build', |
|---|
| 283 |
'Up-to-date build', |
|---|
| 284 |
] |
|---|
| 285 |
|
|---|
| 286 |
run_commands = [ |
|---|
| 287 |
'%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof0)s %(targets0)s', |
|---|
| 288 |
'%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof1)s %(targets1)s', |
|---|
| 289 |
'%(python)s %(scons_wrapper)s %(scons_flags)s --profile=%(prof2)s %(targets2)s', |
|---|
| 290 |
] |
|---|
| 291 |
|
|---|
| 292 |
stages = [ |
|---|
| 293 |
'pre-read', |
|---|
| 294 |
'post-read', |
|---|
| 295 |
'pre-build', |
|---|
| 296 |
'post-build', |
|---|
| 297 |
] |
|---|
| 298 |
|
|---|
| 299 |
stage_strings = { |
|---|
| 300 |
'pre-read' : 'Memory before reading SConscript files:', |
|---|
| 301 |
'post-read' : 'Memory after reading SConscript files:', |
|---|
| 302 |
'pre-build' : 'Memory before building targets:', |
|---|
| 303 |
'post-build' : 'Memory after building targets:', |
|---|
| 304 |
} |
|---|
| 305 |
|
|---|
| 306 |
memory_string_all = 'Memory ' |
|---|
| 307 |
|
|---|
| 308 |
default_stage = stages[-1] |
|---|
| 309 |
|
|---|
| 310 |
time_strings = { |
|---|
| 311 |
'total' : 'Total build time', |
|---|
| 312 |
'SConscripts' : 'Total SConscript file execution time', |
|---|
| 313 |
'SCons' : 'Total SCons execution time', |
|---|
| 314 |
'commands' : 'Total command execution time', |
|---|
| 315 |
} |
|---|
| 316 |
|
|---|
| 317 |
time_string_all = 'Total .* time' |
|---|
| 318 |
|
|---|
| 319 |
|
|---|
| 320 |
|
|---|
| 321 |
def __init__(self): |
|---|
| 322 |
self.__dict__.update(self.default_settings) |
|---|
| 323 |
|
|---|
| 324 |
|
|---|
| 325 |
|
|---|
| 326 |
def subst(self, x, dictionary): |
|---|
| 327 |
try: |
|---|
| 328 |
return x % dictionary |
|---|
| 329 |
except TypeError: |
|---|
| 330 |
|
|---|
| 331 |
|
|---|
| 332 |
return x |
|---|
| 333 |
|
|---|
| 334 |
def subst_variables(self, command, dictionary): |
|---|
| 335 |
""" |
|---|
| 336 |
Substitutes (via the format operator) the values in the specified |
|---|
| 337 |
dictionary into the specified command. |
|---|
| 338 |
|
|---|
| 339 |
The command can be an (action, string) tuple. In all cases, we |
|---|
| 340 |
perform substitution on strings and don't worry if something isn't |
|---|
| 341 |
a string. (It's probably a Python function to be executed.) |
|---|
| 342 |
""" |
|---|
| 343 |
try: |
|---|
| 344 |
command + '' |
|---|
| 345 |
except TypeError: |
|---|
| 346 |
action = command[0] |
|---|
| 347 |
string = command[1] |
|---|
| 348 |
args = command[2:] |
|---|
| 349 |
else: |
|---|
| 350 |
action = command |
|---|
| 351 |
string = action |
|---|
| 352 |
args = (()) |
|---|
| 353 |
action = self.subst(action, dictionary) |
|---|
| 354 |
string = self.subst(string, dictionary) |
|---|
| 355 |
return (action, string, args) |
|---|
| 356 |
|
|---|
| 357 |
def _do_not_display(self, msg, *args): |
|---|
| 358 |
pass |
|---|
| 359 |
|
|---|
| 360 |
def display(self, msg, *args): |
|---|
| 361 |
""" |
|---|
| 362 |
Displays the specified message. |
|---|
| 363 |
|
|---|
| 364 |
Each message is prepended with a standard prefix of our name |
|---|
| 365 |
plus the time. |
|---|
| 366 |
""" |
|---|
| 367 |
if callable(msg): |
|---|
| 368 |
msg = msg(*args) |
|---|
| 369 |
else: |
|---|
| 370 |
msg = msg % args |
|---|
| 371 |
if msg is None: |
|---|
| 372 |
return |
|---|
| 373 |
fmt = '%s[%s]: %s\n' |
|---|
| 374 |
sys.stdout.write(fmt % (self.name, time.strftime('%H:%M:%S'), msg)) |
|---|
| 375 |
|
|---|
| 376 |
def _do_not_execute(self, action, *args): |
|---|
| 377 |
pass |
|---|
| 378 |
|
|---|
| 379 |
def execute(self, action, *args): |
|---|
| 380 |
""" |
|---|
| 381 |
Executes the specified action. |
|---|
| 382 |
|
|---|
| 383 |
The action is called if it's a callable Python function, and |
|---|
| 384 |
otherwise passed to os.system(). |
|---|
| 385 |
""" |
|---|
| 386 |
if callable(action): |
|---|
| 387 |
action(*args) |
|---|
| 388 |
else: |
|---|
| 389 |
os.system(action % args) |
|---|
| 390 |
|
|---|
| 391 |
def run_command_list(self, commands, dict): |
|---|
| 392 |
""" |
|---|
| 393 |
Executes a list of commands, substituting values from the |
|---|
| 394 |
specified dictionary. |
|---|
| 395 |
""" |
|---|
| 396 |
commands = [ self.subst_variables(c, dict) for c in commands ] |
|---|
| 397 |
for action, string, args in commands: |
|---|
| 398 |
self.display(string, *args) |
|---|
| 399 |
sys.stdout.flush() |
|---|
| 400 |
status = self.execute(action, *args) |
|---|
| 401 |
if status: |
|---|
| 402 |
sys.exit(status) |
|---|
| 403 |
|
|---|
| 404 |
def log_display(self, command, log): |
|---|
| 405 |
command = self.subst(command, self.__dict__) |
|---|
| 406 |
if log: |
|---|
| 407 |
command = self.redirect(command, log) |
|---|
| 408 |
return command |
|---|
| 409 |
|
|---|
| 410 |
def log_execute(self, command, log): |
|---|
| 411 |
command = self.subst(command, self.__dict__) |
|---|
| 412 |
output = os.popen(command).read() |
|---|
| 413 |
if self.verbose: |
|---|
| 414 |
sys.stdout.write(output) |
|---|
| 415 |
open(log, 'wb').write(output) |
|---|
| 416 |
|
|---|
| 417 |
|
|---|
| 418 |
|
|---|
| 419 |
def archive_splitext(self, path): |
|---|
| 420 |
""" |
|---|
| 421 |
Splits an archive name into a filename base and extension. |
|---|
| 422 |
|
|---|
| 423 |
This is like os.path.splitext() (which it calls) except that it |
|---|
| 424 |
also looks for '.tar.gz' and treats it as an atomic extensions. |
|---|
| 425 |
""" |
|---|
| 426 |
if path.endswith('.tar.gz'): |
|---|
| 427 |
return path[:-7], path[-7:] |
|---|
| 428 |
else: |
|---|
| 429 |
return os.path.splitext(path) |
|---|
| 430 |
|
|---|
| 431 |
def args_to_files(self, args, tail=None): |
|---|
| 432 |
""" |
|---|
| 433 |
Takes a list of arguments, expands any glob patterns, and |
|---|
| 434 |
returns the last "tail" files from the list. |
|---|
| 435 |
""" |
|---|
| 436 |
files = [] |
|---|
| 437 |
for a in args: |
|---|
| 438 |
g = glob.glob(a) |
|---|
| 439 |
g.sort() |
|---|
| 440 |
files.extend(g) |
|---|
| 441 |
|
|---|
| 442 |
if tail: |
|---|
| 443 |
files = files[-tail:] |
|---|
| 444 |
|
|---|
| 445 |
return files |
|---|
| 446 |
|
|---|
| 447 |
def ascii_table(self, files, columns, |
|---|
| 448 |
line_function, file_function=lambda x: x, |
|---|
| 449 |
*args, **kw): |
|---|
| 450 |
|
|---|
| 451 |
header_fmt = ' '.join(['%12s'] * len(columns)) |
|---|
| 452 |
line_fmt = header_fmt + ' %s' |
|---|
| 453 |
|
|---|
| 454 |
print header_fmt % columns |
|---|
| 455 |
|
|---|
| 456 |
for file in files: |
|---|
| 457 |
t = line_function(file, *args, **kw) |
|---|
| 458 |
diff = len(columns) - len(t) |
|---|
| 459 |
if diff > 0: |
|---|
| 460 |
t += [''] * diff |
|---|
| 461 |
t.append(file_function(file)) |
|---|
| 462 |
print line_fmt % tuple(t) |
|---|
| 463 |
|
|---|
| 464 |
def collect_results(self, files, function, *args, **kw): |
|---|
| 465 |
results = {} |
|---|
| 466 |
|
|---|
| 467 |
for file in files: |
|---|
| 468 |
base = os.path.splitext(file)[0] |
|---|
| 469 |
run, index = string.split(base, '-')[-2:] |
|---|
| 470 |
|
|---|
| 471 |
run = int(run) |
|---|
| 472 |
index = int(index) |
|---|
| 473 |
|
|---|
| 474 |
value = function(file, *args, **kw) |
|---|
| 475 |
|
|---|
| 476 |
try: |
|---|
| 477 |
r = results[index] |
|---|
| 478 |
except KeyError: |
|---|
| 479 |
r = [] |
|---|
| 480 |
results[index] = r |
|---|
| 481 |
r.append((run, value)) |
|---|
| 482 |
|
|---|
| 483 |
return results |
|---|
| 484 |
|
|---|
| 485 |
def doc_to_help(self, obj): |
|---|
| 486 |
""" |
|---|
| 487 |
Translates an object's __doc__ string into help text. |
|---|
| 488 |
|
|---|
| 489 |
This strips a consistent number of spaces from each line in the |
|---|
| 490 |
help text, essentially "outdenting" the text to the left-most |
|---|
| 491 |
column. |
|---|
| 492 |
""" |
|---|
| 493 |
doc = obj.__doc__ |
|---|
| 494 |
if doc is None: |
|---|
| 495 |
return '' |
|---|
| 496 |
return self.outdent(doc) |
|---|
| 497 |
|
|---|
| 498 |
def find_next_run_number(self, dir, prefix): |
|---|
| 499 |
""" |
|---|
| 500 |
Returns the next run number in a directory for the specified prefix. |
|---|
| 501 |
|
|---|
| 502 |
Examines the contents the specified directory for files with the |
|---|
| 503 |
specified prefix, extracts the run numbers from each file name, |
|---|
| 504 |
and returns the next run number after the largest it finds. |
|---|
| 505 |
""" |
|---|
| 506 |
x = re.compile(re.escape(prefix) + '-([0-9]+).*') |
|---|
| 507 |
matches = map(lambda e, x=x: x.match(e), os.listdir(dir)) |
|---|
| 508 |
matches = filter(None, matches) |
|---|
| 509 |
if not matches: |
|---|
| 510 |
return 0 |
|---|
| 511 |
run_numbers = map(lambda m: int(m.group(1)), matches) |
|---|
| 512 |
return int(max(run_numbers)) + 1 |
|---|
| 513 |
|
|---|
| 514 |
def gnuplot_results(self, results, fmt='%s %.3f'): |
|---|
| 515 |
""" |
|---|
| 516 |
Prints out a set of results in Gnuplot format. |
|---|
| 517 |
""" |
|---|
| 518 |
gp = Gnuplotter(self.title, self.key_location) |
|---|
| 519 |
|
|---|
| 520 |
indices = results.keys() |
|---|
| 521 |
indices.sort() |
|---|
| 522 |
|
|---|
| 523 |
for i in indices: |
|---|
| 524 |
try: |
|---|
| 525 |
t = self.run_titles[i] |
|---|
| 526 |
except IndexError: |
|---|
| 527 |
t = '??? %s ???' % i |
|---|
| 528 |
results[i].sort() |
|---|
| 529 |
gp.line(results[i], i+1, t, None, t, fmt=fmt) |
|---|
| 530 |
|
|---|
| 531 |
for bar_tuple in self.vertical_bars: |
|---|
| 532 |
try: |
|---|
| 533 |
x, type, label, comment = bar_tuple |
|---|
| 534 |
except ValueError: |
|---|
| 535 |
x, type, label = bar_tuple |
|---|
| 536 |
comment = label |
|---|
| 537 |
gp.vertical_bar(x, type, None, label, comment) |
|---|
| 538 |
|
|---|
| 539 |
gp.draw() |
|---|
| 540 |
|
|---|
| 541 |
def logfile_name(self, invocation): |
|---|
| 542 |
""" |
|---|
| 543 |
Returns the absolute path of a log file for the specificed |
|---|
| 544 |
invocation number. |
|---|
| 545 |
""" |
|---|
| 546 |
name = self.prefix_run + '-%d.log' % invocation |
|---|
| 547 |
return os.path.join(self.outdir, name) |
|---|
| 548 |
|
|---|
| 549 |
def outdent(self, s): |
|---|
| 550 |
""" |
|---|
| 551 |
Strip as many spaces from each line as are found at the beginning |
|---|
| 552 |
of the first line in the list. |
|---|
| 553 |
""" |
|---|
| 554 |
lines = s.split('\n') |
|---|
| 555 |
if lines[0] == '': |
|---|
| 556 |
lines = lines[1:] |
|---|
| 557 |
spaces = re.match(' *', lines[0]).group(0) |
|---|
| 558 |
def strip_initial_spaces(l, s=spaces): |
|---|
| 559 |
if l.startswith(spaces): |
|---|
| 560 |
l = l[len(spaces):] |
|---|
| 561 |
return l |
|---|
| 562 |
return '\n'.join([ strip_initial_spaces(l) for l in lines ]) + '\n' |
|---|
| 563 |
|
|---|
| 564 |
def profile_name(self, invocation): |
|---|
| 565 |
""" |
|---|
| 566 |
Returns the absolute path of a profile file for the specified |
|---|
| 567 |
invocation number. |
|---|
| 568 |
""" |
|---|
| 569 |
name = self.prefix_run + '-%d.prof' % invocation |
|---|
| 570 |
return os.path.join(self.outdir, name) |
|---|
| 571 |
|
|---|
| 572 |
def set_env(self, key, value): |
|---|
| 573 |
os.environ[key] = value |
|---|
| 574 |
|
|---|
| 575 |
|
|---|
| 576 |
|
|---|
| 577 |
def get_debug_times(self, file, time_string=None): |
|---|
| 578 |
""" |
|---|
| 579 |
Fetch times from the --debug=time strings in the specified file. |
|---|
| 580 |
""" |
|---|
| 581 |
if time_string is None: |
|---|
| 582 |
search_string = self.time_string_all |
|---|
| 583 |
else: |
|---|
| 584 |
search_string = time_string |
|---|
| 585 |
contents = open(file).read() |
|---|
| 586 |
result = re.findall(r'%s: ([\d\.]*)' % search_string, contents)[-4:] |
|---|
| 587 |
result = [ float(r) for r in result ] |
|---|
| 588 |
if not time_string is None: |
|---|
| 589 |
result = result[0] |
|---|
| 590 |
return result |
|---|
| 591 |
|
|---|
| 592 |
def get_function_profile(self, file, function): |
|---|
| 593 |
""" |
|---|
| 594 |
Returns the file, line number, function name, and cumulative time. |
|---|
| 595 |
""" |
|---|
| 596 |
try: |
|---|
| 597 |
import pstats |
|---|
| 598 |
except ImportError, e: |
|---|
| 599 |
sys.stderr.write('%s: func: %s\n' % (self.name, e)) |
|---|
| 600 |
sys.stderr.write('%s This version of Python is missing the profiler.\n' % self.name_spaces) |
|---|
| 601 |
sys.stderr.write('%s Cannot use the "func" subcommand.\n' % self.name_spaces) |
|---|
| 602 |
sys.exit(1) |
|---|
| 603 |
statistics = pstats.Stats(file).stats |
|---|
| 604 |
matches = [ e for e in statistics.items() if e[0][2] == function ] |
|---|
| 605 |
r = matches[0] |
|---|
| 606 |
return r[0][0], r[0][1], r[0][2], r[1][3] |
|---|
| 607 |
|
|---|
| 608 |
def get_function_time(self, file, function): |
|---|
| 609 |
""" |
|---|
| 610 |
Returns just the cumulative time for the specified function. |
|---|
| 611 |
""" |
|---|
| 612 |
return self.get_function_profile(file, function)[3] |
|---|
| 613 |
|
|---|
| 614 |
def get_memory(self, file, memory_string=None): |
|---|
| 615 |
""" |
|---|
| 616 |
Returns a list of integers of the amount of memory used. The |
|---|
| 617 |
default behavior is to return all the stages. |
|---|
| 618 |
""" |
|---|
| 619 |
if memory_string is None: |
|---|
| 620 |
search_string = self.memory_string_all |
|---|
| 621 |
else: |
|---|
| 622 |
search_string = memory_string |
|---|
| 623 |
lines = open(file).readlines() |
|---|
| 624 |
lines = [ l for l in lines if l.startswith(search_string) ][-4:] |
|---|
| 625 |
result = [ int(l.split()[-1]) for l in lines[-4:] ] |
|---|
| 626 |
if len(result) == 1: |
|---|
| 627 |
result = result[0] |
|---|
| 628 |
return result |
|---|
| 629 |
|
|---|
| 630 |
def get_object_counts(self, file, object_name, index=None): |
|---|
| 631 |
""" |
|---|
| 632 |
Returns the counts of the specified object_name. |
|---|
| 633 |
""" |
|---|
| 634 |
object_string = ' ' + object_name + '\n' |
|---|
| 635 |
lines = open(file).readlines() |
|---|
| 636 |
line = [ l for l in lines if l.endswith(object_string) ][0] |
|---|
| 637 |
result = [ int(field) for field in line.split()[:4] ] |
|---|
| 638 |
if not index is None: |
|---|
| 639 |
result = result[index] |
|---|
| 640 |
return result |
|---|
| 641 |
|
|---|
| 642 |
|
|---|
| 643 |
|
|---|
| 644 |
command_alias = {} |
|---|
| 645 |
|
|---|
| 646 |
def execute_subcommand(self, argv): |
|---|
| 647 |
""" |
|---|
| 648 |
Executes the do_*() function for the specified subcommand (argv[0]). |
|---|
| 649 |
""" |
|---|
| 650 |
if not argv: |
|---|
| 651 |
return |
|---|
| 652 |
cmdName = self.command_alias.get(argv[0], argv[0]) |
|---|
| 653 |
try: |
|---|
| 654 |
func = getattr(self, 'do_' + cmdName) |
|---|
| 655 |
except AttributeError: |
|---|
| 656 |
return self.default(argv) |
|---|
| 657 |
try: |
|---|
| 658 |
return func(argv) |
|---|
| 659 |
except TypeError, e: |
|---|
| 660 |
sys.stderr.write("%s %s: %s\n" % (self.name, cmdName, e)) |
|---|
| 661 |
import traceback |
|---|
| 662 |
traceback.print_exc(file=sys.stderr) |
|---|
| 663 |
sys.stderr.write("Try '%s help %s'\n" % (self.name, cmdName)) |
|---|
| 664 |
|
|---|
| 665 |
def default(self, argv): |
|---|
| 666 |
""" |
|---|
| 667 |
The default behavior for an unknown subcommand. Prints an |
|---|
| 668 |
error message and exits. |
|---|
| 669 |
""" |
|---|
| 670 |
sys.stderr.write('%s: Unknown subcommand "%s".\n' % (self.name, argv[0])) |
|---|
| 671 |
sys.stderr.write('Type "%s help" for usage.\n' % self.name) |
|---|
| 672 |
sys.exit(1) |
|---|
| 673 |
|
|---|
| 674 |
|
|---|
| 675 |
|
|---|
| 676 |
def do_help(self, argv): |
|---|
| 677 |
""" |
|---|
| 678 |
""" |
|---|
| 679 |
if argv[1:]: |
|---|
| 680 |
for arg in argv[1:]: |
|---|
| 681 |
try: |
|---|
| 682 |
func = getattr(self, 'do_' + arg) |
|---|
| 683 |
except AttributeError: |
|---|
| 684 |
sys.stderr.write('%s: No help for "%s"\n' % (self.name, arg)) |
|---|
| 685 |
else: |
|---|
| 686 |
try: |
|---|
| 687 |
help = getattr(self, 'help_' + arg) |
|---|
| 688 |
except AttributeError: |
|---|
| 689 |
sys.stdout.write(self.doc_to_help(func)) |
|---|
| 690 |
sys.stdout.flush() |
|---|
| 691 |
else: |
|---|
| 692 |
help() |
|---|
| 693 |
else: |
|---|
| 694 |
doc = self.doc_to_help(self.__class__) |
|---|
| 695 |
if doc: |
|---|
| 696 |
sys.stdout.write(doc) |
|---|
| 697 |
sys.stdout.flush() |
|---|
| 698 |
return None |
|---|
| 699 |
|
|---|
| 700 |
|
|---|
| 701 |
|
|---|
| 702 |
def help_func(self): |
|---|
| 703 |
help = """\ |
|---|
| 704 |
Usage: scons-time func [OPTIONS] FILE [...] |
|---|
| 705 |
|
|---|
| 706 |
-C DIR, --chdir=DIR Change to DIR before looking for files |
|---|
| 707 |
-f FILE, --file=FILE Read configuration from specified FILE |
|---|
| 708 |
--fmt=FORMAT, --format=FORMAT Print data in specified FORMAT |
|---|
| 709 |
--func=NAME, --function=NAME Report time for function NAME |
|---|
| 710 |
-h, --help Print this help and exit |
|---|
| 711 |
-p STRING, --prefix=STRING Use STRING as log file/profile prefix |
|---|
| 712 |
-t NUMBER, --tail=NUMBER Only report the last NUMBER files |
|---|
| 713 |
--title=TITLE Specify the output plot TITLE |
|---|
| 714 |
""" |
|---|
| 715 |
sys.stdout.write(self.outdent(help)) |
|---|
| 716 |
sys.stdout.flush() |
|---|
| 717 |
|
|---|
| 718 |
def do_func(self, argv): |
|---|
| 719 |
""" |
|---|
| 720 |
""" |
|---|
| 721 |
format = 'ascii' |
|---|
| 722 |
function_name = '_main' |
|---|
| 723 |
tail = None |
|---|
| 724 |
|
|---|
| 725 |
short_opts = '?C:f:hp:t:' |
|---|
| 726 |
|
|---|
| 727 |
long_opts = [ |
|---|
| 728 |
'chdir=', |
|---|
| 729 |
'file=', |
|---|
| 730 |
'fmt=', |
|---|
| 731 |
'format=', |
|---|
| 732 |
'func=', |
|---|
| 733 |
'function=', |
|---|
| 734 |
'help', |
|---|
| 735 |
'prefix=', |
|---|
| 736 |
'tail=', |
|---|
| 737 |
'title=', |
|---|
| 738 |
] |
|---|
| 739 |
|
|---|
| 740 |
opts, args = getopt.getopt(argv[1:], short_opts, long_opts) |
|---|
| 741 |
|
|---|
| 742 |
for o, a in opts: |
|---|
| 743 |
if o in ('-C', '--chdir'): |
|---|
| 744 |
self.chdir = a |
|---|
| 745 |
elif o in ('-f', '--file'): |
|---|
| 746 |
self.config_file = a |
|---|
| 747 |
elif o in ('--fmt', '--format'): |
|---|
| 748 |
format = a |
|---|
| 749 |
elif o in ('--func', '--function'): |
|---|
| 750 |
function_name = a |
|---|
| 751 |
elif o in ('-?', '-h', '--help'): |
|---|
| 752 |
self.do_help(['help', 'func']) |
|---|
| 753 |
sys.exit(0) |
|---|
| 754 |
elif o in ('--max'): |
|---|
| 755 |
max_time = int(a) |
|---|
| 756 |
elif o in ('-p', '--prefix'): |
|---|
| 757 |
self.prefix = a |
|---|
| 758 |
elif o in ('-t', '--tail'): |
|---|
| 759 |
tail = int(a) |
|---|
| 760 |
elif o in ('--title'): |
|---|
| 761 |
self.title = a |
|---|
| 762 |
|
|---|
| 763 |
if self.config_file: |
|---|
| 764 |
execfile(self.config_file, self.__dict__) |
|---|
| 765 |
|
|---|
| 766 |
if self.chdir: |
|---|
| 767 |
os.chdir(self.chdir) |
|---|
| 768 |
|
|---|
| 769 |
if not args: |
|---|
| 770 |
|
|---|
| 771 |
pattern = '%s*.prof' % self.prefix |
|---|
| 772 |
args = self.args_to_files([pattern], tail) |
|---|
| 773 |
|
|---|
| 774 |
if not args: |
|---|
| 775 |
if self.chdir: |
|---|
| 776 |
directory = self.chdir |
|---|
| 777 |
else: |
|---|
| 778 |
directory = os.getcwd() |
|---|
| 779 |
|
|---|
| 780 |
sys.stderr.write('%s: func: No arguments specified.\n' % self.name) |
|---|
| 781 |
sys.stderr.write('%s No %s*.prof files found in "%s".\n' % (self.name_spaces, self.prefix, directory)) |
|---|
| 782 |
sys.stderr.write('%s Type "%s help func" for help.\n' % (self.name_spaces, self.name)) |
|---|
| 783 |
sys.exit(1) |
|---|
| 784 |
|
|---|
| 785 |
else: |
|---|
| 786 |
|
|---|
| 787 |
args = self.args_to_files(args, tail) |
|---|
| 788 |
|
|---|
| 789 |
cwd_ = os.getcwd() + os.sep |
|---|
| 790 |
|
|---|
| 791 |
if format == 'ascii': |
|---|
| 792 |
|
|---|
| 793 |
def print_function_timing(file, func): |
|---|
| 794 |
try: |
|---|
| 795 |
f, line, func, time = self.get_function_profile(file, func) |
|---|
| 796 |
except ValueError, e: |
|---|
| 797 |
sys.stderr.write("%s: func: %s: %s\n" % (self.name, file, e)) |
|---|
| 798 |
else: |
|---|
| 799 |
if f.startswith(cwd_): |
|---|
| 800 |
f = f[len(cwd_):] |
|---|
| 801 |
print "%.3f %s:%d(%s)" % (time, f, line, func) |
|---|
| 802 |
|
|---|
| 803 |
for file in args: |
|---|
| 804 |
print_function_timing(file, function_name) |
|---|
| 805 |
|
|---|
| 806 |
elif format == 'gnuplot': |
|---|
| 807 |
|
|---|
| 808 |
results = self.collect_results(args, self.get_function_time, |
|---|
| 809 |
function_name) |
|---|
| 810 |
|
|---|
| 811 |
self.gnuplot_results(results) |
|---|
| 812 |
|
|---|
| 813 |
else: |
|---|
| 814 |
|
|---|
| 815 |
sys.stderr.write('%s: func: Unknown format "%s".\n' % (self.name, format)) |
|---|
| 816 |
sys.exit(1) |
|---|
| 817 |
|
|---|
| 818 |
|
|---|
| 819 |
|
|---|
| 820 |
def help_mem(self): |
|---|
| 821 |
help = """\ |
|---|
| 822 |
Usage: scons-time mem [OPTIONS] FILE [...] |
|---|
| 823 |
|
|---|
| 824 |
-C DIR, --chdir=DIR Change to DIR before looking for files |
|---|
| 825 |
-f FILE, --file=FILE Read configuration from specified FILE |
|---|
| 826 |
--fmt=FORMAT, --format=FORMAT Print data in specified FORMAT |
|---|
| 827 |
-h, --help Print this help and exit |
|---|
| 828 |
-p STRING, --prefix=STRING Use STRING as log file/profile prefix |
|---|
| 829 |
--stage=STAGE Plot memory at the specified stage: |
|---|
| 830 |
&n |
|---|