[hist] cleanup the hist_source and add support for graphing a single column at a time

pull/69/head
Timothy Stack 11 years ago
parent 8908e2569f
commit 5ba26cc5f7

@ -99,8 +99,8 @@ public:
++this->gr_next_field;
};
void grep_end_batch(grep_proc &gp) { this->analyze(); };
void grep_end(grep_proc &gp) { this->analyze(); };
void grep_end_batch(grep_proc &gp) { };
void grep_end(grep_proc &gp) { };
private:

@ -236,6 +236,13 @@ through the file.
to the log view and move to the line number that was
selected in the "log_line" column.
TAB/Shift+TAB In the SQL result view, cycle through the columns that
are graphed. Initially, all number values are displayed
in a stacked graph. Pressing TAB will change the display
to only graph the first column. Repeatedly pressing TAB
will cycle through the columns until they are all graphed
again.
p Enable or disable the display of the fields that the
log message parser knows about or has discovered.
This overlay is temporarily enabled when the semicolon

@ -32,6 +32,8 @@
#include <math.h>
#include <limits.h>
#include <numeric>
#include "lnav_util.hh"
#include "hist_source.hh"
@ -40,7 +42,6 @@ using namespace std;
hist_source::hist_source()
: hs_bucket_size(1),
hs_group_size(100),
hs_analyzed(false),
hs_label_source(NULL),
hs_token_bucket(NULL)
{ }
@ -53,8 +54,6 @@ void hist_source::text_value_for_line(textview_curses &tc,
int grow = row / (this->buckets_per_group() + 1);
int brow = row % (this->buckets_per_group() + 1);
assert(this->hs_analyzed);
if (brow == 0) {
unsigned long width;
vis_line_t height;
@ -68,15 +67,17 @@ void hist_source::text_value_for_line(textview_curses &tc,
this->hs_token_bucket = NULL;
}
else {
bucket_group_t bg = this->hs_group_keys[grow];
std::map<bucket_group_t, bucket_array_t>::iterator group_iter;
bucket_t::iterator iter;
int bucket_index;
bucket_index = brow - 1;
this->hs_token_bucket = &(this->hs_groups[bg][bucket_index]);
group_iter = this->hs_groups.begin();
advance(group_iter, grow);
this->hs_token_bucket = &(group_iter->second[bucket_index]);
if (this->hs_label_source != NULL) {
this->hs_label_source->
hist_label_for_bucket((bg * this->hs_group_size) +
hist_label_for_bucket((group_iter->first * this->hs_group_size) +
(bucket_index * this->hs_bucket_size),
*this->hs_token_bucket,
value_out);
@ -90,29 +91,44 @@ void hist_source::text_attrs_for_line(textview_curses &tc,
{
int grow = row / (this->buckets_per_group() + 1);
int brow = row % (this->buckets_per_group() + 1);
bucket_group_t bg = this->hs_group_keys[grow];
int bucket_index = brow - 1;
assert(this->hs_analyzed);
if (this->hs_token_bucket != NULL) {
std::map<bucket_group_t, bucket_array_t>::iterator group_iter;
view_colors & vc = view_colors::singleton();
unsigned long width, avail_width;
bucket_t::iterator iter;
vis_line_t height;
struct line_range lr;
group_iter = this->hs_groups.begin();
advance(group_iter, grow);
tc.get_dimensions(height, width);
avail_width = width - this->hs_token_bucket->size();
bucket_stats_t overall_stats;
for (std::map<bucket_type_t, bucket_stats_t>::iterator stats_iter =
this->hs_bucket_stats.begin();
stats_iter != this->hs_bucket_stats.end();
++stats_iter) {
if (!this->is_bucket_graphed(stats_iter->first))
continue;
overall_stats.merge(stats_iter->second);
}
lr.lr_start = 0;
for (iter = this->hs_token_bucket->begin();
iter != this->hs_token_bucket->end();
iter++) {
double percent = (double)(iter->second - this->hs_min_count) /
(this->hs_max_count - this->hs_min_count);
double percent = (double)(iter->second - overall_stats.bs_min_count) /
overall_stats.width();
int amount, attrs;
if (!this->is_bucket_graphed(iter->first))
continue;
attrs = vc.
reverse_attrs_for_role(this->get_role_for_type(iter->first));
amount = (int)rint(percent * avail_width);
@ -129,11 +145,11 @@ void hist_source::text_attrs_for_line(textview_curses &tc,
lr.lr_start = lr.lr_end;
}
this->hs_label_source->hist_attrs_for_bucket((bg * this->hs_group_size) +
(bucket_index *
this->hs_bucket_size),
*this->hs_token_bucket,
value_out);
this->hs_label_source->hist_attrs_for_bucket(
(group_iter->first * this->hs_group_size) +
(bucket_index * this->hs_bucket_size),
*this->hs_token_bucket,
value_out);
}
}
@ -143,8 +159,6 @@ void hist_source::add_value(unsigned int value,
{
bucket_group_t bg;
this->hs_analyzed = false;
bg = bucket_group_t(value / this->hs_group_size);
bucket_array_t &ba = this->hs_groups[bg];
@ -157,44 +171,9 @@ void hist_source::add_value(unsigned int value,
this->hs_bucket_size][bt];
bc += amount;
}
void hist_source::analyze(void)
{
std::map<bucket_group_t, bucket_array_t>::iterator iter;
this->hs_group_keys.clear();
this->hs_min_count = 3.40282347e+38F;
this->hs_max_count = 0.0;
for (iter = this->hs_groups.begin();
iter != this->hs_groups.end();
iter++) {
bucket_array_t::iterator ba_iter;
for (ba_iter = iter->second.begin();
ba_iter != iter->second.end();
ba_iter++) {
bucket_count_t total = 0.0;
bucket_t::iterator b_iter;
for (b_iter = ba_iter->begin();
b_iter != ba_iter->end();
b_iter++) {
if (b_iter->second != 0.0 &&
b_iter->second < this->hs_min_count) {
this->hs_min_count = b_iter->second;
}
total += b_iter->second;
}
if (total > this->hs_max_count) {
this->hs_max_count = total;
}
}
this->hs_group_keys.push_back(iter->first);
}
sort(this->hs_group_keys.begin(), this->hs_group_keys.end());
bucket_stats_t &stats = this->hs_bucket_stats[bt];
this->hs_analyzed = true;
stats.bs_max_count = max(stats.bs_max_count, bc);
stats.bs_min_count = min(stats.bs_min_count, bc);
}

@ -32,7 +32,10 @@
#ifndef __hist_source_hh
#define __hist_source_hh
#include <math.h>
#include <map>
#include <limits>
#include <string>
#include <vector>
@ -118,7 +121,10 @@ public:
return this->hs_group_size / this->hs_bucket_size;
};
void clear(void) { this->hs_groups.clear(); };
void clear(void) {
this->hs_groups.clear();
this->hs_bucket_stats.clear();
};
size_t text_line_count()
{
@ -148,8 +154,13 @@ public:
int brow = row % (this->buckets_per_group() + 1);
int retval = 0;
if (!this->hs_group_keys.empty()) {
bucket_group_t bg = this->hs_group_keys[grow];
if (!this->hs_groups.empty()) {
std::map<bucket_group_t, bucket_array_t>::const_iterator iter;
iter = this->hs_groups.begin();
std::advance(iter, grow);
bucket_group_t bg = iter->first;
if (brow > 0) {
brow -= 1;
@ -165,15 +176,13 @@ public:
{
vis_line_t retval;
if (!this->hs_group_keys.empty()) {
if (!this->hs_groups.empty()) {
bucket_group_t bg(value / this->hs_group_size);
std::vector<bucket_group_t>::iterator lb;
std::map<bucket_group_t, bucket_array_t>::iterator lb;
lb = lower_bound(this->hs_group_keys.begin(),
this->hs_group_keys.end(),
bg);
retval = vis_line_t(distance(this->hs_group_keys.begin(), lb) *
lb = this->hs_groups.lower_bound(bg);
retval = vis_line_t(distance(this->hs_groups.begin(), lb) *
(this->buckets_per_group() + 1));
retval += vis_line_t(1 +
(value % this->hs_group_size) /
@ -198,8 +207,6 @@ public:
{
bucket_group_t bg;
this->hs_analyzed = false;
bg = bucket_group_t(value / this->hs_group_size);
bucket_array_t &ba = this->hs_groups[bg];
@ -209,24 +216,50 @@ public:
}
};
void analyze(void);
std::vector<bucket_type_t> &get_displayed_buckets() {
return this->hs_displayed_buckets;
};
bool is_bucket_graphed(bucket_type_t type) const {
return (this->hs_displayed_buckets.empty() ||
std::find(this->hs_displayed_buckets.begin(),
this->hs_displayed_buckets.end(),
type) != this->hs_displayed_buckets.end());
};
protected:
typedef std::vector<bucket_t> bucket_array_t;
struct bucket_stats_t {
bucket_stats_t() :
bs_min_count(std::numeric_limits<bucket_count_t>::max()),
bs_max_count(0)
{
};
void merge(const bucket_stats_t &rhs) {
this->bs_min_count = std::min(this->bs_min_count, rhs.bs_min_count);
this->bs_max_count += rhs.bs_max_count;
};
bucket_count_t width() const {
return fabs(this->bs_max_count - this->bs_min_count);
};
bucket_count_t bs_min_count;
bucket_count_t bs_max_count;
};
std::map<bucket_type_t, view_colors::role_t> hs_type2role;
std::map<bucket_group_t, bucket_array_t> hs_groups;
std::vector<bucket_group_t> hs_group_keys;
std::map<bucket_type_t, bucket_stats_t> hs_bucket_stats;
unsigned int hs_bucket_size; /* hours */
unsigned int hs_group_size; /* days */
bucket_count_t hs_min_count;
bucket_count_t hs_max_count;
/** Indicates that all of the data has been analyze()'d. */
bool hs_analyzed;
label_source *hs_label_source;
bucket_t *hs_token_bucket;
std::vector<bucket_type_t> hs_displayed_buckets;
};
#endif

@ -526,7 +526,6 @@ static void rebuild_hist(size_t old_count, bool force)
~logline::LEVEL__FLAGS));
}
}
hs.analyze();
hist_view.reload_data();
hist_view.set_top(hs.row_for_value(old_time));
}
@ -1650,6 +1649,60 @@ static void handle_paging_key(int ch)
}
break;
case '\t':
if (tc == &lnav_data.ld_views[LNV_DB])
{
hist_source &hs = lnav_data.ld_db_source;
db_label_source &dls = lnav_data.ld_db_rows;
std::vector<bucket_type_t> &displayed = hs.get_displayed_buckets();
std::vector<bool>::iterator start_iter, iter;
start_iter = dls.dls_headers_to_graph.begin();
if (!displayed.empty()) {
advance(start_iter, (int)displayed[0] + 1);
}
displayed.clear();
iter = find(start_iter,
dls.dls_headers_to_graph.end(),
true);
if (iter != dls.dls_headers_to_graph.end()) {
bucket_type_t type;
type = bucket_type_t(distance(dls.dls_headers_to_graph.begin(), iter));
displayed.push_back(type);
}
tc->reload_data();
}
break;
// XXX I'm sure there must be a better way to handle the difference between
// iterator and reverse_iterator.
case KEY_BTAB:
if (tc == &lnav_data.ld_views[LNV_DB])
{
hist_source &hs = lnav_data.ld_db_source;
db_label_source &dls = lnav_data.ld_db_rows;
std::vector<bucket_type_t> &displayed = hs.get_displayed_buckets();
std::vector<bool>::reverse_iterator start_iter, iter;
start_iter = dls.dls_headers_to_graph.rbegin();
if (!displayed.empty()) {
advance(start_iter, dls.dls_headers_to_graph.size() - (int)displayed[0]);
}
displayed.clear();
iter = find(start_iter,
dls.dls_headers_to_graph.rend(),
true);
if (iter != dls.dls_headers_to_graph.rend()) {
bucket_type_t type;
type = bucket_type_t(distance(dls.dls_headers_to_graph.begin(), --iter.base()));
displayed.push_back(type);
}
tc->reload_data();
}
break;
case 'x':
if (tc == &lnav_data.ld_views[LNV_LOG]) {
lnav_data.ld_log_source.toggle_user_mark(&BM_EXAMPLE,
@ -2003,6 +2056,7 @@ static void rl_callback(void *dummy, readline_curses *rc)
lnav_data.ld_bottom_source.grep_error("");
hs.clear();
hs.get_displayed_buckets().clear();
dls.clear();
retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
rc->get_value().c_str(),
@ -2050,7 +2104,6 @@ static void rl_callback(void *dummy, readline_curses *rc)
}
if (retcode == SQLITE_DONE) {
hs.analyze();
lnav_data.ld_views[LNV_LOG].reload_data();
lnav_data.ld_views[LNV_DB].reload_data();
lnav_data.ld_views[LNV_DB].set_left(0);

@ -761,7 +761,6 @@ static string com_summarize(string cmdline, vector<string> &args)
}
if (retcode == SQLITE_DONE) {
hs.analyze();
lnav_data.ld_views[LNV_LOG].reload_data();
lnav_data.ld_views[LNV_DB].reload_data();
lnav_data.ld_views[LNV_DB].set_left(0);

@ -79,6 +79,9 @@ public:
};
strong_int &operator++(void) { this->value++; return *this; };
strong_int &operator--(void) { this->value--; return *this; };
bool operator==(const strong_int &rhs) const {
return this->value == rhs.value;
};
T *out(void) { return &this->value; };
private:
T value;

@ -40,7 +40,6 @@ int main(int argc, char *argv[])
hist_source hs;
assert(hs.text_line_count() == 0);
hs.analyze();
hs.add_value(1, bucket_type_t(1));
assert(hs.text_line_count() == 101);
@ -49,4 +48,4 @@ int main(int argc, char *argv[])
assert(hs.text_line_count() == 101);
return retval;
}
}

Loading…
Cancel
Save