summaryrefslogtreecommitdiffstats
path: root/src/hist_source.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/hist_source.hh')
-rw-r--r--src/hist_source.hh408
1 files changed, 408 insertions, 0 deletions
diff --git a/src/hist_source.hh b/src/hist_source.hh
new file mode 100644
index 0000000..a6399ad
--- /dev/null
+++ b/src/hist_source.hh
@@ -0,0 +1,408 @@
+/**
+ * Copyright (c) 2007-2012, Timothy Stack
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ * list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * * Neither the name of Timothy Stack nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * @file hist_source.hh
+ */
+
+#ifndef hist_source_hh
+#define hist_source_hh
+
+#include <cmath>
+#include <limits>
+#include <map>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include "base/lnav_log.hh"
+#include "mapbox/variant.hpp"
+#include "strong_int.hh"
+#include "textview_curses.hh"
+
+/** Type for indexes into a group of buckets. */
+STRONG_INT_TYPE(int, bucket_group);
+
+/** Type used to differentiate values added to the same row in the histogram */
+STRONG_INT_TYPE(int, bucket_type);
+
+struct stacked_bar_chart_base {
+ virtual ~stacked_bar_chart_base() = default;
+
+ struct show_none {};
+ struct show_all {};
+ struct show_one {
+ size_t so_index;
+
+ explicit show_one(int so_index) : so_index(so_index) {}
+ };
+
+ using show_state = mapbox::util::variant<show_none, show_all, show_one>;
+
+ enum class direction {
+ forward,
+ backward,
+ };
+};
+
+template<typename T>
+class stacked_bar_chart : public stacked_bar_chart_base {
+public:
+ stacked_bar_chart& with_stacking_enabled(bool enabled)
+ {
+ this->sbc_do_stacking = enabled;
+ return *this;
+ }
+
+ stacked_bar_chart& with_attrs_for_ident(const T& ident, text_attrs attrs)
+ {
+ auto& ci = this->find_ident(ident);
+ ci.ci_attrs = attrs;
+ return *this;
+ }
+
+ stacked_bar_chart& with_margins(unsigned long left, unsigned long right)
+ {
+ this->sbc_left = left;
+ this->sbc_right = right;
+ return *this;
+ }
+
+ bool attrs_in_use(const text_attrs& attrs) const {
+ for (const auto& ident : this->sbc_idents) {
+ if (ident.ci_attrs == attrs) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ show_state show_next_ident(direction dir)
+ {
+ bool single_ident = this->sbc_idents.size() == 1;
+
+ if (this->sbc_idents.empty()) {
+ return this->sbc_show_state;
+ }
+
+ this->sbc_show_state = this->sbc_show_state.match(
+ [&](show_none) -> show_state {
+ switch (dir) {
+ case direction::forward:
+ if (single_ident) {
+ return show_all();
+ }
+ return show_one(0);
+ case direction::backward:
+ return show_all();
+ }
+
+ return show_all();
+ },
+ [&](show_one& one) -> show_state {
+ switch (dir) {
+ case direction::forward:
+ if (one.so_index + 1 == this->sbc_idents.size()) {
+ return show_all();
+ }
+ return show_one(one.so_index + 1);
+ case direction::backward:
+ if (one.so_index == 0) {
+ return show_none();
+ }
+ return show_one(one.so_index - 1);
+ }
+
+ return show_all();
+ },
+ [&](show_all) -> show_state {
+ switch (dir) {
+ case direction::forward:
+ return show_none();
+ case direction::backward:
+ if (single_ident) {
+ return show_none();
+ }
+ return show_one(this->sbc_idents.size() - 1);
+ }
+
+ return show_all();
+ });
+
+ return this->sbc_show_state;
+ }
+
+ void get_ident_to_show(T& ident_out) const
+ {
+ this->sbc_show_state.match(
+ [](const show_none) {},
+ [](const show_all) {},
+ [&](const show_one& one) {
+ ident_out = this->sbc_idents[one.so_index].ci_ident;
+ });
+ }
+
+ void chart_attrs_for_value(const listview_curses& lc,
+ int& left,
+ const T& ident,
+ double value,
+ string_attrs_t& value_out) const
+ {
+ auto ident_iter = this->sbc_ident_lookup.find(ident);
+
+ require(ident_iter != this->sbc_ident_lookup.end());
+
+ size_t ident_index = ident_iter->second;
+ unsigned long width, avail_width;
+ bucket_stats_t overall_stats;
+ struct line_range lr;
+ vis_line_t height;
+
+ lr.lr_unit = line_range::unit::codepoint;
+
+ size_t ident_to_show = this->sbc_show_state.match(
+ [](const show_none) { return -1; },
+ [ident_index](const show_all) { return ident_index; },
+ [](const show_one& one) { return one.so_index; });
+
+ if (ident_to_show != ident_index) {
+ return;
+ }
+
+ lc.get_dimensions(height, width);
+
+ for (size_t lpc = 0; lpc < this->sbc_idents.size(); lpc++) {
+ if (this->sbc_show_state.template is<show_all>()
+ || lpc == (size_t) ident_to_show)
+ {
+ overall_stats.merge(this->sbc_idents[lpc].ci_stats,
+ this->sbc_do_stacking);
+ }
+ }
+
+ if (this->sbc_show_state.template is<show_all>()) {
+ avail_width = width - this->sbc_idents.size();
+ } else {
+ avail_width = width - 1;
+ }
+ avail_width -= this->sbc_left + this->sbc_right;
+
+ lr.lr_start = left;
+
+ const auto& ci = this->sbc_idents[ident_index];
+ int amount;
+
+ if (value == 0.0) {
+ amount = 0;
+ } else if ((overall_stats.bs_max_value - 0.01) <= value
+ && value <= (overall_stats.bs_max_value + 0.01))
+ {
+ amount = avail_width;
+ } else {
+ double percent
+ = (value - overall_stats.bs_min_value) / overall_stats.width();
+ amount = (int) rint(percent * avail_width);
+ amount = std::max(1, amount);
+ }
+ lr.lr_end = left = lr.lr_start + amount;
+
+ if (!ci.ci_attrs.empty() && !lr.empty()) {
+ auto rev_attrs = ci.ci_attrs;
+ rev_attrs.ta_attrs |= A_REVERSE;
+ value_out.emplace_back(lr, VC_STYLE.value(rev_attrs));
+ }
+ }
+
+ void clear()
+ {
+ this->sbc_idents.clear();
+ this->sbc_ident_lookup.clear();
+ this->sbc_show_state = show_all();
+ }
+
+ void add_value(const T& ident, double amount = 1.0)
+ {
+ struct chart_ident& ci = this->find_ident(ident);
+ ci.ci_stats.update(amount);
+ }
+
+ struct bucket_stats_t {
+ bucket_stats_t()
+ : bs_min_value(std::numeric_limits<double>::max()), bs_max_value(0)
+ {
+ }
+
+ void merge(const bucket_stats_t& rhs, bool do_stacking)
+ {
+ this->bs_min_value = std::min(this->bs_min_value, rhs.bs_min_value);
+ if (do_stacking) {
+ this->bs_max_value += rhs.bs_max_value;
+ } else {
+ this->bs_max_value
+ = std::max(this->bs_max_value, rhs.bs_max_value);
+ }
+ }
+
+ double width() const
+ {
+ return std::fabs(this->bs_max_value - this->bs_min_value);
+ }
+
+ void update(double value)
+ {
+ this->bs_max_value = std::max(this->bs_max_value, value);
+ this->bs_min_value = std::min(this->bs_min_value, value);
+ }
+
+ double bs_min_value;
+ double bs_max_value;
+ };
+
+ const bucket_stats_t& get_stats_for(const T& ident)
+ {
+ const chart_ident& ci = this->find_ident(ident);
+
+ return ci.ci_stats;
+ }
+
+protected:
+ struct chart_ident {
+ explicit chart_ident(const T& ident) : ci_ident(ident) {}
+
+ T ci_ident;
+ text_attrs ci_attrs;
+ bucket_stats_t ci_stats;
+ };
+
+ struct chart_ident& find_ident(const T& ident)
+ {
+ auto iter = this->sbc_ident_lookup.find(ident);
+ if (iter == this->sbc_ident_lookup.end()) {
+ this->sbc_ident_lookup[ident] = this->sbc_idents.size();
+ this->sbc_idents.emplace_back(ident);
+ return this->sbc_idents.back();
+ }
+ return this->sbc_idents[iter->second];
+ }
+
+ bool sbc_do_stacking{true};
+ unsigned long sbc_left{0}, sbc_right{0};
+ std::vector<struct chart_ident> sbc_idents;
+ std::unordered_map<T, unsigned int> sbc_ident_lookup;
+ show_state sbc_show_state{show_all()};
+};
+
+class hist_source2
+ : public text_sub_source
+ , public text_time_translator {
+public:
+ typedef enum {
+ HT_NORMAL,
+ HT_WARNING,
+ HT_ERROR,
+ HT_MARK,
+
+ HT__MAX
+ } hist_type_t;
+
+ hist_source2() { this->clear(); }
+
+ ~hist_source2() override = default;
+
+ void init();
+
+ void set_time_slice(int64_t slice) { this->hs_time_slice = slice; }
+
+ int64_t get_time_slice() const { return this->hs_time_slice; }
+
+ size_t text_line_count() override { return this->hs_line_count; }
+
+ size_t text_line_width(textview_curses& curses) override
+ {
+ return 48 + 8 * 4;
+ }
+
+ void clear();
+
+ void add_value(time_t row, hist_type_t htype, double value = 1.0);
+
+ void end_of_row();
+
+ void text_value_for_line(textview_curses& tc,
+ int row,
+ std::string& value_out,
+ line_flags_t flags) override;
+
+ void text_attrs_for_line(textview_curses& tc,
+ int row,
+ string_attrs_t& value_out) override;
+
+ size_t text_size_for_line(textview_curses& tc,
+ int row,
+ line_flags_t flags) override
+ {
+ return 0;
+ }
+
+ nonstd::optional<struct timeval> time_for_row(vis_line_t row) override;
+
+ nonstd::optional<vis_line_t> row_for_time(
+ struct timeval tv_bucket) override;
+
+private:
+ struct hist_value {
+ double hv_value;
+ };
+
+ struct bucket_t {
+ time_t b_time;
+ hist_value b_values[HT__MAX];
+ };
+
+ static const int64_t BLOCK_SIZE = 100;
+
+ struct bucket_block {
+ bucket_block()
+ {
+ memset(this->bb_buckets, 0, sizeof(this->bb_buckets));
+ }
+
+ unsigned int bb_used{0};
+ bucket_t bb_buckets[BLOCK_SIZE];
+ };
+
+ bucket_t& find_bucket(int64_t index);
+
+ int64_t hs_time_slice{10 * 60};
+ int64_t hs_line_count;
+ int64_t hs_last_bucket;
+ time_t hs_last_row;
+ std::map<int64_t, struct bucket_block> hs_blocks;
+ stacked_bar_chart<hist_type_t> hs_chart;
+};
+
+#endif