1//===-- BreakpointSite.cpp --------------------------------------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include <inttypes.h>
10
11#include "lldb/Breakpoint/BreakpointSite.h"
12
13#include "lldb/Breakpoint/Breakpoint.h"
14#include "lldb/Breakpoint/BreakpointLocation.h"
15#include "lldb/Breakpoint/BreakpointSiteList.h"
16#include "lldb/Utility/Stream.h"
17
18using namespace lldb;
19using namespace lldb_private;
20
21BreakpointSite::BreakpointSite(BreakpointSiteList *list,
22                               const BreakpointLocationSP &owner,
23                               lldb::addr_t addr, bool use_hardware)
24    : StoppointLocation(GetNextID(), addr, 0, use_hardware),
25      m_type(eSoftware), // Process subclasses need to set this correctly using
26                         // SetType()
27      m_saved_opcode(), m_trap_opcode(),
28      m_enabled(false), // Need to create it disabled, so the first enable turns
29                        // it on.
30      m_owners(), m_owners_mutex() {
31  m_owners.Add(owner);
32}
33
34BreakpointSite::~BreakpointSite() {
35  BreakpointLocationSP bp_loc_sp;
36  const size_t owner_count = m_owners.GetSize();
37  for (size_t i = 0; i < owner_count; i++) {
38    m_owners.GetByIndex(i)->ClearBreakpointSite();
39  }
40}
41
42break_id_t BreakpointSite::GetNextID() {
43  static break_id_t g_next_id = 0;
44  return ++g_next_id;
45}
46
47// RETURNS - true if we should stop at this breakpoint, false if we
48// should continue.
49
50bool BreakpointSite::ShouldStop(StoppointCallbackContext *context) {
51  IncrementHitCount();
52  // ShouldStop can do a lot of work, and might even come come back and hit
53  // this breakpoint site again.  So don't hold the m_owners_mutex the whole
54  // while.  Instead make a local copy of the collection and call ShouldStop on
55  // the copy.
56  BreakpointLocationCollection owners_copy;
57  {
58    std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
59    owners_copy = m_owners;
60  }
61  return owners_copy.ShouldStop(context);
62}
63
64bool BreakpointSite::IsBreakpointAtThisSite(lldb::break_id_t bp_id) {
65  std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
66  const size_t owner_count = m_owners.GetSize();
67  for (size_t i = 0; i < owner_count; i++) {
68    if (m_owners.GetByIndex(i)->GetBreakpoint().GetID() == bp_id)
69      return true;
70  }
71  return false;
72}
73
74void BreakpointSite::Dump(Stream *s) const {
75  if (s == nullptr)
76    return;
77
78  s->Printf("BreakpointSite %u: addr = 0x%8.8" PRIx64
79            "  type = %s breakpoint  hw_index = %i  hit_count = %-4u",
80            GetID(), (uint64_t)m_addr, IsHardware() ? "hardware" : "software",
81            GetHardwareIndex(), GetHitCount());
82}
83
84void BreakpointSite::GetDescription(Stream *s, lldb::DescriptionLevel level) {
85  std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
86  if (level != lldb::eDescriptionLevelBrief)
87    s->Printf("breakpoint site: %d at 0x%8.8" PRIx64, GetID(),
88              GetLoadAddress());
89  m_owners.GetDescription(s, level);
90}
91
92bool BreakpointSite::IsInternal() const { return m_owners.IsInternal(); }
93
94uint8_t *BreakpointSite::GetTrapOpcodeBytes() { return &m_trap_opcode[0]; }
95
96const uint8_t *BreakpointSite::GetTrapOpcodeBytes() const {
97  return &m_trap_opcode[0];
98}
99
100size_t BreakpointSite::GetTrapOpcodeMaxByteSize() const {
101  return sizeof(m_trap_opcode);
102}
103
104bool BreakpointSite::SetTrapOpcode(const uint8_t *trap_opcode,
105                                   uint32_t trap_opcode_size) {
106  if (trap_opcode_size > 0 && trap_opcode_size <= sizeof(m_trap_opcode)) {
107    m_byte_size = trap_opcode_size;
108    ::memcpy(m_trap_opcode, trap_opcode, trap_opcode_size);
109    return true;
110  }
111  m_byte_size = 0;
112  return false;
113}
114
115uint8_t *BreakpointSite::GetSavedOpcodeBytes() { return &m_saved_opcode[0]; }
116
117const uint8_t *BreakpointSite::GetSavedOpcodeBytes() const {
118  return &m_saved_opcode[0];
119}
120
121bool BreakpointSite::IsEnabled() const { return m_enabled; }
122
123void BreakpointSite::SetEnabled(bool enabled) { m_enabled = enabled; }
124
125void BreakpointSite::AddOwner(const BreakpointLocationSP &owner) {
126  std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
127  m_owners.Add(owner);
128}
129
130size_t BreakpointSite::RemoveOwner(lldb::break_id_t break_id,
131                                   lldb::break_id_t break_loc_id) {
132  std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
133  m_owners.Remove(break_id, break_loc_id);
134  return m_owners.GetSize();
135}
136
137size_t BreakpointSite::GetNumberOfOwners() {
138  std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
139  return m_owners.GetSize();
140}
141
142BreakpointLocationSP BreakpointSite::GetOwnerAtIndex(size_t index) {
143  std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
144  return m_owners.GetByIndex(index);
145}
146
147bool BreakpointSite::ValidForThisThread(Thread *thread) {
148  std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
149  return m_owners.ValidForThisThread(thread);
150}
151
152void BreakpointSite::BumpHitCounts() {
153  std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
154  for (BreakpointLocationSP loc_sp : m_owners.BreakpointLocations()) {
155    loc_sp->BumpHitCount();
156  }
157}
158
159bool BreakpointSite::IntersectsRange(lldb::addr_t addr, size_t size,
160                                     lldb::addr_t *intersect_addr,
161                                     size_t *intersect_size,
162                                     size_t *opcode_offset) const {
163  // We only use software traps for software breakpoints
164  if (!IsHardware()) {
165    if (m_byte_size > 0) {
166      const lldb::addr_t bp_end_addr = m_addr + m_byte_size;
167      const lldb::addr_t end_addr = addr + size;
168      // Is the breakpoint end address before the passed in start address?
169      if (bp_end_addr <= addr)
170        return false;
171      // Is the breakpoint start address after passed in end address?
172      if (end_addr <= m_addr)
173        return false;
174      if (intersect_addr || intersect_size || opcode_offset) {
175        if (m_addr < addr) {
176          if (intersect_addr)
177            *intersect_addr = addr;
178          if (intersect_size)
179            *intersect_size =
180                std::min<lldb::addr_t>(bp_end_addr, end_addr) - addr;
181          if (opcode_offset)
182            *opcode_offset = addr - m_addr;
183        } else {
184          if (intersect_addr)
185            *intersect_addr = m_addr;
186          if (intersect_size)
187            *intersect_size =
188                std::min<lldb::addr_t>(bp_end_addr, end_addr) - m_addr;
189          if (opcode_offset)
190            *opcode_offset = 0;
191        }
192      }
193      return true;
194    }
195  }
196  return false;
197}
198
199size_t
200BreakpointSite::CopyOwnersList(BreakpointLocationCollection &out_collection) {
201  std::lock_guard<std::recursive_mutex> guard(m_owners_mutex);
202  for (BreakpointLocationSP loc_sp : m_owners.BreakpointLocations()) {
203    out_collection.Add(loc_sp);
204  }
205  return out_collection.GetSize();
206}
207