Compare commits
50 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
ca956c3c18 | ||
|
f98c8af594 | ||
|
accc47fa34 | ||
|
6f71844f7c | ||
|
bd2a4715dc | ||
|
4886739da6 | ||
|
80a4578847 | ||
|
390ffae2b4 | ||
|
d17ab7ab42 | ||
|
5abd6fb962 | ||
|
3c0bc067fa | ||
|
223e44a6e3 | ||
|
6108de9b4f | ||
|
e761bfa5c4 | ||
|
b9345463c4 | ||
|
dc6c4963a2 | ||
|
cded62246e | ||
|
10befe0a66 | ||
|
4394c7ffee | ||
|
ef1c166e7f | ||
|
d1d2a5c1b5 | ||
|
f56ce2e2ec | ||
|
c91475d416 | ||
|
b2ad3c5d08 | ||
|
896371b987 | ||
|
f5bb2e5d80 | ||
|
7cfccd686e | ||
|
2e502434db | ||
|
aa140cd32a | ||
|
3b499263ec | ||
|
c378600baf | ||
|
68734c44ca | ||
|
05a9dd5743 | ||
|
05335b31d5 | ||
|
f4c21e1ae4 | ||
|
757d3f1b24 | ||
|
7e30ef5541 | ||
|
f7b6b46a5d | ||
|
823f01557b | ||
|
682d0cc5c9 | ||
|
f1cfaa6e89 | ||
|
072dfcdc3b | ||
|
90a3731a90 | ||
|
5d4a19174e | ||
|
2104936b69 | ||
|
5dc1b851cc | ||
|
8e25669a0e | ||
|
ebccc44cdf | ||
|
f3a9b507b0 | ||
|
620004d46b |
@ -13,11 +13,11 @@ I have been developing and maintaining Ostinato [single-handedly](https://github
|
||||
|
||||
I sell binary licenses on [ostinato.org](https://ostinato.org/downloads) to try and cover the costs of development. Please consider buying those - they are priced low enough that you can afford it or you could just as easily expense them to your organisation.
|
||||
|
||||
If you build Ostinato from source and find it useful, please sponsor to keep the lights on and sustain the project.
|
||||
|
||||
[![](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86&style=for-the-badge)](https://github.com/sponsors/pstavirs)
|
||||
If you build Ostinato from source and find it useful, please donate to keep the lights on and sustain the project.
|
||||
|
||||
Read the Ostinato [origin story](https://ostinato.org/about).
|
||||
|
||||
[![Donate](https://ostinato.org/images/donate.png)](https://gum.co/ostdonate)
|
||||
|
||||
Srivats P<br/>
|
||||
Author, Ostinato
|
||||
|
@ -94,7 +94,7 @@
|
||||
<item>
|
||||
<widget class="QLabel" name="CopyrightLabel" >
|
||||
<property name="text" >
|
||||
<string>Copyright (c) 2007-2023 Srivats P.</string>
|
||||
<string>Copyright (c) 2007-2020 Srivats P.</string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignCenter</set>
|
||||
|
@ -31,13 +31,9 @@ class IconOnlyDelegate : public QStyledItemDelegate
|
||||
{
|
||||
QStyleOptionViewItem opt = option;
|
||||
opt.decorationPosition = QStyleOptionViewItem::Top;
|
||||
opt.features &= ~QStyleOptionViewItem::HasDisplay;
|
||||
QStyledItemDelegate::paint(painter, opt, index);
|
||||
}
|
||||
|
||||
QString displayText(const QVariant&, const QLocale&) const
|
||||
{
|
||||
return QString();
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -24,7 +24,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#endif
|
||||
|
||||
#include "clipboardhelper.h"
|
||||
#include "fileformatoptions.h"
|
||||
#include "jumpurl.h"
|
||||
#include "logsmodel.h"
|
||||
#include "logswindow.h"
|
||||
@ -548,7 +547,7 @@ bool MainWindow::openSession(QString fileName, QString &error)
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if ((optDialog = FileFormatOptions::openOptionsDialog(fmt)))
|
||||
if ((optDialog = fmt->openOptionsDialog()))
|
||||
{
|
||||
int ret;
|
||||
optDialog->setParent(this, Qt::Dialog);
|
||||
|
@ -5,26 +5,31 @@ win32:RC_FILE = ostinato.rc
|
||||
macx:ICON = icons/logo.icns
|
||||
QT += widgets network script xml svg
|
||||
INCLUDEPATH += "../rpc/" "../common/"
|
||||
|
||||
OBJDIR = .
|
||||
win32 {
|
||||
QMAKE_LFLAGS += -static
|
||||
CONFIG(debug, debug|release) {
|
||||
OBJDIR = debug
|
||||
LIBS += -L"../common/debug" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc/debug" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/debug/libostprotogui.a" \
|
||||
"../common/debug/libostproto.a" \
|
||||
"../rpc/debug/libpbrpc.a"
|
||||
} else {
|
||||
OBJDIR = release
|
||||
LIBS += -L"../common/release" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc/release" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/release/libostprotogui.a" \
|
||||
"../common/release/libostproto.a" \
|
||||
"../rpc/release/libpbrpc.a"
|
||||
}
|
||||
} else {
|
||||
LIBS += -L"../common" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/libostprotogui.a" \
|
||||
"../common/libostproto.a" \
|
||||
"../rpc/libpbrpc.a"
|
||||
}
|
||||
LIBS += -L"../common/$$OBJDIR" -lostfile -lostfilegui
|
||||
LIBS += -L"../common/$$OBJDIR" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc/$$OBJDIR" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/$$OBJDIR/libostfilegui.a" \
|
||||
"../common/$$OBJDIR/libostfile.a" \
|
||||
"../common/$$OBJDIR/libostprotogui.a" \
|
||||
"../common/$$OBJDIR/libostproto.a" \
|
||||
"../rpc/$$OBJDIR/libpbrpc.a"
|
||||
|
||||
LIBS += -lprotobuf
|
||||
LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2
|
||||
RESOURCES += ostinato.qrc
|
||||
|
@ -20,7 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "port.h"
|
||||
|
||||
#include "emulation.h"
|
||||
#include "fileformatoptions.h"
|
||||
#include "streamfileformat.h"
|
||||
|
||||
#include <QApplication>
|
||||
@ -605,7 +604,7 @@ bool Port::openStreams(QString fileName, bool append, QString &error)
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if ((optDialog = FileFormatOptions::openOptionsDialog(fmt)))
|
||||
if ((optDialog = fmt->openOptionsDialog()))
|
||||
{
|
||||
int ret;
|
||||
optDialog->setParent(mainWindow, Qt::Dialog);
|
||||
|
@ -137,10 +137,7 @@ QVariant PortStatsModel::data(const QModelIndex &index, int role) const
|
||||
|
||||
// States
|
||||
case e_COMBO_STATE:
|
||||
return QString("Link %1%2%3")
|
||||
.arg(LinkStateName.at(stats.state().link_state()))
|
||||
.arg(stats.state().is_transmit_on() ? ";Tx On" : "")
|
||||
.arg(stats.state().is_capture_on() ? ";Cap On" : "");
|
||||
return QVariant();
|
||||
|
||||
// Statistics
|
||||
case e_STAT_FRAMES_RCVD:
|
||||
|
@ -22,12 +22,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
#include <QSet>
|
||||
|
||||
class PortStatsProxyModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PortStatsProxyModel(int userRow, QObject *parent = 0)
|
||||
: QSortFilterProxyModel(parent), userRow_(userRow)
|
||||
PortStatsProxyModel(QSet<int> hiddenRows = QSet<int>(),
|
||||
QObject *parent = 0)
|
||||
: QSortFilterProxyModel(parent), hiddenRows_(hiddenRows)
|
||||
{
|
||||
setFilterRegExp(QRegExp(".*"));
|
||||
}
|
||||
@ -36,7 +39,7 @@ protected:
|
||||
bool filterAcceptsColumn(int sourceColumn,
|
||||
const QModelIndex &sourceParent) const
|
||||
{
|
||||
QModelIndex index = sourceModel()->index(userRow_, sourceColumn,sourceParent);
|
||||
QModelIndex index = sourceModel()->index(0, sourceColumn, sourceParent);
|
||||
QString user = sourceModel()->data(index).toString();
|
||||
|
||||
return filterRegExp().exactMatch(user) ? true : false;
|
||||
@ -44,10 +47,10 @@ protected:
|
||||
bool filterAcceptsRow(int sourceRow,
|
||||
const QModelIndex &/*sourceParent*/) const
|
||||
{
|
||||
return sourceRow == userRow_ ? false : true;
|
||||
return hiddenRows_.contains(sourceRow) ? false : true;
|
||||
}
|
||||
private:
|
||||
int userRow_;
|
||||
QSet<int> hiddenRows_;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -44,7 +44,8 @@ PortStatsWindow::PortStatsWindow(PortGroupList *pgl, QWidget *parent)
|
||||
model = pgl->getPortStatsModel();
|
||||
|
||||
// Hide 'user' row
|
||||
proxyStatsModel = new PortStatsProxyModel(e_INFO_USER, this);
|
||||
proxyStatsModel = new PortStatsProxyModel(
|
||||
QSet<int>({e_INFO_USER}), this);
|
||||
if (proxyStatsModel) {
|
||||
proxyStatsModel->setSourceModel(model);
|
||||
tvPortStats->setModel(proxyStatsModel);
|
||||
|
@ -52,7 +52,6 @@ enum {
|
||||
kAvgTxBitRate,
|
||||
kAvgRxBitRate,
|
||||
kAvgLatency,
|
||||
kAvgJitter,
|
||||
kMaxAggrStreamStats
|
||||
};
|
||||
static QStringList aggrStatTitles = QStringList()
|
||||
@ -64,8 +63,7 @@ static QStringList aggrStatTitles = QStringList()
|
||||
<< "Avg\nRx PktRate"
|
||||
<< "Avg\nTx BitRate"
|
||||
<< "Avg\nRx BitRate"
|
||||
<< "Avg\nLatency"
|
||||
<< "Avg\nJitter";
|
||||
<< "Avg\nLatency";
|
||||
|
||||
static const uint kAggrGuid = 0xffffffff;
|
||||
|
||||
@ -168,12 +166,12 @@ QVariant StreamStatsModel::data(const QModelIndex &index, int role) const
|
||||
return QString("%L1").arg(aggrGuidStats_.value(guid).txDuration);
|
||||
case kAvgTxFrameRate:
|
||||
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
|
||||
XLocale().toPktRateString(
|
||||
QString("%L1").arg(
|
||||
aggrGuidStats_.value(guid).txPkts
|
||||
/ aggrGuidStats_.value(guid).txDuration);
|
||||
case kAvgRxFrameRate:
|
||||
return aggrGuidStats_.value(guid).txDuration <= 0 ? QString("-") :
|
||||
XLocale().toPktRateString(
|
||||
QString("%L1").arg(
|
||||
aggrGuidStats_.value(guid).rxPkts
|
||||
/ aggrGuidStats_.value(guid).txDuration);
|
||||
case kAvgTxBitRate:
|
||||
@ -194,12 +192,6 @@ QVariant StreamStatsModel::data(const QModelIndex &index, int role) const
|
||||
XLocale().toTimeIntervalString(
|
||||
aggrGuidStats_.value(guid).latencySum
|
||||
/ aggrGuidStats_.value(guid).latencyCount);
|
||||
case kAvgJitter:
|
||||
return aggrGuidStats_.value(guid).latencyCount <= 0
|
||||
|| aggrGuidStats_.value(guid).latencySum <= 0 ? QString("-") :
|
||||
XLocale().toTimeIntervalString(
|
||||
aggrGuidStats_.value(guid).jitterSum
|
||||
/ aggrGuidStats_.value(guid).latencyCount);
|
||||
default:
|
||||
break;
|
||||
};
|
||||
@ -275,7 +267,6 @@ void StreamStatsModel::appendStreamStatsList(
|
||||
ss.rxBytes = s.rx_bytes();
|
||||
ss.txBytes = s.tx_bytes();
|
||||
ss.rxLatency = s.latency();
|
||||
ss.rxJitter = s.jitter();
|
||||
|
||||
aggrPort.rxPkts += ss.rxPkts;
|
||||
aggrPort.txPkts += ss.txPkts;
|
||||
@ -291,7 +282,6 @@ void StreamStatsModel::appendStreamStatsList(
|
||||
aggrGuid.txDuration = s.tx_duration(); // XXX: use largest or avg?
|
||||
if (ss.rxLatency) {
|
||||
aggrGuid.latencySum += ss.rxLatency;
|
||||
aggrGuid.jitterSum += ss.rxJitter;
|
||||
aggrGuid.latencyCount++;
|
||||
}
|
||||
|
||||
@ -304,7 +294,6 @@ void StreamStatsModel::appendStreamStatsList(
|
||||
aggrAggr.txDuration = aggrGuid.txDuration;
|
||||
if (ss.rxLatency) {
|
||||
aggrAggr.latencySum += ss.rxLatency;
|
||||
aggrAggr.jitterSum += ss.rxJitter;
|
||||
aggrAggr.latencyCount++;
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,6 @@ private:
|
||||
quint64 rxBytes;
|
||||
quint64 txBytes;
|
||||
quint64 rxLatency;
|
||||
quint64 rxJitter;
|
||||
};
|
||||
struct AggrGuidStats {
|
||||
quint64 rxPkts;
|
||||
@ -68,7 +67,6 @@ private:
|
||||
qint64 pktLoss;
|
||||
double txDuration;
|
||||
quint64 latencySum;
|
||||
quint64 jitterSum;
|
||||
uint latencyCount;
|
||||
};
|
||||
QList<Guid> guidList_;
|
||||
|
@ -46,10 +46,6 @@ StreamStatsWindow::StreamStatsWindow(QAbstractItemModel *model, QWidget *parent)
|
||||
streamStats->verticalHeader()->setHighlightSections(false);
|
||||
streamStats->verticalHeader()->setDefaultSectionSize(
|
||||
streamStats->verticalHeader()->minimumSectionSize());
|
||||
|
||||
// Fit all columns in window whenever data changes
|
||||
connect(model, &QAbstractItemModel::modelReset,
|
||||
[=]() { streamStats->resizeColumnsToContents(); });
|
||||
}
|
||||
|
||||
StreamStatsWindow::~StreamStatsWindow()
|
||||
@ -66,6 +62,4 @@ void StreamStatsWindow::on_actionShowDetails_triggered(bool checked)
|
||||
filterModel_->setFilterRegExp(QRegExp(".*"));
|
||||
else
|
||||
filterModel_->setFilterRegExp(QRegExp(kDefaultFilter_));
|
||||
|
||||
streamStats->resizeColumnsToContents();
|
||||
}
|
||||
|
@ -19,9 +19,6 @@
|
||||
<property name="contextMenuPolicy">
|
||||
<enum>Qt::ActionsContextMenu</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="whatsThis">
|
||||
<string>Oops! We don't seem to have any stream statistics for the requested port(s)
|
||||
|
||||
|
@ -85,33 +85,20 @@ public:
|
||||
return toDouble(text, ok) * multiplier;
|
||||
}
|
||||
|
||||
QString toPktRateString(double pps) const
|
||||
{
|
||||
QString text;
|
||||
|
||||
if (pps >= 1e6)
|
||||
return QObject::tr("%L1 Mpps").arg(pps/1e6, 0, 'f', 3);
|
||||
|
||||
if (pps >= 1e3)
|
||||
return QObject::tr("%L1 Kpps").arg(pps/1e3, 0, 'f', 3);
|
||||
|
||||
return QObject::tr("%L1").arg(pps, 0, 'f', 3);
|
||||
}
|
||||
|
||||
QString toBitRateString(double bps) const
|
||||
{
|
||||
QString text;
|
||||
|
||||
if (bps >= 1e9)
|
||||
return QObject::tr("%L1 Gbps").arg(bps/1e9, 0, 'f', 3);
|
||||
return QObject::tr("%L1 Gbps").arg(bps/1e9, 0, 'f', 4);
|
||||
|
||||
if (bps >= 1e6)
|
||||
return QObject::tr("%L1 Mbps").arg(bps/1e6, 0, 'f', 3);
|
||||
return QObject::tr("%L1 Mbps").arg(bps/1e6, 0, 'f', 4);
|
||||
|
||||
if (bps >= 1e3)
|
||||
return QObject::tr("%L1 Kbps").arg(bps/1e3, 0, 'f', 3);
|
||||
return QObject::tr("%L1 Kbps").arg(bps/1e3, 0, 'f', 4);
|
||||
|
||||
return QObject::tr("%L1 bps").arg(bps, 0, 'f', 3);
|
||||
return QObject::tr("%L1 bps").arg(bps, 0, 'f', 4);
|
||||
}
|
||||
|
||||
QString toTimeIntervalString(qint64 nanosecs) const
|
||||
@ -119,13 +106,13 @@ public:
|
||||
QString text;
|
||||
|
||||
if (nanosecs >= 1e9)
|
||||
return QObject::tr("%L1 s").arg(nanosecs/1e9, 0, 'f', 2);
|
||||
return QObject::tr("%L1 s").arg(nanosecs/1e9, 0, 'f', 3);
|
||||
|
||||
if (nanosecs >= 1e6)
|
||||
return QObject::tr("%L1 ms").arg(nanosecs/1e6, 0, 'f', 2);
|
||||
return QObject::tr("%L1 ms").arg(nanosecs/1e6, 0, 'f', 3);
|
||||
|
||||
if (nanosecs >= 1e3)
|
||||
return QObject::tr("%L1 us").arg(nanosecs/1e3, 0, 'f', 2);
|
||||
return QObject::tr("%L1 us").arg(nanosecs/1e3, 0, 'f', 3);
|
||||
|
||||
return QObject::tr("%L1 ns").arg(nanosecs);
|
||||
}
|
||||
|
@ -169,8 +169,7 @@ private:
|
||||
for (int i = start; i < end; i++)
|
||||
if (indexes.contains(model()->index(indexes.first().row(), i)))
|
||||
text.append(model()->headerData(i, Qt::Horizontal)
|
||||
.toString().replace('\n', ' ')
|
||||
+"\t");;
|
||||
.toString()+"\t");;
|
||||
text.append("\n");
|
||||
}
|
||||
|
||||
|
@ -62,14 +62,12 @@ void ArpProtocol::protoDataCopyFrom(const OstProto::Protocol &protocol)
|
||||
|
||||
QString ArpProtocol::name() const
|
||||
{
|
||||
return isRarp() ?
|
||||
QString("Reverse Address Resolution Protocol") :
|
||||
QString("Address Resolution Protocol");
|
||||
return QString("Address Resolution Protocol");
|
||||
}
|
||||
|
||||
QString ArpProtocol::shortName() const
|
||||
{
|
||||
return isRarp() ? QString("RARP") : QString("ARP");
|
||||
return QString("ARP");
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -98,7 +96,7 @@ quint32 ArpProtocol::protocolId(ProtocolIdType type) const
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case ProtocolIdEth: return isRarp() ? 0x8035 : 0x0806;
|
||||
case ProtocolIdEth: return 0x0806;
|
||||
default:break;
|
||||
}
|
||||
|
||||
@ -810,11 +808,3 @@ int ArpProtocol::protocolFrameVariableCount() const
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool ArpProtocol::isRarp() const
|
||||
{
|
||||
if ((data.op_code() == 3)
|
||||
|| (data.op_code() ==4))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
@ -96,8 +96,6 @@ public:
|
||||
virtual int protocolFrameVariableCount() const;
|
||||
|
||||
private:
|
||||
bool isRarp() const;
|
||||
|
||||
OstProto::Arp data;
|
||||
};
|
||||
|
||||
|
@ -30,8 +30,6 @@ ArpConfigForm::ArpConfigForm(QWidget *parent)
|
||||
opCodeCombo->setValidator(new QIntValidator(0, 0xFFFF, this));
|
||||
opCodeCombo->addItem(1, "ARP Request");
|
||||
opCodeCombo->addItem(2, "ARP Reply");
|
||||
opCodeCombo->addItem(3, "Reverse ARP Request");
|
||||
opCodeCombo->addItem(4, "Reverse ARP Reply");
|
||||
|
||||
connect(senderHwAddrMode, SIGNAL(currentIndexChanged(int)),
|
||||
SLOT(on_senderHwAddrMode_currentIndexChanged(int)));
|
||||
|
@ -1,47 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2022 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This 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 3 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, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "fileformatoptions.h"
|
||||
|
||||
#include "pcapfileformat.h"
|
||||
#include "pcapoptionsdialog.h"
|
||||
#include "streamfileformat.h"
|
||||
|
||||
QDialog* FileFormatOptions::openOptionsDialog(StreamFileFormat *fileFormat)
|
||||
{
|
||||
if (dynamic_cast<PcapFileFormat*>(fileFormat))
|
||||
return new PcapImportOptionsDialog(fileFormat->options());
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QDialog* FileFormatOptions::saveOptionsDialog(StreamFileFormat* /*fileFormat*/)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QDialog* FileFormatOptions::openOptionsDialog(SessionFileFormat* /*fileFormat*/)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QDialog* FileFormatOptions::saveOptionsDialog(SessionFileFormat* /*fileFormat*/)
|
||||
{
|
||||
return NULL;
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2022 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This 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 3 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, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#ifndef _FILE_FORMAT_OPTIONS_H
|
||||
#define _FILE_FORMAT_OPTIONS_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class SessionFileFormat;
|
||||
class StreamFileFormat;
|
||||
class QDialog;
|
||||
|
||||
class FileFormatOptions : QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static QDialog* openOptionsDialog(StreamFileFormat *fileFormat);
|
||||
static QDialog* saveOptionsDialog(StreamFileFormat *fileFormat);
|
||||
|
||||
static QDialog* openOptionsDialog(SessionFileFormat *fileFormat);
|
||||
static QDialog* saveOptionsDialog(SessionFileFormat *fileFormat);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -214,6 +214,12 @@ Length (x4)</string>
|
||||
</item>
|
||||
<item row="1" column="1" >
|
||||
<widget class="QLineEdit" name="leIpSrcAddr" >
|
||||
<property name="inputMask" >
|
||||
<string>009.009.009.009; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
@ -258,6 +264,12 @@ Length (x4)</string>
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="inputMask" >
|
||||
<string>009.009.009.009; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0" >
|
||||
@ -269,6 +281,12 @@ Length (x4)</string>
|
||||
</item>
|
||||
<item row="2" column="1" >
|
||||
<widget class="QLineEdit" name="leIpDstAddr" >
|
||||
<property name="inputMask" >
|
||||
<string>000.000.000.000; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="alignment" >
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
@ -313,6 +331,12 @@ Length (x4)</string>
|
||||
<property name="enabled" >
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="inputMask" >
|
||||
<string>009.009.009.009; </string>
|
||||
</property>
|
||||
<property name="text" >
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -19,7 +19,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "ip4config.h"
|
||||
#include "ip4.h"
|
||||
#include "ipv4addressvalidator.h"
|
||||
|
||||
#include <QHostAddress>
|
||||
|
||||
@ -31,10 +30,6 @@ Ip4ConfigForm::Ip4ConfigForm(QWidget *parent)
|
||||
leIpVersion->setValidator(new QIntValidator(0, 15, this));
|
||||
leIpOptions->setValidator(new QRegExpValidator(QRegExp("[0-9a-fA-F]*"),
|
||||
this));
|
||||
leIpSrcAddr->setValidator(new IPv4AddressValidator(this));
|
||||
leIpSrcAddrMask->setValidator(new IPv4AddressValidator(this));
|
||||
leIpDstAddr->setValidator(new IPv4AddressValidator(this));
|
||||
leIpDstAddrMask->setValidator(new IPv4AddressValidator(this));
|
||||
|
||||
connect(cmbIpSrcAddrMode, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(on_cmbIpSrcAddrMode_currentIndexChanged(int)));
|
||||
|
@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "crc32c.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QApplication>
|
||||
#include <QFile>
|
||||
#include <QVariant>
|
||||
|
||||
@ -437,19 +437,17 @@ _exit:
|
||||
|
||||
void NativeFileFormat::initFileMetaData(OstProto::FileMetaData &metaData)
|
||||
{
|
||||
QCoreApplication *app = QCoreApplication::instance();
|
||||
|
||||
// Fill in the "native" file format version
|
||||
metaData.set_format_version_major(kFileFormatVersionMajor);
|
||||
metaData.set_format_version_minor(kFileFormatVersionMinor);
|
||||
metaData.set_format_version_revision(kFileFormatVersionRevision);
|
||||
|
||||
metaData.set_generator_name(
|
||||
app->applicationName().toUtf8().constData());
|
||||
qApp->applicationName().toUtf8().constData());
|
||||
metaData.set_generator_version(
|
||||
app->property("version").toString().toUtf8().constData());
|
||||
qApp->property("version").toString().toUtf8().constData());
|
||||
metaData.set_generator_revision(
|
||||
app->property("revision").toString().toUtf8().constData());
|
||||
qApp->property("revision").toString().toUtf8().constData());
|
||||
}
|
||||
|
||||
int NativeFileFormat::fileMetaSize(const quint8* file, int size)
|
||||
|
@ -1,60 +0,0 @@
|
||||
TEMPLATE = lib
|
||||
CONFIG += qt staticlib
|
||||
QT += network xml script
|
||||
LIBS += \
|
||||
-lprotobuf
|
||||
|
||||
PROTOS = \
|
||||
fileformat.proto
|
||||
|
||||
HEADERS = \
|
||||
ostprotolib.h \
|
||||
nativefileformat.h \
|
||||
ossnfileformat.h \
|
||||
ostmfileformat.h \
|
||||
pcapfileformat.h \
|
||||
pdmlfileformat.h \
|
||||
pythonfileformat.h \
|
||||
pdmlprotocol.h \
|
||||
pdmlprotocols.h \
|
||||
pdmlreader.h \
|
||||
sessionfileformat.h \
|
||||
streamfileformat.h
|
||||
|
||||
SOURCES += \
|
||||
ostprotolib.cpp \
|
||||
nativefileformat.cpp \
|
||||
ossnfileformat.cpp \
|
||||
ostmfileformat.cpp \
|
||||
pcapfileformat.cpp \
|
||||
pdmlfileformat.cpp \
|
||||
pythonfileformat.cpp \
|
||||
pdmlprotocol.cpp \
|
||||
pdmlprotocols.cpp \
|
||||
pdmlreader.cpp \
|
||||
sessionfileformat.cpp \
|
||||
streamfileformat.cpp \
|
||||
|
||||
SOURCES += \
|
||||
vlanpdml.cpp \
|
||||
svlanpdml.cpp \
|
||||
stppdml.cpp \
|
||||
eth2pdml.cpp \
|
||||
llcpdml.cpp \
|
||||
arppdml.cpp \
|
||||
ip4pdml.cpp \
|
||||
ip6pdml.cpp \
|
||||
grepdml.cpp \
|
||||
icmppdml.cpp \
|
||||
icmp6pdml.cpp \
|
||||
igmppdml.cpp \
|
||||
mldpdml.cpp \
|
||||
tcppdml.cpp \
|
||||
udppdml.cpp \
|
||||
textprotopdml.cpp \
|
||||
samplepdml.cpp
|
||||
|
||||
QMAKE_DISTCLEAN += object_script.*
|
||||
|
||||
include(../protobuf.pri)
|
||||
include(../options.pri)
|
@ -1,15 +0,0 @@
|
||||
TEMPLATE = lib
|
||||
CONFIG += qt staticlib
|
||||
QT += widgets
|
||||
|
||||
FORMS = \
|
||||
pcapfileimport.ui
|
||||
|
||||
HEADERS = \
|
||||
fileformatoptions.h \
|
||||
pcapoptionsdialog.h
|
||||
|
||||
SOURCES = \
|
||||
fileformatoptions.cpp \
|
||||
pcapoptionsdialog.cpp
|
||||
|
@ -2,6 +2,11 @@ TEMPLATE = lib
|
||||
CONFIG += qt staticlib
|
||||
QT += widgets network xml script
|
||||
INCLUDEPATH += "../extra/qhexedit2/src"
|
||||
LIBS += \
|
||||
-lprotobuf
|
||||
|
||||
FORMS = \
|
||||
pcapfileimport.ui \
|
||||
|
||||
FORMS += \
|
||||
mac.ui \
|
||||
@ -27,10 +32,28 @@ FORMS += \
|
||||
sign.ui \
|
||||
userscript.ui
|
||||
|
||||
PROTOS = \
|
||||
fileformat.proto
|
||||
|
||||
# TODO: Move fileformat related stuff into a different library - why?
|
||||
HEADERS = \
|
||||
ostprotolib.h \
|
||||
ipv4addressdelegate.h \
|
||||
ipv6addressdelegate.h \
|
||||
spinboxdelegate.h \
|
||||
nativefileformat.h \
|
||||
ossnfileformat.h \
|
||||
ostmfileformat.h \
|
||||
pcapfileformat.h \
|
||||
pdmlfileformat.h \
|
||||
pythonfileformat.h \
|
||||
pdmlprotocol.h \
|
||||
pdmlprotocols.h \
|
||||
pdmlreader.h \
|
||||
sessionfileformat.h \
|
||||
streamfileformat.h \
|
||||
spinboxdelegate.h
|
||||
|
||||
HEADERS += \
|
||||
tosdscp.h
|
||||
|
||||
HEADERS += \
|
||||
@ -67,7 +90,21 @@ HEADERS += \
|
||||
userscriptconfig.h
|
||||
|
||||
SOURCES += \
|
||||
spinboxdelegate.cpp \
|
||||
ostprotolib.cpp \
|
||||
nativefileformat.cpp \
|
||||
ossnfileformat.cpp \
|
||||
ostmfileformat.cpp \
|
||||
pcapfileformat.cpp \
|
||||
pdmlfileformat.cpp \
|
||||
pythonfileformat.cpp \
|
||||
pdmlprotocol.cpp \
|
||||
pdmlprotocols.cpp \
|
||||
pdmlreader.cpp \
|
||||
sessionfileformat.cpp \
|
||||
streamfileformat.cpp \
|
||||
spinboxdelegate.cpp
|
||||
|
||||
SOURCES += \
|
||||
tosdscp.cpp
|
||||
|
||||
SOURCES += \
|
||||
@ -96,6 +133,26 @@ SOURCES += \
|
||||
signconfig.cpp \
|
||||
userscriptconfig.cpp
|
||||
|
||||
SOURCES += \
|
||||
vlanpdml.cpp \
|
||||
svlanpdml.cpp \
|
||||
stppdml.cpp \
|
||||
eth2pdml.cpp \
|
||||
llcpdml.cpp \
|
||||
arppdml.cpp \
|
||||
ip4pdml.cpp \
|
||||
ip6pdml.cpp \
|
||||
grepdml.cpp \
|
||||
icmppdml.cpp \
|
||||
icmp6pdml.cpp \
|
||||
igmppdml.cpp \
|
||||
mldpdml.cpp \
|
||||
tcppdml.cpp \
|
||||
udppdml.cpp \
|
||||
textprotopdml.cpp \
|
||||
samplepdml.cpp
|
||||
|
||||
QMAKE_DISTCLEAN += object_script.*
|
||||
|
||||
include(../protobuf.pri)
|
||||
include(../options.pri)
|
||||
|
@ -78,7 +78,6 @@ const quint16 kIp4EthType = 0x0800;
|
||||
const quint16 kIp6EthType = 0x86dd;
|
||||
const quint16 kMplsEthType = 0x8847;
|
||||
const QSet<quint16> kVlanEthTypes = {0x8100, 0x9100, 0x88a8};
|
||||
const uint kEthOverhead = 20;
|
||||
|
||||
// VLAN
|
||||
const quint16 kVlanTagSize = 4;
|
||||
|
@ -42,6 +42,38 @@ const quint32 kDltEthernet = 1;
|
||||
|
||||
PcapFileFormat pcapFileFormat;
|
||||
|
||||
PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options)
|
||||
: QDialog(NULL)
|
||||
{
|
||||
setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
options_ = options;
|
||||
|
||||
viaPdml->setChecked(options_->value("ViaPdml").toBool());
|
||||
// XXX: By default this key is absent - so that pcap import tests
|
||||
// evaluate to false and hence show minimal diffs.
|
||||
// However, for the GUI user, this should be enabled by default.
|
||||
recalculateCksums->setChecked(
|
||||
options_->value("RecalculateCksums", QVariant(true))
|
||||
.toBool());
|
||||
doDiff->setChecked(options_->value("DoDiff").toBool());
|
||||
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
}
|
||||
|
||||
PcapImportOptionsDialog::~PcapImportOptionsDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void PcapImportOptionsDialog::accept()
|
||||
{
|
||||
options_->insert("ViaPdml", viaPdml->isChecked());
|
||||
options_->insert("RecalculateCksums", recalculateCksums->isChecked());
|
||||
options_->insert("DoDiff", doDiff->isChecked());
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
||||
PcapFileFormat::PcapFileFormat()
|
||||
{
|
||||
importOptions_.insert("ViaPdml", true);
|
||||
@ -431,7 +463,7 @@ _retry:
|
||||
diffFile.close();
|
||||
if (diffFile.size())
|
||||
{
|
||||
error.append(tr("<p>There is a diff between the original and imported streams. See details to review the diff.</p><p>💡 If you don't need to edit packets, you can retry the import and uncheck the Intelligent Import option.</p><p>Why a diff? See <a href='%1'>possible reasons</a>.</p>\n\n\n\n").arg("https://jump.ostinato.org/pcapdiff"));
|
||||
error.append(tr("<p>There is a diff between the original and imported streams. See details to review the diff.</p><p>Why a diff? See <a href='%1'>possible reasons</a>.</p>\n\n\n\n").arg("https://jump.ostinato.org/pcapdiff"));
|
||||
diffFile.open();
|
||||
diffFile.seek(0);
|
||||
error.append(QString(diffFile.readAll()));
|
||||
@ -699,9 +731,9 @@ _exit:
|
||||
return isOk;
|
||||
}
|
||||
|
||||
QVariantMap* PcapFileFormat::options()
|
||||
QDialog* PcapFileFormat::openOptionsDialog()
|
||||
{
|
||||
return &importOptions_;
|
||||
return new PcapImportOptionsDialog(&importOptions_);
|
||||
}
|
||||
|
||||
bool PcapFileFormat::isMyFileFormat(const QString /*fileName*/)
|
||||
|
@ -20,10 +20,24 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#define _PCAP_FILE_FORMAT_H
|
||||
|
||||
#include "streamfileformat.h"
|
||||
#include "ui_pcapfileimport.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QVariantMap>
|
||||
|
||||
class PcapImportOptionsDialog: public QDialog, public Ui::PcapFileImport
|
||||
{
|
||||
public:
|
||||
PcapImportOptionsDialog(QVariantMap *options);
|
||||
~PcapImportOptionsDialog();
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
|
||||
private:
|
||||
QVariantMap *options_;
|
||||
};
|
||||
|
||||
class PdmlReader;
|
||||
class PcapFileFormat : public StreamFileFormat
|
||||
{
|
||||
@ -38,7 +52,7 @@ public:
|
||||
bool save(const OstProto::StreamConfigList streams,
|
||||
const QString fileName, QString &error);
|
||||
|
||||
virtual QVariantMap* options();
|
||||
virtual QDialog* openOptionsDialog();
|
||||
|
||||
bool isMyFileFormat(const QString fileName);
|
||||
bool isMyFileType(const QString fileType);
|
||||
|
@ -1,55 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2011 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This 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 3 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, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include "pcapoptionsdialog.h"
|
||||
|
||||
PcapImportOptionsDialog::PcapImportOptionsDialog(QVariantMap *options)
|
||||
: QDialog(NULL)
|
||||
{
|
||||
Q_ASSERT(options != NULL);
|
||||
|
||||
setupUi(this);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
options_ = options;
|
||||
|
||||
viaPdml->setChecked(options_->value("ViaPdml").toBool());
|
||||
// XXX: By default this key is absent - so that pcap import tests
|
||||
// evaluate to false and hence show minimal diffs.
|
||||
// However, for the GUI user, this should be enabled by default.
|
||||
recalculateCksums->setChecked(
|
||||
options_->value("RecalculateCksums", QVariant(true))
|
||||
.toBool());
|
||||
doDiff->setChecked(options_->value("DoDiff").toBool());
|
||||
|
||||
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||
}
|
||||
|
||||
PcapImportOptionsDialog::~PcapImportOptionsDialog()
|
||||
{
|
||||
}
|
||||
|
||||
void PcapImportOptionsDialog::accept()
|
||||
{
|
||||
options_->insert("ViaPdml", viaPdml->isChecked());
|
||||
options_->insert("RecalculateCksums", recalculateCksums->isChecked());
|
||||
options_->insert("DoDiff", doDiff->isChecked());
|
||||
|
||||
QDialog::accept();
|
||||
}
|
||||
|
@ -1,39 +0,0 @@
|
||||
/*
|
||||
Copyright (C) 2011 Srivats P.
|
||||
|
||||
This file is part of "Ostinato"
|
||||
|
||||
This 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 3 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, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
#ifndef _PCAP_OPTIONS_DIALOG_H
|
||||
#define _PCAP_OPTIONS_DIALOG_H
|
||||
|
||||
#include "ui_pcapfileimport.h"
|
||||
|
||||
#include <QVariantMap>
|
||||
|
||||
class PcapImportOptionsDialog: public QDialog, public Ui::PcapFileImport
|
||||
{
|
||||
public:
|
||||
PcapImportOptionsDialog(QVariantMap *options);
|
||||
~PcapImportOptionsDialog();
|
||||
|
||||
private slots:
|
||||
void accept();
|
||||
|
||||
private:
|
||||
QVariantMap *options_;
|
||||
};
|
||||
|
||||
#endif
|
@ -293,7 +293,6 @@ message StreamStats {
|
||||
|
||||
optional double tx_duration = 3; // in seconds
|
||||
optional uint64 latency = 4; // in nanoseconds
|
||||
optional uint64 jitter = 5; // in nanoseconds
|
||||
|
||||
optional uint64 rx_pkts = 11;
|
||||
optional uint64 rx_bytes = 12;
|
||||
|
@ -32,7 +32,12 @@ SessionFileFormat::~SessionFileFormat()
|
||||
{
|
||||
}
|
||||
|
||||
QVariantMap* SessionFileFormat::options()
|
||||
QDialog* SessionFileFormat::openOptionsDialog()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QDialog* SessionFileFormat::saveOptionsDialog()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -23,9 +23,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "fileformat.pb.h"
|
||||
#include "protocol.pb.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QThread>
|
||||
#include <QVariantMap>
|
||||
#include <QString>
|
||||
|
||||
class QDialog;
|
||||
|
||||
class SessionFileFormat : public QThread
|
||||
{
|
||||
@ -41,7 +42,8 @@ public:
|
||||
virtual bool save(const OstProto::SessionContent &session,
|
||||
const QString fileName, QString &error) = 0;
|
||||
|
||||
virtual QVariantMap* options();
|
||||
virtual QDialog* openOptionsDialog();
|
||||
virtual QDialog* saveOptionsDialog();
|
||||
|
||||
void openAsync(const QString fileName,
|
||||
OstProto::SessionContent &session, QString &error);
|
||||
|
@ -285,9 +285,7 @@ bool SignProtocol::packetTtagId(const uchar *pkt, int pktLen, uint *ttagId, uint
|
||||
} else if (*p == kTypeLenGuid) {
|
||||
*guid = qFromBigEndian<quint32>(p - 3) >> 8;
|
||||
} else if (*p == kTypeLenTxPort) {
|
||||
#ifdef Q_OS_WIN32
|
||||
*ttagId |= uint(*(p - 1)) << 8;
|
||||
#endif
|
||||
}
|
||||
p -= 1 + (*p >> 5); // move to next TLV
|
||||
}
|
||||
|
@ -41,19 +41,12 @@ TLVs are encoded as
|
||||
Len does NOT include the one byte of TypeLen
|
||||
Size of the value field varies between 0 to 7 bytes
|
||||
|
||||
Defined TLVs
|
||||
Defined TLVs
|
||||
Type = 0, Len = 0 (0x00): End of TLVs
|
||||
Type = 1, Len = 3 (0x61): Stream GUID
|
||||
Type = 2, Len = 1 (0x22): T-Tag Placeholder (0 value)
|
||||
Type = 3, Len = 1 (0x23): T-Tag with actual value
|
||||
Type = 4, Len = 1 (0x24): Tx Port Id
|
||||
|
||||
Order of TLVs from end of packet towards beginning [Offset, Size]
|
||||
[ -4, 4 bytes] Magic
|
||||
[ -6, 2 bytes] TTag (Placeholder or actual)
|
||||
[-10, 4 bytes] Stream Guid
|
||||
[-12, 2 bytes] Tx Port Id
|
||||
[-13, 1 byte ] End
|
||||
*/
|
||||
|
||||
class SignProtocol : public AbstractProtocol
|
||||
|
@ -684,7 +684,6 @@ bool StreamBase::preflightCheck(QStringList &result) const
|
||||
bool chkShort = true;
|
||||
bool chkTrunc = true;
|
||||
bool chkJumbo = true;
|
||||
bool chkSignIcmp = true;
|
||||
int count = isFrameSizeVariable() ? frameSizeVariableCount() : 1;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
@ -702,17 +701,6 @@ bool StreamBase::preflightCheck(QStringList &result) const
|
||||
pass = false;
|
||||
}
|
||||
|
||||
if (chkSignIcmp && hasProtocol(OstProto::Protocol::kSignFieldNumber)
|
||||
&& hasProtocol(OstProto::Protocol::kIcmpFieldNumber))
|
||||
{
|
||||
result << QObject::tr("Stream statistics are not supported "
|
||||
"for ICMP packets - please use a non-ICMP protocol or "
|
||||
"remove special signature from ICMP streams");
|
||||
chkSignIcmp = false;
|
||||
pass = false;
|
||||
}
|
||||
|
||||
|
||||
if (chkTrunc && (pktLen < (frameProtocolLength(i) + kFcsSize)))
|
||||
{
|
||||
result << QObject::tr("One or more frames may be truncated - "
|
||||
|
@ -35,7 +35,12 @@ StreamFileFormat::~StreamFileFormat()
|
||||
{
|
||||
}
|
||||
|
||||
QVariantMap* StreamFileFormat::options()
|
||||
QDialog* StreamFileFormat::openOptionsDialog()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
QDialog* StreamFileFormat::saveOptionsDialog()
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
@ -41,7 +41,8 @@ public:
|
||||
virtual bool save(const OstProto::StreamConfigList streams,
|
||||
const QString fileName, QString &error) = 0;
|
||||
|
||||
virtual QVariantMap* options();
|
||||
virtual QDialog* openOptionsDialog();
|
||||
virtual QDialog* saveOptionsDialog();
|
||||
|
||||
void openAsync(const QString fileName,
|
||||
OstProto::StreamConfigList &streams, QString &error);
|
||||
|
@ -32,8 +32,6 @@ Updater::Updater()
|
||||
|
||||
#if 1
|
||||
// Tests!
|
||||
Q_ASSERT(isVersionNewer("1.3.0", "1.2.0") == true);
|
||||
Q_ASSERT(isVersionNewer("1.3.0", "1.1") == true);
|
||||
Q_ASSERT(isVersionNewer("1.2.0", "1.1") == true);
|
||||
Q_ASSERT(isVersionNewer("1.1", "1") == true);
|
||||
Q_ASSERT(isVersionNewer("10.1", "2") == true);
|
||||
|
10
ost.pro
10
ost.pro
@ -1,20 +1,14 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = client server ostfile ostfilegui ostproto ostprotogui rpc extra
|
||||
SUBDIRS = client server ostproto ostprotogui rpc extra
|
||||
|
||||
client.target = client
|
||||
client.file = client/ostinato.pro
|
||||
client.depends = ostfile ostfilegui ostproto ostprotogui rpc extra
|
||||
client.depends = ostproto ostprotogui rpc extra
|
||||
|
||||
server.target = server
|
||||
server.file = server/drone.pro
|
||||
server.depends = ostproto rpc
|
||||
|
||||
ostfile.file = common/ostfile.pro
|
||||
ostfile.depends = ostproto
|
||||
|
||||
ostfilegui.file = common/ostfilegui.pro
|
||||
ostfilegui.depends = ostfile
|
||||
|
||||
ostproto.file = common/ostproto.pro
|
||||
|
||||
ostprotogui.file = common/ostprotogui.pro
|
||||
|
@ -21,11 +21,11 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "../common/abstractprotocol.h"
|
||||
#include "../common/framevalueattrib.h"
|
||||
#include "../common/packet.h"
|
||||
#include "../common/streambase.h"
|
||||
#include "devicemanager.h"
|
||||
#include "interfaceinfo.h"
|
||||
#include "packetbuffer.h"
|
||||
#include "streamtiming.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QIODevice>
|
||||
@ -245,7 +245,6 @@ int AbstractPort::updatePacketListSequential()
|
||||
quint64 duration = 0; // in nanosec
|
||||
quint64 totalPkts = 0;
|
||||
QList<uint> ttagMarkers;
|
||||
uint ttagRepeatInterval;
|
||||
FrameValueAttrib packetListAttrib;
|
||||
long sec = 0;
|
||||
long nsec = 0;
|
||||
@ -342,8 +341,7 @@ int AbstractPort::updatePacketListSequential()
|
||||
|
||||
if (n >= 1) {
|
||||
loopNextPacketSet(x, n, 0, loopDelay);
|
||||
qDebug("PacketSet: n = %lu, x = %lu, delay = %llu ns",
|
||||
n, x, loopDelay);
|
||||
qDebug("PacketSet: n = %lu, x = %lu", n, x);
|
||||
}
|
||||
else if (n == 0)
|
||||
x = 0;
|
||||
@ -365,8 +363,7 @@ int AbstractPort::updatePacketListSequential()
|
||||
// Create a packet set for 'y' with repeat = 1
|
||||
if (j == x) {
|
||||
loopNextPacketSet(y, 1, 0, loopDelay);
|
||||
qDebug("PacketSet: n = 1, y = %lu, delay = %llu",
|
||||
y, loopDelay);
|
||||
qDebug("PacketSet: n = 1, y = %lu", y);
|
||||
}
|
||||
|
||||
qDebug("q(%d, %d) sec = %lu nsec = %lu",
|
||||
@ -402,18 +399,6 @@ int AbstractPort::updatePacketListSequential()
|
||||
}
|
||||
}
|
||||
|
||||
// loopDelay == 0 implies 0 pps i.e. top speed
|
||||
// For ttag calc/config below we need loopDelay to be non-zero,
|
||||
// so we re-calc based on max line rate (speed). If we don't
|
||||
// have the actual port speed, we assume 1000 Mbps
|
||||
if (loopDelay == 0) {
|
||||
double maxSpeed = data_.speed() ? data_.speed(): 1000;
|
||||
double maxPktRate = (maxSpeed*1e6)
|
||||
/(8*(streamList_[i]->frameLenAvg()
|
||||
+ Packet::kEthOverhead));
|
||||
loopDelay = 1e9/maxPktRate; // in nanosec
|
||||
}
|
||||
|
||||
// Add a Ttag marker after every kTtagTimeInterval_ worth of pkts
|
||||
if (hasTtag) {
|
||||
uint ttagPktInterval = kTtagTimeInterval_*1e9/loopDelay;
|
||||
@ -441,10 +426,9 @@ int AbstractPort::updatePacketListSequential()
|
||||
returnToQIdx = 0;
|
||||
*/
|
||||
|
||||
// XXX: no list loop delay required since we don't create
|
||||
// any implicit packet sets now
|
||||
setPacketListLoopMode(true, 0, 0);
|
||||
qDebug("Seq mode list loop true with 0 delay");
|
||||
setPacketListLoopMode(true, 0,
|
||||
streamList_[i]->sendUnit() ==
|
||||
StreamBase::e_su_bursts ? ibg1 : ipg1);
|
||||
goto _stop_no_more_pkts;
|
||||
|
||||
case StreamBase::e_nw_goto_next:
|
||||
@ -459,17 +443,12 @@ int AbstractPort::updatePacketListSequential()
|
||||
} // if (stream is enabled)
|
||||
} // for (numStreams)
|
||||
|
||||
_out_of_memory:
|
||||
_stop_no_more_pkts:
|
||||
// See comments in updatePacketListInterleaved() for calc explanation
|
||||
ttagRepeatInterval = ttagMarkers.isEmpty() ? 0 :
|
||||
qMax(uint(kTtagTimeInterval_*1e9/(duration)), 1U)
|
||||
* totalPkts;
|
||||
if (!setPacketListTtagMarkers(ttagMarkers, ttagRepeatInterval)) {
|
||||
clearPacketList(); // don't leave it half baked/inconsitent
|
||||
packetListAttrib.errorFlags |= FrameValueAttrib::OutOfMemoryError;
|
||||
}
|
||||
|
||||
_out_of_memory:
|
||||
setPacketListTtagMarkers(ttagMarkers, ttagMarkers.isEmpty() ? 0 :
|
||||
qMax(uint(kTtagTimeInterval_*1e9/(duration)),
|
||||
1U) * totalPkts);
|
||||
isSendQueueDirty_ = false;
|
||||
|
||||
qDebug("PacketListAttrib = %x",
|
||||
@ -653,7 +632,6 @@ int AbstractPort::updatePacketListInterleaved()
|
||||
// i.e. send all streams "simultaneously" as fast as possible
|
||||
// as a result all streams will be at the same rate e.g. for 2 streams,
|
||||
// it would 50% each; for 3 streams - all at 33.3% and so on
|
||||
// FIXME: Should we calc minGap based on max line rate and avg pkt size?
|
||||
if (minGap == ULLONG_MAX) {
|
||||
minGap = 1;
|
||||
duration = 1;
|
||||
@ -679,11 +657,12 @@ int AbstractPort::updatePacketListInterleaved()
|
||||
// Count total packets we are going to add, so that we can create
|
||||
// an explicit packet set first
|
||||
// TODO: Find less expensive way to do this counting
|
||||
// FIXME: Turbo still thinks it has to create implicit packet set for
|
||||
// interleaved mode - Turbo code should be changed once this is validated
|
||||
quint64 totalPkts = 0;
|
||||
QVector<ulong> ttagSchedSec(numStreams, 0);
|
||||
QVector<ulong> ttagSchedNsec(numStreams, 0);
|
||||
QList<uint> ttagMarkers;
|
||||
uint ttagRepeatInterval;
|
||||
|
||||
do
|
||||
{
|
||||
@ -736,23 +715,16 @@ int AbstractPort::updatePacketListInterleaved()
|
||||
}
|
||||
} while ((sec < durSec) || ((sec == durSec) && (nsec < durNsec)));
|
||||
|
||||
qint64 delaySec = durSec - lastPktTxSec;
|
||||
qint64 delayNsec = durNsec - lastPktTxNsec;
|
||||
while (delayNsec < 0)
|
||||
{
|
||||
delayNsec += long(1e9);
|
||||
delaySec--;
|
||||
}
|
||||
|
||||
// XXX: For interleaved mode, we ALWAYS have a single packet set with
|
||||
// one repeat
|
||||
loopNextPacketSet(totalPkts, 1, delaySec, delayNsec);
|
||||
// one repeat and 0n set loop delay
|
||||
loopNextPacketSet(totalPkts, 1, 0, 0);
|
||||
qDebug("Interleaved single PacketSet of size %lld, duration %llu.%09llu "
|
||||
"repeat 1 and delay %lld.%09lld",
|
||||
totalPkts, durSec, durNsec, delaySec, delayNsec);
|
||||
"repeat 1 and delay 0",
|
||||
totalPkts, durSec, durNsec);
|
||||
|
||||
// Reset working sched/counts before building the packet list
|
||||
sec = nsec = 0;
|
||||
lastPktTxSec = lastPktTxNsec = 0;
|
||||
for (int i = 0; i < numStreams; i++)
|
||||
{
|
||||
schedSec[i] = 0;
|
||||
@ -795,6 +767,8 @@ int AbstractPort::updatePacketListInterleaved()
|
||||
packetListAttrib.errorFlags |= FrameValueAttrib::OutOfMemoryError;
|
||||
goto _out_of_memory;
|
||||
}
|
||||
lastPktTxSec = sec;
|
||||
lastPktTxNsec = nsec;
|
||||
|
||||
pktCount[i]++;
|
||||
schedNsec[i] += (pktCount.at(i) < np1.at(i)) ?
|
||||
@ -824,9 +798,17 @@ int AbstractPort::updatePacketListInterleaved()
|
||||
}
|
||||
} while ((sec < durSec) || ((sec == durSec) && (nsec < durNsec)));
|
||||
|
||||
// XXX: The single packet has the delay, so no list loop delay required
|
||||
// XXX: Both seq/interleaved mode no longer use list loop delay!
|
||||
setPacketListLoopMode(true, 0, 0);
|
||||
{
|
||||
qint64 delaySec = durSec - lastPktTxSec;
|
||||
qint64 delayNsec = durNsec - lastPktTxNsec;
|
||||
while (delayNsec < 0)
|
||||
{
|
||||
delayNsec += long(1e9);
|
||||
delaySec--;
|
||||
}
|
||||
qDebug("loop Delay = %lld.%09lld", delaySec, delayNsec);
|
||||
setPacketListLoopMode(true, delaySec, delayNsec);
|
||||
}
|
||||
|
||||
// XXX: TTag repeat interval calculation:
|
||||
// CASE 1. pktListDuration < kTtagTimeInterval:
|
||||
@ -835,13 +817,10 @@ int AbstractPort::updatePacketListInterleaved()
|
||||
// CASE 2. pktListDuration > kTtagTimeInterval:
|
||||
// e.g. if pktListDuration is 7sec and TtagTimerInterval is 5s, we
|
||||
// skip repeat markers every pktList iteration
|
||||
ttagRepeatInterval = ttagMarkers.isEmpty() ? 0 :
|
||||
qMax(uint(kTtagTimeInterval_*1e9/(durSec*1e9+durNsec)), 1U)
|
||||
* totalPkts;
|
||||
if (!setPacketListTtagMarkers(ttagMarkers, ttagRepeatInterval)) {
|
||||
clearPacketList(); // don't leave it half baked/inconsitent
|
||||
packetListAttrib.errorFlags |= FrameValueAttrib::OutOfMemoryError;
|
||||
}
|
||||
setPacketListTtagMarkers(ttagMarkers, ttagMarkers.isEmpty() ? 0 :
|
||||
qMax(uint(kTtagTimeInterval_*1e9
|
||||
/(durSec*1e9+durNsec)),
|
||||
1U) * totalPkts);
|
||||
|
||||
_out_of_memory:
|
||||
isSendQueueDirty_ = false;
|
||||
@ -885,9 +864,9 @@ void AbstractPort::stats(PortStats *stats)
|
||||
stats_.rxFrameErrors + (maxStatsValue_ - epochStats_.rxFrameErrors);
|
||||
}
|
||||
|
||||
StreamTiming::Stats AbstractPort::streamTimingStats(uint guid)
|
||||
quint64 AbstractPort::streamTimingDelay(uint guid)
|
||||
{
|
||||
return streamTiming_->stats(id(), guid);
|
||||
return streamTiming_->delay(id(), guid);
|
||||
}
|
||||
|
||||
void AbstractPort::clearStreamTiming(uint guid)
|
||||
@ -900,22 +879,16 @@ void AbstractPort::streamStats(uint guid, OstProto::StreamStatsList *stats)
|
||||
// In case stats are being maintained elsewhere
|
||||
updateStreamStats();
|
||||
|
||||
// Lock for read here as updateStreamStats() above will take write lock
|
||||
// and the lock is NOT recursive
|
||||
QReadLocker lock(&streamStatsLock_);
|
||||
|
||||
if (streamStats_.contains(guid))
|
||||
{
|
||||
StreamStatsTuple sst = streamStats_.value(guid);
|
||||
OstProto::StreamStats *s = stats->add_stream_stats();
|
||||
StreamTiming::Stats t = streamTimingStats(guid);
|
||||
|
||||
s->mutable_stream_guid()->set_id(guid);
|
||||
s->mutable_port_id()->set_id(id());
|
||||
|
||||
s->set_tx_duration(lastTransmitDuration());
|
||||
s->set_latency(t.latency);
|
||||
s->set_jitter(t.jitter);
|
||||
s->set_latency(streamTimingDelay(guid));
|
||||
|
||||
s->set_tx_pkts(sst.tx_pkts);
|
||||
s->set_tx_bytes(sst.tx_bytes);
|
||||
@ -929,10 +902,6 @@ void AbstractPort::streamStatsAll(OstProto::StreamStatsList *stats)
|
||||
// In case stats are being maintained elsewhere
|
||||
updateStreamStats();
|
||||
|
||||
// Lock for read here as updateStreamStats() above will take write lock
|
||||
// and the lock is NOT recursive
|
||||
QReadLocker lock(&streamStatsLock_);
|
||||
|
||||
// FIXME: change input param to a non-OstProto type and/or have
|
||||
// a getFirst/Next like API?
|
||||
double txDur = lastTransmitDuration();
|
||||
@ -942,14 +911,12 @@ void AbstractPort::streamStatsAll(OstProto::StreamStatsList *stats)
|
||||
i.next();
|
||||
StreamStatsTuple sst = i.value();
|
||||
OstProto::StreamStats *s = stats->add_stream_stats();
|
||||
StreamTiming::Stats t = streamTimingStats(i.key());
|
||||
|
||||
s->mutable_stream_guid()->set_id(i.key());
|
||||
s->mutable_port_id()->set_id(id());
|
||||
|
||||
s->set_tx_duration(txDur);
|
||||
s->set_latency(t.latency);
|
||||
s->set_jitter(t.jitter);
|
||||
s->set_latency(streamTimingDelay(i.key()));
|
||||
|
||||
s->set_tx_pkts(sst.tx_pkts);
|
||||
s->set_tx_bytes(sst.tx_bytes);
|
||||
@ -960,14 +927,12 @@ void AbstractPort::streamStatsAll(OstProto::StreamStatsList *stats)
|
||||
|
||||
void AbstractPort::resetStreamStats(uint guid)
|
||||
{
|
||||
QWriteLocker lock(&streamStatsLock_);
|
||||
streamStats_.remove(guid);
|
||||
clearStreamTiming(guid);
|
||||
}
|
||||
|
||||
void AbstractPort::resetStreamStatsAll()
|
||||
{
|
||||
QWriteLocker lock(&streamStatsLock_);
|
||||
streamStats_.clear();
|
||||
clearStreamTiming();
|
||||
}
|
||||
|
@ -22,10 +22,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "../common/protocol.pb.h"
|
||||
#include "streamstats.h"
|
||||
#include "streamtiming.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QReadWriteLock>
|
||||
#include <QtGlobal>
|
||||
|
||||
#include <limits.h>
|
||||
@ -35,6 +33,7 @@ struct InterfaceInfo;
|
||||
class PacketBuffer;
|
||||
class QIODevice;
|
||||
class StreamBase;
|
||||
class StreamTiming;
|
||||
|
||||
// TODO: send notification back to client(s)
|
||||
#define Xnotify qWarning
|
||||
@ -108,7 +107,7 @@ public:
|
||||
int length) = 0;
|
||||
virtual void setPacketListLoopMode(bool loop,
|
||||
quint64 secDelay, quint64 nsecDelay) = 0;
|
||||
virtual bool setPacketListTtagMarkers(QList<uint> markers,
|
||||
virtual void setPacketListTtagMarkers(QList<uint> markers,
|
||||
uint repeatInterval) = 0;
|
||||
int updatePacketList();
|
||||
|
||||
@ -125,7 +124,7 @@ public:
|
||||
void stats(PortStats *stats);
|
||||
void resetStats() { epochStats_ = stats_; }
|
||||
|
||||
StreamTiming::Stats streamTimingStats(uint guid);
|
||||
quint64 streamTimingDelay(uint guid);
|
||||
void clearStreamTiming(uint guid = UINT_MAX);
|
||||
|
||||
// FIXME: combine single and All calls?
|
||||
@ -164,7 +163,6 @@ protected:
|
||||
quint64 maxStatsValue_;
|
||||
struct PortStats stats_;
|
||||
StreamStats streamStats_;
|
||||
QReadWriteLock streamStatsLock_;
|
||||
//! \todo Need lock for stats access/update
|
||||
|
||||
const uint kTtagTimeInterval_{5}; // in seconds
|
||||
|
@ -7,8 +7,6 @@ linux*:system(grep -q IFLA_STATS64 /usr/include/linux/if_link.h): \
|
||||
DEFINES += HAVE_IFLA_STATS64
|
||||
INCLUDEPATH += "../common"
|
||||
INCLUDEPATH += "../rpc"
|
||||
|
||||
OBJDIR = .
|
||||
win32 {
|
||||
# Support Windows Vista and above only
|
||||
DEFINES += WIN32_LEAN_AND_MEAN NTDDI_VERSION=0x06000000 _WIN32_WINNT=0x0600
|
||||
@ -17,19 +15,24 @@ win32 {
|
||||
QMAKE_LFLAGS += -static
|
||||
LIBS += -lwpcap -lpacket -liphlpapi
|
||||
CONFIG(debug, debug|release) {
|
||||
OBJDIR = debug
|
||||
LIBS += -L"../common/debug" -lostproto
|
||||
LIBS += -L"../rpc/debug" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/debug/libostproto.a" \
|
||||
"../rpc/debug/libpbrpc.a"
|
||||
} else {
|
||||
OBJDIR = release
|
||||
LIBS += -L"../common/release" -lostproto
|
||||
LIBS += -L"../rpc/release" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/release/libostproto.a" \
|
||||
"../rpc/release/libpbrpc.a"
|
||||
}
|
||||
} else {
|
||||
LIBS += -lpcap
|
||||
LIBS += -L"../common" -lostproto
|
||||
LIBS += -L"../rpc" -lpbrpc
|
||||
POST_TARGETDEPS += "../common/libostproto.a" "../rpc/libpbrpc.a"
|
||||
}
|
||||
LIBS += -L"../common/$$OBJDIR" -lostproto
|
||||
LIBS += -L"../rpc/$$OBJDIR" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/$$OBJDIR//libostproto.a" \
|
||||
"../rpc/$$OBJDIR/libpbrpc.a"
|
||||
|
||||
linux {
|
||||
INCLUDEPATH += "/usr/include/libnl3"
|
||||
LIBS += -lnl-3 -lnl-route-3
|
||||
|
@ -31,11 +31,11 @@ PcapPort::PcapPort(int id, const char *device)
|
||||
{
|
||||
monitorRx_ = new PortMonitor(device, kDirectionRx, &stats_);
|
||||
monitorTx_ = new PortMonitor(device, kDirectionTx, &stats_);
|
||||
transmitter_ = new PcapTransmitter(device);
|
||||
transmitter_ = new PcapTransmitter(device, streamStats_);
|
||||
capturer_ = new PortCapturer(device);
|
||||
emulXcvr_ = new EmulationTransceiver(device, deviceManager_);
|
||||
txTtagStatsPoller_ = new PcapTxTtagStats(device, id);
|
||||
rxStatsPoller_ = new PcapRxStats(device, id);
|
||||
rxStatsPoller_ = new PcapRxStats(device, streamStats_, id);
|
||||
|
||||
if (!monitorRx_->handle() || !monitorTx_->handle())
|
||||
isUsable_ = false;
|
||||
@ -148,14 +148,9 @@ bool PcapPort::setRateAccuracy(AbstractPort::Accuracy accuracy)
|
||||
|
||||
void PcapPort::updateStreamStats()
|
||||
{
|
||||
QWriteLocker lock(&streamStatsLock_);
|
||||
// XXX: PcapTxThread already does this at the end of transmit; we
|
||||
// just dump tx/rx stats poller debug stats here
|
||||
|
||||
// XXX: Transmitter may also 'adjust' rx stats in some cases (pcap
|
||||
// direction not supported platforms)
|
||||
transmitter_->updateTxRxStreamStats(streamStats_);
|
||||
rxStatsPoller_->updateRxStreamStats(streamStats_);
|
||||
|
||||
// Dump tx/rx stats poller debug stats
|
||||
qDebug("port %d txTtagStatsPoller: %s",
|
||||
id(), qUtf8Printable(txTtagStatsPoller_->debugStats()));
|
||||
qDebug("port %d rxStatsPoller: %s",
|
||||
@ -401,8 +396,6 @@ PcapPort::PortCapturer::PortCapturer(const char *device)
|
||||
|
||||
PcapPort::PortCapturer::~PortCapturer()
|
||||
{
|
||||
if (isRunning())
|
||||
stop();
|
||||
capFile_.close();
|
||||
}
|
||||
|
||||
@ -551,8 +544,7 @@ PcapPort::EmulationTransceiver::EmulationTransceiver(const char *device,
|
||||
|
||||
PcapPort::EmulationTransceiver::~EmulationTransceiver()
|
||||
{
|
||||
if (isRunning())
|
||||
stop();
|
||||
stop();
|
||||
}
|
||||
|
||||
void PcapPort::EmulationTransceiver::run()
|
||||
@ -741,7 +733,7 @@ void PcapPort::EmulationTransceiver::stop()
|
||||
QThread::msleep(10);
|
||||
}
|
||||
else {
|
||||
qWarning("Emulation Xcvr stop requested but is not running!");
|
||||
qWarning("Receive stop requested but is not running!");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -63,10 +63,10 @@ public:
|
||||
{
|
||||
transmitter_->setPacketListLoopMode(loop, secDelay, nsecDelay);
|
||||
}
|
||||
virtual bool setPacketListTtagMarkers(QList<uint> markers,
|
||||
virtual void setPacketListTtagMarkers(QList<uint> markers,
|
||||
uint repeatInterval)
|
||||
{
|
||||
return transmitter_->setPacketListTtagMarkers(markers, repeatInterval);
|
||||
transmitter_->setPacketListTtagMarkers(markers, repeatInterval);
|
||||
}
|
||||
|
||||
virtual void startTransmit() {
|
||||
@ -173,8 +173,6 @@ protected:
|
||||
PortMonitor *monitorRx_;
|
||||
PortMonitor *monitorTx_;
|
||||
|
||||
PcapRxStats *rxStatsPoller_;
|
||||
|
||||
void updateNotes();
|
||||
|
||||
private:
|
||||
@ -185,6 +183,7 @@ private:
|
||||
PortCapturer *capturer_;
|
||||
EmulationTransceiver *emulXcvr_;
|
||||
PcapTxTtagStats *txTtagStatsPoller_;
|
||||
PcapRxStats *rxStatsPoller_;
|
||||
|
||||
static pcap_if_t *deviceList_;
|
||||
};
|
||||
|
@ -22,12 +22,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "pcapextra.h"
|
||||
#include "../common/debugdefs.h"
|
||||
#include "../common/sign.h"
|
||||
#include "settings.h"
|
||||
#include "streamtiming.h"
|
||||
|
||||
#define Xnotify qWarning // FIXME
|
||||
|
||||
PcapRxStats::PcapRxStats(const char *device, int id)
|
||||
PcapRxStats::PcapRxStats(const char *device, StreamStats &portStreamStats, int id)
|
||||
: streamStats_(portStreamStats)
|
||||
{
|
||||
setObjectName(QString("Rx$:%1").arg(device));
|
||||
device_ = QString::fromLatin1(device);
|
||||
@ -57,20 +57,6 @@ void PcapRxStats::run()
|
||||
SignProtocol::magic(), 0, BASE_HEX);
|
||||
// XXX: Exclude ICMP packets which contain an embedded signed packet
|
||||
// For now we check upto 4 vlan tags
|
||||
// XXX: libpcap for Linux has a special bpf vlan check which generates
|
||||
// incorrect BPF instructions for our capture filter expression,
|
||||
// so we modify it to work correctly
|
||||
// See https://srivatsp.com/ostinato/ostinato-rx-stream-stats-zero/
|
||||
#ifdef Q_OS_LINUX
|
||||
capture_filter.prepend(
|
||||
"not ("
|
||||
"icmp or "
|
||||
"(vlan and icmp) or "
|
||||
"(vlan and icmp) or "
|
||||
"(vlan and icmp) or "
|
||||
"(vlan and icmp) "
|
||||
") and ");
|
||||
#else
|
||||
capture_filter.append(
|
||||
"and not ("
|
||||
"icmp or "
|
||||
@ -79,15 +65,8 @@ void PcapRxStats::run()
|
||||
"(vlan and icmp) or "
|
||||
"(vlan and icmp) "
|
||||
")");
|
||||
#endif
|
||||
|
||||
// Override filter expression if one is specified in .ini
|
||||
if (appSettings->contains(kInternalRxStatsFilterKey))
|
||||
capture_filter = appSettings->value(kInternalRxStatsFilterKey)
|
||||
.toString();
|
||||
|
||||
qDebug("In %s", __PRETTY_FUNCTION__);
|
||||
qDebug("RxStats Filter: %s", qPrintable(capture_filter));
|
||||
|
||||
handle_ = pcap_open_live(qPrintable(device_), 65535,
|
||||
flags, 100 /* ms */, errbuf);
|
||||
@ -155,10 +134,14 @@ _skip_filter:
|
||||
&& (ttagId >> 8 != uint(portId_))) {
|
||||
ttagId &= 0xFF;
|
||||
timing_->recordRxTime(portId_, guid, ttagId, hdr->ts);
|
||||
timingDebug("[%d RX] %ld:%ld ttag %u guid %u", portId_,
|
||||
hdr->ts.tv_sec, long(hdr->ts.tv_usec), ttagId, guid);
|
||||
}
|
||||
#else
|
||||
if (SignProtocol::packetTtagId(data, hdr->caplen, &ttagId, &guid)) {
|
||||
timing_->recordRxTime(portId_, guid, ttagId, hdr->ts);
|
||||
timingDebug("[%d RX] %ld:%ld ttag %u guid %u", portId_,
|
||||
hdr->ts.tv_sec, long(hdr->ts.tv_usec), ttagId, guid);
|
||||
}
|
||||
#endif
|
||||
if (guid != SignProtocol::kInvalidGuid) {
|
||||
@ -236,21 +219,3 @@ bool PcapRxStats::isDirectional()
|
||||
{
|
||||
return isDirectional_;
|
||||
}
|
||||
|
||||
// XXX: Stats are reset on read
|
||||
void PcapRxStats::updateRxStreamStats(StreamStats &streamStats)
|
||||
{
|
||||
QMutexLocker lock(&streamStatsLock_);
|
||||
StreamStatsIterator i(streamStats_);
|
||||
|
||||
while (i.hasNext())
|
||||
{
|
||||
i.next();
|
||||
uint guid = i.key();
|
||||
StreamStatsTuple sst = i.value();
|
||||
|
||||
streamStats[guid].rx_pkts += sst.rx_pkts;
|
||||
streamStats[guid].rx_bytes += sst.rx_bytes;
|
||||
}
|
||||
streamStats_.clear();
|
||||
}
|
||||
|
@ -24,14 +24,12 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "pcapsession.h"
|
||||
|
||||
#include <QMutex>
|
||||
|
||||
class StreamTiming;
|
||||
|
||||
class PcapRxStats: public PcapSession
|
||||
{
|
||||
public:
|
||||
PcapRxStats(const char *device, int id);
|
||||
PcapRxStats(const char *device, StreamStats &portStreamStats, int id);
|
||||
pcap_t* handle();
|
||||
void run();
|
||||
bool start();
|
||||
@ -39,7 +37,6 @@ public:
|
||||
bool isRunning();
|
||||
bool isDirectional();
|
||||
|
||||
void updateRxStreamStats(StreamStats &streamStats); // Reset on read
|
||||
private:
|
||||
enum State {
|
||||
kNotStarted,
|
||||
@ -48,8 +45,7 @@ private:
|
||||
};
|
||||
|
||||
QString device_;
|
||||
StreamStats streamStats_;
|
||||
QMutex streamStatsLock_;
|
||||
StreamStats &streamStats_;
|
||||
volatile bool stop_;
|
||||
volatile State state_;
|
||||
bool isDirectional_;
|
||||
|
@ -20,13 +20,15 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "pcaptransmitter.h"
|
||||
|
||||
PcapTransmitter::PcapTransmitter(
|
||||
const char *device)
|
||||
: txThread_(device)
|
||||
const char *device,
|
||||
StreamStats &portStreamStats)
|
||||
: streamStats_(portStreamStats), txThread_(device)
|
||||
{
|
||||
adjustRxStreamStats_ = false;
|
||||
txStats_.setObjectName(QString("TxStats:%1").arg(device));
|
||||
memset(&stats_, 0, sizeof(stats_));
|
||||
txStats_.setTxThreadStats(&stats_);
|
||||
txStats_.start(); // TODO: alongwith user transmit start
|
||||
|
||||
txThread_.setStats(&stats_);
|
||||
connect(&txThread_, SIGNAL(finished()), SLOT(updateTxThreadStreamStats()));
|
||||
@ -34,10 +36,7 @@ PcapTransmitter::PcapTransmitter(
|
||||
|
||||
PcapTransmitter::~PcapTransmitter()
|
||||
{
|
||||
if (txThread_.isRunning())
|
||||
txThread_.stop();
|
||||
if (txStats_.isRunning())
|
||||
txStats_.stop();
|
||||
txStats_.stop(); // TODO: alongwith user transmit stop
|
||||
}
|
||||
|
||||
bool PcapTransmitter::setRateAccuracy(
|
||||
@ -56,31 +55,6 @@ bool PcapTransmitter::setStreamStatsTracking(bool enable)
|
||||
return txThread_.setStreamStatsTracking(enable);
|
||||
}
|
||||
|
||||
// XXX: Stats are reset on read
|
||||
void PcapTransmitter::updateTxRxStreamStats(StreamStats &streamStats)
|
||||
{
|
||||
QMutexLocker lock(&streamStatsLock_);
|
||||
StreamStatsIterator i(streamStats_);
|
||||
|
||||
while (i.hasNext())
|
||||
{
|
||||
i.next();
|
||||
uint guid = i.key();
|
||||
StreamStatsTuple sst = i.value();
|
||||
|
||||
streamStats[guid].tx_pkts += sst.tx_pkts;
|
||||
streamStats[guid].tx_bytes += sst.tx_bytes;
|
||||
if (adjustRxStreamStats_) {
|
||||
// XXX: rx_pkts counting may lag behind tx_pkts, so stream stats
|
||||
// may become negative after adjustment transiently. But this
|
||||
// should fix itself once all the rx pkts come in
|
||||
streamStats[guid].rx_pkts -= sst.tx_pkts;
|
||||
streamStats[guid].rx_bytes -= sst.tx_bytes;
|
||||
}
|
||||
}
|
||||
streamStats_.clear();
|
||||
}
|
||||
|
||||
void PcapTransmitter::clearPacketList()
|
||||
{
|
||||
txThread_.clearPacketList();
|
||||
@ -114,11 +88,11 @@ void PcapTransmitter::setPacketListLoopMode(
|
||||
txThread_.setPacketListLoopMode(loop, secDelay, nsecDelay);
|
||||
}
|
||||
|
||||
bool PcapTransmitter::setPacketListTtagMarkers(
|
||||
void PcapTransmitter::setPacketListTtagMarkers(
|
||||
QList<uint> markers,
|
||||
uint repeatInterval)
|
||||
{
|
||||
return txThread_.setPacketListTtagMarkers(markers, repeatInterval);
|
||||
txThread_.setPacketListTtagMarkers(markers, repeatInterval);
|
||||
}
|
||||
|
||||
void PcapTransmitter::useExternalStats(AbstractPort::PortStats *stats)
|
||||
@ -128,27 +102,18 @@ void PcapTransmitter::useExternalStats(AbstractPort::PortStats *stats)
|
||||
|
||||
void PcapTransmitter::start()
|
||||
{
|
||||
// XXX: Start the stats thread before the tx thread, so no tx stats
|
||||
// is missed
|
||||
txStats_.start();
|
||||
Q_ASSERT(txStats_.isRunning());
|
||||
txThread_.start();
|
||||
}
|
||||
|
||||
void PcapTransmitter::stop()
|
||||
{
|
||||
// XXX: Stop the tx thread before the stats thread, so no tx stats
|
||||
// is missed
|
||||
txThread_.stop();
|
||||
Q_ASSERT(!txThread_.isRunning());
|
||||
txStats_.stop();
|
||||
}
|
||||
|
||||
bool PcapTransmitter::isRunning()
|
||||
{
|
||||
return txThread_.isRunning();
|
||||
}
|
||||
|
||||
double PcapTransmitter::lastTxDuration()
|
||||
{
|
||||
return txThread_.lastTxDuration();
|
||||
@ -156,9 +121,8 @@ double PcapTransmitter::lastTxDuration()
|
||||
|
||||
void PcapTransmitter::updateTxThreadStreamStats()
|
||||
{
|
||||
QMutexLocker lock(&streamStatsLock_);
|
||||
PcapTxThread *txThread = dynamic_cast<PcapTxThread*>(sender());
|
||||
StreamStats threadStreamStats = txThread->streamStats();
|
||||
const StreamStats& threadStreamStats = txThread->streamStats();
|
||||
StreamStatsIterator i(threadStreamStats);
|
||||
|
||||
while (i.hasNext())
|
||||
@ -169,5 +133,13 @@ void PcapTransmitter::updateTxThreadStreamStats()
|
||||
|
||||
streamStats_[guid].tx_pkts += sst.tx_pkts;
|
||||
streamStats_[guid].tx_bytes += sst.tx_bytes;
|
||||
if (adjustRxStreamStats_) {
|
||||
// XXX: rx_pkts counting may lag behind tx_pkts, so stream stats
|
||||
// may become negative after adjustment transiently. But this
|
||||
// should fix itself once all the rx pkts come in
|
||||
streamStats_[guid].rx_pkts -= sst.tx_pkts;
|
||||
streamStats_[guid].rx_bytes -= sst.tx_bytes;
|
||||
}
|
||||
}
|
||||
txThread->clearStreamStats();
|
||||
}
|
||||
|
@ -29,13 +29,12 @@ class PcapTransmitter : QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PcapTransmitter(const char *device);
|
||||
PcapTransmitter(const char *device, StreamStats &portStreamStats);
|
||||
~PcapTransmitter();
|
||||
|
||||
bool setRateAccuracy(AbstractPort::Accuracy accuracy);
|
||||
bool setStreamStatsTracking(bool enable);
|
||||
void adjustRxStreamStats(bool enable);
|
||||
void updateTxRxStreamStats(StreamStats &streamStats); // Reset on read
|
||||
|
||||
void clearPacketList();
|
||||
void loopNextPacketSet(qint64 size, qint64 repeats,
|
||||
@ -43,7 +42,7 @@ public:
|
||||
bool appendToPacketList(long sec, long usec, const uchar *packet,
|
||||
int length);
|
||||
void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay);
|
||||
bool setPacketListTtagMarkers(QList<uint> markers, uint repeatInterval);
|
||||
void setPacketListTtagMarkers(QList<uint> markers, uint repeatInterval);
|
||||
|
||||
void setHandle(pcap_t *handle);
|
||||
void useExternalStats(AbstractPort::PortStats *stats);
|
||||
@ -55,8 +54,7 @@ public:
|
||||
private slots:
|
||||
void updateTxThreadStreamStats();
|
||||
private:
|
||||
StreamStats streamStats_;
|
||||
QMutex streamStatsLock_;
|
||||
StreamStats &streamStats_;
|
||||
PcapTxThread txThread_;
|
||||
PcapTxStats txStats_;
|
||||
StatsTuple stats_;
|
||||
|
@ -112,6 +112,25 @@ void PcapTxThread::clearPacketList()
|
||||
void PcapTxThread::loopNextPacketSet(qint64 size, qint64 repeats,
|
||||
long repeatDelaySec, long repeatDelayNsec)
|
||||
{
|
||||
#if 0 // Don't let implicit packet sets be created
|
||||
// XXX: The below change was done as part of Turbo code
|
||||
// implementation alongwith calls to this function from
|
||||
// AbstractPort::updatePacketListSequential(). Turbo to
|
||||
// have clean code requires explicit packet sets for all
|
||||
// cases (except interleaved streams). The below change
|
||||
// was done so that the base code should not be affected
|
||||
// after the explict packet set creation calls.
|
||||
// XXX: Since we create implicit packetset for this case, skip
|
||||
// This case =>
|
||||
// 1. Packet set for y when x = 0
|
||||
// 2. n==1 in n*x+y
|
||||
// These two cases were the result of the changes in
|
||||
// updatePacketListSequential() as part of Turbo changes
|
||||
// mentioned above
|
||||
if (repeats == 1)
|
||||
return;
|
||||
#endif
|
||||
|
||||
currentPacketSequence_ = new PacketSequence(trackStreamStats_);
|
||||
currentPacketSequence_->repeatCount_ = repeats;
|
||||
currentPacketSequence_->usecDelay_ = repeatDelaySec * long(1e6)
|
||||
@ -202,7 +221,7 @@ void PcapTxThread::setPacketListLoopMode(
|
||||
loopDelay_ = secDelay*long(1e6) + nsecDelay/1000;
|
||||
}
|
||||
|
||||
bool PcapTxThread::setPacketListTtagMarkers(
|
||||
void PcapTxThread::setPacketListTtagMarkers(
|
||||
QList<uint> markers,
|
||||
uint repeatInterval)
|
||||
{
|
||||
@ -220,7 +239,7 @@ bool PcapTxThread::setPacketListTtagMarkers(
|
||||
qDebug() << "FirstTtagPkt:" << firstTtagPkt_;
|
||||
qDebug() << "TtagMarkers:" << ttagDeltaMarkers_;
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
void PcapTxThread::setHandle(pcap_t *handle)
|
||||
@ -236,20 +255,14 @@ void PcapTxThread::setStats(StatsTuple *stats)
|
||||
stats_ = stats;
|
||||
}
|
||||
|
||||
StreamStats PcapTxThread::streamStats()
|
||||
const StreamStats& PcapTxThread::streamStats()
|
||||
{
|
||||
// This function is typically called in client-specific-RPC-thread
|
||||
// context; hence different client RPC threads may call this function,
|
||||
// so use a lock. Although RPCs are protected by the portLock just
|
||||
// for this purpose, the streamStats RPC takes a Read lock, so it can
|
||||
// still happen that multiple RPC threads land up here - that's why
|
||||
// this lock is required
|
||||
QMutexLocker lock(&streamStatsLock_);
|
||||
return streamStats_;
|
||||
}
|
||||
|
||||
StreamStats ss(streamStats_); // Make a copy
|
||||
streamStats_.clear(); // Reset on read semantics
|
||||
|
||||
return ss; // Return copy
|
||||
void PcapTxThread::clearStreamStats()
|
||||
{
|
||||
streamStats_.clear();
|
||||
}
|
||||
|
||||
void PcapTxThread::run()
|
||||
@ -290,8 +303,6 @@ void PcapTxThread::run()
|
||||
packetSequenceList_.at(i)->ttagL4CksumOffset_);
|
||||
}
|
||||
|
||||
qDebug() << "Loop:" << (returnToQIdx_ >= 0)
|
||||
<< "LoopDelay:" << loopDelay_;
|
||||
qDebug() << "First Ttag: " << firstTtagPkt_
|
||||
<< "Ttag Markers:" << ttagDeltaMarkers_;
|
||||
|
||||
@ -539,8 +550,6 @@ int PcapTxThread::sendQueueTransmit(pcap_t *p, PacketSequence *seq,
|
||||
|
||||
void PcapTxThread::updateTxStreamStats()
|
||||
{
|
||||
QMutexLocker lock(&streamStatsLock_);
|
||||
|
||||
// If no packets in list, nothing to be done
|
||||
if (!packetListSize_)
|
||||
return;
|
||||
|
@ -24,7 +24,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#include "packetsequence.h"
|
||||
#include "statstuple.h"
|
||||
|
||||
#include <QMutex>
|
||||
#include <QThread>
|
||||
#include <pcap.h>
|
||||
|
||||
@ -43,13 +42,14 @@ public:
|
||||
bool appendToPacketList(long sec, long usec, const uchar *packet,
|
||||
int length);
|
||||
void setPacketListLoopMode(bool loop, quint64 secDelay, quint64 nsecDelay);
|
||||
bool setPacketListTtagMarkers(QList<uint> markers, uint repeatInterval);
|
||||
void setPacketListTtagMarkers(QList<uint> markers, uint repeatInterval);
|
||||
|
||||
void setHandle(pcap_t *handle);
|
||||
|
||||
void setStats(StatsTuple *stats);
|
||||
|
||||
StreamStats streamStats(); // reset on read
|
||||
const StreamStats& streamStats();
|
||||
void clearStreamStats();
|
||||
|
||||
void run();
|
||||
|
||||
@ -94,7 +94,6 @@ private:
|
||||
StatsTuple *stats_;
|
||||
StatsTuple lastStats_;
|
||||
StreamStats streamStats_;
|
||||
QMutex streamStatsLock_;
|
||||
quint8 ttagId_{0};
|
||||
|
||||
double lastTxDuration_{0.0}; // in secs
|
||||
|
@ -115,6 +115,8 @@ _skip_filter:
|
||||
ttagId &= 0xFF;
|
||||
#endif
|
||||
timing_->recordTxTime(portId_, guid, ttagId, hdr->ts);
|
||||
timingDebug("[%d TX] %ld:%ld ttag %u guid %u", portId_,
|
||||
hdr->ts.tv_sec, long(hdr->ts.tv_usec), ttagId, guid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -42,8 +42,4 @@ const QString kRpcServerAddress("RpcServer/Address");
|
||||
const QString kPortListIncludeKey("PortList/Include");
|
||||
const QString kPortListExcludeKey("PortList/Exclude");
|
||||
|
||||
//
|
||||
// Internal Section Keys
|
||||
//
|
||||
const QString kInternalRxStatsFilterKey("Internal/RxStatsFilter");
|
||||
#endif
|
||||
|
@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
|
||||
#include "streamtiming.h"
|
||||
|
||||
#include "../common/debugdefs.h"
|
||||
#include "timestamp.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
@ -62,10 +63,52 @@ void StreamTiming::stop(uint portId)
|
||||
}
|
||||
}
|
||||
|
||||
StreamTiming::Stats StreamTiming::stats(uint portId, uint guid)
|
||||
bool StreamTiming::recordTxTime(uint portId, uint guid, uint ttagId,
|
||||
const struct timespec ×tamp)
|
||||
{
|
||||
Stats stats = {0, 0};
|
||||
TxRxKey key = makeKey(guid, ttagId);
|
||||
TtagData value = { .timeStamp = timestamp, .portId = portId};
|
||||
|
||||
QMutexLocker locker(&txHashLock_);
|
||||
txHash_.insert(key, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamTiming::recordRxTime(uint portId, uint guid, uint ttagId,
|
||||
const struct timespec ×tamp)
|
||||
{
|
||||
TxRxKey key = makeKey(guid, ttagId);
|
||||
TtagData value = { .timeStamp = timestamp, .portId = portId};
|
||||
|
||||
QMutexLocker locker(&rxHashLock_);
|
||||
rxHash_.insert(key, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamTiming::recordTxTime(uint portId, uint guid, uint ttagId,
|
||||
const struct timeval ×tamp)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = timestamp.tv_sec;
|
||||
ts.tv_nsec = timestamp.tv_usec*1000;
|
||||
|
||||
return recordTxTime(portId, guid, ttagId, ts);
|
||||
}
|
||||
|
||||
bool StreamTiming::recordRxTime(uint portId, uint guid, uint ttagId,
|
||||
const struct timeval ×tamp)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = timestamp.tv_sec;
|
||||
ts.tv_nsec = timestamp.tv_usec*1000;
|
||||
|
||||
return recordRxTime(portId, guid, ttagId, ts);
|
||||
}
|
||||
|
||||
quint64 StreamTiming::delay(uint portId, uint guid)
|
||||
{
|
||||
Q_ASSERT(guid <= SignProtocol::kMaxGuid);
|
||||
|
||||
// Process anything pending first
|
||||
@ -74,17 +117,13 @@ StreamTiming::Stats StreamTiming::stats(uint portId, uint guid)
|
||||
QMutexLocker locker(&timingLock_);
|
||||
|
||||
if (!timing_.contains(portId))
|
||||
return stats;
|
||||
return 0;
|
||||
|
||||
Timing t = timing_.value(portId)->value(guid);
|
||||
if (t.countDelays == 0)
|
||||
return stats;
|
||||
return 0;
|
||||
|
||||
stats.latency = timespecToNsecs(t.sumDelays)/t.countDelays;
|
||||
if (t.countDelays > 1)
|
||||
stats.jitter = t.sumJitter/(t.countDelays-1);
|
||||
|
||||
return stats;
|
||||
return timespecToNsecs(t.sumDelays)/t.countDelays;
|
||||
}
|
||||
|
||||
void StreamTiming::clear(uint portId, uint guid)
|
||||
@ -101,7 +140,7 @@ void StreamTiming::clear(uint portId, uint guid)
|
||||
if (!portTiming)
|
||||
return;
|
||||
|
||||
if (guid >= SignProtocol::kInvalidGuid)
|
||||
if (guid == SignProtocol::kInvalidGuid)
|
||||
portTiming->clear(); // remove ALL guids
|
||||
else
|
||||
portTiming->remove(guid);
|
||||
@ -125,7 +164,7 @@ int StreamTiming::processRecords()
|
||||
struct timespec diff;
|
||||
timespecsub(&rxTime, &txTime, &diff);
|
||||
|
||||
uint guid = guidFromKey(i.key());
|
||||
uint guid = i.key() >> 8;
|
||||
uint portId = i.value().portId;
|
||||
|
||||
if (!timing_.contains(portId))
|
||||
@ -133,25 +172,19 @@ int StreamTiming::processRecords()
|
||||
PortTiming *portTiming = timing_.value(portId);
|
||||
Timing &guidTiming = (*portTiming)[guid];
|
||||
timespecadd(&guidTiming.sumDelays, &diff, &guidTiming.sumDelays);
|
||||
if (guidTiming.countDelays)
|
||||
guidTiming.sumJitter += abs(
|
||||
diff.tv_sec*long(1e9) + diff.tv_nsec
|
||||
- guidTiming.lastDelay.tv_sec*long(1e9)
|
||||
- guidTiming.lastDelay.tv_nsec);
|
||||
guidTiming.lastDelay = diff;
|
||||
guidTiming.countDelays++;
|
||||
|
||||
count++;
|
||||
|
||||
timingDebug("[%u/%u/%u] diff %ld.%09ld (%ld.%09ld - %ld.%09ld)",
|
||||
i.value().portId, guid, ttagIdFromKey(i.key()),
|
||||
i.value().portId, guid, i.key() & 0xFF,
|
||||
diff.tv_sec, diff.tv_nsec,
|
||||
rxTime.tv_sec, rxTime.tv_nsec,
|
||||
txTime.tv_sec, txTime.tv_nsec);
|
||||
timingDebug("[%u/%u](%d) total %ld.%09ld count %u jittersum %09llu",
|
||||
i.value().portId, guid, count,
|
||||
timingDebug("%d:[%u/%u] total %ld.%09ld count %u",
|
||||
count, i.value().portId, guid,
|
||||
guidTiming.sumDelays.tv_sec, guidTiming.sumDelays.tv_nsec,
|
||||
guidTiming.countDelays, guidTiming.sumJitter);
|
||||
guidTiming.countDelays);
|
||||
}
|
||||
i = rxHash_.erase(i);
|
||||
}
|
||||
|
@ -20,7 +20,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
#ifndef _STREAM_TIMING
|
||||
#define _STREAM_TIMING
|
||||
|
||||
#include "../common/debugdefs.h"
|
||||
#include "../common/sign.h"
|
||||
|
||||
#include <QHash>
|
||||
@ -34,12 +33,6 @@ class StreamTiming : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
struct Stats
|
||||
{
|
||||
quint64 latency;
|
||||
quint64 jitter;
|
||||
};
|
||||
|
||||
bool recordTxTime(uint portId, uint guid, uint ttagId,
|
||||
const struct timespec ×tamp);
|
||||
bool recordRxTime(uint portId, uint guid, uint ttagId,
|
||||
@ -50,10 +43,7 @@ public:
|
||||
bool recordRxTime(uint portId, uint guid, uint ttagId,
|
||||
const struct timeval ×tamp);
|
||||
|
||||
bool recordTxTime(uint portId, uint *ttagList, int count,
|
||||
const struct timespec ×tamp);
|
||||
|
||||
Stats stats(uint portId, uint guid);
|
||||
quint64 delay(uint portId, uint guid);
|
||||
void clear(uint portId, uint guid = SignProtocol::kInvalidGuid);
|
||||
|
||||
static StreamTiming* instance();
|
||||
@ -68,6 +58,10 @@ private:
|
||||
int processRecords();
|
||||
int deleteStaleRecords();
|
||||
|
||||
quint32 makeKey(uint guid, uint ttagId) {
|
||||
return guid << 8 | (ttagId & 0xFF);
|
||||
}
|
||||
|
||||
// XXX: use only time intervals, not absolute time
|
||||
quint64 timespecToNsecs(const struct timespec &interval) {
|
||||
return interval.tv_nsec + interval.tv_sec*1e9;
|
||||
@ -78,38 +72,23 @@ private:
|
||||
uint portId;
|
||||
};
|
||||
|
||||
// XXX: used only as a Qt Container value, so members will get init to 0
|
||||
// when this struct is retrieved from the container due to Qt's default-
|
||||
// cosntructed value semantics
|
||||
struct Timing {
|
||||
struct timespec sumDelays; // nanosec resolution
|
||||
struct timespec lastDelay;
|
||||
quint64 sumJitter; // nanosec resolution
|
||||
uint countDelays;
|
||||
};
|
||||
|
||||
QSet<uint> activePortSet_;
|
||||
|
||||
// XXX: TxRxKey = ttagid (8 bit MSB) + guid (24 bit LSB)
|
||||
// TODO: encode tx port in packet and use as part of key
|
||||
// XXX: TxRxKey = guid (24 bit MSB) + ttagid (8 bit LSB)
|
||||
// TODO: encode tx port in in packet and use as part of key
|
||||
typedef quint32 TxRxKey;
|
||||
TxRxKey makeKey(uint guid, uint ttagId) {
|
||||
return (ttagId << 24 ) | (guid & 0x00FFFFFF);
|
||||
}
|
||||
uint guidFromKey(TxRxKey key) {
|
||||
return uint(key) & 0x00FFFFFF;
|
||||
}
|
||||
uint ttagIdFromKey(TxRxKey key) {
|
||||
return uint(key) >> 24;
|
||||
}
|
||||
|
||||
QHash<TxRxKey, TtagData> txHash_;
|
||||
QHash<TxRxKey, TtagData> rxHash_;
|
||||
QMutex txHashLock_;
|
||||
QMutex rxHashLock_;
|
||||
|
||||
typedef uint PortIdKey;
|
||||
typedef uint GuidKey; // guid only, no ttagid
|
||||
typedef uint GuidKey;
|
||||
typedef QHash<GuidKey, Timing> PortTiming;
|
||||
QHash<PortIdKey, PortTiming*> timing_;
|
||||
QMutex timingLock_;
|
||||
@ -118,79 +97,4 @@ private:
|
||||
QTimer *gcTimer_; // Garbage collection for stale tx records
|
||||
};
|
||||
|
||||
inline
|
||||
bool StreamTiming::recordTxTime(uint portId, uint guid, uint ttagId,
|
||||
const struct timespec ×tamp)
|
||||
{
|
||||
TxRxKey key = makeKey(guid, ttagId);
|
||||
TtagData value = { .timeStamp = timestamp, .portId = portId};
|
||||
|
||||
timingDebug("[%d TX] %ld:%ld ttag %u guid %u", portId,
|
||||
timestamp.tv_sec, long(timestamp.tv_nsec), ttagId, guid);
|
||||
|
||||
QMutexLocker locker(&txHashLock_);
|
||||
txHash_.insert(key, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline
|
||||
bool StreamTiming::recordRxTime(uint portId, uint guid, uint ttagId,
|
||||
const struct timespec ×tamp)
|
||||
{
|
||||
TxRxKey key = makeKey(guid, ttagId);
|
||||
TtagData value = { .timeStamp = timestamp, .portId = portId};
|
||||
|
||||
timingDebug("[%d RX] %ld:%ld ttag %u guid %u", portId,
|
||||
timestamp.tv_sec, long(timestamp.tv_nsec), ttagId, guid);
|
||||
|
||||
QMutexLocker locker(&rxHashLock_);
|
||||
rxHash_.insert(key, value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline
|
||||
bool StreamTiming::recordTxTime(uint portId, uint guid, uint ttagId,
|
||||
const struct timeval ×tamp)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = timestamp.tv_sec;
|
||||
ts.tv_nsec = timestamp.tv_usec*1000;
|
||||
|
||||
return recordTxTime(portId, guid, ttagId, ts);
|
||||
}
|
||||
|
||||
inline
|
||||
bool StreamTiming::recordRxTime(uint portId, uint guid, uint ttagId,
|
||||
const struct timeval ×tamp)
|
||||
{
|
||||
struct timespec ts;
|
||||
ts.tv_sec = timestamp.tv_sec;
|
||||
ts.tv_nsec = timestamp.tv_usec*1000;
|
||||
|
||||
return recordRxTime(portId, guid, ttagId, ts);
|
||||
}
|
||||
|
||||
// TTagList contains 32-bit ttags formatted as ttagId (8msb) + guid (24lsb)
|
||||
inline
|
||||
bool StreamTiming::recordTxTime(uint portId, uint *ttagList, int count,
|
||||
const struct timespec ×tamp)
|
||||
{
|
||||
TtagData value = { .timeStamp = timestamp, .portId = portId};
|
||||
QMutexLocker locker(&txHashLock_);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
TxRxKey key = TxRxKey(ttagList[i]);
|
||||
|
||||
timingDebug("[%d TX] %ld:%ld ttag %u guid %u", portId,
|
||||
timestamp.tv_sec, long(timestamp.tv_nsec),
|
||||
ttagIdFromKey(key), guidFromKey(key));
|
||||
|
||||
txHash_.insert(key, value);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -1,26 +1,33 @@
|
||||
TEMPLATE = app
|
||||
CONFIG += qt console
|
||||
QT += xml network script
|
||||
QT += xml network script widgets
|
||||
INCLUDEPATH += "../rpc/" "../common/" "../client"
|
||||
|
||||
OBJDIR = .
|
||||
win32 {
|
||||
LIBS += -lwpcap -lpacket
|
||||
CONFIG(debug, debug|release) {
|
||||
OBJDIR = debug
|
||||
LIBS += -L"../common/debug" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc/debug" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/debug/libostprotogui.a" \
|
||||
"../common/debug/libostproto.a" \
|
||||
"../rpc/debug/libpbrpc.a"
|
||||
} else {
|
||||
OBJDIR = release
|
||||
LIBS += -L"../common/release" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc/release" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/release/libostprotogui.a" \
|
||||
"../common/release/libostproto.a" \
|
||||
"../rpc/release/libpbrpc.a"
|
||||
}
|
||||
} else {
|
||||
LIBS += -lpcap
|
||||
LIBS += -L"../common" -lostprotogui -lostproto
|
||||
LIBS += -L"../rpc" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/libostprotogui.a" \
|
||||
"../common/libostproto.a" \
|
||||
"../rpc/libpbrpc.a"
|
||||
}
|
||||
LIBS += -L"../common/$$OBJDIR" -lostfile -lostproto
|
||||
LIBS += -L"../rpc/$$OBJDIR" -lpbrpc
|
||||
POST_TARGETDEPS += \
|
||||
"../common/$$OBJDIR/libostfile.a" \
|
||||
"../common/$$OBJDIR/libostproto.a" \
|
||||
"../rpc/$$OBJDIR/libpbrpc.a"
|
||||
|
||||
LIBS += -lprotobuf
|
||||
LIBS += -L"../extra/qhexedit2/$(OBJECTS_DIR)/" -lqhexedit2
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
APP_VERSION = 1.4.0-dev
|
||||
APP_VERSION = 1.3.0-dev
|
||||
APP_REVISION = $(shell git rev-parse --short=12 --verify HEAD)
|
||||
#uncomment the below line in a source package and fill-in the correct revision
|
||||
#APP_REVISION = <rev-hash>@
|
||||
|
Loading…
Reference in New Issue
Block a user