Fix spurious stream stats drops
The problem happens for bidirectional flows. The sequence of events is as follows when you start Tx on Ports p1, p2 with the current code - 1. Clear stream stats on p1 2. Start tx on p1 3. Clear stream stats on p2 4. Start tx on p2 By the time #3 is executed, it may have already rx packets from p1 which are being incorrectly cleared, this will cause these number of packets to show up as dropped instead - incorrectly. The fix is to change the order like this - 1. Clear stream stats on p1 2. Clear stream stats on p2 3. Start tx on p1 4. Start tx on p2 Unidirectional flows will not see this problem - as long as startTx is done only on the Tx port and not the Rx port. This bug is a regression caused due to the code changes introduced for the stream stats rates feature implemented in 1.2.0
This commit is contained in:
parent
e2369c02bc
commit
c70811eaa4
@ -429,13 +429,12 @@ void MyService::startTransmit(::google::protobuf::RpcController* /*controller*/,
|
|||||||
// XXX: stream stats uses port tx duration to calculate per stream
|
// XXX: stream stats uses port tx duration to calculate per stream
|
||||||
// rates; tx duration is for the last tx run only - so stream stats
|
// rates; tx duration is for the last tx run only - so stream stats
|
||||||
// should also correspond to the last run only.
|
// should also correspond to the last run only.
|
||||||
// Hence clear stream stats before Tx
|
// Hence clear stream stats before Tx.
|
||||||
|
// XXX: clear stream stats on ALL ports in user provided list before
|
||||||
|
// starting Tx on any of them
|
||||||
for (int i = 0; i < request->port_id_size(); i++)
|
for (int i = 0; i < request->port_id_size(); i++)
|
||||||
{
|
{
|
||||||
int portId;
|
int portId = request->port_id(i).id();
|
||||||
int frameError = 0;
|
|
||||||
|
|
||||||
portId = request->port_id(i).id();
|
|
||||||
if ((portId < 0) || (portId >= portInfo.size())) {
|
if ((portId < 0) || (portId >= portInfo.size())) {
|
||||||
error = true;
|
error = true;
|
||||||
notes += QString("Port %1 start transmit: invalid port\n")
|
notes += QString("Port %1 start transmit: invalid port\n")
|
||||||
@ -443,10 +442,23 @@ void MyService::startTransmit(::google::protobuf::RpcController* /*controller*/,
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
portLock[portId]->lockForWrite();
|
||||||
|
portInfo[portId]->resetStreamStatsAll();
|
||||||
|
portLock[portId]->unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < request->port_id_size(); i++)
|
||||||
|
{
|
||||||
|
int frameError = 0;
|
||||||
|
int portId = request->port_id(i).id();
|
||||||
|
|
||||||
|
if ((portId < 0) || (portId >= portInfo.size())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
portLock[portId]->lockForWrite();
|
portLock[portId]->lockForWrite();
|
||||||
if (portInfo[portId]->isDirty())
|
if (portInfo[portId]->isDirty())
|
||||||
frameError = portInfo[portId]->updatePacketList();
|
frameError = portInfo[portId]->updatePacketList();
|
||||||
portInfo[portId]->resetStreamStatsAll();
|
|
||||||
portInfo[portId]->startTransmit();
|
portInfo[portId]->startTransmit();
|
||||||
portLock[portId]->unlock();
|
portLock[portId]->unlock();
|
||||||
if (frameError) {
|
if (frameError) {
|
||||||
|
@ -34,7 +34,7 @@ PcapPort::PcapPort(int id, const char *device)
|
|||||||
transmitter_ = new PcapTransmitter(device, streamStats_);
|
transmitter_ = new PcapTransmitter(device, streamStats_);
|
||||||
capturer_ = new PortCapturer(device);
|
capturer_ = new PortCapturer(device);
|
||||||
emulXcvr_ = new EmulationTransceiver(device, deviceManager_);
|
emulXcvr_ = new EmulationTransceiver(device, deviceManager_);
|
||||||
rxStatsPoller_ = new PcapRxStats(device, streamStats_);
|
rxStatsPoller_ = new PcapRxStats(device, streamStats_, id);
|
||||||
|
|
||||||
if (!monitorRx_->handle() || !monitorTx_->handle())
|
if (!monitorRx_->handle() || !monitorTx_->handle())
|
||||||
isUsable_ = false;
|
isUsable_ = false;
|
||||||
@ -142,6 +142,15 @@ bool PcapPort::setRateAccuracy(AbstractPort::Accuracy accuracy)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PcapPort::updateStreamStats()
|
||||||
|
{
|
||||||
|
// XXX: PcapTxThread already does this at the end of transmit; we
|
||||||
|
// just dump rx stats poller debug stats here
|
||||||
|
|
||||||
|
qDebug("port %d rxStatsPoller: %s",
|
||||||
|
id(), qUtf8Printable(rxStatsPoller_->debugStats()));
|
||||||
|
}
|
||||||
|
|
||||||
void PcapPort::startDeviceEmulation()
|
void PcapPort::startDeviceEmulation()
|
||||||
{
|
{
|
||||||
emulXcvr_->start();
|
emulXcvr_->start();
|
||||||
|
@ -77,6 +77,8 @@ public:
|
|||||||
virtual bool isCaptureOn() { return capturer_->isRunning(); }
|
virtual bool isCaptureOn() { return capturer_->isRunning(); }
|
||||||
virtual QIODevice* captureData() { return capturer_->captureFile(); }
|
virtual QIODevice* captureData() { return capturer_->captureFile(); }
|
||||||
|
|
||||||
|
virtual void updateStreamStats();
|
||||||
|
|
||||||
virtual void startDeviceEmulation();
|
virtual void startDeviceEmulation();
|
||||||
virtual void stopDeviceEmulation();
|
virtual void stopDeviceEmulation();
|
||||||
virtual int sendEmulationPacket(PacketBuffer *pktBuf);
|
virtual int sendEmulationPacket(PacketBuffer *pktBuf);
|
||||||
|
@ -24,7 +24,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
|
|
||||||
#define Xnotify qWarning // FIXME
|
#define Xnotify qWarning // FIXME
|
||||||
|
|
||||||
PcapRxStats::PcapRxStats(const char *device, StreamStats &portStreamStats)
|
PcapRxStats::PcapRxStats(const char *device, StreamStats &portStreamStats, int id)
|
||||||
: streamStats_(portStreamStats)
|
: streamStats_(portStreamStats)
|
||||||
{
|
{
|
||||||
device_ = QString::fromLatin1(device);
|
device_ = QString::fromLatin1(device);
|
||||||
@ -33,6 +33,8 @@ PcapRxStats::PcapRxStats(const char *device, StreamStats &portStreamStats)
|
|||||||
isDirectional_ = true;
|
isDirectional_ = true;
|
||||||
|
|
||||||
handle_ = NULL;
|
handle_ = NULL;
|
||||||
|
|
||||||
|
id_ = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
pcap_t* PcapRxStats::handle()
|
pcap_t* PcapRxStats::handle()
|
||||||
@ -104,6 +106,7 @@ void PcapRxStats::run()
|
|||||||
}
|
}
|
||||||
|
|
||||||
_skip_filter:
|
_skip_filter:
|
||||||
|
memset(&lastPcapStats_, 0, sizeof(lastPcapStats_));
|
||||||
PcapSession::preRun();
|
PcapSession::preRun();
|
||||||
state_ = kRunning;
|
state_ = kRunning;
|
||||||
while (1) {
|
while (1) {
|
||||||
@ -143,7 +146,6 @@ _skip_filter:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
PcapSession::postRun();
|
PcapSession::postRun();
|
||||||
|
|
||||||
pcap_close(handle_);
|
pcap_close(handle_);
|
||||||
handle_ = NULL;
|
handle_ = NULL;
|
||||||
stop_ = false;
|
stop_ = false;
|
||||||
@ -191,3 +193,57 @@ bool PcapRxStats::isDirectional()
|
|||||||
{
|
{
|
||||||
return isDirectional_;
|
return isDirectional_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX: Implemented as reset on read
|
||||||
|
QString PcapRxStats::debugStats()
|
||||||
|
{
|
||||||
|
QString dbgStats;
|
||||||
|
|
||||||
|
#ifdef Q_OS_WIN32
|
||||||
|
static_assert(sizeof(struct pcap_stat) == 6*sizeof(uint),
|
||||||
|
"pcap_stat has less or more than 6 values");
|
||||||
|
int size;
|
||||||
|
struct pcap_stat incPcapStats;
|
||||||
|
struct pcap_stat *pcapStats = pcap_stats_ex(handle_, &size);
|
||||||
|
if (pcapStats && (uint(size) >= 6*sizeof(uint))) {
|
||||||
|
incPcapStats.ps_recv = pcapStats->ps_recv - lastPcapStats_.ps_recv;
|
||||||
|
incPcapStats.ps_drop = pcapStats->ps_drop - lastPcapStats_.ps_drop;
|
||||||
|
incPcapStats.ps_ifdrop = pcapStats->ps_ifdrop - lastPcapStats_.ps_ifdrop;
|
||||||
|
incPcapStats.ps_capt = pcapStats->ps_capt - lastPcapStats_.ps_capt;
|
||||||
|
incPcapStats.ps_sent = pcapStats->ps_sent - lastPcapStats_.ps_sent;
|
||||||
|
incPcapStats.ps_netdrop = pcapStats->ps_netdrop - lastPcapStats_.ps_netdrop;
|
||||||
|
dbgStats = QString("recv: %1 drop: %2 ifdrop: %3 "
|
||||||
|
"capt: %4 sent: %5 netdrop: %6")
|
||||||
|
.arg(incPcapStats.ps_recv)
|
||||||
|
.arg(incPcapStats.ps_drop)
|
||||||
|
.arg(incPcapStats.ps_ifdrop)
|
||||||
|
.arg(incPcapStats.ps_capt)
|
||||||
|
.arg(incPcapStats.ps_sent)
|
||||||
|
.arg(incPcapStats.ps_netdrop);
|
||||||
|
lastPcapStats_ = *pcapStats;
|
||||||
|
} else {
|
||||||
|
dbgStats = QString("error reading pcap stats: %1")
|
||||||
|
.arg(pcap_geterr(handle_));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
struct pcap_stat pcapStats;
|
||||||
|
struct pcap_stat incPcapStats;
|
||||||
|
|
||||||
|
int ret = pcap_stats(handle_, &pcapStats);
|
||||||
|
if (ret == 0) {
|
||||||
|
incPcapStats.ps_recv = pcapStats.ps_recv - lastPcapStats_.ps_recv;
|
||||||
|
incPcapStats.ps_drop = pcapStats.ps_drop - lastPcapStats_.ps_drop;
|
||||||
|
incPcapStats.ps_ifdrop = pcapStats.ps_ifdrop - lastPcapStats_.ps_ifdrop;
|
||||||
|
dbgStats = QString("recv: %1 drop: %2 ifdrop: %3")
|
||||||
|
.arg(incPcapStats.ps_recv)
|
||||||
|
.arg(incPcapStats.ps_drop)
|
||||||
|
.arg(incPcapStats.ps_ifdrop);
|
||||||
|
lastPcapStats_ = *pcapStats;
|
||||||
|
} else {
|
||||||
|
dbgStats = QString("error reading pcap stats: %1")
|
||||||
|
.arg(pcap_geterr(handle_));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return dbgStats;
|
||||||
|
}
|
||||||
|
@ -27,7 +27,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
|
|||||||
class PcapRxStats: public PcapSession
|
class PcapRxStats: public PcapSession
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
PcapRxStats(const char *device, StreamStats &portStreamStats);
|
PcapRxStats(const char *device, StreamStats &portStreamStats, int id);
|
||||||
pcap_t* handle();
|
pcap_t* handle();
|
||||||
void run();
|
void run();
|
||||||
bool start();
|
bool start();
|
||||||
@ -35,6 +35,8 @@ public:
|
|||||||
bool isRunning();
|
bool isRunning();
|
||||||
bool isDirectional();
|
bool isDirectional();
|
||||||
|
|
||||||
|
QString debugStats();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum State {
|
enum State {
|
||||||
kNotStarted,
|
kNotStarted,
|
||||||
@ -48,6 +50,9 @@ private:
|
|||||||
pcap_t *handle_;
|
pcap_t *handle_;
|
||||||
volatile State state_;
|
volatile State state_;
|
||||||
bool isDirectional_;
|
bool isDirectional_;
|
||||||
|
|
||||||
|
int id_;
|
||||||
|
struct pcap_stat lastPcapStats_;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -127,6 +127,9 @@ void PcapTransmitter::updateTxThreadStreamStats()
|
|||||||
streamStats_[guid].tx_pkts += sst.tx_pkts;
|
streamStats_[guid].tx_pkts += sst.tx_pkts;
|
||||||
streamStats_[guid].tx_bytes += sst.tx_bytes;
|
streamStats_[guid].tx_bytes += sst.tx_bytes;
|
||||||
if (adjustRxStreamStats_) {
|
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_pkts -= sst.tx_pkts;
|
||||||
streamStats_[guid].rx_bytes -= sst.tx_bytes;
|
streamStats_[guid].rx_bytes -= sst.tx_bytes;
|
||||||
}
|
}
|
||||||
|
@ -365,7 +365,7 @@ _exit2:
|
|||||||
//Q_ASSERT(lastTxDuration_ >= 0);
|
//Q_ASSERT(lastTxDuration_ >= 0);
|
||||||
|
|
||||||
if (trackStreamStats_)
|
if (trackStreamStats_)
|
||||||
updateStreamStats();
|
updateTxStreamStats();
|
||||||
|
|
||||||
state_ = kFinished;
|
state_ = kFinished;
|
||||||
}
|
}
|
||||||
@ -466,7 +466,7 @@ int PcapTxThread::sendQueueTransmit(pcap_t *p,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PcapTxThread::updateStreamStats()
|
void PcapTxThread::updateTxStreamStats()
|
||||||
{
|
{
|
||||||
// If no packets in list, nothing to be done
|
// If no packets in list, nothing to be done
|
||||||
if (!packetListSize_)
|
if (!packetListSize_)
|
||||||
|
@ -68,7 +68,7 @@ private:
|
|||||||
static void udelay(unsigned long usec);
|
static void udelay(unsigned long usec);
|
||||||
int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, long &overHead,
|
int sendQueueTransmit(pcap_t *p, pcap_send_queue *queue, long &overHead,
|
||||||
int sync);
|
int sync);
|
||||||
void updateStreamStats();
|
void updateTxStreamStats();
|
||||||
|
|
||||||
// Intermediate state variables used while building the packet list
|
// Intermediate state variables used while building the packet list
|
||||||
PacketSequence *currentPacketSequence_;
|
PacketSequence *currentPacketSequence_;
|
||||||
|
Loading…
Reference in New Issue
Block a user