From d1855e8042475d1e25179a6a3fd86d383f17d0e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Loi=CC=88c=20Correnson?= <loic.correnson@cea.fr> Date: Thu, 17 Dec 2020 11:51:44 +0100 Subject: [PATCH] [ivette/eva] callstacks cache & selection --- ivette/src/frama-c/eva/Values.tsx | 32 ++++++++++++---- ivette/src/frama-c/eva/cells.ts | 6 ++- ivette/src/frama-c/eva/model.ts | 27 +++++++------ ivette/src/frama-c/eva/probes.ts | 8 ++++ ivette/src/frama-c/eva/stacks.ts | 64 +++++++++++++++++++++++++++++++ ivette/src/frama-c/eva/style.css | 9 +++-- 6 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 ivette/src/frama-c/eva/stacks.ts diff --git a/ivette/src/frama-c/eva/Values.tsx b/ivette/src/frama-c/eva/Values.tsx index c2580b78314..986e41886b3 100644 --- a/ivette/src/frama-c/eva/Values.tsx +++ b/ivette/src/frama-c/eva/Values.tsx @@ -50,23 +50,35 @@ function ProbeEditor() { const label = probe?.label; const code = probe?.code; const rank = probe?.rank; + const byCS = probe?.byCallstacks; const stmt = rank ? `@S${rank}` : undefined; + const stacks = model.getStacks(probe).length; const { cols, rows } = sizeof(code); + const width = WSIZER.dimension(cols) + 4; + const height = HSIZER.dimension(rows) + 3; const visible = probe ? !!code : model.getRowCount() > 0; const visibility = visible ? 'visible' : 'hidden'; return ( <Hpack style={{ visibility }} className="eva-probe"> <Label className="eva-probe-label">{label && `${label}:`}</Label> - <div className="eva-probe-code"> + <div style={{ width, height }} className="eva-probe-code"> <SizedArea cols={cols} rows={rows}>{code}</SizedArea> </div> <Code className="eva-probe-stmt">{stmt}</Code> + <IconButton + className="eva-probe-button" + visible={byCS || stacks > 0} + selected={byCS} + icon="ITEMS.LIST" + title={`Details by callstack (${stacks})`} + onClick={() => { if (probe) probe.setByCallstacks(!byCS); }} + /> <IconButton className="eva-probe-button" kind={transient ? 'selected' : 'warning'} icon={transient ? 'CIRC.CHECK' : 'CIRC.CLOSE'} - onClick={() => { if (probe) probe.setTransient(!transient); }} title={transient ? 'Make the probe persistent' : 'Release the probe'} + onClick={() => { if (probe) probe.setTransient(!transient); }} /> <Filler /> </Hpack> @@ -93,22 +105,26 @@ function TableCell(props: TableCellProps) { const style = { width: minWidth, maxWidth }; let styling = 'dome-text-code'; let contents: React.ReactNode = props.probe.marker; - const { transient, label, code } = probe; + const { transient } = probe; switch (kind) { case 'probes': if (transient) { styling = 'dome-text-label'; contents = '« Probe »'; - } else if (label) { - styling = 'dome-text-label'; - contents = label; } else { - contents = <>{code}</>; + const { rank, code, label } = probe; + const atpoint = rank && ( + <span className='dome-text-code eva-probe-stmt'>@S{probe.rank}</span> + ); + styling = 'dome-text-label'; + contents = ( + <>{label ?? code}{atpoint}</> + ); } break; case 'values': { - const { values } = model.cache.getValues(probe.marker); + const { values } = model.values.getValues(probe.marker); const { cols, rows } = sizeof(values); contents = ( <SizedArea cols={cols} rows={rows}> diff --git a/ivette/src/frama-c/eva/cells.ts b/ivette/src/frama-c/eva/cells.ts index 95a2dd5e3c2..eb3b2b8e225 100644 --- a/ivette/src/frama-c/eva/cells.ts +++ b/ivette/src/frama-c/eva/cells.ts @@ -65,7 +65,7 @@ export function leq(a: Size, b: Size): boolean { } // -------------------------------------------------------------------------- -// --- Value Cache +// --- Data // -------------------------------------------------------------------------- export type EvaStatus = 'True' | 'False' | 'Unknown'; @@ -81,6 +81,10 @@ export interface EvaValues { size: Size; } +// -------------------------------------------------------------------------- +// --- Value Cache +// -------------------------------------------------------------------------- + export class ValueCache { private readonly state: StateCallbacks; diff --git a/ivette/src/frama-c/eva/model.ts b/ivette/src/frama-c/eva/model.ts index a376ea82fac..7f749f28ed4 100644 --- a/ivette/src/frama-c/eva/model.ts +++ b/ivette/src/frama-c/eva/model.ts @@ -13,6 +13,7 @@ import * as Ast from 'frama-c/api/kernel/ast'; // Model import { Probe } from './probes'; +import { StacksCache } from './stacks'; import { callback, StateCallbacks, ValueCache } from './cells'; import { LayoutProps, LayoutEngine, Row } from './layout'; @@ -59,9 +60,15 @@ export class Model implements StateCallbacks { return p; } - // --- Values + getStacks(p: Probe | undefined): Values.callstack[] { + const stmt = p?.stmt; + return stmt ? this.stacks.getStacks(stmt) : []; + } + + // --- Caches - readonly cache = new ValueCache(this); + readonly stacks = new StacksCache(this); + readonly values = new ValueCache(this); // --- Rows @@ -91,7 +98,7 @@ export class Model implements StateCallbacks { toLayout.push(p); } }); - const engine = new LayoutEngine(this.cache, this.layout); + const engine = new LayoutEngine(this.values, this.layout); toLayout.sort(Probe.order).forEach(engine.push); this.rows = engine.flush(); this.forceUpdate(); @@ -128,14 +135,12 @@ export class Model implements StateCallbacks { // --- Force Reload (empty caches) forceReload() { - this.probes.forEach((p) => { - if (p.transient && p !== this.focused) { - this.probes.delete(p.marker); - } else { - p.requestProbeInfo(); - } - }); - this.cache.clear(); + this.focused = undefined; + this.remanent = undefined; + this.selected = undefined; + this.probes.clear(); + this.stacks.clear(); + this.values.clear(); this.forceLayout(); } diff --git a/ivette/src/frama-c/eva/probes.ts b/ivette/src/frama-c/eva/probes.ts index 3b121c23f0f..be73a91aa12 100644 --- a/ivette/src/frama-c/eva/probes.ts +++ b/ivette/src/frama-c/eva/probes.ts @@ -53,6 +53,7 @@ export class Probe { rank?: number; minCols: number = LabelSize; maxCols: number = LabelSize; + byCallstacks = false; constructor(state: StateCallbacks, marker: Ast.marker) { this.marker = marker; @@ -98,6 +99,13 @@ export class Probe { } } + setByCallstacks(byCS: boolean) { + if (byCS !== this.byCallstacks) { + this.byCallstacks = byCS; + this.state.forceLayout(); + } + } + // -------------------------------------------------------------------------- // --- Ordering // -------------------------------------------------------------------------- diff --git a/ivette/src/frama-c/eva/stacks.ts b/ivette/src/frama-c/eva/stacks.ts new file mode 100644 index 00000000000..adbf6bd605b --- /dev/null +++ b/ivette/src/frama-c/eva/stacks.ts @@ -0,0 +1,64 @@ +// -------------------------------------------------------------------------- +// --- CallStacks +// -------------------------------------------------------------------------- + +import * as Server from 'frama-c/server'; +import * as Values from 'frama-c/api/plugins/eva/values'; + +import { StateCallbacks } from './cells'; + +// -------------------------------------------------------------------------- +// --- Callstack infos +// -------------------------------------------------------------------------- + +export type callstacks = Values.callstack[]; + +// -------------------------------------------------------------------------- +// --- CallStacks Cache +// -------------------------------------------------------------------------- + +export class StacksCache { + + private readonly state: StateCallbacks; + private readonly stacks = new Map<string, callstacks>(); + + // -------------------------------------------------------------------------- + // --- LifeCycle + // -------------------------------------------------------------------------- + + constructor(state: StateCallbacks) { + this.state = state; + } + + clear() { + this.stacks.clear(); + } + + // -------------------------------------------------------------------------- + // --- Getters + // -------------------------------------------------------------------------- + + getStacks(stmt: string): callstacks { + const cs = this.stacks.get(stmt); + if (cs !== undefined) return cs; + this.stacks.set(stmt, []); + this.requestCallstacks(stmt); + return []; + } + + // -------------------------------------------------------------------------- + // --- Fetchers + // -------------------------------------------------------------------------- + + private requestCallstacks(stmt: string) { + Server + .send(Values.getCallstacks, stmt) + .then((cs: callstacks) => { + this.stacks.set(stmt, cs); + this.state.forceLayout(); + }); + } + +} + +// -------------------------------------------------------------------------- diff --git a/ivette/src/frama-c/eva/style.css b/ivette/src/frama-c/eva/style.css index 24226a9b2e2..7afd3a1d6c7 100644 --- a/ivette/src/frama-c/eva/style.css +++ b/ivette/src/frama-c/eva/style.css @@ -14,7 +14,7 @@ /* -------------------------------------------------------------------------- */ .eva-probe { - margin: 4px; + margin: 8px; width: 100%; display: flex; } @@ -40,13 +40,14 @@ .eva-probe-stmt { flex: 0 0 auto; color: grey; - margin-left: 3px; - margin-right: 3px; - margin-top: 3px; + margin-left: 2px; + margin-right: 0px; + margin-top: 2px; } .eva-probe-button { flex: 0 0 auto; + margin: 1px; min-width: 16px; } -- GitLab