aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGerald Combs <gerald@wireshark.org>2013-09-09 01:40:06 +0000
committerGerald Combs <gerald@wireshark.org>2013-09-09 01:40:06 +0000
commit829c1ed25ec2734d9c6fec6d8cbcc2b6e69ff6f1 (patch)
treea33d1faa4ba754b7a9c831c61fd8dff5bd8b1130
parent077d252fd92c4a41e3d5396e2f869c6804ce86ef (diff)
Add the TCP window scaling graph. Add zoom selections.
Rename some methods. Add the ability to toggle time and sequence number origins. Add more keyboard shortcuts. Comment out abs_secs abs_usecs in the segment struct since it looks like we aren't using them. Make sure we stay in the same TCP stream. svn path=/trunk/; revision=51856
-rw-r--r--image/openhand-16.pngbin0 -> 160 bytes
-rw-r--r--image/rubberband-16.pngbin0 -> 218 bytes
-rw-r--r--image/toolbar.qrc4
-rw-r--r--ui/qt/main_window.h1
-rw-r--r--ui/qt/main_window.ui9
-rw-r--r--ui/qt/main_window_slots.cpp5
-rw-r--r--ui/qt/tcp_stream_dialog.cpp387
-rw-r--r--ui/qt/tcp_stream_dialog.h24
-rw-r--r--ui/qt/tcp_stream_dialog.ui53
-rw-r--r--ui/tap-tcp-stream.c11
-rw-r--r--ui/tap-tcp-stream.h3
11 files changed, 417 insertions, 80 deletions
diff --git a/image/openhand-16.png b/image/openhand-16.png
new file mode 100644
index 0000000000..9181c859ed
--- /dev/null
+++ b/image/openhand-16.png
Binary files differ
diff --git a/image/rubberband-16.png b/image/rubberband-16.png
new file mode 100644
index 0000000000..229db38c7f
--- /dev/null
+++ b/image/rubberband-16.png
Binary files differ
diff --git a/image/toolbar.qrc b/image/toolbar.qrc
index dc06cf7dfb..3bead0b63a 100644
--- a/image/toolbar.qrc
+++ b/image/toolbar.qrc
@@ -15,4 +15,8 @@
<file>plus-8.png</file>
<file>copy-8.png</file>
</qresource>
+ <qresource prefix="/graph">
+ <file>openhand-16.png</file>
+ <file>rubberband-16.png</file>
+ </qresource>
</RCC>
diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h
index d8c3938005..bba37468ff 100644
--- a/ui/qt/main_window.h
+++ b/ui/qt/main_window.h
@@ -303,6 +303,7 @@ private slots:
void on_actionStatisticsTcpStreamStevens_triggered();
void on_actionStatisticsTcpStreamThroughput_triggered();
void on_actionStatisticsTcpStreamRoundTripTime_triggered();
+ void on_actionStatisticsTcpStreamWindowScaling_triggered();
};
diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui
index 366203bca9..3998565593 100644
--- a/ui/qt/main_window.ui
+++ b/ui/qt/main_window.ui
@@ -322,6 +322,7 @@
<addaction name="actionStatisticsTcpStreamStevens"/>
<addaction name="actionStatisticsTcpStreamThroughput"/>
<addaction name="actionStatisticsTcpStreamRoundTripTime"/>
+ <addaction name="actionStatisticsTcpStreamWindowScaling"/>
</widget>
<addaction name="actionSummary"/>
<addaction name="actionProtocol_Hierarchy"/>
@@ -1274,6 +1275,14 @@
<string>TCP round trip time</string>
</property>
</action>
+ <action name="actionStatisticsTcpStreamWindowScaling">
+ <property name="text">
+ <string>Window Scaling</string>
+ </property>
+ <property name="toolTip">
+ <string>TCP window scaling</string>
+ </property>
+ </action>
</widget>
<layoutdefault spacing="6" margin="11"/>
<customwidgets>
diff --git a/ui/qt/main_window_slots.cpp b/ui/qt/main_window_slots.cpp
index 9ed79afb63..3dda0dca78 100644
--- a/ui/qt/main_window_slots.cpp
+++ b/ui/qt/main_window_slots.cpp
@@ -1717,6 +1717,11 @@ void MainWindow::on_actionStatisticsTcpStreamRoundTripTime_triggered()
openTcpStreamDialog(GRAPH_RTT);
}
+void MainWindow::on_actionStatisticsTcpStreamWindowScaling_triggered()
+{
+ openTcpStreamDialog(GRAPH_WSCALE);
+}
+
// Help Menu
void MainWindow::on_actionHelpContents_triggered() {
diff --git a/ui/qt/tcp_stream_dialog.cpp b/ui/qt/tcp_stream_dialog.cpp
index dbee330d05..8a6000ea7f 100644
--- a/ui/qt/tcp_stream_dialog.cpp
+++ b/ui/qt/tcp_stream_dialog.cpp
@@ -31,8 +31,10 @@
#include "wireshark_application.h"
#include "tango_colors.h"
+#include <QCursor>
#include <QDir>
#include <QFileDialog>
+#include <QIcon>
#include <QPushButton>
#include <QDebug>
@@ -47,11 +49,16 @@ const int moving_avg_period_ = 20;
const QRgb graph_color_1 = tango_sky_blue_5;
const QRgb graph_color_2 = tango_butter_6;
+// 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;
+
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 sequence_number_label_ = QObject::tr("Sequence Number (B)");
const QString time_s_label_ = QObject::tr("Time (s)");
+const QString window_size_label_ = QObject::tr("Window Size (B)");
Q_DECLARE_METATYPE(tcp_graph_type)
@@ -59,7 +66,11 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty
QDialog(parent),
ui(new Ui::TCPStreamDialog),
cap_file_(cf),
+ ts_origin_conn_(true),
+ seq_origin_zero_(true),
tracer_(NULL),
+ mouse_drags_(true),
+ rubber_band_(NULL),
num_dsegs_(-1),
num_acks_(-1),
num_sack_ranges_(-1)
@@ -68,7 +79,8 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty
ui->setupUi(this);
- if (!select_tcpip_session(cap_file_, &current)) {
+ struct tcpheader *header = select_tcpip_session(cap_file_, &current);
+ if (!header) {
done(QDialog::Rejected);
}
@@ -80,15 +92,20 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty
ui->graphTypeComboBox->addItem(tr("Time / Sequence (Stevens)"), qVariantFromValue(GRAPH_TSEQ_STEVENS));
ui->graphTypeComboBox->addItem(tr("Throughput"), qVariantFromValue(GRAPH_THROUGHPUT));
ui->graphTypeComboBox->addItem(tr("Round Trip Time"), qVariantFromValue(GRAPH_RTT));
+ ui->graphTypeComboBox->addItem(tr("Window Scaling"), qVariantFromValue(GRAPH_WSCALE));
ui->graphTypeComboBox->setCurrentIndex(-1);
ui->graphTypeComboBox->setUpdatesEnabled(true);
+ ui->mouseHorizontalLayout->setContentsMargins(0, 0, 0, 0);
+ ui->dragToolButton->setChecked(mouse_drags_);
+
memset (&graph_, 0, sizeof(graph_));
graph_.type = graph_type;
COPY_ADDRESS(&graph_.src_address, &current.ip_src);
graph_.src_port = current.th_sport;
COPY_ADDRESS(&graph_.dst_address, &current.ip_dst);
graph_.dst_port = current.th_dport;
+ graph_.stream = header->th_stream;
QCustomPlot *sp = ui->streamPlot;
QCPPlotTitle *file_title = new QCPPlotTitle(sp, cf_get_display_name(cap_file_));
@@ -106,10 +123,6 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty
// Fills the graph
ui->graphTypeComboBox->setCurrentIndex(ui->graphTypeComboBox->findData(qVariantFromValue(graph_type)));
- sp->setInteractions(
- QCP::iRangeDrag |
- QCP::iRangeZoom
- );
sp->setMouseTracking(true);
sp->graph(0)->setPen(QPen(QBrush(graph_color_1), 0.25));
sp->graph(0)->setScatterStyle(QCPScatterStyle(QCPScatterStyle::ssDisc, 5));
@@ -128,6 +141,9 @@ TCPStreamDialog::TCPStreamDialog(QWidget *parent, capture_file *cf, tcp_graph_ty
connect(sp, SIGNAL(mousePress(QMouseEvent*)), this, SLOT(graphClicked(QMouseEvent*)));
connect(sp, SIGNAL(mouseMove(QMouseEvent*)), this, SLOT(mouseMoved(QMouseEvent*)));
+ connect(sp, SIGNAL(mouseRelease(QMouseEvent*)), this, SLOT(mouseReleased(QMouseEvent*)));
+ connect(sp, SIGNAL(axisClick(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)),
+ this, SLOT(axisClicked(QCPAxis*,QCPAxis::SelectablePart,QMouseEvent*)));
connect(sp->yAxis, SIGNAL(rangeChanged(QCPRange)), this, SLOT(transformYRange(QCPRange)));
disconnect(ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
@@ -192,17 +208,38 @@ void TCPStreamDialog::keyPressEvent(QKeyEvent *event)
toggleTracerStyle();
break;
- // Reset
case Qt::Key_0:
case Qt::Key_ParenRight: // Shifted 0 on U.S. keyboards
case Qt::Key_R:
case Qt::Key_Home:
resetAxes();
break;
- case Qt::Key_S:
+
+ case Qt::Key_D:
on_otherDirectionButton_clicked();
break;
- // Alas, there is no Blade Runner-style Qt::Key_Ehance
+ case Qt::Key_G:
+ if (tracer_->visible() && cap_file_ && packet_num_ > 0) {
+ emit goToPacket(packet_num_);
+ }
+ break;
+ case Qt::Key_S:
+ seq_origin_zero_ = seq_origin_zero_ ? false : true;
+ fillGraph();
+ break;
+ case Qt::Key_T:
+ ts_origin_conn_ = ts_origin_conn_ ? false : true;
+ fillGraph();
+ break;
+ case Qt::Key_Z:
+ if (mouse_drags_) {
+ ui->selectToolButton->toggle();
+ } else {
+ ui->dragToolButton->toggle();
+ }
+ break;
+
+ // Alas, there is no Blade Runner-style Qt::Key_Enhance
}
if (scale_range) {
@@ -223,6 +260,51 @@ void TCPStreamDialog::keyPressEvent(QKeyEvent *event)
sp->replot();
}
QDialog::keyPressEvent(event);
+
+ // GTK+ Shortcuts:
+ // Left Mouse Button selects segment under cursor in Wiresharks packet list
+ // can also drag to zoom in on a rectangular region
+ // Middle Mouse Button zooms in (towards area under cursor)
+ // <Shift>-Middle Mouse Button zooms out
+
+ // Right Mouse Button moves the graph (if zoomed in)
+ // <Ctrl>-Right Mouse Button displays a portion of graph under cursor magnified
+
+ // 1 display Round Trip Time Graph
+ // 2 display Throughput Graph
+ // 3 display Time/Sequence Graph (Stevens)
+ // 4 display Time/Sequence Graph (tcptrace)
+ // 5 display Window Scaling Graph
+
+ // <Space bar> toggles crosshairs on/off
+
+ // i or + zoom in (towards area under mouse pointer)
+ // o or - zoom out
+ // r or <Home> restore graph to initial state (zoom out max)
+ // s toggles relative/absolute sequence numbers
+ // t toggles time origin
+ // g go to frame under cursor in Wiresharks packet list (if possible)
+
+ // <Left> move view left by 100 pixels (if zoomed in)
+ // <Right> move view right 100 pixels (if zoomed in)
+ // <Up> move view up by 100 pixels (if zoomed in)
+ // <Down> move view down by 100 pixels (if zoomed in)
+
+ // <Shift><Left> move view left by 10 pixels (if zoomed in)
+ // <Shift><Right> move view right 10 pixels (if zoomed in)
+ // <Shift><Up> move view up by 10 pixels (if zoomed in)
+ // <Shift><Down> move view down by 10 pixels (if zoomed in)
+
+ // <Ctrl><Left> move view left by 1 pixel (if zoomed in)
+ // <Ctrl><Right> move view right 1 pixel (if zoomed in)
+ // <Ctrl><Up> move view up by 1 pixel (if zoomed in)
+ // <Ctrl><Down> move view down by 1 pixel (if zoomed in)
+
+}
+
+void TCPStreamDialog::mouseReleaseEvent(QMouseEvent *event)
+{
+ mouseReleased(event);
}
void TCPStreamDialog::fillGraph()
@@ -231,7 +313,7 @@ void TCPStreamDialog::fillGraph()
if (sp->graphCount() < 1) return;
- rel_time_map_.clear();
+ time_stamp_map_.clear();
sequence_num_map_.clear();
graph_segment_list_free(&graph_);
tracer_->setGraph(NULL);
@@ -244,6 +326,8 @@ void TCPStreamDialog::fillGraph()
sp->xAxis->setLabel(time_s_label_);
sp->xAxis->setNumberFormat("gb");
sp->xAxis->setNumberPrecision(6);
+ sp->yAxis->setNumberFormat("f");
+ sp->yAxis->setNumberPrecision(0);
sp->yAxis2->setVisible(false);
sp->yAxis2->setLabel(QString());
@@ -261,24 +345,35 @@ void TCPStreamDialog::fillGraph()
// graphs. If the throughput list used the same list we could call this
// above in our ctor.
graph_segment_list_get(cap_file_, &graph_, TRUE);
+ ts_offset_ = 0;
+ seq_offset_ = 0;
+ bool first = true;
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_map_.insertMulti(rt_val, seg);
+ double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
+ if (first) {
+ if (ts_origin_conn_) ts_offset_ = ts;
+ if (seq_origin_zero_) seq_offset_ = seg->th_seq;
+ first = false;
+ }
+ time_stamp_map_.insertMulti(ts - ts_offset_, seg);
}
switch (graph_.type) {
case GRAPH_TSEQ_STEVENS:
- initializeStevens();
+ fillStevens();
break;
case GRAPH_THROUGHPUT:
- initializeThroughput();
+ fillThroughput();
break;
case GRAPH_RTT:
- initializeRoundTripTime();
+ fillRoundTripTime();
+ break;
+ case GRAPH_WSCALE:
+ fillWindowScale();
break;
default:
break;
@@ -319,13 +414,15 @@ void TCPStreamDialog::resetAxes()
sp->replot();
}
-void TCPStreamDialog::initializeStevens()
+void TCPStreamDialog::fillStevens()
{
QString dlg_title = QString(tr("Sequence Numbers")) + streamDescription();
setWindowTitle(dlg_title);
title_->setText(dlg_title);
QCustomPlot *sp = ui->streamPlot;
+ sp->yAxis->setLabel(sequence_number_label_);
+
// True Stevens-style graphs don't have lines but I like them - gcc
sp->graph(0)->setLineStyle(QCPGraph::lsStepLeft);
@@ -335,15 +432,14 @@ void TCPStreamDialog::initializeStevens()
continue;
}
- double rt_val = seg->rel_secs + seg->rel_usecs / 1000000.0;
- rel_time.append(rt_val);
- seq.append(seg->th_seq);
+ double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
+ rel_time.append(ts - ts_offset_);
+ seq.append(seg->th_seq - seq_offset_);
}
sp->graph(0)->setData(rel_time, seq);
- sp->yAxis->setLabel(sequence_number_label_);
}
-void TCPStreamDialog::initializeThroughput()
+void TCPStreamDialog::fillThroughput()
{
QString dlg_title = QString(tr("Throughput")) + streamDescription();
#ifdef MA_1_SECOND
@@ -355,6 +451,12 @@ void TCPStreamDialog::initializeThroughput()
title_->setText(dlg_title);
QCustomPlot *sp = ui->streamPlot;
+ 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);
+
sp->graph(0)->setLineStyle(QCPGraph::lsNone);
sp->graph(1)->setVisible(true);
sp->graph(1)->setPen(QPen(QBrush(graph_color_2), 0.5));
@@ -376,10 +478,10 @@ void TCPStreamDialog::initializeThroughput()
// 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;
+ double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
#ifdef MA_1_SECOND
- while (rt_val - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0) > 1.0) {
+ while (ts - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0) > 1.0) {
oldest_seg = oldest_seg->next;
sum -= oldest_seg->th_seglen;
}
@@ -391,7 +493,7 @@ void TCPStreamDialog::initializeThroughput()
i++;
#endif
- double dtime = rt_val - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0);
+ double dtime = ts - (oldest_seg->rel_secs + oldest_seg->rel_usecs / 1000000.0);
double av_tput;
sum += seg->th_seglen;
if (dtime > 0.0) {
@@ -400,37 +502,35 @@ void TCPStreamDialog::initializeThroughput()
av_tput = 0.0;
}
- rel_time.append(rt_val);
+ rel_time.append(ts - ts_offset_);
seg_len.append(seg->th_seglen);
// 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()) {
+ if (tput_time.size() > 0 && ts <= tput_time.last()) {
tput[tput.size() - 1] = av_tput;
} else {
tput.append(av_tput);
- tput_time.append(rt_val);
+ tput_time.append(ts);
}
}
sp->graph(0)->setData(rel_time, seg_len);
sp->graph(1)->setData(tput_time, tput);
-
- 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);
}
-void TCPStreamDialog::initializeRoundTripTime()
+void TCPStreamDialog::fillRoundTripTime()
{
QString dlg_title = QString(tr("Round Trip Time")) + streamDescription();
setWindowTitle(dlg_title);
title_->setText(dlg_title);
QCustomPlot *sp = ui->streamPlot;
+ sp->xAxis->setLabel(sequence_number_label_);
+ sp->xAxis->setNumberFormat("f");
+ sp->xAxis->setNumberPrecision(0);
+ sp->yAxis->setLabel(round_trip_time_ms_label_);
+
sp->graph(0)->setLineStyle(QCPGraph::lsLine);
QVector<double> seq_no, rtt;
@@ -452,7 +552,7 @@ void TCPStreamDialog::initializeRoundTripTime()
for (u = unack; u; u = v) {
if (ack_no > u->seqno) {
- seq_no.append(u->seqno);
+ seq_no.append(u->seqno - seq_offset_);
rtt.append((rt_val - u->time) * 1000.0);
sequence_num_map_.insert(u->seqno, seg);
rtt_delete_unack_from_list(&unack, u);
@@ -462,10 +562,33 @@ void TCPStreamDialog::initializeRoundTripTime()
}
}
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_);
+}
+
+void TCPStreamDialog::fillWindowScale()
+{
+ QString dlg_title = QString(tr("Window Scaling")) + streamDescription();
+ setWindowTitle(dlg_title);
+ title_->setText(dlg_title);
+
+ QCustomPlot *sp = ui->streamPlot;
+ sp->graph(0)->setLineStyle(QCPGraph::lsLine);
+
+ QVector<double> rel_time, win_size;
+ for (struct segment *seg = graph_.segments; seg != NULL; seg = seg->next) {
+ if (!compareHeaders(seg)) {
+ continue;
+ }
+
+ double ts = seg->rel_secs + seg->rel_usecs / 1000000.0;
+ guint16 flags = seg->th_flags;
+
+ if ( (flags & (TH_SYN|TH_RST)) == 0 ) {
+ rel_time.append(ts - ts_offset_);
+ win_size.append(seg->th_win);
+ }
+ }
+ sp->graph(0)->setData(rel_time, win_size);
+ sp->yAxis->setLabel(window_size_label_);
}
QString TCPStreamDialog::streamDescription()
@@ -511,58 +634,163 @@ void TCPStreamDialog::toggleTracerStyle(bool force_default)
ui->streamPlot->replot();
}
+QRectF TCPStreamDialog::getZoomRanges(QRect zoom_rect)
+{
+ QRectF zoom_ranges = QRectF();
+
+ if (zoom_rect.width() < min_zoom_pixels_ && zoom_rect.height() < min_zoom_pixels_) {
+ return zoom_ranges;
+ }
+
+ QCustomPlot *sp = ui->streamPlot;
+ QRect zr = zoom_rect.normalized();
+ 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;
+}
+
void TCPStreamDialog::graphClicked(QMouseEvent *event)
{
Q_UNUSED(event)
-// QRect spr = ui->streamPlot->axisRect()->rect();
- if (tracer_->visible() && cap_file_ && packet_num_ > 0) {
- emit goToPacket(packet_num_);
+ if (mouse_drags_) {
+ if (tracer_->visible() && cap_file_ && packet_num_ > 0) {
+ emit goToPacket(packet_num_);
+ }
+ } else {
+ if (!rubber_band_) {
+ rubber_band_ = new QRubberBand(QRubberBand::Rectangle, ui->streamPlot);
+ }
+ rb_origin_ = event->pos();
+ rubber_band_->setGeometry(QRect(rb_origin_, QSize()));
+ rubber_band_->show();
}
}
-// 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)
+void TCPStreamDialog::axisClicked(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event)
{
- double tr_key = tracer_->position->key();
- struct segment *packet_seg = NULL;
- packet_num_ = 0;
+ Q_UNUSED(part)
+ Q_UNUSED(event)
+ QCustomPlot *sp = ui->streamPlot;
- if (event && tracer_->graph() && tracer_->position->axisRect()->rect().contains(event->pos())) {
+ if (axis == sp->xAxis) {
switch (graph_.type) {
- case GRAPH_TSEQ_STEVENS:
case GRAPH_THROUGHPUT:
- packet_seg = rel_time_map_.value(tr_key, NULL);
+ case GRAPH_TSEQ_STEVENS:
+ case GRAPH_TSEQ_TCPTRACE:
+ case GRAPH_WSCALE:
+ ts_origin_conn_ = ts_origin_conn_ ? false : true;
+ fillGraph();
break;
case GRAPH_RTT:
- packet_seg = sequence_num_map_.value(tr_key, NULL);
+ seq_origin_zero_ = seq_origin_zero_ ? false : true;
+ 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;
default:
break;
}
}
+}
- if (!packet_seg) {
- tracer_->setVisible(false);
- ui->hintLabel->setText(tr("<small><i>Hover over the graph for details.</i></small>"));
+// 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)
+{
+ if (mouse_drags_) {
+ double tr_key = tracer_->position->key();
+ struct segment *packet_seg = NULL;
+ packet_num_ = 0;
+
+ if (event && tracer_->graph() && tracer_->position->axisRect()->rect().contains(event->pos())) {
+ switch (graph_.type) {
+ case GRAPH_TSEQ_STEVENS:
+ case GRAPH_THROUGHPUT:
+ case GRAPH_WSCALE:
+ packet_seg = time_stamp_map_.value(tr_key, NULL);
+ break;
+ case GRAPH_RTT:
+ packet_seg = sequence_num_map_.value(tr_key, NULL);
+ default:
+ break;
+ }
+ }
+
+ 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;
+ QString hint = QString(tr("<small><i>%1 %2 (%3s len %4 seq %5 ack %6 win %7)</i></small>"))
+ .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);
+ ui->hintLabel->setText(hint);
+ tracer_->setGraphKey(ui->streamPlot->xAxis->pixelToCoord(event->pos().x()));
ui->streamPlot->replot();
- return;
+ } else {
+ QString hint = QString(tr("<small>Click to select a portion of the graph</small>"));
+ if (rubber_band_) {
+ 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) {
+ hint = QString(tr("<small>Release to zoom, x = %1 to %2, y = %3 to %4</small>"))
+ .arg(zoom_ranges.x())
+ .arg(zoom_ranges.x() + zoom_ranges.width())
+ .arg(zoom_ranges.y())
+ .arg(zoom_ranges.y() + zoom_ranges.height());
+ } else {
+ hint = QString(tr("<small>Unable to select range</small>"));
+ }
+ }
+ ui->hintLabel->setText(hint);
}
+}
- tracer_->setVisible(true);
- packet_num_ = packet_seg->num;
- QString hint = QString(tr("<small><i>%1 %2 (%3s len %4 seq %5 ack %6 win %7)</i></small>"))
- .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);
- ui->hintLabel->setText(hint);
- tracer_->setGraphKey(ui->streamPlot->xAxis->pixelToCoord(event->pos().x()));
- ui->streamPlot->replot();
+void TCPStreamDialog::mouseReleased(QMouseEvent *event)
+{
+ if (rubber_band_) {
+ 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();
+ }
+ }
+ }
}
void TCPStreamDialog::transformYRange(const QCPRange &y_range1)
@@ -648,6 +876,23 @@ void TCPStreamDialog::on_otherDirectionButton_clicked()
fillGraph();
}
+void TCPStreamDialog::on_dragToolButton_toggled(bool checked)
+{
+ if (checked) mouse_drags_ = true;
+ ui->streamPlot->setInteractions(
+ QCP::iRangeDrag |
+ QCP::iRangeZoom
+ );
+ ui->streamPlot->setCursor(QCursor(Qt::OpenHandCursor));
+}
+
+void TCPStreamDialog::on_selectToolButton_toggled(bool checked)
+{
+ if (checked) mouse_drags_ = false;
+ ui->streamPlot->setInteractions(0);
+ ui->streamPlot->setCursor(QCursor(Qt::CrossCursor));
+}
+
/*
* Editor modelines
*
diff --git a/ui/qt/tcp_stream_dialog.h b/ui/qt/tcp_stream_dialog.h
index 087aba9d42..481bb2bb20 100644
--- a/ui/qt/tcp_stream_dialog.h
+++ b/ui/qt/tcp_stream_dialog.h
@@ -36,6 +36,7 @@
#include "qcustomplot.h"
#include <QDialog>
+#include <QRubberBand>
namespace Ui {
class TCPStreamDialog;
@@ -58,17 +59,26 @@ public slots:
protected:
void showEvent(QShowEvent *event);
void keyPressEvent(QKeyEvent *event);
+ void mouseReleaseEvent(QMouseEvent *event);
private:
Ui::TCPStreamDialog *ui;
capture_file *cap_file_;
- QMap<double, struct segment *> rel_time_map_;
+ QMap<double, struct segment *> time_stamp_map_;
+ double ts_offset_;
+ bool ts_origin_conn_;
QMap<double, struct segment *> sequence_num_map_;
+ double seq_offset_;
+ bool seq_origin_zero_;
struct tcp_graph graph_;
QCPPlotTitle *title_;
QCPItemTracer *tracer_;
+ QRectF axis_bounds_;
guint32 packet_num_;
QTransform y_axis_xfrm_;
+ bool mouse_drags_;
+ QRubberBand *rubber_band_;
+ QPoint rb_origin_;
int num_dsegs_;
int num_acks_;
@@ -76,21 +86,27 @@ private:
void fillGraph();
void resetAxes();
- void initializeStevens();
- void initializeThroughput();
- void initializeRoundTripTime();
+ void fillStevens();
+ void fillThroughput();
+ void fillRoundTripTime();
+ void fillWindowScale();
QString streamDescription();
bool compareHeaders(struct segment *seg);
void toggleTracerStyle(bool force_default = false);
+ QRectF getZoomRanges(QRect zoom_rect);
private slots:
void graphClicked(QMouseEvent *event);
+ void axisClicked(QCPAxis *axis, QCPAxis::SelectablePart part, QMouseEvent *event);
void mouseMoved(QMouseEvent *event);
+ void mouseReleased(QMouseEvent *event);
void transformYRange(const QCPRange &y_range1);
void on_buttonBox_accepted();
void on_graphTypeComboBox_currentIndexChanged(int index);
void on_resetButton_clicked();
void on_otherDirectionButton_clicked();
+ void on_dragToolButton_toggled(bool checked);
+ void on_selectToolButton_toggled(bool checked);
};
#endif // TCP_STREAM_DIALOG_H
diff --git a/ui/qt/tcp_stream_dialog.ui b/ui/qt/tcp_stream_dialog.ui
index d19d5ed04d..b9e139129e 100644
--- a/ui/qt/tcp_stream_dialog.ui
+++ b/ui/qt/tcp_stream_dialog.ui
@@ -45,7 +45,11 @@
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↑&lt;/th&gt;&lt;td&gt;Move up 10%&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;&lt;i&gt;Shift+&lt;/i&gt;↓&lt;/th&gt;&lt;td&gt;Move down 10%&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;0&lt;/th&gt;&lt;td&gt;Reset graph to its initial state&lt;/td&gt;&lt;/th&gt;
-&lt;tr&gt;&lt;th&gt;s&lt;/th&gt;&lt;td&gt;Switch direction (swap TCP endpoints)&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;d&lt;/th&gt;&lt;td&gt;Switch direction (swap TCP endpoints)&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;g&lt;/th&gt;&lt;td&gt;Go to packet under cursor&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;z&lt;/th&gt;&lt;td&gt;Toggle mouse drag / zoom&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;s&lt;/th&gt;&lt;td&gt;Toggle relative / absolute sequence numbers&lt;/td&gt;&lt;/th&gt;
+&lt;tr&gt;&lt;th&gt;t&lt;/th&gt;&lt;td&gt;Toggle capture / session time origin&lt;/td&gt;&lt;/th&gt;
&lt;tr&gt;&lt;th&gt;Space&lt;/th&gt;&lt;td&gt;Toggle crosshairs&lt;/td&gt;&lt;/th&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;/body&gt;&lt;/html&gt;</string>
@@ -56,7 +60,7 @@
</widget>
</item>
<item>
- <layout class="QHBoxLayout" name="horizontalLayout">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLabel" name="label">
<property name="text">
@@ -81,6 +85,44 @@
</spacer>
</item>
<item>
+ <layout class="QHBoxLayout" name="mouseHorizontalLayout">
+ <item>
+ <widget class="QToolButton" name="dragToolButton">
+ <property name="toolTip">
+ <string>Drag using the mouse button.</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../image/toolbar.qrc">
+ <normaloff>:/graph/openhand-16.png</normaloff>:/graph/openhand-16.png</iconset>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">mouseButtonGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ <item>
+ <widget class="QToolButton" name="selectToolButton">
+ <property name="toolTip">
+ <string>Select using the mouse button.</string>
+ </property>
+ <property name="icon">
+ <iconset resource="../../image/toolbar.qrc">
+ <normaloff>:/graph/rubberband-16.png</normaloff>:/graph/rubberband-16.png</iconset>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <attribute name="buttonGroup">
+ <string notr="true">mouseButtonGroup</string>
+ </attribute>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
<widget class="QPushButton" name="resetButton">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Reset the graph to its initial state.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@@ -127,7 +169,9 @@
<header>elided_label.h</header>
</customwidget>
</customwidgets>
- <resources/>
+ <resources>
+ <include location="../../image/toolbar.qrc"/>
+ </resources>
<connections>
<connection>
<sender>buttonBox</sender>
@@ -162,4 +206,7 @@
</hints>
</connection>
</connections>
+ <buttongroups>
+ <buttongroup name="mouseButtonGroup"/>
+ </buttongroups>
</ui>
diff --git a/ui/tap-tcp-stream.c b/ui/tap-tcp-stream.c
index 6bbf3c1d3a..621c882b09 100644
--- a/ui/tap-tcp-stream.c
+++ b/ui/tap-tcp-stream.c
@@ -61,15 +61,18 @@ tapall_tcpip_packet(void *pct, packet_info *pinfo, epan_dissect_t *edt _U_, cons
tg->src_port, tg->dst_port,
&tcphdr->ip_src, &tcphdr->ip_dst,
tcphdr->th_sport, tcphdr->th_dport,
- ts->direction))
+ ts->direction)
+ && tg->stream == tcphdr->th_stream)
{
struct segment *segment = (struct segment *)g_malloc(sizeof(struct segment));
segment->next = NULL;
segment->num = pinfo->fd->num;
segment->rel_secs = (guint32)pinfo->rel_ts.secs;
segment->rel_usecs = pinfo->rel_ts.nsecs/1000;
+ /* Currently unused
segment->abs_secs = (guint32)pinfo->fd->abs_ts.secs;
segment->abs_usecs = pinfo->fd->abs_ts.nsecs/1000;
+ */
segment->th_seq = tcphdr->th_seq;
segment->th_ack = tcphdr->th_ack;
segment->th_win = tcphdr->th_win;
@@ -111,7 +114,8 @@ graph_segment_list_get(capture_file *cf, struct tcp_graph *tg, gboolean stream_k
if (!cf || !tg) return;
if (!stream_known) {
- if (!select_tcpip_session(cf, &current)) return;
+ struct tcpheader *header = select_tcpip_session(cf, &current);
+ if (!header) return;
if (tg->type == GRAPH_THROUGHPUT)
ts.direction = COMPARE_CURR_DIR;
else
@@ -122,6 +126,7 @@ graph_segment_list_get(capture_file *cf, struct tcp_graph *tg, gboolean stream_k
tg->src_port = current.th_sport;
COPY_ADDRESS(&tg->dst_address, &current.ip_dst);
tg->dst_port = current.th_dport;
+ tg->stream = header->th_stream;
}
/* rescan all the packets and pick up all interesting tcp headers.
@@ -328,8 +333,10 @@ select_tcpip_session(capture_file *cf, struct segment *hdrs)
hdrs->num = fdata->num;
hdrs->rel_secs = (guint32) rel_ts.secs;
hdrs->rel_usecs = rel_ts.nsecs/1000;
+ /* Currently unused
hdrs->abs_secs = (guint32) fdata->abs_ts.secs;
hdrs->abs_usecs = fdata->abs_ts.nsecs/1000;
+ */
hdrs->th_seq = th.tcphdrs[0]->th_seq;
hdrs->th_ack = th.tcphdrs[0]->th_ack;
hdrs->th_win = th.tcphdrs[0]->th_win;
diff --git a/ui/tap-tcp-stream.h b/ui/tap-tcp-stream.h
index 56953adc4e..1aba3c842d 100644
--- a/ui/tap-tcp-stream.h
+++ b/ui/tap-tcp-stream.h
@@ -44,8 +44,10 @@ struct segment {
guint32 num;
guint32 rel_secs;
guint32 rel_usecs;
+ /* Currently unused.
guint32 abs_secs;
guint32 abs_usecs;
+ */
guint32 th_seq;
guint32 th_ack;
@@ -70,6 +72,7 @@ struct tcp_graph {
guint16 src_port;
address dst_address;
guint16 dst_port;
+ guint32 stream;
/* Should this be a map or tree instead? */
struct segment *segments;
};