Skip to content

Commit de2917b

Browse files
committed
app: tooltips for known slot values
Introduce an on-hover tooltip for known memory slots. It shows the value or it's change at the instruction at the cursor (not necessarily selected). Signed-off-by: Ihor Solodrai <ihor.solodrai@linux.dev>
1 parent f0d0ccc commit de2917b

File tree

2 files changed

+107
-26
lines changed

2 files changed

+107
-26
lines changed

app.ts

Lines changed: 83 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -221,11 +221,51 @@ const createApp = (url: string) => {
221221
updateView(state);
222222
};
223223

224+
const getTooltip = (): HTMLElement => {
225+
let tooltip = document.getElementById('mem-slot-tooltip');
226+
if (!tooltip) {
227+
tooltip = document.createElement('div');
228+
tooltip.id = 'mem-slot-tooltip';
229+
document.body.appendChild(tooltip);
230+
}
231+
return tooltip;
232+
}
233+
234+
const getTooltipArrow = (): HTMLElement => {
235+
let arrow = document.getElementById('mem-slot-tooltip-arrow');
236+
if (!arrow) {
237+
arrow = document.createElement('div');
238+
arrow.id = 'mem-slot-tooltip-arrow';
239+
document.body.appendChild(arrow);
240+
}
241+
return arrow;
242+
}
243+
224244
const handleMouseOver = (e: MouseEvent): void => {
225245
const hoveredElement = e.target as HTMLElement;
226246
const memSlot = hoveredElement.closest('.mem-slot');
227247
if (memSlot) {
248+
const tooltip = getTooltip();
249+
const arrow = getTooltipArrow();
250+
const idx = contentLineIdx(memSlot.parentElement);
251+
const displayValue = memSlotDisplayValue(state, memSlot.id, idx);
228252
memSlot.classList.add('hovered-mem-slot');
253+
if (displayValue) {
254+
// Text needs to be set first, so that position is calculated correctly
255+
tooltip.innerHTML = displayValue;
256+
tooltip.style.display = 'block';
257+
const rect = memSlot.getBoundingClientRect();
258+
const tooltipLeft = Math.max(0, rect.left - tooltip.offsetWidth / 2 + rect.width / 2);
259+
tooltip.style.left = `${tooltipLeft}px`;
260+
tooltip.style.top = `${rect.bottom + 5}px`;
261+
arrow.style.display = 'block';
262+
const arrowLeft = Math.max(0, rect.left + rect.width / 2);
263+
arrow.style.left = `${arrowLeft}px`;
264+
arrow.style.top = `${rect.bottom}px`;
265+
} else {
266+
tooltip.style.display = 'none';
267+
arrow.style.display = 'none';
268+
}
229269
}
230270
};
231271

@@ -234,6 +274,10 @@ const createApp = (url: string) => {
234274
const memSlot = hoveredElement.closest('.mem-slot');
235275
if (memSlot) {
236276
memSlot.classList.remove('hovered-mem-slot');
277+
const tooltip = getTooltip();
278+
const arrow = getTooltipArrow();
279+
tooltip.style.display = 'none';
280+
arrow.style.display = 'none';
237281
}
238282
};
239283

@@ -500,52 +544,65 @@ const createApp = (url: string) => {
500544
updateLineNumbers(state);
501545
}
502546

547+
const memSlotDisplayValue = (state: AppState, memSlotId: string, idx: number): string => {
548+
const { state: bpfState, idx: bpfStateIdx } = mostRecentBpfState(state, idx);
549+
const prevBpfState = mostRecentBpfState(state, bpfStateIdx - 1).state;
550+
const prevValue = prevBpfState.values.get(memSlotId);
551+
const value = bpfState.values.get(memSlotId);
552+
const ins = state.lines[idx].bpfIns;
553+
let content = '';
554+
switch (value?.effect) {
555+
case Effect.WRITE:
556+
case Effect.UPDATE:
557+
if (memSlotId == 'MEM') {
558+
// show the value of register that was stored
559+
const reg = ins?.alu?.src.id;
560+
if (reg) {
561+
const regValue = bpfState.values.get(reg);
562+
content = `${RIGHT_ARROW} ${regValue?.value}`;
563+
}
564+
break;
565+
}
566+
let newVal = value?.value;
567+
let oldVal = prevValue?.value || '';
568+
if (newVal === oldVal)
569+
content = newVal;
570+
else if (newVal)
571+
content = `${oldVal} ${RIGHT_ARROW} ${newVal}`;
572+
else
573+
content = `${oldVal} <span style="color:grey">-> scratched</span>`;
574+
break;
575+
case Effect.READ:
576+
case Effect.NONE:
577+
default:
578+
content = value?.value || '';
579+
break;
580+
}
581+
return content;
582+
}
583+
503584
const RIGHT_ARROW = '->';
504585

505586
const updateStatePanel = async (state: AppState): Promise<void> => {
506587
const { state: bpfState, idx } = mostRecentBpfState(state, state.selectedLineIdx);
507-
const ins = state.lines[idx].bpfIns;
508-
const prevBpfState = mostRecentBpfState(state, idx - 1).state;
509-
510588
const statePanel = document.getElementById('state-panel') as HTMLElement;
511589
const table = statePanel.querySelector('table');
512590
table.innerHTML = '';
513591

514592
const addRow = (id: string) => {
515-
const value = bpfState.values.get(id);
516-
const prevValue = prevBpfState.values.get(id);
593+
let content = memSlotDisplayValue(state, id, idx);
517594
const row = document.createElement('tr');
518-
let content = '';
595+
const value = bpfState.values.get(id);
519596
switch (value?.effect) {
520597
case Effect.WRITE:
521598
case Effect.UPDATE:
522599
row.classList.add('effect-write');
523-
if (id == 'MEM') {
524-
// show the value of register that was stored
525-
const reg = ins?.alu?.src.id;
526-
if (reg) {
527-
const regValue = bpfState.values.get(reg);
528-
content = `${regValue?.value} ${RIGHT_ARROW}`;
529-
}
530-
break;
531-
}
532-
533-
let newVal = value?.value;
534-
let oldVal = prevValue?.value || '';
535-
if (newVal === oldVal)
536-
content = newVal;
537-
else if (newVal)
538-
content = `${oldVal} ${RIGHT_ARROW} ${newVal}`;
539-
else
540-
content = `${oldVal} <span style="color:grey">-> scratched</span>`;
541600
break;
542601
case Effect.READ:
543602
row.classList.add('effect-read');
544-
content = value?.value || '';
545603
break;
546604
case Effect.NONE:
547605
default:
548-
content = value?.value || '';
549606
break;
550607
}
551608
const nameCell = document.createElement('td');

styles.css

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,4 +178,28 @@ body {
178178
justify-content: space-between;
179179
align-items: center;
180180
width: 100%;
181+
}
182+
183+
#mem-slot-tooltip {
184+
position: fixed;
185+
display: none;
186+
background-color: white;
187+
padding: 5px 10px;
188+
font-family: monospace;
189+
font-size: 12px;
190+
z-index: 1000;
191+
pointer-events: none;
192+
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
193+
border-style: solid;
194+
border-color: black;
195+
border-width: 1px;
196+
}
197+
198+
#mem-slot-tooltip-arrow {
199+
content: '';
200+
position: absolute;
201+
transform: translateX(-50%);
202+
border-width: 0 5px 5px 5px;
203+
border-style: solid;
204+
border-color: transparent transparent #333 transparent;
181205
}

0 commit comments

Comments
 (0)