From a124bdb8513615a4be3c9a2cd1e46ee3fd93c7d9 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 30 Aug 2019 06:30:28 +0200 Subject: record: Add Client base class Update #3665. --- trace/record/client.h | 70 ++++++++ trace/record/record-client-base.cc | 90 ++++++++++ trace/record/record-main-lttng.cc | 334 ++++++++++++++++--------------------- trace/wscript | 4 +- 4 files changed, 309 insertions(+), 189 deletions(-) create mode 100644 trace/record/client.h create mode 100644 trace/record/record-client-base.cc (limited to 'trace') diff --git a/trace/record/client.h b/trace/record/client.h new file mode 100644 index 0000000..2d55052 --- /dev/null +++ b/trace/record/client.h @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2018, 2019 embedded brains GmbH + * + * 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. + */ + +#ifndef RTEMS_TOOLS_TRACE_RECORD_CLIENT_H_ +#define RTEMS_TOOLS_TRACE_RECORD_CLIENT_H_ + +#include +#include + +#include + +#include + +class Client { + public: + Client() = default; + + Client(const Client&) = delete; + + Client& operator=(const Client&) = delete; + + void Open(const char* file); + + void Connect(const char* host, uint16_t port); + + void Run(); + + void RequestStop() { stop_ = 1; } + + void Destroy(); + + protected: + void Initialize(rtems_record_client_handler handler) { + rtems_record_client_init(&base_, handler, this); + } + + size_t data_size() const { return base_.data_size; }; + + private: + rtems_record_client_context base_; + int fd_ = -1; + ssize_t (*reader_)(int fd, void* buf, size_t n) = nullptr; + sig_atomic_t stop_ = 0; +}; + +#endif // RTEMS_TOOLS_TRACE_RECORD_CLIENT_H_ diff --git a/trace/record/record-client-base.cc b/trace/record/record-client-base.cc new file mode 100644 index 0000000..e56608c --- /dev/null +++ b/trace/record/record-client-base.cc @@ -0,0 +1,90 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (C) 2018, 2019 embedded brains GmbH + * + * 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. + */ + +#include "client.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +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) { + return recv(fd, buf, n, 0); +} + +void Client::Open(const char* file) { + assert(fd_ == -1); + fd_ = open(file, O_RDONLY); + assert(fd_ >= 0); + reader_ = ReadFile; +} + +void Client::Connect(const char* host, uint16_t port) { + assert(fd_ == -1); + fd_ = socket(PF_INET, SOCK_STREAM, 0); + assert(fd_ >= 0); + + struct sockaddr_in in_addr; + 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)); + assert(rv == 0); + + reader_ = ReadSocket; +} + +void Client::Run() { + while (stop_ == 0) { + int buf[8192]; + ssize_t n = (*reader_)(fd_, buf, sizeof(buf)); + + if (n > 0) { + rtems_record_client_run(&base_, buf, static_cast(n)); + } else { + break; + } + } +} + +void Client::Destroy() { + int rv = close(fd_); + assert(rv == 0); + + rtems_record_client_destroy(&base_); +} diff --git a/trace/record/record-main-lttng.cc b/trace/record/record-main-lttng.cc index 9e10c5e..51d4f0e 100644 --- a/trace/record/record-main-lttng.cc +++ b/trace/record/record-main-lttng.cc @@ -26,20 +26,13 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include -#include +#include "client.h" -#include #include -#include #include #include -#include -#include -#include -#include -#include +#include #include #define CTF_MAGIC 0xC1FC1FC1 @@ -82,6 +75,8 @@ struct PacketContext { uint32_t cpu_id; } __attribute__((__packed__)); +static const size_t kPacketContextBits = sizeof(PacketContext) * BITS_PER_CHAR; + struct EventHeaderCompact { uint8_t id; uint32_t event_id; @@ -99,6 +94,9 @@ struct EventSchedSwitch { int32_t next_prio; } __attribute__((__packed__)); +static const size_t kEventSchedSwitchBits = + sizeof(EventSchedSwitch) * BITS_PER_CHAR; + struct PerCPUContext { FILE* event_stream; uint64_t timestamp_begin; @@ -111,8 +109,21 @@ struct PerCPUContext { EventSchedSwitch sched_switch; }; -struct LTTNGClient { - rtems_record_client_context base; +class LTTNGClient : public Client { + public: + LTTNGClient() { + Initialize(LTTNGClient::HandlerCaller); + + memset(&pkt_ctx_, 0, sizeof(pkt_ctx_)); + memcpy(pkt_ctx_.header.uuid, kUUID, sizeof(pkt_ctx_.header.uuid)); + pkt_ctx_.header.ctf_magic = CTF_MAGIC; + } + + void OpenStreamFiles(); + + void CloseStreamFiles(); + + private: PerCPUContext per_cpu_[RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT]; /* @@ -122,32 +133,33 @@ struct LTTNGClient { * POSIX API. */ uint8_t thread_names_[THREAD_API_COUNT][THREAD_ID_COUNT][THREAD_NAME_SIZE]; -}; -static int ConnectClient(const char* host, - uint16_t port, - const char* input_file, - bool input_file_flag) { - struct sockaddr_in in_addr; - int fd; - int rv; - - fd = (input_file_flag) ? open(input_file, O_RDONLY) - : socket(PF_INET, SOCK_STREAM, 0); - assert(fd >= 0); - - 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); - - if (!input_file_flag) { - rv = connect(fd, (struct sockaddr*)&in_addr, sizeof(in_addr)); - assert(rv == 0); + PacketContext pkt_ctx_; + + static rtems_record_client_status HandlerCaller(uint64_t bt, + uint32_t cpu, + rtems_record_event event, + uint64_t data, + void* arg) { + LTTNGClient& self = *static_cast(arg); + return self.Handler(bt, cpu, event, data); } - return fd; -} + rtems_record_client_status Handler(uint64_t bt, + uint32_t cpu, + rtems_record_event event, + uint64_t data); + + void CopyThreadName(const ClientItem& item, + size_t api_index, + uint8_t* dst) const; + + void WriteSchedSwitch(PerCPUContext* pcpu, const ClientItem& item); + + void AddThreadName(PerCPUContext* pcpu, const ClientItem& item); + + void PrintItem(const ClientItem& item); +}; static uint32_t GetAPIIndexOfID(uint32_t id) { return ((id >> 24) & 0x7) - 1; @@ -161,14 +173,13 @@ static bool IsIdleTaskByAPIIndex(uint32_t api_index) { return api_index == 0; } -static void CopyThreadName(const LTTNGClient* ctx, - const ClientItem* item, - size_t api_index, - uint8_t* dst) { +void LTTNGClient::CopyThreadName(const ClientItem& item, + size_t api_index, + uint8_t* dst) const { const uint8_t* name; if (api_index < THREAD_API_COUNT) { - name = ctx->thread_names_[api_index][GetObjIndexOfID(item->data)]; + name = thread_names_[api_index][GetObjIndexOfID(item.data)]; } else { name = kEmptyThreadName; } @@ -181,115 +192,96 @@ static void CopyThreadName(const LTTNGClient* ctx, * RTEMS, the idle threads can move around, so mimic this Linux behaviour. */ snprintf(reinterpret_cast(dst) + 4, THREAD_NAME_SIZE - 4, - "/%" PRIu32, item->cpu); + "/%" PRIu32, item.cpu); } } -static void WriteSchedSwitch(LTTNGClient* ctx, - PerCPUContext* pcpu, - const ClientItem* item) { - size_t se_size; - EventSchedSwitch* se; - uint32_t api_index; - - se_size = sizeof(*se) * BITS_PER_CHAR; - pcpu->content_size += se_size; - pcpu->packet_size += se_size; +void LTTNGClient::WriteSchedSwitch(PerCPUContext* pcpu, + const ClientItem& item) { + pcpu->content_size += kEventSchedSwitchBits; + pcpu->packet_size += kEventSchedSwitchBits; - api_index = GetAPIIndexOfID(item->data); - se = &pcpu->sched_switch; + EventSchedSwitch& ss = pcpu->sched_switch; + ss.header.id = COMPACT_HEADER_ID; + ss.header.event_id = 0; + ss.header.ns = item.ns; - se->header.id = COMPACT_HEADER_ID; - se->header.event_id = 0; - se->header.ns = item->ns; - se->next_tid = IsIdleTaskByAPIIndex(api_index) ? 0 : item->data; + uint32_t api_index = GetAPIIndexOfID(item.data); + ss.next_tid = IsIdleTaskByAPIIndex(api_index) ? 0 : item.data; - CopyThreadName(ctx, item, api_index, se->next_comm); - fwrite(se, sizeof(*se), 1, pcpu->event_stream); + CopyThreadName(item, api_index, ss.next_comm); + fwrite(&ss, sizeof(ss), 1, pcpu->event_stream); } -static void AddThreadName(LTTNGClient* ctx, - PerCPUContext* pcpu, - const ClientItem* item) { - uint64_t name; - uint32_t api_index; - uint32_t obj_index; - size_t i; - +void LTTNGClient::AddThreadName(PerCPUContext* pcpu, const ClientItem& item) { if (pcpu->thread_name_index >= THREAD_NAME_SIZE) { return; } - api_index = GetAPIIndexOfID(pcpu->thread_id); - + uint32_t api_index = GetAPIIndexOfID(pcpu->thread_id); if (api_index >= THREAD_API_COUNT) { return; } - obj_index = GetObjIndexOfID(pcpu->thread_id); - name = item->data; - - for (i = pcpu->thread_name_index; - i < pcpu->thread_name_index + ctx->base.data_size; ++i) { - ctx->thread_names_[api_index][obj_index][i] = static_cast(name); + uint32_t obj_index = GetObjIndexOfID(pcpu->thread_id); + uint64_t name = item.data; + size_t i; + for (i = pcpu->thread_name_index; i < pcpu->thread_name_index + data_size(); + ++i) { + thread_names_[api_index][obj_index][i] = static_cast(name); name >>= BITS_PER_CHAR; } pcpu->thread_name_index = i; } -static void PrintItem(LTTNGClient* ctx, const ClientItem* item) { - PerCPUContext* pcpu; - EventSchedSwitch* se; - uint32_t api_index; - - pcpu = &ctx->per_cpu_[item->cpu]; - se = &pcpu->sched_switch; - - if (pcpu->timestamp_begin == 0) { - pcpu->timestamp_begin = item->ns; +void LTTNGClient::PrintItem(const ClientItem& item) { + PerCPUContext& pcpu = per_cpu_[item.cpu]; + if (pcpu.timestamp_begin == 0) { + pcpu.timestamp_begin = item.ns; } - pcpu->timestamp_end = item->ns; + pcpu.timestamp_end = item.ns; - switch (item->event) { - case RTEMS_RECORD_THREAD_SWITCH_OUT: - api_index = GetAPIIndexOfID(item->data); - se->header.ns = item->ns; + EventSchedSwitch& ss = pcpu.sched_switch; + switch (item.event) { + case RTEMS_RECORD_THREAD_SWITCH_OUT: { + uint32_t api_index = GetAPIIndexOfID(item.data); + ss.header.ns = item.ns; if (IsIdleTaskByAPIIndex(api_index)) { - se->prev_tid = 0; - se->prev_state = TASK_IDLE; + ss.prev_tid = 0; + ss.prev_state = TASK_IDLE; } else { - se->prev_tid = item->data; - se->prev_state = TASK_RUNNING; + ss.prev_tid = item.data; + ss.prev_state = TASK_RUNNING; } - CopyThreadName(ctx, item, api_index, se->prev_comm); + CopyThreadName(item, api_index, ss.prev_comm); break; + } case RTEMS_RECORD_THREAD_SWITCH_IN: - if (item->ns == se->header.ns) { - WriteSchedSwitch(ctx, pcpu, item); + if (item.ns == ss.header.ns) { + WriteSchedSwitch(&pcpu, item); } break; case RTEMS_RECORD_THREAD_ID: - pcpu->thread_id = item->data; - pcpu->thread_ns = item->ns; - pcpu->thread_name_index = 0; + pcpu.thread_id = item.data; + pcpu.thread_ns = item.ns; + pcpu.thread_name_index = 0; break; case RTEMS_RECORD_THREAD_NAME: - AddThreadName(ctx, pcpu, item); + AddThreadName(&pcpu, item); break; default: break; } } -static rtems_record_client_status Handler(uint64_t bt, - uint32_t cpu, - rtems_record_event event, - uint64_t data, - void* arg) { +rtems_record_client_status LTTNGClient::Handler(uint64_t bt, + uint32_t cpu, + rtems_record_event event, + uint64_t data) { ClientItem item; item.ns = rtems_record_client_bintime_to_nanoseconds(bt); @@ -297,11 +289,39 @@ static rtems_record_client_status Handler(uint64_t bt, item.event = event; item.data = data; - PrintItem(static_cast(arg), &item); + PrintItem(item); return RTEMS_RECORD_CLIENT_SUCCESS; } +void LTTNGClient::OpenStreamFiles() { + for (size_t i = 0; i < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT; ++i) { + char filename[256]; + snprintf(filename, sizeof(filename), "event_%zu", i); + FILE* f = fopen(filename, "wb"); + assert(f != NULL); + per_cpu_[i].event_stream = f; + fwrite(&pkt_ctx_, sizeof(pkt_ctx_), 1, f); + } +} + +void LTTNGClient::CloseStreamFiles() { + for (size_t i = 0; i < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT; ++i) { + PerCPUContext* pcpu = &per_cpu_[i]; + fseek(pcpu->event_stream, 0, SEEK_SET); + + pkt_ctx_.header.stream_instance_id = i; + pkt_ctx_.timestamp_begin = pcpu->timestamp_begin; + pkt_ctx_.timestamp_end = pcpu->timestamp_end; + pkt_ctx_.content_size = pcpu->content_size + kPacketContextBits; + pkt_ctx_.packet_size = pcpu->packet_size + kPacketContextBits; + pkt_ctx_.cpu_id = i; + + fwrite(&pkt_ctx_, sizeof(pkt_ctx_), 1, pcpu->event_stream); + fclose(pcpu->event_stream); + } +} + static const char kMetadata[] = "/* CTF 1.8 */\n" "\n" @@ -411,6 +431,13 @@ static void GenerateMetadata() { fclose(file); } +static LTTNGClient client; + +static void SignalHandler(int s) { + client.RequestStop(); + signal(s, SIG_DFL); +} + static const struct option kLongOpts[] = {{"help", 0, NULL, 'h'}, {"host", 1, NULL, 'H'}, {"port", 1, NULL, 'p'}, @@ -434,28 +461,11 @@ static void Usage(char** argv) { } int main(int argc, char** argv) { - LTTNGClient ctx; - PacketContext pkt_ctx; - size_t pkt_ctx_size; - const char* host; - uint16_t port; - const char* input_file; - bool input_file_flag; - bool input_TCP_host; - bool input_TCP_port; - int fd; - int rv; + const char* host = "127.0.0.1"; + uint16_t port = 1234; + const char* file = nullptr; int opt; int longindex; - size_t i; - char filename[256]; - - host = "127.0.0.1"; - port = 1234; - input_file = "raw_data"; - input_file_flag = false; - input_TCP_host = false; - input_TCP_port = false; while ((opt = getopt_long(argc, argv, "hH:p:i:", &kLongOpts[0], &longindex)) != -1) { @@ -465,83 +475,31 @@ int main(int argc, char** argv) { return 0; case 'H': host = optarg; - input_TCP_host = true; break; case 'p': port = (uint16_t)strtoul(optarg, NULL, 10); - input_TCP_port = true; break; case 'i': - input_file = optarg; - assert(input_file != NULL); - input_file_flag = true; + file = optarg; break; default: return 1; } } - if (input_file_flag && (input_TCP_host || input_TCP_port)) { - printf("There should be one input medium\n"); - exit(EXIT_SUCCESS); - } - - memset(&ctx, 0, sizeof(ctx)); - GenerateMetadata(); + client.OpenStreamFiles(); - memset(&pkt_ctx, 0, sizeof(pkt_ctx)); - memcpy(pkt_ctx.header.uuid, kUUID, sizeof(pkt_ctx.header.uuid)); - pkt_ctx.header.ctf_magic = CTF_MAGIC; - - for (i = 0; i < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT; ++i) { - FILE* f; - - snprintf(filename, sizeof(filename), "event_%zu", i); - f = fopen(filename, "wb"); - assert(f != NULL); - ctx.per_cpu_[i].event_stream = f; - fwrite(&pkt_ctx, sizeof(pkt_ctx), 1, f); - } - - fd = ConnectClient(host, port, input_file, input_file_flag); - rtems_record_client_init(&ctx.base, Handler, &ctx); - - while (true) { - int buf[8192]; - ssize_t n; - - n = (input_file_flag) ? read(fd, buf, sizeof(buf)) - : recv(fd, buf, sizeof(buf), 0); - if (n > 0) { - rtems_record_client_run(&ctx.base, buf, (size_t)n); - } else { - break; - } - } - - rtems_record_client_destroy(&ctx.base); - pkt_ctx_size = sizeof(pkt_ctx) * BITS_PER_CHAR; - - for (i = 0; i < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT; ++i) { - PerCPUContext* pcpu; - - pcpu = &ctx.per_cpu_[i]; - fseek(pcpu->event_stream, 0, SEEK_SET); - - pkt_ctx.header.stream_instance_id = i; - pkt_ctx.timestamp_begin = pcpu->timestamp_begin; - pkt_ctx.timestamp_end = pcpu->timestamp_end; - pkt_ctx.content_size = pcpu->content_size + pkt_ctx_size; - pkt_ctx.packet_size = pcpu->packet_size + pkt_ctx_size; - pkt_ctx.cpu_id = i; - - fwrite(&pkt_ctx, sizeof(pkt_ctx), 1, pcpu->event_stream); - fclose(pcpu->event_stream); + if (file != nullptr) { + client.Open(file); + } else { + client.Connect(host, port); } - rv = close(fd); - assert(rv == 0); + signal(SIGINT, SignalHandler); + client.Run(); + client.Destroy(); + client.CloseStreamFiles(); return 0; } diff --git a/trace/wscript b/trace/wscript index bc28e85..d2b5625 100644 --- a/trace/wscript +++ b/trace/wscript @@ -59,7 +59,9 @@ def build(bld): # Build rtems-record-lttng # bld.program(target = 'rtems-record-lttng', - source = ['record/record-client.c', 'record/record-main-lttng.cc'], + source = ['record/record-client.c', + 'record/record-client-base.cc', + 'record/record-main-lttng.cc'], includes = ['record'], defines = defines, cflags = conf['cflags'] + conf['warningflags'], -- cgit v1.2.3