Compare commits

..

50 Commits

Author SHA1 Message Date
Srivats P
ca956c3c18 Fix extra whitespace in latency feature added code 2023-05-26 11:07:27 +05:30
Srivats P
f98c8af594 Comment out L4Cksum code from pcap tx thread
We are not rewriting L4Cksum for ttag packets at the moment. See
comment in packetsequence.h

Commenting out this code doesn't seem to observably improve tx
performance though.

The latency code seems to reduce stream stats max tx performance
by around 3 to 5%. Recovering this may be done separately as part
of overall performance optimzation of Tx code.
2023-05-08 15:31:20 +05:30
Srivats P
accc47fa34 Don't use avgDelay in interleaved mode
The interleaved mode's single packet set MUST always have 0 delay
for accurate rate.

Before latency code, the interleaved packet set was added implicitly
and had 0 delay. Latency code added explicit packet set and used
avgDelay for the set. The avgDelay was needed for the original algo
used to determine when to send ttag packets. That algo is no longer
used (ttag markers are now explicitly configured by AbstractPort).

Turbo still needs avgDelay for other use, but changes will be made in
Turbo code for that.
2023-05-08 12:01:56 +05:30
Srivats P
6f71844f7c Make win32 specific changes for per-stream latency
There are 2 changes -
1. Encode txPort in ttag packets and use it at TxTtagStats and
RxPcapStats to identify Tx and Rx packets respectively
2. Don't use pcap_sendqueue_transmit() if stream timing is in use -
since we can't modify TTAG packets inside that API
2023-05-06 13:15:37 +05:30
Srivats P
bd2a4715dc Fix windows build issues 2023-05-01 15:31:30 +05:30
Srivats P
4886739da6 Compile out timing debug prints
They can be compiled in if required
2023-04-30 11:50:54 +05:30
Srivats P
80a4578847 Fix latency timers not getting started/stopped
In a previous commit, we start/stop these timers based on number of
ports tracking stream stats triggered by RPCs. However, timers cannot
be triggered across threads (RPC thread and main thread in this case).

This fix uses a queued connection to post it to the other queue.
2023-04-30 10:40:49 +05:30
Srivats P
390ffae2b4 Fix tx ttag pcap filter capture filter syntax
The capture filter was not getting compiled earlier
2023-04-29 12:33:34 +05:30
Srivats P
d17ab7ab42 Use TtagTimeInterval to determine ttag markers
This is for interlaved mode; sequential mode was already using it
2023-04-29 12:08:26 +05:30
Srivats P
5abd6fb962 Don't use udiffTimeStamp() with struct timeval
It will fail to build for non-Linux platforms where TimeStamp is NOT timeval
2023-04-27 12:50:22 +05:30
Srivats P
3c0bc067fa Retain seq as param for sendQueueTransmit for now
If and when we remove PacketSequence::ttagL4ChecksumOffset we will take a call
if we should revert back to passing seq->sendQueue instead of seq at that time
2023-04-27 12:48:37 +05:30
Srivats P
223e44a6e3 Run stream latency timers only when required
Required => At least one port is tracking stream stats

Also changed some optimization FIXMEs as TODOs
2023-04-27 12:30:36 +05:30
Srivats P
6108de9b4f Remove comment about trying read-write lock
The stream timingHash is read by getStreamStats() while it is read/write
for processRecords(), the latter is a more frequent operation so there's
no real benefit of using a read-write lock instead of simple mutex.
2023-04-27 11:15:26 +05:30
Srivats P
e761bfa5c4 Make app QObject parent of StreamTiming singleton 2023-04-27 11:09:31 +05:30
Srivats P
b9345463c4 Add thread name for PcapRxStats
PcapTxTtagStats thread name has also been shortened since names beyond 16
chars are truncated.
2023-04-26 12:22:26 +05:30
Srivats P
dc6c4963a2 Change stream timing GC timer to CLOCK_REALTIME 2023-04-26 11:38:00 +05:30
Srivats P
cded62246e Don't fix incorrect checksum for Ttag packets
See comments in code
2023-04-26 10:01:24 +05:30
Srivats P
10befe0a66 Remove PcapTxTtagStats::handle_ member
PcaptxTtagStats inherits from PcapSession which already includes a protected
handle_ member.

This removal was likely left off when PcapTxTtagStats started inheriting from
PcapSession.
2023-04-24 17:26:50 +05:30
Srivats P
4394c7ffee Rework sequential mode build for new ttag algo
The previous commit changed the algo to determine which packets were Ttag'd,
but changes were done only for interleaved mode.

This commit adds the changes required for sequential mode.
2023-04-24 17:05:40 +05:30
Srivats P
ef1c166e7f Change algo to infer next Ttag pkt (interleaved mode)
The algo works for the following cases of interleaved streams -
 * pktListDuration < ttagTimeInterval
 * pktListDuration > ttagTimeInterval
 * some streams have Ttag, some don't
    - first stream has Ttag
    - first stream does NOT have Ttag
 * no streams have Ttag

Changes for sequential mode are pending
2023-04-21 18:32:17 +05:30
Srivats P
d1d2a5c1b5 Fix infinite loop in building interleaved streams
Incorrect timestamp comparison was leading to infinite loop
2023-04-21 17:11:43 +05:30
Srivats P
f56ce2e2ec Add explicit packet set for interleaved streams
Interleaved mode used an implicitly added packet set in both base and Turbo
code. This has been chaned to use an explicit mode to keep things consistent.

Turbo code still has the implicit packet set related code - that needs to be
removed, once the explicit packet set code is validated and tested.
2023-04-17 11:59:00 +05:30
Srivats P
c91475d416 Use qFrom/ToBigEndian instead of ntohs/htons
For consistency with rest of the code
2023-04-13 18:10:43 +05:30
Srivats P
b2ad3c5d08 Recompute L4 checksum on-the-fly for TTag packets 2023-04-12 15:47:55 +05:30
Srivats P
896371b987 Calculate L4 checksum offset for TTag packets 2023-04-11 18:09:14 +05:30
Srivats P
f5bb2e5d80 Rename delay as latency in Protobuf/RPC
The GUI uses the term 'latency', so it's better if the API alsos use the same
term instead of 'delay'
2023-04-04 18:34:20 +05:30
Srivats P
7cfccd686e Fix StreamTiming TxRxKey
makeKey was incorrect by mistake
2023-04-04 13:22:18 +05:30
Srivats P
2e502434db GUI changes to display avg latency
At this time we only show per-guid latency aggregated across all ports
2023-04-04 13:19:53 +05:30
Srivats P
aa140cd32a Process pending before fetching streamTiming delay 2023-04-03 12:58:58 +05:30
Srivats P
3b499263ec Fix MacOS build break by removing unused member
PcapTxTtagStats::lastPcapStats_ was unused because debugStats() was moved to
PcapSession, but removing this member var was left out
2023-04-03 12:54:13 +05:30
Srivats P
c378600baf Fix streamTiming garbage collection infinite loop 2023-04-03 12:47:30 +05:30
Srivats P
68734c44ca Delete PcapTxTtagSession::debugStats
debugStats() was moved to base class PcapSession earlier, but removing it
from here was missed out
2023-03-31 18:21:36 +05:30
Srivats P
05a9dd5743 Rename PcapRxStats::id_ as PcapRxStats::portId_
Better code clarity
2023-03-31 16:58:52 +05:30
Srivats P
05335b31d5 Integrate StreamTiming with the code
Bugs found during integration were fixed and minor code improvements were made
such as using consts, const params, renaming members etc.
2023-03-31 16:55:03 +05:30
Srivats P
f4c21e1ae4 Change StreamTiming::timing_ from QList to QHash
Using QList meant we need to know the port count in the constructor - which is
difficult to know because StreamTiming is designed as a singleton
2023-03-29 17:13:12 +05:30
Srivats P
757d3f1b24 Add initial cut of StreamTiming class
This singleton class will keep track of Ttag timing across all ports and GUIDs.

A bunch of FIXMEs/TODOs are pending for this class implementation; also this
class has not been hooked up to the rest of the code yet.
2023-03-28 16:06:28 +05:30
Srivats P
7e30ef5541 Fix another MacOS build break
Break was due to following warnings (being promoted to errors) -
 * id_ private member was unused
 * tv_usec is int on MacOS (but long on Linux)
2023-03-23 15:55:35 +05:30
Srivats P
f7b6b46a5d Move debugStats() from PcapRx to base PcapSession
With this change, other classes that use PcapSession as base can also
use debugStats(), if required
2023-03-23 15:42:41 +05:30
Srivats P
823f01557b Ignore failures when stopping stream stats tracking
Stop everything irrespective of any failures
2023-03-22 16:42:54 +05:30
Srivats P
682d0cc5c9 Create a Tx Ttag tracker thread
For now we are just debug printing timestamp with T-TagId and GUID. We
need to store this tuple and compare when we Rx the same - this will be
in a upcoming commit.
2023-03-22 16:32:41 +05:30
Srivats P
f1cfaa6e89 Fix MacOS build break
For some reason udiffTimeStamp is not defined for MacOS. To be investigated
later.
2023-03-22 16:29:51 +05:30
Srivats P
072dfcdc3b Add an incrementing tag id to T-Tag packets 2023-03-18 16:34:47 +05:30
Srivats P
90a3731a90 Add infra to update L4 checksum for T-Tag packets
Pending
 * Calculate L4 checksum offset instead of hardcoded value
 * Recalculate packet L4 checksum instead of using 0 value
2023-03-18 15:01:11 +05:30
Srivats P
5d4a19174e Change T-Tag on the fly 2023-03-17 12:45:59 +05:30
Srivats P
2104936b69 Don't create implicit packet sets for Tx
As part of Turbo changes, we made changes to create explicit packet
sets, but for the base code we continued creating implicit packet
sets for some cases. With this change we don't create any implicit
packet set.

This change needs to be tested thoroughly for multiple cases.
2023-03-17 12:36:29 +05:30
Srivats P
5dc1b851cc Reduce vertical whitespace in sendQueueTransmit() 2023-03-16 12:10:42 +05:30
Srivats P
8e25669a0e Add T-Tag placeholder in sign protocol 2023-03-16 11:30:02 +05:30
Srivats P
ebccc44cdf Reformat TxThread/run {} to use less vertical space 2023-03-08 18:14:45 +05:30
Srivats P
f3a9b507b0 Update comments about implict packetset 2023-03-08 18:10:09 +05:30
Srivats P
620004d46b Fix werror warning about 0 being signed by default 2023-03-08 18:09:34 +05:30
58 changed files with 414 additions and 781 deletions

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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:

View File

@ -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

View File

@ -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);

View File

@ -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++;
}

View File

@ -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_;

View File

@ -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();
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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");
}

View File

@ -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;
}

View File

@ -96,8 +96,6 @@ public:
virtual int protocolFrameVariableCount() const;
private:
bool isRarp() const;
OstProto::Arp data;
};

View File

@ -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)));

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View File

@ -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)));

View File

@ -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)

View File

@ -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)

View File

@ -1,15 +0,0 @@
TEMPLATE = lib
CONFIG += qt staticlib
QT += widgets
FORMS = \
pcapfileimport.ui
HEADERS = \
fileformatoptions.h \
pcapoptionsdialog.h
SOURCES = \
fileformatoptions.cpp \
pcapoptionsdialog.cpp

View File

@ -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)

View File

@ -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;

View File

@ -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>&#x1F4A1; 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*/)

View File

@ -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);

View File

@ -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();
}

View File

@ -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

View File

@ -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;

View File

@ -32,7 +32,12 @@ SessionFileFormat::~SessionFileFormat()
{
}
QVariantMap* SessionFileFormat::options()
QDialog* SessionFileFormat::openOptionsDialog()
{
return NULL;
}
QDialog* SessionFileFormat::saveOptionsDialog()
{
return NULL;
}

View File

@ -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);

View File

@ -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
}

View File

@ -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

View File

@ -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 - "

View File

@ -35,7 +35,12 @@ StreamFileFormat::~StreamFileFormat()
{
}
QVariantMap* StreamFileFormat::options()
QDialog* StreamFileFormat::openOptionsDialog()
{
return NULL;
}
QDialog* StreamFileFormat::saveOptionsDialog()
{
return NULL;
}

View File

@ -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);

View File

@ -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
View File

@ -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

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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;
}
}

View File

@ -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_;
};

View File

@ -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();
}

View File

@ -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_;

View File

@ -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();
}

View File

@ -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_;

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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

View File

@ -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 &timestamp)
{
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 &timestamp)
{
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 &timestamp)
{
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 &timestamp)
{
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);
}

View File

@ -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 &timestamp);
bool recordRxTime(uint portId, uint guid, uint ttagId,
@ -50,10 +43,7 @@ public:
bool recordRxTime(uint portId, uint guid, uint ttagId,
const struct timeval &timestamp);
bool recordTxTime(uint portId, uint *ttagList, int count,
const struct timespec &timestamp);
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 &timestamp)
{
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 &timestamp)
{
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 &timestamp)
{
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 &timestamp)
{
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 &timestamp)
{
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

View File

@ -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

View File

@ -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>@