-T=$(filter-out lib.sh,$(wildcard *.sh))
+T:=$(filter-out lib.sh,$(wildcard *.sh))
-.PHONY: $(T)
+# Run slow test at the end
+T:=$(filter-out filter%,$(T)) $(filter filter%,$(T))
-all: $(T) html
+.PHONY: $(T:%=run-%)
+
+all: $(T:%=run-%) html
@echo "Run 'make plot' to generate graphs"
-TEST_FLAGS = -P -t all
+TEST_FLAGS = -P -t all -l all
+
+$(T:%=run-%):run-%:
+ ./$* $(TEST_FLAGS)
+
+.PHONY: plot plotall
+
+PLOT_SCRIPTS=$(shell find results -name plot.sh)
+
+define plot_template
+plot: $1/thumb
+$1/thumb: $1/plot.sh $(notdir $1).sh $(wildcard $1/*.txt)
+ $1/plot.sh -X
+endef
-$(T):%:
- ./$@ $(TEST_FLAGS)
+define plotall_template
+plotall: plot-$1
+.PHONY: plot-$1
+plot-$1:
+ $1/plot.sh -X
+endef
-.PHONY: plot
-plot:
- $(MAKE) TEST_FLAGS="-p -X"
+$(foreach result_dir,$(PLOT_SCRIPTS:%/plot.sh=%),$(eval $(call plot_template,$(result_dir))))
+$(foreach result_dir,$(PLOT_SCRIPTS:%/plot.sh=%),$(eval $(call plotall_template,$(result_dir))))
.PHONY: html
html:
ids="0 $(seq 255 256 2047)"
-main() {
+prepare() {
sshgw 'for i in `seq 0 2047`; do cangw -A -s can0 -d can1 -f $(printf %x $i):c00007ff; done'
+}
+
+main() {
for i in $ids; do
- latester -d can0 -d can1 -d can2 -c $COUNT -i $i $(traffic_and_length 2) -h hist-$i.dat -f time-$i.dat
+ latester -d can0 -d can1 -d can2 -c $COUNT -i $i $(traffic_and_length 2) -n id-$i
done
}
set title "2048 GW jobs (one per id, mask C00007FF), no modifications"
set logscale y
set grid
-set xlabel "Time [{/Symbol m}s]"
+set xlabel "Time [ms]"
set ylabel "Latency profile [messages]"
plot [0:1500] [1:$COUNT] \\
EOF
lt=1
for i in $ids; do
- echo_plot "\"hist-$i.dat\" with lp lt $lt title \"Message id $i\""
+ echo_plot "\"id-$i-hist.txt\" with lp lt $lt title \"Message id $i\""
lt=$((lt+1))
done
echo
ids="0 $(seq 255 256 2047)"
-main() {
+prepare() {
sshgw 'for i in `seq 0 2047`; do cangw -A -s can0 -d can1 -f $(printf %x $i):7ff; done'
+}
+
+main() {
for i in $ids; do
- latester -d can0 -d can1 -d can2 -c $COUNT -i $i $(traffic_and_length 2) -h hist-$i.dat -f time-$i.dat
+ latester -d can0 -d can1 -d can2 -c $COUNT -i $i $(traffic_and_length 2) -n id-$i
done
}
set title "2048 GW jobs (one per id, mask 0x7FF), no modifications"
set logscale y
set grid
-set xlabel "Time [{/Symbol m}s]"
+set xlabel "Time [ms]"
set ylabel "Latency profile [messages]"
-plot [0:1500] [1:$COUNT] \\
+plot [0:] [1:$COUNT] \\
EOF
lt=1
for i in $ids; do
- echo_plot "\"hist-$i.dat\" with lp lt $lt title \"Message id $i\""
+ echo_plot "\"id-$i-hist.txt\" with lp lt $lt title \"Message id $i\""
lt=$((lt+1))
done
}
+all: test results
+
test:
./wvtestrun ./wvtest.py genhtml-test.py
+
+results:
+ $(MAKE) -C .. html
@wvtest
def Dimension_and_DimValue():
d = Dimension('kern', 'Kernel')
- d.addValue('2.6.31')
- d.addValue('2.6.30')
+ DimValue(d, '2.6.31')
+ DimValue(d, '2.6.30')
l=[]
for v in d:
WVPASSEQ(v.__class__, DimValue)
l.append(v.value)
lsorted = ['2.6.30', '2.6.31']
WVPASSEQ(l, lsorted)
- WVPASSEQ(v.htmlLabel(), '2.6.31')
+ WVPASSEQ(v.htmlTableHeading(), '2.6.31')
WVPASSEQ(d['2.6.31'].value, '2.6.31')
@wvtest
def Two_Dimensions_in_Tests():
- t = Tests(None, DimensionTest(), DimensionKern())
- WVPASSEQ([(a.__class__.__name__, b.__class__.__name__) for a, b in t.iterDimensionPairs()],
+ t = Tests(None, Space(DimensionTest(), DimensionKern()))
+ WVPASSEQ([(a.__class__.__name__, b.__class__.__name__) for a, b in t.space.iterDimensionPairs()],
[('DimensionTest', 'DimensionKern'), ('DimensionKern', 'DimensionTest')])
@wvtest
def Three_Dimensions_in_Tests():
dt = DimensionTest()
dk = DimensionKern()
dd = Dimension('tmp', "Tmp")
- t = Tests(None, dt, dk, dd)
- pairs = [(a.__class__.__name__, b.__class__.__name__) for a, b in t.iterDimensionPairs()]
+ t = Tests(None, Space(dt, dk, dd))
+ pairs = [(a.__class__.__name__, b.__class__.__name__) for a, b in t.space.iterDimensionPairs()]
WVPASSEQ(pairs[0], ('DimensionTest', 'DimensionKern'))
WVPASSEQ(pairs[1], ('DimensionKern', 'DimensionTest'))
WVPASSEQ(pairs[2], ('DimensionTest', 'Dimension'))
WVPASSEQ(pairs[4], ('DimensionKern', 'Dimension'))
WVPASSEQ(pairs[5], ('Dimension', 'DimensionKern'))
WVPASSEQ(len(pairs), 6)
- WVEXCEPT(KeyError, t.addTest, None, [1, 2])
- WVEXCEPT(KeyError, t.addTest, None, [1, 2, 3, 4])
+# WVEXCEPT(KeyError, t.addTest, None, [1, 2])
+# WVEXCEPT(KeyError, t.addTest, None, [1, 2, 3, 4])
- class MyTest(Test):
- pass
+# class MyTest(Test):
+# pass
- t.addTest(MyTest, [1, 'a', 'A']);
+# t.addTest(MyTest, [1, 'a', 'A']);
- WVPASSEQ(dt.keys(), [1])
- WVPASSEQ(dk.keys(), ['a'])
- WVPASSEQ(dd.keys(), ['A'])
+# WVPASSEQ(dt.keys(), [1])
+# WVPASSEQ(dk.keys(), ['a'])
+# WVPASSEQ(dd.keys(), ['A'])
- v1 = dt[1]
- v2 = dk['a']
- v3 = dd['A']
+# v1 = dt[1]
+# v2 = dk['a']
+# v3 = dd['A']
- WVPASSEQ(t.getTest((v1, v2, v3)), MyTest)
- WVPASSEQ(t.getTest((v1, v3, v2)), MyTest)
- WVPASSEQ(t.getTest((v3, v2, v1)), MyTest)
- WVEXCEPT(KeyError, t.getTest, (v1, v1, v1))
- WVEXCEPT(KeyError, t.getTest, (v1, v2))
- WVEXCEPT(TypeError, t.getTest, v1)
+# WVPASSEQ(t.getTest((v1, v2, v3)), MyTest)
+# WVPASSEQ(t.getTest((v1, v3, v2)), MyTest)
+# WVPASSEQ(t.getTest((v3, v2, v1)), MyTest)
+# WVEXCEPT(KeyError, t.getTest, (v1, v1, v1))
+# WVEXCEPT(KeyError, t.getTest, (v1, v2))
+# WVEXCEPT(Exception, t.getTest, v1)
- WVPASSEQ([d.__class__.__name__ for d in t.iterRemainingDimensions([dt])], ['DimensionKern', 'Dimension'])
- WVPASSEQ([d.__class__.__name__ for d in t.iterRemainingDimensions([dt, dd])], ['DimensionKern'])
+# WVPASSEQ([d.__class__.__name__ for d in t.space.iterRemainingDimensions([dt])], ['DimensionKern', 'Dimension'])
+# WVPASSEQ([d.__class__.__name__ for d in t.space.iterRemainingDimensions([dt, dd])], ['DimensionKern'])
- WVPASSEQ(str([v for v in iterDimValues([dt, dk])]), str([[1,'a']]))
- dt.addValue(2)
- dk.addValue('b')
- WVPASSEQ(str([v for v in iterDimValues([dt, dk])]), str([[1, 'a'], [2, 'a'], [1, 'b'], [2, 'b']]))
+# WVPASSEQ(str([v for v in Space(dt, dk).iterValues()]), "[[DimValue(Dimension(test), 1), DimValue(Dimension(gwkern), 'a')]]")
+# DimValue(dt, 2)
+# DimValue(dk, 'b')
+# WVPASSEQ(str([v for v in Space(dt, dk).iterValues()]), "[[DimValue(Dimension(test), 1), DimValue(Dimension(gwkern), 'a')], [DimValue(Dimension(test), 2), DimValue(Dimension(gwkern), 'a')], [DimValue(Dimension(test), 1), DimValue(Dimension(gwkern), 'b')], [DimValue(Dimension(test), 2), DimValue(Dimension(gwkern), 'b')]]")
+
+@wvtest
+def SpaceTests():
+ d1 = Dimension(1)
+ d2 = Dimension(2)
+ d3 = Dimension(3)
+ s = Space(d1, d2, d3)
+ WVPASSEQ(str(s), "[Dimension(1), Dimension(2), Dimension(3)]")
+ v11 = DimValue(d1, 'one-one')
+ v12 = DimValue(d1, 'one-two')
+ v21 = DimValue(d2, 'two-one')
+ v22 = DimValue(d2, 'two-two')
+ v31 = DimValue(d3, 'three-one')
+ WVPASSEQ(str(DimValues([v11, v21])), "[DimValue(Dimension(1), 'one-one'), DimValue(Dimension(2), 'two-one')]")
+ WVPASSEQ(str(DimValues([v11, v21]) - d1), "[DimValue(Dimension(2), 'two-one')]")
+ WVPASSEQ(str(DimValues([v11, v21]) - d2), "[DimValue(Dimension(1), 'one-one')]")
+ WVPASSEQ(str(DimValues([v21, v31]) + v11), "[DimValue(Dimension(2), 'two-one'), DimValue(Dimension(3), 'three-one'), DimValue(Dimension(1), 'one-one')]")
+ WVPASSEQ(str(s.reorder(DimValues([v21, v31, v11]))), "[DimValue(Dimension(1), 'one-one'), DimValue(Dimension(2), 'two-one'), DimValue(Dimension(3), 'three-one')]")
import dircache;
import sys;
import urllib
+import traceback
-class DimValue:
+class DimValue(object):
+ def __new__(cls, dim, value):
+ if value in dim:
+ return dim[value]
+ else:
+ return super(DimValue, cls).__new__(cls, dim, value)
def __init__(self, dim, value):
self.dim = dim
self.value = value
+ self.dim.addValue(self)
+ def __str__(self):
+ return self.dim.val2str(self.value)
def __repr__(self):
- return repr(self.value)
- def htmlLabel(self):
- return self.dim.htmlLabel(self.value)
+ return "DimValue(%s, %s)" % (repr(self.dim), repr(self.value))
+ def htmlTableHeading(self):
+ return self.dim.htmlTableHeading(self.value)
+
+class DimValues(list):
+ def replace(self, val):
+ for i in xrange(len(self)):
+ if self[i].dim == val.dim:
+ self[i] = val
+ def __add__(self, val):
+ ret = DimValues(self)
+ ret.append(val)
+ return ret
+ def __sub__(self, dim):
+ result = DimValues(self)
+ for v in self:
+ if v.dim == dim:
+ result.remove(v)
+ return result
+ def key(self):
+ return tuple([v.value for v in self])
class Dimension(dict):
def __init__(self, atype, name=None):
self.name = name
else:
self.name = atype
+ self.sortedKeys = []
def __iter__(self):
- keys = self.keys()
- keys.sort()
- for k in keys:
- yield self[k]
-
- def addValue(self, *values):
- for value in values:
- if value not in self:
- self[value] = DimValue(self, value)
- def htmlLabel(self, v):
- return v
+ for i in xrange(len(self)):
+ yield self.getValue(i)
+ def getValue(self, index):
+ return self[self.sortedKeys[index]]
+
+ def addValue(self, value):
+ if value not in self:
+ if isinstance(value, DimValue):
+ self[value.value] = value
+ else:
+ raise Exception("Unsupported usage of addValue")
+ #self[value] = DimValue(self, value)
+ self.sortedKeys = self.keys()
+ self.sortedKeys.sort()
+ def val2str(self, v):
+ return str(v)
+ def htmlTableHeading(self, v):
+ return self.val2str(v)
+ def __str__(self):
+ return self.name
+ def __repr__(self):
+ return "Dimension(%s)"%self.type
class DimensionKern(Dimension):
def __init__(self):
Dimension.__init__(self, 'gwkern', 'GW kernel')
- def htmlLabel(self, v):
+ def htmlTableHeading(self, v):
i=v.find(":")
if i>0: kver=v[:i]
else: kver=v
class DimensionHostKern(Dimension):
def __init__(self):
Dimension.__init__(self, 'hostkern', 'Host kernel')
- def htmlLabel(self, v):
+ def val2str(self, v):
if v.find("host-") == 0:
v = v[5:]
- # TODO: remove host- prefix
+ return v
+ def htmlTableHeading(self, v):
+ v = self.val2str(v)
i = v.find(":")
if i>0: kver = v[:i]
else: kver = v
class DimensionTest(Dimension):
def __init__(self):
Dimension.__init__(self, 'test', 'Test')
- def htmlLabel(self, v):
+ def htmlTableHeading(self, v):
return v+"<br><a href='%s.sh.html'>source</a>"%(urllib.quote(v))
+class DimensionLoad(Dimension):
+ def __init__(self):
+ Dimension.__init__(self, 'load', 'Load')
+
class DimensionTraffic(Dimension):
def __init__(self):
Dimension.__init__(self, 'traf', 'Traffic')
- def htmlLabel(self, v):
- return v
-
-class Test:
+ def val2str(self, v):
+ if v == "50":
+ return "50%"
+ elif v == "oneatatime":
+ return "one message at a time"
+ else:
+ return v
+ def htmlTableHeading(self, v):
+ return self.val2str(v)
+class Test(object):
@classmethod
def isOnPath(cls, path):
- f = os.path.join(path, 'plot.gp')
+ f = os.path.join(path, '.results')
return os.path.isfile(f)
- def __init__(self, path):
+ def __init__(self, path, values, tests=None):
self.path = path
+ self.name = os.path.basename(path)
+ self.values = values
+ self.tests = tests
def printThumbLink(self, file):
- for img in dircache.listdir(self.path+'/thumb'):
- print >>file, "<a href='%s/%s'><img src='%s/thumb/%s'></a>" % \
- (urllib.quote(self.path), img, urllib.quote(self.path), img)
-
-def iterDimValues(dimensions):
- idx = [0 for i in xrange(len(dimensions))]
- done=False
- while not done:
- values=[]
- for i in xrange(len(dimensions)):
- values.append(dimensions[i].values()[idx[i]])
- yield values
- done=True
- for i in xrange(len(dimensions)):
- idx[i] += 1
- if idx[i] < len(dimensions[i]):
- done=False
- break
- idx[i] = 0
+ thumb = self.path+'/thumb'
+ try:
+ imgs = [img for img in dircache.listdir(thumb)]
+ except OSError:
+ imgs = [ self.name + ".png" ]
+ for img in imgs:
+ print >>file, "<a href='%s/results.html'><img src='%s/thumb/%s'></a>" % \
+ (urllib.quote(self.path), urllib.quote(self.path), img)
+ def generateHtml(self):
+ html = open(os.path.join(self.path, 'results.html'), "w")
+ title = "CAN gateway timing analysis"
+ cdup = "../"*len(self.values)
+ print >> html, """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
+<meta http-equiv="content-type" content="text/html; charset=utf-8" />
+<title>%s</title>
+<link rel="stylesheet" href="%sstyle.css" type="text/css" />
+</head>
+<body>
+<h1>%s</h1>""" % (title, cdup, title)
+ params = ["%s %s" % (v.dim, v) for v in self.values]
+ print >>html, "Results for:", ", ".join(params)
+ print >>html, "<div class='otherview'><h4>Other results</h4><table>"
+ for d in self.tests.space:
+ links = []
+ for v in d:
+ if v in self.values:
+ links.append("<span class='value current'>%s</span>"%str(v))
+ else:
+ vv = DimValues(self.values)
+ vv.replace(v)
+ try:
+ href = cdup + urllib.quote(self.tests[vv.key()].path+"/results.html")
+ links.append("<span class='value other'><a href='%s'>%s</a></span>"%(href, str(v)))
+ except KeyError:
+ links.append("<span class='value missing'>%s</span>"%str(v))
+ print >>html, "<tr><th>%s</th><td>" % d, " ".join(links), "</td></tr>"
+
+ print >>html, "</table></div>"
+ print >>html, "<div><img src='%s' /></div>" % (self.name+".png")
+ print >>html, "<a href='./'>Raw data</a><br />"
+ print >>html, "<a href='%s'>Script source</a><br />" % (cdup+self.name+".sh.html")
+ print >>html, "<a href='%s'>Back to top</a><br />" % cdup
+
+ html.close()
+
+class Space(list):
+ """List of Dimensions()s (order matters)"""
+ def __init__(self, *dimensions):
+ self.extend(list(dimensions))
+ def path2dimValues(self, path):
+ coordinates = path.split("/")
+ if len(coordinates) != len(self):
+ raise KeyError("The number coordinates do not match the number of dimensions: " + str(coordinates))
+
+ dv = DimValues([DimValue(self[i], coordinates[i]) \
+ for i in xrange(len(coordinates))])
+ return dv
+
+ def iterValues(self):
+ idx = [0 for i in xrange(len(self))]
+ done=False
+ while not done:
+ values=DimValues()
+ for i in xrange(len(self)):
+ values.append(self[i].values()[idx[i]])
+ yield values
+ done=True
+ for i in xrange(len(self)):
+ idx[i] += 1
+ if idx[i] < len(self[i]):
+ done=False
+ break
+ idx[i] = 0
+ def reorder(self, dimValues):
+ reordered = DimValues()
+ for d in self:
+ for v in dimValues:
+ if v.dim == d:
+ reordered.append(v)
+ return reordered
+ def iterDimensionPairs(self):
+ for i in xrange(len(self)):
+ for j in xrange(i+1, len(self)):
+ yield (self[i], self[j])
+ yield (self[j], self[i])
+ def iterRemainingDimensions(self, dimensionPair):
+ for d in self:
+ if d not in dimensionPair:
+ yield d
+
class Tests(dict):
"""Represents all tests organized along several dimensions"""
- def __init__(self, rootpath, *dimensions):
+ def __init__(self, rootpath, space):
dict.__init__(self)
- self.dimensions = dimensions
+ self.space = space
if (rootpath):
self.populate(rootpath)
def getTest(self, key):
- realkey=[]
- for d in self.dimensions:
- for i in key:
- if i.dim == d:
- realkey.append(i.value)
- if len(realkey) != len(self.dimensions):
- raise KeyError("The coordinates in key do not match dimensions")
- return self[tuple(realkey)]
-
- def addTest(self, test, coordinates):
- if len(coordinates) != len(self.dimensions):
- raise KeyError("The number coordinates do not match the number of dimensions: " + str(coordinates))
- self[tuple(coordinates)] = test
- for i in xrange(len(coordinates)):
- self.dimensions[i].addValue(coordinates[i])
+ if len(key) != len(self.space):
+ raise KeyError("The coordinates in key do not match the dimension of the space")
+ realkey = self.space.reorder(key)
+ return self[realkey.key()]
+ def addTest(self, test):
+ self[test.values.key()] = test
+
def populate(self, rootpath):
for root, dirs, files in os.walk(rootpath):
if (root.find(rootpath) == 0):
- coordinates = root[len(rootpath):]
+ path = root[len(rootpath):]
else:
- coordinates = rootpath
+ path = rootpath
if Test.isOnPath(root):
- self.addTest(Test(root), coordinates.split("/"))
- def iterDimensionPairs(self):
- for i in xrange(len(self.dimensions)):
- for j in xrange(i+1, len(self.dimensions)):
- yield (self.dimensions[i], self.dimensions[j])
- yield (self.dimensions[j], self.dimensions[i])
- def iterRemainingDimensions(self, dimensionPair):
- for d in self.dimensions:
- if d not in dimensionPair:
- yield d
+ dv = self.space.path2dimValues(path)
+ self.addTest(Test(root, dv, self))
def generateHtml(self):
- for pair in self.iterDimensionPairs():
- remdims = [d for d in self.iterRemainingDimensions(pair)]
- for vals in iterDimValues(remdims):
- page = Page(pair, remdims, vals, self)
+ for pair in self.space.iterDimensionPairs():
+ remDims = Space(*tuple([d for d in self.space.iterRemainingDimensions(pair)]))
+ for vals in remDims.iterValues():
+ page = Page(pair, vals, self)
+ print page.getName()
page.generate()
try:
os.remove("index.html")
except OSError: pass
os.symlink(page.getName(), "index.html")
+ css = open("style.css", "w")
+ print >>css, """img { border: 0; }
+table { border-collapse: collapse; }
+th, td { border: 1px solid lightgray; padding: 4px;}
+h4 { margin: 0; }
+.otherview { margin: 1ex 0}
+.otherview .value { color: black; padding: 0ex 1ex; -moz-border-radius: 1ex; border-radius: 1ex;}
+.otherview .value a { color: inherit; text-decoration: none; }
+.otherview .other:hover { background: #eee; }
+.otherview .missing { color: gray; }
+.otherview .current { background: #ccc; }
+"""
+ css.close()
+ for test in self.values():
+ print test.path
+ test.generateHtml()
- os.system("source-highlight -d --output-dir=. ../*.sh")
+ os.system("source-highlight -d --output-dir=. ../*.sh > /dev/null")
-class Page:
- def __init__(self, dimPair, dimOther, valsOther, tests):
+class Page(object):
+ def __init__(self, dimPair, valsOther, tests):
self.dimy, self.dimx = dimPair
- self.dimOther = dimOther
- self.valsOther = valsOther
+ self.dimOther = [v.dim for v in valsOther]
+ self.valsOther = tests.space.reorder(valsOther)
self.tests = tests
def getName(self):
- return "%s-vs-%s-%s.html"%(self.dimy.type, self.dimx.type, "-".join([v.value for v in self.valsOther]))
+ return "%s-vs-%s-for-%s.html"%(self.dimy.type, self.dimx.type,
+ "-".join([v.value for v in self.valsOther]))
def generate(self):
html = open(self.getName(), "w")
- title = "CAN gateway timing analysis" + ", ".join([v.dim.name+" "+v.value for v in self.valsOther])
- print >> html, """<html>
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+ title = "CAN gateway timing analysis"
+ print >> html, """<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
+<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>%s</title>
-<style>
-img { border: 0; }
-table { border-collapse: collapse; }
-th, td { border: 1px solid lightgray; padding: 4px;}
-</style>
+<link rel="stylesheet" href="style.css" type="text/css" />
</head>
<body>
<h1>%s</h1>""" % (title, title)
+ params = ["%s %s" % (v.dim, v) for v in self.valsOther]
+ print >>html, "<h3>Results for ", ", ".join(params), "</h3>"
+ print >>html, "<div class='otherview'><h4>Other views</h4>"
+ print >>html, "<table><tr>"
for d in self.dimOther:
- pass
-# print >>html, "View for %s: " % str(ps.pageclass.name)
-# for v in ps.values:
-# print >>html, "<a href='%s-%s.html'>%s</a> | "%(ps.values.type, urllib.quote(v), v)
-# print >>html, "<br>"
-# try:
-# print >>html, d.htmlPreamble()
-# except Exception:
-# pass
-
- print >>html, "<table><thead><tr><td> </td>"
+ print >>html, "<th>%s</th>" % d
+ print >>html, "<td><a href='%s'>→</a> " % \
+ Page((self.dimy, d), self.valsOther - d + self.dimx.getValue(0), self.tests).getName()
+ print >>html, "<a href='%s'>↓</a></td>" % \
+ Page((d, self.dimx), self.valsOther - d + self.dimy.getValue(0), self.tests).getName()
+ links = []
+ print >>html, "<td>"
+ for v in d:
+ if v in self.valsOther:
+ links.append("<span class='value current'>%s</span>"%str(v))
+ else:
+ vv = DimValues(self.valsOther)
+ vv.replace(v)
+ links.append("<span class='value other'><a href='%s'>%s</a></span>"%(urllib.quote(Page((self.dimy, self.dimx), vv, self.tests).getName()), str(v)))
+ print >>html, " ".join(links)
+ print >>html, "</td></tr>"
+ print >>html, "</table></div>"
+
+ print >>html, "<table><thead><tr><td>%s → <br />%s ↓</td>" % (self.dimx.name, self.dimy.name)
for x in self.dimx:
- print >>html, "<th>%s</th>" % x.htmlLabel()
+ print >>html, "<th>%s</th>" % x.htmlTableHeading()
print >>html, "</tr></thead>"
for y in self.dimy:
- print >>html, "<tr><th>%s</th>" % y.htmlLabel()
+ print >>html, "<tr><th>%s</th>" % y.htmlTableHeading()
for x in self.dimx:
print >>html, "<td>"
idx = [x,y]
idx.extend(self.valsOther)
- test = tests.getTest(idx)
- test.printThumbLink(html)
- print >>html, "</td>"
+ try:
+ test = tests.getTest(idx)
+ test.printThumbLink(html)
+ except KeyError:
+ print >>html, "N/A"
+ print >>html, "</td>"
print >>html, "</tr>"
print >> html, """
</table>
if __name__ == "__main__":
os.chdir(sys.argv[1])
- tests = Tests("./", DimensionHostKern(), DimensionKern(), DimensionTraffic(), DimensionTest())
+ os.system("rm *.html")
+ tests = Tests("./", Space(DimensionHostKern(), DimensionKern(), DimensionTraffic(), DimensionLoad(), DimensionTest()))
tests.generateHtml()
sys.exit(0)
COUNT=10000
OPT_TRAFFIC=oneatatime
+OPT_LOAD=none
error() {
echo $1 >&2
while [ $# -gt 0 ]; do
case "$1" in
- -P) OPT_PLOT_DISABLE=1; shift;;
- -p) OPT_PLOT_ONLY=1; shift;;
+ -P|--no-plot) OPT_PLOT_DISABLE=1; shift;;
+ -p|--plot) OPT_PLOT_ONLY=1; shift;;
-X|--no-x11-plot) OPT_NO_X11=1; shift;;
-t) case "$2" in
all|flood|50|oneatatime) OPT_TRAFFIC=$2;;
*) error "Unknown traffic specification: $2";;
esac;
shift 2;;
+ -l) case "$2" in
+ all|none|cpu|eth|can) OPT_LOAD=$2;;
+ *) error "Unknown load specification: $2";;
+ esac;
+ shift 2;;
esac
done
if [[ ! -S $socket ]] || ! ssh -x -a -S $socket root@192.168.2.3 true; then
# Create master connection to speed up subsequenct command.
- # The ssh is put into background and the connection is closed
- # after 10 minutes)
- ssh -f -M -S $socket root@192.168.2.3 sleep 600 > /dev/null 2>&1
+ ssh -N -f -M -S $socket root@192.168.2.3 >/dev/null 2>&1
fi
ssh -x -a -S $socket root@192.168.2.3 "$@"
}
_plot() {
local testname=`basename $0 .sh`
- plot_cmds | sed -e "/set title/ s/[\"']\(.*\)[\"']/\"\1\\\\n($kvers)\"/" > plot.gp
+ plot_cmds | sed -e "/set title/ s/[\"']\(.*\)[\"']/\"\1\\\\n(GW kernel $kvers, Traffic $traffic, Load $load)\"/" > plot.gp
if [[ ! -s plot.gp ]]; then return; fi
if [ -z "$OPT_NO_X11" ]; then
echo "set terminal x11 enhanced; $(< plot.gp)" | gnuplot -persist
traffic_and_length() {
local opts
- case $OPT_TRAFFIC in
+ case $traffic in
flood) opts='';;
50) opts="-p $((2*(44+$1*8)))";;
oneatatime) opts="-o";;
- *) error "Unknown traffic specification"
+ *) error "Unknown traffic specification: $traffic"
esac
echo $opts -l $1
}
+start_load() {
+ case $load in
+ none) ;;
+ cpu) sshgw 'hackbench -g 3 -l 10000' & loadpid=$!;;
+ eth) ping -f -s 60000 -q 192.168.2.3 & loadpid=$!;; # TODO: Generate eth load from another computer
+ can) latester -q -d can1 -i 0x7ff & loadpid=$!;;
+ *) error "Unknown load specification: $load"
+ esac
+}
+
+kill_load() {
+ case $load in
+ none) ;;
+ cpu) kill $loadpid; sshgw "killall -q hackbench || :";;
+ eth) kill $loadpid;;
+ can) kill $loadpid;;
+ *) error "Unknown load specification: $load"
+ esac
+}
+
+_measure() {
+ # Remove data from the last measurement
+ rm -rf *
+ touch .results
+ cat > plot.sh <<-EOF
+ #!/bin/bash
+ export kvers=$kvers
+ export hostkvers=$hostkvers
+ export traffic=$traffic
+ export load=$load
+ cd \$(dirname \$0)/$(dirname $script)
+ exec ./$(basename $script) --plot "\$@"
+ EOF
+ chmod +x plot.sh
+ # Kill load generators left possibly from the past runs
+ killall -q ping || :
+ killall -q latester || :
+ sshgw 'killall -q hackbench || :'
+ # Set can interfaces up
+ sshgw 'for i in 0 1; do ip link show dev can$i|grep -q UP || ip link set can$i up type can bitrate 1000000; done'
+ # Delete all vcan interfaces
+ sshgw 'for dev in $(ip l|grep -o vcan[^:]\\+); do ip link del dev $dev; done'
+ # Reset priorities
+ sshgw 'if pid=`pidof irq/145-can0`; then chrt -p -f 50 $pid > /dev/null; fi'
+ sshgw 'if pid=`pidof irq/146-can1`; then chrt -p -f 50 $pid > /dev/null; fi'
+ sshgw 'if pid=`pidof sirq-net-rx/0`; then chrt -p -f 49 $pid > /dev/null; fi'
+ sshgw 'if pid=`pidof sirq-net-tx/0`; then chrt -p -f 49 $pid > /dev/null; fi'
+ # Set the length of qdisc queue to avoid ENOBUFS errors
+ ifconfig can0 txqueuelen 200
+ ifconfig can1 txqueuelen 200
+ cleanupgw
+
+ type prepare >/dev/null 2>&1 && prepare || :
+ start_load
+ main
+ kill_load
+}
+
+
_run() {
- if [[ ! "$OPT_PLOT_ONLY" ]]
- then kernel_versions=$(sshgw uname -r)
- else kernel_versions= # TODO $(ls results/by-kern)
- fi
if [[ $OPT_TRAFFIC = all ]]
then traffics="flood 50 oneatatime"
- else traffics=$OPT_TRAFFIC
+ else traffics=${traffic:-$OPT_TRAFFIC}
+ fi
+ if [[ $OPT_LOAD = all ]]
+ then loads="none cpu eth" # TODO: can
+ else loads=${load:-$OPT_LOAD}
fi
- for OPT_TRAFFIC in $traffics; do
- for kvers in $kernel_versions; do
- dir="results/host-$(uname -r)/$kvers/$OPT_TRAFFIC/$(basename $0 .sh)"
+ hostkvers=${hostkvers:-host-$(uname -r)}
+ kvers=${kvers:-$(sshgw uname -r)}
+ test=$(basename $0 .sh)
+ for load in $loads; do
+ for traffic in $traffics; do
+ dir="results/$hostkvers/$kvers/$traffic/$load/$test"
mkdir -p $dir
- script=$PWD/$0
+ script=$(echo $dir | sed -e 's/[^/]*/../g')/${test}.sh
cd $dir
- echo "Working directory: $dir"
if [[ ! "$OPT_PLOT_ONLY" ]]; then
- # Remove data from the last measurement
- rm -rf *
- # Set can interfaces up
- sshgw 'for i in 0 1; do ip link show dev can$i|grep -q UP || ip link set can$i up type can bitrate 1000000; done'
- # Delete all vcan interfaces
- sshgw 'for dev in $(ip l|grep -o vcan[^:]\\+); do ip link del dev $dev; done'
- # Reset priorities
- sshgw 'if pid=`pidof irq/145-can0`; then chrt -p -f 50 $pid > /dev/null; fi'
- sshgw 'if pid=`pidof irq/146-can1`; then chrt -p -f 50 $pid > /dev/null; fi'
- sshgw 'if pid=`pidof sirq-net-rx/0`; then chrt -p -f 49 $pid > /dev/null; fi'
- sshgw 'if pid=`pidof sirq-net-tx/0`; then chrt -p -f 49 $pid > /dev/null; fi'
- # Set the length of qdisc queue to avoid ENOBUFS errors
- ifconfig can0 txqueuelen 200
- cleanupgw
-
- main
- cp $script .
+ echo "Working directory: $dir"
+ _measure
fi
if [[ ! "$OPT_PLOT_DISABLE" ]]; then
_plot
fi
- cd -
+ cd - > /dev/null
done
done
}
LATESTER_OPTS="-d can0 -d can1 -d can2 $(traffic_and_length 8) -c $COUNT"
sshgw cangw -A -s can0 -d can1
- latester $LATESTER_OPTS -h hist.dat -f time.dat
+ latester $LATESTER_OPTS -n nop
cleanupgw
sshgw cangw -A -s can0 -d can1 -m OR:D:0.0.0000123400000000 \
-m SET:IL:123.8.0000000000000000
- latester $LATESTER_OPTS -h hist-mod.dat -f time-mod.dat
+ latester $LATESTER_OPTS -n mod
cleanupgw
sshgw cangw -A -s can0 -d can1 -m AND:ID:0.0.ffff000000000000 \
-m OR:D:0.0.0000123400000000 \
-m XOR:D:0.0.0000000012345678 \
-m SET:IL:123.8.0000000000000000
- latester $LATESTER_OPTS -h hist-mod2.dat -f time-mod2.dat
+ latester $LATESTER_OPTS -n mod2
cleanupgw
sshgw cangw -A -s can0 -d can1 -m AND:ID:0.0.ffff000000000000 \
-m XOR:D:0.0.0000000012345678 \
-m SET:IL:123.8.0000000000000000 \
-x 0:6:7:0
- latester $LATESTER_OPTS -h hist-modcsxor.dat -f time-modcsxor.dat
+ latester $LATESTER_OPTS -n modcsxor
cleanupgw
sshgw cangw -A -s can0 -d can1 -m AND:ID:0.0.ffff000000000000 \
-m OR:D:0.0.0000123400000000 \
-m XOR:D:0.0.0000000012345678 \
-m SET:IL:123.8.0000000000000000 \
-p 3 -c 0:6:7:0:0:00D013C326F635E54C9C5F8F6ABA79A998488B5BBE6EAD7DD404C717F222E13183539040A575B666CF1FDC0CE939FA2A1BCB08D83DED2EFE5787449471A162B2B565A67693438050F929EA3ADF0FCC1C2DFD3EEE0BDB18C861B172A24797548436E625F510C003D37AAA69B95C8C4F9FAE7EBD6D88589B4BE232F121C414D707D909CA1AFF2FEC3C95458656B363A0704191528267B774A40DDD1ECE2BFB38E85A8A49997CAC6FBF16C605D530E023F3C212D101E434F7278E5E9D4DA878BB6B6CBC7FAF4A9A598920F033E306D615C5F424E737D202C111B868AB7B9E4E8D5DEF3FFC2CC919DA0AA373B0608555964677A764B4518142923BEB28F81DCD0EDE
- latester $LATESTER_OPTS -h hist-modcscrc8.dat -f time-modcscrc8.dat
+ latester $LATESTER_OPTS -n modcscrc8
}
plot_cmds() {
set title "Single GW job for all messages with modifications, 8 byte messages"
set logscale y
set grid
-set xlabel "Time [{/Symbol m}s]"
+set xlabel "Time [ms]"
set ylabel "Latency profile [messages]"
plot [0:600] [1:$COUNT] \
- "hist.dat" with lp lt 1 title "No modifications", \
- "hist-mod.dat" with lp lt 2 title "Two modifications", \
- "hist-mod2.dat" with lp lt 3 title "Four modifications", \
- "hist-modcsxor.dat" with lp lt 4 title "Four modifications and XOR checksum",\
- "hist-modcscrc8.dat" with lp lt 5 title "Four modifications and CRC8 checksum"
+ "nop-hist.txt" with lp lt 1 title "No modifications", \
+ "mod-hist.txt" with lp lt 2 title "Two modifications", \
+ "mod2-hist.txt" with lp lt 3 title "Four modifications", \
+ "modcsxor-hist.txt" with lp lt 4 title "Four modifications and XOR checksum",\
+ "modcscrc8-hist.txt" with lp lt 5 title "Four modifications and CRC8 checksum"
EOF
}
. lib.sh
main() {
- :
+ ln -s ../nop-highprio/*.txt .
}
plot_cmds() {
set title "Single GW job for all messages, no modifications, high (soft)irq task priority"
set grid
set xlabel "Time [s]"
-set ylabel "Latency [{/Symbol m}s]"
+set ylabel "Latency [ms]"
plot [:] [:500] \
- "../nop-highprio/time2.dat" using 2:(1000000*$14) with points title "2 byte messages", \
- "../nop-highprio/time4.dat" using 2:(1000000*$14) with points title "4 byte messages", \
- "../nop-highprio/time6.dat" using 2:(1000000*$14) with points title "6 byte messages", \
- "../nop-highprio/time8.dat" using 2:(1000000*$14) with points title "8 byte messages"
+ "len2-msgs.txt" using 2:(1000*$14) with points title "2 byte messages", \
+ "len4-msgs.txt" using 2:(1000*$14) with points title "4 byte messages", \
+ "len6-msgs.txt" using 2:(1000*$14) with points title "6 byte messages", \
+ "len8-msgs.txt" using 2:(1000*$14) with points title "8 byte messages"
EOF
}
main() {
sshgw cangw -A -s can0 -d can1
- sshgw 'chrt -p -f 99 `pidof irq/145-can0` || :'
- sshgw 'chrt -p -f 99 `pidof irq/146-can1` || :'
- sshgw 'chrt -p -f 98 `pidof sirq-net-rx/0` || :'
- sshgw 'chrt -p -f 98 `pidof sirq-net-tx/0` || :'
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 2) -h hist2.dat -f time2.dat
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 4) -h hist4.dat -f time4.dat
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 6) -h hist6.dat -f time6.dat
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 8) -h hist8.dat -f time8.dat
+ sshgw 'if pid=`pidof irq/145-can0`; then chrt -p -f 99 $pid; fi'
+ sshgw 'if pid=`pidof irq/146-can1`; then chrt -p -f 99 $pid; fi'
+
+ # Unfortunately, we must also increase FEC (Ethernet) IRQ priority
+ # to be above softirq. Otherwise the system crashes with eth load.
+ sshgw 'if pid=`pidof irq/192-mpc52xx`; then chrt -p -f 99 $pid; fi'
+ sshgw 'if pid=`pidof irq/193-mpc52xx`; then chrt -p -f 99 $pid; fi'
+
+ sshgw 'if pid=`pidof sirq-net-rx/0`; then chrt -p -f 98 $pid; fi'
+ sshgw 'if pid=`pidof sirq-net-tx/0`; then chrt -p -f 98 $pid; fi'
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 2) -n len2
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 4) -n len4
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 6) -n len6
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 8) -n len8
}
plot_cmds() {
set title "Single GW job for all messages, no modifications, high (soft)irq task priority"
set logscale y
set grid
-set xlabel "Time [{/Symbol m}s]"
+set xlabel "Time [ms]"
set ylabel "Latency profile [messages]"
plot [0:600] [1:$COUNT] \
- "hist2.dat" with lp lt 1 title "2 byte messages", \
- "hist4.dat" with lp lt 2 title "4 byte messages", \
- "hist6.dat" with lp lt 3 title "6 byte messages", \
- "hist8.dat" with lp lt 4 title "8 byte messages"
+ "len2-hist.txt" with lp lt 1 title "2 byte messages", \
+ "len4-hist.txt" with lp lt 2 title "4 byte messages", \
+ "len6-hist.txt" with lp lt 3 title "6 byte messages", \
+ "len8-hist.txt" with lp lt 4 title "8 byte messages"
EOF
}
. lib.sh
main() {
- :
+ ln -s ../nop-highprio/*.txt .
}
plot_cmds() {
set title "Single GW job for all messages, no modifications"
set grid
set xlabel "Time [s]"
-set ylabel "Latency [{/Symbol m}s]"
+set ylabel "Latency [ms]"
plot [:] [:500] \
- "../nop/time2.dat" using 2:(1000000*$14) with points title "2 byte messages", \
- "../nop/time4.dat" using 2:(1000000*$14) with points title "4 byte messages", \
- "../nop/time6.dat" using 2:(1000000*$14) with points title "6 byte messages", \
- "../nop/time8.dat" using 2:(1000000*$14) with points title "8 byte messages"
+ "len2-msgs.txt" using 2:(1000*$14) with points title "2 byte messages", \
+ "len4-msgs.txt" using 2:(1000*$14) with points title "4 byte messages", \
+ "len6-msgs.txt" using 2:(1000*$14) with points title "6 byte messages", \
+ "len8-msgs.txt" using 2:(1000*$14) with points title "8 byte messages"
EOF
}
main() {
sshgw cangw -A -s can0 -d can1
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 2) -h hist2.dat -f time2.dat
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 4) -h hist4.dat -f time4.dat
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 6) -h hist6.dat -f time6.dat
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 8) -h hist8.dat -f time8.dat
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 2) -n len2
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 4) -n len4
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 6) -n len6
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 8) -n len8
}
plot_cmds() {
set title "Single GW job for all messages, no modifications"
set logscale y
set grid
-set xlabel "Time [{/Symbol m}s]"
+set xlabel "Time [ms]"
set ylabel "Latency profile [messages]"
plot [0:600] [1:$COUNT] \
- "hist2.dat" with lp lt 1 title "2 byte messages", \
- "hist4.dat" with lp lt 2 title "4 byte messages", \
- "hist6.dat" with lp lt 3 title "6 byte messages", \
- "hist8.dat" with lp lt 4 title "8 byte messages"
+ "len2-hist.txt" with lp lt 1 title "2 byte messages", \
+ "len4-hist.txt" with lp lt 2 title "4 byte messages", \
+ "len6-hist.txt" with lp lt 3 title "6 byte messages", \
+ "len8-hist.txt" with lp lt 4 title "8 byte messages"
EOF
}
. lib.sh
main() {
- latester -d can0 -d can1 -c $COUNT $(traffic_and_length 2) -h hist2.dat -f time2.dat
- latester -d can0 -d can1 -c $COUNT $(traffic_and_length 4) -h hist4.dat -f time4.dat
- latester -d can0 -d can1 -c $COUNT $(traffic_and_length 6) -h hist6.dat -f time6.dat
- latester -d can0 -d can1 -c $COUNT $(traffic_and_length 8) -h hist8.dat -f time8.dat
+ latester -d can0 -d can1 -c $COUNT $(traffic_and_length 2) -n len2
+ latester -d can0 -d can1 -c $COUNT $(traffic_and_length 4) -n len4
+ latester -d can0 -d can1 -c $COUNT $(traffic_and_length 6) -n len6
+ latester -d can0 -d can1 -c $COUNT $(traffic_and_length 8) -n len8
}
plot_cmds() {
set title "No GW, two interfaces in PC"
set logscale y
set grid
-set xlabel "Time [{/Symbol m}s]"
+set xlabel "Time [ms]"
set ylabel "Latency profile [messages]"
-plot [0:600] [1:$COUNT] \
- "hist2.dat" with lp lt 1 title "2 byte messages", \
- "hist4.dat" with lp lt 2 title "4 byte messages", \
- "hist6.dat" with lp lt 3 title "6 byte messages", \
- "hist8.dat" with lp lt 4 title "8 byte messages"
+plot [0:] [1:$COUNT] \
+ "len2-hist.txt" with lp lt 1 title "2 byte messages", \
+ "len4-hist.txt" with lp lt 2 title "4 byte messages", \
+ "len6-hist.txt" with lp lt 3 title "6 byte messages", \
+ "len8-hist.txt" with lp lt 4 title "8 byte messages"
EOF
}
main() {
pid=$(sshgw 'chrt -f 90 candump -s2 -b can1 can0 & echo $!')
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 2) -h uhist2.dat -f utime2.dat
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 8) -h uhist8.dat -f utime8.dat
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 2) -n user2
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 8) -n user8
sshgw kill $pid
sshgw cangw -A -s can0 -d can1
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 2) -h khist2.dat -f ktime2.dat
- latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 8) -h khist8.dat -f ktime8.dat
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 2) -n kern2
+ latester -d can0 -d can1 -d can2 -c $COUNT $(traffic_and_length 8) -n kern8
}
plot_cmds() {
set title "Kernel vs. userspace GW, no modifications"
set logscale y
set grid
-set xlabel "Time [{/Symbol m}s]"
+set xlabel "Time [ms]"
set ylabel "Latency profile [messages]"
plot [0:] [1:$COUNT] \
- "khist2.dat" with lp lt 1 title "Kernel GW, 2 byte messages", \
- "khist8.dat" with lp lt 2 title "Kernel GW, 8 byte messages", \
- "uhist2.dat" with lp lt 1 title "Userspace GW, 2 byte messages", \
- "uhist8.dat" with lp lt 2 title "Userspace GW, 8 byte messages"
+ "kern2-hist.txt" with lp lt 1 title "Kernel GW, 2 byte messages", \
+ "kern8-hist.txt" with lp lt 2 title "Kernel GW, 8 byte messages", \
+ "user2-hist.txt" with lp lt 1 title "Userspace GW, 2 byte messages", \
+ "user8-hist.txt" with lp lt 2 title "Userspace GW, 8 byte messages"
EOF
}
done
sshgw cangw -A -s vcan0 -d can1 -f $(printf %x:C00007FF $i)
- latester -d can0 -d can1 -d can2 -c $COUNT -i 0 -h hist-$i.dat -f time-$i.dat
+ latester -d can0 -d can1 -d can2 -c $COUNT -i 0 -n hops$i
done
sshgw ip link del dev vcan0
}
set title "Chained GW jobs on a signle VCAN interface"
set logscale y
set grid
-set xlabel "Time [{/Symbol m}s]"
+set xlabel "Time [ms]"
set ylabel "Latency profile [messages]"
plot [0:10000] [1:$COUNT] \\
EOF
lt=2
for i in $numjobs; do
- echo_plot "\"hist-$i.dat\" with lp lt $lt title \"$((i+2)) GW jobs, 1 vcan\""
+ echo_plot "\"hops$i-hist.txt\" with lp lt $lt title \"$((i+2)) GW jobs, 1 vcan\""
lt=$((lt+1))
done
}
done
sshgw cangw -A -s $lastif -d can1 -f $(printf %x:C00007FF $((i-1)))
- latester -d can0 -d can1 -d can2 -c $COUNT -i 0 $(traffic_and_length 2) -h hist-$i.dat -f time-$i.dat
+ latester -d can0 -d can1 -d can2 -c $COUNT -i 0 $(traffic_and_length 2) -n hops$i
done
}
set title "Chained GW jobs on multiple VCAN interfaces"
set logscale y
set grid
-set xlabel "Time [{/Symbol m}s]"
+set xlabel "Time [ms]"
set ylabel "Latency profile [messages]"
plot [0:1000] [1:$COUNT] \\
EOF
lt=1
for i in $numjobs; do
- echo_plot "\"hist-$i.dat\" with lp lt $lt title \"$i GW jobs, $((i-1)) vcans\""
+ echo_plot "\"hops$i-hist.txt\" with lp lt $lt title \"$i GW jobs, $((i-1)) vcans\""
lt=$((lt+1))
done
}
cum = sum;
for (i = 0; i < h->allocated; i++) {
if (h->data[i] != 0) {
- if (!getenv("CANPING_MS"))
- fprintf(f, "%d %lld\n", i*h->resolution, cum);
- else
- fprintf(f, "%g %lld\n", 1e-3*(i*h->resolution), cum);
+ fprintf(f, "%g %lld\n", 1e-3*(i*h->resolution), cum);
}
cum -= h->data[i];
}
unsigned timeout_ms;
unsigned count;
unsigned oneattime;
- FILE *file;
- FILE *histogram;
+ char *name;
int length;
int userhist;
+ int quiet;
+
+ /* Temporary variables */
+ FILE *f_msgs;
+ FILE *f_hist;
+ FILE *f_stat;
};
struct options opt = {
unsigned enobufs;
unsigned overrun;
unsigned lost;
+ struct timespec tic, tac;
+ unsigned timeouts;
} stats;
int num_interfaces = 0;
#define S(ts) tstamp_str(local, &ts)
#define DIFF(a, b) (timespec_subtract(&diff, &b, &a), S(diff))
- if (num_interfaces == 2)
+ switch (num_interfaces) {
+ case 2:
fprintf(f, "%ld: %s %s -> %s (%s) %s = %s (%s)\n",
num, S(mi->ts_sent), sent, S(mi->ts_rx_final_kern), S(mi->ts_rx_final), received,
DIFF(mi->ts_sent, mi->ts_rx_final_kern),
DIFF(mi->ts_sent, mi->ts_rx_final));
- else
+ break;
+ case 3:
fprintf(f, "%ld: %s %s -> %s (%s) -> %s (%s) %s = %s (%s), %s (%s)\n",
num, S(mi->ts_sent), sent,
S(mi->ts_rx_onwire_kern), S(mi->ts_rx_onwire),
DIFF(mi->ts_sent, mi->ts_rx_onwire),
DIFF(mi->ts_rx_onwire_kern, mi->ts_rx_final_kern),
DIFF(mi->ts_rx_onwire, mi->ts_rx_final));
+ break;
+ }
#undef S
#undef DIFF
num++;
talloc_free (local);
}
-int msg_info_store(FILE *f, struct msg_info *mi)
-{
- struct timespec diff;
- void *local = talloc_new (NULL);
- static long num = 0;
-
-#define S(ts) tstamp_str(local, &ts)
-#define DIFF(a, b) (timespec_subtract(&diff, &b, &a), S(diff))
-
- if (num_interfaces == 2)
- fprintf(f, "%ld %d %d %s\n",
- num, mi->id, mi->length,
- DIFF(mi->ts_sent, mi->ts_rx_final_kern));
- else
- fprintf(f, "%ld %d %d %s\n",
- num, mi->id, mi->length,
- DIFF(mi->ts_rx_onwire_kern, mi->ts_rx_final_kern));
-#undef S
-#undef DIFF
- talloc_free (local);
-}
-
-
/* Subtract the `struct timespec' values X and Y, storing the result in
RESULT. Return 1 if the difference is negative, otherwise 0. */
static inline unsigned get_msg_latency_us(struct msg_info *mi)
{
struct timespec diff;
- if (num_interfaces == 3)
+ switch (num_interfaces) {
+ case 3:
if (opt.userhist)
timespec_subtract(&diff, &mi->ts_rx_final, &mi->ts_rx_onwire);
else
timespec_subtract(&diff, &mi->ts_rx_final_kern, &mi->ts_rx_onwire_kern);
- else
+ break;
+ case 2:
if (opt.userhist)
timespec_subtract(&diff, &mi->ts_rx_final, &mi->ts_sent);
else
timespec_subtract(&diff, &mi->ts_rx_final_kern, &mi->ts_sent);
+ break;
+ default:
+ return 0;
+ }
return diff.tv_sec * 1000000 + diff.tv_nsec/1000;
}
ret = write(socket, &frame, sizeof(frame));
trace_off(ret);
- if (ret == -1)
+ if (ret == -1 || num_interfaces == 1)
msg_info_free(mi);
return ret;
}
mi->ts_rx_final = ts_user;
mi->received = frame;
- if (opt.histogram)
- histogram_add(&histogram, get_msg_latency_us(mi));
+ histogram_add(&histogram, get_msg_latency_us(mi));
ret = write(completion_pipe[1], &mi, sizeof(mi));
if (ret == -1)
struct timespec timeout;
struct sockaddr_can addr;
sigset_t set;
+ int consecutive_timeouts = 0;
MEMSET_ZERO(pfd);
if (opt.oneattime)
SEND();
+ get_tstamp(&stats.tic);
+
while (!finish_flag &&
(opt.count == 0 || count < opt.count || msg_in_progress != 0)) {
SEND();
}
} else {
- error(1, 0, "poll timeout");
+ /* Lost message - send a new one */
+ stats.timeouts++;
+ consecutive_timeouts++;
+ if (consecutive_timeouts < 10)
+ SEND();
+ else /* Something is really broken */
+ finish_flag = 1;
}
break;
default: // Event
i = (num_interfaces == 2) ? 1 : 2;
if (pfd[i].revents != 0) {
+ consecutive_timeouts = 0;
process_final_rx(pfd[i].fd);
msg_in_progress--;
pfd[i].revents = 0;
}
}
+ get_tstamp(&stats.tac);
+
for (i=0; i<num_interfaces; i++)
close(pfd[i].fd);
{ "timeout",'t', POPT_ARG_INT|POPT_ARGFLAG_SHOW_DEFAULT, &opt.timeout_ms,0, "Timeout when period is zero", "ms"},
{ "oneattime",'o', POPT_ARG_NONE, &opt.oneattime,0, "Send the next message only when the previous was finally received"},
{ "verbose",'v', POPT_ARG_NONE, NULL, 'v', "Send the next message only when the previous was finally received"},
- { "file", 'f', POPT_ARG_STRING, NULL, 'f', "File where to store results", "filename"},
- { "histogram", 'h', POPT_ARG_STRING, NULL, 'h', "Store histogram in file", "filename"},
+ { "name", 'n', POPT_ARG_STRING, &opt.name, 0, "Prefix of the generated files"},
{ "length", 'l', POPT_ARG_INT|POPT_ARGFLAG_SHOW_DEFAULT, &opt.length, 0, "The length of generated messages", "bytes"},
{ "userhist", 'u', POPT_ARG_NONE, &opt.userhist, 0, "Generate histogram from userspace timestamps"},
+ { "quiet", 'q', POPT_ARG_NONE, &opt.quiet, 0, "Do not print progress and statistics"},
POPT_AUTOHELP
{ NULL, 0, 0, NULL, 0 }
};
{
int c;
poptContext optCon; /* context for parsing command-line options */
+ void *local = talloc_new (NULL);
optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
//poptSetOtherOptionHelp(optCon, "[OPTIONS]* <port>");
case 'd':
num_interfaces++;
break;
- case 'f':
- opt.file = fopen(poptGetOptArg(optCon), "w");
- if (!opt.file)
- error(1, errno, "fopen: %s", poptGetOptArg(optCon));
- break;
- case 'h':
- opt.histogram = fopen(poptGetOptArg(optCon), "w");
- if (!opt.histogram)
- error(1, errno, "fopen: %s", poptGetOptArg(optCon));
- break;
}
}
if (c < -1)
poptBadOption(optCon, POPT_BADOPTION_NOALIAS),
poptStrerror(c));
- if (num_interfaces < 2 || num_interfaces > 3)
- error(1, 0, "-d option must be given exactly 2 or 3 times");
+ if (num_interfaces < 1 || num_interfaces > 3)
+ error(1, 0, "-d option must only be given one, two or three times");
if (opt.oneattime && opt.period_us)
error(1, 0, "oneattime and period cannot be specified at the same time");
- poptFreeContext(optCon);
+ if (opt.name) {
+ char *f = talloc_asprintf(local, "%s-msgs.txt", opt.name);
+ opt.f_msgs = fopen(f, "w");
+ if (!opt.f_msgs)
+ error(1, errno, "fopen: %s", f);
+ }
+
+ if (opt.name) {
+ char *f = talloc_asprintf(local, "%s-hist.txt", opt.name);
+ opt.f_hist = fopen(f, "w");
+ if (!opt.f_hist)
+ error(1, errno, "fopen: %s", f);
+ }
+ if (opt.name) {
+ char *f = talloc_asprintf(local, "%s-stat.txt", opt.name);
+ opt.f_stat = fopen(f, "w");
+ if (!opt.f_stat)
+ error(1, errno, "fopen: %s", f);
+ }
+
+ poptFreeContext(optCon);
+ talloc_free(local);
return 0;
}
void print_progress()
{
- printf("\rSent %5d, in progress %5d", count, msg_in_progress);
- fflush(stdout);
+ if (! opt.quiet) {
+ if (num_interfaces > 1)
+ printf("\rSent %5d, in progress %5d", count, msg_in_progress);
+ else
+ printf("\rSent %5d", count);
+ fflush(stdout);
+ }
}
int main(int argc, const char *argv[])
for (i=0; i<MAX_INFOS; i++)
msg_infos[i].id = -1;
- if (opt.histogram) {
- histogram_init(&histogram, 5000000, 1);
- }
+ histogram_init(&histogram, 5000000, 1);
ret = pipe(completion_pipe);
if (ret == -1)
ret = read(completion_pipe[0], &mi, sizeof(mi));
if (ret < sizeof(mi))
error(1, errno, "read completion returned %d", ret);
- if (opt.file)
- msg_info_print(opt.file, mi);
+ msg_info_print(opt.f_msgs, mi);
msg_info_free(mi);
completed++;
}
}
}
print_progress();
- printf("\n");
+ if (!opt.quiet)
+ printf("\n");
stats.lost = msg_in_progress;
close(completion_pipe[0]);
close(completion_pipe[1]);
- if (opt.histogram) {
- histogram_fprint(&histogram, opt.histogram);
- fclose(opt.histogram);
- }
- if (opt.file)
- fclose(opt.file);
+ histogram_fprint(&histogram, opt.f_hist);
+ fclose(opt.f_hist);
+ fclose(opt.f_msgs);
+
- if (stats.overrun)
+ fprintf(opt.f_stat, "cmdline='");
+ for (i=0; i<argc; i++)
+ fprintf(opt.f_stat, "%s%s", argv[i], i < argc-1 ? " " : "");
+ fprintf(opt.f_stat, "'\n");
+
+ timespec_subtract(&diff, &stats.tac, &stats.tic);
+ fprintf(opt.f_stat, "duration=%s # seconds\n", tstamp_str(NULL, &diff));
+
+ fprintf(opt.f_stat, "sent=%d\n", count);
+ fprintf(opt.f_stat, "overrun=%d\n", stats.overrun);
+ if (stats.overrun && !opt.quiet)
printf("overrun=%d\n", stats.overrun);
- if (stats.enobufs)
+ fprintf(opt.f_stat, "enobufs=%d\n", stats.enobufs);
+ if (stats.enobufs && !opt.quiet)
printf("enobufs=%d\n", stats.enobufs);
- if (stats.lost)
+ fprintf(opt.f_stat, "lost=%d\n", stats.lost);
+ if (stats.lost && !opt.quiet)
printf("lost=%d\n", stats.lost);
+ fprintf(opt.f_stat, "timeouts=%d\n", stats.timeouts);
+ if (stats.timeouts && !opt.quiet)
+ printf("timeouts=%d\n", stats.timeouts);
+
+ fclose(opt.f_stat);
return 0;
}
--- /dev/null
+/*
+ * setSerialSignal v0.1 9/13/01
+ * www.embeddedlinuxinterfacing.com
+ *
+ *
+ * The original location of this source is
+ * http://www.embeddedlinuxinterfacing.com/chapters/06/setSerialSignal.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Library General Public License as
+ * published by the Free Software Foundation; either version 2 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/* setSerialSignal
+ * setSerialSignal sets the DTR and RTS serial port control signals.
+ * This program queries the serial port status then sets or clears
+ * the DTR or RTS bits based on user supplied command line setting.
+ *
+ * setSerialSignal clears the HUPCL bit. With the HUPCL bit set,
+ * when you close the serial port, the Linux serial port driver
+ * will drop DTR (assertion level 1, negative RS-232 voltage). By
+ * clearing the HUPCL bit, the serial port driver leaves the
+ * assertion level of DTR alone when the port is closed.
+ */
+
+/*
+gcc -o setSerialSignal setSerialSignal.c
+*/
+
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <termios.h>
+
+/* we need a termios structure to clear the HUPCL bit */
+struct termios tio;
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ int status;
+
+ if (argc != 4)
+ {
+ printf("Usage: setSerialSignal port DTR RTS\n");
+ printf("Usage: setSerialSignal /dev/ttyS0|/dev/ttyS1 0|1 0|1\n");
+ exit( 1 );
+ }
+
+ if ((fd = open(argv[1],O_RDWR)) < 0)
+ {
+ printf("Couldn't open %s\n",argv[1]);
+ exit(1);
+ }
+ tcgetattr(fd, &tio); /* get the termio information */
+ tio.c_cflag &= ~HUPCL; /* clear the HUPCL bit */
+ tcsetattr(fd, TCSANOW, &tio); /* set the termio information */
+
+ ioctl(fd, TIOCMGET, &status); /* get the serial port status */
+
+ if ( argv[2][0] == '1' ) /* set the DTR line */
+ status &= ~TIOCM_DTR;
+ else
+ status |= TIOCM_DTR;
+
+ if ( argv[3][0] == '1' ) /* set the RTS line */
+ status &= ~TIOCM_RTS;
+ else
+ status |= TIOCM_RTS;
+
+ ioctl(fd, TIOCMSET, &status); /* set the serial port status */
+
+ close(fd); /* close the device file */
+}
+