mirror of
				https://github.com/ARM-software/workload-automation.git
				synced 2025-11-04 00:52:08 +00:00 
			
		
		
		
	Merge pull request #200 from ep1cman/revent_fixes
revent: Fixed dump command segfault
This commit is contained in:
		@@ -106,3 +106,107 @@ where as UI Automator only works for Android UI elements (such as text boxes or
 | 
			
		||||
radio buttons), which makes the latter useless for things like games. Recording
 | 
			
		||||
revent sequence is also faster than writing automation code (on the other hand,
 | 
			
		||||
one would need maintain a different revent log for each screen resolution).
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
File format of revent
 | 
			
		||||
=====================
 | 
			
		||||
 | 
			
		||||
.. note:: All values below are little endian
 | 
			
		||||
 | 
			
		||||
Recording structure of revent
 | 
			
		||||
-----------------------------
 | 
			
		||||
 | 
			
		||||
revent recordings are made of of five parts:
 | 
			
		||||
 | 
			
		||||
 * A "magic" string of `REVENT` to help identify revent recordings.
 | 
			
		||||
 * A unsigned integer representing the revent file format version.
 | 
			
		||||
 * A signed integer that gives the number of devices in this recording.
 | 
			
		||||
 * A series of device paths, the number of which is given in the previous field.
 | 
			
		||||
   For more detail see `Device path structure`_ below.
 | 
			
		||||
 * An unlimited number of recorded events. For more detail see `Event Structure`_
 | 
			
		||||
   below.
 | 
			
		||||
 | 
			
		||||
::
 | 
			
		||||
 | 
			
		||||
     0                   1                   2                   3
 | 
			
		||||
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                             MAGIC                             |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |          MAGIC cont.          |            Version            |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                       Number of devices                       |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                                                               |
 | 
			
		||||
    |             Device paths              +-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                                       |                       |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+                       |
 | 
			
		||||
    |                                                               |
 | 
			
		||||
    |                             Events                            |
 | 
			
		||||
    |                                                               |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Device path structure
 | 
			
		||||
----------------------
 | 
			
		||||
 | 
			
		||||
This part of an revent recording is used to store the paths to input devices used in the
 | 
			
		||||
recording. It consists of:
 | 
			
		||||
 | 
			
		||||
 * A signed integer giving the size of the following string.
 | 
			
		||||
 * A string, with a maximum length of 30, containing the path of an input device.
 | 
			
		||||
 | 
			
		||||
::
 | 
			
		||||
 | 
			
		||||
     0                   1                   2                   3
 | 
			
		||||
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                     Length of device path                     |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                                                               |
 | 
			
		||||
    |                          Device path                          |
 | 
			
		||||
    |                                                               |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
 | 
			
		||||
Event structure
 | 
			
		||||
---------------
 | 
			
		||||
 | 
			
		||||
The majority of an revent recording will be made up of the input events that were
 | 
			
		||||
recorded. There and be an unlimited number of these events in an revent file and they
 | 
			
		||||
are structured as follows:
 | 
			
		||||
 | 
			
		||||
 * A signed integer representing which device from the list of device paths
 | 
			
		||||
   this event is for (zero indexed). E.g. Device ID = 3 would be the 4th
 | 
			
		||||
   device in the list of device paths.
 | 
			
		||||
 * 32 bits of padding
 | 
			
		||||
 * A signed integer representing the number of seconds since "epoch" when the
 | 
			
		||||
   event was recorded.
 | 
			
		||||
 * A signed integer representing the microseconds part of the timestamp.
 | 
			
		||||
 * An unsigned integer representing the event type
 | 
			
		||||
 * An unsigned integer representing the event code
 | 
			
		||||
 * An unsigned integer representing the event value
 | 
			
		||||
 | 
			
		||||
For more information about the event type, code and value please read:
 | 
			
		||||
https://www.kernel.org/doc/Documentation/input/event-codes.txt
 | 
			
		||||
 | 
			
		||||
::
 | 
			
		||||
 | 
			
		||||
     0                   1                   2                   3
 | 
			
		||||
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                           Device ID                           |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                            PADDING                            |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                       Timestamp Seconds                       |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                    Timestamp Seconds cont.                    |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                     Timestamp Micoseconds                     |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                  Timestamp Micoseconds cont.                  |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |          Event Type           |          Event Code           |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
    |                          Event Value                          |
 | 
			
		||||
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,8 @@
 | 
			
		||||
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import signal
 | 
			
		||||
from math import ceil
 | 
			
		||||
 | 
			
		||||
from wlauto import ExtensionLoader, Command, settings
 | 
			
		||||
from wlauto.common.resources import Executable
 | 
			
		||||
@@ -22,6 +24,7 @@ from wlauto.core.resource import NO_ONE
 | 
			
		||||
from wlauto.core.resolver import ResourceResolver
 | 
			
		||||
from wlauto.core.configuration import RunConfiguration
 | 
			
		||||
from wlauto.core.agenda import Agenda
 | 
			
		||||
from wlauto.utils.revent import ReventParser
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RecordCommand(Command):
 | 
			
		||||
@@ -114,13 +117,15 @@ class RecordCommand(Command):
 | 
			
		||||
 | 
			
		||||
        self.logger.info("Press Enter when you are ready to record...")
 | 
			
		||||
        raw_input("")
 | 
			
		||||
        command = "{} record -t 100000 -s {}".format(self.target_binary, revent_file)
 | 
			
		||||
        command = "{} record -s {}".format(self.target_binary, revent_file)
 | 
			
		||||
        self.device.kick_off(command)
 | 
			
		||||
 | 
			
		||||
        self.logger.info("Press Enter when you have finished recording...")
 | 
			
		||||
        raw_input("")
 | 
			
		||||
        self.device.killall("revent")
 | 
			
		||||
 | 
			
		||||
        self.device.killall("revent", signal.SIGTERM)
 | 
			
		||||
        self.logger.info("Waiting for revent to finish")
 | 
			
		||||
        while self.device.get_pids_of("revent"):
 | 
			
		||||
            pass
 | 
			
		||||
        self.logger.info("Pulling files from device")
 | 
			
		||||
        self.device.pull_file(revent_file, args.output or os.getcwdu())
 | 
			
		||||
 | 
			
		||||
@@ -154,8 +159,10 @@ class ReplayCommand(RecordCommand):
 | 
			
		||||
            self.logger.info("Starting {}".format(args.package))
 | 
			
		||||
            self.device.execute('monkey -p {} -c android.intent.category.LAUNCHER 1'.format(args.package))
 | 
			
		||||
 | 
			
		||||
        self.logger.info("Replaying recording")
 | 
			
		||||
        command = "{} replay {}".format(self.target_binary, revent_file)
 | 
			
		||||
        self.device.execute(command)
 | 
			
		||||
        timeout = ceil(ReventParser.get_revent_duration(args.revent)) + 30
 | 
			
		||||
        self.device.execute(command, timeout=timeout)
 | 
			
		||||
        self.logger.info("Finished replay")
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -16,6 +16,7 @@
 | 
			
		||||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import time
 | 
			
		||||
from math import ceil
 | 
			
		||||
 | 
			
		||||
from wlauto.core.extension import Parameter
 | 
			
		||||
from wlauto.core.workload import Workload
 | 
			
		||||
@@ -25,6 +26,7 @@ from wlauto.common.resources import ExtensionAsset, Executable
 | 
			
		||||
from wlauto.exceptions import WorkloadError, ResourceError, ConfigError
 | 
			
		||||
from wlauto.utils.android import ApkInfo, ANDROID_NORMAL_PERMISSIONS
 | 
			
		||||
from wlauto.utils.types import boolean
 | 
			
		||||
from wlauto.utils.revent import ReventParser
 | 
			
		||||
import wlauto.common.android.resources
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -322,16 +324,13 @@ AndroidBenchmark = ApkWorkload  # backward compatibility
 | 
			
		||||
 | 
			
		||||
class ReventWorkload(Workload):
 | 
			
		||||
 | 
			
		||||
    default_setup_timeout = 5 * 60  # in seconds
 | 
			
		||||
    default_run_timeout = 10 * 60  # in seconds
 | 
			
		||||
 | 
			
		||||
    def __init__(self, device, _call_super=True, **kwargs):
 | 
			
		||||
        if _call_super:
 | 
			
		||||
            super(ReventWorkload, self).__init__(device, **kwargs)
 | 
			
		||||
        devpath = self.device.path
 | 
			
		||||
        self.on_device_revent_binary = devpath.join(self.device.binaries_directory, 'revent')
 | 
			
		||||
        self.setup_timeout = kwargs.get('setup_timeout', self.default_setup_timeout)
 | 
			
		||||
        self.run_timeout = kwargs.get('run_timeout', self.default_run_timeout)
 | 
			
		||||
        self.setup_timeout = kwargs.get('setup_timeout', None)
 | 
			
		||||
        self.run_timeout = kwargs.get('run_timeout', None)
 | 
			
		||||
        self.revent_setup_file = None
 | 
			
		||||
        self.revent_run_file = None
 | 
			
		||||
        self.on_device_setup_revent = None
 | 
			
		||||
@@ -346,6 +345,10 @@ class ReventWorkload(Workload):
 | 
			
		||||
        self.on_device_run_revent = devpath.join(self.device.working_directory,
 | 
			
		||||
                                                 os.path.split(self.revent_run_file)[-1])
 | 
			
		||||
        self._check_revent_files(context)
 | 
			
		||||
        default_setup_timeout = ceil(ReventParser.get_revent_duration(self.revent_setup_file)) + 30
 | 
			
		||||
        default_run_timeout = ceil(ReventParser.get_revent_duration(self.revent_run_file)) + 30
 | 
			
		||||
        self.setup_timeout = self.setup_timeout or default_setup_timeout
 | 
			
		||||
        self.run_timeout = self.run_timeout or default_run_timeout
 | 
			
		||||
 | 
			
		||||
    def setup(self, context):
 | 
			
		||||
        self.device.killall('revent')
 | 
			
		||||
 
 | 
			
		||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										166
									
								
								wlauto/external/revent/revent.c
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										166
									
								
								wlauto/external/revent/revent.c
									
									
									
									
										vendored
									
									
								
							@@ -42,7 +42,10 @@
 | 
			
		||||
 | 
			
		||||
#define INPDEV_MAX_DEVICES  16
 | 
			
		||||
#define INPDEV_MAX_PATH     30
 | 
			
		||||
const char magic[] = "REVENT";
 | 
			
		||||
 | 
			
		||||
//This should be incremented if any changes are made to the file format
 | 
			
		||||
uint16_t file_version = 1;
 | 
			
		||||
 | 
			
		||||
#ifndef ANDROID
 | 
			
		||||
int strlcpy(char *dest, char *source,  size_t size)
 | 
			
		||||
@@ -110,6 +113,56 @@ bool_t is_numeric(char *string)
 | 
			
		||||
    return TRUE;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Has to be done this explicitly to maintain compatibility between
 | 
			
		||||
// 32-bit and 64-bit devices
 | 
			
		||||
int read_replay_event(int fdin, replay_event_t* ev)
 | 
			
		||||
{
 | 
			
		||||
    size_t rb;
 | 
			
		||||
 | 
			
		||||
    rb = read(fdin, &(ev->dev_idx), sizeof(int32_t));
 | 
			
		||||
    if (rb < (int)sizeof(int32_t)) return -1;
 | 
			
		||||
 | 
			
		||||
    rb = read(fdin, &(ev->_padding), sizeof(int32_t));
 | 
			
		||||
    if (rb < (int)sizeof(int32_t)) return -1;
 | 
			
		||||
 | 
			
		||||
    struct timeval time;
 | 
			
		||||
    uint64_t temp_time;
 | 
			
		||||
    rb = read(fdin, &temp_time, sizeof(uint64_t));
 | 
			
		||||
    if (rb < (int)sizeof(uint64_t)) return -1;
 | 
			
		||||
    time.tv_sec = (time_t)temp_time;
 | 
			
		||||
 | 
			
		||||
    rb = read(fdin, &temp_time, sizeof(uint64_t));
 | 
			
		||||
    if (rb < (int)sizeof(uint64_t)) return -1;
 | 
			
		||||
    time.tv_usec = (suseconds_t)temp_time;
 | 
			
		||||
 | 
			
		||||
    ev->event.time = time;
 | 
			
		||||
 | 
			
		||||
    rb = read(fdin, &(ev->event.type), sizeof(uint16_t));
 | 
			
		||||
    if (rb < (int)sizeof(uint16_t)) return -1;
 | 
			
		||||
 | 
			
		||||
    rb = read(fdin, &(ev->event.code), sizeof(uint16_t));
 | 
			
		||||
    if (rb < (int)sizeof(uint16_t)) return -1;
 | 
			
		||||
 | 
			
		||||
    rb = read(fdin, &(ev->event.value), sizeof(int32_t));
 | 
			
		||||
    if (rb < (int)sizeof(int32_t)) return -1;
 | 
			
		||||
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void write_input_event(FILE * fdout, struct input_event* ev)
 | 
			
		||||
{
 | 
			
		||||
    uint64_t time;
 | 
			
		||||
    time = (uint64_t)ev->time.tv_sec;
 | 
			
		||||
    fwrite(&time, sizeof(uint64_t), 1, fdout);
 | 
			
		||||
    time = (uint64_t)ev->time.tv_usec;
 | 
			
		||||
    fwrite(&time, sizeof(uint64_t), 1, fdout);
 | 
			
		||||
    fwrite(&(ev->type), sizeof(uint16_t), 1, fdout);
 | 
			
		||||
    fwrite(&(ev->code), sizeof(uint16_t), 1, fdout);
 | 
			
		||||
    fwrite(&(ev->value), sizeof(int32_t), 1, fdout);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
off_t get_file_size(const char *filename) {
 | 
			
		||||
    struct stat st;
 | 
			
		||||
 | 
			
		||||
@@ -191,41 +244,52 @@ void dump(const char *logfile)
 | 
			
		||||
    int fdin = open(logfile, O_RDONLY);
 | 
			
		||||
    if (fdin < 0) die("Could not open eventlog %s\n", logfile);
 | 
			
		||||
 | 
			
		||||
    int nfds;
 | 
			
		||||
    size_t rb = read(fdin, &nfds, sizeof(nfds));
 | 
			
		||||
    if (rb != sizeof(nfds)) die("problems reading eventlog\n");
 | 
			
		||||
    int *fds = malloc(sizeof(int)*nfds);
 | 
			
		||||
    if (!fds) die("out of memory\n");
 | 
			
		||||
 | 
			
		||||
    int32_t len;
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    char buf[INPDEV_MAX_PATH];
 | 
			
		||||
 | 
			
		||||
    //Read magic
 | 
			
		||||
    len = strlen(magic);
 | 
			
		||||
    size_t rb = read(fdin, &buf[0], len);
 | 
			
		||||
    if (rb != len) die("problems reading eventlog\n");
 | 
			
		||||
    if(strcmp(magic, buf) != 0)
 | 
			
		||||
        die("File is not an revent recording, are you using an old recording?");
 | 
			
		||||
 | 
			
		||||
    //Read file format version
 | 
			
		||||
    uint16_t version;
 | 
			
		||||
    rb = read(fdin, &version, sizeof(version));
 | 
			
		||||
    if (rb != sizeof(version)) die("problems reading eventlog\n");
 | 
			
		||||
    printf("File format version: %i\n", version);
 | 
			
		||||
 | 
			
		||||
    int32_t nfds;
 | 
			
		||||
    rb = read(fdin, &nfds, sizeof(nfds));
 | 
			
		||||
    if (rb != sizeof(nfds)) die("problems reading eventlog\n");
 | 
			
		||||
    int *fds = malloc(sizeof(int)*nfds);
 | 
			
		||||
    if (!fds) die("out of memory\n");
 | 
			
		||||
 | 
			
		||||
    inpdev_t *inpdev = malloc(sizeof(inpdev_t));
 | 
			
		||||
    inpdev->id_pathc = 0;
 | 
			
		||||
    inpdev->id_pathc = nfds;
 | 
			
		||||
    for (i=0; i<nfds; i++) {
 | 
			
		||||
        memset(buf, 0, sizeof(buf));
 | 
			
		||||
        rb = read(fdin, &len, sizeof(len));
 | 
			
		||||
        if (rb != sizeof(len)) die("problems reading eventlog\n");
 | 
			
		||||
        if (len >= INPDEV_MAX_PATH) die("path length too long, file corrupt");
 | 
			
		||||
        rb = read(fdin, &buf[0], len);
 | 
			
		||||
        if (rb != len) die("problems reading eventlog\n");
 | 
			
		||||
        strlcpy(inpdev->id_pathv[inpdev->id_pathc], buf, INPDEV_MAX_PATH);
 | 
			
		||||
        inpdev->id_pathv[inpdev->id_pathc][INPDEV_MAX_PATH-1] = '\0';
 | 
			
		||||
        inpdev->id_pathc++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    replay_event_t rep_ev;
 | 
			
		||||
    struct input_event ev;
 | 
			
		||||
    int count = 0;
 | 
			
		||||
    while(1) {
 | 
			
		||||
        int32_t idx;
 | 
			
		||||
        rb = read(fdin, &idx, sizeof(idx));
 | 
			
		||||
        if (rb != sizeof(idx)) break;
 | 
			
		||||
        rb = read(fdin, &ev, sizeof(ev));
 | 
			
		||||
        if (rb < (int)sizeof(ev)) break;
 | 
			
		||||
        if (read_replay_event(fdin, &rep_ev) == -1)
 | 
			
		||||
            break;
 | 
			
		||||
        ev = rep_ev.event;
 | 
			
		||||
 | 
			
		||||
        printf("%10u.%-6u %30s type %2d code %3d value %4d\n",
 | 
			
		||||
                (unsigned int)ev.time.tv_sec, (unsigned int)ev.time.tv_usec,
 | 
			
		||||
                inpdev->id_pathv[idx], ev.type, ev.code, ev.value);
 | 
			
		||||
                inpdev->id_pathv[rep_ev.dev_idx], ev.type, ev.code, ev.value);
 | 
			
		||||
        count++;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -247,7 +311,22 @@ int replay_buffer_init(replay_buffer_t **buffer, const char *logfile)
 | 
			
		||||
    if (fdin < 0)
 | 
			
		||||
        die("Could not open eventlog %s\n", logfile);
 | 
			
		||||
 | 
			
		||||
    size_t rb = read(fdin, &(buff->num_fds), sizeof(buff->num_fds));
 | 
			
		||||
    int32_t len, i;
 | 
			
		||||
 | 
			
		||||
    //Read magic
 | 
			
		||||
    char buf[7];
 | 
			
		||||
    len = strlen(magic);
 | 
			
		||||
    size_t rb = read(fdin, &buf[0], len);
 | 
			
		||||
    if (rb != len) die("problems reading eventlog\n");
 | 
			
		||||
    if(strcmp(magic, buf) != 0)
 | 
			
		||||
        die("File is not an revent recording, are you using an old recording?");
 | 
			
		||||
 | 
			
		||||
    //Read file format version
 | 
			
		||||
    uint16_t version;
 | 
			
		||||
    rb = read(fdin, &version, sizeof(version));
 | 
			
		||||
    if (rb != sizeof(version)) die("problems reading eventlog\n");
 | 
			
		||||
 | 
			
		||||
    rb = read(fdin, &(buff->num_fds), sizeof(buff->num_fds));
 | 
			
		||||
    if (rb!=sizeof(buff->num_fds))
 | 
			
		||||
        die("problems reading eventlog\n");
 | 
			
		||||
 | 
			
		||||
@@ -255,13 +334,15 @@ int replay_buffer_init(replay_buffer_t **buffer, const char *logfile)
 | 
			
		||||
    if (!buff->fds)
 | 
			
		||||
        die("out of memory\n");
 | 
			
		||||
 | 
			
		||||
    int32_t len, i;
 | 
			
		||||
    char path_buff[256]; // should be more than enough
 | 
			
		||||
 | 
			
		||||
    char path_buff[INPDEV_MAX_PATH];
 | 
			
		||||
    for (i = 0; i < buff->num_fds; i++) {
 | 
			
		||||
        memset(path_buff, 0, sizeof(path_buff));
 | 
			
		||||
        rb = read(fdin, &len, sizeof(len));
 | 
			
		||||
        if (rb!=sizeof(len))
 | 
			
		||||
            die("problems reading eventlog\n");
 | 
			
		||||
        if (len >= INPDEV_MAX_PATH)
 | 
			
		||||
            die("path length too long, file corrupt");
 | 
			
		||||
        rb = read(fdin, &path_buff[0], len);
 | 
			
		||||
        if (rb != len)
 | 
			
		||||
            die("problems reading eventlog\n");
 | 
			
		||||
@@ -275,8 +356,7 @@ int replay_buffer_init(replay_buffer_t **buffer, const char *logfile)
 | 
			
		||||
    replay_event_t rep_ev;
 | 
			
		||||
    i = 0;
 | 
			
		||||
    while(1) {
 | 
			
		||||
        rb = read(fdin, &rep_ev, sizeof(rep_ev));
 | 
			
		||||
        if (rb < (int)sizeof(rep_ev))
 | 
			
		||||
        if (read_replay_event(fdin, &rep_ev) == -1)
 | 
			
		||||
            break;
 | 
			
		||||
 | 
			
		||||
        if (i == 0) {
 | 
			
		||||
@@ -369,6 +449,8 @@ void usage()
 | 
			
		||||
           "                -d DEVICE  the number of the input device form which\n"
 | 
			
		||||
           "                           events will be recoreded. If not specified, \n"
 | 
			
		||||
           "                           all available inputs will be used.\n"
 | 
			
		||||
           "                -s         Recording will not be stopped if there is \n"
 | 
			
		||||
           "                           input on STDIN.\n"
 | 
			
		||||
           "\n"
 | 
			
		||||
           "        replay FILE\n"
 | 
			
		||||
           "            replays previously recorded events from the specified file.\n"
 | 
			
		||||
@@ -488,19 +570,7 @@ int count;
 | 
			
		||||
 | 
			
		||||
void term_handler(int signum)
 | 
			
		||||
{
 | 
			
		||||
    int32_t i;
 | 
			
		||||
    for (i=0; i < inpdev->id_pathc; i++)
 | 
			
		||||
    {
 | 
			
		||||
        close(fds[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fclose(fdout);
 | 
			
		||||
    free(fds);
 | 
			
		||||
    dprintf("Recorded %d events\n", count);
 | 
			
		||||
 | 
			
		||||
    inpdev_close(inpdev);
 | 
			
		||||
    revent_args_close(rargs);
 | 
			
		||||
    exit(0);
 | 
			
		||||
    (void)signum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void record(inpdev_t *inpdev, int delay, const char *logfile)
 | 
			
		||||
@@ -524,6 +594,11 @@ void record(inpdev_t *inpdev, int delay, const char *logfile)
 | 
			
		||||
    fdout = fopen(logfile, "wb");
 | 
			
		||||
    if (!fdout) die("Could not open eventlog %s\n", logfile);
 | 
			
		||||
 | 
			
		||||
    //Write magic & file format version
 | 
			
		||||
    fwrite(&magic, strlen(magic), 1, fdout);
 | 
			
		||||
    fwrite(&file_version, sizeof(file_version), 1, fdout);
 | 
			
		||||
 | 
			
		||||
    //Write device paths
 | 
			
		||||
    fwrite(&inpdev->id_pathc, sizeof(inpdev->id_pathc), 1, fdout);
 | 
			
		||||
    for (i=0; i<inpdev->id_pathc; i++) {
 | 
			
		||||
        int32_t len = strlen(inpdev->id_pathv[i]);
 | 
			
		||||
@@ -536,11 +611,17 @@ void record(inpdev_t *inpdev, int delay, const char *logfile)
 | 
			
		||||
        fds[i] = open(inpdev->id_pathv[i], O_RDONLY);
 | 
			
		||||
        if (fds[i]>maxfd) maxfd = fds[i];
 | 
			
		||||
        dprintf("opened %s with %d\n", inpdev->id_pathv[i], fds[i]);
 | 
			
		||||
        if (fds[i]<0) die("could not open \%s\n", inpdev->id_pathv[i]);
 | 
			
		||||
        if (fds[i]<0) die("could not open %s\n", inpdev->id_pathv[i]);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //Block SIGTERM
 | 
			
		||||
    sigset_t sigset, oldset;
 | 
			
		||||
    sigemptyset(&sigset);
 | 
			
		||||
    sigaddset(&sigset, SIGTERM);
 | 
			
		||||
    sigprocmask(SIG_BLOCK, &sigset, &oldset);
 | 
			
		||||
 | 
			
		||||
    count = 0;
 | 
			
		||||
    struct timeval tout;
 | 
			
		||||
    struct timespec tout;
 | 
			
		||||
    while(1)
 | 
			
		||||
    {
 | 
			
		||||
        FD_ZERO(&readfds);
 | 
			
		||||
@@ -552,10 +633,13 @@ void record(inpdev_t *inpdev, int delay, const char *logfile)
 | 
			
		||||
            FD_SET(fds[i], &readfds);
 | 
			
		||||
        /* wait for input */
 | 
			
		||||
        tout.tv_sec = delay;
 | 
			
		||||
        tout.tv_usec = 0;
 | 
			
		||||
        int32_t r = select(maxfd+1, &readfds, NULL, NULL, &tout);
 | 
			
		||||
        tout.tv_nsec = 0;
 | 
			
		||||
        int32_t r = pselect(maxfd+1, &readfds, NULL, NULL, &tout, &oldset);
 | 
			
		||||
        if (errno == EINTR)
 | 
			
		||||
            break;
 | 
			
		||||
        /* dprintf("got %d (err %d)\n", r, errno); */
 | 
			
		||||
        if (!r) break;
 | 
			
		||||
        if (!r)
 | 
			
		||||
            break;
 | 
			
		||||
        if (wait_for_stdin && FD_ISSET(STDIN_FILENO, &readfds)) {
 | 
			
		||||
            // in this case the key down for the return key will be recorded
 | 
			
		||||
            // so we need to up the key up
 | 
			
		||||
@@ -566,12 +650,12 @@ void record(inpdev_t *inpdev, int delay, const char *logfile)
 | 
			
		||||
            gettimeofday(&ev.time, NULL);
 | 
			
		||||
            fwrite(&keydev, sizeof(keydev), 1, fdout);
 | 
			
		||||
            fwrite(&_padding, sizeof(_padding), 1, fdout);
 | 
			
		||||
            fwrite(&ev, sizeof(ev), 1, fdout);
 | 
			
		||||
            write_input_event(fdout, &ev);
 | 
			
		||||
            memset(&ev, 0, sizeof(ev)); // SYN
 | 
			
		||||
            gettimeofday(&ev.time, NULL);
 | 
			
		||||
            fwrite(&keydev, sizeof(keydev), 1, fdout);
 | 
			
		||||
            fwrite(&_padding, sizeof(_padding), 1, fdout);
 | 
			
		||||
            fwrite(&ev, sizeof(ev), 1, fdout);
 | 
			
		||||
            write_input_event(fdout, &ev);
 | 
			
		||||
            dprintf("added fake return exiting...\n");
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
@@ -589,7 +673,7 @@ void record(inpdev_t *inpdev, int delay, const char *logfile)
 | 
			
		||||
                    keydev = i;
 | 
			
		||||
                fwrite(&i, sizeof(i), 1, fdout);
 | 
			
		||||
                fwrite(&_padding, sizeof(_padding), 1, fdout);
 | 
			
		||||
                fwrite(&ev, sizeof(ev), 1, fdout);
 | 
			
		||||
                write_input_event(fdout, &ev);
 | 
			
		||||
                count++;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -33,6 +33,7 @@ from wlauto.exceptions import ResourceError
 | 
			
		||||
from wlauto.utils.android import ApkInfo
 | 
			
		||||
from wlauto.utils.misc import ensure_directory_exists as _d, ensure_file_directory_exists as _f, sha256, urljoin
 | 
			
		||||
from wlauto.utils.types import boolean
 | 
			
		||||
from wlauto.utils.revent import ReventParser
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
logging.getLogger("requests").setLevel(logging.WARNING)
 | 
			
		||||
@@ -98,7 +99,12 @@ class ReventGetter(ResourceGetter):
 | 
			
		||||
            location = _d(os.path.join(self.get_base_location(resource), 'revent_files'))
 | 
			
		||||
            for candidate in os.listdir(location):
 | 
			
		||||
                if candidate.lower() == filename.lower():
 | 
			
		||||
                    return os.path.join(location, candidate)
 | 
			
		||||
                    path = os.path.join(location, candidate)
 | 
			
		||||
                    try:
 | 
			
		||||
                        ReventParser.check_revent_file(path)
 | 
			
		||||
                        return path
 | 
			
		||||
                    except ValueError as e:
 | 
			
		||||
                        self.logger.warning(e.message)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PackageApkGetter(PackageFileGetter):
 | 
			
		||||
@@ -430,7 +436,11 @@ class HttpGetter(ResourceGetter):
 | 
			
		||||
                for asset in assets:
 | 
			
		||||
                    pathname = os.path.basename(asset['path']).lower()
 | 
			
		||||
                    if pathname == filename:
 | 
			
		||||
                        return asset
 | 
			
		||||
                        try:
 | 
			
		||||
                            ReventParser.check_revent_file(asset['path'])
 | 
			
		||||
                            return asset
 | 
			
		||||
                        except ValueError as e:
 | 
			
		||||
                            self.logger.warning(e.message)
 | 
			
		||||
        else:  # file
 | 
			
		||||
            for asset in assets:
 | 
			
		||||
                if asset['path'].lower() == resource.path.lower():
 | 
			
		||||
@@ -514,14 +524,22 @@ class RemoteFilerGetter(ResourceGetter):
 | 
			
		||||
                # There tends to be some confusion as to where revent files should
 | 
			
		||||
                # be placed. This looks both in the extension's directory, and in
 | 
			
		||||
                # 'revent_files' subdirectory under it, if it exists.
 | 
			
		||||
                path = None
 | 
			
		||||
                if os.path.isdir(alternate_location):
 | 
			
		||||
                    for candidate in os.listdir(alternate_location):
 | 
			
		||||
                        if candidate.lower() == filename.lower():
 | 
			
		||||
                            return os.path.join(alternate_location, candidate)
 | 
			
		||||
                            path = os.path.join(alternate_location, candidate)
 | 
			
		||||
                if os.path.isdir(location):
 | 
			
		||||
                    for candidate in os.listdir(location):
 | 
			
		||||
                        if candidate.lower() == filename.lower():
 | 
			
		||||
                            return os.path.join(location, candidate)
 | 
			
		||||
                            path = os.path.join(location, candidate)
 | 
			
		||||
                if path:
 | 
			
		||||
                    try:
 | 
			
		||||
                        ReventParser.check_revent_file(path)
 | 
			
		||||
                        return path
 | 
			
		||||
                    except ValueError as e:
 | 
			
		||||
                        self.logger.warning(e.message)
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            raise ValueError('Unexpected resource type: {}'.format(resource.name))
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										78
									
								
								wlauto/utils/revent.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								wlauto/utils/revent.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
#    Copyright 2016 ARM Limited
 | 
			
		||||
#
 | 
			
		||||
# Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
# you may not use this file except in compliance with the License.
 | 
			
		||||
# You may obtain a copy of the License at
 | 
			
		||||
#
 | 
			
		||||
#     http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
#
 | 
			
		||||
# Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
# distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
# See the License for the specific language governing permissions and
 | 
			
		||||
# limitations under the License.
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
import struct
 | 
			
		||||
import datetime
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ReventParser(object):
 | 
			
		||||
    """
 | 
			
		||||
    Parses revent binary recording files so they can be easily read within python.
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    int32_struct = struct.Struct("<i")
 | 
			
		||||
    header_struct = struct.Struct("<6sH")
 | 
			
		||||
    event_struct = struct.Struct("<i4xqqHHi")
 | 
			
		||||
 | 
			
		||||
    def __init__(self):
 | 
			
		||||
        self.path = None
 | 
			
		||||
        self.device_paths = []
 | 
			
		||||
 | 
			
		||||
    def parse(self, path):
 | 
			
		||||
        ReventParser.check_revent_file(path)
 | 
			
		||||
 | 
			
		||||
        with open(path, "rb") as f:
 | 
			
		||||
            _read_struct(f, ReventParser.header_struct)
 | 
			
		||||
            path_count, = _read_struct(f, self.int32_struct)
 | 
			
		||||
            for _ in xrange(path_count):
 | 
			
		||||
                path_length, = _read_struct(f, self.int32_struct)
 | 
			
		||||
                if path_length >= 30:
 | 
			
		||||
                    raise ValueError("path length too long. corrupt file")
 | 
			
		||||
                self.device_paths.append(f.read(path_length))
 | 
			
		||||
 | 
			
		||||
            while f.tell() < os.path.getsize(path):
 | 
			
		||||
                device_id, sec, usec, typ, code, value = _read_struct(f, self.event_struct)
 | 
			
		||||
                yield (device_id, datetime.datetime.fromtimestamp(sec + float(usec) / 1000000),
 | 
			
		||||
                       typ, code, value)
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def check_revent_file(path):
 | 
			
		||||
        """
 | 
			
		||||
        Checks whether a file starts with "REVENT"
 | 
			
		||||
        """
 | 
			
		||||
        with open(path, "rb") as f:
 | 
			
		||||
            magic, file_version = _read_struct(f, ReventParser.header_struct)
 | 
			
		||||
 | 
			
		||||
            if magic != "REVENT":
 | 
			
		||||
                msg = "'{}' isn't an revent file, are you using an old recording?"
 | 
			
		||||
                raise ValueError(msg.format(path))
 | 
			
		||||
            return file_version
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def get_revent_duration(path):
 | 
			
		||||
        """
 | 
			
		||||
        Takes an ReventParser and returns the duration of the revent recording in seconds.
 | 
			
		||||
        """
 | 
			
		||||
        revent_parser = ReventParser().parse(path)
 | 
			
		||||
        first = last = next(revent_parser)
 | 
			
		||||
        for last in revent_parser:
 | 
			
		||||
            pass
 | 
			
		||||
        return (last[1] - first[1]).total_seconds()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def _read_struct(f, struct_spec):
 | 
			
		||||
    data = f.read(struct_spec.size)
 | 
			
		||||
    return struct_spec.unpack(data)
 | 
			
		||||
		Reference in New Issue
	
	Block a user