1 //===-- BreakpointResolverFileLine.cpp --------------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "lldb/Breakpoint/BreakpointResolverFileLine.h" 11 12 // C Includes 13 // C++ Includes 14 // Other libraries and framework includes 15 // Project includes 16 #include "lldb/Breakpoint/BreakpointLocation.h" 17 #include "lldb/Core/Module.h" 18 #include "lldb/Symbol/CompileUnit.h" 19 #include "lldb/Symbol/Function.h" 20 #include "lldb/Utility/Log.h" 21 #include "lldb/Utility/StreamString.h" 22 23 using namespace lldb; 24 using namespace lldb_private; 25 26 //---------------------------------------------------------------------- 27 // BreakpointResolverFileLine: 28 //---------------------------------------------------------------------- 29 BreakpointResolverFileLine::BreakpointResolverFileLine( 30 Breakpoint *bkpt, const FileSpec &file_spec, uint32_t line_no, 31 lldb::addr_t offset, bool check_inlines, bool skip_prologue, 32 bool exact_match) 33 : BreakpointResolver(bkpt, BreakpointResolver::FileLineResolver, offset), 34 m_file_spec(file_spec), m_line_number(line_no), m_inlines(check_inlines), 35 m_skip_prologue(skip_prologue), m_exact_match(exact_match) {} 36 37 BreakpointResolverFileLine::~BreakpointResolverFileLine() {} 38 39 BreakpointResolver *BreakpointResolverFileLine::CreateFromStructuredData( 40 Breakpoint *bkpt, const StructuredData::Dictionary &options_dict, 41 Status &error) { 42 llvm::StringRef filename; 43 uint32_t line_no; 44 bool check_inlines; 45 bool skip_prologue; 46 bool exact_match; 47 bool success; 48 49 lldb::addr_t offset = 0; 50 51 success = options_dict.GetValueForKeyAsString(GetKey(OptionNames::FileName), 52 filename); 53 if (!success) { 54 error.SetErrorString("BRFL::CFSD: Couldn't find filename entry."); 55 return nullptr; 56 } 57 58 success = options_dict.GetValueForKeyAsInteger( 59 GetKey(OptionNames::LineNumber), line_no); 60 if (!success) { 61 error.SetErrorString("BRFL::CFSD: Couldn't find line number entry."); 62 return nullptr; 63 } 64 65 success = options_dict.GetValueForKeyAsBoolean(GetKey(OptionNames::Inlines), 66 check_inlines); 67 if (!success) { 68 error.SetErrorString("BRFL::CFSD: Couldn't find check inlines entry."); 69 return nullptr; 70 } 71 72 success = options_dict.GetValueForKeyAsBoolean( 73 GetKey(OptionNames::SkipPrologue), skip_prologue); 74 if (!success) { 75 error.SetErrorString("BRFL::CFSD: Couldn't find skip prologue entry."); 76 return nullptr; 77 } 78 79 success = options_dict.GetValueForKeyAsBoolean( 80 GetKey(OptionNames::ExactMatch), exact_match); 81 if (!success) { 82 error.SetErrorString("BRFL::CFSD: Couldn't find exact match entry."); 83 return nullptr; 84 } 85 86 FileSpec file_spec(filename, false); 87 88 return new BreakpointResolverFileLine(bkpt, file_spec, line_no, offset, 89 check_inlines, skip_prologue, 90 exact_match); 91 } 92 93 StructuredData::ObjectSP 94 BreakpointResolverFileLine::SerializeToStructuredData() { 95 StructuredData::DictionarySP options_dict_sp( 96 new StructuredData::Dictionary()); 97 98 options_dict_sp->AddStringItem(GetKey(OptionNames::FileName), 99 m_file_spec.GetPath()); 100 options_dict_sp->AddIntegerItem(GetKey(OptionNames::LineNumber), 101 m_line_number); 102 options_dict_sp->AddBooleanItem(GetKey(OptionNames::Inlines), m_inlines); 103 options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue), 104 m_skip_prologue); 105 options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch), 106 m_exact_match); 107 108 return WrapOptionsDict(options_dict_sp); 109 } 110 111 // Filter the symbol context list to remove contexts where the line number was 112 // moved into a new function. We do this conservatively, so if e.g. we cannot 113 // resolve the function in the context (which can happen in case of 114 // line-table-only debug info), we leave the context as is. The trickiest part 115 // here is handling inlined functions -- in this case we need to make sure we 116 // look at the declaration line of the inlined function, NOT the function it was 117 // inlined into. 118 void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list) { 119 if (m_exact_match) 120 return; // Nothing to do. Contexts are precise. 121 122 Log * log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS); 123 for(uint32_t i = 0; i < sc_list.GetSize(); ++i) { 124 SymbolContext sc; 125 sc_list.GetContextAtIndex(i, sc); 126 if (! sc.block) 127 continue; 128 129 FileSpec file; 130 uint32_t line; 131 const Block *inline_block = sc.block->GetContainingInlinedBlock(); 132 if (inline_block) { 133 const Declaration &inline_declaration = inline_block->GetInlinedFunctionInfo()->GetDeclaration(); 134 if (!inline_declaration.IsValid()) 135 continue; 136 file = inline_declaration.GetFile(); 137 line = inline_declaration.GetLine(); 138 } else if (sc.function) 139 sc.function->GetStartLineSourceInfo(file, line); 140 else 141 continue; 142 143 if (file != sc.line_entry.file) { 144 LLDB_LOG(log, "unexpected symbol context file {0}", sc.line_entry.file); 145 continue; 146 } 147 148 // Compare the requested line number with the line of the function 149 // declaration. In case of a function declared as: 150 // 151 // int 152 // foo() 153 // { 154 // ... 155 // 156 // the compiler will set the declaration line to the "foo" line, which is 157 // the reason why we have -1 here. This can fail in case of two inline 158 // functions defined back-to-back: 159 // 160 // inline int foo1() { ... } 161 // inline int foo2() { ... } 162 // 163 // but that's the best we can do for now. 164 const int decl_line_is_too_late_fudge = 1; 165 if (m_line_number < line - decl_line_is_too_late_fudge) { 166 LLDB_LOG(log, "removing symbol context at {0}:{1}", file, line); 167 sc_list.RemoveContextAtIndex(i); 168 --i; 169 } 170 } 171 } 172 173 Searcher::CallbackReturn 174 BreakpointResolverFileLine::SearchCallback(SearchFilter &filter, 175 SymbolContext &context, 176 Address *addr, bool containing) { 177 SymbolContextList sc_list; 178 179 assert(m_breakpoint != NULL); 180 181 // There is a tricky bit here. You can have two compilation units that 182 // #include the same file, and in one of them the function at m_line_number is 183 // used (and so code and a line entry for it is generated) but in the other it 184 // isn't. If we considered the CU's independently, then in the second 185 // inclusion, we'd move the breakpoint to the next function that actually 186 // generated code in the header file. That would end up being confusing. So 187 // instead, we do the CU iterations by hand here, then scan through the 188 // complete list of matches, and figure out the closest line number match, and 189 // only set breakpoints on that match. 190 191 // Note also that if file_spec only had a file name and not a directory, there 192 // may be many different file spec's in the resultant list. The closest line 193 // match for one will not be right for some totally different file. So we go 194 // through the match list and pull out the sets that have the same file spec 195 // in their line_entry and treat each set separately. 196 197 const size_t num_comp_units = context.module_sp->GetNumCompileUnits(); 198 for (size_t i = 0; i < num_comp_units; i++) { 199 CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i)); 200 if (cu_sp) { 201 if (filter.CompUnitPasses(*cu_sp)) 202 cu_sp->ResolveSymbolContext(m_file_spec, m_line_number, m_inlines, 203 m_exact_match, eSymbolContextEverything, 204 sc_list); 205 } 206 } 207 208 FilterContexts(sc_list); 209 210 StreamString s; 211 s.Printf("for %s:%d ", m_file_spec.GetFilename().AsCString("<Unknown>"), 212 m_line_number); 213 214 SetSCMatchesByLine(filter, sc_list, m_skip_prologue, s.GetString()); 215 216 return Searcher::eCallbackReturnContinue; 217 } 218 219 Searcher::Depth BreakpointResolverFileLine::GetDepth() { 220 return Searcher::eDepthModule; 221 } 222 223 void BreakpointResolverFileLine::GetDescription(Stream *s) { 224 s->Printf("file = '%s', line = %u, exact_match = %d", 225 m_file_spec.GetPath().c_str(), m_line_number, m_exact_match); 226 } 227 228 void BreakpointResolverFileLine::Dump(Stream *s) const {} 229 230 lldb::BreakpointResolverSP 231 BreakpointResolverFileLine::CopyForBreakpoint(Breakpoint &breakpoint) { 232 lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine( 233 &breakpoint, m_file_spec, m_line_number, m_offset, m_inlines, 234 m_skip_prologue, m_exact_match)); 235 236 return ret_sp; 237 } 238