1 ---------------------------------
5 ---------------------------------
9 local dpdk = require
"dpdk"
10 local device = require
"device"
12 function mod.average(data)
14 for i, v in ipairs(data) do
20 function mod.median(data)
21 return mod.percentile(data, 50)
24 function mod.percentile(data, p)
25 local sortedData = { }
26 for k, v in ipairs(data) do
29 table.sort(sortedData)
30 return data[math.ceil(
#data * p / 100)]
33 function mod.stdDev(data)
34 local avg =
mod.average(data)
36 for i, v in ipairs(data) do
37 sum = sum + (v - avg) ^ 2
39 return (sum / (
#data - 1)) ^ 0.5
42 function mod.sum(data)
44 for i, v in ipairs(data) do
50 function
mod.addStats(data, ignoreFirstAndLast)
52 if ignoreFirstAndLast then
53 for i = 2, #data - 1
do
61 data.avg =
mod.average(copy)
62 data.stdDev =
mod.stdDev(copy)
63 data.median =
mod.median(copy)
64 data.sum =
mod.sum(copy)
67 local
function getPlainUpdate(direction)
68 return function(stats, file, total, mpps, mbit, wireMbit)
69 file:write(("[%s] %s %d packets, current rate %.2f Mpps, %.2f MBit/s, %.2f MBit/s wire rate.\
n"):format(stats.name, direction, total, mpps, mbit, wireMbit))
74 local function getPlainFinal(direction)
75 return function(stats, file)
76 file:write(("[%s] %s %d packets with %d bytes payload (including CRC).\
n"):format(stats.name, direction, stats.total, stats.totalBytes))
77 file:write(("[%s] %s %f (StdDev %f) Mpps, %f (StdDev %f) MBit/s, %f (StdDev %f) MBit/s wire rate on average.\
n"):format(
78 stats.name, direction,
79 stats.mpps.avg, stats.mpps.stdDev,
80 stats.mbit.avg, stats.mbit.stdDev,
81 stats.wireMbit.avg, stats.wireMbit.stdDev
87 local function getIniUpdate(direction)
88 return function(stats, file, total, mpps, mbit, wireMbit)
89 file:write(("%s%s,packets=%d,rate=%.2f,rate_mbits=%.2f,rate_wire=%.2f\
n"):format(stats.name, direction, total, mpps, mbit, wireMbit))
94 local function getIniFinal(direction)
95 return function(stats, file)
96 file:write(("%sTotal%s,packets=%d,bytes=%d,rate=%f,rate_dev=%f,rate_mbits=%f,rate_mbits_dev=%f,rate_wire=%f,rate_wire_dev=%f.\
n"):format(
97 stats.name, direction,
98 stats.total, stats.totalBytes,
99 stats.mpps.avg, stats.mpps.stdDev,
100 stats.mbit.avg, stats.mbit.stdDev,
101 stats.wireMbit.avg, stats.wireMbit.stdDev
106 local formatters = {}
107 formatters[
"plain"] = {
108 rxStatsInit =
function() end, -- nothing
for plain, machine-readable formats can print a header here
109 rxStatsUpdate = getPlainUpdate(
"Received"),
110 rxStatsFinal = getPlainFinal(
"Received"),
112 txStatsInit =
function() end,
113 txStatsUpdate = getPlainUpdate(
"Sent"),
114 txStatsFinal = getPlainFinal(
"Sent"),
117 -- Formatter that does nothing
118 formatters[
"nil"] = {
119 rxStatsInit =
function() end,
120 rxStatsUpdate =
function() end,
121 rxStatsFinal =
function() end,
123 txStatsInit =
function() end,
124 txStatsUpdate =
function() end,
125 txStatsFinal =
function () end,
128 -- Ini-Like Formatting
129 formatters[
"ini"] = {
130 rxStatsInit =
function() end,
131 rxStatsUpdate = getIniUpdate(
"Received"),
132 rxStatsFinal = getIniFinal(
"Received"),
134 txStatsInit =
function() end,
135 txStatsUpdate = getIniUpdate(
"Sent"),
136 txStatsFinal = getIniFinal(
"Sent"),
139 formatters[
"CSV"] = formatters[
"plain"] -- TODO
141 --- base constructor
for rx and tx counters
142 local
function newCounter(ctrType, name, dev, format, file)
143 format = format or "CSV"
144 file = file or io.stdout
145 local closeFile = false
146 if type(file) == "
string" then
150 if not formatters[format] then
151 error("unsupported output format " .. format)
158 closeFile = closeFile,
169 -- base
class for rx and tx counters
171 local function printStats(self, statsType, event, ...)
172 local func = formatters[
self.format][statsType .. event]
174 func(
self,
self.file, ...)
176 print("[Missing formatter for " .. self.format .. "]", self.name, statsType, event, ...)
180 local function updateCounter(self, time, pkts, bytes, dontPrint)
181 if not self.lastUpdate then
182 -- first call, save current stats but do not print anything
183 self.total, self.totalBytes = pkts, bytes
184 self.lastTotal = self.total
185 self.lastTotalBytes = self.totalBytes
186 self.lastUpdate = time
190 local elapsed = time - self.lastUpdate
191 self.lastUpdate = time
192 self.total = self.total + pkts
193 self.totalBytes = self.totalBytes + bytes
194 local mpps = (self.total - self.lastTotal) / elapsed / 10^6
195 local mbit = (self.totalBytes - self.lastTotalBytes) / elapsed / 10^6 * 8
196 local wireRate = mbit + (mpps * 20 * 8)
197 if not dontPrint then
198 self:print("Update", self.total, mpps, mbit, wireRate)
200 table.insert(self.mpps, mpps)
201 table.insert(self.mbit, mbit)
202 table.insert(self.wireMbit, wireRate)
203 self.lastTotal = self.total
204 self.lastTotalBytes = self.totalBytes
208 mod.addStats(self.mpps, true)
209 mod.addStats(self.mbit, true)
210 mod.addStats(self.wireMbit, true)
211 return self.mpps, self.mbit, self.wireMbit, self.total, self.totalBytes
214 local function finalizeCounter(self, sleep)
215 -- wait for any remaining packets to arrive/be sent if necessary
217 -- last stats are probably complete nonsense, especially if sleep ~= 0
218 -- we just do this to get the correct totals
220 updateCounter(self, dpdk.
getTime(), pkts, bytes, true)
221 mod.addStats(self.mpps, true)
222 mod.addStats(self.mbit, true)
223 mod.addStats(self.wireMbit, true)
225 if self.closeFile then
231 local rxCounter = {} -- base
class
232 local devRxCounter = setmetatable({}, rxCounter)
233 local pktRxCounter = setmetatable({}, rxCounter)
234 local manualRxCounter = setmetatable({}, rxCounter)
235 rxCounter.__index = rxCounter
236 devRxCounter.__index = devRxCounter
237 pktRxCounter.__index = pktRxCounter
238 manualRxCounter.__index = manualRxCounter
240 --- Create a
new rx counter
using device statistics registers.
241 --- @param name the name of the counter, included in the output. defaults to the device name
242 --- @param dev the device to track
243 --- @param format the output format,
"CSV" (
default) and
"plain" are currently supported
244 --- @param file the output file, defaults to standard out
246 if type(name) == "table" then
249 -- use device if queue objects are passed
250 dev = dev and dev.dev or dev
251 if type(dev) ~= "table" then
254 name = name or tostring(dev):sub(2, -2) -- strip brackets as they are added by the 'plain' output again
255 local obj =
newCounter("dev", name, dev, format, file)
257 return setmetatable(obj, devRxCounter)
260 --- Create a
new rx counter that can be updated by passing packet buffers to it.
261 --- @param name the name of the counter, included in the output
262 --- @param format the output format, "CSV" (default) and "plain" are currently supported
263 --- @param file the output file, defaults to standard out
266 return setmetatable(obj, pktRxCounter)
269 --- Create a
new rx counter that has to be updated manually.
270 --- @param name the name of the counter, included in the output
271 --- @param format the output format, "CSV" (default) and "plain" are currently supported
272 --- @param file the output file, defaults to standard out
274 local obj =
newCounter("manual", name, nil, format, file)
275 return setmetatable(obj, manualRxCounter)
280 finalizeCounter(self, self.sleep or 0)
283 function rxCounter:print(event, ...)
284 printStats(self, "rxStats", event, ...)
287 function rxCounter:
update()
289 if self.lastUpdate and time <= self.lastUpdate + 1 then
293 updateCounter(self, time, pkts, bytes)
299 updateCounter(self, dpdk.
getTime(), pkts, bytes, true)
303 --- Device-based counter
308 --- Packet-based counter
310 self.current = self.current + 1
311 self.currentBytes = self.currentBytes + buf.pkt.pkt_len + 4 -- include CRC
315 local pkts, bytes = self.current, self.currentBytes
316 self.current, self.currentBytes = 0, 0
321 --- Manual rx counter
322 function manualRxCounter:
update(pkts, bytes)
323 self.current = self.current + pkts
324 self.currentBytes = self.currentBytes + bytes
325 local time = dpdk.getTime()
326 if self.lastUpdate and time <= self.lastUpdate + 1 then
330 updateCounter(self, time, pkts, bytes)
334 local pkts, bytes = self.current, self.currentBytes
335 self.current, self.currentBytes = 0, 0
339 function manualRxCounter:updateWithSize(pkts, size)
340 self.current = self.current + pkts
341 self.currentBytes = self.currentBytes + pkts * (size + 4)
342 local time = dpdk.getTime()
343 if self.lastUpdate and time <= self.lastUpdate + 1 then
347 updateCounter(self, time, pkts, bytes)
351 local txCounter = {} -- base
class for tx counters
352 local devTxCounter = setmetatable({}, txCounter)
353 local pktTxCounter = setmetatable({}, txCounter)
354 local manualTxCounter = setmetatable({}, txCounter)
355 txCounter.__index = txCounter
356 devTxCounter.__index = devTxCounter
357 pktTxCounter.__index = pktTxCounter
358 manualTxCounter.__index = manualTxCounter
360 --- Create a
new tx counter
using device statistics registers.
361 --- @param name the name of the counter, included in the output. defaults to the device name
362 --- @param dev the device to track
363 --- @param format the output format,
"CSV" (
default) and
"plain" are currently supported
364 --- @param file the output file, defaults to standard out
366 if type(name) == "table" then
369 -- use device if queue objects are passed
370 dev = dev and dev.dev or dev
371 if type(dev) ~= "table" then
374 name = name or tostring(dev):sub(2, -2) -- strip brackets as they are added by the 'plain' output again
375 local obj =
newCounter("dev", name, dev, format, file)
377 return setmetatable(obj, devTxCounter)
380 --- Create a
new tx counter that can be updated by passing packet buffers to it.
381 --- @param name the name of the counter, included in the output
382 --- @param format the output format, "CSV" (default) and "plain" are currently supported
383 --- @param file the output file, defaults to standard out
386 return setmetatable(obj, pktTxCounter)
389 --- Create a
new tx counter that has to be updated manually.
390 --- @param name the name of the counter, included in the output
391 --- @param format the output format, "CSV" (default) and "plain" are currently supported
392 --- @param file the output file, defaults to standard out
394 local obj =
newCounter("manual", name, nil, format, file)
395 return setmetatable(obj, manualTxCounter)
400 finalizeCounter(self, self.sleep or 0)
403 function txCounter:print(event, ...)
404 printStats(self, "txStats", event, ...)
407 --- Device-based counter
408 function txCounter:
update()
410 if self.lastUpdate and time <= self.lastUpdate + 1 then
414 updateCounter(self, time, pkts, bytes)
417 --- Get accumulated statistics.
418 --- Calculat the average throughput.
422 updateCounter(self, dpdk.
getTime(), pkts, bytes, true)
427 return self.dev:getTxStats()
430 --- Packet-based counter
432 self.current = self.current + 1
433 self.currentBytes = self.currentBytes + buf.pkt.pkt_len + 4 -- include CRC
437 local pkts, bytes = self.current, self.currentBytes
438 self.current, self.currentBytes = 0, 0
442 --- Manual rx counter
443 function manualTxCounter:
update(pkts, bytes)
444 self.current = self.current + pkts
445 self.currentBytes = self.currentBytes + bytes
446 local time = dpdk.getTime()
447 if self.lastUpdate and time <= self.lastUpdate + 1 then
451 updateCounter(self, time, pkts, bytes)
454 function manualTxCounter:updateWithSize(pkts, size)
455 self.current = self.current + pkts
456 self.currentBytes = self.currentBytes + pkts * (size + 4)
457 local time = dpdk.getTime()
458 if self.lastUpdate and time <= self.lastUpdate + 1 then
462 updateCounter(self, time, pkts, bytes)
466 local pkts, bytes = self.current, self.currentBytes
467 self.current, self.currentBytes = 0, 0
function mod sleepMillis(t)
Delay by t milliseconds.
function mod newDevTxCounter(name, dev, format, file)
Create a new tx counter using device statistics registers.
function mod newPktRxCounter(name, format, file)
Create a new rx counter that can be updated by passing packet buffers to it.
function pktRxCounter countPacket(buf)
Packet-based counter.
function rxCounter update()
Device-based counter.
function rxCounter finalize(sleep)
Base class.
function mod newManualRxCounter(name, format, file)
Create a new rx counter that has to be updated manually.
function mod newDevRxCounter(name, dev, format, file)
Create a new rx counter using device statistics registers.
local mod
high-level dpdk wrapper
local function newCounter(ctrType, name, dev, format, file)
base constructor for rx and tx counters
function mod newManualTxCounter(name, format, file)
Create a new tx counter that has to be updated manually.
function devRxCounter getThroughput()
Device-based counter.
function mod getTime()
gets the time in seconds
local pkt
Module for packets (rte_mbuf)
n
Create a new array of memory buffers (initialized to nil).
function rxCounter getStats()
Get accumulated statistics.
function dev getRxStats()
get the number of packets received since the last call to this function
function mod newPktTxCounter(name, format, file)
Create a new tx counter that can be updated by passing packet buffers to it.