33 struct timespec ts = {};
34 (void)clock_gettime(CLOCK_MONOTONIC, &ts);
116 const uint32_t la = ((
const prof_sym_t*)a)->lo;
117 const uint32_t lb = ((
const prof_sym_t*)b)->lo;
152 const uint32_t lo = symbol->
value & ~1U;
162 const char* mode = getenv(
"RA8_EMU_PROFILE");
163 if (mode ==
nullptr) {
175 :
"wall-time sample",
195 const uint32_t mid = lo + ((hi - lo) / 2U);
196 if (
s_prof[mid].lo <= pc) {
205 const uint32_t idx = lo - 1U;
233 for (uint32_t i = 0U; i <
s_samp_n; i += 2U) {
264 for (uint32_t i = 0U; i < d; i++) {
289 for (uint32_t i =
s_pstk_n; i > 0U; i--) {
290 if (
s_pstk[i - 1U] == (uint16_t)f) {
322 if ((uint32_t)address ==
s_prof[idx].lo) {
334 (void)uc_emu_stop(uc);
388 for (
size_t index = 0U; (index < length) && sink->
ok; index++) {
389 if ((sink->
escape !=
'\0') && (bytes[index] == sink->
escape || bytes[index] ==
'\\')) {
421 prof_name_sink_t sink = {.fd = fd, .escape = escape, .error_sink = error_sink, .ok =
true};
423 s_prof[symbol].name_offset,
446 for (uint32_t i = 0U; i <
s_prof_n; i++) {
476 for (uint32_t i = 0U; i <
s_samp_n; i++) {
481 for (uint8_t j = 0U; j <
s_samp_d[i]; j++) {
511 for (uint32_t i = 0U; i <
s_samp_n; i++) {
542 for (uint32_t i = 0U; i <
s_prof_n; i++) {
569 for (uint32_t i = 0U; i <
s_samp_n; i++) {
574 "{\"$schema\":\"https://www.speedscope.app/file-format-schema.json\",\n"
575 " \"name\":\"ra8_emulator boot\",\"activeProfileIndex\":0,\n"
576 " \"shared\":{\"frames\":["));
581 " \"profiles\":[{\"type\":\"sampled\",\"name\":\"boot\",\"unit\":\"none\",\n"
582 " \"startValue\":0,\"endValue\":%llu,\n"
584 (
unsigned long long)total)) &&
601 "<!doctype html><html><head><meta charset='utf-8'><title>ra8_emulator profile</title>\n"
603 "body{margin:0;font:12px Menlo,monospace;background:#1e1e1e;color:#ddd}\n"
604 "#bar{padding:7px 10px;background:#2a2a2a;border-bottom:1px solid #444}\n"
605 "#bar b{color:#fff}#bar button,#bar input{font:11px monospace;margin-left:10px;"
606 "background:#3a3a3a;color:#ddd;border:1px solid #555;padding:2px 6px}\n"
607 "#tip{position:fixed;pointer-events:none;background:#000;color:#fff;padding:5px 7px;"
608 "border:1px solid #888;display:none;white-space:nowrap;z-index:9;font:11px monospace}\n"
609 "canvas{display:block;cursor:crosshair}\n"
610 "</style></head><body>\n"
611 "<div id='bar'><b id='title'></b><span id='info'></span>"
612 "<button onclick='resetView()'>Reset zoom</button>"
613 "search:<input id='q' size='18' oninput='onSearch()'></div>\n"
614 "<canvas id='fc'></canvas><div id='tip'></div>\n<script>\n";
617 "var cv=document.getElementById('fc'),ctx=cv.getContext('2d'),tip=document.getElementById('tip');\n"
618 "document.getElementById('title').textContent=TITLE;\n"
619 "var ROW=18,total=0,i;for(i=0;i<WEIGHTS.length;i++)total+=WEIGHTS[i];\n"
620 "var maxd=0;for(i=0;i<SAMPLES.length;i++)if(SAMPLES[i].length>maxd)maxd=SAMPLES[i].length;\n"
621 "var rects=[],incl={},self={};\n"
622 "for(var d=0;d<maxd;d++){var cum=0,rs=0,rf=-1,op=false;\n"
623 " for(i=0;i<SAMPLES.length;i++){var ff=d<SAMPLES[i].length?SAMPLES[i][d]:-1;\n"
624 " if(!(op&&ff===rf)){if(op&&rf>=0)rects.push({d:d,a:rs,b:cum,f:rf});rs=cum;rf=ff;op=true;}\n"
625 " cum+=WEIGHTS[i];}\n"
626 " if(op&&rf>=0)rects.push({d:d,a:rs,b:cum,f:rf});}\n"
627 "for(i=0;i<SAMPLES.length;i++){var s=SAMPLES[i],w=WEIGHTS[i];\n"
628 " for(var j=0;j<s.length;j++)incl[s[j]]=(incl[s[j]]||0)+w;\n"
629 " if(s.length)self[s[s.length-1]]=(self[s[s.length-1]]||0)+w;}\n"
630 "var vx0=0,vx1=total,q='';\n"
631 "function col(fi){var n=FRAMES[fi],h=0,k;for(k=0;k<n.length;k++)h=(h*31+n.charCodeAt(k))&0xffffff;\n"
632 " var lit=(q&&n.toLowerCase().indexOf(q)>=0);return 'hsl('+(h%359)+','+(lit?'90%':'48%')+','+(lit?'62%':'44%')+')';}\n"
633 "function resize(){cv.width=window.innerWidth;cv.height=Math.max(maxd*ROW+4,160);draw();}\n"
634 "function draw(){ctx.clearRect(0,0,cv.width,cv.height);var span=vx1-vx0;if(span<=0)return;\n"
635 " ctx.font='11px monospace';ctx.textBaseline='middle';\n"
636 " for(var r=0;r<rects.length;r++){var R=rects[r];if(R.b<=vx0||R.a>=vx1)continue;\n"
637 " var p0=(R.a-vx0)/span*cv.width,p1=(R.b-vx0)/span*cv.width,w=p1-p0;if(w<0.4)continue;\n"
638 " var y=R.d*ROW;ctx.fillStyle=col(R.f);ctx.fillRect(p0,y,Math.max(w-0.6,0.5),ROW-1);\n"
639 " if(w>34){ctx.fillStyle='#111';ctx.fillText(FRAMES[R.f],p0+3,y+ROW/2,w-6);}}}\n"
640 "function pick(mx,my){var d=Math.floor(my/ROW),span=vx1-vx0,wx=vx0+mx/cv.width*span,r;\n"
641 " for(r=0;r<rects.length;r++){var R=rects[r];if(R.d===d&&wx>=R.a&&wx<R.b)return R;}return null;}\n"
642 "cv.onmousemove=function(e){var R=pick(e.offsetX,e.offsetY);if(!R){tip.style.display='none';return;}\n"
643 " var n=FRAMES[R.f],to=incl[R.f]||0,se=self[R.f]||0;\n"
644 " tip.innerHTML=n+'<br>this block: '+((R.b-R.a)/total*100).toFixed(2)+'% ('+(R.b-R.a)+' insns)'+\n"
645 " '<br>total '+(to/total*100).toFixed(2)+'% self '+(se/total*100).toFixed(2)+'%';\n"
646 " tip.style.display='block';tip.style.left=(e.clientX+14)+'priv_px';tip.style.top=(e.clientY+14)+'priv_px';};\n"
647 "cv.onmouseleave=function(){tip.style.display='none';};\n"
648 "cv.onclick=function(e){var R=pick(e.offsetX,e.offsetY);if(R){vx0=R.a;vx1=R.b;draw();}};\n"
649 "function resetView(){vx0=0;vx1=total;draw();}\n"
650 "function onSearch(){q=document.getElementById('q').value.toLowerCase();draw();}\n"
651 "document.getElementById('info').textContent=' | '+SAMPLES.length+' samples, '+total+\n"
652 " ' insns (hover for self/total, click a block to zoom, Reset to zoom out)';\n"
653 "window.onresize=resize;resize();\n";
686 "];\nvar TITLE='ra8_emulator flamechart -- %llu insns, %u samples';\n",
687 (
unsigned long long)total,
701typedef enum : uint32_t {
724 for (uint32_t i = 0U; i <
s_prof_n; i++) {
728 for (uint32_t i = 0U; i <
s_samp_n; i++) {
732 for (uint8_t j = 0U; j < d; j++) {
785 (void)
priv_emu_io_errf(
" [profile] boot timeline (phase = call depth %u; start%% .. width%%):\n",
789 uint32_t segfn = (uint32_t)
k_no_fn;
791 for (uint32_t i = 0U; i <=
s_samp_n; i++) {
792 uint32_t fn = (uint32_t)
k_no_fn;
796 const uint8_t pd = ((uint32_t)(d - 1U) < (uint32_t)
k_phase_depth) ? (uint8_t)(d - 1U)
801 if ((i ==
s_samp_n) || (fn != segfn)) {
802 const bool show = (segfn != (uint32_t)
k_no_fn) &&
834 (void)
priv_emu_io_errf(
" [profile] flamechart GUI -> %s (interactive: hover/zoom/search)\n"
835 " [profile] %u samples over %llu insns (also %s for speedscope.app)\n"
836 " self%% total%% function\n",
839 (
unsigned long long)total,
841 for (uint32_t k = 0U; k < (uint32_t)
k_prof_top_n; k++) {
844 for (uint32_t i = 0U; i <
s_prof_n; i++) {
850 if ((best ==
s_prof_n) || (bestv == 0U)) {
872 const char* out = getenv(
"RA8_EMU_PROFILE_OUT");
873 if ((out ==
nullptr) || (out[0] ==
'\0')) {
874 out =
"ra8_emulator_profile.speedscope.json";
876 const char* html = getenv(
"RA8_EMU_PROFILE_HTML");
877 if ((html ==
nullptr) || (html[0] ==
'\0')) {
878 html =
"ra8_emulator_profile.html";
900 (void)
priv_emu_io_errf(
" [profile] %llu instructions; hottest (by instruction count):\n"
901 " %%insn instructions calls function\n",
904 (void)
priv_emu_io_errf(
" [profile] %.2fs wall sampled; hottest (by wall time):\n", tot);
906 for (uint32_t k = 0U; k < (uint32_t)
k_prof_top_n; k++) {
909 for (uint32_t i = 0U; i <
s_prof_n; i++) {
910 const double v = insn ? (double)
s_prof[i].insns :
s_prof[i].secs;
916 if ((best ==
s_prof_n) || (bestv <= 0.0)) {
922 (
unsigned long long)
s_prof[best].insns,
923 (
unsigned long long)
s_prof[best].calls);
943 static uc_hook s_h_prof;
ELF32 image services for the board emulator (load / symbols / vectors).
bool elf_string_foreach(const emu_elf_source_t *elf, uint64_t offset, emu_elf_string_fn fn, void *ctx)
Stream one NUL-terminated ELF string through bounded stack chunks.
uint32_t elf_foreach_symbol(const emu_elf_source_t *elf, emu_elf_symbol_fn fn, void *ctx)
Walk every symbol in every usable SHT_SYMTAB section.
@ k_elf_st_type_mask
Low nibble of st_info is the type.
Bounded raw-descriptor I/O seam for the RA8 emulator.
emu_io_result_t priv_emu_io_err_text(const char *text)
Write a NUL-terminated literal to the injected error descriptor.
@ k_emu_io_ok
The complete operation succeeded.
emu_io_result_t priv_emu_io_errf(const char *format,...)
Format bounded text and write it to the injected error descriptor.
emu_io_result_t priv_emu_io_filef(int fd, const char *format,...)
Format bounded text and write it to an explicit raw descriptor.
emu_io_result_t priv_emu_io_err_bytes(const void *bytes, size_t length)
Write an exact non-NUL-terminated byte fragment to the error sink.
void priv_emu_io_txn_abort(emu_io_txn_t *txn)
Close and unlink an active sibling transaction without publication.
emu_io_result_t priv_emu_io_file_char(int fd, char value)
Write one byte to an explicit raw descriptor.
emu_io_result_t priv_emu_io_txn_commit(emu_io_txn_t *txn)
Sync, close, and atomically rename an active sibling transaction.
emu_io_result_t priv_emu_io_file_text(int fd, const char *text)
Write a NUL-terminated literal to an explicit raw descriptor.
emu_io_result_t priv_emu_io_txn_begin(const char *path, emu_io_txn_t *txn)
Create a sibling temporary output for failure-atomic publication.
@ k_prof_max_samples
Chronological stack samples (decimated).
@ k_prof_max_depth
Deepest call chain captured per sample.
@ k_prof_samp_every
Default instructions per chain sample.
static bool internal_prof_json_weights(int fd)
Write the speedscope weights array, parallel to the samples array.
static uint32_t s_samp_w[k_prof_max_samples]
Per-sample weight (insns).
static int internal_prof_cmp(const void *a, const void *b)
qsort comparator: order the FUNC symbols by entry address.
static uint16_t s_pstk[k_prof_max_depth]
Live chain.
prof_mode_t emu_prof_mode(void)
Implementation of emu_prof_mode() – plain state read.
void emu_prof_set_stop_pc(uint32_t pc)
Implementation of emu_prof_set_stop_pc() – plain state write.
static uint32_t internal_prof_find(uint32_t pc)
Binary-search the FUNC symbol owning pc; returns s_prof_n if none.
static const char s_k_prof_html_head[]
static bool internal_prof_js_frames(int fd)
Write the flamechart page's FRAMES array as JavaScript string literals.
void prof_add(uint32_t pc, double dt)
Attribute dt wall seconds to pc's function (wall-sample mode).
static const emu_elf_source_t * s_prof_elf
Borrowed run-long source for names.
static uint64_t s_samp_acc
Insns since last sample.
static void internal_prof_write_html(const char *path, uint64_t total)
Write a self-contained, locally-openable HTML flamechart of the samples.
bool emu_prof_stop_hit(void)
Implementation of emu_prof_stop_hit() – plain flag read.
static uint32_t s_pstk_n
Chain depth.
static uint32_t s_samp_n
Stored sample count.
void emu_prof_install(uc_engine *uc)
Implementation of emu_prof_install() – arms the insn hook in insn mode.
static uint64_t internal_prof_accumulate_incl_self(void)
Reset then fill s_incl/s_self from the samples; return total weight.
static void internal_prof_write_speedscope(const char *path)
Perform prof write speedscope for the emu prof model.
static prof_mode_t s_prof_mode
static void internal_prof_report_flamechart(void)
Speedscope export + inclusive/self breakdown + phase timeline (insn mode).
static bool internal_prof_json_frames(int fd)
Write the speedscope shared.frames array: one entry per symbol.
static const double s_nsec_per_sec
Nanoseconds per second (timespec tv_nsec -> seconds).
static uint32_t s_prof_stop_pc
RA8_EMU_STOP_PC (0=off).
static bool internal_prof_name(uint32_t symbol, int fd, char escape, bool error_sink)
Stream one retained-offset symbol name to a file or error sink.
static void internal_prof_print_phase(uint32_t segfn, uint64_t cum, uint64_t segw, uint64_t total)
Print one boot-timeline row: start percent, width percent, and phase.
static void internal_prof_sample(uint32_t weight)
Append the live call chain as one chronological sample of weight insns.
static bool internal_prof_symbol(const emu_elf_symbol_t *symbol, void *ctx)
Collect one sized STT_FUNC symbol into the bounded profiler table.
void prof_load(const emu_elf_source_t *elf)
Collect + sort FUNC symbols (RA8_EMU_PROFILE only) for PC bucketing.
static bool internal_prof_name_chunk(const char *bytes, size_t length, void *opaque)
Emit one transient source-name chunk to a selected sink.
static const char s_k_prof_html_js[]
static uint64_t s_prof_total_i
static bool internal_prof_json_samples(int fd)
Write the speedscope samples array: one frame-index stack per sample.
void prof_report(void)
Print the top hot functions (by wall time or instruction count) at run end.
prof_phase_t
Boot-timeline "phase" collapse constants for internal_prof_print_boot_timeline.
@ k_phase_lines
Cap on printed timeline segments.
@ k_no_fn
Sentinel for "no phase frame".
@ k_phase_depth
Chain depth used as the boot "phase".
@ k_phase_pct_x1
Per-cent base: keep segments >= 1%.
static bool internal_prof_io_ok(emu_io_result_t result)
Convert a host-I/O result to the profiler writer's boolean accumulator.
static void internal_prof_stack_update(uint32_t f)
Fold f (PC's owning FUNC index) into the live call chain (push/pop).
static void internal_prof_decimate(void)
Halve the sample store (merge adjacent pairs) when it fills up.
static const double s_percent_scale
Fraction-to-per-cent scale (fraction * 100.0 == per-cent).
double board_now_s(void)
Monotonic wall-clock seconds.
static double s_prof_total_s
static uint8_t s_samp_d[k_prof_max_samples]
Per-sample chain depth.
static bool s_prof_stop_hit
Set when STOP_PC reached.
static void internal_prof_insn_hook(uc_engine *uc, uint64_t address, uint32_t size, void *user)
Perform prof insn hook for the emu prof model.
static void internal_prof_print_incl_self_table(const char *html, const char *out, uint64_t total)
Print the inclusive/self table (sorted by inclusive weight).
static uint64_t s_samp_every
Insns per sample (>>x2).
static prof_sym_t s_prof[k_prof_max_syms]
static uint64_t s_self[k_prof_max_syms]
Self (leaf) weight.
static void internal_prof_print_boot_timeline(uint64_t total)
Print the boot timeline: each contiguous shallow-depth phase run.
@ k_prof_max_syms
Cap on profiled FUNC symbols.
@ k_prof_top_n
Top entries printed in the report.
static uint16_t s_samp[k_prof_max_samples][k_prof_max_depth]
root..leaf.
uint64_t emu_prof_total_insns(void)
Implementation of emu_prof_total_insns() – plain counter read.
static uint64_t s_incl[k_prof_max_syms]
Inclusive weight (report).
Firmware profiler (RA8_EMU_PROFILE): sampling, hooks, reports.
prof_mode_t
Profiler mode parsed from RA8_EMU_PROFILE.
@ k_prof_insn
=full/=insn: exact per-instruction + calls.
@ k_prof_wall
=1: cheap chunk-start wall-time sampler.
@ k_prof_off
Disabled (no env, zero cost).
#define RA8_INTERNAL
Marker that a function is intended to be static (file-local).
int strcmp(const char *s1, const char *s2)
Compare two null-terminated strings.
void * memcpy(void *dst, const void *src, size_t n)
Copy memory area between non-overlapping regions.
One independently owned immutable raw-descriptor ELF source.
One bounds-checked symbol-table entry plus its string offset.
uint64_t name_offset
Absolute source offset of the NUL-terminated name.
uint32_t size
Symbol byte extent.
uint32_t value
Symbol value.
uint8_t info
ELF st_info byte.
Complete, caller-visible result of a raw host-I/O operation.
emu_io_status_t status
Semantic completion status.
Sibling temporary file used for failure-atomic publication.
int fd
Owned temporary descriptor, or -1 when inactive.
Sink configuration for one streamed profiler symbol name.
int fd
Explicit file descriptor, ignored for error sink.
bool error_sink
Route bytes to the injected error sink.
char escape
Character escaped with backslash, or NUL.
bool ok
Sticky exact-output success.
uint32_t hi
Function end (lo + st_size).
uint64_t insns
Instructions executed (insn mode).
uint64_t calls
Entries to this fn (insn mode).
uint32_t lo
Function entry (Thumb bit cleared).
double secs
Wall seconds (wall mode).
uint64_t name_offset
Source offset of the symbol name.