diff options
author | Josh Blum | 2013-05-11 19:56:48 -0700 |
---|---|---|
committer | Josh Blum | 2013-05-11 19:56:48 -0700 |
commit | 9e9308df1599bd13e808cacc6b3cea5a5c697df3 (patch) | |
tree | f01f195141a26a27b1c34f9cd2e03b92041a2a37 /query | |
parent | a847dd8414816158d11e142a002b67596719813c (diff) | |
download | sandhi-9e9308df1599bd13e808cacc6b3cea5a5c697df3.tar.gz sandhi-9e9308df1599bd13e808cacc6b3cea5a5c697df3.tar.bz2 sandhi-9e9308df1599bd13e808cacc6b3cea5a5c697df3.zip |
gras: moved query app to top level
Diffstat (limited to 'query')
-rw-r--r-- | query/CMakeLists.txt | 28 | ||||
-rw-r--r-- | query/__init__.py | 81 | ||||
-rw-r--r-- | query/chart_factory.js | 281 | ||||
-rw-r--r-- | query/chart_global_counters.js | 58 | ||||
-rw-r--r-- | query/chart_handler_breakdown.js | 39 | ||||
-rw-r--r-- | query/chart_overall_throughput.js | 47 | ||||
-rw-r--r-- | query/chart_overhead_compare.js | 39 | ||||
-rw-r--r-- | query/chart_port_counters.js | 76 | ||||
-rw-r--r-- | query/chart_port_downtime.js | 47 | ||||
-rw-r--r-- | query/main.css | 167 | ||||
-rw-r--r-- | query/main.html | 54 | ||||
-rw-r--r-- | query/main.js | 79 | ||||
-rw-r--r-- | query/utils.js | 88 |
13 files changed, 1084 insertions, 0 deletions
diff --git a/query/CMakeLists.txt b/query/CMakeLists.txt new file mode 100644 index 0000000..f9a855e --- /dev/null +++ b/query/CMakeLists.txt @@ -0,0 +1,28 @@ +######################################################################## +# Install rules +######################################################################## +include(GrPython) + +GR_PYTHON_INSTALL( + FILES + __init__.py + DESTINATION ${GR_PYTHON_DIR}/gras/query + COMPONENT ${GRAS_COMP_PYTHON} +) + +INSTALL( + FILES + main.html + main.js + utils.js + chart_factory.js + chart_overhead_compare.js + chart_overall_throughput.js + chart_handler_breakdown.js + chart_port_counters.js + chart_global_counters.js + chart_port_downtime.js + main.css + DESTINATION ${GR_PYTHON_DIR}/gras/query + COMPONENT ${GRAS_COMP_PYTHON} +) diff --git a/query/__init__.py b/query/__init__.py new file mode 100644 index 0000000..c72222b --- /dev/null +++ b/query/__init__.py @@ -0,0 +1,81 @@ +import time +import BaseHTTPServer +import urlparse +import json +import os + +__path__ = os.path.abspath(os.path.dirname(__file__)) + +server_registry = dict() + +class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler): + + #hide log messages to stdout by default + def log_message(self, format, *args): pass + + def do_HEAD(s): + s.send_response(200) + s.send_header("Content-type", "text/html") + s.end_headers() + + def do_GET(s): + """Respond to a GET request.""" + + #extract the path and set default + o = urlparse.urlparse(s.path) + args = server_registry[s.server] + path = o.path + + #handle json requests + if path.endswith('.json'): + s.send_response(200) + s.send_header("Content-type", "application/json") + s.end_headers() + if path == '/args.json': + arg_strs = dict((str(k), str(v)) for k, v in args.iteritems()) + s.wfile.write(json.dumps(arg_strs)) + else: + #why the fuck does no OS ever patch boost when there is a bug + #https://svn.boost.org/trac/boost/ticket/6785 + #serialize the path args into xml -- but I just wanted json + def xml_from_qs(k, v): + if not isinstance(v, list): v = [v] + return ''.join(['<%s>%s</%s>'%(k, v_i, k) for v_i in v]) + query_args = [xml_from_qs(k,v) for k,v in urlparse.parse_qs(o.query).iteritems()] + query_args.append(xml_from_qs('path', path)) + xml_args = xml_from_qs('args', ''.join(query_args)) + s.wfile.write(args['top_block'].query(xml_args)) + return + + #clean up path for filesystem + if path.startswith('/'): path = path[1:] + if not path: path = 'main.html' + target = os.path.join(__path__, path) + + #get files from the local file system + if os.path.exists(target): + s.send_response(200) + if target.endswith('.js'): s.send_header("Content-type", "text/javascript") + elif target.endswith('.css'): s.send_header("Content-type", "text/css") + else: s.send_header("Content-type", "text") + s.end_headers() + s.wfile.write(open(target).read()) + #otherwise not found do 404 + else: + s.send_response(404) + s.send_header("Content-type", "text/html") + s.end_headers() + s.wfile.write("<p>not found</p>") + +import select + +class http_server(object): + def __init__(self, args, **kwargs): + server_class = BaseHTTPServer.HTTPServer + self._httpd = server_class(args, MyHandler) + server_registry[self._httpd] = kwargs + + def serve_forever(self): + while True: + try: self._httpd.serve_forever() + except select.error: pass diff --git a/query/chart_factory.js b/query/chart_factory.js new file mode 100644 index 0000000..8b9656a --- /dev/null +++ b/query/chart_factory.js @@ -0,0 +1,281 @@ +/*********************************************************************** + * Some constants + **********************************************************************/ +var GRAS_CHARTS_STD_WIDTH = 250; + +/*********************************************************************** + * Chart registry for now chart types + **********************************************************************/ +var gras_chart_get_registry = function() +{ + return [ + {key:'overhead_compare', name:'Overhead Compare', factory:GrasChartOverheadCompare}, + {key:'overall_throughput', name:'Overall Throughput', factory:GrasChartOverallThroughput}, + {key:'handler_breakdown', name:'Handler Breakdown', factory:GrasChartHandlerBreakdown}, + {key:'port_counters', name:'Port Counters', factory:GrasChartPortCounts}, + {key:'global_counters', name:'Global Counters', factory:GrasChartGlobalCounts}, + {key:'port_downtime', name:'Port downtime', factory:GrasChartPortDowntime}, + ]; +} + +/*********************************************************************** + * get blocks that need active querying + **********************************************************************/ +function gras_chart_factory_active_blocks(registry) +{ + var block_ids = new Array(); + $.each(registry.active_charts, function(index, chart_info) + { + $.merge(block_ids, chart_info.args.block_ids); + }); + return $.unique(block_ids); +} + +/*********************************************************************** + * update after new query event + **********************************************************************/ +function gras_chart_factory_update(registry, point) +{ + registry.point = point; //store last data point + $.each(registry.active_charts, function(index, chart_info) + { + chart_info.chart.update(point); + }); +} + +/*********************************************************************** + * chart factory input handler + **********************************************************************/ +function gras_chart_factory_handle_input(registry) +{ + //get a list of the selected blocks + var selected_blocks = new Array(); + $.each($('#chart_designer_blocks input'), function(index, input) + { + var input = $(input); + if (input.is(':checked')) + { + selected_blocks.push(input.attr('name')); + } + }); + + //get the type of chart to create + var chart_type = $('#chart_type_selector').val(); + + //create args for the factory make + var args = { + block_ids:selected_blocks, + chart_type:chart_type, + }; + + //call into the factory with args + gras_chart_factory_make(registry, args); +} + +/*********************************************************************** + * save/load to/from local storage + **********************************************************************/ +function gras_chart_save(registry) +{ + if (typeof(Storage) === "undefined") return; + var all_args = new Array(); + $.each(registry.active_charts, function(index, info) + { + all_args.push(info.args); + }); + localStorage.setItem(registry.top_id, JSON.stringify({ + chart_args: all_args, + overall_rate: registry.overall_rate, + overall_active: registry.overall_active, + })); +} + +function gras_chart_load(registry) +{ + if (typeof(Storage) === "undefined") return; + var storage = JSON.parse(localStorage.getItem(registry.top_id)); + if (!storage) return; + + //restore misc settings in storage + registry.overall_rate = storage.overall_rate; + registry.overall_active = storage.overall_active; + + //rebuild all charts from args + $.each(storage.chart_args, function(args_i, args) + { + //check that the blocks saved in the args actually exist + var do_make = true; + $.each(args.block_ids, function(block_id_i, block_id) + { + if ($.inArray(block_id, registry.block_ids) < 0) + { + do_make = false; + } + }); + if (do_make) gras_chart_factory_make(registry, args); + }); +} + +/*********************************************************************** + * chart factory make routine + **********************************************************************/ +function gras_chart_factory_make(registry, args) +{ + //create containers + var chart_box = $('<table />').attr({class:'chart_container'}); + var tr = $('<tr />'); + var td = $('<td />'); + tr.append(td); + + //call into the factory + try + { + var chart = new registry.chart_factories[args.chart_type](args, td.get(0)); + } + catch(err) + { + return; + } + + //setup the title + var tr_title = $('<tr />'); + var th_title = $('<th />'); + tr_title.append(th_title); + th_title.text(chart.title); + + //register the chart + var chart_info = {chart:chart,args:args,panel:chart_box}; + registry.active_charts.push(chart_info); + $('#charts_panel').append(chart_box); + + //close button + var close_div = $('<div/>').attr({class:'chart_designer_block_close'}); + var close_href = $('<a />').attr({href:'#', class:"ui-dialog-titlebar-close ui-corner-all", role:"button"}); + var close_span = $('<span />').attr({class:"ui-icon ui-icon-closethick"}).text('close'); + close_div.append(close_href); + close_href.append(close_span); + th_title.append(close_div); + $(close_href).click(function() + { + var index = $.inArray(chart_info, registry.active_charts); + registry.active_charts.splice(index, 1); + chart_box.remove(); + gras_chart_save(registry); + }); + gras_chart_save(registry); + + //finish gui building + chart_box.append(tr_title); + chart_box.append(tr); + + //implement draggable and resizable from jquery ui + var handle_stop = function(event, ui) + { + args['width'] = chart_box.width(); + args['height'] = chart_box.height(); + args['position'] = chart_box.offset(); + chart.gc_resize = false; + chart.update(registry.point); + gras_chart_save(registry); + }; + + if ('default_width' in chart) chart_box.width(chart.default_width); + chart_box.resizable({stop: handle_stop, create: function(event, ui) + { + if ('width' in args) chart_box.width(args.width); + if ('height' in args) chart_box.height(args.height); + }, + start: function(event, ui) + { + chart.gc_resize = true; + chart.update(registry.point); + }}); + + chart_box.css('position', 'absolute'); + chart_box.draggable({stop: handle_stop, create: function(event, ui) + { + if ('position' in args) chart_box.offset(args.position); + }, cursor: "move"}); + + //set the cursor on the title bar so its obvious + tr_title.hover( + function(){$(this).css('cursor','move'); close_div.show();}, + function(){$(this).css('cursor','auto'); close_div.hide();} + ); + close_div.hide(); +} + +/*********************************************************************** + * chart factory init + **********************************************************************/ +function gras_chart_factory_init(registry) +{ + //init registry containers + registry.active_charts = new Array(); + registry.chart_factories = new Array(); + + //install callback for chart factory + $('#chart_factory_button').click(function() + { + gras_chart_factory_handle_input(registry); + }); + + //init the chart selection input + $.each(gras_chart_get_registry(), function(index, options) + { + registry.chart_factories[options.key] = options.factory; + var option = $('<option />').attr({value: options.key}); + option.text(options.name); + $('#chart_type_selector').append(option); + }); + + //init chart overall gui controls + var overall_rate = $('#chart_update_rate').attr({size:3}); + overall_rate.spinner({ + min: 1, max: 10, step: 0.5, stop: function(event, ui){$(this).change();} + }); + var overall_active = $('#chart_active_state'); + overall_active.button(); + + //callback for overall gui events + function handle_gui_event() + { + registry.overall_active = overall_active.is(':checked'); + registry.overall_rate = overall_rate.val(); + gras_chart_save(registry); + } + overall_rate.change(handle_gui_event); + overall_active.change(handle_gui_event); + + //block registry and checkboxes init + $.getJSON('/blocks.json', function(data) + { + var container = $('#chart_designer_blocks'); + $.each(data.blocks, function(index, id) + { + registry.block_ids.push(id); + var cb_id = "chart_designer_blocks " + id; + var div = $('<div />'); + var label = $('<label />').text(id).attr({'for':cb_id}); + var input = $('<input />').attr({ + type: 'checkbox', + name: id, + id: cb_id, + }); + input.attr('checked', false); + div.append(input); + div.append(label); + container.append(div); + }); + //container.buttonset(); + + //try to load last settings + try{gras_chart_load(registry);}catch(e){} + + //init gui elements after settings restore + overall_rate.val(registry.overall_rate); + overall_active.attr('checked', registry.overall_active); + handle_gui_event(); + gras_query_stats(registry); + }); +} diff --git a/query/chart_global_counters.js b/query/chart_global_counters.js new file mode 100644 index 0000000..1708c01 --- /dev/null +++ b/query/chart_global_counters.js @@ -0,0 +1,58 @@ +function GrasChartGlobalCounts(args, panel) +{ + //input checking + if (args.block_ids.length != 0) throw gras_error_dialog( + "GrasChartGlobalCounts", + "Error making global counts chart.\n"+ + "Do not specify any blocks for this chart." + ); + + //settings + this.div = $('<div />').attr({class:'chart_total_counts'}); + $(panel).append(this.div); + this.title = "Global Counters" +} + +GrasChartGlobalCounts.prototype.update = function(point) +{ + var ul = $('<ul />'); + $('ul', this.div).remove(); //clear old lists + this.div.append(ul); + + function make_entry(strong, span) + { + var li = $('<li />'); + var strong = $('<strong />').text(strong + ": "); + var span = $('<span />').text(span); + li.append(strong); + li.append(span); + ul.append(li); + } + + var stuff = [ + ['Allocated', 'bytes', 'default_allocator_bytes_allocated'], + ['Peak size', 'bytes', 'default_allocator_peak_bytes_allocated'], + ['Num mallocs', '', 'default_allocator_allocation_count'], + + ['Total msgs', '', 'framework_counter_messages_processed'], + ['Thread yields', '', 'framework_counter_yields'], + ['Local pushes', '', 'framework_counter_local_pushes'], + ['Shared pushes', '', 'framework_counter_shared_pushes'], + ['Msg queue max', '', 'framework_counter_mailbox_queue_max'], + ]; + + var entries = 0; + $.each(stuff, function(contents_i, contents) + { + var dir = contents[0]; + var units = contents[1]; + var key = contents[2]; + var count = (key in point)? point[key] : 0; + if (count > 0) + { + make_entry(dir, count.toString() + ' ' + units); + entries++; + } + }); + if (entries == 0) make_entry("Counts", "none"); +} diff --git a/query/chart_handler_breakdown.js b/query/chart_handler_breakdown.js new file mode 100644 index 0000000..5f76d1d --- /dev/null +++ b/query/chart_handler_breakdown.js @@ -0,0 +1,39 @@ +function GrasChartHandlerBreakdown(args, panel) +{ + //input checking + if (args.block_ids.length != 1) throw gras_error_dialog( + "GrasChartHandlerBreakdown", + "Error making handler breakdown chart.\n"+ + "Specify only one block for this chart." + ); + + //save enable + this.block_id = args.block_ids[0]; + + //make new chart + this.chart = new google.visualization.PieChart(panel); + + this.title = "Handler Breakdown - " + this.block_id; + this.default_width = GRAS_CHARTS_STD_WIDTH; +} + +GrasChartHandlerBreakdown.prototype.update = function(point) +{ + var percents = gras_extract_percent_times(point, this.block_id); + var data = google.visualization.arrayToDataTable([ + ['Task', 'Percent'], + ['Work prep', percents['prep']], + ['Work task', percents['work']], + ['Work post', percents['post']], + ['Input tasks', percents['input']], + ['Output tasks', percents['output']], + ]); + + var options = { + chartArea:{left:5,top:0,right:5,bottom:0,width:"100%",height:"100%"}, + }; + if (this.gc_resize) options.width = 50; + if (this.gc_resize) options.height = 50; + + this.chart.draw(data, options); +}; diff --git a/query/chart_overall_throughput.js b/query/chart_overall_throughput.js new file mode 100644 index 0000000..6d66e40 --- /dev/null +++ b/query/chart_overall_throughput.js @@ -0,0 +1,47 @@ +function GrasChartOverallThroughput(args, panel) +{ + //save enables + this.ids = args.block_ids; + + //input checking + if (this.ids.length == 0) throw gras_error_dialog( + "GrasChartOverallThroughput", + "Error making overall thoughput chart.\n"+ + "Specify at least 1 block for this chart." + ); + + //make new chart + this.chart = new google.visualization.LineChart(panel); + + this.title = "Overall Throughput vs Time in MIps"; + this.history = new Array(); + this.default_width = 2*GRAS_CHARTS_STD_WIDTH; +} + +GrasChartOverallThroughput.prototype.update = function(point) +{ + this.history.push(point); + if (this.history.length == 1) this.p0 = point; + if (this.history.length < 2) return; + if (this.history.length > 10) this.history.splice(0, 1); + + var data_set = [['Throughput'].concat(this.ids)]; + for (var i = 1; i < this.history.length; i++) + { + var row = new Array(); + row.push(gras_extract_stat_time_delta(this.p0, this.history[i]).toFixed(2).toString()); + for (var j = 0; j < this.ids.length; j++) + { + row.push(gras_extract_throughput_delta(this.history[i-1], this.history[i], this.ids[j])/1e6); + } + data_set.push(row); + } + + var chart_data = google.visualization.arrayToDataTable(data_set); + var options = { + legend: {'position': 'bottom'}, + }; + if (this.gc_resize) options.width = 50; + if (this.gc_resize) options.height = 50; + this.chart.draw(chart_data, options); +}; diff --git a/query/chart_overhead_compare.js b/query/chart_overhead_compare.js new file mode 100644 index 0000000..0ec9070 --- /dev/null +++ b/query/chart_overhead_compare.js @@ -0,0 +1,39 @@ +function GrasChartOverheadCompare(args, panel) +{ + //save enables + this.ids = args.block_ids; + + //input checking + if (this.ids.length <= 1) throw gras_error_dialog( + "GrasChartOverheadCompare", + "Error making overhead compare chart.\n"+ + "Specify at least 2 blocks for this chart." + ); + + //make new chart + this.chart = new google.visualization.PieChart(panel); + + this.title = "Overhead Comparison"; + this.default_width = GRAS_CHARTS_STD_WIDTH; +} + +GrasChartOverheadCompare.prototype.update = function(point) +{ + var data_set = new Array(); + data_set.push(['Task', 'Percent']); + $.each(this.ids, function(index, id) + { + var percents = gras_extract_percent_times(point, id); + data_set.push([id, percents['total']]); + }); + + var data = google.visualization.arrayToDataTable(data_set) + + var options = { + chartArea:{left:5,top:0,right:5,bottom:0,width:"100%",height:"100%"}, + }; + if (this.gc_resize) options.width = 50; + if (this.gc_resize) options.height = 50; + + this.chart.draw(data, options); +}; diff --git a/query/chart_port_counters.js b/query/chart_port_counters.js new file mode 100644 index 0000000..af74c33 --- /dev/null +++ b/query/chart_port_counters.js @@ -0,0 +1,76 @@ +function GrasChartPortCounts(args, panel) +{ + //input checking + if (args.block_ids.length != 1) throw gras_error_dialog( + "GrasChartPortCounts", + "Error making total port counts chart.\n"+ + "Specify only one block for this chart." + ); + + //settings + this.block_id = args.block_ids[0]; + this.div = $('<div />').attr({class:'chart_total_counts'}); + $(panel).append(this.div); + this.title = "Port Counters - " + this.block_id; +} + +GrasChartPortCounts.prototype.update = function(point) +{ + var block_data = point.blocks[this.block_id]; + if (!block_data) return; + var ul = $('<ul />'); + $('ul', this.div).remove(); //clear old lists + this.div.append(ul); + + function make_entry(strong, span) + { + var li = $('<li />'); + var strong = $('<strong />').text(strong + ": "); + var span = $('<span />').text(span); + li.append(strong); + li.append(span); + ul.append(li); + } + + //create total time elapsed entry + { + var init_time = block_data.init_time; + var stats_time = block_data.stats_time; + var tps = block_data.tps; + var duration = (stats_time - init_time)/tps; + make_entry('Elapsed', duration.toFixed(2).toString() + ' secs'); + } + + var stuff = [ + ['Enque', 'items', 'items_enqueued'], + ['Enque', 'tags', 'tags_enqueued'], + ['Enque', 'msgs', 'msgs_enqueued'], + ['Input', 'items', 'items_consumed'], + ['Input', 'tags', 'tags_consumed'], + ['Input', 'msgs', 'msgs_consumed'], + ['Output', 'items', 'items_produced'], + ['Output', 'tags', 'tags_produced'], + ['Output', 'msgs', 'msgs_produced'], + ['Copied', 'bytes', 'bytes_copied'], + ]; + + $.each(stuff, function(contents_i, contents) + { + var dir = contents[0]; + var units = contents[1]; + var key = contents[2]; + $.each(block_data[key], function(index, count) + { + if (count > 0) + { + make_entry(dir + index.toString(), count.toString() + ' ' + units); + } + }); + }); + + var actor_depth = block_data.actor_queue_depth; + if (actor_depth > 10) //only show if its large + { + make_entry('Actor depth', actor_depth.toString() + ' msgs'); + } +} diff --git a/query/chart_port_downtime.js b/query/chart_port_downtime.js new file mode 100644 index 0000000..2de421d --- /dev/null +++ b/query/chart_port_downtime.js @@ -0,0 +1,47 @@ +function GrasChartPortDowntime(args, panel) +{ + //input checking + if (args.block_ids.length != 1) throw gras_error_dialog( + "GrasChartPortDowntime", + "Error making port downtime chart.\n"+ + "Specify only one block for this chart." + ); + + //save enable + this.block_id = args.block_ids[0]; + + //make new chart + this.chart = new google.visualization.PieChart(panel); + + this.title = "Port Downtime - " + this.block_id; + this.default_width = GRAS_CHARTS_STD_WIDTH; +} + +GrasChartPortDowntime.prototype.update = function(point) +{ + var block_data = point.blocks[this.block_id]; + if (!block_data) return; + + var raw_data = new Array(); + raw_data.push(['Port', 'Percent']); //key + + //now add input and output port data + $.each(block_data.inputs_idle, function(index, downtime) + { + raw_data.push(['Input'+index.toString(), downtime/block_data.tps]); + }); + $.each(block_data.outputs_idle, function(index, downtime) + { + raw_data.push(['Output'+index.toString(), downtime/block_data.tps]); + }); + + //update the chart from raw data + var data = google.visualization.arrayToDataTable(raw_data); + var options = { + chartArea:{left:5,top:0,right:5,bottom:0,width:"100%",height:"100%"}, + }; + if (this.gc_resize) options.width = 50; + if (this.gc_resize) options.height = 50; + + this.chart.draw(data, options); +}; diff --git a/query/main.css b/query/main.css new file mode 100644 index 0000000..4d933fa --- /dev/null +++ b/query/main.css @@ -0,0 +1,167 @@ +*{ +margin:0px; +padding:0px; +} + +body{ +font-family:Arial, Helvetica, sans-serif; +font-size:9pt; +color:black; +background-color:white; +} + +.chart_designer_block_close +{ +float:right; +} + +#chart_designer_blocks div +{ +float:left; +} + +#chart_designer_blocks label +{ +text-decoration:underline; +} + +#chart_designer_blocks input +{ +margin-right:2px; +margin-left:10px; +} + +#charts_panel +{ +width:100%; +height:100%; +} + +.chart_total_counts li +{ +list-style-type:none; +text-align: left; +padding: 0px; +margin-left: -30px; +font-size:90%; +} + +.chart_container +{ +float:none; +} + +#page{ +padding:10px; +color:inherit; +background-color:inherit; +} + +#page h1{ +font-size:130%; +border-left:1px solid #333333; +border-bottom:1px solid #333333; +text-align:left; +padding:10px 0px 10px 10px; +margin:10px 5px 20px 5px; +color:#333333; +background-color:inherit; +} + +#page h2{ +font-size:130%; +text-align:center; +padding:20px 0px 10px 0px; +color:#333333; +background-color:inherit; +} + +#page h3{ +font-size:110%; +text-align:left; +padding:15px 0px 5px 10px; +text-decoration:underline; +color:#333333; +background-color:inherit; +} + +#page h4{ +font-size:105%; +text-align:center; +padding:5px 0px 3px 0px; +text-decoration:underline; +} + +#page p{ +text-indent:20px; +padding:5px 0px 5px 10px; +} + +#page strong{ +} + +#page em{ +} + +#page li{ +padding:5px 5px 0px 3px; +} + +#page ul, #page ol{ +padding:5px 0px 5px 40px; +} + +#page img{ +margin:10px auto 10px auto; +display:block; +border-style:none; +} + +#page a:link, #page a:visited{ +color:#236B8E; +background-color:inherit; +text-decoration:none; +} + +#page a:hover{ +color:#4985D6; +background-color:inherit; +text-decoration:none; +} + +#page pre{ +border:1px inset #333333; +padding:5px; +margin:10px 5px 10px 5px; +color:inherit; +background-color:#FCFCFC; +font-size:90%; +} + +#page hr{ +margin:10px 0px 0px 0px; +} + +#page table{ +padding:5px; +} + +#page th{ +padding:3px; +border:1px solid #333333; +text-align:center; +color:inherit; +background-color:#ECECEC; +} + +#page tr{ +} + +#page td{ +padding:3px; +border:1px solid #333333; +text-align:center; +color:inherit; +background-color:#FCFCFC; +} + diff --git a/query/main.html b/query/main.html new file mode 100644 index 0000000..3fc3508 --- /dev/null +++ b/query/main.html @@ -0,0 +1,54 @@ +<!DOCTYPE html> +<html> +<head> + <meta http-equiv="content-type" content="text/html;charset=utf-8"/> + <meta http-equiv="Content-Style-Type" content="text/css" /> + <title>GRAS Query Client</title> + <link rel="stylesheet" type="text/css" href="/main.css" /> + <link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.10.2/themes/smoothness/jquery-ui.css" /> + <script type="text/javascript" src="http://code.jquery.com/jquery-1.9.1.js"></script> + <script type="text/javascript" src="http://code.jquery.com/ui/1.10.2/jquery-ui.js"></script> + <script type="text/javascript" src="http://www.google.com/jsapi"></script> + <script type="text/javascript" src="/utils.js"></script> + <script type="text/javascript" src="/chart_factory.js"></script> + <script type="text/javascript" src="/chart_overhead_compare.js"></script> + <script type="text/javascript" src="/chart_overall_throughput.js"></script> + <script type="text/javascript" src="/chart_handler_breakdown.js"></script> + <script type="text/javascript" src="/chart_port_counters.js"></script> + <script type="text/javascript" src="/chart_global_counters.js"></script> + <script type="text/javascript" src="/chart_port_downtime.js"></script> + <script type="text/javascript" src="/main.js"></script> + <script type="text/javascript"> + google.load('visualization', '1.0', {'packages':['corechart']}); + google.setOnLoadCallback(gras_stats_main); + </script> +</head> + +<body id='page'> + <div id="chart_designer"> + <table> + <tr> + <th id="top_name">Blocks Available</th> + <th>Chart Types</th> + <th>Global Options</th> + </tr> + <tr> + <td id="chart_designer_blocks" rowspan='2'></td> + <td><select id="chart_type_selector" /></td> + <td> + <label for="chart_update_rate">Updates/sec:</label> + <input id="chart_update_rate" type="spinner" /> + </td> + </tr> + <tr> + <td><input type="button" value="Create New Chart" id="chart_factory_button" /></td> + <td><input id="chart_active_state" type="checkbox" /><label for="chart_active_state">Active</label></td> + </tr> + </table> + </div> + <div id="charts_panel"></div> + <div style="display:none" id="div-dialog-warning"> + </div> +</body> + +</html> diff --git a/query/main.js b/query/main.js new file mode 100644 index 0000000..b17af63 --- /dev/null +++ b/query/main.js @@ -0,0 +1,79 @@ +/*********************************************************************** + * Stats registry data structure + **********************************************************************/ +var GrasStatsRegistry = function() +{ + this.overall_rate = 3.0; + this.overall_active = true; + this.block_ids = new Array(); + this.top_id = 'top'; + this.online = true; + this.offline_count = 0; +} + +/*********************************************************************** + * Server offline animation + **********************************************************************/ +function gras_handle_offline(registry) +{ + if (!registry.online) registry.offline_count++; + if (registry.online) $('#page').css('background-color', '#EEEEFF'); + else if (registry.offline_count%2 == 0) $('#page').css('background-color', '#FF4848'); + else if (registry.offline_count%2 == 1) $('#page').css('background-color', '#EEEEFF'); +} + +/*********************************************************************** + * Query stats + **********************************************************************/ +var gras_query_stats = function(registry) +{ + $.ajax({ + type: "GET", + async: true, + url: "/stats.json", + dataType: "json", + traditional: true, //needed to parse data + data: {block:gras_chart_factory_active_blocks(registry)}, + success: function(response) + { + registry.online = true; + gras_handle_offline(registry); + if (registry.overall_active) gras_chart_factory_update(registry, response); + + var timeout = registry.overall_active? Math.round(1000/registry.overall_rate) : 1000; + window.setTimeout(function() + { + gras_query_stats(registry); + }, timeout); + }, + error: function() + { + registry.online = false; + gras_handle_offline(registry); + window.setTimeout(function() + { + gras_query_stats(registry); + }, 1000); + }, + }); +} + +/*********************************************************************** + * Init + **********************************************************************/ +var gras_stats_main = function() +{ + //create a new registry - storage for gui state + var registry = new GrasStatsRegistry(); + + //query various server args + $.getJSON('/args.json', function(data) + { + registry.top_id = data.name; + $('#top_name').append(' - ' + registry.top_id); + document.title += ' - ' + registry.top_id; + }); + + //initialize the charts factory + gras_chart_factory_init(registry); +} diff --git a/query/utils.js b/query/utils.js new file mode 100644 index 0000000..3e4fe4b --- /dev/null +++ b/query/utils.js @@ -0,0 +1,88 @@ +/*********************************************************************** + * Utility functions for stats + **********************************************************************/ +var gras_extract_total_items = function(point, id) +{ + var block_data = point.blocks[id]; + var total_items = 0; + $.each(block_data.items_produced, function(index, value) + { + total_items += value; + }); + $.each(block_data.items_consumed, function(index, value) + { + total_items += value; + }); + return total_items; +} + +var gras_extract_throughput_delta = function(p0, p1, id) +{ + var d0 = p0.blocks[id]; + var d1 = p1.blocks[id]; + var t0 = d0.stats_time; + var t1 = d1.stats_time; + var tps = d0.tps; + var items0 = gras_extract_total_items(p0, id); + var items1 = gras_extract_total_items(p1, id); + return ((items1-items0)*tps)/(t1-t0); +} + +var gras_extract_throughput = function(point, id) +{ + var block_data = point.blocks[id]; + var start_time = block_data.start_time; + var stats_time = block_data.stats_time; + var tps = block_data.tps; + var total_items = gras_extract_total_items(point, id); + return (total_items*tps)/(stats_time-start_time); +} + +var gras_extract_stat_time_delta = function(p0, p1) +{ + var t0 = p0.now; + var t1 = p1.now; + var tps = p0.tps; + return (t1-t0)/(tps); +} + +var gras_extract_percent_times = function(point, id) +{ + var block_data = point.blocks[id]; + var data = { + prep: block_data.total_time_prep, + work: block_data.total_time_work, + post: block_data.total_time_post, + input: block_data.total_time_input, + output: block_data.total_time_output, + }; + var total = 0; + $.each(data, function(key, val) + { + total += val; + }); + data['total'] = total; + return data; +} + +var gras_animate_show_hide = function(elem, show) +{ + if (show) elem.slideDown("fast"); + else elem.slideUp("fast"); +} + +var gras_error_dialog = function(error_title, error_text) +{ + $("#div-dialog-warning").text(error_text); + $("#div-dialog-warning").dialog({ + title: error_title, + resizable: false, + height: 160, + modal: true, + buttons: { + "Ok" : function () { + $(this).dialog("close"); + } + } + }).parent().addClass("ui-state-error"); +} |