sharkd: add support for io graph.

Change-Id: I8d23a2b55024e2ef8c644dcef9176c7e3050a703
Reviewed-on: https://code.wireshark.org/review/27376
Petri-Dish: Jakub Zawadzki <darkjames-ws@darkjames.pl>
Tested-by: Petri Dish Buildbot
Reviewed-by: Jakub Zawadzki <darkjames-ws@darkjames.pl>
Reviewed-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
Jakub Zawadzki 2018-05-06 18:15:58 +02:00 committed by Gerald Combs
parent f6679c75f7
commit de447c1544
4 changed files with 377 additions and 146 deletions

View File

@ -34,6 +34,7 @@
#include <ui/ssl_key_export.h>
#include <ui/io_graph_item.h>
#include <epan/stats_tree_priv.h>
#include <epan/stat_tap_ui.h>
#include <epan/conversation_table.h>
@ -2897,6 +2898,215 @@ sharkd_session_process_frame_cb(epan_dissect_t *edt, proto_tree *tree, struct ep
printf("}\n");
}
#define SHARKD_IOGRAPH_MAX_ITEMS 250000 /* 250k limit of items is taken from wireshark-qt, on x86_64 sizeof(io_graph_item_t) is 152, so single graph can take max 36 MB */
struct sharkd_iograph
{
/* config */
int hf_index;
io_graph_item_unit_t calc_type;
guint32 interval;
/* result */
int space_items;
int num_items;
io_graph_item_t *items;
GString *error;
};
static gboolean
sharkd_iograph_packet(void *g, packet_info *pinfo, epan_dissect_t *edt, const void *dummy _U_)
{
struct sharkd_iograph *graph = (struct sharkd_iograph *) g;
int idx;
idx = get_io_graph_index(pinfo, graph->interval);
if (idx < 0 || idx >= SHARKD_IOGRAPH_MAX_ITEMS)
return FALSE;
if (idx + 1 > graph->num_items)
{
if (idx + 1 > graph->space_items)
{
size_t new_size = idx + 1024;
graph->items = (io_graph_item_t *) g_realloc(graph->items, sizeof(io_graph_item_t) * new_size);
reset_io_graph_items(&graph->items[graph->space_items], new_size - graph->space_items);
graph->space_items = new_size;
}
else if (graph->items == NULL)
{
graph->items = (io_graph_item_t *) g_malloc(sizeof(io_graph_item_t) * graph->space_items);
reset_io_graph_items(graph->items, graph->space_items);
}
graph->num_items = idx + 1;
}
return update_io_graph_item(graph->items, idx, pinfo, edt, graph->hf_index, graph->calc_type, graph->interval);
}
/**
* sharkd_session_process_iograph()
*
* Process iograph request
*
* Input:
* (o) interval - interval time in ms, if not specified: 1000ms
* (m) graph0 - First graph request
* (o) graph1...graph9 - Other graph requests
* (o) filter0 - First graph filter
* (o) filter1...filter9 - Other graph filters
*
* Graph requests can be one of: "packets", "bytes", "bits", "sum:<field>", "frames:<field>", "max:<field>", "min:<field>", "avg:<field>", "load:<field>",
* if you use variant with <field>, you need to pass field name in filter request.
*
* Output object with attributes:
* (m) iograph - array of graph results with attributes:
* errmsg - graph cannot be constructed
* items - graph values, zeros are skipped, if value is not a number it's next index encoded as hex string
*/
static void
sharkd_session_process_iograph(char *buf, const jsmntok_t *tokens, int count)
{
const char *tok_interval = json_find_attr(buf, tokens, count, "interval");
struct sharkd_iograph graphs[10];
gboolean is_any_ok = FALSE;
int graph_count;
guint32 interval_ms = 1000; /* default: one per second */
int i;
if (tok_interval)
{
if (!ws_strtou32(tok_interval, NULL, &interval_ms) || interval_ms == 0)
{
fprintf(stderr, "Invalid interval parameter: %s.\n", tok_interval);
return;
}
}
for (i = graph_count = 0; i < (int) G_N_ELEMENTS(graphs); i++)
{
struct sharkd_iograph *graph = &graphs[graph_count];
const char *tok_graph;
const char *tok_filter;
char tok_format_buf[32];
const char *field_name;
snprintf(tok_format_buf, sizeof(tok_format_buf), "graph%d", i);
tok_graph = json_find_attr(buf, tokens, count, tok_format_buf);
if (!tok_graph)
break;
snprintf(tok_format_buf, sizeof(tok_format_buf), "filter%d", i);
tok_filter = json_find_attr(buf, tokens, count, tok_format_buf);
if (!strcmp(tok_graph, "packets"))
graph->calc_type = IOG_ITEM_UNIT_PACKETS;
else if (!strcmp(tok_graph, "bytes"))
graph->calc_type = IOG_ITEM_UNIT_BYTES;
else if (!strcmp(tok_graph, "bits"))
graph->calc_type = IOG_ITEM_UNIT_BITS;
else if (g_str_has_prefix(tok_graph, "sum:"))
graph->calc_type = IOG_ITEM_UNIT_CALC_SUM;
else if (g_str_has_prefix(tok_graph, "frames:"))
graph->calc_type = IOG_ITEM_UNIT_CALC_FRAMES;
else if (g_str_has_prefix(tok_graph, "fields:"))
graph->calc_type = IOG_ITEM_UNIT_CALC_FIELDS;
else if (g_str_has_prefix(tok_graph, "max:"))
graph->calc_type = IOG_ITEM_UNIT_CALC_MAX;
else if (g_str_has_prefix(tok_graph, "min:"))
graph->calc_type = IOG_ITEM_UNIT_CALC_MIN;
else if (g_str_has_prefix(tok_graph, "avg:"))
graph->calc_type = IOG_ITEM_UNIT_CALC_AVERAGE;
else if (g_str_has_prefix(tok_graph, "load:"))
graph->calc_type = IOG_ITEM_UNIT_CALC_LOAD;
else
break;
field_name = strchr(tok_graph, ':');
if (field_name)
field_name = field_name + 1;
graph->interval = interval_ms;
graph->hf_index = -1;
graph->error = check_field_unit(field_name, &graph->hf_index, graph->calc_type);
graph->space_items = 0; /* TODO, can avoid realloc()s in sharkd_iograph_packet() by calculating: capture_time / interval */
graph->num_items = 0;
graph->items = NULL;
if (!graph->error)
graph->error = register_tap_listener("frame", graph, tok_filter, TL_REQUIRES_PROTO_TREE, NULL, sharkd_iograph_packet, NULL);
graph_count++;
if (graph->error == NULL)
is_any_ok = TRUE;
}
/* retap only if we have at least one ok */
if (is_any_ok)
sharkd_retap();
printf("{\"iograph\":[");
for (i = 0; i < graph_count; i++)
{
struct sharkd_iograph *graph = &graphs[i];
if (i)
printf(",");
printf("{");
if (graph->error)
{
printf("\"errmsg\":");
json_puts_string(graph->error->str);
g_string_free(graph->error, TRUE);
}
else
{
int idx;
int next_idx = 0;
const char *sepa = "";
printf("\"items\":[");
for (idx = 0; idx < graph->num_items; idx++)
{
double val;
val = get_io_graph_item(graph->items, graph->calc_type, idx, graph->hf_index, &cfile, graph->interval, graph->num_items);
/* if it's zero, don't display */
if (val == 0.0)
continue;
printf("%s", sepa);
/* cause zeros are not printed, need to output index */
if (next_idx != idx)
printf("\"%x\",", idx);
printf("%f", val);
next_idx = idx + 1;
sepa = ",";
}
printf("]");
}
printf("}");
remove_tap_listener(graph);
g_free(graph->items);
}
printf("]}\n");
}
/**
* sharkd_session_process_intervals()
*
@ -3955,6 +4165,8 @@ sharkd_session_process(char *buf, const jsmntok_t *tokens, int count)
sharkd_session_process_tap(buf, tokens, count);
else if (!strcmp(tok_req, "follow"))
sharkd_session_process_follow(buf, tokens, count);
else if (!strcmp(tok_req, "iograph"))
sharkd_session_process_iograph(buf, tokens, count);
else if (!strcmp(tok_req, "intervals"))
sharkd_session_process_intervals(buf, tokens, count);
else if (!strcmp(tok_req, "frame"))

View File

@ -125,6 +125,156 @@ GString *check_field_unit(const char *field_name, int *hf_index, io_graph_item_u
return err_str;
}
// Adapted from get_it_value in gtk/io_stat.c.
double get_io_graph_item(const io_graph_item_t *items_, io_graph_item_unit_t val_units_, int idx, int hf_index_, const capture_file *cap_file, int interval_, int cur_idx_)
{
double value = 0; /* FIXME: loss of precision, visible on the graph for small values */
int adv_type;
const io_graph_item_t *item;
guint32 interval;
item = &items_[idx];
// Basic units
switch (val_units_) {
case IOG_ITEM_UNIT_PACKETS:
return item->frames;
case IOG_ITEM_UNIT_BYTES:
return (double) item->bytes;
case IOG_ITEM_UNIT_BITS:
return (double) (item->bytes * 8);
case IOG_ITEM_UNIT_CALC_FRAMES:
return item->frames;
case IOG_ITEM_UNIT_CALC_FIELDS:
return (double) item->fields;
default:
/* If it's COUNT_TYPE_ADVANCED but not one of the
* generic ones we'll get it when we switch on the
* adv_type below. */
break;
}
if (hf_index_ < 0) {
return 0;
}
// Advanced units
adv_type = proto_registrar_get_ftype(hf_index_);
switch (adv_type) {
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_UINT64:
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
case FT_INT64:
switch (val_units_) {
case IOG_ITEM_UNIT_CALC_SUM:
value = (double) item->int_tot;
break;
case IOG_ITEM_UNIT_CALC_MAX:
value = (double) item->int_max;
break;
case IOG_ITEM_UNIT_CALC_MIN:
value = (double) item->int_min;
break;
case IOG_ITEM_UNIT_CALC_AVERAGE:
if (item->fields) {
value = (double)item->int_tot / item->fields;
} else {
value = 0;
}
break;
default:
break;
}
break;
case FT_FLOAT:
switch (val_units_) {
case IOG_ITEM_UNIT_CALC_SUM:
value = item->float_tot;
break;
case IOG_ITEM_UNIT_CALC_MAX:
value = item->float_max;
break;
case IOG_ITEM_UNIT_CALC_MIN:
value = item->float_min;
break;
case IOG_ITEM_UNIT_CALC_AVERAGE:
if (item->fields) {
value = item->float_tot / item->fields;
} else {
value = 0;
}
break;
default:
break;
}
break;
case FT_DOUBLE:
switch (val_units_) {
case IOG_ITEM_UNIT_CALC_SUM:
value = item->double_tot;
break;
case IOG_ITEM_UNIT_CALC_MAX:
value = item->double_max;
break;
case IOG_ITEM_UNIT_CALC_MIN:
value = item->double_min;
break;
case IOG_ITEM_UNIT_CALC_AVERAGE:
if (item->fields) {
value = item->double_tot / item->fields;
} else {
value = 0;
}
break;
default:
break;
}
break;
case FT_RELATIVE_TIME:
switch (val_units_) {
case IOG_ITEM_UNIT_CALC_MAX:
value = nstime_to_sec(&item->time_max);
break;
case IOG_ITEM_UNIT_CALC_MIN:
value = nstime_to_sec(&item->time_min);
break;
case IOG_ITEM_UNIT_CALC_SUM:
value = nstime_to_sec(&item->time_tot);
break;
case IOG_ITEM_UNIT_CALC_AVERAGE:
if (item->fields) {
value = nstime_to_sec(&item->time_tot) / item->fields;
} else {
value = 0;
}
break;
case IOG_ITEM_UNIT_CALC_LOAD:
// "LOAD graphs plot the QUEUE-depth of the connection over time"
// (for response time fields such as smb.time, rpc.time, etc.)
// This interval is expressed in milliseconds.
if (idx == cur_idx_ && cap_file) {
interval = (guint32)(nstime_to_msec(&cap_file->elapsed_time) + 0.5);
interval -= (interval_ * idx);
} else {
interval = interval_;
}
value = nstime_to_msec(&item->time_tot) / interval;
break;
default:
break;
}
break;
default:
break;
}
return value;
}
/*
* Editor modelines
*

View File

@ -17,6 +17,8 @@
extern "C" {
#endif /* __cplusplus */
#include "cfile.h"
typedef enum {
IOG_ITEM_UNIT_FIRST,
IOG_ITEM_UNIT_PACKETS = IOG_ITEM_UNIT_FIRST,
@ -112,6 +114,18 @@ int get_io_graph_index(packet_info *pinfo, int interval);
*/
GString *check_field_unit(const char *field_name, int *hf_index, io_graph_item_unit_t item_unit);
/** Get the value at the given interval (idx) for the current value unit.
*
* @param items [in] Array containing the item to get.
* @param val_units [in] The type of unit to calculate. From IOG_ITEM_UNITS.
* @param idx [in] Index of the item to get.
* @param hf_index [in] Header field index for advanced statistics.
* @param cap_file [in] Capture file.
* @param interval [in] Timing interval in ms.
* @param cur_idx [in] Current index.
*/
double get_io_graph_item(const io_graph_item_t *items, io_graph_item_unit_t val_units, int idx, int hf_index, const capture_file *cap_file, int interval, int cur_idx);
/** Update the values of an io_graph_item_t.
*
* Frame and byte counts are always calculated. If edt is non-NULL advanced

View File

@ -1983,156 +1983,11 @@ void IOGraph::setInterval(int interval)
}
// Get the value at the given interval (idx) for the current value unit.
// Adapted from get_it_value in gtk/io_stat.c.
double IOGraph::getItemValue(int idx, const capture_file *cap_file) const
{
double value = 0; /* FIXME: loss of precision, visible on the graph for small values */
int adv_type;
const io_graph_item_t *item;
guint32 interval;
g_assert(idx < max_io_items_);
item = &items_[idx];
// Basic units
switch (val_units_) {
case IOG_ITEM_UNIT_PACKETS:
return item->frames;
case IOG_ITEM_UNIT_BYTES:
return item->bytes;
case IOG_ITEM_UNIT_BITS:
return (item->bytes * 8);
case IOG_ITEM_UNIT_CALC_FRAMES:
return item->frames;
case IOG_ITEM_UNIT_CALC_FIELDS:
return item->fields;
default:
/* If it's COUNT_TYPE_ADVANCED but not one of the
* generic ones we'll get it when we switch on the
* adv_type below. */
break;
}
if (hf_index_ < 0) {
return 0;
}
// Advanced units
adv_type = proto_registrar_get_ftype(hf_index_);
switch (adv_type) {
case FT_UINT8:
case FT_UINT16:
case FT_UINT24:
case FT_UINT32:
case FT_UINT64:
case FT_INT8:
case FT_INT16:
case FT_INT24:
case FT_INT32:
case FT_INT64:
switch (val_units_) {
case IOG_ITEM_UNIT_CALC_SUM:
value = item->int_tot;
break;
case IOG_ITEM_UNIT_CALC_MAX:
value = item->int_max;
break;
case IOG_ITEM_UNIT_CALC_MIN:
value = item->int_min;
break;
case IOG_ITEM_UNIT_CALC_AVERAGE:
if (item->fields) {
value = (double)item->int_tot / item->fields;
} else {
value = 0;
}
break;
default:
break;
}
break;
case FT_FLOAT:
switch (val_units_) {
case IOG_ITEM_UNIT_CALC_SUM:
value = item->float_tot;
break;
case IOG_ITEM_UNIT_CALC_MAX:
value = item->float_max;
break;
case IOG_ITEM_UNIT_CALC_MIN:
value = item->float_min;
break;
case IOG_ITEM_UNIT_CALC_AVERAGE:
if (item->fields) {
value = item->float_tot / item->fields;
} else {
value = 0;
}
break;
default:
break;
}
break;
case FT_DOUBLE:
switch (val_units_) {
case IOG_ITEM_UNIT_CALC_SUM:
value = item->double_tot;
break;
case IOG_ITEM_UNIT_CALC_MAX:
value = item->double_max;
break;
case IOG_ITEM_UNIT_CALC_MIN:
value = item->double_min;
break;
case IOG_ITEM_UNIT_CALC_AVERAGE:
if (item->fields) {
value = item->double_tot / item->fields;
} else {
value = 0;
}
break;
default:
break;
}
break;
case FT_RELATIVE_TIME:
switch (val_units_) {
case IOG_ITEM_UNIT_CALC_MAX:
value = nstime_to_sec(&item->time_max);
break;
case IOG_ITEM_UNIT_CALC_MIN:
value = nstime_to_sec(&item->time_min);
break;
case IOG_ITEM_UNIT_CALC_SUM:
value = nstime_to_sec(&item->time_tot);
break;
case IOG_ITEM_UNIT_CALC_AVERAGE:
if (item->fields) {
value = nstime_to_sec(&item->time_tot) / item->fields;
} else {
value = 0;
}
break;
case IOG_ITEM_UNIT_CALC_LOAD:
// "LOAD graphs plot the QUEUE-depth of the connection over time"
// (for response time fields such as smb.time, rpc.time, etc.)
// This interval is expressed in milliseconds.
if (idx == cur_idx_ && cap_file) {
interval = (guint32)(nstime_to_msec(&cap_file->elapsed_time) + 0.5);
interval -= (interval_ * idx);
} else {
interval = interval_;
}
value = nstime_to_msec(&item->time_tot) / interval;
break;
default:
break;
}
break;
default:
break;
}
return value;
return get_io_graph_item(items_, val_units_, idx, hf_index_, cap_file, interval_, cur_idx_);
}
// "tap_reset" callback for register_tap_listener