| Index: tools/android/file_poller/file_poller.cc
|
| diff --git a/tools/android/file_poller/file_poller.cc b/tools/android/file_poller/file_poller.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c73db8b6e6f7cd2083ff3587e1c1dc1ddd9efd56
|
| --- /dev/null
|
| +++ b/tools/android/file_poller/file_poller.cc
|
| @@ -0,0 +1,207 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +// When run with 2 or more arguments the file_poller tool will open a port on
|
| +// the device, print it on its standard output and then start collect file
|
| +// contents. The first argument is the polling rate in Hz, and the following
|
| +// arguments are file to poll.
|
| +// When run with the port of an already running file_poller, the tool will
|
| +// contact the first instance, retrieve the sample and print those on its
|
| +// standard output. This will also terminate the first instance.
|
| +
|
| +#include <errno.h>
|
| +#include <fcntl.h>
|
| +#include <netinet/in.h>
|
| +#include <stdio.h>
|
| +#include <stdlib.h>
|
| +#include <sys/socket.h>
|
| +#include <sys/stat.h>
|
| +#include <sys/time.h>
|
| +#include <sys/types.h>
|
| +#include <unistd.h>
|
| +
|
| +#include "base/logging.h"
|
| +
|
| +// Context containing the files to poll and the polling rate.
|
| +struct Context {
|
| + size_t nb_files;
|
| + int* file_fds;
|
| + int poll_rate;
|
| +};
|
| +
|
| +// Write from the buffer to the given file descriptor.
|
| +void safe_write(int fd, const char* buffer, int size) {
|
| + const char* index = buffer;
|
| + size_t to_write = size;
|
| + while (to_write > 0) {
|
| + int written = write(fd, index, to_write);
|
| + if (written < 0)
|
| + PLOG(FATAL);
|
| + index += written;
|
| + to_write -= written;
|
| + }
|
| +}
|
| +
|
| +// Transfer the content of a file descriptor to another.
|
| +void transfer_to_fd(int fd_in, int fd_out) {
|
| + char buffer[1024];
|
| + int n;
|
| + while ((n = read(fd_in, buffer, sizeof(buffer))) > 0)
|
| + safe_write(fd_out, buffer, n);
|
| +}
|
| +
|
| +// Transfer the content of a file descriptor to a buffer.
|
| +int transfer_to_buffer(int fd_in, char* bufffer, size_t size) {
|
| + char* index = bufffer;
|
| + size_t to_read = size;
|
| + int n;
|
| + while (to_read > 0 && ((n = read(fd_in, index, to_read)) > 0)) {
|
| + index += n;
|
| + to_read -= n;
|
| + }
|
| + if (n < 0)
|
| + PLOG(FATAL);
|
| + return size - to_read;
|
| +}
|
| +
|
| +// Try to open the file at the given path for reading. Exit in case of failure.
|
| +int checked_open(const char* path) {
|
| + int fd = open(path, O_RDONLY);
|
| + if (fd < 0)
|
| + PLOG(FATAL);
|
| + return fd;
|
| +}
|
| +
|
| +void transfer_measurement(int fd_in, int fd_out, bool last) {
|
| + char buffer[1024];
|
| + if (lseek(fd_in, 0, SEEK_SET) < 0)
|
| + PLOG(FATAL);
|
| + int n = transfer_to_buffer(fd_in, buffer, sizeof(buffer));
|
| + safe_write(fd_out, buffer, n - 1);
|
| + safe_write(fd_out, last ? "\n" : " ", 1);
|
| +}
|
| +
|
| +// Acquire a sample and save it to the given file descriptor.
|
| +void acquire_sample(int fd, const Context& context) {
|
| + struct timeval tv;
|
| + gettimeofday(&tv, NULL);
|
| + char buffer[1024];
|
| + int n = snprintf(buffer, sizeof(buffer), "%d.%06d ", tv.tv_sec, tv.tv_usec);
|
| + safe_write(fd, buffer, n);
|
| +
|
| + for (int i = 0; i < context.nb_files; ++i)
|
| + transfer_measurement(context.file_fds[i], fd, i == (context.nb_files - 1));
|
| +}
|
| +
|
| +void poll_content(const Context& context) {
|
| + // Create and bind the socket so that the port can be written to stdout.
|
| + int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
| + struct sockaddr_in socket_info;
|
| + socket_info.sin_family = AF_INET;
|
| + socket_info.sin_addr.s_addr = htonl(INADDR_ANY);
|
| + socket_info.sin_port = htons(0);
|
| + if (bind(sockfd, (struct sockaddr*)&socket_info, sizeof(socket_info)) < 0)
|
| + PLOG(FATAL);
|
| + socklen_t size = sizeof(socket_info);
|
| + getsockname(sockfd, (struct sockaddr*)&socket_info, &size);
|
| + printf("%d\n", ntohs(socket_info.sin_port));
|
| + // Using a pipe to ensure child is diconnected from the terminal before
|
| + // quitting.
|
| + int pipes[2];
|
| + pipe(pipes);
|
| + pid_t pid = fork();
|
| + if (pid < 0)
|
| + PLOG(FATAL);
|
| + if (pid != 0) {
|
| + close(pipes[1]);
|
| + // Not expecting any data to be received.
|
| + read(pipes[0], NULL, 1);
|
| + signal(SIGCHLD, SIG_IGN);
|
| + return;
|
| + }
|
| +
|
| + // Detach from terminal.
|
| + setsid();
|
| + close(STDIN_FILENO);
|
| + close(STDOUT_FILENO);
|
| + close(STDERR_FILENO);
|
| + close(pipes[0]);
|
| +
|
| + // Start listening for incoming connection.
|
| + if (listen(sockfd, 1) < 0)
|
| + PLOG(FATAL);
|
| +
|
| + // Signal the parent that it can now safely exit.
|
| + close(pipes[1]);
|
| +
|
| + // Prepare file to store the samples.
|
| + int fd;
|
| + char filename[] = "/data/local/tmp/fileXXXXXX";
|
| + fd = mkstemp(filename);
|
| + unlink(filename);
|
| +
|
| + // Collect samples until a client connect on the socket.
|
| + fd_set rfds;
|
| + struct timeval timeout;
|
| + do {
|
| + acquire_sample(fd, context);
|
| + timeout.tv_sec = 0;
|
| + timeout.tv_usec = 1000000 / context.poll_rate;
|
| + FD_ZERO(&rfds);
|
| + FD_SET(sockfd, &rfds);
|
| + } while (select(sockfd + 1, &rfds, NULL, NULL, &timeout) == 0);
|
| +
|
| + // Collect a final sample.
|
| + acquire_sample(fd, context);
|
| +
|
| + // Send the result back.
|
| + struct sockaddr_in remote_socket_info;
|
| + int rfd = accept(sockfd, (struct sockaddr*)&remote_socket_info, &size);
|
| + if (rfd < 0)
|
| + PLOG(FATAL);
|
| + if (lseek(fd, 0, SEEK_SET) < 0)
|
| + PLOG(FATAL);
|
| + transfer_to_fd(fd, rfd);
|
| +}
|
| +
|
| +void content_collection(int port) {
|
| + int sockfd = socket(AF_INET, SOCK_STREAM, 0);
|
| + // Connect to localhost.
|
| + struct sockaddr_in socket_info;
|
| + socket_info.sin_family = AF_INET;
|
| + socket_info.sin_addr.s_addr = htonl(0x7f000001);
|
| + socket_info.sin_port = htons(port);
|
| + if (connect(sockfd, (struct sockaddr*)&socket_info, sizeof(socket_info)) <
|
| + 0) {
|
| + PLOG(FATAL);
|
| + }
|
| + transfer_to_fd(sockfd, STDOUT_FILENO);
|
| +}
|
| +
|
| +int main(int argc, char** argv) {
|
| + if (argc == 1) {
|
| + fprintf(stderr,
|
| + "Usage: \n"
|
| + " %s port\n"
|
| + " %s rate FILE...\n",
|
| + argv[0],
|
| + argv[0]);
|
| + exit(EXIT_FAILURE);
|
| + }
|
| + if (argc == 2) {
|
| + // Argument is the port to connect to.
|
| + content_collection(atoi(argv[1]));
|
| + } else {
|
| + // First argument is the poll frequency, in Hz, following arguments are the
|
| + // file to poll.
|
| + Context context;
|
| + context.poll_rate = atoi(argv[1]);
|
| + context.nb_files = argc - 2;
|
| + context.file_fds = new int[context.nb_files];
|
| + for (int i = 2; i < argc; ++i)
|
| + context.file_fds[i - 2] = checked_open(argv[i]);
|
| + poll_content(context);
|
| + }
|
| + return EXIT_SUCCESS;
|
| +}
|
|
|