diff options
Diffstat (limited to 'trace/record/record-client-base.cc')
-rw-r--r-- | trace/record/record-client-base.cc | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/trace/record/record-client-base.cc b/trace/record/record-client-base.cc new file mode 100644 index 0000000..fa75f53 --- /dev/null +++ b/trace/record/record-client-base.cc @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ + +/* + * Copyright (C) 2018, 2020 embedded brains GmbH (http://www.embedded-brains.de) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "client.h" + +#ifdef _WIN32 +#include <io.h> +#include <winsock2.h> +#else +#include <arpa/inet.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <unistd.h> +#endif + +#include <fcntl.h> +#include <sys/stat.h> + +#include <algorithm> +#include <cassert> +#include <cstring> + +#include <ini.h> + +static ssize_t ReadFile(int fd, void* buf, size_t n) { + return ::read(fd, buf, n); +} + +static ssize_t ReadSocket(int fd, void* buf, size_t n) { + // This cast is necessary for Windows + return ::recv(fd, static_cast<char*>(buf), n, 0); +} + +void FileDescriptor::Open(const char* file) { + assert(fd_ == -1); + + int oflag = O_RDONLY; +#ifdef _WIN32 + oflag |= O_BINARY; +#endif + fd_ = ::open(file, oflag); + if (fd_ < 0) { + throw ErrnoException(std::string("cannot open file '") + file + "'"); + } + + reader_ = ReadFile; +} + +void FileDescriptor::Connect(const char* host, uint16_t port) { + assert(fd_ == -1); + + fd_ = ::socket(PF_INET, SOCK_STREAM, 0); + if (fd_ < 0) { + throw ErrnoException("cannot open socket"); + } + + struct sockaddr_in in_addr; + std::memset(&in_addr, 0, sizeof(in_addr)); + in_addr.sin_family = AF_INET; + in_addr.sin_port = htons(port); + in_addr.sin_addr.s_addr = inet_addr(host); + + int rv = ::connect(fd_, (struct sockaddr*)&in_addr, sizeof(in_addr)); + if (rv != 0) { + throw ErrnoException(std::string("cannot connect to ") + host + " port " + + std::to_string(port)); + } + + reader_ = ReadSocket; +} + +void FileDescriptor::Destroy() { + if (fd_ != -1) { + int rv = ::close(fd_); + if (rv != 0) { + std::cerr << "close failed: " << strerror(errno) << std::endl; + } + } +} + +const std::string ConfigFile::kNoError; + +void ConfigFile::AddParser(const char* section, Parser parser, void* arg) { + parser_[section] = std::make_pair(parser, arg); +} + +void ConfigFile::Parse(const char* file) { + int status = ini_parse(file, INIHandler, this); + if (status < 0) { + throw ErrnoException(std::string("cannot parse configuration file '") + + file + "'"); + } else if (status > 0) { + throw std::runtime_error( + std::string("invalid line ") + std::to_string(status) + + " in configuration file '" + file + "': " + error_); + } +} + +int ConfigFile::INIHandler(void* user, + const char* section, + const char* name, + const char* value) { + ConfigFile* self = static_cast<ConfigFile*>(user); + auto it = self->parser_.find(section); + if (it != self->parser_.end()) { + std::string error = (*it->second.first)(it->second.second, name, value); + if (error == kNoError) { + return 1; + } + + self->error_ = error; + } else { + self->error_ = std::string("unknown section: ") + section; + } + + return 0; +} + +void Client::Flush() { + while (true) { + void* p = nullptr; + size_t n = 0; + for (auto filter : filters_) { + if (!filter->Run(&p, &n)) { + break; + } + } + + if (n > 0) { + rtems_record_client_run(&base_, p, n); + } else { + break; + } + } +} + +void Client::Run() { + uint64_t todo = UINT64_MAX; + + if (limit_ != 0) { + todo = limit_; + } + + while (stop_ == 0 && todo > 0) { + long buf[8192]; + size_t m = std::min(static_cast<uint64_t>(sizeof(buf)), todo); + ssize_t n = input_.Read(buf, m); + if (n <= 0) { + break; + } + + void* p = &buf[0]; + size_t k = static_cast<size_t>(n); + for (auto filter : filters_) { + if (!filter->Run(&p, &k)) { + std::cerr << "error: input filter failure" << std::endl; + return; + } + } + + rtems_record_client_run(&base_, p, k); + todo -= static_cast<size_t>(n); + } + + Flush(); +} + +void Client::Destroy() { + input_.Destroy(); + rtems_record_client_destroy(&base_); +} |