| // Copyright 2015 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 <iomanip> |
| |
| #include "src/execution/arguments-inl.h" |
| #include "src/execution/frames-inl.h" |
| #include "src/execution/isolate-inl.h" |
| #include "src/interpreter/bytecode-array-iterator.h" |
| #include "src/interpreter/bytecode-decoder.h" |
| #include "src/interpreter/bytecode-flags-and-tokens.h" |
| #include "src/interpreter/bytecode-register.h" |
| #include "src/interpreter/bytecodes.h" |
| #include "src/interpreter/interpreter.h" |
| #include "src/logging/counters.h" |
| #include "src/runtime/runtime-utils.h" |
| #include "src/snapshot/snapshot.h" |
| #ifdef V8_DUMPLING |
| #include "src/dumpling/dumpling-manager.h" |
| #endif |
| #include "src/utils/ostreams.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| namespace { |
| |
| #if defined(V8_TRACE_UNOPTIMIZED) || defined(V8_DUMPLING) |
| |
| void AdvanceToOffsetForTracing( |
| interpreter::BytecodeArrayIterator& bytecode_iterator, int offset) { |
| while (bytecode_iterator.current_offset() + |
| bytecode_iterator.current_bytecode_size() <= |
| offset) { |
| bytecode_iterator.Advance(); |
| } |
| DCHECK(bytecode_iterator.current_offset() == offset || |
| ((bytecode_iterator.current_offset() + 1) == offset && |
| bytecode_iterator.current_operand_scale() > |
| interpreter::OperandScale::kSingle)); |
| } |
| |
| #endif // V8_TRACE_UNOPTIMIZED || V8_DUMPLING |
| |
| #ifdef V8_TRACE_UNOPTIMIZED |
| |
| void PrintRegisterRange(UnoptimizedJSFrame* frame, std::ostream& os, |
| interpreter::BytecodeArrayIterator& bytecode_iterator, |
| const int& reg_field_width, const char* arrow_direction, |
| interpreter::Register first_reg, int range) { |
| for (int reg_index = first_reg.index(); reg_index < first_reg.index() + range; |
| reg_index++) { |
| Tagged<Object> reg_object = frame->ReadInterpreterRegister(reg_index); |
| os << " [ " << std::setw(reg_field_width) |
| << interpreter::Register(reg_index).ToString() << arrow_direction; |
| ShortPrint(reg_object, os); |
| os << " ]" << std::endl; |
| } |
| } |
| |
| void PrintRegisters(UnoptimizedJSFrame* frame, std::ostream& os, bool is_input, |
| interpreter::BytecodeArrayIterator& bytecode_iterator, |
| Handle<Object> accumulator) { |
| static const char kAccumulator[] = "accumulator"; |
| static const int kRegFieldWidth = static_cast<int>(sizeof(kAccumulator) - 1); |
| static const char* kInputColourCode = "\033[0;36m"; |
| static const char* kOutputColourCode = "\033[0;35m"; |
| static const char* kNormalColourCode = "\033[0;m"; |
| const char* kArrowDirection = is_input ? " -> " : " <- "; |
| if (v8_flags.log_colour) { |
| os << (is_input ? kInputColourCode : kOutputColourCode); |
| } |
| |
| interpreter::Bytecode bytecode = bytecode_iterator.current_bytecode(); |
| |
| // Print accumulator. |
| if ((is_input && interpreter::Bytecodes::ReadsAccumulator(bytecode)) || |
| (!is_input && |
| interpreter::Bytecodes::WritesOrClobbersAccumulator(bytecode))) { |
| os << " [ " << kAccumulator << kArrowDirection; |
| ShortPrint(*accumulator, os); |
| os << " ]" << std::endl; |
| } |
| |
| // Print the registers. |
| int operand_count = interpreter::Bytecodes::NumberOfOperands(bytecode); |
| for (int operand_index = 0; operand_index < operand_count; operand_index++) { |
| interpreter::OperandType operand_type = |
| interpreter::Bytecodes::GetOperandType(bytecode, operand_index); |
| bool should_print = |
| is_input |
| ? interpreter::Bytecodes::IsRegisterInputOperandType(operand_type) |
| : interpreter::Bytecodes::IsRegisterOutputOperandType(operand_type); |
| if (should_print) { |
| interpreter::Register first_reg = |
| bytecode_iterator.GetRegisterOperand(operand_index); |
| int range = bytecode_iterator.GetRegisterOperandRange(operand_index); |
| PrintRegisterRange(frame, os, bytecode_iterator, kRegFieldWidth, |
| kArrowDirection, first_reg, range); |
| } |
| } |
| if (!is_input && interpreter::Bytecodes::IsShortStar(bytecode)) { |
| PrintRegisterRange(frame, os, bytecode_iterator, kRegFieldWidth, |
| kArrowDirection, |
| interpreter::Register::FromShortStar(bytecode), 1); |
| } |
| if (v8_flags.log_colour) { |
| os << kNormalColourCode; |
| } |
| } |
| |
| #endif // V8_TRACE_UNOPTIMIZED |
| |
| } // namespace |
| |
| #ifdef V8_TRACE_UNOPTIMIZED |
| |
| RUNTIME_FUNCTION(Runtime_TraceUnoptimizedBytecodeEntry) { |
| if (!v8_flags.trace_ignition && !v8_flags.trace_baseline_exec) { |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| |
| JavaScriptStackFrameIterator frame_iterator(isolate); |
| UnoptimizedJSFrame* frame = |
| reinterpret_cast<UnoptimizedJSFrame*>(frame_iterator.frame()); |
| |
| if (frame->is_interpreted() && !v8_flags.trace_ignition) { |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| if (frame->is_baseline() && !v8_flags.trace_baseline_exec) { |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| |
| SealHandleScope shs(isolate); |
| DCHECK_EQ(3, args.length()); |
| Handle<BytecodeArray> bytecode_array = CheckedCast<BytecodeArray>(args.at(0)); |
| int bytecode_offset = args.smi_value_at(1); |
| Handle<Object> accumulator = args.at(2); |
| |
| int offset = bytecode_offset - BytecodeArray::kHeaderSize + kHeapObjectTag; |
| interpreter::BytecodeArrayIterator bytecode_iterator(bytecode_array); |
| AdvanceToOffsetForTracing(bytecode_iterator, offset); |
| if (offset == bytecode_iterator.current_offset()) { |
| StdoutStream os; |
| |
| // Print bytecode. |
| const uint8_t* bytecode_address = bytecode_iterator.current_address(); |
| |
| if (frame->is_baseline()) { |
| os << "B-> "; |
| } else { |
| os << " -> "; |
| } |
| os << static_cast<const void*>(bytecode_address) << " @ " << std::setw(4) |
| << offset << " : "; |
| bytecode_iterator.PrintCurrentBytecodeTo(os); |
| os << std::endl; |
| // Print all input registers and accumulator. |
| PrintRegisters(frame, os, true, bytecode_iterator, accumulator); |
| |
| os << std::flush; |
| } |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| |
| RUNTIME_FUNCTION(Runtime_TraceUnoptimizedBytecodeExit) { |
| if (!v8_flags.trace_ignition && !v8_flags.trace_baseline_exec) { |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| |
| JavaScriptStackFrameIterator frame_iterator(isolate); |
| UnoptimizedJSFrame* frame = |
| reinterpret_cast<UnoptimizedJSFrame*>(frame_iterator.frame()); |
| |
| if (frame->is_interpreted() && !v8_flags.trace_ignition) { |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| if (frame->is_baseline() && !v8_flags.trace_baseline_exec) { |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| |
| SealHandleScope shs(isolate); |
| DCHECK_EQ(3, args.length()); |
| Handle<BytecodeArray> bytecode_array = CheckedCast<BytecodeArray>(args.at(0)); |
| int bytecode_offset = args.smi_value_at(1); |
| Handle<Object> accumulator = args.at(2); |
| |
| int offset = bytecode_offset - BytecodeArray::kHeaderSize + kHeapObjectTag; |
| interpreter::BytecodeArrayIterator bytecode_iterator(bytecode_array); |
| AdvanceToOffsetForTracing(bytecode_iterator, offset); |
| // The offset comparison here ensures registers only printed when the |
| // (potentially) widened bytecode has completed. The iterator reports |
| // the offset as the offset of the prefix bytecode. |
| if (bytecode_iterator.current_operand_scale() == |
| interpreter::OperandScale::kSingle || |
| offset > bytecode_iterator.current_offset()) { |
| StdoutStream os; |
| |
| // Print all output registers and accumulator. |
| PrintRegisters(frame, os, false, bytecode_iterator, accumulator); |
| os << std::flush; |
| } |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| |
| #endif // V8_TRACE_UNOPTIMIZED |
| |
| #ifdef V8_TRACE_FEEDBACK_UPDATES |
| |
| RUNTIME_FUNCTION(Runtime_TraceUpdateFeedback) { |
| if (!v8_flags.trace_feedback_updates) { |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| |
| SealHandleScope shs(isolate); |
| DCHECK_EQ(3, args.length()); |
| Handle<FeedbackVector> vector = args.at<FeedbackVector>(0); |
| int slot = args.smi_value_at(1); |
| auto reason = Cast<String>(args[2]); |
| |
| FeedbackVector::TraceFeedbackChange(isolate, *vector, FeedbackSlot(slot), |
| reason->ToCString().get()); |
| |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| |
| #endif // V8_TRACE_FEEDBACK_UPDATES |
| |
| #ifdef V8_DUMPLING |
| |
| RUNTIME_FUNCTION(Runtime_DumpExecutionFrame) { |
| if (!isolate->dumpling_manager()->IsDumpingEnabled()) { |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| DCHECK_EQ(3, args.length()); |
| |
| SealHandleScope shs(isolate); |
| |
| DisallowGarbageCollection no_gc; |
| |
| JavaScriptStackFrameIterator frame_iterator(isolate); |
| UnoptimizedJSFrame* frame = |
| reinterpret_cast<UnoptimizedJSFrame*>(frame_iterator.frame()); |
| |
| Tagged<JSFunction> function = frame->function(); |
| bool is_sparkplug = frame->is_baseline(); |
| bool is_interpreter = !is_sparkplug; |
| |
| if ((is_sparkplug && !v8_flags.sparkplug_dumping) || |
| (is_interpreter && !v8_flags.interpreter_dumping)) { |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| |
| Handle<BytecodeArray> bytecode_array = CheckedCast<BytecodeArray>(args.at(0)); |
| int bytecode_offset = args.smi_value_at(1); |
| |
| Handle<Object> accumulator = args.at(2); |
| |
| int offset = bytecode_offset - BytecodeArray::kHeaderSize + kHeapObjectTag; |
| interpreter::BytecodeArrayIterator bytecode_iterator(bytecode_array); |
| AdvanceToOffsetForTracing(bytecode_iterator, offset); |
| |
| if (offset == bytecode_iterator.current_offset()) { |
| int function_local_bytecode_offset = bytecode_iterator.current_offset(); |
| DCHECK_GE(function_local_bytecode_offset, 0); |
| DumpFrameType frame_dump_type = |
| is_sparkplug ? kSparkplugFrame : kInterpreterFrame; |
| DumplingUnoptimizedJSFrame frame_view(frame); |
| isolate->dumpling_manager()->DoPrint( |
| &frame_view, function, function_local_bytecode_offset, frame_dump_type, |
| bytecode_array, *accumulator); |
| } |
| |
| return ReadOnlyRoots(isolate).undefined_value(); |
| } |
| |
| #endif // V8_DUMPLING |
| |
| } // namespace internal |
| } // namespace v8 |