[GH-ISSUE #4056] [Feature] Project managed call edges and field accesses into the application graph (project_managed_application_graph) #1608

Closed
opened 2026-10-16 21:07:53 +00:00 by bsakaguchi · 4 comments
Owner

Originally created by @Rinkia on GitHub (Oct 15, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/4056

Originally assigned to: @kaoru0822-kitauji on GitHub.

Before submitting

  • I searched the open and closed issues and pull requests for a related proposal (managed graph calls / call edges / field access): none found. #2160 (closed) added target_declaring_type to member call edges; #3352 (closed) fixed an empty-module-name rejection.
  • I understand the contribution scope must be reviewed before integration; open questions are welcome here.
  • This request does not include credentials, private samples, or sensitive target data.

Area

Evidence, artifacts, or reconstruction

Current limitation

project_managed_application_graph projects member Evidence as a containment tree only: artifact → type → method/field, method → native-implementation, plus method imports → P/Invoke. The decoded call_edges and field_accesses that inspect_managed_members already produces are dropped. The graph schema has a calls relation, which the JavaScript projections use.

Nothing in the result says these relations were omitted. Coverage is complete-within-inputs, and the only limitation is the general "preserves static metadata observations only" line.

LibreHardwareMonitor 0.9.6 LibreHardwareMonitorLib.dll (net472), main @ 337d2e33c, artifact + members Evidence:

member Evidence: call_edges 23059, field_accesses 7518
graph relations: {"contains": 11955}
coverage: complete-within-inputs

With native-boundary Evidence added (6.5.0): 12,338 nodes, 12,337 edges (12,240 contains, 97 imports), no calls.

Use cases

  • Which managed methods reach a P/Invoke? For example, "who calls NtQuerySystemInformation" in LibreHardwareMonitor. The graph has the imports edge from the declaring method, but no way back to its callers.
  • Cross-layer feature tracing, which the commit that introduced the projection names as its purpose (77178bba5: "projects authenticated managed artifact/member/native-boundary Evidence into the existing application graph for cross-layer feature tracing"). Tracing needs call relations.
  • Following a literal or field (e.g. a config key, a static DllName) to the methods that read or write it.

Desired outcome and success criteria

  • Each call_edges row whose caller and target are both methods in the inspected module becomes a calls edge between the two managed-method nodes. It keeps the opcode (call/callvirt/newobj/…), and its evidence links point to the member Evidence.
  • Targets outside the module (member-ref) become either a node per external member (declaring type plus name, already reported by target_declaring_type) or a counted omission that the limitations name.
  • Field accesses get the same treatment (reads/writes as properties on an edge to the managed-field node).
  • If any of this is left out deliberately, the result says so in limitations and doesn't read as complete.

Expected interaction with targets

Static only. It reuses data already in inspect_managed_members Evidence; no new parsing, loading or execution.

Target and scope details (optional)

LibreHardwareMonitor v0.9.6 release LibreHardwareMonitor.zip (sha256 086d9f1b5a99e643edc2cfaaac16051685b551e4c5ac0b32a57c58c0e529c001, matches the GitHub asset digest), LibreHardwareMonitorLib.dll sha256 6ebc194316536ba61af5be24508ad9fcbb2ecc685e716c12e787c79530f66bf0, MPL-2.0. Any assembly shows the same thing.

Verification available so far

Reproduced on rea-agents@6.5.0 (Docker) and on main @ 337d2e33c (node:24-bookworm, Node 24.21.0).

Verification details (optional)

rea inspect-managed-artifact LibreHardwareMonitorLib.dll --json > a.json
rea inspect-managed-members  LibreHardwareMonitorLib.dll --json > m.json
# g-in.json = {"managed_artifact": <a.json>, "managed_members": <m.json>}
rea project-managed-application-graph g-in.json --json > g.json
# count g.normalized_result.graph.edges by relation

Spot-checks show the dropped edges are accurate. For example, NvApi.GetDelegate → NvAPI32_QueryInterface and NvAPI64_QueryInterface match Interop/NvApi.cs at tag v0.9.6.

Alternatives considered

  • Querying call_edges from member Evidence pages (inspect_analysis_view, kind managed-members) works for a single question. It doesn't compose with the graph's other nodes (P/Invoke imports, native implementations) or with other projections.
  • Keeping the containment-only graph but declaring the omission would at least make the result accurate.

Possible direction

src/domain/managed/managedApplicationGraphNodes.ts already creates method nodes keyed by token (managed-method-token) and adds imports edges at line 374. Call edges carry caller_token, target_token and target_kind, so in-module method-def targets can be linked by the same token keys under the same MVID scope check used for boundary tokens. A size budget may be needed: 23k edges here.

References

  • docs/managed-code-analysis.md (tool table: "Add managed findings to an application graph")
  • src/domain/managed/managedApplicationGraph.ts (projectionLimitations)
  • Commit 77178bba5 (feature introduction)
Originally created by @Rinkia on GitHub (Oct 15, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4056 Originally assigned to: @kaoru0822-kitauji on GitHub. ### Before submitting - [x] I searched the open and closed issues and pull requests for a related proposal (managed graph calls / call edges / field access): none found. #2160 (closed) added `target_declaring_type` to member call edges; #3352 (closed) fixed an empty-module-name rejection. - [x] I understand the contribution scope must be reviewed before integration; open questions are welcome here. - [x] This request does not include credentials, private samples, or sensitive target data. ### Area Evidence, artifacts, or reconstruction ### Current limitation `project_managed_application_graph` projects member Evidence as a containment tree only: artifact → type → method/field, method → native-implementation, plus method `imports` → P/Invoke. The decoded `call_edges` and `field_accesses` that `inspect_managed_members` already produces are dropped. The graph schema has a `calls` relation, which the JavaScript projections use. Nothing in the result says these relations were omitted. Coverage is `complete-within-inputs`, and the only limitation is the general "preserves static metadata observations only" line. LibreHardwareMonitor 0.9.6 `LibreHardwareMonitorLib.dll` (net472), `main` @ `337d2e33c`, artifact + members Evidence: ```text member Evidence: call_edges 23059, field_accesses 7518 graph relations: {"contains": 11955} coverage: complete-within-inputs ``` With native-boundary Evidence added (6.5.0): 12,338 nodes, 12,337 edges (12,240 `contains`, 97 `imports`), no `calls`. ### Use cases - Which managed methods reach a P/Invoke? For example, "who calls `NtQuerySystemInformation`" in LibreHardwareMonitor. The graph has the `imports` edge from the declaring method, but no way back to its callers. - Cross-layer feature tracing, which the commit that introduced the projection names as its purpose (77178bba5: "projects authenticated managed artifact/member/native-boundary Evidence into the existing application graph for cross-layer feature tracing"). Tracing needs call relations. - Following a literal or field (e.g. a config key, a static `DllName`) to the methods that read or write it. ### Desired outcome and success criteria - Each `call_edges` row whose caller and target are both methods in the inspected module becomes a `calls` edge between the two `managed-method` nodes. It keeps the opcode (`call`/`callvirt`/`newobj`/…), and its evidence links point to the member Evidence. - Targets outside the module (`member-ref`) become either a node per external member (declaring type plus name, already reported by `target_declaring_type`) or a counted omission that the limitations name. - Field accesses get the same treatment (reads/writes as properties on an edge to the `managed-field` node). - If any of this is left out deliberately, the result says so in `limitations` and doesn't read as complete. ### Expected interaction with targets Static only. It reuses data already in `inspect_managed_members` Evidence; no new parsing, loading or execution. ### Target and scope details (optional) LibreHardwareMonitor v0.9.6 release `LibreHardwareMonitor.zip` (sha256 `086d9f1b5a99e643edc2cfaaac16051685b551e4c5ac0b32a57c58c0e529c001`, matches the GitHub asset digest), `LibreHardwareMonitorLib.dll` sha256 `6ebc194316536ba61af5be24508ad9fcbb2ecc685e716c12e787c79530f66bf0`, MPL-2.0. Any assembly shows the same thing. ### Verification available so far Reproduced on `rea-agents@6.5.0` (Docker) and on `main` @ `337d2e33c` (`node:24-bookworm`, Node 24.21.0). ### Verification details (optional) ```sh rea inspect-managed-artifact LibreHardwareMonitorLib.dll --json > a.json rea inspect-managed-members LibreHardwareMonitorLib.dll --json > m.json # g-in.json = {"managed_artifact": <a.json>, "managed_members": <m.json>} rea project-managed-application-graph g-in.json --json > g.json # count g.normalized_result.graph.edges by relation ``` Spot-checks show the dropped edges are accurate. For example, `NvApi.GetDelegate` → `NvAPI32_QueryInterface` and `NvAPI64_QueryInterface` match `Interop/NvApi.cs` at tag v0.9.6. ### Alternatives considered - Querying `call_edges` from member Evidence pages (`inspect_analysis_view`, kind `managed-members`) works for a single question. It doesn't compose with the graph's other nodes (P/Invoke imports, native implementations) or with other projections. - Keeping the containment-only graph but declaring the omission would at least make the result accurate. ### Possible direction `src/domain/managed/managedApplicationGraphNodes.ts` already creates method nodes keyed by token (`managed-method-token`) and adds `imports` edges at line 374. Call edges carry `caller_token`, `target_token` and `target_kind`, so in-module `method-def` targets can be linked by the same token keys under the same MVID scope check used for boundary tokens. A size budget may be needed: 23k edges here. ### References - `docs/managed-code-analysis.md` (tool table: "Add managed findings to an application graph") - `src/domain/managed/managedApplicationGraph.ts` (`projectionLimitations`) - Commit 77178bba5 (feature introduction)
Author
Owner

@XinwangTao commented on GitHub (Oct 15, 2026):

Strong +1 — a containment-only graph makes "who calls NtQuerySystemInformation"-style questions unanswerable, which defeats the cross-layer tracing purpose the projection was introduced for. The in-module token-key linking you propose is the right hook since method nodes are already keyed by managed-method-token. Agree that external member-ref targets should at least be a counted omission in limitations rather than silently dropped, and reusing target_declaring_type from #2160 for external nodes seems natural. A size budget for the 23k-edge case also sounds prudent.

<!-- gh-comment-id:6189559387 --> @XinwangTao commented on GitHub (Oct 15, 2026): Strong +1 — a containment-only graph makes "who calls NtQuerySystemInformation"-style questions unanswerable, which defeats the cross-layer tracing purpose the projection was introduced for. The in-module token-key linking you propose is the right hook since method nodes are already keyed by managed-method-token. Agree that external member-ref targets should at least be a counted omission in limitations rather than silently dropped, and reusing target_declaring_type from #2160 for external nodes seems natural. A size budget for the 23k-edge case also sounds prudent.
Author
Owner

@kaoru0822-kitauji commented on GitHub (Oct 22, 2026):

Picking this up. Concrete proposal (implementing now; happy to adjust):

  • In-module calls: each call_edges row whose target_kind is method-def and whose caller and target both have managed-method nodes becomes a calls edge. Identical rows (same caller, target, opcode — e.g. the repeated Enumerable.ToList sites in #5151) collapse into one edge with opcode and site_count, so the graph grows by distinct relationships rather than raw sites. Edge evidence links the member Evidence.
  • In-module field accesses: each field_accesses row whose field has a managed-field node becomes an edge with opcode, access (read for ldfld/ldsfld, write for stfld/stsfld, address for ldflda/ldsflda) and site_count. The graph vocabulary has no data-access relation today, so this adds one provider-neutral relation, accesses.
  • Everything else is a counted omission, not silence: member-ref, method-spec and unknown call targets, fields outside the module, and rows whose endpoint node is absent (e.g. a type-selected member Evidence) are counted by reason in limitations, and graph coverage becomes partial with that omitted_count. External member-ref nodes (one node per referenced member) would make BCL/API callers queryable too, but they need a new node kind; I'd leave that for a follow-up so this stays reviewable.
  • New summary counters: calls, field_accesses.
<!-- gh-comment-id:6333829888 --> @kaoru0822-kitauji commented on GitHub (Oct 22, 2026): Picking this up. Concrete proposal (implementing now; happy to adjust): - **In-module calls**: each `call_edges` row whose `target_kind` is `method-def` and whose caller and target both have `managed-method` nodes becomes a `calls` edge. Identical rows (same caller, target, opcode — e.g. the repeated `Enumerable.ToList` sites in #5151) collapse into one edge with `opcode` and `site_count`, so the graph grows by distinct relationships rather than raw sites. Edge evidence links the member Evidence. - **In-module field accesses**: each `field_accesses` row whose field has a `managed-field` node becomes an edge with `opcode`, `access` (`read` for `ldfld`/`ldsfld`, `write` for `stfld`/`stsfld`, `address` for `ldflda`/`ldsflda`) and `site_count`. The graph vocabulary has no data-access relation today, so this adds one provider-neutral relation, `accesses`. - **Everything else is a counted omission, not silence**: `member-ref`, `method-spec` and `unknown` call targets, fields outside the module, and rows whose endpoint node is absent (e.g. a type-selected member Evidence) are counted by reason in `limitations`, and graph coverage becomes `partial` with that `omitted_count`. External member-ref nodes (one node per referenced member) would make BCL/API callers queryable too, but they need a new node kind; I'd leave that for a follow-up so this stays reviewable. - New `summary` counters: `calls`, `field_accesses`.
Author
Owner

@XinwangTao commented on GitHub (Oct 22, 2026):

Implementation matches the proposal well — collapsing repeated call sites into a single edge with site_count keeps the 23k-edge case tractable, and counted omissions for member-ref targets keep the limitations honest instead of silently dropping data. The new provider-neutral accesses relation for field reads/writes also fills a real vocabulary gap. Looking forward to the external member-ref node follow-up so BCL/API callers become queryable too.

<!-- gh-comment-id:6335835303 --> @XinwangTao commented on GitHub (Oct 22, 2026): Implementation matches the proposal well — collapsing repeated call sites into a single edge with site_count keeps the 23k-edge case tractable, and counted omissions for member-ref targets keep the limitations honest instead of silently dropping data. The new provider-neutral accesses relation for field reads/writes also fills a real vocabulary gap. Looking forward to the external member-ref node follow-up so BCL/API callers become queryable too.
Author
Owner

@XinwangTao commented on GitHub (Oct 22, 2026):

Glad this landed in c9b4cae. Collapsing identical call sites into a single edge with site_count is the right call — 23k raw edges would have made the graph unusable, and counting every omitted endpoint by reason in limitations keeps the coverage claim honest. Agree that external member-ref nodes are best left as a follow-up so this stays reviewable.

<!-- gh-comment-id:6335841954 --> @XinwangTao commented on GitHub (Oct 22, 2026): Glad this landed in c9b4cae. Collapsing identical call sites into a single edge with site_count is the right call — 23k raw edges would have made the graph unusable, and counting every omitted endpoint by reason in limitations keeps the coverage claim honest. Agree that external member-ref nodes are best left as a follow-up so this stays reviewable.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#1608
No description provided.