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