diff options
-rw-r--r-- | lib/block_handlers.cpp | 16 | ||||
-rw-r--r-- | lib/gras_impl/input_buffer_queues.hpp | 5 | ||||
-rw-r--r-- | lib/gras_impl/stats.hpp | 6 | ||||
-rw-r--r-- | lib/top_block_query.cpp | 41 | ||||
-rw-r--r-- | python/gras/query/__init__.py | 33 | ||||
-rw-r--r-- | python/gras/query/chart_factory.js | 82 | ||||
-rw-r--r-- | python/gras/query/chart_total_io_counts.js | 31 | ||||
-rw-r--r-- | python/gras/query/main.js | 32 |
8 files changed, 169 insertions, 77 deletions
diff --git a/lib/block_handlers.cpp b/lib/block_handlers.cpp index 9865331..1f744f4 100644 --- a/lib/block_handlers.cpp +++ b/lib/block_handlers.cpp @@ -137,11 +137,25 @@ void BlockActor::handle_get_stats( ){ MESSAGE_TRACER(); + //instantaneous states we update here, + //and not interleaved with the rest of the code + const size_t num_inputs = this->get_num_inputs(); + this->stats.items_enqueued.resize(num_inputs); + this->stats.tags_enqueued.resize(num_inputs); + this->stats.msgs_enqueued.resize(num_inputs); + for (size_t i = 0; i < num_inputs; i++) + { + this->stats.items_enqueued[i] = this->input_queues.get_items_enqueued(i); + this->stats.tags_enqueued[i] = this->input_tags[i].size(); + this->stats.msgs_enqueued[i] = this->input_msgs[i].size(); + } + + //create the message reply object GetStatsMessage message; message.block_id = this->block_ptr->to_string(); message.stats = this->stats; message.stats_time = time_now(); - this->Send(message, from); //ACK + this->Send(message, from); //ACK this->highPrioAck(); } diff --git a/lib/gras_impl/input_buffer_queues.hpp b/lib/gras_impl/input_buffer_queues.hpp index 51ff3e6..48533b9 100644 --- a/lib/gras_impl/input_buffer_queues.hpp +++ b/lib/gras_impl/input_buffer_queues.hpp @@ -146,6 +146,11 @@ struct InputBufferQueues _bitset.set(i, _enqueued_bytes[i] >= _reserve_bytes[i]); } + GRAS_FORCE_INLINE size_t get_items_enqueued(const size_t i) + { + return _enqueued_bytes[i]/_items_sizes[i]; + } + BitSet _bitset; std::vector<size_t> _items_sizes; std::vector<size_t> _enqueued_bytes; diff --git a/lib/gras_impl/stats.hpp b/lib/gras_impl/stats.hpp index 3f78b8d..7edab29 100644 --- a/lib/gras_impl/stats.hpp +++ b/lib/gras_impl/stats.hpp @@ -29,6 +29,7 @@ struct BlockStats time_ticks_t start_time; time_ticks_t stop_time; + //overall tracking of ports std::vector<item_index_t> items_consumed; std::vector<item_index_t> tags_consumed; std::vector<item_index_t> msgs_consumed; @@ -36,6 +37,11 @@ struct BlockStats std::vector<item_index_t> tags_produced; std::vector<item_index_t> msgs_produced; + //instantaneous port status + std::vector<size_t> items_enqueued; + std::vector<size_t> msgs_enqueued; + std::vector<size_t> tags_enqueued; + item_index_t work_count; time_ticks_t time_last_work; time_ticks_t total_time_prep; diff --git a/lib/top_block_query.cpp b/lib/top_block_query.cpp index a8091d2..f00efd2 100644 --- a/lib/top_block_query.cpp +++ b/lib/top_block_query.cpp @@ -5,7 +5,9 @@ #include <boost/foreach.hpp> #include <boost/property_tree/ptree.hpp> #include <boost/property_tree/json_parser.hpp> +#include <boost/property_tree/xml_parser.hpp> #include <boost/regex.hpp> +#include <algorithm> #include <sstream> using namespace gras; @@ -36,13 +38,28 @@ static std::string my_write_json(const boost::property_tree::ptree &pt) return rv; } -std::string TopBlock::query(const std::string &) +static std::string query_blocks(ElementImpl *self, const boost::property_tree::ptree &) +{ + boost::property_tree::ptree root; + boost::property_tree::ptree e; + BOOST_FOREACH(Apology::Worker *worker, self->executor->get_workers()) + { + boost::property_tree::ptree t; + t.put_value(dynamic_cast<BlockActor *>(worker)->block_ptr->to_string()); + e.push_back(std::make_pair("", t)); + } + root.push_back(std::make_pair("blocks", e)); + return my_write_json(root); +} + +static std::string query_stats(ElementImpl *self, const boost::property_tree::ptree &) { //get stats with custom receiver and set high prio GetStatsReceiver receiver; size_t outstandingCount(0); - BOOST_FOREACH(Apology::Worker *worker, (*this)->executor->get_workers()) + BOOST_FOREACH(Apology::Worker *worker, self->executor->get_workers()) { + //send a message to the block's actor to query stats dynamic_cast<BlockActor *>(worker)->highPrioPreNotify(); worker->Push(GetStatsMessage(), receiver.GetAddress()); outstandingCount++; @@ -51,7 +68,6 @@ std::string TopBlock::query(const std::string &) //create root level node boost::property_tree::ptree root; - root.put("id", this->to_string()); root.put("now", time_now()); root.put("tps", time_tps()); @@ -81,6 +97,9 @@ std::string TopBlock::query(const std::string &) } \ block.push_back(std::make_pair(#l, e)); \ } + my_block_ptree_append(items_enqueued); + my_block_ptree_append(tags_enqueued); + my_block_ptree_append(msgs_enqueued); my_block_ptree_append(items_consumed); my_block_ptree_append(tags_consumed); my_block_ptree_append(msgs_consumed); @@ -93,3 +112,19 @@ std::string TopBlock::query(const std::string &) return my_write_json(root); } + +std::string TopBlock::query(const std::string &args) +{ + //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 + std::stringstream query_args_ss(args); + boost::property_tree::ptree query_args_pt; + boost::property_tree::xml_parser::read_xml(query_args_ss, query_args_pt); + + //dispatch based on path arg + std::string path = query_args_pt.get<std::string>("args.path"); + if (path == "/blocks.json") return query_blocks(this->get(), query_args_pt); + if (path == "/stats.json") return query_stats(this->get(), query_args_pt); + return ""; +} diff --git a/python/gras/query/__init__.py b/python/gras/query/__init__.py index 06c7b70..b71b7e0 100644 --- a/python/gras/query/__init__.py +++ b/python/gras/query/__init__.py @@ -1,7 +1,9 @@ import time import BaseHTTPServer +import urlparse import json import os + __path__ = os.path.abspath(os.path.dirname(__file__)) server_registry = dict() @@ -15,23 +17,39 @@ class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler): 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 = s.path - if path.startswith('/'): path = path[1:] - if not path: path = 'main.html' + 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': + if path == '/args.json': arg_strs = dict((str(k), str(v)) for k, v in args.iteritems()) s.wfile.write(json.dumps(arg_strs)) - elif path == 'stats.json': - s.wfile.write(args['top_block'].query(s.path)) else: - s.wfile.write(json.dumps({})) + #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") @@ -39,6 +57,7 @@ class MyHandler(BaseHTTPServer.BaseHTTPRequestHandler): 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") diff --git a/python/gras/query/chart_factory.js b/python/gras/query/chart_factory.js index 5b3239a..ae23728 100644 --- a/python/gras/query/chart_factory.js +++ b/python/gras/query/chart_factory.js @@ -1,4 +1,9 @@ /*********************************************************************** + * Some constants + **********************************************************************/ +var GRAS_CHARTS_STD_WIDTH = 250; + +/*********************************************************************** * Chart registry for now chart types **********************************************************************/ var gras_chart_get_registry = function() @@ -12,43 +17,28 @@ var gras_chart_get_registry = function() } /*********************************************************************** - * update after new query event + * get blocks that need active querying **********************************************************************/ -function gras_chart_factory_update(registry, point) +function gras_chart_factory_active_blocks(registry) { + var block_ids = new Array(); $.each(registry.active_charts, function(index, chart_info) { - chart_info.point = point; //store last data point - chart_info.chart.update(point); + $.merge(block_ids, chart_info.args.block_ids); }); + return $.unique(block_ids); } /*********************************************************************** - * One time setup + * update after new query event **********************************************************************/ -function gras_chart_factory_setup(registry, point) +function gras_chart_factory_update(registry, point) { - //gui init for factory controls - gras_chart_factory_init(registry); - - //block registry and checkboxes init - $.each(point.blocks, function(id, block) + registry.point = point; //store last data point + $.each(registry.active_charts, function(index, chart_info) { - registry.block_ids.push(id); - var container = $('#chart_designer_blocks'); - var div = $('<div />'); - $(div).append('<label>' + id + '</label>'); - var input = $('<input />').attr({ - type: 'checkbox', - name: id - }); - input.attr('checked', false); - $(div).append(input); - $(container).append(div); + chart_info.chart.update(point); }); - - //try to load last settings - try{gras_chart_load(registry);}catch(e){} } /*********************************************************************** @@ -174,7 +164,7 @@ function gras_chart_factory_make(registry, args) args['height'] = chart_box.height(); args['position'] = chart_box.offset(); chart.gc_resize = false; - chart.update(chart_info.point); + chart.update(registry.point); gras_chart_save(registry); }; @@ -187,7 +177,7 @@ function gras_chart_factory_make(registry, args) start: function(event, ui) { chart.gc_resize = true; - chart.update(chart_info.point); + chart.update(registry.point); }}); chart_box.css('position', 'absolute'); @@ -204,20 +194,9 @@ function gras_chart_factory_make(registry, args) } /*********************************************************************** - * chart factory handle online/offline - **********************************************************************/ -function gras_chart_factory_online(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'); -} - -/*********************************************************************** * chart factory init **********************************************************************/ -function gras_chart_factory_init(registry) +function gras_chart_factory_init(registry, done_cb) { //init registry containers registry.active_charts = new Array(); @@ -259,4 +238,29 @@ function gras_chart_factory_init(registry) if (registry.overall_active) gras_query_stats(registry); else window.clearInterval(registry.timeout_handle); }); + + //block registry and checkboxes init + $.getJSON('/blocks.json', function(data) + { + $.each(data.blocks, function(index, id) + { + registry.block_ids.push(id); + var container = $('#chart_designer_blocks'); + var div = $('<div />'); + $(div).append('<label>' + id + '</label>'); + var input = $('<input />').attr({ + type: 'checkbox', + name: id + }); + input.attr('checked', false); + $(div).append(input); + $(container).append(div); + }); + + //try to load last settings + try{gras_chart_load(registry);}catch(e){} + + //done callback because getJSON was async + done_cb(registry); + }); } diff --git a/python/gras/query/chart_total_io_counts.js b/python/gras/query/chart_total_io_counts.js index 2f9ced3..f959414 100644 --- a/python/gras/query/chart_total_io_counts.js +++ b/python/gras/query/chart_total_io_counts.js @@ -21,20 +21,29 @@ GrasChartTotalIoCounts.prototype.update = function(point) $('ul', this.div).remove(); //clear old lists this.div.append(ul); + function make_entry(strong, span) { - var init_time = parseInt(block_data.init_time); - var stats_time = parseInt(block_data.stats_time); - var tps = parseInt(block_data.tps); - var duration = (stats_time - init_time)/tps; var li = $('<li />'); - var strong = $('<strong />').text('Elapsed' + ': '); - var span = $('<span />').text(duration.toFixed(2).toString() + ' secs'); + 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'], @@ -50,12 +59,10 @@ GrasChartTotalIoCounts.prototype.update = function(point) var key = contents[2]; $.each(block_data[key], function(index, count) { - var li = $('<li />'); - var strong = $('<strong />').text(dir + index.toString() + ': '); - var span = $('<span />').text(count.toString() + ' ' + units); - li.append(strong); - li.append(span); - if (count > 0) ul.append(li); + if (count > 0) + { + make_entry(dir + index.toString(), count.toString() + ' ' + units); + } }); }); } diff --git a/python/gras/query/main.js b/python/gras/query/main.js index 8a261f9..a9c255b 100644 --- a/python/gras/query/main.js +++ b/python/gras/query/main.js @@ -1,9 +1,4 @@ /*********************************************************************** - * Some constants - **********************************************************************/ -var GRAS_CHARTS_STD_WIDTH = 250; - -/*********************************************************************** * Stats registry data structure **********************************************************************/ var GrasStatsRegistry = function() @@ -17,6 +12,17 @@ var GrasStatsRegistry = function() } /*********************************************************************** + * 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) @@ -26,10 +32,12 @@ var gras_query_stats = function(registry) 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_chart_factory_online(registry); + gras_handle_offline(registry); if (registry.overall_active) { gras_chart_factory_update(registry, response); @@ -43,7 +51,7 @@ var gras_query_stats = function(registry) error: function() { registry.online = false; - gras_chart_factory_online(registry); + gras_handle_offline(registry); registry.timeout_handle = window.setTimeout(function() { gras_query_stats(registry); @@ -68,12 +76,6 @@ var gras_stats_main = function() document.title += ' - ' + registry.top_id; }); - //query the stats for initial setup - $.getJSON('/stats.json', function(data) - { - gras_chart_factory_setup(registry, data); - }); - - //start the query loop in the background - gras_query_stats(registry); + //initialize the charts factory + gras_chart_factory_init(registry, gras_query_stats); } |