Merge pull request #29 from ep1cman/fixes
AdbConnection: added automatic detection of new line separators
diff --git a/devlib/bin/scripts/shutils.in b/devlib/bin/scripts/shutils.in
index 9200b70..bc47719 100755
--- a/devlib/bin/scripts/shutils.in
+++ b/devlib/bin/scripts/shutils.in
@@ -55,6 +55,20 @@
done
}
+
+################################################################################
+# CGroups Utility Functions
+################################################################################
+
+cgroups_get_attributes() {
+ [[ $# -eq 2 ]] || exit -1
+ CGROUP="$1"
+ CONTROLLER="$2"
+ $GREP '' $CGROUP/* | \
+ $GREP "$CONTROLLER\." | \
+ $SED -e "s|$CONTROLLER\.||" -e "s|$CGROUP/||"
+}
+
################################################################################
# Main Function Dispatcher
################################################################################
@@ -75,6 +89,9 @@
cpufreq_trace_all_frequencies)
cpufreq_trace_all_frequencies $*
;;
+cgroups_get_attributes)
+ cgroups_get_attributes $*
+ ;;
ftrace_get_function_stats)
ftrace_get_function_stats
;;
diff --git a/devlib/module/cgroups.py b/devlib/module/cgroups.py
index c55e135..1d7a24a 100644
--- a/devlib/module/cgroups.py
+++ b/devlib/module/cgroups.py
@@ -68,7 +68,7 @@
self.mount_point),
as_root=True)
- self.logger.info('Controller %s mounted under: %s',
+ self.logger.debug('Controller %s mounted under: %s',
self.kind, self.mount_point)
# Mark this contoller as available
@@ -98,7 +98,7 @@
output = self.target.execute('{} find {} -type d'\
.format(self.target.busybox, self.mount_point))
cgroups = []
- for cg in output.split('\n'):
+ for cg in output.splitlines():
cg = cg.replace(self.mount_point + '/', '/')
cg = cg.replace(self.mount_point, '/')
cg = cg.strip()
@@ -147,7 +147,7 @@
if not create:
return
- self.logger.info('Creating cgroup %s', self.directory)
+ self.logger.debug('Creating cgroup %s', self.directory)
self.target.execute('[ -d {0} ] || mkdir -p {0}'\
.format(self.directory), as_root=True)
@@ -166,14 +166,10 @@
self.controller.kind)
logging.debug(' %s',
self.directory)
- output = self.target.execute('{} grep \'\' {}/{}.*'.format(
- self.target.busybox,
- self.directory,
- self.controller.kind))
- for res in output.split('\n'):
- if res.find(self.controller.kind) < 0:
- continue
- res = res.split('.')[1]
+ output = self.target._execute_util(
+ 'cgroups_get_attributes {} {}'.format(
+ self.directory, self.controller.kind))
+ for res in output.splitlines():
attr = res.split(':')[0]
value = res.split(':')[1]
conf[attr] = value
@@ -241,7 +237,7 @@
subsys = self.list_subsystems()
for (n, h, c, e) in subsys:
controllers.append(n)
- self.logger.info('Available controllers: %s', controllers)
+ self.logger.debug('Available controllers: %s', controllers)
# Initialize controllers
self.controllers = {}
@@ -261,7 +257,7 @@
def list_subsystems(self):
subsystems = []
for line in self.target.execute('{} cat /proc/cgroups'\
- .format(self.target.busybox)).split('\n')[1:]:
+ .format(self.target.busybox)).splitlines()[1:]:
line = line.strip()
if not line or line.startswith('#'):
continue
diff --git a/devlib/target.py b/devlib/target.py
index 5952faf..01f3bb8 100644
--- a/devlib/target.py
+++ b/devlib/target.py
@@ -228,6 +228,13 @@
'(in which case, a hard_reset module must be installed)'
raise TargetError(message)
self.reset()
+ # Wait a fixed delay before starting polling to give the target time to
+ # shut down, otherwise, might create the connection while it's still shutting
+ # down resulting in subsequenct connection failing.
+ self.logger.debug('Waiting for target to power down...')
+ reset_delay = 20
+ time.sleep(reset_delay)
+ timeout = max(timeout - reset_delay, 10)
if self.has('boot'):
self.boot() # pylint: disable=no-member
if connect:
@@ -708,7 +715,6 @@
modules=modules,
load_default_modules=load_default_modules,
shell_prompt=shell_prompt)
- self.executables_directory = executables_directory
self.package_data_directory = package_data_directory
def reset(self, fastboot=False): # pylint: disable=arguments-differ
diff --git a/devlib/trace/ftrace.py b/devlib/trace/ftrace.py
index de9ec1c..761b9d2 100644
--- a/devlib/trace/ftrace.py
+++ b/devlib/trace/ftrace.py
@@ -59,6 +59,7 @@
autoreport=True,
autoview=False,
no_install=False,
+ strict=False,
):
super(FtraceCollector, self).__init__(target)
self.events = events if events is not None else DEFAULT_EVENTS
@@ -74,11 +75,12 @@
self.host_binary = None
self.start_time = None
self.stop_time = None
- self.event_string = _build_trace_events(self.events)
- self.function_string = _build_trace_functions(self.functions)
+ self.event_string = None
+ self.function_string = None
self._reset_needed = True
# Setup tracing paths
+ self.available_events_file = self.target.path.join(self.tracing_path, 'available_events')
self.available_functions_file = self.target.path.join(self.tracing_path, 'available_filter_functions')
self.buffer_size_file = self.target.path.join(self.tracing_path, 'buffer_size_kb')
self.current_tracer_file = self.target.path.join(self.tracing_path, 'current_tracer')
@@ -103,6 +105,32 @@
raise TargetError('No trace-cmd found on device and no_install=True is specified.')
self.target_binary = 'trace-cmd'
+ # Validate required events to be traced
+ available_events = self.target.execute(
+ 'cat {}'.format(self.available_events_file)).splitlines()
+ selected_events = []
+ for event in self.events:
+ # Convert globs supported by FTrace into valid regexp globs
+ _event = event
+ if event[0] != '*':
+ _event = '*' + event
+ event_re = re.compile(_event.replace('*', '.*'))
+ # Select events matching the required ones
+ if len(filter(event_re.match, available_events)) == 0:
+ message = 'Event [{}] not available for tracing'.format(event)
+ if strict:
+ raise TargetError(message)
+ self.target.logger.warning(message)
+ else:
+ selected_events.append(event)
+ # If function profiling is enabled we always need at least one event.
+ # Thus, if not other events have been specified, try to add at least
+ # a tracepoint which is always available and possibly triggered few
+ # times.
+ if self.functions and len(selected_events) == 0:
+ selected_events = ['sched_wakeup_new']
+ self.event_string = _build_trace_events(selected_events)
+
# Check for function tracing support
if self.functions:
if not self.target.file_exists(self.function_profile_file):
@@ -111,9 +139,16 @@
# Validate required functions to be traced
available_functions = self.target.execute(
'cat {}'.format(self.available_functions_file)).splitlines()
+ selected_functions = []
for function in self.functions:
if function not in available_functions:
- raise TargetError('Function [{}] not available for filtering'.format(function))
+ message = 'Function [{}] not available for profiling'.format(function)
+ if strict:
+ raise TargetError(message)
+ self.target.logger.warning(message)
+ else:
+ selected_functions.append(function)
+ self.function_string = _build_trace_functions(selected_functions)
def reset(self):
if self.buffer_size:
@@ -125,9 +160,9 @@
self.start_time = time.time()
if self._reset_needed:
self.reset()
+ self.target.execute('{} start {}'.format(self.target_binary, self.event_string), as_root=True)
if self.automark:
self.mark_start()
- self.target.execute('{} start {}'.format(self.target_binary, self.event_string), as_root=True)
if 'cpufreq' in self.target.modules:
self.logger.debug('Trace CPUFreq frequencies')
self.target.cpufreq.trace_frequencies()
@@ -205,7 +240,7 @@
self.logger.debug("FTrace stats output [%s]...", outfile)
with open(outfile, 'w') as fh:
json.dump(function_stats, fh, indent=4)
- self.logger.info("FTrace function stats save in [%s]", outfile)
+ self.logger.debug("FTrace function stats save in [%s]", outfile)
return function_stats
diff --git a/devlib/utils/ssh.py b/devlib/utils/ssh.py
index 6ad6f9d..8bf17c0 100644
--- a/devlib/utils/ssh.py
+++ b/devlib/utils/ssh.py
@@ -22,6 +22,7 @@
import threading
import tempfile
import shutil
+import time
import pexpect
from distutils.version import StrictVersion as V
@@ -44,19 +45,28 @@
def ssh_get_shell(host, username, password=None, keyfile=None, port=None, timeout=10, telnet=False):
_check_env()
- if telnet:
- if keyfile:
- raise ValueError('keyfile may not be used with a telnet connection.')
- conn = TelnetConnection()
- else: # ssh
- conn = pxssh.pxssh()
- try:
- if keyfile:
- conn.login(host, username, ssh_key=keyfile, port=port, login_timeout=timeout)
- else:
- conn.login(host, username, password, port=port, login_timeout=timeout)
- except EOF:
- raise TargetError('Could not connect to {}; is the host name correct?'.format(host))
+ start_time = time.time()
+ while True:
+ if telnet:
+ if keyfile:
+ raise ValueError('keyfile may not be used with a telnet connection.')
+ conn = TelnetConnection()
+ else: # ssh
+ conn = pxssh.pxssh()
+
+ try:
+ if keyfile:
+ conn.login(host, username, ssh_key=keyfile, port=port, login_timeout=timeout)
+ else:
+ conn.login(host, username, password, port=port, login_timeout=timeout)
+ break
+ except EOF:
+ timeout -= time.time() - start_time
+ if timeout <= 0:
+ message = 'Could not connect to {}; is the host name correct?'
+ raise TargetError(message.format(host))
+ time.sleep(5)
+
conn.setwinsize(500,200)
conn.sendline('')
conn.prompt()
@@ -151,27 +161,35 @@
return self._scp(source, dest, timeout)
def execute(self, command, timeout=None, check_exit_code=True, as_root=False, strip_colors=True):
- with self.lock:
- output = self._execute_and_wait_for_prompt(command, timeout, as_root, strip_colors)
- if check_exit_code:
- exit_code_text = self._execute_and_wait_for_prompt('echo $?', strip_colors=strip_colors, log=False)
- try:
- exit_code = int(exit_code_text.split()[0])
- if exit_code:
- message = 'Got exit code {}\nfrom: {}\nOUTPUT: {}'
- raise TargetError(message.format(exit_code, command, output))
- except (ValueError, IndexError):
- logger.warning('Could not get exit code for "{}",\ngot: "{}"'.format(command, exit_code_text))
- return output
+ try:
+ with self.lock:
+ output = self._execute_and_wait_for_prompt(command, timeout, as_root, strip_colors)
+ if check_exit_code:
+ exit_code_text = self._execute_and_wait_for_prompt('echo $?', strip_colors=strip_colors, log=False)
+ try:
+ exit_code = int(exit_code_text.split()[0])
+ if exit_code:
+ message = 'Got exit code {}\nfrom: {}\nOUTPUT: {}'
+ raise TargetError(message.format(exit_code, command, output))
+ except (ValueError, IndexError):
+ logger.warning('Could not get exit code for "{}",\ngot: "{}"'.format(command, exit_code_text))
+ return output
+ except EOF:
+ raise TargetError('Connection lost.')
- def background(self, command, stdout=subprocess.PIPE, stderr=subprocess.PIPE):
- port_string = '-p {}'.format(self.port) if self.port else ''
- keyfile_string = '-i {}'.format(self.keyfile) if self.keyfile else ''
- command = '{} {} {} {}@{} {}'.format(ssh, keyfile_string, port_string, self.username, self.host, command)
- logger.debug(command)
- if self.password:
- command = _give_password(self.password, command)
- return subprocess.Popen(command, stdout=stdout, stderr=stderr, shell=True)
+ def background(self, command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, as_root=False):
+ try:
+ port_string = '-p {}'.format(self.port) if self.port else ''
+ keyfile_string = '-i {}'.format(self.keyfile) if self.keyfile else ''
+ if as_root:
+ command = "sudo -- sh -c '{}'".format(command)
+ command = '{} {} {} {}@{} {}'.format(ssh, keyfile_string, port_string, self.username, self.host, command)
+ logger.debug(command)
+ if self.password:
+ command = _give_password(self.password, command)
+ return subprocess.Popen(command, stdout=stdout, stderr=stderr, shell=True)
+ except EOF:
+ raise TargetError('Connection lost.')
def close(self):
logger.debug('Logging out {}@{}'.format(self.username, self.host))