record: Add support for user defined event names

This commit is contained in:
Sebastian Huber 2019-09-10 14:34:03 +02:00
parent b60abbfa83
commit 9d16a1789d
3 changed files with 162 additions and 48 deletions

View File

@ -37,8 +37,10 @@
#include <csignal>
#include <cstring>
#include <iostream>
#include <map>
#include <stdexcept>
#include <string>
#include <utility>
class ErrnoException : public std::runtime_error {
public:
@ -69,6 +71,33 @@ class FileDescriptor {
ssize_t (*reader_)(int fd, void* buf, size_t n) = nullptr;
};
class ConfigFile {
public:
static const std::string kNoError;
ConfigFile() = default;
ConfigFile(const ConfigFile&) = delete;
ConfigFile& operator=(const ConfigFile&) = delete;
typedef std::string (*Parser)(void* arg, const char* name, const char* value);
void AddParser(const char* section, Parser parser, void* arg);
void Parse(const char* file);
private:
std::map<std::string, std::pair<Parser, void*> > parser_;
std::string error_;
static int INIHandler(void* user,
const char* section,
const char* name,
const char* value);
};
class Client {
public:
Client() = default;

View File

@ -48,6 +48,8 @@
#include <cassert>
#include <cstring>
#include <ini.h>
static ssize_t ReadFile(int fd, void* buf, size_t n) {
return ::read(fd, buf, n);
}
@ -104,6 +106,44 @@ void FileDescriptor::Destroy() {
}
}
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::Run() {
uint64_t todo = UINT64_MAX;

View File

@ -35,6 +35,7 @@
#include <getopt.h>
#include <cassert>
#include <cerrno>
#include <cinttypes>
#include <cstdio>
#include <cstring>
@ -152,26 +153,11 @@ struct PerCPUContext {
class LTTNGClient : public Client {
public:
LTTNGClient() {
Initialize(LTTNGClient::HandlerCaller);
LTTNGClient();
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;
void ParseConfigFile(const char* config_file);
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;
}
}
void GenerateMetadata();
void OpenExecutable(const char* elf_file);
@ -207,6 +193,8 @@ class LTTNGClient : public Client {
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,
@ -237,6 +225,10 @@ class LTTNGClient : public Client {
void PrintItem(const ClientItem& item);
static std::string EventNameParser(void* arg,
const char* name,
const char* value);
void OpenStreamFiles(uint64_t data);
void CloseStreamFiles();
@ -246,6 +238,32 @@ class LTTNGClient : public Client {
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;
}
@ -512,6 +530,29 @@ rtems_record_client_status LTTNGClient::Handler(uint64_t bt,
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);
@ -671,7 +712,7 @@ static const char kMetadata[] =
"\t};\n"
"};\n";
static void GenerateMetadata() {
void LTTNGClient::GenerateMetadata() {
FILE* f = std::fopen("metadata", "w");
if (f == NULL) {
throw ErrnoException("cannot create file 'metadata'");
@ -684,28 +725,26 @@ static void GenerateMetadata() {
std::fprintf(f,
"\n"
"event {\n"
"\tname = %s;\n"
"\tname = \"%s\";\n"
"\tid = %i;\n"
"\tstream_id = 0;\n"
"\tfields := struct {\n"
"\t\tstring _code;\n"
"\t};\n"
"};\n",
rtems_record_event_text(static_cast<rtems_record_event>(i)),
i);
event_to_name_[i].c_str(), i);
} else {
std::fprintf(f,
"\n"
"event {\n"
"\tname = %s;\n"
"\tname = \"%s\";\n"
"\tid = %i;\n"
"\tstream_id = 0;\n"
"\tfields := struct {\n"
"\t\txint64_t _data;\n"
"\t};\n"
"};\n",
rtems_record_event_text(static_cast<rtems_record_event>(i)),
i);
event_to_name_[i].c_str(), i);
}
}
@ -719,29 +758,30 @@ static void SignalHandler(int s) {
std::signal(s, SIG_DFL);
}
static const struct option kLongOpts[] = {{"help", 0, NULL, 'h'},
{"host", 1, NULL, 'H'},
{"port", 1, NULL, 'p'},
{NULL, 0, NULL, 0}};
static const struct option kLongOpts[] = {
{"elf", 1, NULL, 'e'}, {"help", 0, NULL, 'h'}, {"host", 1, NULL, 'H'},
{"limit", 1, NULL, 'l'}, {"port", 1, NULL, 'p'}, {"config", 1, NULL, 'c'},
{NULL, 0, NULL, 0}};
static void Usage(char** argv) {
std::cout
<< argv[0]
<< " [--host=HOST] [--port=PORT] [--limit=LIMIT] [--elf=ELF] [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
<< " -e, --elf=ELF the ELF executable file" << std::endl
<< " INPUT-FILE the input file" << std::endl;
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
<< " -e, --elf=ELF the ELF executable file"
<< std::endl
<< " -c, --config=CONFIG an INI-style configuration file"
<< std::endl
<< " INPUT-FILE the input file" << std::endl;
}
int main(int argc, char** argv) {
@ -749,10 +789,11 @@ int main(int argc, char** argv) {
uint16_t port = 1234;
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, "e:hH:l:p:", &kLongOpts[0],
while ((opt = getopt_long(argc, argv, "e:hH:l:p:c:", &kLongOpts[0],
&longindex)) != -1) {
switch (opt) {
case 'e':
@ -770,6 +811,9 @@ int main(int argc, char** argv) {
case 'p':
port = (uint16_t)strtoul(optarg, NULL, 0);
break;
case 'c':
config_file = optarg;
break;
default:
return 1;
}
@ -790,7 +834,8 @@ int main(int argc, char** argv) {
}
try {
GenerateMetadata();
client.ParseConfigFile(config_file);
client.GenerateMetadata();
if (elf_file != nullptr) {
client.OpenExecutable(elf_file);