mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-02-20 20:09:11 +00:00
Merge pull request #164 from ep1cman/poller
Poller: Added an instrument to poll files and output a csv of their v…
This commit is contained in:
commit
6e4f6af942
@ -446,22 +446,20 @@ class AndroidDevice(BaseLinuxDevice): # pylint: disable=W0223
|
||||
else:
|
||||
return adb_shell(self.adb_name, command, timeout, check_exit_code, as_root)
|
||||
|
||||
def kick_off(self, command, as_root=True):
|
||||
def kick_off(self, command, as_root=None):
|
||||
"""
|
||||
Like execute but closes adb session and returns immediately, leaving the command running on the
|
||||
device (this is different from execute(background=True) which keeps adb connection open and returns
|
||||
a subprocess object).
|
||||
|
||||
.. note:: This relies on busybox's nohup applet and so won't work on unrooted devices.
|
||||
|
||||
Added in version 2.1.4
|
||||
|
||||
"""
|
||||
if not self.is_rooted or not as_root:
|
||||
raise DeviceError('kick_off uses busybox\'s nohup applet and so can only be run a rooted device.')
|
||||
if as_root is None:
|
||||
as_root = self.is_rooted
|
||||
try:
|
||||
command = 'cd {} && busybox nohup {}'.format(self.working_directory, command)
|
||||
output = self.execute(command, timeout=1, as_root=True)
|
||||
command = 'cd {} && {} nohup {}'.format(self.working_directory, self.busybox, command)
|
||||
output = self.execute(command, timeout=1, as_root=as_root)
|
||||
except TimeoutError:
|
||||
pass
|
||||
else:
|
||||
|
105
wlauto/instrumentation/poller/__init__.py
Normal file
105
wlauto/instrumentation/poller/__init__.py
Normal file
@ -0,0 +1,105 @@
|
||||
# Copyright 2015 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.
|
||||
# pylint: disable=access-member-before-definition,attribute-defined-outside-init,unused-argument
|
||||
|
||||
from wlauto import Instrument, Parameter, Executable
|
||||
from wlauto.exceptions import ConfigError
|
||||
from wlauto.utils.types import list_or_string
|
||||
|
||||
|
||||
class FilePoller(Instrument):
|
||||
name = 'file_poller'
|
||||
description = """
|
||||
Polls the given files at a set sample interval. The values are output in CSV format.
|
||||
|
||||
This instrument places a file called poller.csv in each iterations result directory.
|
||||
This file will contain a timestamp column which will be in uS, the rest of the columns
|
||||
will be the contents of the polled files at that time.
|
||||
|
||||
This instrument can poll any file whos contents do not contain a new line since this
|
||||
breaks the CSV formatting.
|
||||
"""
|
||||
|
||||
parameters = [
|
||||
Parameter('sample_interval', kind=int, default=1000,
|
||||
description="""The interval between samples in mS."""),
|
||||
Parameter('files', kind=list_or_string,
|
||||
description="""A list of paths to the files to be polled"""),
|
||||
Parameter('labels', kind=list_or_string,
|
||||
description="""A list of lables to be used in the CSV output for
|
||||
the corresponding files. This cannot be used if
|
||||
a `*` wildcard is used in a path."""),
|
||||
Parameter('as_root', kind=bool, default=False,
|
||||
description="""
|
||||
Whether or not the poller will be run as root. This should be
|
||||
used when the file you need to poll can only be accessed by root.
|
||||
"""),
|
||||
]
|
||||
|
||||
def validate(self):
|
||||
if not self.device.is_rooted and self.as_root:
|
||||
raise ConfigError('The device is not rooted, cannot run poller as root.')
|
||||
if self.labels and any(['*' in f for f in self.files]):
|
||||
raise ConfigError('You cannot used manual labels with `*` wildcards')
|
||||
|
||||
def initialize(self, context):
|
||||
host_poller = context.resolver.get(Executable(self, self.device.abi,
|
||||
"poller"))
|
||||
target_poller = self.device.install_if_needed(host_poller)
|
||||
|
||||
expanded_paths = []
|
||||
for path in self.files:
|
||||
if "*" in path:
|
||||
for p in self.device.listdir(path):
|
||||
expanded_paths.append(p)
|
||||
else:
|
||||
expanded_paths.append(path)
|
||||
self.files = expanded_paths
|
||||
if not self.labels:
|
||||
self.labels = self._generate_labels()
|
||||
|
||||
self.target_output_path = self.device.path.join(self.device.working_directory, 'poller.csv')
|
||||
self.command = '{} -t {} -l {} {} > {}'.format(target_poller,
|
||||
self.sample_interval * 1000,
|
||||
','.join(self.labels),
|
||||
' '.join(self.files),
|
||||
self.target_output_path)
|
||||
|
||||
def start(self, context):
|
||||
self.device.kick_off(self.command, as_root=self.as_root)
|
||||
|
||||
def stop(self, context):
|
||||
self.device.killall('poller', signal='TERM', as_root=self.as_root)
|
||||
|
||||
def update_result(self, context):
|
||||
self.device.pull_file(self.target_output_path, context.output_directory)
|
||||
|
||||
def teardown(self, context):
|
||||
self.device.delete_file(self.target_output_path)
|
||||
|
||||
def _generate_labels(self):
|
||||
# Split paths into their parts
|
||||
path_parts = [f.split(self.device.path.sep) for f in self.files]
|
||||
# Identify which parts differ between at least two of the paths
|
||||
differ_map = [len(set(x)) > 1 for x in zip(*path_parts)]
|
||||
|
||||
# compose labels from path parts that differ
|
||||
labels = []
|
||||
for pp in path_parts:
|
||||
label_parts = [p for i, p in enumerate(pp[:-1])
|
||||
if i >= len(differ_map) or differ_map[i]]
|
||||
label_parts.append(pp[-1]) # always use file name even if same for all
|
||||
labels.append('-'.join(label_parts))
|
||||
return labels
|
||||
|
BIN
wlauto/instrumentation/poller/bin/arm64/poller
Normal file
BIN
wlauto/instrumentation/poller/bin/arm64/poller
Normal file
Binary file not shown.
BIN
wlauto/instrumentation/poller/bin/armeabi/poller
Normal file
BIN
wlauto/instrumentation/poller/bin/armeabi/poller
Normal file
Binary file not shown.
132
wlauto/instrumentation/poller/poller.c
Normal file
132
wlauto/instrumentation/poller/poller.c
Normal file
@ -0,0 +1,132 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/poll.h>
|
||||
#include <sys/time.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
volatile sig_atomic_t done = 0;
|
||||
void term(int signum)
|
||||
{
|
||||
done = 1;
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv) {
|
||||
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
int c = 0;
|
||||
int show_help = 0;
|
||||
useconds_t interval = 1000000;
|
||||
char buf[1024];
|
||||
memset(buf, 0, sizeof(buf));
|
||||
struct timeval current_time;
|
||||
double time_float;
|
||||
int files_to_poll[argc-optind];
|
||||
char *labels;
|
||||
int labelCount = 0;
|
||||
static char usage[] = "usage: %s [-h] [-t INTERVAL] FILE [FILE ...]\n"
|
||||
"polls FILE(s) every INTERVAL microseconds and outputs\n"
|
||||
"the results in CSV format including a timestamp to STDOUT\n"
|
||||
"\n"
|
||||
" -h Display this message\n"
|
||||
" -t The polling sample interval in microseconds\n"
|
||||
" Defaults to 1000000 (1 second)\n"
|
||||
" -l Comma separated list of labels to use in the CSV\n"
|
||||
" output. This should match the number of files\n";
|
||||
|
||||
|
||||
//Handling command line arguments
|
||||
while ((c = getopt(argc, argv, "ht:l:")) != -1)
|
||||
{
|
||||
switch(c) {
|
||||
case 'h':
|
||||
case '?':
|
||||
default:
|
||||
show_help = 1;
|
||||
break;
|
||||
case 't':
|
||||
interval = (useconds_t)atoi(optarg);
|
||||
break;
|
||||
case 'l':
|
||||
labels = optarg;
|
||||
labelCount = 1;
|
||||
int i;
|
||||
for (i=0; labels[i]; i++)
|
||||
labelCount += (labels[i] == ',');
|
||||
}
|
||||
}
|
||||
|
||||
if (show_help) {
|
||||
fprintf(stderr, usage, argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (optind >= argc) {
|
||||
fprintf(stderr, "%s: missiing file path(s)\n", argv[0]);
|
||||
fprintf(stderr, usage, argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (labelCount && labelCount != argc-optind)
|
||||
{
|
||||
fprintf(stderr, "%s: %d labels specified but %d files specified\n", argv[0],
|
||||
labelCount,
|
||||
argc-optind);
|
||||
fprintf(stderr, usage, argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
//Print headers and open files to poll
|
||||
printf("time");
|
||||
if(labelCount)
|
||||
{
|
||||
printf(",%s", labels);
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < (argc - optind); i++)
|
||||
{
|
||||
files_to_poll[i] = open(argv[optind + i], O_RDONLY);
|
||||
if(!labelCount) {
|
||||
printf(",%s", argv[optind + i]);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
//Setup SIGTERM handler
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(struct sigaction));
|
||||
action.sa_handler = term;
|
||||
sigaction(SIGTERM, &action, NULL);
|
||||
|
||||
//Poll files
|
||||
while (!done) {
|
||||
gettimeofday(¤t_time, NULL);
|
||||
time_float = (double)current_time.tv_sec;
|
||||
time_float += ((double)current_time.tv_usec)/1000/1000;
|
||||
printf("%f", time_float);
|
||||
for (i = 0; i < (argc - optind); i++) {
|
||||
read(files_to_poll[i], buf, 1024);
|
||||
lseek(files_to_poll[i], 0, SEEK_SET);
|
||||
|
||||
//Removes trailing "\n"
|
||||
int new_line = strlen(buf) -1;
|
||||
if (buf[new_line] == '\n')
|
||||
buf[new_line] = '\0';
|
||||
|
||||
printf(",%s", buf);
|
||||
}
|
||||
printf("\n");
|
||||
usleep(interval);
|
||||
}
|
||||
|
||||
//Close files
|
||||
for (i = 0; i < (argc - optind); i++)
|
||||
{
|
||||
files_to_poll[i] = open(argv[optind + i], O_RDONLY);
|
||||
}
|
||||
exit(0);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user