3#include "pedigree/kernel/processor/Processor.h"
4#include "pedigree/kernel/processor/hosted/FunctionProfile.h"
24static_assert(
sizeof(
Event) == 32);
25constexpr size_t Capacity = 2 * 1024 * 1024;
26Event* events =
nullptr;
27const char* directory =
nullptr;
30thread_local bool owner =
false;
31bool previousInterrupts =
false;
32bool openCapture =
false;
35unsigned invalidation = 0;
36const char* phase =
nullptr;
43 clock_gettime(CLOCK_MONOTONIC, &ts);
44 return uint64_t(ts.tv_sec) * 1000000000ULL + ts.tv_nsec;
47void record(
void* function,
void* caller, uint64_t kind) {
48 if (!__atomic_load_n(&active, __ATOMIC_RELAXED) || !owner) {
51 if (eventCount == Capacity) {
58 asm volatile(
"lfence; rdtsc; lfence" :
"=a"(low),
"=d"(high) : :
"memory");
59 events[eventCount++] = {uint64_t(high) << 32 | low,
reinterpret_cast<uintptr_t
>(function),
60 reinterpret_cast<uintptr_t
>(caller), kind};
63bool writeEvents(
const char* path) {
64 int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600);
68 const char* data =
reinterpret_cast<const char*
>(events);
69 size_t remaining = eventCount *
sizeof(
Event);
72 const ssize_t written = write(fd, data, remaining);
73 if (written < 0 && errno == EINTR) {
83 return close(fd) == 0 && ok;
87extern "C" EXPORTED_PUBLIC
void __cyg_profile_func_enter(
void* function,
void* caller) {
88 record(function, caller, 1);
91extern "C" EXPORTED_PUBLIC
void __cyg_profile_func_exit(
void* function,
void* caller) {
92 record(function, caller, 2);
95bool hostedFunctionProfileInitialise() {
96 directory = getenv(
"PEDIGREE_HOSTED_FUNCTION_PROFILE_DIR");
97 if (!directory || !*directory) {
101 const char* value = getenv(
"PEDIGREE_HOSTED_FUNCTION_PROFILE_LIMIT");
104 const unsigned long requested = strtoul(value, &end, 10);
105 if (!*value || *end || !requested || requested > 10000) {
106 fprintf(stderr,
"Function profile limit must be between 1 and 10000.\n");
111 void* storage = mmap(
nullptr, Capacity *
sizeof(
Event), PROT_READ | PROT_WRITE,
112 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
113 if (storage == MAP_FAILED) {
114 perror(
"Function profile buffer");
117 events =
static_cast<Event*
>(storage);
120 volatile char* bytes =
static_cast<volatile char*
>(storage);
121 for (
size_t offset = 0; offset < Capacity *
sizeof(
Event); offset += 4096) {
127size_t hostedFunctionProfileCount(
size_t count) {
128 return directory && count > limit ? limit : count;
131void hostedFunctionProfileBegin(
const char* name,
size_t rep,
size_t count) {
139 repetition = rep + 1;
141 eventCount = dropped = invalidation = 0;
144 __atomic_store_n(&active,
true, __ATOMIC_RELEASE);
147void hostedFunctionProfileInvalidate(HostedProfileInvalidation reason) {
148 if (__atomic_load_n(&active, __ATOMIC_RELAXED) && owner) {
149 invalidation =
static_cast<unsigned>(reason);
151 __atomic_store_n(&active,
false, __ATOMIC_RELEASE);
155bool hostedFunctionProfileEnd() {
156 if (!directory || !openCapture) {
159 __atomic_store_n(&active,
false, __ATOMIC_RELEASE);
160 const uint64_t elapsed = nowNs() - startNs;
166 int length = snprintf(path,
sizeof(path),
"%s/%s-%zu.bin", directory, phase, repetition);
167 bool saved = length > 0 && size_t(length) <
sizeof(path) && writeEvents(path);
168 length = snprintf(path,
sizeof(path),
"%s/%s-%zu.json", directory, phase, repetition);
169 FILE* metadata = length > 0 && size_t(length) <
sizeof(path) ? fopen(path,
"wx") :
nullptr;
173 "{\"format_version\":1,\"phase\":\"%s\",\"repetition\":%zu,\"count\":%zu,"
174 "\"events\":%zu,\"dropped\":%zu,\"invalidation\":%u,\"elapsed_ns\":%llu}\n",
175 phase, repetition, iterations, eventCount, dropped, invalidation,
176 static_cast<unsigned long long>(elapsed));
177 const int closed = fclose(metadata);
178 saved = saved && written > 0 && closed == 0;
183 "HOSTED-FUNCTION-PROFILE: %s phase=%s rep=%zu count=%zu events=%zu "
184 "dropped=%zu invalidation=%u\n",
185 saved && !dropped && !invalidation ?
"PASS" :
"FAIL", phase, repetition, iterations,
186 eventCount, dropped, invalidation);
187 return saved && !dropped && !invalidation;
static bool getInterrupts()
static void setInterrupts(bool bEnable)