mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-01-19 12:24:32 +00:00
Merge pull request #200 from ep1cman/revent_fixes
revent: Fixed dump command segfault
This commit is contained in:
commit
a2d0747b4c
@ -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
|
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,
|
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).
|
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 os
|
||||||
import sys
|
import sys
|
||||||
|
import signal
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
from wlauto import ExtensionLoader, Command, settings
|
from wlauto import ExtensionLoader, Command, settings
|
||||||
from wlauto.common.resources import Executable
|
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.resolver import ResourceResolver
|
||||||
from wlauto.core.configuration import RunConfiguration
|
from wlauto.core.configuration import RunConfiguration
|
||||||
from wlauto.core.agenda import Agenda
|
from wlauto.core.agenda import Agenda
|
||||||
|
from wlauto.utils.revent import ReventParser
|
||||||
|
|
||||||
|
|
||||||
class RecordCommand(Command):
|
class RecordCommand(Command):
|
||||||
@ -114,13 +117,15 @@ class RecordCommand(Command):
|
|||||||
|
|
||||||
self.logger.info("Press Enter when you are ready to record...")
|
self.logger.info("Press Enter when you are ready to record...")
|
||||||
raw_input("")
|
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.device.kick_off(command)
|
||||||
|
|
||||||
self.logger.info("Press Enter when you have finished recording...")
|
self.logger.info("Press Enter when you have finished recording...")
|
||||||
raw_input("")
|
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.logger.info("Pulling files from device")
|
||||||
self.device.pull_file(revent_file, args.output or os.getcwdu())
|
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.logger.info("Starting {}".format(args.package))
|
||||||
self.device.execute('monkey -p {} -c android.intent.category.LAUNCHER 1'.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)
|
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")
|
self.logger.info("Finished replay")
|
||||||
|
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
from math import ceil
|
||||||
|
|
||||||
from wlauto.core.extension import Parameter
|
from wlauto.core.extension import Parameter
|
||||||
from wlauto.core.workload import Workload
|
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.exceptions import WorkloadError, ResourceError, ConfigError
|
||||||
from wlauto.utils.android import ApkInfo, ANDROID_NORMAL_PERMISSIONS
|
from wlauto.utils.android import ApkInfo, ANDROID_NORMAL_PERMISSIONS
|
||||||
from wlauto.utils.types import boolean
|
from wlauto.utils.types import boolean
|
||||||
|
from wlauto.utils.revent import ReventParser
|
||||||
import wlauto.common.android.resources
|
import wlauto.common.android.resources
|
||||||
|
|
||||||
|
|
||||||
@ -322,16 +324,13 @@ AndroidBenchmark = ApkWorkload # backward compatibility
|
|||||||
|
|
||||||
class ReventWorkload(Workload):
|
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):
|
def __init__(self, device, _call_super=True, **kwargs):
|
||||||
if _call_super:
|
if _call_super:
|
||||||
super(ReventWorkload, self).__init__(device, **kwargs)
|
super(ReventWorkload, self).__init__(device, **kwargs)
|
||||||
devpath = self.device.path
|
devpath = self.device.path
|
||||||
self.on_device_revent_binary = devpath.join(self.device.binaries_directory, 'revent')
|
self.on_device_revent_binary = devpath.join(self.device.binaries_directory, 'revent')
|
||||||
self.setup_timeout = kwargs.get('setup_timeout', self.default_setup_timeout)
|
self.setup_timeout = kwargs.get('setup_timeout', None)
|
||||||
self.run_timeout = kwargs.get('run_timeout', self.default_run_timeout)
|
self.run_timeout = kwargs.get('run_timeout', None)
|
||||||
self.revent_setup_file = None
|
self.revent_setup_file = None
|
||||||
self.revent_run_file = None
|
self.revent_run_file = None
|
||||||
self.on_device_setup_revent = 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,
|
self.on_device_run_revent = devpath.join(self.device.working_directory,
|
||||||
os.path.split(self.revent_run_file)[-1])
|
os.path.split(self.revent_run_file)[-1])
|
||||||
self._check_revent_files(context)
|
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):
|
def setup(self, context):
|
||||||
self.device.killall('revent')
|
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_DEVICES 16
|
||||||
#define INPDEV_MAX_PATH 30
|
#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
|
#ifndef ANDROID
|
||||||
int strlcpy(char *dest, char *source, size_t size)
|
int strlcpy(char *dest, char *source, size_t size)
|
||||||
@ -110,6 +113,56 @@ bool_t is_numeric(char *string)
|
|||||||
return TRUE;
|
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) {
|
off_t get_file_size(const char *filename) {
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
|
||||||
@ -191,41 +244,52 @@ void dump(const char *logfile)
|
|||||||
int fdin = open(logfile, O_RDONLY);
|
int fdin = open(logfile, O_RDONLY);
|
||||||
if (fdin < 0) die("Could not open eventlog %s\n", logfile);
|
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 len;
|
||||||
int32_t i;
|
int32_t i;
|
||||||
char buf[INPDEV_MAX_PATH];
|
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_t *inpdev = malloc(sizeof(inpdev_t));
|
||||||
inpdev->id_pathc = 0;
|
inpdev->id_pathc = nfds;
|
||||||
for (i=0; i<nfds; i++) {
|
for (i=0; i<nfds; i++) {
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
rb = read(fdin, &len, sizeof(len));
|
rb = read(fdin, &len, sizeof(len));
|
||||||
if (rb != sizeof(len)) die("problems reading eventlog\n");
|
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);
|
rb = read(fdin, &buf[0], len);
|
||||||
if (rb != len) die("problems reading eventlog\n");
|
if (rb != len) die("problems reading eventlog\n");
|
||||||
strlcpy(inpdev->id_pathv[inpdev->id_pathc], buf, INPDEV_MAX_PATH);
|
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;
|
struct input_event ev;
|
||||||
int count = 0;
|
int count = 0;
|
||||||
while(1) {
|
while(1) {
|
||||||
int32_t idx;
|
if (read_replay_event(fdin, &rep_ev) == -1)
|
||||||
rb = read(fdin, &idx, sizeof(idx));
|
break;
|
||||||
if (rb != sizeof(idx)) break;
|
ev = rep_ev.event;
|
||||||
rb = read(fdin, &ev, sizeof(ev));
|
|
||||||
if (rb < (int)sizeof(ev)) break;
|
|
||||||
|
|
||||||
printf("%10u.%-6u %30s type %2d code %3d value %4d\n",
|
printf("%10u.%-6u %30s type %2d code %3d value %4d\n",
|
||||||
(unsigned int)ev.time.tv_sec, (unsigned int)ev.time.tv_usec,
|
(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++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +311,22 @@ int replay_buffer_init(replay_buffer_t **buffer, const char *logfile)
|
|||||||
if (fdin < 0)
|
if (fdin < 0)
|
||||||
die("Could not open eventlog %s\n", logfile);
|
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))
|
if (rb!=sizeof(buff->num_fds))
|
||||||
die("problems reading eventlog\n");
|
die("problems reading eventlog\n");
|
||||||
|
|
||||||
@ -255,13 +334,15 @@ int replay_buffer_init(replay_buffer_t **buffer, const char *logfile)
|
|||||||
if (!buff->fds)
|
if (!buff->fds)
|
||||||
die("out of memory\n");
|
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++) {
|
for (i = 0; i < buff->num_fds; i++) {
|
||||||
memset(path_buff, 0, sizeof(path_buff));
|
memset(path_buff, 0, sizeof(path_buff));
|
||||||
rb = read(fdin, &len, sizeof(len));
|
rb = read(fdin, &len, sizeof(len));
|
||||||
if (rb!=sizeof(len))
|
if (rb!=sizeof(len))
|
||||||
die("problems reading eventlog\n");
|
die("problems reading eventlog\n");
|
||||||
|
if (len >= INPDEV_MAX_PATH)
|
||||||
|
die("path length too long, file corrupt");
|
||||||
rb = read(fdin, &path_buff[0], len);
|
rb = read(fdin, &path_buff[0], len);
|
||||||
if (rb != len)
|
if (rb != len)
|
||||||
die("problems reading eventlog\n");
|
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;
|
replay_event_t rep_ev;
|
||||||
i = 0;
|
i = 0;
|
||||||
while(1) {
|
while(1) {
|
||||||
rb = read(fdin, &rep_ev, sizeof(rep_ev));
|
if (read_replay_event(fdin, &rep_ev) == -1)
|
||||||
if (rb < (int)sizeof(rep_ev))
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
@ -369,6 +449,8 @@ void usage()
|
|||||||
" -d DEVICE the number of the input device form which\n"
|
" -d DEVICE the number of the input device form which\n"
|
||||||
" events will be recoreded. If not specified, \n"
|
" events will be recoreded. If not specified, \n"
|
||||||
" all available inputs will be used.\n"
|
" all available inputs will be used.\n"
|
||||||
|
" -s Recording will not be stopped if there is \n"
|
||||||
|
" input on STDIN.\n"
|
||||||
"\n"
|
"\n"
|
||||||
" replay FILE\n"
|
" replay FILE\n"
|
||||||
" replays previously recorded events from the specified file.\n"
|
" replays previously recorded events from the specified file.\n"
|
||||||
@ -488,19 +570,7 @@ int count;
|
|||||||
|
|
||||||
void term_handler(int signum)
|
void term_handler(int signum)
|
||||||
{
|
{
|
||||||
int32_t i;
|
(void)signum;
|
||||||
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 record(inpdev_t *inpdev, int delay, const char *logfile)
|
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");
|
fdout = fopen(logfile, "wb");
|
||||||
if (!fdout) die("Could not open eventlog %s\n", logfile);
|
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);
|
fwrite(&inpdev->id_pathc, sizeof(inpdev->id_pathc), 1, fdout);
|
||||||
for (i=0; i<inpdev->id_pathc; i++) {
|
for (i=0; i<inpdev->id_pathc; i++) {
|
||||||
int32_t len = strlen(inpdev->id_pathv[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);
|
fds[i] = open(inpdev->id_pathv[i], O_RDONLY);
|
||||||
if (fds[i]>maxfd) maxfd = fds[i];
|
if (fds[i]>maxfd) maxfd = fds[i];
|
||||||
dprintf("opened %s with %d\n", inpdev->id_pathv[i], 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;
|
count = 0;
|
||||||
struct timeval tout;
|
struct timespec tout;
|
||||||
while(1)
|
while(1)
|
||||||
{
|
{
|
||||||
FD_ZERO(&readfds);
|
FD_ZERO(&readfds);
|
||||||
@ -552,10 +633,13 @@ void record(inpdev_t *inpdev, int delay, const char *logfile)
|
|||||||
FD_SET(fds[i], &readfds);
|
FD_SET(fds[i], &readfds);
|
||||||
/* wait for input */
|
/* wait for input */
|
||||||
tout.tv_sec = delay;
|
tout.tv_sec = delay;
|
||||||
tout.tv_usec = 0;
|
tout.tv_nsec = 0;
|
||||||
int32_t r = select(maxfd+1, &readfds, NULL, NULL, &tout);
|
int32_t r = pselect(maxfd+1, &readfds, NULL, NULL, &tout, &oldset);
|
||||||
|
if (errno == EINTR)
|
||||||
|
break;
|
||||||
/* dprintf("got %d (err %d)\n", r, errno); */
|
/* dprintf("got %d (err %d)\n", r, errno); */
|
||||||
if (!r) break;
|
if (!r)
|
||||||
|
break;
|
||||||
if (wait_for_stdin && FD_ISSET(STDIN_FILENO, &readfds)) {
|
if (wait_for_stdin && FD_ISSET(STDIN_FILENO, &readfds)) {
|
||||||
// in this case the key down for the return key will be recorded
|
// in this case the key down for the return key will be recorded
|
||||||
// so we need to up the key up
|
// 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);
|
gettimeofday(&ev.time, NULL);
|
||||||
fwrite(&keydev, sizeof(keydev), 1, fdout);
|
fwrite(&keydev, sizeof(keydev), 1, fdout);
|
||||||
fwrite(&_padding, sizeof(_padding), 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
|
memset(&ev, 0, sizeof(ev)); // SYN
|
||||||
gettimeofday(&ev.time, NULL);
|
gettimeofday(&ev.time, NULL);
|
||||||
fwrite(&keydev, sizeof(keydev), 1, fdout);
|
fwrite(&keydev, sizeof(keydev), 1, fdout);
|
||||||
fwrite(&_padding, sizeof(_padding), 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");
|
dprintf("added fake return exiting...\n");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -589,7 +673,7 @@ void record(inpdev_t *inpdev, int delay, const char *logfile)
|
|||||||
keydev = i;
|
keydev = i;
|
||||||
fwrite(&i, sizeof(i), 1, fdout);
|
fwrite(&i, sizeof(i), 1, fdout);
|
||||||
fwrite(&_padding, sizeof(_padding), 1, fdout);
|
fwrite(&_padding, sizeof(_padding), 1, fdout);
|
||||||
fwrite(&ev, sizeof(ev), 1, fdout);
|
write_input_event(fdout, &ev);
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ from wlauto.exceptions import ResourceError
|
|||||||
from wlauto.utils.android import ApkInfo
|
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.misc import ensure_directory_exists as _d, ensure_file_directory_exists as _f, sha256, urljoin
|
||||||
from wlauto.utils.types import boolean
|
from wlauto.utils.types import boolean
|
||||||
|
from wlauto.utils.revent import ReventParser
|
||||||
|
|
||||||
|
|
||||||
logging.getLogger("requests").setLevel(logging.WARNING)
|
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'))
|
location = _d(os.path.join(self.get_base_location(resource), 'revent_files'))
|
||||||
for candidate in os.listdir(location):
|
for candidate in os.listdir(location):
|
||||||
if candidate.lower() == filename.lower():
|
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):
|
class PackageApkGetter(PackageFileGetter):
|
||||||
@ -430,7 +436,11 @@ class HttpGetter(ResourceGetter):
|
|||||||
for asset in assets:
|
for asset in assets:
|
||||||
pathname = os.path.basename(asset['path']).lower()
|
pathname = os.path.basename(asset['path']).lower()
|
||||||
if pathname == filename:
|
if pathname == filename:
|
||||||
|
try:
|
||||||
|
ReventParser.check_revent_file(asset['path'])
|
||||||
return asset
|
return asset
|
||||||
|
except ValueError as e:
|
||||||
|
self.logger.warning(e.message)
|
||||||
else: # file
|
else: # file
|
||||||
for asset in assets:
|
for asset in assets:
|
||||||
if asset['path'].lower() == resource.path.lower():
|
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
|
# There tends to be some confusion as to where revent files should
|
||||||
# be placed. This looks both in the extension's directory, and in
|
# be placed. This looks both in the extension's directory, and in
|
||||||
# 'revent_files' subdirectory under it, if it exists.
|
# 'revent_files' subdirectory under it, if it exists.
|
||||||
|
path = None
|
||||||
if os.path.isdir(alternate_location):
|
if os.path.isdir(alternate_location):
|
||||||
for candidate in os.listdir(alternate_location):
|
for candidate in os.listdir(alternate_location):
|
||||||
if candidate.lower() == filename.lower():
|
if candidate.lower() == filename.lower():
|
||||||
return os.path.join(alternate_location, candidate)
|
path = os.path.join(alternate_location, candidate)
|
||||||
if os.path.isdir(location):
|
if os.path.isdir(location):
|
||||||
for candidate in os.listdir(location):
|
for candidate in os.listdir(location):
|
||||||
if candidate.lower() == filename.lower():
|
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:
|
else:
|
||||||
raise ValueError('Unexpected resource type: {}'.format(resource.name))
|
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)
|
Loading…
x
Reference in New Issue
Block a user