ACAV f0ba4b7c9529
Abstract Syntax Tree (AST) visualization tool for C, C++, and Objective-C
Loading...
Searching...
No Matches
AstNode.cpp
1/*$!{
2* Aurora Clang AST Viewer (ACAV)
3*
4* Copyright (c) 2026 Min Liu
5* Copyright (c) 2026 Michael David Adams
6*
7* SPDX-License-Identifier: GPL-2.0-or-later
8*
9* This program is free software; you can redistribute it and/or modify
10* it under the terms of the GNU General Public License as published by
11* the Free Software Foundation; either version 2 of the License, or
12* (at your option) any later version.
13*
14* This program is distributed in the hope that it will be useful,
15* but WITHOUT ANY WARRANTY; without even the implied warranty of
16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17* GNU General Public License for more details.
18*
19* You should have received a copy of the GNU General Public License along
20* with this program; if not, see <https://www.gnu.org/licenses/>.
21}$!*/
22
23#include "core/AstNode.h"
24#include "core/MemoryProfiler.h"
25
26#ifdef ACAV_ENABLE_STRING_STATS
27#include <iomanip>
28#include <iostream>
29#endif
30
31namespace acav {
32
33AstNode::AstNode(AcavJson properties, const SourceRange &sourceRange)
34 : properties_(std::move(properties)), sourceRange_(sourceRange) {}
35
36AstViewNode::AstViewNode(AstNode *node) : node_(node) {
37 if (node_) {
38 node_->hold();
39 }
40}
41
42AstViewNode::~AstViewNode() {
43 // Release reference to data node
44 if (node_) {
45 node_->release();
46 }
47
48 // Clear children vector but don't delete
49 // AstContext owns and will destroy them
50 children_.clear();
51}
52
53void AstViewNode::addChild(AstViewNode *child) {
54 if (child) {
55 child->setParent(this);
56 children_.push_back(child);
57 }
58}
59
60// AstContext implementation
61
62AstContext::~AstContext() {
63 MemoryProfiler::checkpoint("AstContext dtor: START");
64
65 // Delete AstViewNodes first (they hold references to AstNodes)
66 MemoryProfiler::checkpoint("AstContext dtor: Before deleting ViewNodes");
67 for (AstViewNode *viewNode : allAstViewNodes_) {
68 delete viewNode;
69 }
70 allAstViewNodes_.clear();
71 allAstViewNodes_.shrink_to_fit(); // Release vector capacity!
72 MemoryProfiler::checkpoint("AstContext dtor: After deleting ViewNodes");
73
74 // Then delete AstNodes
75 MemoryProfiler::checkpoint("AstContext dtor: Before deleting AstNodes");
76 for (AstNode *node : allAstNodes_) {
77 delete node;
78 }
79 allAstNodes_.clear();
80 allAstNodes_.shrink_to_fit(); // Release vector capacity!
81 MemoryProfiler::checkpoint("AstContext dtor: After deleting AstNodes");
82
83 // Clear deduplication map (pointers are already deleted)
84 typeDeduplicationMap_.clear();
85 // Note: unordered_map also keeps capacity, but typically smaller overhead
86
87 MemoryProfiler::checkpoint("AstContext dtor: END");
88}
89
91 const SourceRange &range) {
92 AstNode *node = new AstNode(std::move(properties), range);
93 allAstNodes_.push_back(node);
94 return node;
95}
96
98 AstViewNode *viewNode = new AstViewNode(node);
99 allAstViewNodes_.push_back(viewNode);
100 return viewNode;
101}
102
104 AcavJson properties,
105 const SourceRange &range) {
106#ifdef ACAV_ENABLE_STRING_STATS
107 ++typeLookupCount_;
108#endif
109
110 // Check if node for this type already exists
111 auto it = typeDeduplicationMap_.find(typePtr);
112 if (it != typeDeduplicationMap_.end()) {
113#ifdef ACAV_ENABLE_STRING_STATS
114 ++typeCacheHits_;
115#endif
116 return it->second; // Return existing deduplicated node
117 }
118
119 // Create new node
120 AstNode *node = createAstNode(std::move(properties), range);
121
122 // Store in deduplication map
123 typeDeduplicationMap_[typePtr] = node;
124
125 return node;
126}
127
128AstNode *AstContext::findTypeNode(const void *typePtr) const {
129 auto it = typeDeduplicationMap_.find(typePtr);
130 if (it != typeDeduplicationMap_.end()) {
131 return it->second;
132 }
133 return nullptr;
134}
135
137 if (node) {
138 locationIndex_.addNode(node);
139 }
140}
141
143 locationIndex_.finalize();
144}
145
146#ifdef ACAV_ENABLE_STRING_STATS
147
148TypeDeduplicationStats AstContext::getTypeDeduplicationStats() const {
149 TypeDeduplicationStats stats{};
150
151 stats.totalTypeLookups = typeLookupCount_;
152 stats.cacheHits = typeCacheHits_;
153 stats.uniqueTypeNodes = typeDeduplicationMap_.size();
154
155 // Count total references to type nodes (sum of refCounts)
156 std::size_t totalRefs = 0;
157 std::size_t totalNodeBytes = 0;
158 for (const auto &[ptr, node] : typeDeduplicationMap_) {
159 totalRefs += node->getUseCount();
160 // Estimate node size: AstNode object + JSON properties overhead
161 totalNodeBytes += sizeof(AstNode) + node->getProperties().dump().size();
162 }
163 stats.totalTypeReferences = totalRefs;
164
165 // Average bytes per type node
166 std::size_t avgNodeBytes = stats.uniqueTypeNodes > 0
167 ? totalNodeBytes / stats.uniqueTypeNodes
168 : 256; // Default estimate
169
170 // The REAL savings: multiple AstViewNodes share the same AstNode
171 // Without sharing: each of the 420017 references would need its own AstNode
172 // With sharing: we only have 28805 AstNodes
173 stats.estimatedSavedNodes = (totalRefs > stats.uniqueTypeNodes)
174 ? (totalRefs - stats.uniqueTypeNodes)
175 : 0;
176
177 // Memory saved = nodes avoided * average node size
178 stats.estimatedSavedBytes = stats.estimatedSavedNodes * avgNodeBytes;
179
180 // Deduplication ratio: how many ViewNodes share each AstNode on average
181 stats.deduplicationRatio = stats.uniqueTypeNodes > 0
182 ? static_cast<double>(stats.totalTypeReferences) / stats.uniqueTypeNodes
183 : 0.0;
184
185 // Savings percentage: what fraction of memory was saved
186 std::size_t wouldHaveUsed = totalRefs * avgNodeBytes; // Without sharing
187 std::size_t actuallyUsed = totalNodeBytes; // With sharing
188 stats.savingsPercent = wouldHaveUsed > 0
189 ? 100.0 * static_cast<double>(wouldHaveUsed - actuallyUsed) / wouldHaveUsed
190 : 0.0;
191
192 return stats;
193}
194
195void AstContext::printTypeDeduplicationStats(const char *label) const {
196 auto stats = getTypeDeduplicationStats();
197
198 std::cerr << "\n";
199 std::cerr << "========== Type Node Sharing Statistics";
200 if (label) {
201 std::cerr << " [" << label << "]";
202 }
203 std::cerr << " ==========\n";
204 std::cerr << std::fixed << std::setprecision(2);
205 std::cerr << " Unique type AstNodes: " << stats.uniqueTypeNodes << "\n";
206 std::cerr << " AstViewNode references: " << stats.totalTypeReferences << "\n";
207 std::cerr << " Nodes avoided by sharing: " << stats.estimatedSavedNodes << "\n";
208 std::cerr << " Estimated memory saved: " << stats.estimatedSavedBytes / 1024.0 << " KB\n";
209 std::cerr << " Sharing ratio: " << stats.deduplicationRatio << "x\n";
210 std::cerr << " Savings percentage: " << stats.savingsPercent << "%\n";
211 std::cerr << "=======================================================\n\n";
212}
213
214#endif // ACAV_ENABLE_STRING_STATS
215
216} // namespace acav
AST data structures and memory management.
nlohmann::basic_json< std::map, std::vector, InternedString, bool, int64_t, uint64_t, double, std::allocator, nlohmann::adl_serializer, std::vector< uint8_t > > AcavJson
Custom JSON type using InternedString for automatic string deduplication.
Definition AstNode.h:51
Cross-platform memory profiling utility.
AstNode * findTypeNode(const void *typePtr) const
Find existing deduplicated Type node (or nullptr).
Definition AstNode.cpp:128
AstViewNode * createAstViewNode(AstNode *node)
Create a new AST view node wrapping data.
Definition AstNode.cpp:97
void finalizeLocationIndex()
Finalize location index (call after all nodes indexed).
Definition AstNode.cpp:142
void indexNode(AstViewNode *node)
Add node to location index.
Definition AstNode.cpp:136
AstNode * getOrCreateTypeNode(const void *typePtr, AcavJson properties, const SourceRange &range)
Get or create deduplicated Type node.
Definition AstNode.cpp:103
AstNode * createAstNode(AcavJson properties, const SourceRange &range)
Create a new AST data node.
Definition AstNode.cpp:90
Pure data container for AST node properties.
Definition AstNode.h:162
Represents node in AST tree hierarchy.
Definition AstNode.h:195
void setParent(AstViewNode *parent)
Set parent node.
Definition AstNode.h:212
void addChild(AstViewNode *child)
Add child node.
Definition AstNode.cpp:53
static void checkpoint(const QString &label)
Log current memory usage with a label.
Represents a span of source code (begin to end).