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 uint32_t column, lldb::addr_t offset, bool check_inlines, 32 bool skip_prologue, bool exact_match) 33 : BreakpointResolver(bkpt, BreakpointResolver::FileLineResolver, offset), 34 m_file_spec(file_spec), m_line_number(line_no), m_column(column), 35 m_inlines(check_inlines), m_skip_prologue(skip_prologue), 36 m_exact_match(exact_match) {} 37 38 BreakpointResolverFileLine::~BreakpointResolverFileLine() {} 39 40 BreakpointResolver *BreakpointResolverFileLine::CreateFromStructuredData( 41 Breakpoint *bkpt, const StructuredData::Dictionary &options_dict, 42 Status &error) { 43 llvm::StringRef filename; 44 uint32_t line_no; 45 uint32_t column; 46 bool check_inlines; 47 bool skip_prologue; 48 bool exact_match; 49 bool success; 50 51 lldb::addr_t offset = 0; 52 53 success = options_dict.GetValueForKeyAsString(GetKey(OptionNames::FileName), 54 filename); 55 if (!success) { 56 error.SetErrorString("BRFL::CFSD: Couldn't find filename entry."); 57 return nullptr; 58 } 59 60 success = options_dict.GetValueForKeyAsInteger( 61 GetKey(OptionNames::LineNumber), line_no); 62 if (!success) { 63 error.SetErrorString("BRFL::CFSD: Couldn't find line number entry."); 64 return nullptr; 65 } 66 67 success = 68 options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Column), column); 69 if (!success) { 70 // Backwards compatibility. 71 column = 0; 72 } 73 74 success = options_dict.GetValueForKeyAsBoolean(GetKey(OptionNames::Inlines), 75 check_inlines); 76 if (!success) { 77 error.SetErrorString("BRFL::CFSD: Couldn't find check inlines entry."); 78 return nullptr; 79 } 80 81 success = options_dict.GetValueForKeyAsBoolean( 82 GetKey(OptionNames::SkipPrologue), skip_prologue); 83 if (!success) { 84 error.SetErrorString("BRFL::CFSD: Couldn't find skip prologue entry."); 85 return nullptr; 86 } 87 88 success = options_dict.GetValueForKeyAsBoolean( 89 GetKey(OptionNames::ExactMatch), exact_match); 90 if (!success) { 91 error.SetErrorString("BRFL::CFSD: Couldn't find exact match entry."); 92 return nullptr; 93 } 94 95 FileSpec file_spec(filename); 96 97 return new BreakpointResolverFileLine(bkpt, file_spec, line_no, column, 98 offset, check_inlines, skip_prologue, 99 exact_match); 100 } 101 102 StructuredData::ObjectSP 103 BreakpointResolverFileLine::SerializeToStructuredData() { 104 StructuredData::DictionarySP options_dict_sp( 105 new StructuredData::Dictionary()); 106 107 options_dict_sp->AddStringItem(GetKey(OptionNames::FileName), 108 m_file_spec.GetPath()); 109 options_dict_sp->AddIntegerItem(GetKey(OptionNames::LineNumber), 110 m_line_number); 111 options_dict_sp->AddIntegerItem(GetKey(OptionNames::Column), 112 m_column); 113 options_dict_sp->AddBooleanItem(GetKey(OptionNames::Inlines), m_inlines); 114 options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue), 115 m_skip_prologue); 116 options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch), 117 m_exact_match); 118 119 return WrapOptionsDict(options_dict_sp); 120 } 121 122 // Filter the symbol context list to remove contexts where the line number was 123 // moved into a new function. We do this conservatively, so if e.g. we cannot 124 // resolve the function in the context (which can happen in case of line-table- 125 // only debug info), we leave the context as is. The trickiest part here is 126 // handling inlined functions -- in this case we need to make sure we look at 127 // the declaration line of the inlined function, NOT the function it was 128 // inlined into. 129 void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list, 130 bool is_relative) { 131 if (m_exact_match) 132 return; // Nothing to do. Contexts are precise. 133 134 llvm::StringRef relative_path; 135 if (is_relative) 136 relative_path = m_file_spec.GetDirectory().GetStringRef(); 137 138 Log * log = GetLogIfAllCategoriesSet(LIBLLDB_LOG_BREAKPOINTS); 139 for(uint32_t i = 0; i < sc_list.GetSize(); ++i) { 140 SymbolContext sc; 141 sc_list.GetContextAtIndex(i, sc); 142 if (is_relative) { 143 // If the path was relative, make sure any matches match as long as the 144 // relative parts of the path match the path from support files 145 auto sc_dir = sc.line_entry.file.GetDirectory().GetStringRef(); 146 if (!sc_dir.endswith(relative_path)) { 147 // We had a relative path specified and the relative directory doesn't 148 // match so remove this one 149 LLDB_LOG(log, "removing not matching relative path {0} since it " 150 "doesn't end with {1}", sc_dir, relative_path); 151 sc_list.RemoveContextAtIndex(i); 152 --i; 153 continue; 154 } 155 } 156 157 if (!sc.block) 158 continue; 159 160 FileSpec file; 161 uint32_t line; 162 const Block *inline_block = sc.block->GetContainingInlinedBlock(); 163 if (inline_block) { 164 const Declaration &inline_declaration = inline_block->GetInlinedFunctionInfo()->GetDeclaration(); 165 if (!inline_declaration.IsValid()) 166 continue; 167 file = inline_declaration.GetFile(); 168 line = inline_declaration.GetLine(); 169 } else if (sc.function) 170 sc.function->GetStartLineSourceInfo(file, line); 171 else 172 continue; 173 174 if (file != sc.line_entry.file) { 175 LLDB_LOG(log, "unexpected symbol context file {0}", sc.line_entry.file); 176 continue; 177 } 178 179 // Compare the requested line number with the line of the function 180 // declaration. In case of a function declared as: 181 // 182 // int 183 // foo() 184 // { 185 // ... 186 // 187 // the compiler will set the declaration line to the "foo" line, which is 188 // the reason why we have -1 here. This can fail in case of two inline 189 // functions defined back-to-back: 190 // 191 // inline int foo1() { ... } 192 // inline int foo2() { ... } 193 // 194 // but that's the best we can do for now. 195 // One complication, if the line number returned from GetStartLineSourceInfo 196 // is 0, then we can't do this calculation. That can happen if 197 // GetStartLineSourceInfo gets an error, or if the first line number in 198 // the function really is 0 - which happens for some languages. 199 const int decl_line_is_too_late_fudge = 1; 200 if (line && m_line_number < line - decl_line_is_too_late_fudge) { 201 LLDB_LOG(log, "removing symbol context at {0}:{1}", file, line); 202 sc_list.RemoveContextAtIndex(i); 203 --i; 204 } 205 } 206 } 207 208 Searcher::CallbackReturn 209 BreakpointResolverFileLine::SearchCallback(SearchFilter &filter, 210 SymbolContext &context, 211 Address *addr, bool containing) { 212 SymbolContextList sc_list; 213 214 assert(m_breakpoint != NULL); 215 216 // There is a tricky bit here. You can have two compilation units that 217 // #include the same file, and in one of them the function at m_line_number 218 // is used (and so code and a line entry for it is generated) but in the 219 // other it isn't. If we considered the CU's independently, then in the 220 // second inclusion, we'd move the breakpoint to the next function that 221 // actually generated code in the header file. That would end up being 222 // confusing. So instead, we do the CU iterations by hand here, then scan 223 // through the complete list of matches, and figure out the closest line 224 // number match, and only set breakpoints on that match. 225 226 // Note also that if file_spec only had a file name and not a directory, 227 // there may be many different file spec's in the resultant list. The 228 // closest line match for one will not be right for some totally different 229 // file. So we go through the match list and pull out the sets that have the 230 // same file spec in their line_entry and treat each set separately. 231 232 FileSpec search_file_spec = m_file_spec; 233 const bool is_relative = m_file_spec.IsRelative(); 234 if (is_relative) 235 search_file_spec.GetDirectory().Clear(); 236 237 const size_t num_comp_units = context.module_sp->GetNumCompileUnits(); 238 for (size_t i = 0; i < num_comp_units; i++) { 239 CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i)); 240 if (cu_sp) { 241 if (filter.CompUnitPasses(*cu_sp)) 242 cu_sp->ResolveSymbolContext(search_file_spec, m_line_number, m_inlines, 243 m_exact_match, eSymbolContextEverything, 244 sc_list); 245 } 246 } 247 248 FilterContexts(sc_list, is_relative); 249 250 StreamString s; 251 s.Printf("for %s:%d ", m_file_spec.GetFilename().AsCString("<Unknown>"), 252 m_line_number); 253 254 SetSCMatchesByLine(filter, sc_list, m_skip_prologue, s.GetString(), 255 m_line_number, m_column); 256 257 return Searcher::eCallbackReturnContinue; 258 } 259 260 lldb::SearchDepth BreakpointResolverFileLine::GetDepth() { 261 return lldb::eSearchDepthModule; 262 } 263 264 void BreakpointResolverFileLine::GetDescription(Stream *s) { 265 s->Printf("file = '%s', line = %u, ", m_file_spec.GetPath().c_str(), 266 m_line_number); 267 if (m_column) 268 s->Printf("column = %u, ", m_column); 269 s->Printf("exact_match = %d", m_exact_match); 270 } 271 272 void BreakpointResolverFileLine::Dump(Stream *s) const {} 273 274 lldb::BreakpointResolverSP 275 BreakpointResolverFileLine::CopyForBreakpoint(Breakpoint &breakpoint) { 276 lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine( 277 &breakpoint, m_file_spec, m_line_number, m_column, m_offset, m_inlines, 278 m_skip_prologue, m_exact_match)); 279 280 return ret_sp; 281 } 282