adb_root() restarts the server, leading to aborting commands ran by
other connections and also apparently leading to command hanging in some
situations.
Therefore, only allow adb_root() when there is only one connection
active. That should not be a big issue as this is typically called by
the first connection ever made when the Target is created.
Add a memoize_method decorator that works for async methods. It will not
leak memory since the memoization cache is held in the instance
__dict__, and it does not rely on hacks to hash unhashable data.
use_governor() was trying to set concurrently both per-cpu and global tunables for
each governor, which lead to a write conflict.
Split the work into the per-governor global tunables and the per-cpu
tunables, and do all that in concurrently. Each task is therefore
responsible of a distinct set of files and all is well.
Also remove @memoized on async functions. It will be reintroduced in a
later commit when there is a safe alternative for async functions.
The conncetion returned by Target.get_connection() does not have its
.busybox attribute initialized. This is expected for the first
connection, but connections created for new threads should have busybox
set.
Currently the cgroups module will pull all available controllers from
/proc/cgroups and then try to mount them, including the disabled ones.
This will result in the entire mount failing.
Lines in /proc/cgroups ending in 0 correspond to disabled controllers.
Filtering those out solves the issue.
With recent versions of adb, adb root can fail if the
daemon is already running as root.
Check the raised error message for this case and avoid
raising an error in this scenario.
When no entry has been recorded by the collector, return an empty string
rather than returning the full dmesg log.
Also fix get_data() that would fail try to add None + '\n' if dmesg_out
property returns None.
Both move_all_tasks_to() and move_tasks() take a list of grep patterns
to exclude.
It turned out that move_all_tasks_to() was calling move_tasks() with a
string instead of a list, leading to broken quoting.
Fix that by passing the pattern list to move_tasks() and let
move_tasks() add the "-e" option in front of it. Also add a
DeprecationWarning in move_tasks() if someone passes a string instead of
an iterable of strings.
Rather than systematically clearing the buffer on reset(), record the
timestamp of the last entry and use it to filter-out old entries in
DmesgCollector.entries property.
This also allows detecting if the ring buffer has ran out of memory, or
if something has cleared the buffer while collecting, leading to missing
entries.
Currently, it is not possible to push/pull files with a relative path when
the destination doesn't exist. This is due to the basename resolution. Fix
this behaviour.
Change default FtraceCollector(tracing_path=...) to None, and
auto-detect mount point when None is given.
Also expose an FtraceCollector.find_tracing_path() method so that user
code can also access this path without having to instantiate an
FtraceCollector.
Check that the function exists and then run it, to avoid endless
copy-pasting.
Also call it with "$@", which will achieve proper CLI params forwarding
unlike "$*" which will not.
The command used to detect the presence of a filepath can return
the wrong value if only accessible by the superuser.
Pass the `as_root` parameter to the detection function to ensure
that files that are to be pulled with elevated permissions are
also queried with elevated permission.
Some Android devices do not have 'su'. But if they are already
rooted, there's no reason to fail. Circumvent this scenario by never
using 'su' for device already rooted.
Use target.list_directory(as_root=target.is_rooted) instead of doing it
as a normal user for paths in /sys/kernel/debug. Since this
list_directory() call can be used with multiple path, we do not force
as_root=True but we increase the chance of it working.
__getstate__ is also used by the copy module, but allows pickling the
class as well. This is useful when using the multiprocessing API, which
requires pickling the Target object to send it to the new process.
The default versions used for sphinx and docuilts on
readthedocs are no longer compatible. Explicitly list
the package versions that should be used when building
the documentation.
SSH servers seem to have a maximum number of opened channels, after
which paramiko will raise an exception:
Could not open an SSH channel: ChannelException(2, 'Connect failed')
Memoizing the SFTPClient object based on the timeout setting leads to
many opened sessions, since the timeout is typically adjusted, e.g. to
match the size of the file when pulling an ftrace trace.dat file.
Solve that by memoizing the SFTPClient based only on the connection
object, with a maximum number of 1 cached object, and update its timeout
setting inplace.
Avoid an execute() by doing the check in the same command. This also
allows to return early if the write is fast, and to extend for longer if
the write is slow. The speed at which you can observe a write in sysfs
depends on the backing kernel handlers, so there is a wide variety of
situations.
Also, make a more fine grained error detection by allowing the write
itself to fail, which can happen when writing invalid values to sysfs.
Add Target{Stable,Transient}CalledProcessError exceptions, with an
.returncode and .output attributes, raised by Target.execute(),
mirroring subprocess.CalledProcessError.
This is very useful in client code that uses "exit N" to signal an
abnormal condition, and then inspects the output to find out more.