mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-01-19 04:21:17 +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
|
||||
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)
|
Loading…
x
Reference in New Issue
Block a user