1Overview 2======== 3 4A :class:`Target` instance serves as the main interface to the target device. 5There currently three target interfaces: 6 7- :class:`LinuxTarget` for interacting with Linux devices over SSH. 8- :class:`AndroidTraget` for interacting with Android devices over adb. 9- :class:`LocalLinuxTarget`: for interacting with the local Linux host. 10 11They all work in more-or-less the same way, with the major difference being in 12how connection settings are specified; though there may also be a few APIs 13specific to a particular target type (e.g. :class:`AndroidTarget` exposes 14methods for working with logcat). 15 16 17Acquiring a Target 18------------------ 19 20To create an interface to your device, you just need to instantiate one of the 21:class:`Target` derivatives listed above, and pass it the right 22``connection_settings``. Code snippet below gives a typical example of 23instantiating each of the three target types. 24 25.. code:: python 26 27 from devlib import LocalLinuxTarget, LinuxTarget, AndroidTarget 28 29 # Local machine requires no special connection settings. 30 t1 = LocalLinuxTarget() 31 32 # For a Linux device, you will need to provide the normal SSH credentials. 33 # Both password-based, and key-based authentication is supported (password 34 # authentication requires sshpass to be installed on your host machine).' 35 t2 = LinuxTarget(connection_settings={'host': '192.168.0.5', 36 'username': 'root', 37 'password': 'sekrit', 38 # or 39 'keyfile': '/home/me/.ssh/id_rsa'}) 40 41 # For an Android target, you will need to pass the device name as reported 42 # by "adb devices". If there is only one device visible to adb, you can omit 43 # this setting and instantiate similar to a local target. 44 t3 = AndroidTarget(connection_settings={'device': '0123456789abcde'}) 45 46Instantiating a target may take a second or two as the remote device will be 47queried to initialize :class:`Target`'s internal state. If you would like to 48create a :class:`Target` instance but not immediately connect to the remote 49device, you can pass ``connect=False`` parameter. If you do that, you would have 50to then explicitly call ``t.connect()`` before you can interact with the device. 51 52There are a few additional parameters you can pass in instantiation besides 53``connection_settings``, but they are usually unnecessary. Please see 54:class:`Target` API documentation for more details. 55 56Target Interface 57---------------- 58 59This is a quick overview of the basic interface to the device. See 60:class:`Target` API documentation for the full list of supported methods and 61more detailed documentation. 62 63One-time Setup 64~~~~~~~~~~~~~~ 65 66.. code:: python 67 68 from devlib import LocalLinuxTarget 69 t = LocalLinuxTarget() 70 71 t.setup() 72 73This sets up the target for ``devlib`` interaction. This includes creating 74working directories, deploying busybox, etc. It's usually enough to do this once 75for a new device, as the changes this makes will persist across reboots. 76However, there is no issue with calling this multiple times, so, to be on the 77safe site, it's a good idea to call this once at the beginning of your scripts. 78 79Command Execution 80~~~~~~~~~~~~~~~~~ 81 82There are several ways to execute a command on the target. In each case, a 83:class:`TargetError` will be raised if something goes wrong. In very case, it is 84also possible to specify ``as_root=True`` if the specified command should be 85executed as root. 86 87.. code:: python 88 89 from devlib import LocalLinuxTarget 90 t = LocalLinuxTarget() 91 92 # Execute a command 93 output = t.execute('echo $PWD') 94 95 # Execute command via a subprocess and return the corresponding Popen object. 96 # This will block current connection to the device until the command 97 # completes. 98 p = t.background('echo $PWD') 99 output, error = p.communicate() 100 101 # Run the command in the background on the device and return immediately. 102 # This will not block the connection, allowing to immediately execute another 103 # command. 104 t.kick_off('echo $PWD') 105 106 # This is used to invoke an executable binary on the device. This allows some 107 # finer-grained control over the invocation, such as specifying the directory 108 # in which the executable will run; however you're limited to a single binary 109 # and cannot construct complex commands (e.g. this does not allow chaining or 110 # piping several commands together). 111 output = t.invoke('echo', args=['$PWD'], in_directory='/') 112 113File Transfer 114~~~~~~~~~~~~~ 115 116.. code:: python 117 118 from devlib import LocalLinuxTarget 119 t = LocalLinuxTarget() 120 121 # "push" a file from the local machine onto the target device. 122 t.push('/path/to/local/file.txt', '/path/to/target/file.txt') 123 124 # "pull" a file from the target device into a location on the local machine 125 t.pull('/path/to/target/file.txt', '/path/to/local/file.txt') 126 127 # Install the specified binary on the target. This will deploy the file and 128 # ensure it's executable. This will *not* guarantee that the binary will be 129 # in PATH. Instead the path to the binary will be returned; this should be 130 # used to call the binary henceforth. 131 target_bin = t.install('/path/to/local/bin.exe') 132 # Example invocation: 133 output = t.execute('{} --some-option'.format(target_bin)) 134 135The usual access permission constraints on the user account (both on the target 136and the host) apply. 137 138Process Control 139~~~~~~~~~~~~~~~ 140 141.. code:: python 142 143 import signal 144 from devlib import LocalLinuxTarget 145 t = LocalLinuxTarget() 146 147 # return PIDs of all running instances of a process 148 pids = t.get_pids_of('sshd') 149 150 # kill a running process. This works the same ways as the kill command, so 151 # SIGTERM will be used by default. 152 t.kill(666, signal=signal.SIGKILL) 153 154 # kill all running instances of a process. 155 t.killall('badexe', signal=signal.SIGKILL) 156 157 # List processes running on the target. This retruns a list of parsed 158 # PsEntry records. 159 entries = t.ps() 160 # e.g. print virtual memory sizes of all running sshd processes: 161 print ', '.join(str(e.vsize) for e in entries if e.name == 'sshd') 162 163 164More... 165~~~~~~~ 166 167As mentioned previously, the above is not intended to be exhaustive 168documentation of the :class:`Target` interface. Please refer to the API 169documentation for the full list of attributes and methods and their parameters. 170 171Super User Privileges 172--------------------- 173 174It is not necessary for the account logged in on the target to have super user 175privileges, however the functionality will obviously be diminished, if that is 176not the case. ``devilib`` will determine if the logged in user has root 177privileges and the correct way to invoke it. You should avoid including "sudo" 178directly in your commands, instead, specify ``as_root=True`` where needed. This 179will make your scripts portable across multiple devices and OS's. 180 181 182On-Target Locations 183------------------- 184 185File system layouts vary wildly between devices and operating systems. 186Hard-coding absolute paths in your scripts will mean there is a good chance they 187will break if run on a different device. To help with this, ``devlib`` defines 188a couple of "standard" locations and a means of working with them. 189 190working_directory 191 This is a directory on the target readable and writable by the account 192 used to log in. This should generally be used for all output generated 193 by your script on the device and as the destination for all 194 host-to-target file transfers. It may or may not permit execution so 195 executables should not be run directly from here. 196 197executables_directory 198 This directory allows execution. This will be used by ``install()``. 199 200.. code:: python 201 202 from devlib import LocalLinuxTarget 203 t = LocalLinuxTarget() 204 205 # t.path is equivalent to Python standard library's os.path, and should be 206 # used in the same way. This insures that your scripts are portable across 207 # both target and host OS variations. e.g. 208 on_target_path = t.path.join(t.working_directory, 'assets.tar.gz') 209 t.push('/local/path/to/assets.tar.gz', on_target_path) 210 211 # Since working_directory is a common base path for on-target locations, 212 # there a short-hand for the above: 213 t.push('/local/path/to/assets.tar.gz', t.get_workpath('assets.tar.gz')) 214 215 216Modules 217------- 218 219Additional functionality is exposed via modules. Modules are initialized as 220attributes of a target instance. By default, ``hotplug``, ``cpufreq``, 221``cpuidle``, ``cgroups`` and ``hwmon`` will attempt to load on target; additional 222modules may be specified when creating a :class:`Target` instance. 223 224A module will probe the target for support before attempting to load. So if the 225underlying platform does not support particular functionality (e.g. the kernel 226on target device was built without hotplug support). To check whether a module 227has been successfully installed on a target, you can use ``has()`` method, e.g. 228 229.. code:: python 230 231 from devlib import LocalLinuxTarget 232 t = LocalLinuxTarget() 233 234 cpu0_freqs = [] 235 if t.has('cpufreq'): 236 cpu0_freqs = t.cpufreq.list_frequencies(0) 237 238 239Please see the modules documentation for more detail. 240 241 242Measurement and Trace 243--------------------- 244 245You can collected traces (currently, just ftrace) using 246:class:`TraceCollector`\ s. For example 247 248.. code:: python 249 250 from devlib import AndroidTarget, FtraceCollector 251 t = LocalLinuxTarget() 252 253 # Initialize a collector specifying the events you want to collect and 254 # the buffer size to be used. 255 trace = FtraceCollector(t, events=['power*'], buffer_size=40000) 256 257 # clear ftrace buffer 258 trace.reset() 259 260 # start trace collection 261 trace.start() 262 263 # Perform the operations you want to trace here... 264 import time; time.sleep(5) 265 266 # stop trace collection 267 trace.stop() 268 269 # extract the trace file from the target into a local file 270 trace.get_trace('/tmp/trace.bin') 271 272 # View trace file using Kernelshark (must be installed on the host). 273 trace.view('/tmp/trace.bin') 274 275 # Convert binary trace into text format. This would normally be done 276 # automatically during get_trace(), unless autoreport is set to False during 277 # instantiation of the trace collector. 278 trace.report('/tmp/trace.bin', '/tmp/trace.txt') 279 280In a similar way, :class:`Instrument` instances may be used to collect 281measurements (such as power) from targets that support it. Please see 282instruments documentation for more details. 283