blob: dce8a7b7e713b72bd663d98d947cee8b8d24be49 [file]
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/objects/debug-objects.h"
#include "src/base/platform/mutex.h"
#include "src/debug/debug-evaluate.h"
#include "src/handles/handles-inl.h"
#include "src/objects/call-site-info-inl.h"
#include "src/objects/debug-objects-inl.h"
#include "src/utils/ostreams.h"
namespace v8 {
namespace internal {
bool DebugInfo::IsEmpty() const {
return flags(kRelaxedLoad) == kNone && debugger_hints() == 0;
}
bool DebugInfo::HasBreakInfo() const {
return flags(kRelaxedLoad).contains(kHasBreakInfo);
}
DebugInfo::ExecutionMode DebugInfo::DebugExecutionMode() const {
return flags(kRelaxedLoad).contains(kDebugExecutionMode) ? kSideEffects
: kBreakpoints;
}
void DebugInfo::SetDebugExecutionMode(ExecutionMode value) {
set_flags(value == kSideEffects
? flags(kRelaxedLoad).with(kDebugExecutionMode)
: flags(kRelaxedLoad).without(kDebugExecutionMode),
kRelaxedStore);
}
void DebugInfo::ClearBreakInfo(Isolate* isolate) {
if (HasInstrumentedBytecodeArray()) {
// If the function is currently running on the stack, we need to update the
// bytecode pointers on the stack so they point to the original
// BytecodeArray before releasing that BytecodeArray from this DebugInfo.
// Otherwise, it could be flushed and cause problems on resume. See v8:9067.
{
RedirectActiveFunctions redirect_visitor(
isolate, shared(),
RedirectActiveFunctions::Mode::kUseOriginalBytecode);
redirect_visitor.VisitThread(isolate, isolate->thread_local_top());
isolate->thread_manager()->IterateArchivedThreads(&redirect_visitor);
}
SharedFunctionInfo::UninstallDebugBytecode(shared(), isolate);
}
set_break_points(ReadOnlyRoots(isolate).empty_fixed_array());
Flags new_flags =
flags(kRelaxedLoad)
.without({kHasBreakInfo, kPreparedForDebugExecution, kBreakAtEntry,
kCanBreakAtEntry, kDebugExecutionMode});
set_flags(new_flags, kRelaxedStore);
}
void DebugInfo::SetBreakAtEntry() {
DCHECK(CanBreakAtEntry());
set_flags(flags(kRelaxedLoad) | kBreakAtEntry, kRelaxedStore);
}
void DebugInfo::ClearBreakAtEntry() {
DCHECK(CanBreakAtEntry());
set_flags(flags(kRelaxedLoad).without(kBreakAtEntry), kRelaxedStore);
}
bool DebugInfo::BreakAtEntry() const {
return flags(kRelaxedLoad).contains(kBreakAtEntry);
}
bool DebugInfo::CanBreakAtEntry() const {
return flags(kRelaxedLoad).contains(kCanBreakAtEntry);
}
// Check if there is a break point at this source position.
bool DebugInfo::HasBreakPoint(Isolate* isolate, int source_position) {
DCHECK(HasBreakInfo());
// Get the break point info object for this code offset.
Tagged<Object> break_point_info = GetBreakPointInfo(isolate, source_position);
// If there is no break point info object or no break points in the break
// point info object there is no break point at this code offset.
if (IsUndefined(break_point_info)) return false;
return Cast<BreakPointInfo>(break_point_info)->GetBreakPointCount(isolate) >
0;
}
// Get the break point info object for this source position.
Tagged<Object> DebugInfo::GetBreakPointInfo(Isolate* isolate,
int source_position) {
DCHECK(HasBreakInfo());
const uint32_t break_points_len = break_points()->ulength().value();
for (uint32_t i = 0; i < break_points_len; i++) {
if (!IsUndefined(break_points()->get(i))) {
Tagged<BreakPointInfo> break_point_info =
Cast<BreakPointInfo>(break_points()->get(i));
if (break_point_info->source_position() == source_position) {
return break_point_info;
}
}
}
return ReadOnlyRoots(isolate).undefined_value();
}
bool DebugInfo::ClearBreakPoint(Isolate* isolate,
DirectHandle<DebugInfo> debug_info,
DirectHandle<BreakPoint> break_point) {
DCHECK(debug_info->HasBreakInfo());
const uint32_t break_points_len =
debug_info->break_points()->ulength().value();
for (uint32_t i = 0; i < break_points_len; i++) {
if (IsUndefined(debug_info->break_points()->get(i))) continue;
DirectHandle<BreakPointInfo> break_point_info(
Cast<BreakPointInfo>(debug_info->break_points()->get(i)), isolate);
if (BreakPointInfo::HasBreakPoint(isolate, break_point_info, break_point)) {
BreakPointInfo::ClearBreakPoint(isolate, break_point_info, break_point);
return true;
}
}
return false;
}
void DebugInfo::SetBreakPoint(Isolate* isolate,
DirectHandle<DebugInfo> debug_info,
int source_position,
DirectHandle<BreakPoint> break_point) {
DCHECK(debug_info->HasBreakInfo());
DirectHandle<Object> break_point_info(
debug_info->GetBreakPointInfo(isolate, source_position), isolate);
if (!IsUndefined(*break_point_info)) {
BreakPointInfo::SetBreakPoint(
isolate, Cast<BreakPointInfo>(break_point_info), break_point);
return;
}
// Adding a new break point for a code offset which did not have any
// break points before. Try to find a free slot.
std::optional<uint32_t> index;
const uint32_t break_points_len =
debug_info->break_points()->ulength().value();
for (uint32_t i = 0; i < break_points_len; i++) {
if (IsUndefined(debug_info->break_points()->get(i))) {
index = i;
break;
}
}
if (!index) {
// No free slot - extend break point info array.
DirectHandle<FixedArray> old_break_points(debug_info->break_points(),
isolate);
DirectHandle<FixedArray> new_break_points =
isolate->factory()->NewFixedArray(
break_points_len + DebugInfo::kEstimatedNofBreakPointsInFunction);
debug_info->set_break_points(*new_break_points);
for (uint32_t i = 0; i < break_points_len; i++) {
new_break_points->set(i, old_break_points->get(i));
}
index = break_points_len;
}
DCHECK(index.has_value());
// Allocate new BreakPointInfo object and set the break point.
DirectHandle<BreakPointInfo> new_break_point_info =
isolate->factory()->NewBreakPointInfo(source_position);
BreakPointInfo::SetBreakPoint(isolate, new_break_point_info, break_point);
debug_info->break_points()->set(*index, *new_break_point_info);
}
// Get the break point objects for a source position.
DirectHandle<Object> DebugInfo::GetBreakPoints(Isolate* isolate,
int source_position) {
DCHECK(HasBreakInfo());
Tagged<Object> break_point_info = GetBreakPointInfo(isolate, source_position);
if (IsUndefined(break_point_info)) {
return isolate->factory()->undefined_value();
}
return DirectHandle<Object>(
Cast<BreakPointInfo>(break_point_info)->break_points(), isolate);
}
// Get the total number of break points.
uint32_t DebugInfo::GetBreakPointCount(Isolate* isolate) {
DCHECK(HasBreakInfo());
uint32_t count = 0;
uint32_t break_points_len = break_points()->ulength().value();
for (uint32_t i = 0; i < break_points_len; i++) {
if (!IsUndefined(break_points()->get(i))) {
Tagged<BreakPointInfo> break_point_info =
Cast<BreakPointInfo>(break_points()->get(i));
count += break_point_info->GetBreakPointCount(isolate);
}
}
return count;
}
DirectHandle<Object> DebugInfo::FindBreakPointInfo(
Isolate* isolate, DirectHandle<DebugInfo> debug_info,
DirectHandle<BreakPoint> break_point) {
DCHECK(debug_info->HasBreakInfo());
const uint32_t break_points_len =
debug_info->break_points()->ulength().value();
for (uint32_t i = 0; i < break_points_len; i++) {
if (!IsUndefined(debug_info->break_points()->get(i))) {
DirectHandle<BreakPointInfo> break_point_info(
Cast<BreakPointInfo>(debug_info->break_points()->get(i)), isolate);
if (BreakPointInfo::HasBreakPoint(isolate, break_point_info,
break_point)) {
return break_point_info;
}
}
}
return isolate->factory()->undefined_value();
}
bool DebugInfo::HasCoverageInfo() const {
return flags(kRelaxedLoad).contains(kHasCoverageInfo);
}
void DebugInfo::ClearCoverageInfo(Isolate* isolate) {
if (HasCoverageInfo()) {
set_coverage_info(ReadOnlyRoots(isolate).undefined_value());
Flags new_flags = flags(kRelaxedLoad).without(kHasCoverageInfo);
set_flags(new_flags, kRelaxedStore);
}
}
DebugInfo::SideEffectState DebugInfo::GetSideEffectState(Isolate* isolate) {
if (side_effect_state() == kNotComputed) {
SideEffectState has_no_side_effect =
DebugEvaluate::FunctionGetSideEffectState(
isolate, direct_handle(shared(), isolate));
set_side_effect_state(has_no_side_effect);
}
return static_cast<SideEffectState>(side_effect_state());
}
namespace {
bool IsEqual(Tagged<BreakPoint> break_point1, Tagged<BreakPoint> break_point2) {
return break_point1->id() == break_point2->id();
}
} // namespace
// Remove the specified break point object.
void BreakPointInfo::ClearBreakPoint(
Isolate* isolate, DirectHandle<BreakPointInfo> break_point_info,
DirectHandle<BreakPoint> break_point) {
// If there are no break points just ignore.
if (IsUndefined(break_point_info->break_points())) return;
// If there is a single break point clear it if it is the same.
if (!IsFixedArray(break_point_info->break_points())) {
if (IsEqual(Cast<BreakPoint>(break_point_info->break_points()),
*break_point)) {
break_point_info->set_break_points(
ReadOnlyRoots(isolate).undefined_value());
}
return;
}
// If there are multiple break points shrink the array
DCHECK(IsFixedArray(break_point_info->break_points()));
DirectHandle<FixedArray> old_array(
Cast<FixedArray>(break_point_info->break_points()), isolate);
const uint32_t old_array_len = old_array->ulength().value();
DCHECK_GT(old_array_len, 0);
DirectHandle<FixedArray> new_array =
isolate->factory()->NewFixedArray(old_array_len - 1);
uint32_t found_count = 0;
for (uint32_t i = 0; i < old_array_len; i++) {
if (IsEqual(Cast<BreakPoint>(old_array->get(i)), *break_point)) {
DCHECK_EQ(found_count, 0);
found_count++;
} else {
new_array->set(i - found_count, old_array->get(i));
}
}
// If the break point was found in the list change it.
if (found_count > 0) break_point_info->set_break_points(*new_array);
}
// Add the specified break point object.
void BreakPointInfo::SetBreakPoint(
Isolate* isolate, DirectHandle<BreakPointInfo> break_point_info,
DirectHandle<BreakPoint> break_point) {
// If there was no break point objects before just set it.
if (IsUndefined(break_point_info->break_points())) {
break_point_info->set_break_points(*break_point);
return;
}
// If there was one break point object before replace with array.
if (!IsFixedArray(break_point_info->break_points())) {
if (IsEqual(Cast<BreakPoint>(break_point_info->break_points()),
*break_point)) {
return;
}
DirectHandle<FixedArray> array = isolate->factory()->NewFixedArray(2);
array->set(0, break_point_info->break_points());
array->set(1, *break_point);
break_point_info->set_break_points(*array);
return;
}
// If there was more than one break point before extend array.
DirectHandle<FixedArray> old_array(
Cast<FixedArray>(break_point_info->break_points()), isolate);
const uint32_t old_array_len = old_array->ulength().value();
DirectHandle<FixedArray> new_array =
isolate->factory()->NewFixedArray(old_array_len + 1);
for (uint32_t i = 0; i < old_array_len; i++) {
// If the break point was there before just ignore.
if (IsEqual(Cast<BreakPoint>(old_array->get(i)), *break_point)) return;
new_array->set(i, old_array->get(i));
}
// Add the new break point.
new_array->set(old_array_len, *break_point);
break_point_info->set_break_points(*new_array);
}
bool BreakPointInfo::HasBreakPoint(
Isolate* isolate, DirectHandle<BreakPointInfo> break_point_info,
DirectHandle<BreakPoint> break_point) {
// No break point.
if (IsUndefined(break_point_info->break_points())) {
return false;
}
// Single break point.
if (!IsFixedArray(break_point_info->break_points())) {
return IsEqual(Cast<BreakPoint>(break_point_info->break_points()),
*break_point);
}
// Multiple break points.
Tagged<FixedArray> array = Cast<FixedArray>(break_point_info->break_points());
const uint32_t array_len = array->ulength().value();
for (uint32_t i = 0; i < array_len; i++) {
if (IsEqual(Cast<BreakPoint>(array->get(i)), *break_point)) {
return true;
}
}
return false;
}
MaybeDirectHandle<BreakPoint> BreakPointInfo::GetBreakPointById(
Isolate* isolate, DirectHandle<BreakPointInfo> break_point_info,
int breakpoint_id) {
// No break point.
if (IsUndefined(break_point_info->break_points())) {
return MaybeDirectHandle<BreakPoint>();
}
// Single break point.
if (!IsFixedArray(break_point_info->break_points())) {
Tagged<BreakPoint> breakpoint =
Cast<BreakPoint>(break_point_info->break_points());
if (breakpoint->id() == breakpoint_id) {
return direct_handle(breakpoint, isolate);
}
} else {
// Multiple break points.
Tagged<FixedArray> array =
Cast<FixedArray>(break_point_info->break_points());
const uint32_t array_len = array->ulength().value();
for (uint32_t i = 0; i < array_len; i++) {
Tagged<BreakPoint> breakpoint = Cast<BreakPoint>(array->get(i));
if (breakpoint->id() == breakpoint_id) {
return direct_handle(breakpoint, isolate);
}
}
}
return MaybeDirectHandle<BreakPoint>();
}
// Get the number of break points.
uint32_t BreakPointInfo::GetBreakPointCount(Isolate* isolate) {
// No break point.
if (IsUndefined(break_points())) return 0;
// Single break point.
if (!IsFixedArray(break_points())) return 1;
// Multiple break points.
return Cast<FixedArray>(break_points())->ulength().value();
}
void CoverageInfo::InitializeSlot(int slot_index, int from_pos, int to_pos) {
set_slots_start_source_position(slot_index, from_pos);
set_slots_end_source_position(slot_index, to_pos);
ResetBlockCount(slot_index);
set_slots_padding(slot_index, 0);
}
void CoverageInfo::ResetBlockCount(int slot_index) {
set_slots_block_count(slot_index, 0);
}
void CoverageInfo::CoverageInfoPrint(std::ostream& os,
std::unique_ptr<char[]> function_name) {
DisallowGarbageCollection no_gc;
os << "Coverage info (";
if (function_name == nullptr) {
os << "{unknown}";
} else if (strlen(function_name.get()) > 0) {
os << function_name.get();
} else {
os << "{anonymous}";
}
os << "):" << std::endl;
for (int i = 0; i < slot_count(); i++) {
os << "{" << slots_start_source_position(i) << ","
<< slots_end_source_position(i) << "}" << std::endl;
}
}
// static
int StackFrameInfo::GetSourcePosition(DirectHandle<StackFrameInfo> info) {
if (IsScript(info->shared_or_script())) {
return info->bytecode_offset_or_source_position();
}
Isolate* isolate = Isolate::Current();
DirectHandle<SharedFunctionInfo> shared(
Cast<SharedFunctionInfo>(info->shared_or_script()), isolate);
SharedFunctionInfo::EnsureSourcePositionsAvailable(isolate, shared);
int source_position = shared->abstract_code(isolate)->SourcePosition(
isolate, info->bytecode_offset_or_source_position());
info->set_shared_or_script(Cast<Script>(shared->script()));
info->set_bytecode_offset_or_source_position(source_position);
return source_position;
}
} // namespace internal
} // namespace v8