blob: 7a60fb828ec475af40b08efa5852fcd1ec3ef0ac [file] [log] [blame]
Sergei Trofimov4e6afe92015-10-09 09:30:04 +01001Overview
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).'
Morten Rasmussen192fb522016-03-21 15:15:26 +000035 t2 = LinuxTarget(connection_settings={'host': '192.168.0.5',
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010036 '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
Morten Rasmussen192fb522016-03-21 15:15:26 +000060:class:`Target` API documentation for the full list of supported methods and
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010061more 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
Marc Bonnici1fd56362017-01-10 15:35:21 +000077safe side, it's a good idea to call this once at the beginning of your scripts.
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010078
79Command Execution
80~~~~~~~~~~~~~~~~~
81
82There are several ways to execute a command on the target. In each case, a
Marc Bonnici1fd56362017-01-10 15:35:21 +000083:class:`TargetError` will be raised if something goes wrong. In each case, it is
Sergei Trofimov4e6afe92015-10-09 09:30:04 +010084also 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
Marc Bonnici1fd56362017-01-10 15:35:21 +0000157 # List processes running on the target. This returns a list of parsed
Sergei Trofimov4e6afe92015-10-09 09:30:04 +0100158 # 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.