15ffd83dbSDimitry Andric //===-- BreakpointResolverFileLine.cpp ------------------------------------===//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric 
90b57cec5SDimitry Andric #include "lldb/Breakpoint/BreakpointResolverFileLine.h"
100b57cec5SDimitry Andric 
110b57cec5SDimitry Andric #include "lldb/Breakpoint/BreakpointLocation.h"
120b57cec5SDimitry Andric #include "lldb/Core/Module.h"
130b57cec5SDimitry Andric #include "lldb/Symbol/CompileUnit.h"
140b57cec5SDimitry Andric #include "lldb/Symbol/Function.h"
15bdd1243dSDimitry Andric #include "lldb/Target/Target.h"
1681ad6265SDimitry Andric #include "lldb/Utility/LLDBLog.h"
170b57cec5SDimitry Andric #include "lldb/Utility/Log.h"
180b57cec5SDimitry Andric #include "lldb/Utility/StreamString.h"
19bdd1243dSDimitry Andric #include <optional>
200b57cec5SDimitry Andric 
210b57cec5SDimitry Andric using namespace lldb;
220b57cec5SDimitry Andric using namespace lldb_private;
230b57cec5SDimitry Andric 
240b57cec5SDimitry Andric // BreakpointResolverFileLine:
BreakpointResolverFileLine(const BreakpointSP & bkpt,lldb::addr_t offset,bool skip_prologue,const SourceLocationSpec & location_spec,std::optional<llvm::StringRef> removed_prefix_opt)250b57cec5SDimitry Andric BreakpointResolverFileLine::BreakpointResolverFileLine(
26fe6060f1SDimitry Andric     const BreakpointSP &bkpt, lldb::addr_t offset, bool skip_prologue,
27bdd1243dSDimitry Andric     const SourceLocationSpec &location_spec,
28bdd1243dSDimitry Andric     std::optional<llvm::StringRef> removed_prefix_opt)
290b57cec5SDimitry Andric     : BreakpointResolver(bkpt, BreakpointResolver::FileLineResolver, offset),
30bdd1243dSDimitry Andric       m_location_spec(location_spec), m_skip_prologue(skip_prologue),
31bdd1243dSDimitry Andric       m_removed_prefix_opt(removed_prefix_opt) {}
320b57cec5SDimitry Andric 
CreateFromStructuredData(const StructuredData::Dictionary & options_dict,Status & error)33*c9157d92SDimitry Andric BreakpointResolverSP BreakpointResolverFileLine::CreateFromStructuredData(
34*c9157d92SDimitry Andric     const StructuredData::Dictionary &options_dict, Status &error) {
350b57cec5SDimitry Andric   llvm::StringRef filename;
36fe6060f1SDimitry Andric   uint32_t line;
37fe6060f1SDimitry Andric   uint16_t column;
380b57cec5SDimitry Andric   bool check_inlines;
390b57cec5SDimitry Andric   bool skip_prologue;
400b57cec5SDimitry Andric   bool exact_match;
410b57cec5SDimitry Andric   bool success;
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric   lldb::addr_t offset = 0;
440b57cec5SDimitry Andric 
450b57cec5SDimitry Andric   success = options_dict.GetValueForKeyAsString(GetKey(OptionNames::FileName),
460b57cec5SDimitry Andric                                                 filename);
470b57cec5SDimitry Andric   if (!success) {
480b57cec5SDimitry Andric     error.SetErrorString("BRFL::CFSD: Couldn't find filename entry.");
490b57cec5SDimitry Andric     return nullptr;
500b57cec5SDimitry Andric   }
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric   success = options_dict.GetValueForKeyAsInteger(
53fe6060f1SDimitry Andric       GetKey(OptionNames::LineNumber), line);
540b57cec5SDimitry Andric   if (!success) {
550b57cec5SDimitry Andric     error.SetErrorString("BRFL::CFSD: Couldn't find line number entry.");
560b57cec5SDimitry Andric     return nullptr;
570b57cec5SDimitry Andric   }
580b57cec5SDimitry Andric 
590b57cec5SDimitry Andric   success =
600b57cec5SDimitry Andric       options_dict.GetValueForKeyAsInteger(GetKey(OptionNames::Column), column);
610b57cec5SDimitry Andric   if (!success) {
620b57cec5SDimitry Andric     // Backwards compatibility.
630b57cec5SDimitry Andric     column = 0;
640b57cec5SDimitry Andric   }
650b57cec5SDimitry Andric 
660b57cec5SDimitry Andric   success = options_dict.GetValueForKeyAsBoolean(GetKey(OptionNames::Inlines),
670b57cec5SDimitry Andric                                                  check_inlines);
680b57cec5SDimitry Andric   if (!success) {
690b57cec5SDimitry Andric     error.SetErrorString("BRFL::CFSD: Couldn't find check inlines entry.");
700b57cec5SDimitry Andric     return nullptr;
710b57cec5SDimitry Andric   }
720b57cec5SDimitry Andric 
730b57cec5SDimitry Andric   success = options_dict.GetValueForKeyAsBoolean(
740b57cec5SDimitry Andric       GetKey(OptionNames::SkipPrologue), skip_prologue);
750b57cec5SDimitry Andric   if (!success) {
760b57cec5SDimitry Andric     error.SetErrorString("BRFL::CFSD: Couldn't find skip prologue entry.");
770b57cec5SDimitry Andric     return nullptr;
780b57cec5SDimitry Andric   }
790b57cec5SDimitry Andric 
800b57cec5SDimitry Andric   success = options_dict.GetValueForKeyAsBoolean(
810b57cec5SDimitry Andric       GetKey(OptionNames::ExactMatch), exact_match);
820b57cec5SDimitry Andric   if (!success) {
830b57cec5SDimitry Andric     error.SetErrorString("BRFL::CFSD: Couldn't find exact match entry.");
840b57cec5SDimitry Andric     return nullptr;
850b57cec5SDimitry Andric   }
860b57cec5SDimitry Andric 
87fe6060f1SDimitry Andric   SourceLocationSpec location_spec(FileSpec(filename), line, column,
88fe6060f1SDimitry Andric                                    check_inlines, exact_match);
89fe6060f1SDimitry Andric   if (!location_spec)
90fe6060f1SDimitry Andric     return nullptr;
910b57cec5SDimitry Andric 
92*c9157d92SDimitry Andric   return std::make_shared<BreakpointResolverFileLine>(
93*c9157d92SDimitry Andric       nullptr, offset, skip_prologue, location_spec);
940b57cec5SDimitry Andric }
950b57cec5SDimitry Andric 
960b57cec5SDimitry Andric StructuredData::ObjectSP
SerializeToStructuredData()970b57cec5SDimitry Andric BreakpointResolverFileLine::SerializeToStructuredData() {
980b57cec5SDimitry Andric   StructuredData::DictionarySP options_dict_sp(
990b57cec5SDimitry Andric       new StructuredData::Dictionary());
1000b57cec5SDimitry Andric 
1010b57cec5SDimitry Andric   options_dict_sp->AddBooleanItem(GetKey(OptionNames::SkipPrologue),
1020b57cec5SDimitry Andric                                   m_skip_prologue);
103fe6060f1SDimitry Andric   options_dict_sp->AddStringItem(GetKey(OptionNames::FileName),
104fe6060f1SDimitry Andric                                  m_location_spec.GetFileSpec().GetPath());
105fe6060f1SDimitry Andric   options_dict_sp->AddIntegerItem(GetKey(OptionNames::LineNumber),
10681ad6265SDimitry Andric                                   m_location_spec.GetLine().value_or(0));
107fe6060f1SDimitry Andric   options_dict_sp->AddIntegerItem(
108fe6060f1SDimitry Andric       GetKey(OptionNames::Column),
10981ad6265SDimitry Andric       m_location_spec.GetColumn().value_or(LLDB_INVALID_COLUMN_NUMBER));
110fe6060f1SDimitry Andric   options_dict_sp->AddBooleanItem(GetKey(OptionNames::Inlines),
111fe6060f1SDimitry Andric                                   m_location_spec.GetCheckInlines());
1120b57cec5SDimitry Andric   options_dict_sp->AddBooleanItem(GetKey(OptionNames::ExactMatch),
113fe6060f1SDimitry Andric                                   m_location_spec.GetExactMatch());
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   return WrapOptionsDict(options_dict_sp);
1160b57cec5SDimitry Andric }
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric // Filter the symbol context list to remove contexts where the line number was
1190b57cec5SDimitry Andric // moved into a new function. We do this conservatively, so if e.g. we cannot
1200b57cec5SDimitry Andric // resolve the function in the context (which can happen in case of line-table-
1210b57cec5SDimitry Andric // only debug info), we leave the context as is. The trickiest part here is
1220b57cec5SDimitry Andric // handling inlined functions -- in this case we need to make sure we look at
1230b57cec5SDimitry Andric // the declaration line of the inlined function, NOT the function it was
1240b57cec5SDimitry Andric // inlined into.
FilterContexts(SymbolContextList & sc_list)125bdd1243dSDimitry Andric void BreakpointResolverFileLine::FilterContexts(SymbolContextList &sc_list) {
126fe6060f1SDimitry Andric   if (m_location_spec.GetExactMatch())
1270b57cec5SDimitry Andric     return; // Nothing to do. Contexts are precise.
1280b57cec5SDimitry Andric 
12981ad6265SDimitry Andric   Log *log = GetLog(LLDBLog::Breakpoints);
1300b57cec5SDimitry Andric   for(uint32_t i = 0; i < sc_list.GetSize(); ++i) {
1310b57cec5SDimitry Andric     SymbolContext sc;
1320b57cec5SDimitry Andric     sc_list.GetContextAtIndex(i, sc);
1330b57cec5SDimitry Andric     if (!sc.block)
1340b57cec5SDimitry Andric       continue;
1350b57cec5SDimitry Andric 
1360b57cec5SDimitry Andric     FileSpec file;
1370b57cec5SDimitry Andric     uint32_t line;
1380b57cec5SDimitry Andric     const Block *inline_block = sc.block->GetContainingInlinedBlock();
1390b57cec5SDimitry Andric     if (inline_block) {
1400b57cec5SDimitry Andric       const Declaration &inline_declaration = inline_block->GetInlinedFunctionInfo()->GetDeclaration();
1410b57cec5SDimitry Andric       if (!inline_declaration.IsValid())
1420b57cec5SDimitry Andric         continue;
1430b57cec5SDimitry Andric       file = inline_declaration.GetFile();
1440b57cec5SDimitry Andric       line = inline_declaration.GetLine();
1450b57cec5SDimitry Andric     } else if (sc.function)
1460b57cec5SDimitry Andric       sc.function->GetStartLineSourceInfo(file, line);
1470b57cec5SDimitry Andric     else
1480b57cec5SDimitry Andric       continue;
1490b57cec5SDimitry Andric 
1500b57cec5SDimitry Andric     if (file != sc.line_entry.file) {
1510b57cec5SDimitry Andric       LLDB_LOG(log, "unexpected symbol context file {0}", sc.line_entry.file);
1520b57cec5SDimitry Andric       continue;
1530b57cec5SDimitry Andric     }
1540b57cec5SDimitry Andric 
1550b57cec5SDimitry Andric     // Compare the requested line number with the line of the function
1560b57cec5SDimitry Andric     // declaration. In case of a function declared as:
1570b57cec5SDimitry Andric     //
1580b57cec5SDimitry Andric     // int
1590b57cec5SDimitry Andric     // foo()
1600b57cec5SDimitry Andric     // {
1610b57cec5SDimitry Andric     //   ...
1620b57cec5SDimitry Andric     //
1630b57cec5SDimitry Andric     // the compiler will set the declaration line to the "foo" line, which is
1640b57cec5SDimitry Andric     // the reason why we have -1 here. This can fail in case of two inline
1650b57cec5SDimitry Andric     // functions defined back-to-back:
1660b57cec5SDimitry Andric     //
1670b57cec5SDimitry Andric     // inline int foo1() { ... }
1680b57cec5SDimitry Andric     // inline int foo2() { ... }
1690b57cec5SDimitry Andric     //
1700b57cec5SDimitry Andric     // but that's the best we can do for now.
1710b57cec5SDimitry Andric     // One complication, if the line number returned from GetStartLineSourceInfo
1720b57cec5SDimitry Andric     // is 0, then we can't do this calculation.  That can happen if
1730b57cec5SDimitry Andric     // GetStartLineSourceInfo gets an error, or if the first line number in
1740b57cec5SDimitry Andric     // the function really is 0 - which happens for some languages.
175e8d8bef9SDimitry Andric 
176e8d8bef9SDimitry Andric     // But only do this calculation if the line number we found in the SC
177e8d8bef9SDimitry Andric     // was different from the one requested in the source file.  If we actually
178e8d8bef9SDimitry Andric     // found an exact match it must be valid.
179e8d8bef9SDimitry Andric 
180fe6060f1SDimitry Andric     if (m_location_spec.GetLine() == sc.line_entry.line)
181e8d8bef9SDimitry Andric       continue;
182e8d8bef9SDimitry Andric 
1830b57cec5SDimitry Andric     const int decl_line_is_too_late_fudge = 1;
184fe6060f1SDimitry Andric     if (line &&
185fe6060f1SDimitry Andric         m_location_spec.GetLine() < line - decl_line_is_too_late_fudge) {
1860b57cec5SDimitry Andric       LLDB_LOG(log, "removing symbol context at {0}:{1}", file, line);
1870b57cec5SDimitry Andric       sc_list.RemoveContextAtIndex(i);
1880b57cec5SDimitry Andric       --i;
1890b57cec5SDimitry Andric     }
1900b57cec5SDimitry Andric   }
1910b57cec5SDimitry Andric }
1920b57cec5SDimitry Andric 
DeduceSourceMapping(const SymbolContextList & sc_list)193bdd1243dSDimitry Andric void BreakpointResolverFileLine::DeduceSourceMapping(
194fe013be4SDimitry Andric     const SymbolContextList &sc_list) {
195bdd1243dSDimitry Andric   Target &target = GetBreakpoint()->GetTarget();
196bdd1243dSDimitry Andric   if (!target.GetAutoSourceMapRelative())
197bdd1243dSDimitry Andric     return;
198bdd1243dSDimitry Andric 
199bdd1243dSDimitry Andric   Log *log = GetLog(LLDBLog::Breakpoints);
200bdd1243dSDimitry Andric   const llvm::StringRef path_separator = llvm::sys::path::get_separator(
201bdd1243dSDimitry Andric       m_location_spec.GetFileSpec().GetPathStyle());
202bdd1243dSDimitry Andric   // Check if "b" is a suffix of "a".
203bdd1243dSDimitry Andric   // And return std::nullopt if not or the new path
204bdd1243dSDimitry Andric   // of "a" after consuming "b" from the back.
205bdd1243dSDimitry Andric   auto check_suffix =
206bdd1243dSDimitry Andric       [path_separator](llvm::StringRef a, llvm::StringRef b,
207bdd1243dSDimitry Andric                        bool case_sensitive) -> std::optional<llvm::StringRef> {
208bdd1243dSDimitry Andric     if (case_sensitive ? a.consume_back(b) : a.consume_back_insensitive(b)) {
209*c9157d92SDimitry Andric       if (a.empty() || a.ends_with(path_separator)) {
210bdd1243dSDimitry Andric         return a;
211bdd1243dSDimitry Andric       }
212bdd1243dSDimitry Andric     }
213bdd1243dSDimitry Andric     return std::nullopt;
214bdd1243dSDimitry Andric   };
215bdd1243dSDimitry Andric 
216bdd1243dSDimitry Andric   FileSpec request_file = m_location_spec.GetFileSpec();
217bdd1243dSDimitry Andric 
218bdd1243dSDimitry Andric   // Only auto deduce source map if breakpoint is full path.
219bdd1243dSDimitry Andric   // Note: an existing source map reverse mapping (m_removed_prefix_opt has
220bdd1243dSDimitry Andric   // value) may make request_file relative.
221bdd1243dSDimitry Andric   if (!m_removed_prefix_opt.has_value() && request_file.IsRelative())
222bdd1243dSDimitry Andric     return;
223bdd1243dSDimitry Andric 
224bdd1243dSDimitry Andric   const bool case_sensitive = request_file.IsCaseSensitive();
225fe013be4SDimitry Andric   for (const SymbolContext &sc : sc_list) {
226bdd1243dSDimitry Andric     FileSpec sc_file = sc.line_entry.file;
227bdd1243dSDimitry Andric 
228bdd1243dSDimitry Andric     if (FileSpec::Equal(sc_file, request_file, /*full*/ true))
229bdd1243dSDimitry Andric       continue;
230bdd1243dSDimitry Andric 
231bdd1243dSDimitry Andric     llvm::StringRef sc_file_dir = sc_file.GetDirectory().GetStringRef();
232bdd1243dSDimitry Andric     llvm::StringRef request_file_dir =
233bdd1243dSDimitry Andric         request_file.GetDirectory().GetStringRef();
234bdd1243dSDimitry Andric 
235bdd1243dSDimitry Andric     llvm::StringRef new_mapping_from;
236bdd1243dSDimitry Andric     llvm::SmallString<256> new_mapping_to;
237bdd1243dSDimitry Andric 
238bdd1243dSDimitry Andric     // Adding back any potentially reverse mapping stripped prefix.
239bdd1243dSDimitry Andric     // for new_mapping_to.
240bdd1243dSDimitry Andric     if (m_removed_prefix_opt.has_value())
241bdd1243dSDimitry Andric       llvm::sys::path::append(new_mapping_to, *m_removed_prefix_opt);
242bdd1243dSDimitry Andric 
243bdd1243dSDimitry Andric     std::optional<llvm::StringRef> new_mapping_from_opt =
244bdd1243dSDimitry Andric         check_suffix(sc_file_dir, request_file_dir, case_sensitive);
245bdd1243dSDimitry Andric     if (new_mapping_from_opt) {
246bdd1243dSDimitry Andric       new_mapping_from = *new_mapping_from_opt;
247bdd1243dSDimitry Andric       if (new_mapping_to.empty())
248bdd1243dSDimitry Andric         new_mapping_to = ".";
249bdd1243dSDimitry Andric     } else {
250bdd1243dSDimitry Andric       std::optional<llvm::StringRef> new_mapping_to_opt =
251bdd1243dSDimitry Andric           check_suffix(request_file_dir, sc_file_dir, case_sensitive);
252bdd1243dSDimitry Andric       if (new_mapping_to_opt) {
253bdd1243dSDimitry Andric         new_mapping_from = ".";
254bdd1243dSDimitry Andric         llvm::sys::path::append(new_mapping_to, *new_mapping_to_opt);
255bdd1243dSDimitry Andric       }
256bdd1243dSDimitry Andric     }
257bdd1243dSDimitry Andric 
258bdd1243dSDimitry Andric     if (!new_mapping_from.empty() && !new_mapping_to.empty()) {
259bdd1243dSDimitry Andric       LLDB_LOG(log, "generating auto source map from {0} to {1}",
260bdd1243dSDimitry Andric                new_mapping_from, new_mapping_to);
261bdd1243dSDimitry Andric       if (target.GetSourcePathMap().AppendUnique(new_mapping_from,
262bdd1243dSDimitry Andric                                                  new_mapping_to,
263bdd1243dSDimitry Andric                                                  /*notify*/ true))
264bdd1243dSDimitry Andric         target.GetStatistics().IncreaseSourceMapDeduceCount();
265bdd1243dSDimitry Andric     }
266bdd1243dSDimitry Andric   }
267bdd1243dSDimitry Andric }
268bdd1243dSDimitry Andric 
SearchCallback(SearchFilter & filter,SymbolContext & context,Address * addr)2699dba64beSDimitry Andric Searcher::CallbackReturn BreakpointResolverFileLine::SearchCallback(
2709dba64beSDimitry Andric     SearchFilter &filter, SymbolContext &context, Address *addr) {
2710b57cec5SDimitry Andric   SymbolContextList sc_list;
2720b57cec5SDimitry Andric 
2730b57cec5SDimitry Andric   // There is a tricky bit here.  You can have two compilation units that
2740b57cec5SDimitry Andric   // #include the same file, and in one of them the function at m_line_number
2750b57cec5SDimitry Andric   // is used (and so code and a line entry for it is generated) but in the
2760b57cec5SDimitry Andric   // other it isn't.  If we considered the CU's independently, then in the
2770b57cec5SDimitry Andric   // second inclusion, we'd move the breakpoint to the next function that
2780b57cec5SDimitry Andric   // actually generated code in the header file.  That would end up being
2790b57cec5SDimitry Andric   // confusing.  So instead, we do the CU iterations by hand here, then scan
2800b57cec5SDimitry Andric   // through the complete list of matches, and figure out the closest line
2810b57cec5SDimitry Andric   // number match, and only set breakpoints on that match.
2820b57cec5SDimitry Andric 
2830b57cec5SDimitry Andric   // Note also that if file_spec only had a file name and not a directory,
2840b57cec5SDimitry Andric   // there may be many different file spec's in the resultant list.  The
2850b57cec5SDimitry Andric   // closest line match for one will not be right for some totally different
2860b57cec5SDimitry Andric   // file.  So we go through the match list and pull out the sets that have the
2870b57cec5SDimitry Andric   // same file spec in their line_entry and treat each set separately.
2880b57cec5SDimitry Andric 
28981ad6265SDimitry Andric   const uint32_t line = m_location_spec.GetLine().value_or(0);
290bdd1243dSDimitry Andric   const std::optional<uint16_t> column = m_location_spec.GetColumn();
2910b57cec5SDimitry Andric 
2920b57cec5SDimitry Andric   const size_t num_comp_units = context.module_sp->GetNumCompileUnits();
2930b57cec5SDimitry Andric   for (size_t i = 0; i < num_comp_units; i++) {
2940b57cec5SDimitry Andric     CompUnitSP cu_sp(context.module_sp->GetCompileUnitAtIndex(i));
2950b57cec5SDimitry Andric     if (cu_sp) {
2960b57cec5SDimitry Andric       if (filter.CompUnitPasses(*cu_sp))
297bdd1243dSDimitry Andric         cu_sp->ResolveSymbolContext(m_location_spec, eSymbolContextEverything,
298bdd1243dSDimitry Andric                                     sc_list);
2990b57cec5SDimitry Andric     }
3000b57cec5SDimitry Andric   }
3010b57cec5SDimitry Andric 
302bdd1243dSDimitry Andric   FilterContexts(sc_list);
303bdd1243dSDimitry Andric 
304bdd1243dSDimitry Andric   DeduceSourceMapping(sc_list);
3050b57cec5SDimitry Andric 
3060b57cec5SDimitry Andric   StreamString s;
307fe6060f1SDimitry Andric   s.Printf("for %s:%d ",
308fe6060f1SDimitry Andric            m_location_spec.GetFileSpec().GetFilename().AsCString("<Unknown>"),
309fe6060f1SDimitry Andric            line);
3100b57cec5SDimitry Andric 
311fe6060f1SDimitry Andric   SetSCMatchesByLine(filter, sc_list, m_skip_prologue, s.GetString(), line,
312fe6060f1SDimitry Andric                      column);
3130b57cec5SDimitry Andric 
3140b57cec5SDimitry Andric   return Searcher::eCallbackReturnContinue;
3150b57cec5SDimitry Andric }
3160b57cec5SDimitry Andric 
GetDepth()3170b57cec5SDimitry Andric lldb::SearchDepth BreakpointResolverFileLine::GetDepth() {
3180b57cec5SDimitry Andric   return lldb::eSearchDepthModule;
3190b57cec5SDimitry Andric }
3200b57cec5SDimitry Andric 
GetDescription(Stream * s)3210b57cec5SDimitry Andric void BreakpointResolverFileLine::GetDescription(Stream *s) {
322fe6060f1SDimitry Andric   s->Printf("file = '%s', line = %u, ",
323fe6060f1SDimitry Andric             m_location_spec.GetFileSpec().GetPath().c_str(),
32481ad6265SDimitry Andric             m_location_spec.GetLine().value_or(0));
325fe6060f1SDimitry Andric   auto column = m_location_spec.GetColumn();
326fe6060f1SDimitry Andric   if (column)
327fe6060f1SDimitry Andric     s->Printf("column = %u, ", *column);
328fe6060f1SDimitry Andric   s->Printf("exact_match = %d", m_location_spec.GetExactMatch());
3290b57cec5SDimitry Andric }
3300b57cec5SDimitry Andric 
Dump(Stream * s) const3310b57cec5SDimitry Andric void BreakpointResolverFileLine::Dump(Stream *s) const {}
3320b57cec5SDimitry Andric 
3330b57cec5SDimitry Andric lldb::BreakpointResolverSP
CopyForBreakpoint(BreakpointSP & breakpoint)3345ffd83dbSDimitry Andric BreakpointResolverFileLine::CopyForBreakpoint(BreakpointSP &breakpoint) {
3350b57cec5SDimitry Andric   lldb::BreakpointResolverSP ret_sp(new BreakpointResolverFileLine(
336fe6060f1SDimitry Andric       breakpoint, GetOffset(), m_skip_prologue, m_location_spec));
3370b57cec5SDimitry Andric 
3380b57cec5SDimitry Andric   return ret_sp;
3390b57cec5SDimitry Andric }
340