blob: 65044ccb316a3b3ce22fa98ee466fab44bccc91e [file] [log] [blame]
#!/usr/bin/python
#
# Copyright (c) 2011 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.
"""mergeoldperf.py - Merge the current and original perf data into the current
data.
"""
import os
import sys
# Prepend the buildbot pylibs directory to our import path.
sys.path.insert(0, os.path.join(os.path.dirname(__file__),
'../../buildbot/build/third_party'))
import errno
import optparse
import re
import simplejson
import subprocess
__version__ = '1.0'
USAGE = r"""%prog [dir1] ... [dirN]"""
ORIGINAL_PATH = '/home/chrome-bot/saved-by-chase/original-perf'
CURRENT_PATH = '/home/chrome-bot/www/perf'
def Backquote(cmd, cwd=None):
"""Like running `cmd` in a shell script."""
return subprocess.Popen(cmd,
cwd=cwd,
stdout=subprocess.PIPE).communicate()[0].strip()
def ReadJson(filename, max_revision=None):
"""Read the JSON file into memory as a Python data structure.
Returns a list of hashes contained in the specified filename.
"""
try:
file = open(filename, 'r')
except IOError, e:
print >> sys.stderr, ("I/O Error reading file %s(%s): %s" %
(filename, e.errno, e.strerror))
raise e
if not file:
return None
data = []
contents = file.read()
file.close()
contentslist = contents.split("\n")
for jsontext in contentslist:
if jsontext is None or len(jsontext) == 0:
continue
try:
json = simplejson.loads(jsontext, parse_float=str,
object_pairs_hook=simplejson.OrderedDict)
except ValueError, e:
print >> sys.stderr, ("Error parsing file %s: '%s'" %
(filename, jsontext))
raise e
# If max_revision is set and revision >= max_revision, skip this entry.
if max_revision and int(json['rev']) >= int(max_revision):
continue
data.append(json)
return data
def WriteJson(filename, data):
"""Write a list of hashes in |data| to the file specified in |filename|."""
try:
file = open(filename, 'w')
except IOError, e:
print >> sys.stderr, ("I/O Error writing file %s(%s): %s" %
(filename, e.errno, e.strerror))
if file:
contentslist = []
for json in data:
contentslist.append(simplejson.dumps(json))
contents = "\n".join(contentslist)
file.write(contents + "\n")
file.close()
return True
def ProcessJson(filename):
"""Reads, sorts, and writes each JSON data file."""
original_filename = filename.replace(CURRENT_PATH, ORIGINAL_PATH)
if not os.path.exists(original_filename):
return
tempfilename = filename + ".tmp"
if os.path.exists(tempfilename):
raise Exception("%s already exists" % tempfilename)
rc = subprocess.Popen(['cp', filename, tempfilename]).wait()
print tempfilename
if rc != 0:
raise Exception("could not copy %s to %s" % (filename, tempfilename))
# Read and sort (descending order) current json data.
current_data = ReadJson(filename)
current_data.sort(lambda x, y: cmp(int(y['rev']), int(x['rev'])))
# Get the current data smallest revision.
smallest_current_revision = int(current_data[-1]['rev'])
print 'before: smallest rev = %s, highest rev = %s' % (
current_data[-1]['rev'], current_data[0]['rev'])
# Read and sort (descending order) original json file.
original_data = ReadJson(
original_filename, max_revision=smallest_current_revision)
original_data.sort(lambda x, y: cmp(int(y['rev']), int(x['rev'])))
# Extend the current data with the original data.
current_data.extend(original_data)
# Sort the current data one last time.
current_data.sort(lambda x, y: cmp(int(y['rev']), int(x['rev'])))
print 'after: smallest rev = %s, highest rev = %s' % (
current_data[-1]['rev'], current_data[0]['rev'])
# Check one last time that the current file matches the tmp current file.
rc = subprocess.Popen(['diff', filename, tempfilename]).wait()
if rc != 0:
subprocess.Popen(['rm', tempfilename]).wait()
print '%s changed before we could write back to it, skipping' % filename
return
WriteJson(filename, current_data)
subprocess.Popen(['rm', tempfilename]).wait()
subprocess.Popen(['rm', original_filename]).wait()
print filename
def GetFilelist(dir, match):
"""Finds all items in dir that match the given arg."""
if not os.path.exists(dir):
print "Directory %s does not exist." % dir
return []
find_cmd = ['find', dir, '-name', match]
find_output = Backquote(find_cmd).strip()
if len(find_output) == 0:
return []
return find_output.split("\n")
def Main(args):
parser = optparse.OptionParser(usage=USAGE, version=__version__)
options, args = parser.parse_args(args)
# Get the given directories the user wants to work in.
options.dir = []
if len(args) > 1:
options.dir.extend(args[1:len(args)])
# If no directories are given, assume the current working directory.
if len(options.dir) == 0:
options.dir.append('.')
if os.getcwd() != CURRENT_PATH:
raise Exception('only run this from %s' % CURRENT_PATH)
for dir in options.dir:
dir = os.path.abspath(dir)
original_dir = dir.replace(CURRENT_PATH, ORIGINAL_PATH)
# Merge old perf data into current perf data.
for filename in GetFilelist(dir=dir, match='*-summary.dat'):
filename = os.path.abspath(filename)
ProcessJson(filename)
# Move old perf summary data into current perf directories.
for original_filename in GetFilelist(dir=original_dir,
match='*-summary.dat'):
filename = original_filename.replace(ORIGINAL_PATH, CURRENT_PATH)
if not os.path.exists(filename):
print "%s -> %s" % (original_filename, filename)
subprocess.Popen(['mv', original_filename, filename]).wait()
# Move old perf revision data into current perf directories.
for original_filename in GetFilelist(dir=original_dir, match='*_t*.dat'):
filename = original_filename.replace(ORIGINAL_PATH, CURRENT_PATH)
if os.path.exists(filename):
print 'removing %s (exists)' % original_filename
subprocess.Popen(['rm', original_filename]).wait()
continue
if not os.path.exists(os.path.dirname(filename)):
print 'skipping %s (parent does not exist)' % filename
continue
print '%s -> %s' % (original_filename, filename)
subprocess.Popen(['mv', original_filename, filename]).wait()
return 0
if __name__ == '__main__':
sys.exit(Main(sys.argv))