mirror of
				https://github.com/ARM-software/devlib.git
				synced 2025-10-31 14:01:20 +00:00 
			
		
		
		
	devlib initial commit.
This commit is contained in:
		
							
								
								
									
										282
									
								
								doc/overview.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										282
									
								
								doc/overview.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,282 @@ | ||||
| Overview | ||||
| ======== | ||||
|  | ||||
| A :class:`Target` instance serves as the main interface to the target device.  | ||||
| There currently three target interfaces: | ||||
|  | ||||
| - :class:`LinuxTarget` for interacting with Linux devices over SSH. | ||||
| - :class:`AndroidTraget` for interacting with Android devices over adb. | ||||
| - :class:`LocalLinuxTarget`: for interacting with the local Linux host. | ||||
|  | ||||
| They all work in more-or-less the same way, with the major difference being in | ||||
| how connection settings are specified; though there may also be a few APIs | ||||
| specific to a particular target type (e.g. :class:`AndroidTarget` exposes | ||||
| methods for working with logcat). | ||||
|  | ||||
|  | ||||
| Acquiring a Target | ||||
| ------------------ | ||||
|  | ||||
| To create an interface to your device, you just need to instantiate one of the | ||||
| :class:`Target` derivatives listed above, and pass it the right | ||||
| ``connection_settings``. Code snippet below gives a typical example of | ||||
| instantiating each of the three target types.  | ||||
|  | ||||
| .. code:: python | ||||
|  | ||||
|    from devlib import LocalLinuxTarget, LinuxTarget, AndroidTarget | ||||
|  | ||||
|    # Local machine requires no special connection settings. | ||||
|    t1 = LocalLinuxTarget() | ||||
|  | ||||
|    # For a Linux device, you will need to provide the normal SSH credentials. | ||||
|    # Both password-based, and key-based authentication is supported (password | ||||
|    # authentication requires sshpass to be installed on your host machine).' | ||||
|    t2 = LinuxTarget(connetion_settings={'host': '192.168.0.5', | ||||
|                                         'username': 'root', | ||||
|                                         'password': 'sekrit', | ||||
|                                         # or | ||||
|                                         'keyfile': '/home/me/.ssh/id_rsa'}) | ||||
|  | ||||
|    # For an Android target, you will need to pass the device name as reported | ||||
|    # by "adb devices". If there is only one device visible to adb, you can omit | ||||
|    # this setting and instantiate similar to a local target. | ||||
|    t3 = AndroidTarget(connection_settings={'device': '0123456789abcde'}) | ||||
|  | ||||
| Instantiating a target may take a second or two as the remote device will be | ||||
| queried to initialize :class:`Target`'s internal state. If you would like to | ||||
| create a :class:`Target` instance but not immediately connect to the remote | ||||
| device, you can pass ``connect=False`` parameter. If you do that, you would have | ||||
| to then explicitly call ``t.connect()`` before you can interact with the device. | ||||
|  | ||||
| There are a few additional parameters you can pass in instantiation besides | ||||
| ``connection_settings``, but they are usually unnecessary. Please see | ||||
| :class:`Target` API documentation for more details. | ||||
|  | ||||
| Target Interface | ||||
| ---------------- | ||||
|  | ||||
| This is a quick overview of the basic interface to the device. See | ||||
| :class:`Targeet` API documentation for the full list of supported methods and | ||||
| more detailed documentation. | ||||
|  | ||||
| One-time Setup | ||||
| ~~~~~~~~~~~~~~ | ||||
|  | ||||
| .. code:: python | ||||
|  | ||||
|    from devlib import LocalLinuxTarget | ||||
|    t = LocalLinuxTarget() | ||||
|  | ||||
|    t.setup() | ||||
|  | ||||
| This sets up the target for ``devlib`` interaction. This includes creating | ||||
| working directories, deploying busybox, etc. It's usually enough to do this once | ||||
| for a new device, as the changes this makes will persist across reboots. | ||||
| However, there is no issue with calling this multiple times, so, to be on the | ||||
| safe site, it's a good idea to call this once at the beginning of your scripts. | ||||
|  | ||||
| Command Execution | ||||
| ~~~~~~~~~~~~~~~~~ | ||||
|  | ||||
| There are several ways to execute a command on the target. In each case, a | ||||
| :class:`TargetError` will be raised if something goes wrong. In very case, it is | ||||
| also possible to specify ``as_root=True`` if the specified command should be | ||||
| executed as root. | ||||
|  | ||||
| .. code:: python | ||||
|  | ||||
|    from devlib import LocalLinuxTarget | ||||
|    t = LocalLinuxTarget() | ||||
|  | ||||
|    # Execute a command  | ||||
|    output = t.execute('echo $PWD') | ||||
|  | ||||
|    # Execute command via a subprocess and return the corresponding Popen object. | ||||
|    # This will block current connection to the device until the command | ||||
|    # completes. | ||||
|    p = t.background('echo $PWD') | ||||
|    output, error = p.communicate() | ||||
|  | ||||
|    # Run the command in the background on the device and return immediately. | ||||
|    # This will not block the connection, allowing to immediately execute another | ||||
|    # command.  | ||||
|    t.kick_off('echo $PWD') | ||||
|  | ||||
|    # This is used to invoke an executable binary on the device. This allows some | ||||
|    # finer-grained control over the invocation, such as specifying the directory | ||||
|    # in which the executable will run; however you're limited to a single binary | ||||
|    # and cannot construct complex commands (e.g. this does not allow chaining or | ||||
|    # piping several commands together). | ||||
|    output = t.invoke('echo', args=['$PWD'], in_directory='/') | ||||
|  | ||||
| File Transfer | ||||
| ~~~~~~~~~~~~~ | ||||
|  | ||||
| .. code:: python | ||||
|  | ||||
|    from devlib import LocalLinuxTarget | ||||
|    t = LocalLinuxTarget() | ||||
|  | ||||
|    # "push" a file from the local machine onto the target device. | ||||
|    t.push('/path/to/local/file.txt', '/path/to/target/file.txt') | ||||
|  | ||||
|    # "pull" a file from the target device into a location on the local machine | ||||
|    t.pull('/path/to/target/file.txt', '/path/to/local/file.txt') | ||||
|  | ||||
|    # Install the specified binary on the target. This will deploy the file and | ||||
|    # ensure it's executable. This will *not* guarantee that the binary will be  | ||||
|    # in PATH. Instead the path to the binary will be returned; this should be | ||||
|    # used to call the binary henceforth. | ||||
|    target_bin = t.install('/path/to/local/bin.exe') | ||||
|    # Example invocation: | ||||
|    output = t.execute('{} --some-option'.format(target_bin)) | ||||
|  | ||||
| The usual access permission constraints on the user account (both on the target | ||||
| and the host) apply.  | ||||
|  | ||||
| Process Control | ||||
| ~~~~~~~~~~~~~~~ | ||||
|  | ||||
| .. code:: python | ||||
|  | ||||
|    import signal | ||||
|    from devlib import LocalLinuxTarget | ||||
|    t = LocalLinuxTarget() | ||||
|  | ||||
|    # return PIDs of all running instances of a process | ||||
|    pids = t.get_pids_of('sshd') | ||||
|  | ||||
|    # kill a running process. This works the same ways as the kill command, so | ||||
|    # SIGTERM will be used by default. | ||||
|    t.kill(666, signal=signal.SIGKILL) | ||||
|  | ||||
|    # kill all running instances of a process. | ||||
|    t.killall('badexe', signal=signal.SIGKILL) | ||||
|  | ||||
|    # List processes running on the target. This retruns a list of parsed | ||||
|    # PsEntry records. | ||||
|    entries = t.ps() | ||||
|    # e.g.  print virtual memory sizes of all running sshd processes: | ||||
|    print ', '.join(str(e.vsize) for e in entries if e.name == 'sshd') | ||||
|  | ||||
|  | ||||
| More... | ||||
| ~~~~~~~ | ||||
|  | ||||
| As mentioned previously, the above is not intended to be exhaustive | ||||
| documentation of the :class:`Target` interface. Please refer to the API | ||||
| documentation for the full list of attributes and methods and their parameters. | ||||
|  | ||||
| Super User Privileges | ||||
| --------------------- | ||||
|  | ||||
| It is not necessary for the account logged in on the target to have super user | ||||
| privileges, however the functionality will obviously be diminished, if that is | ||||
| not the case. ``devilib`` will determine if the logged in user has root | ||||
| privileges and the correct way to invoke it. You should avoid including "sudo" | ||||
| directly in your commands, instead, specify ``as_root=True`` where needed. This | ||||
| will make your scripts portable across multiple devices and OS's. | ||||
|  | ||||
|  | ||||
| On-Target Locations | ||||
| ------------------- | ||||
|  | ||||
| File system layouts vary wildly between devices and operating systems. | ||||
| Hard-coding absolute paths in your scripts will mean there is a good chance they | ||||
| will break if run on a different device.  To help with this, ``devlib`` defines | ||||
| a couple of "standard" locations and a means of working with them. | ||||
|  | ||||
| working_directory | ||||
|         This is a directory on the target readable and writable by the account | ||||
|         used to log in. This should generally be used for all output generated | ||||
|         by your script on the device and as the destination for all | ||||
|         host-to-target file transfers. It may or may not permit execution so | ||||
|         executables should not be run directly from here. | ||||
|          | ||||
| executables_directory | ||||
|         This directory allows execution. This will be used by ``install()``. | ||||
|  | ||||
| .. code:: python | ||||
|  | ||||
|    from devlib import LocalLinuxTarget | ||||
|    t = LocalLinuxTarget() | ||||
|  | ||||
|    # t.path  is equivalent to Python standard library's os.path, and should be | ||||
|    # used in the same way. This insures that your scripts are portable across | ||||
|    # both target and host OS variations. e.g. | ||||
|    on_target_path = t.path.join(t.working_directory, 'assets.tar.gz') | ||||
|    t.push('/local/path/to/assets.tar.gz', on_target_path) | ||||
|  | ||||
|    # Since working_directory is a common base path for on-target locations, | ||||
|    # there a short-hand for the above: | ||||
|    t.push('/local/path/to/assets.tar.gz', t.get_workpath('assets.tar.gz')) | ||||
|  | ||||
|  | ||||
| Modules | ||||
| ------- | ||||
|  | ||||
| Additional functionality is exposed via modules. Modules are initialized as | ||||
| attributes of a target instance. By default, ``hotplug``, ``cpufreq``, | ||||
| ``cpuidle``, ``cgroups`` and ``hwmon`` will attempt to load on target; additional | ||||
| modules may be specified when creating a :class:`Target` instance. | ||||
|  | ||||
| A module will probe the target for support before attempting to load. So if the | ||||
| underlying platform does not support particular functionality (e.g. the kernel | ||||
| on target device was built without hotplug support). To check whether a module | ||||
| has been successfully installed on a target, you can use ``has()`` method, e.g. | ||||
|  | ||||
| .. code:: python | ||||
|  | ||||
|    from devlib import LocalLinuxTarget | ||||
|    t = LocalLinuxTarget() | ||||
|  | ||||
|    cpu0_freqs = [] | ||||
|    if t.has('cpufreq'): | ||||
|        cpu0_freqs = t.cpufreq.list_frequencies(0) | ||||
|  | ||||
|  | ||||
| Please see the modules documentation for more detail. | ||||
|  | ||||
|  | ||||
| Measurement and Trace | ||||
| --------------------- | ||||
|  | ||||
| You can collected traces (currently, just ftrace) using | ||||
| :class:`TraceCollector`\ s. For example | ||||
|  | ||||
| .. code:: python | ||||
|  | ||||
|    from devlib import AndroidTarget, FtraceCollector | ||||
|    t = LocalLinuxTarget() | ||||
|    | ||||
|    # Initialize a collector specifying the events you want to collect and | ||||
|    # the buffer size to be used. | ||||
|    trace = FtraceCollector(t, events=['power*'], buffer_size=40000) | ||||
|  | ||||
|    # clear ftrace buffer | ||||
|    trace.reset() | ||||
|  | ||||
|    # start trace collection | ||||
|    trace.start() | ||||
|  | ||||
|    # Perform the operations you want to trace here... | ||||
|    import time; time.sleep(5) | ||||
|  | ||||
|    # stop trace collection | ||||
|    trace.stop() | ||||
|  | ||||
|    # extract the trace file from the target into a local file | ||||
|    trace.get_trace('/tmp/trace.bin') | ||||
|  | ||||
|    # View trace file using Kernelshark (must be installed on the host). | ||||
|    trace.view('/tmp/trace.bin') | ||||
|  | ||||
|    # Convert binary trace into text format. This would normally be done | ||||
|    # automatically during get_trace(), unless autoreport is set to False during | ||||
|    # instantiation of the trace collector. | ||||
|    trace.report('/tmp/trace.bin', '/tmp/trace.txt') | ||||
|  | ||||
| In a similar way, :class:`Instrument` instances may be used to collect | ||||
| measurements (such as power) from targets that support it. Please see | ||||
| instruments documentation for more details. | ||||
		Reference in New Issue
	
	Block a user