2013-08-27 18:13:20 +00:00
|
|
|
/* tcp_stream_dialog.cpp
|
|
|
|
*
|
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
2018-04-30 07:47:58 +00:00
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
*/
|
2013-08-27 18:13:20 +00:00
|
|
|
|
|
|
|
#include "tcp_stream_dialog.h"
|
2015-06-25 16:17:03 +00:00
|
|
|
#include <ui_tcp_stream_dialog.h>
|
2013-08-27 18:13:20 +00:00
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
#include <algorithm> // for std::sort
|
|
|
|
#include <utility> // for std::pair
|
|
|
|
#include <vector>
|
|
|
|
|
2013-08-30 21:15:24 +00:00
|
|
|
#include "epan/to_str.h"
|
|
|
|
|
2013-09-16 17:28:42 +00:00
|
|
|
#include "wsutil/str_util.h"
|
|
|
|
|
2015-10-04 17:10:29 +00:00
|
|
|
#include <wsutil/utf8_entities.h>
|
2013-08-30 21:15:24 +00:00
|
|
|
|
2017-07-25 14:15:18 +00:00
|
|
|
#include <ui/qt/utils/tango_colors.h>
|
|
|
|
#include <ui/qt/utils/qt_ui_utils.h>
|
2015-08-24 19:33:49 +00:00
|
|
|
#include "progress_frame.h"
|
2022-02-01 03:30:09 +00:00
|
|
|
#include "main_application.h"
|
2018-05-16 00:02:26 +00:00
|
|
|
#include "ui/qt/widgets/wireshark_file_dialog.h"
|
2013-08-27 18:13:20 +00:00
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
#include <QCursor>
|
2013-08-30 23:01:03 +00:00
|
|
|
#include <QDir>
|
2013-09-09 01:40:06 +00:00
|
|
|
#include <QIcon>
|
2013-08-30 23:01:03 +00:00
|
|
|
#include <QPushButton>
|
|
|
|
|
2013-08-27 18:13:20 +00:00
|
|
|
#include <QDebug>
|
|
|
|
|
2013-09-16 17:28:42 +00:00
|
|
|
// To do:
|
2018-03-21 22:03:53 +00:00
|
|
|
// - Make the Help button work.
|
2013-09-16 17:28:42 +00:00
|
|
|
// - Show a message or disable the graph if we don't have any data.
|
|
|
|
// - Add a bytes in flight graph
|
|
|
|
// - Make the crosshairs tracer a vertical band?
|
|
|
|
// - Implement File->Copy
|
|
|
|
// - Add UDP graphs
|
|
|
|
// - Make the first throughput MA period a dotted/dashed line?
|
|
|
|
// - Add range scroll bars?
|
|
|
|
// - ACK & RWIN segment ticks in tcptrace graph
|
|
|
|
// - Add missing elements (retrans, URG, SACK, etc) to tcptrace. It probably makes
|
|
|
|
// sense to subclass QCPGraph for this.
|
|
|
|
|
2013-09-06 19:07:57 +00:00
|
|
|
// The GTK+ version computes a 20 (or 21!) segment moving average. Comment
|
|
|
|
// out the line below to use that. By default we use a 1 second MA.
|
|
|
|
#define MA_1_SECOND
|
|
|
|
|
|
|
|
#ifndef MA_1_SECOND
|
2013-09-04 23:15:59 +00:00
|
|
|
const int moving_avg_period_ = 20;
|
2013-09-06 19:07:57 +00:00
|
|
|
#endif
|
Add a Qt I/O Graph dialog.
For each graph you can set:
- Its visibility
- A name
- A display filter
- Color, from a fixed list
- Plot style: Line, Impulse, Bar, Stacked Bar, Dot, Square, Diamond
- Basic Y Axes (packets/s, bytes/s, bits/s)
- Computed Y Axes (SUM, MIN, AVG, MAX)
- Smoothing
You can pan and zoom using the mouse and keyboard. Clicking on a graph
selects the last packet for that interval. If all graphs have the same Y
axis a single label is shown, otherwise a legend is shown.
The time scale (X axis) can be toggled between relative seconds and the
time of day.
Graphs can be saved as PDF, PNG, BMP, and JPEG. Settings are "sticky"
via the io_graphs UAT.
To do:
- Minimize graph drawing delays.
- Figure out why smoothing differs from GTK+
- Everything else at the top of io_graph_dialog.cpp
- Fix empty resets.
A fair amount of code was copied from TCPStreamDialog. We might want to
subclass QCustomPlot and place the shared code there.
Move common syntax checking to SyntaxLineEdit.
Move some common code from ui/gtk/io_stat.c to ui/io_graph_item.[ch] and
use it in both GTK+ and Qt.
Make the io_graph_item_t array allocation in io_stat.c static. The
behavior should be identical and this gives us additional compile-time
checks.
Change-Id: I9a3d544469b7048f0761fdbf7bcf20f44ae76577
Reviewed-on: https://code.wireshark.org/review/435
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Tested-by: Gerald Combs <gerald@wireshark.org>
2014-02-12 00:07:10 +00:00
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
const QRgb graph_color_1 = tango_sky_blue_5;
|
|
|
|
const QRgb graph_color_2 = tango_butter_6;
|
2013-09-13 22:29:05 +00:00
|
|
|
const QRgb graph_color_3 = tango_chameleon_5;
|
2016-12-22 21:03:03 +00:00
|
|
|
const QRgb graph_color_4 = tango_scarlet_red_4;
|
|
|
|
const QRgb graph_color_5 = tango_scarlet_red_6;
|
2013-09-16 00:50:23 +00:00
|
|
|
|
|
|
|
// Size of selectable packet points in the base graph
|
|
|
|
const double pkt_point_size_ = 3.0;
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
// Don't accidentally zoom into a 1x1 rect if you happen to click on the graph
|
|
|
|
// in zoom mode.
|
|
|
|
const int min_zoom_pixels_ = 20;
|
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
const QString average_throughput_label_ = QObject::tr("Average Throughput (bits/s)");
|
2013-09-08 01:25:27 +00:00
|
|
|
const QString round_trip_time_ms_label_ = QObject::tr("Round Trip Time (ms)");
|
|
|
|
const QString segment_length_label_ = QObject::tr("Segment Length (B)");
|
2013-09-09 01:40:06 +00:00
|
|
|
const QString sequence_number_label_ = QObject::tr("Sequence Number (B)");
|
2013-09-08 01:25:27 +00:00
|
|
|
const QString time_s_label_ = QObject::tr("Time (s)");
|
2013-09-09 01:40:06 +00:00
|
|
|
const QString window_size_label_ = QObject::tr("Window Size (B)");
|
2013-09-08 01:25:27 +00:00
|
|
|
|
2020-03-14 14:08:52 +00:00
|
|
|
QCPErrorBarsNotSelectable::QCPErrorBarsNotSelectable(QCPAxis *keyAxis, QCPAxis *valueAxis) :
|
|
|
|
QCPErrorBars(keyAxis, valueAxis)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QCPErrorBarsNotSelectable::~QCPErrorBarsNotSelectable()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
double QCPErrorBarsNotSelectable::selectTest(const QPointF &pos, bool onlySelectable, QVariant *details) const
|
|
|
|
{
|
|
|
|
Q_UNUSED(pos);
|
|
|
|
Q_UNUSED(onlySelectable);
|
|
|
|
Q_UNUSED(details);
|
|
|
|
return -1.0;
|
|
|
|
}
|
|
|
|
|
2015-08-24 19:33:49 +00:00
|
|
|
TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_type graph_type) :
|
2018-03-21 22:03:53 +00:00
|
|
|
GeometryStateDialog(parent),
|
2013-08-27 18:13:20 +00:00
|
|
|
ui(new Ui::TCPStreamDialog),
|
2013-08-30 21:15:24 +00:00
|
|
|
cap_file_(cf),
|
2016-07-05 21:08:30 +00:00
|
|
|
ts_offset_(0),
|
2013-09-09 01:40:06 +00:00
|
|
|
ts_origin_conn_(true),
|
2016-07-05 21:08:30 +00:00
|
|
|
seq_offset_(0),
|
2013-09-09 01:40:06 +00:00
|
|
|
seq_origin_zero_(true),
|
2020-01-10 00:32:26 +00:00
|
|
|
title_(nullptr),
|
|
|
|
base_graph_(nullptr),
|
|
|
|
tput_graph_(nullptr),
|
|
|
|
goodput_graph_(nullptr),
|
|
|
|
seg_graph_(nullptr),
|
|
|
|
seg_eb_(nullptr),
|
|
|
|
ack_graph_(nullptr),
|
|
|
|
sack_graph_(nullptr),
|
|
|
|
sack_eb_(nullptr),
|
|
|
|
sack2_graph_(nullptr),
|
|
|
|
sack2_eb_(nullptr),
|
|
|
|
rwin_graph_(nullptr),
|
|
|
|
dup_ack_graph_(nullptr),
|
|
|
|
zero_win_graph_(nullptr),
|
|
|
|
tracer_(nullptr),
|
2016-07-05 21:08:30 +00:00
|
|
|
packet_num_(0),
|
2013-09-09 01:40:06 +00:00
|
|
|
mouse_drags_(true),
|
2020-01-10 00:32:26 +00:00
|
|
|
rubber_band_(nullptr),
|
2016-12-22 21:03:03 +00:00
|
|
|
graph_updater_(this),
|
2013-09-04 23:15:59 +00:00
|
|
|
num_dsegs_(-1),
|
|
|
|
num_acks_(-1),
|
2016-12-06 07:50:45 +00:00
|
|
|
num_sack_ranges_(-1),
|
|
|
|
ma_window_size_(1.0)
|
2013-08-27 18:13:20 +00:00
|
|
|
{
|
2013-09-15 20:44:57 +00:00
|
|
|
int graph_idx = -1;
|
2013-08-27 18:13:20 +00:00
|
|
|
|
2020-04-25 15:53:08 +00:00
|
|
|
memset(&graph_, 0, sizeof(graph_));
|
|
|
|
|
2013-08-27 18:13:20 +00:00
|
|
|
ui->setupUi(this);
|
2018-03-21 22:03:53 +00:00
|
|
|
if (parent) loadGeometry(parent->width() * 2 / 3, parent->height() * 4 / 5);
|
2016-01-20 14:13:38 +00:00
|
|
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
2013-08-27 18:13:20 +00:00
|
|
|
|
2022-09-28 19:55:14 +00:00
|
|
|
ui->streamNumberSpinBox->setStyleSheet("QSpinBox { min-width: 2em; }");
|
|
|
|
|
2020-04-13 21:00:48 +00:00
|
|
|
guint32 th_stream = select_tcpip_session(cap_file_);
|
|
|
|
if (th_stream == G_MAXUINT32) {
|
2013-08-27 18:13:20 +00:00
|
|
|
done(QDialog::Rejected);
|
2014-03-01 19:13:08 +00:00
|
|
|
return;
|
2013-08-27 18:13:20 +00:00
|
|
|
}
|
|
|
|
|
2013-09-15 20:44:57 +00:00
|
|
|
QComboBox *gtcb = ui->graphTypeComboBox;
|
|
|
|
gtcb->setUpdatesEnabled(false);
|
2013-09-16 01:19:14 +00:00
|
|
|
gtcb->addItem(ui->actionRoundTripTime->text(), GRAPH_RTT);
|
2013-09-15 20:44:57 +00:00
|
|
|
if (graph_type == GRAPH_RTT) graph_idx = gtcb->count() - 1;
|
2013-09-16 01:19:14 +00:00
|
|
|
gtcb->addItem(ui->actionThroughput->text(), GRAPH_THROUGHPUT);
|
2013-09-15 20:44:57 +00:00
|
|
|
if (graph_type == GRAPH_THROUGHPUT) graph_idx = gtcb->count() - 1;
|
2013-09-16 01:19:14 +00:00
|
|
|
gtcb->addItem(ui->actionStevens->text(), GRAPH_TSEQ_STEVENS);
|
2013-09-15 20:44:57 +00:00
|
|
|
if (graph_type == GRAPH_TSEQ_STEVENS) graph_idx = gtcb->count() - 1;
|
2013-09-16 01:19:14 +00:00
|
|
|
gtcb->addItem(ui->actionTcptrace->text(), GRAPH_TSEQ_TCPTRACE);
|
2013-09-15 20:44:57 +00:00
|
|
|
if (graph_type == GRAPH_TSEQ_TCPTRACE) graph_idx = gtcb->count() - 1;
|
2013-09-16 01:19:14 +00:00
|
|
|
gtcb->addItem(ui->actionWindowScaling->text(), GRAPH_WSCALE);
|
2013-09-15 20:44:57 +00:00
|
|
|
if (graph_type == GRAPH_WSCALE) graph_idx = gtcb->count() - 1;
|
|
|
|
gtcb->setUpdatesEnabled(true);
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-09-12 21:37:47 +00:00
|
|
|
ui->dragRadioButton->setChecked(mouse_drags_);
|
2013-09-09 01:40:06 +00:00
|
|
|
|
2013-09-12 23:46:56 +00:00
|
|
|
ctx_menu_.addAction(ui->actionZoomIn);
|
2015-10-09 05:19:21 +00:00
|
|
|
ctx_menu_.addAction(ui->actionZoomInX);
|
|
|
|
ctx_menu_.addAction(ui->actionZoomInY);
|
2013-09-12 23:46:56 +00:00
|
|
|
ctx_menu_.addAction(ui->actionZoomOut);
|
2015-10-09 05:19:21 +00:00
|
|
|
ctx_menu_.addAction(ui->actionZoomOutX);
|
|
|
|
ctx_menu_.addAction(ui->actionZoomOutY);
|
2013-09-12 23:46:56 +00:00
|
|
|
ctx_menu_.addAction(ui->actionReset);
|
|
|
|
ctx_menu_.addSeparator();
|
|
|
|
ctx_menu_.addAction(ui->actionMoveRight10);
|
|
|
|
ctx_menu_.addAction(ui->actionMoveLeft10);
|
|
|
|
ctx_menu_.addAction(ui->actionMoveUp10);
|
|
|
|
ctx_menu_.addAction(ui->actionMoveDown10);
|
|
|
|
ctx_menu_.addAction(ui->actionMoveRight1);
|
|
|
|
ctx_menu_.addAction(ui->actionMoveLeft1);
|
|
|
|
ctx_menu_.addAction(ui->actionMoveUp1);
|
|
|
|
ctx_menu_.addAction(ui->actionMoveDown1);
|
|
|
|
ctx_menu_.addSeparator();
|
|
|
|
ctx_menu_.addAction(ui->actionNextStream);
|
|
|
|
ctx_menu_.addAction(ui->actionPreviousStream);
|
|
|
|
ctx_menu_.addAction(ui->actionSwitchDirection);
|
|
|
|
ctx_menu_.addAction(ui->actionGoToPacket);
|
|
|
|
ctx_menu_.addSeparator();
|
|
|
|
ctx_menu_.addAction(ui->actionDragZoom);
|
|
|
|
ctx_menu_.addAction(ui->actionToggleSequenceNumbers);
|
|
|
|
ctx_menu_.addAction(ui->actionToggleTimeOrigin);
|
2013-09-13 22:29:05 +00:00
|
|
|
ctx_menu_.addAction(ui->actionCrosshairs);
|
|
|
|
ctx_menu_.addSeparator();
|
|
|
|
ctx_menu_.addAction(ui->actionRoundTripTime);
|
|
|
|
ctx_menu_.addAction(ui->actionThroughput);
|
|
|
|
ctx_menu_.addAction(ui->actionStevens);
|
|
|
|
ctx_menu_.addAction(ui->actionTcptrace);
|
|
|
|
ctx_menu_.addAction(ui->actionWindowScaling);
|
2020-01-12 19:31:47 +00:00
|
|
|
set_action_shortcuts_visible_in_context_menu(ctx_menu_.actions());
|
2013-09-12 23:46:56 +00:00
|
|
|
|
2013-08-27 18:13:20 +00:00
|
|
|
graph_.type = graph_type;
|
2020-04-13 21:00:48 +00:00
|
|
|
graph_.stream = th_stream;
|
2013-09-13 22:29:05 +00:00
|
|
|
findStream();
|
|
|
|
|
2016-12-06 07:50:45 +00:00
|
|
|
showWidgetsForGraphType();
|
|
|
|
|
2013-09-16 02:04:52 +00:00
|
|
|
ui->streamNumberSpinBox->blockSignals(true);
|
2013-09-13 22:29:05 +00:00
|
|
|
ui->streamNumberSpinBox->setMaximum(get_tcp_stream_count() - 1);
|
2016-03-03 20:51:26 +00:00
|
|
|
ui->streamNumberSpinBox->setValue(graph_.stream);
|
2013-09-16 02:04:52 +00:00
|
|
|
ui->streamNumberSpinBox->blockSignals(false);
|
2013-08-27 23:28:35 +00:00
|
|
|
|
2016-12-06 07:50:45 +00:00
|
|
|
#ifdef MA_1_SECOND
|
|
|
|
ui->maWindowSizeSpinBox->blockSignals(true);
|
|
|
|
ui->maWindowSizeSpinBox->setDecimals(6);
|
|
|
|
ui->maWindowSizeSpinBox->setMinimum(0.000001);
|
|
|
|
ui->maWindowSizeSpinBox->setValue(ma_window_size_);
|
|
|
|
ui->maWindowSizeSpinBox->blockSignals(false);
|
|
|
|
#endif
|
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
// set which Throughput graphs are displayed by default
|
|
|
|
ui->showSegLengthCheckBox->blockSignals(true);
|
|
|
|
ui->showSegLengthCheckBox->setChecked(true);
|
|
|
|
ui->showSegLengthCheckBox->blockSignals(false);
|
|
|
|
|
|
|
|
ui->showThroughputCheckBox->blockSignals(true);
|
|
|
|
ui->showThroughputCheckBox->setChecked(true);
|
|
|
|
ui->showThroughputCheckBox->blockSignals(false);
|
|
|
|
|
|
|
|
// set which WScale graphs are displayed by default
|
|
|
|
ui->showRcvWinCheckBox->blockSignals(true);
|
|
|
|
ui->showRcvWinCheckBox->setChecked(true);
|
|
|
|
ui->showRcvWinCheckBox->blockSignals(false);
|
|
|
|
|
|
|
|
ui->showBytesOutCheckBox->blockSignals(true);
|
|
|
|
ui->showBytesOutCheckBox->setChecked(true);
|
|
|
|
ui->showBytesOutCheckBox->blockSignals(false);
|
|
|
|
|
2013-08-30 21:15:24 +00:00
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
2016-09-25 09:57:55 +00:00
|
|
|
QCPTextElement *file_title = new QCPTextElement(sp, gchar_free_to_qstring(cf_get_display_name(cap_file_)));
|
2013-09-06 02:00:14 +00:00
|
|
|
file_title->setFont(sp->xAxis->labelFont());
|
2016-09-25 09:57:55 +00:00
|
|
|
title_ = new QCPTextElement(sp);
|
2013-08-30 23:01:03 +00:00
|
|
|
sp->plotLayout()->insertRow(0);
|
2013-09-06 02:00:14 +00:00
|
|
|
sp->plotLayout()->addElement(0, 0, file_title);
|
|
|
|
sp->plotLayout()->insertRow(0);
|
2013-09-04 23:15:59 +00:00
|
|
|
sp->plotLayout()->addElement(0, 0, title_);
|
2013-09-13 22:29:05 +00:00
|
|
|
|
2018-03-21 22:03:53 +00:00
|
|
|
qreal pen_width = 0.5;
|
2016-12-22 21:03:03 +00:00
|
|
|
// Base Graph - enables selecting segments (both data and SACKs)
|
|
|
|
base_graph_ = sp->addGraph();
|
2018-03-21 22:03:53 +00:00
|
|
|
base_graph_->setPen(QPen(QBrush(graph_color_1), pen_width));
|
2020-01-10 00:32:26 +00:00
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
// Throughput Graph - rate of sent bytes
|
2016-12-22 21:03:03 +00:00
|
|
|
tput_graph_ = sp->addGraph(sp->xAxis, sp->yAxis2);
|
2018-03-21 22:03:53 +00:00
|
|
|
tput_graph_->setPen(QPen(QBrush(graph_color_2), pen_width));
|
2016-12-06 07:50:45 +00:00
|
|
|
tput_graph_->setLineStyle(QCPGraph::lsStepLeft);
|
2020-01-10 00:32:26 +00:00
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
// Goodput Graph - rate of ACKed bytes
|
|
|
|
goodput_graph_ = sp->addGraph(sp->xAxis, sp->yAxis2);
|
2018-03-21 22:03:53 +00:00
|
|
|
goodput_graph_->setPen(QPen(QBrush(graph_color_3), pen_width));
|
2017-01-21 04:57:09 +00:00
|
|
|
goodput_graph_->setLineStyle(QCPGraph::lsStepLeft);
|
2020-01-10 00:32:26 +00:00
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
// Seg Graph - displays forward data segments on tcptrace graph
|
|
|
|
seg_graph_ = sp->addGraph();
|
2013-09-13 23:34:34 +00:00
|
|
|
seg_graph_->setLineStyle(QCPGraph::lsNone);
|
|
|
|
seg_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDot, Qt::transparent, 0));
|
2020-03-14 14:08:52 +00:00
|
|
|
seg_eb_ = new QCPErrorBarsNotSelectable(sp->xAxis, sp->yAxis);
|
2020-01-10 00:32:26 +00:00
|
|
|
seg_eb_->setErrorType(QCPErrorBars::etValueError);
|
|
|
|
seg_eb_->setPen(QPen(QBrush(graph_color_1), pen_width));
|
|
|
|
seg_eb_->setSymbolGap(0.0); // draw error spine as single line
|
|
|
|
seg_eb_->setWhiskerWidth(pkt_point_size_);
|
|
|
|
seg_eb_->removeFromLegend();
|
|
|
|
seg_eb_->setDataPlottable(seg_graph_);
|
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
// Ack Graph - displays ack numbers from reverse packets
|
|
|
|
ack_graph_ = sp->addGraph();
|
2018-03-21 22:03:53 +00:00
|
|
|
ack_graph_->setPen(QPen(QBrush(graph_color_2), pen_width));
|
2013-09-13 22:29:05 +00:00
|
|
|
ack_graph_->setLineStyle(QCPGraph::lsStepLeft);
|
2020-01-10 00:32:26 +00:00
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
// Sack Graph - displays highest number (most recent) SACK block
|
|
|
|
sack_graph_ = sp->addGraph();
|
|
|
|
sack_graph_->setLineStyle(QCPGraph::lsNone);
|
2020-01-10 00:32:26 +00:00
|
|
|
sack_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDot, Qt::transparent, 0));
|
2020-03-14 14:08:52 +00:00
|
|
|
sack_eb_ = new QCPErrorBarsNotSelectable(sp->xAxis, sp->yAxis);
|
2020-01-10 00:32:26 +00:00
|
|
|
sack_eb_->setErrorType(QCPErrorBars::etValueError);
|
|
|
|
sack_eb_->setPen(QPen(QBrush(graph_color_4), pen_width));
|
|
|
|
sack_eb_->setSymbolGap(0.0); // draw error spine as single line
|
|
|
|
sack_eb_->setWhiskerWidth(0.0);
|
|
|
|
sack_eb_->removeFromLegend();
|
|
|
|
sack_eb_->setDataPlottable(sack_graph_);
|
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
// Sack Graph 2 - displays subsequent SACK blocks
|
|
|
|
sack2_graph_ = sp->addGraph();
|
|
|
|
sack2_graph_->setLineStyle(QCPGraph::lsNone);
|
|
|
|
sack2_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDot, Qt::transparent, 0));
|
2020-03-14 14:08:52 +00:00
|
|
|
sack2_eb_ = new QCPErrorBarsNotSelectable(sp->xAxis, sp->yAxis);
|
2020-01-10 00:32:26 +00:00
|
|
|
sack2_eb_->setErrorType(QCPErrorBars::etValueError);
|
|
|
|
sack2_eb_->setPen(QPen(QBrush(graph_color_5), pen_width));
|
|
|
|
sack2_eb_->setSymbolGap(0.0); // draw error spine as single line
|
|
|
|
sack2_eb_->setWhiskerWidth(0.0);
|
|
|
|
sack2_eb_->removeFromLegend();
|
|
|
|
sack2_eb_->setDataPlottable(sack2_graph_);
|
|
|
|
|
|
|
|
// RWin graph - displays upper extent of RWIN advertised on reverse packets
|
2016-12-22 21:03:03 +00:00
|
|
|
rwin_graph_ = sp->addGraph();
|
2018-03-21 22:03:53 +00:00
|
|
|
rwin_graph_->setPen(QPen(QBrush(graph_color_3), pen_width));
|
2013-09-13 22:29:05 +00:00
|
|
|
rwin_graph_->setLineStyle(QCPGraph::lsStepLeft);
|
2020-01-10 00:32:26 +00:00
|
|
|
|
2018-03-21 22:03:53 +00:00
|
|
|
// Duplicate ACK Graph - displays duplicate ack ticks
|
|
|
|
// QCustomPlot doesn't have QCPScatterStyle::ssTick so we have to make our own.
|
|
|
|
int tick_len = 3;
|
|
|
|
tick_len *= devicePixelRatio();
|
|
|
|
QPixmap da_tick_pm = QPixmap(1, tick_len * 2);
|
|
|
|
da_tick_pm.fill(Qt::transparent);
|
|
|
|
QPainter painter(&da_tick_pm);
|
|
|
|
QPen da_tick_pen;
|
|
|
|
da_tick_pen.setColor(graph_color_2);
|
|
|
|
da_tick_pen.setWidthF(pen_width);
|
|
|
|
painter.setPen(da_tick_pen);
|
|
|
|
painter.drawLine(0, tick_len, 0, tick_len * 2);
|
|
|
|
dup_ack_graph_ = sp->addGraph();
|
|
|
|
dup_ack_graph_->setLineStyle(QCPGraph::lsNone);
|
|
|
|
QCPScatterStyle da_ss = QCPScatterStyle(QCPScatterStyle::ssPixmap, graph_color_2, 0);
|
|
|
|
da_ss.setPixmap(da_tick_pm);
|
|
|
|
dup_ack_graph_->setScatterStyle(da_ss);
|
2020-01-10 00:32:26 +00:00
|
|
|
|
2018-03-21 22:03:53 +00:00
|
|
|
// Zero Window Graph - displays zero window crosses (x)
|
|
|
|
zero_win_graph_ = sp->addGraph();
|
|
|
|
zero_win_graph_->setLineStyle(QCPGraph::lsNone);
|
|
|
|
zero_win_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssCross, graph_color_1, 5));
|
2013-09-13 22:29:05 +00:00
|
|
|
|
|
|
|
tracer_ = new QCPItemTracer(sp);
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2017-01-18 04:30:26 +00:00
|
|
|
// Triggers fillGraph() [ UNLESS the index is already graph_idx!! ]
|
|
|
|
if (graph_idx != ui->graphTypeComboBox->currentIndex())
|
|
|
|
// changing the current index will call fillGraph
|
|
|
|
ui->graphTypeComboBox->setCurrentIndex(graph_idx);
|
|
|
|
else
|
|
|
|
// the current index is what we want - so fillGraph() manually
|
|
|
|
fillGraph();
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-08-30 21:15:24 +00:00
|
|
|
sp->setMouseTracking(true);
|
2013-08-27 18:13:20 +00:00
|
|
|
|
2013-09-06 02:00:14 +00:00
|
|
|
sp->yAxis->setLabelColor(QColor(graph_color_1));
|
2013-09-04 23:15:59 +00:00
|
|
|
sp->yAxis->setTickLabelColor(QColor(graph_color_1));
|
2013-08-30 21:15:24 +00:00
|
|
|
|
|
|
|
tracer_->setVisible(false);
|
|
|
|
toggleTracerStyle(true);
|
|
|
|
|
2013-08-30 23:01:03 +00:00
|
|
|
QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save);
|
2020-09-29 00:40:05 +00:00
|
|
|
save_bt->setText(tr("Save As…"));
|
2013-08-30 23:01:03 +00:00
|
|
|
|
2016-09-13 11:37:11 +00:00
|
|
|
QPushButton *close_bt = ui->buttonBox->button(QDialogButtonBox::Close);
|
|
|
|
if (close_bt) {
|
|
|
|
close_bt->setDefault(true);
|
|
|
|
}
|
|
|
|
|
2015-08-24 19:33:49 +00:00
|
|
|
ProgressFrame::addToButtonBox(ui->buttonBox, parent);
|
|
|
|
|
2013-08-30 21:15:24 +00:00
|
|
|
connect(sp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*)));
|
|
|
|
connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*)));
|
2013-09-09 01:40:06 +00:00
|
|
|
connect(sp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*)));
|
|
|
|
connect(sp, SIGNAL(axisClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)),
|
|
|
|
this, SLOT(axisClicked(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)));
|
2013-09-06 00:41:07 +00:00
|
|
|
connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(transformYRange(QCPRange)));
|
2013-08-30 23:01:03 +00:00
|
|
|
disconnect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
2016-01-20 14:15:23 +00:00
|
|
|
this->setResult(QDialog::Accepted);
|
2013-08-27 18:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TCPStreamDialog::~TCPStreamDialog()
|
|
|
|
{
|
2020-04-13 21:00:48 +00:00
|
|
|
graph_segment_list_free(&graph_);
|
|
|
|
|
2013-08-27 18:13:20 +00:00
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
2015-07-07 06:24:50 +00:00
|
|
|
void TCPStreamDialog::showEvent(QShowEvent *)
|
2013-09-06 00:41:07 +00:00
|
|
|
{
|
|
|
|
resetAxes();
|
|
|
|
}
|
|
|
|
|
2013-08-27 23:28:35 +00:00
|
|
|
void TCPStreamDialog::keyPressEvent(QKeyEvent *event)
|
|
|
|
{
|
2013-09-12 23:46:56 +00:00
|
|
|
int pan_pixels = event->modifiers() & Qt::ShiftModifier ? 1 : 10;
|
2013-08-27 23:28:35 +00:00
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
QWidget* focusWidget = QApplication::focusWidget();
|
|
|
|
|
|
|
|
// Block propagation of "Enter" key when focus is not default (e.g. SpinBox)
|
|
|
|
// [ Note that if focus was on, e.g. Close button, event would never reach
|
|
|
|
// here ]
|
2016-12-06 07:50:45 +00:00
|
|
|
if ((event->key() == Qt::Key_Enter || event->key() == Qt::Key_Return) &&
|
2016-12-22 21:03:03 +00:00
|
|
|
focusWidget !=NULL && focusWidget != ui->streamPlot) {
|
2016-12-06 07:50:45 +00:00
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
// reset focus to default, and accept event
|
|
|
|
ui->streamPlot->setFocus();
|
2016-12-06 07:50:45 +00:00
|
|
|
event->accept();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-27 23:28:35 +00:00
|
|
|
// XXX - This differs from the main window but matches other applications (e.g. Mozilla and Safari)
|
|
|
|
switch(event->key()) {
|
|
|
|
case Qt::Key_Minus:
|
|
|
|
case Qt::Key_Underscore: // Shifted minus on U.S. keyboards
|
|
|
|
case Qt::Key_O: // GTK+
|
2013-09-12 23:46:56 +00:00
|
|
|
zoomAxes(false);
|
2013-08-27 23:28:35 +00:00
|
|
|
break;
|
|
|
|
case Qt::Key_Plus:
|
|
|
|
case Qt::Key_Equal: // Unshifted plus on U.S. keyboards
|
|
|
|
case Qt::Key_I: // GTK+
|
2013-09-12 23:46:56 +00:00
|
|
|
zoomAxes(true);
|
2013-08-27 23:28:35 +00:00
|
|
|
break;
|
2015-10-09 05:19:21 +00:00
|
|
|
case Qt::Key_X: // Zoom X axis only
|
2019-11-17 19:02:20 +00:00
|
|
|
if (event->modifiers() & Qt::ShiftModifier) {
|
2015-10-09 05:19:21 +00:00
|
|
|
zoomXAxis(false); // upper case X -> Zoom out
|
|
|
|
} else {
|
|
|
|
zoomXAxis(true); // lower case x -> Zoom in
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case Qt::Key_Y: // Zoom Y axis only
|
2019-11-17 19:02:20 +00:00
|
|
|
if (event->modifiers() & Qt::ShiftModifier) {
|
2015-10-09 05:19:21 +00:00
|
|
|
zoomYAxis(false); // upper case Y -> Zoom out
|
|
|
|
} else {
|
|
|
|
zoomYAxis(true); // lower case y -> Zoom in
|
|
|
|
}
|
|
|
|
break;
|
2013-08-27 23:28:35 +00:00
|
|
|
case Qt::Key_Right:
|
|
|
|
case Qt::Key_L:
|
2013-09-12 23:46:56 +00:00
|
|
|
panAxes(pan_pixels, 0);
|
2013-08-27 23:28:35 +00:00
|
|
|
break;
|
|
|
|
case Qt::Key_Left:
|
|
|
|
case Qt::Key_H:
|
2013-09-12 23:46:56 +00:00
|
|
|
panAxes(-1 * pan_pixels, 0);
|
2013-08-27 23:28:35 +00:00
|
|
|
break;
|
|
|
|
case Qt::Key_Up:
|
|
|
|
case Qt::Key_K:
|
Add a Qt I/O Graph dialog.
For each graph you can set:
- Its visibility
- A name
- A display filter
- Color, from a fixed list
- Plot style: Line, Impulse, Bar, Stacked Bar, Dot, Square, Diamond
- Basic Y Axes (packets/s, bytes/s, bits/s)
- Computed Y Axes (SUM, MIN, AVG, MAX)
- Smoothing
You can pan and zoom using the mouse and keyboard. Clicking on a graph
selects the last packet for that interval. If all graphs have the same Y
axis a single label is shown, otherwise a legend is shown.
The time scale (X axis) can be toggled between relative seconds and the
time of day.
Graphs can be saved as PDF, PNG, BMP, and JPEG. Settings are "sticky"
via the io_graphs UAT.
To do:
- Minimize graph drawing delays.
- Figure out why smoothing differs from GTK+
- Everything else at the top of io_graph_dialog.cpp
- Fix empty resets.
A fair amount of code was copied from TCPStreamDialog. We might want to
subclass QCustomPlot and place the shared code there.
Move common syntax checking to SyntaxLineEdit.
Move some common code from ui/gtk/io_stat.c to ui/io_graph_item.[ch] and
use it in both GTK+ and Qt.
Make the io_graph_item_t array allocation in io_stat.c static. The
behavior should be identical and this gives us additional compile-time
checks.
Change-Id: I9a3d544469b7048f0761fdbf7bcf20f44ae76577
Reviewed-on: https://code.wireshark.org/review/435
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Tested-by: Gerald Combs <gerald@wireshark.org>
2014-02-12 00:07:10 +00:00
|
|
|
panAxes(0, pan_pixels);
|
2013-08-27 23:28:35 +00:00
|
|
|
break;
|
|
|
|
case Qt::Key_Down:
|
|
|
|
case Qt::Key_J:
|
Add a Qt I/O Graph dialog.
For each graph you can set:
- Its visibility
- A name
- A display filter
- Color, from a fixed list
- Plot style: Line, Impulse, Bar, Stacked Bar, Dot, Square, Diamond
- Basic Y Axes (packets/s, bytes/s, bits/s)
- Computed Y Axes (SUM, MIN, AVG, MAX)
- Smoothing
You can pan and zoom using the mouse and keyboard. Clicking on a graph
selects the last packet for that interval. If all graphs have the same Y
axis a single label is shown, otherwise a legend is shown.
The time scale (X axis) can be toggled between relative seconds and the
time of day.
Graphs can be saved as PDF, PNG, BMP, and JPEG. Settings are "sticky"
via the io_graphs UAT.
To do:
- Minimize graph drawing delays.
- Figure out why smoothing differs from GTK+
- Everything else at the top of io_graph_dialog.cpp
- Fix empty resets.
A fair amount of code was copied from TCPStreamDialog. We might want to
subclass QCustomPlot and place the shared code there.
Move common syntax checking to SyntaxLineEdit.
Move some common code from ui/gtk/io_stat.c to ui/io_graph_item.[ch] and
use it in both GTK+ and Qt.
Make the io_graph_item_t array allocation in io_stat.c static. The
behavior should be identical and this gives us additional compile-time
checks.
Change-Id: I9a3d544469b7048f0761fdbf7bcf20f44ae76577
Reviewed-on: https://code.wireshark.org/review/435
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Tested-by: Gerald Combs <gerald@wireshark.org>
2014-02-12 00:07:10 +00:00
|
|
|
panAxes(0, -1 * pan_pixels);
|
2013-08-30 21:15:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::Key_Space:
|
|
|
|
toggleTracerStyle();
|
2013-08-27 23:28:35 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::Key_0:
|
|
|
|
case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards
|
|
|
|
case Qt::Key_R:
|
|
|
|
case Qt::Key_Home:
|
2013-09-04 23:15:59 +00:00
|
|
|
resetAxes();
|
2013-08-27 23:28:35 +00:00
|
|
|
break;
|
2013-09-09 01:40:06 +00:00
|
|
|
|
2013-09-12 21:37:47 +00:00
|
|
|
case Qt::Key_PageUp:
|
2013-09-13 22:29:05 +00:00
|
|
|
on_actionNextStream_triggered();
|
|
|
|
break;
|
|
|
|
case Qt::Key_PageDown:
|
|
|
|
on_actionPreviousStream_triggered();
|
2013-09-12 21:37:47 +00:00
|
|
|
break;
|
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
case Qt::Key_D:
|
2013-09-13 22:29:05 +00:00
|
|
|
on_actionSwitchDirection_triggered();
|
2013-09-08 01:25:27 +00:00
|
|
|
break;
|
2013-09-09 01:40:06 +00:00
|
|
|
case Qt::Key_G:
|
2013-09-12 23:46:56 +00:00
|
|
|
on_actionGoToPacket_triggered();
|
2013-09-09 01:40:06 +00:00
|
|
|
break;
|
|
|
|
case Qt::Key_S:
|
2013-09-13 22:29:05 +00:00
|
|
|
on_actionToggleSequenceNumbers_triggered();
|
2013-09-09 01:40:06 +00:00
|
|
|
break;
|
|
|
|
case Qt::Key_T:
|
2013-09-12 23:46:56 +00:00
|
|
|
on_actionToggleTimeOrigin_triggered();
|
2013-09-09 01:40:06 +00:00
|
|
|
break;
|
|
|
|
case Qt::Key_Z:
|
2013-09-12 23:46:56 +00:00
|
|
|
on_actionDragZoom_triggered();
|
2013-09-09 01:40:06 +00:00
|
|
|
break;
|
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
case Qt::Key_1:
|
|
|
|
on_actionRoundTripTime_triggered();
|
|
|
|
break;
|
|
|
|
case Qt::Key_2:
|
|
|
|
on_actionThroughput_triggered();
|
|
|
|
break;
|
|
|
|
case Qt::Key_3:
|
|
|
|
on_actionStevens_triggered();
|
|
|
|
break;
|
|
|
|
case Qt::Key_4:
|
|
|
|
on_actionTcptrace_triggered();
|
|
|
|
break;
|
|
|
|
case Qt::Key_5:
|
|
|
|
on_actionWindowScaling_triggered();
|
|
|
|
break;
|
2013-09-09 01:40:06 +00:00
|
|
|
// Alas, there is no Blade Runner-style Qt::Key_Enhance
|
2013-08-27 23:28:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QDialog::keyPressEvent(event);
|
2013-09-09 01:40:06 +00:00
|
|
|
}
|
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
void TCPStreamDialog::mousePressEvent(QMouseEvent *event)
|
|
|
|
{
|
|
|
|
// if no-one else wants the event, then this is a click on blank space.
|
|
|
|
// Use this opportunity to set focus back to default, and accept event.
|
|
|
|
ui->streamPlot->setFocus();
|
|
|
|
event->accept();
|
|
|
|
}
|
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
void TCPStreamDialog::mouseReleaseEvent(QMouseEvent *event)
|
|
|
|
{
|
|
|
|
mouseReleased(event);
|
2013-08-27 23:28:35 +00:00
|
|
|
}
|
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
void TCPStreamDialog::findStream()
|
|
|
|
{
|
2015-08-20 19:39:12 +00:00
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
|
|
|
|
disconnect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*)));
|
2016-12-22 21:03:03 +00:00
|
|
|
// if streamNumberSpinBox has focus -
|
|
|
|
// first clear focus, then disable/enable, then restore focus
|
|
|
|
bool spin_box_focused = ui->streamNumberSpinBox->hasFocus();
|
|
|
|
if (spin_box_focused)
|
|
|
|
ui->streamNumberSpinBox->clearFocus();
|
2015-08-20 19:39:12 +00:00
|
|
|
ui->streamNumberSpinBox->setEnabled(false);
|
2013-09-13 22:29:05 +00:00
|
|
|
graph_segment_list_free(&graph_);
|
2020-04-13 21:00:48 +00:00
|
|
|
graph_segment_list_get(cap_file_, &graph_);
|
2015-08-20 19:39:12 +00:00
|
|
|
ui->streamNumberSpinBox->setEnabled(true);
|
2016-12-22 21:03:03 +00:00
|
|
|
if (spin_box_focused)
|
|
|
|
ui->streamNumberSpinBox->setFocus();
|
|
|
|
|
2015-08-20 19:39:12 +00:00
|
|
|
connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*)));
|
2013-09-13 22:29:05 +00:00
|
|
|
}
|
|
|
|
|
2016-12-06 07:50:45 +00:00
|
|
|
void TCPStreamDialog::fillGraph(bool reset_axes, bool set_focus)
|
2013-09-04 23:15:59 +00:00
|
|
|
{
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
|
|
|
|
if (sp->graphCount() < 1) return;
|
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
base_graph_->setLineStyle(QCPGraph::lsNone);
|
2013-09-04 23:15:59 +00:00
|
|
|
tracer_->setGraph(NULL);
|
2013-09-12 21:37:47 +00:00
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
// base_graph_ is always visible.
|
2013-09-04 23:15:59 +00:00
|
|
|
for (int i = 0; i < sp->graphCount(); i++) {
|
2016-09-25 09:57:55 +00:00
|
|
|
sp->graph(i)->data()->clear();
|
2013-09-04 23:15:59 +00:00
|
|
|
sp->graph(i)->setVisible(i == 0 ? true : false);
|
|
|
|
}
|
2021-04-15 22:25:24 +00:00
|
|
|
// also clear and hide ErrorBars plottables
|
|
|
|
seg_eb_->setVisible(false);
|
|
|
|
seg_eb_->data()->clear();
|
|
|
|
sack_eb_->setVisible(false);
|
|
|
|
sack_eb_->data()->clear();
|
|
|
|
sack2_eb_->setVisible(false);
|
|
|
|
sack2_eb_->data()->clear();
|
2013-09-08 01:25:27 +00:00
|
|
|
|
2013-09-16 00:50:23 +00:00
|
|
|
base_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, pkt_point_size_));
|
|
|
|
|
2013-09-08 01:25:27 +00:00
|
|
|
sp->xAxis->setLabel(time_s_label_);
|
|
|
|
sp->xAxis->setNumberFormat("gb");
|
2017-01-21 04:57:09 +00:00
|
|
|
// Use enough precision to mark microseconds
|
|
|
|
// when zooming in on a <100s capture
|
|
|
|
sp->xAxis->setNumberPrecision(8);
|
2013-09-09 01:40:06 +00:00
|
|
|
sp->yAxis->setNumberFormat("f");
|
|
|
|
sp->yAxis->setNumberPrecision(0);
|
2013-09-04 23:15:59 +00:00
|
|
|
sp->yAxis2->setVisible(false);
|
|
|
|
sp->yAxis2->setLabel(QString());
|
|
|
|
|
|
|
|
if (!cap_file_) {
|
2013-09-08 01:25:27 +00:00
|
|
|
QString dlg_title = QString(tr("No Capture Data"));
|
2013-09-04 23:15:59 +00:00
|
|
|
setWindowTitle(dlg_title);
|
|
|
|
title_->setText(dlg_title);
|
|
|
|
sp->setEnabled(false);
|
2013-09-06 00:41:07 +00:00
|
|
|
sp->yAxis->setLabel(QString());
|
|
|
|
sp->replot();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
ts_offset_ = 0;
|
|
|
|
seq_offset_ = 0;
|
|
|
|
bool first = true;
|
2013-09-16 17:28:42 +00:00
|
|
|
guint64 bytes_fwd = 0;
|
|
|
|
guint64 bytes_rev = 0;
|
|
|
|
int pkts_fwd = 0;
|
|
|
|
int pkts_rev = 0;
|
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
time_stamp_map_.clear();
|
2013-09-06 00:41:07 +00:00
|
|
|
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
2016-12-06 07:50:45 +00:00
|
|
|
// NOTE - adding both forward and reverse packets to time_stamp_map_
|
|
|
|
// so that both data and acks are selectable
|
|
|
|
// (this is important especially in selecting particular SACK pkts)
|
2016-12-22 21:03:03 +00:00
|
|
|
bool insert = true;
|
2013-09-06 00:41:07 +00:00
|
|
|
if (!compareHeaders(seg)) {
|
2013-09-16 17:28:42 +00:00
|
|
|
bytes_rev += seg->th_seglen;
|
|
|
|
pkts_rev++;
|
2016-12-22 21:03:03 +00:00
|
|
|
// only insert reverse packets if SACK present
|
|
|
|
insert = (seg->num_sack_ranges != 0);
|
2016-12-06 07:50:45 +00:00
|
|
|
} else {
|
|
|
|
bytes_fwd += seg->th_seglen;
|
|
|
|
pkts_fwd++;
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
2013-09-09 01:40:06 +00:00
|
|
|
double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
|
|
|
|
if (first) {
|
|
|
|
if (ts_origin_conn_) ts_offset_ = ts;
|
2016-12-06 07:50:45 +00:00
|
|
|
if (seq_origin_zero_) {
|
|
|
|
if (compareHeaders(seg))
|
|
|
|
seq_offset_ = seg->th_seq;
|
|
|
|
else
|
|
|
|
seq_offset_ = seg->th_ack;
|
|
|
|
}
|
2013-09-09 01:40:06 +00:00
|
|
|
first = false;
|
|
|
|
}
|
2016-12-22 21:03:03 +00:00
|
|
|
if (insert) {
|
2020-05-28 18:40:08 +00:00
|
|
|
time_stamp_map_.insert(ts - ts_offset_, seg);
|
2016-12-22 21:03:03 +00:00
|
|
|
}
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
2013-09-06 00:41:07 +00:00
|
|
|
|
|
|
|
switch (graph_.type) {
|
|
|
|
case GRAPH_TSEQ_STEVENS:
|
2013-09-09 01:40:06 +00:00
|
|
|
fillStevens();
|
2013-09-06 00:41:07 +00:00
|
|
|
break;
|
2013-09-13 22:29:05 +00:00
|
|
|
case GRAPH_TSEQ_TCPTRACE:
|
|
|
|
fillTcptrace();
|
|
|
|
break;
|
2013-09-06 00:41:07 +00:00
|
|
|
case GRAPH_THROUGHPUT:
|
2013-09-09 01:40:06 +00:00
|
|
|
fillThroughput();
|
2013-09-06 00:41:07 +00:00
|
|
|
break;
|
2013-09-08 01:25:27 +00:00
|
|
|
case GRAPH_RTT:
|
2013-09-09 01:40:06 +00:00
|
|
|
fillRoundTripTime();
|
|
|
|
break;
|
|
|
|
case GRAPH_WSCALE:
|
|
|
|
fillWindowScale();
|
2013-09-08 01:25:27 +00:00
|
|
|
break;
|
2013-09-06 00:41:07 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sp->setEnabled(true);
|
|
|
|
|
2013-09-16 17:28:42 +00:00
|
|
|
stream_desc_ = tr("%1 %2 pkts, %3 %4 %5 pkts, %6 ")
|
|
|
|
.arg(UTF8_RIGHTWARDS_ARROW)
|
2021-11-29 19:29:55 +00:00
|
|
|
.arg(gchar_free_to_qstring(format_size(pkts_fwd, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI)))
|
|
|
|
.arg(gchar_free_to_qstring(format_size(bytes_fwd, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI)))
|
2013-09-16 17:28:42 +00:00
|
|
|
.arg(UTF8_LEFTWARDS_ARROW)
|
2021-11-29 19:29:55 +00:00
|
|
|
.arg(gchar_free_to_qstring(format_size(pkts_rev, FORMAT_SIZE_UNIT_NONE, FORMAT_SIZE_PREFIX_SI)))
|
|
|
|
.arg(gchar_free_to_qstring(format_size(bytes_rev, FORMAT_SIZE_UNIT_BYTES, FORMAT_SIZE_PREFIX_SI)));
|
2013-09-16 17:28:42 +00:00
|
|
|
mouseMoved(NULL);
|
2016-12-06 07:50:45 +00:00
|
|
|
if (reset_axes)
|
|
|
|
resetAxes();
|
|
|
|
else
|
|
|
|
sp->replot();
|
2017-01-21 04:57:09 +00:00
|
|
|
// Throughput and Window Scale graphs can hide base_graph_
|
2017-12-28 18:34:36 +00:00
|
|
|
if (base_graph_->visible())
|
2017-01-21 04:57:09 +00:00
|
|
|
tracer_->setGraph(base_graph_);
|
2013-09-16 02:04:52 +00:00
|
|
|
|
|
|
|
// XXX QCustomPlot doesn't seem to draw any sort of focus indicator.
|
2016-12-06 07:50:45 +00:00
|
|
|
if (set_focus)
|
|
|
|
sp->setFocus();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::showWidgetsForGraphType()
|
|
|
|
{
|
2017-09-03 14:07:37 +00:00
|
|
|
if (graph_.type == GRAPH_RTT) {
|
|
|
|
ui->bySeqNumberCheckBox->setVisible(true);
|
|
|
|
} else {
|
|
|
|
ui->bySeqNumberCheckBox->setVisible(false);
|
|
|
|
}
|
2016-12-06 07:50:45 +00:00
|
|
|
if (graph_.type == GRAPH_THROUGHPUT) {
|
2017-01-21 04:57:09 +00:00
|
|
|
#ifdef MA_1_SECOND
|
2016-12-06 07:50:45 +00:00
|
|
|
ui->maWindowSizeLabel->setVisible(true);
|
|
|
|
ui->maWindowSizeSpinBox->setVisible(true);
|
2017-01-21 04:57:09 +00:00
|
|
|
#else
|
|
|
|
ui->maWindowSizeLabel->setVisible(false);
|
|
|
|
ui->maWindowSizeSpinBox->setVisible(false);
|
|
|
|
#endif
|
|
|
|
ui->showSegLengthCheckBox->setVisible(true);
|
|
|
|
ui->showThroughputCheckBox->setVisible(true);
|
|
|
|
ui->showGoodputCheckBox->setVisible(true);
|
2016-12-06 07:50:45 +00:00
|
|
|
} else {
|
|
|
|
ui->maWindowSizeLabel->setVisible(false);
|
|
|
|
ui->maWindowSizeSpinBox->setVisible(false);
|
2017-01-21 04:57:09 +00:00
|
|
|
ui->showSegLengthCheckBox->setVisible(false);
|
|
|
|
ui->showThroughputCheckBox->setVisible(false);
|
|
|
|
ui->showGoodputCheckBox->setVisible(false);
|
2016-12-06 07:50:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (graph_.type == GRAPH_TSEQ_TCPTRACE) {
|
2016-12-22 21:03:03 +00:00
|
|
|
ui->selectSACKsCheckBox->setVisible(true);
|
2016-12-06 07:50:45 +00:00
|
|
|
} else {
|
2016-12-22 21:03:03 +00:00
|
|
|
ui->selectSACKsCheckBox->setVisible(false);
|
2016-12-06 07:50:45 +00:00
|
|
|
}
|
2017-01-21 04:57:09 +00:00
|
|
|
|
|
|
|
if (graph_.type == GRAPH_WSCALE) {
|
|
|
|
ui->showRcvWinCheckBox->setVisible(true);
|
|
|
|
ui->showBytesOutCheckBox->setVisible(true);
|
|
|
|
} else {
|
|
|
|
ui->showRcvWinCheckBox->setVisible(false);
|
|
|
|
ui->showBytesOutCheckBox->setVisible(false);
|
|
|
|
}
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
|
|
|
|
2013-09-12 23:46:56 +00:00
|
|
|
void TCPStreamDialog::zoomAxes(bool in)
|
|
|
|
{
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
double h_factor = sp->axisRect()->rangeZoomFactor(Qt::Horizontal);
|
|
|
|
double v_factor = sp->axisRect()->rangeZoomFactor(Qt::Vertical);
|
|
|
|
|
|
|
|
if (!in) {
|
|
|
|
h_factor = pow(h_factor, -1);
|
|
|
|
v_factor = pow(v_factor, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
sp->xAxis->scaleRange(h_factor, sp->xAxis->range().center());
|
|
|
|
sp->yAxis->scaleRange(v_factor, sp->yAxis->range().center());
|
|
|
|
sp->replot();
|
|
|
|
}
|
|
|
|
|
2015-10-09 05:19:21 +00:00
|
|
|
void TCPStreamDialog::zoomXAxis(bool in)
|
|
|
|
{
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
double h_factor = sp->axisRect()->rangeZoomFactor(Qt::Horizontal);
|
|
|
|
|
|
|
|
if (!in) {
|
|
|
|
h_factor = pow(h_factor, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
sp->xAxis->scaleRange(h_factor, sp->xAxis->range().center());
|
|
|
|
sp->replot();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::zoomYAxis(bool in)
|
|
|
|
{
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
double v_factor = sp->axisRect()->rangeZoomFactor(Qt::Vertical);
|
|
|
|
|
|
|
|
if (!in) {
|
|
|
|
v_factor = pow(v_factor, -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
sp->yAxis->scaleRange(v_factor, sp->yAxis->range().center());
|
|
|
|
sp->replot();
|
|
|
|
}
|
|
|
|
|
2013-09-12 23:46:56 +00:00
|
|
|
void TCPStreamDialog::panAxes(int x_pixels, int y_pixels)
|
|
|
|
{
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
double h_pan = 0.0;
|
|
|
|
double v_pan = 0.0;
|
|
|
|
|
|
|
|
h_pan = sp->xAxis->range().size() * x_pixels / sp->xAxis->axisRect()->width();
|
|
|
|
v_pan = sp->yAxis->range().size() * y_pixels / sp->yAxis->axisRect()->height();
|
|
|
|
// The GTK+ version won't pan unless we're zoomed. Should we do the same here?
|
|
|
|
if (h_pan) {
|
|
|
|
sp->xAxis->moveRange(h_pan);
|
|
|
|
sp->replot();
|
|
|
|
}
|
|
|
|
if (v_pan) {
|
|
|
|
sp->yAxis->moveRange(v_pan);
|
|
|
|
sp->replot();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
void TCPStreamDialog::resetAxes()
|
|
|
|
{
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
|
2013-09-06 00:41:07 +00:00
|
|
|
y_axis_xfrm_.reset();
|
|
|
|
double pixel_pad = 10.0; // per side
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-09-16 00:50:23 +00:00
|
|
|
sp->rescaleAxes(true);
|
2017-01-21 04:57:09 +00:00
|
|
|
// tput_graph_->rescaleValueAxis(false, true);
|
2013-09-16 00:50:23 +00:00
|
|
|
// base_graph_->rescaleAxes(false, true);
|
|
|
|
// for (int i = 0; i < sp->graphCount(); i++) {
|
|
|
|
// sp->graph(i)->rescaleValueAxis(false, true);
|
|
|
|
// }
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-09-06 00:41:07 +00:00
|
|
|
double axis_pixels = sp->xAxis->axisRect()->width();
|
|
|
|
sp->xAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, sp->xAxis->range().center());
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
if (sp->yAxis2->visible()) {
|
2013-09-06 00:41:07 +00:00
|
|
|
double ratio = sp->yAxis2->range().size() / sp->yAxis->range().size();
|
|
|
|
y_axis_xfrm_.translate(0.0, sp->yAxis2->range().lower - (sp->yAxis->range().lower * ratio));
|
|
|
|
y_axis_xfrm_.scale(1.0, ratio);
|
|
|
|
}
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
axis_pixels = sp->yAxis->axisRect()->height();
|
|
|
|
sp->yAxis->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, sp->yAxis->range().center());
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
sp->replot();
|
|
|
|
}
|
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
void TCPStreamDialog::fillStevens()
|
2013-09-04 23:15:59 +00:00
|
|
|
{
|
2013-09-13 22:29:05 +00:00
|
|
|
QString dlg_title = QString(tr("Sequence Numbers (Stevens)")) + streamDescription();
|
2013-09-04 23:15:59 +00:00
|
|
|
setWindowTitle(dlg_title);
|
|
|
|
title_->setText(dlg_title);
|
|
|
|
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
2013-09-09 01:40:06 +00:00
|
|
|
sp->yAxis->setLabel(sequence_number_label_);
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
// True Stevens-style graphs don't have lines but I like them - gcc
|
2013-09-13 22:29:05 +00:00
|
|
|
base_graph_->setLineStyle(QCPGraph::lsStepLeft);
|
2013-09-04 23:15:59 +00:00
|
|
|
|
|
|
|
QVector<double> rel_time, seq;
|
|
|
|
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
|
|
|
if (!compareHeaders(seg)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
|
|
|
|
rel_time.append(ts - ts_offset_);
|
|
|
|
seq.append(seg->th_seq - seq_offset_);
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
2013-09-13 22:29:05 +00:00
|
|
|
base_graph_->setData(rel_time, seq);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::fillTcptrace()
|
|
|
|
{
|
|
|
|
QString dlg_title = QString(tr("Sequence Numbers (tcptrace)")) + streamDescription();
|
|
|
|
setWindowTitle(dlg_title);
|
|
|
|
title_->setText(dlg_title);
|
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
bool allow_sack_select = ui->selectSACKsCheckBox->isChecked();
|
2016-12-06 07:50:45 +00:00
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
sp->yAxis->setLabel(sequence_number_label_);
|
|
|
|
|
2013-09-16 00:50:23 +00:00
|
|
|
base_graph_->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDot));
|
|
|
|
|
2013-09-13 23:34:34 +00:00
|
|
|
seg_graph_->setVisible(true);
|
2021-04-15 22:25:24 +00:00
|
|
|
seg_eb_->setVisible(true);
|
2013-09-13 22:29:05 +00:00
|
|
|
ack_graph_->setVisible(true);
|
2016-12-22 21:03:03 +00:00
|
|
|
sack_graph_->setVisible(true);
|
2021-04-15 22:25:24 +00:00
|
|
|
sack_eb_->setVisible(true);
|
2016-12-22 21:03:03 +00:00
|
|
|
sack2_graph_->setVisible(true);
|
2021-04-15 22:25:24 +00:00
|
|
|
sack2_eb_->setVisible(true);
|
2013-09-13 22:29:05 +00:00
|
|
|
rwin_graph_->setVisible(true);
|
2018-03-21 22:03:53 +00:00
|
|
|
dup_ack_graph_->setVisible(true);
|
|
|
|
zero_win_graph_->setVisible(true);
|
2013-09-13 22:29:05 +00:00
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
QVector<double> pkt_time, pkt_seqnums;
|
|
|
|
QVector<double> sb_time, sb_center, sb_span;
|
|
|
|
QVector<double> ackrwin_time, ack, rwin;
|
|
|
|
QVector<double> sack_time, sack_center, sack_span;
|
|
|
|
QVector<double> sack2_time, sack2_center, sack2_span;
|
2018-03-21 22:03:53 +00:00
|
|
|
QVector<double> dup_ack_time, dup_ack;
|
|
|
|
QVector<double> zero_win_time, zero_win;
|
2016-12-22 21:03:03 +00:00
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
|
|
|
double ts = (seg->rel_secs + seg->rel_usecs / 1000000.0) - ts_offset_;
|
|
|
|
if (compareHeaders(seg)) {
|
2016-12-22 21:03:03 +00:00
|
|
|
double half = seg->th_seglen / 2.0;
|
|
|
|
double center = seg->th_seq - seq_offset_ + half;
|
|
|
|
|
2016-12-06 07:50:45 +00:00
|
|
|
// Add forward direction to base_graph_ (to select data packets)
|
2013-09-13 22:29:05 +00:00
|
|
|
// Forward direction: seq + data
|
2016-12-06 07:50:45 +00:00
|
|
|
pkt_time.append(ts);
|
2016-12-22 21:03:03 +00:00
|
|
|
pkt_seqnums.append(center);
|
2013-09-13 23:34:34 +00:00
|
|
|
|
|
|
|
// QCP doesn't have a segment graph type. For now, fake
|
|
|
|
// it with error bars.
|
|
|
|
if (seg->th_seglen > 0) {
|
|
|
|
sb_time.append(ts);
|
2016-12-22 21:03:03 +00:00
|
|
|
sb_center.append(center);
|
2013-09-13 23:34:34 +00:00
|
|
|
sb_span.append(half);
|
|
|
|
}
|
2018-03-21 22:03:53 +00:00
|
|
|
|
|
|
|
// Look for zero window sizes.
|
|
|
|
// Should match the TCP_A_ZERO_WINDOW test in packet-tcp.c.
|
|
|
|
if (seg->th_win == 0 && (seg->th_flags & (TH_RST|TH_FIN|TH_SYN)) == 0) {
|
|
|
|
zero_win_time.append(ts);
|
|
|
|
zero_win.append(center);
|
|
|
|
}
|
2013-09-13 22:29:05 +00:00
|
|
|
} else {
|
|
|
|
// Reverse direction: ACK + RWIN
|
|
|
|
if (! (seg->th_flags & TH_ACK)) {
|
|
|
|
// SYNs and RSTs do not necessarily have ACKs
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
double ackno = seg->th_ack - seq_offset_;
|
2016-12-22 21:03:03 +00:00
|
|
|
// add SACK segments to sack, sack2, and selectable packet graph
|
|
|
|
for (int i = 0; i < seg->num_sack_ranges; ++i) {
|
|
|
|
double half = seg->sack_right_edge[i] - seg->sack_left_edge[i];
|
|
|
|
half = half/2.0;
|
|
|
|
double center = seg->sack_left_edge[i] - seq_offset_ + half;
|
|
|
|
if (i == 0) {
|
|
|
|
sack_time.append(ts);
|
|
|
|
sack_center.append(center);
|
|
|
|
sack_span.append(half);
|
|
|
|
if (allow_sack_select) {
|
|
|
|
pkt_time.append(ts);
|
|
|
|
pkt_seqnums.append(center);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
sack2_time.append(ts);
|
|
|
|
sack2_center.append(center);
|
|
|
|
sack2_span.append(half);
|
|
|
|
}
|
2016-12-06 07:50:45 +00:00
|
|
|
}
|
2018-03-21 22:03:53 +00:00
|
|
|
// If ackno is the same as our last one mark it as a duplicate.
|
2021-04-15 22:25:24 +00:00
|
|
|
// (but don't mark window updates as duplicate acks)
|
|
|
|
if (ack.size() > 0 && ack.last() == ackno
|
|
|
|
&& rwin.last() == ackno + seg->th_win) {
|
2018-03-21 22:03:53 +00:00
|
|
|
dup_ack_time.append(ts);
|
|
|
|
dup_ack.append(ackno);
|
|
|
|
}
|
2016-12-06 07:50:45 +00:00
|
|
|
// Also add reverse packets to the ack_graph_
|
2013-09-13 22:29:05 +00:00
|
|
|
ackrwin_time.append(ts);
|
|
|
|
ack.append(ackno);
|
|
|
|
rwin.append(ackno + seg->th_win);
|
|
|
|
}
|
|
|
|
}
|
2021-04-15 22:25:24 +00:00
|
|
|
base_graph_->setData(pkt_time, pkt_seqnums, true);
|
|
|
|
ack_graph_->setData(ackrwin_time, ack, true);
|
|
|
|
seg_graph_->setData(sb_time, sb_center, true);
|
2020-01-10 00:32:26 +00:00
|
|
|
seg_eb_->setData(sb_span);
|
2021-04-15 22:25:24 +00:00
|
|
|
sack_graph_->setData(sack_time, sack_center, true);
|
2020-01-10 00:32:26 +00:00
|
|
|
sack_eb_->setData(sack_span);
|
2021-04-15 22:25:24 +00:00
|
|
|
sack2_graph_->setData(sack2_time, sack2_center, true);
|
2020-01-10 00:32:26 +00:00
|
|
|
sack2_eb_->setData(sack2_span);
|
2021-04-15 22:25:24 +00:00
|
|
|
rwin_graph_->setData(ackrwin_time, rwin, true);
|
|
|
|
dup_ack_graph_->setData(dup_ack_time, dup_ack, true);
|
|
|
|
zero_win_graph_->setData(zero_win_time, zero_win, true);
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
// If the current implementation of incorporating SACKs in goodput calc
|
|
|
|
// is slow, comment out the following line to ignore SACKs in goodput calc.
|
|
|
|
#define USE_SACKS_IN_GOODPUT_CALC
|
|
|
|
|
|
|
|
#ifdef USE_SACKS_IN_GOODPUT_CALC
|
|
|
|
// to incorporate SACKED segments into goodput calculation,
|
|
|
|
// need to keep track of all the SACK blocks we haven't yet
|
|
|
|
// fully ACKed.
|
|
|
|
// I expect this to be _relatively_ small, so using vector to store
|
|
|
|
// them. If this performs badly, it can be refactored with std::list
|
|
|
|
// or std::map.
|
|
|
|
typedef std::pair<guint32, guint32> sack_t;
|
|
|
|
typedef std::vector<sack_t> sack_list_t;
|
|
|
|
static inline bool compare_sack(const sack_t& s1, const sack_t& s2) {
|
|
|
|
return tcp_seq_before(s1.first, s2.first);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper function to adjust an acked seglen for goodput:
|
|
|
|
// - removes previously sacked ranges from seglen (and from old_sacks),
|
|
|
|
// - adds newly sacked ranges to seglen (and to old_sacks)
|
|
|
|
static void
|
|
|
|
goodput_adjust_for_sacks(guint32 *seglen, guint32 last_ack,
|
|
|
|
sack_list_t& new_sacks, guint8 num_sack_ranges,
|
|
|
|
sack_list_t& old_sacks) {
|
|
|
|
|
|
|
|
// Step 1 - For any old_sacks acked by last_ack,
|
|
|
|
// delete their acked length from seglen,
|
|
|
|
// and remove the sack block (or portion)
|
|
|
|
// from (sorted) old_sacks.
|
|
|
|
sack_list_t::iterator unacked = old_sacks.begin();
|
|
|
|
while (unacked != old_sacks.end()) {
|
|
|
|
// break on first sack not fully acked
|
|
|
|
if (tcp_seq_before(last_ack, unacked->second)) {
|
|
|
|
if (tcp_seq_after(last_ack, unacked->first)) {
|
|
|
|
// partially acked - modify to remove acked part
|
|
|
|
*seglen -= (last_ack - unacked->first);
|
|
|
|
unacked->first = last_ack;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// remove fully acked sacks from seglen and move on
|
|
|
|
// (we'll actually remove from the list when loop is done)
|
|
|
|
*seglen -= (unacked->second - unacked->first);
|
|
|
|
++unacked;
|
|
|
|
}
|
|
|
|
// actually remove all fully acked sacks from old_sacks list
|
|
|
|
if (unacked != old_sacks.begin())
|
|
|
|
old_sacks.erase(old_sacks.begin(), unacked);
|
|
|
|
|
|
|
|
// Step 2 - for any new_sacks that precede last_ack,
|
|
|
|
// ignore them. (These would generally be SACKed dup-acks of
|
|
|
|
// a retransmitted seg).
|
|
|
|
// [ in the unlikely case that any new SACK straddles last_ack,
|
|
|
|
// the sack block will be modified to remove the acked portion ]
|
|
|
|
int next_new_idx = 0;
|
|
|
|
while (next_new_idx < num_sack_ranges) {
|
|
|
|
if (tcp_seq_before(last_ack, new_sacks[next_new_idx].second)) {
|
|
|
|
// if a new SACK block is unacked by its own packet, then it's
|
|
|
|
// likely fully unacked, but let's check for partial ack anyway,
|
|
|
|
// and truncate the SACK so that it's fully unacked:
|
|
|
|
if (tcp_seq_before(new_sacks[next_new_idx].first, last_ack))
|
|
|
|
new_sacks[next_new_idx].first = last_ack;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++next_new_idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step 3 - for any byte ranges in remaining new_sacks
|
|
|
|
// that don't already exist in old_sacks, add
|
|
|
|
// their length to seglen
|
|
|
|
// and add that range (by extension, if possible) to
|
|
|
|
// the list of old_sacks.
|
|
|
|
|
|
|
|
sack_list_t::iterator next_old = old_sacks.begin();
|
|
|
|
|
|
|
|
while (next_new_idx < num_sack_ranges &&
|
|
|
|
next_old != old_sacks.end()) {
|
|
|
|
sack_t* next_new = &new_sacks[next_new_idx];
|
|
|
|
|
|
|
|
// Assumptions / Invariants:
|
|
|
|
// - new and old lists are sorted
|
|
|
|
// - span of leftmost to rightmost endpt. is less than half uint32 range
|
|
|
|
// [ensures transitivity - e.g. before(a,b) and before(b,c) ==> before(a,c)]
|
|
|
|
// - all SACKs are non-empty (sack.left before sack.right)
|
|
|
|
// - adjacent SACKs in list always have a gap between them
|
|
|
|
// (sack.right before next_sack.left)
|
|
|
|
|
|
|
|
// Given these assumptions, and noting that there are only three
|
|
|
|
// possible comparisons for a pair of points (before/equal/after),
|
|
|
|
// there are only a few possible relative configurations
|
|
|
|
// of next_old and next_new:
|
|
|
|
// next_new:
|
|
|
|
// [-------------)
|
|
|
|
// next_old:
|
|
|
|
// 1. [---)
|
|
|
|
// 2. [-------)
|
|
|
|
// 3. [----------------)
|
|
|
|
// 4. [---------------------)
|
|
|
|
// 5. [----------------------------)
|
|
|
|
// 6. [--------)
|
|
|
|
// 7. [-------------)
|
|
|
|
// 8. [--------------------)
|
|
|
|
// 9. [---)
|
|
|
|
// 10. [--------)
|
|
|
|
// 11. [---------------)
|
|
|
|
// 12. [------)
|
|
|
|
// 13. [--)
|
|
|
|
|
|
|
|
// Case 1: end of next_old is before beginning of next_new
|
|
|
|
// next_new:
|
|
|
|
// [-------------) ... <end>
|
|
|
|
// next_old:
|
|
|
|
// 1. [---) ... <end>
|
|
|
|
if (tcp_seq_before(next_old->second, next_new->first)) {
|
|
|
|
// Actions:
|
|
|
|
// advance to the next sack in old_sacks
|
|
|
|
++next_old;
|
|
|
|
// retry from the top
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Case 13: end of next_new is before beginning of next_old
|
|
|
|
// next_new:
|
|
|
|
// [-------------) ... <end>
|
|
|
|
// next_old:
|
|
|
|
// 13. [--) ... <end>
|
|
|
|
if (tcp_seq_before(next_new->second, next_old->first)) {
|
|
|
|
// Actions:
|
|
|
|
// add then entire length of next_new into seglen
|
|
|
|
*seglen += (next_new->second - next_new->first);
|
|
|
|
// insert next_new before next_old in old_sacks
|
|
|
|
// (be sure to save and restore next_old iterator around insert!)
|
2017-02-28 09:19:28 +00:00
|
|
|
int next_old_idx = int(next_old - old_sacks.begin());
|
2017-01-21 04:57:09 +00:00
|
|
|
old_sacks.insert(next_old, *next_new);
|
|
|
|
next_old = old_sacks.begin() + next_old_idx + 1;
|
|
|
|
// advance to the next remaining sack in new_sacks
|
|
|
|
++next_new_idx;
|
|
|
|
// retry from the top
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remaining possible configurations:
|
|
|
|
// next_new:
|
|
|
|
// [-------------)
|
|
|
|
// next_old:
|
|
|
|
// 2. [-------)
|
|
|
|
// 3. [----------------)
|
|
|
|
// 4. [---------------------)
|
|
|
|
// 5. [----------------------------)
|
|
|
|
// 6. [--------)
|
|
|
|
// 7. [-------------)
|
|
|
|
// 8. [--------------------)
|
|
|
|
// 9. [---)
|
|
|
|
// 10. [--------)
|
|
|
|
// 11. [---------------)
|
|
|
|
// 12. [------)
|
|
|
|
|
|
|
|
// Cases 2,3,6,9: end of next_old is before end of next_new
|
|
|
|
// next_new:
|
|
|
|
// [-------------)
|
|
|
|
// next_old:
|
|
|
|
// 2. [-------)
|
|
|
|
// 3. [----------------)
|
|
|
|
// 6. [--------)
|
|
|
|
// 9. [---)
|
|
|
|
// Actions:
|
|
|
|
// until end of next_old is equal or after end of next_new,
|
|
|
|
// repeatedly extend next_old, coalescing with next_next_old
|
|
|
|
// if necessary. (and add extended bytes to seglen)
|
|
|
|
while (tcp_seq_before(next_old->second, next_new->second)) {
|
|
|
|
// if end of next_new doesn't collide with start of next_next_old,
|
|
|
|
if (((next_old+1) == old_sacks.end()) ||
|
|
|
|
tcp_seq_before(next_new->second, (next_old + 1)->first)) {
|
|
|
|
// extend end of next_old up to end of next_new,
|
|
|
|
// adding extended bytes to seglen
|
|
|
|
*seglen += (next_new->second - next_old->second);
|
|
|
|
next_old->second = next_new->second;
|
|
|
|
}
|
|
|
|
// otherwise, coalesce next_old with next_next_old
|
|
|
|
else {
|
|
|
|
// add bytes to close gap between sacks to seglen
|
|
|
|
*seglen += ((next_old + 1)->first - next_old->second);
|
|
|
|
// coalesce next_next_old into next_old
|
|
|
|
next_old->second = (next_old + 1)->second;
|
|
|
|
old_sacks.erase(next_old + 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// This operation turns:
|
|
|
|
// Cases 2 and 3 into Case 4 or 5
|
|
|
|
// Case 6 into Case 7
|
|
|
|
// Case 9 into Case 10
|
|
|
|
// Leaving:
|
|
|
|
|
|
|
|
// Remaining possible configurations:
|
|
|
|
// next_new:
|
|
|
|
// [-------------)
|
|
|
|
// next_old:
|
|
|
|
// 4. [---------------------)
|
|
|
|
// 5. [----------------------------)
|
|
|
|
// 7. [-------------)
|
|
|
|
// 8. [--------------------)
|
|
|
|
// 10. [--------)
|
|
|
|
// 11. [---------------)
|
|
|
|
// 12. [------)
|
|
|
|
|
|
|
|
// Cases 10,11,12: start of next_new is before start of next_old
|
|
|
|
// next_new:
|
|
|
|
// [-------------)
|
|
|
|
// next_old:
|
|
|
|
// 10. [--------)
|
|
|
|
// 11. [---------------)
|
|
|
|
// 12. [------)
|
|
|
|
if (tcp_seq_before(next_new->first, next_old->first)) {
|
|
|
|
// Actions:
|
|
|
|
// add the unaccounted bytes in next_new to seglen
|
|
|
|
*seglen += (next_old->first - next_new->first);
|
|
|
|
// then pull the start of next_old back to the start of next_new
|
|
|
|
next_old->first = next_new->first;
|
|
|
|
}
|
|
|
|
// This operation turns:
|
|
|
|
// Case 10 into Case 7
|
|
|
|
// Cases 11 and 12 into Case 8
|
|
|
|
// Leaving:
|
|
|
|
|
|
|
|
// Remaining possible configurations:
|
|
|
|
// next_new:
|
|
|
|
// [-------------)
|
|
|
|
// next_old:
|
|
|
|
// 4. [---------------------)
|
|
|
|
// 5. [----------------------------)
|
|
|
|
// 7. [-------------)
|
|
|
|
// 8. [--------------------)
|
|
|
|
|
|
|
|
// In these cases, the bytes in next_new are fully accounted
|
|
|
|
// by the bytes in next_old, so we can move on to look at
|
|
|
|
// the next sack block in new_sacks
|
|
|
|
++next_new_idx;
|
|
|
|
}
|
|
|
|
// Conditions for leaving loop:
|
|
|
|
// - we processed all remaining new_sacks - nothing left to do
|
|
|
|
// (next_new_idx == num_sack_ranges)
|
|
|
|
// OR
|
|
|
|
// - all remaining new_sacks start at least one byte after
|
|
|
|
// the rightmost edge of the last old_sack
|
|
|
|
// (meaning we can just add the remaining new_sacks to old_sacks list,
|
|
|
|
// and add them directly to the goodput seglen)
|
|
|
|
while (next_new_idx < num_sack_ranges) {
|
|
|
|
sack_t* next_new = &new_sacks[next_new_idx];
|
|
|
|
*seglen += (next_new->second - next_new->first);
|
|
|
|
old_sacks.push_back(*next_new);
|
|
|
|
++next_new_idx;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif // USE_SACKS_IN_GOODPUT_CALC
|
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
void TCPStreamDialog::fillThroughput()
|
2013-09-04 23:15:59 +00:00
|
|
|
{
|
2013-09-06 19:07:57 +00:00
|
|
|
QString dlg_title = QString(tr("Throughput")) + streamDescription();
|
|
|
|
#ifdef MA_1_SECOND
|
2016-12-06 07:50:45 +00:00
|
|
|
dlg_title.append(tr(" (MA)"));
|
2013-09-06 19:07:57 +00:00
|
|
|
#else
|
2013-09-08 01:25:27 +00:00
|
|
|
dlg_title.append(QString(tr(" (%1 Segment MA)")).arg(moving_avg_period_));
|
2013-09-06 19:07:57 +00:00
|
|
|
#endif
|
2013-09-04 23:15:59 +00:00
|
|
|
setWindowTitle(dlg_title);
|
|
|
|
title_->setText(dlg_title);
|
|
|
|
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
2013-09-09 01:40:06 +00:00
|
|
|
sp->yAxis->setLabel(segment_length_label_);
|
|
|
|
sp->yAxis2->setLabel(average_throughput_label_);
|
|
|
|
sp->yAxis2->setLabelColor(QColor(graph_color_2));
|
|
|
|
sp->yAxis2->setTickLabelColor(QColor(graph_color_2));
|
|
|
|
sp->yAxis2->setVisible(true);
|
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
base_graph_->setVisible(ui->showSegLengthCheckBox->isChecked());
|
|
|
|
tput_graph_->setVisible(ui->showThroughputCheckBox->isChecked());
|
|
|
|
goodput_graph_->setVisible(ui->showGoodputCheckBox->isChecked());
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2016-12-06 07:50:45 +00:00
|
|
|
#ifdef MA_1_SECOND
|
|
|
|
if (!graph_.segments) {
|
|
|
|
#else
|
2013-09-04 23:15:59 +00:00
|
|
|
if (!graph_.segments || !graph_.segments->next) {
|
2016-12-06 07:50:45 +00:00
|
|
|
#endif
|
2013-09-04 23:15:59 +00:00
|
|
|
dlg_title.append(tr(" [not enough data]"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
QVector<double> seg_rel_times, ack_rel_times;
|
|
|
|
QVector<double> seg_lens, ack_lens;
|
|
|
|
QVector<double> tput_times, gput_times;
|
|
|
|
QVector<double> tputs, gputs;
|
|
|
|
int oldest_seg = 0, oldest_ack = 0;
|
|
|
|
guint64 seg_sum = 0, ack_sum = 0;
|
|
|
|
guint32 seglen = 0;
|
|
|
|
|
|
|
|
#ifdef USE_SACKS_IN_GOODPUT_CALC
|
|
|
|
// to incorporate SACKED segments into goodput calculation,
|
|
|
|
// need to keep track of all the SACK blocks we haven't yet
|
|
|
|
// fully ACKed.
|
|
|
|
sack_list_t old_sacks, new_sacks;
|
|
|
|
new_sacks.reserve(MAX_TCP_SACK_RANGES);
|
|
|
|
// statically allocate current_sacks vector
|
|
|
|
// [ std::array might be better, but that is C++11 ]
|
|
|
|
for (int i = 0; i < MAX_TCP_SACK_RANGES; ++i) {
|
|
|
|
new_sacks.push_back(sack_t(0,0));
|
|
|
|
}
|
|
|
|
old_sacks.reserve(2*MAX_TCP_SACK_RANGES);
|
|
|
|
#endif // USE_SACKS_IN_GOODPUT_CALC
|
|
|
|
|
|
|
|
// need first acked sequence number to jump-start
|
|
|
|
// computation of acked bytes per packet
|
|
|
|
guint32 last_ack = 0;
|
|
|
|
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
|
|
|
// first reverse packet with ACK flag tells us first acked sequence #
|
|
|
|
if (!compareHeaders(seg) && (seg->th_flags & TH_ACK)) {
|
|
|
|
last_ack = seg->th_ack;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2013-09-04 23:15:59 +00:00
|
|
|
// Financial charts don't show MA data until a full period has elapsed.
|
2016-12-06 07:50:45 +00:00
|
|
|
// [ NOTE - this is because they assume that there's old data that they
|
|
|
|
// don't have access to - but in our case we know that there's NO
|
|
|
|
// data prior to the first packet in the stream - so it's fine to
|
|
|
|
// spit out the MA immediately... ]
|
2013-09-04 23:15:59 +00:00
|
|
|
// The Rosetta Code MA examples start spitting out values immediately.
|
|
|
|
// For now use not-really-correct initial values just to keep our vector
|
|
|
|
// lengths the same.
|
2016-12-06 07:50:45 +00:00
|
|
|
#ifdef MA_1_SECOND
|
|
|
|
// NOTE that for the time-based MA case, you certainly can start with the
|
|
|
|
// first segment!
|
|
|
|
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
|
|
|
#else
|
2013-09-04 23:15:59 +00:00
|
|
|
for (struct segment *seg = graph_.segments->next; seg != NULL; seg = seg->next) {
|
2016-12-06 07:50:45 +00:00
|
|
|
#endif
|
2017-01-21 04:57:09 +00:00
|
|
|
bool is_forward_seg = compareHeaders(seg);
|
|
|
|
QVector<double>& r_pkt_times = is_forward_seg ? seg_rel_times : ack_rel_times;
|
|
|
|
QVector<double>& r_lens = is_forward_seg ? seg_lens : ack_lens;
|
|
|
|
QVector<double>& r_Xput_times = is_forward_seg ? tput_times : gput_times;
|
|
|
|
QVector<double>& r_Xputs = is_forward_seg ? tputs : gputs;
|
|
|
|
int& r_oldest = is_forward_seg ? oldest_seg : oldest_ack;
|
|
|
|
guint64& r_sum = is_forward_seg ? seg_sum : ack_sum;
|
2013-09-13 22:29:05 +00:00
|
|
|
|
2013-09-15 23:55:51 +00:00
|
|
|
double ts = (seg->rel_secs + seg->rel_usecs / 1000000.0) - ts_offset_;
|
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
if (is_forward_seg) {
|
|
|
|
seglen = seg->th_seglen;
|
|
|
|
} else {
|
|
|
|
if ((seg->th_flags & TH_ACK) &&
|
|
|
|
tcp_seq_eq_or_after(seg->th_ack, last_ack)) {
|
|
|
|
seglen = seg->th_ack - last_ack;
|
|
|
|
last_ack = seg->th_ack;
|
|
|
|
#ifdef USE_SACKS_IN_GOODPUT_CALC
|
|
|
|
// copy any sack_ranges into new_sacks, and sort.
|
2019-11-17 19:02:20 +00:00
|
|
|
for (int i = 0; i < seg->num_sack_ranges; ++i) {
|
2017-01-21 04:57:09 +00:00
|
|
|
new_sacks[i].first = seg->sack_left_edge[i];
|
|
|
|
new_sacks[i].second = seg->sack_right_edge[i];
|
|
|
|
}
|
|
|
|
std::sort(new_sacks.begin(),
|
|
|
|
new_sacks.begin() + seg->num_sack_ranges,
|
|
|
|
compare_sack);
|
|
|
|
|
|
|
|
// adjust the seglen based on new and old sacks,
|
|
|
|
// and update the old_sacks list
|
|
|
|
goodput_adjust_for_sacks(&seglen, last_ack,
|
|
|
|
new_sacks, seg->num_sack_ranges,
|
|
|
|
old_sacks);
|
|
|
|
#endif // USE_SACKS_IN_GOODPUT_CALC
|
|
|
|
} else {
|
|
|
|
seglen = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r_pkt_times.append(ts);
|
|
|
|
r_lens.append(seglen);
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-09-06 19:07:57 +00:00
|
|
|
#ifdef MA_1_SECOND
|
2017-08-30 14:48:30 +00:00
|
|
|
while (r_oldest < r_pkt_times.size() && ts - r_pkt_times[r_oldest] > ma_window_size_) {
|
2017-01-21 04:57:09 +00:00
|
|
|
r_sum -= r_lens[r_oldest];
|
2016-12-06 07:50:45 +00:00
|
|
|
// append points where a packet LEAVES the MA window
|
|
|
|
// (as well as, below, where they ENTER the MA window)
|
2017-01-21 04:57:09 +00:00
|
|
|
r_Xputs.append(r_sum * 8.0 / ma_window_size_);
|
|
|
|
r_Xput_times.append(r_pkt_times[r_oldest] + ma_window_size_);
|
|
|
|
r_oldest++;
|
2013-09-06 19:07:57 +00:00
|
|
|
}
|
|
|
|
#else
|
2017-01-21 04:57:09 +00:00
|
|
|
if (r_lens.size() > moving_avg_period_) {
|
|
|
|
r_sum -= r_lens[r_oldest];
|
|
|
|
r_oldest++;
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
2013-09-06 19:07:57 +00:00
|
|
|
#endif
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
// av_Xput computes Xput, i.e.:
|
|
|
|
// throughput for forward packets
|
|
|
|
// goodput for reverse packets
|
|
|
|
double av_Xput;
|
|
|
|
r_sum += seglen;
|
2016-12-06 07:50:45 +00:00
|
|
|
#ifdef MA_1_SECOND
|
|
|
|
// for time-based MA, delta_t is constant
|
2017-01-21 04:57:09 +00:00
|
|
|
av_Xput = r_sum * 8.0 / ma_window_size_;
|
2016-12-06 07:50:45 +00:00
|
|
|
#else
|
2017-01-21 04:57:09 +00:00
|
|
|
double dtime = 0.0;
|
|
|
|
if (r_oldest > 0)
|
|
|
|
dtime = ts - r_pkt_times[r_oldest-1];
|
2013-09-04 23:15:59 +00:00
|
|
|
if (dtime > 0.0) {
|
2017-01-21 04:57:09 +00:00
|
|
|
av_Xput = r_sum * 8.0 / dtime;
|
2013-09-04 23:15:59 +00:00
|
|
|
} else {
|
2017-01-21 04:57:09 +00:00
|
|
|
av_Xput = 0.0;
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
2016-12-06 07:50:45 +00:00
|
|
|
#endif
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-09-07 03:51:27 +00:00
|
|
|
// Add a data point only if our time window has advanced. Otherwise
|
|
|
|
// update the most recent point. (We might want to show a warning
|
|
|
|
// for out-of-order packets.)
|
2017-01-21 04:57:09 +00:00
|
|
|
if (r_Xput_times.size() > 0 && ts <= r_Xput_times.last()) {
|
|
|
|
r_Xputs[r_Xputs.size() - 1] = av_Xput;
|
2013-09-07 03:51:27 +00:00
|
|
|
} else {
|
2017-01-21 04:57:09 +00:00
|
|
|
r_Xputs.append(av_Xput);
|
|
|
|
r_Xput_times.append(ts);
|
2013-09-07 03:51:27 +00:00
|
|
|
}
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
2017-01-21 04:57:09 +00:00
|
|
|
base_graph_->setData(seg_rel_times, seg_lens);
|
|
|
|
tput_graph_->setData(tput_times, tputs);
|
|
|
|
goodput_graph_->setData(gput_times, gputs);
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
|
|
|
|
2017-01-18 04:30:26 +00:00
|
|
|
// rtt_selectively_ack_range:
|
|
|
|
// "Helper" function for fillRoundTripTime
|
|
|
|
// given an rtt_unack list, two pointers to a range of segments in the list,
|
|
|
|
// and the [left,right) edges of a SACK block, selectively ACKs the range
|
|
|
|
// from "begin" to "end" - possibly splitting one segment in the range
|
|
|
|
// into two (and relinking the new segment in order after the first)
|
|
|
|
//
|
|
|
|
// Assumptions:
|
|
|
|
// "begin must be non-NULL
|
|
|
|
// "begin" must precede "end" (or "end" must be NULL)
|
|
|
|
// [ there are minor optimizations that could be added if
|
|
|
|
// the range from "begin" to "end" are in sequence number order.
|
|
|
|
// (this function would preserve that as an invariant). ]
|
|
|
|
static struct rtt_unack *
|
2017-09-03 14:07:37 +00:00
|
|
|
rtt_selectively_ack_range(QVector<double>& x_vals, bool bySeqNumber,
|
|
|
|
QVector<double>& rtt,
|
2017-01-18 04:30:26 +00:00
|
|
|
struct rtt_unack **list,
|
|
|
|
struct rtt_unack *begin, struct rtt_unack *end,
|
|
|
|
unsigned int left, unsigned int right, double rt_val) {
|
|
|
|
struct rtt_unack *cur, *next;
|
|
|
|
// sanity check:
|
|
|
|
if (tcp_seq_eq_or_after(left, right))
|
|
|
|
return begin;
|
|
|
|
// real work:
|
|
|
|
for (cur = begin; cur != end; cur = next) {
|
|
|
|
next = cur->next;
|
|
|
|
// check #1: does [left,right) intersect current unack at all?
|
|
|
|
// (if not, we can just move on to the next unack)
|
|
|
|
if (tcp_seq_eq_or_after(cur->seqno, right) ||
|
|
|
|
tcp_seq_eq_or_after(left, cur->end_seqno)) {
|
|
|
|
// no intersection - just skip this.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// yes, we intersect!
|
|
|
|
int left_end_acked = tcp_seq_eq_or_after(cur->seqno, left);
|
|
|
|
int right_end_acked = tcp_seq_eq_or_after(right, cur->end_seqno);
|
|
|
|
// check #2 - did we fully ack the current unack?
|
|
|
|
// (if so, we can delete it and move on)
|
|
|
|
if (left_end_acked && right_end_acked) {
|
|
|
|
// ACK the whole segment
|
2017-09-03 14:07:37 +00:00
|
|
|
if (bySeqNumber) {
|
|
|
|
x_vals.append(cur->seqno);
|
|
|
|
} else {
|
|
|
|
x_vals.append(cur->time);
|
|
|
|
}
|
2017-01-18 04:30:26 +00:00
|
|
|
rtt.append((rt_val - cur->time) * 1000.0);
|
|
|
|
// in this case, we will delete current unack
|
|
|
|
// [ update "begin" if necessary - we will return it to the
|
|
|
|
// caller to let them know we deleted it ]
|
|
|
|
if (cur == begin)
|
|
|
|
begin = next;
|
|
|
|
rtt_delete_unack_from_list(list, cur);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// check #3 - did we ACK the left-hand side of the current unack?
|
|
|
|
// (if so, we can just modify it and move on)
|
|
|
|
if (left_end_acked) { // and right_end_not_acked
|
|
|
|
// ACK the left end
|
2017-09-03 14:07:37 +00:00
|
|
|
if (bySeqNumber) {
|
|
|
|
x_vals.append(cur->seqno);
|
|
|
|
} else {
|
|
|
|
x_vals.append(cur->time);
|
|
|
|
}
|
2017-01-18 04:30:26 +00:00
|
|
|
rtt.append((rt_val - cur->time) * 1000.0);
|
|
|
|
// in this case, "right" marks the start of remaining bytes
|
|
|
|
cur->seqno = right;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// check #4 - did we ACK the right-hand side of the current unack?
|
|
|
|
// (if so, we can just modify it and move on)
|
|
|
|
if (right_end_acked) { // and left_end_not_acked
|
|
|
|
// ACK the right end
|
2017-09-03 14:07:37 +00:00
|
|
|
if (bySeqNumber) {
|
|
|
|
x_vals.append(left);
|
|
|
|
} else {
|
|
|
|
x_vals.append(cur->time);
|
|
|
|
}
|
2017-01-18 04:30:26 +00:00
|
|
|
rtt.append((rt_val - cur->time) * 1000.0);
|
|
|
|
// in this case, "left" is just beyond the remaining bytes
|
|
|
|
cur->end_seqno = left;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// at this point, we know:
|
|
|
|
// - the SACK block does intersect this unack, but
|
|
|
|
// - it does not intersect the left or right endpoints
|
|
|
|
// Therefore, it must intersect the middle, so we must split the unack
|
|
|
|
// into left and right unacked segments:
|
|
|
|
// ACK the SACK block
|
2017-09-03 14:07:37 +00:00
|
|
|
if (bySeqNumber) {
|
|
|
|
x_vals.append(left);
|
|
|
|
} else {
|
|
|
|
x_vals.append(cur->time);
|
|
|
|
}
|
2017-01-18 04:30:26 +00:00
|
|
|
rtt.append((rt_val - cur->time) * 1000.0);
|
|
|
|
// then split cur into two unacked segments
|
|
|
|
// (linking the right-hand unack after the left)
|
|
|
|
cur->next = rtt_get_new_unack(cur->time, right, cur->end_seqno - right);
|
|
|
|
cur->next->next = next;
|
|
|
|
cur->end_seqno = left;
|
|
|
|
}
|
|
|
|
return begin;
|
|
|
|
}
|
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
void TCPStreamDialog::fillRoundTripTime()
|
2013-09-08 01:25:27 +00:00
|
|
|
{
|
|
|
|
QString dlg_title = QString(tr("Round Trip Time")) + streamDescription();
|
|
|
|
setWindowTitle(dlg_title);
|
|
|
|
title_->setText(dlg_title);
|
|
|
|
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
2017-09-03 14:07:37 +00:00
|
|
|
bool bySeqNumber = ui->bySeqNumberCheckBox->isChecked();
|
2017-01-18 04:30:26 +00:00
|
|
|
|
2017-09-03 14:07:37 +00:00
|
|
|
if (bySeqNumber) {
|
|
|
|
sequence_num_map_.clear();
|
|
|
|
sp->xAxis->setLabel(sequence_number_label_);
|
|
|
|
sp->xAxis->setNumberFormat("f");
|
|
|
|
sp->xAxis->setNumberPrecision(0);
|
|
|
|
}
|
2013-09-09 01:40:06 +00:00
|
|
|
sp->yAxis->setLabel(round_trip_time_ms_label_);
|
2017-01-18 04:30:26 +00:00
|
|
|
sp->yAxis->setNumberFormat("gb");
|
|
|
|
sp->yAxis->setNumberPrecision(3);
|
2013-09-09 01:40:06 +00:00
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
base_graph_->setLineStyle(QCPGraph::lsLine);
|
2013-09-08 01:25:27 +00:00
|
|
|
|
2017-09-03 14:07:37 +00:00
|
|
|
QVector<double> x_vals, rtt;
|
2013-09-08 15:05:08 +00:00
|
|
|
guint32 seq_base = 0;
|
2017-01-18 04:30:26 +00:00
|
|
|
struct rtt_unack *unack_list = NULL, *u = NULL;
|
2013-09-08 01:25:27 +00:00
|
|
|
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
2013-09-15 20:44:57 +00:00
|
|
|
if (compareHeaders(seg)) {
|
2013-09-08 01:25:27 +00:00
|
|
|
seq_base = seg->th_seq;
|
2013-09-15 20:44:57 +00:00
|
|
|
break;
|
2013-09-08 01:25:27 +00:00
|
|
|
}
|
2013-09-15 20:44:57 +00:00
|
|
|
}
|
|
|
|
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
2013-09-08 01:25:27 +00:00
|
|
|
if (compareHeaders(seg)) {
|
2013-09-15 20:44:57 +00:00
|
|
|
guint32 seqno = seg->th_seq - seq_base;
|
2017-01-18 04:30:26 +00:00
|
|
|
if (seg->th_seglen && !rtt_is_retrans(unack_list, seqno)) {
|
2013-09-08 01:25:27 +00:00
|
|
|
double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
|
2017-09-03 14:07:37 +00:00
|
|
|
rt_val -= ts_offset_;
|
2017-01-18 04:30:26 +00:00
|
|
|
u = rtt_get_new_unack(rt_val, seqno, seg->th_seglen);
|
|
|
|
if (!u) {
|
|
|
|
// make sure to free list before returning!
|
|
|
|
rtt_destroy_unack_list(&unack_list);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
rtt_put_unack_on_list(&unack_list, u);
|
2013-09-08 01:25:27 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
guint32 ack_no = seg->th_ack - seq_base;
|
|
|
|
double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
|
2017-09-03 14:07:37 +00:00
|
|
|
rt_val -= ts_offset_;
|
2017-01-18 04:30:26 +00:00
|
|
|
struct rtt_unack *v;
|
2013-09-08 01:25:27 +00:00
|
|
|
|
2017-01-18 04:30:26 +00:00
|
|
|
for (u = unack_list; u; u = v) {
|
|
|
|
if (tcp_seq_after(ack_no, u->seqno)) {
|
|
|
|
// full or partial ack of seg by ack_no
|
2017-09-03 14:07:37 +00:00
|
|
|
if (bySeqNumber) {
|
|
|
|
x_vals.append(u->seqno);
|
|
|
|
sequence_num_map_.insert(u->seqno, seg);
|
|
|
|
} else {
|
|
|
|
x_vals.append(u->time);
|
|
|
|
}
|
2013-09-08 01:25:27 +00:00
|
|
|
rtt.append((rt_val - u->time) * 1000.0);
|
2017-01-18 04:30:26 +00:00
|
|
|
if (tcp_seq_eq_or_after(ack_no, u->end_seqno)) {
|
|
|
|
// fully acked segment - nothing more to see here
|
|
|
|
v = u->next;
|
|
|
|
rtt_delete_unack_from_list(&unack_list, u);
|
|
|
|
// no need to compare SACK blocks for fully ACKed seg
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
// partial ack of GSO seg
|
|
|
|
u->seqno = ack_no;
|
|
|
|
// (keep going - still need to compare SACK blocks...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
v = u->next;
|
|
|
|
// selectively acking u more than once
|
|
|
|
// can shatter it into multiple intervals.
|
|
|
|
// If we link those back into the list between u and v,
|
|
|
|
// then each subsequent SACK selectively ACKs that range.
|
|
|
|
for (int i = 0; i < seg->num_sack_ranges; ++i) {
|
|
|
|
guint32 left = seg->sack_left_edge[i] - seq_base;
|
|
|
|
guint32 right = seg->sack_right_edge[i] - seq_base;
|
2017-09-03 14:07:37 +00:00
|
|
|
u = rtt_selectively_ack_range(x_vals, bySeqNumber, rtt,
|
2017-01-18 04:30:26 +00:00
|
|
|
&unack_list, u, v,
|
|
|
|
left, right, rt_val);
|
|
|
|
// if range is empty after selective ack, we can
|
|
|
|
// skip the rest of the SACK blocks
|
|
|
|
if (u == v) break;
|
2013-09-08 01:25:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-01-18 04:30:26 +00:00
|
|
|
// it's possible there's still unacked segs - so be sure to free list!
|
|
|
|
rtt_destroy_unack_list(&unack_list);
|
2017-09-03 14:07:37 +00:00
|
|
|
base_graph_->setData(x_vals, rtt);
|
2013-09-09 01:40:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::fillWindowScale()
|
|
|
|
{
|
|
|
|
QString dlg_title = QString(tr("Window Scaling")) + streamDescription();
|
|
|
|
setWindowTitle(dlg_title);
|
|
|
|
title_->setText(dlg_title);
|
|
|
|
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
2017-01-18 00:53:42 +00:00
|
|
|
// use base_graph_ to represent unacked window size
|
|
|
|
// (estimate of congestion window)
|
|
|
|
base_graph_->setLineStyle(QCPGraph::lsStepLeft);
|
|
|
|
// use rwin_graph_ here to show rwin window scale
|
|
|
|
// (derived from ACK packets)
|
2017-01-21 04:57:09 +00:00
|
|
|
base_graph_->setVisible(ui->showBytesOutCheckBox->isChecked());
|
|
|
|
rwin_graph_->setVisible(ui->showRcvWinCheckBox->isChecked());
|
2013-09-09 01:40:06 +00:00
|
|
|
|
|
|
|
QVector<double> rel_time, win_size;
|
2017-01-18 00:53:42 +00:00
|
|
|
QVector<double> cwnd_time, cwnd_size;
|
|
|
|
guint32 last_ack = 0;
|
|
|
|
bool found_first_ack = false;
|
2013-09-09 01:40:06 +00:00
|
|
|
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
|
|
|
double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
|
|
|
|
|
2017-01-18 00:53:42 +00:00
|
|
|
// The receive window that applies to this flow comes
|
|
|
|
// from packets in the opposite direction
|
|
|
|
if (compareHeaders(seg)) {
|
|
|
|
// compute bytes_in_flight for cwnd graph
|
|
|
|
guint32 end_seq = seg->th_seq + seg->th_seglen;
|
|
|
|
if (found_first_ack &&
|
|
|
|
tcp_seq_eq_or_after(end_seq, last_ack)) {
|
|
|
|
cwnd_time.append(ts - ts_offset_);
|
|
|
|
cwnd_size.append((double)(end_seq - last_ack));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// packet in opposite direction - has advertised rwin
|
|
|
|
guint16 flags = seg->th_flags;
|
|
|
|
|
|
|
|
if ((flags & (TH_SYN|TH_RST)) == 0) {
|
|
|
|
rel_time.append(ts - ts_offset_);
|
|
|
|
win_size.append(seg->th_win);
|
|
|
|
}
|
|
|
|
if ((flags & (TH_ACK)) != 0) {
|
|
|
|
// use this to update last_ack
|
|
|
|
if (!found_first_ack ||
|
|
|
|
tcp_seq_eq_or_after(seg->th_ack, last_ack)) {
|
|
|
|
last_ack = seg->th_ack;
|
|
|
|
found_first_ack = true;
|
|
|
|
}
|
|
|
|
}
|
2013-09-09 01:40:06 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-18 00:53:42 +00:00
|
|
|
base_graph_->setData(cwnd_time, cwnd_size);
|
|
|
|
rwin_graph_->setData(rel_time, win_size);
|
2013-09-09 01:40:06 +00:00
|
|
|
sp->yAxis->setLabel(window_size_label_);
|
2013-09-08 01:25:27 +00:00
|
|
|
}
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
QString TCPStreamDialog::streamDescription()
|
|
|
|
{
|
2015-01-05 16:12:08 +00:00
|
|
|
QString description(tr(" for %1:%2 %3 %4:%5")
|
2015-01-29 21:55:18 +00:00
|
|
|
.arg(address_to_qstring(&graph_.src_address))
|
2013-09-04 23:15:59 +00:00
|
|
|
.arg(graph_.src_port)
|
|
|
|
.arg(UTF8_RIGHTWARDS_ARROW)
|
2015-01-29 21:55:18 +00:00
|
|
|
.arg(address_to_qstring(&graph_.dst_address))
|
2015-01-05 16:12:08 +00:00
|
|
|
.arg(graph_.dst_port));
|
|
|
|
return description;
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
|
|
|
|
2013-08-30 21:15:24 +00:00
|
|
|
bool TCPStreamDialog::compareHeaders(segment *seg)
|
|
|
|
{
|
|
|
|
return (compare_headers(&graph_.src_address, &graph_.dst_address,
|
|
|
|
graph_.src_port, graph_.dst_port,
|
|
|
|
&seg->ip_src, &seg->ip_dst,
|
|
|
|
seg->th_sport, seg->th_dport,
|
|
|
|
COMPARE_CURR_DIR));
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::toggleTracerStyle(bool force_default)
|
|
|
|
{
|
|
|
|
if (!tracer_->visible() && !force_default) return;
|
|
|
|
|
|
|
|
QPen sp_pen = ui->streamPlot->graph(0)->pen();
|
|
|
|
QCPItemTracer::TracerStyle tstyle = QCPItemTracer::tsCrosshair;
|
|
|
|
QPen tr_pen = QPen(tracer_->pen());
|
|
|
|
QColor tr_color = sp_pen.color();
|
|
|
|
|
|
|
|
if (force_default || tracer_->style() != QCPItemTracer::tsCircle) {
|
|
|
|
tstyle = QCPItemTracer::tsCircle;
|
|
|
|
tr_color.setAlphaF(1.0);
|
|
|
|
tr_pen.setWidthF(1.5);
|
|
|
|
} else {
|
|
|
|
tr_color.setAlphaF(0.5);
|
|
|
|
tr_pen.setWidthF(1.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
tracer_->setStyle(tstyle);
|
|
|
|
tr_pen.setColor(tr_color);
|
|
|
|
tracer_->setPen(tr_pen);
|
|
|
|
ui->streamPlot->replot();
|
|
|
|
}
|
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
QRectF TCPStreamDialog::getZoomRanges(QRect zoom_rect)
|
|
|
|
{
|
|
|
|
QRectF zoom_ranges = QRectF();
|
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
QRect zr = zoom_rect.normalized();
|
|
|
|
|
|
|
|
if (zr.width() < min_zoom_pixels_ && zr.height() < min_zoom_pixels_) {
|
2013-09-09 01:40:06 +00:00
|
|
|
return zoom_ranges;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect ar = sp->axisRect()->rect();
|
|
|
|
if (ar.intersects(zr)) {
|
|
|
|
QRect zsr = ar.intersected(zr);
|
|
|
|
zoom_ranges.setX(sp->xAxis->range().lower
|
|
|
|
+ sp->xAxis->range().size() * (zsr.left() - ar.left()) / ar.width());
|
|
|
|
zoom_ranges.setWidth(sp->xAxis->range().size() * zsr.width() / ar.width());
|
|
|
|
|
|
|
|
// QRects grow down
|
|
|
|
zoom_ranges.setY(sp->yAxis->range().lower
|
|
|
|
+ sp->yAxis->range().size() * (ar.bottom() - zsr.bottom()) / ar.height());
|
|
|
|
zoom_ranges.setHeight(sp->yAxis->range().size() * zsr.height() / ar.height());
|
|
|
|
}
|
|
|
|
return zoom_ranges;
|
|
|
|
}
|
|
|
|
|
2013-08-30 21:15:24 +00:00
|
|
|
void TCPStreamDialog::graphClicked(QMouseEvent *event)
|
|
|
|
{
|
2013-09-12 21:37:47 +00:00
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
2013-08-30 21:15:24 +00:00
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
// mouse press on graph should reset focus to graph
|
|
|
|
sp->setFocus();
|
|
|
|
|
2013-09-12 23:46:56 +00:00
|
|
|
if (event->button() == Qt::RightButton) {
|
|
|
|
// XXX We should find some way to get streamPlot to handle a
|
|
|
|
// contextMenuEvent instead.
|
2022-03-22 21:47:18 +00:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(6, 0 ,0)
|
2022-07-03 11:35:48 +00:00
|
|
|
ctx_menu_.popup(event->globalPosition().toPoint());
|
2022-03-22 21:47:18 +00:00
|
|
|
#else
|
2022-07-03 11:35:48 +00:00
|
|
|
ctx_menu_.popup(event->globalPos());
|
2022-03-22 21:47:18 +00:00
|
|
|
#endif
|
2013-09-12 23:46:56 +00:00
|
|
|
} else if (mouse_drags_) {
|
2013-09-12 21:37:47 +00:00
|
|
|
if (sp->axisRect()->rect().contains(event->pos())) {
|
|
|
|
sp->setCursor(QCursor(Qt::ClosedHandCursor));
|
|
|
|
}
|
2013-09-12 23:46:56 +00:00
|
|
|
on_actionGoToPacket_triggered();
|
2013-09-09 01:40:06 +00:00
|
|
|
} else {
|
|
|
|
if (!rubber_band_) {
|
Add a Qt I/O Graph dialog.
For each graph you can set:
- Its visibility
- A name
- A display filter
- Color, from a fixed list
- Plot style: Line, Impulse, Bar, Stacked Bar, Dot, Square, Diamond
- Basic Y Axes (packets/s, bytes/s, bits/s)
- Computed Y Axes (SUM, MIN, AVG, MAX)
- Smoothing
You can pan and zoom using the mouse and keyboard. Clicking on a graph
selects the last packet for that interval. If all graphs have the same Y
axis a single label is shown, otherwise a legend is shown.
The time scale (X axis) can be toggled between relative seconds and the
time of day.
Graphs can be saved as PDF, PNG, BMP, and JPEG. Settings are "sticky"
via the io_graphs UAT.
To do:
- Minimize graph drawing delays.
- Figure out why smoothing differs from GTK+
- Everything else at the top of io_graph_dialog.cpp
- Fix empty resets.
A fair amount of code was copied from TCPStreamDialog. We might want to
subclass QCustomPlot and place the shared code there.
Move common syntax checking to SyntaxLineEdit.
Move some common code from ui/gtk/io_stat.c to ui/io_graph_item.[ch] and
use it in both GTK+ and Qt.
Make the io_graph_item_t array allocation in io_stat.c static. The
behavior should be identical and this gives us additional compile-time
checks.
Change-Id: I9a3d544469b7048f0761fdbf7bcf20f44ae76577
Reviewed-on: https://code.wireshark.org/review/435
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Tested-by: Gerald Combs <gerald@wireshark.org>
2014-02-12 00:07:10 +00:00
|
|
|
rubber_band_ = new QRubberBand(QRubberBand::Rectangle, sp);
|
2013-09-09 01:40:06 +00:00
|
|
|
}
|
|
|
|
rb_origin_ = event->pos();
|
|
|
|
rubber_band_->setGeometry(QRect(rb_origin_, QSize()));
|
|
|
|
rubber_band_->show();
|
2013-08-30 21:15:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-07 06:24:50 +00:00
|
|
|
void TCPStreamDialog::axisClicked(QCPAxis *axis, QCPAxis::SelectablePart, QMouseEvent *)
|
2013-08-30 21:15:24 +00:00
|
|
|
{
|
2013-09-09 01:40:06 +00:00
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
2013-08-30 21:15:24 +00:00
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
if (axis == sp->xAxis) {
|
2013-09-08 01:25:27 +00:00
|
|
|
switch (graph_.type) {
|
|
|
|
case GRAPH_THROUGHPUT:
|
2013-09-09 01:40:06 +00:00
|
|
|
case GRAPH_TSEQ_STEVENS:
|
|
|
|
case GRAPH_TSEQ_TCPTRACE:
|
|
|
|
case GRAPH_WSCALE:
|
2013-09-08 01:25:27 +00:00
|
|
|
case GRAPH_RTT:
|
2017-01-21 04:57:09 +00:00
|
|
|
ts_origin_conn_ = ts_origin_conn_ ? false : true;
|
2013-09-09 01:40:06 +00:00
|
|
|
fillGraph();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (axis == sp->yAxis) {
|
|
|
|
switch (graph_.type) {
|
|
|
|
case GRAPH_TSEQ_STEVENS:
|
|
|
|
case GRAPH_TSEQ_TCPTRACE:
|
|
|
|
seq_origin_zero_ = seq_origin_zero_ ? false : true;
|
|
|
|
fillGraph();
|
|
|
|
break;
|
2013-09-08 01:25:27 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-08-30 21:15:24 +00:00
|
|
|
}
|
2013-09-09 01:40:06 +00:00
|
|
|
}
|
2013-08-30 21:15:24 +00:00
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
// Setting mouseTracking on our streamPlot may not be as reliable
|
|
|
|
// as we need. If it's not we might want to poll the mouse position
|
|
|
|
// using a QTimer instead.
|
|
|
|
void TCPStreamDialog::mouseMoved(QMouseEvent *event)
|
|
|
|
{
|
2013-09-12 21:37:47 +00:00
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
Qt::CursorShape shape = Qt::ArrowCursor;
|
|
|
|
if (event) {
|
2013-09-13 22:29:05 +00:00
|
|
|
if (event->buttons().testFlag(Qt::LeftButton)) {
|
2013-09-12 21:37:47 +00:00
|
|
|
if (mouse_drags_) {
|
|
|
|
shape = Qt::ClosedHandCursor;
|
|
|
|
} else {
|
|
|
|
shape = Qt::CrossCursor;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (sp->axisRect()->rect().contains(event->pos())) {
|
|
|
|
if (mouse_drags_) {
|
|
|
|
shape = Qt::OpenHandCursor;
|
|
|
|
} else {
|
|
|
|
shape = Qt::CrossCursor;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sp->setCursor(QCursor(shape));
|
|
|
|
|
2013-09-16 17:28:42 +00:00
|
|
|
QString hint = "<small><i>";
|
2013-09-09 01:40:06 +00:00
|
|
|
if (mouse_drags_) {
|
|
|
|
double tr_key = tracer_->position->key();
|
|
|
|
struct segment *packet_seg = NULL;
|
|
|
|
packet_num_ = 0;
|
|
|
|
|
2013-09-16 00:50:23 +00:00
|
|
|
// XXX If we have multiple packets with the same timestamp tr_key
|
|
|
|
// may not return the packet we want. It might be possible to fudge
|
|
|
|
// unique keys using nextafter().
|
2013-09-09 01:40:06 +00:00
|
|
|
if (event && tracer_->graph() && tracer_->position->axisRect()->rect().contains(event->pos())) {
|
|
|
|
switch (graph_.type) {
|
|
|
|
case GRAPH_TSEQ_STEVENS:
|
2013-09-13 22:29:05 +00:00
|
|
|
case GRAPH_TSEQ_TCPTRACE:
|
2013-09-09 01:40:06 +00:00
|
|
|
case GRAPH_THROUGHPUT:
|
|
|
|
case GRAPH_WSCALE:
|
|
|
|
packet_seg = time_stamp_map_.value(tr_key, NULL);
|
|
|
|
break;
|
2017-09-03 14:07:37 +00:00
|
|
|
case GRAPH_RTT:
|
|
|
|
if (ui->bySeqNumberCheckBox->isChecked())
|
|
|
|
packet_seg = sequence_num_map_.value(tr_key, NULL);
|
|
|
|
else
|
|
|
|
packet_seg = time_stamp_map_.value(tr_key, NULL);
|
2013-09-09 01:40:06 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!packet_seg) {
|
|
|
|
tracer_->setVisible(false);
|
2013-09-16 17:28:42 +00:00
|
|
|
hint += "Hover over the graph for details. " + stream_desc_ + "</i></small>";
|
|
|
|
ui->hintLabel->setText(hint);
|
2013-09-09 01:40:06 +00:00
|
|
|
ui->streamPlot->replot();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tracer_->setVisible(true);
|
|
|
|
packet_num_ = packet_seg->num;
|
2013-09-16 17:28:42 +00:00
|
|
|
hint += tr("%1 %2 (%3s len %4 seq %5 ack %6 win %7)")
|
2013-09-09 01:40:06 +00:00
|
|
|
.arg(cap_file_ ? tr("Click to select packet") : tr("Packet"))
|
|
|
|
.arg(packet_num_)
|
|
|
|
.arg(QString::number(packet_seg->rel_secs + packet_seg->rel_usecs / 1000000.0, 'g', 4))
|
|
|
|
.arg(packet_seg->th_seglen)
|
|
|
|
.arg(packet_seg->th_seq)
|
|
|
|
.arg(packet_seg->th_ack)
|
|
|
|
.arg(packet_seg->th_win);
|
|
|
|
tracer_->setGraphKey(ui->streamPlot->xAxis->pixelToCoord(event->pos().x()));
|
Add a Qt I/O Graph dialog.
For each graph you can set:
- Its visibility
- A name
- A display filter
- Color, from a fixed list
- Plot style: Line, Impulse, Bar, Stacked Bar, Dot, Square, Diamond
- Basic Y Axes (packets/s, bytes/s, bits/s)
- Computed Y Axes (SUM, MIN, AVG, MAX)
- Smoothing
You can pan and zoom using the mouse and keyboard. Clicking on a graph
selects the last packet for that interval. If all graphs have the same Y
axis a single label is shown, otherwise a legend is shown.
The time scale (X axis) can be toggled between relative seconds and the
time of day.
Graphs can be saved as PDF, PNG, BMP, and JPEG. Settings are "sticky"
via the io_graphs UAT.
To do:
- Minimize graph drawing delays.
- Figure out why smoothing differs from GTK+
- Everything else at the top of io_graph_dialog.cpp
- Fix empty resets.
A fair amount of code was copied from TCPStreamDialog. We might want to
subclass QCustomPlot and place the shared code there.
Move common syntax checking to SyntaxLineEdit.
Move some common code from ui/gtk/io_stat.c to ui/io_graph_item.[ch] and
use it in both GTK+ and Qt.
Make the io_graph_item_t array allocation in io_stat.c static. The
behavior should be identical and this gives us additional compile-time
checks.
Change-Id: I9a3d544469b7048f0761fdbf7bcf20f44ae76577
Reviewed-on: https://code.wireshark.org/review/435
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Tested-by: Gerald Combs <gerald@wireshark.org>
2014-02-12 00:07:10 +00:00
|
|
|
sp->replot();
|
2013-09-09 01:40:06 +00:00
|
|
|
} else {
|
2014-02-23 19:05:04 +00:00
|
|
|
if (rubber_band_ && rubber_band_->isVisible() && event) {
|
2013-09-09 01:40:06 +00:00
|
|
|
rubber_band_->setGeometry(QRect(rb_origin_, event->pos()).normalized());
|
|
|
|
QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos()));
|
|
|
|
if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) {
|
2013-09-16 17:28:42 +00:00
|
|
|
hint += tr("Release to zoom, x = %1 to %2, y = %3 to %4")
|
2013-09-09 01:40:06 +00:00
|
|
|
.arg(zoom_ranges.x())
|
|
|
|
.arg(zoom_ranges.x() + zoom_ranges.width())
|
|
|
|
.arg(zoom_ranges.y())
|
|
|
|
.arg(zoom_ranges.y() + zoom_ranges.height());
|
|
|
|
} else {
|
2013-09-16 17:28:42 +00:00
|
|
|
hint += tr("Unable to select range.");
|
2013-09-09 01:40:06 +00:00
|
|
|
}
|
2013-09-16 17:28:42 +00:00
|
|
|
} else {
|
|
|
|
hint += tr("Click to select a portion of the graph.");
|
2013-09-09 01:40:06 +00:00
|
|
|
}
|
2013-08-30 21:15:24 +00:00
|
|
|
}
|
2013-09-16 17:28:42 +00:00
|
|
|
hint += " " + stream_desc_ + "</i></small>";
|
|
|
|
ui->hintLabel->setText(hint);
|
2013-09-09 01:40:06 +00:00
|
|
|
}
|
2013-08-30 21:15:24 +00:00
|
|
|
|
2013-09-09 01:40:06 +00:00
|
|
|
void TCPStreamDialog::mouseReleased(QMouseEvent *event)
|
|
|
|
{
|
2017-01-21 04:57:09 +00:00
|
|
|
if (rubber_band_ && rubber_band_->isVisible()) {
|
2013-09-09 01:40:06 +00:00
|
|
|
rubber_band_->hide();
|
|
|
|
if (!mouse_drags_) {
|
|
|
|
QRectF zoom_ranges = getZoomRanges(QRect(rb_origin_, event->pos()));
|
|
|
|
if (zoom_ranges.width() > 0.0 && zoom_ranges.height() > 0.0) {
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
sp->xAxis->setRangeLower(zoom_ranges.x());
|
|
|
|
sp->xAxis->setRangeUpper(zoom_ranges.x() + zoom_ranges.width());
|
|
|
|
sp->yAxis->setRangeLower(zoom_ranges.y());
|
|
|
|
sp->yAxis->setRangeUpper(zoom_ranges.y() + zoom_ranges.height());
|
|
|
|
sp->replot();
|
|
|
|
}
|
|
|
|
}
|
2013-09-12 21:37:47 +00:00
|
|
|
} else if (ui->streamPlot->cursor().shape() == Qt::ClosedHandCursor) {
|
|
|
|
ui->streamPlot->setCursor(QCursor(Qt::OpenHandCursor));
|
2013-09-09 01:40:06 +00:00
|
|
|
}
|
2013-08-30 21:15:24 +00:00
|
|
|
}
|
|
|
|
|
2013-09-06 00:41:07 +00:00
|
|
|
void TCPStreamDialog::transformYRange(const QCPRange &y_range1)
|
2013-09-04 23:15:59 +00:00
|
|
|
{
|
2013-09-06 00:41:07 +00:00
|
|
|
if (y_axis_xfrm_.isIdentity()) return;
|
2013-09-04 23:15:59 +00:00
|
|
|
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
2013-09-06 00:41:07 +00:00
|
|
|
QLineF yp1 = QLineF(1.0, y_range1.lower, 1.0, y_range1.upper);
|
|
|
|
QLineF yp2 = y_axis_xfrm_.map(yp1);
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-09-06 00:41:07 +00:00
|
|
|
sp->yAxis2->setRangeUpper(yp2.y2());
|
|
|
|
sp->yAxis2->setRangeLower(yp2.y1());
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
|
|
|
|
2020-05-05 18:58:52 +00:00
|
|
|
// XXX - We have similar code in io_graph_dialog and packet_diagram. Should this be a common routine?
|
2013-08-30 23:01:03 +00:00
|
|
|
void TCPStreamDialog::on_buttonBox_accepted()
|
|
|
|
{
|
|
|
|
QString file_name, extension;
|
2022-02-01 03:30:09 +00:00
|
|
|
QDir path(mainApp->lastOpenDir());
|
2013-08-30 23:01:03 +00:00
|
|
|
QString pdf_filter = tr("Portable Document Format (*.pdf)");
|
|
|
|
QString png_filter = tr("Portable Network Graphics (*.png)");
|
|
|
|
QString bmp_filter = tr("Windows Bitmap (*.bmp)");
|
|
|
|
// Gaze upon my beautiful graph with lossy artifacts!
|
|
|
|
QString jpeg_filter = tr("JPEG File Interchange Format (*.jpeg *.jpg)");
|
|
|
|
QString filter = QString("%1;;%2;;%3;;%4")
|
|
|
|
.arg(pdf_filter)
|
|
|
|
.arg(png_filter)
|
|
|
|
.arg(bmp_filter)
|
|
|
|
.arg(jpeg_filter);
|
|
|
|
|
2022-02-01 03:30:09 +00:00
|
|
|
file_name = WiresharkFileDialog::getSaveFileName(this, mainApp->windowTitleString(tr("Save Graph As…")),
|
2013-08-30 23:01:03 +00:00
|
|
|
path.canonicalPath(), filter, &extension);
|
|
|
|
|
|
|
|
if (file_name.length() > 0) {
|
|
|
|
bool save_ok = false;
|
|
|
|
if (extension.compare(pdf_filter) == 0) {
|
|
|
|
save_ok = ui->streamPlot->savePdf(file_name);
|
|
|
|
} else if (extension.compare(png_filter) == 0) {
|
|
|
|
save_ok = ui->streamPlot->savePng(file_name);
|
|
|
|
} else if (extension.compare(bmp_filter) == 0) {
|
|
|
|
save_ok = ui->streamPlot->saveBmp(file_name);
|
|
|
|
} else if (extension.compare(jpeg_filter) == 0) {
|
|
|
|
save_ok = ui->streamPlot->saveJpg(file_name);
|
|
|
|
}
|
|
|
|
// else error dialog?
|
|
|
|
if (save_ok) {
|
2022-02-01 03:30:09 +00:00
|
|
|
mainApp->setLastOpenDirFromFilename(file_name);
|
2013-08-30 23:01:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
void TCPStreamDialog::on_graphTypeComboBox_currentIndexChanged(int index)
|
|
|
|
{
|
|
|
|
if (index < 0) return;
|
2013-09-16 01:19:14 +00:00
|
|
|
graph_.type = static_cast<tcp_graph_type>(ui->graphTypeComboBox->itemData(index).toInt());
|
2016-12-06 07:50:45 +00:00
|
|
|
showWidgetsForGraphType();
|
2016-12-22 21:03:03 +00:00
|
|
|
|
|
|
|
fillGraph(/*reset_axes=*/true, /*set_focus=*/false);
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
|
|
|
|
2013-09-07 03:51:27 +00:00
|
|
|
void TCPStreamDialog::on_resetButton_clicked()
|
|
|
|
{
|
|
|
|
resetAxes();
|
|
|
|
}
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
void TCPStreamDialog::setCaptureFile(capture_file *cf)
|
|
|
|
{
|
2013-09-06 00:41:07 +00:00
|
|
|
if (!cf) { // We only want to know when the file closes.
|
|
|
|
cap_file_ = NULL;
|
|
|
|
}
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
|
|
|
|
2016-12-06 07:50:45 +00:00
|
|
|
void TCPStreamDialog::updateGraph()
|
|
|
|
{
|
2016-12-22 21:03:03 +00:00
|
|
|
graph_updater_.doUpdate();
|
2016-12-06 07:50:45 +00:00
|
|
|
}
|
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
void TCPStreamDialog::on_streamNumberSpinBox_valueChanged(int new_stream)
|
2013-09-12 21:37:47 +00:00
|
|
|
{
|
2013-09-13 22:29:05 +00:00
|
|
|
if (new_stream >= 0 && new_stream < int(get_tcp_stream_count())) {
|
2016-12-22 21:03:03 +00:00
|
|
|
graph_updater_.triggerUpdate(1000, /*reset_axes =*/true);
|
2016-12-06 07:50:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
void TCPStreamDialog::on_streamNumberSpinBox_editingFinished()
|
|
|
|
{
|
|
|
|
updateGraph();
|
|
|
|
}
|
|
|
|
|
2016-12-06 07:50:45 +00:00
|
|
|
void TCPStreamDialog::on_maWindowSizeSpinBox_valueChanged(double new_ma_size)
|
|
|
|
{
|
|
|
|
if (new_ma_size > 0.0) {
|
|
|
|
ma_window_size_ = new_ma_size;
|
2016-12-22 21:03:03 +00:00
|
|
|
graph_updater_.triggerUpdate(1000, /*reset_axes =*/false);
|
2013-09-12 21:37:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-12-06 07:50:45 +00:00
|
|
|
void TCPStreamDialog::on_maWindowSizeSpinBox_editingFinished()
|
|
|
|
{
|
2016-12-22 21:03:03 +00:00
|
|
|
updateGraph();
|
2016-12-06 07:50:45 +00:00
|
|
|
}
|
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
void TCPStreamDialog::on_selectSACKsCheckBox_stateChanged(int /* state */)
|
2016-12-06 07:50:45 +00:00
|
|
|
{
|
2016-12-22 21:03:03 +00:00
|
|
|
fillGraph(/*reset_axes=*/false, /*set_focus=*/false);
|
2016-12-06 07:50:45 +00:00
|
|
|
}
|
|
|
|
|
2013-09-08 01:25:27 +00:00
|
|
|
void TCPStreamDialog::on_otherDirectionButton_clicked()
|
|
|
|
{
|
2013-09-13 22:29:05 +00:00
|
|
|
on_actionSwitchDirection_triggered();
|
2013-09-08 01:25:27 +00:00
|
|
|
}
|
|
|
|
|
2013-09-12 21:37:47 +00:00
|
|
|
void TCPStreamDialog::on_dragRadioButton_toggled(bool checked)
|
2013-09-09 01:40:06 +00:00
|
|
|
{
|
2017-01-21 04:57:09 +00:00
|
|
|
if (checked) {
|
|
|
|
mouse_drags_ = true;
|
|
|
|
if (rubber_band_ && rubber_band_->isVisible())
|
|
|
|
rubber_band_->hide();
|
|
|
|
ui->streamPlot->setInteractions(
|
|
|
|
QCP::iRangeDrag |
|
|
|
|
QCP::iRangeZoom
|
|
|
|
);
|
|
|
|
}
|
2013-09-09 01:40:06 +00:00
|
|
|
}
|
|
|
|
|
2013-11-12 01:08:08 +00:00
|
|
|
void TCPStreamDialog::on_zoomRadioButton_toggled(bool checked)
|
2013-09-09 01:40:06 +00:00
|
|
|
{
|
2017-01-21 04:57:09 +00:00
|
|
|
if (checked) {
|
|
|
|
mouse_drags_ = false;
|
2020-05-28 18:40:08 +00:00
|
|
|
ui->streamPlot->setInteractions(QCP::Interactions());
|
2017-01-21 04:57:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-03 14:07:37 +00:00
|
|
|
void TCPStreamDialog::on_bySeqNumberCheckBox_stateChanged(int /* state */)
|
|
|
|
{
|
|
|
|
fillGraph(/*reset_axes=*/true, /*set_focus=*/false);
|
|
|
|
}
|
|
|
|
|
2017-01-21 04:57:09 +00:00
|
|
|
void TCPStreamDialog::on_showSegLengthCheckBox_stateChanged(int state)
|
|
|
|
{
|
|
|
|
bool visible = (state != 0);
|
|
|
|
if (graph_.type == GRAPH_THROUGHPUT && base_graph_ != NULL) {
|
|
|
|
base_graph_->setVisible(visible);
|
|
|
|
tracer_->setGraph(visible ? base_graph_ : NULL);
|
|
|
|
ui->streamPlot->replot();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_showThroughputCheckBox_stateChanged(int state)
|
|
|
|
{
|
|
|
|
bool visible = (state != 0);
|
|
|
|
if (graph_.type == GRAPH_THROUGHPUT && tput_graph_ != NULL) {
|
|
|
|
tput_graph_->setVisible(visible);
|
|
|
|
ui->streamPlot->replot();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_showGoodputCheckBox_stateChanged(int state)
|
|
|
|
{
|
|
|
|
bool visible = (state != 0);
|
|
|
|
if (graph_.type == GRAPH_THROUGHPUT && goodput_graph_ != NULL) {
|
|
|
|
goodput_graph_->setVisible(visible);
|
|
|
|
ui->streamPlot->replot();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_showRcvWinCheckBox_stateChanged(int state)
|
|
|
|
{
|
|
|
|
bool visible = (state != 0);
|
|
|
|
if (graph_.type == GRAPH_WSCALE && rwin_graph_ != NULL) {
|
|
|
|
rwin_graph_->setVisible(visible);
|
|
|
|
ui->streamPlot->replot();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_showBytesOutCheckBox_stateChanged(int state)
|
|
|
|
{
|
|
|
|
bool visible = (state != 0);
|
|
|
|
if (graph_.type == GRAPH_WSCALE && base_graph_ != NULL) {
|
|
|
|
base_graph_->setVisible(visible);
|
|
|
|
tracer_->setGraph(visible ? base_graph_ : NULL);
|
|
|
|
ui->streamPlot->replot();
|
|
|
|
}
|
2013-09-09 01:40:06 +00:00
|
|
|
}
|
|
|
|
|
2013-09-12 23:46:56 +00:00
|
|
|
void TCPStreamDialog::on_actionZoomIn_triggered()
|
|
|
|
{
|
|
|
|
zoomAxes(true);
|
|
|
|
}
|
|
|
|
|
2015-10-09 05:19:21 +00:00
|
|
|
void TCPStreamDialog::on_actionZoomInX_triggered()
|
|
|
|
{
|
|
|
|
zoomXAxis(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionZoomInY_triggered()
|
|
|
|
{
|
|
|
|
zoomYAxis(true);
|
|
|
|
}
|
|
|
|
|
2013-09-12 23:46:56 +00:00
|
|
|
void TCPStreamDialog::on_actionZoomOut_triggered()
|
|
|
|
{
|
|
|
|
zoomAxes(false);
|
|
|
|
}
|
|
|
|
|
2015-10-09 05:19:21 +00:00
|
|
|
void TCPStreamDialog::on_actionZoomOutX_triggered()
|
|
|
|
{
|
|
|
|
zoomXAxis(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionZoomOutY_triggered()
|
|
|
|
{
|
|
|
|
zoomYAxis(false);
|
|
|
|
}
|
|
|
|
|
2013-09-12 23:46:56 +00:00
|
|
|
void TCPStreamDialog::on_actionReset_triggered()
|
|
|
|
{
|
|
|
|
on_resetButton_clicked();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionMoveRight10_triggered()
|
|
|
|
{
|
|
|
|
panAxes(10, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionMoveLeft10_triggered()
|
|
|
|
{
|
|
|
|
panAxes(-10, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionMoveUp10_triggered()
|
|
|
|
{
|
Add a Qt I/O Graph dialog.
For each graph you can set:
- Its visibility
- A name
- A display filter
- Color, from a fixed list
- Plot style: Line, Impulse, Bar, Stacked Bar, Dot, Square, Diamond
- Basic Y Axes (packets/s, bytes/s, bits/s)
- Computed Y Axes (SUM, MIN, AVG, MAX)
- Smoothing
You can pan and zoom using the mouse and keyboard. Clicking on a graph
selects the last packet for that interval. If all graphs have the same Y
axis a single label is shown, otherwise a legend is shown.
The time scale (X axis) can be toggled between relative seconds and the
time of day.
Graphs can be saved as PDF, PNG, BMP, and JPEG. Settings are "sticky"
via the io_graphs UAT.
To do:
- Minimize graph drawing delays.
- Figure out why smoothing differs from GTK+
- Everything else at the top of io_graph_dialog.cpp
- Fix empty resets.
A fair amount of code was copied from TCPStreamDialog. We might want to
subclass QCustomPlot and place the shared code there.
Move common syntax checking to SyntaxLineEdit.
Move some common code from ui/gtk/io_stat.c to ui/io_graph_item.[ch] and
use it in both GTK+ and Qt.
Make the io_graph_item_t array allocation in io_stat.c static. The
behavior should be identical and this gives us additional compile-time
checks.
Change-Id: I9a3d544469b7048f0761fdbf7bcf20f44ae76577
Reviewed-on: https://code.wireshark.org/review/435
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Tested-by: Gerald Combs <gerald@wireshark.org>
2014-02-12 00:07:10 +00:00
|
|
|
panAxes(0, 10);
|
2013-09-12 23:46:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionMoveDown10_triggered()
|
|
|
|
{
|
Add a Qt I/O Graph dialog.
For each graph you can set:
- Its visibility
- A name
- A display filter
- Color, from a fixed list
- Plot style: Line, Impulse, Bar, Stacked Bar, Dot, Square, Diamond
- Basic Y Axes (packets/s, bytes/s, bits/s)
- Computed Y Axes (SUM, MIN, AVG, MAX)
- Smoothing
You can pan and zoom using the mouse and keyboard. Clicking on a graph
selects the last packet for that interval. If all graphs have the same Y
axis a single label is shown, otherwise a legend is shown.
The time scale (X axis) can be toggled between relative seconds and the
time of day.
Graphs can be saved as PDF, PNG, BMP, and JPEG. Settings are "sticky"
via the io_graphs UAT.
To do:
- Minimize graph drawing delays.
- Figure out why smoothing differs from GTK+
- Everything else at the top of io_graph_dialog.cpp
- Fix empty resets.
A fair amount of code was copied from TCPStreamDialog. We might want to
subclass QCustomPlot and place the shared code there.
Move common syntax checking to SyntaxLineEdit.
Move some common code from ui/gtk/io_stat.c to ui/io_graph_item.[ch] and
use it in both GTK+ and Qt.
Make the io_graph_item_t array allocation in io_stat.c static. The
behavior should be identical and this gives us additional compile-time
checks.
Change-Id: I9a3d544469b7048f0761fdbf7bcf20f44ae76577
Reviewed-on: https://code.wireshark.org/review/435
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Tested-by: Gerald Combs <gerald@wireshark.org>
2014-02-12 00:07:10 +00:00
|
|
|
panAxes(0, -10);
|
2013-09-12 23:46:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionMoveRight1_triggered()
|
|
|
|
{
|
|
|
|
panAxes(1, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionMoveLeft1_triggered()
|
|
|
|
{
|
|
|
|
panAxes(-1, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionMoveUp1_triggered()
|
|
|
|
{
|
Add a Qt I/O Graph dialog.
For each graph you can set:
- Its visibility
- A name
- A display filter
- Color, from a fixed list
- Plot style: Line, Impulse, Bar, Stacked Bar, Dot, Square, Diamond
- Basic Y Axes (packets/s, bytes/s, bits/s)
- Computed Y Axes (SUM, MIN, AVG, MAX)
- Smoothing
You can pan and zoom using the mouse and keyboard. Clicking on a graph
selects the last packet for that interval. If all graphs have the same Y
axis a single label is shown, otherwise a legend is shown.
The time scale (X axis) can be toggled between relative seconds and the
time of day.
Graphs can be saved as PDF, PNG, BMP, and JPEG. Settings are "sticky"
via the io_graphs UAT.
To do:
- Minimize graph drawing delays.
- Figure out why smoothing differs from GTK+
- Everything else at the top of io_graph_dialog.cpp
- Fix empty resets.
A fair amount of code was copied from TCPStreamDialog. We might want to
subclass QCustomPlot and place the shared code there.
Move common syntax checking to SyntaxLineEdit.
Move some common code from ui/gtk/io_stat.c to ui/io_graph_item.[ch] and
use it in both GTK+ and Qt.
Make the io_graph_item_t array allocation in io_stat.c static. The
behavior should be identical and this gives us additional compile-time
checks.
Change-Id: I9a3d544469b7048f0761fdbf7bcf20f44ae76577
Reviewed-on: https://code.wireshark.org/review/435
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Tested-by: Gerald Combs <gerald@wireshark.org>
2014-02-12 00:07:10 +00:00
|
|
|
panAxes(0, 1);
|
2013-09-12 23:46:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionMoveDown1_triggered()
|
|
|
|
{
|
Add a Qt I/O Graph dialog.
For each graph you can set:
- Its visibility
- A name
- A display filter
- Color, from a fixed list
- Plot style: Line, Impulse, Bar, Stacked Bar, Dot, Square, Diamond
- Basic Y Axes (packets/s, bytes/s, bits/s)
- Computed Y Axes (SUM, MIN, AVG, MAX)
- Smoothing
You can pan and zoom using the mouse and keyboard. Clicking on a graph
selects the last packet for that interval. If all graphs have the same Y
axis a single label is shown, otherwise a legend is shown.
The time scale (X axis) can be toggled between relative seconds and the
time of day.
Graphs can be saved as PDF, PNG, BMP, and JPEG. Settings are "sticky"
via the io_graphs UAT.
To do:
- Minimize graph drawing delays.
- Figure out why smoothing differs from GTK+
- Everything else at the top of io_graph_dialog.cpp
- Fix empty resets.
A fair amount of code was copied from TCPStreamDialog. We might want to
subclass QCustomPlot and place the shared code there.
Move common syntax checking to SyntaxLineEdit.
Move some common code from ui/gtk/io_stat.c to ui/io_graph_item.[ch] and
use it in both GTK+ and Qt.
Make the io_graph_item_t array allocation in io_stat.c static. The
behavior should be identical and this gives us additional compile-time
checks.
Change-Id: I9a3d544469b7048f0761fdbf7bcf20f44ae76577
Reviewed-on: https://code.wireshark.org/review/435
Reviewed-by: Gerald Combs <gerald@wireshark.org>
Tested-by: Gerald Combs <gerald@wireshark.org>
2014-02-12 00:07:10 +00:00
|
|
|
panAxes(0, -1);
|
2013-09-12 23:46:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionNextStream_triggered()
|
|
|
|
{
|
2013-09-13 22:29:05 +00:00
|
|
|
if (int(graph_.stream) < int(get_tcp_stream_count()) - 1) {
|
|
|
|
ui->streamNumberSpinBox->setValue(graph_.stream + 1);
|
2016-12-22 21:03:03 +00:00
|
|
|
updateGraph();
|
2013-09-13 22:29:05 +00:00
|
|
|
}
|
2013-09-12 23:46:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionPreviousStream_triggered()
|
|
|
|
{
|
2013-09-13 22:29:05 +00:00
|
|
|
if (graph_.stream > 0) {
|
|
|
|
ui->streamNumberSpinBox->setValue(graph_.stream - 1);
|
2016-12-22 21:03:03 +00:00
|
|
|
updateGraph();
|
2013-09-13 22:29:05 +00:00
|
|
|
}
|
2013-09-12 23:46:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionSwitchDirection_triggered()
|
|
|
|
{
|
2013-09-13 22:29:05 +00:00
|
|
|
address tmp_addr;
|
|
|
|
guint16 tmp_port;
|
|
|
|
|
2013-09-16 21:33:26 +00:00
|
|
|
copy_address(&tmp_addr, &graph_.src_address);
|
2013-09-13 22:29:05 +00:00
|
|
|
tmp_port = graph_.src_port;
|
2020-04-13 21:00:48 +00:00
|
|
|
free_address(&graph_.src_address);
|
2013-09-16 21:33:26 +00:00
|
|
|
copy_address(&graph_.src_address, &graph_.dst_address);
|
2013-09-13 22:29:05 +00:00
|
|
|
graph_.src_port = graph_.dst_port;
|
2020-04-13 21:00:48 +00:00
|
|
|
free_address(&graph_.dst_address);
|
2013-09-16 21:33:26 +00:00
|
|
|
copy_address(&graph_.dst_address, &tmp_addr);
|
2013-09-13 22:29:05 +00:00
|
|
|
graph_.dst_port = tmp_port;
|
2020-04-13 21:00:48 +00:00
|
|
|
free_address(&tmp_addr);
|
2013-09-13 22:29:05 +00:00
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
fillGraph(/*reset_axes=*/true, /*set_focus=*/false);
|
2013-09-12 23:46:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionGoToPacket_triggered()
|
|
|
|
{
|
|
|
|
if (tracer_->visible() && cap_file_ && packet_num_ > 0) {
|
|
|
|
emit goToPacket(packet_num_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionDragZoom_triggered()
|
|
|
|
{
|
|
|
|
if (mouse_drags_) {
|
2013-11-12 01:08:08 +00:00
|
|
|
ui->zoomRadioButton->toggle();
|
2013-09-12 23:46:56 +00:00
|
|
|
} else {
|
|
|
|
ui->dragRadioButton->toggle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionToggleSequenceNumbers_triggered()
|
|
|
|
{
|
|
|
|
seq_origin_zero_ = seq_origin_zero_ ? false : true;
|
|
|
|
fillGraph();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionToggleTimeOrigin_triggered()
|
|
|
|
{
|
|
|
|
ts_origin_conn_ = ts_origin_conn_ ? false : true;
|
|
|
|
fillGraph();
|
|
|
|
}
|
|
|
|
|
2013-09-13 22:29:05 +00:00
|
|
|
void TCPStreamDialog::on_actionRoundTripTime_triggered()
|
|
|
|
{
|
2013-09-16 01:19:14 +00:00
|
|
|
ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(GRAPH_RTT));
|
2013-09-13 22:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionThroughput_triggered()
|
|
|
|
{
|
2013-09-16 01:19:14 +00:00
|
|
|
ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(GRAPH_THROUGHPUT));
|
2013-09-13 22:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionStevens_triggered()
|
|
|
|
{
|
2013-09-16 01:19:14 +00:00
|
|
|
ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(GRAPH_TSEQ_STEVENS));
|
2013-09-13 22:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionTcptrace_triggered()
|
|
|
|
{
|
2013-09-16 01:19:14 +00:00
|
|
|
ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(GRAPH_TSEQ_TCPTRACE));
|
2013-09-13 22:29:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::on_actionWindowScaling_triggered()
|
|
|
|
{
|
2013-09-16 01:19:14 +00:00
|
|
|
ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(GRAPH_WSCALE));
|
2013-09-13 22:29:05 +00:00
|
|
|
}
|
|
|
|
|
2016-12-22 21:03:03 +00:00
|
|
|
void TCPStreamDialog::GraphUpdater::triggerUpdate(int timeout, bool reset_axes)
|
|
|
|
{
|
|
|
|
if (!hasPendingUpdate()) {
|
|
|
|
graph_update_timer_ = new QTimer(dialog_);
|
|
|
|
graph_update_timer_->setSingleShot(true);
|
|
|
|
dialog_->connect(graph_update_timer_, SIGNAL(timeout()), dialog_, SLOT(updateGraph()));
|
|
|
|
}
|
2017-01-08 01:09:23 +00:00
|
|
|
reset_axes_ = (reset_axes_ || reset_axes);
|
2016-12-22 21:03:03 +00:00
|
|
|
graph_update_timer_->start(timeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::GraphUpdater::clearPendingUpdate()
|
|
|
|
{
|
|
|
|
if (hasPendingUpdate()) {
|
|
|
|
if (graph_update_timer_->isActive())
|
|
|
|
graph_update_timer_->stop();
|
|
|
|
delete graph_update_timer_;
|
|
|
|
graph_update_timer_ = NULL;
|
|
|
|
reset_axes_ = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::GraphUpdater::doUpdate()
|
|
|
|
{
|
|
|
|
if (hasPendingUpdate()) {
|
|
|
|
bool reset_axes = reset_axes_;
|
|
|
|
clearPendingUpdate();
|
|
|
|
// if the stream has changed, update the data here
|
|
|
|
int new_stream = dialog_->ui->streamNumberSpinBox->value();
|
|
|
|
if ((int(dialog_->graph_.stream) != new_stream) &&
|
|
|
|
(new_stream >= 0 && new_stream < int(get_tcp_stream_count()))) {
|
|
|
|
dialog_->graph_.stream = new_stream;
|
|
|
|
dialog_->findStream();
|
|
|
|
}
|
|
|
|
dialog_->fillGraph(reset_axes, /*set_focus =*/false);
|
|
|
|
}
|
|
|
|
}
|
2018-03-21 22:03:53 +00:00
|
|
|
|
|
|
|
void TCPStreamDialog::on_buttonBox_helpRequested()
|
|
|
|
{
|
2022-02-01 03:30:09 +00:00
|
|
|
mainApp->helpTopicAction(HELP_STATS_TCP_STREAM_GRAPHS_DIALOG);
|
2018-03-21 22:03:53 +00:00
|
|
|
}
|