Files view: per-file tracking (counters, image, device/card changes)
- new `simtrace_analyser/filetrack.py`: a single-pass tracker over a session's decoded messages, keyed by (DF, FID) and trace-wide (resets only wipe the per-channel selection): counters (`select`/`read`/`update`, successful operations only), a sparse image of the observed bytes (transparent files by offset, record files per record number) and the change log - changes: **device** = an UPDATE seen in the trace (applied to the image, `no_change` flags a provable no-op); **card** = a READ whose bytes differ from the image on bytes already known (logged at the surfacing read with the changed ranges and the before/after hex; never-seen bytes just fill the image, they are an observation, not a change) - SFI-addressed accesses join the same entry as path/FID ones: the SFI ↔ FID relations come from the normative tables and from every surfaced FCP `88` DO (including the spec's "SFI = 5 LSBs of the FID" rule when the DO is absent); an SFI used before its relation is learned stays provisional and merges into the resolved entry - the DF identity follows TS 102 221 §8.4/Table 8.2: `selected_df_fid()` now resolves an EF path selection to its parent DF and returns `aid:<hex>` for applications selected by an unknown AID (named from EF.DIR when the trace surfaced it); `Database._decode_rows` gained an `on_message` hook and `track_files()` - `GET /api/sessions/<id>/files`: the operation timeline, the per-file counters/coverage/change log, the SFI relations and the AID labels - PWA: the side panel's Map/Stats tabs became a view selector (Map / Stats / Files); the Files view is a timeline of Read / Update / Changed (card) rows (colour-coded, the same file names as the Map view), a row click jumps to the APDU (loading the pages it needs) and a card-change row's Δ opens the changed ranges with the previous and the new bytes; EN/RU - bounds: the image freezes past 64 KB of observed bytes per file, the change log keeps the last 1000 events per file (counters keep counting) - tests: tests/test_filetrack.py (counters, resets, channels, image/diff semantics, record files, SFI resolution and the provisional merge, EF.DIR labels, caps, the endpoint) and Node tests for the Files-view helpers and wiring - v2.3.0; MIN_SERVER_VERSION 2.3.0; SW cache simtrace-analyser-v63
This commit is contained in:
1 parent
b8d9c4cc63
commit
c494bf4111
14 files changed
+1302
-36
No files matched your search
@@ -192,6 +192,20 @@ class Database:
|
||||
initial_states, clk_hz = self._replay_context(session_id, after_id + 1)
|
||||
return self._decode_rows(rows, initial_states=initial_states, clk_hz=clk_hz)
|
||||
|
||||
def track_files(self, session_id):
|
||||
"""Track file state over the whole session (see filetrack.py).
|
||||
|
||||
Returns the tracker's result dict: the operation timeline, per-file
|
||||
counters/images/changes, the SFI relations and the AID labels.
|
||||
"""
|
||||
from .filetrack import FileTracker
|
||||
rows = self._conn.execute(
|
||||
'SELECT id, session_id, elapsed, type, data, flags FROM messages '
|
||||
'WHERE session_id=? ORDER BY elapsed, id', (session_id,)).fetchall()
|
||||
tracker = FileTracker()
|
||||
self._decode_rows(rows, on_message=tracker.observe)
|
||||
return tracker.result()
|
||||
|
||||
def get_messages_raw(self, session_id):
|
||||
rows = self._conn.execute(
|
||||
'SELECT elapsed, type, data FROM messages WHERE session_id=? ORDER BY elapsed, id',
|
||||
@@ -300,7 +314,7 @@ class Database:
|
||||
def _advance_state(self, d, sel, df, sfi_map):
|
||||
"""Update (sel, df, sfi_map) for one decoded TPDU."""
|
||||
from .decode import selected_df_fid, sfi_table, KNOWN_FIDS
|
||||
new_df = selected_df_fid(d)
|
||||
new_df = selected_df_fid(d, df)
|
||||
if new_df is not None and new_df != df:
|
||||
# Only a successful SELECT changes the current DF.
|
||||
if (d.get('sw') or {}).get('sw1') in _SUCCESS_SW1:
|
||||
@@ -363,7 +377,7 @@ class Database:
|
||||
return new
|
||||
return sel
|
||||
|
||||
def _decode_rows(self, rows, initial_states=None, clk_hz=None):
|
||||
def _decode_rows(self, rows, initial_states=None, clk_hz=None, on_message=None):
|
||||
from .decode import atr_rate_fields
|
||||
msgs = []
|
||||
states = dict(initial_states or {})
|
||||
@@ -380,6 +394,10 @@ class Database:
|
||||
ctx['sfi_map'] = st['sfi_map']
|
||||
msg = self._row_to_message(row, prev=ctx, clk_hz=clk_hz)
|
||||
d = msg.get('decoded')
|
||||
if on_message is not None:
|
||||
# Observers (the per-file tracker) see the message with the
|
||||
# channel state as it was *before* this message.
|
||||
on_message(msg, st, channel)
|
||||
if msg['type'] == 'tpdu' and d and d.get('ins_hex'):
|
||||
st['sel'], st['df'], st['sfi_map'] = self._advance_state(d, st['sel'], st['df'], st['sfi_map'])
|
||||
st['prev'] = self._context_from_decoded(d)
|
||||
|
||||
Reference in new issue
Block a user