1// Copyright 2017 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "src/objects/debug-objects.h"
6
7#include "src/debug/debug-evaluate.h"
8#include "src/handles-inl.h"
9#include "src/objects/debug-objects-inl.h"
10#include "src/ostreams.h"
11
12namespace v8 {
13namespace internal {
14
15bool DebugInfo::IsEmpty() const {
16 return flags() == kNone && debugger_hints() == 0;
17}
18
19bool DebugInfo::HasBreakInfo() const { return (flags() & kHasBreakInfo) != 0; }
20
21DebugInfo::ExecutionMode DebugInfo::DebugExecutionMode() const {
22 return (flags() & kDebugExecutionMode) != 0 ? kSideEffects : kBreakpoints;
23}
24
25void DebugInfo::SetDebugExecutionMode(ExecutionMode value) {
26 set_flags(value == kSideEffects ? (flags() | kDebugExecutionMode)
27 : (flags() & ~kDebugExecutionMode));
28}
29
30void DebugInfo::ClearBreakInfo(Isolate* isolate) {
31 if (HasInstrumentedBytecodeArray()) {
32 // Reset function's bytecode array field to point to the original bytecode
33 // array.
34 shared()->SetDebugBytecodeArray(OriginalBytecodeArray());
35
36 // If the function is currently running on the stack, we need to update the
37 // bytecode pointers on the stack so they point to the original
38 // BytecodeArray before releasing that BytecodeArray from this DebugInfo.
39 // Otherwise, it could be flushed and cause problems on resume. See v8:9067.
40 {
41 RedirectActiveFunctions redirect_visitor(
42 shared(), RedirectActiveFunctions::Mode::kUseOriginalBytecode);
43 redirect_visitor.VisitThread(isolate, isolate->thread_local_top());
44 isolate->thread_manager()->IterateArchivedThreads(&redirect_visitor);
45 }
46
47 set_original_bytecode_array(ReadOnlyRoots(isolate).undefined_value());
48 set_debug_bytecode_array(ReadOnlyRoots(isolate).undefined_value());
49 }
50 set_break_points(ReadOnlyRoots(isolate).empty_fixed_array());
51
52 int new_flags = flags();
53 new_flags &= ~kHasBreakInfo & ~kPreparedForDebugExecution;
54 new_flags &= ~kBreakAtEntry & ~kCanBreakAtEntry;
55 new_flags &= ~kDebugExecutionMode;
56 set_flags(new_flags);
57}
58
59void DebugInfo::SetBreakAtEntry() {
60 DCHECK(CanBreakAtEntry());
61 set_flags(flags() | kBreakAtEntry);
62}
63
64void DebugInfo::ClearBreakAtEntry() {
65 DCHECK(CanBreakAtEntry());
66 set_flags(flags() & ~kBreakAtEntry);
67}
68
69bool DebugInfo::BreakAtEntry() const { return (flags() & kBreakAtEntry) != 0; }
70
71bool DebugInfo::CanBreakAtEntry() const {
72 return (flags() & kCanBreakAtEntry) != 0;
73}
74
75// Check if there is a break point at this source position.
76bool DebugInfo::HasBreakPoint(Isolate* isolate, int source_position) {
77 DCHECK(HasBreakInfo());
78 // Get the break point info object for this code offset.
79 Object break_point_info = GetBreakPointInfo(isolate, source_position);
80
81 // If there is no break point info object or no break points in the break
82 // point info object there is no break point at this code offset.
83 if (break_point_info->IsUndefined(isolate)) return false;
84 return BreakPointInfo::cast(break_point_info)->GetBreakPointCount(isolate) >
85 0;
86}
87
88// Get the break point info object for this source position.
89Object DebugInfo::GetBreakPointInfo(Isolate* isolate, int source_position) {
90 DCHECK(HasBreakInfo());
91 for (int i = 0; i < break_points()->length(); i++) {
92 if (!break_points()->get(i)->IsUndefined(isolate)) {
93 BreakPointInfo break_point_info =
94 BreakPointInfo::cast(break_points()->get(i));
95 if (break_point_info->source_position() == source_position) {
96 return break_point_info;
97 }
98 }
99 }
100 return ReadOnlyRoots(isolate).undefined_value();
101}
102
103bool DebugInfo::ClearBreakPoint(Isolate* isolate, Handle<DebugInfo> debug_info,
104 Handle<BreakPoint> break_point) {
105 DCHECK(debug_info->HasBreakInfo());
106 for (int i = 0; i < debug_info->break_points()->length(); i++) {
107 if (debug_info->break_points()->get(i)->IsUndefined(isolate)) continue;
108 Handle<BreakPointInfo> break_point_info = Handle<BreakPointInfo>(
109 BreakPointInfo::cast(debug_info->break_points()->get(i)), isolate);
110 if (BreakPointInfo::HasBreakPoint(isolate, break_point_info, break_point)) {
111 BreakPointInfo::ClearBreakPoint(isolate, break_point_info, break_point);
112 return true;
113 }
114 }
115 return false;
116}
117
118void DebugInfo::SetBreakPoint(Isolate* isolate, Handle<DebugInfo> debug_info,
119 int source_position,
120 Handle<BreakPoint> break_point) {
121 DCHECK(debug_info->HasBreakInfo());
122 Handle<Object> break_point_info(
123 debug_info->GetBreakPointInfo(isolate, source_position), isolate);
124 if (!break_point_info->IsUndefined(isolate)) {
125 BreakPointInfo::SetBreakPoint(
126 isolate, Handle<BreakPointInfo>::cast(break_point_info), break_point);
127 return;
128 }
129
130 // Adding a new break point for a code offset which did not have any
131 // break points before. Try to find a free slot.
132 static const int kNoBreakPointInfo = -1;
133 int index = kNoBreakPointInfo;
134 for (int i = 0; i < debug_info->break_points()->length(); i++) {
135 if (debug_info->break_points()->get(i)->IsUndefined(isolate)) {
136 index = i;
137 break;
138 }
139 }
140 if (index == kNoBreakPointInfo) {
141 // No free slot - extend break point info array.
142 Handle<FixedArray> old_break_points =
143 Handle<FixedArray>(debug_info->break_points(), isolate);
144 Handle<FixedArray> new_break_points = isolate->factory()->NewFixedArray(
145 old_break_points->length() +
146 DebugInfo::kEstimatedNofBreakPointsInFunction);
147
148 debug_info->set_break_points(*new_break_points);
149 for (int i = 0; i < old_break_points->length(); i++) {
150 new_break_points->set(i, old_break_points->get(i));
151 }
152 index = old_break_points->length();
153 }
154 DCHECK_NE(index, kNoBreakPointInfo);
155
156 // Allocate new BreakPointInfo object and set the break point.
157 Handle<BreakPointInfo> new_break_point_info =
158 isolate->factory()->NewBreakPointInfo(source_position);
159 BreakPointInfo::SetBreakPoint(isolate, new_break_point_info, break_point);
160 debug_info->break_points()->set(index, *new_break_point_info);
161}
162
163// Get the break point objects for a source position.
164Handle<Object> DebugInfo::GetBreakPoints(Isolate* isolate,
165 int source_position) {
166 DCHECK(HasBreakInfo());
167 Object break_point_info = GetBreakPointInfo(isolate, source_position);
168 if (break_point_info->IsUndefined(isolate)) {
169 return isolate->factory()->undefined_value();
170 }
171 return Handle<Object>(BreakPointInfo::cast(break_point_info)->break_points(),
172 isolate);
173}
174
175// Get the total number of break points.
176int DebugInfo::GetBreakPointCount(Isolate* isolate) {
177 DCHECK(HasBreakInfo());
178 int count = 0;
179 for (int i = 0; i < break_points()->length(); i++) {
180 if (!break_points()->get(i)->IsUndefined(isolate)) {
181 BreakPointInfo break_point_info =
182 BreakPointInfo::cast(break_points()->get(i));
183 count += break_point_info->GetBreakPointCount(isolate);
184 }
185 }
186 return count;
187}
188
189Handle<Object> DebugInfo::FindBreakPointInfo(Isolate* isolate,
190 Handle<DebugInfo> debug_info,
191 Handle<BreakPoint> break_point) {
192 DCHECK(debug_info->HasBreakInfo());
193 for (int i = 0; i < debug_info->break_points()->length(); i++) {
194 if (!debug_info->break_points()->get(i)->IsUndefined(isolate)) {
195 Handle<BreakPointInfo> break_point_info = Handle<BreakPointInfo>(
196 BreakPointInfo::cast(debug_info->break_points()->get(i)), isolate);
197 if (BreakPointInfo::HasBreakPoint(isolate, break_point_info,
198 break_point)) {
199 return break_point_info;
200 }
201 }
202 }
203 return isolate->factory()->undefined_value();
204}
205
206bool DebugInfo::HasCoverageInfo() const {
207 return (flags() & kHasCoverageInfo) != 0;
208}
209
210void DebugInfo::ClearCoverageInfo(Isolate* isolate) {
211 if (HasCoverageInfo()) {
212 set_coverage_info(ReadOnlyRoots(isolate).undefined_value());
213
214 int new_flags = flags() & ~kHasCoverageInfo;
215 set_flags(new_flags);
216 }
217}
218
219DebugInfo::SideEffectState DebugInfo::GetSideEffectState(Isolate* isolate) {
220 if (side_effect_state() == kNotComputed) {
221 SideEffectState has_no_side_effect =
222 DebugEvaluate::FunctionGetSideEffectState(isolate,
223 handle(shared(), isolate));
224 set_side_effect_state(has_no_side_effect);
225 }
226 return static_cast<SideEffectState>(side_effect_state());
227}
228
229namespace {
230bool IsEqual(BreakPoint break_point1, BreakPoint break_point2) {
231 return break_point1->id() == break_point2->id();
232}
233} // namespace
234
235// Remove the specified break point object.
236void BreakPointInfo::ClearBreakPoint(Isolate* isolate,
237 Handle<BreakPointInfo> break_point_info,
238 Handle<BreakPoint> break_point) {
239 // If there are no break points just ignore.
240 if (break_point_info->break_points()->IsUndefined(isolate)) return;
241 // If there is a single break point clear it if it is the same.
242 if (!break_point_info->break_points()->IsFixedArray()) {
243 if (IsEqual(BreakPoint::cast(break_point_info->break_points()),
244 *break_point)) {
245 break_point_info->set_break_points(
246 ReadOnlyRoots(isolate).undefined_value());
247 }
248 return;
249 }
250 // If there are multiple break points shrink the array
251 DCHECK(break_point_info->break_points()->IsFixedArray());
252 Handle<FixedArray> old_array = Handle<FixedArray>(
253 FixedArray::cast(break_point_info->break_points()), isolate);
254 Handle<FixedArray> new_array =
255 isolate->factory()->NewFixedArray(old_array->length() - 1);
256 int found_count = 0;
257 for (int i = 0; i < old_array->length(); i++) {
258 if (IsEqual(BreakPoint::cast(old_array->get(i)), *break_point)) {
259 DCHECK_EQ(found_count, 0);
260 found_count++;
261 } else {
262 new_array->set(i - found_count, old_array->get(i));
263 }
264 }
265 // If the break point was found in the list change it.
266 if (found_count > 0) break_point_info->set_break_points(*new_array);
267}
268
269// Add the specified break point object.
270void BreakPointInfo::SetBreakPoint(Isolate* isolate,
271 Handle<BreakPointInfo> break_point_info,
272 Handle<BreakPoint> break_point) {
273 // If there was no break point objects before just set it.
274 if (break_point_info->break_points()->IsUndefined(isolate)) {
275 break_point_info->set_break_points(*break_point);
276 return;
277 }
278 // If the break point object is the same as before just ignore.
279 if (break_point_info->break_points() == *break_point) return;
280 // If there was one break point object before replace with array.
281 if (!break_point_info->break_points()->IsFixedArray()) {
282 Handle<FixedArray> array = isolate->factory()->NewFixedArray(2);
283 array->set(0, break_point_info->break_points());
284 array->set(1, *break_point);
285 break_point_info->set_break_points(*array);
286 return;
287 }
288 // If there was more than one break point before extend array.
289 Handle<FixedArray> old_array = Handle<FixedArray>(
290 FixedArray::cast(break_point_info->break_points()), isolate);
291 Handle<FixedArray> new_array =
292 isolate->factory()->NewFixedArray(old_array->length() + 1);
293 for (int i = 0; i < old_array->length(); i++) {
294 // If the break point was there before just ignore.
295 if (IsEqual(BreakPoint::cast(old_array->get(i)), *break_point)) return;
296 new_array->set(i, old_array->get(i));
297 }
298 // Add the new break point.
299 new_array->set(old_array->length(), *break_point);
300 break_point_info->set_break_points(*new_array);
301}
302
303bool BreakPointInfo::HasBreakPoint(Isolate* isolate,
304 Handle<BreakPointInfo> break_point_info,
305 Handle<BreakPoint> break_point) {
306 // No break point.
307 if (break_point_info->break_points()->IsUndefined(isolate)) {
308 return false;
309 }
310 // Single break point.
311 if (!break_point_info->break_points()->IsFixedArray()) {
312 return IsEqual(BreakPoint::cast(break_point_info->break_points()),
313 *break_point);
314 }
315 // Multiple break points.
316 FixedArray array = FixedArray::cast(break_point_info->break_points());
317 for (int i = 0; i < array->length(); i++) {
318 if (IsEqual(BreakPoint::cast(array->get(i)), *break_point)) {
319 return true;
320 }
321 }
322 return false;
323}
324
325// Get the number of break points.
326int BreakPointInfo::GetBreakPointCount(Isolate* isolate) {
327 // No break point.
328 if (break_points()->IsUndefined(isolate)) return 0;
329 // Single break point.
330 if (!break_points()->IsFixedArray()) return 1;
331 // Multiple break points.
332 return FixedArray::cast(break_points())->length();
333}
334
335int CoverageInfo::SlotCount() const {
336 DCHECK_EQ(kFirstSlotIndex, length() % kSlotIndexCount);
337 return (length() - kFirstSlotIndex) / kSlotIndexCount;
338}
339
340int CoverageInfo::StartSourcePosition(int slot_index) const {
341 DCHECK_LT(slot_index, SlotCount());
342 const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
343 return Smi::ToInt(get(slot_start + kSlotStartSourcePositionIndex));
344}
345
346int CoverageInfo::EndSourcePosition(int slot_index) const {
347 DCHECK_LT(slot_index, SlotCount());
348 const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
349 return Smi::ToInt(get(slot_start + kSlotEndSourcePositionIndex));
350}
351
352int CoverageInfo::BlockCount(int slot_index) const {
353 DCHECK_LT(slot_index, SlotCount());
354 const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
355 return Smi::ToInt(get(slot_start + kSlotBlockCountIndex));
356}
357
358void CoverageInfo::InitializeSlot(int slot_index, int from_pos, int to_pos) {
359 DCHECK_LT(slot_index, SlotCount());
360 const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
361 set(slot_start + kSlotStartSourcePositionIndex, Smi::FromInt(from_pos));
362 set(slot_start + kSlotEndSourcePositionIndex, Smi::FromInt(to_pos));
363 set(slot_start + kSlotBlockCountIndex, Smi::kZero);
364}
365
366void CoverageInfo::IncrementBlockCount(int slot_index) {
367 DCHECK_LT(slot_index, SlotCount());
368 const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
369 const int old_count = BlockCount(slot_index);
370 set(slot_start + kSlotBlockCountIndex, Smi::FromInt(old_count + 1));
371}
372
373void CoverageInfo::ResetBlockCount(int slot_index) {
374 DCHECK_LT(slot_index, SlotCount());
375 const int slot_start = CoverageInfo::FirstIndexForSlot(slot_index);
376 set(slot_start + kSlotBlockCountIndex, Smi::kZero);
377}
378
379void CoverageInfo::Print(std::unique_ptr<char[]> function_name) {
380 DCHECK(FLAG_trace_block_coverage);
381 DisallowHeapAllocation no_gc;
382
383 StdoutStream os;
384 os << "Coverage info (";
385 if (strlen(function_name.get()) > 0) {
386 os << function_name.get();
387 } else {
388 os << "{anonymous}";
389 }
390 os << "):" << std::endl;
391
392 for (int i = 0; i < SlotCount(); i++) {
393 os << "{" << StartSourcePosition(i) << "," << EndSourcePosition(i) << "}"
394 << std::endl;
395 }
396}
397
398} // namespace internal
399} // namespace v8
400