Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multifile support (rebase from https://github.com/jiffyclub/snakeviz/pull/99) #170

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 31 additions & 9 deletions snakeviz/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
from pstats import Stats

try:
from urllib.parse import quote
from urllib.parse import quote, quote_plus
except ImportError:
from urllib import quote
from urllib import quote, quote_plus

from . import version

Expand Down Expand Up @@ -45,9 +45,9 @@ def error(self, message):

def build_parser():
parser = SVArgumentParser(
description='Start SnakeViz to view a Python profile.')
description='Start SnakeViz to view a Python profile. If multiple profiles are provided, they are merged and loaded as one.')

parser.add_argument('filename', help='Python profile to view')
parser.add_argument('profile', nargs='+', help='Python profile(s) to view, or directory containing profiles')

parser.add_argument('-v', '--version', action='version',
version=('%(prog)s ' + version.version))
Expand Down Expand Up @@ -79,10 +79,7 @@ def main(argv=None):
if args.browser and args.server:
parser.error("options --browser and --server are mutually exclusive")

filename = os.path.abspath(args.filename)
if not os.path.exists(filename):
parser.error('the path %s does not exist' % filename)

filename = os.path.abspath(args.profile[0])
if not os.path.isdir(filename):
try:
open(filename)
Expand All @@ -101,6 +98,31 @@ def main(argv=None):

filename = quote(filename, safe='')

paths = [os.path.abspath(p) for p in args.profile]
for p in paths:
if not os.path.exists(p):
parser.error('path %s does not exist' % p)

# A single directory is allowed
if len(paths) == 1 and os.path.isdir(paths[0]):
pass # OK
else:
for p in paths:
if os.path.isdir(p):
parser.error('May only specify a single directory: "%s" is a directory' % p)
try:
Stats(p)
except IOError as e:
parser.error('the file %s could not be opened: %s'
% (filename, str(e)))
except:
parser.error(('the file %s is not a valid profile. ' % p) +
'Generate profiles using: \n\n'
'\tpython -m cProfile -o my_program.prof my_program.py\n')

paths='&'.join(paths)
paths = quote_plus(paths)

hostname = args.hostname
port = args.port

Expand Down Expand Up @@ -137,7 +159,7 @@ def main(argv=None):
print('No available port found.')
return 1

url = "http://{0}:{1}/snakeviz/{2}".format(hostname, port, filename)
url = "http://{0}:{1}/snakeviz/{2}".format(hostname, port, paths)
print(('snakeviz web server started on %s:%d; enter Ctrl-C to exit' %
(hostname, port)))
print(url)
Expand Down
24 changes: 14 additions & 10 deletions snakeviz/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
import json

try:
from urllib.parse import quote
from urllib.parse import quote, unquote_plus
except ImportError:
from urllib import quote
from urllib import quote, unquote_plus

import tornado.ioloop
import tornado.web
Expand All @@ -23,17 +23,21 @@


class VizHandler(tornado.web.RequestHandler):
def get(self, profile_name):
abspath = os.path.abspath(profile_name)
if os.path.isdir(abspath):
self._list_dir(abspath)
def get(self, profiles):
profiles = unquote_plus(profiles)
individual_profiles = profiles.split('&')

# abspath = os.path.abspath(profile_name) Already absolute from client
if len(individual_profiles) == 1 and os.path.isdir(individual_profiles[0]):
self._list_dir(individual_profiles[0])
else:
display_name = 'Multiple profiles' if len(individual_profiles)>1 else individual_profiles[0]
try:
s = Stats(profile_name)
except:
raise RuntimeError('Could not read %s.' % profile_name)
s = Stats(*individual_profiles) # Merge one or more profiles
except Exception as e:
raise RuntimeError('Error getting stats for %s: %s' % (individual_profiles, str(e)))
self.render(
'viz.html', profile_name=profile_name,
'viz.html', display_name=display_name,
table_rows=table_rows(s), callees=json_stats(s))

def _list_dir(self, path):
Expand Down
2 changes: 1 addition & 1 deletion snakeviz/templates/viz.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<html>
<head>
<meta charset="utf-8" .>
<title>{{profile_name.split('/')[-1].split('.')[0]}}</title>
<title>{{display_name.split('/')[-1].split('.')[0]}}</title>

<meta name="viewport" content="width=device-width, initial-scale=1.0">

Expand Down