Skip to content
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
23 changes: 0 additions & 23 deletions main.css

This file was deleted.

15 changes: 0 additions & 15 deletions main.html

This file was deleted.

65 changes: 0 additions & 65 deletions main.js

This file was deleted.

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
9 changes: 9 additions & 0 deletions static/jquery.ba-throttle-debounce.min.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/*
* jQuery throttle / debounce - v1.1 - 3/7/2010
* http://benalman.com/projects/jquery-throttle-debounce-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function(b,c){var $=b.jQuery||b.Cowboy||(b.Cowboy={}),a;$.throttle=a=function(e,f,j,i){var h,d=0;if(typeof f!=="boolean"){i=j;j=f;f=c}function g(){var o=this,m=+new Date()-d,n=arguments;function l(){d=+new Date();j.apply(o,n)}function k(){h=c}if(i&&!h){l()}h&&clearTimeout(h);if(i===c&&m>e){l()}else{if(f!==true){h=setTimeout(i?k:l,i===c?e-m:e)}}}if($.guid){g.guid=j.guid=j.guid||$.guid++}return g};$.debounce=function(d,e,f){return f===c?a(d,e,false):a(d,f,e!==false)}})(this);
16 changes: 16 additions & 0 deletions static/main.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
html { width: 100%; height: 100%; }
body { font-family: sans-serif; width: 100%; height: 100%; }
#chart {
width: 70%;
height: 95%;
float: left;
}
#stacktrace {
float: left;
margin-left: 1%;
width: 29%;
font-size: 80%;
}
.stacktrace-line span:nth-child(1) { font-weight: bold; }
.stacktrace-line span:nth-child(2) { margin-left: 1em; color: grey; }
.stacktrace-line { display: block; }
15 changes: 15 additions & 0 deletions static/main.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<!doctype html>
<html>
<head>
<script src="/static/flot/jquery.min.js"></script>
<script src="/static/flot/jquery.flot.min.js"></script>
<script src="/static/flot/jquery.flot.navigate.min.js"></script>
<script src="/static/jquery.ba-throttle-debounce.min.js"></script>
<script src="/static/main.js"></script>
<link href="/static/main.css" rel="stylesheet">
</head>
<body>
<div id="chart"></div>
<div id="stacktrace"></div>
</body>
</html>
75 changes: 75 additions & 0 deletions static/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
function zoom_out()
{
$.getJSON("/get_json", plot_data);
clicks = [];
}

function plothover(event, pos, item)
{
var x = Math.ceil(pos);
if (item) {
$("#stacktrace").html(glob_data.mem[item.dataIndex][2]);
}
}

function format_y_axis(val, axis)
{
if (val > 1024) {
return (val / 1024).toFixed() + " MiB";
}
return val + " KiB";
}

var lastWindow = {};
function resample(render, plot)
{
var xAxis = plot.getXAxes()[0];
var newWindow = {
x0: Math.floor(xAxis.min),
x1: Math.ceil(xAxis.max)
};
if (newWindow.x0 != lastWindow.x0 || newWindow.x1 != lastWindow.x1) {
$.getJSON("/get_json?x0=" + newWindow.x0 + "&x1=" + newWindow.x1, render);
lastWindow = newWindow;
}
}

function plot_data(data)
{
glob_data = data;
var axesMax = {
x: Math.ceil(data.mem[data.mem.length-1][0]),
y: Math.max.apply(null, data.mem.map(function (x) { return x[1]; }))
};
var axesMargin = {
x: 0.05 * axesMax.x,
y: 0.05 * axesMax.y
}
var maxAxesWindows = {
x: [-axesMargin.x, axesMax.x + axesMargin.x],
y: [-axesMargin.y, axesMax.y + axesMargin.y],
};

var plotOpts = {
grid: {hoverable: true, clickable: true},
xaxis: {panRange: maxAxesWindows.x},
yaxis: {panRange: maxAxesWindows.y, tickFormatter: format_y_axis},
zoom: {interactive: true},
pan: {interactive: true}
};
function render(data) {
$.plot("#chart", [data.mem], plotOpts);
}

render(data);

$("#chart").bind("plothover", plothover);
var resampleZoom = $.debounce(10, resample);
var resamplePan = $.debounce(100, resample);
$("#chart").on("plotzoom", function (event, plot) { resampleZoom(render, plot); });
$("#chart").on("plotpan", function (event, plot) { resamplePan(render, plot); });
}

$(function() {
$.getJSON("/get_json", plot_data);
})
66 changes: 19 additions & 47 deletions vmemprof.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
Run with output of vmprof run with memory profiling (like vmprof.enable(fileno, 0.01, True))
"""

import re, os, json, sys
import vmprof
import os, json, sys
from flask import Flask, Response, request
from flask import Flask, Response, request, escape
app = Flask(__name__)

if len(sys.argv) != 2:
Expand All @@ -15,15 +15,17 @@

stats = vmprof.read_profile(sys.argv[1])

MAX = 60
MAX_SOURCEFILE_LEN = 60

def strip(s):
s = s.replace("<", "&lt;").replace(">", "&gt;")
l = s.split(":")
if len(l[3]) > MAX:
l[3] = "..." + l[3][-(MAX - 3):]
l[1] = "<b>" + l[1] + "</b>"
return "%s %s:%s" % (l[1], l[3], l[2])
def format_stracktrace_line(line):
if line is None:
return "<unknown>"
_, funcname, lineno, sourcefile = line.split(':')
sourcefile = re.sub('.*/lib/python[\d\.]+/(site-packages/)?', '', sourcefile)
if len(sourcefile) > MAX_SOURCEFILE_LEN:
sourcefile = "..." + sourcefile[-(MAX_SOURCEFILE_LEN - 3):]
return "<span class=stacktrace-line><span>%s</span> <span>%s:%s</span></span>" \
% (escape(funcname), escape(sourcefile), escape(lineno))

def resample_and_pack(profiles, start, end, window_size):
next = []
Expand All @@ -32,52 +34,22 @@ def resample_and_pack(profiles, start, end, window_size):
skip = (end - start) / window_size
while i < end:
prof = profiles[int(i)]
stack_trace = "<br/>".join([strip(stats.adr_dict.get(x, '<unknown>')) for x in prof[0]])
stack_trace = "".join([format_stracktrace_line(stats.adr_dict.get(x)) for x in prof[0]])
mem.append((i, prof[3], stack_trace))
i += skip
return {'mem': mem}

def root_dir():
return os.path.abspath(os.path.dirname(__file__))

def get_file(filename): # pragma: no cover
try:
src = os.path.join(root_dir(), filename)
# Figure out how flask returns static files
# Tried:
# - render_template
# - send_file
# This should not be so non-obvious
return open(src).read()
except IOError as exc:
return str(exc)

@app.route('/', methods=['GET'])
def index(): # pragma: no cover
content = get_file('main.html')
return Response(content, mimetype="text/html")

@app.route('/get_json', methods=['GET'])
def get_json():
default_size = 800
x0 = float(request.args.get("x0", "0"))
x1 = float(request.args.get("x1", len(stats.profiles)))
content = resample_and_pack(stats.profiles, x0, x1, default_size)
x0 = max(0, float(request.args.get("x0", 0)))
x1 = min(len(stats.profiles), float(request.args.get("x1", 'inf')))
content = resample_and_pack(stats.profiles, x0, x1, default_size)
return Response(json.dumps(content), mimetype="text/json")

@app.route('/', defaults={'path': ''})
@app.route('/<path:path>')
def get_resource(path): # pragma: no cover
mimetypes = {
".css": "text/css",
".html": "text/html",
".js": "application/javascript",
}
complete_path = os.path.join(root_dir(), path)
ext = os.path.splitext(path)[1]
mimetype = mimetypes.get(ext, "text/html")
content = get_file(complete_path)
return Response(content, mimetype=mimetype)
@app.route('/')
def get_resource(): # pragma: no cover
return app.send_static_file('main.html')

if __name__ == '__main__':
app.run(debug=True)