mirror of
https://github.com/ARM-software/workload-automation.git
synced 2025-02-21 20:38:57 +00:00
instrumentaion/file_poller: Port instrument from WA2
This commit is contained in:
parent
6022c38ae2
commit
5729df0a5b
17
wa/instrumentation/poller/Makefile
Normal file
17
wa/instrumentation/poller/Makefile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# CROSS_COMPILE=aarch64-linux-gnu- make
|
||||||
|
#
|
||||||
|
CC=gcc
|
||||||
|
|
||||||
|
ifdef DEBUG
|
||||||
|
CFLAGS=-static -lc -g
|
||||||
|
else
|
||||||
|
CFLAGS=-static -lc -O2
|
||||||
|
endif
|
||||||
|
|
||||||
|
poller: poller.c
|
||||||
|
$(CROSS_COMPILE)$(CC) $(CFLAGS) poller.c -o poller
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf poller
|
||||||
|
|
||||||
|
.PHONY: clean
|
122
wa/instrumentation/poller/__init__.py
Normal file
122
wa/instrumentation/poller/__init__.py
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
# 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
|
||||||
|
import os
|
||||||
|
|
||||||
|
from wa import Instrument, Parameter, Executable
|
||||||
|
from wa.framework.exception import ConfigError, InstrumentError
|
||||||
|
from wa.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 will strip any commas or new lines for the files' values
|
||||||
|
before writing them.
|
||||||
|
"""
|
||||||
|
|
||||||
|
parameters = [
|
||||||
|
Parameter('sample_interval', kind=int, default=1000,
|
||||||
|
description="""The interval between samples in mS."""),
|
||||||
|
Parameter('files', kind=list_or_string, mandatory=True,
|
||||||
|
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.files:
|
||||||
|
raise ConfigError('You must specify atleast one file to poll')
|
||||||
|
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):
|
||||||
|
if not self.target.is_rooted and self.as_root:
|
||||||
|
raise ConfigError('The target is not rooted, cannot run poller as root.')
|
||||||
|
host_poller = context.resolver.get(Executable(self, self.target.abi,
|
||||||
|
"poller"))
|
||||||
|
target_poller = self.target.install(host_poller)
|
||||||
|
|
||||||
|
expanded_paths = []
|
||||||
|
for path in self.files:
|
||||||
|
if "*" in path:
|
||||||
|
for p in self.target.list_directory(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.target.path.join(self.target.working_directory, 'poller.csv')
|
||||||
|
self.target_log_path = self.target.path.join(self.target.working_directory, 'poller.log')
|
||||||
|
self.command = '{} -t {} -l {} {} > {} 2>{}'.format(target_poller,
|
||||||
|
self.sample_interval * 1000,
|
||||||
|
','.join(self.labels),
|
||||||
|
' '.join(self.files),
|
||||||
|
self.target_output_path,
|
||||||
|
self.target_log_path)
|
||||||
|
|
||||||
|
def start(self, context):
|
||||||
|
self.target.kick_off(self.command, as_root=self.as_root)
|
||||||
|
|
||||||
|
def stop(self, context):
|
||||||
|
self.target.killall('poller', signal='TERM', as_root=self.as_root)
|
||||||
|
|
||||||
|
def update_result(self, context):
|
||||||
|
host_output_file = os.path.join(context.output_directory, 'poller.csv')
|
||||||
|
self.target.pull(self.target_output_path, host_output_file)
|
||||||
|
context.add_artifact('poller_output', host_output_file, kind='data')
|
||||||
|
host_log_file = os.path.join(context.output_directory, 'poller.log')
|
||||||
|
self.target.pull(self.target_log_path, host_log_file)
|
||||||
|
context.add_artifact('poller_log', host_log_file, kind='log')
|
||||||
|
|
||||||
|
with open(host_log_file) as fh:
|
||||||
|
for line in fh:
|
||||||
|
if 'ERROR' in line:
|
||||||
|
raise InstrumentError(line.strip())
|
||||||
|
if 'WARNING' in line:
|
||||||
|
self.logger.warning(line.strip())
|
||||||
|
|
||||||
|
def teardown(self, context):
|
||||||
|
self.target.remove(self.target_output_path)
|
||||||
|
self.target.remove(self.target_log_path)
|
||||||
|
|
||||||
|
def _generate_labels(self):
|
||||||
|
# Split paths into their parts
|
||||||
|
path_parts = [f.split(self.target.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
wa/instrumentation/poller/bin/arm64/poller
Executable file
BIN
wa/instrumentation/poller/bin/arm64/poller
Executable file
Binary file not shown.
BIN
wa/instrumentation/poller/bin/armeabi/poller
Executable file
BIN
wa/instrumentation/poller/bin/armeabi/poller
Executable file
Binary file not shown.
163
wa/instrumentation/poller/poller.c
Normal file
163
wa/instrumentation/poller/poller.c
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
#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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void strip(char *s) {
|
||||||
|
char *stripped_s = s;
|
||||||
|
while(*s != '\0') {
|
||||||
|
if(*s != ',' && *s != '\n') {
|
||||||
|
*stripped_s++ = *s++;
|
||||||
|
} else {
|
||||||
|
++s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*stripped_s = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int fd;
|
||||||
|
char *path;
|
||||||
|
} poll_source_t;
|
||||||
|
|
||||||
|
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;
|
||||||
|
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, "ERROR: %s: missing file path(s)\n", argv[0]);
|
||||||
|
fprintf(stderr, usage, argv[0]);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int num_files = argc - optind;
|
||||||
|
poll_source_t files_to_poll[num_files];
|
||||||
|
|
||||||
|
if (labelCount && labelCount != num_files)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "ERROR: %s: %d labels specified but %d files specified\n",
|
||||||
|
argv[0], labelCount, num_files);
|
||||||
|
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 < num_files; i++)
|
||||||
|
{
|
||||||
|
files_to_poll[i].path = argv[optind + i];
|
||||||
|
files_to_poll[i].fd = open(files_to_poll[i].path, O_RDONLY);
|
||||||
|
if (files_to_poll[i].fd == -1) {
|
||||||
|
fprintf(stderr, "ERROR: Could not open \"%s\", got: %s\n",
|
||||||
|
files_to_poll[i].path, strerror(errno));
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
int bytes_read = 0;
|
||||||
|
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 < num_files; i++) {
|
||||||
|
lseek(files_to_poll[i].fd, 0, SEEK_SET);
|
||||||
|
bytes_read = read(files_to_poll[i].fd, buf, 1024);
|
||||||
|
|
||||||
|
if (bytes_read < 0) {
|
||||||
|
fprintf(stderr, "WARNING: Read nothing from \"%s\"\n",
|
||||||
|
files_to_poll[i].path);
|
||||||
|
printf(",");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
strip(buf);
|
||||||
|
printf(",%s", buf);
|
||||||
|
buf[0] = '\0'; // "Empty" buffer
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
usleep(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
//Close files
|
||||||
|
for (i = 0; i < num_files; i++)
|
||||||
|
{
|
||||||
|
close(files_to_poll[i].fd);
|
||||||
|
}
|
||||||
|
exit(0);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user