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 | |
12 | namespace v8 { |
13 | namespace internal { |
14 | |
15 | bool DebugInfo::IsEmpty() const { |
16 | return flags() == kNone && debugger_hints() == 0; |
17 | } |
18 | |
19 | bool DebugInfo::HasBreakInfo() const { return (flags() & kHasBreakInfo) != 0; } |
20 | |
21 | DebugInfo::ExecutionMode DebugInfo::DebugExecutionMode() const { |
22 | return (flags() & kDebugExecutionMode) != 0 ? kSideEffects : kBreakpoints; |
23 | } |
24 | |
25 | void DebugInfo::SetDebugExecutionMode(ExecutionMode value) { |
26 | set_flags(value == kSideEffects ? (flags() | kDebugExecutionMode) |
27 | : (flags() & ~kDebugExecutionMode)); |
28 | } |
29 | |
30 | void 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 | |
59 | void DebugInfo::SetBreakAtEntry() { |
60 | DCHECK(CanBreakAtEntry()); |
61 | set_flags(flags() | kBreakAtEntry); |
62 | } |
63 | |
64 | void DebugInfo::ClearBreakAtEntry() { |
65 | DCHECK(CanBreakAtEntry()); |
66 | set_flags(flags() & ~kBreakAtEntry); |
67 | } |
68 | |
69 | bool DebugInfo::BreakAtEntry() const { return (flags() & kBreakAtEntry) != 0; } |
70 | |
71 | bool DebugInfo::CanBreakAtEntry() const { |
72 | return (flags() & kCanBreakAtEntry) != 0; |
73 | } |
74 | |
75 | // Check if there is a break point at this source position. |
76 | bool 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. |
89 | Object 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 | |
103 | bool 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 | |
118 | void 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. |
164 | Handle<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. |
176 | int 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 | |
189 | Handle<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 | |
206 | bool DebugInfo::HasCoverageInfo() const { |
207 | return (flags() & kHasCoverageInfo) != 0; |
208 | } |
209 | |
210 | void 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 | |
219 | DebugInfo::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 | |
229 | namespace { |
230 | bool 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. |
236 | void 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. |
270 | void 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 | |
303 | bool 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. |
326 | int 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 | |
335 | int CoverageInfo::SlotCount() const { |
336 | DCHECK_EQ(kFirstSlotIndex, length() % kSlotIndexCount); |
337 | return (length() - kFirstSlotIndex) / kSlotIndexCount; |
338 | } |
339 | |
340 | int 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 | |
346 | int 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 | |
352 | int 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 | |
358 | void 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 | |
366 | void 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 | |
373 | void 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 | |
379 | void 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 | |