summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
m---------Theron0
-rw-r--r--benchmark/bm_registry.py95
-rw-r--r--benchmark/run_benchmarks.py23
-rw-r--r--benchmark/tb_combiner_array.py (renamed from benchmark/tb_many_1_to_1_blocks.py)0
-rw-r--r--benchmark/tb_linear_chain.py27
-rw-r--r--benchmark/tb_many_rate_changes.py4
-rw-r--r--lib/CMakeLists.txt3
-rw-r--r--lib/block_actor.cpp4
-rw-r--r--lib/gras_impl/block_actor.hpp2
-rw-r--r--lib/gras_impl/stats.hpp4
-rw-r--r--lib/theron_allocator.cpp100
-rw-r--r--lib/top_block_query.cpp10
-rw-r--r--python/gras/query/CMakeLists.txt1
-rw-r--r--python/gras/query/chart_allocator_counts.js46
-rw-r--r--python/gras/query/chart_factory.js1
-rw-r--r--python/gras/query/main.html1
16 files changed, 182 insertions, 139 deletions
diff --git a/Theron b/Theron
-Subproject e31d0a51e9ea245e7a981365fb58023d24e8649
+Subproject 1a87688a68c58305947ec2d3eb83e51d8724655
diff --git a/benchmark/bm_registry.py b/benchmark/bm_registry.py
index 7c9bb1c..da7efe4 100644
--- a/benchmark/bm_registry.py
+++ b/benchmark/bm_registry.py
@@ -6,7 +6,7 @@ def tokwargs(**kwargs): return kwargs
INSTALL_PREFIX = '/opt/usr'
-DURATION = '1.7' #seconds
+DURATION = '1.3' #seconds
GRAS_ENV = {
'PATH': os.path.join(INSTALL_PREFIX, 'gras/bin:$PATH:%s'%os.getenv('PATH')),
@@ -20,15 +20,27 @@ GR_ENV = {
'PYTHONPATH': os.path.join(INSTALL_PREFIX, 'gr/lib/python2.7/dist-packages:%s'%os.getenv('PYTHONPATH')),
}
-BENCHMARK_MANY_11_BLOCKS = tokwargs(
- wat='Benchmark the schedulers with many 1:1 ratio blocks',
+BENCHMARK_LINEAR_CHAIN = tokwargs(
+ wat='Benchmark the schedulers with linear chain topology',
moar='''\
-- Compare simultaneous 1:1 ratio blocks in each scheduler.
+- Topology is a linear chain of one input/one output blocks.
- GRAS will use only the buffer pool allocator,
and every work will fully consume available buffers.''',
tests = [
- tokwargs(wat='GRAS', args=['tb_many_1_to_1_blocks.py', DURATION], env=GRAS_ENV),
- tokwargs(wat='GR', args=['tb_many_1_to_1_blocks.py', DURATION], env=GR_ENV),
+ tokwargs(wat='GRAS', args=['tb_linear_chain.py', DURATION], env=GRAS_ENV, expand=True),
+ tokwargs(wat='GRSS', args=['tb_linear_chain.py', DURATION], env=GR_ENV),
+ ],
+)
+
+BENCHMARK_COMBINER_ARRAY = tokwargs(
+ wat='Benchmark the schedulers with combiner array topology',
+ moar='''\
+- Topology is a tower of two input math blocks.
+- GRAS will use only the buffer pool allocator,
+and every work will fully consume available buffers.''',
+ tests = [
+ tokwargs(wat='GRAS', args=['tb_combiner_array.py', DURATION], env=GRAS_ENV, expand=True),
+ tokwargs(wat='GRSS', args=['tb_combiner_array.py', DURATION], env=GR_ENV),
],
)
@@ -39,40 +51,58 @@ BENCHMARK_MANY_RATE_BLOCKS = tokwargs(
- GRAS will use only the buffer pool allocator,
and every work will fully consume available buffers.''',
tests = [
- tokwargs(wat='GRAS', args=['tb_many_rate_changes.py', '--dur', DURATION], env=GRAS_ENV),
- tokwargs(wat='GR', args=['tb_many_rate_changes.py', '--dur', DURATION], env=GR_ENV),
+ tokwargs(wat='GRAS', args=['tb_many_rate_changes.py', '--dur', DURATION], env=GRAS_ENV, expand=True),
+ tokwargs(wat='GRSS', args=['tb_many_rate_changes.py', '--dur', DURATION], env=GR_ENV),
+ ],
+)
+
+BENCHMARK_DFIR_BLOCK = tokwargs(
+ wat='Benchmark the schedulers with a FIR block',
+ moar='''\
+- Compare filter blocks in each scheduler.
+- Shows both schedulers using circular buffer.''',
+ tests = [
+ tokwargs(wat='GRAS', args=['tb_filter_block.py', '--dur', DURATION, '--which', 'dfir'], env=GRAS_ENV, expand=True),
+ tokwargs(wat='GRSS', args=['tb_filter_block.py', '--dur', DURATION, '--which', 'dfir'], env=GR_ENV),
],
)
-BENCHMARK_FILTER_BLOCK = tokwargs(
- wat='Benchmark the schedulers with a filter block',
+BENCHMARK_RESAMP_BLOCK = tokwargs(
+ wat='Benchmark the schedulers with a resampler block',
moar='''\
- Compare filter blocks in each scheduler.
-- Shows both schedulers using circular buffer.
-- The decimating FIR filter is compared.
-- The rational resampler filter is compared.''',
+- Shows both schedulers using circular buffer.''',
+ tests = [
+ tokwargs(wat='GRAS', args=['tb_filter_block.py', '--dur', DURATION, '--which', 'resamp'], env=GRAS_ENV, expand=True),
+ tokwargs(wat='GRSS', args=['tb_filter_block.py', '--dur', DURATION, '--which', 'resamp'], env=GR_ENV),
+ ],
+)
+
+BENCHMARK_ADD_OPS = tokwargs(
+ wat='Benchmark GrExtras vs gr-blocks adder blocks',
+ moar='''\
+- Compare math block implementations using GRAS.
+- All blocks are using vector optimization.
+- GrExtras math blocks avoid an unnecessary memcpy.
+- GrExtras math blocks enable automatic bufer in-placing.''',
tests = [
- tokwargs(wat='GRAS decim FIR', args=['tb_filter_block.py', '--dur', DURATION, '--which', 'dfir'], env=GRAS_ENV),
- tokwargs(wat='GR decim FIR', args=['tb_filter_block.py', '--dur', DURATION, '--which', 'dfir'], env=GR_ENV),
- tokwargs(wat='GRAS resampler', args=['tb_filter_block.py', '--dur', DURATION, '--which', 'resamp'], env=GRAS_ENV),
- tokwargs(wat='GR resampler', args=['tb_filter_block.py', '--dur', DURATION, '--which', 'resamp'], env=GR_ENV),
+ tokwargs(wat='GrExtras\n(GRAS)', args=['tb_grextras_math.py', DURATION, 'extras_add'], env=GRAS_ENV, expand=True),
+ tokwargs(wat='gr-blocks\n(GRAS)', args=['tb_grextras_math.py', DURATION, 'blocks_add'], env=GRAS_ENV, expand=True),
+ tokwargs(wat='gr-blocks\n(GRSS)', args=['tb_grextras_math.py', DURATION, 'blocks_add'], env=GR_ENV),
],
)
-BENCHMARK_MATH_OPS = tokwargs(
- wat='Benchmark GrExtras vs gr-blocks math blocks',
+BENCHMARK_MULT_OPS = tokwargs(
+ wat='Benchmark GrExtras vs gr-blocks multiplier blocks',
moar='''\
- Compare math block implementations using GRAS.
- All blocks are using vector optimization.
- GrExtras math blocks avoid an unnecessary memcpy.
- GrExtras math blocks enable automatic bufer in-placing.''',
tests = [
- tokwargs(wat='GrExtras Add\n(GRAS)', args=['tb_grextras_math.py', DURATION, 'extras_add'], env=GRAS_ENV),
- tokwargs(wat='gr-blocks Add\n(GRAS)', args=['tb_grextras_math.py', DURATION, 'blocks_add'], env=GRAS_ENV),
- tokwargs(wat='gr-blocks Add\n(GR)', args=['tb_grextras_math.py', DURATION, 'blocks_add'], env=GR_ENV),
- tokwargs(wat='GrExtras Mult\n(GRAS)', args=['tb_grextras_math.py', DURATION, 'extras_mult'], env=GRAS_ENV),
- tokwargs(wat='gr-blocks Mult\n(GRAS)', args=['tb_grextras_math.py', DURATION, 'blocks_mult'], env=GRAS_ENV),
- tokwargs(wat='gr-blocks Mult\n(GR)', args=['tb_grextras_math.py', DURATION, 'blocks_mult'], env=GR_ENV),
+ tokwargs(wat='GrExtras\n(GRAS)', args=['tb_grextras_math.py', DURATION, 'extras_mult'], env=GRAS_ENV, expand=True),
+ tokwargs(wat='gr-blocks\n(GRAS)', args=['tb_grextras_math.py', DURATION, 'blocks_mult'], env=GRAS_ENV, expand=True),
+ tokwargs(wat='gr-blocks\n(GRSS)', args=['tb_grextras_math.py', DURATION, 'blocks_mult'], env=GR_ENV),
],
)
@@ -82,16 +112,19 @@ BENCHMARK_DELAY_BLOCKS = tokwargs(
- Compare delay block implementations using GRAS.
- The GrExtras implementation uses zero-copy.''',
tests = [
- tokwargs(wat='GrExtras Delay\n(GRAS)', args=['tb_grextras_delay.py', DURATION, 'extras_delay'], env=GRAS_ENV),
- tokwargs(wat='gr-core Delay\n(GRAS)', args=['tb_grextras_delay.py', DURATION, 'core_delay'], env=GRAS_ENV),
- tokwargs(wat='gr-core Delay\n(GR)', args=['tb_grextras_delay.py', DURATION, 'core_delay'], env=GR_ENV),
+ tokwargs(wat='GrExtras\n(GRAS)', args=['tb_grextras_delay.py', DURATION, 'extras_delay'], env=GRAS_ENV, expand=True),
+ tokwargs(wat='gr-core\n(GRAS)', args=['tb_grextras_delay.py', DURATION, 'core_delay'], env=GRAS_ENV, expand=True),
+ tokwargs(wat='gr-core\n(GRSS)', args=['tb_grextras_delay.py', DURATION, 'core_delay'], env=GR_ENV),
],
)
BENCHMARKS = (
- BENCHMARK_MANY_11_BLOCKS,
+ BENCHMARK_LINEAR_CHAIN,
+ BENCHMARK_COMBINER_ARRAY,
BENCHMARK_MANY_RATE_BLOCKS,
- BENCHMARK_FILTER_BLOCK,
- BENCHMARK_MATH_OPS,
+ BENCHMARK_DFIR_BLOCK,
+ BENCHMARK_RESAMP_BLOCK,
+ BENCHMARK_ADD_OPS,
+ BENCHMARK_MULT_OPS,
BENCHMARK_DELAY_BLOCKS,
)
diff --git a/benchmark/run_benchmarks.py b/benchmark/run_benchmarks.py
index 8cb3503..779aa6a 100644
--- a/benchmark/run_benchmarks.py
+++ b/benchmark/run_benchmarks.py
@@ -15,7 +15,7 @@ cpu_count = multiprocessing.cpu_count()
from bm_registry import BENCHMARKS
-NUM_RUNS_PER_TEST = 5
+NUM_RUNS_PER_TEST = 3
BAD_BOOST_KILL_DURATION = 5.0 #seconds
@@ -45,6 +45,25 @@ def run_a_single_one(args, env):
raise Exception, 'no result found!'
#return t1-t0
+def expand_tests(bm):
+ for run in bm['tests']:
+ if run.has_key('expand') and run['expand']:
+ import copy
+ new_run = copy.deepcopy(run)
+ new_run['wat'] += '\n(Block)'
+ new_run['env']['GRAS_YIELD'] = 'BLOCKING'
+ yield new_run
+ new_run = copy.deepcopy(run)
+ new_run['wat'] += '\n(Spin)'
+ new_run['env']['GRAS_YIELD'] = 'STRONG'
+ yield new_run
+ new_run = copy.deepcopy(run)
+ new_run['wat'] += '\n(TPB)'
+ new_run['env']['GRAS_YIELD'] = 'BLOCKING'
+ new_run['env']['GRAS_TPP'] = '1'
+ yield new_run
+ else: yield run
+
def do_a_benchmark(bm):
title = bm['wat']
print '#'*(len(title)+25)
@@ -53,7 +72,7 @@ def do_a_benchmark(bm):
result_means = list()
result_stddevs = list()
test_names = list()
- for run in bm['tests']:
+ for run in expand_tests(bm):
test_name = run['wat']
print '-'*(len(test_name)+25)
print '-- running test:', test_name.replace('\n', ' ')
diff --git a/benchmark/tb_many_1_to_1_blocks.py b/benchmark/tb_combiner_array.py
index c545d7e..c545d7e 100644
--- a/benchmark/tb_many_1_to_1_blocks.py
+++ b/benchmark/tb_combiner_array.py
diff --git a/benchmark/tb_linear_chain.py b/benchmark/tb_linear_chain.py
new file mode 100644
index 0000000..184b6e1
--- /dev/null
+++ b/benchmark/tb_linear_chain.py
@@ -0,0 +1,27 @@
+import gnuradio
+from gnuradio import gr
+from gnuradio import blocks as grblocks
+import sys
+
+if __name__ == '__main__':
+
+ duration = float(sys.argv[1])
+
+ tb = gr.top_block()
+ src = gr.null_source(8)
+ b0 = gr.copy(8)
+ b1 = grblocks.sub_cc()
+ b2 = gr.copy(8)
+ b3 = grblocks.divide_cc()
+ b4 = gr.copy(8)
+ sink = gr.null_sink(8)
+
+ tb.connect(src, b0, b1, b2, b3, b4, sink)
+
+ import time
+ tb.start()
+ time.sleep(duration)
+ print '##RESULT##', sink.nitems_read(0)/duration
+ import sys; sys.stdout.flush()
+ tb.stop()
+ tb.wait()
diff --git a/benchmark/tb_many_rate_changes.py b/benchmark/tb_many_rate_changes.py
index e94179f..2930dd6 100644
--- a/benchmark/tb_many_rate_changes.py
+++ b/benchmark/tb_many_rate_changes.py
@@ -30,15 +30,13 @@ class many_rate_changing(gr.top_block):
self.gr_unpacked_to_packed_xx_0 = gr.unpacked_to_packed_bb(2, gr.GR_LSB_FIRST)
self.gr_packed_to_unpacked_xx_0 = gr.packed_to_unpacked_bb(2, gr.GR_MSB_FIRST)
self.gr_null_sink_0_2 = gr.null_sink(gr.sizeof_char*1)
- self.blocks_keep_m_in_n_0 = blocks.keep_m_in_n(gr.sizeof_float, 3, 20, 0)
self.blocks_float_to_char_0 = blocks.float_to_char(1, 1)
self.blocks_char_to_float_0 = blocks.char_to_float(1, 1)
##################################################
# Connections
##################################################
- self.connect((self.blocks_char_to_float_0, 0), (self.blocks_keep_m_in_n_0, 0))
- self.connect((self.blocks_keep_m_in_n_0, 0), (self.blocks_float_to_char_0, 0))
+ self.connect((self.blocks_char_to_float_0, 0), (self.blocks_float_to_char_0, 0))
self.connect((self.blocks_float_to_char_0, 0), (self.gr_packed_to_unpacked_xx_0, 0))
self.connect((self.gr_unpacked_to_packed_xx_0, 0), (self.blocks_char_to_float_0, 0))
self.connect((self.random_source_x_0, 0), (self.gr_unpacked_to_packed_xx_0, 0))
diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt
index ba7e76e..bb74dc9 100644
--- a/lib/CMakeLists.txt
+++ b/lib/CMakeLists.txt
@@ -24,6 +24,8 @@ add_definitions(${THERON_DEFINES})
list(APPEND GRAS_LIBRARIES ${THERON_LIBRARIES})
list(APPEND GRAS_SOURCES ${THERON_SOURCES})
+add_definitions(-DTHERON_ENABLE_DEFAULTALLOCATOR_CHECKS=1)
+
########################################################################
# Setup Apology Deps
########################################################################
@@ -62,7 +64,6 @@ list(APPEND GRAS_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/top_block.cpp
${CMAKE_CURRENT_SOURCE_DIR}/top_block_query.cpp
${CMAKE_CURRENT_SOURCE_DIR}/register_messages.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/theron_allocator.cpp
${CMAKE_CURRENT_SOURCE_DIR}/weak_container.cpp
)
diff --git a/lib/block_actor.cpp b/lib/block_actor.cpp
index 750e52d..fe7f79e 100644
--- a/lib/block_actor.cpp
+++ b/lib/block_actor.cpp
@@ -14,7 +14,7 @@ ThreadPoolConfig::ThreadPoolConfig(void)
thread_count = std::max(size_t(2), thread_count);
node_mask = 0;
processor_mask = 0xffffffff;
- yield_strategy = "STRONG";
+ yield_strategy = "BLOCKING";
//environment variable override
const char * gras_yield = getenv("GRAS_YIELD");
@@ -44,7 +44,7 @@ ThreadPool::ThreadPool(const ThreadPoolConfig &config)
);
if (config.yield_strategy.empty()) params.mYieldStrategy = Theron::YIELD_STRATEGY_STRONG;
- //else if (config.yield_strategy == "BLOCKING") params.mYieldStrategy = Theron::YIELD_STRATEGY_BLOCKING;
+ else if (config.yield_strategy == "BLOCKING") params.mYieldStrategy = Theron::YIELD_STRATEGY_BLOCKING;
else if (config.yield_strategy == "POLITE") params.mYieldStrategy = Theron::YIELD_STRATEGY_POLITE;
else if (config.yield_strategy == "STRONG") params.mYieldStrategy = Theron::YIELD_STRATEGY_STRONG;
else if (config.yield_strategy == "AGGRESSIVE") params.mYieldStrategy = Theron::YIELD_STRATEGY_AGGRESSIVE;
diff --git a/lib/gras_impl/block_actor.hpp b/lib/gras_impl/block_actor.hpp
index 5444bb0..502f974 100644
--- a/lib/gras_impl/block_actor.hpp
+++ b/lib/gras_impl/block_actor.hpp
@@ -129,6 +129,8 @@ struct BlockActor : Apology::Worker
OutputBufferQueues output_queues;
std::vector<bool> produce_outputs;
BitSet inputs_available;
+ std::vector<time_ticks_t> time_input_not_ready;
+ std::vector<time_ticks_t> time_output_not_ready;
//tag and msg tracking
std::vector<bool> input_tags_changed;
diff --git a/lib/gras_impl/stats.hpp b/lib/gras_impl/stats.hpp
index 7edab29..d9e2341 100644
--- a/lib/gras_impl/stats.hpp
+++ b/lib/gras_impl/stats.hpp
@@ -37,6 +37,10 @@ struct BlockStats
std::vector<item_index_t> tags_produced;
std::vector<item_index_t> msgs_produced;
+ //port starvation tracking
+ std::vector<time_ticks_t> inputs_idle;
+ std::vector<time_ticks_t> outputs_idle;
+
//instantaneous port status
std::vector<size_t> items_enqueued;
std::vector<size_t> msgs_enqueued;
diff --git a/lib/theron_allocator.cpp b/lib/theron_allocator.cpp
deleted file mode 100644
index 9db9367..0000000
--- a/lib/theron_allocator.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-// Copyright (C) by Josh Blum. See LICENSE.txt for licensing information.
-
-/***********************************************************************
- * There is allocation overhead for sending messages.
- * Want per worker-allocator for message allocation...
- * But until thats possible, install a new global allocator.
- * This allocator uses a fixed pool for small sized buffers,
- * and otherwise the regular malloc/free for larger buffers.
- **********************************************************************/
-
-#include <gras/gras.hpp>
-#include <gras_impl/debug.hpp>
-#include <Theron/Detail/Threading/SpinLock.h>
-#include <Theron/IAllocator.h>
-#include <Theron/AllocatorManager.h>
-#include <boost/circular_buffer.hpp>
-
-#define MY_ALLOCATOR_CHUNK_SIZE 256
-#define MY_ALLOCATOR_POOL_SIZE (MY_ALLOCATOR_CHUNK_SIZE * (1 << 18))
-
-static unsigned long long unwanted_malloc_count = 0;
-
-static struct ExitPrinter
-{
- ExitPrinter(void){}
- ~ExitPrinter(void)
- {
- if (unwanted_malloc_count)
- {
- VAR(unwanted_malloc_count);
- }
- }
-} exit_printer;
-
-static struct WorkerAllocator : Theron::IAllocator
-{
- WorkerAllocator(void)
- {
- const size_t N = MY_ALLOCATOR_POOL_SIZE/MY_ALLOCATOR_CHUNK_SIZE;
- queue.set_capacity(N);
- for (size_t i = 0; i < N; i++)
- {
- const ptrdiff_t pool_ptr = ptrdiff_t(pool) + i*MY_ALLOCATOR_CHUNK_SIZE;
- queue.push_back((void *)pool_ptr);
- }
- pool_end = ptrdiff_t(pool) + MY_ALLOCATOR_POOL_SIZE;
- Theron::AllocatorManager::Instance().SetAllocator(this);
- }
-
- ~WorkerAllocator(void)
- {
- //NOP
- }
-
- void *Allocate(const SizeType size)
- {
- if GRAS_LIKELY(size <= MY_ALLOCATOR_CHUNK_SIZE)
- {
- mSpinLock.Lock();
- if GRAS_UNLIKELY(queue.empty())
- {
- unwanted_malloc_count++;
- mSpinLock.Unlock();
- return std::malloc(size);
- }
- void *memory = queue.front();
- queue.pop_front();
- mSpinLock.Unlock();
- return memory;
- }
- else
- {
- //std::cout << "malloc size " << size << std::endl;
- return std::malloc(size);
- }
- }
-
- void Free(void *const memory)
- {
- const bool in_pool = ptrdiff_t(memory) >= ptrdiff_t(pool) and ptrdiff_t(memory) < pool_end;
- if GRAS_LIKELY(in_pool)
- {
- mSpinLock.Lock();
- queue.push_front(memory);
- mSpinLock.Unlock();
- }
- else
- {
- std::free(memory);
- }
- }
-
- boost::circular_buffer<void *> queue;
- THERON_PREALIGN(GRAS_MAX_ALIGNMENT)
- char pool[MY_ALLOCATOR_POOL_SIZE]
- THERON_POSTALIGN(GRAS_MAX_ALIGNMENT);
- ptrdiff_t pool_end;
- Theron::Detail::SpinLock mSpinLock;
-
-} my_alloc;
diff --git a/lib/top_block_query.cpp b/lib/top_block_query.cpp
index b000a2d..3203507 100644
--- a/lib/top_block_query.cpp
+++ b/lib/top_block_query.cpp
@@ -7,6 +7,7 @@
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/xml_parser.hpp>
#include <boost/regex.hpp>
+#include <Theron/DefaultAllocator.h>
#include <algorithm>
#include <sstream>
@@ -87,6 +88,15 @@ static std::string query_stats(ElementImpl *self, const boost::property_tree::pt
root.put("now", time_now());
root.put("tps", time_tps());
+ //allocator debugs
+ Theron::DefaultAllocator *allocator = dynamic_cast<Theron::DefaultAllocator *>(Theron::AllocatorManager::Instance().GetAllocator());
+ if (allocator)
+ {
+ root.put("bytes_allocated", allocator->GetBytesAllocated());
+ root.put("peak_bytes_allocated", allocator->GetPeakBytesAllocated());
+ root.put("allocation_count", allocator->GetAllocationCount());
+ }
+
//iterate through blocks
boost::property_tree::ptree blocks;
BOOST_FOREACH(const GetStatsMessage &message, receiver.messages)
diff --git a/python/gras/query/CMakeLists.txt b/python/gras/query/CMakeLists.txt
index ab49532..a448068 100644
--- a/python/gras/query/CMakeLists.txt
+++ b/python/gras/query/CMakeLists.txt
@@ -20,6 +20,7 @@ INSTALL(
chart_overall_throughput.js
chart_handler_breakdown.js
chart_total_io_counts.js
+ chart_allocator_counts.js
main.css
DESTINATION ${GR_PYTHON_DIR}/gras/query
COMPONENT ${GRAS_COMP_PYTHON}
diff --git a/python/gras/query/chart_allocator_counts.js b/python/gras/query/chart_allocator_counts.js
new file mode 100644
index 0000000..cc06d24
--- /dev/null
+++ b/python/gras/query/chart_allocator_counts.js
@@ -0,0 +1,46 @@
+function GrasChartAllocatorCounts(args, panel)
+{
+ //input checking
+ if (args.block_ids.length != 0) throw gras_error_dialog(
+ "GrasChartAllocatorCounts",
+ "Error making allocator counts chart.\n"+
+ "Do not specify any blocks for this chart."
+ );
+
+ //settings
+ this.div = $('<div />').attr({class:'chart_total_io_counts'});
+ $(panel).append(this.div);
+ this.title = "Theron allocator counts"
+}
+
+GrasChartAllocatorCounts.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', 'bytes_allocated'],
+ ['Peak size', 'bytes', 'peak_bytes_allocated'],
+ ['Malloc\'d', 'times', 'allocation_count'],
+ ];
+
+ $.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);
+ });
+}
diff --git a/python/gras/query/chart_factory.js b/python/gras/query/chart_factory.js
index 122d222..dbb141a 100644
--- a/python/gras/query/chart_factory.js
+++ b/python/gras/query/chart_factory.js
@@ -13,6 +13,7 @@ var gras_chart_get_registry = function()
{key:'overall_throughput', name:'Overall Throughput', factory:GrasChartOverallThroughput},
{key:'handler_breakdown', name:'Handler Breakdown', factory:GrasChartHandlerBreakdown},
{key:'total_io_counts', name:'I/O port Totals', factory:GrasChartTotalIoCounts},
+ {key:'allocator_counts', name:'Allocator Counts', factory:GrasChartAllocatorCounts},
];
}
diff --git a/python/gras/query/main.html b/python/gras/query/main.html
index b64d53f..64d5809 100644
--- a/python/gras/query/main.html
+++ b/python/gras/query/main.html
@@ -15,6 +15,7 @@
<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_total_io_counts.js"></script>
+ <script type="text/javascript" src="/chart_allocator_counts.js"></script>
<script type="text/javascript" src="/main.js"></script>
<script type="text/javascript">
google.load('visualization', '1.0', {'packages':['corechart']});