| 219 | } |
| 220 | |
| 221 | QHash<uint, ProfileComparator::CallTreeNode*> ProfileComparator::BuildCallTreeWithHashMap( |
| 222 | const ProfileData& data, QVector<CallTreeNode*>& roots) |
| 223 | { |
| 224 | // Build a tree structure matching MainWindow::GetMergedCallstacks logic |
| 225 | // This uses hash of callstack suffix for O(1) node lookup |
| 226 | QHash<uint, CallTreeNode*> nodeMap; |
| 227 | |
| 228 | for (const auto& record : data.stackRecords) { |
| 229 | // Get the call stack for this record |
| 230 | auto stackIt = data.callStackMap.find(record.uuid_); |
| 231 | if (stackIt == data.callStackMap.end()) { |
| 232 | continue; |
| 233 | } |
| 234 | |
| 235 | const auto& callStack = stackIt.value(); |
| 236 | |
| 237 | // Skip if call stack is too short after skipRootLevels_ |
| 238 | if (callStack.size() <= skipRootLevels_) { |
| 239 | continue; |
| 240 | } |
| 241 | |
| 242 | // Build list of function names and metadata, skipping root levels |
| 243 | // Call stacks are stored leaf-first (index 0 = allocation site, last index = root) |
| 244 | // We iterate from 0 to (size - skipRootLevels_) to match MainWindow::GetMergedCallstacks |
| 245 | QStringList callstackNames; |
| 246 | QVector<QPair<QString, quint64>> callstackMetadata; // Store library + address for each frame |
| 247 | int endIndex = callStack.size() - skipRootLevels_; |
| 248 | for (int i = 0; i < endIndex; ++i) { |
| 249 | const auto& frame = callStack[i]; |
| 250 | QString libraryName = frame.first.Get(); |
| 251 | quint64 funcAddr = frame.second; |
| 252 | |
| 253 | // Resolve function name from symbol map |
| 254 | QString funcName; |
| 255 | auto libIt = data.symbolMap.find(libraryName); |
| 256 | if (libIt != data.symbolMap.end()) { |
| 257 | auto symIt = libIt.value().find(funcAddr); |
| 258 | if (symIt != libIt.value().end()) { |
| 259 | funcName = symIt.value(); |
| 260 | } else { |
| 261 | funcName = QString("%1!0x%2").arg(libraryName).arg(funcAddr, 0, 16); |
| 262 | } |
| 263 | } else { |
| 264 | funcName = QString("%1!0x%2").arg(libraryName).arg(funcAddr, 0, 16); |
| 265 | } |
| 266 | |
| 267 | callstackNames << funcName; |
| 268 | callstackMetadata.append(qMakePair(libraryName, funcAddr)); |
| 269 | } |
| 270 | |
| 271 | // Now build the tree using hash-based merging (same as MainWindow::GetMergedCallstacks) |
| 272 | CallTreeNode* child = nullptr; |
| 273 | for (int idx = 0; idx < callstackNames.size(); ++idx) { |
| 274 | // Hash of the suffix from current position to end - this uniquely identifies a path |
| 275 | auto it = callstackNames.begin() + idx; |
| 276 | auto curHash = qHashRange(it, callstackNames.end()); |
| 277 | auto itemIt = nodeMap.find(curHash); |
| 278 | CallTreeNode* node = nullptr; |