blob: 3eb26e2538b64803095a2032a492bbff184a604d [file] [log] [blame]
# Copyright (c) 2015 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.
import datetime
import json
import os
import tempfile
import time
import urllib
import urllib2
import perf_insights_project
import webapp2
from webapp2 import Route
from perf_insights.mre import cloud_storage
from perf_insights.mre import local_directory_corpus_driver
from perf_insights.mre import corpus_query
from perf_insights.mre import map_runner
from perf_insights.mre import job as job_module
from perf_insights.mre import json_output_formatter
MAX_TRACES = 10000
def _RelPathToUnixPath(p):
return p.replace(os.sep, '/')
class TestListHandler(webapp2.RequestHandler):
def get(self, *args, **kwargs): # pylint: disable=unused-argument
project = perf_insights_project.PerfInsightsProject()
test_relpaths = ['/' + _RelPathToUnixPath(x)
for x in project.FindAllTestModuleRelPaths()]
tests = {'test_relpaths': test_relpaths}
tests_as_json = json.dumps(tests)
self.response.content_type = 'application/json'
return self.response.write(tests_as_json)
class RunMapFunctionHandler(webapp2.RequestHandler):
def post(self, *args, **kwargs): # pylint: disable=unused-argument
job_dict = json.loads(self.request.body)
job = job_module.Job.FromDict(job_dict)
job_with_filenames = job_module.Job(
job.map_function_handle.ConvertHrefsToAbsFilenames(self.app))
corpus_driver = local_directory_corpus_driver.LocalDirectoryCorpusDriver(
trace_directory=kwargs.pop('_pi_data_dir'),
url_resolver=self.app.GetURLForAbsFilename)
# TODO(nduca): pass self.request.params to the map function [maybe].
query_string = self.request.get('corpus_query', 'True')
query = corpus_query.CorpusQuery.FromString(query_string)
trace_handles = corpus_driver.GetTraceHandlesMatchingQuery(query)
self._RunMapper(trace_handles, job_with_filenames)
# TODO(eakuefner): Rename this and other things that assume we only have map
def _RunMapper(self, trace_handles, job):
self.response.content_type = 'application/json'
output_formatter = json_output_formatter.JSONOutputFormatter(
self.response.out)
runner = map_runner.MapRunner(trace_handles, job,
jobs=map_runner.AUTO_JOB_COUNT,
output_formatters=[output_formatter])
runner.Run()
class RunDownloadHandler(webapp2.RequestHandler):
def post(self, *args, **kwargs): # pylint: disable=unused-argument
self.response.content_type = 'application/json'
url = self.request.get('url', 'True')
# Doesn't need to be downloaded since it's not a cloud file, just return
# true and the path to the file.
if not 'gs://' in url:
self.response.write(json.dumps({'success': True, 'file': url}))
return
output_name = os.path.join(kwargs.pop('_pi_data_dir'), url.split('/')[-1])
try:
print 'Downloading: %s' % url
cloud_storage.Copy(url, output_name)
except cloud_storage.CloudStorageError:
print ' -> Failed to download: %s' % url
self.response.write(json.dumps({'success': False}))
return
output_name = os.path.join('/perf_insights/test_data', url.split('/')[-1])
self.response.write(json.dumps({'success': True, 'file': output_name}))
class RunCloudMapperHandler(webapp2.RequestHandler):
def post(self, *args, **kwargs): # pylint: disable=unused-argument
job_dict = json.loads(self.request.body)
job = job_module.Job.FromDict(job_dict)
job_with_filenames = job_module.Job(
job.map_function_handle.ConvertHrefsToAbsFilenames(self.app))
mapper_handle = job_with_filenames.map_function_handle
with open(mapper_handle.modules_to_load[0].filename, 'r') as f:
mapper = f.read()
mapper_name = job_with_filenames.map_function_handle.function_name
query_string = self.request.get('corpus_query', 'True')
query = corpus_query.CorpusQuery.FromString(query_string)
if query.max_trace_handles > MAX_TRACES:
print 'Capping query at %d' % MAX_TRACES
query.max_trace_handles = MAX_TRACES
query_string = query.AsQueryString()
params = urllib.urlencode({
'query': query_string,
'mapper': mapper,
'mapper_function': mapper_name,
'revision': 'HEAD',
'corpus': 'https://performance-insights.appspot.com',
'timeout': 240,
'function_timeout': 120
})
cloud_mapper_url = 'https://performance-insights.appspot.com'
if self.request.get('local') == 'true':
cloud_mapper_url = 'http://localhost:8080'
create_url = '%s/cloud_mapper/create' % cloud_mapper_url
response = urllib2.urlopen(create_url, data=params)
response_data = response.read()
print response_data
results = json.loads(response_data)
if results['status']:
jobid = results['jobid']
status_url = '%s/cloud_mapper/status?jobid=%s' % (cloud_mapper_url, jobid)
start_time = datetime.datetime.now()
while datetime.datetime.now() - start_time < datetime.timedelta(
seconds=300):
time.sleep(1)
print 'Waiting for results.'
response = urllib2.urlopen(status_url)
results = json.loads(response.read())
if results['status'] == 'COMPLETE':
print 'Mapping complete. Downloading results.'
output_handle, output_name = tempfile.mkstemp()
try:
print ' -> %s' % results['data']
cloud_storage.Copy(results['data'], output_name)
except cloud_storage.CloudStorageError as e:
print 'Cloud storage error: %s' % str(e)
return
map_results = ''
with open(output_name, 'r') as f:
map_results = f.read()
os.close(output_handle)
self.response.write(map_results)
total_time = datetime.datetime.now() - start_time
print 'Time taken: %ss' % total_time.total_seconds()
print map_results[:128]
return
class PerfInsightsDevServerConfig(object):
def __init__(self):
self.project = perf_insights_project.PerfInsightsProject()
def GetName(self):
return 'perf_insights'
def GetRunUnitTestsUrl(self):
return '/perf_insights/tests.html'
def AddOptionstToArgParseGroup(self, g):
g.add_argument('--pi-data-dir',
default=self.project.perf_insights_test_data_path)
def GetRoutes(self, args): # pylint: disable=unused-argument
return [
Route('/perf_insights/tests', TestListHandler),
Route('/perf_insights_examples/run_map_function',
RunMapFunctionHandler,
defaults={
'_pi_data_dir':
os.path.abspath(os.path.expanduser(args.pi_data_dir))
}),
Route('/perf_insights_examples/run_cloud_mapper',
RunCloudMapperHandler,
defaults={
'_pi_data_dir':
os.path.abspath(os.path.expanduser(args.pi_data_dir))
}),
Route('/perf_insights_examples/download',
RunDownloadHandler,
defaults={
'_pi_data_dir':
os.path.abspath(os.path.expanduser(args.pi_data_dir))
})
]
def GetSourcePaths(self, args): # pylint: disable=unused-argument
return list(self.project.source_paths)
def GetTestDataPaths(self, args): # pylint: disable=unused-argument
return [('/perf_insights/test_data/',
os.path.abspath(os.path.expanduser(args.pi_data_dir)))]