Qt: Conversation time column updates.

Add a checkbox which lets you toggle between absolute and relative start
times. Use the local time for now. Fixes bug 11618.

Adjust our time precision based on the capture file's time precision.
Fixes bug 12803.

Update the User's Guide accordingly.

Bug: 11618
Bug: 12803
Change-Id: I0049d6db6e4d0b6967bf35e6d056a61bfb4de10f
Reviewed-on: https://code.wireshark.org/review/17448
Petri-Dish: Gerald Combs <gerald@wireshark.org>
Tested-by: Petri Dish Buildbot <buildbot-no-reply@wireshark.org>
Reviewed-by: Gerald Combs <gerald@wireshark.org>
This commit is contained in:
Gerald Combs 2016-09-01 13:51:13 -07:00
parent df3bf9ca79
commit 5846524f0b
12 changed files with 130 additions and 10 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 245 KiB

After

Width:  |  Height:  |  Size: 235 KiB

View File

@ -141,10 +141,10 @@ description of the known endpoint types can be found in
The conversations window is similar to the endpoint Window. See The conversations window is similar to the endpoint Window. See
<<ChStatEndpointsWindow>> for a description of their common features. Along with <<ChStatEndpointsWindow>> for a description of their common features. Along with
addresses, packet counters, and byte counters the conversation window adds four addresses, packet counters, and byte counters the conversation window adds four
columns: the time in seconds between the start of the capture and the start of columns: the start time of the conversation (``Rel Start'') or (``Abs Start''),
the conversation (``Rel Start''), the duration of the conversation in seconds, and the duration of the conversation in seconds, and the average bits (not bytes)
the average bits (not bytes) per second in each direction. A timeline graph is per second in each direction. A timeline graph is also drawn across the
also drawn across the ``Rel Start'' and ``Duration'' columns. ``Rel Start'' / ``Abs Start'' and ``Duration'' columns.
.The ``Conversations'' window .The ``Conversations'' window
image::wsug_graphics/ws-stats-conversations.png[scaledwidth="100%"] image::wsug_graphics/ws-stats-conversations.png[scaledwidth="100%"]
@ -154,7 +154,10 @@ Each row in the list shows the statistical values for exactly one conversation.
_Name resolution_ will be done if selected in the window and if it is active for _Name resolution_ will be done if selected in the window and if it is active for
the specific protocol layer (MAC layer for the selected Ethernet endpoints the specific protocol layer (MAC layer for the selected Ethernet endpoints
page). _Limit to display filter_ will only show conversations matching the page). _Limit to display filter_ will only show conversations matching the
current display filter. current display filter. _Absolute start time_ switches the start time column
between relative (``Rel Start'') and absolute (``Abs Start'') times. Relative start
times match the ``Seconds Since Beginning of Capture'' time display format in the
packet list and absolute start times match the ``Time of Day'' display format.
The button:[Copy] button will copy the list values to the clipboard in CSV The button:[Copy] button will copy the list values to the clipboard in CSV
(Comma Separated Values) or YAML format. The button:[Follow Stream...] button (Comma Separated Values) or YAML format. The button:[Follow Stream...] button

View File

@ -48,7 +48,7 @@ col_format_to_string(const gint fmt) {
"%At", /* 3) COL_ABS_TIME */ "%At", /* 3) COL_ABS_TIME */
"%V", /* 4) COL_VSAN - !! DEPRECATED !!*/ "%V", /* 4) COL_VSAN - !! DEPRECATED !!*/
"%B", /* 5) COL_CUMULATIVE_BYTES */ "%B", /* 5) COL_CUMULATIVE_BYTES */
"%Cus", /* 6 COL_CUSTOM */ "%Cus", /* 6) COL_CUSTOM */
"%y", /* 7) COL_DCE_CALL */ "%y", /* 7) COL_DCE_CALL */
"%Tt", /* 8) COL_DELTA_TIME */ "%Tt", /* 8) COL_DELTA_TIME */
"%Gt", /* 9) COL_DELTA_TIME_DIS */ "%Gt", /* 9) COL_DELTA_TIME_DIS */

View File

@ -99,6 +99,14 @@ struct _packet_info *CaptureFile::packetInfo()
return NULL; return NULL;
} }
int CaptureFile::timestampPrecision()
{
if (capFile() && capFile()->wth) {
return wtap_file_tsprec(capFile()->wth);
}
return WTAP_TSPREC_UNKNOWN;
}
void CaptureFile::retapPackets() void CaptureFile::retapPackets()
{ {
if (cap_file_) { if (cap_file_) {

View File

@ -76,6 +76,12 @@ public:
*/ */
struct _packet_info *packetInfo(); struct _packet_info *packetInfo();
/** Timestamp precision for the current file.
* @return One of the WTAP_TSPREC_x values defined in wiretap/wtap.h,
* or WTAP_TSPREC_UNKNOWN if no file is open.
*/
int timestampPrecision();
/** Reload the capture file /** Reload the capture file
*/ */
void reload(); void reload();

View File

@ -35,6 +35,7 @@
#include "wireshark_application.h" #include "wireshark_application.h"
#include <QCheckBox> #include <QCheckBox>
#include <QDateTime>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QPushButton> #include <QPushButton>
@ -58,6 +59,9 @@
// Fixed bugs: // Fixed bugs:
// - Friendly unit displays https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9231 // - Friendly unit displays https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=9231
// - Misleading bps calculation https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8703 // - Misleading bps calculation https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8703
// - Show Absolute time in conversation tables https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=11618
// - The value of 'Rel start' and 'Duration' in "Conversations" no need too precise https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=12803
static const QString table_name_ = QObject::tr("Conversation"); static const QString table_name_ = QObject::tr("Conversation");
ConversationDialog::ConversationDialog(QWidget &parent, CaptureFile &cf, int cli_proto_id, const char *filter) : ConversationDialog::ConversationDialog(QWidget &parent, CaptureFile &cf, int cli_proto_id, const char *filter) :
@ -71,6 +75,8 @@ ConversationDialog::ConversationDialog(QWidget &parent, CaptureFile &cf, int cli
graph_bt_->setToolTip(tr("Graph a TCP conversation.")); graph_bt_->setToolTip(tr("Graph a TCP conversation."));
connect(graph_bt_, SIGNAL(clicked()), this, SLOT(graphTcp())); connect(graph_bt_, SIGNAL(clicked()), this, SLOT(graphTcp()));
absoluteTimeCheckBox()->show();
addProgressFrame(&parent); addProgressFrame(&parent);
QList<int> conv_protos; QList<int> conv_protos;
@ -163,6 +169,9 @@ bool ConversationDialog::addTrafficTable(register_ct_t* table)
this, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType))); this, SIGNAL(filterAction(QString,FilterAction::Action,FilterAction::ActionType)));
connect(nameResolutionCheckBox(), SIGNAL(toggled(bool)), connect(nameResolutionCheckBox(), SIGNAL(toggled(bool)),
conv_tree, SLOT(setNameResolutionEnabled(bool))); conv_tree, SLOT(setNameResolutionEnabled(bool)));
connect(absoluteTimeCheckBox(), SIGNAL(toggled(bool)),
conv_tree, SLOT(updateStartTime(bool)));
// XXX Move to ConversationTreeWidget ctor? // XXX Move to ConversationTreeWidget ctor?
QByteArray filter_utf8; QByteArray filter_utf8;
@ -365,9 +374,31 @@ public:
case CONV_COLUMN_BYTES_BA: case CONV_COLUMN_BYTES_BA:
return gchar_free_to_qstring(format_size(conv_item->rx_bytes, format_size_unit_none|format_size_prefix_si)); return gchar_free_to_qstring(format_size(conv_item->rx_bytes, format_size_unit_none|format_size_prefix_si));
case CONV_COLUMN_START: case CONV_COLUMN_START:
return QString::number(nstime_to_sec(&conv_item->start_time), 'f', 9); {
bool use_ns = treeWidget()->window()->property("nanosecond_precision").toBool();
int width = use_ns ? 9 : 6;
if (treeWidget()->window()->property("absolute_start_time").toBool()) {
nstime_t *abs_time = &conv_item->start_abs_time;
QDateTime abs_dt = QDateTime::fromMSecsSinceEpoch(nstime_to_msec(abs_time));
return QString("%1.%2")
// Mimic column-utils:set_abs_time as best we can
.arg(abs_dt.toString("hh:mm:ss"))
.arg(use_ns ? abs_time->nsecs : abs_time->nsecs / 1000, width, 10, QChar('0'));
}
return QString::number(nstime_to_sec(&conv_item->start_time), 'f', width);
}
case CONV_COLUMN_DURATION: case CONV_COLUMN_DURATION:
return QString::number(duration, 'f', 6); {
// The GTK+ UI uses 9 digit precision for the start time and 4 for the duration.
// Do the same here and above for non-nanosecond precision and add a couple
// of digits for nanosecond precision.
bool use_ns = treeWidget()->window()->property("nanosecond_precision").toBool();
int width = use_ns ? 6 : 4;
return QString::number(duration, 'f', width);
}
case CONV_COLUMN_BPS_AB: case CONV_COLUMN_BPS_AB:
if (duration > min_bw_calc_duration_) { if (duration > min_bw_calc_duration_) {
bps_ab = gchar_free_to_qstring(format_size((gint64) conv_item->tx_bytes * 8 / duration, format_size_unit_none|format_size_prefix_si)); bps_ab = gchar_free_to_qstring(format_size((gint64) conv_item->tx_bytes * 8 / duration, format_size_unit_none|format_size_prefix_si));
@ -665,6 +696,19 @@ void ConversationTreeWidget::tapDraw(void *conv_hash_ptr)
conv_tree->updateItems(); conv_tree->updateItems();
} }
void ConversationTreeWidget::updateStartTime(bool absolute)
{
headerItem()->setText(CONV_COLUMN_START, absolute
? conv_abs_start_title
: conv_column_titles[CONV_COLUMN_START]);
dataChanged(QModelIndex(), QModelIndex());
if (topLevelItemCount() > 0) {
resizeColumnToContents(CONV_COLUMN_START);
}
}
QMap<FilterAction::ActionDirection, conv_direction_e> fad_to_cd_; QMap<FilterAction::ActionDirection, conv_direction_e> fad_to_cd_;
void ConversationTreeWidget::initDirectionMap() void ConversationTreeWidget::initDirectionMap()

View File

@ -38,6 +38,9 @@ public:
double minRelStartTime() { return min_rel_start_time_; } double minRelStartTime() { return min_rel_start_time_; }
double maxRelStopTime() { return max_rel_stop_time_; } double maxRelStopTime() { return max_rel_stop_time_; }
public slots:
void updateStartTime(bool absolute);
private: private:
void initDirectionMap(); void initDirectionMap();
void updateItems(); void updateItems();

View File

@ -56,12 +56,14 @@ TrafficTableDialog::TrafficTableDialog(QWidget &parent, CaptureFile &cf, const c
ui(new Ui::TrafficTableDialog), ui(new Ui::TrafficTableDialog),
cap_file_(cf), cap_file_(cf),
file_closed_(false), file_closed_(false),
filter_(filter) filter_(filter),
nanosecond_timestamps_(false)
{ {
ui->setupUi(this); ui->setupUi(this);
loadGeometry(parent.width(), parent.height() * 3 / 4); loadGeometry(parent.width(), parent.height() * 3 / 4);
ui->enabledTypesPushButton->setText(tr("%1 Types").arg(table_name)); ui->enabledTypesPushButton->setText(tr("%1 Types").arg(table_name));
ui->absoluteTimeCheckBox->hide();
setWindowSubtitle(QString("%1s").arg(table_name)); setWindowSubtitle(QString("%1s").arg(table_name));
QMenu *copy_menu = new QMenu(); QMenu *copy_menu = new QMenu();
@ -78,6 +80,10 @@ TrafficTableDialog::TrafficTableDialog(QWidget &parent, CaptureFile &cf, const c
ui->enabledTypesPushButton->setMenu(&traffic_type_menu_); ui->enabledTypesPushButton->setMenu(&traffic_type_menu_);
ui->trafficTableTabWidget->setFocus(); ui->trafficTableTabWidget->setFocus();
if (cf.timestampPrecision() == WTAP_TSPREC_NSEC) {
nanosecond_timestamps_ = true;
}
connect(wsApp, SIGNAL(addressResolutionChanged()), this, SLOT(currentTabChanged())); connect(wsApp, SIGNAL(addressResolutionChanged()), this, SLOT(currentTabChanged()));
connect(wsApp, SIGNAL(addressResolutionChanged()), this, SLOT(updateWidgets())); connect(wsApp, SIGNAL(addressResolutionChanged()), this, SLOT(updateWidgets()));
connect(ui->trafficTableTabWidget, SIGNAL(currentChanged(int)), connect(ui->trafficTableTabWidget, SIGNAL(currentChanged(int)),
@ -93,6 +99,11 @@ TrafficTableDialog::~TrafficTableDialog()
delete ui; delete ui;
} }
bool TrafficTableDialog::absoluteStartTime()
{
return absoluteTimeCheckBox()->isChecked();
}
const QList<int> TrafficTableDialog::defaultProtos() const const QList<int> TrafficTableDialog::defaultProtos() const
{ {
// Reasonable defaults? // Reasonable defaults?
@ -144,6 +155,11 @@ QCheckBox *TrafficTableDialog::nameResolutionCheckBox() const
return ui->nameResolutionCheckBox; return ui->nameResolutionCheckBox;
} }
QCheckBox *TrafficTableDialog::absoluteTimeCheckBox() const
{
return ui->absoluteTimeCheckBox;
}
QPushButton *TrafficTableDialog::enabledTypesPushButton() const QPushButton *TrafficTableDialog::enabledTypesPushButton() const
{ {
return ui->enabledTypesPushButton; return ui->enabledTypesPushButton;

View File

@ -103,6 +103,8 @@ signals:
class TrafficTableDialog : public WiresharkDialog class TrafficTableDialog : public WiresharkDialog
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(bool absolute_start_time READ absoluteStartTime)
Q_PROPERTY(bool nanosecond_timestamps READ nanosecondTimestamps)
public: public:
/** Create a new conversation window. /** Create a new conversation window.
@ -115,6 +117,16 @@ public:
explicit TrafficTableDialog(QWidget &parent, CaptureFile &cf, const char *filter = NULL, const QString &table_name = tr("Unknown")); explicit TrafficTableDialog(QWidget &parent, CaptureFile &cf, const char *filter = NULL, const QString &table_name = tr("Unknown"));
~TrafficTableDialog(); ~TrafficTableDialog();
/** Use absolute start times.
* @return true if the "Absolute start time" checkbox is checked, false otherwise.
*/
bool absoluteStartTime();
/** Use nanosecond timestamps.
* @return true if the current capture file uses nanosecond timestamps, false otherwise.
*/
bool nanosecondTimestamps() { return nanosecond_timestamps_; }
public slots: public slots:
signals: signals:
@ -143,6 +155,7 @@ protected:
QTabWidget *trafficTableTabWidget() const; QTabWidget *trafficTableTabWidget() const;
QCheckBox *displayFilterCheckBox() const; QCheckBox *displayFilterCheckBox() const;
QCheckBox *nameResolutionCheckBox() const; QCheckBox *nameResolutionCheckBox() const;
QCheckBox *absoluteTimeCheckBox() const;
QPushButton *enabledTypesPushButton() const; QPushButton *enabledTypesPushButton() const;
protected slots: protected slots:
@ -151,6 +164,7 @@ protected slots:
private: private:
QString window_name_; QString window_name_;
bool nanosecond_timestamps_;
QList<QVariant> curTreeRowData(int row) const; QList<QVariant> curTreeRowData(int row) const;

View File

@ -15,7 +15,7 @@
<widget class="QTabWidget" name="trafficTableTabWidget"/> <widget class="QTabWidget" name="trafficTableTabWidget"/>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,1,0"> <layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,0,0,0,1,0">
<item> <item>
<widget class="QCheckBox" name="nameResolutionCheckBox"> <widget class="QCheckBox" name="nameResolutionCheckBox">
<property name="toolTip"> <property name="toolTip">
@ -49,6 +49,29 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="absoluteTimeCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Show absolute times in the start time column.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Absolute start time</string>
</property>
</widget>
</item>
<item> <item>
<spacer name="horizontalSpacer"> <spacer name="horizontalSpacer">
<property name="orientation"> <property name="orientation">
@ -83,6 +106,7 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<resources/>
<connections> <connections>
<connection> <connection>
<sender>buttonBox</sender> <sender>buttonBox</sender>

View File

@ -65,6 +65,7 @@ const char *conv_column_titles[CONV_NUM_COLUMNS] = {
const char *conv_conn_a_title = "Connection A"; const char *conv_conn_a_title = "Connection A";
const char *conv_conn_b_title = "Connection B"; const char *conv_conn_b_title = "Connection B";
const char *conv_abs_start_title = "Abs Start";
const char *endp_column_titles[ENDP_NUM_COLUMNS] = { const char *endp_column_titles[ENDP_NUM_COLUMNS] = {
"Address", "Address",

View File

@ -54,6 +54,7 @@ typedef enum {
extern const char *conv_column_titles[CONV_NUM_COLUMNS]; extern const char *conv_column_titles[CONV_NUM_COLUMNS];
extern const char *conv_conn_a_title; extern const char *conv_conn_a_title;
extern const char *conv_conn_b_title; extern const char *conv_conn_b_title;
extern const char *conv_abs_start_title;
typedef enum typedef enum
{ {