|
49 | 49 |
|
50 | 50 | #include <cstdint>
|
51 | 51 | #include <cstring>
|
| 52 | +#include <unordered_map> |
52 | 53 | #include <utility>
|
53 | 54 |
|
54 | 55 | #include <cassert>
|
@@ -394,67 +395,121 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
|
394 | 395 | // The goal is to give users helpful live variable hints alongside the
|
395 | 396 | // disassembled instruction stream, similar to how debug information
|
396 | 397 | // enhances source-level debugging.
|
397 |
| - |
| 398 | + |
| 399 | + struct VarState { |
| 400 | + std::string name; // display name |
| 401 | + std::string last_loc; // last printed location (empty means <undef>) |
| 402 | + bool seen_this_inst = false; |
| 403 | + }; |
| 404 | + |
| 405 | + // Track live variables across instructions (keyed by stable LLDB user_id_t) |
| 406 | + std::unordered_map<lldb::user_id_t, VarState> live_vars; |
| 407 | + |
| 408 | + // Stateful annotator: updates live_vars and returns only what should be printed for THIS instruction. |
398 | 409 | auto annotate_variables = [&](Instruction &inst) -> std::vector<std::string> {
|
399 |
| - std::vector<std::string> annotations; |
| 410 | + std::vector<std::string> events; |
400 | 411 |
|
401 | 412 | StackFrame *frame = exe_ctx.GetFramePtr();
|
402 | 413 | TargetSP target_sp = exe_ctx.GetTargetSP();
|
403 |
| - if (!frame || !target_sp) |
404 |
| - return annotations; |
| 414 | + ProcessSP process_sp = exe_ctx.GetProcessSP(); |
| 415 | + if (!frame || !target_sp || !process_sp) |
| 416 | + return events; |
| 417 | + |
| 418 | + // Reset "seen" flags for this instruction |
| 419 | + for (auto &kv : live_vars) |
| 420 | + kv.second.seen_this_inst = false; |
405 | 421 |
|
406 | 422 | addr_t current_pc = inst.GetAddress().GetLoadAddress(target_sp.get());
|
407 | 423 | addr_t original_pc = frame->GetFrameCodeAddress().GetLoadAddress(target_sp.get());
|
408 | 424 |
|
| 425 | + // We temporarily move the frame PC so variable locations resolve at this inst |
409 | 426 | if (!frame->ChangePC(current_pc))
|
410 |
| - return annotations; |
| 427 | + return events; |
411 | 428 |
|
412 | 429 | VariableListSP var_list_sp = frame->GetInScopeVariableList(true);
|
413 |
| - if (!var_list_sp) |
414 |
| - return annotations; |
| 430 | + if (!var_list_sp) { |
| 431 | + // No variables in scope: everything previously live becomes <undef> |
| 432 | + for (auto it = live_vars.begin(); it != live_vars.end(); ) { |
| 433 | + events.push_back(llvm::formatv("{0} = <undef>", it->second.name).str()); |
| 434 | + it = live_vars.erase(it); |
| 435 | + } |
| 436 | + frame->ChangePC(original_pc); |
| 437 | + return events; |
| 438 | + } |
415 | 439 |
|
416 | 440 | SymbolContext sc = frame->GetSymbolContext(eSymbolContextFunction);
|
417 | 441 | addr_t func_load_addr = sc.function
|
418 |
| - ? sc.function->GetAddress().GetLoadAddress(target_sp.get()) |
419 |
| - : LLDB_INVALID_ADDRESS; |
| 442 | + ? sc.function->GetAddress().GetLoadAddress(target_sp.get()) |
| 443 | + : LLDB_INVALID_ADDRESS; |
420 | 444 |
|
| 445 | + // Walk all in-scope variables and try to resolve a location |
421 | 446 | for (const VariableSP &var_sp : *var_list_sp) {
|
422 | 447 | if (!var_sp)
|
423 | 448 | continue;
|
424 | 449 |
|
425 |
| - const char *name = var_sp->GetName().AsCString(); |
| 450 | + const auto var_id = var_sp->GetID(); // lldb::user_id_t – stable key |
| 451 | + const char *name_cstr = var_sp->GetName().AsCString(); |
| 452 | + llvm::StringRef name = name_cstr ? name_cstr : "<anon>"; |
| 453 | + |
426 | 454 | auto &expr_list = var_sp->LocationExpressionList();
|
427 | 455 | if (!expr_list.IsValid())
|
428 | 456 | continue;
|
429 | 457 |
|
430 |
| - if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc)) { |
431 |
| - auto entry = *entryOrErr; |
| 458 | + // Try to get the expression entry for this PC |
| 459 | + auto entry_or_err = expr_list.GetExpressionEntryAtAddress(func_load_addr, current_pc); |
| 460 | + if (!entry_or_err) |
| 461 | + continue; |
432 | 462 |
|
433 |
| - if (!entry.file_range || |
434 |
| - entry.file_range->ContainsFileAddress( |
435 |
| - (current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) { |
| 463 | + auto entry = *entry_or_err; |
436 | 464 |
|
437 |
| - StreamString loc_str; |
438 |
| - ABI *abi = exe_ctx.GetProcessPtr()->GetABI().get(); |
439 |
| - llvm::DIDumpOptions opts; |
440 |
| - opts.ShowAddresses = false; |
441 |
| - opts.PrintRegisterOnly = true; |
| 465 | + // Check range if present |
| 466 | + if (entry.file_range && |
| 467 | + !entry.file_range->ContainsFileAddress( |
| 468 | + (current_pc - func_load_addr) + expr_list.GetFuncFileAddress())) |
| 469 | + continue; |
442 | 470 |
|
443 |
| - entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts); |
| 471 | + // Render a compact location string |
| 472 | + ABI *abi = process_sp->GetABI().get(); |
| 473 | + llvm::DIDumpOptions opts; |
| 474 | + opts.ShowAddresses = false; |
| 475 | + opts.PrintRegisterOnly = true; |
444 | 476 |
|
445 |
| - llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim(); |
446 |
| - if (!loc_clean.empty()) { |
447 |
| - annotations.push_back(llvm::formatv("{0} = {1}", name, loc_clean)); |
448 |
| - } |
| 477 | + StreamString loc_str; |
| 478 | + entry.expr->DumpLocation(&loc_str, eDescriptionLevelBrief, abi, opts); |
| 479 | + llvm::StringRef loc_clean = llvm::StringRef(loc_str.GetString()).trim(); |
| 480 | + if (loc_clean.empty()) |
| 481 | + continue; |
| 482 | + |
| 483 | + // Update map + decide if we print |
| 484 | + auto it = live_vars.find(var_id); |
| 485 | + if (it == live_vars.end()) { |
| 486 | + // New var → print |
| 487 | + live_vars.emplace(var_id, VarState{std::string(name), loc_clean.str(), true}); |
| 488 | + events.push_back(llvm::formatv("{0} = {1}", name, loc_clean).str()); |
| 489 | + } else { |
| 490 | + it->second.seen_this_inst = true; |
| 491 | + if (it->second.last_loc != loc_clean) { |
| 492 | + it->second.last_loc = loc_clean.str(); |
| 493 | + events.push_back(llvm::formatv("{0} = {1}", it->second.name, loc_clean).str()); |
449 | 494 | }
|
450 | 495 | }
|
451 | 496 | }
|
452 | 497 |
|
| 498 | + // Anything previously live that we didn't see a location for at this inst is now <undef> |
| 499 | + for (auto it = live_vars.begin(); it != live_vars.end(); ) { |
| 500 | + if (!it->second.seen_this_inst) { |
| 501 | + events.push_back(llvm::formatv("{0} = <undef>", it->second.name).str()); |
| 502 | + it = live_vars.erase(it); |
| 503 | + } else { |
| 504 | + ++it; |
| 505 | + } |
| 506 | + } |
| 507 | + |
| 508 | + // Restore PC |
453 | 509 | frame->ChangePC(original_pc);
|
454 |
| - return annotations; |
| 510 | + return events; |
455 | 511 | };
|
456 | 512 |
|
457 |
| - |
458 | 513 | previous_symbol = nullptr;
|
459 | 514 | SourceLine previous_line;
|
460 | 515 | for (size_t i = 0; i < num_instructions_found; ++i) {
|
@@ -626,7 +681,7 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
|
626 | 681 | show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr,
|
627 | 682 | address_text_size);
|
628 | 683 |
|
629 |
| - if(enable_rich_annotations){ |
| 684 | + if (enable_rich_annotations){ |
630 | 685 | std::vector<std::string> annotations = annotate_variables(*inst);
|
631 | 686 | if (!annotations.empty()) {
|
632 | 687 | const size_t annotation_column = 100;
|
|
0 commit comments