Commit 4aebf706 authored by Reto Da Forno's avatar Reto Da Forno
Browse files

wip

parent a39ac2b4
......@@ -266,9 +266,18 @@ def start_test(testid, cur, cn, obsdict_key, obsdict_id):
logger.debug("Got start time wish for test from database: %s" % starttime)
logger.debug("Got end time wish for test from database: %s" % stoptime)
# Check whether datatrace debug feature is used
datatrace_used = False
cur.execute("SELECT serv_tests_key FROM `tbl_serv_tests` WHERE (`serv_tests_key` = %s) AND (`testconfig_xml` LIKE '%%<dataTraceConf>%%')" % testid)
ret = cur.fetchone()
if ret:
datatrace_used = True
logger.debug("Use of data trace detected.")
# Image processing ---
# Get all images from the database:
imagedict_key = {}
symtable = {}
sql_image = """ SELECT `t`.`binary`, `m`.`observer_fk`, `m`.`node_id`, LOWER(`a`.`architecture`), `t`.`serv_targetimages_key`, LOWER(`p`.`name`) AS `platname`, `a`.`core` AS `core`
FROM `tbl_serv_targetimages` AS `t`
LEFT JOIN `tbl_serv_map_test_observer_targetimages` AS `m`
......@@ -308,6 +317,10 @@ def start_test(testid, cur, cn, obsdict_key, obsdict_id):
logger.debug("Found %s target architecture on platform %s for observer ID %s (node ID to be used: %s)." % (arch, platname, str(obs_id), str(node_id)))
# get symbols table if necessary
if datatrace_used and not obs_id in symtable: # allow only one entry per observer
symtable[obs_id] = flocklab.extract_variables_from_symtable(flocklab.get_symtable_from_binary(imagepath))
# binary patching
if (node_id != None):
# set node ID
......@@ -369,7 +382,7 @@ def start_test(testid, cur, cn, obsdict_key, obsdict_id):
tree = lxml.etree.fromstring(bytes(bytearray(ret[0], encoding = 'utf-8')), parser)
ns = {'d': flocklab.config.get('xml', 'namespace')}
logger.debug("Got XML from database.")
# Create XML files ---
# Create XML files for observers ---
# Create an empty XML config file for every observer used and organize them in a dictionary:
xmldict_key = {}
for obs_key, obs_id, obs_ether in obsdict_key.values():
......@@ -448,19 +461,35 @@ def start_test(testid, cur, cn, obsdict_key, obsdict_id):
logger.debug("No <serialConf> found, not using serial service.")
# debugConf ---
srconfs = tree.xpath('//d:debugConf', namespaces=ns)
if srconfs:
for srconf in srconfs:
obsids = srconf.xpath('d:obsIds', namespaces=ns)[0].text.strip().split()
dbgconfs = tree.xpath('//d:debugConf', namespaces=ns)
if dbgconfs:
for dbgconf in dbgconfs:
obsids = dbgconf.xpath('d:obsIds', namespaces=ns)[0].text.strip().split()
xmlblock = "<obsDebugConf>\n"
remoteIp = srconf.xpath('d:remoteIp', namespaces=ns)
remoteIp = dbgconf.xpath('d:remoteIp', namespaces=ns)
if remoteIp:
remoteIp = remoteIp[0].text.strip()
xmlblock += "\t<remoteIp>%s</remoteIp>\n" % (remoteIp)
gdbPort = srconf.xpath('d:gdbPort', namespaces=ns)
gdbPort = dbgconf.xpath('d:gdbPort', namespaces=ns)
if gdbPort:
gdbPort = gdbPort[0].text.strip()
xmlblock += "\t<gdbPort>%s</gdbPort>\n" % (gdbPort)
dwtconfs = dbgconf.xpath('d:dataTraceConf', namespaces=ns)
for dwtconf in dwtconfs:
var = dwtconf.xpath('d:variable', namespaces=ns)[0].text.strip()
# convert variable name to address
obskey = int(float(obsids[0]))
if obskey in symtable:
if var in symtable[obskey]:
logger.debug("Variable %s replaced with address 0x%x." % (var, symtable[obskey][var][0]))
var = "0x%x" % symtable[obskey][var][0]
else:
logger.warning("Variable %s not found in symbol table." % var)
continue
else:
logger.debug("Key %u not found in symbol table." % (obskey))
mode = dwtconf.xpath('d:mode', namespaces=ns)[0].text.strip()
xmlblock += "\t<dataTraceConf>\n\t\t<variable>%s</variable>\n\t\t<mode>%s</mode>\n\t</dataTraceConf>\n" % (var, mode)
xmlblock += "</obsDebugConf>\n\n"
for obsid in obsids:
obsid = int(obsid)
......
......@@ -347,6 +347,37 @@ def worker_logs(queueitem=None, nodeid=None, resultfile_path=None, logqueue=None
### END worker_logs()
##############################################################################
#
# worker_datatrace
#
##############################################################################
def worker_datatrace(queueitem=None, nodeid=None, resultfile_path=None, logqueue=None, arg=None):
try:
_errors = []
cur_p = multiprocessing.current_process()
(itemtype, obsid, fdir, f, workerstate) = queueitem
obsdbfile_path = "%s/%s" % (fdir, f)
loggername = "(%s.%d) " % (cur_p.name, obsid)
with open(resultfile_path, "a") as outfile:
infile = open(obsdbfile_path, "r")
for line in infile:
(timestamp, msg) = line.strip().split(',', 1)
outfile.write("%s,%s,%s,%s\n" % (timestamp, obsid, nodeid, msg))
infile.close()
os.remove(obsdbfile_path)
except:
msg = "Error in datatrace worker process: %s: %s\n%s" % (str(sys.exc_info()[0]), str(sys.exc_info()[1]), traceback.format_exc())
_errors.append((msg, errno.ECOMM, obsid))
logqueue.put_nowait((loggername, logging.ERROR, msg))
finally:
processeditem = list(queueitem)
processeditem[0] = ITEM_PROCESSED
return (_errors, tuple(processeditem))
### END worker_datatrace()
##############################################################################
#
# worker_callback: Callback function which reports errors from worker processes
......@@ -854,7 +885,7 @@ def main(argv):
# Find out which services are used to allocate working threads later on ---
# Get the XML config from the database and check which services are used in the test.
servicesUsed_dict = {'gpiotracing': 'gpioTracingConf', 'powerprofiling': 'powerProfilingConf', 'serial': 'serialConf'}
servicesUsed_dict = {'gpiotracing': 'gpioTracingConf', 'powerprofiling': 'powerProfilingConf', 'serial': 'serialConf', 'datatrace': 'dataTraceConf'}
cur.execute("SELECT `testconfig_xml` FROM `tbl_serv_tests` WHERE (`serv_tests_key` = %s)" %testid)
ret = cur.fetchone()
if not ret:
......@@ -886,6 +917,9 @@ def main(argv):
msg = "XML parsing failed: %s: %s" % (str(sys.exc_info()[0]), str(sys.exc_info()[1]))
errors.append(msg)
logger.error(msg)
# Append log services (always used)
servicesUsed_dict['errorlog'] = True
servicesUsed_dict['timesynclog'] = True
cur.close()
cn.close()
......@@ -913,7 +947,9 @@ def main(argv):
os.makedirs(testresultsdir)
logger.debug("Created %s" % testresultsdir)
manager = multiprocessing.Manager()
for service in ('errorlog', 'gpiotracing', 'powerprofiling', 'serial', 'timesynclog'):
for service in ('errorlog', 'gpiotracing', 'powerprofiling', 'serial', 'timesynclog', 'datatrace'):
if servicesUsed_dict[service] == False:
continue # only create files for used services
path = "%s/%s.csv" % (testresultsdir, service)
lock = manager.Lock()
testresultsfile_dict[service] = (path, lock)
......@@ -928,6 +964,8 @@ def main(argv):
header = 'timestamp,observer_id,node_id,current_mA,voltage_V\n'
elif service == 'serial':
header = 'timestamp,observer_id,node_id,direction,output\n'
elif service == 'datatrace':
header = 'timestamp,observer_id,node_id,variable,value,access,pc\n'
lock.acquire()
f = open(path, 'w')
f.write(header)
......@@ -963,6 +1001,10 @@ def main(argv):
else:
cpus_powerprofiling = 0
#cpus_free = cpus_free + flocklab.config.getint('fetcher', 'cpus_powerprofiling')
if servicesUsed_dict['datatrace'] == True:
cpus_datatrace = 1
else:
cpus_datatrace = 0
# If there are free CPUs left, give them to GPIO tracing and power profiling evenly as these services need the most CPU power:
if cpus_free > 0:
if (cpus_powerprofiling > 0) and (cpus_gpiomonitoring > 0):
......@@ -981,7 +1023,7 @@ def main(argv):
cpus_serial = cpus_serial + cpus_free
cpus_total = cpus_logs + cpus_serial + cpus_gpiomonitoring + cpus_powerprofiling
service_pools_dict = { 'logs': cpus_logs, 'serial': cpus_serial, 'gpiotracing': cpus_gpiomonitoring, 'powerprofiling': cpus_powerprofiling }
service_pools_dict = { 'logs': cpus_logs, 'serial': cpus_serial, 'gpiotracing': cpus_gpiomonitoring, 'powerprofiling': cpus_powerprofiling, 'datatrace': cpus_datatrace }
if (cpus_total > multiprocessing.cpu_count()):
logger.warning("Number of requested CPUs for all aggregating processes (%d) is higher than number of available CPUs (%d) on system." % (cpus_total, multiprocessing.cpu_count()))
......@@ -1048,6 +1090,10 @@ def main(argv):
elif (re.search("^serial_[0-9]{14}\.db$", f) != None):
pool = service_pools_dict['serial']
worker_args = [nextitem, nodeid, testresultsfile_dict['serial'][0], testresultsfile_dict['serial'][1], commitsize, parse_serial, convert_serial, logqueue]
elif (re.search("^datatrace_[0-9]{14}\.csv$", f) != None):
pool = service_pools_dict['datatrace']
worker_args = [nextitem, nodeid, testresultsfile_dict['datatrace'][0], logqueue, None]
worker_f = worker_logs
elif (re.search("^error_[0-9]{14}\.log$", f) != None):
pool = service_pools_dict['logs']
worker_args = [nextitem, nodeid, testresultsfile_dict['errorlog'][0], logqueue, None]
......
......@@ -1131,11 +1131,11 @@ def parse_int(s):
def binary_has_symbol(symbol=None, binaryfile=None):
if symbol is None or binaryfile is None or not os.path.isfile(binaryfile):
return FAILED
p = subprocess.Popen(['objdump', '-t', imagepath], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
p = subprocess.Popen(['objdump', '-t', binaryfile], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
(out, err) = p.communicate()
if p.returncode == 0:
if symbol in out:
logger.debug("Found symbol %s in binary file '%s'." % (symbol, imagepath))
logger.debug("Found symbol %s in binary file '%s'." % (symbol, binaryfile))
return True
return False
return FAILED
......@@ -1257,3 +1257,45 @@ def is_hex_file(filename=None, data=None):
pass # not a valid hex file
return False
### END is_hex_file()
##############################################################################
#
# get_symtable_from_binary() retrieves the symbol table from a target image (elf file)
#
##############################################################################
def get_symtable_from_binary(binaryfile=None):
if binaryfile == None:
return ""
# either use readelf -s or objdump -t
p = subprocess.Popen(['readelf', '-s', binaryfile], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
(out, err) = p.communicate()
if p.returncode == 0:
return out
return ""
### END get_symtable_from_binary()
##############################################################################
#
# extract_variables_from_symtable() extracts all global variables from a symbol table and returns them as a dictionary
#
##############################################################################
def extract_variables_from_symtable(symtable=""):
if not symtable:
return symtable
symbols = {}
for line in symtable.split('\n'):
if not "OBJECT" in line:
continue
parts = line.split()
if len(parts) >= 8: # at least 8 parts required
try:
symname = parts[-1]
symbols[symname] = [ int(parts[1], 16), int(parts[-2]) ]
print("found symbol: %s, 0x%x, %d" % (symname, symbols[symname][0], symbols[symname][1]))
except ValueError:
pass
return symbols
### END extract_variables_from_symtable()
......@@ -562,6 +562,10 @@ def main(argv):
if not quiet:
print("Element debugConf: Some observer IDs have been used but do not have a targetConf element associated with them.")
errcnt = errcnt + 1
if tree.xpath('//d:debugConf/d:dataTraceConf', namespaces=ns) and platform.lower() not in ('nrf5', 'dpp2lora', 'dpp2lorahg'):
if not quiet:
print("Element dataTraceConf: data tracing is not supported on the platform %s." % platform)
errcnt = errcnt + 1
# gpioTracingConf additional validation ---------------------------------------
# * observer ids need to have a targetConf associated and must be unique
......
......@@ -216,9 +216,17 @@
<xs:complexType name="debugConfType">
<xs:sequence>
<xs:element name="obsIds" type="obsIdListRestType"/>
<xs:choice minOccurs="0" maxOccurs="2">
<xs:choice>
<xs:element name="gdbPort" type="portType" minOccurs="0" maxOccurs="1"/>
<xs:element name="remoteIp" type="ipType" minOccurs="0" maxOccurs="1"/>
<xs:element name="dataTraceConf" minOccurs="0" maxOccurs="4">
<xs:complexType>
<xs:sequence>
<xs:element name="variable" type="xs:string"/>
<xs:element name="mode" type="dataTraceModeType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:choice>
</xs:sequence>
</xs:complexType>
......@@ -436,4 +444,10 @@
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="dataTraceModeType">
<xs:restriction base="xs:string">
<xs:pattern value="R|W|RW|PC"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment