diff --git a/ivette/src/frama-c/plugins/eva/layout.ts b/ivette/src/frama-c/plugins/eva/layout.ts index 3d887e9b618a5b4ab9ce7ff6b53b18fb59bd844d..732060b775c3feefd2c63eb84a560882e08fc274 100644 --- a/ivette/src/frama-c/plugins/eva/layout.ts +++ b/ivette/src/frama-c/plugins/eva/layout.ts @@ -104,8 +104,9 @@ export class LayoutEngine { }; } - layout(ps: Probe[]): Row[] { + layout(ps: Probe[], byCallstacks: boolean): Row[] { this.chained = undefined; + this.byStk = byCallstacks; ps.sort(LayoutEngine.order).forEach(this.push); return this.flush(); } @@ -114,23 +115,19 @@ export class LayoutEngine { const fp = p.fct; const fq = q.fct; if (fp === fq) { - const cp = p.byCallstacks; - const cq = q.byCallstacks; - if (!cp && cq) return (-1); - if (cp && !cq) return (+1); + const rp = p.rank ?? 0; + const rq = q.rank ?? 0; + if (rp < rq) return (-1); + if (rp > rq) return (+1); + if (p.marker < q.marker) return (-1); + if (p.marker > q.marker) return (+1); } - const rp = p.rank ?? 0; - const rq = q.rank ?? 0; - if (rp < rq) return (-1); - if (rp > rq) return (+1); - if (p.marker < q.marker) return (-1); - if (p.marker > q.marker) return (+1); return 0; } private push(p: Probe) { // --- sectionning - const { fct, byCallstacks: stk } = p; + const { fct } = p; if (fct !== this.byFct) { this.flush(); this.rows.push({ @@ -141,12 +138,10 @@ export class LayoutEngine { hlines: 1, }); this.byFct = fct; - this.byStk = stk; + this.csRowsCounter = 1; this.skip = this.folded(fct); - } else if (stk !== this.byStk) { - this.flush(); - this.byStk = stk; } + else {} if (this.skip) return; // --- chaining const q = this.chained; @@ -160,7 +155,7 @@ export class LayoutEngine { p.minCols = s.cols; p.maxCols = Math.max(p.minCols, probeSize.cols); // --- queueing - if (!stk && s.cols + this.rowSize.cols > this.margin) + if (s.cols + this.rowSize.cols > this.margin) this.flush(); this.rowSize = addH(this.rowSize, s); this.rowSize.cols += CELL_PADDING; @@ -169,6 +164,8 @@ export class LayoutEngine { // --- Flush Rows + private csRowsCounter = 1; + private flush(): Row[] { const ps = this.buffer; const rs = this.rows; @@ -182,8 +179,9 @@ export class LayoutEngine { const stacks = this.stacks.getStacks(...markers); const summary = fct ? this.stacks.getSummary(fct) : false; const callstacks = stacks.length; + const csRowsCounter = this.csRowsCounter; rs.push({ - key: `P:${fct}`, + key: `P:${fct}:${csRowsCounter}`, kind: 'probes', probes: ps, stackCount: callstacks, @@ -191,7 +189,7 @@ export class LayoutEngine { hlines: 1, }); if (summary) rs.push({ - key: `V:${fct}`, + key: `V:${fct}:${csRowsCounter}`, kind: 'values', probes: ps, stackIndex: -1, @@ -201,7 +199,7 @@ export class LayoutEngine { }); stacks.forEach((cs, k) => { rs.push({ - key: `C:${fct}:${cs}`, + key: `C:${fct}:${csRowsCounter}:${cs}`, kind: 'callstack', probes: ps, stackIndex: k, @@ -231,6 +229,7 @@ export class LayoutEngine { } this.buffer = []; this.rowSize = EMPTY; + this.csRowsCounter++; return rs; } diff --git a/ivette/src/frama-c/plugins/eva/model.ts b/ivette/src/frama-c/plugins/eva/model.ts index a1f679d807a48d5f6dd91740d3f281df29e5b97e..dfc39fb2693eac88759d3add18ea5f220d82ccb2 100644 --- a/ivette/src/frama-c/plugins/eva/model.ts +++ b/ivette/src/frama-c/plugins/eva/model.ts @@ -72,6 +72,7 @@ export class Model implements ModelCallbacks { private remanent?: Probe; // last transient private probes = new Map<string, Probe>(); private folded = new Map<string, boolean>(); // folded functions + private byCallstacks = false; getFocused() { return this.focused; } isFocused(p: Probe | undefined) { return this.focused === p; } @@ -113,6 +114,15 @@ export class Model implements ModelCallbacks { this.forceLayout(); } + getByCallstacks(): boolean { + return this.byCallstacks; + } + + setByCallstacks(b: boolean) { + this.byCallstacks = b; + this.forceLayout(); + } + // --- Caches readonly stacks = new StacksCache(this); @@ -151,7 +161,7 @@ export class Model implements ModelCallbacks { } isSelectedRow(row: Row): boolean { - if (!this.focused?.byCallstacks) return false; + if (!this.byCallstacks) return false; const cs = this.callstack; return cs !== undefined && cs === row.callstack; } @@ -184,9 +194,7 @@ export class Model implements ModelCallbacks { const p = this.getProbe(location); if (p) { if (p.transient) { - if (this.focused?.byCallstacks) - p.setByCallstacks(true); - else + if (!this.byCallstacks) p.setPersistent(); } } @@ -228,7 +236,7 @@ export class Model implements ModelCallbacks { this.stacks, this.isFolded, ); - this.rows = engine.layout(toLayout); + this.rows = engine.layout(toLayout, this.byCallstacks); this.laidout.emit(); this.lock = false; } diff --git a/ivette/src/frama-c/plugins/eva/probeinfos.tsx b/ivette/src/frama-c/plugins/eva/probeinfos.tsx index 8e0bf56eab1a450f92664812fe0541f4158a98cb..87e1ee07b318a72b3718b1dbdb1773aea89eb3f4 100644 --- a/ivette/src/frama-c/plugins/eva/probeinfos.tsx +++ b/ivette/src/frama-c/plugins/eva/probeinfos.tsx @@ -51,9 +51,6 @@ function ProbeEditor() { const { label } = probe; const { code } = probe; const { stmt, marker } = probe; - const byCS = probe.byCallstacks; - const stacks = model.getStacks(probe); - const stackable = byCS || stacks.length > 1; const { cols, rows } = sizeof(code); const { transient } = probe; const { zoomed } = probe; @@ -65,14 +62,6 @@ function ProbeEditor() { <SizedArea cols={cols} rows={rows}>{code}</SizedArea> </div> <Code><Stmt stmt={stmt} marker={marker} /></Code> - <IconButton - icon="ITEMS.LIST" - className="eva-probeinfo-button" - display={stackable} - selected={byCS} - title={`Details by callstack (${stacks})`} - onClick={() => { if (probe) probe.setByCallstacks(!byCS); }} - /> <IconButton icon="SEARCH" className="eva-probeinfo-button" @@ -120,7 +109,7 @@ export function ProbeInfos() { const model = useModel(); const probe = model.getFocused(); const fct = probe?.fct; - const byCS = probe?.byCallstacks; + const byCS = model.getByCallstacks(); const effects = probe ? probe.effects : false; const condition = probe ? probe.condition : false; const summary = fct ? model.stacks.getSummary(fct) : false; diff --git a/ivette/src/frama-c/plugins/eva/probes.ts b/ivette/src/frama-c/plugins/eva/probes.ts index 946a32e085e61508aa9097be92c4721c359c29c8..1b3c7552aa584da089f720f4a80b526e472cbf68 100644 --- a/ivette/src/frama-c/plugins/eva/probes.ts +++ b/ivette/src/frama-c/plugins/eva/probes.ts @@ -78,7 +78,6 @@ export class Probe { rank?: number; minCols: number = LabelSize; maxCols: number = LabelSize; - byCallstacks = false; zoomed = false; zoomable = false; effects = false; @@ -143,14 +142,6 @@ export class Probe { } } - setByCallstacks(byCS: boolean) { - if (byCS !== this.byCallstacks) { - this.byCallstacks = byCS; - if (byCS) this.setPersistent(); - this.model.forceLayout(); - } - } - setZoomed(zoomed: boolean) { if (zoomed !== this.zoomed) { this.zoomed = zoomed; diff --git a/ivette/src/frama-c/plugins/eva/style.css b/ivette/src/frama-c/plugins/eva/style.css index 129d0adc9f85679adb3bdad13994dc0e82a10e3d..799bdf86592257f421b3ccae77e29418edc0089d 100644 --- a/ivette/src/frama-c/plugins/eva/style.css +++ b/ivette/src/frama-c/plugins/eva/style.css @@ -177,6 +177,7 @@ overflow: hidden; display: flex; justify-content: center; + position: relative; } .eva-cell .eva-stmt { diff --git a/ivette/src/frama-c/plugins/eva/valuetable.tsx b/ivette/src/frama-c/plugins/eva/valuetable.tsx index dfd8b34ee76c408741837cac8a17c178301eca75..170ffed58be8267e9209373cecae7b99d95ac592 100644 --- a/ivette/src/frama-c/plugins/eva/valuetable.tsx +++ b/ivette/src/frama-c/plugins/eva/valuetable.tsx @@ -146,9 +146,10 @@ function TableCell(props: TableCellProps) { else status = 'Unknown'; } const alarmClass = `eva-cell-alarms eva-alarm-${status}`; + const title = 'At least one alarm is raised in one callstack'; contents = ( <> - <Icon className={alarmClass} size={10} id="WARNING" /> + <Icon className={alarmClass} size={10} title={title} id="WARNING" /> <SizedArea cols={cols} rows={rows}> <span className={`eva-state-${vstate}`}> <Diff {...vdiffs} /> @@ -197,6 +198,8 @@ interface TableSectionProps { folded: boolean; foldable: boolean; onClick: () => void; + byCallstacks: boolean; + onCallstackClick: () => void; } function TableSection(props: TableSectionProps) { @@ -214,6 +217,14 @@ function TableSection(props: TableSectionProps) { onClick={onClick} /> <Cell className="eva-fct-name">{fct}</Cell> + <Filler/> + <IconButton + icon="ITEMS.LIST" + className="eva-probeinfo-button" + selected={props.byCallstacks} + title={`Details by callstack`} + onClick={props.onCallstackClick} + /> </> ); } @@ -277,6 +288,7 @@ function TableRow(props: TableRowProps) { if (!fct) return null; const folded = model.isFolded(fct); const foldable = model.isFoldable(fct); + const byCallstacks = model.getByCallstacks(); return ( <Hpack className="eva-function" style={props.style}> <TableSection @@ -284,6 +296,8 @@ function TableRow(props: TableRowProps) { folded={folded} foldable={foldable} onClick={() => model.setFolded(fct, !folded)} + byCallstacks = {byCallstacks} + onCallstackClick = {() => model.setByCallstacks(!byCallstacks)} /> </Hpack> );