mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-24 20:28:06 +02:00
201 lines
5.3 KiB
TypeScript
201 lines
5.3 KiB
TypeScript
|
|
import { useMemo } from 'react';
|
|||
|
|
import { useDebugPointer } from '../../api/queries/debug';
|
|||
|
|
import { ApiError } from '../../api/client';
|
|||
|
|
import { EmptyState } from '../../components/ui/EmptyState';
|
|||
|
|
import { ErrorState } from '../../components/ui/ErrorState';
|
|||
|
|
import { LoadingState } from '../../components/ui/LoadingState';
|
|||
|
|
import type {
|
|||
|
|
PointerLocationView,
|
|||
|
|
PointerValueView,
|
|||
|
|
PointerFieldEntryView,
|
|||
|
|
} from '../../api/types';
|
|||
|
|
|
|||
|
|
interface PointerAnalysisPanelProps {
|
|||
|
|
file: string;
|
|||
|
|
functionName: string;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function PointerAnalysisPanel({
|
|||
|
|
file,
|
|||
|
|
functionName,
|
|||
|
|
}: PointerAnalysisPanelProps) {
|
|||
|
|
const { data, isLoading, error } = useDebugPointer(file, functionName);
|
|||
|
|
|
|||
|
|
if (isLoading) {
|
|||
|
|
return <LoadingState message="Loading points-to facts..." />;
|
|||
|
|
}
|
|||
|
|
if (error) {
|
|||
|
|
if (error instanceof ApiError && error.status === 404) {
|
|||
|
|
return (
|
|||
|
|
<EmptyState message="Pointer analysis is not available for the selected function." />
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
return <ErrorState message="Failed to load pointer analysis." />;
|
|||
|
|
}
|
|||
|
|
if (
|
|||
|
|
!data ||
|
|||
|
|
(data.values.length === 0 &&
|
|||
|
|
data.field_reads.length === 0 &&
|
|||
|
|
data.field_writes.length === 0)
|
|||
|
|
) {
|
|||
|
|
return (
|
|||
|
|
<EmptyState message="No points-to facts were derived for this function. Pointer analysis flags up parameters, allocation sites, and field projections; functions that only manipulate scalars will appear empty." />
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="abstract-interp-viewer">
|
|||
|
|
<div className="abstract-block">
|
|||
|
|
<div className="abstract-block-header">
|
|||
|
|
<h3 style={{ margin: 0 }}>Per-Value Points-To</h3>
|
|||
|
|
<span className="text-secondary">
|
|||
|
|
{data.values.length} value
|
|||
|
|
{data.values.length === 1 ? '' : 's'} · {data.location_count}{' '}
|
|||
|
|
location
|
|||
|
|
{data.location_count === 1 ? '' : 's'}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
{data.values.length === 0 ? (
|
|||
|
|
<p className="abstract-empty">
|
|||
|
|
All SSA values point to nothing tracked.
|
|||
|
|
</p>
|
|||
|
|
) : (
|
|||
|
|
<PointerValueTable values={data.values} locations={data.locations} />
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{data.field_reads.length > 0 && (
|
|||
|
|
<FieldEntriesBlock
|
|||
|
|
title="Field Reads"
|
|||
|
|
entries={data.field_reads}
|
|||
|
|
emptyHint="(no parameter field reads recorded)"
|
|||
|
|
/>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
{data.field_writes.length > 0 && (
|
|||
|
|
<FieldEntriesBlock
|
|||
|
|
title="Field Writes"
|
|||
|
|
entries={data.field_writes}
|
|||
|
|
emptyHint="(no parameter field writes recorded)"
|
|||
|
|
/>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function PointerValueTable({
|
|||
|
|
values,
|
|||
|
|
locations,
|
|||
|
|
}: {
|
|||
|
|
values: PointerValueView[];
|
|||
|
|
locations: PointerLocationView[];
|
|||
|
|
}) {
|
|||
|
|
const locById = useMemo(() => {
|
|||
|
|
const map = new Map<number, PointerLocationView>();
|
|||
|
|
for (const loc of locations) map.set(loc.id, loc);
|
|||
|
|
return map;
|
|||
|
|
}, [locations]);
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<table className="abstract-table">
|
|||
|
|
<thead>
|
|||
|
|
<tr>
|
|||
|
|
<th>Value</th>
|
|||
|
|
<th>Name</th>
|
|||
|
|
<th>Points-To</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
{values.map((v) => (
|
|||
|
|
<tr key={v.ssa_value}>
|
|||
|
|
<td className="mono">v{v.ssa_value}</td>
|
|||
|
|
<td className="mono">{v.var_name ?? '-'}</td>
|
|||
|
|
<td>
|
|||
|
|
{v.is_top ? (
|
|||
|
|
<span
|
|||
|
|
className="cap-badge cap-badge-sink"
|
|||
|
|
title="Over-approximation"
|
|||
|
|
>
|
|||
|
|
⊤ (top)
|
|||
|
|
</span>
|
|||
|
|
) : (
|
|||
|
|
v.points_to.map((id) => (
|
|||
|
|
<LocationChip key={id} loc={locById.get(id)} />
|
|||
|
|
))
|
|||
|
|
)}
|
|||
|
|
</td>
|
|||
|
|
</tr>
|
|||
|
|
))}
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function LocationChip({ loc }: { loc?: PointerLocationView }) {
|
|||
|
|
if (!loc) {
|
|||
|
|
return (
|
|||
|
|
<span className="cap-badge" title="Unknown location id">
|
|||
|
|
?
|
|||
|
|
</span>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
const className =
|
|||
|
|
loc.kind === 'Top'
|
|||
|
|
? 'cap-badge cap-badge-sink'
|
|||
|
|
: loc.kind === 'Field'
|
|||
|
|
? 'cap-badge cap-badge-sanitizer'
|
|||
|
|
: 'cap-badge cap-badge-source';
|
|||
|
|
return (
|
|||
|
|
<span
|
|||
|
|
className={className}
|
|||
|
|
title={`${loc.kind} (loc#${loc.id})`}
|
|||
|
|
style={{ marginRight: 4 }}
|
|||
|
|
>
|
|||
|
|
{loc.display}
|
|||
|
|
</span>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function FieldEntriesBlock({
|
|||
|
|
title,
|
|||
|
|
entries,
|
|||
|
|
emptyHint,
|
|||
|
|
}: {
|
|||
|
|
title: string;
|
|||
|
|
entries: PointerFieldEntryView[];
|
|||
|
|
emptyHint: string;
|
|||
|
|
}) {
|
|||
|
|
return (
|
|||
|
|
<div className="abstract-block">
|
|||
|
|
<div className="abstract-block-header">
|
|||
|
|
<h3 style={{ margin: 0 }}>{title}</h3>
|
|||
|
|
<span className="text-secondary">
|
|||
|
|
{entries.length} entr{entries.length === 1 ? 'y' : 'ies'}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
{entries.length === 0 ? (
|
|||
|
|
<p className="abstract-empty">{emptyHint}</p>
|
|||
|
|
) : (
|
|||
|
|
<table className="abstract-table">
|
|||
|
|
<thead>
|
|||
|
|
<tr>
|
|||
|
|
<th>Target</th>
|
|||
|
|
<th>Field</th>
|
|||
|
|
</tr>
|
|||
|
|
</thead>
|
|||
|
|
<tbody>
|
|||
|
|
{entries.map((e, i) => (
|
|||
|
|
<tr key={`${e.param_index ?? 'self'}-${e.field}-${i}`}>
|
|||
|
|
<td className="mono">
|
|||
|
|
{e.param_index === null ? 'self' : `param[${e.param_index}]`}
|
|||
|
|
</td>
|
|||
|
|
<td className="mono">{e.field}</td>
|
|||
|
|
</tr>
|
|||
|
|
))}
|
|||
|
|
</tbody>
|
|||
|
|
</table>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|