2013-08-27 18:13:20 +00:00
|
|
|
/* tcp_stream_dialog.cpp
|
|
|
|
*
|
|
|
|
* $Id$
|
|
|
|
*
|
|
|
|
* Wireshark - Network traffic analyzer
|
|
|
|
* By Gerald Combs <gerald@wireshark.org>
|
|
|
|
* Copyright 1998 Gerald Combs
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "tcp_stream_dialog.h"
|
|
|
|
#include "ui_tcp_stream_dialog.h"
|
|
|
|
|
2013-08-30 21:15:24 +00:00
|
|
|
#include "epan/to_str.h"
|
|
|
|
|
|
|
|
#include "ui/utf8_entities.h"
|
|
|
|
|
2013-08-30 23:01:03 +00:00
|
|
|
#include "wireshark_application.h"
|
2013-08-27 18:13:20 +00:00
|
|
|
#include "tango_colors.h"
|
|
|
|
|
2013-08-30 23:01:03 +00:00
|
|
|
#include <QDir>
|
|
|
|
#include <QFileDialog>
|
|
|
|
#include <QPushButton>
|
|
|
|
|
2013-08-27 18:13:20 +00:00
|
|
|
#include <QDebug>
|
|
|
|
|
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
|
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-08 01:25:27 +00:00
|
|
|
const QString average_throughput_label_ = QObject::tr("Avgerage Througput (bits/s)");
|
|
|
|
const QString round_trip_time_ms_label_ = QObject::tr("Round Trip Time (ms)");
|
|
|
|
const QString segment_length_label_ = QObject::tr("Segment Length (B)");
|
|
|
|
const QString sequence_number_label_ = QObject::tr("Relative Sequence Number (B)");
|
|
|
|
const QString time_s_label_ = QObject::tr("Time (s)");
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
Q_DECLARE_METATYPE(tcp_graph_type)
|
|
|
|
|
2013-08-27 18:13:20 +00:00
|
|
|
TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_type graph_type) :
|
|
|
|
QDialog(parent),
|
|
|
|
ui(new Ui::TCPStreamDialog),
|
2013-08-30 21:15:24 +00:00
|
|
|
cap_file_(cf),
|
2013-09-04 23:15:59 +00:00
|
|
|
tracer_(NULL),
|
|
|
|
num_dsegs_(-1),
|
|
|
|
num_acks_(-1),
|
|
|
|
num_sack_ranges_(-1)
|
2013-08-27 18:13:20 +00:00
|
|
|
{
|
|
|
|
struct segment current;
|
|
|
|
|
|
|
|
ui->setupUi(this);
|
|
|
|
|
|
|
|
if (!select_tcpip_session(cap_file_, ¤t)) {
|
|
|
|
done(QDialog::Rejected);
|
|
|
|
}
|
|
|
|
|
2013-08-30 21:15:24 +00:00
|
|
|
//#ifdef Q_OS_MAC
|
|
|
|
// ui->hintLabel->setAttribute(Qt::WA_MacSmallSize, true);
|
|
|
|
//#endif
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
ui->graphTypeComboBox->setUpdatesEnabled(false);
|
|
|
|
ui->graphTypeComboBox->addItem(tr("Time / Sequence (Stevens)"), qVariantFromValue(GRAPH_TSEQ_STEVENS));
|
|
|
|
ui->graphTypeComboBox->addItem(tr("Throughput"), qVariantFromValue(GRAPH_THROUGHPUT));
|
2013-09-08 01:25:27 +00:00
|
|
|
ui->graphTypeComboBox->addItem(tr("Round Trip Time"), qVariantFromValue(GRAPH_RTT));
|
2013-09-04 23:15:59 +00:00
|
|
|
ui->graphTypeComboBox->setCurrentIndex(-1);
|
|
|
|
ui->graphTypeComboBox->setUpdatesEnabled(true);
|
|
|
|
|
2013-08-27 18:13:20 +00:00
|
|
|
memset (&graph_, 0, sizeof(graph_));
|
|
|
|
graph_.type = graph_type;
|
2013-09-08 01:25:27 +00:00
|
|
|
COPY_ADDRESS(&graph_.src_address, ¤t.ip_src);
|
|
|
|
graph_.src_port = current.th_sport;
|
|
|
|
COPY_ADDRESS(&graph_.dst_address, ¤t.ip_dst);
|
|
|
|
graph_.dst_port = current.th_dport;
|
2013-08-27 23:28:35 +00:00
|
|
|
|
2013-08-30 21:15:24 +00:00
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
2013-09-06 02:00:14 +00:00
|
|
|
QCPPlotTitle *file_title = new QCPPlotTitle(sp, cf_get_display_name(cap_file_));
|
|
|
|
file_title->setFont(sp->xAxis->labelFont());
|
2013-09-04 23:15:59 +00:00
|
|
|
title_ = new QCPPlotTitle(sp);
|
|
|
|
tracer_ = new QCPItemTracer(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_);
|
|
|
|
sp->addGraph(); // 0 - All: Selectable segments
|
|
|
|
sp->addGraph(sp->xAxis, sp->yAxis2); // 1 - Throughput: Moving average
|
|
|
|
sp->addItem(tracer_);
|
|
|
|
|
|
|
|
// Fills the graph
|
|
|
|
ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(qVariantFromValue(graph_type)));
|
|
|
|
|
2013-08-30 21:15:24 +00:00
|
|
|
sp->setInteractions(
|
|
|
|
QCP::iRangeDrag |
|
|
|
|
QCP::iRangeZoom
|
|
|
|
);
|
|
|
|
sp->setMouseTracking(true);
|
2013-09-04 23:15:59 +00:00
|
|
|
sp->graph(0)->setPen(QPen(QBrush(graph_color_1), 0.25));
|
2013-08-30 21:15:24 +00:00
|
|
|
sp->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 5));
|
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-09-04 23:15:59 +00:00
|
|
|
// XXX QCustomPlot doesn't seem to draw any sort of focus indicator.
|
2013-08-30 21:15:24 +00:00
|
|
|
sp->setFocus();
|
|
|
|
|
2013-08-30 23:01:03 +00:00
|
|
|
QPushButton *save_bt = ui->buttonBox->button(QDialogButtonBox::Save);
|
|
|
|
save_bt->setText(tr("Save As..."));
|
|
|
|
|
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-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()));
|
2013-09-04 23:15:59 +00:00
|
|
|
|
|
|
|
mouseMoved(NULL);
|
2013-08-27 18:13:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TCPStreamDialog::~TCPStreamDialog()
|
|
|
|
{
|
|
|
|
delete ui;
|
|
|
|
}
|
|
|
|
|
2013-09-06 00:41:07 +00:00
|
|
|
void TCPStreamDialog::showEvent(QShowEvent *event)
|
|
|
|
{
|
|
|
|
Q_UNUSED(event)
|
|
|
|
resetAxes();
|
|
|
|
}
|
|
|
|
|
2013-08-27 23:28:35 +00:00
|
|
|
void TCPStreamDialog::keyPressEvent(QKeyEvent *event)
|
|
|
|
{
|
2013-08-30 21:15:24 +00:00
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
double h_factor = sp->axisRect()->rangeZoomFactor(Qt::Horizontal);
|
|
|
|
double v_factor = sp->axisRect()->rangeZoomFactor(Qt::Vertical);
|
2013-08-27 23:28:35 +00:00
|
|
|
bool scale_range = false;
|
|
|
|
|
|
|
|
double h_pan = 0.0;
|
|
|
|
double v_pan = 0.0;
|
|
|
|
|
|
|
|
// 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+
|
|
|
|
h_factor = pow(h_factor, -1);
|
|
|
|
v_factor = pow(v_factor, -1);
|
|
|
|
scale_range = true;
|
|
|
|
break;
|
|
|
|
case Qt::Key_Plus:
|
|
|
|
case Qt::Key_Equal: // Unshifted plus on U.S. keyboards
|
|
|
|
case Qt::Key_I: // GTK+
|
|
|
|
scale_range = true;
|
|
|
|
break;
|
|
|
|
|
2013-09-06 00:41:07 +00:00
|
|
|
// XXX Use pixel sizes instead
|
2013-08-27 23:28:35 +00:00
|
|
|
case Qt::Key_Right:
|
|
|
|
case Qt::Key_L:
|
2013-09-06 00:48:21 +00:00
|
|
|
h_pan = sp->xAxis->range().size() * 10.0 / sp->xAxis->axisRect()->width();
|
2013-08-27 23:28:35 +00:00
|
|
|
break;
|
|
|
|
case Qt::Key_Left:
|
|
|
|
case Qt::Key_H:
|
2013-09-06 00:48:21 +00:00
|
|
|
h_pan = sp->xAxis->range().size() * -10.0 / sp->xAxis->axisRect()->width();
|
2013-08-27 23:28:35 +00:00
|
|
|
break;
|
|
|
|
case Qt::Key_Up:
|
|
|
|
case Qt::Key_K:
|
2013-09-06 00:48:21 +00:00
|
|
|
v_pan = sp->yAxis->range().size() * 10.0 / sp->yAxis->axisRect()->height();
|
2013-08-27 23:28:35 +00:00
|
|
|
break;
|
|
|
|
case Qt::Key_Down:
|
|
|
|
case Qt::Key_J:
|
2013-09-06 00:48:21 +00:00
|
|
|
v_pan = sp->yAxis->range().size() * -10.0 / sp->yAxis->axisRect()->height();
|
2013-08-30 21:15:24 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case Qt::Key_Space:
|
|
|
|
toggleTracerStyle();
|
2013-08-27 23:28:35 +00:00
|
|
|
break;
|
|
|
|
|
2013-08-30 21:15:24 +00:00
|
|
|
// Reset
|
2013-08-27 23:28:35 +00:00
|
|
|
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-08 01:25:27 +00:00
|
|
|
case Qt::Key_S:
|
|
|
|
on_otherDirectionButton_clicked();
|
|
|
|
break;
|
2013-08-28 00:23:01 +00:00
|
|
|
// Alas, there is no Blade Runner-style Qt::Key_Ehance
|
2013-08-27 23:28:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (scale_range) {
|
2013-08-30 21:15:24 +00:00
|
|
|
sp->xAxis->scaleRange(h_factor, sp->xAxis->range().center());
|
|
|
|
sp->yAxis->scaleRange(v_factor, sp->yAxis->range().center());
|
|
|
|
sp->replot();
|
2013-08-27 23:28:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
double pan_mul = event->modifiers() & Qt::ShiftModifier ? 0.1 : 1.0;
|
|
|
|
|
|
|
|
// The GTK+ version won't pan unless we're zoomed. Should we do the same here?
|
|
|
|
if (h_pan) {
|
2013-08-30 21:15:24 +00:00
|
|
|
sp->xAxis->moveRange(h_pan * pan_mul);
|
|
|
|
sp->replot();
|
2013-08-27 23:28:35 +00:00
|
|
|
}
|
|
|
|
if (v_pan) {
|
2013-08-30 21:15:24 +00:00
|
|
|
sp->yAxis->moveRange(v_pan * pan_mul);
|
|
|
|
sp->replot();
|
2013-08-27 23:28:35 +00:00
|
|
|
}
|
|
|
|
QDialog::keyPressEvent(event);
|
|
|
|
}
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
void TCPStreamDialog::fillGraph()
|
|
|
|
{
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
|
|
|
|
if (sp->graphCount() < 1) return;
|
|
|
|
|
2013-09-08 01:25:27 +00:00
|
|
|
rel_time_map_.clear();
|
|
|
|
sequence_num_map_.clear();
|
2013-09-04 23:15:59 +00:00
|
|
|
graph_segment_list_free(&graph_);
|
|
|
|
tracer_->setGraph(NULL);
|
|
|
|
// We need at least one graph, so don't bother deleting the first one.
|
|
|
|
for (int i = 0; i < sp->graphCount(); i++) {
|
|
|
|
sp->graph(i)->clearData();
|
|
|
|
sp->graph(i)->setVisible(i == 0 ? true : false);
|
|
|
|
}
|
2013-09-08 01:25:27 +00:00
|
|
|
|
|
|
|
sp->xAxis->setLabel(time_s_label_);
|
|
|
|
sp->xAxis->setNumberFormat("gb");
|
|
|
|
sp->xAxis->setNumberPrecision(6);
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX graph_segment_list_get returns a different list for throughput
|
|
|
|
// graphs. If the throughput list used the same list we could call this
|
|
|
|
// above in our ctor.
|
2013-09-08 01:25:27 +00:00
|
|
|
graph_segment_list_get(cap_file_, &graph_, TRUE);
|
2013-09-06 00:41:07 +00:00
|
|
|
|
|
|
|
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
|
|
|
if (!compareHeaders(seg)) {
|
|
|
|
continue;
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
2013-09-06 00:41:07 +00:00
|
|
|
double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
|
2013-09-08 01:25:27 +00:00
|
|
|
rel_time_map_.insertMulti(rt_val, seg);
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
2013-09-06 00:41:07 +00:00
|
|
|
|
|
|
|
switch (graph_.type) {
|
|
|
|
case GRAPH_TSEQ_STEVENS:
|
|
|
|
initializeStevens();
|
|
|
|
break;
|
|
|
|
case GRAPH_THROUGHPUT:
|
|
|
|
initializeThroughput();
|
|
|
|
break;
|
2013-09-08 01:25:27 +00:00
|
|
|
case GRAPH_RTT:
|
|
|
|
initializeRoundTripTime();
|
|
|
|
break;
|
2013-09-06 00:41:07 +00:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
sp->setEnabled(true);
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
resetAxes();
|
|
|
|
tracer_->setGraph(sp->graph(0));
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
sp->graph(0)->rescaleAxes(false, true);
|
|
|
|
for (int i = 1; i < sp->graphCount(); i++) {
|
|
|
|
sp->graph(i)->rescaleValueAxis(false, true);
|
|
|
|
}
|
|
|
|
|
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-06 00:41:07 +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
|
|
|
|
2013-09-06 00:41:07 +00:00
|
|
|
if (sp->graph(1)->visible()) {
|
|
|
|
axis_pixels = sp->yAxis2->axisRect()->height();
|
|
|
|
sp->yAxis2->scaleRange((axis_pixels + (pixel_pad * 2)) / axis_pixels, sp->yAxis2->range().center());
|
2013-09-04 23:15:59 +00:00
|
|
|
|
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
|
|
|
|
|
|
|
sp->replot();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::initializeStevens()
|
|
|
|
{
|
2013-09-08 01:25:27 +00:00
|
|
|
QString dlg_title = QString(tr("Sequence Numbers")) + streamDescription();
|
2013-09-04 23:15:59 +00:00
|
|
|
setWindowTitle(dlg_title);
|
|
|
|
title_->setText(dlg_title);
|
|
|
|
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
// True Stevens-style graphs don't have lines but I like them - gcc
|
|
|
|
sp->graph(0)->setLineStyle(QCPGraph::lsStepLeft);
|
|
|
|
|
|
|
|
QVector<double> rel_time, seq;
|
|
|
|
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
|
|
|
if (!compareHeaders(seg)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
|
|
|
|
rel_time.append(rt_val);
|
|
|
|
seq.append(seg->th_seq);
|
|
|
|
}
|
|
|
|
sp->graph(0)->setData(rel_time, seq);
|
2013-09-08 01:25:27 +00:00
|
|
|
sp->yAxis->setLabel(sequence_number_label_);
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::initializeThroughput()
|
|
|
|
{
|
2013-09-06 19:07:57 +00:00
|
|
|
QString dlg_title = QString(tr("Throughput")) + streamDescription();
|
|
|
|
#ifdef MA_1_SECOND
|
2013-09-07 00:33:41 +00:00
|
|
|
dlg_title.append(tr(" (1s 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;
|
|
|
|
sp->graph(0)->setLineStyle(QCPGraph::lsNone);
|
|
|
|
sp->graph(1)->setVisible(true);
|
|
|
|
sp->graph(1)->setPen(QPen(QBrush(graph_color_2), 0.5));
|
|
|
|
sp->graph(1)->setLineStyle(QCPGraph::lsLine);
|
|
|
|
|
|
|
|
if (!graph_.segments || !graph_.segments->next) {
|
|
|
|
dlg_title.append(tr(" [not enough data]"));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-09-07 03:51:27 +00:00
|
|
|
QVector<double> rel_time, seg_len, tput_time, tput;
|
2013-09-04 23:15:59 +00:00
|
|
|
struct segment *oldest_seg = graph_.segments;
|
2013-09-06 19:07:57 +00:00
|
|
|
#ifndef MA_1_SECOND
|
|
|
|
int i = 1;
|
|
|
|
#endif
|
|
|
|
int sum = 0;
|
2013-09-04 23:15:59 +00:00
|
|
|
// Financial charts don't show MA data until a full period has elapsed.
|
|
|
|
// 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.
|
|
|
|
for (struct segment *seg = graph_.segments->next; seg != NULL; seg = seg->next) {
|
|
|
|
double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
|
|
|
|
|
2013-09-06 19:07:57 +00:00
|
|
|
#ifdef MA_1_SECOND
|
|
|
|
while (rt_val - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0) > 1.0) {
|
|
|
|
oldest_seg = oldest_seg->next;
|
|
|
|
sum -= oldest_seg->th_seglen;
|
|
|
|
}
|
|
|
|
#else
|
2013-09-04 23:15:59 +00:00
|
|
|
if (i > moving_avg_period_) {
|
|
|
|
oldest_seg = oldest_seg->next;
|
|
|
|
sum -= oldest_seg->th_seglen;
|
|
|
|
}
|
|
|
|
i++;
|
2013-09-06 19:07:57 +00:00
|
|
|
#endif
|
2013-09-04 23:15:59 +00:00
|
|
|
|
|
|
|
double dtime = rt_val - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0);
|
|
|
|
double av_tput;
|
|
|
|
sum += seg->th_seglen;
|
|
|
|
if (dtime > 0.0) {
|
|
|
|
av_tput = sum * 8.0 / dtime;
|
|
|
|
} else {
|
|
|
|
av_tput = 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
rel_time.append(rt_val);
|
|
|
|
seg_len.append(seg->th_seglen);
|
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.)
|
|
|
|
if (tput_time.size() > 0 && rt_val <= tput_time.last()) {
|
|
|
|
tput[tput.size() - 1] = av_tput;
|
|
|
|
} else {
|
|
|
|
tput.append(av_tput);
|
|
|
|
tput_time.append(rt_val);
|
|
|
|
}
|
2013-09-04 23:15:59 +00:00
|
|
|
}
|
|
|
|
sp->graph(0)->setData(rel_time, seg_len);
|
2013-09-07 03:51:27 +00:00
|
|
|
sp->graph(1)->setData(tput_time, tput);
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-09-08 01:25:27 +00:00
|
|
|
sp->yAxis->setLabel(segment_length_label_);
|
2013-09-04 23:15:59 +00:00
|
|
|
|
2013-09-08 01:25:27 +00:00
|
|
|
sp->yAxis2->setLabel(average_throughput_label_);
|
2013-09-06 02:00:14 +00:00
|
|
|
sp->yAxis2->setLabelColor(QColor(graph_color_2));
|
2013-09-04 23:15:59 +00:00
|
|
|
sp->yAxis2->setTickLabelColor(QColor(graph_color_2));
|
|
|
|
sp->yAxis2->setVisible(true);
|
|
|
|
}
|
|
|
|
|
2013-09-08 01:25:27 +00:00
|
|
|
void TCPStreamDialog::initializeRoundTripTime()
|
|
|
|
{
|
|
|
|
QString dlg_title = QString(tr("Round Trip Time")) + streamDescription();
|
|
|
|
setWindowTitle(dlg_title);
|
|
|
|
title_->setText(dlg_title);
|
|
|
|
|
|
|
|
QCustomPlot *sp = ui->streamPlot;
|
|
|
|
sp->graph(0)->setLineStyle(QCPGraph::lsLine);
|
|
|
|
|
|
|
|
QVector<double> seq_no, rtt;
|
2013-09-08 15:05:08 +00:00
|
|
|
guint32 seq_base = 0;
|
2013-09-08 01:25:27 +00:00
|
|
|
struct unack *unack = NULL, *u;
|
|
|
|
for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
|
|
|
|
if (seg == graph_.segments) {
|
|
|
|
seq_base = seg->th_seq;
|
|
|
|
}
|
|
|
|
if (compareHeaders(seg)) {
|
|
|
|
if (seg->th_seglen && !rtt_is_retrans(unack, seg->th_seq)) {
|
|
|
|
double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
|
|
|
|
rtt_put_unack_on_list(&unack, rtt_get_new_unack(rt_val, seg->th_seq));
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
guint32 ack_no = seg->th_ack - seq_base;
|
|
|
|
double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
|
|
|
|
struct unack *v;
|
|
|
|
|
|
|
|
for (u = unack; u; u = v) {
|
|
|
|
if (ack_no > u->seqno) {
|
|
|
|
seq_no.append(u->seqno);
|
|
|
|
rtt.append((rt_val - u->time) * 1000.0);
|
|
|
|
sequence_num_map_.insert(u->seqno, seg);
|
|
|
|
rtt_delete_unack_from_list(&unack, u);
|
|
|
|
}
|
|
|
|
v = u->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sp->graph(0)->setData(seq_no, rtt);
|
|
|
|
sp->xAxis->setLabel(sequence_number_label_);
|
|
|
|
sp->xAxis->setNumberFormat("f");
|
|
|
|
sp->xAxis->setNumberPrecision(0);
|
|
|
|
sp->yAxis->setLabel(round_trip_time_ms_label_);
|
|
|
|
}
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
QString TCPStreamDialog::streamDescription()
|
|
|
|
{
|
2013-09-06 02:00:14 +00:00
|
|
|
return QString(tr(" for %1:%2 %3 %4:%5"))
|
2013-09-04 23:15:59 +00:00
|
|
|
.arg(ep_address_to_str(&graph_.src_address))
|
|
|
|
.arg(graph_.src_port)
|
|
|
|
.arg(UTF8_RIGHTWARDS_ARROW)
|
|
|
|
.arg(ep_address_to_str(&graph_.dst_address))
|
|
|
|
.arg(graph_.dst_port);
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TCPStreamDialog::graphClicked(QMouseEvent *event)
|
|
|
|
{
|
|
|
|
Q_UNUSED(event)
|
|
|
|
// QRect spr = ui->streamPlot->axisRect()->rect();
|
|
|
|
|
2013-09-06 00:41:07 +00:00
|
|
|
if (tracer_->visible() && cap_file_ && packet_num_ > 0) {
|
2013-08-30 21:15:24 +00:00
|
|
|
emit goToPacket(packet_num_);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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-08 01:25:27 +00:00
|
|
|
double tr_key = tracer_->position->key();
|
2013-08-30 21:15:24 +00:00
|
|
|
struct segment *packet_seg = NULL;
|
|
|
|
packet_num_ = 0;
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
if (event && tracer_->graph() && tracer_->position->axisRect()->rect().contains(event->pos())) {
|
2013-09-08 01:25:27 +00:00
|
|
|
switch (graph_.type) {
|
|
|
|
case GRAPH_TSEQ_STEVENS:
|
|
|
|
case GRAPH_THROUGHPUT:
|
|
|
|
packet_seg = rel_time_map_.value(tr_key, NULL);
|
|
|
|
break;
|
|
|
|
case GRAPH_RTT:
|
|
|
|
packet_seg = sequence_num_map_.value(tr_key, NULL);
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-08-30 21:15:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!packet_seg) {
|
|
|
|
tracer_->setVisible(false);
|
|
|
|
ui->hintLabel->setText(tr("<small><i>Hover over the graph for details.</i></small>"));
|
|
|
|
ui->streamPlot->replot();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
tracer_->setVisible(true);
|
|
|
|
packet_num_ = packet_seg->num;
|
2013-09-07 00:33:41 +00:00
|
|
|
QString hint = QString(tr("<small><i>%1 %2 (%3s len %4 seq %5 ack %6 win %7)</i></small>"))
|
2013-09-06 00:41:07 +00:00
|
|
|
.arg(cap_file_ ? tr("Click to select packet") : tr("Packet"))
|
2013-08-30 21:15:24 +00:00
|
|
|
.arg(packet_num_)
|
2013-09-08 01:25:27 +00:00
|
|
|
.arg(QString::number(packet_seg->rel_secs + packet_seg->rel_usecs / 1000000.0, 'g', 4))
|
2013-08-30 21:15:24 +00:00
|
|
|
.arg(packet_seg->th_seglen)
|
|
|
|
.arg(packet_seg->th_seq)
|
|
|
|
.arg(packet_seg->th_ack)
|
|
|
|
.arg(packet_seg->th_win);
|
|
|
|
ui->hintLabel->setText(hint);
|
|
|
|
tracer_->setGraphKey(ui->streamPlot->xAxis->pixelToCoord(event->pos().x()));
|
|
|
|
ui->streamPlot->replot();
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2013-08-30 23:01:03 +00:00
|
|
|
void TCPStreamDialog::on_buttonBox_accepted()
|
|
|
|
{
|
|
|
|
QString file_name, extension;
|
|
|
|
QDir path(wsApp->lastOpenDir());
|
|
|
|
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);
|
|
|
|
|
|
|
|
file_name = QFileDialog::getSaveFileName(this, tr("Wireshark: Save Graph As..."),
|
|
|
|
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) {
|
|
|
|
path = QDir(file_name);
|
|
|
|
wsApp->setLastOpenDir(path.canonicalPath().toUtf8().constData());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-04 23:15:59 +00:00
|
|
|
void TCPStreamDialog::on_graphTypeComboBox_currentIndexChanged(int index)
|
|
|
|
{
|
|
|
|
if (index < 0) return;
|
|
|
|
graph_.type = ui->graphTypeComboBox->itemData(index).value<tcp_graph_type>();
|
|
|
|
fillGraph();
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2013-09-08 01:25:27 +00:00
|
|
|
void TCPStreamDialog::on_otherDirectionButton_clicked()
|
|
|
|
{
|
|
|
|
address tmp_addr;
|
|
|
|
guint16 tmp_port;
|
|
|
|
|
|
|
|
COPY_ADDRESS(&tmp_addr, &graph_.src_address);
|
|
|
|
tmp_port = graph_.src_port;
|
|
|
|
COPY_ADDRESS(&graph_.src_address, &graph_.dst_address);
|
|
|
|
graph_.src_port = graph_.dst_port;
|
|
|
|
COPY_ADDRESS(&graph_.dst_address, &tmp_addr);
|
|
|
|
graph_.dst_port = tmp_port;
|
|
|
|
|
|
|
|
fillGraph();
|
|
|
|
}
|
|
|
|
|
2013-08-27 18:13:20 +00:00
|
|
|
/*
|
|
|
|
* Editor modelines
|
|
|
|
*
|
|
|
|
* Local Variables:
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* tab-width: 8
|
|
|
|
* indent-tabs-mode: nil
|
|
|
|
* End:
|
|
|
|
*
|
|
|
|
* ex: set shiftwidth=4 tabstop=8 expandtab:
|
|
|
|
* :indentSize=4:tabSize=8:noTabs=true:
|
|
|
|
*/
|