Skip to content

Commit 67ec2a9

Browse files
author
Alexander Merkle
committed
exporter/driver: change t32tcpusb strategy for USBLauterbachDebugger
Instead of invoking `t32tcpusb` from the client via SSH use a strategy that is more like `ser2net`. Upload the `t32tcpusb` agent to the exporter, the exporter notices this upload and restarts the matching `t32tcpusb` agent. The port used by `t32tcpusb` is blocked by the exporter as long as no `t32tcpusb` agent is uploaded.
1 parent bb25d8b commit 67ec2a9

File tree

3 files changed

+123
-63
lines changed

3 files changed

+123
-63
lines changed

labgrid/driver/lauterbachdriver.py

Lines changed: 6 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import logging
22
import os
3-
import re
4-
import subprocess
3+
import time
54

65
from importlib import import_module
76

@@ -11,10 +10,8 @@
1110
from ..protocol import DebuggerProtocol
1211
from ..resource.lauterbach import NetworkLauterbachDebugger, NetworkUSBLauterbachDebugger
1312
from ..resource.udev import USBLauterbachDebugger
14-
from ..util.helper import get_uname_machine, processwrapper
1513
from ..util.managedfile import ManagedFile
1614
from ..util.proxy import proxymanager
17-
from ..util import Timeout
1815

1916
from .common import Driver
2017

@@ -54,81 +51,27 @@ def __attrs_post_init__(self):
5451
self.t32sys=self.target.env.config.get_path(self.t32_sys)
5552

5653
self.connection = None
57-
self.t32tcpusb = None
5854

59-
def _get_t32tcpusb_version(self):
60-
# get version running `t32tcpusb` on client
61-
t32tcpusb = os.path.join(self.t32sys, self.pathmap.get(get_uname_machine()), "t32tcpusb")
62-
version = 0x0
63-
# convert 'Sw.Version: N.<year>.<month>.<revision>' to 0x<year><month>
64-
output = processwrapper.check_output([t32tcpusb, '-h']).decode('utf-8')
65-
versionmatch = re.search(r"Version:\s[NSRP]\.(\d{4})\.(\d{2})\.\d+", str(output))
66-
67-
if versionmatch is not None:
68-
version = int(f'0x{versionmatch[1]}{versionmatch[2]}', 16)
69-
70-
return version
71-
7255
def on_activate(self):
7356
if isinstance(self.interface, USBLauterbachDebugger):
7457
self.connection = self._pystart.USBConnection(f"{self.interface.busnum:03d}:{self.interface.devnum:03d}")
7558
elif isinstance(self.interface, NetworkLauterbachDebugger):
7659
self.connection = self._pystart.UDPConnection(self.interface.node)
7760
elif isinstance(self.interface, NetworkUSBLauterbachDebugger):
78-
port = "auto"
79-
version = self._get_t32tcpusb_version()
80-
if version<0x202212:
81-
print(f"Version {version:06x} of `t32tcpusb` too old.")
82-
return
83-
if version<0x202309:
84-
port = "8455"
8561

8662
path = os.path.join(self.t32sys, self.pathmap.get(self.interface.architecture), "t32tcpusb")
8763
self.logger.debug("Upload %s to exporter", path)
8864
mf = ManagedFile(path, self.interface)
89-
mf.sync_to_resource()
90-
91-
cmd = self.interface.command_prefix
92-
cmd.insert(-2, "-tt") # force a tty in order to get `terminate` working
93-
cmd += [
94-
mf.get_remote_path(),
95-
"--device",
96-
f"{self.interface.busnum:03d}:{self.interface.devnum:03d}",
97-
port
98-
]
99-
100-
self.logger.debug("Running command '%s'", " ".join(cmd))
101-
self.t32tcpusb = subprocess.Popen(cmd, stdout=subprocess.PIPE)
102-
103-
# check output of `t32tcpusb` to get the used port
104-
line = ""
105-
timeout = Timeout(5.0)
106-
while not timeout.expired:
107-
if self.t32tcpusb.poll():
108-
break
109-
c = self.t32tcpusb.stdout.read(1).decode("utf-8")
110-
if c=='\n':
111-
self.logger.debug(line)
112-
portmatch = re.search(r"TRACE32 TCP USB Proxy Service.*listening on port (\d+)\s", line)
113-
if portmatch:
114-
portused = int(portmatch[1])
115-
break
116-
line = ""
117-
else:
118-
line = line + c
119-
120-
if self.t32tcpusb.poll():
121-
Exception("Agent `t32tcpusb` ended unexpectedly")
65+
symlink = f"/var/cache/labgrid/t32tcpusb-{self.interface.path}"
66+
mf.sync_to_resource(symlink=symlink)
67+
# the exporter notices the change and restarts, for the moment give it some time
68+
time.sleep(0.5)
12269

123-
host, port = proxymanager.get_host_and_port(self.interface, default_port=portused)
70+
host, port = proxymanager.get_host_and_port(self.interface)
12471
self.connection = self._pystart.USBProxyConnection(host, port)
12572
return super().on_activate()
12673

12774
def on_deactivate(self):
128-
if self.t32tcpusb:
129-
self.logger.debug("Try to terminate `t32tcpusb` on exporter")
130-
self.t32tcpusb.terminate()
131-
13275
self.connection = None
13376

13477
return super().on_deactivate()

labgrid/remote/exporter.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import sys
77
import os
88
import os.path
9+
import re
910
import time
1011
import traceback
1112
import shutil
@@ -500,11 +501,125 @@ class USBLauterbachDebuggerExport(USBGenericExport):
500501

501502
def __attrs_post_init__(self):
502503
super().__attrs_post_init__()
504+
self.stat = None
505+
self.port = None
506+
self.socket = None
507+
self.child = None
508+
self.machine = get_uname_machine()
509+
510+
import random
511+
for port in random.sample(range(8455,8555),100):
512+
s = self._block_port(port)
513+
if s is not None:
514+
self.port = port
515+
self.socket = s
516+
break
517+
518+
def _block_port(self, port):
519+
from socket import socket, AF_INET, SOCK_STREAM, SOL_SOCKET, SO_REUSEADDR
520+
s = None
521+
try:
522+
s = socket(AF_INET, SOCK_STREAM)
523+
except OSError:
524+
s = None
525+
if s is not None:
526+
try:
527+
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
528+
s.bind(('', port))
529+
s.listen()
530+
except:
531+
s.close()
532+
s = None
533+
self.logger.debug("block port %d", port)
534+
return s
535+
536+
def _get_t32tcpusb_version(self, path):
537+
"""Get version of `t32tcpusb`"""
538+
version = 0x0
539+
# convert 'Sw.Version: N.<year>.<month>.<revision>' to 0x<year><month>
540+
output = subprocess.check_output([path, '-h']).decode('utf-8')
541+
versionmatch = re.search(r"Version:\s[NSRP]\.(\d{4})\.(\d{2})\.\d+", str(output))
542+
543+
if versionmatch is not None:
544+
version = int(f'0x{versionmatch[1]}{versionmatch[2]}', 16)
545+
546+
return version
547+
548+
def _get_start_params(self):
549+
assert not self.broken
550+
self.stat = None
551+
symlink = f"/var/cache/labgrid/t32tcpusb-{self.local.path}"
552+
try:
553+
if os.path.exists(symlink):
554+
stat = os.stat(symlink)
555+
self.stat = (stat.st_ino, stat.st_size, stat.st_mtime)
556+
except Exception as e:
557+
self.logger.debug(e)
558+
self.broken = f"cannot access {symlink}"
559+
self.stat = None
560+
if self.stat is None:
561+
return None
562+
return {
563+
'stat': self.stat,
564+
'symlink': symlink,
565+
'busnum': self.local.busnum,
566+
'devnum': self.local.devnum
567+
}
568+
569+
def _start(self, start_params):
570+
"""Start ``t32tcpusb`` subprocess"""
571+
assert self.local.avail
572+
assert self.child is None
573+
574+
# t32tcpusb available yet?
575+
if start_params is None:
576+
#self.logger.info("skip start of t32tcpusb (Reason: link %s-<path> not found)", self.t32tcpusb_basename)
577+
return
578+
579+
tcpusbVersion = self._get_t32tcpusb_version(start_params['symlink'])
580+
581+
cmd = [start_params['symlink'],]
582+
if tcpusbVersion >= 0x202212:
583+
cmd += [
584+
'--device',
585+
f'{start_params["busnum"]:03}:{start_params["devnum"]:03}'
586+
]
587+
cmd += [f'{self.port}']
588+
self.logger.info("starting t32tcpusb with: %s", " ".join(cmd))
589+
self.socket.close()
590+
self.child = subprocess.Popen(cmd)
591+
try:
592+
self.child.wait(timeout=0.5)
593+
raise ExporterError("t32tcpusb exited immediately")
594+
except subprocess.TimeoutExpired:
595+
# good, t32tcpusb didn't exit immediately
596+
pass
597+
self.logger.info("started t32tcpusb for bus %03d device %03d on port %d", start_params['busnum'], start_params['devnum'], self.port)
598+
599+
def _stop(self, start_params):
600+
"""Stop ``t32tcpusb`` subprocess"""
601+
if start_params is None:
602+
return
603+
604+
assert self.child
605+
child = self.child
606+
self.child = None
607+
child.terminate()
608+
try:
609+
child.wait(2.0) # t32tcpusb takes about a second to react
610+
except subprocess.TimeoutExpired:
611+
self.logger.warning("t32tcpusb still running after SIGTERM")
612+
log_subprocess_kernel_stack(self.logger, child)
613+
child.kill()
614+
child.wait(1.0)
615+
self.logger.info("stopped t32tcpusb for bus %03d device %03d on port %d", start_params['busnum'], start_params['devnum'], self.port)
616+
self._block_port(self.port)
503617

504618
def _get_params(self):
505619
"""Helper function to return parameters"""
506620
p = super()._get_params()
507621
p['architecture'] = get_uname_machine()
622+
p['port'] = self.port
508623
return p
509624

510625
@attr.s(eq=False)

labgrid/resource/lauterbach.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,10 @@ class NetworkUSBLauterbachDebugger(RemoteUSBResource):
2323
2424
Args:
2525
architecture (str): Architecture of the exporter userspace
26+
port (str): socket port to connect to
2627
"""
2728
architecture = attr.ib(validator=attr.validators.instance_of(str))
29+
port = attr.ib(validator=attr.validators.instance_of(int))
2830
def __attrs_post_init__(self):
2931
self.timeout = 10.0
3032
super().__attrs_post_init__()

0 commit comments

Comments
 (0)