Commit 509656b2 authored by Michael Ott's avatar Michael Ott
Browse files

Reformatted dcdbpusher code with clang-format to establish common coding style

parent 2d8dc277
BasedOnStyle: LLVM
IndentWidth: 8
UseTab: ForContinuationAndIndentation
BreakBeforeBraces: Attach
AlignConsecutiveDeclarations: true
AllowShortBlocksOnASingleLine: true
AllowShortFunctionsOnASingleLine: Inline
ColumnLimit: 0
IndentCaseLabels: true
......@@ -63,10 +63,10 @@
#include <errno.h>
#include <fcntl.h>
#include <features.h>
#include <shared_mutex>
#include <sched.h>
#include <semaphore.h>
#include <set>
#include <shared_mutex>
#include <string.h>
#include <thread>
#include <unistd.h>
......@@ -78,18 +78,18 @@
#include <sys/types.h>
#include <sys/un.h>
#include <libelf.h>
#include <gelf.h>
#include <libelf.h>
using namespace cali;
namespace {
//TODO move definitions into common header file for cali service and pusher plugin
#define MSGQ_SIZE 16*1024*1024
#define STR_PREFIX "/cali_dcdb_"
#define SHM_SIZE (17*1024*1024)
#define SOCK_NAME "DCDBPusherCaliSocket"
#define MSGQ_SIZE 16 * 1024 * 1024
#define STR_PREFIX "/cali_dcdb_"
#define SHM_SIZE (17 * 1024 * 1024)
#define SOCK_NAME "DCDBPusherCaliSocket"
/*
* Queue entry layout:
......@@ -101,61 +101,60 @@ namespace {
// each thread has a local buffer for relevant snapshot data to reduce
// writes to shm queue as this requires locking
static constexpr size_t shm_buf_size = 32 * 1024;
static constexpr size_t max_dat_size = shm_buf_size - sizeof(uint64_t);
static constexpr size_t shm_buf_size = 32 * 1024;
static constexpr size_t max_dat_size = shm_buf_size - sizeof(uint64_t);
static thread_local size_t shm_buf_idx;
static thread_local char shm_buf[shm_buf_size];
class DcdbPusher {
struct func_symbol {
uintptr_t start_addr;
uintptr_t end_addr;
std::string name;
/* make it sortable for faster lookups */
bool operator<(const func_symbol& rhs) const {
return end_addr < rhs.end_addr;
}
};
/* Defines a contiguous executable memory block */
struct addr_range {
uintptr_t start_addr;
uintptr_t end_addr;
std::string pathname;
std::set<func_symbol> symbols; // Filepath + name of the binary where
// this memory range comes from or
// "[Anonymous]" if unknown
/* make it sortable for faster lookups */
bool operator<(const addr_range& rhs) const {
return end_addr < rhs.end_addr;
}
};
private:
static const ConfigSet::Entry s_configdata[];
/* General service attributes */
unsigned snapshots_processed = 0;
unsigned snapshots_failed = 0;
unsigned snapshots_sampler = 0;
unsigned snapshots_event = 0;
Attribute sampler_pc { Attribute::invalid };
Attribute event_begin { Attribute::invalid };
Attribute event_set { Attribute::invalid };
Attribute event_end { Attribute::invalid };
Attribute timestamp { Attribute::invalid };
Attribute thread_id { Attribute::invalid };
/* We store information about function symbols and their addresses here */
std::set<addr_range> addr_data;
mutable std::shared_mutex symbol_lock;
/*
struct func_symbol {
uintptr_t start_addr;
uintptr_t end_addr;
std::string name;
/* make it sortable for faster lookups */
bool operator<(const func_symbol &rhs) const {
return end_addr < rhs.end_addr;
}
};
/* Defines a contiguous executable memory block */
struct addr_range {
uintptr_t start_addr;
uintptr_t end_addr;
std::string pathname;
std::set<func_symbol> symbols; // Filepath + name of the binary where
// this memory range comes from or
// "[Anonymous]" if unknown
/* make it sortable for faster lookups */
bool operator<(const addr_range &rhs) const {
return end_addr < rhs.end_addr;
}
};
private:
static const ConfigSet::Entry s_configdata[];
/* General service attributes */
unsigned snapshots_processed = 0;
unsigned snapshots_failed = 0;
unsigned snapshots_sampler = 0;
unsigned snapshots_event = 0;
Attribute sampler_pc{Attribute::invalid};
Attribute event_begin{Attribute::invalid};
Attribute event_set{Attribute::invalid};
Attribute event_end{Attribute::invalid};
Attribute timestamp{Attribute::invalid};
Attribute thread_id{Attribute::invalid};
/* We store information about function symbols and their addresses here */
std::set<addr_range> addr_data;
mutable std::shared_mutex symbol_lock;
/*
* Layout of shared-memory file used to communicate with dcdbpusher plugin:
*
* //Communication queue, aka ring buffer:
......@@ -167,20 +166,20 @@ private:
* //TODO do not exceed SHM_SIZE
*/
//TODO investigate consequences of fork() on Caliper - DCDBPusher construct
void* shm; // pointer to shared memory object
int shm_file; // fd of the underlying shared memory file
int sock; // unix socket fd for initial shm setup communication
//TODO investigate consequences of fork() on Caliper - DCDBPusher construct
void *shm; // pointer to shared memory object
int shm_file; // fd of the underlying shared memory file
int sock; // unix socket fd for initial shm setup communication
size_t sus_cycle = 15;
std::atomic<bool> run_sus;
std::atomic<bool> sus_trigger;
std::thread sus; //symbol update service
size_t sus_cycle = 15;
std::atomic<bool> run_sus;
std::atomic<bool> sus_trigger;
std::thread sus; //symbol update service
const std::string pid_str; // PID at process start
bool initialized;
const std::string pid_str; // PID at process start
bool initialized;
/*
/*
* Retrieve function symbols from an ELF file and store them in the given
* set.
*
......@@ -206,155 +205,155 @@ private:
* We skip support for separate debug symbols. Unstripped binaries should
* provide us with enough symbol information.
*/
void write_function_symbols(const std::string filename,
const uintptr_t start_addr,
const uintptr_t end_addr,
const uintptr_t offset,
std::set<func_symbol>& dest,
Channel* chn) {
Elf *elf;
Elf_Scn *scn = NULL;
GElf_Ehdr ehdr;
GElf_Shdr shdr;
Elf_Data *data;
int fd;
uintptr_t sym_offset;
elf_version(EV_CURRENT);
fd = open(filename.c_str(), O_RDONLY);
if (fd == -1) {
Log(1).stream() << chn->name() << ": DcdbPusher: Could not open ELF file: "
<< strerror(errno) << std::endl;
return;
}
//search ELF header for symbol table
elf = elf_begin(fd, ELF_C_READ, NULL);
gelf_getehdr(elf, &ehdr);
//check if ELF file type is supported
if (ehdr.e_type != ET_DYN && ehdr.e_type != ET_EXEC) {
// we should only encounter executables and shared libraries during runtime
// we could not process other types anyway
Log(1).stream() << chn->name() << ": DcdbPusher: Unknown ELF type" << std::endl;
return;
}
sym_offset = (ehdr.e_type == ET_DYN ? (start_addr - offset) : 0);
//search for symtab (= symbol table) section
while ((scn = elf_nextscn(elf, scn)) != NULL) {
gelf_getshdr(scn, &shdr);
if (shdr.sh_type == SHT_SYMTAB) {
// found symbol table
data = elf_getdata(scn, NULL);
break;
}
}
//check if symtab section found and if it is usable
if (scn == NULL || shdr.sh_entsize == 0) {
Log(2).stream() << chn->name() << ": DcdbPusher: \"" << filename
<< "\": No symbol table present. Falling back to dynamic symtab." << std::endl;
scn = NULL;
//Fall back to dynamic symbol table. This one should always be present
while ((scn = elf_nextscn(elf, scn)) != NULL) {
gelf_getshdr(scn, &shdr);
if (shdr.sh_type == SHT_DYNSYM) {
// found dynamic symbol table
data = elf_getdata(scn, NULL);
break;
}
}
if (scn == NULL || shdr.sh_entsize == 0) {
Log(1).stream() << chn->name() << ": DcdbPusher: Absolutely no symbols found in \""
<< filename << "\"" << std::endl;
return;
}
}
//retrieve symbol data
int count = shdr.sh_size / shdr.sh_entsize;
//debug
// printf("Section has %d symbols\n", count);
for (int ii = 0; ii < count; ++ii) {
GElf_Sym sym;
if (gelf_getsym(data, ii, &sym) == NULL) {
Log(1).stream() << chn->name() << ": DcdbPusher: Got no symbol" << std::endl;
continue;
}
if (GELF_ST_TYPE(sym.st_info) != STT_FUNC || //only interested in symbols related to executable code
sym.st_shndx == SHN_UNDEF || //external symbol
sym.st_shndx == SHN_ABS) { //absolute symbol, unlikely for STT_FUNC
continue;
}
//resolve symbol name
char* symstr;
char* dsymstr;
func_symbol symdat;
symstr = elf_strptr(elf, shdr.sh_link, sym.st_name);
if (symstr != NULL) {
// Demangle if necessary. Require GNU v3 ABI by the "_Z" prefix.
int status = -1;
if (symstr[0] == '_' && symstr[1] == 'Z') {
dsymstr = abi::__cxa_demangle(symstr, NULL, NULL, &status);
}
if (status == 0) {
symdat.name = std::string(dsymstr);
free((void*) dsymstr);
} else {
symdat.name = std::string(symstr);
}
} else {
symdat.name = "";
}
//resolve symbol value aka its address in this' process virtual memory
symdat.start_addr = sym_offset + sym.st_value;
symdat.end_addr = symdat.start_addr + sym.st_size - 1;
if (symdat.start_addr >= start_addr &&
symdat.start_addr <= end_addr) {
//debug
// printf("Symbol %s in mem range (%llx-%llx, size %llx)\n", symdat.name,
// symdat.start_addr,
// symdat.end_addr,
// sym.st_size);
if (!dest.insert(symdat).second) {
//TODO check again here
//Log(1).stream() << chn->name() << ": DcdbPusher: Could not insert symbol!" << std::endl;
}
} else {
// printf("Symbol %s out of mem range (%llx-%llx, size %llx)\n", symdat.name,
// symdat.start_addr,
// symdat.end_addr,
// sym.st_size);
}
}
//debug
// printf("%s:\n", filename.c_str());
// if (shdr.sh_type == SHT_DYNSYM) {
// printf("Retrieved %d symbols of dynsym\n", dest.size());
// } else {
// printf("Retrieved %d symbols of symtab\n", dest.size());
// }
elf_end(elf);
close(fd);
return;
}
/**
void write_function_symbols(const std::string filename,
const uintptr_t start_addr,
const uintptr_t end_addr,
const uintptr_t offset,
std::set<func_symbol> &dest,
Channel * chn) {
Elf * elf;
Elf_Scn * scn = NULL;
GElf_Ehdr ehdr;
GElf_Shdr shdr;
Elf_Data *data;
int fd;
uintptr_t sym_offset;
elf_version(EV_CURRENT);
fd = open(filename.c_str(), O_RDONLY);
if (fd == -1) {
Log(1).stream() << chn->name() << ": DcdbPusher: Could not open ELF file: "
<< strerror(errno) << std::endl;
return;
}
//search ELF header for symbol table
elf = elf_begin(fd, ELF_C_READ, NULL);
gelf_getehdr(elf, &ehdr);
//check if ELF file type is supported
if (ehdr.e_type != ET_DYN && ehdr.e_type != ET_EXEC) {
// we should only encounter executables and shared libraries during runtime
// we could not process other types anyway
Log(1).stream() << chn->name() << ": DcdbPusher: Unknown ELF type" << std::endl;
return;
}
sym_offset = (ehdr.e_type == ET_DYN ? (start_addr - offset) : 0);
//search for symtab (= symbol table) section
while ((scn = elf_nextscn(elf, scn)) != NULL) {
gelf_getshdr(scn, &shdr);
if (shdr.sh_type == SHT_SYMTAB) {
// found symbol table
data = elf_getdata(scn, NULL);
break;
}
}
//check if symtab section found and if it is usable
if (scn == NULL || shdr.sh_entsize == 0) {
Log(2).stream() << chn->name() << ": DcdbPusher: \"" << filename
<< "\": No symbol table present. Falling back to dynamic symtab." << std::endl;
scn = NULL;
//Fall back to dynamic symbol table. This one should always be present
while ((scn = elf_nextscn(elf, scn)) != NULL) {
gelf_getshdr(scn, &shdr);
if (shdr.sh_type == SHT_DYNSYM) {
// found dynamic symbol table
data = elf_getdata(scn, NULL);
break;
}
}
if (scn == NULL || shdr.sh_entsize == 0) {
Log(1).stream() << chn->name() << ": DcdbPusher: Absolutely no symbols found in \""
<< filename << "\"" << std::endl;
return;
}
}
//retrieve symbol data
int count = shdr.sh_size / shdr.sh_entsize;
//debug
// printf("Section has %d symbols\n", count);
for (int ii = 0; ii < count; ++ii) {
GElf_Sym sym;
if (gelf_getsym(data, ii, &sym) == NULL) {
Log(1).stream() << chn->name() << ": DcdbPusher: Got no symbol" << std::endl;
continue;
}
if (GELF_ST_TYPE(sym.st_info) != STT_FUNC || //only interested in symbols related to executable code
sym.st_shndx == SHN_UNDEF || //external symbol
sym.st_shndx == SHN_ABS) { //absolute symbol, unlikely for STT_FUNC
continue;
}
//resolve symbol name
char *symstr;
char *dsymstr;
func_symbol symdat;
symstr = elf_strptr(elf, shdr.sh_link, sym.st_name);
if (symstr != NULL) {
// Demangle if necessary. Require GNU v3 ABI by the "_Z" prefix.
int status = -1;
if (symstr[0] == '_' && symstr[1] == 'Z') {
dsymstr = abi::__cxa_demangle(symstr, NULL, NULL, &status);
}
if (status == 0) {
symdat.name = std::string(dsymstr);
free((void *)dsymstr);
} else {
symdat.name = std::string(symstr);
}
} else {
symdat.name = "";
}
//resolve symbol value aka its address in this' process virtual memory
symdat.start_addr = sym_offset + sym.st_value;
symdat.end_addr = symdat.start_addr + sym.st_size - 1;
if (symdat.start_addr >= start_addr &&
symdat.start_addr <= end_addr) {
//debug
// printf("Symbol %s in mem range (%llx-%llx, size %llx)\n", symdat.name,
// symdat.start_addr,
// symdat.end_addr,
// sym.st_size);
if (!dest.insert(symdat).second) {
//TODO check again here
//Log(1).stream() << chn->name() << ": DcdbPusher: Could not insert symbol!" << std::endl;
}
} else {
// printf("Symbol %s out of mem range (%llx-%llx, size %llx)\n", symdat.name,
// symdat.start_addr,
// symdat.end_addr,
// sym.st_size);
}
}
//debug
// printf("%s:\n", filename.c_str());
// if (shdr.sh_type == SHT_DYNSYM) {
// printf("Retrieved %d symbols of dynsym\n", dest.size());
// } else {
// printf("Retrieved %d symbols of symtab\n", dest.size());
// }
elf_end(elf);
close(fd);
return;
}
/**
* Set up address data. Parse all address ranges and their pathnames
* which are marked as executable from /proc//maps.
* Address ranges associated to a binary ELF file will be enriched with
......@@ -364,567 +363,564 @@ private:
* Fortran -> mangling compiler dependent; no demangling implemented,
* other languages?)
*/
bool read_addr_data(Channel* chn) {
FILE* file;
addr_range addr;
char exec;
uintptr_t offset;
char buf[4096];
addr_data.clear();
//read mapped address ranges from /proc/self/maps
if (!(file = fopen("/proc/self/maps", "r"))) {
Log(1).stream() << chn->name() << ": DcdbPusher: Could not open memory map: "
<< strerror(errno) << std::endl;
return false;
}
//read one line = one address range
while(fscanf(file, "%llx-%llx %*2c%1c%*s%llx%*s%*s%4096[^\n]",
&(addr.start_addr),
&(addr.end_addr),
&exec,
&(offset),
buf) == 5) {
//debug
//printf("%llx-%llx %c %llx %s\n", addr.start_addr, addr.end_addr, exec, offset, buf);
//Only executable memory ranges are interesting. If the program counter
//ever points in a non-executable section --> HCF
if (exec == 'x') {
addr.pathname = std::string(buf);
//get rid of leading whitespaces
addr.pathname.erase(0, addr.pathname.find_first_not_of(" \t\n\r\f\v"));
//mem ranges are not required to be associated with a name
if (addr.pathname.empty()) {
addr.pathname = "[Anonymous]";
} else if (addr.pathname[0] == '/') {
//address ranges associated with a binary file get enriched
//with symbol data
//debug
//printf("Parsing symbols for %s (%llx-%llx; %llx)\n", addr.pathname.c_str(), addr.start_addr, addr.end_addr, offset);
write_function_symbols(addr.pathname,
addr.start_addr,
addr.end_addr,
offset,
addr.symbols,
chn);
}
//forward slashes are reserved for MQTT topics, use double colon instead
std::replace(addr.pathname.begin(), addr.pathname.end(), '/', ':');
//save this range
if (!addr_data.insert(addr).second) {
Log(1).stream() << chn->name() << ": DcdbPusher: Could not insert address range!" << std::endl;
fclose(file);
return false;
}
}
}
fclose(file);
// Log(1).stream() << chn->name() << ": DcdbPusher: Scan error: "
// << strerror(errno) << std::endl;
return true;
}
/**
bool read_addr_data(Channel *chn) {
FILE * file;
addr_range addr;
char exec;
uintptr_t offset;
char buf[4096];
addr_data.clear();
//read mapped address ranges from /proc/self/maps
if (!(file = fopen("/proc/self/maps", "r"))) {
Log(1).stream() << chn->name() << ": DcdbPusher: Could not open memory map: "
<< strerror(errno) << std::endl;
return false;
}
//read one line = one address range
while (fscanf(file, "%llx-%llx %*2c%1c%*s%llx%*s%*s%4096[^\n]",
&(addr.start_addr),
&(addr.end_addr),
&exec,
&(offset),
buf) == 5) {