Commit 751ac54f authored by 4ast's avatar 4ast

Merge pull request #498 from vmg/vmg/usdt

[WIP] Native implementation for USDT probes
parents 55513a3a 9259841a
......@@ -2,3 +2,4 @@
BasedOnStyle: Google
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
AccessModifierOffset: -2
......@@ -33,12 +33,12 @@ if (CMAKE_COMPILER_IS_GNUCC)
endif()
endif()
add_library(bcc-shared SHARED bpf_common.cc bpf_module.cc libbpf.c perf_reader.c shared_table.cc exported_files.cc bcc_elf.c bcc_proc.c bcc_syms.cc)
add_library(bcc-shared SHARED bpf_common.cc bpf_module.cc libbpf.c perf_reader.c shared_table.cc exported_files.cc bcc_elf.c bcc_proc.c bcc_syms.cc usdt_args.cc usdt.cc)
set_target_properties(bcc-shared PROPERTIES VERSION ${REVISION_LAST} SOVERSION 0)
set_target_properties(bcc-shared PROPERTIES OUTPUT_NAME bcc)
add_library(bcc-loader-static libbpf.c perf_reader.c bcc_elf.c bcc_proc.c)
add_library(bcc-static STATIC bpf_common.cc bpf_module.cc shared_table.cc exported_files.cc bcc_syms.cc)
add_library(bcc-static STATIC bpf_common.cc bpf_module.cc shared_table.cc exported_files.cc bcc_syms.cc usdt_args.cc usdt.cc)
set_target_properties(bcc-static PROPERTIES OUTPUT_NAME bcc)
# BPF is still experimental otherwise it should be available
......
......@@ -20,6 +20,8 @@
extern "C" {
#endif
#include <stdint.h>
struct bcc_elf_usdt {
uint64_t pc;
uint64_t base_addr;
......
......@@ -100,8 +100,10 @@ int bcc_procutils_each_module(int pid, bcc_procutils_modulecb callback,
while (isspace(mapname[0])) mapname++;
if (strchr(perm, 'x') && mapname[0] && mapname[0] != '[')
callback(mapname, (uint64_t)begin, (uint64_t)end, payload);
if (strchr(perm, 'x') && mapname[0] && mapname[0] != '[') {
if (callback(mapname, (uint64_t)begin, (uint64_t)end, payload) < 0)
break;
}
}
} while (ret && ret != EOF);
......
......@@ -22,8 +22,9 @@
extern "C" {
#endif
typedef void (*bcc_procutils_modulecb)(const char *, uint64_t, uint64_t,
void *);
#include <stdint.h>
typedef int (*bcc_procutils_modulecb)(const char *, uint64_t, uint64_t, void *);
typedef void (*bcc_procutils_ksymcb)(const char *, uint64_t, void *);
const char *bcc_procutils_which_so(const char *libname);
......
......@@ -13,56 +13,41 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vector>
#include <string>
#include <algorithm>
#include <unordered_map>
#include <sys/types.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include "bcc_syms.h"
#include "bcc_proc.h"
#include "bcc_elf.h"
#include "bcc_proc.h"
#include "bcc_syms.h"
class SymbolCache {
public:
virtual void refresh() = 0;
virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym) = 0;
virtual bool resolve_name(const char *name, uint64_t *addr) = 0;
};
class KSyms : SymbolCache {
struct Symbol {
Symbol(const char *name, uint64_t addr) : name(name), addr(addr) {}
std::string name;
uint64_t addr;
bool operator<(const Symbol &rhs) const { return addr < rhs.addr; }
};
std::vector<Symbol> syms_;
std::unordered_map<std::string, uint64_t> symnames_;
static void _add_symbol(const char *, uint64_t, void *);
public:
virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym);
virtual bool resolve_name(const char *name, uint64_t *addr);
virtual void refresh() {
if (syms_.empty()) {
bcc_procutils_each_ksym(_add_symbol, this);
std::sort(syms_.begin(), syms_.end());
}
}
};
#include "syms.h"
ino_t ProcStat::getinode_() {
struct stat s;
return (!stat(procfs_.c_str(), &s)) ? s.st_ino : -1;
}
ProcStat::ProcStat(int pid) : inode_(-1) {
char buffer[128];
snprintf(buffer, sizeof(buffer), "/proc/%d/exe", pid);
procfs_ = buffer;
}
void KSyms::_add_symbol(const char *symname, uint64_t addr, void *p) {
KSyms *ks = static_cast<KSyms *>(p);
ks->syms_.emplace_back(symname, addr);
}
void KSyms::refresh() {
if (syms_.empty()) {
bcc_procutils_each_ksym(_add_symbol, this);
std::sort(syms_.begin(), syms_.end());
}
}
bool KSyms::resolve_addr(uint64_t addr, struct bcc_symbol *sym) {
refresh();
......@@ -80,7 +65,8 @@ bool KSyms::resolve_addr(uint64_t addr, struct bcc_symbol *sym) {
return true;
}
bool KSyms::resolve_name(const char *name, uint64_t *addr) {
bool KSyms::resolve_name(const char *_unused, const char *name,
uint64_t *addr) {
refresh();
if (syms_.size() != symnames_.size()) {
......@@ -98,65 +84,6 @@ bool KSyms::resolve_name(const char *name, uint64_t *addr) {
return true;
}
class ProcStat {
std::string procfs_;
ino_t inode_;
ino_t getinode_() {
struct stat s;
return (!stat(procfs_.c_str(), &s)) ? s.st_ino : -1;
}
public:
ProcStat(int pid) : inode_(-1) {
char buffer[128];
snprintf(buffer, sizeof(buffer), "/proc/%d/exe", pid);
procfs_ = buffer;
}
bool is_stale() { return inode_ != getinode_(); }
void reset() { inode_ = getinode_(); }
};
class ProcSyms : SymbolCache {
struct Symbol {
Symbol(const char *name, uint64_t start, uint64_t size, int flags = 0)
: name(name), start(start), size(size), flags(flags) {}
std::string name;
uint64_t start;
uint64_t size;
int flags;
};
struct Module {
Module(const char *name, uint64_t start, uint64_t end)
: name_(name), start_(start), end_(end) {}
std::string name_;
uint64_t start_;
uint64_t end_;
std::vector<Symbol> syms_;
void load_sym_table();
bool decode_sym(uint64_t addr, struct bcc_symbol *sym);
bool is_so() { return strstr(name_.c_str(), ".so") != nullptr; }
static int _add_symbol(const char *symname, uint64_t start, uint64_t end,
int flags, void *p);
};
int pid_;
std::vector<Module> modules_;
ProcStat procstat_;
static void _add_module(const char *, uint64_t, uint64_t, void *);
public:
ProcSyms(int pid);
virtual void refresh();
virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym);
virtual bool resolve_name(const char *name, uint64_t *addr);
};
ProcSyms::ProcSyms(int pid) : pid_(pid), procstat_(pid) { refresh(); }
void ProcSyms::refresh() {
......@@ -165,10 +92,11 @@ void ProcSyms::refresh() {
procstat_.reset();
}
void ProcSyms::_add_module(const char *modname, uint64_t start, uint64_t end,
void *payload) {
int ProcSyms::_add_module(const char *modname, uint64_t start, uint64_t end,
void *payload) {
ProcSyms *ps = static_cast<ProcSyms *>(payload);
ps->modules_.emplace_back(modname, start, end);
return 0;
}
bool ProcSyms::resolve_addr(uint64_t addr, struct bcc_symbol *sym) {
......@@ -181,13 +109,20 @@ bool ProcSyms::resolve_addr(uint64_t addr, struct bcc_symbol *sym) {
for (Module &mod : modules_) {
if (addr >= mod.start_ && addr <= mod.end_)
return mod.decode_sym(addr, sym);
return mod.find_addr(addr, sym);
}
return false;
}
bool ProcSyms::resolve_name(const char *name, uint64_t *addr) {
*addr = 0x0;
bool ProcSyms::resolve_name(const char *module, const char *name,
uint64_t *addr) {
if (procstat_.is_stale())
refresh();
for (Module &mod : modules_) {
if (mod.name_ == module)
return mod.find_name(name, addr);
}
return false;
}
......@@ -198,6 +133,10 @@ int ProcSyms::Module::_add_symbol(const char *symname, uint64_t start,
return 0;
}
bool ProcSyms::Module::is_so() const {
return strstr(name_.c_str(), ".so") != nullptr;
}
void ProcSyms::Module::load_sym_table() {
if (syms_.size())
return;
......@@ -205,7 +144,19 @@ void ProcSyms::Module::load_sym_table() {
bcc_elf_foreach_sym(name_.c_str(), _add_symbol, this);
}
bool ProcSyms::Module::decode_sym(uint64_t addr, struct bcc_symbol *sym) {
bool ProcSyms::Module::find_name(const char *symname, uint64_t *addr) {
load_sym_table();
for (Symbol &s : syms_) {
if (s.name == symname) {
*addr = is_so() ? start_ + s.start : s.start;
return true;
}
}
return false;
}
bool ProcSyms::Module::find_addr(uint64_t addr, struct bcc_symbol *sym) {
uint64_t offset = is_so() ? (addr - start_) : addr;
load_sym_table();
......@@ -240,7 +191,7 @@ int bcc_symcache_resolve(void *resolver, uint64_t addr,
int bcc_symcache_resolve_name(void *resolver, const char *name,
uint64_t *addr) {
SymbolCache *cache = static_cast<SymbolCache *>(resolver);
return cache->resolve_name(name, addr) ? 0 : -1;
return cache->resolve_name(nullptr, name, addr) ? 0 : -1;
}
void bcc_symcache_refresh(void *resolver) {
......@@ -251,6 +202,7 @@ void bcc_symcache_refresh(void *resolver) {
static int _find_sym(const char *symname, uint64_t addr, uint64_t end,
int flags, void *payload) {
struct bcc_symbol *sym = (struct bcc_symbol *)payload;
// TODO: check for actual function symbol in flags
if (!strcmp(sym->name, symname)) {
sym->offset = addr;
return -1;
......@@ -258,6 +210,10 @@ static int _find_sym(const char *symname, uint64_t addr, uint64_t end,
return 0;
}
int bcc_find_symbol_addr(struct bcc_symbol *sym) {
return bcc_elf_foreach_sym(sym->module, _find_sym, sym);
}
int bcc_resolve_symname(const char *module, const char *symname,
const uint64_t addr, struct bcc_symbol *sym) {
uint64_t load_addr;
......@@ -286,8 +242,10 @@ int bcc_resolve_symname(const char *module, const char *symname,
sym->name = symname;
sym->offset = addr;
if (sym->name && sym->offset == 0x0)
bcc_elf_foreach_sym(sym->module, _find_sym, sym);
if (sym->name && sym->offset == 0x0) {
if (bcc_find_symbol_addr(sym) < 0)
return -1;
}
if (sym->offset == 0x0)
return -1;
......
......@@ -20,6 +20,8 @@
extern "C" {
#endif
#include <stdint.h>
struct bcc_symbol {
const char *name;
const char *module;
......@@ -31,6 +33,7 @@ int bcc_symcache_resolve(void *symcache, uint64_t addr, struct bcc_symbol *sym);
int bcc_symcache_resolve_name(void *resolver, const char *name, uint64_t *addr);
void bcc_symcache_refresh(void *resolver);
int bcc_find_symbol_addr(struct bcc_symbol *sym);
int bcc_resolve_symname(const char *module, const char *symname,
const uint64_t addr, struct bcc_symbol *sym);
#ifdef __cplusplus
......
/*
* Copyright (c) 2016 GitHub, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <algorithm>
#include <string>
#include <unordered_map>
#include <vector>
#include <sys/types.h>
class ProcStat {
std::string procfs_;
ino_t inode_;
ino_t getinode_();
public:
ProcStat(int pid);
bool is_stale() { return inode_ != getinode_(); }
void reset() { inode_ = getinode_(); }
};
class SymbolCache {
public:
virtual void refresh() = 0;
virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym) = 0;
virtual bool resolve_name(const char *module, const char *name,
uint64_t *addr) = 0;
};
class KSyms : SymbolCache {
struct Symbol {
Symbol(const char *name, uint64_t addr) : name(name), addr(addr) {}
std::string name;
uint64_t addr;
bool operator<(const Symbol &rhs) const { return addr < rhs.addr; }
};
std::vector<Symbol> syms_;
std::unordered_map<std::string, uint64_t> symnames_;
static void _add_symbol(const char *, uint64_t, void *);
public:
virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym);
virtual bool resolve_name(const char *unused, const char *name,
uint64_t *addr);
virtual void refresh();
};
class ProcSyms : SymbolCache {
struct Symbol {
Symbol(const char *name, uint64_t start, uint64_t size, int flags = 0)
: name(name), start(start), size(size), flags(flags) {}
std::string name;
uint64_t start;
uint64_t size;
int flags;
};
struct Module {
Module(const char *name, uint64_t start, uint64_t end)
: name_(name), start_(start), end_(end) {}
std::string name_;
uint64_t start_;
uint64_t end_;
std::vector<Symbol> syms_;
void load_sym_table();
bool find_addr(uint64_t addr, struct bcc_symbol *sym);
bool find_name(const char *symname, uint64_t *addr);
bool is_so() const;
static int _add_symbol(const char *symname, uint64_t start, uint64_t end,
int flags, void *p);
};
int pid_;
std::vector<Module> modules_;
ProcStat procstat_;
static int _add_module(const char *, uint64_t, uint64_t, void *);
public:
ProcSyms(int pid);
virtual void refresh();
virtual bool resolve_addr(uint64_t addr, struct bcc_symbol *sym);
virtual bool resolve_name(const char *module, const char *name,
uint64_t *addr);
};
/*
* Copyright (c) 2016 GitHub, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <sstream>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include "bcc_elf.h"
#include "bcc_proc.h"
#include "usdt.h"
#include "vendor/tinyformat.hpp"
namespace USDT {
Probe::Location::Location(uint64_t addr, const char *arg_fmt) : address_(addr) {
ArgumentParser_x64 parser(arg_fmt);
while (!parser.done()) {
Argument *arg = new Argument();
if (!parser.parse(arg)) {
delete arg; // TODO: report error
continue;
}
arguments_.push_back(arg);
}
}
Probe::Probe(const char *bin_path, const char *provider, const char *name,
uint64_t semaphore)
: bin_path_(bin_path),
provider_(provider),
name_(name),
semaphore_(semaphore) {}
bool Probe::in_shared_object() {
if (!in_shared_object_)
in_shared_object_ = (bcc_elf_is_shared_obj(bin_path_.c_str()) == 1);
return in_shared_object_.value();
}
bool Probe::lookup_semaphore_addr(uint64_t *address, int pid) {
auto it = semaphores_.find(pid);
if (it != semaphores_.end()) {
*address = it->second;
return true;
}
if (in_shared_object()) {
uint64_t load_address = 0x0; // TODO
*address = load_address + semaphore_;
} else {
*address = semaphore_;
}
semaphores_[pid] = *address;
return true;
}
bool Probe::add_to_semaphore(int pid, int16_t val) {
uint64_t address;
if (!lookup_semaphore_addr(&address, pid))
return false;
std::string procmem = tfm::format("/proc/%d/mem", pid);
int memfd = ::open(procmem.c_str(), O_RDWR);
if (memfd < 0)
return false;
int16_t original; // TODO: should this be unsigned?
if (::lseek(memfd, static_cast<off_t>(address), SEEK_SET) < 0 ||
::read(memfd, &original, 2) != 2) {
::close(memfd);
return false;
}
original = original + val;
if (::lseek(memfd, static_cast<off_t>(address), SEEK_SET) < 0 ||
::write(memfd, &original, 2) != 2) {
::close(memfd);
return false;
}
::close(memfd);
return true;
}
bool Probe::enable(int pid) {
if (!add_to_semaphore(pid, +1))
return false;
// TODO: what happens if we enable this twice?
enabled_semaphores_.emplace(pid, std::move(ProcStat(pid)));
return true;
}
bool Probe::disable(int pid) {
auto it = enabled_semaphores_.find(pid);
if (it == enabled_semaphores_.end())
return false;
bool result = true;
if (!it->second.is_stale())
result = add_to_semaphore(pid, -1);
enabled_semaphores_.erase(it);
return result;
}
bool Probe::usdt_thunks(std::ostream &stream, const std::string &prefix) {
assert(!locations_.empty());
for (size_t i = 0; i < locations_.size(); ++i) {
tfm::format(
stream,
"int %s_thunk_%d(struct pt_regs *ctx) { return %s(ctx, %d); }\n",
prefix, i, prefix, i);
}
return true;
}
bool Probe::usdt_cases(std::ostream &stream, const optional<int> &pid) {
assert(!locations_.empty());
const size_t arg_count = locations_[0].arguments_.size();
for (size_t arg_n = 0; arg_n < arg_count; ++arg_n) {
Argument *largest = nullptr;
for (Location &location : locations_) {
Argument *candidate = location.arguments_[arg_n];
if (!largest ||
std::abs(candidate->arg_size()) > std::abs(largest->arg_size()))
largest = candidate;
}
tfm::format(stream, "%s arg%d = 0;\n", largest->ctype(), arg_n + 1);
}
for (size_t loc_n = 0; loc_n < locations_.size(); ++loc_n) {
Location &location = locations_[loc_n];
tfm::format(stream, "if (__loc_id == %d) {\n", loc_n);
for (size_t arg_n = 0; arg_n < location.arguments_.size(); ++arg_n) {
Argument *arg = location.arguments_[arg_n];
if (!arg->assign_to_local(stream, tfm::format("arg%d", arg_n + 1),
bin_path_, pid))
return false;
}
stream << "}\n";
}
return true;
}
void Probe::add_location(uint64_t addr, const char *fmt) {
locations_.emplace_back(addr, fmt);
}
void Context::_each_probe(const char *binpath, const struct bcc_elf_usdt *probe,
void *p) {
Context *ctx = static_cast<Context *>(p);
ctx->add_probe(binpath, probe);
}
int Context::_each_module(const char *modpath, uint64_t, uint64_t, void *p) {
bcc_elf_foreach_usdt(modpath, _each_probe, p);
return 0;
}
void Context::add_probe(const char *binpath, const struct bcc_elf_usdt *probe) {
Probe *found_probe = nullptr;
for (Probe *p : probes_) {
if (p->provider_ == probe->provider && p->name_ == probe->name) {
found_probe = p;
break;
}
}
if (!found_probe) {
found_probe =
new Probe(binpath, probe->provider, probe->name, probe->semaphore);
probes_.push_back(found_probe);
}
found_probe->add_location(probe->pc, probe->arg_fmt);
}
std::string Context::resolve_bin_path(const std::string &bin_path) {
std::string result;
if (char *which = bcc_procutils_which(bin_path.c_str())) {
result = which;
::free(which);
} else if (const char *which_so = bcc_procutils_which_so(bin_path.c_str())) {
result = which_so;
}
return result;
}
Probe *Context::find_probe(const std::string &probe_name) {
for (Probe *p : probes_) {
if (p->name_ == probe_name)
return p;
}
return nullptr;
}
Context::Context(const std::string &bin_path) : loaded_(false) {
std::string full_path = resolve_bin_path(bin_path);
if (!full_path.empty()) {
if (bcc_elf_foreach_usdt(full_path.c_str(), _each_probe, this) == 0)
loaded_ = true;
}
}
Context::Context(int pid) : loaded_(false) {
if (bcc_procutils_each_module(pid, _each_module, this) == 0)
loaded_ = true;
}
}
/*
* Copyright (c) 2016 GitHub, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
#include "syms.h"
#include "vendor/optional.hpp"
namespace USDT {
using std::experimental::optional;
using std::experimental::nullopt;
class ArgumentParser;
class Argument {
private:
optional<int> arg_size_;
optional<int> constant_;
optional<int> deref_offset_;
optional<std::string> deref_ident_;
optional<std::string> register_name_;
bool get_global_address(uint64_t *address, const std::string &binpath,
const optional<int> &pid) const;
public:
Argument();
~Argument();
bool assign_to_local(std::ostream &stream, const std::string &local_name,
const std::string &binpath,
const optional<int> &pid = nullopt) const;
int arg_size() const { return arg_size_.value_or(sizeof(void *)); }
std::string ctype() const;
const optional<std::string> &deref_ident() const { return deref_ident_; }
const optional<std::string> &register_name() const { return register_name_; }
const optional<int> constant() const { return constant_; }
const optional<int> deref_offset() const { return deref_offset_; }
friend class ArgumentParser;
};
class ArgumentParser {
const char *arg_;
ssize_t cur_pos_;
protected:
virtual bool normalize_register(std::string *reg, int *reg_size) = 0;
ssize_t parse_number(ssize_t pos, optional<int> *number);
ssize_t parse_identifier(ssize_t pos, optional<std::string> *ident);
ssize_t parse_register(ssize_t pos, Argument *dest);
ssize_t parse_expr(ssize_t pos, Argument *dest);
ssize_t parse_1(ssize_t pos, Argument *dest);
void print_error(ssize_t pos);
public:
bool parse(Argument *dest);
bool done() { return cur_pos_ < 0 || arg_[cur_pos_] == '\0'; }
ArgumentParser(const char *arg) : arg_(arg), cur_pos_(0) {}
};
class ArgumentParser_x64 : public ArgumentParser {
enum Register {
REG_A,
REG_B,
REG_C,
REG_D,
REG_SI,
REG_DI,
REG_BP,
REG_SP,
REG_8,
REG_9,
REG_10,
REG_11,
REG_12,
REG_13,
REG_14,
REG_15,
REG_RIP,
};
struct RegInfo {
Register reg;
int size;
};
static const std::unordered_map<std::string, RegInfo> registers_;
bool normalize_register(std::string *reg, int *reg_size);
void reg_to_name(std::string *norm, Register reg);
public:
ArgumentParser_x64(const char *arg) : ArgumentParser(arg) {}
};
class Probe {
std::string bin_path_;
std::string provider_;
std::string name_;
uint64_t semaphore_;
struct Location {
uint64_t address_;
std::vector<Argument *> arguments_;
Location(uint64_t addr, const char *arg_fmt);
};
std::vector<Location> locations_;
std::unordered_map<int, uint64_t> semaphores_;
std::unordered_map<int, ProcStat> enabled_semaphores_;
optional<bool> in_shared_object_;
bool add_to_semaphore(int pid, int16_t val);
bool lookup_semaphore_addr(uint64_t *address, int pid);
void add_location(uint64_t addr, const char *fmt);
public:
Probe(const char *bin_path, const char *provider, const char *name,
uint64_t semaphore);
size_t num_locations() const { return locations_.size(); }
size_t num_arguments() const { return locations_.front().arguments_.size(); }
bool usdt_thunks(std::ostream &stream, const std::string &prefix);
bool usdt_cases(std::ostream &stream, const optional<int> &pid = nullopt);
bool need_enable() const { return semaphore_ != 0x0; }
bool enable(int pid);
bool disable(int pid);
bool in_shared_object();
const std::string &name() { return name_; }
const std::string &bin_path() { return bin_path_; }
const std::string &provider() { return provider_; }
friend class Context;
};
class Context {
std::vector<Probe *> probes_;
bool loaded_;
static void _each_probe(const char *binpath, const struct bcc_elf_usdt *probe,
void *p);
static int _each_module(const char *modpath, uint64_t, uint64_t, void *p);
void add_probe(const char *binpath, const struct bcc_elf_usdt *probe);
std::string resolve_bin_path(const std::string &bin_path);
public:
Context(const std::string &bin_path);
Context(int pid);
bool loaded() const { return loaded_; }
size_t num_probes() const { return probes_.size(); }
Probe *find_probe(const std::string &probe_name);
};
}
/*
* Copyright (c) 2016 GitHub, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <unordered_map>
#include "syms.h"
#include "usdt.h"
#include "vendor/tinyformat.hpp"
#include "bcc_elf.h"
#include "bcc_syms.h"
namespace USDT {
Argument::Argument() {}
Argument::~Argument() {}
std::string Argument::ctype() const {
const int s = arg_size() * 8;
return (s < 0) ? tfm::format("int%d_t", -s) : tfm::format("uint%d_t", s);
}
bool Argument::get_global_address(uint64_t *address, const std::string &binpath,
const optional<int> &pid) const {
if (pid) {
return ProcSyms(*pid)
.resolve_name(binpath.c_str(), deref_ident_->c_str(), address);
}
if (bcc_elf_is_shared_obj(binpath.c_str()) == 0) {
struct bcc_symbol sym = {deref_ident_->c_str(), binpath.c_str(), 0x0};
if (!bcc_find_symbol_addr(&sym) && sym.offset) {
*address = sym.offset;
return true;
}
}
return false;
}
bool Argument::assign_to_local(std::ostream &stream,
const std::string &local_name,
const std::string &binpath,
const optional<int> &pid) const {
if (constant_) {
tfm::format(stream, "%s = %d;\n", local_name, *constant_);
return true;
}
if (!deref_offset_) {
tfm::format(stream, "%s = (%s)ctx->%s;\n", local_name, ctype(),
*register_name_);
return true;
}
if (deref_offset_ && !deref_ident_) {
tfm::format(stream,
"{\n"
" u64 __temp = ctx->%s + (%d);\n"
" bpf_probe_read(&%s, sizeof(%s), (void *)__temp);\n"
"}\n",
*register_name_, *deref_offset_, local_name, local_name);
return true;
}
if (deref_offset_ && deref_ident_) {
uint64_t global_address;
if (!get_global_address(&global_address, binpath, pid))
return false;
tfm::format(stream,
"{\n"
" u64 __temp = 0x%xull + %d;\n"
" bpf_probe_read(&%s, sizeof(%s), (void *)__temp);\n"
"}\n",
global_address, *deref_offset_, local_name, local_name);
return true;
}
return false;
}
ssize_t ArgumentParser::parse_number(ssize_t pos, optional<int> *result) {
char *endp;
int number = strtol(arg_ + pos, &endp, 0);
if (endp > arg_ + pos)
*result = number;
return endp - arg_;
}
ssize_t ArgumentParser::parse_identifier(ssize_t pos,
optional<std::string> *result) {
if (isalpha(arg_[pos]) || arg_[pos] == '_') {
ssize_t start = pos++;
while (isalnum(arg_[pos]) || arg_[pos] == '_') pos++;
if (pos - start)
result->emplace(arg_ + start, pos - start);
}
return pos;
}
ssize_t ArgumentParser::parse_register(ssize_t pos, Argument *dest) {
ssize_t start = ++pos;
if (arg_[start - 1] != '%')
return -start;
while (isalnum(arg_[pos])) pos++;
std::string regname(arg_ + start, pos - start);
int regsize = 0;
if (!normalize_register(&regname, &regsize))
return -start;
dest->register_name_ = regname;
if (!dest->arg_size_)
dest->arg_size_ = regsize;
return pos;
}
ssize_t ArgumentParser::parse_expr(ssize_t pos, Argument *dest) {
if (arg_[pos] == '$')
return parse_number(pos + 1, &dest->constant_);
if (arg_[pos] == '%')
return parse_register(pos, dest);
if (isdigit(arg_[pos]) || arg_[pos] == '-') {
pos = parse_number(pos, &dest->deref_offset_);
if (arg_[pos] == '+') {
pos = parse_identifier(pos + 1, &dest->deref_ident_);
if (!dest->deref_ident_)
return -pos;
}
} else {
dest->deref_offset_ = 0;
pos = parse_identifier(pos, &dest->deref_ident_);
}
if (arg_[pos] != '(')
return -pos;
pos = parse_register(pos + 1, dest);
if (pos < 0)
return pos;
return (arg_[pos] == ')') ? pos + 1 : -pos;
}
ssize_t ArgumentParser::parse_1(ssize_t pos, Argument *dest) {
if (isdigit(arg_[pos]) || arg_[pos] == '-') {
optional<int> asize;
ssize_t m = parse_number(pos, &asize);
if (arg_[m] == '@' && asize) {
dest->arg_size_ = asize;
return parse_expr(m + 1, dest);
}
}
return parse_expr(pos, dest);
}
void ArgumentParser::print_error(ssize_t pos) {
fprintf(stderr, "Parse error:\n %s\n", arg_);
for (ssize_t i = 0; i < pos + 4; ++i) fputc('-', stderr);
fputc('^', stderr);
fputc('\n', stderr);
}
bool ArgumentParser::parse(Argument *dest) {
if (done())
return false;
ssize_t res = parse_1(cur_pos_, dest);
if (res < 0) {
print_error(-res);
return false;
}
if (!isspace(arg_[res]) && arg_[res] != '\0') {
print_error(res);
return false;
}
while (isspace(arg_[res])) res++;
cur_pos_ = res;
return true;
}
const std::unordered_map<std::string, ArgumentParser_x64::RegInfo>
ArgumentParser_x64::registers_ = {
{"rax", {REG_A, 8}}, {"eax", {REG_A, 4}},
{"ax", {REG_A, 2}}, {"al", {REG_A, 1}},
{"rbx", {REG_B, 8}}, {"ebx", {REG_B, 4}},
{"bx", {REG_B, 2}}, {"bl", {REG_B, 1}},
{"rcx", {REG_C, 8}}, {"ecx", {REG_C, 4}},
{"cx", {REG_C, 2}}, {"cl", {REG_C, 1}},
{"rdx", {REG_D, 8}}, {"edx", {REG_D, 4}},
{"dx", {REG_D, 2}}, {"dl", {REG_D, 1}},
{"rsi", {REG_SI, 8}}, {"esi", {REG_SI, 4}},
{"si", {REG_SI, 2}}, {"sil", {REG_SI, 1}},
{"rdi", {REG_DI, 8}}, {"edi", {REG_DI, 4}},
{"di", {REG_DI, 2}}, {"dil", {REG_DI, 1}},
{"rbp", {REG_BP, 8}}, {"ebp", {REG_BP, 4}},
{"bp", {REG_BP, 2}}, {"bpl", {REG_BP, 1}},
{"rsp", {REG_SP, 8}}, {"esp", {REG_SP, 4}},
{"sp", {REG_SP, 2}}, {"spl", {REG_SP, 1}},
{"r8", {REG_8, 8}}, {"r8d", {REG_8, 4}},
{"r8w", {REG_8, 2}}, {"r8b", {REG_8, 1}},
{"r9", {REG_9, 8}}, {"r9d", {REG_9, 4}},
{"r9w", {REG_9, 2}}, {"r9b", {REG_9, 1}},
{"r10", {REG_10, 8}}, {"r10d", {REG_10, 4}},
{"r10w", {REG_10, 2}}, {"r10b", {REG_10, 1}},
{"r11", {REG_11, 8}}, {"r11d", {REG_11, 4}},
{"r11w", {REG_11, 2}}, {"r11b", {REG_11, 1}},
{"r12", {REG_12, 8}}, {"r12d", {REG_12, 4}},
{"r12w", {REG_12, 2}}, {"r12b", {REG_12, 1}},
{"r13", {REG_13, 8}}, {"r13d", {REG_13, 4}},
{"r13w", {REG_13, 2}}, {"r13b", {REG_13, 1}},
{"r14", {REG_14, 8}}, {"r14d", {REG_14, 4}},
{"r14w", {REG_14, 2}}, {"r14b", {REG_14, 1}},
{"r15", {REG_15, 8}}, {"r15d", {REG_15, 4}},
{"r15w", {REG_15, 2}}, {"r15b", {REG_15, 1}},
{"rip", {REG_RIP, 8}},
};
void ArgumentParser_x64::reg_to_name(std::string *norm, Register reg) {
switch (reg) {
case REG_A:
*norm = "ax";
break;
case REG_B:
*norm = "bx";
break;
case REG_C:
*norm = "cx";
break;
case REG_D:
*norm = "dx";
break;
case REG_SI:
*norm = "si";
break;
case REG_DI:
*norm = "di";
break;
case REG_BP:
*norm = "bp";
break;
case REG_SP:
*norm = "sp";
break;
case REG_8:
*norm = "r8";
break;
case REG_9:
*norm = "r9";
break;
case REG_10:
*norm = "r10";
break;
case REG_11:
*norm = "r11";
break;
case REG_12:
*norm = "r12";
break;
case REG_13:
*norm = "r13";
break;
case REG_14:
*norm = "r14";
break;
case REG_15:
*norm = "r15";
break;
case REG_RIP:
*norm = "ip";
break;
}
}
bool ArgumentParser_x64::normalize_register(std::string *reg, int *reg_size) {
auto it = registers_.find(*reg);
if (it == registers_.end())
return false;
*reg_size = it->second.size;
reg_to_name(reg, it->second.reg);
return true;
}
}
// Copyright (C) 2011 - 2012 Andrzej Krzemienski.
//
// Use, modification, and distribution is subject to the Boost Software
// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// The idea and interface is based on Boost.Optional library
// authored by Fernando Luis Cacciola Carballal
# ifndef ___OPTIONAL_HPP___
# define ___OPTIONAL_HPP___
# include <utility>
# include <type_traits>
# include <initializer_list>
# include <cassert>
# include <functional>
# include <string>
# include <stdexcept>
# define TR2_OPTIONAL_REQUIRES(...) typename enable_if<__VA_ARGS__::value, bool>::type = false
# if defined __GNUC__ // NOTE: GNUC is also defined for Clang
# if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 8)
# define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___
# elif (__GNUC__ > 4)
# define TR2_OPTIONAL_GCC_4_8_AND_HIGHER___
# endif
#
# if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)
# define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___
# elif (__GNUC__ > 4)
# define TR2_OPTIONAL_GCC_4_7_AND_HIGHER___
# endif
#
# if (__GNUC__ == 4) && (__GNUC_MINOR__ == 8) && (__GNUC_PATCHLEVEL__ >= 1)
# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___
# elif (__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)
# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___
# elif (__GNUC__ > 4)
# define TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___
# endif
# endif
#
# if defined __clang_major__
# if (__clang_major__ == 3 && __clang_minor__ >= 5)
# define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_
# elif (__clang_major__ > 3)
# define TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_
# endif
# if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_
# define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_
# elif (__clang_major__ == 3 && __clang_minor__ == 4 && __clang_patchlevel__ >= 2)
# define TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_
# endif
# endif
#
# if defined _MSC_VER
# if (_MSC_VER >= 1900)
# define TR2_OPTIONAL_MSVC_2015_AND_HIGHER___
# endif
# endif
# if defined __clang__
# if (__clang_major__ > 2) || (__clang_major__ == 2) && (__clang_minor__ >= 9)
# define OPTIONAL_HAS_THIS_RVALUE_REFS 1
# else
# define OPTIONAL_HAS_THIS_RVALUE_REFS 0
# endif
# elif defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___
# define OPTIONAL_HAS_THIS_RVALUE_REFS 1
# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___
# define OPTIONAL_HAS_THIS_RVALUE_REFS 1
# else
# define OPTIONAL_HAS_THIS_RVALUE_REFS 0
# endif
# if defined TR2_OPTIONAL_GCC_4_8_1_AND_HIGHER___
# define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 1
# define OPTIONAL_CONSTEXPR_INIT_LIST constexpr
# else
# define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 0
# define OPTIONAL_CONSTEXPR_INIT_LIST
# endif
# if defined TR2_OPTIONAL_CLANG_3_5_AND_HIGHTER_ && (defined __cplusplus) && (__cplusplus != 201103L)
# define OPTIONAL_HAS_MOVE_ACCESSORS 1
# else
# define OPTIONAL_HAS_MOVE_ACCESSORS 0
# endif
# // In C++11 constexpr implies const, so we need to make non-const members also non-constexpr
# if (defined __cplusplus) && (__cplusplus == 201103L)
# define OPTIONAL_MUTABLE_CONSTEXPR
# else
# define OPTIONAL_MUTABLE_CONSTEXPR constexpr
# endif
namespace std{
namespace experimental{
// BEGIN workaround for missing is_trivially_destructible
# if defined TR2_OPTIONAL_GCC_4_8_AND_HIGHER___
// leave it: it is already there
# elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_
// leave it: it is already there
# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___
// leave it: it is already there
# elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS
// leave it: the user doesn't want it
# else
template <typename T>
using is_trivially_destructible = std::has_trivial_destructor<T>;
# endif
// END workaround for missing is_trivially_destructible
# if (defined TR2_OPTIONAL_GCC_4_7_AND_HIGHER___)
// leave it; our metafunctions are already defined.
# elif defined TR2_OPTIONAL_CLANG_3_4_2_AND_HIGHER_
// leave it; our metafunctions are already defined.
# elif defined TR2_OPTIONAL_MSVC_2015_AND_HIGHER___
// leave it: it is already there
# elif defined TR2_OPTIONAL_DISABLE_EMULATION_OF_TYPE_TRAITS
// leave it: the user doesn't want it
# else
// workaround for missing traits in GCC and CLANG
template <class T>
struct is_nothrow_move_constructible
{
constexpr static bool value = std::is_nothrow_constructible<T, T&&>::value;
};
template <class T, class U>
struct is_assignable
{
template <class X, class Y>
constexpr static bool has_assign(...) { return false; }
template <class X, class Y, size_t S = sizeof((std::declval<X>() = std::declval<Y>(), true)) >
// the comma operator is necessary for the cases where operator= returns void
constexpr static bool has_assign(bool) { return true; }
constexpr static bool value = has_assign<T, U>(true);
};
template <class T>
struct is_nothrow_move_assignable
{
template <class X, bool has_any_move_assign>
struct has_nothrow_move_assign {
constexpr static bool value = false;
};
template <class X>
struct has_nothrow_move_assign<X, true> {
constexpr static bool value = noexcept( std::declval<X&>() = std::declval<X&&>() );
};
constexpr static bool value = has_nothrow_move_assign<T, is_assignable<T&, T&&>::value>::value;
};
// end workaround
# endif
// 20.5.4, optional for object types
template <class T> class optional;
// 20.5.5, optional for lvalue reference types
template <class T> class optional<T&>;
// workaround: std utility functions aren't constexpr yet
template <class T> inline constexpr T&& constexpr_forward(typename std::remove_reference<T>::type& t) noexcept
{
return static_cast<T&&>(t);
}
template <class T> inline constexpr T&& constexpr_forward(typename std::remove_reference<T>::type&& t) noexcept
{
static_assert(!std::is_lvalue_reference<T>::value, "!!");
return static_cast<T&&>(t);
}
template <class T> inline constexpr typename std::remove_reference<T>::type&& constexpr_move(T&& t) noexcept
{
return static_cast<typename std::remove_reference<T>::type&&>(t);
}
#if defined NDEBUG
# define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR)
#else
# define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : ([]{assert(!#CHECK);}(), (EXPR)))
#endif
namespace detail_
{
// static_addressof: a constexpr version of addressof
template <typename T>
struct has_overloaded_addressof
{
template <class X>
constexpr static bool has_overload(...) { return false; }
template <class X, size_t S = sizeof(std::declval<X&>().operator&()) >
constexpr static bool has_overload(bool) { return true; }
constexpr static bool value = has_overload<T>(true);
};
template <typename T, TR2_OPTIONAL_REQUIRES(!has_overloaded_addressof<T>)>
constexpr T* static_addressof(T& ref)
{
return &ref;
}
template <typename T, TR2_OPTIONAL_REQUIRES(has_overloaded_addressof<T>)>
T* static_addressof(T& ref)
{
return std::addressof(ref);
}
// the call to convert<A>(b) has return type A and converts b to type A iff b decltype(b) is implicitly convertible to A
template <class U>
U convert(U v) { return v; }
} // namespace detail
constexpr struct trivial_init_t{} trivial_init{};
// 20.5.6, In-place construction
constexpr struct in_place_t{} in_place{};
// 20.5.7, Disengaged state indicator
struct nullopt_t
{
struct init{};
constexpr explicit nullopt_t(init){}
};
constexpr nullopt_t nullopt{nullopt_t::init()};
// 20.5.8, class bad_optional_access
class bad_optional_access : public logic_error {
public:
explicit bad_optional_access(const string& what_arg) : logic_error{what_arg} {}
explicit bad_optional_access(const char* what_arg) : logic_error{what_arg} {}
};
template <class T>
union storage_t
{
unsigned char dummy_;
T value_;
constexpr storage_t( trivial_init_t ) noexcept : dummy_() {};
template <class... Args>
constexpr storage_t( Args&&... args ) : value_(constexpr_forward<Args>(args)...) {}
~storage_t(){}
};
template <class T>
union constexpr_storage_t
{
unsigned char dummy_;
T value_;
constexpr constexpr_storage_t( trivial_init_t ) noexcept : dummy_() {};
template <class... Args>
constexpr constexpr_storage_t( Args&&... args ) : value_(constexpr_forward<Args>(args)...) {}
~constexpr_storage_t() = default;
};
template <class T>
struct optional_base
{
bool init_;
storage_t<T> storage_;
constexpr optional_base() noexcept : init_(false), storage_(trivial_init) {};
explicit constexpr optional_base(const T& v) : init_(true), storage_(v) {}
explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {}
template <class... Args> explicit optional_base(in_place_t, Args&&... args)
: init_(true), storage_(constexpr_forward<Args>(args)...) {}
template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
explicit optional_base(in_place_t, std::initializer_list<U> il, Args&&... args)
: init_(true), storage_(il, std::forward<Args>(args)...) {}
~optional_base() { if (init_) storage_.value_.T::~T(); }
};
template <class T>
struct constexpr_optional_base
{
bool init_;
constexpr_storage_t<T> storage_;
constexpr constexpr_optional_base() noexcept : init_(false), storage_(trivial_init) {};
explicit constexpr constexpr_optional_base(const T& v) : init_(true), storage_(v) {}
explicit constexpr constexpr_optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {}
template <class... Args> explicit constexpr constexpr_optional_base(in_place_t, Args&&... args)
: init_(true), storage_(constexpr_forward<Args>(args)...) {}
template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
OPTIONAL_CONSTEXPR_INIT_LIST explicit constexpr_optional_base(in_place_t, std::initializer_list<U> il, Args&&... args)
: init_(true), storage_(il, std::forward<Args>(args)...) {}
~constexpr_optional_base() = default;
};
template <class T>
using OptionalBase = typename std::conditional<
is_trivially_destructible<T>::value,
constexpr_optional_base<T>,
optional_base<T>
>::type;
template <class T>
class optional : private OptionalBase<T>
{
static_assert( !std::is_same<typename std::decay<T>::type, nullopt_t>::value, "bad T" );
static_assert( !std::is_same<typename std::decay<T>::type, in_place_t>::value, "bad T" );
constexpr bool initialized() const noexcept { return OptionalBase<T>::init_; }
T* dataptr() { return std::addressof(OptionalBase<T>::storage_.value_); }
constexpr const T* dataptr() const { return detail_::static_addressof(OptionalBase<T>::storage_.value_); }
# if OPTIONAL_HAS_THIS_RVALUE_REFS == 1
constexpr const T& contained_val() const& { return OptionalBase<T>::storage_.value_; }
# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
OPTIONAL_MUTABLE_CONSTEXPR T&& contained_val() && { return std::move(OptionalBase<T>::storage_.value_); }
OPTIONAL_MUTABLE_CONSTEXPR T& contained_val() & { return OptionalBase<T>::storage_.value_; }
# else
T& contained_val() & { return OptionalBase<T>::storage_.value_; }
T&& contained_val() && { return std::move(OptionalBase<T>::storage_.value_); }
# endif
# else
constexpr const T& contained_val() const { return OptionalBase<T>::storage_.value_; }
T& contained_val() { return OptionalBase<T>::storage_.value_; }
# endif
void clear() noexcept {
if (initialized()) dataptr()->T::~T();
OptionalBase<T>::init_ = false;
}
template <class... Args>
void initialize(Args&&... args) noexcept(noexcept(T(std::forward<Args>(args)...)))
{
assert(!OptionalBase<T>::init_);
::new (static_cast<void*>(dataptr())) T(std::forward<Args>(args)...);
OptionalBase<T>::init_ = true;
}
template <class U, class... Args>
void initialize(std::initializer_list<U> il, Args&&... args) noexcept(noexcept(T(il, std::forward<Args>(args)...)))
{
assert(!OptionalBase<T>::init_);
::new (static_cast<void*>(dataptr())) T(il, std::forward<Args>(args)...);
OptionalBase<T>::init_ = true;
}
public:
typedef T value_type;
// 20.5.5.1, constructors
constexpr optional() noexcept : OptionalBase<T>() {};
constexpr optional(nullopt_t) noexcept : OptionalBase<T>() {};
optional(const optional& rhs)
: OptionalBase<T>()
{
if (rhs.initialized()) {
::new (static_cast<void*>(dataptr())) T(*rhs);
OptionalBase<T>::init_ = true;
}
}
optional(optional&& rhs) noexcept(is_nothrow_move_constructible<T>::value)
: OptionalBase<T>()
{
if (rhs.initialized()) {
::new (static_cast<void*>(dataptr())) T(std::move(*rhs));
OptionalBase<T>::init_ = true;
}
}
constexpr optional(const T& v) : OptionalBase<T>(v) {}
constexpr optional(T&& v) : OptionalBase<T>(constexpr_move(v)) {}
template <class... Args>
explicit constexpr optional(in_place_t, Args&&... args)
: OptionalBase<T>(in_place_t{}, constexpr_forward<Args>(args)...) {}
template <class U, class... Args, TR2_OPTIONAL_REQUIRES(is_constructible<T, std::initializer_list<U>>)>
OPTIONAL_CONSTEXPR_INIT_LIST explicit optional(in_place_t, std::initializer_list<U> il, Args&&... args)
: OptionalBase<T>(in_place_t{}, il, constexpr_forward<Args>(args)...) {}
// 20.5.4.2, Destructor
~optional() = default;
// 20.5.4.3, assignment
optional& operator=(nullopt_t) noexcept
{
clear();
return *this;
}
optional& operator=(const optional& rhs)
{
if (initialized() == true && rhs.initialized() == false) clear();
else if (initialized() == false && rhs.initialized() == true) initialize(*rhs);
else if (initialized() == true && rhs.initialized() == true) contained_val() = *rhs;
return *this;
}
optional& operator=(optional&& rhs)
noexcept(is_nothrow_move_assignable<T>::value && is_nothrow_move_constructible<T>::value)
{
if (initialized() == true && rhs.initialized() == false) clear();
else if (initialized() == false && rhs.initialized() == true) initialize(std::move(*rhs));
else if (initialized() == true && rhs.initialized() == true) contained_val() = std::move(*rhs);
return *this;
}
template <class U>
auto operator=(U&& v)
-> typename enable_if
<
is_same<typename decay<U>::type, T>::value,
optional&
>::type
{
if (initialized()) { contained_val() = std::forward<U>(v); }
else { initialize(std::forward<U>(v)); }
return *this;
}
template <class... Args>
void emplace(Args&&... args)
{
clear();
initialize(std::forward<Args>(args)...);
}
template <class U, class... Args>
void emplace(initializer_list<U> il, Args&&... args)
{
clear();
initialize<U, Args...>(il, std::forward<Args>(args)...);
}
// 20.5.4.4, Swap
void swap(optional<T>& rhs) noexcept(is_nothrow_move_constructible<T>::value && noexcept(swap(declval<T&>(), declval<T&>())))
{
if (initialized() == true && rhs.initialized() == false) { rhs.initialize(std::move(**this)); clear(); }
else if (initialized() == false && rhs.initialized() == true) { initialize(std::move(*rhs)); rhs.clear(); }
else if (initialized() == true && rhs.initialized() == true) { using std::swap; swap(**this, *rhs); }
}
// 20.5.4.5, Observers
explicit constexpr operator bool() const noexcept { return initialized(); }
constexpr T const* operator ->() const {
return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), dataptr());
}
# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
OPTIONAL_MUTABLE_CONSTEXPR T* operator ->() {
assert (initialized());
return dataptr();
}
constexpr T const& operator *() const& {
return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val());
}
OPTIONAL_MUTABLE_CONSTEXPR T& operator *() & {
assert (initialized());
return contained_val();
}
OPTIONAL_MUTABLE_CONSTEXPR T&& operator *() && {
assert (initialized());
return constexpr_move(contained_val());
}
constexpr T const& value() const& {
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
}
OPTIONAL_MUTABLE_CONSTEXPR T& value() & {
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
}
OPTIONAL_MUTABLE_CONSTEXPR T&& value() && {
if (!initialized()) throw bad_optional_access("bad optional access");
return std::move(contained_val());
}
# else
T* operator ->() {
assert (initialized());
return dataptr();
}
constexpr T const& operator *() const {
return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val());
}
T& operator *() {
assert (initialized());
return contained_val();
}
constexpr T const& value() const {
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
}
T& value() {
return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val());
}
# endif
# if OPTIONAL_HAS_THIS_RVALUE_REFS == 1
template <class V>
constexpr T value_or(V&& v) const&
{
return *this ? **this : detail_::convert<T>(constexpr_forward<V>(v));
}
# if OPTIONAL_HAS_MOVE_ACCESSORS == 1
template <class V>
OPTIONAL_MUTABLE_CONSTEXPR T value_or(V&& v) &&
{
return *this ? constexpr_move(const_cast<optional<T>&>(*this).contained_val()) : detail_::convert<T>(constexpr_forward<V>(v));
}
# else
template <class V>
T value_or(V&& v) &&
{
return *this ? constexpr_move(const_cast<optional<T>&>(*this).contained_val()) : detail_::convert<T>(constexpr_forward<V>(v));
}
# endif
# else
template <class V>
constexpr T value_or(V&& v) const
{
return *this ? **this : detail_::convert<T>(constexpr_forward<V>(v));
}
# endif
};
template <class T>
class optional<T&>
{
static_assert( !std::is_same<T, nullopt_t>::value, "bad T" );
static_assert( !std::is_same<T, in_place_t>::value, "bad T" );
T* ref;
public:
// 20.5.5.1, construction/destruction
constexpr optional() noexcept : ref(nullptr) {}
constexpr optional(nullopt_t) noexcept : ref(nullptr) {}
constexpr optional(T& v) noexcept : ref(detail_::static_addressof(v)) {}
optional(T&&) = delete;
constexpr optional(const optional& rhs) noexcept : ref(rhs.ref) {}
explicit constexpr optional(in_place_t, T& v) noexcept : ref(detail_::static_addressof(v)) {}
explicit optional(in_place_t, T&&) = delete;
~optional() = default;
// 20.5.5.2, mutation
optional& operator=(nullopt_t) noexcept {
ref = nullptr;
return *this;
}
// optional& operator=(const optional& rhs) noexcept {
// ref = rhs.ref;
// return *this;
// }
// optional& operator=(optional&& rhs) noexcept {
// ref = rhs.ref;
// return *this;
// }
template <typename U>
auto operator=(U&& rhs) noexcept
-> typename enable_if
<
is_same<typename decay<U>::type, optional<T&>>::value,
optional&
>::type
{
ref = rhs.ref;
return *this;
}
template <typename U>
auto operator=(U&& rhs) noexcept
-> typename enable_if
<
!is_same<typename decay<U>::type, optional<T&>>::value,
optional&
>::type
= delete;
void emplace(T& v) noexcept {
ref = detail_::static_addressof(v);
}
void emplace(T&&) = delete;
void swap(optional<T&>& rhs) noexcept
{
std::swap(ref, rhs.ref);
}
// 20.5.5.3, observers
constexpr T* operator->() const {
return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, ref);
}
constexpr T& operator*() const {
return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, *ref);
}
constexpr T& value() const {
return ref ? *ref : (throw bad_optional_access("bad optional access"), *ref);
}
explicit constexpr operator bool() const noexcept {
return ref != nullptr;
}
template <class V>
constexpr typename decay<T>::type value_or(V&& v) const
{
return *this ? **this : detail_::convert<typename decay<T>::type>(constexpr_forward<V>(v));
}
};
template <class T>
class optional<T&&>
{
static_assert( sizeof(T) == 0, "optional rvalue references disallowed" );
};
// 20.5.8, Relational operators
template <class T> constexpr bool operator==(const optional<T>& x, const optional<T>& y)
{
return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y;
}
template <class T> constexpr bool operator!=(const optional<T>& x, const optional<T>& y)
{
return !(x == y);
}
template <class T> constexpr bool operator<(const optional<T>& x, const optional<T>& y)
{
return (!y) ? false : (!x) ? true : *x < *y;
}
template <class T> constexpr bool operator>(const optional<T>& x, const optional<T>& y)
{
return (y < x);
}
template <class T> constexpr bool operator<=(const optional<T>& x, const optional<T>& y)
{
return !(y < x);
}
template <class T> constexpr bool operator>=(const optional<T>& x, const optional<T>& y)
{
return !(x < y);
}
// 20.5.9, Comparison with nullopt
template <class T> constexpr bool operator==(const optional<T>& x, nullopt_t) noexcept
{
return (!x);
}
template <class T> constexpr bool operator==(nullopt_t, const optional<T>& x) noexcept
{
return (!x);
}
template <class T> constexpr bool operator!=(const optional<T>& x, nullopt_t) noexcept
{
return bool(x);
}
template <class T> constexpr bool operator!=(nullopt_t, const optional<T>& x) noexcept
{
return bool(x);
}
template <class T> constexpr bool operator<(const optional<T>&, nullopt_t) noexcept
{
return false;
}
template <class T> constexpr bool operator<(nullopt_t, const optional<T>& x) noexcept
{
return bool(x);
}
template <class T> constexpr bool operator<=(const optional<T>& x, nullopt_t) noexcept
{
return (!x);
}
template <class T> constexpr bool operator<=(nullopt_t, const optional<T>&) noexcept
{
return true;
}
template <class T> constexpr bool operator>(const optional<T>& x, nullopt_t) noexcept
{
return bool(x);
}
template <class T> constexpr bool operator>(nullopt_t, const optional<T>&) noexcept
{
return false;
}
template <class T> constexpr bool operator>=(const optional<T>&, nullopt_t) noexcept
{
return true;
}
template <class T> constexpr bool operator>=(nullopt_t, const optional<T>& x) noexcept
{
return (!x);
}
// 20.5.10, Comparison with T
template <class T> constexpr bool operator==(const optional<T>& x, const T& v)
{
return bool(x) ? *x == v : false;
}
template <class T> constexpr bool operator==(const T& v, const optional<T>& x)
{
return bool(x) ? v == *x : false;
}
template <class T> constexpr bool operator!=(const optional<T>& x, const T& v)
{
return bool(x) ? *x != v : true;
}
template <class T> constexpr bool operator!=(const T& v, const optional<T>& x)
{
return bool(x) ? v != *x : true;
}
template <class T> constexpr bool operator<(const optional<T>& x, const T& v)
{
return bool(x) ? *x < v : true;
}
template <class T> constexpr bool operator>(const T& v, const optional<T>& x)
{
return bool(x) ? v > *x : true;
}
template <class T> constexpr bool operator>(const optional<T>& x, const T& v)
{
return bool(x) ? *x > v : false;
}
template <class T> constexpr bool operator<(const T& v, const optional<T>& x)
{
return bool(x) ? v < *x : false;
}
template <class T> constexpr bool operator>=(const optional<T>& x, const T& v)
{
return bool(x) ? *x >= v : false;
}
template <class T> constexpr bool operator<=(const T& v, const optional<T>& x)
{
return bool(x) ? v <= *x : false;
}
template <class T> constexpr bool operator<=(const optional<T>& x, const T& v)
{
return bool(x) ? *x <= v : true;
}
template <class T> constexpr bool operator>=(const T& v, const optional<T>& x)
{
return bool(x) ? v >= *x : true;
}
// Comparison of optional<T&> with T
template <class T> constexpr bool operator==(const optional<T&>& x, const T& v)
{
return bool(x) ? *x == v : false;
}
template <class T> constexpr bool operator==(const T& v, const optional<T&>& x)
{
return bool(x) ? v == *x : false;
}
template <class T> constexpr bool operator!=(const optional<T&>& x, const T& v)
{
return bool(x) ? *x != v : true;
}
template <class T> constexpr bool operator!=(const T& v, const optional<T&>& x)
{
return bool(x) ? v != *x : true;
}
template <class T> constexpr bool operator<(const optional<T&>& x, const T& v)
{
return bool(x) ? *x < v : true;
}
template <class T> constexpr bool operator>(const T& v, const optional<T&>& x)
{
return bool(x) ? v > *x : true;
}
template <class T> constexpr bool operator>(const optional<T&>& x, const T& v)
{
return bool(x) ? *x > v : false;
}
template <class T> constexpr bool operator<(const T& v, const optional<T&>& x)
{
return bool(x) ? v < *x : false;
}
template <class T> constexpr bool operator>=(const optional<T&>& x, const T& v)
{
return bool(x) ? *x >= v : false;
}
template <class T> constexpr bool operator<=(const T& v, const optional<T&>& x)
{
return bool(x) ? v <= *x : false;
}
template <class T> constexpr bool operator<=(const optional<T&>& x, const T& v)
{
return bool(x) ? *x <= v : true;
}
template <class T> constexpr bool operator>=(const T& v, const optional<T&>& x)
{
return bool(x) ? v >= *x : true;
}
// Comparison of optional<T const&> with T
template <class T> constexpr bool operator==(const optional<const T&>& x, const T& v)
{
return bool(x) ? *x == v : false;
}
template <class T> constexpr bool operator==(const T& v, const optional<const T&>& x)
{
return bool(x) ? v == *x : false;
}
template <class T> constexpr bool operator!=(const optional<const T&>& x, const T& v)
{
return bool(x) ? *x != v : true;
}
template <class T> constexpr bool operator!=(const T& v, const optional<const T&>& x)
{
return bool(x) ? v != *x : true;
}
template <class T> constexpr bool operator<(const optional<const T&>& x, const T& v)
{
return bool(x) ? *x < v : true;
}
template <class T> constexpr bool operator>(const T& v, const optional<const T&>& x)
{
return bool(x) ? v > *x : true;
}
template <class T> constexpr bool operator>(const optional<const T&>& x, const T& v)
{
return bool(x) ? *x > v : false;
}
template <class T> constexpr bool operator<(const T& v, const optional<const T&>& x)
{
return bool(x) ? v < *x : false;
}
template <class T> constexpr bool operator>=(const optional<const T&>& x, const T& v)
{
return bool(x) ? *x >= v : false;
}
template <class T> constexpr bool operator<=(const T& v, const optional<const T&>& x)
{
return bool(x) ? v <= *x : false;
}
template <class T> constexpr bool operator<=(const optional<const T&>& x, const T& v)
{
return bool(x) ? *x <= v : true;
}
template <class T> constexpr bool operator>=(const T& v, const optional<const T&>& x)
{
return bool(x) ? v >= *x : true;
}
// 20.5.12, Specialized algorithms
template <class T>
void swap(optional<T>& x, optional<T>& y) noexcept(noexcept(x.swap(y)))
{
x.swap(y);
}
template <class T>
constexpr optional<typename decay<T>::type> make_optional(T&& v)
{
return optional<typename decay<T>::type>(constexpr_forward<T>(v));
}
template <class X>
constexpr optional<X&> make_optional(reference_wrapper<X> v)
{
return optional<X&>(v.get());
}
} // namespace experimental
} // namespace std
namespace std
{
template <typename T>
struct hash<std::experimental::optional<T>>
{
typedef typename hash<T>::result_type result_type;
typedef std::experimental::optional<T> argument_type;
constexpr result_type operator()(argument_type const& arg) const {
return arg ? std::hash<T>{}(*arg) : result_type{};
}
};
template <typename T>
struct hash<std::experimental::optional<T&>>
{
typedef typename hash<T>::result_type result_type;
typedef std::experimental::optional<T&> argument_type;
constexpr result_type operator()(argument_type const& arg) const {
return arg ? std::hash<T>{}(*arg) : result_type{};
}
};
}
# undef TR2_OPTIONAL_REQUIRES
# undef TR2_OPTIONAL_ASSERTED_EXPRESSION
# endif //___OPTIONAL_HPP___
// tinyformat.h
// Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com]
//
// Boost Software License - Version 1.0
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//------------------------------------------------------------------------------
// Tinyformat: A minimal type safe printf replacement
//
// tinyformat.h is a type safe printf replacement library in a single C++
// header file. Design goals include:
//
// * Type safety and extensibility for user defined types.
// * C99 printf() compatibility, to the extent possible using std::ostream
// * Simplicity and minimalism. A single header file to include and distribute
// with your projects.
// * Augment rather than replace the standard stream formatting mechanism
// * C++98 support, with optional C++11 niceties
//
//
// Main interface example usage
// ----------------------------
//
// To print a date to std::cout:
//
// std::string weekday = "Wednesday";
// const char* month = "July";
// size_t day = 27;
// long hour = 14;
// int min = 44;
//
// tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min);
//
// The strange types here emphasize the type safety of the interface; it is
// possible to print a std::string using the "%s" conversion, and a
// size_t using the "%d" conversion. A similar result could be achieved
// using either of the tfm::format() functions. One prints on a user provided
// stream:
//
// tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n",
// weekday, month, day, hour, min);
//
// The other returns a std::string:
//
// std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n",
// weekday, month, day, hour, min);
// std::cout << date;
//
// These are the three primary interface functions. There is also a
// convenience function printfln() which appends a newline to the usual result
// of printf() for super simple logging.
//
//
// User defined format functions
// -----------------------------
//
// Simulating variadic templates in C++98 is pretty painful since it requires
// writing out the same function for each desired number of arguments. To make
// this bearable tinyformat comes with a set of macros which are used
// internally to generate the API, but which may also be used in user code.
//
// The three macros TINYFORMAT_ARGTYPES(n), TINYFORMAT_VARARGS(n) and
// TINYFORMAT_PASSARGS(n) will generate a list of n argument types,
// type/name pairs and argument names respectively when called with an integer
// n between 1 and 16. We can use these to define a macro which generates the
// desired user defined function with n arguments. To generate all 16 user
// defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an
// example, see the implementation of printf() at the end of the source file.
//
// Sometimes it's useful to be able to pass a list of format arguments through
// to a non-template function. The FormatList class is provided as a way to do
// this by storing the argument list in a type-opaque way. Continuing the
// example from above, we construct a FormatList using makeFormatList():
//
// FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min);
//
// The format list can now be passed into any non-template function and used
// via a call to the vformat() function:
//
// tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList);
//
//
// Additional API information
// --------------------------
//
// Error handling: Define TINYFORMAT_ERROR to customize the error handling for
// format strings which are unsupported or have the wrong number of format
// specifiers (calls assert() by default).
//
// User defined types: Uses operator<< for user defined types by default.
// Overload formatValue() for more control.
#ifndef TINYFORMAT_H_INCLUDED
#define TINYFORMAT_H_INCLUDED
namespace tinyformat {}
//------------------------------------------------------------------------------
// Config section. Customize to your liking!
// Namespace alias to encourage brevity
namespace tfm = tinyformat;
// Error handling; calls assert() by default.
// #define TINYFORMAT_ERROR(reasonString) your_error_handler(reasonString)
// Define for C++11 variadic templates which make the code shorter & more
// general. If you don't define this, C++11 support is autodetected below.
// #define TINYFORMAT_USE_VARIADIC_TEMPLATES
//------------------------------------------------------------------------------
// Implementation details.
#include <algorithm>
#include <cassert>
#include <iostream>
#include <sstream>
#ifndef TINYFORMAT_ERROR
# define TINYFORMAT_ERROR(reason) assert(0 && reason)
#endif
#if !defined(TINYFORMAT_USE_VARIADIC_TEMPLATES) && !defined(TINYFORMAT_NO_VARIADIC_TEMPLATES)
# ifdef __GXX_EXPERIMENTAL_CXX0X__
# define TINYFORMAT_USE_VARIADIC_TEMPLATES
# endif
#endif
#if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201
// std::showpos is broken on old libstdc++ as provided with OSX. See
// http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html
# define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
#endif
#ifdef __APPLE__
// Workaround OSX linker warning: xcode uses different default symbol
// visibilities for static libs vs executables (see issue #25)
# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden")))
#else
# define TINYFORMAT_HIDDEN
#endif
namespace tinyformat {
//------------------------------------------------------------------------------
namespace detail {
// Test whether type T1 is convertible to type T2
template <typename T1, typename T2>
struct is_convertible
{
private:
// two types of different size
struct fail { char dummy[2]; };
struct succeed { char dummy; };
// Try to convert a T1 to a T2 by plugging into tryConvert
static fail tryConvert(...);
static succeed tryConvert(const T2&);
static const T1& makeT1();
public:
# ifdef _MSC_VER
// Disable spurious loss of precision warnings in tryConvert(makeT1())
# pragma warning(push)
# pragma warning(disable:4244)
# pragma warning(disable:4267)
# endif
// Standard trick: the (...) version of tryConvert will be chosen from
// the overload set only if the version taking a T2 doesn't match.
// Then we compare the sizes of the return types to check which
// function matched. Very neat, in a disgusting kind of way :)
static const bool value =
sizeof(tryConvert(makeT1())) == sizeof(succeed);
# ifdef _MSC_VER
# pragma warning(pop)
# endif
};
// Detect when a type is not a wchar_t string
template<typename T> struct is_wchar { typedef int tinyformat_wchar_is_not_supported; };
template<> struct is_wchar<wchar_t*> {};
template<> struct is_wchar<const wchar_t*> {};
template<int n> struct is_wchar<const wchar_t[n]> {};
template<int n> struct is_wchar<wchar_t[n]> {};
// Format the value by casting to type fmtT. This default implementation
// should never be called.
template<typename T, typename fmtT, bool convertible = is_convertible<T, fmtT>::value>
struct formatValueAsType
{
static void invoke(std::ostream& /*out*/, const T& /*value*/) { assert(0); }
};
// Specialized version for types that can actually be converted to fmtT, as
// indicated by the "convertible" template parameter.
template<typename T, typename fmtT>
struct formatValueAsType<T,fmtT,true>
{
static void invoke(std::ostream& out, const T& value)
{ out << static_cast<fmtT>(value); }
};
#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
template<typename T, bool convertible = is_convertible<T, int>::value>
struct formatZeroIntegerWorkaround
{
static bool invoke(std::ostream& /**/, const T& /**/) { return false; }
};
template<typename T>
struct formatZeroIntegerWorkaround<T,true>
{
static bool invoke(std::ostream& out, const T& value)
{
if (static_cast<int>(value) == 0 && out.flags() & std::ios::showpos)
{
out << "+0";
return true;
}
return false;
}
};
#endif // TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
// Convert an arbitrary type to integer. The version with convertible=false
// throws an error.
template<typename T, bool convertible = is_convertible<T,int>::value>
struct convertToInt
{
static int invoke(const T& /*value*/)
{
TINYFORMAT_ERROR("tinyformat: Cannot convert from argument type to "
"integer for use as variable width or precision");
return 0;
}
};
// Specialization for convertToInt when conversion is possible
template<typename T>
struct convertToInt<T,true>
{
static int invoke(const T& value) { return static_cast<int>(value); }
};
// Format at most ntrunc characters to the given stream.
template<typename T>
inline void formatTruncated(std::ostream& out, const T& value, int ntrunc)
{
std::ostringstream tmp;
tmp << value;
std::string result = tmp.str();
out.write(result.c_str(), std::min(ntrunc, static_cast<int>(result.size())));
}
#define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type) \
inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \
{ \
std::streamsize len = 0; \
while(len < ntrunc && value[len] != 0) \
++len; \
out.write(value, len); \
}
// Overload for const char* and char*. Could overload for signed & unsigned
// char too, but these are technically unneeded for printf compatibility.
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char)
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char)
#undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR
} // namespace detail
//------------------------------------------------------------------------------
// Variable formatting functions. May be overridden for user-defined types if
// desired.
/// Format a value into a stream, delegating to operator<< by default.
///
/// Users may override this for their own types. When this function is called,
/// the stream flags will have been modified according to the format string.
/// The format specification is provided in the range [fmtBegin, fmtEnd). For
/// truncating conversions, ntrunc is set to the desired maximum number of
/// characters, for example "%.7s" calls formatValue with ntrunc = 7.
///
/// By default, formatValue() uses the usual stream insertion operator
/// operator<< to format the type T, with special cases for the %c and %p
/// conversions.
template<typename T>
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
const char* fmtEnd, int ntrunc, const T& value)
{
#ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS
// Since we don't support printing of wchar_t using "%ls", make it fail at
// compile time in preference to printing as a void* at runtime.
typedef typename detail::is_wchar<T>::tinyformat_wchar_is_not_supported DummyType;
(void) DummyType(); // avoid unused type warning with gcc-4.8
#endif
// The mess here is to support the %c and %p conversions: if these
// conversions are active we try to convert the type to a char or const
// void* respectively and format that instead of the value itself. For the
// %p conversion it's important to avoid dereferencing the pointer, which
// could otherwise lead to a crash when printing a dangling (const char*).
bool canConvertToChar = detail::is_convertible<T,char>::value;
bool canConvertToVoidPtr = detail::is_convertible<T, const void*>::value;
if(canConvertToChar && *(fmtEnd-1) == 'c')
detail::formatValueAsType<T, char>::invoke(out, value);
else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p')
detail::formatValueAsType<T, const void*>::invoke(out, value);
#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
else if(detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/;
#endif
else if(ntrunc >= 0)
{
// Take care not to overread C strings in truncating conversions like
// "%.4s" where at most 4 characters may be read.
detail::formatTruncated(out, value, ntrunc);
}
else
out << value;
}
// Overloaded version for char types to support printing as an integer
#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \
const char* fmtEnd, int /**/, charType value) \
{ \
switch(*(fmtEnd-1)) \
{ \
case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \
out << static_cast<int>(value); break; \
default: \
out << value; break; \
} \
}
// per 3.9.1: char, signed char and unsigned char are all distinct types
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char)
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char)
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char)
#undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR
//------------------------------------------------------------------------------
// Tools for emulating variadic templates in C++98. The basic idea here is
// stolen from the boost preprocessor metaprogramming library and cut down to
// be just general enough for what we need.
#define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_ ## n
#define TINYFORMAT_VARARGS(n) TINYFORMAT_VARARGS_ ## n
#define TINYFORMAT_PASSARGS(n) TINYFORMAT_PASSARGS_ ## n
#define TINYFORMAT_PASSARGS_TAIL(n) TINYFORMAT_PASSARGS_TAIL_ ## n
// To keep it as transparent as possible, the macros below have been generated
// using python via the excellent cog.py code generation script. This avoids
// the need for a bunch of complex (but more general) preprocessor tricks as
// used in boost.preprocessor.
//
// To rerun the code generation in place, use `cog.py -r tinyformat.h`
// (see http://nedbatchelder.com/code/cog). Alternatively you can just create
// extra versions by hand.
/*[[[cog
maxParams = 16
def makeCommaSepLists(lineTemplate, elemTemplate, startInd=1):
for j in range(startInd,maxParams+1):
list = ', '.join([elemTemplate % {'i':i} for i in range(startInd,j+1)])
cog.outl(lineTemplate % {'j':j, 'list':list})
makeCommaSepLists('#define TINYFORMAT_ARGTYPES_%(j)d %(list)s',
'class T%(i)d')
cog.outl()
makeCommaSepLists('#define TINYFORMAT_VARARGS_%(j)d %(list)s',
'const T%(i)d& v%(i)d')
cog.outl()
makeCommaSepLists('#define TINYFORMAT_PASSARGS_%(j)d %(list)s', 'v%(i)d')
cog.outl()
cog.outl('#define TINYFORMAT_PASSARGS_TAIL_1')
makeCommaSepLists('#define TINYFORMAT_PASSARGS_TAIL_%(j)d , %(list)s',
'v%(i)d', startInd = 2)
cog.outl()
cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' +
' '.join(['m(%d)' % (j,) for j in range(1,maxParams+1)]))
]]]*/
#define TINYFORMAT_ARGTYPES_1 class T1
#define TINYFORMAT_ARGTYPES_2 class T1, class T2
#define TINYFORMAT_ARGTYPES_3 class T1, class T2, class T3
#define TINYFORMAT_ARGTYPES_4 class T1, class T2, class T3, class T4
#define TINYFORMAT_ARGTYPES_5 class T1, class T2, class T3, class T4, class T5
#define TINYFORMAT_ARGTYPES_6 class T1, class T2, class T3, class T4, class T5, class T6
#define TINYFORMAT_ARGTYPES_7 class T1, class T2, class T3, class T4, class T5, class T6, class T7
#define TINYFORMAT_ARGTYPES_8 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8
#define TINYFORMAT_ARGTYPES_9 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9
#define TINYFORMAT_ARGTYPES_10 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10
#define TINYFORMAT_ARGTYPES_11 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11
#define TINYFORMAT_ARGTYPES_12 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12
#define TINYFORMAT_ARGTYPES_13 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13
#define TINYFORMAT_ARGTYPES_14 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14
#define TINYFORMAT_ARGTYPES_15 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15
#define TINYFORMAT_ARGTYPES_16 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10, class T11, class T12, class T13, class T14, class T15, class T16
#define TINYFORMAT_VARARGS_1 const T1& v1
#define TINYFORMAT_VARARGS_2 const T1& v1, const T2& v2
#define TINYFORMAT_VARARGS_3 const T1& v1, const T2& v2, const T3& v3
#define TINYFORMAT_VARARGS_4 const T1& v1, const T2& v2, const T3& v3, const T4& v4
#define TINYFORMAT_VARARGS_5 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5
#define TINYFORMAT_VARARGS_6 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6
#define TINYFORMAT_VARARGS_7 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7
#define TINYFORMAT_VARARGS_8 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8
#define TINYFORMAT_VARARGS_9 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9
#define TINYFORMAT_VARARGS_10 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10
#define TINYFORMAT_VARARGS_11 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11
#define TINYFORMAT_VARARGS_12 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12
#define TINYFORMAT_VARARGS_13 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13
#define TINYFORMAT_VARARGS_14 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14
#define TINYFORMAT_VARARGS_15 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15
#define TINYFORMAT_VARARGS_16 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6, const T7& v7, const T8& v8, const T9& v9, const T10& v10, const T11& v11, const T12& v12, const T13& v13, const T14& v14, const T15& v15, const T16& v16
#define TINYFORMAT_PASSARGS_1 v1
#define TINYFORMAT_PASSARGS_2 v1, v2
#define TINYFORMAT_PASSARGS_3 v1, v2, v3
#define TINYFORMAT_PASSARGS_4 v1, v2, v3, v4
#define TINYFORMAT_PASSARGS_5 v1, v2, v3, v4, v5
#define TINYFORMAT_PASSARGS_6 v1, v2, v3, v4, v5, v6
#define TINYFORMAT_PASSARGS_7 v1, v2, v3, v4, v5, v6, v7
#define TINYFORMAT_PASSARGS_8 v1, v2, v3, v4, v5, v6, v7, v8
#define TINYFORMAT_PASSARGS_9 v1, v2, v3, v4, v5, v6, v7, v8, v9
#define TINYFORMAT_PASSARGS_10 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10
#define TINYFORMAT_PASSARGS_11 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11
#define TINYFORMAT_PASSARGS_12 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12
#define TINYFORMAT_PASSARGS_13 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13
#define TINYFORMAT_PASSARGS_14 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14
#define TINYFORMAT_PASSARGS_15 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15
#define TINYFORMAT_PASSARGS_16 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16
#define TINYFORMAT_PASSARGS_TAIL_1
#define TINYFORMAT_PASSARGS_TAIL_2 , v2
#define TINYFORMAT_PASSARGS_TAIL_3 , v2, v3
#define TINYFORMAT_PASSARGS_TAIL_4 , v2, v3, v4
#define TINYFORMAT_PASSARGS_TAIL_5 , v2, v3, v4, v5
#define TINYFORMAT_PASSARGS_TAIL_6 , v2, v3, v4, v5, v6
#define TINYFORMAT_PASSARGS_TAIL_7 , v2, v3, v4, v5, v6, v7
#define TINYFORMAT_PASSARGS_TAIL_8 , v2, v3, v4, v5, v6, v7, v8
#define TINYFORMAT_PASSARGS_TAIL_9 , v2, v3, v4, v5, v6, v7, v8, v9
#define TINYFORMAT_PASSARGS_TAIL_10 , v2, v3, v4, v5, v6, v7, v8, v9, v10
#define TINYFORMAT_PASSARGS_TAIL_11 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11
#define TINYFORMAT_PASSARGS_TAIL_12 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12
#define TINYFORMAT_PASSARGS_TAIL_13 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13
#define TINYFORMAT_PASSARGS_TAIL_14 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14
#define TINYFORMAT_PASSARGS_TAIL_15 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15
#define TINYFORMAT_PASSARGS_TAIL_16 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16
#define TINYFORMAT_FOREACH_ARGNUM(m) \
m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) m(15) m(16)
//[[[end]]]
namespace detail {
// Type-opaque holder for an argument to format(), with associated actions on
// the type held as explicit function pointers. This allows FormatArg's for
// each argument to be allocated as a homogenous array inside FormatList
// whereas a naive implementation based on inheritance does not.
class FormatArg
{
public:
FormatArg() {}
template<typename T>
FormatArg(const T& value)
: m_value(static_cast<const void*>(&value)),
m_formatImpl(&formatImpl<T>),
m_toIntImpl(&toIntImpl<T>)
{ }
void format(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc) const
{
m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
}
int toInt() const
{
return m_toIntImpl(m_value);
}
private:
template<typename T>
TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc, const void* value)
{
formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast<const T*>(value));
}
template<typename T>
TINYFORMAT_HIDDEN static int toIntImpl(const void* value)
{
return convertToInt<T>::invoke(*static_cast<const T*>(value));
}
const void* m_value;
void (*m_formatImpl)(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc, const void* value);
int (*m_toIntImpl)(const void* value);
};
// Parse and return an integer from the string c, as atoi()
// On return, c is set to one past the end of the integer.
inline int parseIntAndAdvance(const char*& c)
{
int i = 0;
for(;*c >= '0' && *c <= '9'; ++c)
i = 10*i + (*c - '0');
return i;
}
// Print literal part of format string and return next format spec
// position.
//
// Skips over any occurrences of '%%', printing a literal '%' to the
// output. The position of the first % character of the next
// nontrivial format spec is returned, or the end of string.
inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
{
const char* c = fmt;
for(;; ++c)
{
switch(*c)
{
case '\0':
out.write(fmt, c - fmt);
return c;
case '%':
out.write(fmt, c - fmt);
if(*(c+1) != '%')
return c;
// for "%%", tack trailing % onto next literal section.
fmt = ++c;
break;
default:
break;
}
}
}
// Parse a format string and set the stream state accordingly.
//
// The format mini-language recognized here is meant to be the one from C99,
// with the form "%[flags][width][.precision][length]type".
//
// Formatting options which can't be natively represented using the ostream
// state are returned in spacePadPositive (for space padded positive numbers)
// and ntrunc (for truncating conversions). argIndex is incremented if
// necessary to pull out variable width and precision . The function returns a
// pointer to the character after the end of the current format spec.
inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive,
int& ntrunc, const char* fmtStart,
const detail::FormatArg* formatters,
int& argIndex, int numFormatters)
{
if(*fmtStart != '%')
{
TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string");
return fmtStart;
}
// Reset stream state to defaults.
out.width(0);
out.precision(6);
out.fill(' ');
// Reset most flags; ignore irrelevant unitbuf & skipws.
out.unsetf(std::ios::adjustfield | std::ios::basefield |
std::ios::floatfield | std::ios::showbase | std::ios::boolalpha |
std::ios::showpoint | std::ios::showpos | std::ios::uppercase);
bool precisionSet = false;
bool widthSet = false;
int widthExtra = 0;
const char* c = fmtStart + 1;
// 1) Parse flags
for(;; ++c)
{
switch(*c)
{
case '#':
out.setf(std::ios::showpoint | std::ios::showbase);
continue;
case '0':
// overridden by left alignment ('-' flag)
if(!(out.flags() & std::ios::left))
{
// Use internal padding so that numeric values are
// formatted correctly, eg -00010 rather than 000-10
out.fill('0');
out.setf(std::ios::internal, std::ios::adjustfield);
}
continue;
case '-':
out.fill(' ');
out.setf(std::ios::left, std::ios::adjustfield);
continue;
case ' ':
// overridden by show positive sign, '+' flag.
if(!(out.flags() & std::ios::showpos))
spacePadPositive = true;
continue;
case '+':
out.setf(std::ios::showpos);
spacePadPositive = false;
widthExtra = 1;
continue;
default:
break;
}
break;
}
// 2) Parse width
if(*c >= '0' && *c <= '9')
{
widthSet = true;
out.width(parseIntAndAdvance(c));
}
if(*c == '*')
{
widthSet = true;
int width = 0;
if(argIndex < numFormatters)
width = formatters[argIndex++].toInt();
else
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width");
if(width < 0)
{
// negative widths correspond to '-' flag set
out.fill(' ');
out.setf(std::ios::left, std::ios::adjustfield);
width = -width;
}
out.width(width);
++c;
}
// 3) Parse precision
if(*c == '.')
{
++c;
int precision = 0;
if(*c == '*')
{
++c;
if(argIndex < numFormatters)
precision = formatters[argIndex++].toInt();
else
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision");
}
else
{
if(*c >= '0' && *c <= '9')
precision = parseIntAndAdvance(c);
else if(*c == '-') // negative precisions ignored, treated as zero.
parseIntAndAdvance(++c);
}
out.precision(precision);
precisionSet = true;
}
// 4) Ignore any C99 length modifier
while(*c == 'l' || *c == 'h' || *c == 'L' ||
*c == 'j' || *c == 'z' || *c == 't')
++c;
// 5) We're up to the conversion specifier character.
// Set stream flags based on conversion specifier (thanks to the
// boost::format class for forging the way here).
bool intConversion = false;
switch(*c)
{
case 'u': case 'd': case 'i':
out.setf(std::ios::dec, std::ios::basefield);
intConversion = true;
break;
case 'o':
out.setf(std::ios::oct, std::ios::basefield);
intConversion = true;
break;
case 'X':
out.setf(std::ios::uppercase);
case 'x': case 'p':
out.setf(std::ios::hex, std::ios::basefield);
intConversion = true;
break;
case 'E':
out.setf(std::ios::uppercase);
case 'e':
out.setf(std::ios::scientific, std::ios::floatfield);
out.setf(std::ios::dec, std::ios::basefield);
break;
case 'F':
out.setf(std::ios::uppercase);
case 'f':
out.setf(std::ios::fixed, std::ios::floatfield);
break;
case 'G':
out.setf(std::ios::uppercase);
case 'g':
out.setf(std::ios::dec, std::ios::basefield);
// As in boost::format, let stream decide float format.
out.flags(out.flags() & ~std::ios::floatfield);
break;
case 'a': case 'A':
TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs "
"are not supported");
break;
case 'c':
// Handled as special case inside formatValue()
break;
case 's':
if(precisionSet)
ntrunc = static_cast<int>(out.precision());
// Make %s print booleans as "true" and "false"
out.setf(std::ios::boolalpha);
break;
case 'n':
// Not supported - will cause problems!
TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported");
break;
case '\0':
TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly "
"terminated by end of string");
return c;
default:
break;
}
if(intConversion && precisionSet && !widthSet)
{
// "precision" for integers gives the minimum number of digits (to be
// padded with zeros on the left). This isn't really supported by the
// iostreams, but we can approximately simulate it with the width if
// the width isn't otherwise used.
out.width(out.precision() + widthExtra);
out.setf(std::ios::internal, std::ios::adjustfield);
out.fill('0');
}
return c+1;
}
//------------------------------------------------------------------------------
inline void formatImpl(std::ostream& out, const char* fmt,
const detail::FormatArg* formatters,
int numFormatters)
{
// Saved stream state
std::streamsize origWidth = out.width();
std::streamsize origPrecision = out.precision();
std::ios::fmtflags origFlags = out.flags();
char origFill = out.fill();
for (int argIndex = 0; argIndex < numFormatters; ++argIndex)
{
// Parse the format string
fmt = printFormatStringLiteral(out, fmt);
bool spacePadPositive = false;
int ntrunc = -1;
const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt,
formatters, argIndex, numFormatters);
if (argIndex >= numFormatters)
{
// Check args remain after reading any variable width/precision
TINYFORMAT_ERROR("tinyformat: Not enough format arguments");
return;
}
const FormatArg& arg = formatters[argIndex];
// Format the arg into the stream.
if(!spacePadPositive)
arg.format(out, fmt, fmtEnd, ntrunc);
else
{
// The following is a special case with no direct correspondence
// between stream formatting and the printf() behaviour. Simulate
// it crudely by formatting into a temporary string stream and
// munging the resulting string.
std::ostringstream tmpStream;
tmpStream.copyfmt(out);
tmpStream.setf(std::ios::showpos);
arg.format(tmpStream, fmt, fmtEnd, ntrunc);
std::string result = tmpStream.str(); // allocates... yuck.
for(size_t i = 0, iend = result.size(); i < iend; ++i)
if(result[i] == '+') result[i] = ' ';
out << result;
}
fmt = fmtEnd;
}
// Print remaining part of format string.
fmt = printFormatStringLiteral(out, fmt);
if(*fmt != '\0')
TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
// Restore stream state
out.width(origWidth);
out.precision(origPrecision);
out.flags(origFlags);
out.fill(origFill);
}
} // namespace detail
/// List of template arguments format(), held in a type-opaque way.
///
/// A const reference to FormatList (typedef'd as FormatListRef) may be
/// conveniently used to pass arguments to non-template functions: All type
/// information has been stripped from the arguments, leaving just enough of a
/// common interface to perform formatting as required.
class FormatList
{
public:
FormatList(detail::FormatArg* formatters, int N)
: m_formatters(formatters), m_N(N) { }
friend void vformat(std::ostream& out, const char* fmt,
const FormatList& list);
private:
const detail::FormatArg* m_formatters;
int m_N;
};
/// Reference to type-opaque format list for passing to vformat()
typedef const FormatList& FormatListRef;
namespace detail {
// Format list subclass with fixed storage to avoid dynamic allocation
template<int N>
class FormatListN : public FormatList
{
public:
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
template<typename... Args>
FormatListN(const Args&... args)
: FormatList(&m_formatterStore[0], N),
m_formatterStore { FormatArg(args)... }
{ static_assert(sizeof...(args) == N, "Number of args must be N"); }
#else // C++98 version
void init(int) {}
# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \
\
template<TINYFORMAT_ARGTYPES(n)> \
FormatListN(TINYFORMAT_VARARGS(n)) \
: FormatList(&m_formatterStore[0], n) \
{ assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \
\
template<TINYFORMAT_ARGTYPES(n)> \
void init(int i, TINYFORMAT_VARARGS(n)) \
{ \
m_formatterStore[i] = FormatArg(v1); \
init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \
}
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR)
# undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR
#endif
private:
FormatArg m_formatterStore[N];
};
// Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard
template<> class FormatListN<0> : public FormatList
{
public: FormatListN() : FormatList(0, 0) {}
};
} // namespace detail
//------------------------------------------------------------------------------
// Primary API functions
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
/// Make type-agnostic format list from list of template arguments.
///
/// The exact return type of this function is an implementation detail and
/// shouldn't be relied upon. Instead it should be stored as a FormatListRef:
///
/// FormatListRef formatList = makeFormatList( /*...*/ );
template<typename... Args>
detail::FormatListN<sizeof...(Args)> makeFormatList(const Args&... args)
{
return detail::FormatListN<sizeof...(args)>(args...);
}
#else // C++98 version
inline detail::FormatListN<0> makeFormatList()
{
return detail::FormatListN<0>();
}
#define TINYFORMAT_MAKE_MAKEFORMATLIST(n) \
template<TINYFORMAT_ARGTYPES(n)> \
detail::FormatListN<n> makeFormatList(TINYFORMAT_VARARGS(n)) \
{ \
return detail::FormatListN<n>(TINYFORMAT_PASSARGS(n)); \
}
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST)
#undef TINYFORMAT_MAKE_MAKEFORMATLIST
#endif
/// Format list of arguments to the stream according to the given format string.
///
/// The name vformat() is chosen for the semantic similarity to vprintf(): the
/// list of format arguments is held in a single function argument.
inline void vformat(std::ostream& out, const char* fmt, FormatListRef list)
{
detail::formatImpl(out, fmt, list.m_formatters, list.m_N);
}
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
/// Format list of arguments to the stream according to given format string.
template<typename... Args>
void format(std::ostream& out, const char* fmt, const Args&... args)
{
vformat(out, fmt, makeFormatList(args...));
}
/// Format list of arguments according to the given format string and return
/// the result as a string.
template<typename... Args>
std::string format(const char* fmt, const Args&... args)
{
std::ostringstream oss;
format(oss, fmt, args...);
return oss.str();
}
/// Format list of arguments to std::cout, according to the given format string
template<typename... Args>
void printf(const char* fmt, const Args&... args)
{
format(std::cout, fmt, args...);
}
template<typename... Args>
void printfln(const char* fmt, const Args&... args)
{
format(std::cout, fmt, args...);
std::cout << '\n';
}
#else // C++98 version
inline void format(std::ostream& out, const char* fmt)
{
vformat(out, fmt, makeFormatList());
}
inline std::string format(const char* fmt)
{
std::ostringstream oss;
format(oss, fmt);
return oss.str();
}
inline void printf(const char* fmt)
{
format(std::cout, fmt);
}
inline void printfln(const char* fmt)
{
format(std::cout, fmt);
std::cout << '\n';
}
#define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \
\
template<TINYFORMAT_ARGTYPES(n)> \
void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n))); \
} \
\
template<TINYFORMAT_ARGTYPES(n)> \
std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
std::ostringstream oss; \
format(oss, fmt, TINYFORMAT_PASSARGS(n)); \
return oss.str(); \
} \
\
template<TINYFORMAT_ARGTYPES(n)> \
void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \
} \
\
template<TINYFORMAT_ARGTYPES(n)> \
void printfln(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \
std::cout << '\n'; \
}
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS)
#undef TINYFORMAT_MAKE_FORMAT_FUNCS
#endif
} // namespace tinyformat
#endif // TINYFORMAT_H_INCLUDED
......@@ -8,7 +8,16 @@ target_link_libraries(test_static bcc-static)
add_test(NAME c_test_static COMMAND ${TEST_WRAPPER} c_test_static sudo ${CMAKE_CURRENT_BINARY_DIR}/test_static)
add_executable(test_c_api test_c_api.c)
target_link_libraries(test_c_api bcc-shared dl)
add_executable(test_libbcc
test_libbcc.cc
test_c_api.cc
test_usdt_args.cc
test_usdt_probes.cc)
add_test(NAME test_c_api COMMAND ${TEST_WRAPPER} c_test_api sudo ${CMAKE_CURRENT_BINARY_DIR}/test_c_api)
target_link_libraries(test_libbcc bcc-shared dl)
add_test(NAME test_libbcc COMMAND ${TEST_WRAPPER} c_test_all sudo ${CMAKE_CURRENT_BINARY_DIR}/test_libbcc)
find_path(SDT_HEADER NAMES "sys/sdt.h")
if (SDT_HEADER)
target_compile_definitions(test_libbcc PRIVATE HAVE_SDT_HEADER=1)
endif()
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
* sput - Simple, Portable Unit Testing Framework for C/C++ v1.3.1
*
* http://www.lingua-systems.com/unit-testing/
*
*
* Copyright (c) 2011-2015 Lingua-Systems Software GmbH
*
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * 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 HOLDER 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 HAVE_SPUT_H
#define HAVE_SPUT_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
/* ===================================================================
* definitions
* =================================================================== */
#define SPUT_VERSION_MAJOR 1
#define SPUT_VERSION_MINOR 3
#define SPUT_VERSION_PATCH 1
#define SPUT_VERSION_STRING "1.3.1"
#define SPUT_DEFAULT_SUITE_NAME "Unlabeled Suite"
#define SPUT_DEFAULT_CHECK_NAME "Unlabeled Check"
#define SPUT_INITIALIZED 0x06 /* ACK */
/* ===================================================================
* sput global variable
* =================================================================== */
static struct sput {
FILE *out;
char initialized;
struct sput_overall {
unsigned long checks;
unsigned long suites;
unsigned long ok;
unsigned long nok;
} overall;
struct sput_suite {
const char *name;
unsigned long nr;
unsigned long checks;
unsigned long ok;
unsigned long nok;
} suite;
struct sput_test {
const char *name;
unsigned long nr;
} test;
struct sput_check {
const char *name;
const char *cond;
const char *type;
unsigned long line;
} check;
struct sput_time {
time_t start;
time_t end;
} time;
} __sput;
/* ==================================================================
* sput internal macros
* ================================================================== */
#define _sput_die_unless_initialized() \
if (__sput.initialized != SPUT_INITIALIZED) { \
fputs("sput_start_testing() omitted\n", stderr); \
exit(EXIT_FAILURE); \
}
#define _sput_die_unless_suite_set() \
if (!__sput.suite.name) { \
fputs("sput_enter_suite() omitted\n", __sput.out); \
exit(EXIT_FAILURE); \
}
#define _sput_die_unless_test_set() \
if (!__sput.test.name) { \
fputs("sput_run_test() omitted\n", __sput.out); \
exit(EXIT_FAILURE); \
}
#define _sput_check_failed() \
{ \
_sput_die_unless_initialized(); \
_sput_die_unless_suite_set(); \
__sput.suite.nok++; \
fprintf(__sput.out, \
"[%lu:%lu] %s:#%lu \"%s\" FAIL\n" \
"! Type: %s\n" \
"! Condition: %s\n" \
"! Line: %lu\n", \
__sput.suite.nr, __sput.suite.checks, __sput.test.name, \
__sput.test.nr, __sput.check.name, __sput.check.type, \
__sput.check.cond, __sput.check.line); \
}
#define _sput_check_succeeded() \
{ \
_sput_die_unless_initialized(); \
_sput_die_unless_suite_set(); \
__sput.suite.ok++; \
fprintf(__sput.out, "[%lu:%lu] %s:#%lu \"%s\" pass\n", __sput.suite.nr, \
__sput.suite.checks, __sput.test.name, __sput.test.nr, \
__sput.check.name); \
}
/* ==================================================================
* user macros
* ================================================================== */
#define sput_start_testing() \
do { \
memset(&__sput, 0, sizeof(__sput)); \
__sput.out = stdout; \
__sput.time.start = time(NULL); \
__sput.initialized = SPUT_INITIALIZED; \
} while (0)
#define sput_leave_suite() \
do { \
float failpls = 0.0f; \
_sput_die_unless_initialized(); \
_sput_die_unless_suite_set(); \
failpls = __sput.suite.checks \
? (float)((__sput.suite.nok * 100.0) / __sput.suite.checks) \
: 0.0f; \
fprintf(__sput.out, "\n--> %lu check(s), %lu ok, %lu failed (%.2f%%)\n", \
__sput.suite.checks, __sput.suite.ok, __sput.suite.nok, failpls); \
__sput.overall.checks += __sput.suite.checks; \
__sput.overall.ok += __sput.suite.ok; \
__sput.overall.nok += __sput.suite.nok; \
memset(&__sput.suite, 0, sizeof(__sput.suite)); \
} while (0)
#define sput_get_return_value() \
(__sput.overall.nok > 0 ? EXIT_FAILURE : EXIT_SUCCESS)
#define sput_enter_suite(_name) \
do { \
_sput_die_unless_initialized(); \
if (__sput.suite.name) { \
sput_leave_suite(); \
} \
__sput.suite.name = _name != NULL ? _name : SPUT_DEFAULT_SUITE_NAME; \
__sput.suite.nr = ++__sput.overall.suites; \
fprintf(__sput.out, "\n== Entering suite #%lu, \"%s\" ==\n\n", \
__sput.suite.nr, __sput.suite.name); \
} while (0)
#define sput_finish_testing() \
do { \
float failpft = 0.0f; \
_sput_die_unless_initialized(); \
if (__sput.suite.name) { \
sput_leave_suite(); \
} \
failpft = \
__sput.overall.checks \
? (float)((__sput.overall.nok * 100.0) / __sput.overall.checks) \
: 0.0f; \
__sput.time.end = time(NULL); \
fprintf( \
__sput.out, \
"\n==> %lu check(s) in %lu suite(s) finished after %.2f " \
"second(s),\n" \
" %lu succeeded, %lu failed (%.2f%%)\n" \
"\n[%s]\n", \
__sput.overall.checks, __sput.overall.suites, \
difftime(__sput.time.end, __sput.time.start), __sput.overall.ok, \
__sput.overall.nok, failpft, \
(sput_get_return_value() == EXIT_SUCCESS) ? "SUCCESS" : "FAILURE"); \
} while (0)
#define sput_set_output_stream(_fp) \
do { \
__sput.out = _fp != NULL ? _fp : stdout; \
} while (0)
#define sput_fail_if(_cond, _name) \
do { \
_sput_die_unless_initialized(); \
_sput_die_unless_suite_set(); \
_sput_die_unless_test_set(); \
__sput.check.name = _name != NULL ? _name : SPUT_DEFAULT_CHECK_NAME; \
__sput.check.line = __LINE__; \
__sput.check.cond = #_cond; \
__sput.check.type = "fail-if"; \
__sput.test.nr++; \
__sput.suite.checks++; \
if ((_cond)) { \
_sput_check_failed(); \
} else { \
_sput_check_succeeded(); \
} \
} while (0)
#define sput_fail_unless(_cond, _name) \
do { \
_sput_die_unless_initialized(); \
_sput_die_unless_suite_set(); \
_sput_die_unless_test_set(); \
__sput.check.name = _name != NULL ? _name : SPUT_DEFAULT_CHECK_NAME; \
__sput.check.line = __LINE__; \
__sput.check.cond = #_cond; \
__sput.check.type = "fail-unless"; \
__sput.test.nr++; \
__sput.suite.checks++; \
if (!(_cond)) { \
_sput_check_failed(); \
} else { \
_sput_check_succeeded(); \
} \
} while (0)
#define sput_run_test(_func) \
do { \
_sput_die_unless_initialized(); \
_sput_die_unless_suite_set(); \
memset(&__sput.test, 0, sizeof(__sput.test)); \
__sput.test.name = #_func; \
_func(); \
} while (0)
#ifdef __cplusplus
}
#endif
#endif /* HAVE_SPUT_H */
/* vim: set ft=c sts=4 sw=4 ts=4 ai et: */
#include <stdint.h>
#include <unistd.h>
#include <dlfcn.h>
#include "sput.h"
#include "bcc_elf.h"
#include "bcc_proc.h"
#include "bcc_syms.h"
static void test_procutils__which_so(void) {
const char *libm = bcc_procutils_which_so("m");
sput_fail_unless(libm, "find libm");
sput_fail_unless(libm[0] == '/', "resolve libm absolute path");
sput_fail_unless(strstr(libm, "libm.so"), "resolve libm so");
}
static void test_procutils__which(void) {
char *ld = bcc_procutils_which("ld");
sput_fail_unless(ld, "find `ld` binary");
sput_fail_unless(ld[0] == '/', "find `ld` absolute path");
free(ld);
}
static void _test_ksym(const char *sym, uint64_t addr, void *_) {
if (!strcmp(sym, "startup_64")) {
sput_fail_unless(addr == 0xffffffff81000000ull, "ksym `startup_64`");
} else if (!strcmp(sym, "__per_cpu_start"))
sput_fail_unless(addr == 0x0, "ksym `__per_cpu_start`");
}
static void test_procutils__each_ksym(void) {
sput_fail_unless(geteuid() == 0, "ensure we are root");
bcc_procutils_each_ksym(_test_ksym, NULL);
}
static void test_syms__resolve_symname(void) {
struct bcc_symbol sym;
sput_fail_unless(bcc_resolve_symname("c", "malloc", 0x0, &sym) == 0,
"bcc_resolve_symname(c, malloc)");
sput_fail_unless(strstr(sym.module, "libc.so"), "resolve to module");
sput_fail_unless(sym.module[0] == '/', "resolve to abspath");
sput_fail_unless(sym.offset != 0, "resolve sym offset");
}
static void test_syms__resolver_pid(void) {
struct bcc_symbol sym;
void *resolver = bcc_symcache_new(getpid());
sput_fail_unless(resolver, "create a new resolver for PID");
sput_fail_unless(bcc_symcache_resolve(
resolver, (uint64_t)&test_syms__resolver_pid, &sym) == 0,
"resolve the current function address");
char *this_exe = realpath("/proc/self/exe", NULL);
sput_fail_unless(strcmp(this_exe, sym.module) == 0,
"resolve a function to our own binary");
free(this_exe);
sput_fail_unless(strcmp("test_syms__resolver_pid", sym.name) == 0,
"resolve a function to its actual name");
void *libbcc = dlopen("libbcc.so", RTLD_LAZY | RTLD_NOLOAD);
sput_fail_unless(libbcc, "dlopen(libbcc.so)");
void *libbcc_fptr = dlsym(libbcc, "bcc_resolve_symname");
sput_fail_unless(libbcc_fptr, "dlsym(bcc_resolve_symname)");
sput_fail_unless(
bcc_symcache_resolve(resolver, (uint64_t)libbcc_fptr, &sym) == 0,
"resolve a function in libbcc in our current process");
sput_fail_unless(strstr(sym.module, "libbcc.so"),
"resolve a function to the loaded libbcc module");
sput_fail_unless(strcmp("bcc_resolve_symname", sym.name) == 0,
"resolve a function in libbcc to its actual name");
void *libc_fptr = dlsym(NULL, "strtok");
sput_fail_unless(libc_fptr, "dlsym(strtok)");
sput_fail_unless(
bcc_symcache_resolve(resolver, (uint64_t)libc_fptr, &sym) == 0,
"resolve a function in libc in our current process");
sput_fail_unless(
sym.module && sym.module[0] == '/' && strstr(sym.module, "libc"),
"resolve a function to linked libc module");
sput_fail_unless(strcmp("strtok", sym.name) == 0,
"resolve a function in libc to its actual name");
}
int main(int argc, char *argv[]) {
sput_start_testing();
sput_enter_suite("procutils: which_so");
sput_run_test(test_procutils__which_so);
sput_enter_suite("procutils: which");
sput_run_test(test_procutils__which);
sput_enter_suite("procutils: each_ksym");
sput_run_test(test_procutils__each_ksym);
sput_enter_suite("syms: resolve_symname");
sput_run_test(test_syms__resolve_symname);
sput_enter_suite("syms: resolver_pid");
sput_run_test(test_syms__resolver_pid);
sput_finish_testing();
return sput_get_return_value();
}
/*
* Copyright (c) 2016 GitHub, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <dlfcn.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include "bcc_elf.h"
#include "bcc_proc.h"
#include "bcc_syms.h"
#include "catch.hpp"
using namespace std;
TEST_CASE("shared object resolution", "[c_api]") {
const char *libm = bcc_procutils_which_so("m");
REQUIRE(libm);
REQUIRE(libm[0] == '/');
REQUIRE(string(libm).find("libm.so") != string::npos);
}
TEST_CASE("binary resolution with `which`", "[c_api]") {
char *ld = bcc_procutils_which("ld");
REQUIRE(ld);
REQUIRE(ld[0] == '/');
free(ld);
}
static void _test_ksym(const char *sym, uint64_t addr, void *_) {
if (!strcmp(sym, "startup_64")) {
REQUIRE(addr == 0xffffffff81000000ull);
} else if (!strcmp(sym, "__per_cpu_start"))
REQUIRE(addr == 0x0);
}
TEST_CASE("list all kernel symbols", "[c_api]") {
if (geteuid() != 0)
return;
bcc_procutils_each_ksym(_test_ksym, NULL);
}
TEST_CASE("resolve symbol name in external library", "[c_api]") {
struct bcc_symbol sym;
REQUIRE(bcc_resolve_symname("c", "malloc", 0x0, &sym) == 0);
REQUIRE(string(sym.module).find("libc.so") != string::npos);
REQUIRE(sym.module[0] == '/');
REQUIRE(sym.offset != 0);
}
extern "C" int _a_test_function(const char *a_string) {
int i;
for (i = 0; a_string[i]; ++i)
;
return i;
}
TEST_CASE("resolve symbol addresses for a given PID", "[c_api]") {
struct bcc_symbol sym;
void *resolver = bcc_symcache_new(getpid());
REQUIRE(resolver);
SECTION("resolve in our own binary memory space") {
REQUIRE(bcc_symcache_resolve(resolver, (uint64_t)&_a_test_function, &sym) ==
0);
char *this_exe = realpath("/proc/self/exe", NULL);
REQUIRE(string(this_exe) == sym.module);
free(this_exe);
REQUIRE(string("_a_test_function") == sym.name);
}
SECTION("resolve in libbcc.so") {
void *libbcc = dlopen("libbcc.so", RTLD_LAZY | RTLD_NOLOAD);
REQUIRE(libbcc);
void *libbcc_fptr = dlsym(libbcc, "bcc_resolve_symname");
REQUIRE(libbcc_fptr);
REQUIRE(bcc_symcache_resolve(resolver, (uint64_t)libbcc_fptr, &sym) == 0);
REQUIRE(string(sym.module).find("libbcc.so") != string::npos);
REQUIRE(string("bcc_resolve_symname") == sym.name);
}
SECTION("resolve in libc") {
void *libc_fptr = dlsym(NULL, "strtok");
REQUIRE(libc_fptr);
REQUIRE(bcc_symcache_resolve(resolver, (uint64_t)libc_fptr, &sym) == 0);
REQUIRE(sym.module);
REQUIRE(sym.module[0] == '/');
REQUIRE(string(sym.module).find("libc") != string::npos);
REQUIRE(string("strtok") == sym.name);
}
}
#define CATCH_CONFIG_MAIN
#include "catch.hpp"
/*
* Copyright (c) 2016 GitHub, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iostream>
#include <string>
#include "catch.hpp"
#include "usdt.h"
using std::experimental::optional;
using std::experimental::nullopt;
static void verify_register(USDT::ArgumentParser_x64 &parser, int arg_size,
int constant) {
USDT::Argument arg;
REQUIRE(parser.parse(&arg));
REQUIRE(arg.arg_size() == arg_size);
REQUIRE(arg.constant());
REQUIRE(arg.constant() == constant);
}
static void verify_register(USDT::ArgumentParser_x64 &parser, int arg_size,
const std::string &regname,
optional<int> deref_offset = nullopt,
optional<std::string> deref_ident = nullopt) {
USDT::Argument arg;
REQUIRE(parser.parse(&arg));
REQUIRE(arg.arg_size() == arg_size);
REQUIRE(arg.register_name());
REQUIRE(arg.register_name() == regname);
REQUIRE(arg.deref_offset() == deref_offset);
REQUIRE(arg.deref_ident() == deref_ident);
}
TEST_CASE("test usdt argument parsing", "[usdt]") {
SECTION("argument examples from the Python implementation") {
USDT::ArgumentParser_x64 parser(
"-4@$0 8@$1234 %rdi %rax %rsi "
"-8@%rbx 4@%r12 8@-8(%rbp) 4@(%rax) "
"-4@global_max_action(%rip) "
"8@24+mp_(%rip) ");
verify_register(parser, -4, 0);
verify_register(parser, 8, 1234);
verify_register(parser, 8, "di");
verify_register(parser, 8, "ax");
verify_register(parser, 8, "si");
verify_register(parser, -8, "bx");
verify_register(parser, 4, "r12");
verify_register(parser, 8, "bp", -8);
verify_register(parser, 4, "ax", 0);
verify_register(parser, -4, "ip", 0, std::string("global_max_action"));
verify_register(parser, 8, "ip", 24, std::string("mp_"));
REQUIRE(parser.done());
}
}
/*
* Copyright (c) 2016 GitHub, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <signal.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "catch.hpp"
#include "usdt.h"
#ifdef HAVE_SDT_HEADER
/* required to insert USDT probes on this very executable --
* we're gonna be testing them live! */
#include <sys/sdt.h>
static int a_probed_function() {
int an_int = 23 + getpid();
void *a_pointer = malloc(4);
DTRACE_PROBE2(libbcc_test, sample_probe_1, an_int, a_pointer);
free(a_pointer);
return an_int;
}
TEST_CASE("test finding a probe in our own process", "[usdt]") {
USDT::Context ctx(getpid());
REQUIRE(ctx.num_probes() >= 1);
SECTION("our test probe") {
USDT::Probe *probe = ctx.find_probe("sample_probe_1");
REQUIRE(probe != nullptr);
REQUIRE(probe->in_shared_object() == false);
REQUIRE(probe->name() == "sample_probe_1");
REQUIRE(probe->provider() == "libbcc_test");
REQUIRE(probe->bin_path().find("/test_libbcc") != std::string::npos);
REQUIRE(probe->num_locations() == 1);
REQUIRE(probe->num_arguments() == 2);
REQUIRE(probe->need_enable() == false);
REQUIRE(a_probed_function() != 0);
std::ostringstream case_stream;
REQUIRE(probe->usdt_cases(case_stream));
std::string cases = case_stream.str();
REQUIRE(cases.find("int32_t arg1") != std::string::npos);
REQUIRE(cases.find("uint64_t arg2") != std::string::npos);
}
}
#endif // HAVE_SDT_HEADER
static size_t countsubs(const std::string &str, const std::string &sub) {
size_t count = 0;
for (size_t offset = str.find(sub); offset != std::string::npos;
offset = str.find(sub, offset + sub.length())) {
++count;
}
return count;
}
class ChildProcess {
pid_t pid_;
public:
ChildProcess(const char *name, char *const argv[]) {
pid_ = fork();
if (pid_ == 0) {
execvp(name, argv);
exit(0);
}
if (spawned()) {
usleep(250000);
if (kill(pid_, 0) < 0)
pid_ = -1;
}
}
~ChildProcess() {
if (spawned()) {
int status;
kill(pid_, SIGKILL);
if (waitpid(pid_, &status, 0) != pid_)
abort();
}
}
bool spawned() const { return pid_ > 0; }
pid_t pid() const { return pid_; }
};
TEST_CASE("test listing all USDT probes in Ruby/MRI", "[usdt]") {
size_t mri_probe_count = 0;
SECTION("without a running Ruby process") {
USDT::Context ctx("ruby");
if (!ctx.loaded())
return;
REQUIRE(ctx.num_probes() > 10);
mri_probe_count = ctx.num_probes();
SECTION("GC static probe") {
USDT::Probe *probe = ctx.find_probe("gc__mark__begin");
REQUIRE(probe != nullptr);
REQUIRE(probe->in_shared_object() == true);
REQUIRE(probe->name() == "gc__mark__begin");
REQUIRE(probe->provider() == "ruby");
REQUIRE(probe->bin_path().find("/ruby") != std::string::npos);
REQUIRE(probe->num_locations() == 1);
REQUIRE(probe->num_arguments() == 0);
REQUIRE(probe->need_enable() == true);
}
SECTION("object creation probe") {
USDT::Probe *probe = ctx.find_probe("object__create");
REQUIRE(probe != nullptr);
REQUIRE(probe->in_shared_object() == true);
REQUIRE(probe->name() == "object__create");
REQUIRE(probe->provider() == "ruby");
REQUIRE(probe->bin_path().find("/ruby") != std::string::npos);
REQUIRE(probe->num_locations() == 1);
REQUIRE(probe->num_arguments() == 3);
REQUIRE(probe->need_enable() == true);
std::ostringstream thunks_stream;
REQUIRE(probe->usdt_thunks(thunks_stream, "ruby_usdt"));
std::string thunks = thunks_stream.str();
REQUIRE(std::count(thunks.begin(), thunks.end(), '\n') == 1);
REQUIRE(thunks.find("ruby_usdt_thunk_0") != std::string::npos);
std::ostringstream case_stream;
REQUIRE(probe->usdt_cases(case_stream));
std::string cases = case_stream.str();
REQUIRE(countsubs(cases, "arg1") == 2);
REQUIRE(countsubs(cases, "arg2") == 2);
REQUIRE(countsubs(cases, "arg3") == 2);
REQUIRE(countsubs(cases, "uint64_t") == 4);
REQUIRE(countsubs(cases, "int32_t") == 2);
}
SECTION("array creation probe") {
USDT::Probe *probe = ctx.find_probe("array__create");
REQUIRE(probe != nullptr);
REQUIRE(probe->name() == "array__create");
REQUIRE(probe->num_locations() == 7);
REQUIRE(probe->num_arguments() == 3);
REQUIRE(probe->need_enable() == true);
std::ostringstream thunks_stream;
REQUIRE(probe->usdt_thunks(thunks_stream, "ruby_usdt"));
std::string thunks = thunks_stream.str();
REQUIRE(std::count(thunks.begin(), thunks.end(), '\n') == 7);
REQUIRE(thunks.find("ruby_usdt_thunk_0") != std::string::npos);
REQUIRE(thunks.find("ruby_usdt_thunk_6") != std::string::npos);
REQUIRE(thunks.find("ruby_usdt_thunk_7") == std::string::npos);
std::ostringstream case_stream;
REQUIRE(probe->usdt_cases(case_stream));
std::string cases = case_stream.str();
REQUIRE(countsubs(cases, "arg1") == 8);
REQUIRE(countsubs(cases, "arg2") == 8);
REQUIRE(countsubs(cases, "arg3") == 8);
REQUIRE(countsubs(cases, "__loc_id") == 7);
REQUIRE(cases.find("int64_t arg1 =") != std::string::npos);
}
}
SECTION("with a running Ruby process") {
static char _ruby[] = "ruby";
char *const argv[2] = {_ruby, NULL};
ChildProcess ruby(argv[0], argv);
if (!ruby.spawned())
return;
USDT::Context ctx(ruby.pid());
REQUIRE(ctx.num_probes() >= mri_probe_count);
SECTION("get probe in running process") {
USDT::Probe *probe = ctx.find_probe("gc__mark__begin");
REQUIRE(probe != nullptr);
REQUIRE(probe->in_shared_object() == true);
REQUIRE(probe->name() == "gc__mark__begin");
REQUIRE(probe->provider() == "ruby");
REQUIRE(probe->bin_path().find("/ruby") != std::string::npos);
REQUIRE(probe->num_locations() == 1);
REQUIRE(probe->num_arguments() == 0);
REQUIRE(probe->need_enable() == true);
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment