/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2019 Ravindra Kumar Meena <rmeena840@gmail.com>
* 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"
#include <getopt.h>
#include <cassert>
#include <cerrno>
#include <cinttypes>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <string>
#include <vector>
#ifdef HAVE_LLVM_DEBUGINFO_SYMBOLIZE_SYMBOLIZE_H
#include <llvm/DebugInfo/Symbolize/Symbolize.h>
#include <llvm/Support/Path.h>
#endif
#define CTF_MAGIC 0xC1FC1FC1
#define TASK_RUNNING 0x0000
#define TASK_IDLE 0x0402
#define UUID_SIZE 16
#define THREAD_NAME_SIZE 16
#define THREAD_API_COUNT 3
#define THREAD_ID_COUNT 0x10000
#define BITS_PER_CHAR 8
#define COMPACT_HEADER_ID 31
static const uint8_t kEmptyThreadName[THREAD_NAME_SIZE] = "";
static const uint8_t kUUID[] = {0x6a, 0x77, 0x15, 0xd0, 0xb5, 0x02, 0x4c, 0x65,
0x86, 0x78, 0x67, 0x77, 0xac, 0x7f, 0x75, 0x5a};
struct ClientItem {
uint64_t ns;
uint32_t cpu;
rtems_record_event event;
uint64_t data;
};
struct PacketHeader {
uint32_t ctf_magic;
uint8_t uuid[UUID_SIZE];
uint32_t stream_id;
uint64_t stream_instance_id;
} __attribute__((__packed__));
struct PacketContext {
PacketHeader header;
uint64_t timestamp_begin;
uint64_t timestamp_end;
uint64_t content_size;
uint64_t packet_size;
uint64_t packet_seq_num;
uint64_t events_discarded;
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;
uint64_t ns;
} __attribute__((__packed__));
struct EventRecordItem {
EventHeaderCompact header;
uint64_t data;
} __attribute__((__packed__));
static const size_t kEventRecordItemBits =
sizeof(EventRecordItem) * BITS_PER_CHAR;
struct EventSchedSwitch {
EventHeaderCompact header;
uint8_t prev_comm[THREAD_NAME_SIZE];
int32_t prev_tid;
int32_t prev_prio;
int64_t prev_state;
uint8_t next_comm[THREAD_NAME_SIZE];
int32_t next_tid;
int32_t next_prio;
} __attribute__((__packed__));
static const size_t kEventSchedSwitchBits =
sizeof(EventSchedSwitch) * BITS_PER_CHAR;
struct EventIRQHandlerEntry {
EventHeaderCompact header;
int32_t irq;
uint8_t name[1];
} __attribute__((__packed__));
static const size_t kEventIRQHandlerEntryBits =
sizeof(EventIRQHandlerEntry) * BITS_PER_CHAR;
struct EventIRQHandlerExit {
EventHeaderCompact header;
int32_t irq;
int32_t ret;
} __attribute__((__packed__));
static const size_t kEventIRQHandlerExitBits =
sizeof(EventIRQHandlerExit) * BITS_PER_CHAR;
struct PerCPUContext {
FILE* event_stream;
uint64_t timestamp_begin;
uint64_t timestamp_end;
uint64_t size_in_bits;
uint32_t thread_id;
uint64_t thread_ns;
size_t thread_name_index;
EventRecordItem record_item;
EventSchedSwitch sched_switch;
EventIRQHandlerEntry irq_handler_entry;
EventIRQHandlerExit irq_handler_exit;
};
class LTTNGClient : public Client {
public:
LTTNGClient();
void ParseConfigFile(const char* config_file);
void GenerateMetadata();
void OpenExecutable(const char* elf_file);
void Destroy() {
Client::Destroy();
CloseStreamFiles();
}
private:
PerCPUContext per_cpu_[RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT];
/*
* @brief Thread names indexed by API and object index.
*
* The API indices are 0 for Internal API, 1 for Classic API and 2 for
* POSIX API.
*/
uint8_t thread_names_[THREAD_API_COUNT][THREAD_ID_COUNT][THREAD_NAME_SIZE];
PacketContext pkt_ctx_;
size_t cpu_count_ = 0;
#ifdef HAVE_LLVM_DEBUGINFO_SYMBOLIZE_SYMBOLIZE_H
llvm::symbolize::LLVMSymbolizer symbolizer_;
#endif
std::string elf_file_;
bool resolve_address_ = false;
typedef std::map<uint64_t, std::vector<char>> AddressToLineMap;
AddressToLineMap address_to_line_;
std::vector<std::string> event_to_name_;
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<LTTNGClient*>(arg);
return self.Handler(bt, cpu, event, data);
}
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 WriteRecordItem(PerCPUContext* pcpu, const ClientItem& item);
void WriteSchedSwitch(PerCPUContext* pcpu, const ClientItem& item);
void WriteIRQHandlerEntry(PerCPUContext* pcpu, const ClientItem& item);
void WriteIRQHandlerExit(PerCPUContext* pcpu, const ClientItem& item);
void ResetThreadName(PerCPUContext* pcpu, const ClientItem& item);
void AddThreadName(PerCPUContext* pcpu, const ClientItem& item);
void PrintItem(const ClientItem& item);
static std::string EventNameParser(void* arg,
const char* name,
const char* value);
void OpenStreamFiles(uint64_t data);
void CloseStreamFiles();
AddressToLineMap::iterator AddAddressAsHexNumber(const ClientItem& item);
AddressToLineMap::iterator ResolveAddress(const ClientItem& item);
};
LTTNGClient::LTTNGClient() : event_to_name_(RTEMS_RECORD_LAST + 1) {
Initialize(LTTNGClient::HandlerCaller);
std::memset(&pkt_ctx_, 0, sizeof(pkt_ctx_));
std::memcpy(pkt_ctx_.header.uuid, kUUID, sizeof(pkt_ctx_.header.uuid));
pkt_ctx_.header.ctf_magic = CTF_MAGIC;
for (size_t i = 0; i < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT; ++i) {
PerCPUContext& pcpu = per_cpu_[i];
pcpu.sched_switch.header.id = COMPACT_HEADER_ID;
pcpu.sched_switch.header.event_id = 1024;
pcpu.irq_handler_entry.header.id = COMPACT_HEADER_ID;
pcpu.irq_handler_entry.header.event_id = 1025;
pcpu.irq_handler_entry.name[0] = '\0';
pcpu.irq_handler_exit.header.id = COMPACT_HEADER_ID;
pcpu.irq_handler_exit.header.event_id = 1026;
pcpu.irq_handler_exit.ret = 1;
pcpu.record_item.header.id = COMPACT_HEADER_ID;
}
for (int i = 0; i <= RTEMS_RECORD_LAST; ++i) {
event_to_name_[i] =
rtems_record_event_text(static_cast<rtems_record_event>(i));
}
}
static uint32_t GetAPIIndexOfID(uint32_t id) {
return ((id >> 24) & 0x7) - 1;
}
static uint32_t GetObjIndexOfID(uint32_t id) {
return id & (THREAD_ID_COUNT - 1);
}
static bool IsIdleTaskByAPIIndex(uint32_t api_index) {
return api_index == 0;
}
static const uint8_t kCPUPostfix[RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT][2] = {
{'0', '\0'}, {'1', '\0'}, {'2', '\0'}, {'3', '\0'}, {'4', '\0'},
{'5', '\0'}, {'6', '\0'}, {'7', '\0'}, {'8', '\0'}, {'9', '\0'},
{'1', '0'}, {'1', '1'}, {'1', '2'}, {'1', '3'}, {'1', '4'},
{'1', '5'}, {'1', '6'}, {'1', '7'}, {'1', '8'}, {'1', '9'},
{'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'}, {'2', '4'},
{'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
{'3', '0'}, {'3', '1'}};
void LTTNGClient::OpenExecutable(const char* elf_file) {
elf_file_ = elf_file;
resolve_address_ = true;
}
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 = thread_names_[api_index][GetObjIndexOfID(item.data)];
} else {
name = kEmptyThreadName;
}
std::memcpy(dst, name, THREAD_NAME_SIZE);
if (IsIdleTaskByAPIIndex(api_index)) {
/*
* In Linux, the idle threads are bound to a specific CPU (swapper/n). In
* RTEMS, the idle threads can move around, so mimic this Linux behaviour.
*/
dst[4] = '/';
dst[5] = kCPUPostfix[item.cpu][0];
dst[6] = kCPUPostfix[item.cpu][1];
}
}
static bool IsCodeEvent(rtems_record_event event) {
switch (event) {
default:
return false;
case RTEMS_RECORD_CALLER:
case RTEMS_RECORD_FUNCTION_ENTRY:
case RTEMS_RECORD_FUNCTION_EXIT:
case RTEMS_RECORD_ISR_DISABLE:
case RTEMS_RECORD_ISR_ENABLE:
case RTEMS_RECORD_LINE:
case RTEMS_RECORD_THREAD_DISPATCH_DISABLE:
case RTEMS_RECORD_THREAD_DISPATCH_ENABLE:
return true;
}
}
LTTNGClient::AddressToLineMap::iterator LTTNGClient::AddAddressAsHexNumber(
const ClientItem& item) {
char hex[19];
int n = std::snprintf(hex, sizeof(hex), "0x%" PRIx64, item.data);
assert(static_cast<size_t>(n) < sizeof(hex));
std::vector<char> code(hex, hex + n + 1);
return address_to_line_.emplace(item.data, std::move(code)).first;
}
LTTNGClient::AddressToLineMap::iterator LTTNGClient::ResolveAddress(
const ClientItem& item) {
#ifdef HAVE_LLVM_DEBUGINFO_SYMBOLIZE_SYMBOLIZE_H
if (resolve_address_) {
auto res_or_err = symbolizer_.symbolizeCode(
elf_file_,
#if LLVM_VERSION_MAJOR >= 9
{item.data, llvm::object::SectionedAddress::UndefSection});
#else
item.data);
#endif
if (res_or_err) {
auto info = res_or_err.get();
std::string fn = info.FunctionName;
std::string str;
if (fn != "<invalid>") {
str += fn;
str += " at ";
}
str += llvm::sys::path::filename(info.FileName);
str += ":";
str += std::to_string(info.Line);
std::vector<char> code(str.begin(), str.end());
code.push_back('\0');
return address_to_line_.emplace(item.data, std::move(code)).first;
}
}
#endif
return AddAddressAsHexNumber(item);
}
void LTTNGClient::WriteRecordItem(PerCPUContext* pcpu, const ClientItem& item) {
if (IsCodeEvent(item.event)) {
EventHeaderCompact header;
header.id = COMPACT_HEADER_ID;
header.event_id = item.event;
header.ns = item.ns;
auto it = address_to_line_.find(item.data);
if (it == address_to_line_.end()) {
it = ResolveAddress(item);
}
pcpu->size_in_bits += (sizeof(header) + it->second.size()) * BITS_PER_CHAR;
std::fwrite(&header, sizeof(header), 1, pcpu->event_stream);
std::fwrite(&(*it->second.begin()), it->second.size(), 1,
pcpu->event_stream);
} else {
pcpu->size_in_bits += kEventRecordItemBits;
EventRecordItem& ri = pcpu->record_item;
ri.header.ns = item.ns;
ri.header.event_id = item.event;
ri.data = item.data;
std::fwrite(&ri, sizeof(ri), 1, pcpu->event_stream);
}
}
void LTTNGClient::WriteSchedSwitch(PerCPUContext* pcpu,
const ClientItem& item) {
pcpu->size_in_bits += kEventSchedSwitchBits;
EventSchedSwitch& ss = pcpu->sched_switch;
ss.header.ns = item.ns;
uint32_t api_index = GetAPIIndexOfID(item.data);
ss.next_tid = IsIdleTaskByAPIIndex(api_index) ? 0 : item.data;
CopyThreadName(item, api_index, ss.next_comm);
std::fwrite(&ss, sizeof(ss), 1, pcpu->event_stream);
}
void LTTNGClient::WriteIRQHandlerEntry(PerCPUContext* pcpu,
const ClientItem& item) {
pcpu->size_in_bits += kEventIRQHandlerEntryBits;
EventIRQHandlerEntry& ih = pcpu->irq_handler_entry;
ih.header.ns = item.ns;
ih.irq = static_cast<int32_t>(item.data);
std::fwrite(&ih, sizeof(ih), 1, pcpu->event_stream);
}
void LTTNGClient::WriteIRQHandlerExit(PerCPUContext* pcpu,
const ClientItem& item) {
pcpu->size_in_bits += kEventIRQHandlerExitBits;
EventIRQHandlerExit& ih = pcpu->irq_handler_exit;
ih.header.ns = item.ns;
ih.irq = static_cast<int32_t>(item.data);
std::fwrite(&ih, sizeof(ih), 1, pcpu->event_stream);
}
void LTTNGClient::ResetThreadName(PerCPUContext* pcpu, const ClientItem& item) {
pcpu->thread_id = item.data;
pcpu->thread_ns = item.ns;
pcpu->thread_name_index = 0;
uint32_t api_index = GetAPIIndexOfID(pcpu->thread_id);
if (api_index >= THREAD_API_COUNT) {
return;
}
uint32_t obj_index = GetObjIndexOfID(pcpu->thread_id);
std::memset(&thread_names_[api_index][obj_index][0], 0, THREAD_NAME_SIZE);
}
void LTTNGClient::AddThreadName(PerCPUContext* pcpu, const ClientItem& item) {
if (pcpu->thread_name_index >= THREAD_NAME_SIZE) {
return;
}
uint32_t api_index = GetAPIIndexOfID(pcpu->thread_id);
if (api_index >= THREAD_API_COUNT) {
return;
}
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<uint8_t>(name);
name >>= BITS_PER_CHAR;
}
pcpu->thread_name_index = i;
}
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;
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)) {
ss.prev_tid = 0;
ss.prev_state = TASK_IDLE;
} else {
ss.prev_tid = item.data;
ss.prev_state = TASK_RUNNING;
}
CopyThreadName(item, api_index, ss.prev_comm);
break;
}
case RTEMS_RECORD_THREAD_SWITCH_IN:
if (item.ns == ss.header.ns) {
WriteSchedSwitch(&pcpu, item);
}
break;
case RTEMS_RECORD_THREAD_CREATE:
case RTEMS_RECORD_THREAD_ID:
ResetThreadName(&pcpu, item);
break;
case RTEMS_RECORD_INTERRUPT_ENTRY:
WriteIRQHandlerEntry(&pcpu, item);
break;
case RTEMS_RECORD_INTERRUPT_EXIT:
WriteIRQHandlerExit(&pcpu, item);
break;
case RTEMS_RECORD_THREAD_NAME:
AddThreadName(&pcpu, item);
break;
case RTEMS_RECORD_PROCESSOR_MAXIMUM:
OpenStreamFiles(item.data);
break;
default:
if (item.ns != 0) {
WriteRecordItem(&pcpu, item);
}
break;
}
}
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);
item.cpu = cpu;
item.event = event;
item.data = data;
PrintItem(item);
return RTEMS_RECORD_CLIENT_SUCCESS;
}
std::string LTTNGClient::EventNameParser(void* arg,
const char* name,
const char* value) {
LTTNGClient* self = static_cast<LTTNGClient*>(arg);
errno = 0;
char* end;
long event = std::strtol(name, &end, 0);
if (errno == 0 && *end == '\0' && event >= 0 && event <= RTEMS_RECORD_LAST) {
self->event_to_name_[event] = value;
return ConfigFile::kNoError;
}
return std::string("invalid event: ") + name;
}
void LTTNGClient::ParseConfigFile(const char* config_file) {
if (config_file != nullptr) {
ConfigFile file;
file.AddParser("EventNames", EventNameParser, this);
file.Parse(config_file);
}
}
void LTTNGClient::OpenStreamFiles(uint64_t data) {
// Assertions are ensured by C record client
assert(cpu_count_ == 0 && data < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT);
cpu_count_ = static_cast<size_t>(data) + 1;
for (size_t i = 0; i < cpu_count_; ++i) {
std::string filename("stream_");
filename += std::to_string(i);
FILE* f = std::fopen(filename.c_str(), "wb");
if (f == NULL) {
throw ErrnoException("cannot create file '" + filename + "'");
}
per_cpu_[i].event_stream = f;
std::fwrite(&pkt_ctx_, sizeof(pkt_ctx_), 1, f);
}
}
void LTTNGClient::CloseStreamFiles() {
for (size_t i = 0; i < cpu_count_; ++i) {
PerCPUContext* pcpu = &per_cpu_[i];
std::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->size_in_bits + kPacketContextBits;
pkt_ctx_.packet_size = pkt_ctx_.content_size;
pkt_ctx_.cpu_id = i;
std::fwrite(&pkt_ctx_, sizeof(pkt_ctx_), 1, pcpu->event_stream);
std::fclose(pcpu->event_stream);
}
}
static const char kMetadata[] =
"/* CTF 1.8 */\n"
"\n"
"typealias integer { size = 5; align = 1; signed = false; } := uint5_t;\n"
"typealias integer { size = 8; align = 8; signed = false; } := uint8_t;\n"
"typealias integer { size = 32; align = 8; signed = true; } := int32_t;\n"
"typealias integer { size = 32; align = 8; signed = false; } := uint32_t;\n"
"typealias integer { size = 64; align = 8; signed = true; } := int64_t;\n"
"typealias integer { size = 64; align = 8; signed = false; } := uint64_t;\n"
"\n"
"typealias integer {\n"
"\tsize = 64; align = 8; signed = false; base = 16;\n"
"} := xint64_t;\n"
"\n"
"typealias integer {\n"
"\tsize = 8; align = 8; signed = false; encoding = UTF8; base = 10;\n"
"} := utf8_t;\n"
"\n"
"typealias integer {\n"
"\tsize = 27; align = 1; signed = false;\n"
"\tmap = clock.monotonic.value;\n"
"} := uint27_clock_monotonic_t;\n"
"\n"
"typealias integer {\n"
"\tsize = 64; align = 8; signed = false;\n"
"\tmap = clock.monotonic.value;\n"
"} := uint64_clock_monotonic_t;\n"
"\n"
"trace {\n"
"\tmajor = 1;\n"
"\tminor = 8;\n"
"\tuuid = \"6a7715d0-b502-4c65-8678-6777ac7f755a\";\n"
"\tbyte_order = le;\n"
"\tpacket.header := struct {\n"
"\t\tuint32_t magic;\n"
"\t\tuint8_t uuid[16];\n"
"\t\tuint32_t stream_id;\n"
"\t\tuint64_t stream_instance_id;\n"
"\t};\n"
"};\n"
"\n"
"env {\n"
"\thostname = \"RTEMS\";\n"
"\tdomain = \"kernel\";\n"
"\tsysname = \"Linux\";\n"
"\tkernel_release = \"5\";\n"
"\tkernel_version = \"0\";\n"
"\ttracer_name = \"lttng-modules\";\n"
"\ttracer_major = 2;\n"
"\ttracer_minor = 11;\n"
"\ttracer_patchlevel = 0;\n"
"};\n"
"\n"
"clock {\n"
"\tname = \"monotonic\";\n"
"\tuuid = \"234d669d-7651-4bc1-a7fd-af581ecc6232\";\n"
"\tdescription = \"Monotonic Clock\";\n"
"\tfreq = 1000000000;\n"
"\toffset = 0;\n"
"};\n"
"\n"
"struct packet_context {\n"
"\tuint64_clock_monotonic_t timestamp_begin;\n"
"\tuint64_clock_monotonic_t timestamp_end;\n"
"\tuint64_t content_size;\n"
"\tuint64_t packet_size;\n"
"\tuint64_t packet_seq_num;\n"
"\tuint64_t events_discarded;\n"
"\tuint32_t cpu_id;\n"
"};\n"
"\n"
"struct event_header_compact {\n"
"\tenum : uint5_t { compact = 0 ... 30, extended = 31 } id;\n"
"\tvariant <id> {\n"
"\t\tstruct {\n"
"\t\t\tuint27_clock_monotonic_t timestamp;\n"
"\t\t} compact;\n"
"\t\tstruct {\n"
"\t\t\tuint32_t id;\n"
"\t\t\tuint64_clock_monotonic_t timestamp;\n"
"\t\t} extended;\n"
"\t} v;\n"
"} align(8);\n"
"\n"
"stream {\n"
"\tid = 0;\n"
"\tevent.header := struct event_header_compact;\n"
"\tpacket.context := struct packet_context;\n"
"};\n"
"\n"
"event {\n"
"\tname = sched_switch;\n"
"\tid = 1024;\n"
"\tstream_id = 0;\n"
"\tfields := struct {\n"
"\t\tutf8_t _prev_comm[16];\n"
"\t\tint32_t _prev_tid;\n"
"\t\tint32_t _prev_prio;\n"
"\t\tint64_t _prev_state;\n"
"\t\tutf8_t _next_comm[16];\n"
"\t\tint32_t _next_tid;\n"
"\t\tint32_t _next_prio;\n"
"\t};\n"
"};\n"
"\n"
"event {\n"
"\tname = irq_handler_entry;\n"
"\tid = 1025;\n"
"\tstream_id = 0;\n"
"\tfields := struct {\n"
"\t\tint32_t _irq;\n"
"\t\tstring _name;\n"
"\t};\n"
"};\n"
"\n"
"event {\n"
"\tname = irq_handler_exit;\n"
"\tid = 1026;\n"
"\tstream_id = 0;\n"
"\tfields := struct {\n"
"\t\tint32_t _irq;\n"
"\t\tint32_t _ret;\n"
"\t};\n"
"};\n";
void LTTNGClient::GenerateMetadata() {
FILE* f = std::fopen("metadata", "w");
if (f == NULL) {
throw ErrnoException("cannot create file 'metadata'");
}
std::fwrite(kMetadata, sizeof(kMetadata) - 1, 1, f);
for (int i = 0; i <= RTEMS_RECORD_LAST; ++i) {
if (IsCodeEvent(static_cast<rtems_record_event>(i))) {
std::fprintf(f,
"\n"
"event {\n"
"\tname = \"%s\";\n"
"\tid = %i;\n"
"\tstream_id = 0;\n"
"\tfields := struct {\n"
"\t\tstring _code;\n"
"\t};\n"
"};\n",
event_to_name_[i].c_str(), i);
} else {
std::fprintf(f,
"\n"
"event {\n"
"\tname = \"%s\";\n"
"\tid = %i;\n"
"\tstream_id = 0;\n"
"\tfields := struct {\n"
"\t\txint64_t _data;\n"
"\t};\n"
"};\n",
event_to_name_[i].c_str(), i);
}
}
std::fclose(f);
}
static LTTNGClient client;
static void SignalHandler(int s) {
client.RequestStop();
std::signal(s, SIG_DFL);
}
static const struct option kLongOpts[] = {
{"elf", 1, NULL, 'e'}, {"help", 0, NULL, 'h'},
{"host", 1, NULL, 'H'}, {"port", 1, NULL, 'p'},
{"limit", 1, NULL, 'l'}, {"base64", 0, NULL, 'b'},
{"zlib", 0, NULL, 'z'}, {"config", 1, NULL, 'c'},
{"defaults", 0, NULL, 'd'}, {NULL, 0, NULL, 0}};
static void Usage(char** argv) {
std::cout << argv[0] << " [OPTION]... [INPUT-FILE]" << std::endl
<< std::endl
<< "Mandatory arguments to long options are mandatory for short "
"options too."
<< std::endl
<< " -h, --help print this help text" << std::endl
<< " -H, --host=HOST the host IPv4 address of the "
"record server"
<< std::endl
<< " -p, --port=PORT the TCP port of the record server"
<< std::endl
<< " -l, --limit=LIMIT limit in bytes to process"
<< std::endl
<< " -b, --base64 input is base64 encoded"
<< std::endl
<< " -z, --zlib input is zlib compressed"
<< std::endl
<< " -e, --elf=ELF the ELF executable file"
<< std::endl
<< " -c, --config=CONFIG an INI-style configuration file"
<< std::endl
<< " -d, --defaults print default values for "
"configuration file"
<< std::endl
<< " INPUT-FILE the input file" << std::endl;
}
static void PrintDefaults() {
std::cout << "[EventNames]" << std::endl;
for (int i = 0; i <= RTEMS_RECORD_LAST; ++i) {
std::cout << i << " = "
<< rtems_record_event_text(static_cast<rtems_record_event>(i))
<< std::endl;
}
}
int main(int argc, char** argv) {
const char* host = "127.0.0.1";
uint16_t port = 1234;
bool is_base64_encoded = false;
bool is_zlib_compressed = false;
const char* elf_file = nullptr;
const char* input_file = nullptr;
const char* config_file = nullptr;
int opt;
int longindex;
while ((opt = getopt_long(argc, argv, "hH:p:l:bze:c:d", &kLongOpts[0],
&longindex)) != -1) {
switch (opt) {
case 'h':
Usage(argv);
return 0;
case 'H':
host = optarg;
break;
case 'p':
port = (uint16_t)strtoul(optarg, NULL, 0);
break;
case 'l':
client.set_limit(strtoull(optarg, NULL, 0));
break;
case 'b':
is_base64_encoded = true;
break;
case 'z':
is_zlib_compressed = true;
break;
case 'e':
elf_file = optarg;
break;
case 'c':
config_file = optarg;
break;
case 'd':
PrintDefaults();
return 0;
default:
return 1;
}
}
if (optind == argc - 1) {
input_file = argv[optind];
++optind;
}
if (optind != argc) {
std::cerr << argv[0] << ": unrecognized options:";
for (int i = optind; i < argc; ++i) {
std::cerr << " '" << argv[i] << "'";
}
std::cerr << std::endl;
return 1;
}
try {
if (is_base64_encoded) {
client.AddFilter(new Base64Filter());
}
if (is_zlib_compressed) {
#ifdef HAVE_ZLIB_H
client.AddFilter(new ZlibFilter());
#endif
}
client.ParseConfigFile(config_file);
client.GenerateMetadata();
if (elf_file != nullptr) {
client.OpenExecutable(elf_file);
}
if (input_file != nullptr) {
client.Open(input_file);
} else {
client.Connect(host, port);
}
std::signal(SIGINT, SignalHandler);
client.Run();
client.Destroy();
} catch (std::exception& e) {
std::cerr << argv[0] << ": " << e.what() << std::endl;
return 1;
}
return 0;
}