3#include <sysio/vm/exceptions.hpp>
4#include <sysio/vm/utils.hpp>
5#include <sysio/vm/debug_info.hpp>
10#include <condition_variable>
23 template<
typename Backend>
24 profile_data(
const std::string& file, Backend&
bkend,
const std::size_t buffer_size = 65536, std::size_t hash_table_size = 1024) :
49 fd = open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0755);
66 write(
reinterpret_cast<const char*
>(header),
sizeof(header));
70 write(
reinterpret_cast<const char*
>(trailer),
sizeof(trailer));
73 for(std::size_t i = 0; i <
outpos;) {
85 void write(
const char* data, std::size_t size) {
117 static_assert(offsetof(
item,
len) % 8 == 0);
118 constexpr std::uint64_t
mul = 0xc6a4a7935bd1e995ull;
119 constexpr std::uint64_t seed = 0xbadd00d00ull;
120 constexpr auto shift_mix = [](std::uint64_t v) {
return v ^ (v >> 47); };
121 int word_len =
len/2+1;
123 const char* base_addr =
reinterpret_cast<const char*
>(&
len);
124 for(
int i = 0; i < word_len; ++i) {
126 memcpy(&val, base_addr + 8*i, 8);
127 hash = (hash ^ shift_mix(val *
mul) *
mul) *
mul;
129 return shift_mix(shift_mix(hash) *
mul);
134 if(lhs->
len != rhs->
len) {
151 if(result->bucket != 0xFFFFFFFFu) {
153 for(
item** entry = &table[result->bucket]; ; entry = &(*entry)->
next) {
154 if(*entry == result) {
155 *entry = result->next;
159 result->bucket = 0xFFFFFFFFu;
175 std::size_t hash = new_item->
hash();
177 for(
item* bucket_entry = table[idx]; bucket_entry; bucket_entry = bucket_entry->
next) {
179 ++bucket_entry->count;
184 new_item->
next = table[idx];
187 table[idx] = new_item;
193 for(
item* entry = table[i]; entry; entry = entry->
next) {
195 entry->bucket = 0xFFFFFFFF;
209 if(addr != 0xFFFFFFFFu) {
213 entry->
frames[out++] = addr;
240 template<
typename Context>
243 return static_cast<const Context*
>(ctx)->backtrace(data,
len, uc);
259 sigemptyset(&sa.sa_mask);
260 sa.sa_flags = SA_SIGINFO | SA_RESTART;
261 sigaction(SIGPROF, &sa,
nullptr);
278 static_assert(std::atomic<profile_data*>::is_always_lock_free);
279 auto * ptr = std::atomic_load(
static_cast<std::atomic<profile_data*>*
>(info->si_value.sival_ptr));
281 int saved_errno = errno;
283 int count = ptr->get_backtrace_fn(ptr->exec_context, data,
sizeof(data)/
sizeof(data[0]), uc);
284 ptr->handle_tick(data,
count);
289struct profile_manager {
293 event.sigev_notify = SIGEV_THREAD_ID;
294 event.sigev_signo = SIGPROF;
295 event.sigev_value.sival_ptr = ¤t_data;
296 event._sigev_un._tid = gettid();
297 int res = timer_create(CLOCK_MONOTONIC, &event, &timer);
298 SYS_VM_ASSERT(res == 0, profile_exception,
"Failed to start timer");
299 struct itimerspec spec;
300 spec.it_interval.tv_sec = 0;
302 spec.it_value.tv_sec = 0;
304 res = timer_settime(timer, 0, &spec,
nullptr);
305 SYS_VM_ASSERT(res == 0, profile_exception,
"Failed to start timer");
307 void start(profile_data* data) {
308 SYS_VM_ASSERT(!current_data, profile_exception,
"Already profiling in the current thread");
312 current_data =
nullptr;
315 current_data =
nullptr;
319 std::atomic<profile_data*> current_data;
323inline thread_local
std::unique_ptr<profile_manager> per_thread_profile_manager;
325struct scoped_profile {
326 explicit scoped_profile(profile_data* data) {
328 if(!per_thread_profile_manager) {
329 per_thread_profile_manager = std::make_unique<profile_manager>();
331 per_thread_profile_manager->start(data);
335 if(per_thread_profile_manager) {
336 per_thread_profile_manager->stop();
344inline thread_local
std::atomic<profile_data*> per_thread_profile_data = ATOMIC_VAR_INIT(
nullptr);
347 static_assert(std::atomic<profile_data*>::is_always_lock_free);
348 auto * ptr = std::atomic_load(&per_thread_profile_data);
350 int saved_errno = errno;
352 int count = ptr->get_backtrace_fn(ptr->exec_context, data,
sizeof(data)/
sizeof(data[0]), uc);
353 ptr->handle_tick(data,
count);
362 auto lock = std::unique_lock(mutex);
365 pthread_kill(notify, SIGPROF);
371 auto lock = std::lock_guard(mutex);
373 per_thread_profile_data = data;
377 per_thread_profile_data =
nullptr;
378 auto lock = std::lock_guard(mutex);
383 auto lock = std::scoped_lock(mutex);
std::uint32_t translate(const void *pc) const
scoped_profile(profile_data *data)
backend_t bkend(hello_wasm, ehm, &wa)
void close(T *e, websocketpp::connection_hdl hdl)
__attribute__((always_inline)) inline uint64_t rotl64(uint64_t x
void profile_handler(int sig, siginfo_t *info, void *)
void register_profile_signal_handler()
void ignore_unused_variable_warning(T &...)
void register_profile_signal_handler_impl()
uint32_t profile_interval_us
void set_profile_interval_us(uint32_t value)
sysio::client::http::http_context context
unsigned __int64 uint64_t
uint32_t frames[max_frames]
void init_backtrace(const Context &context)
const profile_instr_map & addr_map
profile_data(const std::string &file, Backend &bkend, const std::size_t buffer_size=65536, std::size_t hash_table_size=1024)
std::vector< item > items_storage
int(* get_backtrace_fn)(const void *, void **, int, void *)
std::vector< item * > table_storage
void handle_tick(void **data, int count)
void insert_hash(item *new_item)
const void * exec_context
void write(const char *data, std::size_t size)
void move_to_head(list_header *new_head)
static constexpr std::size_t max_frames
std::vector< char > outbuf_storage
void write(const item *item)
static bool traces_equal(const item *lhs, const item *rhs)
std::condition_variable timer_cond
std::vector< pthread_t > threads_to_notify
static profile_manager & instance()
void start(profile_data *data)
#define SYS_VM_ASSERT(expr, exc_type, msg)
void mul(const Operand &op)
memcpy((char *) pInfo->slotDescription, s, l)