@@ -379,6 +379,82 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
379379 }
380380 }
381381
382+ // Add rich variable location annotations to the disassembly output.
383+ //
384+ // For each instruction, this block attempts to resolve in-scope variables
385+ // and determine if the current PC falls within their
386+ // DWARF location entry. If so, it prints a simplified annotation using the
387+ // variable name and its resolved location (e.g., "var = reg; " ).
388+ //
389+ // Annotations are only included if the variable has a valid DWARF location
390+ // entry, and the location string is non-empty after filtering. Decoding
391+ // errors and DWARF opcodes are intentionally omitted to keep the output
392+ // concise and user-friendly.
393+ //
394+ // The goal is to give users helpful live variable hints alongside the
395+ // disassembled instruction stream, similar to how debug information
396+ // enhances source-level debugging.
397+
398+ auto annotate_variables = [&](Instruction &inst) -> std::vector<std::string> {
399+ std::vector<std::string> annotations;
400+
401+ StackFrame *frame = exe_ctx.GetFramePtr ();
402+ TargetSP target_sp = exe_ctx.GetTargetSP ();
403+ if (!frame || !target_sp)
404+ return annotations;
405+
406+ addr_t current_pc = inst.GetAddress ().GetLoadAddress (target_sp.get ());
407+ addr_t original_pc = frame->GetFrameCodeAddress ().GetLoadAddress (target_sp.get ());
408+
409+ if (!frame->ChangePC (current_pc))
410+ return annotations;
411+
412+ VariableListSP var_list_sp = frame->GetInScopeVariableList (true );
413+ if (!var_list_sp)
414+ return annotations;
415+
416+ SymbolContext sc = frame->GetSymbolContext (eSymbolContextFunction);
417+ addr_t func_load_addr = sc.function
418+ ? sc.function ->GetAddress ().GetLoadAddress (target_sp.get ())
419+ : LLDB_INVALID_ADDRESS;
420+
421+ for (const VariableSP &var_sp : *var_list_sp) {
422+ if (!var_sp)
423+ continue ;
424+
425+ const char *name = var_sp->GetName ().AsCString ();
426+ auto &expr_list = var_sp->LocationExpressionList ();
427+ if (!expr_list.IsValid ())
428+ continue ;
429+
430+ if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress (func_load_addr, current_pc)) {
431+ auto entry = *entryOrErr;
432+
433+ if (!entry.file_range ||
434+ entry.file_range ->ContainsFileAddress (
435+ (current_pc - func_load_addr) + expr_list.GetFuncFileAddress ())) {
436+
437+ StreamString loc_str;
438+ ABI *abi = exe_ctx.GetProcessPtr ()->GetABI ().get ();
439+ llvm::DIDumpOptions opts;
440+ opts.ShowAddresses = false ;
441+ opts.PrintRegisterOnly = true ;
442+
443+ entry.expr ->DumpLocation (&loc_str, eDescriptionLevelBrief, abi, opts);
444+
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+ }
449+ }
450+ }
451+ }
452+
453+ frame->ChangePC (original_pc);
454+ return annotations;
455+ };
456+
457+
382458 previous_symbol = nullptr ;
383459 SourceLine previous_line;
384460 for (size_t i = 0 ; i < num_instructions_found; ++i) {
@@ -543,10 +619,25 @@ void Disassembler::PrintInstructions(Debugger &debugger, const ArchSpec &arch,
543619 const bool show_bytes = (options & eOptionShowBytes) != 0 ;
544620 const bool show_control_flow_kind =
545621 (options & eOptionShowControlFlowKind) != 0 ;
546- inst->Dump (&strm, max_opcode_byte_size, true , show_bytes,
547- show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr ,
548- address_text_size);
622+
623+ StreamString inst_line;
624+
625+ inst->Dump (&inst_line, max_opcode_byte_size, true , show_bytes,
626+ show_control_flow_kind, &exe_ctx, &sc, &prev_sc, nullptr ,
627+ address_text_size);
628+
629+ std::vector<std::string> annotations = annotate_variables (*inst);
630+ if (!annotations.empty ()) {
631+ const size_t annotation_column = 100 ;
632+ inst_line.FillLastLineToColumn (annotation_column, ' ' );
633+ inst_line.PutCString (" ; " );
634+ inst_line.PutCString (llvm::join (annotations, " , " ));
635+ }
636+
637+ strm.PutCString (inst_line.GetString ());
549638 strm.EOL ();
639+
640+
550641 } else {
551642 break ;
552643 }
@@ -707,104 +798,6 @@ void Instruction::Dump(lldb_private::Stream *s, uint32_t max_opcode_byte_size,
707798 ss.FillLastLineToColumn (opcode_pos + opcode_column_width, ' ' );
708799 ss.PutCString (mnemonics);
709800
710- // Add rich variable location annotations to the disassembly output.
711- //
712- // For each instruction, this block attempts to resolve in-scope variables
713- // and determine if the current PC falls within their
714- // DWARF location entry. If so, it prints a simplified annotation using the
715- // variable name and its resolved location (e.g., "var = reg; " ).
716- //
717- // Annotations are only included if the variable has a valid DWARF location
718- // entry, and the location string is non-empty after filtering. Decoding
719- // errors and DWARF opcodes are intentionally omitted to keep the output
720- // concise and user-friendly.
721- //
722- // The goal is to give users helpful live variable hints alongside the
723- // disassembled instruction stream, similar to how debug information
724- // enhances source-level debugging.
725-
726- const size_t annotation_column = 150 ;
727-
728- auto annotate_variables = [&]() {
729- StackFrame *frame = exe_ctx->GetFramePtr ();
730- TargetSP target_sp = exe_ctx->GetTargetSP ();
731- if (!frame || !target_sp)
732- return ;
733-
734- addr_t current_pc = m_address.GetLoadAddress (target_sp.get ());
735- addr_t original_pc =
736- frame->GetFrameCodeAddress ().GetLoadAddress (target_sp.get ());
737-
738- if (!frame->ChangePC (current_pc))
739- return ;
740-
741- VariableListSP var_list_sp = frame->GetInScopeVariableList (true );
742- if (!var_list_sp)
743- return ;
744-
745- SymbolContext sc = frame->GetSymbolContext (eSymbolContextFunction);
746- addr_t func_load_addr = LLDB_INVALID_ADDRESS;
747- if (sc.function )
748- func_load_addr =
749- sc.function ->GetAddress ().GetLoadAddress (target_sp.get ());
750-
751- // Only annotate if the current disassembly line is short enough
752- // to keep annotations aligned past the desired annotation_column.
753- if (ss.GetSizeOfLastLine () >= annotation_column)
754- return ;
755-
756- std::vector<std::string> annotations;
757-
758- for (const VariableSP &var_sp : *var_list_sp) {
759- if (!var_sp)
760- continue ;
761-
762- const char *name = var_sp->GetName ().AsCString ();
763- auto &expr_list = var_sp->LocationExpressionList ();
764- if (!expr_list.IsValid ())
765- continue ;
766-
767- // Handle std::optional<DWARFExpressionEntry>.
768- if (auto entryOrErr = expr_list.GetExpressionEntryAtAddress (
769- func_load_addr, current_pc)) {
770- auto entry = *entryOrErr;
771- // Check if entry has a file_range, and filter on address if so.
772- if (!entry.file_range || entry.file_range ->ContainsFileAddress (
773- (current_pc - func_load_addr) +
774- expr_list.GetFuncFileAddress ())) {
775-
776- StreamString loc_str;
777- ABI *abi = exe_ctx->GetProcessPtr ()->GetABI ().get ();
778- llvm::DIDumpOptions opts;
779- opts.ShowAddresses = false ;
780- opts.PrintRegisterOnly =
781- true ; // <-- important: suppress DW_OP_... annotations, etc.
782-
783- entry.expr ->DumpLocation (&loc_str, eDescriptionLevelBrief, abi, opts);
784-
785- // Only include if not empty.
786- llvm::StringRef loc_clean =
787- llvm::StringRef (loc_str.GetString ()).trim ();
788- if (!loc_clean.empty ()) {
789- annotations.push_back (llvm::formatv (" {0} = {1}" , name, loc_clean));
790- }
791- }
792- }
793- }
794-
795- if (!annotations.empty ()) {
796- ss.FillLastLineToColumn (annotation_column, ' ' );
797- ss.PutCString (" ; " );
798- ss.PutCString (llvm::join (annotations, " , " ));
799- }
800-
801- frame->ChangePC (original_pc);
802- };
803-
804- if (exe_ctx && exe_ctx->GetFramePtr ()) {
805- annotate_variables ();
806- }
807-
808801 if (!m_comment.empty ()) {
809802 ss.FillLastLineToColumn (
810803 opcode_pos + opcode_column_width + operand_column_width, ' ' );
0 commit comments