Add callsite-level, source-located call graph export example - #863
Open
WizardBornov wants to merge 2 commits into
Open
WizardBornov wants to merge 2 commits into
WizardBornov wants to merge 2 commits into
Conversation
PhASAR's existing exportICFGAsJson() aggregates function-to-function edges and builds one in-memory JSON object before writing anything to disk. Correlating a static call graph against runtime instrumentation data needs callsite-level resolution, and on FFmpeg-scale IR (~1.2M+ edges) the in-memory approach hits an unbounded memory ceiling before writing a single byte. This adds a streaming CSV export built on the same getCallsFromWithin/getCalleesOfCallAt primitives, plus a resolveLocation() helper that fixes a File/Line pairing bug in getSrcCodeInfoFromIR (it composes File and Line from separate helper calls with different fallback branches, which can mismatch a File from one instruction with a Line from another). Invited by Fabian Schiebel to submit this as a PR. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Unresolved moderate findings affect source accuracy, validation, traversal efficiency, error handling, and memory claims.
Get a fresh assessment by requesting another Copilot review.
Review effort: Lite
Findings: 4
Open (4)
What changed in this PR
This PR adds a standalone PhASAR how-to example for exporting callsite-level, source-located call-graph edges as CSV.
Changes:
- Registers and documents the new example.
- Implements callsite traversal, source-location resolution, and incremental CSV output.
- Adds a standalone CMake target.
| File | Reviewed changes | Findings |
|---|---|---|
examples/how-to/README.md |
Adds the example to the index. | None |
examples/how-to/09-export-callsite-cg/README.md |
Documents building, usage, and CSV output. | Nit (1 vote): qualify memory claims because the complete call graph is already materialized. |
examples/how-to/09-export-callsite-cg/export_callsite_cg_streaming.cpp |
Implements traversal, source-location handling, and CSV export. | Moderate: preserve full source paths (3 votes); iterate reachable graph functions (3); handle output errors (3); qualify eager graph-construction/streaming claims (1); reject unknown analysis types (2); validate entry points (1); validate the project IR database before ICFG construction and close output on failure (1). |
examples/how-to/09-export-callsite-cg/CMakeLists.txt |
Defines the standalone executable and PhASAR linkage. | None |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+62
to
+63
| return {Loc->getFilename().str(), Loc->getLine(), Loc->getColumn(), | ||
| false}; |
Comment on lines
+87
to
+89
| llvm::errs() << "Unknown call-graph analysis type '" << S | ||
| << "', defaulting to OTF\n"; | ||
| return CallGraphAnalysisType::OTF; |
| // exactly the same traversal exportICFGAsJson does internally, just | ||
| // writing (and forgetting) each result immediately instead of | ||
| // accumulating all of them. | ||
| for (const llvm::Function *Fun : ICF.getAllFunctions()) { |
Comment on lines
+157
to
+161
| std::fprintf( | ||
| Out, "%s,%s,%u,%u,%s,%s,%u,%u,%s\n", | ||
| csvField(Fun->getName().str()).c_str(), | ||
| csvField(CallerLoc.File).c_str(), | ||
| CallerLoc.Line, CallerLoc.Column, |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Summary
Adds a standalone example (
examples/how-to/09-export-callsite-cg/) that exports PhASAR's call graph as callsite-level, source-located CSV. This came out of a thread with @fabianbs96 about baseline call-graph findings for an FSE 2027 paper — he offered to take this if it looked useful:Two problems it solves, both hit against a real FFmpeg build (~1.2M+ call edges):
Callsite-level, not function-level.
CallGraph::printAsDot()/printAsJson()aggregate function-to-function edges. Correlating against runtime instrumentation ground truth needs individual call-site resolution, so this walksgetCallsFromWithin/getCalleesOfCallAtdirectly and emits one row per resolved edge.Streaming, not buffered.
exportICFGAsJson()builds one in-memorynlohmann::jsonobject holding every edge before writing anything to disk. On FFmpeg-scale input this grew unbounded — 11GB resident plus climbing swap, zero bytes written the entire time. This driver writes each edge to disk the moment it's produced and discards it, so memory stays roughly constant regardless of total edge count.It also documents and works around a source-location bug:
getSrcCodeInfoFromIRresolvesFileandLinethrough separate helper calls with different fallback branches for instructions lacking direct!dbgmetadata, which can pair aFilefrom one resolution path with aLinefrom an unrelated one. A concrete case from the FFmpeg run: a callsite reported line 2776 inside a 63-line header. TheresolveLocation()helper here instead resolves a singleDILocationper instruction and readsFile/Line/Columnoff that same object, explicitly marking (rather than silently guessing at) instructions with no direct location at all.As Fabian's reply anticipated, this is submitted mostly as-is — C-style
FILE*I/O, manual CSV escaping,argvparsing. Happy to rework any of it toward LLVM utilities (raw_ostream,cl::opt, etc.) if that's preferred over him doing it during review.Test plan
CMakeLists.txtagainst an installed PhASAR (same pattern as the otherhow-to/examples)llvm-hello-worldsample bitcode files and produces a well-formed CSVgetSrcCodeInfoFromIRfix belongs here as documentation-of-workaround, or should instead be filed as a separate bug against that helper🤖 Generated with Claude Code